diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index 0543f42317..99e41cbece 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -6,29 +6,35 @@ services: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ volumes: !override # bind mount host to /workspaces/immich - ..:/workspaces/immich - - cli_node_modules:/workspaces/immich/cli/node_modules - - e2e_node_modules:/workspaces/immich/e2e/node_modules - - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - - server_node_modules:/workspaces/immich/server/node_modules - - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload - - ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload + - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data + - pnpm-store:/usr/src/app/.pnpm-store + - server-node_modules:/usr/src/app/server/node_modules + - web-node_modules:/usr/src/app/web/node_modules + - github-node_modules:/usr/src/app/.github/node_modules + - cli-node_modules:/usr/src/app/cli/node_modules + - docs-node_modules:/usr/src/app/docs/node_modules + - e2e-node_modules:/usr/src/app/e2e/node_modules + - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules + - app-node_modules:/usr/src/app/node_modules + - sveltekit:/usr/src/app/web/.svelte-kit + - coverage:/usr/src/app/web/coverage - /etc/localtime:/etc/localtime:ro - + immich-web: + env_file: !reset [] + immich-machine-learning: + env_file: !reset [] database: + env_file: !reset [] + environment: !override + POSTGRES_PASSWORD: ${DB_PASSWORD-postgres} + POSTGRES_USER: ${DB_USERNAME-postgres} + POSTGRES_DB: ${DB_DATABASE_NAME-immich} + POSTGRES_INITDB_ARGS: '--data-checksums' + POSTGRES_HOST_AUTH_METHOD: md5 volumes: - - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - + - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data + redis: + env_file: !reset [] volumes: - # Node modules for each service to avoid conflicts and ensure consistent dependencies - cli_node_modules: - e2e_node_modules: - open_api_node_modules: - server_node_modules: - web_node_modules: - - # UPLOAD_LOCATION must be set to a absolute path or vol-upload - vol-upload: - - # DB_DATA_LOCATION must be set to a absolute path or vol-database - vol-database: + upload-devcontainer-volume: + postgres-devcontainer-volume: diff --git a/.devcontainer/mobile/devcontainer.json b/.devcontainer/mobile/devcontainer.json index 0dbcc8e9c8..140a2ecac3 100644 --- a/.devcontainer/mobile/devcontainer.json +++ b/.devcontainer/mobile/devcontainer.json @@ -40,7 +40,7 @@ "userEnvProbe": "loginInteractiveShell", "remoteEnv": { // The location where your uploaded files are stored - "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./Library}", + "UPLOAD_LOCATION": "${localEnv:UPLOAD_LOCATION:./library}", // Connection secret for postgres. You should change it to a random password // Please use only the characters `A-Za-z0-9`, without special characters or spaces "DB_PASSWORD": "${localEnv:DB_PASSWORD:postgres}", diff --git a/.devcontainer/server/container-common.sh b/.devcontainer/server/container-common.sh index 544674e169..3aa72379c3 100755 --- a/.devcontainer/server/container-common.sh +++ b/.devcontainer/server/container-common.sh @@ -49,10 +49,11 @@ fix_permissions() { log "Fixing permissions for ${IMMICH_WORKSPACE}" - run_cmd sudo find "${IMMICH_WORKSPACE}/server/upload" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres/*" -not -path "${IMMICH_WORKSPACE}/server/upload/postgres" -exec chown node {} + - # Change ownership for directories that exist for dir in "${IMMICH_WORKSPACE}/.vscode" \ + "${IMMICH_WORKSPACE}/server/upload" \ + "${IMMICH_WORKSPACE}/.pnpm-store" \ + "${IMMICH_WORKSPACE}/.github/node_modules" \ "${IMMICH_WORKSPACE}/cli/node_modules" \ "${IMMICH_WORKSPACE}/e2e/node_modules" \ "${IMMICH_WORKSPACE}/open-api/typescript-sdk/node_modules" \ diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index 24ac9734b1..3be5cd8f3f 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -8,21 +8,23 @@ services: - IMMICH_SERVER_URL=http://127.0.0.1:2283/ volumes: !override - ..:/workspaces/immich - - cli_node_modules:/workspaces/immich/cli/node_modules - - e2e_node_modules:/workspaces/immich/e2e/node_modules - - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - - server_node_modules:/workspaces/immich/server/node_modules - - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/usr/src/app/upload - - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/usr/src/app/upload/upload + - ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data - /etc/localtime:/etc/localtime:ro - + - pnpm-store:/usr/src/app/.pnpm-store + - server-node_modules:/usr/src/app/server/node_modules + - web-node_modules:/usr/src/app/web/node_modules + - github-node_modules:/usr/src/app/.github/node_modules + - cli-node_modules:/usr/src/app/cli/node_modules + - docs-node_modules:/usr/src/app/docs/node_modules + - e2e-node_modules:/usr/src/app/e2e/node_modules + - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules + - app-node_modules:/usr/src/app/node_modules + - sveltekit:/usr/src/app/web/.svelte-kit + - coverage:/usr/src/app/web/coverage immich-web: env_file: !reset [] - immich-machine-learning: env_file: !reset [] - database: env_file: !reset [] environment: !override @@ -33,17 +35,8 @@ services: POSTGRES_HOST_AUTH_METHOD: md5 volumes: - ${UPLOAD_LOCATION:-postgres-devcontainer-volume}${UPLOAD_LOCATION:+/postgres}:/var/lib/postgresql/data - redis: env_file: !reset [] - volumes: - # Node modules for each service to avoid conflicts and ensure consistent dependencies - cli_node_modules: - e2e_node_modules: - open_api_node_modules: - server_node_modules: - web_node_modules: - upload1-devcontainer-volume: - upload2-devcontainer-volume: + upload-devcontainer-volume: postgres-devcontainer-volume: diff --git a/.devcontainer/server/container-start-backend.sh b/.devcontainer/server/container-start-backend.sh index aeb70df72d..35fa60f89b 100755 --- a/.devcontainer/server/container-start-backend.sh +++ b/.devcontainer/server/container-start-backend.sh @@ -3,6 +3,11 @@ # shellcheck disable=SC1091 source /immich-devcontainer/container-common.sh +log "Preparing Immich Nest API Server" +log "" +export CI=1 +run_cmd pnpm --filter immich install + log "Starting Nest API Server" log "" cd "${IMMICH_WORKSPACE}/server" || ( @@ -11,7 +16,7 @@ cd "${IMMICH_WORKSPACE}/server" || ( ) while true; do - run_cmd node ./node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch + run_cmd pnpm --filter immich exec nest start --debug "0.0.0.0:9230" --watch log "Nest API Server crashed with exit code $?. Respawning in 3s ..." sleep 3 done diff --git a/.devcontainer/server/container-start-frontend.sh b/.devcontainer/server/container-start-frontend.sh index 633dcc3a93..9a0d617d41 100755 --- a/.devcontainer/server/container-start-frontend.sh +++ b/.devcontainer/server/container-start-frontend.sh @@ -3,6 +3,13 @@ # shellcheck disable=SC1091 source /immich-devcontainer/container-common.sh +export CI=1 +log "Preparing Immich Web Frontend" +log "" +run_cmd pnpm --filter @immich/sdk install +run_cmd pnpm --filter @immich/sdk build +run_cmd pnpm --filter immich-web install + log "Starting Immich Web Frontend" log "" cd "${IMMICH_WORKSPACE}/web" || ( @@ -16,7 +23,7 @@ until curl --output /dev/null --silent --head --fail "http://127.0.0.1:${IMMICH_ done while true; do - run_cmd node ./node_modules/.bin/vite dev --host 0.0.0.0 --port "${DEV_PORT}" + run_cmd pnpm --filter immich-web exec vite dev --host 0.0.0.0 --port "${DEV_PORT}" log "Web crashed with exit code $?. Respawning in 3s ..." sleep 3 done diff --git a/.devcontainer/server/container-start.sh b/.devcontainer/server/container-start.sh index 860b2826b0..0edd38172e 100755 --- a/.devcontainer/server/container-start.sh +++ b/.devcontainer/server/container-start.sh @@ -6,9 +6,6 @@ source /immich-devcontainer/container-common.sh log "Setting up Immich dev container..." fix_permissions -log "Installing npm dependencies (node_modules)..." -install_dependencies - log "Setup complete, please wait while backend and frontend services automatically start" log log "If necessary, the services may be manually started using" diff --git a/.github/.nvmrc b/.github/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 1ac0e04332..9ed1be3655 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -64,6 +64,11 @@ body: - label: Web - label: Mobile + - type: input + attributes: + label: Device make and model + placeholder: Samsung S25 Android 16 + - type: textarea validations: required: true diff --git a/.github/labeler.yml b/.github/labeler.yml index c0c52f1d7e..d8923a3035 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -6,7 +6,6 @@ cli: documentation: - changed-files: - any-glob-to-any-file: - - docs/blob/** - docs/docs/** - docs/src/** - docs/static/** diff --git a/.github/package-lock.json b/.github/package-lock.json deleted file mode 100644 index bea1c66e46..0000000000 --- a/.github/package-lock.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": ".github", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "prettier": "^3.5.3" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - } - } -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index aa756a7d08..0bd3b30814 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -34,3 +34,7 @@ The `/api/something` endpoint is now `/api/something-else` - [ ] I have followed naming conventions/patterns in the surrounding code - [ ] All code in `src/services/` uses repositories implementations for database calls, filesystem operations, etc. - [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services/`) + +## Please describe to which degree, if any, an LLM was used in creating this pull request. + +... diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index ca24e33b52..454b954597 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -32,24 +32,18 @@ jobs: permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | mobile: - 'mobile/**' - workflow: - - '.github/workflows/build-mobile.yml' - - name: Check if we should force jobs to run - id: should_force - run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT" + force-filters: | + - '.github/workflows/build-mobile.yml' + force-events: 'workflow_call,workflow_dispatch' build-sign-android: name: Build and sign Android @@ -57,11 +51,11 @@ jobs: permissions: contents: read # Skip when PR from a fork - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && needs.pre-job.outputs.should_run == 'true' }} + if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && fromJSON(needs.pre-job.outputs.should_run).mobile == true }} runs-on: mich steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: ref: ${{ inputs.ref || github.sha }} persist-credentials: false @@ -79,7 +73,7 @@ jobs: - name: Restore Gradle Cache id: cache-gradle-restore - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.gradle/caches @@ -106,7 +100,7 @@ jobs: run: flutter pub get - name: Generate translation file - run: make translation + run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart working-directory: ./mobile - name: Generate platform APIs @@ -122,21 +116,21 @@ jobs: IS_MAIN: ${{ github.ref == 'refs/heads/main' }} run: | if [[ $IS_MAIN == 'true' ]]; then - flutter build apk --release --flavor production - flutter build apk --release --flavor production --split-per-abi --target-platform android-arm,android-arm64,android-x64 + flutter build apk --release + flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64 else - flutter build apk --debug --flavor production --split-per-abi --target-platform android-arm64 + flutter build apk --debug --split-per-abi --target-platform android-arm64 fi - name: Publish Android Artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-apk-signed - path: mobile/build/app/outputs/flutter-apk/**/*.apk + path: mobile/build/app/outputs/flutter-apk/*.apk - name: Save Gradle Cache id: cache-gradle-save - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 if: github.ref == 'refs/heads/main' with: path: | diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml index 68ab8af24e..cdff8ed931 100644 --- a/.github/workflows/cache-cleanup.yml +++ b/.github/workflows/cache-cleanup.yml @@ -19,7 +19,7 @@ jobs: actions: write steps: - name: Check out code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 9729450a91..27dab9aef3 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -29,25 +29,28 @@ jobs: working-directory: ./cli steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - # Setup .npmrc file to publish to npm - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' registry-url: 'https://registry.npmjs.org' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - - name: Prepare SDK - run: npm ci --prefix ../open-api/typescript-sdk/ - - name: Build SDK - run: npm run build --prefix ../open-api/typescript-sdk/ - - run: npm ci - - run: npm run build - - run: npm publish + - name: Setup typescript-sdk + run: pnpm install && pnpm run build + working-directory: ./open-api/typescript-sdk + + - run: pnpm install --frozen-lockfile + - run: pnpm build + - run: pnpm publish --no-git-checks if: ${{ github.event_name == 'release' }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -62,7 +65,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false @@ -73,7 +76,7 @@ jobs: uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 if: ${{ !github.event.pull_request.head.repo.fork }} with: registry: ghcr.io @@ -88,7 +91,7 @@ jobs: - name: Generate docker image tags id: metadata - uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 with: flavor: | latest=false diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml new file mode 100644 index 0000000000..8470e0e18c --- /dev/null +++ b/.github/workflows/close-duplicates.yml @@ -0,0 +1,107 @@ +on: + issues: + types: [opened] + discussion: + types: [created] + +name: Close likely duplicates +permissions: {} + +jobs: + should_run: + runs-on: ubuntu-latest + outputs: + should_run: ${{ steps.should_run.outputs.run }} + steps: + - id: should_run + run: echo "run=${{ github.event_name == 'issues' || github.event.discussion.category.name == 'Feature Request' }}" >> $GITHUB_OUTPUT + + get_body: + runs-on: ubuntu-latest + needs: should_run + if: ${{ needs.should_run.outputs.should_run == 'true' }} + env: + EVENT: ${{ toJSON(github.event) }} + outputs: + body: ${{ steps.get_body.outputs.body }} + steps: + - id: get_body + run: | + BODY=$(echo """$EVENT""" | jq -r '.issue // .discussion | .body' | base64 -w 0) + echo "body=$BODY" >> $GITHUB_OUTPUT + + get_checkbox_json: + runs-on: ubuntu-latest + needs: [get_body, should_run] + if: ${{ needs.should_run.outputs.should_run == 'true' }} + container: + image: ghcr.io/immich-app/mdq:main@sha256:d8ae47cf2e6cf4e2559bd57a60b73674fe44f897cba2c2bddff2987a05be10a4 + outputs: + checked: ${{ steps.get_checkbox.outputs.checked }} + steps: + - id: get_checkbox + env: + BODY: ${{ needs.get_body.outputs.body }} + run: | + CHECKED=$(echo "$BODY" | base64 -d | /mdq --output json '# I have searched | - [?] Yes' | jq '.items[0].list[0].checked // false') + echo "checked=$CHECKED" >> $GITHUB_OUTPUT + + close_and_comment: + runs-on: ubuntu-latest + needs: [get_checkbox_json, should_run] + if: ${{ needs.should_run.outputs.should_run == 'true' && needs.get_checkbox_json.outputs.checked != 'true' }} + permissions: + issues: write + discussions: write + steps: + - name: Close issue + if: ${{ github.event_name == 'issues' }} + env: + GH_TOKEN: ${{ github.token }} + NODE_ID: ${{ github.event.issue.node_id }} + run: | + gh api graphql \ + -f issueId="$NODE_ID" \ + -f body="This issue has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one. If you're sure this is not a duplicate, please leave a comment and we will reopen the thread if necessary." \ + -f query=' + mutation CommentAndCloseIssue($issueId: ID!, $body: String!) { + addComment(input: { + subjectId: $issueId, + body: $body + }) { + __typename + } + + closeIssue(input: { + issueId: $issueId, + stateReason: DUPLICATE + }) { + __typename + } + }' + + - name: Close discussion + if: ${{ github.event_name == 'discussion' && github.event.discussion.category.name == 'Feature Request' }} + env: + GH_TOKEN: ${{ github.token }} + NODE_ID: ${{ github.event.discussion.node_id }} + run: | + gh api graphql \ + -f discussionId="$NODE_ID" \ + -f body="This discussion has automatically been closed as it is likely a duplicate. We get a lot of duplicate threads each day, which is why we ask you in the template to confirm that you searched for duplicates before opening one. If you're sure this is not a duplicate, please leave a comment and we will reopen the thread if necessary." \ + -f query=' + mutation CommentAndCloseDiscussion($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + __typename + } + + closeDiscussion(input: { + discussionId: $discussionId, + reason: DUPLICATE + }) { + __typename + } + }' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f1e68afce..4d4cbdf49b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -44,13 +44,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 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@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 # â„šī¸ 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@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 03f74dd7a3..7a63fcc881 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,15 +20,11 @@ jobs: permissions: contents: read outputs: - should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | server: @@ -38,14 +34,11 @@ jobs: - 'i18n/**' machine-learning: - 'machine-learning/**' - workflow: - - '.github/workflows/docker.yml' - - '.github/workflows/multi-runner-build.yml' - - '.github/actions/image-build' - - - name: Check if we should force jobs to run - id: should_force - run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT" + force-filters: | + - '.github/workflows/docker.yml' + - '.github/workflows/multi-runner-build.yml' + - '.github/actions/image-build' + force-events: 'workflow_dispatch,release' retag_ml: name: Re-Tag ML @@ -53,14 +46,14 @@ jobs: permissions: contents: read packages: write - if: ${{ needs.pre-job.outputs.should_run_ml == 'false' && !github.event.pull_request.head.repo.fork }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).machine-learning == false && !github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest strategy: matrix: suffix: ['', '-cuda', '-rocm', '-openvino', '-armnn', '-rknn'] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -82,14 +75,14 @@ jobs: permissions: contents: read packages: write - if: ${{ needs.pre-job.outputs.should_run_server == 'false' && !github.event.pull_request.head.repo.fork }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == false && !github.event.pull_request.head.repo.fork }} runs-on: ubuntu-latest strategy: matrix: suffix: [''] steps: - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -108,7 +101,7 @@ jobs: machine-learning: name: Build and Push ML needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).machine-learning == true }} strategy: fail-fast: false matrix: @@ -153,7 +146,7 @@ jobs: server: name: Build and Push Server needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@129aeda75a450666ce96e8bc8126652e717917a7 # multi-runner-build-workflow-0.1.1 permissions: contents: read diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 93b6c8ad04..0879c30386 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -18,30 +18,28 @@ jobs: permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.docs == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | docs: - 'docs/**' - workflow: - - '.github/workflows/docs-build.yml' - - name: Check if we should force jobs to run - id: should_force - run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' || github.ref_name == 'main' }}" >> "$GITHUB_OUTPUT" + open-api: + - 'open-api/immich-openapi-specs.json' + force-filters: | + - '.github/workflows/docs-build.yml' + force-events: 'release' + force-branches: 'main' build: name: Docs Build needs: pre-job permissions: contents: read - if: ${{ needs.pre-job.outputs.should_run == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).docs == true }} runs-on: ubuntu-latest defaults: run: @@ -49,25 +47,28 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './docs/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - - name: Run npm install - run: npm ci + - name: Run install + run: pnpm install - name: Check formatting - run: npm run format + run: pnpm format - name: Run build - run: npm run build + run: pnpm build - name: Upload build output uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index c04adbafc6..b504b811e3 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -20,7 +20,7 @@ jobs: run: echo 'The triggering workflow did not succeed' && exit 1 - name: Get artifact id: get-artifact - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -38,7 +38,7 @@ jobs: return { found: true, id: matchArtifact.id }; - name: Determine deploy parameters id: parameters - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 env: HEAD_SHA: ${{ github.event.workflow_run.head_sha }} with: @@ -108,13 +108,13 @@ jobs: if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - name: Load parameters id: parameters - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 env: PARAM_JSON: ${{ needs.checks.outputs.parameters }} with: @@ -125,7 +125,7 @@ jobs: core.setOutput("shouldDeploy", parameters.shouldDeploy); - name: Download artifact - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 env: ARTIFACT_JSON: ${{ needs.checks.outputs.artifact }} with: diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index cd095b117f..37653c0990 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 7ef80306ba..849de79a47 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -16,24 +16,27 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: 'Checkout' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ steps.generate-token.outputs.token }} persist-credentials: true + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Fix formatting run: make install-all && make format-all @@ -45,7 +48,7 @@ jobs: message: 'chore: fix formatting' - name: Remove label - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 if: always() with: script: | diff --git a/.github/workflows/merge-translations.yml b/.github/workflows/merge-translations.yml new file mode 100644 index 0000000000..d494460320 --- /dev/null +++ b/.github/workflows/merge-translations.yml @@ -0,0 +1,128 @@ +name: Merge translations + +on: + workflow_dispatch: + workflow_call: + secrets: + PUSH_O_MATIC_APP_ID: + required: true + PUSH_O_MATIC_APP_KEY: + required: true + WEBLATE_TOKEN: + required: true + inputs: + skip: + description: 'Skip translations' + required: false + type: boolean + +permissions: {} + +env: + WEBLATE_HOST: 'https://hosted.weblate.org' + WEBLATE_COMPONENT: 'immich/immich' + +jobs: + merge: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Find translation PR + id: find_pr + if: ${{ inputs.skip != true }} + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + PR=$(gh pr list --repo $GITHUB_REPOSITORY --author weblate --json number,mergeable) + echo "$PR" + + PR_NUMBER=$(echo "$PR" | jq ' + if length == 1 then + .[0].number + else + error("Expected exactly 1 entry, got \(length)") + end + ' 2>&1) || exit 1 + + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "Selected PR $PR_NUMBER" + + if ! echo "$PR" | jq -e '.[0].mergeable == "MERGEABLE"'; then + echo "PR is not mergeable" + exit 1 + fi + + - name: Generate a token + id: generate_token + if: ${{ inputs.skip != true }} + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + with: + app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} + private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} + + - name: Lock weblate + if: ${{ inputs.skip != true }} + env: + WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }} + run: | + curl --fail-with-body -X POST -H "Authorization: Token $WEBLATE_TOKEN" "$WEBLATE_HOST/api/components/$WEBLATE_COMPONENT/lock/" -d lock=true + + - name: Commit translations + if: ${{ inputs.skip != true }} + env: + WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }} + run: | + curl --fail-with-body -X POST -H "Authorization: Token $WEBLATE_TOKEN" "$WEBLATE_HOST/api/components/$WEBLATE_COMPONENT/repository/" -d operation=commit + curl --fail-with-body -X POST -H "Authorization: Token $WEBLATE_TOKEN" "$WEBLATE_HOST/api/components/$WEBLATE_COMPONENT/repository/" -d operation=push + + - name: Merge PR + id: merge_pr + if: ${{ inputs.skip != true }} + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + PR_NUMBER: ${{ steps.find_pr.outputs.PR_NUMBER }} + run: | + set -euo pipefail + + REVIEW_ID=$(gh api -X POST "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" --field event='APPROVE' --field body='Automatically merging translations PR' \ + | jq '.id') + echo "REVIEW_ID=$REVIEW_ID" >> $GITHUB_OUTPUT + gh pr merge "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --auto --squash + + - name: Wait for PR to merge + if: ${{ inputs.skip != true }} + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + PR_NUMBER: ${{ steps.find_pr.outputs.PR_NUMBER }} + REVIEW_ID: ${{ steps.merge_pr.outputs.REVIEW_ID }} + run: | + # So we clean up no matter what + set +e + + for i in {1..100}; do + if gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json state | jq -e '.state == "MERGED"'; then + echo "PR merged" + exit 0 + else + echo "PR not merged yet, waiting..." + sleep 6 + fi + done + echo "PR did not merge in time" + gh api -X PUT "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews/$REVIEW_ID/dismissals" --field message='Merge attempt timed out' --field event='DISMISS' + gh pr merge "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --disable-auto + exit 1 + + - name: Unlock weblate + if: ${{ inputs.skip != true }} + env: + WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }} + run: | + curl --fail-with-body -X POST -H "Authorization: Token $WEBLATE_TOKEN" "$WEBLATE_HOST/api/components/$WEBLATE_COMPONENT/lock/" -d lock=false + + - name: Report success + run: | + echo "Workflow completed successfully (or was skipped)" diff --git a/.github/workflows/org-checks.yml b/.github/workflows/org-checks.yml deleted file mode 100644 index 9781dc3b83..0000000000 --- a/.github/workflows/org-checks.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Org Checks - -on: - pull_request_review: - pull_request: - -jobs: - check-approvals: - name: Check for Team/Admin Review - uses: immich-app/devtools/.github/workflows/required-approval.yml@main - permissions: - pull-requests: read - contents: read diff --git a/.github/workflows/org-pr-require-conventional-commit.yml b/.github/workflows/org-pr-require-conventional-commit.yml new file mode 100644 index 0000000000..5e5f84ef39 --- /dev/null +++ b/.github/workflows/org-pr-require-conventional-commit.yml @@ -0,0 +1,12 @@ +name: PR Conventional Commit + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + validate-pr-title: + name: Validate PR Title (conventional commit) + uses: immich-app/devtools/.github/workflows/shared-pr-require-conventional-commit.yml@main + permissions: + pull-requests: write diff --git a/.github/workflows/org-zizmor.yml b/.github/workflows/org-zizmor.yml new file mode 100644 index 0000000000..8510fd85b4 --- /dev/null +++ b/.github/workflows/org-zizmor.yml @@ -0,0 +1,15 @@ +name: Zizmor + +on: + pull_request: + push: + branches: [main] + +jobs: + zizmor: + name: Zizmor + uses: immich-app/devtools/.github/workflows/shared-zizmor.yml@main + permissions: + actions: read + contents: read + security-events: write diff --git a/.github/workflows/pr-require-conventional-commit.yml b/.github/workflows/pr-require-conventional-commit.yml deleted file mode 100644 index 78ba77495c..0000000000 --- a/.github/workflows/pr-require-conventional-commit.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: PR Conventional Commit Validation - -on: - pull_request: - types: [opened, synchronize, reopened, edited] - -permissions: {} - -jobs: - validate-pr-title: - runs-on: ubuntu-latest - permissions: - pull-requests: write - steps: - - name: PR Conventional Commit Validation - uses: ytanikin/PRConventionalCommits@b628c5a234cc32513014b7bfdd1e47b532124d98 # 1.3.0 - with: - task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]' - add_label: 'false' diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index fa1152c336..8b6dc0af1c 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -10,12 +10,17 @@ on: type: choice options: - 'false' + - major - minor - patch mobileBump: description: 'Bump mobile build number' required: false type: boolean + skipTranslations: + description: 'Skip translations' + required: false + type: boolean concurrency: group: ${{ github.workflow }}-${{ github.ref }}-root @@ -24,28 +29,51 @@ concurrency: permissions: {} jobs: + merge_translations: + uses: ./.github/workflows/merge-translations.yml + with: + skip: ${{ inputs.skipTranslations }} + permissions: + pull-requests: write + secrets: + PUSH_O_MATIC_APP_ID: ${{ secrets.PUSH_O_MATIC_APP_ID }} + PUSH_O_MATIC_APP_KEY: ${{ secrets.PUSH_O_MATIC_APP_KEY }} + WEBLATE_TOKEN: ${{ secrets.WEBLATE_TOKEN }} + bump_version: runs-on: ubuntu-latest + needs: [merge_translations] outputs: ref: ${{ steps.push-tag.outputs.commit_long_sha }} permissions: {} # No job-level permissions are needed because it uses the app-token steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: token: ${{ steps.generate-token.outputs.token }} persist-credentials: true + ref: main - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: './server/.nvmrc' + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' + - name: Bump version env: SERVER_BUMP: ${{ inputs.serverBump }} @@ -83,13 +111,13 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: token: ${{ steps.generate-token.outputs.token }} persist-credentials: false @@ -100,7 +128,7 @@ jobs: name: release-apk-signed - name: Create draft release - uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 + uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3 with: draft: true tag_name: ${{ env.IMMICH_VERSION }} diff --git a/.github/workflows/preview-label.yaml b/.github/workflows/preview-label.yaml index edd9dfdae9..1d9a0060ad 100644 --- a/.github/workflows/preview-label.yaml +++ b/.github/workflows/preview-label.yaml @@ -20,11 +20,11 @@ jobs: remove-label: runs-on: ubuntu-latest - if: ${{ github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'preview') }} + if: ${{ (github.event.action == 'closed' || github.event.pull_request.head.repo.fork) && contains(github.event.pull_request.labels.*.name, 'preview') }} permissions: pull-requests: write steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | github.rest.issues.removeLabel({ @@ -33,3 +33,15 @@ jobs: repo: context.repo.repo, name: 'preview' }) + + - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 + if: ${{ github.event.pull_request.head.repo.fork }} + with: + message-id: 'preview-status' + message: 'PRs from forks cannot have preview environments.' + + - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 + if: ${{ !github.event.pull_request.head.repo.fork }} + with: + message-id: 'preview-status' + message: 'Preview environment has been removed.' diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index c94ee14209..c541fac3c1 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -16,22 +16,25 @@ jobs: run: working-directory: ./open-api/typescript-sdk steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + # Setup .npmrc file to publish to npm - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './open-api/typescript-sdk/.nvmrc' registry-url: 'https://registry.npmjs.org' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Install deps - run: npm ci + run: pnpm install --frozen-lockfile - name: Build - run: npm run build + run: pnpm build - name: Publish - run: npm publish + run: pnpm publish --no-git-checks env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 4cb788f5fa..d30f95422c 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -17,28 +17,23 @@ jobs: permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | mobile: - 'mobile/**' - workflow: - - '.github/workflows/static_analysis.yml' - - name: Check if we should force jobs to run - id: should_force - run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'release' }}" >> "$GITHUB_OUTPUT" + force-filters: | + - '.github/workflows/static_analysis.yml' + force-events: 'workflow_dispatch,release' mobile-dart-analyze: name: Run Dart Code Analysis needs: pre-job - if: ${{ needs.pre-job.outputs.should_run == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).mobile == true }} runs-on: ubuntu-latest permissions: contents: read @@ -47,7 +42,7 @@ jobs: working-directory: ./mobile steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false @@ -68,7 +63,7 @@ jobs: working-directory: ./mobile - name: Generate translation file - run: make translation + run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart - name: Run Build Runner run: make build @@ -90,7 +85,7 @@ jobs: env: CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} run: | - echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory" + echo "ERROR: Generated files not up to date! Run 'make build' and 'make pigeon' inside the mobile directory" echo "Changed files: ${CHANGED_FILES}" exit 1 @@ -98,38 +93,12 @@ jobs: run: dart analyze --fatal-infos - name: Run dart format - run: dart format lib/ --set-exit-if-changed + run: make format - - name: Run dart custom_lint - run: dart run custom_lint + # TODO: Re-enable after upgrading custom_lint + # - name: Run dart custom_lint + # run: dart run custom_lint # TODO: Use https://github.com/CQLabs/dcm-action - name: Run DCM run: dcm analyze lib --fatal-style --fatal-warnings - - zizmor: - name: zizmor - runs-on: ubuntu-latest - permissions: - security-events: write - contents: read - actions: read - steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Install the latest version of uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - - 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@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 - with: - sarif_file: results.sarif - category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 47a11c8232..eaf9e2c080 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,37 +4,21 @@ on: pull_request: push: branches: [main] - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true - permissions: {} - jobs: pre-job: runs-on: ubuntu-latest permissions: contents: read outputs: - should_run_i18n: ${{ steps.found_paths.outputs.i18n == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_web: ${{ steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_server: ${{ steps.found_paths.outputs.server == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_cli: ${{ steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_e2e: ${{ steps.found_paths.outputs.e2e == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_mobile: ${{ steps.found_paths.outputs.mobile == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_ml: ${{ steps.found_paths.outputs.machine-learning == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_e2e_web: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.web == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_e2e_server_cli: ${{ steps.found_paths.outputs.e2e == 'true' || steps.found_paths.outputs.server == 'true' || steps.found_paths.outputs.cli == 'true' || steps.should_force.outputs.should_force == 'true' }} - should_run_.github: ${{ steps.found_paths.outputs['.github'] == 'true' || steps.should_force.outputs.should_force == 'true' }} # redundant to have should_force but if someone changes the trigger then this won't have to be changed + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | i18n: @@ -54,260 +38,225 @@ jobs: - 'mobile/**' machine-learning: - 'machine-learning/**' - workflow: - - '.github/workflows/test.yml' .github: - '.github/**' - - - name: Check if we should force jobs to run - id: should_force - run: echo "should_force=${{ steps.found_paths.outputs.workflow == 'true' || github.event_name == 'workflow_dispatch' }}" >> "$GITHUB_OUTPUT" + force-filters: | + - '.github/workflows/test.yml' + force-events: 'workflow_dispatch' server-unit-tests: name: Test & Lint Server needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./server - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - - - name: Run npm install - run: npm ci - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' + - name: Run package manager install + run: pnpm install - name: Run linter - run: npm run lint + run: pnpm lint if: ${{ !cancelled() }} - - name: Run formatter - run: npm run format + run: pnpm format if: ${{ !cancelled() }} - - name: Run tsc - run: npm run check + run: pnpm check if: ${{ !cancelled() }} - - name: Run small tests & coverage - run: npm test + run: pnpm test if: ${{ !cancelled() }} - cli-unit-tests: name: Unit Test CLI needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).cli == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./cli - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Setup typescript-sdk - run: npm ci && npm run build + run: pnpm install && pnpm run build working-directory: ./open-api/typescript-sdk - - name: Install deps - run: npm ci - + run: pnpm install - name: Run linter - run: npm run lint + run: pnpm lint if: ${{ !cancelled() }} - - name: Run formatter - run: npm run format + run: pnpm format if: ${{ !cancelled() }} - - name: Run tsc - run: npm run check + run: pnpm check if: ${{ !cancelled() }} - - name: Run unit tests & coverage - run: npm run test + run: pnpm test if: ${{ !cancelled() }} - cli-unit-tests-win: name: Unit Test CLI (Windows) needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_cli == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).cli == true }} runs-on: windows-latest permissions: contents: read defaults: run: working-directory: ./cli - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './cli/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk - - name: Install deps - run: npm ci - + run: pnpm install --frozen-lockfile # Skip linter & formatter in Windows test. - name: Run tsc - run: npm run check + run: pnpm check if: ${{ !cancelled() }} - - name: Run unit tests & coverage - run: npm run test + run: pnpm test if: ${{ !cancelled() }} - web-lint: name: Lint Web needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} + if: ${{ fromJSON(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.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Run setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk - - - name: Run npm install - run: npm ci - + - name: Run pnpm install + run: pnpm rebuild && pnpm install --frozen-lockfile - name: Run linter - run: npm run lint:p + run: pnpm lint if: ${{ !cancelled() }} - - name: Run formatter - run: npm run format + run: pnpm format if: ${{ !cancelled() }} - - name: Run svelte checks - run: npm run check:svelte + run: pnpm check:svelte if: ${{ !cancelled() }} - web-unit-tests: name: Test Web needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_web == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).web == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./web - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Run setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk - - name: Run npm install - run: npm ci - + run: pnpm install --frozen-lockfile - name: Run tsc - run: npm run check:typescript + run: pnpm check:typescript if: ${{ !cancelled() }} - - name: Run unit tests & coverage - run: npm run test + run: pnpm test if: ${{ !cancelled() }} - i18n-tests: name: Test i18n needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_i18n == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }} runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './web/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Install dependencies - run: npm --prefix=web ci - + run: pnpm --filter=immich-web install --frozen-lockfile - name: Format - run: npm --prefix=web run format:i18n - + run: pnpm --filter=immich-web format:i18n - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files with: files: | i18n/** - - name: Verify files have not changed if: steps.verify-changed-files.outputs.files_changed == 'true' env: @@ -316,87 +265,77 @@ jobs: echo "ERROR: i18n files not up to date!" echo "Changed files: ${CHANGED_FILES}" exit 1 - e2e-tests-lint: name: End-to-End Lint needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_e2e == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./e2e - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Run setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk if: ${{ !cancelled() }} - - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile if: ${{ !cancelled() }} - - name: Run linter - run: npm run lint + run: pnpm lint if: ${{ !cancelled() }} - - name: Run formatter - run: npm run format + run: pnpm format if: ${{ !cancelled() }} - - name: Run tsc - run: npm run check + run: pnpm check if: ${{ !cancelled() }} - server-medium-tests: name: Medium Tests (Server) needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_server == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./server - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - - - name: Run npm install - run: npm ci - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' + - name: Run pnpm install + run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile - name: Run medium tests - run: npm run test:medium + run: pnpm test:medium if: ${{ !cancelled() }} - e2e-tests-server-cli: name: End-to-End Tests (Server & CLI) needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_e2e_server_cli == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).server == true || fromJSON(needs.pre-job.outputs.should_run).cli == true }} runs-on: ${{ matrix.runner }} permissions: contents: read @@ -406,47 +345,45 @@ jobs: strategy: matrix: runner: [ubuntu-latest, ubuntu-24.04-arm] - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false submodules: 'recursive' - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Run setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk if: ${{ !cancelled() }} - + - name: Run setup web + run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync + working-directory: ./web + if: ${{ !cancelled() }} - name: Run setup cli - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./cli if: ${{ !cancelled() }} - - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile if: ${{ !cancelled() }} - - name: Docker build run: docker compose build if: ${{ !cancelled() }} - - name: Run e2e tests (api & cli) - run: npm run test + run: pnpm test if: ${{ !cancelled() }} - e2e-tests-web: name: End-to-End Tests (Web) needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_e2e_web == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).web == true }} runs-on: ${{ matrix.runner }} permissions: contents: read @@ -456,42 +393,36 @@ jobs: strategy: matrix: runner: [ubuntu-latest, ubuntu-24.04-arm] - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false submodules: 'recursive' - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './e2e/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Run setup typescript-sdk - run: npm ci && npm run build + run: pnpm install --frozen-lockfile && pnpm build working-directory: ./open-api/typescript-sdk if: ${{ !cancelled() }} - - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile if: ${{ !cancelled() }} - - name: Install Playwright Browsers run: npx playwright install chromium --only-shell if: ${{ !cancelled() }} - - name: Docker build run: docker compose build if: ${{ !cancelled() }} - - name: Run e2e tests (web) run: npx playwright test if: ${{ !cancelled() }} - success-check-e2e: name: End-to-End Tests Success needs: [e2e-tests-server-cli, e2e-tests-web] @@ -502,37 +433,32 @@ jobs: - uses: immich-app/devtools/actions/success-check@68f10eb389bb02a3cf9d1156111964c549eb421b # 0.0.4 with: needs: ${{ toJSON(needs) }} - mobile-unit-tests: name: Unit Test Mobile needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_mobile == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).mobile == true }} runs-on: ubuntu-latest permissions: contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - - name: Setup Flutter SDK uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0 with: channel: 'stable' flutter-version-file: ./mobile/pubspec.yaml - - name: Generate translation file - run: make translation + run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart working-directory: ./mobile - - name: Run tests working-directory: ./mobile run: flutter test -j 1 - ml-unit-tests: name: Unit Test ML needs: pre-job - if: ${{ needs.pre-job.outputs.should_run_ml == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).machine-learning == true }} runs-on: ubuntu-latest permissions: contents: read @@ -540,10 +466,9 @@ jobs: run: working-directory: ./machine-learning steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 @@ -566,56 +491,48 @@ jobs: - name: Run tests and coverage run: | uv run pytest --cov=immich_ml --cov-report term-missing - github-files-formatting: name: .github Files Formatting needs: pre-job - if: ${{ needs.pre-job.outputs['should_run_.github'] == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run)['.github'] == true }} runs-on: ubuntu-latest permissions: contents: read defaults: run: working-directory: ./.github - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './.github/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - - - name: Run npm install - run: npm ci - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' + - name: Run pnpm install + run: pnpm install --frozen-lockfile - name: Run formatter - run: npm run format + run: pnpm format if: ${{ !cancelled() }} - shellcheck: name: ShellCheck runs-on: ubuntu-latest permissions: contents: read steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: ignore_paths: >- - **/open-api/** - **/openapi** - **/node_modules/** - + **/open-api/** **/openapi** **/node_modules/** generated-api-up-to-date: name: OpenAPI Clients runs-on: ubuntu-latest @@ -623,26 +540,24 @@ jobs: contents: read steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Install server dependencies - run: npm --prefix=server ci - + run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile - name: Build the app - run: npm --prefix=server run build - + run: pnpm --filter immich build - name: Run API generation - run: make open-api - + run: ./bin/generate-open-api.sh + working-directory: open-api - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files @@ -651,7 +566,6 @@ jobs: mobile/openapi open-api/typescript-sdk open-api/immich-openapi-specs.json - - name: Verify files have not changed if: steps.verify-changed-files.outputs.files_changed == 'true' env: @@ -660,7 +574,6 @@ jobs: echo "ERROR: Generated files not up to date!" echo "Changed files: ${CHANGED_FILES}" exit 1 - sql-schema-up-to-date: name: SQL Schema Checks runs-on: ubuntu-latest @@ -668,51 +581,42 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:da52bbead5d818adaa8077c8dcdaad0aaf93038c31ad8348b51f9f0ec1310a4d env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres POSTGRES_DB: immich options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 + --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 defaults: run: working-directory: ./server - steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: persist-credentials: false - + - name: Setup pnpm + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: './server/.nvmrc' - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - + cache: 'pnpm' + cache-dependency-path: '**/pnpm-lock.yaml' - name: Install server dependencies - run: npm ci - + run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile - name: Build the app - run: npm run build - + run: pnpm build - name: Run existing migrations - run: npm run migrations:run - + run: pnpm migrations:run - name: Test npm run schema:reset command works - run: npm run schema:reset - + run: pnpm schema:reset - name: Generate new migrations continue-on-error: true - run: npm run migrations:generate src/TestMigration - + run: pnpm migrations:generate src/TestMigration - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-files @@ -728,19 +632,16 @@ jobs: echo "Changed files: ${CHANGED_FILES}" cat ./src/*-TestMigration.ts exit 1 - - name: Run SQL generation - run: npm run sync:sql + run: pnpm sync:sql env: DB_URL: postgres://postgres:postgres@localhost:5432/immich - - name: Find file changes uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4 id: verify-changed-sql-files with: files: | server/src/queries - - name: Verify SQL files have not changed if: steps.verify-changed-sql-files.outputs.files_changed == 'true' env: @@ -751,77 +652,77 @@ jobs: git diff exit 1 - # mobile-integration-tests: - # name: Run mobile end-to-end integration tests - # runs-on: macos-latest - # steps: - # - uses: actions/checkout@v4 - # - uses: actions/setup-java@v3 - # with: - # distribution: 'zulu' - # java-version: '12.x' - # cache: 'gradle' - # - name: Cache android SDK - # uses: actions/cache@v3 - # id: android-sdk - # with: - # key: android-sdk - # path: | - # /usr/local/lib/android/ - # ~/.android - # - name: Cache Gradle - # uses: actions/cache@v3 - # with: - # path: | - # ./mobile/build/ - # ./mobile/android/.gradle/ - # key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }} - # - name: Setup Android SDK - # if: steps.android-sdk.outputs.cache-hit != 'true' - # uses: android-actions/setup-android@v2 - # - name: AVD cache - # uses: actions/cache@v3 - # id: avd-cache - # with: - # path: | - # ~/.android/avd/* - # ~/.android/adb* - # key: avd-29 - # - name: create AVD and generate snapshot for caching - # if: steps.avd-cache.outputs.cache-hit != 'true' - # uses: reactivecircus/android-emulator-runner@v2.27.0 - # with: - # working-directory: ./mobile - # cores: 2 - # api-level: 29 - # arch: x86_64 - # profile: pixel - # target: default - # force-avd-creation: false - # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - # disable-animations: false - # script: echo "Generated AVD snapshot for caching." - # - name: Setup Flutter SDK - # uses: subosito/flutter-action@v2 - # with: - # channel: 'stable' - # flutter-version: '3.7.3' - # cache: true - # - name: Run integration tests - # uses: Wandalen/wretry.action@master - # with: - # action: reactivecircus/android-emulator-runner@v2.27.0 - # with: | - # working-directory: ./mobile - # cores: 2 - # api-level: 29 - # arch: x86_64 - # profile: pixel - # target: default - # force-avd-creation: false - # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - # disable-animations: true - # script: | - # flutter pub get - # flutter test integration_test - # attempt_limit: 3 +# mobile-integration-tests: +# name: Run mobile end-to-end integration tests +# runs-on: macos-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: actions/setup-java@v3 +# with: +# distribution: 'zulu' +# java-version: '12.x' +# cache: 'gradle' +# - name: Cache android SDK +# uses: actions/cache@v3 +# id: android-sdk +# with: +# key: android-sdk +# path: | +# /usr/local/lib/android/ +# ~/.android +# - name: Cache Gradle +# uses: actions/cache@v3 +# with: +# path: | +# ./mobile/build/ +# ./mobile/android/.gradle/ +# key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }} +# - name: Setup Android SDK +# if: steps.android-sdk.outputs.cache-hit != 'true' +# uses: android-actions/setup-android@v2 +# - name: AVD cache +# uses: actions/cache@v3 +# id: avd-cache +# with: +# path: | +# ~/.android/avd/* +# ~/.android/adb* +# key: avd-29 +# - name: create AVD and generate snapshot for caching +# if: steps.avd-cache.outputs.cache-hit != 'true' +# uses: reactivecircus/android-emulator-runner@v2.27.0 +# with: +# working-directory: ./mobile +# cores: 2 +# api-level: 29 +# arch: x86_64 +# profile: pixel +# target: default +# force-avd-creation: false +# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: false +# script: echo "Generated AVD snapshot for caching." +# - name: Setup Flutter SDK +# uses: subosito/flutter-action@v2 +# with: +# channel: 'stable' +# flutter-version: '3.7.3' +# cache: true +# - name: Run integration tests +# uses: Wandalen/wretry.action@master +# with: +# action: reactivecircus/android-emulator-runner@v2.27.0 +# with: | +# working-directory: ./mobile +# cores: 2 +# api-level: 29 +# arch: x86_64 +# profile: pixel +# target: default +# force-avd-creation: false +# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none +# disable-animations: true +# script: | +# flutter pub get +# flutter test integration_test +# attempt_limit: 3 diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 084e1a97d6..d7deb244f9 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -3,48 +3,52 @@ name: Weblate checks on: pull_request: branches: [main] + types: + - opened + - synchronize + - ready_for_review + - auto_merge_enabled + - auto_merge_disabled permissions: {} +env: + BOT_NAME: immich-push-o-matic + jobs: pre-job: runs-on: ubuntu-latest permissions: contents: read outputs: - should_run: ${{ steps.found_paths.outputs.i18n == 'true' && github.head_ref != 'chore/translations'}} + should_run: ${{ steps.check.outputs.should_run }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - id: found_paths - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + - name: Check what should run + id: check + uses: immich-app/devtools/actions/pre-job@5f91b52dfbb92b8d96ca411ab59c896cd59714ca # pre-job-action-v1.1.0 with: filters: | i18n: - 'i18n/!(en)**\.json' + exclude-branches: 'chore/translations' + skip-force-logic: 'true' enforce-lock: name: Check Weblate Lock needs: [pre-job] runs-on: ubuntu-latest permissions: {} - if: ${{ needs.pre-job.outputs.should_run == 'true' }} + if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }} steps: - - name: Check weblate lock + - name: Bot review status + env: + PR_NUMBER: ${{ github.event.pull_request.number || github.event.pull_request_review.pull_request.number }} + GH_TOKEN: ${{ github.token }} run: | - if [[ "false" = $(curl https://hosted.weblate.org/api/components/immich/immich/lock/ | jq .locked) ]]; then - exit 1 - fi - - name: Find Pull Request - uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 - id: find-pr - with: - branch: chore/translations - - name: Fail if existing weblate PR - if: ${{ steps.find-pr.outputs.number }} - run: exit 1 + # Then check for APPROVED by the bot, if absent fail + gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json reviews | jq -e '.reviews | map(select(.author.login == env.BOT_NAME and .state == "APPROVED")) | length > 0' \ + || (echo "The push-o-matic bot has not approved this PR yet" && exit 1) + success-check-lock: name: Weblate Lock Check Success needs: [enforce-lock] diff --git a/.gitignore b/.gitignore index af85d96c02..3220701cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ mobile/libisar.dylib mobile/openapi/test mobile/openapi/doc mobile/openapi/.openapi-generator/FILES +mobile/ios/build open-api/typescript-sdk/build mobile/android/fastlane/report.xml @@ -25,3 +26,5 @@ mobile/ios/fastlane/report.xml vite.config.js.timestamp-* .pnpm-store +.devcontainer/library +.devcontainer/.env* diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs new file mode 100644 index 0000000000..0e76dabe66 --- /dev/null +++ b/.pnpmfile.cjs @@ -0,0 +1,18 @@ +module.exports = { + hooks: { + readPackage: (pkg) => { + if (!pkg.name) { + return pkg; + } + if (pkg.name === "exiftool-vendored") { + if (pkg.optionalDependencies["exiftool-vendored.pl"]) { + // make exiftool-vendored.pl a regular dependency + pkg.dependencies["exiftool-vendored.pl"] = + pkg.optionalDependencies["exiftool-vendored.pl"]; + delete pkg.optionalDependencies["exiftool-vendored.pl"]; + } + } + return pkg; + }, + }, +}; diff --git a/.vscode/launch.json b/.vscode/launch.json index 8682376cda..9ed2bb77b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "restart": true, "port": 9231, "name": "Immich API Server", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" }, { @@ -16,27 +16,22 @@ "restart": true, "port": 9230, "name": "Immich Workers", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" }, { - "name": "Flavor - Production", + "type": "node", "request": "launch", - "type": "dart", - "codeLens": { - "for": [ - "run-test", - "run-test-file", - "run-file", - "debug-test", - "debug-test-file", - "debug-file", - ], - "title": "${debugType}", - }, - "args": [ - "--flavor", "production" - ], + "name": "Immich CLI", + "program": "${workspaceFolder}/cli/dist/index.js", + "args": ["upload", "--help"], + "runtimeArgs": ["--enable-source-maps"], + "console": "integratedTerminal", + "resolveSourceMapLocations": ["${workspaceFolder}/cli/dist/**/*.js.map"], + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/cli/dist/**/*.js"], + "skipFiles": ["/**"], + "preLaunchTask": "Build Immich CLI" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 396755a634..b386502a11 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,7 +56,8 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "*.dart": "${capture}.g.dart,${capture}.gr.dart,${capture}.drift.dart", - "*.ts": "${capture}.spec.ts,${capture}.mock.ts" + "*.ts": "${capture}.spec.ts,${capture}.mock.ts", + "package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock, pnpm-workspace.yaml, .pnpmfile.cjs" }, "svelte.enable-ts-plugin": true, "typescript.preferences.importModuleSpecifier": "non-relative" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 119d6a961b..478a46b4bd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,6 +5,7 @@ "label": "Fix Permissions, Install Dependencies", "type": "shell", "command": "[ -f /immich-devcontainer/container-start.sh ] && /immich-devcontainer/container-start.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -25,6 +26,7 @@ "dependsOn": ["Fix Permissions, Install Dependencies"], "type": "shell", "command": "[ -f /immich-devcontainer/container-start-backend.sh ] && /immich-devcontainer/container-start-backend.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -45,6 +47,7 @@ "dependsOn": ["Fix Permissions, Install Dependencies"], "type": "shell", "command": "[ -f /immich-devcontainer/container-start-frontend.sh ] && /immich-devcontainer/container-start-frontend.sh || exit 0", + "isBackground": true, "presentation": { "echo": true, "reveal": "always", @@ -67,6 +70,11 @@ "runOn": "folderOpen" }, "problemMatcher": [] + }, + { + "label": "Build Immich CLI", + "type": "shell", + "command": "pnpm --filter cli build:dev" } ] } diff --git a/CODEOWNERS b/CODEOWNERS index cd61814ff8..8759cf2357 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,5 +1,7 @@ /.github/ @bo0tzz /docker/ @bo0tzz /server/ @danieldietzler +/web/ @danieldietzler /machine-learning/ @mertalev /e2e/ @danieldietzler +/mobile/ @shenlong-tanwen diff --git a/Makefile b/Makefile index 815d1a153b..fc99170676 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,14 @@ dev-update: @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --remove-orphans dev-scale: - @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans + @trap 'make dev-down' EXIT; COMPOSE_BAKE=true docker compose -f ./docker/docker-compose.dev.yml up --build -V --scale immich-server=3 --remove-orphans + +dev-docs: + npm --prefix docs run start .PHONY: e2e e2e: - @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans + @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --remove-orphans e2e-update: @trap 'make e2e-down' EXIT; COMPOSE_BAKE=true docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans @@ -40,7 +43,7 @@ open-api-typescript: cd ./open-api && bash ./bin/generate-open-api.sh typescript sql: - npm --prefix server run sync:sql + pnpm --filter immich run sync:sql attach-server: docker exec -it docker_immich-server_1 sh @@ -48,33 +51,57 @@ attach-server: renovate: LOG_LEVEL=debug npx renovate --platform=local --repository-cache=reset +# Directories that need to be created for volumes or build output +VOLUME_DIRS = \ + ./.pnpm-store \ + ./web/.svelte-kit \ + ./web/node_modules \ + ./web/coverage \ + ./e2e/node_modules \ + ./docs/node_modules \ + ./server/node_modules \ + ./open-api/typescript-sdk/node_modules \ + ./.github/node_modules \ + ./node_modules \ + ./cli/node_modules + +# Include .env file if it exists +-include docker/.env + MODULES = e2e server web cli sdk docs .github +# directory to package name mapping function +# cli = @immich/cli +# docs = documentation +# e2e = immich-e2e +# open-api/typescript-sdk = @immich/sdk +# server = immich +# web = immich-web +map-package = $(subst sdk,@immich/sdk,$(subst cli,@immich/cli,$(subst docs,documentation,$(subst e2e,immich-e2e,$(subst server,immich,$(subst web,immich-web,$1)))))) + audit-%: - npm --prefix $(subst sdk,open-api/typescript-sdk,$*) audit fix + pnpm --filter $(call map-package,$*) audit fix install-%: - npm --prefix $(subst sdk,open-api/typescript-sdk,$*) i -ci-%: - npm --prefix $(subst sdk,open-api/typescript-sdk,$*) ci + pnpm --filter $(call map-package,$*) install $(if $(FROZEN),--frozen-lockfile) $(if $(OFFLINE),--offline) build-cli: build-sdk build-web: build-sdk build-%: install-% - npm --prefix $(subst sdk,open-api/typescript-sdk,$*) run build + pnpm --filter $(call map-package,$*) run build format-%: - npm --prefix $* run format:fix + pnpm --filter $(call map-package,$*) run format:fix lint-%: - npm --prefix $* run lint:fix + pnpm --filter $(call map-package,$*) run lint:fix check-%: - npm --prefix $* run check + pnpm --filter $(call map-package,$*) run check check-web: - npm --prefix web run check:typescript - npm --prefix web run check:svelte + pnpm --filter immich-web run check:typescript + pnpm --filter immich-web run check:svelte test-%: - npm --prefix $* run test + pnpm --filter $(call map-package,$*) run test test-e2e: docker compose -f ./e2e/docker-compose.yml build - npm --prefix e2e run test - npm --prefix e2e run test:web + pnpm --filter immich-e2e run test + pnpm --filter immich-e2e run test:web test-medium: docker run \ --rm \ @@ -84,27 +111,39 @@ test-medium: -v ./server/tsconfig.json:/usr/src/app/tsconfig.json \ -e NODE_ENV=development \ immich-server:latest \ - -c "npm ci && npm run test:medium -- --run" + -c "pnpm test:medium -- --run" test-medium-dev: - docker exec -it immich_server /bin/sh -c "npm run test:medium" + docker exec -it immich_server /bin/sh -c "pnpm run test:medium" -build-all: $(foreach M,$(filter-out e2e .github,$(MODULES)),build-$M) ; -install-all: $(foreach M,$(MODULES),install-$M) ; -ci-all: $(foreach M,$(filter-out .github,$(MODULES)),ci-$M) ; -check-all: $(foreach M,$(filter-out sdk cli docs .github,$(MODULES)),check-$M) ; -lint-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),lint-$M) ; -format-all: $(foreach M,$(filter-out sdk,$(MODULES)),format-$M) ; -audit-all: $(foreach M,$(MODULES),audit-$M) ; -hygiene-all: lint-all format-all check-all sql audit-all; -test-all: $(foreach M,$(filter-out sdk docs .github,$(MODULES)),test-$M) ; +install-all: + pnpm -r --filter '!documentation' install + +build-all: $(foreach M,$(filter-out e2e docs .github,$(MODULES)),build-$M) ; + +check-all: + pnpm -r --filter '!documentation' run "/^(check|check\:svelte|check\:typescript)$/" +lint-all: + pnpm -r --filter '!documentation' run lint:fix +format-all: + pnpm -r --filter '!documentation' run format:fix +audit-all: + pnpm -r --filter '!documentation' audit fix +hygiene-all: audit-all + pnpm -r --filter '!documentation' run "/(format:fix|check|check:svelte|check:typescript|sql)/" + +test-all: + pnpm -r --filter '!documentation' run "/^test/" clean: find . -name "node_modules" -type d -prune -exec rm -rf {} + find . -name "dist" -type d -prune -exec rm -rf '{}' + find . -name "build" -type d -prune -exec rm -rf '{}' + - find . -name "svelte-kit" -type d -prune -exec rm -rf '{}' + - command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml rm -v -f || true - command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml rm -v -f || true + find . -name ".svelte-kit" -type d -prune -exec rm -rf '{}' + + find . -name "coverage" -type d -prune -exec rm -rf '{}' + + find . -name ".pnpm-store" -type d -prune -exec rm -rf '{}' + + command -v docker >/dev/null 2>&1 && docker compose -f ./docker/docker-compose.dev.yml down -v --remove-orphans || true + command -v docker >/dev/null 2>&1 && docker compose -f ./e2e/docker-compose.yml down -v --remove-orphans || true + setup-server-dev: install-server setup-web-dev: install-sdk build-sdk install-web diff --git a/README.md b/README.md index 459cda481c..b540408475 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -38,26 +39,25 @@ ā¸ ā¸˛ā¸Šā¸˛āš„ā¸—ā¸ĸ

-## Disclaimer -- âš ī¸ The project is under **very active** development. -- âš ī¸ Expect bugs and breaking changes. -- âš ī¸ **Do not use the app as the only way to store your photos and videos.** -- âš ī¸ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos! +> [!WARNING] +> âš ī¸ Always follow [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) backup plan for your precious photos and videos! +> + > [!NOTE] > You can find the main documentation, including installation guides, at https://immich.app/. ## Links -- [Documentation](https://immich.app/docs) -- [About](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) +- [Documentation](https://docs.immich.app/) +- [About](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) - [Roadmap](https://immich.app/roadmap) - [Demo](#demo) - [Features](#features) -- [Translations](https://immich.app/docs/developer/translations) -- [Contributing](https://immich.app/docs/overview/support-the-project) +- [Translations](https://docs.immich.app/developer/translations) +- [Contributing](https://docs.immich.app/overview/support-the-project) ## Demo @@ -106,7 +106,7 @@ Access the demo [here](https://demo.immich.app). For the mobile app, you can use ## Translations -Read more about translations [here](https://immich.app/docs/developer/translations). +Read more about translations [here](https://docs.immich.app/developer/translations). Translation status diff --git a/cli/.nvmrc b/cli/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/cli/Dockerfile b/cli/Dockerfile index 8fc39670a1..8c74fe12b1 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,19 +1,14 @@ FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS core -WORKDIR /usr/src/open-api/typescript-sdk -COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ -RUN npm ci -COPY open-api/typescript-sdk/ ./ -RUN npm run build - WORKDIR /usr/src/app - -COPY cli/package.json cli/package-lock.json ./ -RUN npm ci - -COPY cli . -RUN npm run build +COPY package* pnpm* .pnpmfile.cjs ./ +COPY ./cli ./cli/ +COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/ +RUN corepack enable pnpm && \ + pnpm install --filter @immich/sdk --filter @immich/cli --frozen-lockfile && \ + pnpm --filter @immich/sdk build && \ + pnpm --filter @immich/cli build WORKDIR /import -ENTRYPOINT ["node", "/usr/src/app/dist"] +ENTRYPOINT ["node", "/usr/src/app/cli/dist"] diff --git a/cli/README.md b/cli/README.md index 8fa2ace483..b9d61fce09 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,30 +1,38 @@ A command-line interface for interfacing with the self-hosted photo manager [Immich](https://immich.app/). -Please see the [Immich CLI documentation](https://immich.app/docs/features/command-line-interface). +Please see the [Immich CLI documentation](https://docs.immich.app/features/command-line-interface). # For developers Before building the CLI, you must build the immich server and the open-api client. To build the server run the following in the server folder: - $ npm install - $ npm run build + $ pnpm install + $ pnpm run build Then, to build the open-api client run the following in the open-api folder: $ ./bin/generate-open-api.sh -To run the Immich CLI from source, run the following in the cli folder: +## Run from build - $ npm install - $ npm run build - $ ts-node . +Go to the cli folder and build it: -You'll need ts-node, the easiest way to install it is to use npm: + $ pnpm install + $ pnpm run build + $ node dist/index.js - $ npm i -g ts-node +## Run and Debug from source (VSCode) + +With VScode you can run and debug the Immich CLI. Go to the launch.json file, find the Immich CLI config and change this with the command you need to debug + +`"args": ["upload", "--help"],` + +replace that for the command of your choice. + +## Install from build You can also build and install the CLI using - $ npm run build - $ npm install -g . + $ pnpm run build + $ pnpm install -g . **** diff --git a/cli/package-lock.json b/cli/package-lock.json deleted file mode 100644 index 7c87c3be2e..0000000000 --- a/cli/package-lock.json +++ /dev/null @@ -1,4617 +0,0 @@ -{ - "name": "@immich/cli", - "version": "2.2.72", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@immich/cli", - "version": "2.2.72", - "license": "GNU Affero General Public License version 3", - "dependencies": { - "chokidar": "^4.0.3", - "fast-glob": "^3.3.2", - "fastq": "^1.17.1", - "lodash-es": "^4.17.21", - "micromatch": "^4.0.8" - }, - "bin": { - "immich": "bin/immich" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.8.0", - "@immich/sdk": "file:../open-api/typescript-sdk", - "@types/byte-size": "^8.1.0", - "@types/cli-progress": "^3.11.0", - "@types/lodash-es": "^4.17.12", - "@types/micromatch": "^4.0.9", - "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", - "@vitest/coverage-v8": "^3.0.0", - "byte-size": "^9.0.0", - "cli-progress": "^3.12.0", - "commander": "^12.0.0", - "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", - "globals": "^16.0.0", - "mock-fs": "^5.2.0", - "prettier": "^3.2.5", - "prettier-plugin-organize-imports": "^4.0.0", - "typescript": "^5.3.3", - "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.0.0", - "vitest": "^3.0.0", - "vitest-fetch-mock": "^0.4.0", - "yaml": "^2.3.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "../open-api/typescript-sdk": { - "name": "@immich/sdk", - "version": "1.135.3", - "dev": true, - "license": "GNU Affero General Public License version 3", - "dependencies": { - "@oazapfts/runtime": "^1.0.2" - }, - "devDependencies": { - "@types/node": "^22.15.33", - "typescript": "^5.3.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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/@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==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "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.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/@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/@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/@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/@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/@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/@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/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "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/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, - "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", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@immich/sdk": { - "resolved": "../open-api/typescript-sdk", - "link": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", - "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "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", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "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==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "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/@types/braces": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.5.tgz", - "integrity": "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/byte-size": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/byte-size/-/byte-size-8.1.2.tgz", - "integrity": "sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", - "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*" - } - }, - "node_modules/@types/cli-progress": { - "version": "3.11.6", - "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", - "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", - "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/micromatch": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.9.tgz", - "integrity": "sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/braces": "*" - } - }, - "node_modules/@types/mock-fs": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", - "integrity": "sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/builtin-modules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/byte-size": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-9.0.1.tgz", - "integrity": "sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.17" - }, - "peerDependencies": { - "@75lb/nature": "latest" - }, - "peerDependenciesMeta": { - "@75lb/nature": { - "optional": true - } - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/cli-progress": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", - "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.3" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-module-lexer": { - "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" - }, - "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==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "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" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", - "esquery": "^1.6.0", - "find-up-simple": "^1.0.1", - "globals": "^16.0.0", - "indent-string": "^5.0.0", - "is-builtin-module": "^5.0.0", - "jsesc": "^3.1.0", - "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.12.0", - "semver": "^7.7.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/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/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "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/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/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/glob/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/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-builtin-module": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", - "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "builtin-modules": "^5.0.0" - }, - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", - "dev": true, - "license": "MIT" - }, - "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/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mock-fs": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz", - "integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "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", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": ">=2.0", - "typescript": ">=2.9", - "vue-tsc": "^2.1.0" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "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", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/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/test-exclude/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/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "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.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", - "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tsconfck": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz", - "integrity": "sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", - "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "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.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest-fetch-mock": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/vitest-fetch-mock/-/vitest-fetch-mock-0.4.5.tgz", - "integrity": "sha512-nhWdCQIGtaSEUVl96pMm0WggyDGPDv5FUy/Q9Hx3cs2RGmh3Q/uRsLClGbdG3kXBkJ3br5yTUjB2MeW25TwdOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "vitest": ">=2.0.0" - } - }, - "node_modules/vitest/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/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/cli/package.json b/cli/package.json index e1f49475c0..3fff0c1031 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.72", + "version": "2.2.96", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", @@ -13,7 +13,6 @@ "cli" ], "devDependencies": { - "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@immich/sdk": "file:../open-api/typescript-sdk", "@types/byte-size": "^8.1.0", @@ -21,15 +20,15 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.18.8", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -44,6 +43,7 @@ }, "scripts": { "build": "vite build", + "build:dev": "vite build --sourcemap true", "lint": "eslint \"src/**/*.ts\" --max-warnings 0", "lint:fix": "npm run lint -- --fix", "prepack": "npm run build", @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" } } diff --git a/deployment/.env b/deployment/.env new file mode 100644 index 0000000000..f6ce050d29 --- /dev/null +++ b/deployment/.env @@ -0,0 +1,4 @@ +export CLOUDFLARE_ACCOUNT_ID="op://tf/cloudflare/account_id" +export CLOUDFLARE_API_TOKEN="op://tf/cloudflare/api_token" +export TF_STATE_POSTGRES_CONN_STR="op://tf/tf_state/postgres_conn_str" +export TF_VAR_env=$ENVIRONMENT diff --git a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl index 417f262157..0869dd28bc 100644 --- a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.5" + constraints = "4.52.5" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=", + "h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=", + "h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=", + "h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=", + "h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=", + "h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=", + "h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=", + "h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=", + "h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=", + "h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=", + "h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=", + "h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=", + "h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=", + "h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=", + "zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b", + "zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a", + "zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7", + "zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238", + "zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b", + "zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072", + "zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661", + "zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54", + "zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852", + "zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0", + "zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5", + "zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036", + "zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803", ] } diff --git a/deployment/modules/cloudflare/docs-release/config.tf b/deployment/modules/cloudflare/docs-release/config.tf index cd370f9353..63347cf67e 100644 --- a/deployment/modules/cloudflare/docs-release/config.tf +++ b/deployment/modules/cloudflare/docs-release/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.5" } } } diff --git a/deployment/modules/cloudflare/docs-release/domain.tf b/deployment/modules/cloudflare/docs-release/domain.tf index 0602045f71..3a6f479a74 100644 --- a/deployment/modules/cloudflare/docs-release/domain.tf +++ b/deployment/modules/cloudflare/docs-release/domain.tf @@ -1,11 +1,11 @@ resource "cloudflare_pages_domain" "immich_app_release_domain" { account_id = var.cloudflare_account_id project_name = data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_name - domain = "immich.app" + domain = "docs.immich.app" } resource "cloudflare_record" "immich_app_release_domain" { - name = "immich.app" + name = "docs.immich.app" proxied = true ttl = 1 type = "CNAME" diff --git a/deployment/modules/cloudflare/docs/.terraform.lock.hcl b/deployment/modules/cloudflare/docs/.terraform.lock.hcl index 417f262157..0869dd28bc 100644 --- a/deployment/modules/cloudflare/docs/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.5" + constraints = "4.52.5" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:+rfzF+16ZcWZWnTyW/p1HHTzYbPKX8Zt2nIFtR/+f+E=", + "h1:18bXaaOSq8MWKuMxo/4y7EB7/i7G90y5QsKHZRmkoDo=", + "h1:4vZVOpKeEQZsF2VrARRZFeL37Ed/gD4rRMtfnvWQres=", + "h1:BZOsTF83QPKXTAaYqxPKzdl1KRjk/L2qbPpFjM0w28A=", + "h1:CDuC+HXLvc1z6wkCRsSDcc/+QENIHEtssYshiWg3opA=", + "h1:DE+YFzLnqSe79pI2R4idRGx5QzLdrA7RXvngTkGfZ30=", + "h1:DfaJwH3Ml4yrRbdAY4AcDVy0QTQk5T3A622TXzS/u2E=", + "h1:EIDXP0W3kgIv2pecrFmqtK/DnlqkyckzBzhxKaXU+4A=", + "h1:EV4kYyaOnwGA0bh/3hU6Ezqnt1PFDxopH7i85e48IzY=", + "h1:M0iXabfzamU+MPDi0G9XACpbacFKMakmM+Z9HZ8HrsM=", + "h1:YWmCbGF/KbsrUzcYVBLscwLizidbp95TDQa0N2qpmVo=", + "h1:cxPcCB5gbrpUO1+IXkQYs1YTY50/0IlApCzGea0cwuQ=", + "h1:g6DldikTV2HXUu9uoeNY5FuLufgaYWF4ufgZg7wq62s=", + "h1:oi/Hrx9pwoQ+Z52CBC+rrowVH387EIj0qvnxQgDeI+0=", + "zh:1a3400cb38863b2585968d1876706bcfc67a148e1318a1d325c6c7704adc999b", + "zh:4c5062cb9e9da1676f06ae92b8370186d98976cc4c7030d3cd76df12af54282a", + "zh:52110f493b5f0587ef77a1cfd1a67001fd4c617b14c6502d732ab47352bdc2f7", + "zh:5aa536f9eaeb43823aaf2aa80e7d39b25ef2b383405ed034aa16a28b446a9238", + "zh:5cc39459a1c6be8a918f17054e4fbba573825ed5597dcada588fe99614d98a5b", + "zh:629ae6a7ba298815131da826474d199312d21cec53a4d5ded4fa56a692e6f072", + "zh:719cc7c75dc1d3eb30c22ff5102a017996d9788b948078c7e1c5b3446aeca661", + "zh:8698635a3ca04383c1e93b21d6963346bdae54d27177a48e4b1435b7f731731c", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8a9993f1dcadf1dd6ca43b23348abe374605d29945a2fafc07fb3457644e6a54", + "zh:b1b9a1e6bcc24d5863a664a411d2dc906373ae7a2399d2d65548ce7377057852", + "zh:b270184cdeec277218e84b94cb136fead753da717f9b9dc378e51907f3f00bb0", + "zh:dff2bc10071210181726ce270f954995fe42c696e61e2e8f874021fed02521e5", + "zh:e8e87b40b6a87dc097b0fdc20d3f725cec0d82abc9cc3755c1f89f8f6e8b0036", + "zh:ee964a6573d399a5dd22ce328fb38ca1207797a02248f14b2e4913ee390e7803", ] } diff --git a/deployment/modules/cloudflare/docs/config.tf b/deployment/modules/cloudflare/docs/config.tf index cd370f9353..63347cf67e 100644 --- a/deployment/modules/cloudflare/docs/config.tf +++ b/deployment/modules/cloudflare/docs/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.5" } } } diff --git a/deployment/modules/cloudflare/docs/domain.tf b/deployment/modules/cloudflare/docs/domain.tf index a28fb4c0f8..c5f77de6b4 100644 --- a/deployment/modules/cloudflare/docs/domain.tf +++ b/deployment/modules/cloudflare/docs/domain.tf @@ -1,11 +1,11 @@ resource "cloudflare_pages_domain" "immich_app_branch_domain" { account_id = var.cloudflare_account_id project_name = local.is_release ? data.terraform_remote_state.cloudflare_account.outputs.immich_app_archive_pages_project_name : data.terraform_remote_state.cloudflare_account.outputs.immich_app_preview_pages_project_name - domain = "${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" + domain = "docs.${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" } resource "cloudflare_record" "immich_app_branch_subdomain" { - name = "${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" + name = "docs.${var.prefix_name}.${local.deploy_domain_prefix}.immich.app" proxied = true ttl = 1 type = "CNAME" diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 32ff115102..eba1f632c1 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -8,8 +8,8 @@ # The compose file on main may not be compatible with the latest release. # For development see: -# - https://immich.app/docs/developer/setup -# - https://immich.app/docs/developer/troubleshooting +# - https://docs.immich.app/developer/setup +# - https://docs.immich.app/developer/troubleshooting name: immich-dev @@ -23,16 +23,24 @@ services: # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding build: context: ../ - dockerfile: server/Dockerfile + dockerfile: server/Dockerfile.dev target: dev restart: unless-stopped volumes: - - ../server:/usr/src/app/server - - ../open-api:/usr/src/app/open-api - - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload - - ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload - - /usr/src/app/server/node_modules + - ..:/usr/src/app + - ${UPLOAD_LOCATION}/photos:/data - /etc/localtime:/etc/localtime:ro + - pnpm-store:/usr/src/app/.pnpm-store + - server-node_modules:/usr/src/app/server/node_modules + - web-node_modules:/usr/src/app/web/node_modules + - github-node_modules:/usr/src/app/.github/node_modules + - cli-node_modules:/usr/src/app/cli/node_modules + - docs-node_modules:/usr/src/app/docs/node_modules + - e2e-node_modules:/usr/src/app/e2e/node_modules + - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules + - app-node_modules:/usr/src/app/node_modules + - sveltekit:/usr/src/app/web/.svelte-kit + - coverage:/usr/src/app/web/coverage env_file: - .env environment: @@ -47,8 +55,8 @@ services: IMMICH_BUILD_IMAGE_URL: https://github.com/immich-app/immich/pkgs/container/immich-server IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/ IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues - IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs - IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides + IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app + IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides ulimits: nofile: soft: 1048576 @@ -58,19 +66,20 @@ services: - 9231:9231 - 2283:2283 depends_on: - - redis - - database + redis: + condition: service_started + database: + condition: service_started healthcheck: disable: false immich-web: container_name: immich_web image: immich-web-dev:latest - # Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919 - # user: 0:0 build: context: ../ - dockerfile: web/Dockerfile + dockerfile: server/Dockerfile.dev + target: dev command: ['immich-web'] env_file: - .env @@ -78,18 +87,26 @@ services: - 3000:3000 - 24678:24678 volumes: - - ../web:/usr/src/app/web - - ../i18n:/usr/src/app/i18n - - ../open-api/:/usr/src/app/open-api/ - # - ../../ui:/usr/ui - - /usr/src/app/web/node_modules + - ..:/usr/src/app + - pnpm-store:/usr/src/app/.pnpm-store + - server-node_modules:/usr/src/app/server/node_modules + - web-node_modules:/usr/src/app/web/node_modules + - github-node_modules:/usr/src/app/.github/node_modules + - cli-node_modules:/usr/src/app/cli/node_modules + - docs-node_modules:/usr/src/app/docs/node_modules + - e2e-node_modules:/usr/src/app/e2e/node_modules + - sdk-node_modules:/usr/src/app/open-api/typescript-sdk/node_modules + - app-node_modules:/usr/src/app/node_modules + - sveltekit:/usr/src/app/web/.svelte-kit + - coverage:/usr/src/app/web/coverage ulimits: nofile: soft: 1048576 hard: 1048576 restart: unless-stopped depends_on: - - immich-server + immich-server: + condition: service_started immich-machine-learning: container_name: immich_machine_learning @@ -117,13 +134,13 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:41eacbe83eca995561fe43814fd4891e16e39632806253848efaf04d3c8a8b84 env_file: - .env environment: @@ -161,3 +178,14 @@ volumes: model-cache: prometheus-data: grafana-data: + pnpm-store: + server-node_modules: + web-node_modules: + github-node_modules: + cli-node_modules: + docs-node_modules: + e2e-node_modules: + sdk-node_modules: + app-node_modules: + sveltekit: + coverage: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index a025ff4cc5..e27012cf56 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -20,7 +20,7 @@ services: context: ../ dockerfile: server/Dockerfile volumes: - - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload + - ${UPLOAD_LOCATION}/photos:/data - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -56,14 +56,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:41eacbe83eca995561fe43814fd4891e16e39632806253848efaf04d3c8a8b84 env_file: - .env environment: @@ -95,7 +95,7 @@ services: command: ['./run.sh', '-disable-reporting'] ports: - 3000:3000 - image: grafana/grafana:12.0.2-ubuntu@sha256:0512d81cdeaaff0e370a9aa66027b465d1f1f04379c3a9c801a905fabbdbc7a5 + image: grafana/grafana:12.1.1-ubuntu@sha256:d1da838234ff2de93e0065ee1bf0e66d38f948dcc5d718c25fa6237e14b4424a volumes: - grafana-data:/var/lib/grafana diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6af0e0aa2a..b4ff05f366 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,5 @@ # -# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose +# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose # # Make sure to use the docker-compose.yml of the current release: # @@ -18,7 +18,7 @@ services: # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -36,7 +36,7 @@ services: # For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag. # Example tag: ${IMMICH_VERSION:-release}-cuda image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} - # extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration + # extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration # file: hwaccel.ml.yml # service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable volumes: @@ -49,14 +49,14 @@ services: redis: container_name: immich_redis - image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11 + image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 restart: always database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:41eacbe83eca995561fe43814fd4891e16e39632806253848efaf04d3c8a8b84 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} diff --git a/docker/example.env b/docker/example.env index 0450dc0805..6d6fd1e3fe 100644 --- a/docker/example.env +++ b/docker/example.env @@ -1,4 +1,4 @@ -# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables +# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables # The location where your uploaded files are stored UPLOAD_LOCATION=./library diff --git a/docker/hwaccel.ml.yml b/docker/hwaccel.ml.yml index 111202d022..c95ac7ee4c 100644 --- a/docker/hwaccel.ml.yml +++ b/docker/hwaccel.ml.yml @@ -4,7 +4,7 @@ # you can inline the config for a backend by copying its contents # into the immich-machine-learning service in the docker-compose.yml file. -# See https://immich.app/docs/features/ml-hardware-acceleration for info on usage. +# See https://docs.immich.app/features/ml-hardware-acceleration for info on usage. services: armnn: diff --git a/docker/hwaccel.transcoding.yml b/docker/hwaccel.transcoding.yml index 60ee7e8fa3..0857faf465 100644 --- a/docker/hwaccel.transcoding.yml +++ b/docker/hwaccel.transcoding.yml @@ -4,7 +4,7 @@ # you can inline the config for a backend by copying its contents # into the immich-microservices service in the docker-compose.yml file. -# See https://immich.app/docs/features/hardware-transcoding for more info on using hardware transcoding. +# See https://docs.immich.app/features/hardware-transcoding for more info on using hardware transcoding. services: cpu: {} diff --git a/docs/.gitignore b/docs/.gitignore index 502ac97045..fbb000246e 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -18,4 +18,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -yarn.lock \ No newline at end of file +yarn.lock + +/static/openapi.json diff --git a/docs/.nvmrc b/docs/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/docs/README.md b/docs/README.md index cdf0733949..547cc5e6c4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,13 +5,13 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati ### Installation ``` -$ npm install +$ pnpm install ``` ### Local Development ``` -$ npm run start +$ pnpm run start ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. @@ -19,7 +19,7 @@ This command starts a local development server and opens up a browser window. Mo ### Build ``` -$ npm run build +$ pnpm run build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. @@ -29,13 +29,13 @@ This command generates static content into the `build` directory and can be serv Using SSH: ``` -$ USE_SSH=true npm run deploy +$ USE_SSH=true pnpm run deploy ``` Not using SSH: ``` -$ GIT_USER= npm run deploy +$ GIT_USER= pnpm run deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/docs/blog/2022/11-10/release-1.36.mdx b/docs/blog/2022/11-10/release-1.36.mdx deleted file mode 100644 index 5f5643196c..0000000000 --- a/docs/blog/2022/11-10/release-1.36.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -slug: release-1-36 -title: Release v1.36.0 -authors: [alextran] -tags: [release] -date: 2022-11-10 ---- - -Hello everyone, it is my pleasure to deliver the new release of Immich to you. The team has been working hard to bring you the new features and improvements. This release includes some big features that the community has been asking since the beginning of Immich. We hope you will enjoy it. - -Some notable features are: - -- OAuth integration -- LivePhoto support on iOS -- User config system - - - -## LivePhoto iOS Support 🎉 - -LivePhoto on iOS is now supported in Immich. - -The motion part will now be uploaded and can be played on the mobile app and the web. - -:::caution - -- The server and the app has to be on version **1.36.x** for the application to work correctly. -- Previous uploaded photos will not be updated automatically, you will have to remove and reupload them if you want to keep the LivePhoto functionality. - -::: - - - -## OAuth Integration 🎉 - -I want to borrow this chance to express my gratitude to [@EnricoBilla](https://github.com/EnricoBilla), who has been the trailblazer for this feature since the beginning days of Immich. His PR has sparked ideas, suggestions, and discussion among the team member on how to integrate this feature successfully into the app. Thank you so much for your work and your time. - -OAuth is now integrated into the system. Please follow the guide [here](https://immich.app/docs/usage/oauth) to set up your OAuth integration - -After setting up the correct environment variables in the `.env` file, as shown below - -| Key | Type | Default | Description | -| ------------------- | ------- | -------------------- | ------------------------------------------------------------------------- | -| OAUTH_ENABLED | boolean | false | Enable/disable OAuth2 | -| OAUTH_ISSUER_URL | URL | (required) | Required. Self-discovery URL for client | -| OAUTH_CLIENT_ID | string | (required) | Required. Client ID | -| OAUTH_CLIENT_SECRET | string | (required) | Required. Client Secret | -| OAUTH_SCOPE | string | openid email profile | Full list of scopes to send with the request (space delimited) | -| OAUTH_AUTO_REGISTER | boolean | true | When true, will automatically register a user the first time they sign in | -| OAUTH_BUTTON_TEXT | string | Login with OAuth | Text for the OAuth button on the web | - -```bash title="Authentik Example" -OAUTH_ENABLED=true -OAUTH_ISSUER_URL=http://10.1.15.216:9000/application/o/immich-test/ -OAUTH_CLIENT_ID=30596v8f78a4b6a97d5985c3076b6b4c4d12ddc33 -OAUTH_CLIENT_SECRET=50f1eafdec353b95b1c638db390db4ab67ef035a51212dbec2f56175e2eb272b5d572c099176e6fe116ecf47ffdd544bgdb9e2edc588307ee0339d25eeccd88 -OAUTH_BUTTON_TEXT=Login with Authentik -``` - -The web will have the option to sign in with OAuth. - - - -The mobile app will check if the server has OAuth enabled before displaying the OAuth -sign-in button. - - - -## Support - - - -If you find the project helpful and it helps you in some ways, you can support the project [one time](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) or [monthly](https://github.com/sponsors/alextran1502) from GitHub Sponsor - -It is a great way to let me know that you want me to continue developing and working on this project for years to come. - -## Details - -For more details, please check out the [release note](https://github.com/immich-app/immich/releases/tag/v1.36.0_55-dev) diff --git a/docs/blog/2023/06-24/update.mdx b/docs/blog/2023/06-24/update.mdx deleted file mode 100644 index 464d3e44d9..0000000000 --- a/docs/blog/2023/06-24/update.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Immich Update - June 2023 -authors: [alextran] -tags: [update] ---- - -Hello everybody, Alex here! - -I am back with another update on Immich. It has been only a month since my last update (May 18th, 2023), but it seems forever. I think the rapid releases of Immich and the amount of work make the perspective of time change in Immich’s world. We have some exciting updates that I think you will like. - -Before going into detail, on behalf of the core team, I would like to thank all of you for loving Immich and contributing to the project. Thank you for helping me make Immich an enjoyable alternative solution to Google Photos so that you have complete control of your data and privacy. I know we are still young and have a lot of work to do, but I am confident we will get there with help from the community. I appreciate all of you from the bottom of my heart! - - - -And now, to the exciting part, what is new in Immich’s world? - -- Initial support for existing gallery. -- Memory feature. -- Support XMP sidecar. -- Support more raw formats. -- Justified layout for web timeline and blurred thumbnail hash. -- Mechanism to host machine learning on a completely different machine. - -## Support for existing gallery - -I know this is the most controversial feature when it comes to Immich’s way of ingesting photos and videos. For many users, having to upload photos and videos to Immich is simply not working. We listen, discuss, and digest this feature internally more than you imagine because it is not a simple feature to tackle while keeping the performance and the user experience at the top level, which is Immich’s primary goal. - -Thankfully, we have many great contributors and developers that want to make this come true. So we came up with an initial implementation of this feature in the form of a supporting read-only gallery. - -To be concise, Immich can now read in the gallery files, register the path into the database, and then generate necessary files and put them through Immich’s machine learning pipeline so you can use all the goodness of Immich without the need to upload them. Since this is the initial implementation, some actions/behavior are not yet supported, and we aim to build toward them in future releases, namely: - -- Assets are not automatically synced and must instead be manually synced with the CLI tool. -- Only new files that are added to the gallery will be detected. -- Deleted and moved files will not be detected. - -## Memory feature - -This is considered a fun feature that the team and I wanted to build for so long, but we had to put it off because of the refactoring of the code base. The code base is now in a good enough form to circle back and add more exciting features. - -This memory feature is very much similar to GPhotos' implementation of “x years sinceâ€Ļ”. We are aiming to add more categories of memories in the future, such as “Spotlight of the day” or “Day of the Week highlights” - - - -This feature is now available on the web and will be ported to the mobile app in the near future. - -## Support XMP Sidecar - -Immich can now import/upload XMP sidecars from the CLI and use the information as the metadata of assets. - -## Support more raw formats. - -With the recent updates on the dependencies of Immich, we are now extending and hardening support for multiple raw formats. So users with DSLR or mirrorless cameras can now upload their original files to Immich and have them displayed in high-quality thumbnails on the web and mobile view. - -## Justified layout for web timeline and blurred thumbnail hash - -This is an aesthetic improvement in user experience when browsing the timeline. Photos and videos are now displayed correctly with perspective orientation, making the browsing experience more pleasurable. - -To further improve the browsing experience, we now added a blur hash to the thumbnail, so the transition is more natural with a dreamy fade in effect, similar to how our brain goes from faded to vivid memory - - - -## Hosting machine learning container on a different machine - -With more capabilities Immich is building toward, machine learning will get more powerful and therefore require more resources to run effectively. However, we understand that users might not have the best server resources where they host the Immich instance. Therefore, we changed how machine learning interacts and receives the photos and videos to run through its inference pipeline. - -The machine learning container is now a headless system that can run on any machine. As long as your Immich instance can communicate with the system running the machine learning container, it can send the files and receive the required information to make Immich powerful in terms of searching and intelligence. This helps you to utilize a more powerful machine in your home/infrastructure to perform the CPU-intensive tasks while letting Immich only handle the I/O operations for a pleasant and smooth experience. - ---- - -So, those are the highlights for the team and the community after a busy month. There are a lot more changes and improvements. I encourage you to read some release notes, starting from version [v1.57.0](https://github.com/immich-app/immich/releases/tag/v1.57.0) to now. - -Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life works for the community and my family. You can find the support channels below: - -- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502) -- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) -- [Liberapay](https://liberapay.com/alex.tran1502/) -- [buymeacoffee](https://www.buymeacoffee.com/altran1502) -- Bitcoin: 3QVAb9dCHutquVejeNXitPqZX26Yg5kxb7 -- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky. - -Join our friendly [Discord](https://discord.immich.app) to talk and discuss Immich, tech, or anything - -Cheer! - -Until next time! - -Alex diff --git a/docs/blog/2023/07-29/images/web-shortcuts-panel.png b/docs/blog/2023/07-29/images/web-shortcuts-panel.png deleted file mode 100644 index 5a16c9f289..0000000000 Binary files a/docs/blog/2023/07-29/images/web-shortcuts-panel.png and /dev/null differ diff --git a/docs/blog/2023/07-29/update.mdx b/docs/blog/2023/07-29/update.mdx deleted file mode 100644 index 6d50ddfdc0..0000000000 --- a/docs/blog/2023/07-29/update.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Immich Update - July 2023 -authors: [alextran] -tags: [update, v1.64.0-v1.71.0] ---- - -Hello, Immich fans, another month, another milestone. We hope you are staying cool and safe in this scorching hot summer across the globe. - -Immich recently got some good recognition when getting to the front page of HackerNews, which helped to let more people know about the project's existence. The project will help more and more people find a solution to control the privacy of their most precious moments. And with the gain in popularity and recognition, we have gotten new users and more questions from the community than ever. - -I want to express my gratitude to all the contributors and the community who have been tremendously helpful to new users' questions and provided technical support. - -Below are the highlights of new features we added to the application over the past month, along with countless bug fixes and improvements across the board, from developer experience to resource optimization and UI/UX improvement. I hope you find these topics as exciting as I am. - -## Highlights - -- Memories feature. -- Facial recognition improvements. -- Improvements on multi selection behavior on the web. -- Shortcuts for common actions on the web. -- Support viewer for 360-panorama photos. - - - ---- - -### Memories feature - -We've added the memory feature on the mobile app, so you can reminisce about your past memories. - - - -### Facial recognition improvements - -Over the past few releases, we have added many UI improvements to the facial recognition feature to help you manage the recognized people better. Some of the highlights: - -#### Choose a new feature photo for a person. - - - -#### Hide and show faces. - -You can now select irrelevant faces to hide them. The hidden faces won’t be displayed in search results and the people section in the info panel. - -#### Merge faces. - -This is useful when you have multiple faces of the same person in your photos, and you want to merge them into one. - - - -We also added a nifty mechanism that when naming a face, similar names will prompt you a merge face option for the convenience. - - - -### Improvements on multi selection behavior on the web - -We have added a new multi selection behavior on the web to help you select multiple items easier. You can now select a range of photos and videos by holding the `Shift` key. - - - -### Shortcuts for common actions on the web. - -Some of us only navigate the world and the web with a keyboard (looking at you, Vim and Emacs users). So it would take away the sacred weapon of choice to require many clicks to perform repetitive actions. So we added quick shortcuts for the following action on the web. - -Dot Env Example - -### Support viewer for 360-panorama photos. - -Photos with the EXIF property of `ProjectionType` will now have a special viewer on the web to view all the angles of the panorama. - -The thumbnail of the 360 degrees panoramas will have a special icon on the top right of the thumbnail - -Dot Env Example - -Panorama in the detail view - -Dot Env Example - ---- - -Thank you, and I am asking for your support for the project. I hope to be a full-time maintainer of Immich one day to dedicate myself to the project as my life's work for the community and my family. You can find the support channels below: - -- Monthly donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502) -- One-time donation via [GitHub Sponsors](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) -- [Liberapay](https://liberapay.com/alex.tran1502/) -- [buymeacoffee](https://www.buymeacoffee.com/altran1502) -- Bitcoin: 3QVAb9dCHutquVejeNXitPqZX26Yg5kxb7 -- Give a project a star - the contributors love gazing at the stars and seeing their creations shining in the sky. - -Join our friendly [Discord](https://discord.immich.app) to talk and discuss Immich, tech, or anything - -Cheer! - -Until next time! - -Alex diff --git a/docs/blog/2023/2023-recap.mdx b/docs/blog/2023/2023-recap.mdx deleted file mode 100644 index e9d93a52be..0000000000 --- a/docs/blog/2023/2023-recap.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Immich Recap 2023 -authors: [alextran] -tags: [update, recap-2023] -date: 2023-12-30T00:00 ---- - -Hi everyone, - -Alex from Immich here. - -We are entering the last few weeks of 2023, and it has been quite a year for Immich. The project has grown so much in terms of users, developers, features, maturity, and the community around it. When I started working on Immich, it was simply a challenge for myself and an opportunity to learn new technologies, crafting something fun and useful for my wife during my free time to satisfy my urge to build and create things. I never thought it would become so popular and help so many people. At the end of the day, all we have is memory. I am proud that the team and I have created something to make storing and viewing those precious memories easier without restrictions and without sacrificing our privacy. As the year closes, here’s a recap of everything the project accomplished in 2023. - -# Milestones - -- Public shared links -- Favorites page -- Immich turned 1 -- Material Design 3 on the mobile app -- Auto-link LivePhotos server-side -- iOS background backup -- Explore page -- CLIP search -- Search by metadata -- Responsive web app -- Archive page -- Asset descriptions -- 10,000 stars on GitHub -- Manage auth devices -- Map view -- Facial recognition, clustering, searching, renaming, and person management -- Partner sharing and unifying timeline between partners' users -- Custom storage label -- XMP sidecar reading -- RAW file formats -- Justified layout on the web -- Memories -- Multi-select via SHIFT -- Android Motion Photos -- 360° Photos -- Album description -- Album performance improvements (time buckets) -- Video hardware transcoding -- Slideshow mode on the web -- Configuration file -- External libraries -- Trash page -- Custom theme -- Asset Stacking -- 20,000 stars on GitHub -- Shared album activity and comments -- CLI v2 -- Down to 5 containers (from 8) - -# Fun Statistics - -- We have gone from the release version `1.41.0` to `1.90.0` at the time of writing. On average, we see a release every 7 days. -- According to GitHub's metrics, the `immich-server` container image has been pulled almost _4 million_ times. -- According to mobile app store metrics, we have 22,000 installations on Android and 6700 installation units on iOS (opt-in only). -- Immich is making around $1200/month on average from donations. (Thank you all so much!) -- We were guests on two podcasts: - - [Self-hosted](https://selfhosted.show/110) - - [The Vergecast](https://www.theverge.com/23938533/self-hosting-local-first-software-vergecast) -- There are over 4,500 members on the Discord server. -- We have over 22,000 stars on the main GitHub repository, gaining 15,000 stars since January 2023. - -Diving into the next year, the team will continue to build on the foundation we have laid out over the past year, implementing more advanced features for searching, organizing, and sharing between users. Bugs will continue to be squashed and conquered. “Shit Alex wrote'' code will continue to be replaced by beautiful, clean code from Jason, Zack, Boet, Daniel, Osorin, Mert, Fynn, Marty, Martin, and Jonathan. The team has my eternal gratitude for creating a welcoming environment for new contributors, helping, teaching, and learning from each other. I’ve realized that hardly a day has gone by where the team hasn’t been in communication about Immich related topics over the past year. - -My long-term goal is to help hone Immich into a diamond in the FOSS space, where the UI, UX, development experiences, documentation, and quality are at a high standard while remaining free for everybody to use. - -I hope you enjoy Immich and have a happy and peaceful holiday. diff --git a/docs/blog/2024/immich-core-team-goes-fulltime.mdx b/docs/blog/2024/immich-core-team-goes-fulltime.mdx deleted file mode 100644 index 0cba2b467c..0000000000 --- a/docs/blog/2024/immich-core-team-goes-fulltime.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: The Immich core team goes full-time -authors: [alextran] -tags: [update, announcement, FUTO] -date: 2024-05-01T00:00 ---- - -**Immich is joining [FUTO](https://futo.org/)!** - -Since the beginning of this adventure, my goal has always been to create a better world for my children. Memories are priceless, and privacy should not be a luxury. However, building quality open source has its challenges. Over the past two years, it has taken significant dedication, time, and effort. - -Recently, a company in Austin, Texas, called FUTO contacted the team. FUTO strives to develop quality and sustainable open software. They build software alternatives that focus on giving control to users. From their mission statement: - -“Computers should belong to you, the people. We develop and fund technology to give them back.” - -FUTO loved Immich and wanted to see if we’d consider working with them to take the project to the next level. In short, FUTO offered to: - -- Pay the core team to work on Immich full-time -- Let us keep full autonomy about the project’s direction and leadership -- Continue to license Immich under AGPL -- Keep Immich’s development direction with no paywalled features -- Keep Immich “built for the people” (no ads, data mining/selling, or alternative motives) -- Provide us with financial, technical, legal, and administrative support - -After careful deliberation, the team decided that FUTO’s vision closely aligns with our own: to build a better future by providing a polished, performant, and privacy-preserving open-source software solution for photo and video management delivered in a sustainable way. - -Immich’s future has never looked brighter, and we look forward to realizing our vision for Immich as part of FUTO. - -If you have more questions, we’ll host a Q&A live stream on May 9th at 3PM UTC (10AM CST). [You can ask questions here](https://www.live-ask.com/event/01HWP2SB99A1K8EXFBDKZ5Z9CF), and the stream will be live [here on our YouTube channel](https://youtube.com/live/cwz2iZwYpgg). - -Cheers, - -The Immich Team - ---- - -## FAQs - -### What is FUTO? - -[https://futo.org/what-is-futo/](https://futo.org/what-is-futo/) - -### Will the license change? - -No. Immich will continue to be licensed under AGPL without a CLA. - -### Will Immich continue to be free? - -Yes. The Immich source code will remain freely available under the AGPL license. - -### Is Immich getting VC funding? - -No. Venture capital implies investment in a business, often with the expectation of a future payout (exit plan). Immich is neither a business that can be acquired nor comes with a money-making exit plan. - -### I am currently supporting Immich through GitHub sponsors. What will happen to my donation? - -Effective immediately, all donations to the Immich organization will be canceled. In the future, we will offer an optional, modest payment option instead. Thank you to everyone who donated to help us get this far! - -### How is funding sustainable? - -Immich and FUTO believe a sustainable future requires a model that does not rely on users-as-a-product. To this end, FUTO advocates that users pay for good, open software. In keeping with this model, we will adopt a purchase price. This means we no longer accept donations, but — _without limiting features for those who do not pay_ — we will soon allow you to purchase Immich through a modest payment. We encourage you to pay for the high-quality software you use to foster a healthy software culture where developers build great applications without hidden motives for their users. - -### When does this change take effect? - -This change takes effect immediately. - -### What will change? - -The following things will change as Immich joins FUTO: - -- The brand, logo, and other Immich trademarks will be transferred to FUTO. -- We will stop all donations to the project. -- The core team can now dedicate our full attention to Immich -- Before the end of the year, we plan to have a roadmap for what it will take to get Immich to a stable release. -- Bugs will be squashed, and features will be delivered faster. diff --git a/docs/blog/2024/immich-licensing.mdx b/docs/blog/2024/immich-licensing.mdx deleted file mode 100644 index 773abcb666..0000000000 --- a/docs/blog/2024/immich-licensing.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Licensing announcement - Purchase a license to support Immich -authors: [alextran] -tags: [update, announcement, FUTO] -date: 2024-07-18T00:00 ---- - -Hello everybody, - -Firstly, on behalf of the Immich team, I'd like to thank everybody for your continuous support of Immich since the very first day! Your contributions, encouragement, and community engagement have helped bring Immich to its current state. The team and I are forever grateful for that. - -Since our [last announcement of the core team joining FUTO to work on Immich full-time](https://immich.app/blog/2024/immich-core-team-goes-fulltime), one of the goals of our new position is to foster a healthy relationship between the developers and the users. We believe that this enables us to create great software, establish transparent policies and build trust. - -We want to build a great software application that brings value to you and your loved ones' lives. We are not using you as a product, i.e., selling or tracking your data. We are not putting annoying ads into our software. We respect your privacy. We want to be compensated for the hard work we put in to build Immich for you. - -With those notes, we have enabled a way for you to financially support the continued development of Immich, ensuring the software can move forward and will be maintained, by offering a lifetime license of the software. We think if you like and use software, you should pay for it, but _we're never going to force anyone to pay or try to limit Immich for those who don't._ - -There are two types of license that you can choose to purchase: **Server License** and **Individual License**. - -### Server License - -This is a lifetime license costing **$99.99**. The license is applied to the whole server. You and all users that use your server are licensed. - -### Individual License - -This is a lifetime license costing **$24.99**. The license is applied to a single user, and can be used on any server they choose to connect to. - -license-social-gh - -You can purchase the license on [our page - https://buy.immich.app](https://buy.immich.app). - -Starting with release `v1.109.0` you can purchase and enter your purchased license key directly in the app. - -license-page-gh - -## Thank you - -Thank you again for your support, this will help create a strong foundation and stability for the Immich team to continue developing and maintaining the project that you love to use. - -

- -

- -
-
- -Cheers! 🎉 - -Immich team - -# FAQ - -### 1. Where can I purchase a license? - -There are several places where you can purchase the license from - -- [https://buy.immich.app](https://buy.immich.app) -- [https://pay.futo.org](https://pay.futo.org/) -- or directly from the app. - -### 2. Do I need both _Individual License_ and _Server License_? - -No, - -If you are the admin and the sole user, or your instance has less than a total of 4 users, you can buy the **Individual License** for each user. - -If your instance has more than 4 users, it is more cost-effective to buy the **Server License**, which will license all the users on your instance. - -### 3. What do I do if I don't pay? - -You can continue using Immich without any restriction. - -### 4. Will there be any paywalled features? - -No, there will never be any paywalled features. - -### 5. Where can I get support regarding payment issues? - -You can email us with your `orderId` and your email address `billing@futo.org` or on our Discord server. diff --git a/docs/blog/2024/update-july-2024.mdx b/docs/blog/2024/update-july-2024.mdx deleted file mode 100644 index cbe99177e7..0000000000 --- a/docs/blog/2024/update-july-2024.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Immich Update - July 2024 -authors: [alextran] -date: 2024-07-01T00:00 -tags: [update, v1.106.0] ---- - -Hello everybody! Alex from Immich here and I am back with another development progress update for the project. - -Summer has returned once again, and the night sky is filled with stars, thank you for **38_000 shining stars** you have sent to our [GitHub repo](https://github.com/immich-app/immich)! Since the last announcement several core contributors have started full time. Everything is going great with development, PRs get merged with _brrrrrrr_ rate, conversation exchange between team members is on a new high, we met and are working with the great engineers at FUTO. The spirit is high and we have a lot of things brewing that we think you will like. - -Let's go over some of the updates we had since the last post. - -### Container consolidation - -Reduced the number of total containers from 5 to 4 by making the microservices thread get spawned directly in the server container. Woohoo, remember when Immich had 7 containers? - -### Email notifications - -![smtp](https://github.com/immich-app/immich/assets/27055614/949cba85-d3f1-4cd3-b246-a6f5fb5d3ae8) - -We added email notifications to the app with SMTP settings that you can configure for the following events - -- A new account is created for you. -- You are added to a shared album. -- New media is added to an album. - -### Versioned docs - -You can now jump back into the past or take a peek at the unreleased version of the documentation by selecting the version on the website. - -![version-doc](https://github.com/immich-app/immich/assets/27055614/6d22898a-5093-41ad-b416-4573d7ce6e03) - -### Similarity deduplication - -With more machine learning and CLIP magic, we now have similarity deduplication built into the application where it will search for closely similar images and let you decide what to do with them; i.e keep or trash. - -![similarity-deduplication](https://github.com/immich-app/immich/assets/27055614/3cac8478-fbf7-47ea-acb6-0146901dc67e) - -### Permanent URL for asset on the web - -The detail view for an asset now has a permanent URL so you can easily share them with your loved ones. - -### Web app translations - -We now have a public Weblate project which the community can use to translate the webapp to their native languages. We are planning to port the mobile app translation to this platform as well. If you would like to contribute, you can take a look [here](https://hosted.weblate.org/projects/immich/immich/). We're already close to 50% translations -- we really appreciate everyone contributing to that! - -![web-translation](https://github.com/immich-app/immich/assets/27055614/363df2ed-656c-4584-bd82-0708a693c5bc) - -### Read-only/Editor mode on shared album - -As the owner of the album, you can choose if the shared user can edit the album or to only view the content of the album without any modification. - -![read-only-album](https://github.com/immich-app/immich/assets/27055614/c6f66375-b869-495a-9a86-3e87b316d109) - -### Better video thumbnails - -Immich now tries to find a descriptive video thumbnail instead of simply using the first frame. No more black images for thumbnails! - -### Public Roadmap - -We now have a [public roadmap](https://immich.app/roadmap), giving you a high-level overview of things the team is working on. The first goal of this roadmap is to bring Immich to a stable release, which is expected sometime later this year. Some of the highlights include - -- Auto stacking - Auto stacking of burst photos -- Basic editor - Basic photo editing capabilities -- Workflows - Automate tasks with workflows -- Fine grained access controls - Granular access controls for users and api keys -- Better background backups - Rework background backups to be more reliable -- Private/locked photos - Private assets with extra protections - -Beyond the items in the roadmap, we have _many many_ more ideas for Immich. The team and I hope that you are enjoying the application, find it helpful in your life and we have nothing but the intention of building out great software for you all! - -Have an amazing Summer or Winter for those in the southern hemisphere! :D - -Until next time, - -Cheers! -Alex diff --git a/docs/blog/authors.yml b/docs/blog/authors.yml deleted file mode 100644 index f331efa927..0000000000 --- a/docs/blog/authors.yml +++ /dev/null @@ -1,5 +0,0 @@ -alextran: - name: Alex Tran - title: Maintainer of Immich - url: https://github.com/alextran1502 - image_url: https://github.com/alextran1502.png diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 9e14d10a0e..14ac1de298 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -1,14 +1,40 @@ # FAQ +## Commercial Guidelines + +### Are you open to commercial partnerships and collaborations? + +We are working to commercialize Immich and we'd love for you to help us by making Immich better. FUTO is dedicated to developing sustainable models for developing open source software for our customers. We want our customers to be delighted by the products our engineers deliver, and we want our engineers to be paid when they succeed. + +If you wish to use Immich in a commercial product not owned by FUTO, we have the following requirements: + +- Plugin Integrations: Integrations for other platforms are typically approved, provided proper notification is given. + +- Reseller Partnerships: Must adhere to the guidelines outlined below regarding trademark usage, and proper representation. + +- Strategic Collaborations: We welcome discussions about mutually beneficial partnerships that enhance the value proposition for both organizations. + +### What are your guidelines for resellers and trademark usage? + +For organizations seeking to resell Immich, we have established the following guidelines to protect our brand integrity and ensure proper representation. + +- We request that resellers do not display our trademarks on their websites or marketing materials. If such usage is discovered, we will contact you to request removal. + +- Do not misrepresent your reseller site or services as being officially affiliated with or endorsed by Immich or our development team. + +- For small resellers who wish to contribute financially to Immich's development, we recommend directing your customers to purchase licenses directy from us rather than attempting to broker revenue-sharing arrangements. We ask that you refrain from misrepresenting reseller activities as directly supporting our development work. + +When in doubt or if you have an edge case scenario, we encourage you to contact us directly via email to discuss the use of our trademark. We can provide clear guidance on what is acceptable and what is not. You can reach out at: questions@immich.app + ## User ### How can I reset the admin password? -The admin password can be reset by running the [reset-admin-password](/docs/administration/server-commands.md) command on the immich-server. +The admin password can be reset by running the [reset-admin-password](/administration/server-commands.md) command on the immich-server. ### How can I see a list of all users in Immich? -You can see the list of all users by running [list-users](/docs/administration/server-commands.md) Command on the Immich-server. +You can see the list of all users by running [list-users](/administration/server-commands.md) Command on the Immich-server. --- @@ -80,20 +106,20 @@ However, Immich will delete original files that have been trashed when the trash When Storage Template is off (default) Immich saves the file names in a random string (also known as random UUIDs) to prevent duplicate file names. To retrieve the original file names, you must enable the Storage Template and then run the STORAGE TEMPLATE MIGRATION job. -It is recommended to read about [Storage Template](https://immich.app/docs/administration/storage-template) before activation. +It is recommended to read about [Storage Template](/administration/storage-template) before activation. ### Can I add my existing photo library? -Yes, with an [External Library](/docs/features/libraries.md). +Yes, with an [External Library](/features/libraries.md). -### What happens to existing files after I choose a new [Storage Template](/docs/administration/storage-template.mdx)? +### What happens to existing files after I choose a new [Storage Template](/administration/storage-template.mdx)? -Template changes will only apply to _new_ assets. To retroactively apply the template to previously uploaded assets, run the Storage Migration Job, available on the [Jobs](/docs/administration/jobs-workers/#jobs) page. +Template changes will only apply to _new_ assets. To retroactively apply the template to previously uploaded assets, run the Storage Migration Job, available on the [Jobs](/administration/jobs-workers/#jobs) page. ### Why are only photos and not videos being uploaded to Immich? This often happens when using a reverse proxy in front of Immich. -Make sure to [set your reverse proxy](/docs/administration/reverse-proxy/) to allow large requests. +Make sure to [set your reverse proxy](/administration/reverse-proxy/) to allow large requests. Also, check the disk space of your reverse proxy. In some cases, proxies cache requests to disk before passing them on, and if disk space runs out, the request fails. @@ -113,7 +139,7 @@ You can _archive_ them. ### How can I backup data from Immich? -See [Backup and Restore](/docs/administration/backup-and-restore.md). +See [Backup and Restore](/administration/backup-and-restore.md). ### Does Immich support reading existing face tag metadata? @@ -180,7 +206,7 @@ services: ... volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro + - originals:/usr/src/app/originals ... @@ -199,7 +225,7 @@ volumes: ### Can I keep my existing album structure while importing assets into Immich? -Yes, by using the [Immich CLI](/docs/features/command-line-interface) along with the `--album` flag. +Yes, by using the [Immich CLI](/features/command-line-interface) along with the `--album` flag. ### Is there a way to reorder photos within an album? @@ -240,7 +266,7 @@ Immich uses CLIP models. An ML model converts each image to an "embedding", whic ### How does facial recognition work? -See [How Facial Recognition Works](/docs/features/facial-recognition#how-facial-recognition-works) for details. +See [How Facial Recognition Works](/features/facial-recognition#how-facial-recognition-works) for details. ### How can I disable machine learning? @@ -262,7 +288,7 @@ No, this is not supported. Only models listed in the [Hugging Face][huggingface] ### I want to be able to search in other languages besides English. How can I do that? -You can change to a multilingual CLIP model. See [here](/docs/features/searching#clip-models) for instructions. +You can change to a multilingual CLIP model. See [here](/features/searching#clip-models) for instructions. ### Does Immich support Facial Recognition for videos? @@ -273,7 +299,7 @@ Scanning the entire video for faces may be implemented in the future. No. :::tip -You can use [Smart Search](/docs/features/searching.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results. +You can use [Smart Search](/features/searching.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results. ::: ### I'm getting a lot of "faces" that aren't faces, what can I do? @@ -303,7 +329,7 @@ ls clip/ facial-recognition/ ### Why is Immich slow on low-memory systems like the Raspberry Pi? -Immich optionally uses transcoding and machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/docs/FAQ#can-i-lower-cpu-and-ram-usage) this or host Immich's machine-learning container on a [more powerful system](/docs/guides/remote-machine-learning), or [disable](/docs/FAQ#how-can-i-disable-machine-learning) machine learning entirely. +Immich optionally uses transcoding and machine learning for several features. However, it can be too heavy to run on a Raspberry Pi. You can [mitigate](/FAQ#can-i-lower-cpu-and-ram-usage) this or host Immich's machine-learning container on a [more powerful system](/guides/remote-machine-learning), or [disable](/FAQ#how-can-i-disable-machine-learning) machine learning entirely. ### Can I lower CPU and RAM usage? @@ -313,9 +339,9 @@ The initial backup is the most intensive due to the number of jobs running. The - Under Settings > Transcoding Settings > Threads, set the number of threads to a low number like 1 or 2. - Under Settings > Machine Learning Settings > Facial Recognition > Model Name, you can change the facial recognition model to `buffalo_s` instead of `buffalo_l`. The former is a smaller and faster model, albeit not as good. - For facial recognition on new images to work properly, You must re-run the Face Detection job for all images after this. -- At the container level, you can [set resource constraints](/docs/FAQ#can-i-limit-cpu-and-ram-usage) to lower usage further. +- At the container level, you can [set resource constraints](/FAQ#can-i-limit-cpu-and-ram-usage) to lower usage further. - It's recommended to only apply these constraints _after_ taking some of the measures here for best performance. -- If these changes are not enough, see [above](/docs/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning. +- If these changes are not enough, see [above](/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning. ### Can I limit CPU and RAM usage? @@ -357,7 +383,7 @@ Do not exaggerate with the job concurrency because you're probably thoroughly ov ### My server shows Server Status Offline | Version Unknown. What can I do? -You need to [enable WebSockets](/docs/administration/reverse-proxy/) on your reverse proxy. +You need to [enable WebSockets](/administration/reverse-proxy/) on your reverse proxy. --- @@ -365,7 +391,7 @@ You need to [enable WebSockets](/docs/administration/reverse-proxy/) on your rev ### How can I see Immich logs? -Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/docs/guides/docker-help.md). +Immich components are typically deployed using docker. To see logs for deployed docker containers, you can use the [Docker CLI](https://docs.docker.com/engine/reference/commandline/cli/), specifically the `docker logs` command. For examples, see [Docker Help](/guides/docker-help.md). ### How can I reduce the log verbosity of Redis? @@ -409,7 +435,7 @@ cap_drop: Data for Immich comes in two forms: 1. **Metadata** stored in a Postgres database, stored in the `DB_DATA_LOCATION` folder (previously `pg_data` Docker volume). -2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/docs/administration/backup-and-restore#asset-types-and-storage-locations). +2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/administration/backup-and-restore#asset-types-and-storage-locations). :::warning This will destroy your database and reset your instance, meaning that you start from scratch. @@ -447,7 +473,7 @@ If it mentions SIGILL (note the lack of a K) or error code 132, it most likely m ### Why am I getting database ownership errors? If you get database errors such as `FATAL: data directory "/var/lib/postgresql/data" has wrong ownership` upon database startup, this is likely due to an issue with your filesystem. -NTFS and ex/FAT/32 filesystems are not supported. See [here](/docs/install/requirements#special-requirements-for-windows-users) for more details. +NTFS and ex/FAT/32 filesystems are not supported. See [here](/install/requirements#special-requirements-for-windows-users) for more details. ### How can I verify the integrity of my database? diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index deeefa5635..f9c00c7df7 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -3,7 +3,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -A [3-2-1 backup strategy](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution. This page provides an overview on how to backup the database and the location of user-uploaded pictures and videos. A template bash script that can be run as a cron job is provided [here](/docs/guides/template-backup-script.md) +A [3-2-1 backup strategy](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution. This page provides an overview on how to backup the database and the location of user-uploaded pictures and videos. A template bash script that can be run as a cron job is provided [here](/guides/template-backup-script.md) :::danger The instructions on this page show you how to prepare your Immich instance to be backed up, and which files to take a backup of. You still need to take care of using an actual backup tool to make a backup yourself. @@ -160,7 +160,7 @@ for more info read the [release notes](https://github.com/immich-app/immich/rele :::danger A backup of this folder does not constitute a backup of your database! - Follow the instructions listed [here](/docs/administration/backup-and-restore#database) to learn how to perform a proper backup. + Follow the instructions listed [here](/administration/backup-and-restore#database) to learn how to perform a proper backup. ::: @@ -205,7 +205,7 @@ When you turn off the storage template engine, it will leave the assets in `UPLO :::danger A backup of this folder does not constitute a backup of your database! - Follow the instructions listed [here](/docs/administration/backup-and-restore#database) to learn how to perform a proper backup. + Follow the instructions listed [here](/administration/backup-and-restore#database) to learn how to perform a proper backup. ::: diff --git a/docs/docs/administration/email-notification.mdx b/docs/docs/administration/email-notification.mdx index 2ad4fba2be..0da132161f 100644 --- a/docs/docs/administration/email-notification.mdx +++ b/docs/docs/administration/email-notification.mdx @@ -12,7 +12,7 @@ You can access the settings panel from the web at `Administration -> Settings -> Under Email, enter the required details to connect with an SMTP server. -You can use [this guide](/docs/guides/smtp-gmail) to use Gmail's SMTP server. +You can use [this guide](/guides/smtp-gmail) to use Gmail's SMTP server. ## User's notifications settings diff --git a/docs/docs/administration/jobs-workers.md b/docs/docs/administration/jobs-workers.md index 4634151b9a..8ed3ba2694 100644 --- a/docs/docs/administration/jobs-workers.md +++ b/docs/docs/administration/jobs-workers.md @@ -11,7 +11,7 @@ The `immich-server` container contains multiple workers: ## Split workers -If you prefer to throttle or distribute the workers, you can do this using the [environment variables](/docs/install/environment-variables) to specify which container should pick up which tasks. +If you prefer to throttle or distribute the workers, you can do this using the [environment variables](/install/environment-variables) to specify which container should pick up which tasks. For example, for a simple setup with one container for the Web/API and one for all other microservices, you can do the following: @@ -53,5 +53,21 @@ Additionally, some jobs (such as memories generation) run on a schedule, which i :::note -Some jobs ([External Libraries](/docs/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings. +Some jobs ([External Libraries](/features/libraries) scanning, Database Dump) are configured in their own sections in System Settings. ::: + +## Job processing order + +The below diagram shows the job run order for newly uploaded files + +```mermaid +graph TD + A[Asset Upload] --> B[Metadata Extraction] + B --> C[Storage Template Migration] + C --> D["Thumbnail Generation (Large, small, blurred and person)"] + D --> E[Smart Search] + D --> F[Face Detection] + D --> G[Video Transcoding] + E --> H[Duplicate Detection] + F --> I[Facial Recognition] +``` diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 833b70f77a..47f4a96c6a 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -10,7 +10,7 @@ Unable to set `app.immich:///oauth-callback` as a valid redirect URI? See [Mobil Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including: -- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect) +- [Authentik](https://integrations.goauthentik.io/media/immich/) - [Authelia](https://www.authelia.com/integration/openid-connect/immich/) - [Okta](https://www.okta.com/openid-connect/) - [Google](https://developers.google.com/identity/openid-connect/openid-connect) @@ -28,7 +28,7 @@ Before enabling OAuth in Immich, a new client application needs to be configured 2. Configure Redirect URIs/Origins The **Sign-in redirect URIs** should include: - - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/docs/features/mobile-app.mdx) + - `app.immich:///oauth-callback` - for logging in with OAuth from the [Mobile App](/features/mobile-app.mdx) - `http://DOMAIN:PORT/auth/login` - for logging in with OAuth from the Web Client - `http://DOMAIN:PORT/user-settings` - for manually linking OAuth in the Web Client @@ -64,7 +64,7 @@ Once you have a new OAuth client application configured, Immich can be configure | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** | -| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | +| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (empty for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | @@ -88,7 +88,7 @@ The `.well-known/openid-configuration` part of the url is optional and will be a ## Auto Launch When Auto Launch is enabled, the login page will automatically redirect the user to the OAuth authorization url, to login with OAuth. To access the login screen again, use the browser's back button, or navigate directly to `/auth/login?autoLaunch=0`. -Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?authLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich. +Auto Launch can also be enabled on a per-request basis by navigating to `/auth/login?autoLaunch=1`, this can be useful in situations where Immich is called from e.g. Nextcloud using the _External sites_ app and the _oidc_ app so as to enable users to directly interact with a logged-in instance of Immich. ## Mobile Redirect URI @@ -98,7 +98,7 @@ The redirect URI for the mobile app is `app.immich:///oauth-callback`, which is 2. Whitelist the new endpoint as a valid redirect URI with your provider. 3. Specify the new endpoint as the `Mobile Redirect URI Override`, in the OAuth settings. -With these steps in place, you should be able to use OAuth from the [Mobile App](/docs/features/mobile-app.mdx) without a custom scheme redirect URI. +With these steps in place, you should be able to use OAuth from the [Mobile App](/features/mobile-app.mdx) without a custom scheme redirect URI. :::info Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to forward requests to `app.immich:///oauth-callback`, and can be used for step 1. @@ -106,6 +106,89 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to ## Example Configuration +
+Authelia Example + +### Authelia Example + +Here's an example of OAuth configured for Authelia: + +This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich. +The configuration concerning the quota is optional. + +```yaml +authentication_backend: + ldap: + # The LDAP server configuration goes here. + # See: https://www.authelia.com/c/ldap + attributes: + extra: + immichquota: # The attribute name from LDAP + name: 'immich_quota' + multi_valued: false + value_type: 'integer' +identity_providers: + oidc: + ## The other portions of the mandatory OpenID Connect 1.0 configuration go here. + ## See: https://www.authelia.com/c/oidc + claims_policies: + immich_policy: + custom_claims: + immich_quota: + attribute: 'immich_quota' + scopes: + immich_scope: + claims: + - 'immich_quota' + + clients: + - client_id: 'immich' + client_name: 'Immich' + # https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret + client_secret: $pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' + public: false + require_pkce: false + redirect_uris: + - 'https://example.immich.app/auth/login' + - 'https://example.immich.app/user-settings' + - 'app.immich:///oauth-callback' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'immich_scope' + claims_policy: 'immich_policy' + response_types: + - 'code' + grant_types: + - 'authorization_code' + id_token_signed_response_alg: 'RS256' + userinfo_signed_response_alg: 'RS256' + token_endpoint_auth_method: 'client_secret_post' +``` + +Configuration of OAuth in Immich System Settings + +| Setting | Value | +| ---------------------------------- | ------------------------------------------------------------------- | +| Issuer URL | `https://example.immich.app/.well-known/openid-configuration` | +| Client ID | immich | +| Client Secret | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... | +| Token Endpoint Auth Method | client_secret_post | +| Scope | openid email profile immich_scope | +| ID Token Signed Response Algorithm | RS256 | +| Userinfo Signed Response Algorithm | RS256 | +| Storage Label Claim | uid | +| Storage Quota Claim | immich_quota | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | +| Button Text | Sign in with Authelia (optional) | +| Auto Register | Enabled (optional) | +| Auto Launch | Enabled (optional) | +| Mobile Redirect URI Override | Disable | +| Mobile Redirect URI | | + +
+
Authentik Example @@ -128,7 +211,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Authentik (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled (optional) | @@ -159,7 +242,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Google (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled | diff --git a/docs/docs/administration/reverse-proxy.md b/docs/docs/administration/reverse-proxy.md index 742f0e110f..f13814d91f 100644 --- a/docs/docs/administration/reverse-proxy.md +++ b/docs/docs/administration/reverse-proxy.md @@ -2,10 +2,6 @@ Users can deploy a custom reverse proxy that forwards requests to Immich. This way, the reverse proxy can handle TLS termination, load balancing, or other advanced features. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Real-IP`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich. -:::note -The Repair page can take a long time to load. To avoid server timeouts or errors, we recommend specifying a timeout of at least 10 minutes on your proxy server. -::: - :::caution Immich does not support being served on a sub-path such as `location /immich {`. It has to be served on the root path of a (sub)domain. ::: diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index b275d8fede..3838635c24 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -16,7 +16,7 @@ The `immich-server` docker image comes preinstalled with an administrative CLI ( ## How to run a command -To run a command, [connect](/docs/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin `. +To run a command, [connect](/guides/docker-help.md#attach-to-a-container) to the `immich_server` container and then execute the command via `immich-admin `. ## Examples @@ -94,19 +94,16 @@ Change media location ``` immich-admin change-media-location -? Enter the previous value of IMMICH_MEDIA_LOCATION: /usr/src/app/upload -? Enter the new value of IMMICH_MEDIA_LOCATION: /data +? Enter the previous value of IMMICH_MEDIA_LOCATION: /data +? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data +... + Previous value: /data + Current value: /my-data - Previous value: /usr/src/app/upload - Current value: /data - - Changing database paths from "/usr/src/app/upload/*" to "/data/*" + Changing database paths from "/data/*" to "/my-data/*" ? Do you want to proceed? [Y/n] y Database file paths updated successfully! 🎉 - -You may now set IMMICH_MEDIA_LOCATION=/data and restart! - -(please remember to update applicable volume mounts e.g. ${UPLOAD_LOCATION}:/data) +... ``` diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md index f241050136..fdfdad29ea 100644 --- a/docs/docs/administration/system-settings.md +++ b/docs/docs/administration/system-settings.md @@ -12,14 +12,14 @@ Manage password, OAuth, and other authentication settings ### OAuth Authentication -Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth). +Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/administration/oauth). ### Password Authentication -The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts. +The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts. :::tip -You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login. +You can always use the [Server CLI](/administration/server-commands) to re-enable password login. ::: ## Image Settings (thumbnails and previews) @@ -108,7 +108,7 @@ If more than one URL is provided, each server will be attempted one-at-a-time un ### Smart Search -The [smart search](/docs/features/searching) settings allow you to change the [CLIP model](https://openai.com/research/clip). Larger models will typically provide [more accurate search results](https://github.com/immich-app/immich/discussions/11862) but consume more processing power and RAM. When [changing the CLIP model](/docs/FAQ#can-i-use-a-custom-clip-model) it is mandatory to re-run the Smart Search job on all images to fully apply the change. +The [smart search](/features/searching) settings allow you to change the [CLIP model](https://openai.com/research/clip). Larger models will typically provide [more accurate search results](https://github.com/immich-app/immich/discussions/11862) but consume more processing power and RAM. When [changing the CLIP model](/FAQ#can-i-use-a-custom-clip-model) it is mandatory to re-run the Smart Search job on all images to fully apply the change. :::info Internet connection Changing models requires a connection to the Internet to download the model. @@ -132,7 +132,7 @@ Editable settings: - **Max Recognition Distance** - **Min Recognized Faces** -You can learn more about these options on the [Facial Recognition page](/docs/features/facial-recognition#how-face-detection-works) +You can learn more about these options on the [Facial Recognition page](/features/facial-recognition#how-face-detection-works) :::info When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces. @@ -154,15 +154,15 @@ The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for ### Reverse Geocoding Settings -Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. +Immich supports [Reverse Geocoding](/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database. ## Notification Settings -SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/docs/administration/email-notification) +SMTP server setup, for user creation notifications, new albums, etc. More information can be found [here](/administration/email-notification) ## Notification Templates -Override the default notifications text with notification templates. More information can be found [here](/docs/administration/email-notification) +Override the default notifications text with notification templates. More information can be found [here](/administration/email-notification) ## Server Settings @@ -176,7 +176,7 @@ The administrator can set a custom message on the login screen (the message will ## Storage Template -Immich supports a custom [Storage Template](/docs/administration/storage-template). Learn more about this feature and its configuration [here](/docs/administration/storage-template). +Immich supports a custom [Storage Template](/administration/storage-template). Learn more about this feature and its configuration [here](/administration/storage-template). ## Theme Settings diff --git a/docs/docs/developer/architecture.mdx b/docs/docs/developer/architecture.mdx index a8d38ba5c1..42d9c1b974 100644 --- a/docs/docs/developer/architecture.mdx +++ b/docs/docs/developer/architecture.mdx @@ -44,7 +44,7 @@ The web app is a [TypeScript](https://www.typescriptlang.org/) project that uses ### CLI -The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users control their Immich instance from the command line. It uses the API to perform various tasks, especially uploading assets. See the [CLI documentation](/docs/features/command-line-interface.md) for more information. +The Immich CLI is an [npm](https://www.npmjs.com/) package that lets users control their Immich instance from the command line. It uses the API to perform various tasks, especially uploading assets. See the [CLI documentation](/features/command-line-interface.md) for more information. ## Server @@ -83,11 +83,11 @@ Immich uses a [worker](https://github.com/immich-app/immich/blob/main/server/src - Smart Search - Facial Recognition - Storage Template Migration -- Sidecar (see [XMP Sidecars](/docs/features/xmp-sidecars.md)) +- Sidecar (see [XMP Sidecars](/features/xmp-sidecars.md)) - Background jobs (file deletion, user deletion) :::info -This list closely matches what is available on the [Administration > Jobs](/docs/administration/jobs-workers/#jobs) page, which provides some remote queue management capabilities. +This list closely matches what is available on the [Administration > Jobs](/administration/jobs-workers/#jobs) page, which provides some remote queue management capabilities. ::: ### Machine Learning diff --git a/docs/docs/developer/database-migrations.md b/docs/docs/developer/database-migrations.md index c0c61340cb..f032048b7a 100644 --- a/docs/docs/developer/database-migrations.md +++ b/docs/docs/developer/database-migrations.md @@ -5,7 +5,7 @@ After making any changes in the `server/src/schema`, a database migration need t 1. Run the command ```bash -npm run migrations:generate +pnpm run migrations:generate ``` 2. Check if the migration file makes sense. diff --git a/docs/docs/developer/devcontainers.md b/docs/docs/developer/devcontainers.md index c4c5396466..0a1946e6c1 100644 --- a/docs/docs/developer/devcontainers.md +++ b/docs/docs/developer/devcontainers.md @@ -204,8 +204,8 @@ When the Dev Container starts, it automatically: 1. **Runs post-create script** (`container-server-post-create.sh`): - Adjusts file permissions for the `node` user - - Installs dependencies: `npm install` in all packages - - Builds TypeScript SDK: `npm run build` in `open-api/typescript-sdk` + - Installs dependencies: `pnpm install` in all packages + - Builds TypeScript SDK: `pnpm run build` in `open-api/typescript-sdk` 2. **Starts development servers** via VS Code tasks: - `Immich API Server (Nest)` - API server with hot-reloading on port 2283 @@ -243,7 +243,7 @@ To connect the mobile app to your Dev Container: - **Server code** (`/server`): Changes trigger automatic restart - **Web code** (`/web`): Changes trigger hot module replacement -- **Database migrations**: Run `npm run sync:sql` in the server directory +- **Database migrations**: Run `pnpm run sync:sql` in the server directory - **API changes**: Regenerate TypeScript SDK with `make open-api` ## Testing @@ -273,19 +273,19 @@ make test-medium-dev # End-to-end tests ```bash # Server tests cd /workspaces/immich/server -npm test # Run all tests -npm run test:watch # Watch mode -npm run test:cov # Coverage report +pnpm test # Run all tests +pnpm run test:watch # Watch mode +pnpm run test:cov # Coverage report # Web tests cd /workspaces/immich/web -npm test # Run all tests -npm run test:watch # Watch mode +pnpm test # Run all tests +pnpm run test:watch # Watch mode # E2E tests cd /workspaces/immich/e2e -npm run test # Run API tests -npm run test:web # Run web UI tests +pnpm run test # Run API tests +pnpm run test:web # Run web UI tests ``` ### Code Quality Commands @@ -431,7 +431,7 @@ While the Dev Container focuses on server and web development, you can connect m - Server URL: `http://YOUR_IP:2283/api` - Ensure firewall allows port 2283 -3. **For full mobile development**, see the [mobile development guide](/docs/developer/setup) which covers: +3. **For full mobile development**, see the [mobile development guide](/developer/setup) which covers: - Flutter setup - Running on simulators/devices - Mobile-specific debugging @@ -474,7 +474,7 @@ Recommended minimums: ## Next Steps -- Read the [architecture overview](/docs/developer/architecture) -- Learn about [database migrations](/docs/developer/database-migrations) -- Explore [API documentation](/docs/api) +- Read the [architecture overview](/developer/architecture) +- Learn about [database migrations](/developer/database-migrations) +- Explore [API documentation](https://api.immich.app/) - Join `#immich` on [Discord](https://discord.immich.app) diff --git a/docs/docs/developer/open-api.md b/docs/docs/developer/open-api.md index 2c29c7365b..f627b2c459 100644 --- a/docs/docs/developer/open-api.md +++ b/docs/docs/developer/open-api.md @@ -1,6 +1,6 @@ # OpenAPI -Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](/docs/api). +Immich uses the [OpenAPI](https://swagger.io/specification/) standard to generate API documentation. To view the published docs see [here](https://api.immich.app/). ## Generator diff --git a/docs/docs/developer/pr-checklist.md b/docs/docs/developer/pr-checklist.md index 58581e669a..f855e854c4 100644 --- a/docs/docs/developer/pr-checklist.md +++ b/docs/docs/developer/pr-checklist.md @@ -8,40 +8,53 @@ When contributing code through a pull request, please check the following: ## Web Checks -- [ ] `npm run lint` (linting via ESLint) -- [ ] `npm run format` (formatting via Prettier) -- [ ] `npm run check:svelte` (Type checking via SvelteKit) -- [ ] `npm run check:typescript` (check typescript) -- [ ] `npm test` (unit tests) +- [ ] `pnpm run lint` (linting via ESLint) +- [ ] `pnpm run format` (formatting via Prettier) +- [ ] `pnpm run check:svelte` (Type checking via SvelteKit) +- [ ] `pnpm run check:typescript` (check typescript) +- [ ] `pnpm test` (unit tests) ## Documentation -- [ ] `npm run format` (formatting via Prettier) +- [ ] `pnpm run format` (formatting via Prettier) - [ ] Update the `_redirects` file if you have renamed a page or removed it from the documentation. :::tip AIO -Run all web checks with `npm run check:all` +Run all web checks with `pnpm run check:all` ::: ## Server Checks -- [ ] `npm run lint` (linting via ESLint) -- [ ] `npm run format` (formatting via Prettier) -- [ ] `npm run check` (Type checking via `tsc`) -- [ ] `npm test` (unit tests) +- [ ] `pnpm run lint` (linting via ESLint) +- [ ] `pnpm run format` (formatting via Prettier) +- [ ] `pnpm run check` (Type checking via `tsc`) +- [ ] `pnpm test` (unit tests) :::tip AIO -Run all server checks with `npm run check:all` +Run all server checks with `pnpm run check:all` ::: :::info Auto Fix -You can use `npm run __:fix` to potentially correct some issues automatically for `npm run format` and `lint`. +You can use `pnpm run __:fix` to potentially correct some issues automatically for `pnpm run format` and `lint`. +::: + +## Mobile Checks + +The following commands must be executed from within the mobile app directory of the codebase. + +- [ ] `make build` (auto-generate files using build_runner) +- [ ] `make analyze` (static analysis via Dart Analyzer and DCM) +- [ ] `make format` (formatting via Dart Formatter) +- [ ] `make test` (unit tests) + +:::info Auto Fix +You can use `dart fix --apply` and `dcm fix lib` to potentially correct some issues automatically for `make analyze`. ::: ## OpenAPI -The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/docs/developer/open-api.md) for more details. +The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/developer/open-api.md) for more details. ## Database Migrations -A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/docs/developer/database-migrations.md) for more details. +A database migration needs to be generated whenever there are changes to `server/src/infra/src/entities`. See [Database Migration](/developer/database-migrations.md) for more details. diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index 32705e3248..bf1003b781 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -54,20 +54,20 @@ You can access the web from `http://your-machine-ip:3000` or `http://localhost:3 If you only want to do web development connected to an existing, remote backend, follow these steps: -1. Build the Immich SDK - `cd open-api/typescript-sdk && npm i && npm run build && cd -` +1. Build the Immich SDK - `cd open-api/typescript-sdk && pnpm i && pnpm run build && cd -` 2. Enter the web directory - `cd web/` -3. Install web dependencies - `npm i` +3. Install web dependencies - `pnpm i` 4. Start the web development server ```bash -IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev +IMMICH_SERVER_URL=https://demo.immich.app/ pnpm run dev ``` If you're using PowerShell on Windows you may need to set the env var separately like so: ```powershell $env:IMMICH_SERVER_URL = "https://demo.immich.app/" -npm run dev +pnpm run dev ``` #### `@immich/ui` @@ -75,12 +75,12 @@ npm run dev To see local changes to `@immich/ui` in Immich, do the following: 1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui` -2. Build the `@immich/ui` project via `npm run build` +2. Build the `@immich/ui` project via `pnpm run build` 3. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`) 4. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`) 5. Uncomment the import statement in `web/src/app.css` file `@import '/usr/ui/dist/theme/default.css';` and comment out `@import '@immich/ui/theme/default.css';` 6. Start up the stack via `make dev` -7. After making changes in `@immich/ui`, rebuild it (`npm run build`) +7. After making changes in `@immich/ui`, rebuild it (`pnpm run build`) ### Mobile app diff --git a/docs/docs/developer/testing.md b/docs/docs/developer/testing.md index ad64ba015c..e1b96f9164 100644 --- a/docs/docs/developer/testing.md +++ b/docs/docs/developer/testing.md @@ -4,8 +4,8 @@ ### Unit tests -Unit are run by calling `npm run test` from the `server/` directory. -You need to run `npm install` (in `server/`) before _once_. +Unit are run by calling `pnpm run test` from the `server/` directory. +You need to run `pnpm install` (in `server/`) before _once_. ### End to end tests @@ -17,14 +17,14 @@ make e2e Before you can run the tests, you need to run the following commands _once_: -- `npm install` (in `e2e/`) +- `pnpm install` (in `e2e/`) - `make open-api` (in the project root `/`) Once the test environment is running, the e2e tests can be run via: ```bash cd e2e/ -npm test +pnpm test ``` The tests check various things including: diff --git a/docs/docs/features/automatic-backup.md b/docs/docs/features/automatic-backup.md index 8fcbedaa6e..30d132cef8 100644 --- a/docs/docs/features/automatic-backup.md +++ b/docs/docs/features/automatic-backup.md @@ -16,7 +16,7 @@ If foreground backup is enabled: whenever the app is opened or resumed, it will ## Background backup -This feature is intended for everyday use. For initial bulk uploading, please use the foreground upload feature. For more information on why background upload is not working as expected, please refer to the [FAQ](/docs/FAQ#why-does-foreground-backup-stop-when-i-navigate-away-from-the-app-shouldnt-it-transfer-the-job-to-background-backup). +This feature is intended for everyday use. For initial bulk uploading, please use the foreground upload feature. For more information on why background upload is not working as expected, please refer to the [FAQ](/FAQ#why-does-foreground-backup-stop-when-i-navigate-away-from-the-app-shouldnt-it-transfer-the-job-to-background-backup). If background backup is enabled. The app will periodically check if there are any new photos or videos in the selected album(s) to be uploaded to the server. If there are, it will upload them to the cloud in the background. diff --git a/docs/docs/features/facial-recognition.md b/docs/docs/features/facial-recognition.md index f0dec55484..85712ef5f6 100644 --- a/docs/docs/features/facial-recognition.md +++ b/docs/docs/features/facial-recognition.md @@ -70,7 +70,7 @@ Navigating to Administration > Settings > Machine Learning Settings > Facial Rec :::tip It's better to only tweak the parameters here than to set them to something very different unless you're ready to test a variety of options. If you do need to set a parameter to a strict setting, relaxing other settings can be a good option to compensate, and vice versa. -You can learn how the tune the result in this [Guide](/docs/guides/better-facial-clusters) +You can learn how the tune the result in this [Guide](/guides/better-facial-clusters) ::: ### Facial recognition model diff --git a/docs/docs/features/img/xmp-sidecars.webp b/docs/docs/features/img/xmp-sidecars.webp deleted file mode 100644 index f00b32c730..0000000000 Binary files a/docs/docs/features/img/xmp-sidecars.webp and /dev/null differ diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index ad76eb44b2..9f1cef0bc4 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -1,5 +1,9 @@ # External Libraries +:::info +Currently an external library can only belong to a single user which is selected when the library is initially created. +::: + External libraries track assets stored in the filesystem outside of Immich. When the external library is scanned, Immich will load videos and photos from disk and create the corresponding assets. These assets will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. Later, if a file is modified outside of Immich, you need to scan the library for the changes to show up. If an external asset is deleted from disk, Immich will move it to trash on rescan. To restore the asset, you need to restore the original file. After 30 days the file will be removed from trash, and any changes to metadata within Immich will be lost. @@ -33,7 +37,7 @@ Sometimes, an external library will not scan correctly. This can happen if Immic - Are the permissions set correctly? - Make sure you are using forward slashes (`/`) and not backward slashes. -To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/data/import/photos`, check it with `ls /data/import/photos`. Do the same check for the same in any microservices containers. +To validate that Immich can reach your external library, start a shell inside the container. Run `docker exec -it immich_server bash` to a bash shell. If your import path is `/mnt/photos`, check it with `ls /mnt/photos`. If you are using a dedicated microservices container, make sure to add the same mount point and check for availability within the microservices container as well. ### Exclusion Patterns @@ -58,7 +62,7 @@ Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package t This feature is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. -If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes. +If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a [periodic library refresh](#set-custom-scan-interval) to pull in your changes. #### Troubleshooting @@ -72,7 +76,9 @@ In rare cases, the library watcher can hang, preventing Immich from starting up. ### Nightly job -There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page. +There is an automatic scan job that is scheduled to run once a day. Its schedule is configurable, see [Set Custom Scan Interval](#set-custom-scan-interval). + +This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page. ## Usage @@ -91,7 +97,7 @@ The `immich-server` container will need access to the gallery. Modify your docke ```diff title="docker-compose.yml" immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro + - /home/user/old-pics:/mnt/media/old-pics:ro + - /mnt/media/videos:/mnt/media/videos:ro @@ -101,7 +107,7 @@ The `immich-server` container will need access to the gallery. Modify your docke :::tip The `ro` flag at the end only gives read-only access to the volumes. -This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/docs/features/xmp-sidecars)). +This will disallow the images from being deleted in the web UI, or adding metadata to the library ([XMP sidecars](/features/xmp-sidecars)). ::: :::info diff --git a/docs/docs/features/ml-hardware-acceleration.md b/docs/docs/features/ml-hardware-acceleration.md index a94f8c8c64..086f93a000 100644 --- a/docs/docs/features/ml-hardware-acceleration.md +++ b/docs/docs/features/ml-hardware-acceleration.md @@ -35,7 +35,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - Where and how you can get this file depends on device and vendor, but typically, the device vendor also supplies these - The `hwaccel.ml.yml` file assumes the path to it is `/usr/lib/libmali.so`, so update accordingly if it is elsewhere - The `hwaccel.ml.yml` file assumes an additional file `/lib/firmware/mali_csffw.bin`, so update accordingly if your device's driver does not require this file -- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for ARM NN specific settings +- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for ARM NN specific settings - In particular, the `MACHINE_LEARNING_ANN_FP16_TURBO` can significantly improve performance at the cost of very slightly lower accuracy #### CUDA @@ -49,7 +49,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - The GPU must be supported by ROCm. If it isn't officially supported, you can attempt to use the `HSA_OVERRIDE_GFX_VERSION` environmental variable: `HSA_OVERRIDE_GFX_VERSION=`. If this doesn't work, you might need to also set `HSA_USE_SVM=0`. - The ROCm image is quite large and requires at least 35GiB of free disk space. However, pulling later updates to the service through Docker will generally only amount to a few hundred megabytes as the rest will be cached. -- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/docs/install/environment-variables) setting). +- This backend is new and may experience some issues. For example, GPU power consumption can be higher than usual after running inference, even if the machine learning service is idle. In this case, it will only go back to normal after being idle for 5 minutes (configurable with the [MACHINE_LEARNING_MODEL_TTL](/install/environment-variables) setting). #### OpenVINO @@ -64,7 +64,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele - This is usually pre-installed on the device vendor's Linux images - RKNPU driver V0.9.8 or later must be available in the host server - You may confirm this by running `cat /sys/kernel/debug/rknpu/version` to check the version -- Optional: Configure your `.env` file, see [environment variables](/docs/install/environment-variables) for RKNN specific settings +- Optional: Configure your `.env` file, see [environment variables](/install/environment-variables) for RKNN specific settings - In particular, setting `MACHINE_LEARNING_RKNN_THREADS` to 2 or 3 can _dramatically_ improve performance for RK3576 and RK3588 compared to the default of 1, at the expense of multiplying the amount of RAM each model uses by that amount. ## Setup diff --git a/docs/docs/features/mobile-app.mdx b/docs/docs/features/mobile-app.mdx index 38222479b0..82a2976b41 100644 --- a/docs/docs/features/mobile-app.mdx +++ b/docs/docs/features/mobile-app.mdx @@ -28,7 +28,7 @@ The beta release channel allows users to test upcoming changes before they are o :::info -You can enable automatic backup on supported devices. For more information see [Automatic Backup](/docs/features/automatic-backup.md). +You can enable automatic backup on supported devices. For more information see [Automatic Backup](/features/automatic-backup.md). ::: ## Sync only selected photos @@ -75,7 +75,7 @@ You can sync or mirror an album from your phone to the Immich server on your acc - **User-Specific Sync:** Album synchronization is unique to each server user and does not sync between different users or partners. -- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/docs/features/libraries) for further details. +- **Mobile-Only Feature:** Album synchronization is currently only available on mobile. For similar options on a computer, refer to [Libraries](/features/libraries) for further details. ### Synchronizing albums from the past @@ -88,9 +88,9 @@ It will only reflect files you add. ::: If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually. -To overcome this limitation, the files must be removed from the blacklist by +To overcome this limitation, the files must be removed from the ignore list by App settings -> Advanced -> Duplicate Assets -> Clear :::info -Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization. +Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the ignore list again at the end of the synchronization. ::: diff --git a/docs/docs/features/monitoring.md b/docs/docs/features/monitoring.md index 64377ec073..f087a3306f 100644 --- a/docs/docs/features/monitoring.md +++ b/docs/docs/features/monitoring.md @@ -28,7 +28,7 @@ The metrics in immich are grouped into API (endpoint calls and response times), Immich will not expose an endpoint for metrics by default. To enable this endpoint, you can add the `IMMICH_TELEMETRY_INCLUDE=all` environmental variable to your `.env` file. Note that only the server container currently use this variable. :::tip -`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/docs/install/environment-variables.md#prometheus). +`IMMICH_TELEMETRY_INCLUDE=all` enables all metrics. For a more granular configuration you can enumerate the telemetry metrics that should be included as a comma separated list (e.g. `IMMICH_TELEMETRY_INCLUDE=repo,api`). Alternatively, you can also exclude specific metrics with `IMMICH_TELEMETRY_EXCLUDE`. For more information refer to the [environment section](/install/environment-variables.md#prometheus). ::: The next step is to configure a new or existing Prometheus instance to scrape this endpoint. The following steps assume that you do not have an existing Prometheus instance, but the steps will be similar either way. @@ -66,9 +66,9 @@ The provided file is just a starting point. There are a ton of ways to configure After bringing down the containers with `docker compose down` and back up with `docker compose up -d`, a Prometheus instance will now collect metrics from the immich server and microservices containers. Note that we didn't need to expose any new ports for these containers - the communication is handled in the internal Docker network. :::note -To see exactly what metrics are made available, you can additionally add `8081:8081` to the server container's ports and `8082:8082` to the microservices container's ports. +To see exactly what metrics are made available, you can additionally add `8081:8081` (API metrics) and `8082:8082` (microservices metrics) to the immich_server container's ports. Visiting the `/metrics` endpoint for these services will show the same raw data that Prometheus collects. -To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/docs/install/environment-variables/#general). +To configure these ports see [`IMMICH_API_METRICS_PORT` & `IMMICH_MICROSERVICES_METRICS_PORT`](/install/environment-variables/#general). ::: ### Usage diff --git a/docs/docs/features/reverse-geocoding.md b/docs/docs/features/reverse-geocoding.md index 399bdd9b48..b1aee74a99 100644 --- a/docs/docs/features/reverse-geocoding.md +++ b/docs/docs/features/reverse-geocoding.md @@ -8,7 +8,7 @@ During Exif Extraction, assets with latitudes and longitudes are reverse geocode ## Usage -Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/docs/features/searching.md). +Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/features/searching.md). diff --git a/docs/docs/features/sharing.md b/docs/docs/features/sharing.md index ff0a03beea..9ba7470407 100644 --- a/docs/docs/features/sharing.md +++ b/docs/docs/features/sharing.md @@ -24,7 +24,7 @@ After creating an album, you can access the sharing options by clicking on the s Partner sharing allows you to share your _entire_ library with other users of your choice. They can then view your library and download the assets. -You can read this guide to learn more about [partner sharing](/docs/features/partner-sharing). +You can read this guide to learn more about [partner sharing](/features/partner-sharing). ## Public sharing diff --git a/docs/docs/features/tags.md b/docs/docs/features/tags.md index ca663e9edd..79a9696d9a 100644 --- a/docs/docs/features/tags.md +++ b/docs/docs/features/tags.md @@ -1,6 +1,6 @@ # Tags -Immich supports hierarchical tags, with the ability to read existing tags from the `TagList` and `Keywords` EXIF properties. Any changes to tags made through Immich are also written back to a [sidecar](/docs/features/xmp-sidecars) file. You can re-run the metadata extraction jobs for all assets to import your existing tags. +Immich supports hierarchical tags, with the ability to read existing tags from the XMP `TagsList` field and IPTC `Keywords` field. Any changes to tags made through Immich are also written back to a [sidecar](/features/xmp-sidecars) file. You can re-run the metadata extraction jobs for all assets to import your existing tags. ## Enable tags feature diff --git a/docs/docs/features/user-settings.md b/docs/docs/features/user-settings.md index a2d0308541..402105cd43 100644 --- a/docs/docs/features/user-settings.md +++ b/docs/docs/features/user-settings.md @@ -15,9 +15,9 @@ You can access the [user settings](https://my.immich.app/user-settings) by click --- :::tip Reset Password -The admin can reset a user password through the [User Management](/docs/administration/user-management.mdx) screen. +The admin can reset a user password through the [User Management](/administration/user-management.mdx) screen. ::: :::tip Reset Admin Password -The admin password can be reset using a [Server Command](/docs/administration/server-commands.md) +The admin password can be reset using a [Server Command](/administration/server-commands.md) ::: diff --git a/docs/docs/features/xmp-sidecars.md b/docs/docs/features/xmp-sidecars.md index 98ce8782e6..3536777d8a 100644 --- a/docs/docs/features/xmp-sidecars.md +++ b/docs/docs/features/xmp-sidecars.md @@ -1,13 +1,68 @@ # XMP Sidecars -Immich can ingest XMP sidecars on file upload (via the CLI) as well as detect new sidecars that are placed in the filesystem for existing images. +Immich supports XMP sidecar files — external `.xmp` files that store metadata for an image or video in XML format. During the metadata extraction job Immich will read & import metadata from `.xmp` files, and during the Sidecar Write job it will _write_ metadata back to `.xmp`. - +:::tip +Tools like Lightroom, Darktable, digiKam and other applications can also be configured to write changes to `.xmp` files, in order to avoid modifying the original file. +::: -XMP sidecars are external XML files that contain metadata related to media files. Many applications read and write these files either exclusively or in addition to the metadata written to image files. They can be a powerful tool for editing and storing metadata of a media file without modifying the media file itself. When Immich receives or detects an XMP sidecar for a media file, it will attempt to extract the metadata from both the sidecar as well as the media file. It will prioritize the metadata for fields in the sidecar but will fall back and use the metadata in the media file if necessary. +## Metadata Fields -When importing files via the CLI bulk uploader or parsing photo metadata for external libraries, Immich will automatically detect XMP sidecar files as files that exist next to the original media file. Immich will look files that have the same name as the photo, but with the `.xmp` file extension. The same name can either include the photo's file extension or without the photo's file extension. For example, for a photo named `PXL_20230401_203352928.MP.jpg`, Immich will look for an XMP file named either `PXL_20230401_203352928.MP.jpg.xmp` or `PXL_20230401_203352928.MP.xmp`. If both `PXL_20230401_203352928.MP.jpg.xmp` and `PXL_20230401_203352928.MP.xmp` are present, Immich will prefer `PXL_20230401_203352928.MP.jpg.xmp`. +Immich does not support _all_ metadata fields. Below is a table showing what fields Immich can _read_ and _write_. It's important to note that writes do not replace the entire file contents, but are merged together with any existing fields. -There are 2 administrator jobs associated with sidecar files: `SYNC` and `DISCOVER`. The sync job will re-scan all media with existing sidecar files and queue them for a metadata refresh. This is a great use case when third-party applications are used to modify the metadata of media. The discover job will attempt to scan the filesystem for new sidecar files for all media that does not currently have a sidecar file associated with it. +:::info +Immich automatically queues a Sidecar Write job after editing the description, rating, or updating tags. +::: - +| Metadata | Immich writes to XMP | Immich reads from XMP | +| --------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Description** | `dc:description`, `tiff:ImageDescription` | `dc:description`, `tiff:ImageDescription` | +| **Rating** | `xmp:Rating` | `xmp:Rating` | +| **DateTime** | `exif:DateTimeOriginal`, `photoshop:DateCreated` | In prioritized order:
`exif:SubSecDateTimeOriginal`
`exif:DateTimeOriginal`
`xmp:SubSecCreateDate`
`xmp:CreateDate`
`xmp:CreationDate`
`xmp:MediaCreateDate`
`xmp:SubSecMediaCreateDate`
`xmp:DateTimeCreated` | +| **Location** | `exif:GPSLatitude`, `exif:GPSLongitude` | `exif:GPSLatitude`, `exif:GPSLongitude` | +| **Tags** | `digiKam:TagsList` | In prioritized order:
`digiKam:TagsList`
`lr:HierarchicalSubject`
`IPTC:Keywords` | + +:::note +All other fields (e.g. `Creator`, `Source`, IPTC, Lightroom edits) remain in the `.xmp` file and are **not searchable** in Immich. +::: + +## File Naming Rules + +A sidecar must share the base name of the media file: + +- ✅ `IMG_0001.jpg.xmp` ← preferred +- ✅ `IMG_0001.xmp` ← fallback +- ❌ `myphoto_meta.xmp` ← not recognized + +If both `.jpg.xmp` and `.xmp` are present, Immich uses the **`.jpg.xmp`** file. + +## CLI Support + +1. **Detect** – Immich looks for a `.xmp` file placed next to each media file during upload. +2. **Copy** – Both the media and the sidecar file are copied into Immich’s internal library folder. + The sidecar is renamed to match the internal filename template, e.g.: + `upload/library//YYYY/YYYY-MM-DD/IMG_0001.jpg` + `upload/library//YYYY/YYYY-MM-DD/IMG_0001.jpg.xmp` +3. **Extract** – Selected metadata (title, description, date, rating, tags) is parsed from the sidecar and saved to the database. +4. **Write-back** – If you later update tags, rating, or description in the web UI, Immich will update **both** the database _and_ the copied `.xmp` file to stay in sync. + +## External Library (Mounted Folder) Support + +1. **Detect** – The `DISCOVER` job automatically associates `.xmp` files that sit next to existing media files in your mounted folder. No files are moved or renamed. +2. **Extract** – Immich reads and saves the same metadata fields from the sidecar to the database. +3. **Write-back** – If Immich has **write access** to the mount, any future metadata edits (e.g., rating or tags) are also written back to the original `.xmp` file on disk. + +:::danger +If the mount is **read-only**, Immich cannot update either the sidecar **or** the database — **metadata edits will silently fail** with no warning see issue [#10538](https://github.com/immich-app/immich/issues/10538) for more details. +::: + +## Admin Jobs + +Immich provides two admin jobs for managing sidecars: + +| Job | What it does | +| ---------- | ------------------------------------------------------------------------------------------------- | +| `DISCOVER` | Finds new `.xmp` files next to media that don’t already have one linked | +| `SYNC` | Re-reads existing `.xmp` files and refreshes metadata in the database (e.g. after external edits) | + +![Sidecar Admin Jobs](./img/sidecar-jobs.webp) diff --git a/docs/docs/guides/better-facial-clusters.md b/docs/docs/guides/better-facial-clusters.md index f4409b441c..40796983a5 100644 --- a/docs/docs/guides/better-facial-clusters.md +++ b/docs/docs/guides/better-facial-clusters.md @@ -10,7 +10,7 @@ This guide explains how to optimize facial recognition in systems with large ima - **Best Suited For:** Large image libraries after importing a significant number of images. - **Warning:** This method deletes all previously assigned names. -- **Tip:** **Always take a [backup](/docs/administration/backup-and-restore#database) before proceeding!** +- **Tip:** **Always take a [backup](/administration/backup-and-restore#database) before proceeding!** --- diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md index ba5caf4b26..e0274d3bd9 100644 --- a/docs/docs/guides/custom-locations.md +++ b/docs/docs/guides/custom-locations.md @@ -9,7 +9,7 @@ It is important to remember to update the backup settings after following the gu In our `.env` file, we will define the paths we want to use. Note that you don't have to define all of these: UPLOAD_LOCATION will be the base folder that files are stored in by default, with the other paths acting as overrides. ```diff title=".env" -# You can find documentation for all the supported environment variables [here](/docs/install/environment-variables) +# You can find documentation for all the supported environment variables [here](/install/environment-variables) # Custom location where your uploaded, thumbnails, and transcoded video files are stored - UPLOAD_LOCATION=./library @@ -27,11 +27,11 @@ After defining the locations of these files, we will edit the `docker-compose.ym services: immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload -+ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs -+ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video -+ - ${PROFILE_LOCATION}:/usr/src/app/upload/profile -+ - ${BACKUP_LOCATION}:/usr/src/app/upload/backups + - ${UPLOAD_LOCATION}:/data ++ - ${THUMB_LOCATION}:/data/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video ++ - ${PROFILE_LOCATION}:/data/profile ++ - ${BACKUP_LOCATION}:/data/backups - /etc/localtime:/etc/localtime:ro ``` @@ -44,7 +44,7 @@ docker compose up -d :::note Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device. -For this reason, we mount the HDD or the network storage (NAS) to `/usr/src/app/upload` and then mount the folders we want to access under that folder. +For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder. The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated. diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 209f673993..5cdcdc04c4 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -7,123 +7,156 @@ Keep in mind that mucking around in the database might set the Moon on fire. Avo :::tip Run `docker exec -it immich_postgres psql --dbname= --username=` to connect to the database via the container directly. -(Replace `` and `` with the values from your [`.env` file](/docs/install/environment-variables#database)). +(Replace `` and `` with the values from your [`.env` file](/install/environment-variables#database)). ::: ## Assets +### Name + :::note The `"originalFileName"` column is the name of the file at time of upload, including the extension. ::: ```sql title="Find by original filename" -SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; -SELECT * FROM "assets" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ -SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle +SELECT * FROM "asset" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; +SELECT * FROM "asset" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ +SELECT * FROM "asset" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle ``` ```sql title="Find by path" -SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; -SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; +SELECT * FROM "asset" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; +SELECT * FROM "asset" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; ``` +### ID + ```sql title="Find by ID" -SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9'; +SELECT * FROM "asset" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9'; ``` ```sql title="Find by partial ID" -SELECT * FROM "assets" WHERE "id"::text LIKE '%ab431d3a%'; +SELECT * FROM "asset" WHERE "id"::text LIKE '%ab431d3a%'; ``` +### Checksum + :::note You can calculate the checksum for a particular file by using the command `sha1sum `. ::: ```sql title="Find by checksum (SHA-1)" -SELECT encode("checksum", 'hex') FROM "assets"; -SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); -SELECT * FROM "assets" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation +SELECT encode("checksum", 'hex') FROM "asset"; +SELECT * FROM "asset" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); +SELECT * FROM "asset" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation ``` ```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)" -SELECT T1."checksum", array_agg(T2."id") ids FROM "assets" T1 - INNER JOIN "assets" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL +SELECT T1."checksum", array_agg(T2."id") ids FROM "asset" T1 + INNER JOIN "asset" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum"; ``` +### Metadata + ```sql title="Live photos" -SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL; +SELECT * FROM "asset" WHERE "livePhotoVideoId" IS NOT NULL; ``` ```sql title="By description" -SELECT "assets".*, "exif"."description" FROM "exif" - JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE TRIM("exif"."description") <> ''; -- all files with a description -SELECT "assets".*, "exif"."description" FROM "exif" - JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."description" ILIKE '%string to match%'; -- search by string +SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" + JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE TRIM("asset_exif"."description") <> ''; -- all files with a description +SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" + JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."description" ILIKE '%string to match%'; -- search by string ``` ```sql title="Without metadata" -SELECT "assets".* FROM "exif" - LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."assetId" IS NULL; +SELECT "asset".* FROM "asset_exif" + LEFT JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."assetId" IS NULL; ``` ```sql title="size < 100,000 bytes, smallest to largest" -SELECT * FROM "assets" - JOIN "exif" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."fileSizeInByte" < 100000 - ORDER BY "exif"."fileSizeInByte" ASC; +SELECT * FROM "asset" + JOIN "asset_exif" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."fileSizeInByte" < 100000 + ORDER BY "asset_exif"."fileSizeInByte" ASC; ``` -```sql title="Without thumbnails" -SELECT * FROM "assets" WHERE "assets"."previewPath" IS NULL OR "assets"."thumbnailPath" IS NULL; -``` +### Type ```sql title="By type" -SELECT * FROM "assets" WHERE "assets"."type" = 'VIDEO'; -SELECT * FROM "assets" WHERE "assets"."type" = 'IMAGE'; +SELECT * FROM "asset" WHERE "asset"."type" = 'VIDEO'; +SELECT * FROM "asset" WHERE "asset"."type" = 'IMAGE'; ``` ```sql title="Count by type" -SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type"; +SELECT "asset"."type", COUNT(*) FROM "asset" GROUP BY "asset"."type"; ``` ```sql title="Count by type (per user)" -SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets" - JOIN "users" ON "assets"."ownerId" = "users"."id" - GROUP BY "assets"."type", "users"."email" ORDER BY "users"."email"; +SELECT "user"."email", "asset"."type", COUNT(*) FROM "asset" + JOIN "user" ON "asset"."ownerId" = "user"."id" + GROUP BY "asset"."type", "user"."email" ORDER BY "user"."email"; ``` -```sql title="Failed file movements" -SELECT * FROM "move_history"; +## Tags + +```sql title="Count by tag" +SELECT "t"."value" AS "tag_name", COUNT(*) AS "number_assets" FROM "tag" "t" + JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id" + WHERE "a"."visibility" != 'hidden' + GROUP BY "t"."value" ORDER BY "number_assets" DESC; +``` + +```sql title="Count by tag (per user)" +SELECT "t"."value" AS "tag_name", "u"."email" as "user_email", COUNT(*) AS "number_assets" FROM "tag" "t" + JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id" JOIN "user" "u" ON "a"."ownerId" = "u"."id" + WHERE "a"."visibility" != 'hidden' + GROUP BY "t"."value", "u"."email" ORDER BY "number_assets" DESC; ``` ## Users ```sql title="List all users" -SELECT * FROM "users"; +SELECT * FROM "user"; ``` ```sql title="Get owner info from asset ID" -SELECT "users".* FROM "users" JOIN "assets" ON "users"."id" = "assets"."ownerId" WHERE "assets"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f'; +SELECT "user".* FROM "user" JOIN "asset" ON "user"."id" = "asset"."ownerId" WHERE "asset"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f'; ``` -## System Config - -```sql title="Custom settings" -SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; -``` - -(Only used when not using the [config file](/docs/install/config-file)) - ## Persons ```sql title="Delete person and unset it for the faces it was associated with" DELETE FROM "person" WHERE "name" = 'PersonNameHere'; ``` +## System + +### Config + +```sql title="Custom settings" +SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; +``` + +(Only used when not using the [config file](/install/config-file)) + +### File properties + +```sql title="Without thumbnails" +SELECT * FROM "asset" +WHERE (NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'thumbnail') + OR NOT EXISTS (SELECT 1 FROM "asset_file" WHERE "asset"."id" = "asset_file"."assetId" AND "asset_file"."type" = 'preview')) +AND "asset"."visibility" = 'timeline'; +``` + +```sql title="Failed file movements" +SELECT * FROM "move_history"; +``` + ## Postgres internal ```sql title="Change DB_PASSWORD" diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index 3ad1679423..8ff45f2806 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -1,18 +1,18 @@ # External Library -This guide walks you through adding an [External Library](/docs/features/libraries). +This guide walks you through adding an [External Library](/features/libraries). This guide assumes you are running Immich in Docker and that the files you wish to access are stored in a directory on the same machine. # Mount the directory into the containers. Edit `docker-compose.yml` to add one or more new mount points in the section `immich-server:` under `volumes:`. -If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/docs/features/xmp-sidecars)), remove `:ro` from the end of the mount point. +If you want Immich to be able to delete the images in the external library or add metadata ([XMP sidecars](/features/xmp-sidecars)), remove `:ro` from the end of the mount point. ```diff immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /home/user/photos1:/home/user/photos1:ro + - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two ``` @@ -21,6 +21,10 @@ Restart Immich by running `docker compose up -d`. # Create the library +:::info +External library management requires administrator access and the steps below assume you are using an admin account. +::: + In the Immich web UI: - click the **Administration** link in the upper right corner. diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md index 6f401dfc5a..518b003c3a 100644 --- a/docs/docs/guides/remote-access.md +++ b/docs/docs/guides/remote-access.md @@ -46,7 +46,7 @@ You can learn how to set up Tailscale together with Immich with the [tutorial vi A reverse proxy is a service that sits between web servers and clients. A reverse proxy can either be hosted on the server itself or remotely. Clients can connect to the reverse proxy via https, and the proxy relays data to Immich. This setup makes most sense if you have your own domain and want to access your Immich instance just like any other website, from outside your LAN. You can also use a DDNS provider like DuckDNS or no-ip if you don't have a domain. This configuration allows the Immich Android and iphone apps to connect to your server without a VPN or tailscale app on the client side. -If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md). +If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/administration/reverse-proxy.md). You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accessible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser. diff --git a/docs/docs/guides/remote-machine-learning.md b/docs/docs/guides/remote-machine-learning.md index 72ae0e3fa1..0a8ddf2577 100644 --- a/docs/docs/guides/remote-machine-learning.md +++ b/docs/docs/guides/remote-machine-learning.md @@ -1,6 +1,6 @@ # Remote Machine Learning -To alleviate [performance issues on low-memory systems](/docs/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user. +To alleviate [performance issues on low-memory systems](/FAQ.mdx#why-is-immich-slow-on-low-memory-systems-like-the-raspberry-pi) like the Raspberry Pi, you may also host Immich's machine learning container on a more powerful system, such as your laptop or desktop computer. The server container will send requests containing the image preview to the remote machine learning container for processing. The machine learning container does not persist this data or associate it with a particular user. :::info Smart Search and Face Detection will use this feature, but Facial Recognition will not. This is because Facial Recognition uses the _outputs_ of these models that have already been saved to the database. As such, its processing is between the server container and the database. @@ -14,7 +14,7 @@ Image previews are sent to the remote machine learning container. Use this optio 2. Copy the following `docker-compose.yml` to the remote server :::info -If using hardware acceleration, the [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be added and the `docker-compose.yml` needs to be configured as described in the [hardware acceleration documentation](/docs/features/ml-hardware-acceleration) +If using hardware acceleration, the [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be added and the `docker-compose.yml` needs to be configured as described in the [hardware acceleration documentation](/features/ml-hardware-acceleration) ::: ```yaml diff --git a/docs/docs/guides/template-backup-script.md b/docs/docs/guides/template-backup-script.md index 34381dd0ee..19647d4ae1 100644 --- a/docs/docs/guides/template-backup-script.md +++ b/docs/docs/guides/template-backup-script.md @@ -7,7 +7,7 @@ This script assumes you have a second hard drive connected to your server for on The database is saved to your Immich upload folder in the `database-backup` subdirectory. The database is then backed up and versioned with your assets by Borg. This ensures that the database backup is in sync with your assets in every snapshot. :::info -This script makes backups of your database along with your photo/video library. This is redundant with the [automatic database backup tool](https://immich.app/docs/administration/backup-and-restore#automatic-database-backups) built into Immich. Using this script to backup your database has two advantages over the built-in backup tool: +This script makes backups of your database along with your photo/video library. This is redundant with the [automatic database backup tool](/administration/backup-and-restore#automatic-database-dumps) built into Immich. Using this script to backup your database has two advantages over the built-in backup tool: - This script uses storage more efficiently by versioning your backups instead of making multiple copies. - The database backups are performed at the same time as the library backup, ensuring that the backups of your database and the library are always in sync. diff --git a/docs/docs/install/config-file.md b/docs/docs/install/config-file.md index 54d7c61bb3..3fb0687e4a 100644 --- a/docs/docs/install/config-file.md +++ b/docs/docs/install/config-file.md @@ -209,7 +209,7 @@ So you can just grab it from there, paste it into a file and you're pretty much ### Step 2 - Specify the file location In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config. -For more information, refer to the [Environment Variables](/docs/install/environment-variables.md) section. +For more information, refer to the [Environment Variables](/install/environment-variables.md) section. :::tip YAML-formatted config files are also supported. diff --git a/docs/docs/install/docker-compose.mdx b/docs/docs/install/docker-compose.mdx index 7a0b566f5d..46b144eb4a 100644 --- a/docs/docs/install/docker-compose.mdx +++ b/docs/docs/install/docker-compose.mdx @@ -29,4 +29,4 @@ If you get an error `can't set healthcheck.start_interval as feature require Doc ## Next Steps -Read the [Post Installation](/docs/install/post-install.mdx) steps and [upgrade instructions](/docs/install/upgrading.md). +Read the [Post Installation](/install/post-install.mdx) steps and [upgrade instructions](/install/upgrading.md). diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index 8d5ab55049..e606d03dee 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -29,29 +29,26 @@ These environment variables are used by the `docker-compose.yml` file and do **N ## General -| Variable | Description | Default | Containers | Workers | -| :---------------------------------- | :---------------------------------------------------------------------------------------- | :---------------------------------: | :----------------------- | :----------------- | -| `TZ` | Timezone | \*1 | server | microservices | -| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | -| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container âš ī¸**You probably shouldn't set this**\*2âš ī¸ | `/usr/src/app/upload`\*3 | server | api, microservices | -| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | -| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | -| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | -| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | -| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | -| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | -| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | -| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/docs/administration/system-integrity) | | server | api, microservices | +| Variable | Description | Default | Containers | Workers | +| :---------------------------------- | :---------------------------------------------------------------------------------------- | :--------------------------: | :----------------------- | :----------------- | +| `TZ` | Timezone | \*1 | server | microservices | +| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | +| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container âš ī¸**You probably shouldn't set this**\*2âš ī¸ | `/data` | server | api, microservices | +| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | +| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | +| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | +| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | +| `IMMICH_MICROSERVICES_METRICS_PORT` | Port for the OTEL metrics | `8082` | server | microservices | +| `IMMICH_PROCESS_INVALID_IMAGES` | When `true`, generate thumbnails for invalid images | | server | microservices | +| `IMMICH_TRUSTED_PROXIES` | List of comma-separated IPs set as trusted proxies | | server | api | +| `IMMICH_IGNORE_MOUNT_CHECK_ERRORS` | See [System Integrity](/administration/system-integrity) | | server | api, microservices | \*1: `TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is used by `exiftool` as a fallback in case the timezone cannot be determined from the image metadata. It is also used for logfile timestamps and cron job execution. \*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead. -\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`. -It only needs to be set if the Immich deployment method is changing. - ## Workers | Variable | Description | Default | Containers | @@ -60,7 +57,7 @@ It only needs to be set if the Immich deployment method is changing. | `IMMICH_WORKERS_EXCLUDE` | Do not run these workers. Matches against default workers, or `IMMICH_WORKERS_INCLUDE` if specified. | | server | :::info -Information on the current workers can be found [here](/docs/administration/jobs-workers). +Information on the current workers can be found [here](/administration/jobs-workers). ::: ## Ports @@ -172,8 +169,6 @@ Redis (Sentinel) URL example JSON before encoding: | `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning | | `MACHINE_LEARNING_DEVICE_IDS`\*4 | Device IDs to use in multi-GPU environments | `0` | machine learning | | `MACHINE_LEARNING_MAX_BATCH_SIZE__FACIAL_RECOGNITION` | Set the maximum number of faces that will be processed at once by the facial recognition model | None (`1` if using OpenVINO) | machine learning | -| `MACHINE_LEARNING_PING_TIMEOUT` | How long (ms) to wait for a PING response when checking if an ML server is available | `2000` | server | -| `MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME` | How long to ignore ML servers that are offline before trying again | `30000` | server | | `MACHINE_LEARNING_RKNN` | Enable RKNN hardware acceleration if supported | `True` | machine learning | | `MACHINE_LEARNING_RKNN_THREADS` | How many threads of RKNN runtime should be spinned up while inferencing. | `1` | machine learning | @@ -202,12 +197,11 @@ Additional machine learning parameters can be tuned from the admin UI. | `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all | | server | api, microservices | | `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices | -## Docker Secrets +## Secrets -The following variables support the use of [Docker secrets][docker-secrets] for additional security. +The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security. -To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of -the `_FILE` variable should be set to the path of a file containing the variable value. +To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the “regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value. | Regular Variable | Equivalent Docker Secrets '\_FILE' Variable | | :----------------- | :------------------------------------------ | @@ -229,3 +223,4 @@ to use a Docker secret for the password in the Redis container. [docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets [docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ [ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis +[systemd-creds]: https://systemd.io/CREDENTIALS/ diff --git a/docs/docs/install/img/truenas/truenas00.webp b/docs/docs/install/img/truenas/truenas00.webp new file mode 100644 index 0000000000..4f3db057b0 Binary files /dev/null and b/docs/docs/install/img/truenas/truenas00.webp differ diff --git a/docs/docs/install/img/truenas01.webp b/docs/docs/install/img/truenas/truenas01.webp similarity index 100% rename from docs/docs/install/img/truenas01.webp rename to docs/docs/install/img/truenas/truenas01.webp diff --git a/docs/docs/install/img/truenas02.webp b/docs/docs/install/img/truenas/truenas02.webp similarity index 100% rename from docs/docs/install/img/truenas02.webp rename to docs/docs/install/img/truenas/truenas02.webp diff --git a/docs/docs/install/img/truenas03.webp b/docs/docs/install/img/truenas/truenas03.webp similarity index 100% rename from docs/docs/install/img/truenas03.webp rename to docs/docs/install/img/truenas/truenas03.webp diff --git a/docs/docs/install/img/truenas/truenas04.webp b/docs/docs/install/img/truenas/truenas04.webp new file mode 100644 index 0000000000..73ca9ee727 Binary files /dev/null and b/docs/docs/install/img/truenas/truenas04.webp differ diff --git a/docs/docs/install/img/truenas11.webp b/docs/docs/install/img/truenas/truenas05.webp similarity index 100% rename from docs/docs/install/img/truenas11.webp rename to docs/docs/install/img/truenas/truenas05.webp diff --git a/docs/docs/install/img/truenas/truenas06.webp b/docs/docs/install/img/truenas/truenas06.webp new file mode 100644 index 0000000000..f13e04f424 Binary files /dev/null and b/docs/docs/install/img/truenas/truenas06.webp differ diff --git a/docs/docs/install/img/truenas/truenas07.webp b/docs/docs/install/img/truenas/truenas07.webp new file mode 100644 index 0000000000..24cceb7faa Binary files /dev/null and b/docs/docs/install/img/truenas/truenas07.webp differ diff --git a/docs/docs/install/img/truenas/truenas08.webp b/docs/docs/install/img/truenas/truenas08.webp new file mode 100644 index 0000000000..6956eba48b Binary files /dev/null and b/docs/docs/install/img/truenas/truenas08.webp differ diff --git a/docs/docs/install/img/truenas/truenas09.webp b/docs/docs/install/img/truenas/truenas09.webp new file mode 100644 index 0000000000..5ed2ba0182 Binary files /dev/null and b/docs/docs/install/img/truenas/truenas09.webp differ diff --git a/docs/docs/install/img/truenas/truenas10.webp b/docs/docs/install/img/truenas/truenas10.webp new file mode 100644 index 0000000000..21a8b2588a Binary files /dev/null and b/docs/docs/install/img/truenas/truenas10.webp differ diff --git a/docs/docs/install/img/truenas09.webp b/docs/docs/install/img/truenas/truenas11.webp similarity index 100% rename from docs/docs/install/img/truenas09.webp rename to docs/docs/install/img/truenas/truenas11.webp diff --git a/docs/docs/install/img/truenas04.webp b/docs/docs/install/img/truenas/truenas12.webp similarity index 100% rename from docs/docs/install/img/truenas04.webp rename to docs/docs/install/img/truenas/truenas12.webp diff --git a/docs/docs/install/img/truenas05.webp b/docs/docs/install/img/truenas05.webp deleted file mode 100644 index c5e451770f..0000000000 Binary files a/docs/docs/install/img/truenas05.webp and /dev/null differ diff --git a/docs/docs/install/img/truenas06.webp b/docs/docs/install/img/truenas06.webp deleted file mode 100644 index b7c7c51aa4..0000000000 Binary files a/docs/docs/install/img/truenas06.webp and /dev/null differ diff --git a/docs/docs/install/img/truenas07.webp b/docs/docs/install/img/truenas07.webp deleted file mode 100644 index c08e2bdd9c..0000000000 Binary files a/docs/docs/install/img/truenas07.webp and /dev/null differ diff --git a/docs/docs/install/img/truenas08.webp b/docs/docs/install/img/truenas08.webp deleted file mode 100644 index c5a3a87b47..0000000000 Binary files a/docs/docs/install/img/truenas08.webp and /dev/null differ diff --git a/docs/docs/install/img/truenas10.webp b/docs/docs/install/img/truenas10.webp deleted file mode 100644 index 21668cfccd..0000000000 Binary files a/docs/docs/install/img/truenas10.webp and /dev/null differ diff --git a/docs/docs/install/img/truenas12.webp b/docs/docs/install/img/truenas12.webp deleted file mode 100644 index 1ed1e54c46..0000000000 Binary files a/docs/docs/install/img/truenas12.webp and /dev/null differ diff --git a/docs/docs/install/one-click.md b/docs/docs/install/one-click.md new file mode 100644 index 0000000000..53fcb20d21 --- /dev/null +++ b/docs/docs/install/one-click.md @@ -0,0 +1,32 @@ +--- +sidebar_position: 65 +--- + +# One-Click [Cloud Service] + +:::note +This version of Immich is provided via cloud service providers' one-click marketplaces. Hosting costs are set by the cloud service providers. +Support for these are provided by the individual cloud service providers. + +**Please report issues to the corresponding [Github Repository][github].** +::: + +## Installation + +Go to the provider's marketplace and choose Immich, then follow the provided instructions. + +## One-Click Immich marketplace providers + +### DigitalOcean + +https://marketplace.digitalocean.com/apps/immich + +### Vultr + +https://www.vultr.com/marketplace/apps/immich + +## Issues + +For issues, open an issue on the associated [GitHub Repository][github]. + +[github]: https://github.com/immich-app/immich/ diff --git a/docs/docs/install/portainer.md b/docs/docs/install/portainer.md index 916d89a0d5..07fd255292 100644 --- a/docs/docs/install/portainer.md +++ b/docs/docs/install/portainer.md @@ -45,5 +45,5 @@ alt="Dot Env Example" 11. Click on "**Deploy the stack**". :::tip -For more information on how to use the application, please refer to the [Post Installation](/docs/install/post-install.mdx) guide. +For more information on how to use the application, please refer to the [Post Installation](/install/post-install.mdx) guide. ::: diff --git a/docs/docs/install/post-install.mdx b/docs/docs/install/post-install.mdx index 636274aaea..b30e91f3cd 100644 --- a/docs/docs/install/post-install.mdx +++ b/docs/docs/install/post-install.mdx @@ -44,6 +44,6 @@ A list of common steps to take after installing Immich include: ## Setting up optional features -- [External Libraries](/docs/features/libraries.md): Adding your existing photo library to Immich -- [Hardware Transcoding](/docs/features/hardware-transcoding.md): Speeding up video transcoding -- [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich +- [External Libraries](/features/libraries.md): Adding your existing photo library to Immich +- [Hardware Transcoding](/features/hardware-transcoding.md): Speeding up video transcoding +- [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md): Speeding up various machine learning tasks in Immich diff --git a/docs/docs/install/script.md b/docs/docs/install/script.md index 93d1fb166c..ce05dc82d9 100644 --- a/docs/docs/install/script.md +++ b/docs/docs/install/script.md @@ -5,12 +5,12 @@ sidebar_position: 20 # Install script [Experimental] :::caution -This method is experimental and not currently recommended for production use. For production, please refer to installing with [Docker Compose](/docs/install/docker-compose.mdx). +This method is experimental and not currently recommended for production use. For production, please refer to installing with [Docker Compose](/install/docker-compose.mdx). ::: ## Requirements -Follow the [requirements page](/docs/install/requirements) to get started. +Follow the [requirements page](/install/requirements) to get started. The install script only supports Linux operating systems and requires Docker to be already installed on the system. @@ -32,5 +32,5 @@ The web application and mobile app will be available at `http:// + Updating Immich using Container Manager +Check the post installation and upgrade instructions at the links above before proceeding with this section. + +## Step 1. Backup + +Ensure your photos and videos are backed up. Your `.env` settings will define where they are stored. There is no need to delete any files or folders within the `docker` folder when doing a release upgrade unless instructed in the release notes. + +## Step 2. Check release notes + +Always check the [release notes](https://github.com/immich-app/immich/releases) before proceeding with an update! + +## Step 3. Stop containers & clean up + +Open **Container Manager**. Select **Project** then your Immich app + +![Select project](../../static/img/synology-select-proj.png) + +Select **Stop** + +![Stop project](../../static/img/synology-project-stop.png) + +Select **Action** then **Clean**. This removes the containers. + +![Clean project](../../static/img/synology-action-clean.png) + +Go to **Image** and select **Remove Unused Images**. + +![Remove unused](../../static/img/synology-remove-unused.png) + +## Step 4. Build + +Go to **Project**, select **Action** then **Build**. This will download, unpack, install and start the containers. + +![Build](../../static/img/synology-build.png) + +## Step 5. Update firewall rule + +The default behavior is to automatically start the containers once installed. If `immich_server` runs for a few seconds and then stops, it may be because the firewall rule no longer matches the server IP address. + +Go to the **Container** section. Click on `immich_server` and scroll down on **General** to find the IP address. +![Container IP](../../static/img/synology-container-ip.png) + +Go to Synology **Control Panel**. Select **Security** and **Firewall**. + +![Firewall](../../static/img/synology-fw-rules.png) + +In this example, the IP addresses mismatch and the firewall rule needs to be edited to match above. + +![Edit IP](../../static/img/synology-fw-ipedit.png) + +
diff --git a/docs/docs/install/truenas.md b/docs/docs/install/truenas.md index de110e00a1..9135b72fe6 100644 --- a/docs/docs/install/truenas.md +++ b/docs/docs/install/truenas.md @@ -2,6 +2,9 @@ sidebar_position: 80 --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # TrueNAS [Community] :::note @@ -9,214 +12,327 @@ This is a community contribution and not officially supported by the Immich team Community support can be found in the dedicated channel on the [Discord Server](https://discord.immich.app/). -**Please report app issues to the corresponding [Github Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** +**Please report app issues to the corresponding [GitHub Repository](https://github.com/truenas/apps/tree/master/trains/community/immich).** +::: + +:::warning +This guide covers the installation of Immich on TrueNAS Community Edition 24.10.2.2 (Electric Eel) and later. + +We recommend keeping TrueNAS Community Edition and Immich relatively up to date with the latest versions to avoid any issues. + +If you are using an older version of TrueNAS, we ask that you upgrade to the latest version before installing Immich. Check the [TrueNAS Community Edition Release Notes](https://www.truenas.com/docs/softwarereleases/) for more information on breaking changes, new features, and how to upgrade your system. ::: Immich can easily be installed on TrueNAS Community Edition via the **Community** train application. Consider reviewing the TrueNAS [Apps resources](https://apps.truenas.com/getting-started/) if you have not previously configured applications on your system. -TrueNAS Community Edition makes installing and updating Immich easy, but you must use the Immich web portal and mobile app to configure accounts and access libraries. - ## First Steps -The Immich app in TrueNAS Community Edition installs, completes the initial configuration, then starts the Immich web portal. -When updates become available, TrueNAS alerts and provides easy updates. - -Before installing the Immich app in TrueNAS, review the [Environment Variables](#environment-variables) documentation to see if you want to configure any during installation. -You may also configure environment variables at any time after deploying the application. - ### Setting up Storage Datasets Before beginning app installation, [create the datasets](https://www.truenas.com/docs/scale/scaletutorials/storage/datasets/datasetsscale/) to use in the **Storage Configuration** section during installation. -Immich requires seven datasets: `library`, `upload`, `thumbs`, `profile`, `video`, `backups`, and `pgData`. -You can organize these as one parent with seven child datasets, for example `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, and so on. + +In TrueNAS, Immich requires 2 datasets for the application to function correctly: `data` and `pgData`. You can set the datasets to any names to match your naming conventions or preferences. +You can organize these as one parent with two child datasets, for example `/mnt/tank/immich/data` and `/mnt/tank/immich/pgData`. Immich App Widget -:::info Permissions -The **pgData** dataset must be owned by the user `netdata` (UID 999) for postgres to start. The other datasets must be owned by the user `root` (UID 0) or a group that includes the user `root` (UID 0) for immich to have the necessary permissions. +:::info Datasets Permissions -If the **library** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/core/coretutorials/storage/pools/permissions/#access-control-lists) set to `Passthrough` if you plan on using a [storage template](/docs/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library**, Immich performs `chmod` internally and needs to be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) +The **pgData** dataset must be owned by the user `netdata` (UID 999) for Postgres to start. + +The `data` dataset must have given the **_modify_** permission to the user who will run Immich. + +Since TrueNAS Community Edition 24.10.2.2 and later, Immich can be run as any user and group, the default user being `apps` (UID 568) and the default group being `apps` (GID 568). This user, either `apps` or another user you choose, must have **_modify_** permissions on the **data** dataset. + +For an easy setup: + +- Create the parent dataset `immich` keeping the default **Generic** preset. +- Select `Dataset Preset` **Apps** instead of **Generic** when creating the `data` dataset. This will automatically give the correct permissions to the dataset. If you want to use another user for Immich, you can keep the **Generic** preset, but you will need to give the **_modify_** permission to that other user. +- For the `pgData` dataset, you can keep the default preset **Generic** as permissions can be set during the installation of the Immich app (See [Storage Configuration](#storage-configuration) section). + ::: + +:::tip +To improve performance, Immich recommends using SSDs for the database. If you have a pool made of SSDs, you can create the `pgData` dataset on that pool. + +Thumbnails can also be stored on the SSDs for faster access. This is an advanced option and not required for Immich to run. More information on how you can use multiple datasets to manage Immich storage in a finer-grained manner can be found in the [Additional Storage: Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section below. +::: + +:::warning +If you just created the datasets using the **Apps** preset, you can skip this warning section. + +If the **data** dataset uses ACL it must have [ACL mode](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/) set to `Passthrough` if you plan on using a [storage template](/administration/storage-template.mdx) and the dataset is configured for network sharing (its ACL type is set to `SMB/NFSv4`). When the template is applied and files need to be moved from **upload** to **library** (internal folder created by Immich within the **data** dataset), Immich performs `chmod` internally and must be allowed to execute the command. [More info.](https://github.com/immich-app/immich/pull/13017) + +To change or verify the ACL mode, go to the **Datasets** screen, select the **library** dataset, click on the **Edit** button next to **Dataset Details**, then click on the **Advanced Options** tab, scroll down to the **ACL Mode** section, and select `Passthrough` from the dropdown menu. Click **Save** to apply the changes. If the option is greyed out, set the **ACL Type** to `SMB/NFSv4` first, then you can change the **ACL Mode** to `Passthrough`. ::: ## Installing the Immich Application -To install the **Immich** application, go to **Apps**, click **Discover Apps**, either begin typing Immich into the search field or scroll down to locate the **Immich** application widget. +To install the **Immich** application, go to **Apps**, click **Discover Apps**, and either begin typing Immich into the search field or scroll down to locate the **Immich** application widget. +
+ +Click on the widget to open the **Immich** application details screen. Immich App Widget -Click on the widget to open the **Immich** application details screen. +
-

+
+Click **Install** to open the Immich application configuration screen. Immich App Details Screen -Click **Install** to open the Immich application configuration screen. - -

+
Application configuration settings are presented in several sections, each explained below. -To find specific fields click in the **Search Input Fields** search field, scroll down to a particular section or click on the section heading on the navigation area in the upper-right corner. +To find specific fields, click in the **Search Input Fields** search field, scroll down to a particular section, or click on the section heading on the navigation area in the upper-right corner. ### Application Name and Version Install Immich Screen -Accept the default value or enter a name in **Application Name** field. -In most cases use the default name, but if adding a second deployment of the application you must change this name. +Keep the default value or enter a name in the **Application Name** field. +Change it if you’re deploying a second instance. -Accept the default version number in **Version**. -When a new version becomes available, the application has an update badge. -The **Installed Applications** screen shows the option to update applications. +Immich version within TrueNAS catalog (Different from Immich release version). ### Immich Configuration Configuration Settings + +The **Timezone** is set to the system default, which usually matches your local timezone. You can change it to another timezone if you prefer. + +**Enable Machine Learning** is enabled by default. It allows Immich to use machine learning features such as face recognition, image search, and smart duplicate detection. Untick this option if you do not want to use these features. + +Select the **Machine Learning Image Type** based on the hardware you have. More details here: [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md) + +**Database Password** should be set to a custom value using only the characters `A-Za-z0-9`. This password is used to secure the Postgres database. + +**Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. Preferably, use a different password from the database password. + +Keep the **Log Level** to the default `Log` value. + +Leave **Hugging Face Endpoint** blank. (This is used to download ML models from a different source.) + +Set **Database Storage Type** to the type of storage (**HDD** or **SSD**) that the pool where the **pgData** dataset is located uses. + +**Additional Environment Variables** can be left blank. + +
+Advanced users: Adding Environment Variables + +Environment variables can be set by clicking the **Add** button and filling in the **Name** and **Value** fields. + + -Accept the default value in **Timezone** or change to match your local timezone. -**Timezone** is only used by the Immich `exiftool` microservice if it cannot be determined from the image metadata. +These are used to add custom configuration options or to enable specific features. +More information on available environment variables can be found in the **[environment variables documentation](/install/environment-variables/)**. -Untick **Enable Machine Learning** if you will not use face recognition, image search, and smart duplicate detection. +:::info +Some environment variables are not available for the TrueNAS Community Edition app as they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). -Accept the default option or select the **Machine Learning Image Type** for your hardware based on the [Hardware-Accelerated Machine Learning Supported Backends](/docs/features/ml-hardware-acceleration.md#supported-backends). +Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. +::: -Immich's default is `postgres` but you should consider setting the **Database Password** to a custom value using only the characters `A-Za-z0-9`. +
-The **Redis Password** should be set to a custom value using only the characters `A-Za-z0-9`. +### User and Group Configuration -Accept the **Log Level** default of **Log**. +Application in TrueNAS runs as a specific user and group. Immich uses the default user `apps` (UID 568) and the default group `apps` (GID 568). -Leave **Hugging Face Endpoint** blank. (This is for downloading ML models from a different source.) + -Leave **Additional Environment Variables** blank or see [Environment Variables](#environment-variables) to set before installing. +- **User ID**: Keep the default value `apps` (UID 568) or define a different one if needed. + +- **Group ID**: Keep the default value `apps` (GID 568) or define a different one if needed. + +:::warning +If you change the user or group, make sure that the datasets you created for Immich data storage have the correct permissions set for that user and group as specified in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above. +::: ### Network Configuration Networking Settings -Accept the default port `30041` in **WebUI Port** or enter a custom port number. -:::info Allowed Port Numbers -Only numbers within the range 9000-65535 may be used on TrueNAS versions below TrueNAS Community Edition 24.10 Electric Eel. +- **Port Bind Mode**: This lets you expose the port to the host system, allowing you to access Immich from outside the TrueNAS system. Keep the default **_Publish port on the host for external access_** value unless you have a specific reason to change it. -Regardless of version, to avoid port conflicts, don't use [ports on this list](https://www.truenas.com/docs/solutions/optimizations/security/#truenas-default-ports). -::: +- **Port Number**: Keep the default port `30041` or enter a custom port number. + +- **Host IPs**: Leave the default blank value. ### Storage Configuration -Immich requires seven storage datasets. - - - -:::note Default Setting (Not recommended) -The default setting for datasets is **ixVolume (dataset created automatically by the system)** but this results in your data being harder to access manually and can result in data loss if you delete the immich app. (Not recommended) +:::danger Default Settings (Not recommended) +The default setting for datasets is **ixVolume (dataset created automatically by the system)**. This is not recommended as this results in your data being harder to access manually and can result in data loss if you delete the immich app. It is also harder to manage snapshots and replication tasks. It is recommended to use the **Host Path (Path that already exists on the system)** option instead. ::: -For each Storage option select **Host Path (Path that already exists on the system)** and then select the matching dataset [created before installing the app](#setting-up-storage-datasets): **Immich Library Storage**: `library`, **Immich Uploads Storage**: `upload`, **Immich Thumbs Storage**: `thumbs`, **Immich Profile Storage**: `profile`, **Immich Video Storage**: `video`, **Immich Backups Storage**: `backups`, **Postgres Data Storage**: `pgData`. +The storage configuration section allows you to set up the storage locations for Immich data. You can select the datasets created in the previous step. -The image above has example values. -
+For the Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Immich data storage, for example, `data`. -### Additional Storage [(External Libraries)](/docs/features/libraries) +The Machine Learning cache can be left with default _Temporary_ + +For the Postgres Data Storage, select **Host Path (Path that already exists on the system)** and then select the dataset you created for Postgres data storage, for example, `pgData`. + +:::info +**Postgres Data Storage** +Once **Host Path** is selected, a checkbox appears with **_Automatic Permissions_**. If you have not set the ownership of the **pgData** dataset to `netdata` (UID 999), tick this box as it will set the user ownership to `netdata` (UID 999) and the group ownership to `docker` (GID 999) automatically. If you have set the ownership of the **pgData** dataset to `netdata` (UID 999), you can leave this box unticked. +::: + +### Additional Storage (Advanced Users) + +
+External Libraries :::danger Advanced Users Only -This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. Also, your mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. The picture below shows a valid example. +This feature should only be used by advanced users. If this is your first time installing Immich, then DO NOT mount an external library until you have a working setup. ::: -You may configure [External Libraries](/docs/features/libraries) by mounting them using **Additional Storage**. -The **Mount Path** is the location you will need to copy and paste into the External Library settings within Immich. -The **Host Path** is the location on the TrueNAS Community Edition server where your external library is located. +You may configure [external libraries](/features/libraries) by mounting them using **Additional Storage**. - +The dataset that contains your external library files must at least give **read** access to the user running Immich (Default: `apps` (UID 568), `apps` (GID 568)). +If you want to be able to delete files or edit metadata in the external library using Immich, you will need to give the **modify** permission to the user running Immich. + +- **Mount Path** is the location you will need to copy and paste into the external library settings within Immich. +- **Host Path** is the location on the TrueNAS Community Edition server where your external library is located. +- **Read Only** is a checkbox that you can tick if you want to prevent Immich from modifying the files in the external library. This is useful if you want to use Immich to view and search your external library without modifying it. + +:::warning +Each mount path MUST be something unique and should NOT be your library or upload location or a Linux directory like `/lib`. + +A general recommendation is to mount any external libraries to a path beginning with `/mnt` or `/media` followed by a unique name, such as `/mnt/external-libraries` or `/media/my-external-libraries`. If you plan to mount multiple external libraries, you can use paths like `/mnt/external-libraries/library1`, `/mnt/external-libraries/library2`, etc. +::: + +
+ +
+Multiple Datasets for Immich Storage + +:::danger Advanced Users Only +This feature should only be used by advanced users. +::: + +Immich can use multiple datasets for its storage, allowing you to manage your data more granularly, similar to the old storage configuration. This is useful if you want to separate your data into different datasets for performance or organizational reasons. There is a general guide for this [here](/guides/custom-locations), but read on for the TrueNAS guide. + +Each additional dataset has to give the permission **_modify_** to the user who will run Immich (Default: `apps` (UID 568), `apps` (GID 568)) +As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, you have to create the datasets with the **Apps** preset to ensure the correct permissions are set, or you can set the permissions manually after creating the datasets. + +Immich uses 6 folders for its storage: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. You can create a dataset for each of these folders or only for some of them. + +To mount these datasets: + +1. Add an **Additional Storage** entry for each dataset you want to use. +2. Select **Type** as **Host Path (Path that already exists on the system)**. +3. Enter the **Mount Path** with `/data/`. The `` is the name of the folder you want to mount, for example, `library`, `upload`, `thumbs`, `profile`, `encoded-video`, or `backups`. + :::danger Important + You have to write the full path, including `/data/`, as Immich expects the data to be in that location. + If you do not include this path, Immich will not be able to find the data and will not write the data to the location you specified. + ::: +4. Select the **Host Path** as the dataset you created for that folder, for example, `/mnt/tank/immich/library`, `/mnt/tank/immich/upload`, etc. + + + +
+ + ### Resources Configuration -Accept the default **CPU** limit of `2` threads or specify the number of threads (CPUs with Multi-/Hyper-threading have 2 threads per core). +- **CPU**: Depending on your system resources, you can keep the default value of `2` threads or specify a different number. Immich recommends at least `8` threads. -Specify the **Memory** limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. +- **Memory**: Limit in MB of RAM. Immich recommends at least 6000 MB (6GB). If you selected **Enable Machine Learning** in **Immich Configuration**, you should probably set this above 8000 MB. -:::info Older TrueNAS Versions -Before TrueNAS Community Edition version 24.10 Electric Eel: +Both **CPU** and **Memory** are limits, not reservations. This means that Immich can use up to the specified amount of CPU threads and RAM, but it will not reserve that amount of resources at all times. The system will allocate resources as needed, and Immich will use less than the specified amount most of the time. -The **CPU** value was specified in a different format with a default of `4000m` which is 4 threads. +- Enable **GPU Configuration** options if you have a GPU or CPU with integrated graphics that you will use for [Hardware Transcoding](/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/features/ml-hardware-acceleration.md). -The **Memory** value was specified in a different format with a default of `8Gi` which is 8 GiB of RAM. The value was specified in bytes or a number with a measurement suffix. Examples: `129M`, `123Mi`, `1000000000` -::: - -Enable **GPU Configuration** options if you have a GPU that you will use for [Hardware Transcoding](/docs/features/hardware-transcoding) and/or [Hardware-Accelerated Machine Learning](/docs/features/ml-hardware-acceleration.md). More info: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) +The process for NVIDIA GPU passthrough requires additional steps. +More details here: [GPU Passthrough Docs for TrueNAS Apps](https://apps.truenas.com/managing-apps/installing-apps/#gpu-passthrough) ### Install Finally, click **Install**. The system opens the **Installed Applications** screen with the Immich app in the **Deploying** state. -When the installation completes it changes to **Running**. +When the installation completes, it changes to **Running**. Immich Installed -Click **Web Portal** on the **Application Info** widget to open the Immich web interface to set up your account and begin uploading photos. +Click **Web Portal** on the **Application Info** widget, or go to the URL `http://:30041` in your web browser to open the Immich web interface. This will show you the onboarding process to set up your first user account, which will be an administrator account. + +After that, you can start using Immich to upload and manage your photos and videos. :::tip -For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. +For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide. ::: ## Edit App Settings @@ -228,27 +344,10 @@ For more information on how to use the application once installed, please refer - Click **Update** at the very bottom of the page to save changes. - TrueNAS automatically updates, recreates, and redeploys the Immich container with the updated settings. -## Environment Variables - -You can set [Environment Variables](/docs/install/environment-variables) by clicking **Add** on the **Additional Environment Variables** option and filling in the **Name** and **Value**. - - - -:::info -Some Environment Variables are not available for the TrueNAS Community Edition app. This is mainly because they can be configured through GUI options in the [Edit Immich screen](#edit-app-settings). - -Some examples are: `IMMICH_VERSION`, `UPLOAD_LOCATION`, `DB_DATA_LOCATION`, `TZ`, `IMMICH_LOG_LEVEL`, `DB_PASSWORD`, `REDIS_PASSWORD`. -::: - ## Updating the App :::danger -Make sure to read the general [upgrade instructions](/docs/install/upgrading.md). +Make sure to read the general [upgrade instructions](/install/upgrading.md). ::: When updates become available, TrueNAS alerts and provides easy updates. @@ -261,3 +360,123 @@ To update the app to the latest version: - You may view the Changelog. - Click **Upgrade** to begin the process and open a counter dialog that shows the upgrade progress. - When complete, the update badge and buttons disappear and the application Update state on the Installed screen changes from Update Available to Up to date. + +## Migration + +:::danger +Perform a backup of your Immich data before proceeding with the migration steps below. This is crucial to prevent any data loss if something goes wrong during the migration process. + +The migration should also be performed when the Immich app is not running to ensure no data is being written while you are copying the data. +::: + +### Migration from Old Storage Configuration + +There are two ways to migrate from the old storage configuration to the new one, depending on whether you want to keep the old multiple datasets or if you want to move to a double dataset configuration with a single dataset for Immich data storage and a single dataset for Postgres data storage. + +:::note Old TrueNAS Versions Permissions +If you were using an older version of TrueNAS (before 24.10.2.2), the datasets, except the one for **pgData** had only to be owned by the `root` user (UID 0). You might have to add the **modify** permission to the `apps` user (UID 568) or the user you want to run Immich as, to all of them, except **pgData**. The steps to add or change ACL permissions are described in the [TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/datasets/permissionsscale/). +::: + + + + +To migrate from the old storage configuration to the new one, you will need to create a new dataset for the Immich data storage and copy the data from the old datasets to the new ones. The steps are as follows: + +1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are copying the data. +2. **Create a new dataset** for the Immich data storage, for example, `data`. As described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, create the dataset with the **Apps** preset to ensure the correct permissions are set. +3. **Copy the data** from the old datasets to the new dataset. We advise using the `rsync` command to copy the data, as it will preserve the permissions and ownership of the files. The following commands are examples: + +```bash +sudo rsync -av /mnt/tank/immich/library/ /mnt/tank/immich/data/library/ +sudo rsync -av /mnt/tank/immich/upload/ /mnt/tank/immich/data/upload/ +sudo rsync -av /mnt/tank/immich/thumbs/ /mnt/tank/immich/data/thumbs/ +sudo rsync -av /mnt/tank/immich/profile/ /mnt/tank/immich/data/profile/ +sudo rsync -av /mnt/tank/immich/video/ /mnt/tank/immich/data/encoded-video/ +sudo rsync -av /mnt/tank/immich/backups/ /mnt/tank/immich/data/backups/ +``` + +Make sure to replace `/mnt/tank/immich/` with the correct path to your old datasets and `/mnt/tank/immich/data/` with the correct path to your new dataset. + +:::tip +If you were using **ixVolume (dataset created automatically by the system)** for some of Immich data storage, the path to the data should be `/mnt/.ix-apps/app_mounts/immich/`. You have to use this path instead of `/mnt/tank/immich/` in the `rsync` command above, for example: + +```bash +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/library/ /mnt/tank/immich/data/library/ +``` + +If you also were storing your files in the **ixVolume**, the **_upload_** folder is named `uploads` instead of `upload`, so the command to run should be: + +```bash +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/uploads/ /mnt/tank/immich/data/upload/ +``` + +This means that depending on your old storage configuration, you might have to use a mix of paths in the `rsync` commands above. + +If you were also using an ixVolume for Postgres data storage, you also should, first create the pgData dataset, as described in the [Setting up Storage Datasets](#setting-up-storage-datasets) section above, and then you can use the following command to copy the Postgres data: + +```bash +sudo rsync -av /mnt/.ix-apps/app_mounts/immich/pgData/ /mnt/tank/immich/pgData/ +``` + +::: + +:::warning +Make sure that for each folder, the `.immich` file is copied as well, as it contains important metadata for Immich. If for some reason the `.immich` file is not copied, you can copy it manually with the `rsync` command, for example: + +```bash +sudo rsync -av /mnt/tank/immich/library/.immich /mnt/tank/immich/data/library/ +``` + +Replace `library` with the name of the folder where you are copying the file. +::: + +4. **Update the permissions** as the permissions of the data that have been copied has been preserved, to ensure that the `apps` user (UID 568) has the correct permissions on all the copied data. If you just created the dataset with the **Apps** preset, from the TrueNAS web interface, go to the **Datasets** screen, select the **data** dataset, click on the **Edit** button next to **Permissions**, tick the "Apply permissions recursively" checkbox, and click **Save**. This will apply the correct permissions to all the copied data. +5. **Update the Immich app** to use the new dataset: + - Go to the **Installed Applications** screen and select Immich from the list of installed applications. + - Click **Edit** on the **Application Info** widget. + - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. + - For the **Data Storage**, select **Host Path (Path that already exists on the system)** and then select the new dataset you created for Immich data storage, for example, `data`. + - For the **Postgres Data Storage**, verify that it is still set to the dataset you created for Postgres data storage, for example, `pgData`. + - Click **Update** at the bottom of the page to save changes. + +6. **Start the Immich app** from the TrueNAS web interface. + +This will recreate the Immich container with the new storage configuration and start the app. + +If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data has been copied correctly by checking the Immich web interface and ensuring that all your photos and videos are still available. You may delete the old datasets, if you no longer need them, using the TrueNAS web interface. + +:::tip +If you were using **ixVolume (dataset created automatically by the system)** or folders for Immich data storage, you can delete the old datasets using the following commands: + +```bash +sudo rm -r /mnt/.ix-apps/app_mounts/immich/* +``` + +::: + + + + +To migrate from the old storage configuration to the new one without creating new datasets. + +1. **Stop the Immich app** from the TrueNAS web interface to ensure no data is being written while you are updating the app. +2. **Update the datasets permissions**: Ensure that the datasets used for Immich data storage (`library`, `upload`, `thumbs`, `profile`, `video`, `backups`) have the correct permissions set for the user who will run Immich. The user should have **_modify_** permissions on these datasets. The default user for Immich is `apps` (UID 568) and the default group is `apps` (GID 568). If you are using a different user, make sure to set the permissions accordingly. You can do this from the TrueNAS web interface by going to the **Datasets** screen, selecting each dataset, clicking on the **Edit** button next to **Permissions**, and adding the user with **_modify_** permissions. +3. **Update the Immich app** to use the existing datasets: + - Go to the **Installed Applications** screen and select Immich from the list of installed applications. + - Click **Edit** on the **Application Info** widget. + - In the **Storage Configuration** section, untick the **Use Old Storage Configuration (Deprecated)** checkbox. + - For the **Data Storage**, you can keep the **ixVolume (dataset created automatically by the system)** as no data will be directly written to it. We recommend selecting **Host Path (Path that already exists on the system)** and then select a **new** dataset you created for Immich data storage, for example, `data`. + - For the **Postgres Data Storage**, keep **Host Path (Path that already exists on the system)** and then select the existing dataset you used for Postgres data storage, for example, `pgData`. + - Following the instructions in the [Multiple Datasets for Immich Storage](#additional-storage-advanced-users) section, you can add, **for each old dataset**, a new Additional Storage with the following settings: + - **Type**: `Host Path (Path that already exists on the system)` + - **Mount Path**: `/data/` (e.g. `/data/library`) + - **Host Path**: `/mnt//` (e.g. `/mnt/tank/immich/library`) + :::danger Ensure using the correct paths names + Make sure to replace `` with the actual name of the folder used by Immich: `library`, `upload`, `thumbs`, `profile`, `encoded-video`, and `backups`. Also, replace `` and `` with the actual names of your pool and dataset. + ::: + - **Read Only**: Keep it unticked as Immich needs to write to these datasets. + - Click **Update** at the bottom of the page to save changes. +4. **Start the Immich app** from the TrueNAS web interface. This will recreate the Immich container with the new storage configuration and start the app. If everything went well, you should now be able to access Immich with the new storage configuration. You can verify that the data is still available by checking the Immich web interface and ensuring that all your photos and videos are still accessible. + + + diff --git a/docs/docs/install/unraid.md b/docs/docs/install/unraid.md index efb493f267..ca7263a1e8 100644 --- a/docs/docs/install/unraid.md +++ b/docs/docs/install/unraid.md @@ -125,13 +125,13 @@ alt="Go to Docker Tab and visit the address listed next to immich-web" :::tip -For more information on how to use the application once installed, please refer to the [Post Install](/docs/install/post-install.mdx) guide. +For more information on how to use the application once installed, please refer to the [Post Install](/install/post-install.mdx) guide. ::: ## Updating Steps :::danger -Make sure to read the general [upgrade instructions](/docs/install/upgrading.md). +Make sure to read the general [upgrade instructions](/install/upgrading.md). ::: Updating is extremely easy however it's important to be aware that containers managed via the Docker Compose Manager plugin do not integrate with Unraid's native dockerman UI, the label "_update ready_" will always be present on containers installed via the Docker Compose Manager. diff --git a/docs/docs/install/upgrading.md b/docs/docs/install/upgrading.md index 4425e23d68..da95222911 100644 --- a/docs/docs/install/upgrading.md +++ b/docs/docs/install/upgrading.md @@ -4,9 +4,7 @@ sidebar_position: 95 # Upgrading -:::danger Read the release notes -Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. You should read the release notes prior to updating and take special care when using automated tools like [Watchtower][watchtower]. - +:::tip Breaking changes You can see versions that had breaking changes [here][breaking]. ::: @@ -27,3 +25,102 @@ docker image prune [watchtower]: https://containrrr.dev/watchtower/ [breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Achangelog%3Abreaking-change+sort%3Adate_created [releases]: https://github.com/immich-app/immich/releases + +## Migrating to VectorChord + +:::info +If you deploy Immich using Docker Compose, see `ghcr.io/immich-app/postgres` in the `docker-compose.yml` file and have not explicitly set the `DB_VECTOR_EXTENSION` environmental variable, your Immich database is already using VectorChord and this section does not apply to you. +::: + +:::important +If you do not deploy Immich using Docker Compose and see a deprecation warning for pgvecto.rs on server startup, you should refer to the maintainers of the Immich distribution for guidance (if using a turnkey solution) or adapt the instructions for your specific setup. +::: + +Immich has migrated off of the deprecated pgvecto.rs database extension to its successor, [VectorChord](https://github.com/tensorchord/VectorChord), which comes with performance improvements in almost every aspect. This section will guide you on how to make this change in a Docker Compose setup. + +Before making any changes, please [back up your database](/administration/backup-and-restore). While every effort has been made to make this migration as smooth as possible, there’s always a chance that something can go wrong. + +After making a backup, please modify your `docker-compose.yml` file with the following information. + +```diff + [...] + + database: + container_name: immich_postgres +- image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:739cdd626151ff1f796dc95a6591b55a714f341c737e27f045019ceabf8e8c52 ++ image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_DB: ${DB_DATABASE_NAME} + POSTGRES_INITDB_ARGS: '--data-checksums' ++ # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs ++ # DB_STORAGE_TYPE: 'HDD' + volumes: + # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data +- healthcheck: +- test: >- +- pg_isready --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" || exit 1; +- Chksum="$$(psql --dbname="$${POSTGRES_DB}" --username="$${POSTGRES_USER}" --tuples-only --no-align +- --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; +- echo "checksum failure count is $$Chksum"; +- [ "$$Chksum" = '0' ] || exit 1 +- interval: 5m +- start_interval: 30s +- start_period: 5m +- command: >- +- postgres +- -c shared_preload_libraries=vectors.so +- -c 'search_path="$$user", public, vectors' +- -c logging_collector=on +- -c max_wal_size=2GB +- -c shared_buffers=512MB +- -c wal_compression=on ++ shm_size: 128mb + restart: always + + [...] +``` + +:::important +If you deviated from the defaults of pg14 or pgvectors0.2.0, you must adjust the pg major version and pgvecto.rs version. If you are still using the default `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` image, you can just follow the changes above. For example, if the previous image is `docker.io/tensorchord/pgvecto-rs:pg16-v0.3.0`, the new image should be `ghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.3.0` instead of the image specified in the diff. +::: + +After making these changes, you can start Immich as normal. Immich will make some changes to the DB during startup, which can take seconds to minutes to finish, depending on hardware and library size. In particular, it’s normal for the server logs to be seemingly stuck at `Reindexing clip_index` and `Reindexing face_index`for some time if you have over 100k assets in Immich and/or Immich is on a relatively weak server. If you see these logs and there are no errors, just give it time. + +:::danger +After switching to VectorChord, you should not downgrade Immich below 1.133.0. +::: + +Please don’t hesitate to contact us on [GitHub](https://github.com/immich-app/immich/discussions) or [Discord](https://discord.immich.app/) if you encounter migration issues. + +### VectorChord FAQ + +#### I have a separate PostgreSQL instance shared with multiple services. How can I switch to VectorChord? + +Please see the [standalone PostgreSQL documentation](/administration/postgres-standalone#migrating-to-vectorchord) for migration instructions. The migration path will be different depending on whether you’re currently using pgvecto.rs or pgvector, as well as whether Immich has superuser DB permissions. + +#### Why are so many lines removed from the `docker-compose.yml` file? Does this mean the health check is removed? + +These lines are now incorporated into the image itself along with some additional tuning. + +#### What does this change mean for my existing DB backups? + +The new DB image includes pgvector and pgvecto.rs in addition to VectorChord, so you can use this image to restore from existing backups that used either of these extensions. However, backups made after switching to VectorChord require an image containing VectorChord to restore successfully. + +#### Do I still need pgvecto.rs installed after migrating to VectorChord? + +pgvecto.rs only needs to be available during the migration, or if you need to restore from a backup that used pgvecto.rs. For a leaner DB and a smaller image, you can optionally switch to an image variant that doesn’t have pgvecto.rs installed after you’ve performed the migration and started Immich: `ghcr.io/immich-app/postgres:14-vectorchord0.4.3`, changing the PostgreSQL version as appropriate. + +#### Why does it matter whether my database is on an SSD or an HDD? + +These storage mediums have different performance characteristics. As a result, the optimal settings for an SSD are not the same as those for an HDD. Either configuration is compatible with SSD and HDD, but using the right configuration will make Immich snappier. As a general tip, we recommend users store the database on an SSD whenever possible. + +#### Can I use the new database image as a general PostgreSQL image outside of Immich? + +It’s a standard PostgreSQL container image that additionally contains the VectorChord, pgvector, and (optionally) pgvecto.rs extensions. If you were using the previous pgvecto.rs image for other purposes, you can similarly do so with this image. + +#### If pgvecto.rs and pgvector still work, why should I switch to VectorChord? + +VectorChord is faster, more stable, uses less RAM, and (with the settings Immich uses) offers higher-quality results than pgvector and pgvecto.rs. This translates to better search and facial recognition experiences. In addition, pgvecto.rs support will be dropped in the future, so changing it sooner will avoid disruption. diff --git a/docs/docs/overview/help.md b/docs/docs/overview/help.md index f38ecde168..e6523547fa 100644 --- a/docs/docs/overview/help.md +++ b/docs/docs/overview/help.md @@ -6,7 +6,7 @@ sidebar_position: 6 Running into an issue or have a question? Try the following: -1. Check the [FAQs](/docs/FAQ.mdx). +1. Check the [FAQs](/FAQ.mdx). 2. Read through the [Release Notes][github-releases]. 3. Search through existing [GitHub Issues][github-issues]. 4. Open a help ticket on [Discord][discord-link]. diff --git a/docs/docs/overview/img/social-preview-light.webp b/docs/docs/overview/img/social-preview-light.webp deleted file mode 100644 index 3d088f6522..0000000000 Binary files a/docs/docs/overview/img/social-preview-light.webp and /dev/null differ diff --git a/docs/docs/overview/quick-start.mdx b/docs/docs/overview/quick-start.mdx index 28cee15007..d80a194ad2 100644 --- a/docs/docs/overview/quick-start.mdx +++ b/docs/docs/overview/quick-start.mdx @@ -13,7 +13,7 @@ to install and use it. - A system with at least 4GB of RAM and 2 CPU cores. - [Docker](https://docs.docker.com/engine/install/) -> For a more detailed list of requirements, see the [requirements page](/docs/install/requirements). +> For a more detailed list of requirements, see the [requirements page](/install/requirements). --- @@ -61,7 +61,7 @@ import MobileAppBackup from '/docs/partials/_mobile-app-backup.md'; The backup time differs depending on how many photos are on your mobile device. Large uploads may take quite a while. -To quickly get going, you can selectively upload few photos first, by following this [guide](/docs/features/mobile-app#sync-only-selected-photos). +To quickly get going, you can selectively upload few photos first, by following this [guide](/features/mobile-app#sync-only-selected-photos). You can select the **Jobs** tab to see Immich processing your photos. @@ -72,7 +72,7 @@ You can select the **Jobs** tab to see Immich processing your photos. ## Review the database backup and restore process Immich has built-in database backups. You can refer to the -[database backup](/docs/administration/backup-and-restore) for more information. +[database backup](/administration/backup-and-restore) for more information. :::danger The database only contains metadata and user information. You must setup manual backups of the images and videos stored in `UPLOAD_LOCATION`. @@ -86,8 +86,8 @@ You may decide you'd like to install the server a different way; the Install cat You may decide you'd like to add the _rest_ of your photos from Google Photos, even those not on your mobile device, via Google Takeout. You can use [immich-go](https://github.com/simulot/immich-go) for this. -You may want to [upload photos from your own archive](/docs/features/command-line-interface). +You may want to [upload photos from your own archive](/features/command-line-interface). -You may want to incorporate a pre-existing archive of photos from an [External Library](/docs/features/libraries); there's a [guide](/docs/guides/external-library) for that. +You may want to incorporate a pre-existing archive of photos from an [External Library](/features/libraries); there's a [guide](/guides/external-library) for that. -You may want your mobile device to [back photos up to your server automatically](/docs/features/automatic-backup). +You may want your mobile device to [back photos up to your server automatically](/features/automatic-backup). diff --git a/docs/docs/overview/support-the-project.md b/docs/docs/overview/support-the-project.md index a439893a7e..ae24a3f1ce 100644 --- a/docs/docs/overview/support-the-project.md +++ b/docs/docs/overview/support-the-project.md @@ -10,11 +10,11 @@ By far the easiest way to help make Immich better it to use it and report issues ## Translations -Support the project by localizing on [Weblate](https://hosted.weblate.org/projects/immich/immich/). For more information, see the [Translations](/docs/developer/translations) section. +Support the project by localizing on [Weblate](https://hosted.weblate.org/projects/immich/immich/). For more information, see the [Translations](/developer/translations) section. ## Development -If you are a programmer or developer, take a look at Immich's [technology stack](/docs/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/docs/developer/architecture.mdx) section. +If you are a programmer or developer, take a look at Immich's [technology stack](/developer/architecture.mdx) and consider fixing bugs or building new features. The team and I are always looking for new contributors. For information about how to contribute as a developer, see the [Developer](/developer/architecture.mdx) section. ## Purchase Immich diff --git a/docs/docs/overview/welcome.mdx b/docs/docs/overview/welcome.mdx deleted file mode 100644 index 93ce705369..0000000000 --- a/docs/docs/overview/welcome.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Welcome to Immich - -Immich - Self-hosted photos and videos backup tool - -## Welcome! - -Hello, I am glad you are here. - -My name is Alex. I am an Electrical Engineer by schooling, then turned into a Software Engineer by trade and the pure love of problem solving. - -We were lying in bed with our newborn, and my wife said, "We are starting to accumulate a lot of photos and videos of our baby, and I don't want to pay for **_App-Which-Must-Not-Be-Named_** anymore. You always want to build something for me, so why don't you build me an app which can do that?" - -That was how the idea started to grow in my head. After that, I began to find existing solutions in the self-hosting space with similar backup functionality and the performance level of the **_App-Which-Must-Not-Be-Named_**. I found that the current solutions mainly focus on the gallery-type application. However, I want a simple-to-use backup tool with a native mobile app that can view photos and videos efficiently. So I set sail on this journey as a hungry engineer on the hunt. - -Another motivation that pushed me to deliver my execution of the **_App-Which-Must-Not-Be-Named_** alternative or replacement is for contributing back to the open source community that I have greatly benefited from over the years. - -I'm proud to share this creation with you, which values privacy, memories, and the joy of looking back at those moments in an easy-to-use and friendly interface. - -If you like the application or it helps you in some way, please consider [supporting](./support-the-project.md) the project. It will help me to continue to develop and maintain the application. diff --git a/docs/docs/partials/_server-backup.md b/docs/docs/partials/_server-backup.md index b9479600aa..34e09670e9 100644 --- a/docs/docs/partials/_server-backup.md +++ b/docs/docs/partials/_server-backup.md @@ -1,7 +1,6 @@ Now that you have imported some pictures, you should setup server backups to preserve your memories. -You can do so by following our [backup guide](/docs/administration/backup-and-restore.md). +You can do so by following our [backup guide](/administration/backup-and-restore.md). -:::danger -Immich is still under heavy development _and_ handles very important data. -It is essential that you set up good backups, and test them. +:::info +A 3-2-1 backup strategy is still crucial. The team has the responsibility to ensure that the application doesn’t cause loss of your precious memories; however, we cannot guarantee that hard drives will not fail, or an electrical event causes unexpected shutdown of your server/system, leading to data loss. Therefore, we still encourage users to follow best practices when safeguarding their data. Keep multiple copies of your most precious data: at least two local copies and one copy offsite in cold storage. ::: diff --git a/docs/docs/partials/_storage-template.md b/docs/docs/partials/_storage-template.md index 20e9caac43..84236e0ac1 100644 --- a/docs/docs/partials/_storage-template.md +++ b/docs/docs/partials/_storage-template.md @@ -1,7 +1,7 @@ -Immich allows the admin user to set the uploaded filename pattern at the directory and filename level as well as the [storage label for a user](/docs/administration/user-management/#set-storage-label-for-user). +Immich allows the admin user to set the uploaded filename pattern at the directory and filename level as well as the [storage label for a user](/administration/user-management/#set-storage-label-for-user). :::tip -You can read more about the differences between storage template engine on and off [here](/docs/administration/backup-and-restore#asset-types-and-storage-locations) +You can read more about the differences between storage template engine on and off [here](/administration/backup-and-restore#asset-types-and-storage-locations) ::: The admin user can set the template by using the template builder in the `Administration -> Settings -> Storage Template`. Immich provides a set of variables that you can use in constructing the template, along with additional custom text. If the template produces [multiple files with the same filename, they won't be overwritten](https://github.com/immich-app/immich/discussions/3324) as a sequence number is appended to the filename. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index d612dda253..70e0189a00 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -7,7 +7,7 @@ const prism = require('prism-react-renderer'); const config = { title: 'Immich', tagline: 'High performance self-hosted photo and video backup solution directly from your mobile phone', - url: 'https://immich.app', + url: 'https://docs.immich.app', baseUrl: '/', onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'warn', @@ -42,26 +42,19 @@ const config = { ], presets: [ [ - 'docusaurus-preset-openapi', - /** @type {import('docusaurus-preset-openapi').Options} */ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { showLastUpdateAuthor: true, showLastUpdateTime: true, + routeBasePath: '/', sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. // Remove this to remove the "edit this page" links. editUrl: 'https://github.com/immich-app/immich/tree/main/docs/', }, - api: { - path: '../open-api/immich-openapi-specs.json', - routeBasePath: '/docs/api', - }, - // blog: { - // showReadingTime: true, - // editUrl: "https://github.com/immich-app/immich/tree/main/docs/", - // }, theme: { customCss: require.resolve('./src/css/custom.css'), }, @@ -72,11 +65,6 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - announcementBar: { - id: 'site_announcement_immich', - content: `âš ī¸ The project is under very active development. Expect bugs and changes. Do not use it as the only way to store your photos and videos!`, - isCloseable: false, - }, docs: { sidebar: { autoCollapseCategories: false, @@ -95,17 +83,17 @@ const config = { position: 'right', }, { - to: '/docs/overview/welcome', + to: '/overview/quick-start', position: 'right', label: 'Docs', }, { - to: '/roadmap', + href: 'https://immich.app/roadmap', position: 'right', label: 'Roadmap', }, { - to: '/docs/api', + href: 'https://api.immich.app/', position: 'right', label: 'API', }, @@ -139,16 +127,16 @@ const config = { title: 'Overview', items: [ { - label: 'Welcome', - to: '/docs/overview/welcome', + label: 'Quick start', + to: '/overview/quick-start', }, { label: 'Installation', - to: '/docs/install/requirements', + to: '/install/requirements', }, { label: 'Contributing', - to: '/docs/overview/support-the-project', + to: '/overview/support-the-project', }, { label: 'Privacy Policy', @@ -161,15 +149,15 @@ const config = { items: [ { label: 'Roadmap', - to: '/roadmap', + href: 'https://immich.app/roadmap', }, { label: 'API', - to: '/docs/api', + href: 'https://api.immich.app/', }, { label: 'Cursed Knowledge', - to: '/cursed-knowledge', + href: 'https://immich.app/cursed-knowledge', }, ], }, diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 53ac997e3b..0000000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,20545 +0,0 @@ -{ - "name": "documentation", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "documentation", - "version": "0.0.0", - "dependencies": { - "@docusaurus/core": "~3.8.0", - "@docusaurus/preset-classic": "~3.8.0", - "@docusaurus/theme-common": "~3.8.0", - "@mdi/js": "^7.3.67", - "@mdi/react": "^1.6.1", - "@mdx-js/react": "^3.0.0", - "autoprefixer": "^10.4.17", - "classnames": "^2.3.2", - "clsx": "^2.0.0", - "docusaurus-lunr-search": "^3.3.2", - "docusaurus-preset-openapi": "^0.7.5", - "lunr": "^2.3.9", - "postcss": "^8.4.25", - "prism-react-renderer": "^2.3.1", - "raw-loader": "^4.0.2", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "tailwindcss": "^3.2.4", - "url": "^0.11.0" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "~3.8.0", - "@docusaurus/tsconfig": "^3.7.0", - "@docusaurus/types": "^3.7.0", - "prettier": "^3.2.4", - "typescript": "^5.1.6" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.9.tgz", - "integrity": "sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.17.9", - "@algolia/autocomplete-shared": "1.17.9" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.9.tgz", - "integrity": "sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.9" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.9.tgz", - "integrity": "sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.9" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.9.tgz", - "integrity": "sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==", - "license": "MIT", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/client-abtesting": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.23.4.tgz", - "integrity": "sha512-WIMT2Kxy+FFWXWQxIU8QgbTioL+SGE24zhpj0kipG4uQbzXwONaWt7ffaYLjfge3gcGSgJVv+1VlahVckafluQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.23.4.tgz", - "integrity": "sha512-4B9gChENsQA9kFmFlb+x3YhBz2Gx3vSsm81FHI1yJ3fn2zlxREHmfrjyqYoMunsU7BybT/o5Nb7ccCbm/vfseA==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.23.4.tgz", - "integrity": "sha512-bsj0lwU2ytiWLtl7sPunr+oLe+0YJql9FozJln5BnIiqfKOaseSDdV42060vUy+D4373f2XBI009K/rm2IXYMA==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-insights": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.23.4.tgz", - "integrity": "sha512-XSCtAYvJ/hnfDHfRVMbBH0dayR+2ofVZy3jf5qyifjguC6rwxDsSdQvXpT0QFVyG+h8UPGtDhMPoUIng4wIcZA==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.23.4.tgz", - "integrity": "sha512-l/0QvqgRFFOf7BnKSJ3myd1WbDr86ftVaa3PQwlsNh7IpIHmvVcT83Bi5zlORozVGMwaKfyPZo6O48PZELsOeA==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-query-suggestions": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.23.4.tgz", - "integrity": "sha512-TB0htrDgVacVGtPDyENoM6VIeYqR+pMsDovW94dfi2JoaRxfqu/tYmLpvgWcOknP6wLbr8bA+G7t/NiGksNAwQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-search": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.23.4.tgz", - "integrity": "sha512-uBGo6KwUP6z+u6HZWRui8UJClS7fgUIAiYd1prUqCbkzDiCngTOzxaJbEvrdkK0hGCQtnPDiuNhC5MhtVNN4Eg==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/events": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", - "license": "MIT" - }, - "node_modules/@algolia/ingestion": { - "version": "1.23.4", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.23.4.tgz", - "integrity": "sha512-Si6rFuGnSeEUPU9QchYvbknvEIyCRK7nkeaPVQdZpABU7m4V/tsiWdHmjVodtx3h20VZivJdHeQO9XbHxBOcCw==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/monitoring": { - "version": "1.23.4", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.23.4.tgz", - "integrity": "sha512-EXGoVVTshraqPJgr5cMd1fq7Jm71Ew6MpGCEaxI5PErBpJAmKdtjRIzs6JOGKHRaWLi+jdbJPYc2y8RN4qcx5Q==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.23.4.tgz", - "integrity": "sha512-1t6glwKVCkjvBNlng2itTf8fwaLSqkL4JaMENgR3WTGR8mmW2akocUy/ZYSQcG4TcR7qu4zW2UMGAwLoWoflgQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.23.4.tgz", - "integrity": "sha512-UUuizcgc5+VSY8hqzDFVdJ3Wcto03lpbFRGPgW12pHTlUQHUTADtIpIhkLLOZRCjXmCVhtr97Z+eR6LcRYXa3Q==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-fetch": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.23.4.tgz", - "integrity": "sha512-UhDg6elsek6NnV5z4VG1qMwR6vbp+rTMBEnl/v4hUyXQazU+CNdYkl++cpdmLwGI/7nXc28xtZiL90Es3I7viQ==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-node-http": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.23.4.tgz", - "integrity": "sha512-jXGzGBRUS0oywQwnaCA6mMDJO7LoC3dYSLsyNfIqxDR4SNGLhtg3je0Y31lc24OA4nYyKAYgVLtjfrpcpsWShg==", - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz", - "integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.27.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.0.tgz", - "integrity": "sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", - "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", - "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", - "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.0.tgz", - "integrity": "sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", - "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", - "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.26.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", - "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz", - "integrity": "sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", - "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", - "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", - "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", - "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz", - "integrity": "sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", - "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.26.5", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", - "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.0.tgz", - "integrity": "sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.0.tgz", - "integrity": "sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.27.0", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-syntax-typescript": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", - "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-transform-react-display-name": "^7.25.9", - "@babel/plugin-transform-react-jsx": "^7.25.9", - "@babel/plugin-transform-react-jsx-development": "^7.25.9", - "@babel/plugin-transform-react-pure-annotations": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.0.tgz", - "integrity": "sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-typescript": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.0.tgz", - "integrity": "sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew==", - "license": "MIT", - "dependencies": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", - "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", - "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.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "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==", - "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.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "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.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "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.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "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.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", - "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", - "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.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", - "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.10.tgz", - "integrity": "sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.10.tgz", - "integrity": "sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.0.tgz", - "integrity": "sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.6.tgz", - "integrity": "sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", - "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-4.0.0.tgz", - "integrity": "sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.10.tgz", - "integrity": "sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.10.tgz", - "integrity": "sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.10.tgz", - "integrity": "sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.2.tgz", - "integrity": "sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-initial": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz", - "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", - "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.9.tgz", - "integrity": "sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-float-and-clear": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-3.0.0.tgz", - "integrity": "sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overflow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overflow/-/postcss-logical-overflow-2.0.0.tgz", - "integrity": "sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-overscroll-behavior": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-overscroll-behavior/-/postcss-logical-overscroll-behavior-2.0.0.tgz", - "integrity": "sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-resize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-3.0.0.tgz", - "integrity": "sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", - "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", - "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", - "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-4.0.0.tgz", - "integrity": "sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.0.tgz", - "integrity": "sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.10.tgz", - "integrity": "sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", - "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-random-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", - "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.10.tgz", - "integrity": "sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-4.0.1.tgz", - "integrity": "sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", - "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", - "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.2.tgz", - "integrity": "sha512-8XvCRrFNseBSAGxeaVTaNijAu+FzUvjwFXtcrynmazGb/9WUdsPCpBX+mHEHShVRq47Gy4peYAoxYs8ltUnmzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", - "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-4.0.0.tgz", - "integrity": "sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/utilities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-2.0.0.tgz", - "integrity": "sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.9.0.tgz", - "integrity": "sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==", - "license": "MIT" - }, - "node_modules/@docsearch/react": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.9.0.tgz", - "integrity": "sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ==", - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-core": "1.17.9", - "@algolia/autocomplete-preset-algolia": "1.17.9", - "@docsearch/css": "3.9.0", - "algoliasearch": "^5.14.2" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 20.0.0", - "react": ">= 16.8.0 < 20.0.0", - "react-dom": ">= 16.8.0 < 20.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@docusaurus/babel": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.1.tgz", - "integrity": "sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.25.9", - "@babel/preset-env": "^7.25.9", - "@babel/preset-react": "^7.25.9", - "@babel/preset-typescript": "^7.25.9", - "@babel/runtime": "^7.25.9", - "@babel/runtime-corejs3": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "babel-plugin-dynamic-import-node": "^2.3.3", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/bundler": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.1.tgz", - "integrity": "sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.8.1", - "@docusaurus/cssnano-preset": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "babel-loader": "^9.2.1", - "clean-css": "^5.3.3", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.11.0", - "css-minimizer-webpack-plugin": "^5.0.1", - "cssnano": "^6.1.2", - "file-loader": "^6.2.0", - "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.2", - "null-loader": "^4.0.1", - "postcss": "^8.5.4", - "postcss-loader": "^7.3.4", - "postcss-preset-env": "^10.2.1", - "terser-webpack-plugin": "^5.3.9", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "webpack": "^5.95.0", - "webpackbar": "^6.0.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/faster": "*" - }, - "peerDependenciesMeta": { - "@docusaurus/faster": { - "optional": true - } - } - }, - "node_modules/@docusaurus/core": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.1.tgz", - "integrity": "sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.8.1", - "@docusaurus/bundler": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "boxen": "^6.2.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cli-table3": "^0.6.3", - "combine-promises": "^1.1.0", - "commander": "^5.1.0", - "core-js": "^3.31.1", - "detect-port": "^1.5.1", - "escape-html": "^1.0.3", - "eta": "^2.2.0", - "eval": "^0.1.8", - "execa": "5.1.1", - "fs-extra": "^11.1.1", - "html-tags": "^3.3.1", - "html-webpack-plugin": "^5.6.0", - "leven": "^3.1.0", - "lodash": "^4.17.21", - "open": "^8.4.0", - "p-map": "^4.0.0", - "prompts": "^2.4.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", - "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.3.4", - "react-router-config": "^5.1.1", - "react-router-dom": "^5.3.4", - "semver": "^7.5.4", - "serve-handler": "^6.1.6", - "tinypool": "^1.0.2", - "tslib": "^2.6.0", - "update-notifier": "^6.0.2", - "webpack": "^5.95.0", - "webpack-bundle-analyzer": "^4.10.2", - "webpack-dev-server": "^4.15.2", - "webpack-merge": "^6.0.1" - }, - "bin": { - "docusaurus": "bin/docusaurus.mjs" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@mdx-js/react": "^3.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/cssnano-preset": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.1.tgz", - "integrity": "sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==", - "license": "MIT", - "dependencies": { - "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.5.4", - "postcss-sort-media-queries": "^5.2.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/logger": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.1.tgz", - "integrity": "sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/mdx-loader": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz", - "integrity": "sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@mdx-js/mdx": "^3.0.0", - "@slorber/remark-comment": "^1.0.0", - "escape-html": "^1.0.3", - "estree-util-value-to-estree": "^3.0.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "image-size": "^2.0.2", - "mdast-util-mdx": "^3.0.0", - "mdast-util-to-string": "^4.0.0", - "rehype-raw": "^7.0.0", - "remark-directive": "^3.0.0", - "remark-emoji": "^4.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "stringify-object": "^3.3.0", - "tslib": "^2.6.0", - "unified": "^11.0.3", - "unist-util-visit": "^5.0.0", - "url-loader": "^4.1.1", - "vfile": "^6.0.1", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/module-type-aliases": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz", - "integrity": "sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==", - "license": "MIT", - "dependencies": { - "@docusaurus/types": "3.8.1", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "@types/react-router-dom": "*", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.8.1.tgz", - "integrity": "sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "cheerio": "1.0.0-rc.12", - "feed": "^4.2.2", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "srcset": "^4.0.0", - "tslib": "^2.6.0", - "unist-util-visit": "^5.0.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", - "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@types/react-router-config": "^5.0.7", - "combine-promises": "^1.1.0", - "fs-extra": "^11.1.1", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2", - "tslib": "^2.6.0", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.8.1.tgz", - "integrity": "sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "fs-extra": "^11.1.1", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.8.1.tgz", - "integrity": "sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/plugin-debug": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.8.1.tgz", - "integrity": "sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "fs-extra": "^11.1.1", - "react-json-view-lite": "^2.3.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.8.1.tgz", - "integrity": "sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.8.1.tgz", - "integrity": "sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@types/gtag.js": "^0.0.12", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.8.1.tgz", - "integrity": "sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.8.1.tgz", - "integrity": "sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "fs-extra": "^11.1.1", - "sitemap": "^7.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/plugin-svgr": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.8.1.tgz", - "integrity": "sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@svgr/core": "8.1.0", - "@svgr/webpack": "^8.1.0", - "tslib": "^2.6.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/preset-classic": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.8.1.tgz", - "integrity": "sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/plugin-content-blog": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/plugin-content-pages": "3.8.1", - "@docusaurus/plugin-css-cascade-layers": "3.8.1", - "@docusaurus/plugin-debug": "3.8.1", - "@docusaurus/plugin-google-analytics": "3.8.1", - "@docusaurus/plugin-google-gtag": "3.8.1", - "@docusaurus/plugin-google-tag-manager": "3.8.1", - "@docusaurus/plugin-sitemap": "3.8.1", - "@docusaurus/plugin-svgr": "3.8.1", - "@docusaurus/theme-classic": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-search-algolia": "3.8.1", - "@docusaurus/types": "3.8.1" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-classic": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.8.1.tgz", - "integrity": "sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/plugin-content-blog": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/plugin-content-pages": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-translations": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.45", - "lodash": "^4.17.21", - "nprogress": "^0.2.0", - "postcss": "^8.5.4", - "prism-react-renderer": "^2.3.0", - "prismjs": "^1.29.0", - "react-router-dom": "^5.3.4", - "rtlcss": "^4.1.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-common": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.1.tgz", - "integrity": "sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "3.8.1", - "@docusaurus/module-type-aliases": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router-config": "*", - "clsx": "^2.0.0", - "parse-numeric-range": "^1.3.0", - "prism-react-renderer": "^2.3.0", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "@docusaurus/plugin-content-docs": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.1.tgz", - "integrity": "sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==", - "license": "MIT", - "dependencies": { - "@docsearch/react": "^3.9.0", - "@docusaurus/core": "3.8.1", - "@docusaurus/logger": "3.8.1", - "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/theme-common": "3.8.1", - "@docusaurus/theme-translations": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-validation": "3.8.1", - "algoliasearch": "^5.17.1", - "algoliasearch-helper": "^3.22.6", - "clsx": "^2.0.0", - "eta": "^2.2.0", - "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "tslib": "^2.6.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/theme-translations": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.1.tgz", - "integrity": "sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==", - "license": "MIT", - "dependencies": { - "fs-extra": "^11.1.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/tsconfig": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.8.1.tgz", - "integrity": "sha512-XBWCcqhRHhkhfolnSolNL+N7gj3HVE3CoZVqnVjfsMzCoOsuQw2iCLxVVHtO+rePUUfouVZHURDgmqIySsF66A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docusaurus/types": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.1.tgz", - "integrity": "sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==", - "license": "MIT", - "dependencies": { - "@mdx-js/mdx": "^3.0.0", - "@types/history": "^4.7.11", - "@types/react": "*", - "commander": "^5.1.0", - "joi": "^17.9.2", - "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", - "utility-types": "^3.10.0", - "webpack": "^5.95.0", - "webpack-merge": "^5.9.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/@docusaurus/types/node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@docusaurus/utils": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.1.tgz", - "integrity": "sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/types": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "escape-string-regexp": "^4.0.0", - "execa": "5.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^11.1.1", - "github-slugger": "^1.5.0", - "globby": "^11.1.0", - "gray-matter": "^4.0.3", - "jiti": "^1.20.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "micromatch": "^4.0.5", - "p-queue": "^6.6.2", - "prompts": "^2.4.2", - "resolve-pathname": "^3.0.0", - "tslib": "^2.6.0", - "url-loader": "^4.1.1", - "utility-types": "^3.10.0", - "webpack": "^5.88.1" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/utils-common": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.1.tgz", - "integrity": "sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==", - "license": "MIT", - "dependencies": { - "@docusaurus/types": "3.8.1", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@docusaurus/utils-validation": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz", - "integrity": "sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==", - "license": "MIT", - "dependencies": { - "@docusaurus/logger": "3.8.1", - "@docusaurus/utils": "3.8.1", - "@docusaurus/utils-common": "3.8.1", - "fs-extra": "^11.2.0", - "joi": "^17.9.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "tslib": "^2.6.0" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@exodus/schemasafe": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", - "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", - "license": "MIT" - }, - "node_modules/@faker-js/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", - "deprecated": "Please update to a newer version.", - "license": "MIT" - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@mdi/js": { - "version": "7.4.47", - "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz", - "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==", - "license": "Apache-2.0" - }, - "node_modules/@mdi/react": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@mdi/react/-/react-1.6.1.tgz", - "integrity": "sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.7.2" - } - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", - "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@monaco-editor/loader": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz", - "integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==", - "license": "MIT", - "dependencies": { - "state-local": "^1.0.6" - } - }, - "node_modules/@monaco-editor/react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", - "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", - "license": "MIT", - "dependencies": { - "@monaco-editor/loader": "^1.5.0" - }, - "peerDependencies": { - "monaco-editor": ">= 0.25.0 < 1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pnpm/config.env-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "license": "MIT", - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "4.2.10" - }, - "engines": { - "node": ">=12.22.0" - } - }, - "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "license": "ISC" - }, - "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "license": "MIT", - "dependencies": { - "@pnpm/config.env-replace": "^1.1.0", - "@pnpm/network.ca-file": "^1.0.1", - "config-chain": "^1.1.11" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "license": "MIT" - }, - "node_modules/@reduxjs/toolkit": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", - "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", - "license": "MIT", - "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "license": "MIT" - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@slorber/remark-comment": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@slorber/remark-comment/-/remark-comment-1.0.0.tgz", - "integrity": "sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==", - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.1.0", - "micromark-util-symbol": "^1.0.1" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "license": "MIT", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/webpack": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", - "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/gtag.js": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==", - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", - "license": "MIT" - }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", - "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", - "license": "MIT", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "license": "MIT" - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.16", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", - "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", - "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==", - "license": "MIT" - }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", - "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-redux": { - "version": "7.1.34", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", - "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", - "license": "MIT", - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-config": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.11.tgz", - "integrity": "sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "^5.1.0" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" - }, - "node_modules/@types/sax": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", - "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/algoliasearch": { - "version": "5.23.4", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.23.4.tgz", - "integrity": "sha512-QzAKFHl3fm53s44VHrTdEo0TkpL3XVUYQpnZy1r6/EHvMAyIg+O4hwprzlsNmcCHTNyVcF2S13DAUn7XhkC6qg==", - "license": "MIT", - "dependencies": { - "@algolia/client-abtesting": "5.23.4", - "@algolia/client-analytics": "5.23.4", - "@algolia/client-common": "5.23.4", - "@algolia/client-insights": "5.23.4", - "@algolia/client-personalization": "5.23.4", - "@algolia/client-query-suggestions": "5.23.4", - "@algolia/client-search": "5.23.4", - "@algolia/ingestion": "1.23.4", - "@algolia/monitoring": "1.23.4", - "@algolia/recommend": "5.23.4", - "@algolia/requester-browser-xhr": "5.23.4", - "@algolia/requester-fetch": "5.23.4", - "@algolia/requester-node-http": "5.23.4" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/algoliasearch-helper": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.24.3.tgz", - "integrity": "sha512-3QKg5lzSfUiPN8Hn1ViHEGv6PjK7i4SFEDLzwlSzPO/4mVOsyos7B7/AsEtFQW5KHHPiCq6DyJl+mzg7CYlEgw==", - "license": "MIT", - "dependencies": { - "@algolia/events": "^4.0.1" - }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 6" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "license": "ISC" - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autocomplete.js": { - "version": "0.37.1", - "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz", - "integrity": "sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==", - "license": "MIT", - "dependencies": { - "immediate": "^3.2.3" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "license": "MIT", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", - "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.4", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", - "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.4" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "license": "MIT" - }, - "node_modules/bcp-47-match": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz", - "integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/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/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/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/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/bonjour-service": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", - "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", - "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^6.2.0", - "chalk": "^4.1.2", - "cli-boxes": "^3.0.0", - "string-width": "^5.0.1", - "type-fest": "^2.5.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.14", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", - "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "^4.0.2", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001726", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", - "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/charset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", - "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "license": "MIT" - }, - "node_modules/combine-promises": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz", - "integrity": "sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "license": "ISC" - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "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/compute-gcd": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", - "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==", - "dependencies": { - "validate.io-array": "^1.0.3", - "validate.io-function": "^1.0.2", - "validate.io-integer-array": "^1.0.0" - } - }, - "node_modules/compute-lcm": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz", - "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==", - "dependencies": { - "compute-gcd": "^1.2.1", - "validate.io-array": "^1.0.3", - "validate.io-function": "^1.0.2", - "validate.io-integer-array": "^1.0.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/configstore": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", - "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", - "license": "BSD-2-Clause", - "dependencies": { - "dot-prop": "^6.0.1", - "graceful-fs": "^4.2.6", - "unique-string": "^3.0.0", - "write-file-atomic": "^3.0.3", - "xdg-basedir": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "license": "MIT" - }, - "node_modules/copy-text-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", - "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "license": "MIT", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", - "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz", - "integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/crypto-random-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", - "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", - "license": "MIT", - "dependencies": { - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/crypto-random-string/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-blank-pseudo": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", - "integrity": "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", - "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", - "license": "ISC", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-7.0.2.tgz", - "integrity": "sha512-nzol/h+E0bId46Kn2dQH5VElaknX2Sr0hFuB/1EomdC7j+OISt2ZzK7EHX9DZDY53WbIVAR7FYKSO2XnSf07MQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-has-pseudo/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", - "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", - "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "cssnano": "^6.0.1", - "jest-worker": "^29.4.3", - "postcss": "^8.4.24", - "schema-utils": "^4.0.1", - "serialize-javascript": "^6.0.1" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "lightningcss": { - "optional": true - } - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-10.0.0.tgz", - "integrity": "sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-selector-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", - "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==", - "license": "MIT" - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssdb": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.1.tgz", - "integrity": "sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ], - "license": "MIT-0" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", - "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", - "license": "MIT", - "dependencies": { - "cssnano-preset-default": "^6.1.2", - "lilconfig": "^3.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-advanced": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", - "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", - "license": "MIT", - "dependencies": { - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.0", - "cssnano-preset-default": "^6.1.2", - "postcss-discard-unused": "^6.0.5", - "postcss-merge-idents": "^6.0.3", - "postcss-reduce-idents": "^6.0.3", - "postcss-zindex": "^6.0.2" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-preset-default": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", - "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^4.0.2", - "postcss-calc": "^9.0.1", - "postcss-colormin": "^6.1.0", - "postcss-convert-values": "^6.1.0", - "postcss-discard-comments": "^6.0.2", - "postcss-discard-duplicates": "^6.0.3", - "postcss-discard-empty": "^6.0.3", - "postcss-discard-overridden": "^6.0.2", - "postcss-merge-longhand": "^6.0.5", - "postcss-merge-rules": "^6.1.1", - "postcss-minify-font-values": "^6.1.0", - "postcss-minify-gradients": "^6.0.3", - "postcss-minify-params": "^6.1.0", - "postcss-minify-selectors": "^6.0.4", - "postcss-normalize-charset": "^6.0.2", - "postcss-normalize-display-values": "^6.0.2", - "postcss-normalize-positions": "^6.0.2", - "postcss-normalize-repeat-style": "^6.0.2", - "postcss-normalize-string": "^6.0.2", - "postcss-normalize-timing-functions": "^6.0.2", - "postcss-normalize-unicode": "^6.1.0", - "postcss-normalize-url": "^6.0.2", - "postcss-normalize-whitespace": "^6.0.2", - "postcss-ordered-values": "^6.0.2", - "postcss-reduce-initial": "^6.1.0", - "postcss-reduce-transforms": "^6.0.2", - "postcss-svgo": "^6.0.3", - "postcss-unique-selectors": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/cssnano-utils": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", - "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", - "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "license": "BSD-2-Clause", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, - "node_modules/detect-package-manager": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-3.0.2.tgz", - "integrity": "sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ==", - "license": "MIT", - "dependencies": { - "execa": "^5.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", - "license": "MIT", - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/direction": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", - "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", - "license": "MIT", - "bin": { - "direction": "cli.js" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/docusaurus-lunr-search": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-3.6.0.tgz", - "integrity": "sha512-CCEAnj5e67sUZmIb2hOl4xb4nDN07fb0fvRDDmdWlYpUvyS1CSKbw4lsGInLyUFEEEBzxQmT6zaVQdF/8Zretg==", - "license": "MIT", - "dependencies": { - "autocomplete.js": "^0.37.1", - "clsx": "^2.1.1", - "gauge": "^3.0.2", - "hast-util-select": "^4.0.2", - "hast-util-to-text": "^2.0.1", - "hogan.js": "^3.0.2", - "lunr": "^2.3.9", - "lunr-languages": "^1.4.0", - "mark.js": "^8.11.1", - "minimatch": "^3.1.2", - "rehype-parse": "^7.0.1", - "to-vfile": "^6.1.0", - "unified": "^9.2.2", - "unist-util-is": "^4.1.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "peerDependencies": { - "@docusaurus/core": "^2.0.0-alpha.60 || ^2.0.0 || ^3.0.0", - "react": "^16.8.4 || ^17 || ^18 || ^19", - "react-dom": "^16.8.4 || ^17 || ^18 || ^19" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/docusaurus-lunr-search/node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/unified": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", - "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", - "license": "MIT", - "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/docusaurus-lunr-search/node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/docusaurus-plugin-openapi": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.7.6.tgz", - "integrity": "sha512-LR8DI0gO9WFy8K+r0xrVgqDkKKA9zQtDgOnX9CatP3I3Oz5lKegfTJM2fVUIp5m25elzHL+vVKNHS12Jg7sWVA==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "^3.6.0", - "@docusaurus/plugin-content-docs": "^3.6.0", - "@docusaurus/utils": "^3.6.0", - "@docusaurus/utils-common": "^3.6.0", - "@docusaurus/utils-validation": "^3.6.0", - "chalk": "^4.1.2", - "clsx": "^1.2.1", - "js-yaml": "^4.1.0", - "json-refs": "^3.0.15", - "json-schema-resolve-allof": "^1.5.0", - "lodash": "^4.17.20", - "openapi-to-postmanv2": "^4.20.1", - "postman-collection": "^4.1.0", - "webpack": "^5.95.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/docusaurus-plugin-openapi/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/docusaurus-plugin-proxy": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/docusaurus-plugin-proxy/-/docusaurus-plugin-proxy-0.7.6.tgz", - "integrity": "sha512-MgjzMEsQOHMljwQGglXXoGjQvs0v1DklhRgzqNLKFwpHB9xLWJZ0KQ3GgbPerW/2vy8tWGJeVhKHy5cPrmweUw==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/docusaurus-preset-openapi": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/docusaurus-preset-openapi/-/docusaurus-preset-openapi-0.7.6.tgz", - "integrity": "sha512-QnArH/3X0lePB7667FyNK3EeTS8ZP8V2PQxz5m+3BMO2kIzdXDwfTIQ37boB0BTqsDfUE0yCWTVjB0W/BA1UXA==", - "license": "MIT", - "dependencies": { - "@docusaurus/preset-classic": "^3.6.0", - "docusaurus-plugin-openapi": "^0.7.6", - "docusaurus-plugin-proxy": "^0.7.6", - "docusaurus-theme-openapi": "^0.7.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/docusaurus-theme-openapi": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/docusaurus-theme-openapi/-/docusaurus-theme-openapi-0.7.6.tgz", - "integrity": "sha512-euoEh8tYX/ssQcMQxBOxt3wPttz3zvPu0l5lSe6exiIwMrORB4O2b8XRB7fVa/awF7xzdIkKHMH55uc5zVOKYA==", - "license": "MIT", - "dependencies": { - "@docusaurus/theme-common": "^3.6.0", - "@mdx-js/react": "^3.0.0", - "@monaco-editor/react": "^4.3.1", - "@reduxjs/toolkit": "^1.7.1", - "buffer": "^6.0.3", - "clsx": "^1.2.1", - "crypto-js": "^4.1.1", - "docusaurus-plugin-openapi": "^0.7.6", - "immer": "^9.0.7", - "lodash": "^4.17.20", - "marked": "^11.0.0", - "monaco-editor": "^0.31.1", - "postman-code-generators": "^1.0.0", - "postman-collection": "^4.1.0", - "prism-react-renderer": "^2.1.0", - "process": "^0.11.10", - "react-magic-dropzone": "^1.0.1", - "react-redux": "^7.2.0", - "redux-devtools-extension": "^2.13.8", - "refractor": "^4.8.1", - "striptags": "^3.2.0", - "webpack": "^5.95.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/docusaurus-theme-openapi/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.178", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", - "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/emoticon": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", - "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", - "license": "MIT" - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.3.3.tgz", - "integrity": "sha512-Db+m1WSD4+mUO7UgMeKkAwdbfNWwIxLt48XF2oFU9emPfXkIu+k5/nlOj313v7wqtAPo0f9REhUvznFrPkG8CQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", - "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/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/express/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/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "license": "MIT", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", - "license": "MIT", - "dependencies": { - "xml-js": "^1.6.11" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/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/finalhandler/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/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "license": "MIT", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "license": "MIT", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreach": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", - "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", - "license": "MIT" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", - "engines": { - "node": ">= 14.17" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "license": "MIT", - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/gauge/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "license": "ISC" - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", - "license": "ISC" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "license": "MIT", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC" - }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", - "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", - "license": "MIT", - "dependencies": { - "@types/parse5": "^5.0.0", - "hastscript": "^6.0.0", - "property-information": "^5.0.0", - "vfile": "^4.0.0", - "vfile-location": "^3.2.0", - "web-namespaces": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-has-property": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz", - "integrity": "sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", - "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", - "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/hast-util-parse-selector/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-raw/node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-raw/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-raw/node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-raw/node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-select": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-4.0.2.tgz", - "integrity": "sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==", - "license": "MIT", - "dependencies": { - "bcp-47-match": "^1.0.0", - "comma-separated-tokens": "^1.0.0", - "css-selector-parser": "^1.0.0", - "direction": "^1.0.0", - "hast-util-has-property": "^1.0.0", - "hast-util-is-element": "^1.0.0", - "hast-util-to-string": "^1.0.0", - "hast-util-whitespace": "^1.0.0", - "not": "^0.1.0", - "nth-check": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0", - "unist-util-visit": "^2.0.0", - "zwitch": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-select/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/hast-util-select/node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-select/node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-estree/node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree/node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-estree/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-estree/node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-jsx-runtime/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-parse5/node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-string": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz", - "integrity": "sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz", - "integrity": "sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==", - "license": "MIT", - "dependencies": { - "hast-util-is-element": "^1.0.0", - "repeat-string": "^1.0.0", - "unist-util-find-after": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", - "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^3.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/hastscript/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/hastscript/node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hastscript/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hastscript/node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/hogan.js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", - "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", - "dependencies": { - "mkdirp": "0.3.0", - "nopt": "1.0.10" - }, - "bin": { - "hulk": "bin/hulk" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", - "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", - "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "~5.3.2", - "commander": "^10.0.0", - "entities": "^4.4.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.15.1" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/html-tags": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", - "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "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==", - "license": "BSD-2-Clause" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/http-reasons": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", - "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==", - "license": "Apache-2.0" - }, - "node_modules/http2-client": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", - "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", - "license": "MIT" - }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", - "license": "MIT" - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/infima": { - "version": "0.2.0-alpha.45", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.45.tgz", - "integrity": "sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "license": "MIT", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-yarn-global": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", - "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "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/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-pointer": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", - "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", - "license": "MIT", - "dependencies": { - "foreach": "^2.0.4" - } - }, - "node_modules/json-refs": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-3.0.15.tgz", - "integrity": "sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==", - "license": "MIT", - "dependencies": { - "commander": "~4.1.1", - "graphlib": "^2.1.8", - "js-yaml": "^3.13.1", - "lodash": "^4.17.15", - "native-promise-only": "^0.8.1", - "path-loader": "^1.0.10", - "slash": "^3.0.0", - "uri-js": "^4.2.2" - }, - "bin": { - "json-refs": "bin/json-refs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/json-refs/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/json-refs/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/json-refs/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-compare": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", - "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.4" - } - }, - "node_modules/json-schema-merge-allof": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz", - "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==", - "license": "MIT", - "dependencies": { - "compute-lcm": "^1.1.2", - "json-schema-compare": "^0.2.2", - "lodash": "^4.17.20" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/json-schema-resolve-allof": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/json-schema-resolve-allof/-/json-schema-resolve-allof-1.5.0.tgz", - "integrity": "sha512-Jgn6BQGSLDp3D7bTYrmCbP/p7SRFz5BfpeEJ9A7sXuVADMc14aaDN1a49zqk9D26wwJlcNvjRpT63cz1VgFZeg==", - "license": "ISC", - "dependencies": { - "get-stdin": "^5.0.1", - "lodash": "^4.14.0" - }, - "bin": { - "json-schema-resolve-allof": "bin/json-schema-resolve-allof" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", - "license": "MIT", - "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/launch-editor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", - "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/liquid-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", - "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "license": "MIT" - }, - "node_modules/lunr-languages": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.14.0.tgz", - "integrity": "sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA==", - "license": "MPL-1.1" - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "license": "MIT" - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-directive": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", - "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown/node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "license": "CC0-1.0" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-directive/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-frontmatter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", - "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", - "license": "MIT", - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-destination/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-space/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character/node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-normalize-identifier/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark/node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark/node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-format": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", - "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", - "license": "Apache-2.0", - "dependencies": { - "charset": "^1.0.0" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "license": "MIT/X11", - "engines": { - "node": "*" - } - }, - "node_modules/monaco-editor": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", - "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==", - "license": "MIT" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==", - "license": "MIT" - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/neotraverse": { - "version": "0.6.15", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.15.tgz", - "integrity": "sha512-HZpdkco+JeXq0G+WWpMJ4NsX3pqb5O7eR9uGz3FfoFt+LYzU8iRWp49nJtud6hsDoywM8tIrDo3gjgmOqJA8LA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", - "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch-h2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", - "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", - "license": "MIT", - "dependencies": { - "http2-client": "^1.2.5" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-readfiles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", - "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", - "license": "MIT", - "dependencies": { - "es6-promise": "^3.2.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "license": "MIT", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/not": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz", - "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==" - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", - "license": "MIT" - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/null-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/null-loader/-/null-loader-4.0.1.tgz", - "integrity": "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/null-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/null-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/null-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/null-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/oas-kit-common": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", - "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", - "license": "BSD-3-Clause", - "dependencies": { - "fast-safe-stringify": "^2.0.7" - } - }, - "node_modules/oas-linter": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", - "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@exodus/schemasafe": "^1.0.0-rc.2", - "should": "^13.2.1", - "yaml": "^1.10.0" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-resolver": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", - "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", - "license": "BSD-3-Clause", - "dependencies": { - "node-fetch-h2": "^2.3.0", - "oas-kit-common": "^1.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "resolve": "resolve.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-resolver-browser": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz", - "integrity": "sha512-Jw5elT/kwUJrnGaVuRWe1D7hmnYWB8rfDDjBnpQ+RYY/dzAewGXeTexXzt4fGEo6PUE4eqKqPWF79MZxxvMppA==", - "license": "BSD-3-Clause", - "dependencies": { - "node-fetch-h2": "^2.3.0", - "oas-kit-common": "^1.0.8", - "path-browserify": "^1.0.1", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "resolve": "resolve.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-schema-walker": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", - "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", - "license": "BSD-3-Clause", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/oas-validator": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", - "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", - "license": "BSD-3-Clause", - "dependencies": { - "call-me-maybe": "^1.0.1", - "oas-kit-common": "^1.0.8", - "oas-linter": "^3.2.2", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "reftools": "^1.1.9", - "should": "^13.2.1", - "yaml": "^1.10.0" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi-to-postmanv2": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/openapi-to-postmanv2/-/openapi-to-postmanv2-4.25.0.tgz", - "integrity": "sha512-sIymbkQby0gzxt2Yez8YKB6hoISEel05XwGwNrAhr6+vxJWXNxkmssQc/8UEtVkuJ9ZfUXLkip9PYACIpfPDWg==", - "license": "Apache-2.0", - "dependencies": { - "ajv": "8.11.0", - "ajv-draft-04": "1.0.0", - "ajv-formats": "2.1.1", - "async": "3.2.4", - "commander": "2.20.3", - "graphlib": "2.1.8", - "js-yaml": "4.1.0", - "json-pointer": "0.6.2", - "json-schema-merge-allof": "0.8.1", - "lodash": "4.17.21", - "neotraverse": "0.6.15", - "oas-resolver-browser": "2.5.6", - "object-hash": "3.0.0", - "path-browserify": "1.0.1", - "postman-collection": "^4.4.0", - "swagger2openapi": "7.0.8", - "yaml": "1.10.2" - }, - "bin": { - "openapi2postmanv2": "bin/openapi2postmanv2.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/openapi-to-postmanv2/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "license": "MIT", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", - "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", - "license": "MIT", - "dependencies": { - "got": "^12.1.0", - "registry-auth-token": "^5.0.1", - "registry-url": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=14.16" - }, - "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", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", - "license": "ISC" - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "license": "MIT", - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "license": "MIT", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-loader": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.12.tgz", - "integrity": "sha512-n7oDG8B+k/p818uweWrOixY9/Dsr89o2TkCm6tOTex3fpdo2+BFDgR+KpB37mGKBRsBAlR8CIJMFN0OEy/7hIQ==", - "license": "MIT", - "dependencies": { - "native-promise-only": "^0.8.1", - "superagent": "^7.1.6" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "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==", - "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/path-scurry/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/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "license": "MIT", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "license": "MIT", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-7.0.1.tgz", - "integrity": "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-calc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", - "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", - "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-10.0.0.tgz", - "integrity": "sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-10.0.0.tgz", - "integrity": "sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-colormin": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", - "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "colord": "^2.9.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-convert-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", - "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-custom-media": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", - "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/media-query-list-parser": "^4.0.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-properties": { - "version": "14.0.6", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", - "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", - "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.5", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-9.0.1.tgz", - "integrity": "sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-discard-comments": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", - "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", - "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-empty": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", - "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", - "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-discard-unused": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", - "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.2.tgz", - "integrity": "sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-10.0.1.tgz", - "integrity": "sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-focus-within": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-9.0.1.tgz", - "integrity": "sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-6.0.0.tgz", - "integrity": "sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-image-set-function": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-7.0.0.tgz", - "integrity": "sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/utilities": "^2.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-lab-function": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.10.tgz", - "integrity": "sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/css-color-parser": "^3.0.10", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/utilities": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/postcss-loader": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.4.tgz", - "integrity": "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==", - "license": "MIT", - "dependencies": { - "cosmiconfig": "^8.3.5", - "jiti": "^1.20.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-logical": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz", - "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-merge-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", - "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", - "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^6.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-merge-rules": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", - "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^4.0.2", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", - "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", - "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", - "license": "MIT", - "dependencies": { - "colord": "^2.9.3", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-params": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", - "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", - "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", - "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/selector-resolve-nested": "^3.1.0", - "@csstools/selector-specificity": "^5.0.0", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", - "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", - "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", - "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", - "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", - "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-string": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", - "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", - "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", - "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-url": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", - "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", - "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-3.0.0.tgz", - "integrity": "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-ordered-values": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", - "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^4.0.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-6.0.0.tgz", - "integrity": "sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-10.0.0.tgz", - "integrity": "sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-preset-env": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.4.tgz", - "integrity": "sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "@csstools/postcss-cascade-layers": "^5.0.2", - "@csstools/postcss-color-function": "^4.0.10", - "@csstools/postcss-color-mix-function": "^3.0.10", - "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", - "@csstools/postcss-content-alt-text": "^2.0.6", - "@csstools/postcss-exponential-functions": "^2.0.9", - "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.10", - "@csstools/postcss-gradients-interpolation-method": "^5.0.10", - "@csstools/postcss-hwb-function": "^4.0.10", - "@csstools/postcss-ic-unit": "^4.0.2", - "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.3", - "@csstools/postcss-light-dark-function": "^2.0.9", - "@csstools/postcss-logical-float-and-clear": "^3.0.0", - "@csstools/postcss-logical-overflow": "^2.0.0", - "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", - "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.4", - "@csstools/postcss-media-minmax": "^2.0.9", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", - "@csstools/postcss-nested-calc": "^4.0.0", - "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.10", - "@csstools/postcss-progressive-custom-properties": "^4.1.0", - "@csstools/postcss-random-function": "^2.0.1", - "@csstools/postcss-relative-color-syntax": "^3.0.10", - "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.4", - "@csstools/postcss-stepped-value-functions": "^4.0.9", - "@csstools/postcss-text-decoration-shorthand": "^4.0.2", - "@csstools/postcss-trigonometric-functions": "^4.0.9", - "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.21", - "browserslist": "^4.25.0", - "css-blank-pseudo": "^7.0.1", - "css-has-pseudo": "^7.0.2", - "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.3.0", - "postcss-attribute-case-insensitive": "^7.0.1", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.10", - "postcss-color-hex-alpha": "^10.0.0", - "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.6", - "postcss-custom-properties": "^14.0.6", - "postcss-custom-selectors": "^8.0.5", - "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.2", - "postcss-focus-visible": "^10.0.1", - "postcss-focus-within": "^9.0.1", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^6.0.0", - "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.10", - "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.2", - "postcss-opacity-percentage": "^3.0.0", - "postcss-overflow-shorthand": "^6.0.0", - "postcss-page-break": "^3.0.4", - "postcss-place": "^10.0.0", - "postcss-pseudo-class-any-link": "^10.0.1", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^8.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz", - "integrity": "sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-reduce-idents": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", - "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", - "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", - "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "license": "MIT", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-8.0.1.tgz", - "integrity": "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sort-media-queries": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", - "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", - "license": "MIT", - "dependencies": { - "sort-css-media-queries": "2.2.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.4.23" - } - }, - "node_modules/postcss-svgo": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", - "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^3.2.0" - }, - "engines": { - "node": "^14 || ^16 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", - "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/postcss-zindex": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", - "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postman-code-generators": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/postman-code-generators/-/postman-code-generators-1.14.2.tgz", - "integrity": "sha512-qZAyyowfQAFE4MSCu2KtMGGQE/+oG1JhMZMJNMdZHYCSfQiVVeKxgk3oI4+KJ3d1y5rrm2D6C6x+Z+7iyqm+fA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "async": "3.2.2", - "detect-package-manager": "3.0.2", - "lodash": "4.17.21", - "path": "0.12.7", - "postman-collection": "^4.4.0", - "shelljs": "0.8.5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/postman-code-generators/node_modules/async": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", - "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==", - "license": "MIT" - }, - "node_modules/postman-collection": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.5.0.tgz", - "integrity": "sha512-152JSW9pdbaoJihwjc7Q8lc3nPg/PC9lPTHdMk7SHnHhu/GBJB7b2yb9zG7Qua578+3PxkQ/HYBuXpDSvsf7GQ==", - "license": "Apache-2.0", - "dependencies": { - "@faker-js/faker": "5.5.3", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.3", - "liquid-json": "0.3.1", - "lodash": "4.17.21", - "mime-format": "2.0.1", - "mime-types": "2.1.35", - "postman-url-encoder": "3.0.5", - "semver": "7.6.3", - "uuid": "8.3.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-collection/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postman-url-encoder": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.5.tgz", - "integrity": "sha512-jOrdVvzUXBC7C+9gkIkpDJ3HIxOHTIqjpQ4C1EMt1ZGeMvSEpbFCKq23DEfgsj46vMnDgyQf+1ZLp2Wm+bKSsA==", - "license": "Apache-2.0", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/prism-react-renderer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", - "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", - "license": "MIT", - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "license": "MIT", - "dependencies": { - "escape-goat": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/raw-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", - "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/raw-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/raw-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/raw-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/raw-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", - "license": "MIT" - }, - "node_modules/react-helmet-async": { - "name": "@slorber/react-helmet-async", - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@slorber/react-helmet-async/-/react-helmet-async-1.3.0.tgz", - "integrity": "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.12.5", - "invariant": "^2.2.4", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.2.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/react-json-view-lite": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.4.1.tgz", - "integrity": "sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-loadable": { - "name": "@docusaurus/react-loadable", - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", - "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "react-loadable": "*", - "webpack": ">=4.41.1 || 5.x" - } - }, - "node_modules/react-magic-dropzone": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-magic-dropzone/-/react-magic-dropzone-1.0.1.tgz", - "integrity": "sha512-0BIROPARmXHpk4AS3eWBOsewxoM5ndk2psYP/JmbCq8tz3uR2LIV1XiroZ9PKrmDRMctpW+TvsBCtWasuS8vFA==", - "license": "MIT" - }, - "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - }, - "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, - "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.1.2" - }, - "peerDependencies": { - "react": ">=15", - "react-router": ">=5" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz", - "integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-devtools-extension": { - "version": "2.13.9", - "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", - "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", - "deprecated": "Package moved to @redux-devtools/extension.", - "license": "MIT", - "peerDependencies": { - "redux": "^3.1.0 || ^4.0.0" - } - }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "license": "MIT", - "peerDependencies": { - "redux": "^4" - } - }, - "node_modules/refractor": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz", - "integrity": "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^7.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/refractor/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/reftools": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", - "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", - "license": "BSD-3-Clause", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/registry-auth-token": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", - "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", - "license": "MIT", - "dependencies": { - "@pnpm/npm-conf": "^2.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/registry-url": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", - "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "license": "MIT", - "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/rehype-parse": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", - "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", - "license": "MIT", - "dependencies": { - "hast-util-from-parse5": "^6.0.0", - "parse5": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT" - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-directive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", - "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^3.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-emoji": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-4.0.1.tgz", - "integrity": "sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.2", - "emoticon": "^4.0.1", - "mdast-util-find-and-replace": "^3.0.1", - "node-emoji": "^2.1.0", - "unified": "^11.0.4" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-frontmatter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", - "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-frontmatter": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz", - "integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", - "engines": { - "node": "*" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", - "license": "MIT" - }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rtlcss": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", - "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0", - "postcss": "^8.4.21", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-dts": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/schema-dts/-/schema-dts-1.1.5.tgz", - "integrity": "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==", - "license": "Apache-2.0" - }, - "node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "license": "MIT", - "peer": true - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "license": "MIT", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "license": "MIT", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", - "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/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/send/node_modules/debug/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/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", - "license": "MIT", - "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "mime-types": "2.1.18", - "minimatch": "3.1.2", - "path-is-inside": "1.0.2", - "path-to-regexp": "3.3.0", - "range-parser": "1.2.0" - } - }, - "node_modules/serve-handler/node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-handler/node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "license": "MIT", - "dependencies": { - "mime-db": "~1.33.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/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/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/serve-index/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/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "license": "ISC" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "license": "MIT", - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "license": "MIT", - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "license": "MIT" - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "license": "MIT" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/sitemap": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", - "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", - "license": "MIT", - "dependencies": { - "@types/node": "^17.0.5", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" - }, - "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" - } - }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "license": "MIT" - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "license": "MIT", - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sort-css-media-queries": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", - "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", - "license": "MIT", - "engines": { - "node": ">= 6.3.0" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, - "node_modules/srcset": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", - "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/state-local": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", - "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "license": "BSD-2-Clause", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/striptags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.2.0.tgz", - "integrity": "sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==", - "license": "MIT" - }, - "node_modules/style-to-js": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", - "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.8" - } - }, - "node_modules/style-to-object": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", - "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/stylehacks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", - "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "postcss-selector-parser": "^6.0.16" - }, - "engines": { - "node": "^14 || ^16 || >=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/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==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/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==", - "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/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/superagent": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.6.tgz", - "integrity": "sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==", - "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.3", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.0.1", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.10.3", - "readable-stream": "^3.6.0", - "semver": "^7.3.7" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "license": "MIT" - }, - "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", - "license": "MIT", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/swagger2openapi": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", - "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", - "license": "BSD-3-Clause", - "dependencies": { - "call-me-maybe": "^1.0.1", - "node-fetch": "^2.6.1", - "node-fetch-h2": "^2.3.0", - "node-readfiles": "^0.2.0", - "oas-kit-common": "^1.0.8", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "oas-validator": "^5.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "boast": "boast.js", - "oas-validate": "oas-validate.js", - "swagger2openapi": "swagger2openapi.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", - "license": "MIT" - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/to-vfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz", - "integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==", - "license": "MIT", - "dependencies": { - "is-buffer": "^2.0.0", - "vfile": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/to-vfile/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/to-vfile/node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/to-vfile/node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/to-vfile/node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unique-string": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", - "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", - "license": "MIT", - "dependencies": { - "crypto-random-string": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unist-util-find-after": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", - "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", - "license": "MIT", - "dependencies": { - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/update-notifier": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", - "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", - "license": "BSD-2-Clause", - "dependencies": { - "boxen": "^7.0.0", - "chalk": "^5.0.1", - "configstore": "^6.0.0", - "has-yarn": "^3.0.0", - "import-lazy": "^4.0.0", - "is-ci": "^3.0.1", - "is-installed-globally": "^0.4.0", - "is-npm": "^6.0.0", - "is-yarn-global": "^0.4.0", - "latest-version": "^7.0.0", - "pupa": "^3.1.0", - "semver": "^7.3.7", - "semver-diff": "^4.0.0", - "xdg-basedir": "^5.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" - } - }, - "node_modules/update-notifier/node_modules/boxen": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", - "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^7.0.1", - "chalk": "^5.2.0", - "cli-boxes": "^3.0.0", - "string-width": "^5.1.2", - "type-fest": "^2.13.0", - "widest-line": "^4.0.1", - "wrap-ansi": "^8.1.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/update-notifier/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", - "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", - "license": "MIT", - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.12.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true - } - } - }, - "node_modules/url-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/url-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/url-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" - }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "license": "MIT" - }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "license": "MIT", - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "license": "MIT" - }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate.io-array": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", - "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", - "license": "MIT" - }, - "node_modules/validate.io-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", - "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" - }, - "node_modules/validate.io-integer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", - "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", - "dependencies": { - "validate.io-number": "^1.0.3" - } - }, - "node_modules/validate.io-integer-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz", - "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==", - "dependencies": { - "validate.io-array": "^1.0.3", - "validate.io-integer": "^1.0.4" - } - }, - "node_modules/validate.io-number": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", - "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==" - }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", - "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-namespaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", - "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/webpack": { - "version": "5.99.5", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", - "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", - "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "0.5.7", - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "commander": "^7.2.0", - "debounce": "^1.2.1", - "escape-string-regexp": "^4.0.0", - "gzip-size": "^6.0.0", - "html-escaper": "^2.0.2", - "opener": "^1.5.2", - "picocolors": "^1.0.0", - "sirv": "^2.0.3", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", - "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.4", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "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/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpackbar": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-6.0.1.tgz", - "integrity": "sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "consola": "^3.2.3", - "figures": "^3.2.0", - "markdown-table": "^2.0.0", - "pretty-time": "^1.1.0", - "std-env": "^3.7.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "webpack": "3 || 4 || 5" - } - }, - "node_modules/webpackbar/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/webpackbar/node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "license": "MIT", - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/webpackbar/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpackbar/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wide-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", - "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "license": "MIT", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", - "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/docs/package.json b/docs/package.json index b1faa4cfcb..d984427622 100644 --- a/docs/package.json +++ b/docs/package.json @@ -7,7 +7,8 @@ "format": "prettier --check .", "format:fix": "prettier --write .", "start": "docusaurus start --port 3005", - "build": "docusaurus build", + "copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0", + "build": "npm run copy:openapi && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", @@ -23,10 +24,7 @@ "@mdi/react": "^1.6.1", "@mdx-js/react": "^3.0.0", "autoprefixer": "^10.4.17", - "classnames": "^2.3.2", - "clsx": "^2.0.0", "docusaurus-lunr-search": "^3.3.2", - "docusaurus-preset-openapi": "^0.7.5", "lunr": "^2.3.9", "postcss": "^8.4.25", "prism-react-renderer": "^2.3.1", @@ -59,6 +57,6 @@ "node": ">=20" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" } } diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx index 49ba7a8a08..08c8e096d9 100644 --- a/docs/src/components/community-guides.tsx +++ b/docs/src/components/community-guides.tsx @@ -28,6 +28,12 @@ const guides: CommunityGuidesProps[] = [ description: `synchronize folders in imported library with albums having the folders name.`, url: 'https://github.com/immich-app/immich/discussions/3382', }, + { + title: 'Immich Podman Quadlets Handbook', + description: + 'A rewrite of the original Immich Docker Compose file using Podman Quadlets, with a set of extra guides in the repository’s wiki.', + url: 'https://github.com/linux-universe/immich-podman-quadlets/blob/main/README.md', + }, { title: 'Podman/Quadlets Install', description: 'Documentation for simple podman setup using quadlets.', diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 03a384162b..6b74ae7ad8 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -23,11 +23,6 @@ const projects: CommunityProjectProps[] = [ description: 'A Python script to sync folders as albums.', url: 'https://git.orenit.solutions/open/immichalbumpull', }, - { - title: 'Remove offline files', - description: 'A simple way to remove orphaned offline assets from the Immich database', - url: 'https://github.com/Thoroslives/immich_remove_offline_files', - }, { title: 'Immich-Tools', description: 'Provides scripts for handling problems on the repair page.', @@ -100,6 +95,31 @@ const projects: CommunityProjectProps[] = [ description: 'Automatically optimize files uploaded to Immich in order to save storage space', url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', }, + { + title: 'Immich Machine Learning Load Balancer', + description: 'Speed up your machine learning by load balancing your requests to multiple computers', + url: 'https://github.com/apetersson/immich_ml_balancer', + }, + { + title: 'Immich Drop Uploader', + description: 'A tiny, zero-login web app for collecting photos/videos from anyone into your Immich server.', + url: 'https://github.com/Nasogaa/immich-drop', + }, + { + title: 'Immich Birthday Sync', + description: 'Bulk-upload and -download birthdays, with CardDAV sync support', + url: 'https://github.com/sid3windr/immich-birthday', + }, + { + title: 'Immich Stack', + description: 'Auto-stack photos with identical filenames and differing extensions (i.e. JPG+RAW)', + url: 'https://github.com/sid3windr/immich-stack', + }, + { + title: 'Immich Stack', + description: 'Automatically groups similar photos into stacks within the Immich photo management system.', + url: 'https://github.com/Majorfi/immich-stack/', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { diff --git a/docs/src/components/version-switcher.tsx b/docs/src/components/version-switcher.tsx index 5cb23891aa..739d7bd001 100644 --- a/docs/src/components/version-switcher.tsx +++ b/docs/src/components/version-switcher.tsx @@ -11,7 +11,7 @@ export default function VersionSwitcher(): JSX.Element { useEffect(() => { async function getVersions() { try { - let baseUrl = 'https://immich.app'; + let baseUrl = 'https://docs.immich.app'; if (window.location.origin === 'http://localhost:3005') { baseUrl = window.location.origin; } @@ -21,12 +21,13 @@ export default function VersionSwitcher(): JSX.Element { const archiveVersions = await response.json(); const allVersions = [ - { label: 'Next', url: 'https://main.preview.immich.app' }, - { label: 'Latest', url: 'https://immich.app' }, + { label: 'Next', url: 'https://docs.main.preview.immich.app' }, + { label: 'Latest', url: 'https://docs.immich.app' }, ...archiveVersions, - ].map(({ label, url }) => ({ + ].map(({ label, url, rootPath }) => ({ label, url: new URL(url), + rootPath, })); setVersions(allVersions); @@ -50,12 +51,18 @@ export default function VersionSwitcher(): JSX.Element { className="version-switcher-34ab39" label={activeLabel} mobile={windowSize === 'mobile'} - items={versions.map(({ label, url }) => ({ - label, - to: new URL(location.pathname + location.search + location.hash, url).href, - target: '_self', - className: label === activeLabel ? 'dropdown__link--active menu__link--active' : '', // workaround because React Router `` only supports using URL path for checking if active: https://v5.reactrouter.com/web/api/NavLink/isactive-func - }))} + items={versions.map(({ label, url, rootPath }) => { + let path = location.pathname + location.search + location.hash; + if (rootPath && !path.startsWith(rootPath)) { + path = rootPath + path; + } + return { + label, + to: new URL(path, url).href, + target: '_self', + className: label === activeLabel ? 'dropdown__link--active menu__link--active' : '', // workaround because React Router `` only supports using URL path for checking if active: https://v5.reactrouter.com/web/api/NavLink/isactive-func + }; + })} /> ) ); diff --git a/docs/src/pages/cursed-knowledge.tsx b/docs/src/pages/cursed-knowledge.tsx deleted file mode 100644 index 0db89fbfe7..0000000000 --- a/docs/src/pages/cursed-knowledge.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { - mdiBug, - mdiCalendarToday, - mdiCrosshairsOff, - mdiCrop, - mdiDatabase, - mdiLeadPencil, - mdiLockOff, - mdiLockOutline, - mdiMicrosoftWindows, - mdiSecurity, - mdiSpeedometerSlow, - mdiTrashCan, - mdiWeb, - mdiWrap, - mdiCloudKeyOutline, - mdiRegex, - mdiCodeJson, -} from '@mdi/js'; -import Layout from '@theme/Layout'; -import React from 'react'; -import { Timeline, Item as TimelineItem } from '../components/timeline'; - -const withLanguage = (date: Date) => (language: string) => date.toLocaleDateString(language); - -type Item = Omit & { date: Date }; - -const items: Item[] = [ - { - icon: mdiRegex, - iconColor: 'purple', - title: 'Zitadel Actions are cursed', - description: - "Zitadel is cursed because its custom scripting feature is executed with a JS engine that doesn't support regex named capture groups.", - link: { - url: 'https://github.com/dop251/goja', - text: 'Go JS engine', - }, - date: new Date(2025, 5, 4), - }, - { - icon: mdiCloudKeyOutline, - iconColor: '#0078d4', - title: 'Entra is cursed', - description: - "Microsoft Entra supports PKCE, but doesn't include it in its OpenID discovery document. This leads to clients thinking PKCE isn't available.", - link: { - url: 'https://github.com/immich-app/immich/pull/18725', - text: '#18725', - }, - date: new Date(2025, 4, 30), - }, - { - icon: mdiCrop, - iconColor: 'tomato', - title: 'Image dimensions in EXIF metadata are cursed', - description: - 'The dimensions in EXIF metadata can be different from the actual dimensions of the image, causing issues with cropping and resizing.', - link: { - url: 'https://github.com/immich-app/immich/pull/17974', - text: '#17974', - }, - date: new Date(2025, 4, 5), - }, - { - icon: mdiCodeJson, - iconColor: 'yellow', - title: 'YAML whitespace is cursed', - description: 'YAML whitespaces are often handled in unintuitive ways.', - link: { - url: 'https://github.com/immich-app/immich/pull/17309', - text: '#17309', - }, - date: new Date(2025, 3, 1), - }, - { - icon: mdiMicrosoftWindows, - iconColor: '#357EC7', - title: 'Hidden files in Windows are cursed', - description: - 'Hidden files in Windows cannot be opened with the "w" flag. That, combined with SMB option "hide dot files" leads to a lot of confusion.', - link: { - url: 'https://github.com/immich-app/immich/pull/12812', - text: '#12812', - }, - date: new Date(2024, 8, 20), - }, - { - icon: mdiWrap, - iconColor: 'gray', - title: 'Carriage returns in bash scripts are cursed', - description: 'Git can be configured to automatically convert LF to CRLF on checkout and CRLF breaks bash scripts.', - link: { - url: 'https://github.com/immich-app/immich/pull/11613', - text: '#11613', - }, - date: new Date(2024, 7, 7), - }, - { - icon: mdiLockOff, - iconColor: 'red', - title: 'Fetch inside Cloudflare Workers is cursed', - description: - 'Fetch requests in Cloudflare Workers use http by default, even if you explicitly specify https, which can often cause redirect loops.', - link: { - url: 'https://community.cloudflare.com/t/does-cloudflare-worker-allow-secure-https-connection-to-fetch-even-on-flexible-ssl/68051/5', - text: 'Cloudflare', - }, - date: new Date(2024, 7, 7), - }, - { - icon: mdiCrosshairsOff, - iconColor: 'gray', - title: 'GPS sharing on mobile is cursed', - description: - 'Some phones will silently strip GPS data from images when apps without location permission try to access them.', - link: { - url: 'https://github.com/immich-app/immich/discussions/11268', - text: '#11268', - }, - date: new Date(2024, 6, 21), - }, - { - icon: mdiLeadPencil, - iconColor: 'gold', - title: 'PostgreSQL NOTIFY is cursed', - description: - 'PostgreSQL does everything in a transaction, including NOTIFY. This means using the socket.io postgres-adapter writes to WAL every 5 seconds.', - link: { url: 'https://github.com/immich-app/immich/pull/10801', text: '#10801' }, - date: new Date(2024, 6, 3), - }, - { - icon: mdiWeb, - iconColor: 'lightskyblue', - title: 'npm scripts are cursed', - description: - 'npm scripts make a http call to the npm registry each time they run, which means they are a terrible way to execute a health check.', - link: { url: 'https://github.com/immich-app/immich/issues/10796', text: '#10796' }, - date: new Date(2024, 6, 3), - }, - { - icon: mdiSpeedometerSlow, - iconColor: 'brown', - title: '50 extra packages are cursed', - description: - 'There is a user in the JavaScript community who goes around adding "backwards compatibility" to projects. They do this by adding 50 extra package dependencies to your project, which are maintained by them.', - link: { url: 'https://github.com/immich-app/immich/pull/10690', text: '#10690' }, - date: new Date(2024, 5, 28), - }, - { - icon: mdiLockOutline, - iconColor: 'gold', - title: 'Long passwords are cursed', - description: - 'The bcrypt implementation only uses the first 72 bytes of a string. Any characters after that are ignored.', - // link: GHSA-4p64-9f7h-3432 - date: new Date(2024, 5, 25), - }, - { - icon: mdiCalendarToday, - iconColor: 'greenyellow', - title: 'JavaScript Date objects are cursed', - description: 'JavaScript date objects are 1 indexed for years and days, but 0 indexed for months.', - link: { url: 'https://github.com/immich-app/immich/pull/6787', text: '#6787' }, - date: new Date(2024, 0, 31), - }, - { - icon: mdiBug, - iconColor: 'green', - title: 'ESM imports are cursed', - description: - 'Prior to Node.js v20.8 using --experimental-vm-modules in a CommonJS project that imported an ES module that imported a CommonJS modules would create a segfault and crash Node.js', - link: { - url: 'https://github.com/immich-app/immich/pull/6719', - text: '#6179', - }, - date: new Date(2024, 0, 9), - }, - { - icon: mdiDatabase, - iconColor: 'gray', - title: 'PostgreSQL parameters are cursed', - description: `PostgresSQL has a limit of ${Number(65535).toLocaleString()} parameters, so bulk inserts can fail with large datasets.`, - link: { - url: 'https://github.com/immich-app/immich/pull/6034', - text: '#6034', - }, - date: new Date(2023, 11, 28), - }, - { - icon: mdiSecurity, - iconColor: 'gold', - title: 'Secure contexts are cursed', - description: `Some web features like the clipboard API only work in "secure contexts" (ie. https or localhost)`, - link: { - url: 'https://github.com/immich-app/immich/issues/2981', - text: '#2981', - }, - date: new Date(2023, 5, 26), - }, - { - icon: mdiTrashCan, - iconColor: 'gray', - title: 'TypeORM deletes are cursed', - description: `The remove implementation in TypeORM mutates the input, deleting the id property from the original object.`, - link: { - url: 'https://github.com/typeorm/typeorm/issues/7024#issuecomment-948519328', - text: 'typeorm#6034', - }, - date: new Date(2023, 1, 23), - }, -]; - -export default function CursedKnowledgePage(): JSX.Element { - return ( - -
-

- Cursed Knowledge -

-

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

-
- b.date.getTime() - a.date.getTime()) - .map((item) => ({ ...item, getDateLabel: withLanguage(item.date) }))} - /> -
-
-
- ); -} diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index e9bf09770c..fed72f21c7 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,4 +2,33 @@ ## TypeORM Upgrade -The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. +If you encountered "Migrations failed: Error: Invalid upgrade path" then perform an intermediate upgrade to `v1.132.3` first. + +:::tip +We recommend users upgrade to `v1.132.3` since it does not have any breaking changes or bugs on this upgrade path. +::: + +In order to update to Immich `v1.137.0` or above, the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to `v1.137.0` (or above). + +:::caution +Avoid `v1.136.0` if upgrading from `v1.131.0` (or earlier) due to a bug blocking this upgrade in some installations. +::: + +## Inconsistent Media Location + +:::caution +This error is related to the location of media files _inside the container_. Never move files on the host system when you run into this error message. +::: + +Immich automatically tries to detect where your Immich data is located. On start up, it compares the detected media location with the file paths in the database and throws an Inconsistent Media Location error when they do not match. + +To fix this issue, verify that the `IMMICH_MEDIA_LOCATION` environment variable and `UPLOAD_LOCATION` volume mount are in sync with the database paths. + +If you would like to migrate from one media location to another, simply successfully start Immich on `v1.136.0` or later, then do the following steps: + +1. Stop Immich +2. Update `IMMICH_MEDIA_LOCATION` to the new location +3. Update the right-hand side of the `UPLOAD_LOCATION` volume mount to the new location +4. Start up Immich + +After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync. diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 277a1d0b46..37455cde16 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -1,123 +1,5 @@ -import React from 'react'; -import Link from '@docusaurus/Link'; -import Layout from '@theme/Layout'; -import { discordPath, discordViewBox } from '@site/src/components/svg-paths'; -import ThemedImage from '@theme/ThemedImage'; -import Icon from '@mdi/react'; - -function HomepageHeader() { - return ( -
-
- Immich logo -
-
-
- - - - -
-

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

- -

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

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

Download the mobile app

-

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

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

This project is available under GNU AGPL v3 license.

-

Privacy should not be a luxury

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

- {title} -

-

{description}

-
- -
-
-
- ); -} diff --git a/docs/static/.well-known/security.txt b/docs/static/.well-known/security.txt deleted file mode 100644 index 5a8414c3e2..0000000000 --- a/docs/static/.well-known/security.txt +++ /dev/null @@ -1,5 +0,0 @@ -Policy: https://github.com/immich-app/immich/blob/main/SECURITY.md -Contact: mailto:security@immich.app -Preferred-Languages: en -Expires: 2026-05-01T23:59:00.000Z -Canonical: https://immich.app/.well-known/security.txt diff --git a/docs/static/_redirects b/docs/static/_redirects index 7b01d1e3bb..ecbdf19303 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -1,34 +1,34 @@ -/docs /docs/overview/welcome 307 -/docs/ /docs/overview/welcome 307 -/docs/mobile-app-beta-program /docs/features/mobile-app 307 -/docs/contribution-guidelines /docs/overview/support-the-project#contributing 307 -/docs/install /docs/install/docker-compose 307 -/docs/installation/one-step-installation /docs/install/script 307 -/docs/installation/portainer-installation /docs/install/portainer 307 -/docs/installation/recommended-installation /docs/install/docker-compose 307 -/docs/installation/unraid /docs/install/unraid 307 -/docs/installation/requirements /docs/install/requirements 307 -/docs/overview/logo-meaning /docs/overview/logo 307 -/docs/overview/technology-stack /docs/developer/architecture 307 -/docs/usage/automatic-backup /docs/features/automatic-backup 307 -/docs/usage/bulk-upload /docs/features/command-line-interface 307 -/docs/features/bulk-upload /docs/features/command-line-interface 307 -/docs/usage/oauth /docs/administration/oauth 307 -/docs/usage/post-installation /docs/install/post-install 307 -/docs/usage/update /docs/install/docker-compose#step-4---upgrading 307 -/docs/usage/server-commands /docs/administration/server-commands 307 -/docs/features/jobs /docs/administration/jobs 307 -/docs/features/oauth /docs/administration/oauth 307 -/docs/features/password-login /docs/administration/password-login 307 -/docs/features/server-commands /docs/administration/server-commands 307 -/docs/features/storage-template /docs/administration/storage-template 307 -/docs/features/user-management /docs/administration/user-management 307 -/docs/developer/contributing /docs/developer/pr-checklist 307 -/docs/guides/machine-learning /docs/guides/remote-machine-learning 307 -/docs/administration/password-login /docs/administration/system-settings 307 -/docs/features/search /docs/features/searching 307 -/docs/features/smart-search /docs/features/searching 307 -/docs/guides/api-album-sync /docs/community-projects 307 -/docs/guides/remove-offline-files /docs/community-projects 307 -/milestones /roadmap 307 -/docs/overview/introduction /docs/overview/welcome 307 +/ /overview/quick-start 307 +/mobile-app-beta-program /features/mobile-app 307 +/contribution-guidelines /overview/support-the-project#contributing 307 +/install /install/docker-compose 307 +/installation/one-step-installation /install/script 307 +/installation/portainer-installation /install/portainer 307 +/installation/recommended-installation /install/docker-compose 307 +/installation/unraid /install/unraid 307 +/installation/requirements /install/requirements 307 +/overview/logo-meaning /overview/logo 307 +/overview/technology-stack /developer/architecture 307 +/usage/automatic-backup /features/automatic-backup 307 +/usage/bulk-upload /features/command-line-interface 307 +/features/bulk-upload /features/command-line-interface 307 +/usage/oauth /administration/oauth 307 +/usage/post-installation /install/post-install 307 +/usage/update /install/docker-compose#step-4---upgrading 307 +/usage/server-commands /administration/server-commands 307 +/features/jobs /administration/jobs 307 +/features/oauth /administration/oauth 307 +/features/password-login /administration/password-login 307 +/features/server-commands /administration/server-commands 307 +/features/storage-template /administration/storage-template 307 +/features/user-management /administration/user-management 307 +/developer/contributing /developer/pr-checklist 307 +/guides/machine-learning /guides/remote-machine-learning 307 +/administration/password-login /administration/system-settings 307 +/features/search /features/searching 307 +/features/smart-search /features/searching 307 +/guides/api-album-sync /community-projects 307 +/guides/remove-offline-files /community-projects 307 +/overview/introduction /overview/quick-start 307 +/overview/welcome /overview/quick-start 307 +/docs/* /:splat 307 diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index adbd9aa717..46dec8c35e 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,162 +1,237 @@ [ + { + "label": "v2.0.1", + "url": "https://docs.v2.0.1.archive.immich.app" + }, + { + "label": "v2.0.0", + "url": "https://docs.v2.0.0.archive.immich.app" + }, + { + "label": "v1.144.1", + "url": "https://docs.v1.144.1.archive.immich.app" + }, + { + "label": "v1.144.0", + "url": "https://docs.v1.144.0.archive.immich.app" + }, + { + "label": "v1.143.1", + "url": "https://docs.v1.143.1.archive.immich.app" + }, + { + "label": "v1.142.1", + "url": "https://v1.142.1.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.141.1", + "url": "https://v1.141.1.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.140.1", + "url": "https://v1.140.1.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.139.4", + "url": "https://v1.139.4.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.138.1", + "url": "https://v1.138.1.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.137.3", + "url": "https://v1.137.3.archive.immich.app", + "rootPath": "/docs" + }, + { + "label": "v1.136.0", + "url": "https://v1.136.0.archive.immich.app", + "rootPath": "/docs" + }, { "label": "v1.135.3", - "url": "https://v1.135.3.archive.immich.app" - }, - { - "label": "v1.135.2", - "url": "https://v1.135.2.archive.immich.app" - }, - { - "label": "v1.135.1", - "url": "https://v1.135.1.archive.immich.app" - }, - { - "label": "v1.135.0", - "url": "https://v1.135.0.archive.immich.app" + "url": "https://v1.135.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.134.0", - "url": "https://v1.134.0.archive.immich.app" + "url": "https://v1.134.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.133.1", - "url": "https://v1.133.1.archive.immich.app" - }, - { - "label": "v1.133.0", - "url": "https://v1.133.0.archive.immich.app" + "url": "https://v1.133.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.132.3", - "url": "https://v1.132.3.archive.immich.app" + "url": "https://v1.132.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.131.3", - "url": "https://v1.131.3.archive.immich.app" + "url": "https://v1.131.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.130.3", - "url": "https://v1.130.3.archive.immich.app" + "url": "https://v1.130.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.129.0", - "url": "https://v1.129.0.archive.immich.app" + "url": "https://v1.129.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.128.0", - "url": "https://v1.128.0.archive.immich.app" + "url": "https://v1.128.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.127.0", - "url": "https://v1.127.0.archive.immich.app" + "url": "https://v1.127.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.126.1", - "url": "https://v1.126.1.archive.immich.app" + "url": "https://v1.126.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.125.7", - "url": "https://v1.125.7.archive.immich.app" + "url": "https://v1.125.7.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.124.2", - "url": "https://v1.124.2.archive.immich.app" + "url": "https://v1.124.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.123.0", - "url": "https://v1.123.0.archive.immich.app" + "url": "https://v1.123.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.122.3", - "url": "https://v1.122.3.archive.immich.app" + "url": "https://v1.122.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.121.0", - "url": "https://v1.121.0.archive.immich.app" + "url": "https://v1.121.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.120.2", - "url": "https://v1.120.2.archive.immich.app" + "url": "https://v1.120.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.119.1", - "url": "https://v1.119.1.archive.immich.app" + "url": "https://v1.119.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.118.2", - "url": "https://v1.118.2.archive.immich.app" + "url": "https://v1.118.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.117.0", - "url": "https://v1.117.0.archive.immich.app" + "url": "https://v1.117.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.116.2", - "url": "https://v1.116.2.archive.immich.app" + "url": "https://v1.116.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.115.0", - "url": "https://v1.115.0.archive.immich.app" + "url": "https://v1.115.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.114.0", - "url": "https://v1.114.0.archive.immich.app" + "url": "https://v1.114.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.113.1", - "url": "https://v1.113.1.archive.immich.app" + "url": "https://v1.113.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.112.1", - "url": "https://v1.112.1.archive.immich.app" + "url": "https://v1.112.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.111.0", - "url": "https://v1.111.0.archive.immich.app" + "url": "https://v1.111.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.110.0", - "url": "https://v1.110.0.archive.immich.app" + "url": "https://v1.110.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.109.2", - "url": "https://v1.109.2.archive.immich.app" + "url": "https://v1.109.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.108.0", - "url": "https://v1.108.0.archive.immich.app" + "url": "https://v1.108.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.107.2", - "url": "https://v1.107.2.archive.immich.app" + "url": "https://v1.107.2.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.106.4", - "url": "https://v1.106.4.archive.immich.app" + "url": "https://v1.106.4.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.105.1", - "url": "https://v1.105.1.archive.immich.app" + "url": "https://v1.105.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.104.0", - "url": "https://v1.104.0.archive.immich.app" + "url": "https://v1.104.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.103.1", - "url": "https://v1.103.1.archive.immich.app" + "url": "https://v1.103.1.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.102.3", - "url": "https://v1.102.3.archive.immich.app" + "url": "https://v1.102.3.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.101.0", - "url": "https://v1.101.0.archive.immich.app" + "url": "https://v1.101.0.archive.immich.app", + "rootPath": "/docs" }, { "label": "v1.100.0", - "url": "https://v1.100.0.archive.immich.app" + "url": "https://v1.100.0.archive.immich.app", + "rootPath": "/docs" } ] diff --git a/docs/static/img/synology-action-clean.png b/docs/static/img/synology-action-clean.png new file mode 100644 index 0000000000..4d168b0bd8 Binary files /dev/null and b/docs/static/img/synology-action-clean.png differ diff --git a/docs/static/img/synology-build.png b/docs/static/img/synology-build.png new file mode 100644 index 0000000000..50c4d0fc98 Binary files /dev/null and b/docs/static/img/synology-build.png differ diff --git a/docs/static/img/synology-container-ip.png b/docs/static/img/synology-container-ip.png new file mode 100644 index 0000000000..21617d8c72 Binary files /dev/null and b/docs/static/img/synology-container-ip.png differ diff --git a/docs/static/img/synology-container-manager-customize-docker-compose.png b/docs/static/img/synology-container-manager-customize-docker-compose.png index 558557487a..2c0a40def0 100644 Binary files a/docs/static/img/synology-container-manager-customize-docker-compose.png and b/docs/static/img/synology-container-manager-customize-docker-compose.png differ diff --git a/docs/static/img/synology-custom-port-firewall-rule.png b/docs/static/img/synology-custom-port-firewall-rule.png new file mode 100644 index 0000000000..26ee17785c Binary files /dev/null and b/docs/static/img/synology-custom-port-firewall-rule.png differ diff --git a/docs/static/img/synology-fw-ipedit.png b/docs/static/img/synology-fw-ipedit.png new file mode 100644 index 0000000000..7f4e561395 Binary files /dev/null and b/docs/static/img/synology-fw-ipedit.png differ diff --git a/docs/static/img/synology-fw-rules.png b/docs/static/img/synology-fw-rules.png new file mode 100644 index 0000000000..2ec43a682f Binary files /dev/null and b/docs/static/img/synology-fw-rules.png differ diff --git a/docs/static/img/synology-ipaddress-firewall-rule.png b/docs/static/img/synology-ipaddress-firewall-rule.png new file mode 100644 index 0000000000..d1982b053d Binary files /dev/null and b/docs/static/img/synology-ipaddress-firewall-rule.png differ diff --git a/docs/static/img/synology-project-stop.png b/docs/static/img/synology-project-stop.png new file mode 100644 index 0000000000..8a77446dc2 Binary files /dev/null and b/docs/static/img/synology-project-stop.png differ diff --git a/docs/static/img/synology-remove-unused.png b/docs/static/img/synology-remove-unused.png new file mode 100644 index 0000000000..9b1a217902 Binary files /dev/null and b/docs/static/img/synology-remove-unused.png differ diff --git a/docs/static/img/synology-select-proj.png b/docs/static/img/synology-select-proj.png new file mode 100644 index 0000000000..21642d8713 Binary files /dev/null and b/docs/static/img/synology-select-proj.png differ diff --git a/e2e/.gitignore b/e2e/.gitignore index 68c5d18f00..bbc06c5549 100644 --- a/e2e/.gitignore +++ b/e2e/.gitignore @@ -3,3 +3,4 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ +/dist diff --git a/e2e/.nvmrc b/e2e/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 26c3951278..baf63cfe9c 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -35,10 +35,10 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0 + image: redis:6.2-alpine@sha256:2185e741f4c1e7b0ea9ca1e163a3767c4270a73086b6bbea2049a7203212fb7f database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:11ced39d65a92a54d12890ced6a26cc2003f92697d6f0d4d944b98459dba7138 command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres diff --git a/e2e/package-lock.json b/e2e/package-lock.json deleted file mode 100644 index 1e2f8b47e1..0000000000 --- a/e2e/package-lock.json +++ /dev/null @@ -1,7410 +0,0 @@ -{ - "name": "immich-e2e", - "version": "1.135.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "immich-e2e", - "version": "1.135.3", - "license": "GNU Affero General Public License version 3", - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.8.0", - "@immich/cli": "file:../cli", - "@immich/sdk": "file:../open-api/typescript-sdk", - "@playwright/test": "^1.44.1", - "@socket.io/component-emitter": "^3.1.2", - "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", - "@types/oidc-provider": "^9.0.0", - "@types/pg": "^8.15.1", - "@types/pngjs": "^6.0.4", - "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^3.0.0", - "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", - "exiftool-vendored": "^28.3.1", - "globals": "^16.0.0", - "jose": "^5.6.3", - "luxon": "^3.4.4", - "oidc-provider": "^9.0.0", - "pg": "^8.11.3", - "pngjs": "^7.0.0", - "prettier": "^3.2.5", - "prettier-plugin-organize-imports": "^4.0.0", - "sharp": "^0.34.0", - "socket.io-client": "^4.7.4", - "supertest": "^7.0.0", - "typescript": "^5.3.3", - "typescript-eslint": "^8.28.0", - "utimes": "^5.2.1", - "vitest": "^3.0.0" - } - }, - "../cli": { - "name": "@immich/cli", - "version": "2.2.72", - "dev": true, - "license": "GNU Affero General Public License version 3", - "dependencies": { - "chokidar": "^4.0.3", - "fast-glob": "^3.3.2", - "fastq": "^1.17.1", - "lodash-es": "^4.17.21", - "micromatch": "^4.0.8" - }, - "bin": { - "immich": "bin/immich" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.8.0", - "@immich/sdk": "file:../open-api/typescript-sdk", - "@types/byte-size": "^8.1.0", - "@types/cli-progress": "^3.11.0", - "@types/lodash-es": "^4.17.12", - "@types/micromatch": "^4.0.9", - "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", - "@vitest/coverage-v8": "^3.0.0", - "byte-size": "^9.0.0", - "cli-progress": "^3.12.0", - "commander": "^12.0.0", - "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", - "globals": "^16.0.0", - "mock-fs": "^5.2.0", - "prettier": "^3.2.5", - "prettier-plugin-organize-imports": "^4.0.0", - "typescript": "^5.3.3", - "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.0.0", - "vitest": "^3.0.0", - "vitest-fetch-mock": "^0.4.0", - "yaml": "^2.3.1" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "../open-api/typescript-sdk": { - "name": "@immich/sdk", - "version": "1.135.3", - "dev": true, - "license": "GNU Affero General Public License version 3", - "dependencies": { - "@oazapfts/runtime": "^1.0.2" - }, - "devDependencies": { - "@types/node": "^22.15.33", - "typescript": "^5.3.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/runtime": { - "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==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "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/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, - "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", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "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" - ], - "dev": true, - "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.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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@immich/cli": { - "resolved": "../cli", - "link": true - }, - "node_modules/@immich/sdk": { - "resolved": "../open-api/typescript-sdk", - "link": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@koa/cors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-5.0.0.tgz", - "integrity": "sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", - "dev": true, - "license": "MIT", - "dependencies": { - "http-errors": "^2.0.0", - "koa-compose": "^4.1.0", - "path-to-regexp": "^6.3.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@photostructure/tz-lookup": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-11.2.0.tgz", - "integrity": "sha512-DwrvodcXHNSdGdeSF7SBL5o8aBlsaeuCuG7633F04nYsL3hn5Hxe3z/5kCqxv61J1q7ggKZ27GPylR3x0cPNXQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", - "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.53.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", - "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", - "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", - "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", - "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", - "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", - "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", - "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", - "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", - "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", - "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", - "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", - "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", - "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", - "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", - "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", - "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", - "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", - "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", - "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", - "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", - "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/content-disposition": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.8.tgz", - "integrity": "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cookies": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", - "integrity": "sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", - "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-assert": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.6.tgz", - "integrity": "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keygrip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", - "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/koa": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.0.tgz", - "integrity": "sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/accepts": "*", - "@types/content-disposition": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/http-errors": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" - } - }, - "node_modules/@types/koa-compose": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.8.tgz", - "integrity": "sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/koa": "*" - } - }, - "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/oidc-provider": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/oidc-provider/-/oidc-provider-9.1.1.tgz", - "integrity": "sha512-sG4UcE4AbUwAsEpyrcyoqZ383wJiQObZU+gTa1Iv288+l09HwSr88hBZE2IBLlXS+RKmLId0i4B430PBFO/XRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/keygrip": "*", - "@types/koa": "*", - "@types/node": "*" - } - }, - "node_modules/@types/pg": { - "version": "8.15.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", - "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pngjs": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", - "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/supertest": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", - "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/batch-cluster": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", - "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/builtin-modules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cache-content-type": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001713", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", - "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookies": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", - "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", - "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "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" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", - "esquery": "^1.6.0", - "find-up-simple": "^1.0.1", - "globals": "^16.0.0", - "indent-string": "^5.0.0", - "is-builtin-module": "^5.0.0", - "jsesc": "^3.1.0", - "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.12.0", - "semver": "^7.7.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/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/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eta": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", - "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" - } - }, - "node_modules/exiftool-vendored": { - "version": "28.8.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.8.0.tgz", - "integrity": "sha512-R7tirJLr9fWuH9JS/KFFLB+O7jNGKuPXGxREc6YybYangEudGb+X8ERsYXk9AifMiAWh/2agNfbgkbcQcF+MxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@photostructure/tz-lookup": "^11.0.0", - "@types/luxon": "^3.4.2", - "batch-cluster": "^13.0.0", - "he": "^1.2.0", - "luxon": "^3.5.0" - }, - "optionalDependencies": { - "exiftool-vendored.exe": "13.0.0", - "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==", - "dev": true, - "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", - "integrity": "sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "!win32" - ] - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/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/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/gauge/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/gauge/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/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/glob/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/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", - "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-equal": "~1.0.1", - "http-errors": "~1.8.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-assert/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-assert/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-assert/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-builtin-module": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", - "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "builtin-modules": "^5.0.0" - }, - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/jose": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", - "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tsscmp": "1.0.6" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/koa": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/koa/-/koa-3.0.0.tgz", - "integrity": "sha512-Usyqf1o+XN618R3Jzq4S4YWbKsRtPcGpgyHXD4APdGYQQyqQ59X+Oyc7fXHS2429stzLsBiDjj6zqqYe8kknfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.9.1", - "debug": "^4.3.2", - "delegates": "^1.0.0", - "destroy": "^1.0.4", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^2.0.0", - "koa-compose": "^4.1.0", - "on-finished": "^2.3.0", - "parseurl": "^1.3.2", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", - "dev": true, - "license": "MIT" - }, - "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/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/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/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", - "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/oidc-provider": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.2.0.tgz", - "integrity": "sha512-L0JL1ymI/hLKzDRqYzhKluNfRRQUR0++q5fTTziniKmJgNrJ6DnI5h5SP6w8Z0U/3wZrCndpVmbbu0VpKpY0CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@koa/cors": "^5.0.0", - "@koa/router": "^13.1.0", - "debug": "^4.4.1", - "eta": "^3.5.0", - "jose": "^6.0.11", - "jsesc": "^3.1.0", - "koa": "^3.0.0", - "nanoid": "^5.1.5", - "oidc-token-hash": "^5.1.0", - "quick-lru": "^7.0.1", - "raw-body": "^3.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/oidc-provider/node_modules/jose": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", - "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "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==", - "dev": true, - "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", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "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", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.53.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pngjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", - "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.19.0" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": ">=2.0", - "typescript": ">=2.9", - "vue-tsc": "^2.1.0" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.0.1.tgz", - "integrity": "sha512-kLjThirJMkWKutUKbZ8ViqFc09tDQhlbQo2MNuVeLWbRauqYP96Sm6nzlQ24F0HFjUNZ4i9+AgldJ9H6DZXi7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.41.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", - "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.41.1", - "@rollup/rollup-android-arm64": "4.41.1", - "@rollup/rollup-darwin-arm64": "4.41.1", - "@rollup/rollup-darwin-x64": "4.41.1", - "@rollup/rollup-freebsd-arm64": "4.41.1", - "@rollup/rollup-freebsd-x64": "4.41.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", - "@rollup/rollup-linux-arm-musleabihf": "4.41.1", - "@rollup/rollup-linux-arm64-gnu": "4.41.1", - "@rollup/rollup-linux-arm64-musl": "4.41.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", - "@rollup/rollup-linux-riscv64-gnu": "4.41.1", - "@rollup/rollup-linux-riscv64-musl": "4.41.1", - "@rollup/rollup-linux-s390x-gnu": "4.41.1", - "@rollup/rollup-linux-x64-gnu": "4.41.1", - "@rollup/rollup-linux-x64-musl": "4.41.1", - "@rollup/rollup-win32-arm64-msvc": "4.41.1", - "@rollup/rollup-win32-ia32-msvc": "4.41.1", - "@rollup/rollup-win32-x64-msvc": "4.41.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@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.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true, - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^10.2.2" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/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/test-exclude/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/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "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.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", - "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.x" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/type-is/node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/utimes": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/utimes/-/utimes-5.2.1.tgz", - "integrity": "sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^4.3.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "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", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "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.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/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/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wide-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/ylru": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", - "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/e2e/package.json b/e2e/package.json index 24b97bb4b7..9d2a33ba25 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.135.3", + "version": "2.0.1", "description": "", "main": "index.js", "type": "module", @@ -19,23 +19,21 @@ "author": "", "license": "GNU Affero General Public License version 3", "devDependencies": { - "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@immich/cli": "file:../cli", "@immich/sdk": "file:../open-api/typescript-sdk", "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.18.8", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "exiftool-vendored": "^28.3.1", "globals": "^16.0.0", "jose": "^5.6.3", @@ -45,7 +43,7 @@ "pngjs": "^7.0.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.0.0", - "sharp": "^0.34.0", + "sharp": "^0.34.3", "socket.io-client": "^4.7.4", "supertest": "^7.0.0", "typescript": "^5.3.3", @@ -54,6 +52,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" } } diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index eedf70dc58..5615a312f2 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -470,7 +470,7 @@ describe('/albums', () => { .send({ ids: [asset.id] }); expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest('Not found or no album.addAsset access')); + expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.create access')); }); it('should add duplicate assets only once', async () => { @@ -599,7 +599,7 @@ describe('/albums', () => { .send({ ids: [user1Asset1.id] }); expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest('Not found or no album.removeAsset access')); + expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.delete access')); }); it('should remove duplicate assets only once', async () => { @@ -683,7 +683,7 @@ describe('/albums', () => { .set('Authorization', `Bearer ${user1.accessToken}`) .send({ role: AlbumUserRole.Editor }); - expect(status).toBe(200); + expect(status).toBe(204); // Get album to verify the role change const { body } = await request(app) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index c1e9f9dfb8..5c30ff5cbe 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -555,7 +555,7 @@ describe('/asset', () => { expect(body).toMatchObject({ id: user1Assets[0].id, livePhotoVideoId: null }); }); - it('should update date time original when sidecar file contains DateTimeOriginal', async () => { + it.skip('should update date time original when sidecar file contains DateTimeOriginal', async () => { const sidecarData = ` @@ -854,6 +854,30 @@ describe('/asset', () => { }); }); + describe('PUT /assets', () => { + it('should update date time original relatively', async () => { + const { status, body } = await request(app) + .put(`/assets/`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send({ ids: [user1Assets[0].id], dateTimeRelative: -1441 }); + + expect(body).toEqual({}); + expect(status).toEqual(204); + + const result = await request(app) + .get(`/assets/${user1Assets[0].id}`) + .set('Authorization', `Bearer ${user1.accessToken}`) + .send(); + + expect(result.body).toMatchObject({ + id: user1Assets[0].id, + exifInfo: expect.objectContaining({ + dateTimeOriginal: '2023-11-19T01:10:00+00:00', + }), + }); + }); + }); + describe('POST /assets', () => { beforeAll(setupTests, 30_000); @@ -1442,10 +1466,10 @@ describe('/asset', () => { expectedDate: '2023-04-04T04:00:00.000Z', }, { - name: 'CreateDate when DateTimeOriginal missing', + name: 'CreationDate when DateTimeOriginal missing', exifData: { - CreateDate: '2023:05:05 05:00:00', // TESTABLE - CreationDate: '2023:07:07 07:00:00', // TESTABLE + CreationDate: '2023:05:05 05:00:00', // TESTABLE + CreateDate: '2023:07:07 07:00:00', // TESTABLE GPSDateTime: '2023:10:10 10:00:00', // TESTABLE }, expectedDate: '2023-05-05T05:00:00.000Z', diff --git a/e2e/src/api/specs/partner.e2e-spec.ts b/e2e/src/api/specs/partner.e2e-spec.ts index 1654f04e18..9047a97055 100644 --- a/e2e/src/api/specs/partner.e2e-spec.ts +++ b/e2e/src/api/specs/partner.e2e-spec.ts @@ -23,8 +23,8 @@ describe('/partners', () => { ]); await Promise.all([ - createPartner({ id: user2.userId }, { headers: asBearerAuth(user1.accessToken) }), - createPartner({ id: user1.userId }, { headers: asBearerAuth(user2.accessToken) }), + createPartner({ partnerCreateDto: { sharedWithId: user2.userId } }, { headers: asBearerAuth(user1.accessToken) }), + createPartner({ partnerCreateDto: { sharedWithId: user1.userId } }, { headers: asBearerAuth(user2.accessToken) }), ]); }); @@ -116,7 +116,7 @@ describe('/partners', () => { .delete(`/partners/${user3.userId}`) .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); it('should throw a bad request if partner not found', async () => { diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts index d9f8672c66..f25a54786a 100644 --- a/e2e/src/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -9,7 +9,7 @@ import { } from '@immich/sdk'; import { createUserDto, uuidDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; -import { app, asBearerAuth, shareUrl, utils } from 'src/utils'; +import { app, asBearerAuth, baseUrl, shareUrl, utils } from 'src/utils'; import request from 'supertest'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -78,6 +78,7 @@ describe('/shared-links', () => { type: SharedLinkType.Album, albumId: metadataAlbum.id, showMetadata: true, + slug: 'metadata-album', }), utils.createSharedLink(user1.accessToken, { type: SharedLinkType.Album, @@ -138,6 +139,17 @@ describe('/shared-links', () => { }); }); + describe('GET /s/:slug', () => { + it('should work for slug auth', async () => { + const resp = await request(baseUrl).get(`/s/${linkWithMetadata.slug}`); + expect(resp.status).toBe(200); + expect(resp.header['content-type']).toContain('text/html'); + expect(resp.text).toContain( + ``, + ); + }); + }); + describe('GET /shared-links', () => { it('should require authentication', async () => { const { status, body } = await request(app).get('/shared-links'); @@ -473,7 +485,7 @@ describe('/shared-links', () => { .delete(`/shared-links/${linkWithAlbum.id}`) .set('Authorization', `Bearer ${user1.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); }); }); diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts index b9eb140c56..3f280dddf5 100644 --- a/e2e/src/api/specs/user.e2e-spec.ts +++ b/e2e/src/api/specs/user.e2e-spec.ts @@ -304,7 +304,7 @@ describe('/users', () => { const { status } = await request(app) .delete(`/users/me/license`) .set('Authorization', `Bearer ${nonAdmin.accessToken}`); - expect(status).toBe(200); + expect(status).toBe(204); }); }); }); diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 3fcc4ab552..b33d6cb190 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -79,7 +79,7 @@ export const tempDir = tmpdir(); export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` }); export const asKeyAuth = (key: string) => ({ 'x-api-key': key }); export const immichCli = (args: string[]) => - executeCommand('node', ['node_modules/.bin/immich', '-d', `/${tempDir}/immich/`, ...args]).promise; + executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../cli' }).promise; export const immichAdmin = (args: string[]) => executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', `immich-admin ${args.join(' ')}`]); export const specialCharStrings = ["'", '"', ',', '{', '}', '*']; @@ -186,18 +186,6 @@ export const utils = { } }, - resetFilesystem: async () => { - const mediaInternal = '/usr/src/app/upload'; - const dirs = [ - `"${mediaInternal}/thumbs"`, - `"${mediaInternal}/upload"`, - `"${mediaInternal}/library"`, - `"${mediaInternal}/encoded-video"`, - ].join(' '); - - await execPromise(`docker exec -i "immich-e2e-server" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`); - }, - unzip: async (input: string, output: string) => { await execPromise(`unzip -o -d "${output}" "${input}"`); }, @@ -474,7 +462,8 @@ export const utils = { updateLibrary: (accessToken: string, id: string, dto: UpdateLibraryDto) => updateLibrary({ id, updateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }), - createPartner: (accessToken: string, id: string) => createPartner({ id }, { headers: asBearerAuth(accessToken) }), + createPartner: (accessToken: string, id: string) => + createPartner({ partnerCreateDto: { sharedWithId: id } }, { headers: asBearerAuth(accessToken) }), updateMyPreferences: (accessToken: string, userPreferencesUpdateDto: UserPreferencesUpdateDto) => updateMyPreferences({ userPreferencesUpdateDto }, { headers: asBearerAuth(accessToken) }), diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index 0fde9a6ec6..173131ec5e 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -37,6 +37,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Server Privacy' }).click(); await page.getByRole('button', { name: 'User Privacy' }).click(); await page.getByRole('button', { name: 'Storage Template' }).click(); + await page.getByRole('button', { name: 'Backups' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success diff --git a/e2e/test-assets b/e2e/test-assets index 18736fc27a..37f60ea537 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 +Subproject commit 37f60ea537c0228f5f92e4f42dc42f0bb39a6d7f diff --git a/i18n/af.json b/i18n/af.json index 55555c8398..fce944504b 100644 --- a/i18n/af.json +++ b/i18n/af.json @@ -4,6 +4,7 @@ "account_settings": "Rekeninginstellings", "acknowledge": "Erken", "action": "Aksie", + "action_common_update": "Opdateur", "actions": "Aksies", "active": "Aktief", "activity": "Aktiwiteite", @@ -13,6 +14,8 @@ "add_a_location": "Voeg 'n ligging by", "add_a_name": "Voeg 'n naam by", "add_a_title": "Voeg 'n titel by", + "add_birthday": "Voeg 'n verjaarsdag by", + "add_endpoint": "Voeg Koppelvlakpunt by", "add_exclusion_pattern": "Voeg uitsgluitingspatrone by", "add_import_path": "Voeg invoerpad by", "add_location": "Voeg ligging by", @@ -20,26 +23,37 @@ "add_partner": "Voeg vennoot by", "add_path": "Voeg pad by", "add_photos": "Voeg foto's by", + "add_tag": "Voeg tag by", "add_to": "Voeg byâ€Ļ", "add_to_album": "Voeg na album", - "add_to_shared_album": "Voeg na gedeelde album", + "add_to_album_bottom_sheet_added": "By {album} bygevoeg", + "add_to_album_bottom_sheet_already_exists": "Reeds in {album}", + "add_to_albums": "Voeg by albums", + "add_to_albums_count": "Voeg by ({count}) albums", + "add_to_shared_album": "Voeg toe aan gedeelde album", "add_url": "Voeg URL by", - "added_to_archive": "By argief gevoeg", - "added_to_favorites": "By gunstelinge gevoeg", - "added_to_favorites_count": "Het {count, number} by gunstelinge gevoeg", + "added_to_archive": "By argief toegevoegd", + "added_to_favorites": "By gunstelinge toegevoegd", + "added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd", "admin": { "add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lÃĒers in enige lÃĒergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lÃĒers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".", + "admin_user": "Admin gebruiker", "asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lÃĒer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lÃĒerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.", "authentication_settings": "Verifikasie instellings", "authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings", "authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.", "authentication_settings_reenable": "Om te heraktiveer, gebruik 'n Server Command.", "background_task_job": "Agtergrondtake", - "backup_database": "Rugsteun databasis", + "backup_database": "Skep DatastortlÃĒer", "backup_database_enable_description": "Aktiveer databasisrugsteun", "backup_keep_last_amount": "Aantal vorige rugsteune om te hou", + "backup_onboarding_3_description": "totale kopieÃĢ van jou data, insluitende die oorspronklikke lÃĒers. Dit sluit in 1 kopie op 'n ander perseel en 2 kopieÃĢ om die huidige rekenaar.", + "backup_onboarding_description": "'N 3-2-1 rugsteun strategie word sterk aanbeveel om jou data veilig te hou. Hou kopieÃĢ van jou fotos/videos so wel as die Immich databasis vir 'n volledige rugsteun oplossing.", + "backup_onboarding_footer": "Vir meer inligting oor hoe om 'n rugsteun kopie van Immich te maak, gaan lees asseblief hierdie dokument.", + "backup_onboarding_parts_title": "'N 3-2-1 rugsteun sluit in:", + "backup_onboarding_title": "Rugsteun kopieÃĢ", "backup_settings": "Rugsteun instellings", - "backup_settings_description": "Bestuur databasis rugsteun instellings", + "backup_settings_description": "Bestuur databasis rugsteun instellings.", "cleared_jobs": "Poste gevee vir: {job}", "config_set_by_file": "Config word tans deur 'n konfigurasielÃĒer gestel", "confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?", @@ -47,6 +61,7 @@ "confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder", "confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.", "confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?", + "confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?", "create_job": "Skep werk", "cron_expression": "Cron uitdrukking", "cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. Crontab Guru", @@ -55,11 +70,15 @@ "duplicate_detection_job_description": "Begin masjienleer op bates om soortgelyke beelde op te spoor. Maak staat op Smart Search", "exclusion_pattern_description": "Met uitsluitingspatrone kan jy lÃĒers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lÃĒers bevat wat jy nie wil invoer nie, soos RAW-lÃĒers.", "external_library_management": "Eksterne Biblioteekbestuur", - "face_detection": "Gesig deteksie", + "face_detection": "Gesig herkenning", + "face_detection_description": "Identifiseer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Geidentifiseerde gesigte sal nÃĄ voltooiing van Gesigidentifikasie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.", + "facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.", "failed_job_command": "Opdrag {command} het misluk vir werk: {job}", "force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lÃĒers kan nie herstel word nie.", "image_format": "Formaat", "image_format_description": "WebP produseer kleiner lÃĒers as JPEG, maar is stadiger om te enkodeer.", + "image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem", + "image_fullsize_enabled": "Skakel aan vol grote prent generasie", "image_prefer_embedded_preview": "Verkies ingebedde voorskou", "image_prefer_wide_gamut": "Verkies wide gamut", "image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.", @@ -77,8 +96,117 @@ "job_concurrency": "{job} gelyktydigheid", "job_created": "Taak gemaak", "job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.", - "job_settings": "Agtergrondtaakinstellings" + "job_settings": "Agtergrondtaakinstellings", + "job_settings_description": "Bestuur werkgelyktydigheid", + "job_status": "Werkstatus", + "library_created": "Biblioteek geskep: {library}", + "library_deleted": "Biblioteek verwyder", + "library_import_path_description": "Spesifiseer 'n leer om in te neem. Hierdie leer, en al die sub leers, gaan deursoek word vir prente en videos.", + "library_scanning": "Periodieke Soek", + "library_scanning_description": "Stel periodieke deursoek van biblioteek in", + "library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering", + "library_settings": "Eksterne Biblioteek", + "library_settings_description": "Eksterne biblioteek verstellings", + "library_tasks_description": "Deursoek eksterne biblioteke vir nuwe of veranderde bates", + "library_watching_enable_description": "Hou eksterne biblioteke dop vir leer veranderinge", + "library_watching_settings": "Biblioteek dop hou (EKSPERIMENTEEL)", + "library_watching_settings_description": "Hou automaties dop vir veranderinge", + "logging_enable_description": "Aktifeer \"logging\"", + "logging_level_description": "Wanneer aktief, watter vlak van \"logs\" om te skep.", + "logging_settings": "\"Logs\"", + "machine_learning_clip_model": "CLIP model", + "machine_learning_duplicate_detection": "Duplikaat herkenning", + "machine_learning_duplicate_detection_enabled": "Aktifeer duplikaat herkenning", + "machine_learning_enabled": "Aktifeer masjienleer", + "machine_learning_facial_recognition": "Gesigsherkenning", + "machine_learning_facial_recognition_description": "Herken, identifiseer en groepeer gesigte in fotos", + "machine_learning_facial_recognition_model": "Gesigsherkennings model", + "machine_learning_facial_recognition_setting": "Aktifeer gesigsherkenning", + "machine_learning_max_detection_distance": "Maksimum herkennings afstand", + "map_settings": "Kaart", + "migration_job": "Migrasie", + "oauth_settings": "OAuth", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_preferred_hardware_device": "Verkiesde hardeware" }, + "administration": "Administrasie", + "advanced": "Gevorderde", + "albums": "Albums", + "all": "Alle", + "anti_clockwise": "Anti-kloksgewys", + "archive": "Argief", + "asset_skipped": "Oorgeslaan", + "asset_uploaded": "Opgelaai", + "asset_uploading": "Oplaaiâ€Ļ", + "assets": "Bates", + "back": "Terug", + "backward": "Agteruit", + "build": "Bou", + "camera": "Kamera", + "cancel": "Kanselleer", + "city": "Stad", + "clockwise": "Kloksgewys", + "close": "Maak toe", + "color": "Kleur", + "confirm": "Bevestig", + "contain": "Bevat", + "context": "Konteks", + "continue": "Gaan voort", + "country": "Land", + "cover": "Bedek", + "create": "Skep", + "created": "Geskep", + "dark": "Donker", + "day": "Dag", + "delete": "Verwyder", + "description": "Beskrywing", + "details": "Besonderhede", + "direction": "Rigting", + "discover": "Ontdek", + "documentation": "Dokumentasie", + "done": "Klaar", + "download": "Aflaai", + "download_settings": "Aflaai", + "duplicates": "Duplikate", + "duration": "Duur", + "edit": "Wysig", + "edited": "Gewysigd", "search_by_description": "Soek by beskrywing", - "search_by_description_example": "Stapdag in Sapa" + "search_by_description_example": "Stapdag in Sapa", + "version": "Weergawe", + "version_announcement_closing": "Jou friend, Alex", + "version_history": "Weergawegeskiedenis", + "version_history_item": "{version} geinstaleerd op {date}", + "video": "Video", + "videos": "Video's", + "view": "Bekyk", + "view_album": "Bekyk Album", + "view_all": "Bekyk alle", + "view_all_users": "Bekyk alle gebruikers", + "view_in_timeline": "Bekyk in tydlyn", + "view_link": "Bekyk skakel", + "view_links": "Bekyk skakels", + "view_name": "Bekyk", + "view_next_asset": "Bekyk volgende bate", + "view_previous_asset": "Bekyk vorige bate", + "view_qr_code": "Bekyk QR-kode", + "view_stack": "Bekyk stapel", + "view_user": "Bekyk gebruiker", + "viewer_remove_from_stack": "Verwyder van stapel", + "viewer_stack_use_as_main_asset": "Gebruik as hoofbate", + "viewer_unstack": "Ontstapel", + "visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}", + "waiting": "Wag", + "warning": "Waaskuwing", + "week": "Week", + "welcome": "Welkom", + "welcome_to_immich": "Welkom by Immich", + "wifi_name": "Wi-Fi Naam", + "wrong_pin_code": "Verkeerde PIN-kode", + "year": "Jaar", + "years_ago": "{years, plural, one {# year} other {# years}} gelede", + "yes": "Ja", + "you_dont_have_any_shared_links": "Jy het geen gedeelde skakels", + "your_wifi_name": "Jou Wi-Fi naam", + "zoom_image": "Vergroot Prent" } diff --git a/i18n/ar.json b/i18n/ar.json index 67f7752ae1..a94c920bec 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -1,19 +1,21 @@ { - "about": "Ų…Ų† Ų†Ø­Ų†", - "account": "Ø§Ų„Ø­ØŗØ§Ø¨", + "about": "Ø­ŲŽŲˆŲ’Ų„", + "account": "Ø­ØŗØ§Ø¨", "account_settings": "ØĨؚداداØĒ Ø§Ų„Ø­ØŗØ§Ø¨", "acknowledge": "ØŖŲØ¯ØąŲƒ Ø°Ų„Ųƒ", - "action": "Ø§Ų„ØĒØ­ŲƒŲ…", + "action": "ØšŲ…Ų„ŲŠØŠ", "action_common_update": "ØĒØ­Ø¯ŲŠØĢ", - "actions": "Ø§Ų„ØšŲ…Ų„ŲŠØ§ØĒ", + "actions": "ØšŲ…Ų„ŲŠØ§ØĒ", "active": "Ų†Ø´Øˇ", - "activity": "Ø§Ų„Ų†Ø´Ø§Øˇ", + "activity": "Ų†Ø´Ø§Øˇ", "activity_changed": "Ø§Ų„Ų†Ø´Ø§Øˇ {enabled, select, true {Ų…ŲŲŲ’ØšŲ„} other {Ų…ØšØˇŲ‘Ų„}}", "add": "ØĨØļØ§ŲØŠ", "add_a_description": "ØĨØļØ§ŲØŠ ؈Øĩ؁", "add_a_location": "ØĨØļØ§ŲØŠ Ų…ŲˆŲ‚Øš", "add_a_name": "ØĨØļØ§ŲØŠ ØĨØŗŲ…", "add_a_title": "ØĨØļØ§ŲØŠ ØšŲ†ŲˆØ§Ų†", + "add_birthday": "ØŖØļ؁ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯", + "add_endpoint": "اØļ؁ Ų†Ų‚ØˇØŠ Ų†Ų‡Ø§ŲŠØŠ", "add_exclusion_pattern": "ØĨØļØ§ŲØŠ Ų†Ų…Øˇ ØĨØŗØĒØĢŲ†Ø§ØĄ", "add_import_path": "ØĨØļØ§ŲØŠ Ų…ØŗØ§Øą Ø§Ų„ØĨØŗØĒŲŠØąØ§Ø¯", "add_location": "ØĨØļØ§ŲØŠ Ų…ŲˆŲ‚Øš", @@ -21,28 +23,41 @@ "add_partner": "ØŖØļ؁ Ø´ØąŲŠŲƒŲ‹Ø§", "add_path": "ØĨØļØ§ŲØŠ Ų…ØŗØ§Øą", "add_photos": "ØĨØļØ§ŲØŠ ØĩŲˆØą", + "add_tag": "اØļ؁ ØšŲ„Ø§Ų…ØŠ", "add_to": "ØĨØļØ§ŲØŠ ØĨŲ„Ų‰â€Ļ", "add_to_album": "ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØŖŲ„Ø¨ŲˆŲ…", - "add_to_album_bottom_sheet_added": "ØĒŲ…ØĒ Ø§Ų„Ø§ØļØ§ŲØŠ{album}", - "add_to_album_bottom_sheet_already_exists": "Ų…ŲˆØŦŲˆØ¯ØŠ Ų…ØŗØ¨Ų‚Ø§ {album}", - "add_to_shared_album": "ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØŖŲ„Ø¨ŲˆŲ… Ų…Ø´ØĒØąŲƒ", + "add_to_album_bottom_sheet_added": "ØĒŲ…ØĒ Ø§Ų„Ø§ØļØ§ŲØŠ Ø§Ų„Ų‰ {album}", + "add_to_album_bottom_sheet_already_exists": "Ų…ŲˆØŦŲˆØ¯ Ų…ØŗØ¨Ų‚Ø§ ؁؊ {album}", + "add_to_album_bottom_sheet_some_local_assets": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨØļØ§ŲØŠ بؚØļ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", + "add_to_album_toggle": "ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„ØĒØ­Ø¯ŲŠØ¯ Ų„Ų€{album}", + "add_to_albums": "ØĨØļØ§ŲØŠ Ø§Ų„Ų‰ Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ", + "add_to_albums_count": "ØĨØļØ§ŲŲ‡ ØĨŲ„Ų‰ Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ ({count})", + "add_to_shared_album": "ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØŖŲ„Ø¨ŲˆŲ… Ų…Ø´Ø§ØąŲƒ", "add_url": "ØĨØļØ§ŲØŠ ØąØ§Ø¨Øˇ", "added_to_archive": "ØŖŲØļ؊؁ØĒ Ų„Ų„ØŖØąØ´ŲŠŲ", "added_to_favorites": "ØŖŲØļ؊؁ØĒ ؄؄؅؁ØļŲ„Ø§ØĒ", "added_to_favorites_count": "ØĒŲ… ØĨØļØ§ŲØŠ {count, number} ØĨŲ„Ų‰ Ø§Ų„Ų…ŲØļŲ„Ø§ØĒ", "admin": { "add_exclusion_pattern_description": "ØĨØļØ§ŲØŠ ØŖŲ†Ų…Ø§Øˇ Ø§Ų„Ø§ØŗØĒبؚاد. ŲŠØ¯ØšŲ… Ø§Ų„ØĒŲ…ŲˆŲŠŲ‡ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… *، **، ŲˆØŸ. Ų„ØĒØŦØ§Ų‡Ų„ ØŦŲ…ŲŠØš Ø§Ų„Ų…Ų„ŲØ§ØĒ ؁؊ ØŖŲŠ Ø¯Ų„ŲŠŲ„ ŲŠØŗŲ…Ų‰ \"Raw\"، Ø§ØŗØĒØŽØ¯Ų… \"**/Raw/**\". Ų„ØĒØŦØ§Ų‡Ų„ ØŦŲ…ŲŠØš Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ†ØĒŲ‡ŲŠ Ø¨Ų€ \".tif\"، Ø§ØŗØĒØŽØ¯Ų… \"**/*.tif\". Ų„ØĒØŦØ§Ų‡Ų„ Ų…ØŗØ§Øą Ų…ØˇŲ„Ų‚ØŒ Ø§ØŗØĒØŽØ¯Ų… \"/path/to/ignore/**\".", + "admin_user": "Ų…ØŗØĒØŽØ¯Ų… Ų…ØŗØ¤ŲˆŲ„", "asset_offline_description": "Ų„Ų… ŲŠØšØ¯ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ Ø§Ų„ØŽØ§Øĩ Ø¨Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ Ų…ŲˆØŦŲˆØ¯Ų‹Ø§ ØšŲ„Ų‰ Ø§Ų„Ų‚ØąØĩ ؈ØĒŲ… Ų†Ų‚Ų„Ų‡ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ. ØĨذا ØĒŲ… Ų†Ų‚Ų„ Ø§Ų„Ų…Ų„Ų Ø¯Ø§ØŽŲ„ Ø§Ų„Ų…ŲƒØĒب؊، ؁ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŦØ¯ŲˆŲ„ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ų„Ų…ØšØąŲØŠ Ø§Ų„ØŖØĩŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ Ø§Ų„Ų…Ų‚Ø§Ø¨Ų„. Ų„Ø§ØŗØĒؚاد؊ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØĒØŖŲƒØ¯ Ų…Ų† ØĨŲ…ŲƒØ§Ų†ŲŠØŠ Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ Ų…ØŗØ§Øą Ø§Ų„Ų…Ų„Ų ØŖØ¯Ų†Ø§Ų‡ Ø¨ŲˆØ§ØŗØˇØŠ Immich ŲˆŲ…Ų† ØĢŲ… Ų‚Ų… Ø¨Ų…ØŗØ­ Ø§Ų„Ų…ŲƒØĒب؊.", "authentication_settings": "ØĨؚداداØĒ Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ", "authentication_settings_description": "ØĨØ¯Ø§ØąØŠ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą ؈OAuth ؈ØĨؚداداØĒ Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ø§Ų„ØŖŲØŽØąŲ‰", "authentication_settings_disable_all": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĒØšØˇŲŠŲ„ ØŦŲ…ŲŠØš ŲˆØŗØ§ØĻŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ØŸ ØŗŲŠØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§Ų„ŲƒØ§Ų…Ų„.", "authentication_settings_reenable": "Ų„ØĨؚاد؊ Ø§Ų„ØĒŲØšŲŠŲ„ØŒ Ø§ØŗØĒØŽØ¯Ų… ØŖŲ…Øą Ø§Ų„ØŽØ§Ø¯Ų….", - "background_task_job": "Ø§Ų„Ų…Ų‡Ø§Ų… Ø§Ų„ØŽŲ„ŲŲŠØŠ", - "backup_database": "Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ", - "backup_database_enable_description": "ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", - "backup_keep_last_amount": "Ų…Ų‚Ø¯Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ Ų„Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų‡Ø§", - "backup_settings": "ØĨؚداداØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", - "backup_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "background_task_job": "Ø§Ų„Ų…Ų‡Ø§Ų… ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ", + "backup_database": "Ø§Ų†Ø´Ø§ØĄ ØĒŲØąŲŠØē Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "backup_database_enable_description": "ØĒŲ…ŲƒŲŠŲ† ØĒŲØąŲŠØē Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "backup_keep_last_amount": "Ų…Ų‚Ø¯Ø§Øą Ø§Ų„ØĒŲØąŲŠØēاØĒ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ Ų„Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų‡Ø§", + "backup_onboarding_1_description": "Ų†ØŗØŽØŠ ØŽØ§ØąØŦ Ø§Ų„Ų…ŲˆŲ‚Øš ؁؊ Ų…ŲˆŲ‚Øš ØĸØŽØą.", + "backup_onboarding_2_description": "Ų†ØŗØŽ Ų…Ø­Ų„ŲŠØŠ ØšŲ„Ų‰ ØŖØŦŲ‡Ø˛ØŠ Ų…ØŽØĒŲ„ŲØŠ. ŲŠØ´Ų…Ų„ Ø°Ų„Ųƒ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ ŲˆŲ†ØŗØŽØŠ احØĒŲŠØ§ØˇŲŠØŠ Ų…Ø­Ų„ŲŠØŠ Ų…Ų†Ų‡Ø§.", + "backup_onboarding_3_description": "ØĨØŦŲ…Ø§Ų„ŲŠ Ų†ØŗØŽ Ø¨ŲŠØ§Ų†Ø§ØĒŲƒØŒ Ø¨Ų…Ø§ ؁؊ Ø°Ų„Ųƒ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØŖØĩŲ„ŲŠØŠ. ŲŠØ´Ų…Ų„ Ø°Ų„Ųƒ Ų†ØŗØŽØŠŲ‹ ŲˆØ§Ø­Ø¯ØŠŲ‹ ØŽØ§ØąØŦ Ø§Ų„Ų…ŲˆŲ‚Øš ŲˆŲ†ØŗØŽØĒŲŠŲ† Ų…Ø­Ų„ŲŠØĒŲŠŲ†.", + "backup_onboarding_description": "ŲŠŲŲ†ØĩØ­ باØĒباؚ Ø§ØŗØĒØąØ§ØĒ؊ØŦŲŠØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ 3-2-1 Ų„Ø­Ų…Ø§ŲŠØŠ Ø¨ŲŠØ§Ų†Ø§ØĒ؃. احØĒŲØ¸ Ø¨Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠØŠ Ų…Ų† ØĩŲˆØąŲƒ/ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ؃ Ø§Ų„Ų…Ø­Ų…Ų‘Ų„ØŠØŒ Ø¨Ø§Ų„ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ Ų‚Ø§ØšØ¯ØŠ Ø¨ŲŠØ§Ų†Ø§ØĒ Immich، Ų„ØļŲ…Ø§Ų† Ø­Ų„ Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠ Ø´Ø§Ų…Ų„.", + "backup_onboarding_footer": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø­ŲˆŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų€ Immich، ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Ø§Ų„ØĒØšŲ„ŲŠŲ…Ø§ØĒ .", + "backup_onboarding_parts_title": "؊ØĒØļŲ…Ų† Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ 3-2-1 Ų…Ø§ ŲŠŲ„ŲŠ:", + "backup_onboarding_title": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ", + "backup_settings": "ØĨؚداداØĒ ØĒŲØąŲŠØē Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "backup_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØĒŲØąŲŠØē Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ.", "cleared_jobs": "ØĒŲ… ØĨØŽŲ„Ø§ØĄ Ų…Ų‡Ø§Ų…: {job}", "config_set_by_file": "Ø§Ų„ØĨؚداداØĒ Ø­Ø§Ų„ŲŠŲ‹Ø§ Ų…ØšŲŠŲ†ØŠ ØšŲ† ØˇØąŲŠŲ‚ ؅؄؁ Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ", "confirm_delete_library": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų…ŲƒØĒب؊ {library}؟", @@ -50,6 +65,7 @@ "confirm_email_below": "Ų„Ų„ØĒØŖŲƒŲŠØ¯ØŒ Ø§ŲƒØĒب \"{email}\" Ø¨Ø§Ų„ØŖØŗŲŲ„", "confirm_reprocess_all_faces": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨؚاد؊ Ų…ØšØ§Ų„ØŦØŠ ØŦŲ…ŲŠØš Ø§Ų„ŲˆØŦŲˆŲ‡ØŸ ØŗŲŠØŽŲ„ŲŠ Ų‡Ø°Ø§ ŲƒŲ„ Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ø°ŲŠŲ† ØŗŲŽŲ…ŲŠØĒŲŽŲ‡Ų….", "confirm_user_password_reset": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą {user}؟", + "confirm_user_pin_code_reset": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØ§ŲƒØ¯ Ų…Ų† اؚاد؊ ØļØ¨Øˇ ØąŲ…Ø˛ PIN Ø§Ų„ØŽØ§Øĩ ب {user}؟", "create_job": "ØĨŲ†Ø´Ø§ØĄ ŲˆØ¸ŲŠŲØŠ", "cron_expression": "ØĒØšØ¨ŲŠØą Cron", "cron_expression_description": "اØļØ¨Øˇ Ø§Ų„ŲØ§ØĩŲ„ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ų„Ų„ŲØ­Øĩ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØĒŲ†ØŗŲŠŲ‚ cron. Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ ŲŠŲØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Crontab Guru ØšŲ„Ų‰ ØŗØ¨ŲŠŲ„ Ø§Ų„Ų…ØĢØ§Ų„", @@ -65,10 +81,15 @@ "force_delete_user_warning": "ØĒØ­Ø°ŲŠØą: ØŗŲŠØ¤Ø¯ŲŠ Ø°Ų„Ųƒ ØĨŲ„Ų‰ ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ؈ØŦŲ…ŲŠØš Ų…Ø­ØĒŲˆŲŠØ§ØĒŲ‡ ØšŲ„Ų‰ Ø§Ų„ŲŲˆØą. Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ ŲˆŲ„Ø§ ŲŠŲ…ŲƒŲ† Ø§ØŗØĒØąØ¯Ø§Ø¯ Ø§Ų„Ų…Ų„ŲØ§ØĒ.", "image_format": "Ø§Ų„ØĒŲ†ØŗŲŠŲ‚", "image_format_description": "ŲŠŲŲ†ØĒØŦ WebP Ų…Ų„ŲØ§ØĒ ØŖØĩØēØą Ø­ØŦŲ…Ų‹Ø§ Ų…Ų† Ų…Ų„ŲØ§ØĒ JPEG، ŲˆŲ„ŲƒŲ†Ų‡ ØŖØ¨ØˇØŖ ؁؊ ØšŲ…Ų„ŲŠØŠ Ø§Ų„ØĒØąŲ…ŲŠØ˛.", + "image_fullsize_description": "ØĩŲˆØąØŠ بحØŦŲ… ŲƒØ§Ų…Ų„ Ų…Øš Ø§Ø˛Ø§Ų„ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„ŲˆØĩŲŲŠØŠØŒ ØĒØŗØĒØŽØ¯Ų… ØšŲ†Ø¯ Ø§Ų„ØĒŲƒØ¨ŲŠØą", + "image_fullsize_enabled": "ØĒŲ…ŲƒŲŠŲ† ØĒŲˆŲ„ŲŠØ¯ Ø§Ų„ØĩŲˆØą بحØŦŲ… ŲƒØ§Ų…Ų„", + "image_fullsize_enabled_description": "ØĒŲˆŲ„ŲŠØ¯ ØĩŲˆØą بحØŦŲ… ŲƒØ§Ų…Ų„ Ų„Ų„Øĩ؊Øē Ø§Ų„ØēŲŠØą ØĩØ¯ŲŠŲ‚ØŠ Ų„Ų„ŲˆŲŠØ¨. ØšŲ†Ø¯ ØĒŲØšŲŠŲ„ \"ØĒ؁ØļŲŠŲ„ Ø§Ų„ØšØąØļ Ø§Ų„Ų…Ø¯Ų…ØŦ\" ، Ø§Ų„ØšØąŲˆØļ Ø§Ų„Ų…Ø¯Ų…ØŦŲ‡ ØĒØŗØĒØŽØ¯Ų… Ø¨Ø´ŲƒŲ„ Ų…Ø¨Ø§Ø´Øą Ø¨Ø¯ŲˆŲ† ØĒØ­ŲˆŲŠŲ„. Ų„Ø§ ŲŠØ¤ØĢØą ØšŲ„Ų‰ Ø§Ų„Øĩ؊Øē Ø§Ų„ØĩØ¯ŲŠŲ‚ØŠ Ų„Ų„ŲˆŲŠØ¨ Ų…ØĢŲ„ JPEG.", + "image_fullsize_quality_description": "ØĩŲˆØą Ø¨Ø¯Ų‚ØŠ ŲƒØ§Ų…Ų„ØŠ Ų…Ų† ŲĄ-ŲĄŲ Ų . Ø§Ų„Ø§ØšŲ„Ų‰ Ø§ŲØļŲ„ ŲˆŲ„ŲƒŲ† ŲŠŲ†ØĒØŦ Ų…Ų„ŲØ§ØĒ بحØŦŲ… Ø§ŲƒØ¨Øą.", + "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 بØĒŲ†ØŗŲŠŲ‚ sRGB Ų„ØĒØŦŲ†Ø¨ ØĒØēŲŠØąØ§ØĒ Ø§Ų„Ų„ŲˆŲ†.", + "image_prefer_embedded_preview_setting_description": "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ų…ØšØ§ŲŠŲ†Ø§ØĒ Ø§Ų„Ų…ØļŲ…Ų†ØŠ ؁؊ ØĩŲˆØą RAW ŲƒŲ…Ø¯ØŽŲ„ Ų„Ų…ØšØ§Ų„ØŦØŠ Ø§Ų„ØĩŲˆØą ØšŲ†Ø¯Ų…Ø§ ØĒŲƒŲˆŲ† Ų…ØĒاح؊. ŲŠŲ†ØĒØŦ ØšŲ†Ų‡ Ø§Ų†ØĒاØŦ ØŖŲ„ŲˆØ§Ų† ØŖŲƒØĢØą Ø¯Ų‚ØŠ Ų„Ø¨ØšØļ Ø§Ų„ØĩŲˆØąØŒ Ų„ŲƒŲ† ØŦŲˆØ¯ØŠ Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ ØĒØšØĒŲ…Ø¯ ØšŲ„Ų‰ Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§ ŲˆŲ‚Ø¯ ØĒØ­ØĒ؈؊ Ø§Ų„ØĩŲˆØąØŠ ØšŲ„Ų‰ Ø´ŲˆØ§ØĻب ØļØēØˇŲ ØŖŲƒØĢØą.", + "image_prefer_wide_gamut": "ØĒ؁ØļŲŠŲ„ ØĒØ¯ØąØŦ Ø§Ų„ØŖŲ„ŲˆØ§Ų† Ø§Ų„ŲˆØ§ØŗØš", + "image_prefer_wide_gamut_setting_description": "Ø§ØŗØĒØŽØ¯Ų… ØšØąØļ P3 Ų„Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ. ŲŠØ­Ø§ŲØ¸ Ų‡Ø°Ø§ ØšŲ„Ų‰ Ø­ŲŠŲˆŲŠØŠ Ø§Ų„ØĩŲˆØą ذاØĒ Ų…ØŗØ§Ø­Ø§ØĒ Ø§Ų„ØŖŲ„ŲˆØ§Ų† Ø§Ų„ŲˆØ§ØŗØšØŠ Ø¨Ø´ŲƒŲ„ ØŖŲØļŲ„ØŒ ŲˆŲ„ŲƒŲ† Ų‚Ø¯ ØĒØ¸Ų‡Øą Ø§Ų„ØĩŲˆØą Ø¨Ø´ŲƒŲ„ Ų…ØŽØĒ؄؁ ØšŲ„Ų‰ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ ذاØĒ ØĨØĩØ¯Ø§Øą Ų…ØĒØĩŲØ­ Ų‚Ø¯ŲŠŲ…. ؊ØĒŲ… Ø§Ų„Ø§Ø­ØĒŲØ§Ø¸ بØĩŲˆØą sRGB بØĒŲ†ØŗŲŠŲ‚ sRGB Ų„ØĒØŦŲ†Ø¨ ØĒØēŲŠØąØ§ØĒ Ø§Ų„Ų„ŲˆŲ†.", "image_preview_description": "ØĩŲˆØąØŠ Ų…ØĒŲˆØŗØˇØŠ Ø§Ų„Ø­ØŦŲ… Ų…Øš Ø¨ŲŠØ§Ų†Ø§ØĒ ؈ØĩŲŲŠØŠ Ų…ØŦØąØ¯ØŠØŒ ØĒŲØŗØĒØŽØ¯Ų… ØšŲ†Ø¯ ØšØąØļ ØŖØĩŲ„ ŲˆØ§Ø­Ø¯ ŲˆŲ„Ų„ØĒØšŲ„Ų… Ø§Ų„ØĸŲ„ŲŠ", "image_preview_quality_description": "ØŦŲˆØ¯ØŠ Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ Ų…Ų† 1 ØĨŲ„Ų‰ 100. ŲƒŲ„Ų…Ø§ ŲƒØ§Ų†ØĒ Ø§Ų„Ų‚ŲŠŲ…ØŠ ØŖØšŲ„Ų‰ ŲƒØ§Ų† Ø°Ų„Ųƒ ØŖŲØļŲ„ØŒ ŲˆŲ„ŲƒŲ†Ų‡Ø§ ØĒŲ†ØĒØŦ Ų…Ų„ŲØ§ØĒ ØŖŲƒØ¨Øą ŲˆŲ‚Ø¯ ØĒŲ‚Ų„Ų„ Ų…Ų† Ø§ØŗØĒØŦاب؊ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚. Ų‚Ø¯ ŲŠØ¤ØĢØą ØļØ¨Øˇ Ų‚ŲŠŲ…ØŠ Ų…Ų†ØŽŲØļØŠ ØšŲ„Ų‰ ØŦŲˆØ¯ØŠ Ø§Ų„ØĒØšŲ„Ų… Ø§Ų„ØĸŲ„ŲŠ.", "image_preview_title": "ØĨؚداداØĒ Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ", @@ -91,18 +112,25 @@ "library_created": "ØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ø§Ų„Ų…ŲƒØĒب؊: {library}", "library_deleted": "ØĒŲ… Ø­Ø°Ų Ø§Ų„Ų…ŲƒØĒب؊", "library_import_path_description": "حدد Ų…ØŦŲ„Ø¯Ų‹Ø§ Ų„Ų„Ø§ØŗØĒŲŠØąØ§Ø¯. ØŗŲŠØĒŲ… ŲØ­Øĩ Ų‡Ø°Ø§ Ø§Ų„Ų…ØŦŲ„Ø¯ØŒ Ø¨Ų…Ø§ ؁؊ Ø°Ų„Ųƒ Ø§Ų„Ų…ØŦŲ„Ø¯Ø§ØĒ Ø§Ų„ŲØąØšŲŠØŠØŒ بحØĢŲ‹Ø§ ØšŲ† Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ.", - "library_scanning": "Ø§Ų„ŲØ­Øĩ Ø§Ų„Ø¯ŲˆØąŲŠ", - "library_scanning_description": "ØĨؚداد ŲØ­Øĩ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„Ø¯ŲˆØąŲŠ", - "library_scanning_enable_description": "ØĒŲØšŲŠŲ„ ŲØ­Øĩ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„Ø¯ŲˆØąŲŠ", + "library_scanning": "Ø§Ų„Ų…ØŗØ­ Ø§Ų„Ø¯ŲˆØąŲŠ", + "library_scanning_description": "ØĨؚداد Ų…ØŗØ­ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„Ø¯ŲˆØąŲŠ", + "library_scanning_enable_description": "ØĒŲØšŲŠŲ„ Ų…ØŗØ­ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„Ø¯ŲˆØąŲŠ", "library_settings": "Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ", "library_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ", "library_tasks_description": "Ų…ØŗØ­ Ø§Ų„Ų…ŲƒØĒباØĒ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ Ų„Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ؈/ØŖŲˆ Ø§Ų„Ų…ØĒØēŲŠØąØŠ", - "library_watching_enable_description": "ØąØ§Ų‚Ø¨ Ø§Ų„Ų…ŲƒØĒباØĒ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ Ų„ØĒØĒبؚ ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų…Ų„ŲØ§ØĒ", + "library_watching_enable_description": "ØąØ§Ų‚Ø¨ Ø§Ų„Ų…ŲƒØĒباØĒ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ Ų„ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų…Ų„ŲØ§ØĒ", "library_watching_settings": "Ų…ØąØ§Ų‚Ø¨ØŠ Ø§Ų„Ų…ŲƒØĒباØĒ (ØĒØŦØąŲŠØ¨ŲŠ)", "library_watching_settings_description": "ØąØ§Ų‚Ø¨ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ Ø§Ų„ØĒØēŲŠŲŠØąØ§ØĒ ؁؊ Ø§Ų„Ų…Ų„ŲØ§ØĒ", "logging_enable_description": "ØĒŲØšŲŠŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŖØ­Ø¯Ø§ØĢ", "logging_level_description": "ØšŲ†Ø¯ Ø§Ų„ØĒŲØšŲŠŲ„ØŒ ØŖŲŠ Ų…ØŗØĒŲˆŲ‰ ØĒØŗØŦŲŠŲ„ ØŗŲŠØŗØĒØŽØ¯Ų….", - "logging_settings": "ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŖØ­Ø¯Ø§ØĢ", + "logging_settings": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø§Ø­Ø¯Ø§ØĢ", + "machine_learning_availability_checks": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØĒŲˆŲØą", + "machine_learning_availability_checks_description": "ØĒØ­Ø¯ŲŠØ¯ ØŽŲˆØ§Ø¯Ų… Ø§Ų„ØĒØšŲ„Ų… Ø§Ų„ØĸŲ„ŲŠ Ø§Ų„Ų…ØĒاح؊ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ؈ØĨØšØˇØ§ØĄŲ‡Ø§ Ø§Ų„ØŖŲˆŲ„ŲˆŲŠØŠ", + "machine_learning_availability_checks_enabled": "ØĒŲØšŲŠŲ„ ØšŲ…Ų„ŲŠØ§ØĒ ŲØ­Øĩ Ø§Ų„ØĒŲˆŲØą", + "machine_learning_availability_checks_interval": "؁ØĒØąØŠ Ø§Ų„ØĒØ­Ų‚Ų‚", + "machine_learning_availability_checks_interval_description": "Ø§Ų„ŲØĒØąØŠ Ø§Ų„Ø˛Ų…Ų†ŲŠØŠ Ø¨Ø§Ų„Ų…Ų„Ų„ŲŠ ØĢØ§Ų†ŲŠØŠ Ø¨ŲŠŲ† ØšŲ…Ų„ŲŠØ§ØĒ ŲØ­Øĩ Ø§Ų„ØĒŲˆŲØą", + "machine_learning_availability_checks_timeout": "Ø§Ų†ØĒŲ‡ØĒ Ų…Ø¯ØŠ Ø§Ų†ØĒØ¸Ø§Øą Ø§Ų„ØˇŲ„Ø¨", + "machine_learning_availability_checks_timeout_description": "Ų…Ø¯ØŠ Ø§Ų†ØĒØ¸Ø§Øą (Ø¨Ø§Ų„Ų…Ų„Ų„ŲŠ ØĢØ§Ų†ŲŠØŠ) Ų„Ø§ØŽØĒØ¨Ø§ØąØ§ØĒ ØĒŲˆŲØą Ø§Ų„ØŽØ¯Ų…ØŠ", "machine_learning_clip_model": "Ų†Ų…ŲˆØ°ØŦ CLIP", "machine_learning_clip_model_description": "Ø§ØŗŲ… Ų†Ų…ŲˆØ°ØŦ CLIP Ų…Ø¯ØąØŦ، Ų‡Ų†Ø§. ŲŠØąØŦŲ‰ Ų…Ų„Ø§Ø­Ø¸ØŠ ØŖŲ†Ų‡ ؊ØŦب ØĨؚاد؊ ØĒØ´ØēŲŠŲ„ ŲˆØ¸ŲŠŲØŠ \"Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„Ø°ŲƒŲŠ\" Ų„ØŦŲ…ŲŠØš Ø§Ų„ØĩŲˆØą بؚد ØĒØēŲŠŲŠØą Ø§Ų„Ų†Ų…ŲˆØ°ØŦ.", "machine_learning_duplicate_detection": "ŲƒØ´Ų Ø§Ų„ØĒŲƒØąØ§Øą", @@ -142,10 +170,10 @@ "map_light_style": "Ø§Ų„Ų†Ų…Øˇ Ø§Ų„ŲØ§ØĒØ­", "map_manage_reverse_geocoding_settings": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„ØĒŲƒŲˆŲŠŲ† Ø§Ų„ØŦØēØąØ§ŲŲŠ Ø§Ų„Ų…ØšŲƒŲˆØŗ", "map_reverse_geocoding": "ØšŲƒØŗ Ø§Ų„ØĒØąŲ…ŲŠØ˛ Ø§Ų„ØŦØēØąØ§ŲŲŠ", - "map_reverse_geocoding_enable_description": "ØĒŲØšŲŠŲ„ ØšŲƒØŗ Ø§Ų„ØĒØąŲ…ŲŠØ˛ Ø§Ų„ØŦØēØąØ§ŲŲŠ", - "map_reverse_geocoding_settings": "ØĨؚداداØĒ ØšŲƒØŗ Ø§Ų„ØĒØąŲ…ŲŠØ˛ Ø§Ų„ØŦØēØąØ§ŲŲŠ", - "map_settings": "Ø§Ų„ØŽØąŲŠØˇØŠ", - "map_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„ØŽØąŲŠØˇØŠ", + "map_reverse_geocoding_enable_description": "ØĒŲØšŲŠŲ„ Ø§Ų„ØĒØąŲ…ŲŠØ˛ Ø§Ų„ØŦØēØąØ§ŲŲŠ Ø§Ų„ØšŲƒØŗŲŠ", + "map_reverse_geocoding_settings": "ØĨؚداداØĒ Ø§Ų„ØĒØąŲ…ŲŠØ˛ Ø§Ų„ØŦØēØąØ§ŲŲŠ Ø§Ų„ØšŲƒØŗŲŠ", + "map_settings": "Ø§Ų„ØŽØ§ØąØˇØŠ", + "map_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„ØŽØ§ØąØˇØŠ", "map_style_description": "ØšŲ†ŲˆØ§Ų† URL Ų„ØŗŲ…ØŠ Ø§Ų„ØŽØąŲŠØˇØŠ style.json", "memory_cleanup_job": "ØĒŲ†Ø¸ŲŠŲ Ø§Ų„Ø°Ø§ŲƒØąØŠ", "memory_generate_job": "ØĒŲˆŲ„ŲŠØ¯ Ø§Ų„Ø°Ø§ŲƒØąØŠ", @@ -157,12 +185,26 @@ "metadata_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„ŲˆØĩŲŲŠØŠ", "migration_job": "ØĒØąØ­ŲŠŲ„", "migration_job_description": "ØĒØąØ­ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ Ų„Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ ŲˆØ§Ų„ŲˆØŦŲˆŲ‡ ØĨŲ„Ų‰ ØŖØ­Ø¯ØĢ Ų‡ŲŠŲƒŲ„ Ų…ØŦŲ„Ø¯Ø§ØĒ", + "nightly_tasks_cluster_faces_setting_description": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ Ø§Ų„ØĒØšØąŲ ØšŲ„Ų‰ Ø§Ų„ŲˆØŦŲ‡ ØšŲ„Ų‰ Ø§Ų„ŲˆØŦŲˆŲ‡ Ø§Ų„Ų…ŲƒØĒØ´ŲØŠ Ø­Ø¯ŲŠØĢا", + "nightly_tasks_cluster_new_faces_setting": "Ų…ØŦŲ…ŲˆØšØŠ Ø§Ų„ŲˆØŦŲˆŲ‡ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ", + "nightly_tasks_database_cleanup_setting": "Ų…Ų‡Ø§Ų… ØĒŲ†Ø¸ŲŠŲ Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "nightly_tasks_database_cleanup_setting_description": "Ų‚Ų… بØĒŲ†Ø¸ŲŠŲ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ Ų…Ų†ØĒŲ‡ŲŠØŠ Ø§Ų„ØĩŲ„Ø§Ø­ŲŠØŠ Ų…Ų† Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "nightly_tasks_generate_memories_setting": "ØĨŲ†Ø´Ø§ØĄ Ø§Ų„Ø°ŲƒØąŲŠØ§ØĒ", + "nightly_tasks_generate_memories_setting_description": "ØĨŲ†Ø´Ø§ØĄ Ø°ŲƒØąŲŠØ§ØĒ ØŦØ¯ŲŠØ¯ØŠ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„", + "nightly_tasks_missing_thumbnails_setting": "ØĨŲ†Ø´Ø§ØĄ ØĩŲˆØą Ų…ØĩØēØąØŠ Ų…ŲŲ‚ŲˆØ¯ØŠ", + "nightly_tasks_missing_thumbnails_setting_description": "ØŖØĩŲˆŲ„ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą Ø¨Ø¯ŲˆŲ† ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ", + "nightly_tasks_settings": "ØĨؚداداØĒ Ø§Ų„Ų…Ų‡Ø§Ų… Ø§Ų„Ų„ŲŠŲ„ŲŠØŠ", + "nightly_tasks_settings_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų…Ų‡Ø§Ų… Ø§Ų„Ų„ŲŠŲ„ŲŠØŠ", + "nightly_tasks_start_time_setting": "ŲˆŲ‚ØĒ Ø§Ų„Ø¨Ø¯ØĄ", + "nightly_tasks_start_time_setting_description": "Ø§Ų„ŲˆŲ‚ØĒ Ø§Ų„Ø°ŲŠ ŲŠØ¨Ø¯ØŖ ŲŲŠŲ‡ Ø§Ų„ØŽØ§Ø¯Ų… ؁؊ ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…Ų‡Ø§Ų… Ø§Ų„Ų„ŲŠŲ„ŲŠØŠ", + "nightly_tasks_sync_quota_usage_setting": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø­ØĩØŠ Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų…", + "nightly_tasks_sync_quota_usage_setting_description": "ØĒØ­Ø¯ŲŠØĢ Ø­ØĩØŠ ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ØŒ Ø¨Ų†Ø§ØĄ ØšŲ„Ų‰ Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø­Ø§Ų„ŲŠ", "no_paths_added": "Ų„Ų… ؊ØĒŲ… ØĨØļØ§ŲØŠ ØŖŲŠ Ų…ØŗØ§ØąØ§ØĒ", "no_pattern_added": "Ų„Ų… ؊ØĒŲ… ØĨØļØ§ŲØŠ ØŖŲŠ ØŖŲ†Ų…Ø§Øˇ", - "note_apply_storage_label_previous_assets": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„ØĒØˇØ¨ŲŠŲ‚ ØĒØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„", + "note_apply_storage_label_previous_assets": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„ØĒØˇØ¨ŲŠŲ‚ ØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„", "note_cannot_be_changed_later": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØēŲŠŲŠØą Ų‡Ø°Ø§ Ų„Ø§Ø­Ų‚Ų‹Ø§!", "notification_email_from_address": "ØšŲ†ŲˆØ§Ų† Ø§Ų„Ų…ØąØŗŲ„", - "notification_email_from_address_description": "ØšŲ†ŲˆØ§Ų† Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ų„Ų„Ų…ØąØŗŲ„ØŒ ØšŲ„Ų‰ ØŗØ¨ŲŠŲ„ Ø§Ų„Ų…ØĢØ§Ų„: \"Immich Photo Server noreply@example.com\"", + "notification_email_from_address_description": "ØšŲ†ŲˆØ§Ų† Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ų„Ų„Ų…ØąØŗŲ„ØŒ ØšŲ„Ų‰ ØŗØ¨ŲŠŲ„ Ø§Ų„Ų…ØĢØ§Ų„: \"Immich Photo Server noreply@example.com\". ØĒØ§ŲƒØ¯ Ų…Ų† Ø§ØŗØĒØŽØ¯Ø§Ų… ØšŲ†ŲˆØ§Ų† Ø¨ØąŲŠØ¯ Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ ŲŠØŗŲ…Ø­ Ų„Ųƒ Ø¨Ø§ØąØŗØ§Ų„ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ Ų…Ų†Ų‡.", "notification_email_host_description": "Ų…Øļ؊؁ ØŽØ§Ø¯Ų… Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ (Ų…ØĢŲ„Ų‹Ø§: smtp.immich.app)", "notification_email_ignore_certificate_errors": "ØĒØŦØ§Ų‡Ų„ ØŖØŽØˇØ§ØĄ Ø§Ų„Ø´Ų‡Ø§Ø¯ØŠ", "notification_email_ignore_certificate_errors_description": "ØĒØŦØ§Ų‡Ų„ ØŖØŽØˇØ§ØĄ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Øĩح؊ Ø´Ų‡Ø§Ø¯ØŠ TLS (ØēŲŠØą Ų…ØŗØĒØ­ØŗŲ†)", @@ -182,19 +224,24 @@ "oauth_auto_register": "Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊", "oauth_auto_register_description": "Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„ØŦدد بؚد ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… OAuth", "oauth_button_text": "Ų†Øĩ Ø§Ų„Ø˛Øą", + "oauth_client_secret_description": "Ų…ØˇŲ„ŲˆØ¨ اذاPKCE(؅؁ØĒاح Ø§Ų„Ø§ØĢباØĒ Ų„ØĒØ¨Ø§Ø¯Ų„ Ø§Ų„ŲƒŲˆØ¯) Ų„Ų… ؊ØĒŲ… ØĒŲˆŲŲŠØąŲ‡ Ų…Ų† Ų…Ø˛ŲˆØ¯ OAuth", "oauth_enable_description": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… OAuth", "oauth_mobile_redirect_uri": "ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", "oauth_mobile_redirect_uri_override": "ØĒØŦØ§ŲˆØ˛ ØšŲ†ŲˆØ§Ų† URI Ų„ØĨؚاد؊ Ø§Ų„ØĒ؈ØŦŲŠŲ‡ ØšŲ„Ų‰ Ø§Ų„Ų‡Ø§ØĒ؁", "oauth_mobile_redirect_uri_override_description": "Ų‚Ų… بØĒŲØšŲŠŲ„Ų‡ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ŲŠØŗŲ…Ø­ Ų…ŲˆŲØą OAuth Ø¨Ų…ØšØąŲ URI Ų„Ų„ØŦŲˆØ§Ų„ØŒ Ų…ØĢŲ„ ''{callback}''", + "oauth_role_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ Ø¨Ø§Ų„Ø¯ŲˆØą(ØĩŲ„Ø§Ø­ŲŠØ§ØĒ)", + "oauth_role_claim_description": "Ų…Ų†Ø­ ؈ØĩŲˆŲ„ Ø§Ų„Ų…ØŗØ¤ŲˆŲ„ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ ؈ØŦŲˆØ¯ Ų‡Ø°Ø§ Ø§Ų„ØˇŲ„Ø¨. Ų‚Ø¯ ŲŠŲƒŲˆŲ† Ø§Ų„ØˇŲ„Ø¨ ØĨŲ…Ø§ 'Ų…ØŗØĒØŽØ¯Ų…' ØŖŲˆ 'Ų…ØŗØ¤ŲˆŲ„'.", "oauth_settings": "OAuth", "oauth_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ OAuth", "oauth_settings_more_details": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø­ŲˆŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Ø§Ų„ŲˆØĢاØĻŲ‚.", - "oauth_storage_label_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ بØĒØĩŲ†ŲŠŲ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", - "oauth_storage_label_claim_description": "Ų‚Ų… ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ بØĒØšŲŠŲŠŲ† ØĒØĩŲ†ŲŠŲ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„ØŽØ§Øĩ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ØšŲ„Ų‰ Ų‚ŲŠŲ…ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ.", + "oauth_storage_label_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ Ø¨ØŗŲ…ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", + "oauth_storage_label_claim_description": "Ų‚Ų… ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ بØĒØšŲŠŲŠŲ† ØŗŲ…ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„ØŽØ§Øĩ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ØšŲ„Ų‰ Ų‚ŲŠŲ…ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ.", "oauth_storage_quota_claim": "Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ بحØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "oauth_storage_quota_claim_description": "Ų‚Ų… ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ بØĒØšŲŠŲŠŲ† Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ų„Ų„Ų…ØŗØĒØŽØ¯Ų… ØšŲ„Ų‰ Ų‚ŲŠŲ…ØŠ Ų‡Ø°Ų‡ Ø§Ų„Ų…ØˇØ§Ų„Ø¨ØŠ.", "oauth_storage_quota_default": "Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ (ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ)", - "oauth_storage_quota_default_description": "Ø§Ų„Ø­ØĩØŠ Ø¨Ø§Ų„ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų…Ų‡Ø§ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ؊ØĒŲ… ØĒŲˆŲŲŠØą Ų…ØˇØ§Ų„Ø¨ØŠ (ØŖØ¯ØŽŲ„ 0 Ų„Ø­ØĩØŠ ØēŲŠØą Ų…Ø­Ø¯ŲˆØ¯ØŠ).", + "oauth_storage_quota_default_description": "Ø§Ų„Ø­ØĩØŠ Ø¨Ø§Ų„ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų…Ų‡Ø§ ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ؊ØĒŲ… ØĒŲˆŲŲŠØą Ų…ØˇØ§Ų„Ø¨ØŠ.", + "oauth_timeout": "Ų†ŲØ§Ø° ŲˆŲ‚ØĒ Ø§Ų„ØˇŲ„Ø¨", + "oauth_timeout_description": "Ų†ŲØ§Ø° ŲˆŲ‚ØĒ Ø§Ų„ØˇŲ„Ø¨ Ø¨Ø§Ų„Ų…ŲŠŲ„ŲŠ ØĢØ§Ų†ŲŠØŠ", "password_enable_description": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ ŲˆŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "password_settings": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "password_settings_description": "ØĨØ¯Ø§ØąØŠ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", @@ -229,13 +276,14 @@ "storage_template_hash_verification_enabled_description": "ØĒŲØšŲŠŲ„ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„Ų‡Ø§Ø´ØŒ Ų„Ø§ ØĒØšØˇŲ„ Ų‡Ø°Ø§ ØĨŲ„Ø§ ØĨذا ŲƒŲ†ØĒ Ų…ØĒØŖŲƒØ¯Ų‹Ø§ Ų…Ų† ØĒØŖØĢŲŠØąØ§ØĒŲ‡", "storage_template_migration": "ØĒŲ‡ØŦŲŠØą Ų‚Ø§Ų„Ø¨ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "storage_template_migration_description": "Ų‚Ų… بØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų‚Ø§Ų„Ø¨ Ø§Ų„Ø­Ø§Ų„ŲŠ {template} ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§", - "storage_template_migration_info": "ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų‚Ø§Ų„Ø¨ ØŗØĒŲ†ØˇØ¨Ų‚ ŲŲ‚Øˇ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ. Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų‚Ø§Ų„Ø¨ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„ {job}.", + "storage_template_migration_info": "ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų†Ų…ŲˆØ°ØŦ Ø§Ų„ØŽØ˛Ų†ŲŠ ØŗØĒØēŲŠØą ØŦŲ…ŲŠØš Ø§Ų„Øĩ؊Øē Ø§Ų„Ų‰ Ø§Ø­ØąŲ ØĩØēŲŠØąØŠ. ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„Ų†Ų…ŲˆØ°ØŦ ØŗØĒŲ†ØˇØ¨Ų‚ ŲŲ‚Øˇ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ. Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų†Ų…ŲˆØ°ØŦ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ ØŗØ§Ø¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„ {job}.", "storage_template_migration_job": "ŲˆØ¸ŲŠŲØŠ ØĒŲ‡ØŦŲŠØą Ų‚Ø§Ų„Ø¨ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "storage_template_more_details": "Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø­ŲˆŲ„ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ ŲŠØąØŦŲ‰ Ø§Ų„ØąØŦŲˆØš ØĨŲ„Ų‰ Storage Template ؈implications", + "storage_template_onboarding_description_v2": "ØšŲ†Ø¯ Ø§Ų„ØĒŲØšŲŠŲ„. Ų‡Ø°Ų‡ Ø§Ų„ØŽØ§ØĩŲŠØŠ ØŗØĒŲ‚ŲˆŲ… Ø¨Ø§Ų„ØĒØąØĒŲŠØ¨ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„Ų…Ų„ŲØ§ØĒ Ø¨Ų†Ø§ØĄ ØšŲ„Ų‰ Ų†Ų…ŲˆØ°ØŦ Ų…ØšØąŲ Ų…Ų† Ų‚Ø¨Ų„ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…. ØąØŦØ§ØĄ Ø§ØˇŲ„Øš ØšŲ„Ų‰ Ø§Ų„ØĒ؈ØĢŲŠŲ‚.", "storage_template_path_length": "Ø§Ų„Ø­Ø¯ Ø§Ų„ØĒŲ‚ØąŲŠØ¨ŲŠ Ų„ØˇŲˆŲ„ Ø§Ų„Ų…ØŗØ§Øą: {length, number}/{limit, number}", "storage_template_settings": "Ų‚Ø§Ų„Ø¨ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", "storage_template_settings_description": "ØĨØ¯Ø§ØąØŠ Ų‡ŲŠŲƒŲ„ Ø§Ų„Ų…ØŦŲ„Ø¯ ŲˆØ§ØŗŲ… Ø§Ų„Ų…Ų„Ų Ų„Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…ØąŲŲˆØšØŠ", - "storage_template_user_label": "{label} Ų‡Ųˆ ØĒØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", + "storage_template_user_label": "{label} Ų‡Ųˆ ØŗŲ…ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "system_settings": "ØĨؚداداØĒ Ø§Ų„Ų†Ø¸Ø§Ų…", "tag_cleanup_job": "ØĒŲ†Ø¸ŲŠŲ Ø§Ų„ØšŲ„Ø§Ų…ØŠ", "template_email_available_tags": "ŲŠŲ…ŲƒŲ†Ųƒ Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ų…ØĒØēŲŠØąØ§ØĒ Ø§Ų„ØĒØ§Ų„ŲŠØŠ ؁؊ Ø§Ų„Ų‚Ø§Ų„Ø¨ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ: {tags}", @@ -246,15 +294,15 @@ "template_email_update_album": "ØĒØ­Ø¯ŲŠØĢ Ų‚Ø§Ų„Ø¨ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "template_email_welcome": "Ų‚Ø§Ų„Ø¨ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ø§Ų„ØĒØąØ­ŲŠØ¨ŲŠ", "template_settings": "Ų‚ŲˆØ§Ų„Ø¨ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", - "template_settings_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų‚ŲˆØ§Ų„Ø¨ Ø§Ų„Ų…ØŽØĩØĩØŠ Ų„Ų„ØĨØ´ØšØ§ØąØ§ØĒ.", + "template_settings_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų‚ŲˆØ§Ų„Ø¨ Ø§Ų„Ų…ØŽØĩØĩØŠ Ų„Ų„ØĨØ´ØšØ§ØąØ§ØĒ", "theme_custom_css_settings": "CSS Ų…ØŽØĩØĩ", "theme_custom_css_settings_description": "ØŖŲˆØąØ§Ų‚ Ø§Ų„ØŖŲ†Ų…Ø§Øˇ Ø§Ų„Ų…ØĒØĒØ§Ų„ŲŠØŠ ØĒØŗŲ…Ø­ بØĒØŽØĩ؊Øĩ ØĒØĩŲ…ŲŠŲ… Immich.", "theme_settings": "ØĨؚداداØĒ Ø§Ų„ØŗŲ…ØŠ", "theme_settings_description": "ØĨØ¯Ø§ØąØŠ ØĒØŽØĩ؊Øĩ ŲˆØ§ØŦŲ‡ØŠ ŲˆŲŠØ¨ Immich", "thumbnail_generation_job": "ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ", "thumbnail_generation_job_description": "ØĨŲ†Ø´Ø§ØĄ ØĩŲˆØą Ų…ØĩØēØąØŠ ŲƒØ¨ŲŠØąØŠ ؈ØĩØēŲŠØąØŠ ؈ØēŲŠØą ŲˆØ§Øļح؊ Ų„ŲƒŲ„ ØŖØĩŲ„ØŒ Ø¨Ø§Ų„ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„ŲƒŲ„ Ø´ØŽØĩ", - "transcoding_acceleration_api": "ŲˆØ§ØŦŲ‡ØŠ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ Ų„Ų„ØĒØŗØąŲŠØš", - "transcoding_acceleration_api_description": "Ø§Ų„ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„Ø¨ØąŲ…ØŦŲŠØŠ Ø§Ų„ØĒ؊ ØŗØĒØĒŲØ§ØšŲ„ Ų…Øš ØŦŲ‡Ø§Ø˛Ųƒ Ų„ØĒØŗØąŲŠØš Ø§Ų„ØĒØ­ŲˆŲŠŲ„. Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد Ų‡Ųˆ \"ØŖŲØļŲ„ Ų…Ø­Ø§ŲˆŲ„ØŠ\": ØŗŲŠØšŲˆØ¯ ØĨŲ„Ų‰ Ø§Ų„ØĒØ­ŲˆŲŠŲ„ Ø§Ų„Ø¨ØąŲ…ØŦ؊ ؁؊ Ø­Ø§Ų„ØŠ Ø§Ų„ŲØ´Ų„. Ų‚Ø¯ Ų„Ø§ ŲŠØšŲ…Ų„ VP9 اؚØĒŲ…Ø§Ø¯Ų‹Ø§ ØšŲ„Ų‰ ØšØĒØ§Ø¯Ųƒ.", + "transcoding_acceleration_api": "ØĒØŗØąŲŠØš API", + "transcoding_acceleration_api_description": "API Ø§Ų„ØĒ؊ ØŗØĒØĒŲØ§ØšŲ„ Ų…Øš ØŦŲ‡Ø§Ø˛Ųƒ Ų„ØĒØŗØąŲŠØš Ø§Ų„ØĒØ­ŲˆŲŠŲ„. Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد Ų‡Ųˆ \"ØŖŲØļŲ„ Ų…Ø­Ø§ŲˆŲ„ØŠ\": ØŗŲŠØšŲˆØ¯ ØĨŲ„Ų‰ Ø§Ų„ØĒØ­ŲˆŲŠŲ„ Ø§Ų„Ø¨ØąŲ…ØŦ؊ ؁؊ Ø­Ø§Ų„ØŠ Ø§Ų„ŲØ´Ų„. Ų‚Ø¯ Ų„Ø§ ŲŠØšŲ…Ų„ VP9 اؚØĒŲ…Ø§Ø¯Ų‹Ø§ ØšŲ„Ų‰ ØšØĒØ§Ø¯Ųƒ.", "transcoding_acceleration_nvenc": "NVENC (؊ØĒØˇŲ„Ø¨ GPU Ų…Ų† NVIDIA)", "transcoding_acceleration_qsv": "Quick Sync (؊ØĒØˇŲ„Ø¨ Ų…ØšØ§Ų„ØŦ Intel Ų…Ų† Ø§Ų„ØŦŲŠŲ„ Ø§Ų„ØŗØ§Ø¨Øš ØŖŲˆ ØŖØ­Ø¯ØĢ)", "transcoding_acceleration_rkmpp": "RKMPP (ŲŲ‚Øˇ ØšŲ„Ų‰ Ø´ØąØ§ØĻØ­ Rockchip SOC)", @@ -278,13 +326,13 @@ "transcoding_encoding_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØĒØąŲ…ŲŠØ˛", "transcoding_encoding_options_description": "اØļØ¨Øˇ Ø¨ØąØ§Ų…ØŦ Ø§Ų„ØĒØąŲ…ŲŠØ˛ ŲˆØ§Ų„Ø¯Ų‚ØŠ ŲˆØ§Ų„ØŦŲˆØ¯ØŠ ŲˆØ§Ų„ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØŖØŽØąŲ‰ Ų„Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø´ŲØąØŠ", "transcoding_hardware_acceleration": "Ø§Ų„ØĒØŗØąŲŠØš Ø§Ų„ØšØĒØ§Ø¯ŲŠ", - "transcoding_hardware_acceleration_description": "ØĒØŦØąŲŠØ¨ŲŠØ› ØŖØŗØąØš Ø¨ŲƒØĢŲŠØąØŒ ŲˆŲ„ŲƒŲ† ØŗØĒŲƒŲˆŲ† ØŦŲˆØ¯ØĒŲ‡Ø§ ØŖŲ‚Ų„ ØšŲ†Ø¯ Ų†ŲØŗ Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ", + "transcoding_hardware_acceleration_description": "ØĒØŦØąŲŠØ¨ŲŠ: ØĒØąŲ…ŲŠØ˛ Ø§ØŗØąØš Ų„ŲƒŲ† Ų‚Ø¯ ŲŠŲ‚Ų„Ų„ Ų…Ų† Ø§Ų„ØŦŲˆØ¯ØŠ Ų…Øš Ų…ØšØ¯Ų„ بØĒ Ø§Ų‚Ų„", "transcoding_hardware_decoding": "؁؃ ØĒØ´ŲŲŠØą Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ", "transcoding_hardware_decoding_setting_description": "ŲŠŲ†ØˇØ¨Ų‚ Ø°Ų„Ųƒ ŲŲ‚Øˇ ØšŲ„Ų‰ NVENC، QSV، ؈ RKMPP. ŲŠŲ…ŲƒŲ† Ø§Ų„ØĒØŗØąŲŠØš Ų…Ų† ØˇØąŲ Ų„ØˇØąŲ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† ØĒØŗØąŲŠØš Ø§Ų„ØĒØąŲ…ŲŠØ˛ ŲŲ‚Øˇ. Ų‚Ø¯ Ų„Ø§ ŲŠØšŲ…Ų„ ØšŲ„Ų‰ ØŦŲ…ŲŠØš Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ.", "transcoding_max_b_frames": "ØŖŲ‚ØĩŲ‰ ؚدد Ų…Ų† Ø§Ų„ØĨØˇØ§ØąØ§ØĒ B", "transcoding_max_b_frames_description": "Ø§Ų„Ų‚ŲŠŲ… Ø§Ų„ØŖØšŲ„Ų‰ ØĒØšØ˛Ø˛ ŲƒŲØ§ØĄØŠ Ø§Ų„ØļØēØˇØŒ ŲˆŲ„ŲƒŲ†Ų‡Ø§ ØĒØ¨ØˇØĻ ØšŲ…Ų„ŲŠØŠ Ø§Ų„ØĒØąŲ…ŲŠØ˛. Ų‚Ø¯ Ų„Ø§ ØĒŲƒŲˆŲ† Ų…ØĒŲˆØ§ŲŲ‚ØŠ Ų…Øš Ø§Ų„ØĒØŗØąŲŠØš Ø§Ų„ØšØĒØ§Ø¯ŲŠ ØšŲ„Ų‰ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ. Ų‚ŲŠŲ…ØŠ 0 ØĒØšØˇŲ„ ØĨØˇØ§ØąØ§ØĒ B، Ø¨ŲŠŲ†Ų…Ø§ ØĒØļØ¨Øˇ Ø§Ų„Ų‚ŲŠŲ…ØŠ -1 Ų‡Ø°Ø§ Ø§Ų„Ų‚ŲŠŲ…ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§.", "transcoding_max_bitrate": "Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ", - "transcoding_max_bitrate_description": "ŲŠŲ…ŲƒŲ† ØŖŲ† ŲŠØ¤Ø¯ŲŠ ØĒØšŲŠŲŠŲ† Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ ØĨŲ„Ų‰ ØŦØšŲ„ ØŖØ­ØŦØ§Ų… Ø§Ų„Ų…Ų„ŲØ§ØĒ ØŖŲƒØĢØą Ų‚Ø§Ø¨Ų„ŲŠØŠ Ų„Ų„ØĒŲ†Ø¨Ø¤ Ø¨Ų‡Ø§ بØĒŲƒŲ„ŲØŠ Ø¨ØŗŲŠØˇØŠ Ø¨Ø§Ų„Ų†ØŗØ¨ØŠ Ų„Ų„ØŦŲˆØ¯ØŠ. ØšŲ†Ø¯ Ø¯Ų‚ØŠ 720 Ø¨ŲƒØŗŲ„ØŒ ØĒŲƒŲˆŲ† Ø§Ų„Ų‚ŲŠŲ… Ø§Ų„Ų†Ų…ŲˆØ°ØŦŲŠØŠ 2600 ŲƒŲŠŲ„Ųˆ Ø¨Ø§ŲŠØĒ Ų„Ų€ VP9 ØŖŲˆ HEVC، ØŖŲˆ 4500 ŲƒŲŠŲ„Ųˆ Ø¨Ø§ŲŠØĒ Ų„Ų€ H.264. Ų…ØšØˇŲ„ ØĨذا ØĒŲ… ØļØ¨ØˇŲ‡ ØšŲ„Ų‰ 0.", + "transcoding_max_bitrate_description": "ŲŠŲ…ŲƒŲ† ØŖŲ† ŲŠØ¤Ø¯ŲŠ ØĒØšŲŠŲŠŲ† Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų…ØšØ¯Ų„ Ø§Ų„Ø¨ØĒ ØĨŲ„Ų‰ ØŦØšŲ„ ØŖØ­ØŦØ§Ų… Ø§Ų„Ų…Ų„ŲØ§ØĒ ØŖŲƒØĢØą Ų‚Ø§Ø¨Ų„ŲŠØŠ Ų„Ų„ØĒŲ†Ø¨Ø¤ Ø¨Ų‡Ø§ بØĒŲƒŲ„ŲØŠ Ø¨ØŗŲŠØˇØŠ Ø¨Ø§Ų„Ų†ØŗØ¨ØŠ Ų„Ų„ØŦŲˆØ¯ØŠ. ØšŲ†Ø¯ Ø¯Ų‚ØŠ 720 Ø¨ŲƒØŗŲ„ØŒ ØĒŲƒŲˆŲ† Ø§Ų„Ų‚ŲŠŲ… Ø§Ų„Ų†Ų…ŲˆØ°ØŦŲŠØŠ 2600 ŲƒŲŠŲ„Ųˆ بØĒ Ų„Ų€ VP9 ØŖŲˆ HEVC، ØŖŲˆ 4500 ŲƒŲŠŲ„Ųˆ بØĒ Ų„Ų€ H.264. Ų…ØšØˇŲ„ ØĨذا ØĒŲ… ØļØ¨ØˇŲ‡ ØšŲ„Ų‰ 0.", "transcoding_max_keyframe_interval": "Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų„ŲØ§ØĩŲ„ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ų„Ų„ØĨØˇØ§Øą Ø§Ų„ØąØĻŲŠØŗŲŠ", "transcoding_max_keyframe_interval_description": "؊ØļØ¨Øˇ Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖŲ‚ØĩŲ‰ Ų„Ų…ØŗØ§ŲØŠ Ø§Ų„ØĨØˇØ§Øą Ø¨ŲŠŲ† Ø§Ų„ØĨØˇØ§ØąØ§ØĒ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ. ØĒØ¤Ø¯ŲŠ Ø§Ų„Ų‚ŲŠŲ… Ø§Ų„Ų…Ų†ØŽŲØļØŠ ØĨŲ„Ų‰ Ø˛ŲŠØ§Ø¯ØŠ ØŗŲˆØĄ ŲƒŲØ§ØĄØŠ Ø§Ų„ØļØēØˇØŒ ŲˆŲ„ŲƒŲ†Ų‡Ø§ ØĒØšŲ…Ų„ ØšŲ„Ų‰ ØĒØ­ØŗŲŠŲ† ØŖŲˆŲ‚Ø§ØĒ Ø§Ų„Ø¨Ø­ØĢ ŲˆŲ‚Ø¯ ØĒØšŲ…Ų„ ØšŲ„Ų‰ ØĒØ­ØŗŲŠŲ† Ø§Ų„ØŦŲˆØ¯ØŠ ؁؊ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ ذاØĒ Ø§Ų„Ø­ØąŲƒØŠ Ø§Ų„ØŗØąŲŠØšØŠ. 0 ؊ØļØ¨Øˇ Ų‡Ø°Ų‡ Ø§Ų„Ų‚ŲŠŲ…ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§.", "transcoding_optimal_description": "Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„Ø¯Ų‚ØŠ Ø§Ų„ØŖØšŲ„Ų‰ Ų…Ų† Ø§Ų„Ø¯Ų‚ØŠ Ø§Ų„Ų…ØŗØĒŲ‡Ø¯ŲØŠ ØŖŲˆ بØĒŲ†ØŗŲŠŲ‚ ØēŲŠØą Ų…Ų‚Ø¨ŲˆŲ„", @@ -318,12 +366,16 @@ "trash_number_of_days_description": "ؚدد ØŖŲŠØ§Ų… Ø§Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ ؁؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ Ų‚Ø¨Ų„ Ø­Ø°ŲŲ‡Ø§ Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§", "trash_settings": "ØĨؚداداØĒ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_settings_description": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", + "unlink_all_oauth_accounts": "Ø§Ø˛Ø§Ų„ØŠ ØąØ¨Øˇ ØŦŲ…ŲŠØš Ø­ØŗØ§Ø¨Ø§ØĒ OAuth", + "unlink_all_oauth_accounts_description": "ØĒØ°ŲƒŲ‘Øą Ø§Ų† ØĒØ˛ŲŠŲ„ ØąØ¨Øˇ ØŦŲ…ŲŠØš Ø­ØŗØ§Ø¨Ø§ØĒ OAuth Ų‚Ø¨Ų„ Ø§Ų† ØĒŲ†Ų‚Ų„ Ø§Ų„Ų‰ Ų…Ø˛ŲˆØ¯ ØŦØ¯ŲŠØ¯.", + "unlink_all_oauth_accounts_prompt": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† Ø§Ø˛Ø§Ų„ØŠ ØąØ¨Øˇ ØŦŲ…ŲŠØš Ø­ØŗØ§Ø¨Ø§ØĒ OAuth؟ Ų‡Ø°Ø§ ØŗŲŠŲ‚ŲˆŲ… باؚاد؊ ØļØ¨Øˇ Ø§Ų„ID Ø§Ų„ØŽØ§Øĩ Ø¨Ø§Ų„OAuth Ų„ŲƒŲ„ Ų…ØŗØĒØŽØ¯Ų… ŲˆŲ„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ø§Ų„ØšŲ…Ų„ŲŠØŠ.", "user_cleanup_job": "ØĒŲ†Ø¸ŲŠŲ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_delete_delay": "ØŗŲŠØĒŲ… ØŦØ¯ŲˆŲ„ØŠ Ø­ØŗØ§Ø¨ {user} ŲˆŲ…Ø­ØĒŲˆŲŠØ§ØĒŲ‡ Ų„Ų„Ø­Ø°Ų Ø§Ų„Ų†Ų‡Ø§ØĻ؊ ؁؊ ØēØļŲˆŲ† {delay, plural, one {# ŲŠŲˆŲ…} other {# ØŖŲŠØ§Ų…}}.", "user_delete_delay_settings": "؁ØĒØąØŠ Ø§Ų„ØĒØŖØŽŲŠØą Ų‚Ø¨Ų„ Ø§Ų„Ø­Ø°Ų", "user_delete_delay_settings_description": "ؚدد Ø§Ų„ØŖŲŠØ§Ų… بؚد Ø§Ų„ØĨØ˛Ø§Ų„ØŠ Ų„Ø­Ø°Ų Ø­ØŗØ§Ø¨ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ŲˆŲ…Ø­ØĒŲˆŲŠØ§ØĒŲ‡ Ø¨Ø´ŲƒŲ„ داØĻŲ…. ØĒŲ‚ŲˆŲ… ŲˆØ¸ŲŠŲØŠ Ø­Ø°Ų Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø¨Ø§Ų„ØĒØ´ØēŲŠŲ„ ؁؊ Ų…Ų†ØĒØĩ؁ Ø§Ų„Ų„ŲŠŲ„ Ų„Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„ØŦØ§Ų‡Ø˛ŲŠŲ† Ų„Ų„Ø­Ø°Ų. ØŗŲŠØĒŲ… ØĒŲ‚ŲŠŲŠŲ… Ø§Ų„ØĒØēŲŠŲŠØąØ§ØĒ ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد ؁؊ Ø§Ų„ØĒŲ†ŲŲŠØ° Ø§Ų„Ų‚Ø§Ø¯Ų….", "user_delete_immediately": "ØŗŲŠØĒŲ… ؈ØļØš Ø­ØŗØ§Ø¨ {user} ŲˆŲ…Ø­ØĒŲˆŲŠØ§ØĒŲ‡ ؁؊ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą Ų„Ų„Ø­Ø°Ų Ø§Ų„Ø¯Ø§ØĻŲ… ØšŲ„Ų‰ Ø§Ų„ŲŲˆØą.", "user_delete_immediately_checkbox": "Ų‚Ø§ØĻŲ…ØŠ Ø§Ų†ØĒØ¸Ø§Øą Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ŲˆØ§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ų„Ų„Ø­Ø°Ų Ø§Ų„ŲŲˆØąŲŠ", + "user_details": "ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_management": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_password_has_been_reset": "ØĒŲ…ØĒ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…:", "user_password_reset_description": "ŲŠØąØŦŲ‰ ØĒØ˛ŲˆŲŠØ¯ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø¨ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„Ų…Ø¤Ų‚ØĒØŠ ؈ØĨØ¨Ų„Ø§ØēŲ‡ Ø¨ØŖŲ†Ų‡ ØŗŲŠØ­ØĒاØŦ ØĨŲ„Ų‰ ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą ØšŲ†Ø¯ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø§Ų„ØĒØ§Ų„ŲŠ.", @@ -343,9 +395,19 @@ "admin_password": "ŲƒŲ„Ų…ØŠ ØŗØą Ø§Ų„Ų…Ø´ØąŲ", "administration": "Ø§Ų„ØĨØ¯Ø§ØąØŠ", "advanced": "Ų…ØĒŲ‚Ø¯Ų…", - "advanced_settings_prefer_remote_subtitle": "ØĒŲƒŲˆŲ† بؚØļ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø¨ØˇŲŠØĻØŠ Ų„Ų„ØēØ§ŲŠØŠ ؁؊ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛. Ų‚Ų… بØĒŲ†Ø´ŲŠØˇ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ø¨ØšŲŠØ¯ØŠ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† Ø°Ų„Ųƒ.", + "advanced_settings_enable_alternate_media_filter_subtitle": "Ø§ØŗØĒØŽØ¯Ų… Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą Ų„ØĒØĩŲŲŠØŠ Ø§Ų„ŲˆØŗØ§ØĻØˇ اØĢŲ†Ø§ØĄ Ø§Ų„Ų…Ø˛Ø§Ų…Ų†Ų‡ Ø¨Ų†Ø§ØĄ ØšŲ„Ų‰ Ų…ØšØ§ŲŠŲŠØą Ø¨Ø¯ŲŠŲ„ØŠ. ØŦØąØ¨ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą ŲŲ‚Øˇ ŲƒØ§Ų† Ų„Ø¯ŲŠŲƒ Ų…Ø´Ø§ŲƒŲ„ Ų…Øš Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø¨Ø§Ų„ŲƒØ´Ų ØšŲ† ØŦŲ…ŲŠØš Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ.", + "advanced_settings_enable_alternate_media_filter_title": "[ØĒØŦØąŲŠØ¨ŲŠ] Ø§ØŗØĒØŽØ¯Ų… ØŦŲ‡Ø§Ø˛ ØĒØĩŲŲŠØŠ Ų…Ø˛Ø§Ų…Ų†Ų‡ Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ Ø¨Ø¯ŲŠŲ„", + "advanced_settings_log_level_title": "Ų…ØŗØĒŲˆŲ‰ Ø§Ų„ØŗØŦŲ„: {level}", + "advanced_settings_prefer_remote_subtitle": "ØĒŲƒŲˆŲ† بؚØļ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø¨ØˇŲŠØĻØŠ Ų„Ų„ØēØ§ŲŠØŠ ؁؊ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ. Ų‚Ų… بØĒŲØšŲŠŲ„ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ø¨ØšŲŠØ¯ØŠ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† Ø°Ų„Ųƒ.", "advanced_settings_prefer_remote_title": "ØĒ؁ØļŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ø¨ØšŲŠØ¯ØŠ", + "advanced_settings_proxy_headers_subtitle": "ØšØąŲ ØšŲ†Ø§ŲˆŲŠŲ† Ø§Ų„ŲˆŲƒŲŠŲ„ Ø§Ų„ØĒ؊ ŲŠØŗØĒØŽØ¯Ų…Ų‡Ø§ Immich Ų„Ø§ØąØŗØ§Ų„ ŲƒŲ„ ØˇŲ„Ø¨ Ø´Ø¨ŲƒŲŠ", + "advanced_settings_proxy_headers_title": "ØšŲ†Ø§ŲˆŲŠŲ† Ø§Ų„ŲˆŲƒŲŠŲ„", + "advanced_settings_readonly_mode_subtitle": "ØĒØĒŲŠØ­ Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠ ؈ØļØš Ø§Ų„ØšØąØļ ŲŲ‚ØˇØŒ Ø­ŲŠØĢ ŲŠŲ…ŲƒŲ† Ų„Ų„Ų…ØŗØĒØŽØ¯Ų… Ų…ØšØ§ŲŠŲ†ØŠ Ø§Ų„ØĩŲˆØą ŲŲ‚ØˇØŒ Ø¨ŲŠŲ†Ų…Ø§ ؊ØĒŲ… ØĒØšØˇŲŠŲ„ ØŦŲ…ŲŠØš Ø§Ų„ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØŖØŽØąŲ‰ Ų…ØĢŲ„ ØĒØ­Ø¯ŲŠØ¯ ؚد؊ ØĩŲˆØąØŒ ØŖŲˆ Ų…Ø´Ø§ØąŲƒØĒŲ‡Ø§ØŒ ØŖŲˆ بØĢŲ‡Ø§ØŒ ØŖŲˆ Ø­Ø°ŲŲ‡Ø§. ŲŠŲ…ŲƒŲ† ØĒŲØšŲŠŲ„/ØĒØšØˇŲŠŲ„ ؈ØļØš Ø§Ų„ØšØąØļ ŲŲ‚Øˇ Ų…Ų† ØŽŲ„Ø§Ų„ ØĩŲˆØąØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ؁؊ Ø§Ų„Ø´Ø§Ø´ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ", + "advanced_settings_readonly_mode_title": "؈ØļØš Ø§Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚Øˇ", + "advanced_settings_self_signed_ssl_subtitle": "ØĒØŽØˇŲŠ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø´Ų‡Ø§Ø¯ØŠ SSL Ų„ØŽØ§Ø¯Ų… Ø§Ų„Ų†Ų‚ØˇØŠ Ø§Ų„Ų†Ų‡Ø§ØĻ؊. Ų…ŲƒŲ„ŲˆØ¨ Ų„Ų„Ø´Ų‡Ø§Ø¯Ø§ØĒ Ø§Ų„Ų…ŲˆŲ‚ØšØŠ ذاØĒŲŠØ§.", "advanced_settings_self_signed_ssl_title": "Ø§Ų„ØŗŲ…Ø§Ø­ Ø¨Ø´Ų‡Ø§Ø¯Ø§ØĒ SSL Ø§Ų„Ų…ŲˆŲ‚ØšØŠ ذاØĒŲŠŲ‹Ø§", + "advanced_settings_sync_remote_deletions_subtitle": "Ø­Ø°Ų Ø§Ųˆ Ø§ØŗØĒؚاد؊ ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„Ø§ØĩŲˆŲ„ ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØŦŲ‡Ø§Ø˛ ØšŲ†Ø¯ ØĒŲ†ŲŲŠØ° Ø§Ų„ØšŲ…Ų„ŲŠØŠ ØšŲ„Ų‰ Ø§Ų„ŲˆŲŠØ¨", + "advanced_settings_sync_remote_deletions_title": "Ų…Ø˛Ø§Ų…Ų†ØŠ ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø­Ø°Ų ØšŲ† بؚد [ØĒØŦØąŲŠØ¨ŲŠ]", "advanced_settings_tile_subtitle": "ØĨؚداداØĒ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø§Ų„Ų…ØĒŲ‚Ø¯Ų…ØŠ", "advanced_settings_troubleshooting_subtitle": "ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĨØļØ§ŲŲŠØŠ Ų„Ø§ØŗØĒŲƒØ´Ø§Ų Ø§Ų„ØŖØŽØˇØ§ØĄ ؈ØĨØĩŲ„Ø§Ø­Ų‡Ø§", "advanced_settings_troubleshooting_title": "Ø§ØŗØĒŲƒØ´Ø§Ų Ø§Ų„ØŖØŽØˇØ§ØĄ ؈ØĨØĩŲ„Ø§Ø­Ų‡Ø§", @@ -357,6 +419,7 @@ "album_cover_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ ØēŲ„Ø§Ų Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_delete_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… {album}؟", "album_delete_confirmation_description": "ØĨذا ØĒŲ…ØĒ Ų…Ø´Ø§ØąŲƒØŠ Ų‡Ø°Ø§ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…ØŒ ؁؄؆ ؊ØĒŲ…ŲƒŲ† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„ØĸØŽØąŲˆŲ† Ų…Ų† Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„ŲŠŲ‡ بؚد Ø§Ų„ØĸŲ†.", + "album_deleted": "ØĒŲ… Ø­Ø°Ų Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…", "album_info_card_backup_album_excluded": "Ų…ØŗØĒبؚد", "album_info_card_backup_album_included": "Ų…ØĒØļŲ…Ų†ØŠ", "album_info_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", @@ -366,7 +429,9 @@ "album_options": "ØĨؚداداØĒ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_remove_user": "Ų‡Ų„ ØĒØąØēب ؁؊ ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ØŸ", "album_remove_user_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØŠ {user}؟", + "album_search_not_found": "Ų„Ų… ؊ØĒŲ… Ø§ŲŠØŦاد Ø§Ų„Ø¨ŲˆŲ… Ų…ØˇØ§Ø¨Ų‚ Ų„Ø¨Ø­ØĢ؃", "album_share_no_users": "ŲŠØ¨Ø¯Ųˆ ØŖŲ†Ųƒ Ų‚Ų…ØĒ Ø¨Ų…Ø´Ø§ØąŲƒØŠ Ų‡Ø°Ø§ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ų…Øš ØŦŲ…ŲŠØš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØŖŲˆ Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØŖŲŠ Ų…ØŗØĒØŽØ¯Ų… Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…ØšŲ‡.", + "album_summary": "Ų…Ų„ØŽØĩ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "album_updated_setting_description": "ØĒŲ„Ų‚ŲŠ ØĨØ´ØšØ§ØąŲ‹Ø§ ØšØ¨Øą Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØšŲ†Ø¯Ų…Ø§ ŲŠØ­ØĒ؈؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø§Ų„Ų…Ø´ØĒØąŲƒ ØšŲ„Ų‰ Ų…Ø­ØĒŲˆŲŠØ§ØĒ ØŦØ¯ŲŠØ¯ØŠ", "album_user_left": "ØĒŲ… ØĒØąŲƒ {album}", @@ -382,6 +447,10 @@ "album_with_link_access": "Ø§Ų„ØŗŲ…Ø§Ø­ Ų„ØŖŲŠ Ø´ØŽØĩ Ų„Ø¯ŲŠŲ‡ Ø§Ų„ØąØ§Ø¨Øˇ Ø¨ØąØ¤ŲŠØŠ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ŲŠŲ† ؁؊ Ų‡Ø°Ø§ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ….", "albums": "Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", "albums_count": "{count, plural, one {{count, number} ØŖŲ„Ø¨ŲˆŲ…} other {{count, number} ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ}}", + "albums_default_sort_order": "ØĒØąØĒŲŠØ¨ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø§Ų„Ø§ŲØĒØąØ§Øļ؊", + "albums_default_sort_order_description": "ØĒØąØĒŲŠØ¨ ŲØąØ˛ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŖŲˆŲ„ŲŠ ØšŲ†Ø¯ ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ ØŦØ¯ŲŠØ¯ØŠ.", + "albums_feature_description": "Ų…ØŦŲ…ŲˆØšØŠ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØĒ؊ ŲŠŲ…ŲƒŲ† Ų…Ø´Ø§ØąŲƒØĒŲ‡Ø§ Ų…Øš Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØĸØŽØąŲŠŲ†.", + "albums_on_device_count": "ؚدد Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛ ({count})", "all": "Ø§Ų„ŲƒŲ„", "all_albums": "ØŦŲ…ŲŠØš Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", "all_people": "ØŦŲ…ŲŠØš Ø§Ų„ØŖØ´ØŽØ§Øĩ", @@ -392,20 +461,24 @@ "allow_public_user_to_upload": "Ø§Ų„ØŗŲ…Ø§Ø­ Ų„Ų„Ų…ØŗØĒØŽØ¯Ų… Ø§Ų„ØšØ§Ų… Ø¨Ø§Ų„ØąŲØš", "alt_text_qr_code": "ØĩŲˆØąØŠ ØąŲ…Ø˛ Ø§Ų„Ø§ØŗØĒØŦاب؊ Ø§Ų„ØŗØąŲŠØšØŠ (QR)", "anti_clockwise": "ØšŲƒØŗ اØĒØŦØ§Ų‡ ØšŲ‚Ø§ØąØ¨ Ø§Ų„ØŗØ§ØšØŠ", - "api_key": "؅؁ØĒاح ŲˆØ§ØŦŲ‡ØŠ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ", + "api_key": "؅؁ØĒاح API", "api_key_description": "ØŗŲŠØĒŲ… ØšØąØļ Ų‡Ø°Ų‡ Ø§Ų„Ų‚ŲŠŲ…ØŠ Ų…ØąØŠ ŲˆØ§Ø­Ø¯ØŠ ŲŲ‚Øˇ. ŲŠØąØŦŲ‰ Ø§Ų„ØĒØŖŲƒØ¯ Ų…Ų† Ų†ØŗØŽŲ‡Ø§ Ų‚Ø¨Ų„ ØĨØēŲ„Ø§Ų‚ Ø§Ų„Ų†Ø§ŲØ°ØŠ.", "api_key_empty": "؊ØŦب ØŖŲ„Ø§ ŲŠŲƒŲˆŲ† Ø§ØŗŲ… ؅؁ØĒاح API ŲØ§ØąØēŲ‹Ø§", - "api_keys": "Ų…ŲØ§ØĒŲŠØ­ ŲˆØ§ØŦŲ‡ØŠ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ", - "app_bar_signout_dialog_content": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§Ų„ØŽØąŲˆØŦ", + "api_keys": "Ų…ŲØ§ØĒŲŠØ­ API", + "app_bar_signout_dialog_content": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ؟", "app_bar_signout_dialog_ok": "Ų†ØšŲ…", "app_bar_signout_dialog_title": "ØŽØąŲˆØŦ", "app_settings": "ØĨؚداداØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "appears_in": "ŲŠØ¸Ų‡Øą ؁؊", + "apply_count": "ØĒØˇØ¨ŲŠŲ‚ ({count, number})", "archive": "Ø§Ų„ØŖØąØ´ŲŠŲ", + "archive_action_prompt": "{count} اØļ؊؁ ØĨŲ„Ų‰ Ø§Ų„Ø§ØąØ´ŲŠŲ", "archive_or_unarchive_photo": "ØŖØąØ´ŲØŠ Ø§Ų„ØĩŲˆØąØŠ ØŖŲˆ ØĨŲ„ØēØ§ØĄ ØŖØąØ´ŲØĒŲ‡Ø§", "archive_page_no_archived_assets": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø¤ØąØ´ŲØŠ", + "archive_page_title": "Ø§ØąØ´ŲŠŲ ({count})", "archive_size": "Ø­ØŦŲ… Ø§Ų„ØŖØąØ´ŲŠŲ", "archive_size_description": "ØĒŲƒŲˆŲŠŲ† Ø­ØŦŲ… Ø§Ų„ØŖØąØ´ŲŠŲ Ų„Ų„ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ (Ø¨Ø§Ų„ØŦ؊ØŦØ§Ø¨Ø§ŲŠØĒ)", + "archived": "Ų…Ø¤ØąØ´ŲØŠ", "archived_count": "{count, plural, other {Ø§Ų„ØŖØąØ´ŲŠŲ #}}", "are_these_the_same_person": "Ų‡Ų„ Ų‡Ø¤Ų„Ø§ØĄ Ų‡Ų… Ų†ŲØŗ Ø§Ų„Ø´ØŽØĩ؟", "are_you_sure_to_do_this": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØŖŲ† ØĒŲØšŲ„ Ų‡Ø°Ø§ØŸ", @@ -427,37 +500,64 @@ "asset_list_settings_title": "Ø´Ø¨ŲƒØŠ Ø§Ų„ØĩŲˆØą", "asset_offline": "Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ ØēŲŠØą اØĒØĩØ§Ų„", "asset_offline_description": "Ų„Ų… ŲŠØšØ¯ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ Ø§Ų„ØŽØ§ØąØŦ؊ Ų…ŲˆØŦŲˆØ¯Ų‹Ø§ ØšŲ„Ų‰ Ø§Ų„Ų‚ØąØĩ. ŲŠØąØŦŲ‰ Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ų…ØŗØ¤ŲˆŲ„ Immich Ų„Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØ§ØšØ¯ØŠ.", + "asset_restored_successfully": "ØĒŲ… Ø§ØŗØĒؚاد؊ Ø§Ų„Ø§ØĩŲ„ Ø¨Ų†ØŦاح", "asset_skipped": "ØĒŲ… ØĒØŽØˇŲŠŲ‡", "asset_skipped_in_trash": "؁؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", + "asset_trashed": "اØĩŲˆŲ„ Ų…Ø­Ø°ŲˆŲØŠ", + "asset_troubleshoot": "Ø§ØŗØĒŲƒØ´Ø§Ų Ų…Ø´Ø§ŲƒŲ„ Ø§Ų„ØŖØĩŲˆŲ„", "asset_uploaded": "ØĒŲ… Ø§Ų„ØąŲØš", "asset_uploading": "ØŦØ§ØąŲ Ø§Ų„ØąŲØšâ€Ļ", + "asset_viewer_settings_subtitle": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ ØšØ§ØąØļ Ø§Ų„Ų…ØšØąØļ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "asset_viewer_settings_title": "ØšØ§ØąØļ Ø§Ų„ØŖØĩŲˆŲ„", "assets": "Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", "assets_added_count": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", "assets_added_to_album_count": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {count, plural, one {# Ø§Ų„ØŖØĩŲ„} other {# Ø§Ų„ØŖØĩŲˆŲ„}} ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", - "assets_added_to_name_count": "ØĒŲ… ØĨØļØ§ŲØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ }} ØĨŲ„Ų‰ {hasName, select, true {{name}} other {ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯}}", + "assets_added_to_albums_count": "ØĒŲ…ØĒ اØļØ§ŲØŠ {assetTotal, plural, one {# اØĩŲ„} other {# اØĩŲˆŲ„}} to {albumTotal, plural, one {# Ø§Ų„Ø¨ŲˆŲ…} other {# Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} Ų„Ø§ŲŠŲ…ŲƒŲ† اØļØ§ŲØĒŲ‡ Ø§Ų„Ų‰ Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…", + "assets_cannot_be_added_to_albums": "{count, plural, one {اØĩŲ„} other {اØĩŲˆŲ„}} Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨØļØ§ŲØĒŲ‡ ØĨŲ„Ų‰ ØŖŲŠ Ų…Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", "assets_count": "{count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", + "assets_deleted_permanently": "{count} Ø§Ų„Ø§Øĩ(؈)Ų„ Ø§Ų„Ų…Ø­Ø°ŲˆŲ(Ų‡) Ø¨Ø´ŲƒŲ„ داØĻŲ…", + "assets_deleted_permanently_from_server": "{count} Ø§Ų„Ø§Øĩ(؈)Ų„ Ø§Ų„Ų…Ø­Ø°ŲˆŲ(Ų‡) Ø¨Ø´ŲƒŲ„ داØĻŲ…ŲŠ Ų…Ų† ØŽØ§Ø¯Ų… Immich", + "assets_downloaded_failed": "{count, plural, one {ØĒŲ… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ # ؅؄؁ - {error} ؅؄؁ ŲØ´Ų„} other {ØĒŲ… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ # Ų…Ų„ŲØ§ØĒ - {error} Ų…Ų„ŲØ§ØĒ ŲØ´Ų„ØĒ}}", + "assets_downloaded_successfully": "{count, plural, one {ØĒŲ… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ # ؅؄؁ Ø¨Ų†ØŦاح} other {ØĒŲ… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ # Ų…Ų„ŲØ§ØĒ Ø¨Ų†ØŦاح}}", "assets_moved_to_trash_count": "ØĒŲ… Ų†Ų‚Ų„ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "assets_permanently_deleted_count": "ØĒŲ… Ø­Ø°Ų {count, plural, one {# Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {# Ų‡Ø°Ų‡ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} Ø¨Ø´ŲƒŲ„ داØĻŲ…", "assets_removed_count": "ØĒŲ…ØĒ ØĨØ˛Ø§Ų„ØŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", + "assets_removed_permanently_from_device": "{count} Ø§Ų„Ø§Øĩ(؈)Ų„ Ų…Ø­Ø°ŲˆŲ(Ų‡) Ų…Ų† Ø§Ų„ØŦŲ‡Ø§Ø˛", "assets_restore_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§ØŗØĒؚاد؊ ØŦŲ…ŲŠØš Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠØŸ Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ! Ų„Ø§Ø­Ø¸ ØŖŲ†Ų‡ Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§ØŗØĒؚاد؊ ØŖŲŠ ØŖØĩŲˆŲ„ ØēŲŠØą Ų…ØĒØĩŲ„ØŠ Ø¨Ų‡Ø°Ų‡ Ø§Ų„ØˇØąŲŠŲ‚ØŠ.", "assets_restored_count": "ØĒŲ…ØĒ Ø§ØŗØĒؚاد؊ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}}", + "assets_restored_successfully": "{count} Ø§Ų„Ø§Øĩ(؈)Ų„ Ø§Ų„Ų…ØŗØĒؚاد(Ų‡) Ø¨Ų†ØŦاح", + "assets_trashed": "{count} Ø§Ų„Ø§ØĩŲ„(؈) Ų„ Ø§Ų„Ų…Ų†Ų‚ŲˆŲ„Ų‡ Ø§Ų„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "assets_trashed_count": "ØĒŲ… ØĨØąØŗØ§Ų„ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", + "assets_trashed_from_server": "{count} Ø§Ų„Ø§Øĩ(؈)Ų„ Ø§Ų„Ų…Ų†Ų‚ŲˆŲ„ØŠ Ø§Ų„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ Ų…Ų† ØŽØ§Ø¯Ų… Immich", "assets_were_part_of_album_count": "{count, plural, one {Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {Ų‡Ø°Ų‡ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø¨Ø§Ų„ŲØšŲ„", + "assets_were_part_of_albums_count": "{count, plural, one {اØĩŲ„ Ų‡Ųˆ} other {اØĩŲˆŲ„ Ų‡ŲŠ}}Ø¨Ø§Ų„ŲØšŲ„ ØŦØ˛ØĄ Ų…Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", "authorized_devices": "Ø§Ų„ØŖØŦŲ‡Ø˛Ų‡ Ø§Ų„Ų…ØŽŲˆŲ„ØŠ", + "automatic_endpoint_switching_subtitle": "اØĒØĩŲ„ Ų…Ø­Ų„ŲŠØ§ Ų…Ų† ØŽŲ„Ø§Ų„ Ø´Ø¨ŲƒŲ‡ Wi-Fi ØšŲ†Ø¯ ØĒŲˆŲØąŲ‡Ø§ ؈ Ø§ØŗØĒØŽØ¯Ų… اØĒØĩØ§Ų„Ø§ØĒ Ø¨Ø¯ŲŠŲ„Ų‡ ؁؊ Ø§Ų„Ø§Ų…Ø§ŲƒŲ† Ø§Ų„Ø§ØŽØąŲ‰", + "automatic_endpoint_switching_title": "ØĒØ¨Ø¯ŲŠŲ„ URL ØĒŲ„Ų‚Ø§ØĻ؊", + "autoplay_slideshow": "ØĒØ´ØēŲŠŲ„ ØĒŲ„Ų‚Ø§ØĻ؊ Ų„ØšØąØļ Ø§Ų„Ø´ØąØ§ØĻØ­", "back": "ØŽŲ„Ų", "back_close_deselect": "Ø§Ų„ØąØŦŲˆØš ØŖŲˆ Ø§Ų„ØĨØēŲ„Ø§Ų‚ ØŖŲˆ ØĨŲ„ØēØ§ØĄ Ø§Ų„ØĒØ­Ø¯ŲŠØ¯", + "background_backup_running_error": "؊ØĒŲ… ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ø­Ø§Ų„ŲŠŲ‹Ø§ØŒ ŲˆŲ„Ø§ ŲŠŲ…ŲƒŲ† Ø¨Ø¯ØĄ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ŲŠØ¯ŲˆŲŠ", + "background_location_permission": "Ø§Ø°Ų† Ø§Ų„ŲˆØĩŲˆŲ„ Ų„Ų„Ų…ŲˆŲ‚Øš ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ", + "background_location_permission_content": "Ų„Ų„ØĒŲ…ŲƒŲ† Ų…Ų† ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„Ø´Ø¨ŲƒŲ‡ Ø¨Ø§Ų„ØŽŲ„ŲŲŠØŠØŒ Immich ŲŠØ­ØĒاØŦ*داØĻŲ…Ø§* Ų„Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ų…ŲˆŲ‚Øš Ø¯Ų‚ŲŠŲ‚ Ų„ŲŠØĒŲ…ŲƒŲ† Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ų…Ų† Ų‚ØąØ§ØĻØŠ Ø§ØŗŲ… Ø´Ø¨ŲƒØŠ Ø§Ų„Wi-Fi", + "background_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØŽŲ„ŲŲŠØŠ", + "backup": "Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠ", + "backup_album_selection_page_albums_device": "Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛ ({count})", "backup_album_selection_page_albums_tap": "Ø§Ų†Ų‚Øą Ų„Ų„ØĒØļŲ…ŲŠŲ†ØŒ ŲˆØ§Ų†Ų‚Øą Ų†Ų‚ØąŲ‹Ø§ Ų…Ø˛Ø¯ŲˆØŦŲ‹Ø§ Ų„Ų„Ø§ØŗØĒØĢŲ†Ø§ØĄ", "backup_album_selection_page_assets_scatter": "ŲŠŲ…ŲƒŲ† ØŖŲ† ØĒŲ†ØĒØ´Øą Ø§Ų„ØŖØĩŲˆŲ„ ØšØ¨Øą ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ų…ØĒؚدد؊. ŲˆØ¨Ø§Ų„ØĒØ§Ų„ŲŠØŒ ŲŠŲ…ŲƒŲ† ØĒØļŲ…ŲŠŲ† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ ØŖŲˆ Ø§ØŗØĒØ¨ØšØ§Ø¯Ų‡Ø§ ØŖØĢŲ†Ø§ØĄ ØšŲ…Ų„ŲŠØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ.", "backup_album_selection_page_select_albums": "حدد Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", "backup_album_selection_page_selection_info": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ø§ØŽØĒŲŠØ§Øą", "backup_album_selection_page_total_assets": "ØĨØŦŲ…Ø§Ų„ŲŠ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ŲØąŲŠØ¯ØŠ", + "backup_albums_sync": "Ų…Ø˛Ø§Ų…Ų†ØŠ ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_all": "Ø§Ų„ØŦŲ…ŲŠØš", - "backup_background_service_backup_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ...", - "backup_background_service_connection_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ...", - "backup_background_service_default_notification": "Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ...", + "backup_background_service_backup_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠâ€Ļ", + "backup_background_service_connection_failed_message": "ŲØ´Ų„ ؁؊ Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…. ØŦØ§ØąŲ ØĨؚاد؊ Ø§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠâ€Ļ", + "backup_background_service_current_upload_notification": "ØĒØ­Ų…ŲŠŲ„ {filename}", + "backup_background_service_default_notification": "Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠâ€Ļ", "backup_background_service_error_title": "ØŽØˇØŖ ؁؊ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", - "backup_background_service_in_progress_notification": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ...", + "backup_background_service_in_progress_notification": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒâ€Ļ", + "backup_background_service_upload_failure_notification": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ {filename}", "backup_controller_page_albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ احØĒŲŠØ§ØˇŲŠØŠ", "backup_controller_page_background_app_refresh_disabled_content": "Ų‚Ų… بØĒŲ…ŲƒŲŠŲ† ØĒØ­Ø¯ŲŠØĢ ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŽŲ„ŲŲŠØŠ ؁؊ Ø§Ų„ØĨؚداداØĒ > ØšØ§Ų… > ØĒØ­Ø¯ŲŠØĢ ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ.", "backup_controller_page_background_app_refresh_disabled_title": "ØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ", @@ -468,17 +568,22 @@ "backup_controller_page_background_battery_info_title": "ØĒØ­ØŗŲŠŲ† Ø§Ų„Ø¨ØˇØ§ØąŲŠØŠ", "backup_controller_page_background_charging": "ŲŲ‚Øˇ ØŖØĢŲ†Ø§ØĄ Ø§Ų„Ø´Ø­Ų†", "backup_controller_page_background_configure_error": "ŲØ´Ų„ ؁؊ ØĒŲƒŲˆŲŠŲ† ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", + "backup_controller_page_background_delay": "ØĒØ§ØŽŲŠØą Ø§Ų„ØŽØ˛Ų† Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„Ø§ØĩŲˆŲ„: {duration}", "backup_controller_page_background_description": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ų„ØĨØŦØąØ§ØĄ Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠ Ų„ØŖŲŠ ØŖØĩŲˆŲ„ ØŦØ¯ŲŠØ¯ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ Ø¯ŲˆŲ† Ø§Ų„Ø­Ø§ØŦØŠ ØĨŲ„Ų‰ ؁ØĒØ­ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "backup_controller_page_background_is_off": "ØĒŲ… ØĨŲŠŲ‚Ø§Ų Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„ØŽŲ„ŲŲŠØŠ", "backup_controller_page_background_is_on": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„Ų„ØŽŲ„ŲŲŠØŠ Ų‚ŲŠØ¯ Ø§Ų„ØĒØ´ØēŲŠŲ„", "backup_controller_page_background_turn_off": "Ų‚Ų… بØĨŲŠŲ‚Ø§Ų ØĒØ´ØēŲŠŲ„ ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", "backup_controller_page_background_turn_on": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ ØŽØ¯Ų…ØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", - "backup_controller_page_background_wifi": "ŲŲ‚Øˇ ØšŲ„Ų‰ ŲˆØ§ŲŠ ŲØ§ŲŠ", + "backup_controller_page_background_wifi": "ŲŲ‚Øˇ ØšŲ„Ų‰ Wi-Fi", "backup_controller_page_backup": "Ø¯ØšŲ…", "backup_controller_page_backup_selected": "Ø§Ų„Ų…Ø­Ø¯Ø¯: ", "backup_controller_page_backup_sub": "Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ", + "backup_controller_page_created": "Ø§Ų†Ø´ØĻ ؁؊ :{date}", "backup_controller_page_desc_backup": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØŖŲ…Ø§Ų…ŲŠ Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ØĨŲ„Ų‰ Ø§Ų„ØŽØ§Ø¯Ų… ØšŲ†Ø¯ ؁ØĒØ­ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚.", "backup_controller_page_excluded": "Ų…ØŗØĒبؚد: ", + "backup_controller_page_failed": "ŲØ´Ų„ ({count})", + "backup_controller_page_filename": "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų : {filename} [{size}]", + "backup_controller_page_id": "Ų‡ŲˆŲŠØŠ: {id}", "backup_controller_page_info": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_controller_page_none_selected": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØĒØ­Ø¯ŲŠØ¯", "backup_controller_page_remainder": "Ø¨Ų‚ŲŠØŠ", @@ -487,19 +592,28 @@ "backup_controller_page_start_backup": "Ø¨Ø¯ØĄ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_controller_page_status_off": "Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ ØēŲŠØą ŲØšØ§Ų„ØŠ", "backup_controller_page_status_on": "Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ ŲØšØ§Ų„ØŠ", - "backup_controller_page_to_backup": "Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ", + "backup_controller_page_storage_format": "{used} Ų…Ų† {total} Ų…ØŗØĒØŽØ¯Ų…", + "backup_controller_page_to_backup": "Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ų†ØŗØŽŲ‡Ø§ احØĒŲŠØ§ØˇŲŠØ§", "backup_controller_page_total_sub": "ØŦŲ…ŲŠØš Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ŲØąŲŠØ¯ØŠ Ų…Ų† ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ų…ØŽØĒØ§ØąØŠ", "backup_controller_page_turn_off": "Ų‚Ų… بØĨŲŠŲ‚Ø§Ų ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„Ų…Ų‚Ø¯Ų…ØŠ", "backup_controller_page_turn_on": "Ų‚Ų… بØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„Ų…Ų‚Ø¯Ų…ØŠ", "backup_controller_page_uploading_file_info": "ØĒØ­Ų…ŲŠŲ„ Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ų…Ų„Ų", "backup_err_only_album": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨØ˛Ø§Ų„ØŠ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… Ø§Ų„ŲˆØ­ŲŠØ¯", + "backup_error_sync_failed": "ŲØ´Ų„ Ø§Ų„Ų…Ø˛Ø§Ų…Ų†ØŠ. Ų„Ø§ ŲŠŲ…ŲƒŲ† Ų…ØšØ§Ų„ØŦØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ.", "backup_info_card_assets": "ØŖØĩŲˆŲ„", "backup_manual_cancelled": "Ų…Ų„Øē؊", "backup_manual_in_progress": "Ų‚ŲŠØ¯ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ Ø­Ø§ŲˆŲ„ Ų…ØąŲ‡ Ø§ØŽØąŲ‰", "backup_manual_success": "Ų†ØŦاح", "backup_manual_title": "Ø­Ø§Ų„ØŠ Ø§Ų„ØĒØ­Ų…ŲŠŲ„", + "backup_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", "backup_options_page_title": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", + "backup_setting_subtitle": "Ø§Ø¯Ø§ØąØŠ اؚداداØĒ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ ŲˆØ§Ų„Ų…Ų‚Ø¯Ų…ØŠ", + "backup_settings_subtitle": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„ØĒØ­Ų…ŲŠŲ„", "backward": "Ø§Ų„Ų‰ Ø§Ų„ŲˆØąØ§ØĄ", + "biometric_auth_enabled": "Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ø§Ų„Ø¨Ø§ŲŠŲˆŲ…ØĒØąŲŠØŠ Ų…ŲØšŲ„Ų‡", + "biometric_locked_out": "Ų„Ų‚Ø¯ ؂؁؄ØĒ ØšŲ†Ųƒ Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ø§Ų„Ø¨ŲŠŲˆŲ…ØĒØąŲŠØŠ", + "biometric_no_options": "Ų„Ø§ ØĒ؈ØŦد ØŽŲŠØ§ØąØ§ØĒ Ø¨Ø§ŲŠŲˆŲ…ØĒØąŲŠØŠ Ų…ØĒŲˆŲØąØŠ", + "biometric_not_available": "Ø§Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ø§Ų„Ø¨ŲŠŲˆŲ…ØĒØąŲŠØŠ ØēŲŠØą Ų…ØĒاح؊ ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØŦŲ‡Ø§Ø˛", "birthdate_saved": "ØĒŲ… Ø­ŲØ¸ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ Ø¨Ų†ØŦاح", "birthdate_set_description": "؊ØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų… ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ Ų„Ø­ØŗØ§Ø¨ ØšŲ…Øą Ų‡Ø°Ø§ Ø§Ų„Ø´ØŽØĩ ŲˆŲ‚ØĒ Ø§Ų„ØĒŲ‚Ø§Øˇ Ø§Ų„ØĩŲˆØąØŠ.", "blurred_background": "ØŽŲ„ŲŲŠØŠ Ų…Ø´ŲˆØ´ØŠ", @@ -513,13 +627,14 @@ "cache_settings_clear_cache_button": "Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", "cache_settings_clear_cache_button_title": "ŲŠŲ‚ŲˆŲ… Ø¨Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„Ų„ØĒØˇØ¨ŲŠŲ‚.ØŗŲŠØ¤ØĢØą Ų‡Ø°Ø§ Ø¨Ø´ŲƒŲ„ ŲƒØ¨ŲŠØą ØšŲ„Ų‰ ØŖØ¯Ø§ØĄ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø­ØĒŲ‰ ØĨؚاد؊ Ø¨Ų†Ø§ØĄ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ.", "cache_settings_duplicated_assets_clear_button": "ŲˆØ§ØļØ­", - "cache_settings_duplicated_assets_subtitle": "Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų„ØĒ؊ ØĒŲ… ØĒØŦØ§Ų‡Ų„Ų‡Ø§ Ø§Ų„Ų…Ø¯ØąØŦØŠ ؁؊ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", + "cache_settings_duplicated_assets_subtitle": "Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ų„ØĒ؊ ØĒŲ… ØĒØŦØ§Ų‡Ų„Ų‡Ø§ ؁؊ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", + "cache_settings_duplicated_assets_title": "Ø§Ų„Ø§ØĩŲˆŲ„ Ø§Ų„Ų…ŲƒØąØąØŠ ({count})", "cache_settings_statistics_album": "Ų…ŲƒØĒØ¨Ų‡ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąŲ‡", "cache_settings_statistics_full": "ØĩŲˆØą ŲƒØ§Ų…Ų„ØŠ", "cache_settings_statistics_shared": "ØĩŲˆØąØŠ ØŖŲ„Ø¨ŲˆŲ… Ų…Ø´ØĒØąŲƒØŠ", "cache_settings_statistics_thumbnail": "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ų…ØĩØēØąØŠ", "cache_settings_statistics_title": "Ø§ØŗØĒØŽØ¯Ø§Ų… Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", - "cache_settings_subtitle": "ØĒØ­ŲƒŲ… ؁؊ ØŗŲ„ŲˆŲƒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØŦŲˆØ§Ų„.", + "cache_settings_subtitle": "ØĒØ­ŲƒŲ… ؁؊ ØŗŲ„ŲˆŲƒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„ØĒØˇØ¨ŲŠŲ‚ Immich Ø§Ų„ØŦŲˆØ§Ų„", "cache_settings_tile_subtitle": "Ø§Ų„ØĒØ­ŲƒŲ… ؁؊ ØŗŲ„ŲˆŲƒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø­Ų„ŲŠ", "cache_settings_tile_title": "Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø­Ų„ŲŠ", "cache_settings_title": "ØĨؚداداØĒ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ", @@ -527,11 +642,17 @@ "camera_brand": "ØšŲ„Ø§Ų…ØŠ Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§ Ø§Ų„ØĒØŦØ§ØąŲŠØŠ", "camera_model": "ØˇØąØ§Ø˛ Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§", "cancel": "ØĨŲ„ØēØ§ØĄ", - "cancel_search": "Ø§Ų„Øē؊ Ø§Ų„Ø¨Ø­ØĢ", + "cancel_search": "Ø§Ų„ØēØ§ØĄ Ø§Ų„Ø¨Ø­ØĢ", + "canceled": "ØĒŲ… Ø§Ų„Ø§Ų„ØēØ§ØĄ", + "canceling": "ØŦØ§ØąŲ Ø§Ų„Ø§Ų„ØēØ§ØĄ", "cannot_merge_people": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø¯Ų…ØŦ Ø§Ų„ØŖØ´ØŽØ§Øĩ", "cannot_undo_this_action": "Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ!", "cannot_update_the_description": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØĩ؁", + "cast": "بØĢ", + "cast_description": "ØļØ¨Øˇ ؈ØŦŲ‡Ø§ØĒ Ø§Ų„Ø¨ØĢ Ø§Ų„Ų…ØĒŲˆŲØąØŠ", "change_date": "ØēŲŠŲ‘Øą Ø§Ų„ØĒØ§ØąŲŠØŽ", + "change_description": "ØĒØēŲŠŲŠØą Ø§Ų„ŲˆØĩ؁", + "change_display_order": "ØĒØēŲŠŲŠØą ØĒØąØĒŲŠØ¨ Ø§Ų„ØšØąØļ", "change_expiration_time": "ØĒØēŲŠŲŠØą ŲˆŲ‚ØĒ Ø§Ų†ØĒŲ‡Ø§ØĄ Ø§Ų„ØĩŲ„Ø§Ø­ŲŠØŠ", "change_location": "ØēŲŠŲ‘Øą Ø§Ų„Ų…ŲˆŲ‚Øš", "change_name": "ØĒØēŲŠŲŠØą Ø§Ų„ØĨØŗŲ…", @@ -539,21 +660,35 @@ "change_password": "ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "change_password_description": "Ų‡Ø°Ų‡ ØĨŲ…Ø§ Ų‡ŲŠ Ø§Ų„Ų…ØąØŠ Ø§Ų„ØŖŲˆŲ„Ų‰ Ø§Ų„ØĒ؊ ØĒŲ‚ŲˆŲ… ŲŲŠŲ‡Ø§ بØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„Ų†Ø¸Ø§Ų… ØŖŲˆ ØŖŲ†Ų‡ ØĒŲ… ØĒŲ‚Ø¯ŲŠŲ… ØˇŲ„Ø¨ Ų„ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ. Ø§Ų„ØąØŦØ§ØĄ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ØŖØ¯Ų†Ø§Ų‡.", "change_password_form_confirm_password": "ØĒØŖŲƒŲŠØ¯ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", - "change_password_form_description": "Ų…ØąØ­Ø¨Ų‹Ø§ ØŒŲ‡Ø°Ų‡ Ų‡ŲŠ Ø§Ų„Ų…ØąØŠ Ø§Ų„ØŖŲˆŲ„Ų‰ Ø§Ų„ØĒ؊ ØĒŲ‚ŲˆŲ… ŲŲŠŲ‡Ø§ Ø¨Ø§Ų„ØĒØŗØŦŲŠŲ„ ؁؊ Ø§Ų„Ų†Ø¸Ø§Ų… ØŖŲˆ ØĒŲ… ØĒŲ‚Ø¯ŲŠŲ… ØˇŲ„Ø¨ Ų„ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ.Ø§Ų„ØąØŦØ§ØĄ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ØŖØ¯Ų†Ø§Ų‡", + "change_password_form_description": "Ų…ØąØ­Ø¨Ų‹Ø§ {name}،\n\nØ§Ų…Ø§ Ø§Ų† ØĒŲƒŲˆŲ† Ų‡Ø°Ų‡ Ų‡ŲŠ Ø§Ų„Ų…ØąØŠ Ø§Ų„ØŖŲˆŲ„Ų‰ Ø§Ų„ØĒ؊ ØĒŲ‚ŲˆŲ… ŲŲŠŲ‡Ø§ Ø¨Ø§Ų„ØĒØŗØŦŲŠŲ„ ؁؊ Ø§Ų„Ų†Ø¸Ø§Ų… ØŖŲˆ ØĒŲ… ØĒŲ‚Ø¯ŲŠŲ… ØˇŲ„Ø¨ Ų„ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ. Ø§Ų„ØąØŦØ§ØĄ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ ØŖØ¯Ų†Ø§Ų‡.", "change_password_form_new_password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ", "change_password_form_password_mismatch": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą ØēŲŠØą Ų…ØˇØ§Ø¨Ų‚ØŠ", "change_password_form_reenter_new_password": "ØŖØšØ¯ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą ØŦØ¯ŲŠØ¯ØŠ", - "change_pin_code": "ØĒØēŲŠŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", + "change_pin_code": "ØĒØēŲŠŲŠØą ØąŲ…Ø˛ PIN", "change_your_password": "ØēŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", "changed_visibility_successfully": "ØĒŲ… ØĒØēŲŠŲŠØą Ø§Ų„ØąØ¤ŲŠØŠ Ø¨Ų†ØŦاح", + "charging": "Ø§Ų„Ø´Ø­Ų†", + "charging_requirement_mobile_backup": "؊ØĒØˇŲ„Ø¨ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ ØŖŲ† ŲŠŲƒŲˆŲ† Ø§Ų„ØŦŲ‡Ø§Ø˛ Ų‚ŲŠØ¯ Ø§Ų„Ø´Ø­Ų†", + "check_corrupt_asset_backup": "Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† ؈ØŦŲˆØ¯ Ų†ØŗØŽ احØĒŲŠØ§ØˇŲŠØŠ ŲØ§ØŗØ¯ØŠ Ų„Ų„Ø§ØĩŲˆŲ„", + "check_corrupt_asset_backup_button": "اØŦØąØ§ØĄ ŲØ­Øĩ", + "check_corrupt_asset_backup_description": "Ų‚Ų… بØĨØŦØąØ§ØĄ Ų‡Ø°Ø§ Ø§Ų„ŲØ­Øĩ ŲŲ‚Øˇ ØšØ¨Øą Ø´Ø¨ŲƒØŠ Wi-Fi ŲˆØ¨ØšØ¯ Ų†ØŗØŽ ØŦŲ…ŲŠØš Ø§Ų„ØŖØĩŲˆŲ„ احØĒŲŠØ§ØˇŲŠŲ‹Ø§. Ų‚Ø¯ ŲŠØŗØĒØēØąŲ‚ Ø§Ų„ØĨØŦØąØ§ØĄ بØļØš Ø¯Ų‚Ø§ØĻŲ‚.", "check_logs": "ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŗØŦŲ„Ø§ØĒ", "choose_matching_people_to_merge": "ا؎ØĒØą Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ų…ØĒØˇØ§Ø¨Ų‚ŲŠŲ† Ų„Ø¯Ų…ØŦŲ‡Ų…", "city": "Ø§Ų„Ų…Ø¯ŲŠŲ†ØŠ", "clear": "ØĨØŽŲ„Ø§ØĄ", "clear_all": "ØĨØŽŲ„Ø§ØĄ Ø§Ų„ŲƒŲ„", "clear_all_recent_searches": "Ų…ØŗØ­ ØŦŲ…ŲŠØš ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„ØŖØŽŲŠØąØŠ", + "clear_file_cache": "Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„Ų„Ų…Ų„ŲØ§ØĒ", "clear_message": "ØĨØŽŲ„Ø§ØĄ Ø§Ų„ØąØŗØ§Ų„ØŠ", "clear_value": "ØĨØŽŲ„Ø§ØĄ Ø§Ų„Ų‚ŲŠŲ…ØŠ", + "client_cert_dialog_msg_confirm": "Ø­ØŗŲ†Ø§", + "client_cert_enter_password": "Ø§Ø¯ØŽŲ„ ŲƒŲ„Ų…ØŠ ØŗØą", + "client_cert_import": "Ø§ØŗØĒŲŠØąØ§Ø¯", + "client_cert_import_success_msg": "ØĒŲ… Ø§ØŗØĒŲŠØąØ§Ø¯ Ø´Ų‡Ø§Ø¯ØŠ Ø§Ų„ØšŲ…ŲŠŲ„", + "client_cert_invalid_msg": "؅؄؁ Ø´Ų‡Ø§Ø¯ØŠ ØšŲ…ŲŠŲ„ ØēŲŠØą ØĩØ§Ų„Ø­ØŠ Ø§Ųˆ ŲƒŲ„Ų…ØŠ ØŗØą ØēŲŠØą ØĩØ­ŲŠØ­ØŠ", + "client_cert_remove_msg": "ØĒŲ… Ø§Ø˛Ø§Ų„ØŠ Ø´Ų‡Ø§Ø¯ØŠ Ø§Ų„ØšŲ…ŲŠŲ„", + "client_cert_subtitle": "ŲŠØ¯ØšŲ… Øĩ؊Øē PKCS12 (.p12, .pfx)ŲŲ‚Øˇ. Ø§ØŗØĒŲŠØąØ§Ø¯/Ø§Ø˛Ø§Ų„ØŠ Ø§Ų„Ø´Ų‡Ø§Ø¯Ø§ØĒ Ų…ØĒاح ŲŲ‚Øˇ Ų‚Ø¨Ų„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„", + "client_cert_title": "Ø´Ų‡Ø§Ø¯ØŠ Ų…ØŗØĒØŽØ¯Ų… SSL", "clockwise": "باØĒØŦØ§Ų‡ ØšŲ‚Ø§ØąØ¨ Ø§Ų„ØŗØ§ØšØŠ", "close": "ØĨØēŲ„Ø§Ų‚", "collapse": "ØˇŲŠ", @@ -566,21 +701,27 @@ "comments_are_disabled": "Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ Ų…ØšØˇŲ„ØŠ", "common_create_new_album": "ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", "common_server_error": "ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† اØĒØĩØ§Ų„ Ø§Ų„Ø´Ø¨ŲƒØŠ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ، ŲˆØ§Ų„ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ† Ø§Ų„ØŦŲ‡Ø§Ø˛ Ų‚Ø§Ø¨Ų„ Ų„Ų„ŲˆØĩŲˆŲ„ ؈ØĨØĩØ¯Ø§ØąØ§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚/Ø§Ų„ØŦŲ‡Ø§Ø˛ Ų…ØĒŲˆØ§ŲŲ‚ØŠ.", + "completed": "Ø§ŲƒØĒŲ…Ų„", "confirm": "ØĒØŖŲƒŲŠØ¯", "confirm_admin_password": "ØĒØŖŲƒŲŠØ¯ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą Ø§Ų„Ų…ØŗØ¤ŲˆŲ„", "confirm_delete_face": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† Ø­Ø°Ų ؈ØŦŲ‡ {name} Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„ØŸ", "confirm_delete_shared_link": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų‡Ø°Ø§ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŸ", "confirm_keep_this_delete_others": "ØŗŲŠØĒŲ… Ø­Ø°Ų ØŦŲ…ŲŠØš Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŖØŽØąŲ‰ ؁؊ Ø§Ų„Ų…ØŦŲ…ŲˆØšØŠ Ø¨Ø§ØŗØĒØĢŲ†Ø§ØĄ Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„. Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§Ų„Ų…ØĒابؚ؊؟", - "confirm_new_pin_code": "ØĢبØĒ Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„ØŦØ¯ŲŠØ¯", + "confirm_new_pin_code": "ØĢبØĒ ØąŲ…Ø˛ PIN Ø§Ų„ØŦØ¯ŲŠØ¯", "confirm_password": "ØĒØŖŲƒŲŠØ¯ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", + "confirm_tag_face": "Ų‡Ų„ ØĒØąŲŠØ¯ ؈ØļØš ØšŲ„Ø§Ų…ØŠ ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ŲˆØŦŲ‡ {name}؟", + "confirm_tag_face_unnamed": "Ų‡Ų„ ØĒØąŲŠØ¯ ؈ØļØš ØšŲ„Ø§Ų…ØŠ ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ŲˆØŦŲ‡ØŸ", + "connected_device": "ØŦŲ‡Ø§Ø˛ Ų…ØĒØĩŲ„", + "connected_to": "Ų…ØĒØĩŲ„ ب", "contain": "Ų…Ø­ØĒŲˆØ§ØŠ", "context": "Ø§Ų„ØŗŲŠØ§Ų‚", "continue": "Ų…ØĒابؚ؊", "control_bottom_app_bar_create_new_album": "ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", - "control_bottom_app_bar_delete_from_immich": " Ø­Ø°Ų Ų…Ų†Ø§Ų„ ØĒØˇØ¨ŲŠŲ‚", + "control_bottom_app_bar_delete_from_immich": "Ø­Ø°Ų Ų…Ų† Immich", "control_bottom_app_bar_delete_from_local": "Ø­Ø°Ų Ų…Ų† Ø§Ų„ØŦŲ‡Ø§Ø˛", "control_bottom_app_bar_edit_location": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲˆØŦŲ‡ØŠ", "control_bottom_app_bar_edit_time": "ØĒØ­ØąŲŠØą Ø§Ų„ØĒØ§ØąŲŠØŽ ŲˆØ§Ų„ŲˆŲ‚ØĒ", + "control_bottom_app_bar_share_link": "Ų…Ø´Ø§ØąŲƒØŠ ØąØ§Ø¨Øˇ", "control_bottom_app_bar_share_to": "Ų…Ø´Ø§ØąŲƒØŠ ØĨŲ„Ų‰", "control_bottom_app_bar_trash_from_immich": "Ø­Ø°ŲŲ‡ ŲˆŲ†Ų‚Ų„Ų‡ ؁؊ ØŗŲ„Ų‡ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "copied_image_to_clipboard": "ØĒŲ… Ų†ØŗØŽ Ø§Ų„ØĩŲˆØąØŠ ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ.", @@ -602,23 +743,31 @@ "create_link": "ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ", "create_link_to_share": "ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ", "create_link_to_share_description": "Ø§Ų„ØŗŲ…Ø§Ø­ Ų„ØŖŲŠ Ø´ØŽØĩ Ų„Ø¯ŲŠŲ‡ Ø§Ų„ØąØ§Ø¨Øˇ Ø¨Ų…Ø´Ø§Ų‡Ø¯ØŠ Ø§Ų„ØĩŲˆØąØŠ (Ø§Ų„ØĩŲˆØą) Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ", + "create_new": "Ø§Ų†Ø´Ø§ØĄ ØŦØ¯ŲŠØ¯", "create_new_person": "ØĨŲ†Ø´Ø§ØĄ Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", "create_new_person_hint": "ØĒØšŲŠŲŠŲ† Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ Ų„Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", "create_new_user": "ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų… ØŦØ¯ŲŠØ¯", "create_shared_album_page_share_add_assets": "ØĨØļØ§ŲØŠ Ø§Ų„ØŖØĩŲˆŲ„", "create_shared_album_page_share_select_photos": "حدد Ø§Ų„ØĩŲˆØą", + "create_shared_link": "Ø§Ų†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒ", "create_tag": "ØĨŲ†Ø´Ø§ØĄ ØšŲ„Ø§Ų…ØŠ", "create_tag_description": "ØŖŲ†Ø´ØĻ ØšŲ„Ø§Ų…ØŠ ØŦØ¯ŲŠØ¯ØŠ. Ø¨Ø§Ų„Ų†ØŗØ¨ØŠ Ų„Ų„ØšŲ„Ø§Ų…Ø§ØĒ Ø§Ų„Ų…ØĒØ¯Ø§ØŽŲ„ØŠØŒ ŲŠØąØŦŲ‰ ØĨØ¯ØŽØ§Ų„ Ø§Ų„Ų…ØŗØ§Øą Ø§Ų„ŲƒØ§Ų…Ų„ Ų„Ų„ØšŲ„Ø§Ų…ØŠ Ø¨Ų…Ø§ ؁؊ Ø°Ų„Ųƒ Ø§Ų„ØŽØˇŲˆØˇ Ø§Ų„Ų…Ø§ØĻŲ„ØŠ Ų„Ų„ØŖŲ…Ø§Ų….", "create_user": "ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų…", "created": "ØĒŲ… Ø§Ų„ØĨŲ†Ø´Ø§ØĄ", + "created_at": "Ų…ØŽŲ„ŲˆŲ‚", + "creating_linked_albums": "ØŦØ§ØąŲŠ ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ų…ØąØĒØ¨ØˇØŠ...", + "crop": "Ų‚Øĩ", "curated_object_page_title": "ØŖØ´ŲŠØ§ØĄ", "current_device": "Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ø­Ø§Ų„ŲŠ", - "current_pin_code": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„Ø­Ø§Ų„ŲŠ", + "current_pin_code": "ØąŲ…Ø˛ PIN Ø§Ų„Ø­Ø§Ų„ŲŠ", + "current_server_address": "ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŽØ§Ø¯Ų… Ø§Ų„Ø­Ø§Ų„ŲŠ", "custom_locale": "Ų„ØēØŠ Ų…ØŽØĩØĩØŠ", "custom_locale_description": "ØĒŲ†ØŗŲŠŲ‚ Ø§Ų„ØĒŲˆØ§ØąŲŠØŽ ŲˆØ§Ų„ØŖØąŲ‚Ø§Ų… Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ Ø§Ų„Ų„ØēØŠ ŲˆØ§Ų„Ų…Ų†ØˇŲ‚ØŠ", + "custom_url": "ØąØ§Ø¨Øˇ Ų…ØŽØĩØĩ", "daily_title_text_date": "E ، MMM DD", "daily_title_text_date_year": "E ، MMM DD ، yyyy", "dark": "Ų…ØšØĒŲ…", + "dark_theme": "ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„Ų…Ø¸Ų‡Øą Ø§Ų„Ø¯Ø§ŲƒŲ†", "date_after": "Ø§Ų„ØĒØ§ØąØŽ بؚد", "date_and_time": "Ø§Ų„ØĒØ§ØąŲŠØŽ ؈ Ø§Ų„ŲˆŲ‚ØĒ", "date_before": "Ø§Ų„ØĒØ§ØąŲŠØŽ Ų‚Ø¨Ų„", @@ -626,6 +775,7 @@ "date_of_birth_saved": "ØĒŲ… Ø­ŲØ¸ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ Ø¨Ų†ØŦاح", "date_range": "Ų†ØˇØ§Ų‚ Ø§Ų„Ų…ŲˆØšØ¯", "day": "ŲŠŲˆŲ…", + "days": "Ø§ŲŠØ§Ų…", "deduplicate_all": "ØĨŲ„ØēØ§ØĄ ØĒŲƒØąØ§Øą Ø§Ų„ŲƒŲ„", "deduplication_criteria_1": "Ø­ØŦŲ… Ø§Ų„ØĩŲˆØąØŠ Ø¨ŲˆØ­Ø¯Ø§ØĒ Ø§Ų„Ø¨Ø§ŲŠØĒ", "deduplication_criteria_2": "ؚدد Ø¨ŲŠØ§Ų†Ø§ØĒ EXIF", @@ -634,11 +784,13 @@ "default_locale": "Ø§Ų„Ų„ØēØŠ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ", "default_locale_description": "ØĒŲ†ØŗŲŠŲ‚ Ø§Ų„ØĒŲˆØ§ØąŲŠØŽ ŲˆØ§Ų„ØŖØąŲ‚Ø§Ų… Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ Ų„ØēØŠ Ø§Ų„Ų…ØĒØĩŲØ­ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "delete": "Ø­Ø°Ų", + "delete_action_confirmation_message": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† Ø­Ø°Ų Ų‡Ø°Ø§ Ø§Ų„Ų…Ų„ŲØŸ Ų‡Ø°Ø§ ØŗØ¤Ø¯ŲŠ Ø§Ų„Ų‰ Ų†Ų‚Ų„ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ų‰ ØŗŲ„ØŠ Ų…Ų‡Ų…Ų„Ø§ØĒ Ø§Ų„ØŽØ§Ø¯Ų… ŲˆØŗŲŠØĒŲ… Ø§Ø´ØšØ§ØąŲƒ Ø§Ų† ŲƒŲ†ØĒ ØĒØąŲŠØ¯ Ø­Ø°ŲŲ‡ ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛", + "delete_action_prompt": "ØĒŲ… Ø­Ø°Ų {count}", "delete_album": "Ø­Ø°Ų Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "delete_api_key_prompt": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų ؅؁ØĒاح API Ų‡Ø°Ø§ØŸ", - "delete_dialog_alert": " Ų‡Ø°Ų‡ Ø§Ų„ØšŲ†Ø§ØĩØą ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ø¨Ø´ŲƒŲ„ داØĻŲ… Ų…Ų† ØŦŲ‡Ø§Ø˛Ųƒ ŲˆŲ…Ų† ØĒØˇØ¨ŲŠŲ‚", - "delete_dialog_alert_local": " Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„ØĒ؊ ØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ų…Ų† ØŦŲ‡Ø§Ø˛Ųƒ ŲˆŲ„ŲƒŲ†Ų‡Ø§ Ų…ŲˆØŦŲˆØ¯Ų‡ ؁؊ ØĒØˇØ¨ŲŠŲ‚", - "delete_dialog_alert_local_non_backed_up": "بؚØļ Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ø¨Ø´ŲƒŲ„ داØĻŲ… ŲˆŲ„Ø§ ؊؈ØŦد Ų„Ų‡Ø§ Ų†ØŗØŽŲ‡ احØĒŲŠØ§ØˇŲŠŲ‡ ؁؊ ØĒØˇØ¨ŲŠŲ‚ ", + "delete_dialog_alert": "Ų‡Ø°Ų‡ Ø§Ų„ØšŲ†Ø§ØĩØą ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ø¨Ø´ŲƒŲ„ داØĻŲ… Ų…Ų† Immich ؈ Ų…Ų† ØŦŲ‡Ø§Ø˛Ųƒ", + "delete_dialog_alert_local": "Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ų…Ų† ØŦŲ‡Ø§Ø˛Ųƒ ŲˆŲ„ŲƒŲ† ØĒØ¨Ų‚Ų‰ Ų…ŲˆØŦŲˆØ¯Ų‡ ؁؊ ØŽØ§Ø¯Ų… Immich", + "delete_dialog_alert_local_non_backed_up": "بؚØļ Ø§Ų„ØšŲ†Ø§ØĩØą ØēŲŠØą Ų…Ø¯ØšŲˆŲ…ØŠ Ø¨Ų†ØŗØŽØŠ احØĒŲŠØ§ØˇŲŠØŠ ØšŲ„Ų‰ Immich ŲˆØŗŲŠØĒŲ… ØĨØ˛Ø§Ų„ØĒŲ‡Ø§ Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§ Ų…Ų† ØŦŲ‡Ø§Ø˛Ųƒ", "delete_dialog_alert_remote": "Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„ØĒ؊ ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ø¨Ø´ŲƒŲ„ داØĻŲ… Ų…Ų† ØĒØˇØ¨ŲŠŲ‚", "delete_dialog_ok_force": "Ø§Ø­Ø°Ų ØšŲ„Ų‰ ØŖŲŠ Ø­Ø§Ų„", "delete_dialog_title": "Ø§Ų„Ø­Ø°Ų Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊", @@ -647,9 +799,12 @@ "delete_key": "Ø­Ø°Ų Ø§Ų„Ų…ŲØĒاح", "delete_library": "Ø­Ø°Ų Ø§Ų„Ų…ŲƒØĒب؊", "delete_link": "Ø­Ø°Ų Ø§Ų„ØąØ§Ø¨Øˇ", + "delete_local_action_prompt": "ØĒŲ… Ø­Ø°Ų {count} Ų…Ų† Ø§Ų„ØŦŲ‡Ø§Ø˛", "delete_local_dialog_ok_backed_up_only": "Ø­Ø°Ų Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ ŲŲ‚Øˇ", "delete_local_dialog_ok_force": "Ø§Ø­Ø°Ų ØšŲ„Ų‰ ØŖŲŠ Ø­Ø§Ų„", "delete_others": "Ø­Ø°Ų Ø§Ų„ØŖØŽØąŲ‰", + "delete_permanently": "Ø­Ø°Ų Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊", + "delete_permanently_action_prompt": "ØĒŲ… Ø­Ø°Ų {count} Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊", "delete_shared_link": "Ø­Ø°Ų Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "delete_shared_link_dialog_title": "Ø­Ø°Ų Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "delete_tag": "Ø­Ø°Ų Ø§Ų„ØšŲ„Ø§Ų…ØŠ", @@ -660,11 +815,14 @@ "description": "؈Øĩ؁", "description_input_hint_text": "اØļ؁ ؈ØĩŲØ§...", "description_input_submit_error": "ØŽØˇØŖ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØĩ؁ ، ØĒØ­Ų‚Ų‚ Ų…Ų† Ø§Ų„ØŗØŦŲ„ Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒŲØ§ØĩŲŠŲ„", + "deselect_all": "Ø§Ų„ØēØ§ØĄ ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„", "details": "ØĒŲØ§ØĩŲŠŲ„", "direction": "Ø§Ų„ØĨØĒØŦØ§Ų‡", "disabled": "Ų…ØšØˇŲ„", "disallow_edits": "Ų…Ų†Øš Ø§Ų„ØĒØšØ¯ŲŠŲ„Ø§ØĒ", + "discord": "Ø¯ØŗŲƒŲˆØąØ¯", "discover": "Ø§ŲƒØĒØ´Ų", + "discovered_devices": "اØŦŲ‡Ø˛ØŠ Ų…ŲƒØĒØ´ŲØŠ", "dismiss_all_errors": "ØĒØŦØ§Ų‡Ų„ ŲƒØ§ŲØŠ Ø§Ų„ØŖØŽØˇØ§ØĄ", "dismiss_error": "ØĒØŦØ§Ų‡Ų„ Ø§Ų„ØŽØˇØŖ", "display_options": "ØšØąØļ Ø§Ų„ØŽŲŠØ§ØąØ§ØĒ", @@ -675,12 +833,26 @@ "documentation": "Ø§Ų„ŲˆØĢاØĻŲ‚", "done": "ØĒŲ…", "download": "ØĒŲ†Ø˛ŲŠŲ„", + "download_action_prompt": "؊ØĒŲ… ØĒŲ†Ø˛ŲŠŲ„ {count} ؅؄؁", + "download_canceled": "Ø§Ų„Øē؊ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_complete": "Ø§ŲƒØĒŲ…Ų„ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_enqueue": "ØĒŲ†Ø˛ŲŠŲ„ ؁؊ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą", + "download_error": "ØŽØˇØ§ ؁؊ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_failed": "ŲØ´Ų„ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_finished": "Ø§Ų†ØĒŲ‡Ų‰ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "download_include_embedded_motion_videos": "Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø¯Ų…ØŦØŠ", "download_include_embedded_motion_videos_description": "ØĒØļŲ…ŲŠŲ† Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ØļŲ…Ų†ØŠ ؁؊ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒØŠ ŲƒŲ…Ų„Ų ؅؆؁ØĩŲ„", + "download_notfound": "Ų„Ų… ŲŠØšØĢØą ØšŲ„Ų‰ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_paused": "Ø§ŲˆŲ‚Ų Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "download_settings": "Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ", "download_settings_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ų…ØĒØšŲ„Ų‚ØŠ بØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", + "download_started": "بدا Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_sucess": "Ų†ØŦØ­ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "download_sucess_android": "ØĒŲ… ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ŲˆØŗØ§ØĻØˇ Ø§Ų„Ų‰ DCIM/Immich", + "download_waiting_to_retry": "Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą Ų„Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ", "downloading": "ØŦØ§ØąŲ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "downloading_asset_filename": "{filename} Ų‚ŲŠØ¯ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", + "downloading_media": "ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ŲˆØŗØ§ØĻØˇ", "drop_files_to_upload": "Ų‚Ų… بØĨØŗŲ‚Ø§Øˇ Ø§Ų„Ų…Ų„ŲØ§ØĒ ؁؊ ØŖŲŠ Ų…ŲƒØ§Ų† Ų„ØąŲØšŲ‡Ø§", "duplicates": "Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "duplicates_description": "Ų‚Ų… Ø¨Ø­Ų„ ŲƒŲ„ Ų…ØŦŲ…ŲˆØšØŠ Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„ØĨØ´Ø§ØąØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ، ØĨŲ† ؈ØŦدØĒ", @@ -688,8 +860,14 @@ "edit": "ØĒØšØ¯ŲŠŲ„", "edit_album": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "edit_avatar": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ", + "edit_birthday": "ØĒØšØ¯ŲŠŲ„ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯", "edit_date": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĒØ§ØąŲŠØŽ", "edit_date_and_time": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĒØ§ØąŲŠØŽ ŲˆØ§Ų„ŲˆŲ‚ØĒ", + "edit_date_and_time_action_prompt": "ØĒŲ… ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĒØ§ØąŲŠØŽ ŲˆØ§Ų„ŲˆŲ‚ØĒ Ų„{count} ؅؄؁(اØĒ)", + "edit_date_and_time_by_offset": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĒØ§ØąŲŠØŽ Ø­ØŗØ¨ Ų‚ŲŠŲ…ØŠ Ø§Ø˛Ø§Ø­ØŠ Ų…ØšŲŠŲ†ØŠ", + "edit_date_and_time_by_offset_interval": "Ų†ØˇØ§Ų‚ Ø§Ų„ØĒØ§ØąŲŠØŽ Ø§Ų„ØŦØ¯ŲŠØ¯: {from} - {to}", + "edit_description": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ŲˆØĩ؁", + "edit_description_prompt": "Ø§Ų„ØąØŦØ§ØĄ ا؎ØĒŲŠØ§Øą ؈Øĩ؁ ØŦØ¯ŲŠØ¯:", "edit_exclusion_pattern": "ØĒØšØ¯ŲŠŲ„ Ų†Ų…Øˇ Ø§Ų„Ø§ØŗØĒبؚاد", "edit_faces": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ŲˆØŦŲˆŲ‡", "edit_import_path": "ØĒØšØ¯ŲŠŲ„ Ų…ØŗØ§Øą Ø§Ų„Ø§ØŗØĒŲŠØąØ§Ø¯", @@ -697,6 +875,7 @@ "edit_key": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„Ų…ŲØĒاح", "edit_link": "ØĒØēŲŠŲŠØą Ø§Ų„ØąØ§Ø¨Øˇ", "edit_location": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„Ų…ŲˆŲ‚Øš", + "edit_location_action_prompt": "{count} Ų…ŲˆŲ‚Øš ØĒŲ… ØĒØšØ¯ŲŠŲ„Ų‡", "edit_location_dialog_title": "Ų…ŲˆŲ‚Øš", "edit_name": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„Ø§ØŗŲ…", "edit_people": "ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØŖØ´ØŽØ§Øĩ", @@ -710,15 +889,27 @@ "editor_crop_tool_h2_aspect_ratios": "Ų†ØŗØ¨ Ø§Ų„ØšØąØļ ØĨŲ„Ų‰ Ø§Ų„Ø§ØąØĒŲØ§Øš", "editor_crop_tool_h2_rotation": "Ø§Ų„ØĒØ¯ŲˆŲŠØą", "email": "Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ", + "email_notifications": "ØĒŲ†Ø¨ŲŠŲ‡Ø§ØĒ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ", + "empty_folder": "Ų‡Ø°Ø§ Ø§Ų„Ų…ØŦŲ„Ø¯ ŲØ§ØąØē", "empty_trash": "ØŖŲØąØē ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "empty_trash_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨŲØąØ§Øē ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ؟ ØŗŲŠØ¤Ø¯ŲŠ Ų‡Ø°Ø§ ØĨŲ„Ų‰ ØĨØ˛Ø§Ų„ØŠ ØŦŲ…ŲŠØš Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ؁؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊ Ų…Ų† Immich.\nŲ„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨØŦØąØ§ØĄ!", "enable": "ØĒŲØšŲŠŲ„", + "enable_backup": "ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", + "enable_biometric_auth_description": "ØŖØ¯ØŽŲ„ ØąŲ…Ø˛ PIN Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ų„ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ø§Ų„Ø¨ŲŠŲˆŲ…ØĒØąŲŠØŠ", "enabled": "Ų…ŲØšŲ„", "end_date": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨŲ†ØĒŲ‡Ø§ØĄ", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Ų…ŲØ¯ØąØŦ ؁؊ Ø§Ų„ØˇØ§Ø¨ŲˆØą", + "enter_wifi_name": "Ø§Ø¯ØŽŲ„ Ø§ØŗŲ… Wi-Fi", + "enter_your_pin_code": "ØŖØ¯ØŽŲ„ ØąŲ…Ø˛ PIN Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", + "enter_your_pin_code_subtitle": "ØŖØ¯ØŽŲ„ ØąŲ…Ø˛ PIN Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ų„Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", "error": "ØŽØˇØŖ", + "error_change_sort_album": "ŲØ´Ų„ ؁؊ ØĒØēŲŠŲŠØą ØĒØąØĒŲŠØ¨ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "error_delete_face": "حدØĢ ØŽØˇØŖ ؁؊ Ø­Ø°Ų Ø§Ų„ŲˆØŦŲ‡ Ų…Ų† Ø§Ų„ØŖØĩŲˆŲ„", + "error_getting_places": "ØŽØˇØŖ ØŖØĢŲ†Ø§ØĄ Ø§ØŗØĒØąØŦاؚ Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ų…ŲˆØ§Ų‚Øš", "error_loading_image": "حدØĢ ØŽØˇØŖ ØŖØĢŲ†Ø§ØĄ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ", + "error_loading_partners": "ØŽØˇØŖ بØĒØ­Ų…ŲŠŲ„ Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ø´ØąŲƒØ§ØĄ: {error}", + "error_saving_image": "ØŽØˇØŖ: {error}", + "error_tag_face_bounding_box": "ØŽØˇØŖ ؁؊ ؈ØļØš ØšŲ„Ø§Ų…ØŠ ØšŲ„Ų‰ Ø§Ų„ŲˆØŦŲ‡ - Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ ØĨحداØĢŲŠØ§ØĒ Ø§Ų„Ų…ØąØ¨Øš Ø§Ų„Ų…Ø­ŲŠØˇ", "error_title": "ØŽØˇØŖ - حدØĢ ØŽŲ„Ų„ŲŒ Ų…Ø§", "errors": { "cannot_navigate_next_asset": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø§Ų†ØĒŲ‚Ø§Ų„ ØĨŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ØĒØ§Ų„ŲŠ", @@ -746,15 +937,19 @@ "failed_to_keep_this_delete_others": "ŲØ´Ų„ ؁؊ Ø§Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ ŲˆØ­Ø°Ų Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„ØŖØŽØąŲ‰", "failed_to_load_asset": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰", "failed_to_load_assets": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", + "failed_to_load_notifications": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", "failed_to_load_people": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØ´ØŽØ§Øĩ", "failed_to_remove_product_key": "ØĒØšØ°Øą ØĨØ˛Ø§Ų„ØŠ ؅؁ØĒاح Ø§Ų„Ų…Ų†ØĒØŦ", + "failed_to_reset_pin_code": "ŲØ´Ų„ اؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ Ø§Ų„PIN", "failed_to_stack_assets": "ŲØ´Ų„ ؁؊ ØĒŲƒØ¯ŲŠØŗ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", "failed_to_unstack_assets": "ŲØ´Ų„ ؁؊ ؁ØĩŲ„ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", + "failed_to_update_notification_status": "ŲØ´Ų„ ؁؊ ØĒØ­Ø¯ŲŠØĢ Ø­Ø§Ų„ØŠ Ø§Ų„ØĨØ´ØšØ§Øą", "import_path_already_exists": "Ų…ØŗØ§Øą Ø§Ų„Ø§ØŗØĒŲŠØąØ§Ø¯ Ų‡Ø°Ø§ Ų…ŲˆØŦŲˆØ¯ Ų…ØŗØ¨Ų‚Ų‹Ø§.", "incorrect_email_or_password": "Ø¨ØąŲŠØ¯ ØŖŲˆ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą ØēŲŠØą ØĩØ­ŲŠØ­ØŠ", "paths_validation_failed": "ŲØ´Ų„ ؁؊ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† {paths, plural, one {# Ų…ØŗØ§Øą} other {# Ų…ØŗØ§ØąØ§ØĒ}}", "profile_picture_transparent_pixels": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŖŲ† ØĒØ­ØĒ؈؊ ØĩŲˆØą Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊ ØšŲ„Ų‰ ØŖØŦØ˛Ø§ØĄ/Ø¨ŲƒØŗŲ„Ø§ØĒ Ø´ŲØ§ŲØŠ. ŲŠØąØŦŲ‰ Ø§Ų„ØĒŲƒØ¨ŲŠØą ؈/ØŖŲˆ ØĒØ­ØąŲŠŲƒ Ø§Ų„ØĩŲˆØąØŠ.", "quota_higher_than_disk_size": "Ų„Ų‚Ø¯ Ų‚Ų…ØĒ بØĒØšŲŠŲŠŲ† Ø­ØĩØŠ Ų†ØŗØ¨ŲŠØŠ ØŖØšŲ„Ų‰ Ų…Ų† Ø­ØŦŲ… Ø§Ų„Ų‚ØąØĩ", + "something_went_wrong": "حدØĢ ØŽØˇØŖ Ų…Ø§", "unable_to_add_album_users": "ØĒØšØ°Øą ØĨØļØ§ŲØŠ Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "unable_to_add_assets_to_shared_link": "ØĒØšØ°Øą ØĨØļØ§ŲØŠ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ ØĨŲ„Ų‰ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "unable_to_add_comment": "ØĒØšØ°Øą ØĨØļØ§ŲØŠ Ø§Ų„ØĒØšŲ„ŲŠŲ‚", @@ -766,6 +961,7 @@ "unable_to_archive_unarchive": "ØĒØšØ°Øą {archived, select, true {Ø§Ų„ØŖØąØ´ŲØŠ} other {Ø§Ų„ØĨØŽØąØ§ØŦ Ų…Ų† Ø§Ų„ØŖØąØ´ŲŠŲ}}", "unable_to_change_album_user_role": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą Ø¯ŲˆØą Ų…ØŗØĒØŽØ¯Ų… Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "unable_to_change_date": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą Ø§Ų„ØĒØ§ØąŲŠØŽ", + "unable_to_change_description": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą Ø§Ų„ŲˆØĩ؁", "unable_to_change_favorite": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą Ø§Ų„Ų…ŲØļŲ„ØŠ Ų„Ų…Ø­ØĒŲˆŲ‰", "unable_to_change_location": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą Ø§Ų„Ų…ŲˆŲ‚Øš", "unable_to_change_password": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", @@ -809,6 +1005,7 @@ "unable_to_remove_partner": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ø´ØąŲŠŲƒ", "unable_to_remove_reaction": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨØ˛Ø§Ų„ØŠ ØąØ¯ Ø§Ų„ŲØšŲ„", "unable_to_reset_password": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", + "unable_to_reset_pin_code": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ PIN", "unable_to_resolve_duplicate": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ Ø­Ų„ Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "unable_to_restore_assets": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ Ø§ØŗØĒؚاد؊ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ", "unable_to_restore_trash": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ Ø§ØŗØĒؚاد؊ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", @@ -838,6 +1035,7 @@ }, "exif": "Exif (Øĩ؊ØēØŠ ؅؄؁ ØĩŲˆØąŲŠ Ų‚Ø§Ø¨Ų„ Ų„Ų„ØĒØ¨Ø§Ø¯Ų„)", "exif_bottom_sheet_description": "اØļ؁ ؈ØĩŲØ§...", + "exif_bottom_sheet_description_error": "ØŽØˇØŖ ؁؊ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØĩ؁", "exif_bottom_sheet_details": "ØĒŲØ§ØĩŲŠŲ„", "exif_bottom_sheet_location": "Ų…ŲˆŲ‚Øš", "exif_bottom_sheet_people": "Ø§Ų„Ų†Ø§Øŗ", @@ -855,35 +1053,56 @@ "explorer": "Ø§Ų„Ų…ØŗØĒŲƒØ´Ų", "export": "ØĒØĩØ¯ŲŠØą", "export_as_json": "ØĒØĩØ¯ŲŠØą ŲƒŲ€ JSON", + "export_database": "ØĒØĩØ¯ŲŠØą Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "export_database_description": "ØĒØĩØ¯ŲŠØą Ų‚Ø§ØšØ¯ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ų…Ų† Ų†ŲˆØš SQLite", "extension": "Ø§Ų„ØĨŲ…ØĒداد", "external": "ØŽØ§ØąØŦ؊", "external_libraries": "Ø§Ų„Ų…ŲƒØĒباØĒ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ", - "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": "Ø´Ø¨ŲƒØŠ ØŽØ§ØąØŦŲŠØŠ", + "external_network_sheet_info": "ØšŲ†Ø¯Ų…Ø§ Ų„Ø§ ؊ØĒŲˆØ§ØŦد ØšŲ„Ų‰ Ø´Ø¨ŲƒØŠ Wi-Fi Ø§Ų„Ų…ŲØļŲ„ØŠØŒ ؁ØĨŲ†Ų‡ ØŗŲŠØĒØĩŲ„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų… Ų…Ų† ØŽŲ„Ø§Ų„ ØŖŲˆŲ„ ØšŲ†Ø§ŲˆŲŠŲ† URL ØŖØ¯Ų†Ø§Ų‡ Ø§Ų„ØĒ؊ ŲŠŲ…ŲƒŲ†Ų‡ Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„ŲŠŲ‡Ø§ØŒ Ø¨Ø¯ØĄŲ‹Ø§ Ų…Ų† Ø§Ų„ØŖØšŲ„Ų‰ ØĨŲ„Ų‰ Ø§Ų„ØŖØŗŲŲ„", "face_unassigned": "ØēŲŠØą Ų…ØšŲŠŲ†", + "failed": "ŲØ´Ų„", + "failed_to_authenticate": "ŲØ´Ų„ ؁؊ Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ", "failed_to_load_assets": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲˆŲ„", + "failed_to_load_folder": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…ØŦŲ„Ø¯", "favorite": "؅؁ØļŲ„", + "favorite_action_prompt": "{count} اØļ؊؁ ØĨŲ„Ų‰ Ø§Ų„Ų…ŲØļŲ„Ø§ØĒ", "favorite_or_unfavorite_photo": "ØĒ؁ØļŲŠŲ„ ØŖŲˆ ØĨŲ„ØēØ§ØĄ ØĒ؁ØļŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ", "favorites": "Ø§Ų„Ų…ŲØļŲ„ØŠ", "favorites_page_no_favorites": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…ŲØļŲ„ØŠ", "feature_photo_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ų…Ų…ŲŠØ˛ØŠ", "features": "Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ", + "features_in_development": "Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ų‚ŲŠØ¯ Ø§Ų„ØĒØˇŲˆŲŠØą", "features_setting_description": "ØĨØ¯Ø§ØąØŠ Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "file_name": "ØĨØŗŲ… Ø§Ų„Ų…Ų„Ų", "file_name_or_extension": "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų ØŖŲˆ Ø§Ų…ØĒØ¯Ø§Ø¯Ų‡", "filename": "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų", "filetype": "Ų†ŲˆØš Ø§Ų„Ų…Ų„Ų", + "filter": "ØĒØĩŲŲŠØŠ", "filter_people": "ØĒØĩŲŲŠØŠ Ø§Ų„Ø§Ø´ØŽØ§Øĩ", + "filter_places": "ØĒØĩŲŲŠØŠ Ø§Ų„Ø§Ų…Ø§ŲƒŲ†", "find_them_fast": "ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØšØĢŲˆØą ØšŲ„ŲŠŲ‡Ø§ Ø¨ØŗØąØšØŠ Ø¨Ø§Ų„Ø§ØŗŲ… Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„Ø¨Ø­ØĢ", + "first": "Ø§Ų„Ø§ŲˆŲ„", "fix_incorrect_match": "ØĨØĩŲ„Ø§Ø­ Ø§Ų„Ų…ØˇØ§Ø¨Ų‚ØŠ ØēŲŠØą Ø§Ų„ØĩØ­ŲŠØ­ØŠ", + "folder": "Ų…ØŦŲ„Ø¯", + "folder_not_found": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯", "folders": "Ø§Ų„Ų…ØŦŲ„Ø¯Ø§ØĒ", "folders_feature_description": "ØĒØĩŲØ­ ØšØąØļ Ø§Ų„Ų…ØŦŲ„Ø¯ Ų„Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ØšŲ„Ų‰ Ų†Ø¸Ø§Ų… Ø§Ų„Ų…Ų„ŲØ§ØĒ", + "forgot_pin_code_question": "Ų‡Ų„ Ų†ØŗŲŠØĒ ØąŲ…Ø˛ Ø§Ų„PIN Ø§Ų„ØŽØ§Øĩ Ø¨ŲƒØŸ", "forward": "ØĨŲ„Ų‰ Ø§Ų„ØŖŲ…Ø§Ų…", + "gcast_enabled": "ŲƒŲˆŲƒŲ„ ŲƒØ§ØŗØĒ", + "gcast_enabled_description": "ØĒŲ‚ŲˆŲ… Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠ بØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…ŲˆØ§ØąØ¯ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ Ų…Ų† Google Ø­ØĒŲ‰ ØĒØšŲ…Ų„.", "general": "ØšØ§Ų…", + "geolocation_instruction_location": "Ø§Ų†Ų‚Øą ØšŲ„Ų‰ Ø§Ų„Ø§ØĩŲ„ Ø§Ų„Ø°ŲŠ ŲŠØ­ØĒ؈؊ ØšŲ„Ų‰ ØĨحداØĢŲŠØ§ØĒ Ų†Ø¸Ø§Ų… ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„Ų…ŲˆØ§Ų‚Øš Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ŲˆŲ‚ØšŲ‡ØŒ ØŖŲˆ ا؎ØĒØą Ø§Ų„Ų…ŲˆŲ‚Øš Ų…Ø¨Ø§Ø´ØąØŠ Ų…Ų† Ø§Ų„ØŽØąŲŠØˇØŠ", "get_help": "Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØ§ØšØ¯ØŠ", + "get_wifiname_error": "ØĒØšØ°Øą Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ø§ØŗŲ… Ø´Ø¨ŲƒØŠ Wi-Fi. ØĒØŖŲƒØ¯ Ų…Ų† Ų…Ų†Ø­ Ø§Ų„ØŖØ°ŲˆŲ†Ø§ØĒ Ø§Ų„Ų„Ø§Ø˛Ų…ØŠ ŲˆØ§ØĒØĩØ§Ų„Ųƒ Ø¨Ø´Ø¨ŲƒØŠ Wi-Fi", "getting_started": "Ø§Ų„Ø¨Ø¯ØĄ", "go_back": "Ø§Ų„ØąØŦŲˆØš Ų„Ų„ØŽŲ„Ų", "go_to_folder": "Ø§Ø°Ų‡Ø¨ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯", "go_to_search": "Ø§Ø°Ų‡Ø¨ ØĨŲ„Ų‰ Ø§Ų„Ø¨Ø­ØĢ", + "gps": "Ų†Ø¸Ø§Ų… ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„Ų…ŲˆØ§Ų‚Øš", + "gps_missing": "Ų„Ø§ ؊؈ØŦد Ų†Ø¸Ø§Ų… ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„Ų…ŲˆØ§Ų‚Øš", + "grant_permission": "Ų…Ų†Ø­ Ø§Ų„Ø§Ø°Ų†", "group_albums_by": "ØĒØŦŲ…ŲŠØš Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø­ØŗØ¨...", "group_country": "Ų…ØŦŲ…ŲˆØšØŠ Ø§Ų„Ø¨Ų„Ø¯", "group_no": "Ø¨Ø¯ŲˆŲ† ØĒØŦŲ…ŲŠØš", @@ -893,6 +1112,15 @@ "haptic_feedback_switch": "ØĒŲ…ŲƒŲŠŲ† ØąØ¯ŲˆØ¯ Ø§Ų„ŲØšŲ„ Ø§Ų„Ų„Ų…ØŗŲŠØŠ", "haptic_feedback_title": "ØąØ¯ŲˆØ¯ ŲØšŲ„ Ų„Ų…ØŗŲŠØŠ", "has_quota": "Ų…Ø­Ø¯Ø¯ بحØĩØŠ", + "hash_asset": "ØšŲ…Ų„ Hash Ų„Ų„ØŖØĩŲ„ (؄؄؅؄؁)", + "hashed_assets": "ØŖØĩŲˆŲ„ (Ų…Ų„ŲØ§ØĒ) ØĒŲ… ØšŲ…Ų„ Hash Ų„Ų‡Ø§", + "hashing": "؊ØĒŲ… ØšŲ…Ų„ Hash", + "header_settings_add_header_tip": "اØļØ§Ų ØąØ§Øŗ", + "header_settings_field_validator_msg": "Ø§Ų„Ų‚ŲŠŲ…ØŠ Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų† ØĒŲƒŲˆŲ† ŲØ§ØąØēØŠ", + "header_settings_header_name_input": "Ø§ØŗŲ… Ø§Ų„ØąØŖØŗ", + "header_settings_header_value_input": "Ų‚ŲŠŲ…ØŠ Ø§Ų„ØąØŖØŗ", + "headers_settings_tile_subtitle": "Ų‚Ų… بØĒØšØąŲŠŲ ØąØ¤ŲˆØŗ Ø§Ų„ŲˆŲƒŲŠŲ„ Ø§Ų„ØĒ؊ ؊ØŦب ØŖŲ† ŲŠØąØŗŲ„Ų‡Ø§ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ų…Øš ŲƒŲ„ ØˇŲ„Ø¨ Ø´Ø¨ŲƒØŠ", + "headers_settings_tile_title": "ØąØ¤ŲˆØŗ ŲˆŲƒŲŠŲ„ Ų…ØŽØĩØĩØŠ", "hi_user": "Ų…ØąØ­Ø¨Ø§ {name} ({email})", "hide_all_people": "ØĨØŽŲØ§ØĄ ØŦŲ…ŲŠØš Ø§Ų„ØŖØ´ØŽØ§Øĩ", "hide_gallery": "Ø§ØŽŲØ§ØĄ Ø§Ų„Ų…ØšØąØļ", @@ -900,9 +1128,9 @@ "hide_password": "Ø§ØŽŲØ§ØĄ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "hide_person": "Ø§ØŽŲØ§ØĄ Ø§Ų„Ø´ØŽØĩ", "hide_unnamed_people": "ØĨØŽŲØ§ØĄ Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø¨Ø¯ŲˆŲ† ØĨØŗŲ…", - "home_page_add_to_album_conflicts": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {ØĒŲ…ØĒ ØĨØļØ§ŲØŠ} Ø§Ų„ØŖØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… {Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…}.{ŲØ´Ų„} Ø§Ų„ØŖØĩŲˆŲ„ Ų…ŲˆØŦŲˆØ¯ØŠ Ø¨Ø§Ų„ŲØšŲ„ ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ….", + "home_page_add_to_album_conflicts": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {added} ØŖØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… {album}. {failed} ØŖØĩŲˆŲ„ Ų…ŲˆØŦŲˆØ¯ØŠ Ø¨Ø§Ų„ŲØšŲ„ ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ….", "home_page_add_to_album_err_local": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨØļØ§ŲØŠ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø­ØĒŲ‰ Ø§Ų„ØĸŲ† ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", - "home_page_add_to_album_success": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {ØĒŲ…ØĒ ØĨØļØ§ŲØŠ} Ø§Ų„ØŖØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… {Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…}.", + "home_page_add_to_album_success": "ØĒŲ…ØĒ ØĨØļØ§ŲØŠ {added} ØŖØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… {album}.", "home_page_album_err_partner": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨØļØ§ŲØŠ ØŖØĩŲˆŲ„ Ø´ØąŲŠŲƒØŠ ØĨŲ„Ų‰ ØŖŲ„Ø¨ŲˆŲ… Ø­ØĒŲ‰ Ø§Ų„ØĸŲ† ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "home_page_archive_err_local": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŖØąØ´ŲØŠ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ Ø­ØĒŲ‰ Ø§Ų„ØĸŲ† ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "home_page_archive_err_partner": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŖØąØ´ŲØŠ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ø´ØąŲŠŲƒØŠ ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", @@ -911,11 +1139,18 @@ "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_locked_error_local": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ų†Ų‚Ų„ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„ØŒ ؊ØĒŲ… Ø§Ų„ØĒØŽØˇŲŠ", + "home_page_locked_error_partner": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ų†Ų‚Ų„ ØŖØĩŲˆŲ„ Ø§Ų„Ø´ØąŲŠŲƒ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„ØŒ ؊ØĒŲ… Ø§Ų„ØĒØŽØˇŲŠ", "home_page_share_err_local": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ų…Ø´Ø§ØąŲƒØŠ Ø§Ų„ØŖØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ ØšØ¨Øą Ø§Ų„ØąØ§Ø¨Øˇ ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "home_page_upload_err_limit": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨŲ„Ø§ ØĒØ­Ų…ŲŠŲ„ 30 ØŖØ­Ø¯ Ø§Ų„ØŖØĩŲˆŲ„ ؁؊ ŲˆŲ‚ØĒ ŲˆØ§Ø­Ø¯ ، ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "host": "Ø§Ų„Ų…Øļ؊؁", "hour": "ØŗØ§ØšØŠ", + "hours": "ØŗØ§ØšØ§ØĒ", + "id": "Ø§Ų„Ų…ØšØąŲ", + "idle": "ØŽØ§Ų…Ų„", + "ignore_icloud_photos": "ØĒØŦØ§Ų‡Ų„ ØĩŲˆØą iCloud", + "ignore_icloud_photos_description": "Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØŽØ˛Ų†ØŠ ؁؊ Cloud Ų„Ų† ؊ØĒŲ… ØĒØ­Ų…ŲŠŲ„Ų‡Ø§ ØĨŲ„Ų‰ ØŽØ§Ø¯Ų… Immich", "image": "ØĩŲˆØąØŠ", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ Ų…Øš {person1} ؁؊ {date}", @@ -927,6 +1162,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}، {country} Ų…Øš {person1} ؈{person2} ؁؊ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}، {country} Ų…Øš {person1}، {person2}، ؈{person3} ؁؊ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ ؁؊ {city}, {country} with {person1}, {person2}, Ų…Øš {additionalCount, number} ØĸØŽØąŲŠŲ† ؁؊ {date}", + "image_saved_successfully": "Ø§Ų„ØĩŲˆØą Ø­ŲŲØ¸ØĒ", "image_viewer_page_state_provider_download_started": "Ø¨Ø¯ØŖ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„", "image_viewer_page_state_provider_download_success": "ØĒŲ… Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø¨Ų†ØŦاح", "image_viewer_page_state_provider_share_error": "ØŽØˇØŖ ؁؊ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", @@ -948,8 +1184,16 @@ "night_at_midnight": "ŲƒŲ„ Ų„ŲŠŲ„ØŠ ØšŲ†Ø¯ Ų…Ų†ØĒØĩ؁ Ø§Ų„Ų„ŲŠŲ„", "night_at_twoam": "ŲƒŲ„ Ų„ŲŠŲ„ØŠ Ø§Ų„ØŗØ§ØšØŠ 2 Øĩباحا" }, + "invalid_date": "ØĒØ§ØąŲŠØŽ ØēŲŠØą ØĩØ§Ų„Ø­", + "invalid_date_format": "Øĩ؊ØēØŠ ØĒØ§ØąŲŠØŽ ØēŲŠØą ØĩØ§Ų„Ø­ØŠ", "invite_people": "Ø¯ØšŲˆØŠ Ø§Ų„ØŖØ´ØŽØ§Øĩ", "invite_to_album": "Ø¯ØšŲˆØŠ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", + "ios_debug_info_fetch_ran_at": "ØŦØąØĒ ØšŲ…Ų„ŲŠØŠ Ø§Ų„ØŦŲ„Ø¨ ؁؊ {dateTime}", + "ios_debug_info_last_sync_at": "Ø§ØŽØą Ų…Ø˛Ø§Ų…Ų†ØŠ {dateTime}", + "ios_debug_info_no_processes_queued": "Ų„Ø§ ØĒ؈ØŦد ØšŲ…Ų„ŲŠØ§ØĒ ØŽŲ„ŲŲŠØŠ ؁؊ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą", + "ios_debug_info_no_sync_yet": "Ų„Ų… ؊ØĒŲ… ØĒØ´ØēŲŠŲ„ ØŖŲŠ Ų…Ų‡Ų…ØŠ Ų…Ø˛Ø§Ų…Ų†ØŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ø­ØĒŲ‰ Ø§Ų„ØĸŲ†", + "ios_debug_info_processes_queued": "{count, plural, one {{count} ØšŲ…Ų„ŲŠØŠ ØŽŲ„ŲŲŠØŠ Ø§Ø¯ØŽŲ„ØĒØŠŲŲŠ ØˇØ§Ø¨ŲˆØą} other {{count} ØšŲ…Ų„ŲŠØ§ØĒ ØŽŲ„ŲŲŠØŠ Ø§Ø¯ØŽŲ„ØĒ ؁؊ ØˇØ§Ø¨ŲˆØą}}", + "ios_debug_info_processing_ran_at": "Ø§Ų„Ų…ØšØ§Ų„ØŦØŠ ØŦØąØĒ ؁؊ {dateTime}", "items_count": "{count, plural, one {# ØšŲ†ØĩØą} other {# ØšŲ†Ø§ØĩØą}}", "jobs": "Ø§Ų„ŲˆØ¸Ø§ØĻ؁", "keep": "احØĒŲØ¸", @@ -958,11 +1202,17 @@ "kept_this_deleted_others": "ØĒŲ… Ø§Ų„Ø§Ø­ØĒŲØ§Ø¸ Ø¨Ų‡Ø°Ø§ Ø§Ų„ØŖØĩŲ„ ŲˆØ­Ø°Ų {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "ا؎ØĒØĩØ§ØąØ§ØĒ Ų„ŲˆØ­ØŠ Ø§Ų„Ų…ŲØ§ØĒŲŠØ­", "language": "Ø§Ų„Ų„ØēØŠ", + "language_no_results_subtitle": "Ø­Ø§ŲˆŲ„ ØĒØšØ¯ŲŠŲ„ Ų…ØĩØˇŲ„Ø­ Ø§Ų„Ø¨Ø­ØĢ", + "language_no_results_title": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ų„ØēاØĒ", + "language_search_hint": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ų„ØēاØĒ...", "language_setting_description": "ا؎ØĒØą Ų„ØēØĒ؃ Ø§Ų„Ų…ŲØļŲ„ØŠ", + "large_files": "Ų…Ų„ŲØ§ØĒ ŲƒØ¨ŲŠØąØŠ", + "last": "Ø§Ų„Ø§ØŽŲŠØą", "last_seen": "Ø§ØŽØą Ø¸Ų‡ŲˆØą", "latest_version": "احدØĢ اØĩØ¯Ø§Øą", "latitude": "ØŽØˇ Ø§Ų„ØšØąØļ", "leave": "Ų…ØēØ§Ø¯ØąØŠ", + "leave_album": "اØĒØąŲƒ Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…", "lens_model": "Ų†Ų…ŲˆØ°ØŦ Ø§Ų„ØšØ¯ØŗØ§ØĒ", "let_others_respond": "دؚ Ø§Ų„ØĸØŽØąŲŠŲ† ŲŠØŗØĒØŦŲŠØ¨ŲˆŲ†", "level": "Ø§Ų„Ų…ØŗØĒŲˆŲ‰", @@ -974,30 +1224,43 @@ "library_page_sort_created": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨŲ†Ø´Ø§ØĄ", "library_page_sort_last_modified": "ØĸØŽØą ØĒØšØ¯ŲŠŲ„", "library_page_sort_title": "ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", + "licenses": "ØąŲØŽŲŽØĩ", "light": "Ø§Ų„Ų…Øļ؊ØĻ", + "like": "اؚØŦاب", "like_deleted": "ØĒŲ… Ø­Ø°Ų Ø§Ų„ØĨØšØŦاب", "link_motion_video": "ØąØ§Ø¨Øˇ ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø­ØąŲƒØŠ", - "link_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØąØ§Ø¨Øˇ", "link_to_oauth": "Ø§Ų„ØąØ¨Øˇ Ų…Øš OAuth", "linked_oauth_account": "Ø­ØŗØ§Ø¨ Ų…ØąØĒØ¨Øˇ Ø¨Ų€ OAuth", "list": "Ų‚Ø§ØĻŲ…ØŠ", "loading": "ØĒØ­Ų…ŲŠŲ„", "loading_search_results_failed": "ŲØ´Ų„ ØĒØ­Ų…ŲŠŲ„ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ", - "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": "Ų…Ø­Ų„Ų‘ŲŠ", + "local_asset_cast_failed": "ØēŲŠØą Ų‚Ø§Ø¯Øą ØšŲ„Ų‰ بØĢ ØŖØĩŲ„ Ų„Ų… ؊ØĒŲ… ØĒØ­Ų…ŲŠŲ„Ų‡ ØĨŲ„Ų‰ Ø§Ų„ØŽØ§Ø¯Ų…", + "local_assets": "ØŖŲØĩŲˆŲ„ (Ų…Ų„ŲØ§ØĒ) Ų…Ø­Ų„ŲŠØŠ", + "local_media_summary": "Ų…Ų„ØŽØĩ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ", + "local_network": "Ø´Ø¨ŲƒØŠ Ų…Ø­Ų„ŲŠØŠ", + "local_network_sheet_info": "ØŗŲŠØĒØĩŲ„ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø¨Ø§Ų„ØŽØ§Ø¯Ų… Ų…Ų† ØŽŲ„Ø§Ų„ ØšŲ†ŲˆØ§Ų† URL Ų‡Ø°Ø§ ØšŲ†Ø¯ Ø§ØŗØĒØŽØ¯Ø§Ų… Ø´Ø¨ŲƒØŠ Wi-Fi Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ", + "location_permission": "Ø§Ø°Ų† Ø§Ų„Ų…ŲˆŲ‚Øš", + "location_permission_content": "Ų…Ų† ØŖØŦŲ„ Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ŲŠØ˛ØŠ Ø§Ų„ØĒØ¨Ø¯ŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŒ ŲŠØ­ØĒاØŦ Immich ØĨŲ„Ų‰ ØĨØ°Ų† Ų…ŲˆŲ‚Øš Ø¯Ų‚ŲŠŲ‚ Ø­ØĒŲ‰ ؊ØĒŲ…ŲƒŲ† Ų…Ų† Ų‚ØąØ§ØĄØŠ Ø§ØŗŲ… Ø´Ø¨ŲƒØŠ Wi-Fi Ø§Ų„Ø­Ø§Ų„ŲŠØŠ", "location_picker_choose_on_map": "ا؎ØĒØą ØšŲ„Ų‰ Ø§Ų„ØŽØąŲŠØˇØŠ", "location_picker_latitude_error": "ØŖØ¯ØŽŲ„ ØŽØˇ ØšØąØļ ØĩØ§Ų„Ø­", "location_picker_latitude_hint": "ØŖØ¯ØŽŲ„ ØŽØˇ Ø§Ų„ØšØąØļ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ų‡Ų†Ø§", "location_picker_longitude_error": "ØŖØ¯ØŽŲ„ ØŽØˇ Ø§Ų„ØˇŲˆŲ„ Ø§Ų„ØĩØ­ŲŠØ­", "location_picker_longitude_hint": "ØŖØ¯ØŽŲ„ ØŽØˇ Ø§Ų„ØˇŲˆŲ„ Ų‡Ų†Ø§", + "lock": "؂؁؄", + "locked_folder": "Ų…ØŦŲ„Ø¯ Ų…Ų‚ŲŲˆŲ„", + "log_detail_title": "ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„ØŗØŦŲ„", "log_out": "ØĒØŗØŦŲŠŲ„ ØŽØąŲˆØŦ", "log_out_all_devices": "ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† ŲƒØ§ŲØŠ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ", + "logged_in_as": "ØĒŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗŲ… {user}", "logged_out_all_devices": "ØĒŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† ØŦŲ…ŲŠØš Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ", "logged_out_device": "ØĒŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† Ø§Ų„ØŦŲ‡Ø§Ø˛", "login": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„", "login_disabled": "ØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„", - "login_form_api_exception": " Ø§ØŗØĒØĢŲ†Ø§ØĄ Ø¨ØąŲ…ØŦØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ. ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŽØ§Ø¯Ų… ŲˆØ§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ Ų…ØąØŠ ØŖØŽØąŲ‰ ", + "login_form_api_exception": "Ø§ØŗØĒØĢŲ†Ø§ØĄ API. ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų…Ų† ØšŲ†ŲˆØ§Ų† URL Ø§Ų„ØŽØ§Ø¯Ų… ŲˆØ§Ų„Ų…Ø­Ø§ŲˆŲ„ØŠ Ų…ØąØŠ ØŖØŽØąŲ‰.", "login_form_back_button_text": "Ø§Ų„ØąØŦŲˆØš Ų„Ų„ØŽŲ„Ų", "login_form_email_hint": "yoursemail@email.com", + "login_form_endpoint_hint": "http://Ø§Ų„Ų…Ų†ŲØ°:ØšŲ†ŲˆØ§Ų†â€Ģ-ip-Ø§Ų„ØŽØ§Ø¯Ų…", "login_form_endpoint_url": "url Ų†Ų‚ØˇØŠ Ų†Ų‡Ø§ŲŠØŠ Ø§Ų„ØŽØ§Ø¯Ų…", "login_form_err_http": "ŲŠØąØŦŲ‰ ØĒØ­Ø¯ŲŠØ¯ http:// ØŖŲˆ https://", "login_form_err_invalid_email": "Ø¨ØąŲŠØ¯ ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØŽØ§ØˇØĻ", @@ -1017,12 +1280,15 @@ "login_password_changed_success": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ ŲƒŲ„Ų…ØŠ Ø§Ų„ØŗØą Ø¨Ų†ØŦاح", "logout_all_device_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† ØŦŲ…ŲŠØš Ø§Ų„ØŖØŦŲ‡Ø˛ØŠØŸ", "logout_this_device_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† Ų‡Ø°Ø§ Ø§Ų„ØŦŲ‡Ø§Ø˛ØŸ", + "logs": "Ø§Ų„ØŗØŦŲ„Ø§ØĒ", "longitude": "ØŽØˇ Ø§Ų„ØˇŲˆŲ„", "look": "Ø§Ų„Ø´ŲƒŲ„", "loop_videos": "ØĒŲƒØąØ§Øą Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ", "loop_videos_description": "ŲŲŽØšŲ’Ų„ Ų„ØĒŲƒØąØ§Øą Ų…Ų‚ØˇØš ŲŲŠØ¯ŲŠŲˆ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ؁؊ ØšØ§ØąØļ Ø§Ų„ØĒŲØ§ØĩŲŠŲ„.", - "main_branch_warning": "ØŖŲ†ØĒ ØĒØŗØĒØŽØ¯Ų… ØĨØĩØ¯Ø§ØąØ§Ų‹ ØĒØˇŲˆŲŠØąŲŠØ§Ų‹Ø› ŲˆŲ†Ø­Ų† Ų†ŲˆØĩ؊ بشد؊ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØĨØĩØ¯Ø§Øą Ø§Ų„Ų†Ø´Øą!", + "main_branch_warning": "ØŖŲ†ØĒ ØĒØŗØĒØŽØ¯Ų… ØĨØĩØ¯Ø§ØąØ§Ų‹ Ų‚ŲŠØ¯ Ø§Ų„ØĒØˇŲˆŲŠØąØ› ŲˆŲ†Ø­Ų† Ų†ŲˆØĩ؊ بشد؊ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØĨØĩØ¯Ø§Øą Ø§Ų„Ų†Ø´Øą!", + "main_menu": "Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ", "make": "ØĩŲ†Øš", + "manage_geolocation": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų…ŲˆŲ‚Øš", "manage_shared_links": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØąŲˆØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŠ", "manage_sharing_with_partners": "ØĨØ¯Ø§ØąØŠ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…Øš Ø§Ų„Ø´ØąŲƒØ§ØĄ", "manage_the_app_settings": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", @@ -1031,6 +1297,7 @@ "manage_your_devices": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØŖØŦŲ‡Ø˛ØŠ Ø§Ų„ØĒ؊ ØĒŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ ØĨŲ„ŲŠŲ‡Ø§", "manage_your_oauth_connection": "ØĨØ¯Ø§ØąØŠ اØĒØĩØ§Ų„ OAuth Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "map": "Ø§Ų„ØŽØąŲŠØˇØŠ", + "map_assets_in_bounds": "{count, plural, =0 {Ų„Ø§ŲŠŲˆØŦد ØĩŲˆØą ؁؊ Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų†ØˇŲ‚ØŠ} one {# ØĩŲˆØąØŠ} other {# ØĩŲˆØą}}", "map_cannot_get_user_location": "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„Ø­ØĩŲˆŲ„ ØšŲ„Ų‰ Ų…ŲˆŲ‚Øš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "map_location_dialog_yes": "Ų†ØšŲ…", "map_location_picker_page_use_location": "Ø§ØŗØĒØŽØ¯Ų… Ų‡Ø°Ø§ Ø§Ų„Ų…ŲˆŲ‚Øš", @@ -1038,20 +1305,25 @@ "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_title": "ØĒŲ… ØąŲØļ ØĨØ°Ų† Ø§Ų„Ų…ŲˆŲ‚Øš", "map_settings": "ØĨؚداداØĒ Ø§Ų„ØŽØąŲŠØˇØŠ", "map_settings_dark_mode": "Ø§Ų„ŲˆØļØš Ø§Ų„Ų…Ø¸Ų„Ų…", "map_settings_date_range_option_day": "24 ØŗØ§ØšØŠ Ø§Ų„Ų…Ø§ØļŲŠØŠ", + "map_settings_date_range_option_days": "Ø§Ų„Ø§ŲŠØ§Ų… {days} Ø§Ų„Ų…Ø§ØļŲŠØŠ", "map_settings_date_range_option_year": "Ø§Ų„ØŗŲ†ØŠ Ø§Ų„ŲØ§ØĻØĒØŠ", + "map_settings_date_range_option_years": "Ø§Ų„ØŗŲ†ŲˆØ§ØĒ {years} Ø§Ų„Ų…Ø§ØļŲŠØŠ", "map_settings_dialog_title": "ØĨؚداداØĒ Ø§Ų„ØŽØąŲŠØˇØŠ", "map_settings_include_show_archived": "ØĒØ´Ų…Ų„ Ø§Ų„ØŖØąØ´ŲØŠ", "map_settings_include_show_partners": "ØĒØļŲ…ŲŠŲ† Ø§Ų„Ø´ØąŲƒØ§ØĄ", "map_settings_only_show_favorites": "Ø§Ø¸Ų‡Ø§Øą Ø§Ų„Ų…ŲØļŲ„ØŠ ŲŲ‚Øˇ", "map_settings_theme_settings": "Ų…Ø¸Ų‡Øą Ø§Ų„ØŽØąŲŠØˇØŠ", "map_zoom_to_see_photos": "Ų‚Ų… بØĒØĩØēŲŠØąŲ‡Ø§ Ų„ØąØ¤ŲŠØŠ Ø§Ų„ØĩŲˆØą", + "mark_all_as_read": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„ ŲƒŲ…Ų‚ØąŲˆØĄ", + "mark_as_read": "ØĒØ­Ø¯ŲŠØ¯ ŲƒŲ…Ų‚ØąŲˆØĄ", + "marked_all_as_read": "ØĒŲ… ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„ ŲƒŲ…Ų‚ØąŲˆØĄ", "matches": "ØĒØˇØ§Ø¨Ų‚Ø§ØĒ", + "matching_assets": "â€Ø§Ų„Ø§ØĩŲˆŲ„ Ø§Ų„Ų…ØˇØ§Ø¨Ų‚ØŠ", "media_type": "Ų†ŲˆØš Ø§Ų„ŲˆØŗØ§ØĻØˇ", "memories": "Ø§Ų„Ø°ŲƒØąŲŠØ§ØĒ", "memories_all_caught_up": "ŲƒŲ„ Ø´ŲŠØĄ Ų…Ø­Ø¯ØĢ", @@ -1070,11 +1342,19 @@ "merged_people_count": "Ø¯Ų…ØŦ {count, plural, one {Ø´ØŽØĩ ŲˆØ§Ø­Ø¯} other {# ØŖØ´ØŽØ§Øĩ}}", "minimize": "ØĒØĩØēŲŠØą", "minute": "Ø¯Ų‚ŲŠŲ‚ØŠ", + "minutes": "Ø¯Ų‚Ø§ØĻŲ‚", "missing": "Ø§Ų„Ų…ŲŲ‚ŲˆØ¯ØŠ", "model": "Ų†Ų…ŲˆØ°ØŦ", "month": "Ø´Ų‡Øą", "monthly_title_text_date_format": "Øˇ Øˇ Øˇ", "more": "Ø§Ų„Ų…Ø˛ŲŠØ¯", + "move": "ØĒØ­ØąŲŠŲƒ", + "move_off_locked_folder": "ØĒØ­ØąŲŠŲƒ ØŽØ§ØąØŦ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", + "move_to_lock_folder_action_prompt": "{count} اØļ؊؁ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", + "move_to_locked_folder": "Ø§Ų„Ų†Ų‚Ų„ Ø§Ų„Ų‰ Ų…ØŦŲ„Ø¯ Ų…ØēŲ„Ų‚", + "move_to_locked_folder_confirmation": "Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲØ¯ŲŠŲˆØ§ØĒ ØŗØĒØĒŲ… Ø§Ø˛Ø§Ų„ØĒŲ‡Ø§ Ų…Ų† ØŦŲ…ŲŠØš Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ، ŲˆŲŠŲ…ŲƒŲ†Ø§Ų† ØĒØĒŲ… Ų…Ø´Ø§Ų‡Ø¯ØĒŲ‡Ø§ ŲŲ‚Øˇ Ų…Ų† ØŽŲ„Ø§Ų„ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", + "moved_to_archive": "ØĒŲ… Ų†Ų‚Ų„ {count, plural, one {# اØĩŲ„} other {# اØĩŲˆŲ„}} Ø§Ų„Ų‰ Ø§Ų„Ø§ØąØ´ŲŠŲ", + "moved_to_library": "ØĒŲ… Ų†Ų‚Ų„ {count, plural, one {# اØĩŲ„} other {# اØĩŲˆŲ„}} Ø§Ų„Ų‰ Ø§Ų„Ų…ŲƒØĒب؊", "moved_to_trash": "ØĒŲ… Ø§Ų„Ų†Ų‚Ų„ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "multiselect_grid_edit_date_time_err_read_only": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØšØ¯ŲŠŲ„ ØĒØ§ØąŲŠØŽ Ø§Ų„ØŖØĩŲˆŲ„ (Ø§Ų„Ų…ŲˆØ§Ø¯) Ų„Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚ØˇØŒ ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", "multiselect_grid_edit_gps_err_read_only": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØšØ¯ŲŠŲ„ Ų…ŲˆŲ‚Øš Ø§Ų„ØŖØĩŲˆŲ„ (Ø§Ų„Ų…ŲˆØ§Ø¯) Ų„Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚ØˇØŒ ØŗŲˆŲ ؊ØĒØŽØˇŲ‰", @@ -1082,12 +1362,20 @@ "my_albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ؊", "name": "Ø§Ų„Ø§ØŗŲ…", "name_or_nickname": "Ø§Ų„Ø§ØŗŲ… ØŖŲˆ Ø§Ų„Ų„Ų‚Ø¨", + "network_requirement_photos_upload": "Ø§ØŗØĒØŽØ¯Ø§Ų… Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ų‡Ø§ØĒ؁ Ø§Ų„Ų…Ø­Ų…ŲˆŲ„ Ų„ØšŲ…Ų„ Ų†ØŗØŽØŠ احØĒŲŠØ§ØˇŲŠØŠ Ų„Ų„ØĩŲˆØą", + "network_requirement_videos_upload": "Ø§ØŗØĒØŽØ¯Ø§Ų… Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„Ų‡Ø§ØĒ؁ Ø§Ų„Ų…Ø­Ų…ŲˆŲ„ Ų„ØšŲ…Ų„ Ų†ØŗØŽØŠ احØĒŲŠØ§ØˇŲŠØŠ Ų„Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ", + "network_requirements": "Ų…ØĒØˇŲ„Ø¨Ø§ØĒ Ø§Ų„Ø´Ø¨ŲƒØŠ", + "network_requirements_updated": "ØĒŲ… ØĒØēŲŠŲŠØą Ų…ØĒØˇŲ„Ø¨Ø§ØĒ Ø§Ų„Ø´Ø¨ŲƒØŠØŒ ؊ØĒŲ… ØĨؚاد؊ ØĒØšŲŠŲŠŲ† Ų‚Ø§ØĻŲ…ØŠ Ø§Ų†ØĒØ¸Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ", + "networking_settings": "Ø§Ų„Ø´Ø¨ŲƒØ§ØĒ", + "networking_subtitle": "ØĨØ¯Ø§ØąØŠ ØĨؚداداØĒ Ų†Ų‚ØˇØŠ Ø§Ų„ØŽØ§Ø¯Ų… Ø§Ų„Ų†Ų‡Ø§ØĻŲŠØŠ", "never": "ØŖØ¨Ø¯Ø§Ų‹", "new_album": "Ø§Ų„Ø¨ŲˆŲ… ØŦØ¯ŲŠØ¯", "new_api_key": "؅؁ØĒاح API ØŦØ¯ŲŠØ¯", "new_password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ", "new_person": "Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", - "new_pin_code": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ Ø§Ų„ØŦØ¯ŲŠØ¯", + "new_pin_code": "ØąŲ…Ø˛ PIN Ø§Ų„ØŦØ¯ŲŠØ¯", + "new_pin_code_subtitle": "Ų‡Ø°Ų‡ ØŖŲˆŲ„ Ų…ØąØŠ ØĒØ¯ØŽŲ„ ŲŲŠŲ‡Ø§ ØĨŲ„Ų‰ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„. ØŖŲ†Ø´ØĻ ØąŲ…Ø˛Ų‹Ø§ PIN Ų„Ų„ŲˆØĩŲˆŲ„ Ø¨Ø§Ų…Ø§Ų† ØĨŲ„Ų‰ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲØ­ØŠ", + "new_timeline": "Ø§Ų„ØŽØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„ØŦØ¯ŲŠØ¯", "new_user_created": "ØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ų…ØŗØĒØŽØ¯Ų… ØŦØ¯ŲŠØ¯", "new_version_available": "ØĨØĩØ¯Ø§Øą ØŦØ¯ŲŠØ¯ Ų…ØĒاح", "newest_first": "Ø§Ų„ØŖØ­Ø¯ØĢ ØŖŲˆŲ„Ø§Ų‹", @@ -1100,19 +1388,31 @@ "no_archived_assets_message": "ØŖØąØ´ŲØŠ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų„ØĨØŽŲØ§ØĻŲ‡Ø§ Ų…Ų† ØšØąØļ Ø§Ų„ØĩŲˆØą Ų„Ø¯ŲŠŲƒ", "no_assets_message": "Ø§Ų†Ų‚Øą Ų„ØĒØ­Ų…ŲŠŲ„ ØĩŲˆØąØĒ؃ Ø§Ų„ØŖŲˆŲ„Ų‰", "no_assets_to_show": "Ų„Ø§ ØĒ؈ØŦد ØŖØĩŲˆŲ„ Ų„ØšØąØļŲ‡Ø§", + "no_cast_devices_found": "Ų„Ų… ؊ØĒŲ… Ø§ŲŠØŦاد ØŦŲ‡Ø§Ø˛ بØĢ", + "no_checksum_local": "Ų„Ø§ ØĒ؈ØŦد Ø¨ŲŠØ§Ų†Ø§ØĒ ØĒØ­Ų‚Ų‚ Ų…ØĒاح؊ - ؊ØĒØšØ°Øą ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ø§ØĩŲˆŲ„ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ", + "no_checksum_remote": "Ų„Ø§ ؊؈ØŦد ØąŲ…Ø˛ ØĒØ­Ų‚Ų‚ Ų…ØĒاح - ؊ØĒØšØ°Øą ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ø§ØĩŲ„ Ų…Ų† Ø§Ų„Ų…ŲˆŲ‚Øš Ø§Ų„Ø¨ØšŲŠØ¯", "no_duplicates_found": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ ØŖŲŠ ØĒŲƒØąØ§ØąØ§ØĒ.", "no_exif_info_available": "Ų„Ø§ ØĒØĒŲˆŲØą Ų…ØšŲ„ŲˆŲ…Ø§ØĒ exif", "no_explore_results_message": "Ų‚Ų… Ø¨ØąŲØš Ø§Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĩŲˆØą Ų„Ø§ØŗØĒŲƒØ´Ø§Ų Ų…ØŦŲ…ŲˆØšØĒ؃.", "no_favorites_message": "ØŖØļ؁ Ø§Ų„Ų…ŲØļŲ„ØŠ Ų„Ų„ØšØĢŲˆØą Ø¨ØŗØąØšØŠ ØšŲ„Ų‰ ØŖŲØļŲ„ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ", "no_libraries_message": "ØĨŲ†Ø´Ø§ØĄ Ų…ŲƒØĒب؊ ØŽØ§ØąØŦŲŠØŠ Ų„ØšØąØļ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", + "no_local_assets_found": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ ØŖŲŠ اØĩŲˆŲ„ Ų…Ø­Ų„ŲŠØŠ ØĒØĒØˇØ§Ø¨Ų‚ Ų…Øš Ų‚ŲŠŲ…ØŠ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų‡Ø°Ų‡", + "no_locked_photos_message": "Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„ Ų…ØŽŲŲŠØŠ ŲˆŲ„Ų† ØĒØĩŲ‡Øą ؁؊ Ø§Ų„ØĒØĩŲØ­ Ø§Ųˆ Ø§Ų„Ø¨Ø­ØĢ ؁؊ Ų…ŲƒØĒبØĒ؃.", "no_name": "Ų„Ø§ Ø§ØŗŲ…", + "no_notifications": "Ų„Ø§ ØĒ؈ØŦد ØĒŲ†Ø¨ŲŠŲ‡Ø§ØĒ", + "no_people_found": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ اش؎اØĩ Ų…ØˇØ§Ø¨Ų‚ŲŠŲ†", "no_places": "Ų„Ø§ ØŖŲ…Ø§ŲƒŲ†", + "no_remote_assets_found": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ ØŖŲŠ اØĩŲˆŲ„ Ø¨ØšŲŠØ¯ØŠ ØĒØĒØˇØ§Ø¨Ų‚ Ų…Øš ØąŲ…Ø˛ Ø§Ų„ØĒØ­Ų‚Ų‚ Ų‡Ø°Ų„", "no_results": "Ų„Ø§ ؊؈ØŦد Ų†ØĒاØĻØŦ", "no_results_description": "ØŦØąØ¨ ŲƒŲ„Ų…ØŠ ØąØĻŲŠØŗŲŠØŠ Ų…ØąØ§Ø¯ŲØŠ ØŖŲˆ ØŖŲƒØĢØą ØšŲ…ŲˆŲ…ŲŠØŠ", "no_shared_albums_message": "Ų‚Ų… بØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ… Ų„Ų…Ø´Ø§ØąŲƒØŠ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…Øš Ø§Ų„ØŖØ´ØŽØ§Øĩ ؁؊ Ø´Ø¨ŲƒØĒ؃", + "no_uploads_in_progress": "Ų„Ø§ ؊؈ØŦد Ø§ŲŠ Ų…Ų„ŲØ§ØĒ Ų‚ŲŠØ¯ Ø§Ų„ØąŲØš", + "not_available": "ØēŲŠØą Ų…ØĒاح", "not_in_any_album": "Ų„ŲŠØŗØĒ ؁؊ ØŖŲŠ ØŖŲ„Ø¨ŲˆŲ…", - "note_apply_storage_label_to_previously_uploaded assets": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„ØĒØˇØ¨ŲŠŲ‚ ØĒØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ Ų…ØŗØ¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„", + "not_selected": "Ų„Ų… ŲŠØŽØĒØ§Øą", + "note_apply_storage_label_to_previously_uploaded assets": "Ų…Ų„Ø§Ø­Ø¸ØŠ: Ų„ØĒØˇØ¨ŲŠŲ‚ ØŗŲ…ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§ Ų…ØŗØ¨Ų‚Ų‹Ø§ØŒ Ų‚Ų… بØĒØ´ØēŲŠŲ„", "notes": "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ", + "nothing_here_yet": "Ų„Ø§ ؊؈ØŦد Ø´ŲŠØĄ Ų‡Ų†Ø§ بؚد", "notification_permission_dialog_content": "Ų„ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĨØŽØˇØ§ØąØ§ØĒ ، Ø§Ų†ØĒŲ‚Ų„ ØĨŲ„Ų‰ Ø§Ų„ØĨؚداداØĒ ؈ ا؎ØĒØ§Øą Ø§Ų„ØŗŲ…Ø§Ø­.", "notification_permission_list_tile_content": "Ų…Ų†Ø­ ØĨØ°Ų† Ų„ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĨØŽØˇØ§ØąØ§ØĒ.", "notification_permission_list_tile_enable_button": "ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĨØŽØˇØ§ØąØ§ØĒ", @@ -1120,25 +1420,35 @@ "notification_toggle_setting_description": "ØĒŲØšŲŠŲ„ ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ", "notifications": "ØĨØ´ØšØ§ØąØ§ØĒ", "notifications_setting_description": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ", + "oauth": "OAuth", "official_immich_resources": "Ø§Ų„Ų…ŲˆØ§ØąØ¯ Ø§Ų„ØąØŗŲ…ŲŠØŠ Ų„Ø´ØąŲƒØŠ Immich", "offline": "ØēŲŠØą Ų…ØĒØĩŲ„", + "offset": "Ø§Ø˛Ø§Ø­ØŠ", "ok": "Ų†ØšŲ…", "oldest_first": "Ø§Ų„ØŖŲ‚Ø¯Ų… ØŖŲˆŲ„Ø§", + "on_this_device": "ØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØŦŲ‡Ø§Ø˛", "onboarding": "Ø§Ų„ØĨؚداد Ø§Ų„ØŖŲˆŲ„ŲŠ", - "onboarding_privacy_description": "ØĒØšØĒŲ…Ø¯ Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØ§Ų„ŲŠØŠ (ا؎ØĒŲŠØ§ØąŲŠ) ØšŲ„Ų‰ ØŽØ¯Ų…Ø§ØĒ ØŽØ§ØąØŦŲŠØŠØŒ ŲˆŲŠŲ…ŲƒŲ† ØĒØšØˇŲŠŲ„Ų‡Ø§ ؁؊ ØŖŲŠ ŲˆŲ‚ØĒ ؁؊ ØĨؚداداØĒ Ø§Ų„ØĨØ¯Ø§ØąØŠ.", + "onboarding_locale_description": "ا؎ØĒØą Ų„ØēØĒ؃ Ø§Ų„Ų…ŲØļŲ„ØŠ. ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØąŲ‡Ø§ ŲŲŠŲ…Ø§ بؚد ؁؊ Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ.", + "onboarding_privacy_description": "ØĒØšØĒŲ…Ø¯ Ø§Ų„Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØ§Ų„ŲŠØŠ (ا؎ØĒŲŠØ§ØąŲŠ) ØšŲ„Ų‰ ØŽØ¯Ų…Ø§ØĒ ØŽØ§ØąØŦŲŠØŠØŒ ŲˆŲŠŲ…ŲƒŲ† ØĒØšØˇŲŠŲ„Ų‡Ø§ ؁؊ ØŖŲŠ ŲˆŲ‚ØĒ ؁؊ Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ.", + "onboarding_server_welcome_description": "Ų„Ų†Ų‚Ų… باؚداد Ų†ØŗØŽØĒ؃ Ų…Ų† Ø§Ų„Ø¨ØąŲ†Ø§Ų…ØŦ Ų…Øš بؚØļ Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ Ø§Ų„Ø´Ø§ØĻؚ؊.", "onboarding_theme_description": "ا؎ØĒØą Ų†ØŗŲ‚ Ø§Ų„ØŖŲ„ŲˆØ§Ų† Ų„Ų„Ų†ØŗØŽØŠ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ. ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØą Ø°Ų„Ųƒ Ų„Ø§Ø­Ų‚Ų‹Ø§ ؁؊ ØĨؚداداØĒ؃.", + "onboarding_user_welcome_description": "Ų„Ų†ØŗØ§ØšØ¯Ųƒ ØšŲ„Ų‰ Ø§Ų„Ø¨Ø¯ØĄ!", "onboarding_welcome_user": "Ų…ØąØ­Ø¨Ø§ØŒ {user}", "online": "Ų…ØĒØĩŲ„", "only_favorites": "Ø§Ų„Ų…ŲØļŲ„ØŠ ŲŲ‚Øˇ", + "open": "؁ØĒØ­", "open_in_map_view": "؁ØĒØ­ ؁؊ ØšØąØļ Ø§Ų„ØŽØąŲŠØˇØŠ", "open_in_openstreetmap": "؁ØĒØ­ ؁؊ OpenStreetMap", "open_the_search_filters": "Ø§ŲØĒØ­ Ų…ØąØ´Ø­Ø§ØĒ Ø§Ų„Ø¨Ø­ØĢ", "options": "ØŽŲŠØ§ØąØ§ØĒ", "or": "ØŖŲˆ", + "organize_into_albums": "ØĒØąØĒŲŠØ¨ ؁؊ ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ", + "organize_into_albums_description": "ØŖØļ؁ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ØĨŲ„Ų‰ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØĨؚداداØĒ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ØĒØ˛Ø§Ų…Ų† Ø§Ų„Ø­Ø§Ų„ŲŠØŠ", "organize_your_library": "ØĒŲ†Ø¸ŲŠŲ… Ų…ŲƒØĒبØĒ؃", "original": "ØŖØĩŲ„ŲŠ", "other": "ØŖØŽØąŲ‰", "other_devices": "ØŖØŦŲ‡Ø˛ØŠ ØŖØŽØąŲ‰", + "other_entities": "ŲƒŲŠØ§Ų†Ø§ØĒ ØŖØŽØąŲ‰", "other_variables": "Ų…ØĒØēŲŠØąØ§ØĒ ØŖØŽØąŲ‰", "owned": "Ų…Ų…Ų„ŲˆŲƒØŠ", "owner": "Ø§Ų„Ų…Ø§Ų„Ųƒ", @@ -1146,12 +1456,14 @@ "partner_can_access": "ŲŠØŗØĒØˇŲŠØš {partner} Ø§Ų„ŲˆØĩŲˆŲ„", "partner_can_access_assets": "ØŦŲ…ŲŠØš Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ Ø¨Ø§ØŗØĒØĢŲ†Ø§ØĄ ØĒŲ„Ųƒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ؁؊ Ø§Ų„Ų…Ø¤ØąØ´ŲØŠ ŲˆØ§Ų„Ų…Ø­Ø°ŲˆŲØŠ", "partner_can_access_location": "Ø§Ų„Ų…ŲˆŲ‚Øš Ø§Ų„Ø°ŲŠ ØĒŲ… Ø§Ų„ØĒŲ‚Ø§Øˇ ØĩŲˆØąŲƒ ŲŲŠŲ‡", + "partner_list_user_photos": "ØĩŲˆØą {user}", "partner_list_view_all": "ØšØąØļ Ø§Ų„ŲƒŲ„", "partner_page_empty_message": "Ų„Ų… ؊ØĒŲ… Ų…Ø´Ø§ØąŲƒØŠ ØĩŲˆØąŲƒ بؚد Ų…Øš ØŖŲŠ Ø´ØąŲŠŲƒ.", "partner_page_no_more_users": "Ų„Ø§ Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų„ØĨØļØ§ŲØŠ", "partner_page_partner_add_failed": "ŲØ´Ų„ ؁؊ ØĨØļØ§ŲØŠ Ø´ØąŲŠŲƒ", "partner_page_select_partner": "حدد Ø´ØąŲŠŲƒŲ‹Ø§", "partner_page_shared_to_title": "Ų…Ø´ØĒØąŲƒ Ų„", + "partner_page_stop_sharing_content": "{partner} Ų„Ų† ŲŠØšŲˆØ¯ Ų‚Ø§Ø¯ØąØ§ ØšŲ„Ų‰ Ø§Ų„ŲˆØĩ؈؁ Ø§Ų„Ų‰ ØĩŲˆØąŲƒ.", "partner_sharing": "Ų…Ø´Ø§ØąŲƒØŠ Ø§Ų„Ø´ØąŲƒØ§ØĄ", "partners": "Ø§Ų„Ø´ØąŲƒØ§ØĄ", "password": "ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", @@ -1180,16 +1492,21 @@ "permanently_delete_assets_prompt": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų {count, plural, one {Ų‡Ø°Ø§ Ø§Ų„ØšŲ†ØĩØąØŸ} other {Ų‡Ø°Ų‡ Ø§Ų„ØšŲ†Ø§ØĩØą #؟}} ØŗŲŠØĒŲ… ØŖŲŠØļŲ‹Ø§ ØĨØ˛Ø§Ų„ØĒŲ‡ {count, plural, one {Ų…Ų† ØŖŲ„Ø¨ŲˆŲ…Ų‡} other {Ų…Ų† ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒŲ‡Ų…}}.", "permanently_deleted_asset": "ØĒŲ… Ø­Ø°Ų Ø§Ų„ØŖØĩŲ„ Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊", "permanently_deleted_assets_count": "ØĒŲ… Ø­Ø°Ų {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰} other {# Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§", + "permission": "Ø§Ø°Ų†", + "permission_empty": "Ø§Ų„Ø§Ø°Ų† Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ؊ØŦب Ø§Ų† Ų„Ø§ ŲŠŲƒŲˆŲ† ŲØ§ØąØēا", "permission_onboarding_back": "ØŽŲ„Ų", "permission_onboarding_continue_anyway": "ØĒŲˆØ§ØĩŲ„ ØšŲ„Ų‰ ØŖŲŠ Ø­Ø§Ų„", "permission_onboarding_get_started": "Ø§Ų„Ø¨Ø¯ØĄ", "permission_onboarding_go_to_settings": "Ø§Ø°Ų‡Ø¨ Ų„Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ", - "permission_onboarding_permission_denied": "ØĒŲ… ØąŲØļ Ø§Ų„ØĨØ°Ų†. Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ØŒ Ų‚Ų… Ø¨Ų…Ų†Ø­ ØŖØ°ŲˆŲ†Ø§ØĒ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ Ø§Ų„ØĨؚداداØĒ ", + "permission_onboarding_permission_denied": "ØĒŲ… ØąŲØļ Ø§Ų„ØĨØ°Ų†. Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ØŒ Ų‚Ų… Ø¨Ų…Ų†Ø­ ØŖØ°ŲˆŲ†Ø§ØĒ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ Ø§Ų„ØĨؚداداØĒ.", "permission_onboarding_permission_granted": "ØĒŲ… ØĒØŖŲ…ŲŠŲ† Ø§Ų„ØĒØĩØąŲŠØ­! ؈ØļØšŲƒ ØĒŲ…Ø§Ų….", "permission_onboarding_permission_limited": "ØĨØ°Ų† Ų…Ø­Ø¯ŲˆØ¯. Ų„Ų„ØŗŲ…Ø§Ø­ Ø¨Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØĒØˇØ¨ŲŠŲ‚ ؈ØĨØ¯Ø§ØąØŠ Ų…ØŦŲ…ŲˆØšØŠ Ø§Ų„Ų…ØšØąØļ Ø¨Ø§Ų„ŲƒØ§Ų…Ų„ØŒ Ø§Ų…Ų†Ø­ ØŖØ°ŲˆŲ†Ø§ØĒ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ Ø§Ų„ØĨؚداداØĒ.", - "permission_onboarding_request": "؊ØĒØˇŲ„Ø¨ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ ØĨØ°Ų†Ų‹Ø§ Ų„ØšØąØļ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", + "permission_onboarding_request": "؊ØĒØˇŲ„Ø¨ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ ØĨØ°Ų†Ų‹Ø§ Ų„ØšØąØļ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ.", "person": "Ø´ØŽØĩ", - "person_birthdate": "ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯ {Ø§Ų„ØĒØ§ØąŲŠØŽ}", + "person_age_months": "{months, plural, one {# Ø´Ų‡Øą} other {# Ø§Ø´Ų‡Øą}} Ų…Ų† Ø§Ų„ØšŲ…Øą", + "person_age_year_months": "1 ØšØ§Ų…, {months, plural, one {# Ø´Ų‡Øą} other {# Ø§Ø´Ų‡Øą}} Ų…Ų† Ø§Ų„ØšŲ…Øą", + "person_age_years": "{years, plural, other {# Ø§ØšŲˆØ§Ų…}} Ų…Ų† Ø§Ų„ØšŲ…Øą", + "person_birthdate": "ŲˆŲ„Ø¯ ؁؊ {date}", "person_hidden": "{name}{hidden, select, true { (Ų…ØŽŲŲŠ)} other {}}", "photo_shared_all_users": "ŲŠØ¨Ø¯Ųˆ ØŖŲ†Ųƒ Ø´Ø§ØąŲƒØĒ ØĩŲˆØąŲƒ Ų…Øš ØŦŲ…ŲŠØš Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØŖŲˆ Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØŖŲŠ Ų…ØŗØĒØŽØ¯Ų… Ų„Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…ØšŲ‡.", "photos": "Ø§Ų„ØĩŲˆØą", @@ -1197,9 +1514,10 @@ "photos_count": "{count, plural, one {{count, number} ØĩŲˆØąØŠ} other {{count, number} ØĩŲˆØą}}", "photos_from_previous_years": "ØĩŲˆØą Ų…Ų† Ø§Ų„ØŗŲ†ŲˆØ§ØĒ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ", "pick_a_location": "ا؎ØĒØą Ų…ŲˆŲ‚ØšŲ‹Ø§", - "pin_code_changed_successfully": "ØĒŲ… ØĒØēŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", - "pin_code_reset_successfully": "ØĒŲ… اؚاد؊ ØĒØšŲŠŲŠŲ† Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", - "pin_code_setup_successfully": "ØĒŲ… Ø§Ų†Ø´Ø§ØĄ ØąŲ‚Ų… ØŗØąŲŠ", + "pin_code_changed_successfully": "ØĒŲ… ØĒØēŲŠØą ØąŲ…Ø˛ PIN Ø¨Ų†ØŦاح", + "pin_code_reset_successfully": "ØĒŲ… اؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ PIN Ø¨Ų†ØŦاح", + "pin_code_setup_successfully": "ØĒŲ… Ø§Ų†Ø´Ø§ØĄ ØąŲ…Ø˛ PIN Ø¨Ų†ØŦاح", + "pin_verification": "Ø§Ų„ØĒØ­Ų‚Ų‚ Ø¨ØąŲ…Ø˛ PIN", "place": "Ų…ŲƒØ§Ų†", "places": "Ø§Ų„ØŖŲ…Ø§ŲƒŲ†", "places_count": "{count, plural, one {{count, number} Ų…ŲƒØ§Ų†} other {{count, number} ØŖŲ…Ø§ŲƒŲ†}}", @@ -1207,20 +1525,28 @@ "play_memories": "ØĒØ´ØēŲŠŲ„ Ø§Ų„Ø°ŲƒØąŲŠØ§ØĒ", "play_motion_photo": "ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒØŠ", "play_or_pause_video": "ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ØŖŲˆ ØĨŲŠŲ‚Ø§ŲŲ‡ Ų…Ø¤Ų‚ØĒŲ‹Ø§", + "please_auth_to_access": "Ø§Ų„ØąØŦØ§ØĄ Ø§Ų„Ų‚ŲŠØ§Ų… Ø¨Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ Ų„Ų„ŲˆØĩŲˆŲ„", "port": "Ø§Ų„Ų…Ų†ŲØ°", + "preferences_settings_subtitle": "Ø§Ø¯Ø§ØąØŠ ØĒ؁ØļŲŠŲ„Ø§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "preferences_settings_title": "Ø§Ų„ØĒ؁ØļŲŠŲ„Ø§ØĒ", + "preparing": "Ų‚ŲŠØ¯ Ø§Ų„ØĒØ­ØļŲŠØą", "preset": "Ø§Ų„ØĨؚداد Ø§Ų„Ų…ØŗØ¨Ų‚", "preview": "Ų…ØšØ§ŲŠŲ†ØŠ", "previous": "Ø§Ų„ØŗØ§Ø¨Ų‚", "previous_memory": "Ø§Ų„Ø°ŲƒØąŲ‰ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ", - "previous_or_next_photo": "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØŗØ§Ø¨Ų‚ØŠ ØŖŲˆ Ø§Ų„ØĒØ§Ų„ŲŠØŠ", + "previous_or_next_day": "ŲŠŲˆŲ… ØĒØ§Ų„ŲŠ/ØŗØ§Ø¨Ų‚", + "previous_or_next_month": "Ø´Ų‡Øą ØĒØ§Ų„ŲŠ/ØŗØ§Ø¨Ų‚", + "previous_or_next_photo": "ØĩŲˆØąØŠ ØĒØ§Ų„ŲŠØŠ/ØŗØ§Ø¨Ų‚ØŠ", + "previous_or_next_year": "ØŗŲ†ØŠ ØĒØ§Ų„ŲŠØŠ/ØŗØ§Ø¨Ų‚ØŠ", "primary": "ØŖØŗØ§ØŗŲŠ", "privacy": "Ø§Ų„ØŽØĩ؈ØĩŲŠØŠ", + "profile": "Ø­ØŗØ§Ø¨ ØĒØšØąŲŠŲŲŠ", "profile_drawer_app_logs": "Ø§Ų„ØŗØŦŲ„Ø§ØĒ", "profile_drawer_client_out_of_date_major": "ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų‡Ø§ØĒ؁ Ø§Ų„Ų…Ø­Ų…ŲˆŲ„ Ų‚Ø¯ŲŠŲ….ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ø¯ŲŠØĢ ØĨŲ„Ų‰ ØŖØ­Ø¯ØĢ ØĨØĩØ¯Ø§Øą ØąØĻŲŠØŗŲŠ.", "profile_drawer_client_out_of_date_minor": "ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų‡Ø§ØĒ؁ Ø§Ų„Ų…Ø­Ų…ŲˆŲ„ Ų‚Ø¯ŲŠŲ….ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ø¯ŲŠØĢ ØĨŲ„Ų‰ ØŖØ­Ø¯ØĢ ØĨØĩØ¯Ø§Øą ØĩØēŲŠØą.", "profile_drawer_client_server_up_to_date": "Ø§Ų„ØšŲ…ŲŠŲ„ ŲˆØ§Ų„ØŽØ§Ø¯Ų… Ų…Ø­Ø¯ØĢØ§Ų†", "profile_drawer_github": "Github", + "profile_drawer_readonly_mode": "ØĒŲ… ØĒŲØšŲŠŲ„ ؈ØļØš Ø§Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚Øˇ. اØļØēØˇ Ų…ØˇŲˆŲ„Ø§ ØšŲ„Ų‰ ØąŲ…Ø˛ ØĩŲˆØąØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ų„Ų„ØŽØąŲˆØŦ.", "profile_drawer_server_out_of_date_major": "Ø§Ų„ØŽØ§Ø¯Ų… Ų‚Ø¯ŲŠŲ….ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ø¯ŲŠØĢ ØĨŲ„Ų‰ ØŖØ­Ø¯ØĢ ØĨØĩØ¯Ø§Øą ØąØĻŲŠØŗŲŠ.", "profile_drawer_server_out_of_date_minor": "Ø§Ų„ØŽØ§Ø¯Ų… Ų‚Ø¯ŲŠŲ….ŲŠØąØŦŲ‰ Ø§Ų„ØĒØ­Ø¯ŲŠØĢ ØĨŲ„Ų‰ ØŖØ­Ø¯ØĢ ØĨØĩØ¯Ø§Øą ØĩØēŲŠØą.", "profile_image_of_user": "ØĩŲˆØąØŠ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊ Ų„Ų€ {user}", @@ -1247,7 +1573,7 @@ "purchase_lifetime_description": "Ø§Ų„Ø´ØąØ§ØĄ Ų„Ų…Ø¯Ų‰ Ø§Ų„Ø­ŲŠØ§ØŠ", "purchase_option_title": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ø´ØąØ§ØĄ", "purchase_panel_info_1": "؊ØĒØˇŲ„Ø¨ Ø¨Ų†Ø§ØĄ Immich Ø§Ų„ŲƒØĢŲŠØą Ų…Ų† Ø§Ų„ŲˆŲ‚ØĒ ŲˆØ§Ų„ØŦŲ‡Ø¯ØŒ ŲˆŲ„Ø¯ŲŠŲ†Ø§ Ų…Ų‡Ų†Ø¯ØŗŲˆŲ† ŲŠØšŲ…Ų„ŲˆŲ† Ø¨Ø¯ŲˆØ§Ų… ŲƒØ§Ų…Ų„ Ų„ØŦØšŲ„Ų‡ ØŖŲØļŲ„ Ų…Ø§ ŲŠŲ…ŲƒŲ†. Ų…Ų‡Ų…ØĒŲ†Ø§ Ų‡ŲŠ ØŖŲ† ØĒØĩبح Ø§Ų„Ø¨ØąŲ…ØŦŲŠØ§ØĒ ؅؁ØĒŲˆØ­ØŠ Ø§Ų„Ų…ØĩØ¯Øą ŲˆŲ…Ų…Ø§ØąØŗØ§ØĒ Ø§Ų„ØšŲ…Ų„ Ø§Ų„ØŖØŽŲ„Ø§Ų‚ŲŠØŠ Ų…ØĩØ¯Øą Ø¯ØŽŲ„ Ų…ØŗØĒØ¯Ø§Ų… Ų„Ų„Ų…ØˇŲˆØąŲŠŲ† ؈ØĨŲ†Ø´Ø§ØĄ Ų†Ø¸Ø§Ų… Ø¨ŲŠØĻ؊ ŲŠØ­ØĒØąŲ… Ø§Ų„ØŽØĩ؈ØĩŲŠØŠ Ų…Øš بداØĻŲ„ Ø­Ų‚ŲŠŲ‚ŲŠØŠ Ų„Ų„ØŽØ¯Ų…Ø§ØĒ Ø§Ų„ØŗØ­Ø§Ø¨ŲŠØŠ Ø§Ų„Ø§ØŗØĒØēŲ„Ø§Ų„ŲŠØŠ.", - "purchase_panel_info_2": "Ų†Ø¸ØąŲ‹Ø§ Ų„ØŖŲ†Ų†Ø§ Ų…Ų„ØĒØ˛Ų…ŲˆŲ† Ø¨ØšØ¯Ų… ØĨØļØ§ŲØŠ Ų†Ø¸Ø§Ų… Ø­Ø¸Øą Ø§Ų„Ø§Ø´ØĒØąØ§Ųƒ ØēŲŠØą Ø§Ų„Ų…Ø¯ŲŲˆØšØŒ ؁ØĨŲ† Ų‡Ø°Ø§ Ø§Ų„Ø´ØąØ§ØĄ Ų„Ų† ŲŠŲ…Ų†Ø­Ųƒ ØŖŲŠ Ų…ŲŠØ˛Ø§ØĒ ØĨØļØ§ŲŲŠØŠ ؁؊ Immich. Ų†Ø­Ų† Ų†ØšØĒŲ…Ø¯ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų…ØĢŲ„Ųƒ Ų„Ø¯ØšŲ… Ø§Ų„ØĒØˇŲˆŲŠØą Ø§Ų„Ų…ØŗØĒŲ…Øą Ų„Ų€ Immich.", + "purchase_panel_info_2": "Ų†Ø¸ØąŲ‹Ø§ Ų„ØŖŲ†Ų†Ø§ Ų…Ų„ØĒØ˛Ų…ŲˆŲ† Ø¨ØšØ¯Ų… ØĨØļØ§ŲØŠ حاØŦØ˛ Ø¯ŲØšØŒ ؁ØĨŲ† Ų‡Ø°Ø§ Ø§Ų„Ø´ØąØ§ØĄ Ų„Ų† ŲŠŲ…Ų†Ø­Ųƒ ØŖŲŠ Ų…ŲŠØ˛Ø§ØĒ ØĨØļØ§ŲŲŠØŠ ؁؊ Immich. Ų†Ø­Ų† Ų†ØšØĒŲ…Ø¯ ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų…ØĢŲ„Ųƒ Ų„Ø¯ØšŲ… Ø§Ų„ØĒØˇŲˆŲŠØą Ø§Ų„Ų…ØŗØĒŲ…Øą Ų„Ų€ Immich.", "purchase_panel_title": "Ø§Ø¯ØšŲ… Ø§Ų„Ų…Ø´ØąŲˆØš", "purchase_per_server": "Ų„ŲƒŲ„ ØŽØ§Ø¯Ų…", "purchase_per_user": "Ų„ŲƒŲ„ Ų…ØŗØĒØŽØ¯Ų…", @@ -1259,12 +1585,17 @@ "purchase_server_description_2": "Ø­Ø§Ų„ØŠ Ø§Ų„Ø¯Ø§ØšŲ…", "purchase_server_title": "Ø§Ų„ØŽØ§Ø¯Ų…", "purchase_settings_server_activated": "؊ØĒŲ… ØĨØ¯Ø§ØąØŠ ؅؁ØĒاح Ų…Ų†ØĒØŦ Ø§Ų„ØŽØ§Ø¯Ų… Ų…Ų† Ų‚Ø¨Ų„ Ų…Ø¯ŲŠØą Ø§Ų„Ų†Ø¸Ø§Ų…", + "query_asset_id": "Ø§ØŗØĒØšŲ„Ø§Ų… ØšŲ† Ų…ØšØąŲ Ø§Ų„ØŖØĩŲ„", + "queue_status": "؊ØĒŲ… Ø§Ų„Ø§ØļØ§ŲØŠ Ø§Ų„Ų‰ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų†ØĒØ¸Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ {count}/{total}", "rating": "ØĒŲ‚ŲŠŲŠŲ… Ų†ØŦŲ…ŲŠ", "rating_clear": "Ų…ØŗØ­ Ø§Ų„ØĒŲ‚ŲŠŲŠŲ…", "rating_count": "{count, plural, one {# Ų†ØŦŲ…ØŠ} other {# Ų†ØŦŲˆŲ…}}", "rating_description": "â€Ģâ€ŒØ§ØšØąØļ ØĒŲ‚ŲŠŲŠŲ… EXIF ؁؊ Ų„ŲˆØ­ØŠ Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ", "reaction_options": "ØŽŲŠØ§ØąØ§ØĒ ØąØ¯ Ø§Ų„ŲØšŲ„", "read_changelog": "Ų‚ØąØ§ØĄØŠ ØŗØŦŲ„ Ø§Ų„ØĒØēŲŠŲŠØą", + "readonly_mode_disabled": "ØĒŲ… ØĒØšØˇŲŠŲ„ ؈ØļØš Ø§Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚Øˇ", + "readonly_mode_enabled": "ØĒŲ… ØĒŲØšŲŠŲ„ ؈ØļØš Ø§Ų„Ų‚ØąØ§ØĄØŠ ŲŲ‚Øˇ", + "ready_for_upload": "ØŦØ§Ų‡Ø˛ Ų„Ų„ØąŲØš", "reassign": "ØĨؚاد؊ Ø§Ų„ØĒØšŲŠŲŠŲ†", "reassigned_assets_to_existing_person": "ØĒŲ…ØĒ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† {count, plural, one {# Ø§Ų„ØŖØĩŲ„} other {# Ø§Ų„Ø§ØĩŲˆŲ„}} ØĨŲ„Ų‰ {name, select, null {Ø´ØŽØĩ Ų…ŲˆØŦŲˆØ¯ } other {{name}}}", "reassigned_assets_to_new_person": "ØĒŲ…ØĒ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† {count, plural, one {# Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {# Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} ØĨŲ„Ų‰ Ø´ØŽØĩ ØŦØ¯ŲŠØ¯", @@ -1272,18 +1603,24 @@ "recent": "Ø­Ø¯ŲŠØĢ", "recent-albums": "ØŖŲ„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ø­Ø¯ŲŠØĢØŠ", "recent_searches": "ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„ØŖØŽŲŠØąØŠ", + "recently_added": "اØļ؊؁ Ų…Ø¤ØŽØąØ§", "recently_added_page_title": "ØŖØļ؊؁ Ų…Ø¤ØŽØąØ§", + "recently_taken": "ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ Ų…Ø¤ØŽØąŲ‹Ø§", + "recently_taken_page_title": "ØĒŲ… Ø§Ų„ØĒŲ‚Ø§ØˇŲ‡Ø§ Ų…Ø¤ØŽØąŲ‹Ø§", "refresh": "ØĒØ­Ø¯ŲŠØĢ", "refresh_encoded_videos": "ØĒØ­Ø¯ŲŠØĢ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø´ŲØąØŠ", "refresh_faces": "ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØŦŲˆŲ‡", "refresh_metadata": "ØĒØ­Ø¯ŲŠØĢ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„ŲˆØĩŲŲŠØŠ", "refresh_thumbnails": "ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ", - "refreshed": "ØĒŲ… Ø§Ų„ØĒØ­Ø¯ŲŠØĢ", + "refreshed": "اؚاد؊ ØĒØ­Ų…ŲŠŲ„", "refreshes_every_file": "ØĨؚاد؊ Ų‚ØąØ§ØĄØŠ ŲƒØ§ŲØŠ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ ŲˆØ§Ų„ØŦØ¯ŲŠØ¯ØŠ", "refreshing_encoded_video": "ØŦØ§ØąŲ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ØąŲ…Ø˛", "refreshing_faces": "ØŦØ§ØąŲŠ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ŲˆØŦŲˆŲ‡", "refreshing_metadata": "ØŦØ§ØąŲ ØĒØ­Ø¯ŲŠØĢ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„ŲˆØĩŲŲŠØŠ", "regenerating_thumbnails": "ØŦØ§ØąŲ ØĒØŦØ¯ŲŠØ¯ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ", + "remote": "Ø¨ØšŲŠØ¯", + "remote_assets": "Ø§Ų„ØŖŲØĩŲˆŲ„ Ø§Ų„Ø¨ØšŲŠØ¯ØŠ", + "remote_media_summary": "Ų…Ų„ØŽØĩ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ø¨ØšŲŠØ¯ØŠ", "remove": "ØĨØ˛Ø§Ų„ØŠ", "remove_assets_album_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØŠ {count, plural, one {# Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {# Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} Ų…Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ… ؟", "remove_assets_shared_link_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ ØŖŲ†Ųƒ ØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØŠ {count, plural, one {# Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {# Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ}} Ų…Ų† ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų‡Ø°Ø§ØŸ", @@ -1291,13 +1628,18 @@ "remove_custom_date_range": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų†ØˇØ§Ų‚ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„Ų…ØŽØĩØĩ", "remove_deleted_assets": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ØēŲŠØą Ų…ØĒØĩŲ„ØŠ", "remove_from_album": "ØĨØ˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", + "remove_from_album_action_prompt": "ØĒŲ… Ø§Ø˛Ø§Ų„ØŠ {count} Ų…Ų† Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…", "remove_from_favorites": "ØĨØ˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„Ų…ŲØļŲ„ØŠ", + "remove_from_lock_folder_action_prompt": "{count} ØŖŲˆŲŠŲ„ Ų…Ų† Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", + "remove_from_locked_folder": "Ø§Ø˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„", + "remove_from_locked_folder_confirmation": "Ų‡Ų„ Ø§Ų†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† Ø§Ø˛Ø§Ų„ØŠ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØą ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ų…Ų† Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ų…Ų‚ŲŲ„ØŸ ØŗŲŠŲƒŲˆŲ†ŲˆŲ† Ų…ØąØĻŲŠŲŠŲ† ؁؊ Ø§Ų„Ų…ŲƒØĒب؊ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ.", "remove_from_shared_link": "ØĨØ˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "remove_memory": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ø°Ø§ŲƒØąØŠ", "remove_photo_from_memory": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„ØĩŲˆØąØŠ Ų…Ų† Ų‡Ø°Ų‡ Ø§Ų„Ø°ŲƒØąŲ‰", + "remove_tag": "Ø§Ø˛Ø§Ų„ØŠ ØšŲ„Ø§Ų…ØŠ", "remove_url": "ØĨØ˛Ø§Ų„ØŠ ØšŲ†ŲˆØ§Ų† URL", "remove_user": "ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", - "removed_api_key": "ØĒŲ… ØĨØ˛Ø§Ų„ØŠ ؅؁ØĒاح API: {name}", + "removed_api_key": "ØĒŲ… ØĨØ˛Ø§ØŠ ؅؁ØĒاح API: â€Ēâ€Ģ{name}", "removed_from_archive": "ØĒŲ…ØĒ ØĨØ˛Ø§Ų„ØĒŲ‡Ø§ Ų…Ų† Ø§Ų„ØŖØąØ´ŲŠŲ", "removed_from_favorites": "ØĒŲ…ØĒ Ø§Ų„ØĨØ˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„Ų…ŲØļŲ„ØŠ", "removed_from_favorites_count": "{count, plural, other {ØŖŲØ˛ŲŠŲ„ØĒ #}} Ų…Ų† Ø§Ų„ØĒ؁ØļŲŠŲ„Ø§ØĒ", @@ -1315,20 +1657,32 @@ "reset": "ØĨؚاد؊ ØļØ¨Øˇ", "reset_password": "ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "reset_people_visibility": "ØĨؚاد؊ ØļØ¨Øˇ Ø¸Ų‡ŲˆØą Ø§Ų„ØŖØ´ØŽØ§Øĩ", + "reset_pin_code": "اؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ PIN", + "reset_pin_code_description": "اذا Ų†ØŗŲŠØĒ ØąŲ…Ø˛ Ø§Ų„PIN Ø§Ų„ØŽØ§Øĩ Ø¨ŲƒØŒ Ø¨Ø§Ų…ŲƒØ§Ų†Ųƒ Ø§Ų„ØĒŲˆØ§ØĩŲ„ Ų…Øš Ų…Ø¯ŲŠØą Ø§Ų„ØŽØ§Ø¯Ų… Ų„Ø¯ŲŠŲƒ Ų„Ø§ØšØ§Ø¯ØŠ ØĒØšŲŠŲŠŲ†Ų‡", + "reset_pin_code_success": "ØĒŲ… اؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ Ø§Ų„PIN Ø¨Ų†ØŦاح", + "reset_pin_code_with_password": "ŲŠŲ…ŲƒŲ†Ųƒ داØĻŲ…Ø§ اؚاد؊ ØĒØšŲŠŲŠŲ† ØąŲ…Ø˛ Ø§Ų„PIN Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ØšŲ† ØˇØąŲŠŲ‚ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", + "reset_sqlite": "ØĨؚاد؊ ØĒØšŲŠŲŠŲ† Ų‚Ø§ØšØ¯ØŠ Ø¨ŲŠØ§Ų†Ø§ØĒ SQLite", + "reset_sqlite_confirmation": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØąØēبØĒ؃ ؁؊ ØĨؚاد؊ ØļØ¨Øˇ Ų‚Ø§ØšØ¯ØŠ Ø¨ŲŠØ§Ų†Ø§ØĒ SQLite؟ ØŗØĒØ­ØĒاØŦ ØĨŲ„Ų‰ ØĒØŗØŦŲŠŲ„ Ø§Ų„ØŽØąŲˆØŦ ØĢŲ… ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ų…ØąØŠ ØŖØŽØąŲ‰ Ų„ØĨؚاد؊ Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", + "reset_sqlite_success": "ØĒŲ… ØĨؚاد؊ ØĒØšŲŠŲŠŲ† Ų‚Ø§ØšØ¯ØŠ Ø¨ŲŠØ§Ų†Ø§ØĒ SQLite Ø¨Ų†ØŦاح", "reset_to_default": "ØĨؚاد؊ Ø§Ų„ØĒØšŲŠŲŠŲ† ØĨŲ„Ų‰ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊", "resolve_duplicates": "Ų…ØšØ§Ų„ØŦØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ", "resolved_all_duplicates": "ØĒŲ… Ø­Ų„ ØŦŲ…ŲŠØš Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "restore": "Ø§Ų„Ø§ØŗØĒØšØ§Ø¯Ų‡ Ų…Ų† ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "restore_all": "Ø§ØŗØĒؚاد؊ Ø§Ų„ŲƒŲ„", + "restore_trash_action_prompt": "ØĒŲ… Ø§ØŗØĒؚاد؊ {count} Ų…Ų† Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "restore_user": "Ø§ØŗØĒؚاد؊ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "restored_asset": "Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…ØŗØĒؚاد؊", "resume": "Ø§ØŗØĒØĻŲ†Ø§Ų", + "resume_paused_jobs": "Ø§ØŗØĒŲƒŲ…Ø§Ų„ {count, plural, one {# ŲˆØ¸ŲŠŲØŠ Ų…ØšŲ„Ų‚ØŠ} other {# ŲˆØ¸Ø§ØĻ؁ Ų…ØšŲ„Ų‚ØŠ}}", "retry_upload": "ØŖØšØ¯ Ų…Ø­Ø§ŲˆŲ„ØŠ Ø§Ų„ØąŲØš", "review_duplicates": "Ų…ØąØ§ØŦؚ؊ Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", + "review_large_files": "Ų…ØąØ§ØŦؚ؊ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„ŲƒØ¨ŲŠØąØŠ", "role": "Ø§Ų„Ø¯ŲˆØą", "role_editor": "Ø§Ų„Ų…Ø­ØąØą", "role_viewer": "Ø§Ų„ØšØ§ØąØļ", + "running": "Ų‚ŲŠØ¯ Ø§Ų„ØĒØ´ØēŲŠŲ„", "save": "Ø­ŲØ¸", + "save_to_gallery": "Ø­ŲØ¸ Ø§Ų„Ų‰ Ø§Ų„Ų…ØšØąØļ", "saved_api_key": "ØĒŲ… Ø­ŲØ¸ ؅؁ØĒاح Ø§Ų„Ų€ API", "saved_profile": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„Ų…Ų„Ų", "saved_settings": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„ØĨؚداداØĒ", @@ -1349,19 +1703,33 @@ "search_camera_model": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ų…ŲˆØ¯ŲŠŲ„ Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§...", "search_city": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ø§Ų„Ų…Ø¯ŲŠŲ†ØŠ...", "search_country": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ø§Ų„Ø¯ŲˆŲ„ØŠ...", - "search_filter_apply": "ا؎ØĒØ§Øą Ø§Ų„ŲŲ„ØĒØą ", + "search_filter_apply": "ا؎ØĒØ§Øą Ø§Ų„ŲŲ„ØĒØą", + "search_filter_camera_title": "ا؎ØĒØą Ų†ŲˆØš Ø§Ų„ŲƒØ§Ų…ŲŠØąØ§", + "search_filter_date": "ØĒØ§ØąŲŠØŽ", + "search_filter_date_interval": "{start} Ø§Ų„Ų‰ {end}", + "search_filter_date_title": "حدد Ų†ØˇØ§Ų‚ Ø§Ų„ØĒØ§ØąŲŠØŽ", "search_filter_display_option_not_in_album": "Ų„ŲŠØŗ ؁؊ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", + "search_filter_display_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØšØąØļ", + "search_filter_filename": "بحØĢ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø§ØŗŲ…", + "search_filter_location": "Ø§Ų„Ų…ŲˆŲ‚Øš", + "search_filter_location_title": "ا؎ØĒØą Ø§Ų„Ų…ŲˆŲ‚Øš", + "search_filter_media_type": "Ų†ŲˆØš Ø§Ų„ŲˆØŗØ§ØĻØˇ", + "search_filter_media_type_title": "ا؎ØĒØą Ų†ŲˆØš Ø§Ų„ŲˆØŗØ§ØĻØˇ", + "search_filter_people_title": "ا؎ØĒØą Ø§Ų„Ø§Ø´ØŽØ§Øĩ", "search_for": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ†", "search_for_existing_person": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ø´ØŽØĩ Ų…ŲˆØŦŲˆØ¯", + "search_no_more_result": "Ų„Ø§ ØĒ؈ØŦد Ų†ØĒاØĻØŦ اØļØ§ŲŲŠØŠ", "search_no_people": "Ų„Ø§ ؊؈ØŦد ØŖØ´ØŽØ§Øĩ", "search_no_people_named": "Ų„Ø§ ؊؈ØŦد ØŖØ´ØŽØ§Øĩ Ø¨Ø§Ų„Ø§ØŗŲ… \"{name}\"", + "search_no_result": "Ų„Ø§ ØĒ؈ØŦد Ų†ØĒاØĻØŦ، Ø­Ø§ŲˆŲ„ Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ØĩØˇŲ„Ø­ بحØĢ Ų…ØŽØĒ؄؁ Ø§Ųˆ ØĒØąŲƒŲŠØ¨ØŠ", "search_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ", "search_page_categories": "؁ØĻاØĒ", "search_page_motion_photos": "Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒŲ‡", "search_page_no_objects": "Ų„Ø§ ØĒ؈ØŦد Ų…ØšŲ„ŲˆŲ…Ø§ØĒ ØšŲ† ØŖØ´ŲŠØ§ØĄ Ų…ØĒاح؊", "search_page_no_places": "Ų„Ø§ ØĒ؈ØŦد Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ų…ØĒŲˆŲØąØŠ Ų„Ų„ØŖŲ…Ø§ŲƒŲ†", "search_page_screenshots": "Ų„Ų‚ØˇØ§ØĒ Ø§Ų„Ø´Ø§Ø´ØŠ", - "search_page_selfies": " ØĩŲˆØą ذاØĒŲŠŲ‡", + "search_page_search_photos_videos": "ابحØĢ ØšŲ† ØĩŲˆØąŲƒ Ø§Ųˆ ŲØ¯ŲŠŲˆŲ‡Ø§ØĒ؃", + "search_page_selfies": "ØĩŲˆØą ذاØĒŲŠŲ‡", "search_page_things": "ØŖØ´ŲŠØ§ØĄ", "search_page_view_all_button": "ØšØąØļ Ø§Ų„ŲƒŲ„", "search_page_your_activity": "Ų†Ø´Ø§ØˇŲƒ", @@ -1372,7 +1740,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": "Ų…: Ø§Ų„Ø¨Ø­ØĢ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "search_tags": "Ø§Ų„Ø¨Ø­ØĢ ØšŲ† Ø§Ų„ØšŲ„Ø§Ų…Ø§ØĒ...", "search_timezone": "Ø§Ų„Ø¨Ø­ØĢ Ø­ØŗØ¨ Ø§Ų„Ų…Ų†ØˇŲ‚ØŠ Ø§Ų„Ø˛Ų…Ų†ŲŠØŠ...", @@ -1385,6 +1753,7 @@ "select_album_cover": "ØĒØ­Ø¯ŲŠØ¯ ØēŲ„Ø§Ų Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "select_all": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„", "select_all_duplicates": "ØĒØ­Ø¯ŲŠØ¯ ØŦŲ…ŲŠØš Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ", + "select_all_in": "ا؎ØĒØą Ø§Ų„ŲƒŲ„ ؁؊ {group}", "select_avatar_color": "ØĒØ­Ø¯ŲŠØ¯ Ų„ŲˆŲ† Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ", "select_face": "ØĒØ­Ø¯ŲŠØ¯ ؈ØŦŲ‡", "select_featured_photo": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ų…Ų…ŲŠØ˛ØŠ", @@ -1392,17 +1761,21 @@ "select_keep_all": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ØŖØ­ØĒŲØ§Ø¸ Ø¨Ø§Ų„ŲƒŲ„", "select_library_owner": "ØĒØ­Ø¯ŲŠØ¯ Ų…Ø§Ų„ŲŲƒ Ø§Ų„Ų…ŲƒØĒب؊", "select_new_face": "ØĒØ­Ø¯ŲŠØ¯ ؈ØŦŲ‡ ØŦØ¯ŲŠØ¯", + "select_person_to_tag": "ا؎ØĒØą Ø´ØŽØĩ Ų„ŲˆØļØš ØšŲ„Ø§Ų…ØŠ", "select_photos": "ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ØĩŲˆØą", "select_trash_all": "ØĒØ­Ø¯ŲŠØ¯ Ø­Ø°Ų Ø§Ų„ŲƒŲ„Ų", "select_user_for_sharing_page_err_album": "ŲØ´Ų„ ؁؊ ØĨŲ†Ø´Ø§ØĄ ØŖŲ„Ø¨ŲˆŲ…", "selected": "Ø§Ų„ØĒØ­Ø¯ŲŠØ¯", "selected_count": "{count, plural, other {# Ų…Ø­Ø¯Ø¯ØŠ }}", + "selected_gps_coordinates": "ØĨحداØĢŲŠØ§ØĒ Ų†Ø¸Ø§Ų… ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„Ų…ŲˆØ§Ų‚Øš Ø§Ų„Ų…ØŽØĒØ§ØąØŠ", "send_message": "‏ØĨØąØŗØ§Ų„ ØąØŗØ§Ų„ØŠ", "send_welcome_email": "ØĨØąØŗØ§Ų„ Ø¨ØąŲŠØ¯Ų‹Ø§ ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠŲ‹Ø§ ØĒØąØ­ŲŠØ¨ŲŠŲ‹Ø§", + "server_endpoint": "Ų†Ų‚ØˇØŠ Ų†Ų‡Ø§ŲŠØŠ Ø§Ų„ØŽØ§Ø¯Ų…", "server_info_box_app_version": "Ų†ØŗØŽØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "server_info_box_server_url": "ØšŲ†ŲˆØ§Ų† URL Ø§Ų„ØŽØ§Ø¯Ų…", "server_offline": "Ø§Ų„ØŽØ§Ø¯Ų… ØēŲŠØą Ų…ØĒØĩŲ„", "server_online": "Ø§Ų„ØŽØ§Ø¯Ų… Ų…ØĒØĩŲ„", + "server_privacy": "ØŽØĩ؈ØĩŲŠØŠ Ø§Ų„ØŽØ§Ø¯Ų…", "server_stats": "ØĨØ­ØĩاØĻŲŠØ§ØĒ Ø§Ų„ØŽØ§Ø¯Ų…", "server_version": "ØĨØĩØ¯Ø§Øą Ø§Ų„ØŽØ§Ø¯Ų…", "set": "‏ØĒØ­Ø¯ŲŠØ¯", @@ -1412,6 +1785,7 @@ "set_date_of_birth": "ØĒØ­Ø¯ŲŠØ¯ ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯", "set_profile_picture": "ØĒØ­Ø¯ŲŠØ¯ ØĩŲˆØąØŠ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊", "set_slideshow_to_fullscreen": "ØĒØ­Ø¯ŲŠØ¯ ØšØąØļ Ø§Ų„Ø´ØąØ§ØĻØ­ ØšŲ„Ų‰ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ", + "set_stack_primary_asset": "ØĒØšŲŠŲŠŲ† ŲƒØŖØĩŲ„ Ø§ØŗØ§ØŗŲŠ", "setting_image_viewer_help": "ŲŠŲ‚ŲˆŲ… ØšØ§ØąØļ Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ بØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„Ų…ØĩØēØąØŠ Ø§Ų„ØĩØēŲŠØąØŠ ØŖŲˆŲ„Ø§Ų‹ ، ØĢŲ… ŲŠŲ‚ŲˆŲ… بØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ Ų…ØĒŲˆØŗØˇØŠ Ø§Ų„Ø­ØŦŲ… (ØĨذا ØĒŲ… ØĒŲ…ŲƒŲŠŲ†Ų‡Ø§) ، ŲˆŲŠŲ‚ŲˆŲ… ØŖØŽŲŠØąŲ‹Ø§ بØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲ„ (ØĨذا ØĒŲ… ØĒŲ…ŲƒŲŠŲ†Ų‡).", "setting_image_viewer_original_subtitle": "ØĒŲ…ŲƒŲŠŲ† ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ŲƒØ§Ų…Ų„ØŠ Ø§Ų„Ø¯Ų‚ØŠ Ø§Ų„ØŖØĩŲ„ŲŠØŠ (ŲƒØ¨ŲŠØąØŠ!).ØĒØšØˇŲŠŲ„ Ų„ØĒŲ‚Ų„ŲŠŲ„ Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ (ŲƒŲ„ Ų…Ų† Ø§Ų„Ø´Ø¨ŲƒØŠ ŲˆØšŲ„Ų‰ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ Ų„Ų„ØŦŲ‡Ø§Ø˛).", "setting_image_viewer_original_title": "ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØŖØĩŲ„ŲŠØŠ", @@ -1419,21 +1793,31 @@ "setting_image_viewer_preview_title": "ØĒØ­Ų…ŲŠŲ„ ØĩŲˆØąØŠ Ų…ØšØ§ŲŠŲ†ØŠ", "setting_image_viewer_title": "Ø§Ų„ØĩŲˆØą", "setting_languages_apply": "ØĒØēŲŠŲŠØą Ø§Ų„ØĨؚداداØĒ", + "setting_languages_subtitle": "ØĒØēŲŠŲŠØą Ų„ØēØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", + "setting_notifications_notify_failures_grace_period": "Ø§Ų„ØĒŲ†Ø¨ŲŠŲ‡ Ø¨ŲØ´Ų„ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ: {duration}", + "setting_notifications_notify_hours": "{count} ØŗØ§ØšØ§ØĒ", "setting_notifications_notify_immediately": "؁؊ Ø§Ų„Ø­Ø§Ų„", + "setting_notifications_notify_minutes": "{count} Ø¯Ų‚Ø§ØĻŲ‚", "setting_notifications_notify_never": "ØŖØ¨Ø¯Ø§Ų‹", + "setting_notifications_notify_seconds": "{count} ØĢŲˆØ§Ų†ŲŠ", "setting_notifications_single_progress_subtitle": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒ؁ØĩŲŠŲ„ŲŠØŠ ØĒØ­Ų…ŲŠŲ„ Ų„ŲƒŲ„ ØŖØĩŲ„", "setting_notifications_single_progress_title": "ØĨØ¸Ų‡Ø§Øą ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ", "setting_notifications_subtitle": "اØļØ¨Øˇ ØĒ؁ØļŲŠŲ„Ø§ØĒ Ø§Ų„ØĨØŽØˇØ§Øą", "setting_notifications_total_progress_subtitle": "Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØšØ§Ų… (ØĒŲ… Ø§Ų„Ų‚ŲŠØ§Ų… Ø¨Ų‡/ØĨØŦŲ…Ø§Ų„ŲŠ Ø§Ų„ØŖØĩŲˆŲ„)", "setting_notifications_total_progress_title": "ØĨØ¸Ų‡Ø§Øą Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ Ø§Ų„ØĒŲ‚Ø¯Ų… Ø§Ų„Ų…Ø­ØąØ˛", "setting_video_viewer_looping_title": "ØĒŲƒØąØ§Øą Ų…Ų‚ØˇØš ŲŲŠØ¯ŲŠŲˆ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§", + "setting_video_viewer_original_video_subtitle": "ØšŲ†Ø¯ بØĢ ŲŲŠØ¯ŲŠŲˆ Ų…Ų† Ø§Ų„ØŽØ§Ø¯Ų…ØŒ Ø´ØēŲ‘Ų„ Ø§Ų„Ų†ØŗØŽØŠ Ø§Ų„ØŖØĩŲ„ŲŠØŠ Ø­ØĒŲ‰ Ų…Øš ØĒŲˆŲØą ØĒØąŲ…ŲŠØ˛ Ø¨Ø¯ŲŠŲ„. Ų‚Ø¯ ŲŠØ¤Ø¯ŲŠ Ø°Ų„Ųƒ ØĨŲ„Ų‰ ØĒŲ‚ØˇŲŠØš اØĢŲ†Ø§ØĄ Ø§Ų„ØšØąØļ . ØĒŲØ´ØēŲ‘Ų„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ų…ØĒŲˆŲØąØŠ Ų…Ø­Ų„ŲŠŲ‹Ø§ بØŦŲˆØ¯ØŠ ØŖØĩŲ„ŲŠØŠ بØēØļ Ø§Ų„Ų†Ø¸Øą ØšŲ† Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد.", + "setting_video_viewer_original_video_title": "اØŦØ¨Ø§Øą ØšØąØļ Ø§Ų„ŲØ¯ŲŠŲˆ Ø§Ų„Ø§ØĩŲ„ŲŠ", "settings": "Ø§Ų„ØĨؚداداØĒ", "settings_require_restart": "ŲŠØąØŦŲ‰ ØĨؚاد؊ ØĒØ´ØēŲŠŲ„ Ų„ØĒØˇØ¨ŲŠŲ‚ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد", "settings_saved": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„ØĨؚداداØĒ", - "setup_pin_code": "ØĒØ­Ø¯ŲŠØ¯ ØąŲ‚Ų… ØŗØąŲŠ", + "setup_pin_code": "ØĒØ­Ø¯ŲŠØ¯ ØąŲ…Ø˛ PIN", "share": "Ų…Ø´Ø§ØąŲƒØŠ", + "share_action_prompt": "ØĒŲ… Ų…Ø´Ø§ØąŲƒØŠ {count} ØŖØĩŲ„ (؅؄؁)", "share_add_photos": "ØĨØļØ§ŲØŠ Ø§Ų„ØĩŲˆØą", + "share_assets_selected": "ا؎ØĒŲŠØ§Øą {count}", "share_dialog_preparing": "ØĒØ­ØļŲŠØą...", + "share_link": "Ų…Ø´Ø§ØąŲƒØŠ ØąØ§Ø¨Øˇ", "shared": "Ų…ŲØ´ØĒŲŽØąŲƒ", "shared_album_activities_input_disable": "Ø§Ų„ØĒØšŲ„ŲŠŲ‚ Ų…ØšØˇŲ„", "shared_album_activity_remove_content": "Ų‡Ų„ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų‡Ø°Ø§ Ø§Ų„Ų†Ø´Ø§ØˇØŸ", @@ -1446,22 +1830,42 @@ "shared_by_user": "ØĒŲ…ØĒ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ø¨ŲˆØ§ØŗØˇØŠ {user}", "shared_by_you": "ØĒŲ…ØĒ Ų…Ø´Ø§ØąŲƒØĒŲ‡ Ų…Ų† Ų‚ŲØ¨Ų„Ųƒ", "shared_from_partner": "ØĩŲˆØą Ų…Ų† {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} ØĒŲ… ØąŲØš", "shared_link_app_bar_title": "ØąŲˆØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒØŠ", "shared_link_clipboard_copied_massage": "Ų†ØŗØŽ ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ", + "shared_link_clipboard_text": "ØąØ§Ø¨Øˇ: {link}\nŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą: {password}", "shared_link_create_error": "ØŽØˇØŖ ØŖØĢŲ†Ø§ØĄ ØĨŲ†Ø´Ø§ØĄ ØąØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒ", + "shared_link_custom_url_description": "Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØšŲ†ŲˆØ§Ų† URL Ų…ØŽØĩØĩ", "shared_link_edit_description_hint": "ØŖØ¯ØŽŲ„ ؈Øĩ؁ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", "shared_link_edit_expire_after_option_day": "ŲŠŲˆŲ… 1", + "shared_link_edit_expire_after_option_days": "{count} Ø§ŲŠØ§Ų…", "shared_link_edit_expire_after_option_hour": "1 ØŗØ§ØšØŠ", + "shared_link_edit_expire_after_option_hours": "{count} ØŗØ§ØšØ§ØĒ", "shared_link_edit_expire_after_option_minute": "1 Ø¯Ų‚ŲŠŲ‚ØŠ", + "shared_link_edit_expire_after_option_minutes": "{count} Ø¯Ų‚Ø§ØĻŲ‚", + "shared_link_edit_expire_after_option_months": "{count} Ø§Ø´Ų‡Øą", + "shared_link_edit_expire_after_option_year": "{count} ØŗŲ†ØŠ", "shared_link_edit_password_hint": "ØŖØ¯ØŽŲ„ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ", "shared_link_edit_submit_button": "ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØąØ§Ø¨Øˇ", "shared_link_error_server_url_fetch": "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŦŲ„Ø¨ ØšŲ†ŲˆØ§Ų† Ø§Ų„ØŽØ§Ø¯Ų…", + "shared_link_expires_day": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} ŲŠŲˆŲ…", + "shared_link_expires_days": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} Ø§ŲŠØ§Ų…", + "shared_link_expires_hour": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØŠ ؁؊ {count} ØŗØ§ØšØŠ", + "shared_link_expires_hours": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} ØŗØ§ØšØ§ØĒ", + "shared_link_expires_minute": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} Ø¯Ų‚ŲŠŲ‚ØŠ", + "shared_link_expires_minutes": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} Ø¯Ų‚Ø§ØĻŲ‚", "shared_link_expires_never": "ØĒŲ†ØĒŲ‡ŲŠ ∞", + "shared_link_expires_second": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} ØĢØ§Ų†ŲŠØŠ", + "shared_link_expires_seconds": "ØĒŲ†ØĒŲ‡ŲŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡ ؁؊ {count} ØĢŲˆØ§Ų†ŲŠ", + "shared_link_individual_shared": "Ų…Ø´Ø§ØąŲƒØŠ ŲØąØ¯ŲŠØŠ", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØąŲˆØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŠ", "shared_link_options": "ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", + "shared_link_password_description": "ØˇŲ„Ø¨ ŲƒŲ„Ų…ØŠ Ų…ØąŲˆØą Ų„Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒ", "shared_links": "ØąŲˆØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒØŠ", "shared_links_description": "؈Øĩ؁ Ø§Ų„ØąŲˆØ§Ø¨Øˇ Ø§Ų„Ų…Ø´ØĒØąŲƒØŠ", "shared_photos_and_videos_count": "{assetCount, plural, other {# Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲØ´Ø§ØąŲŽŲƒØŠ.}}", + "shared_with_me": "ØĒŲ…ØĒ Ų…Ø´Ø§ØąŲƒØĒŲ‡Ø§ Ų…ØšŲŠ", "shared_with_partner": "ØĒŲ…ØĒ Ø§Ų„Ų…Ø´Ø§ØąŲƒØŠ Ų…Øš {partner}", "sharing": "Ų…Ø´Ø§ØąŲƒØŠ", "sharing_enter_password": "Ø§Ų„ØąØŦØ§ØĄ ØĨØ¯ØŽØ§Ų„ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ų„ØšØąØļ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲØ­ØŠ.", @@ -1492,6 +1896,7 @@ "show_slideshow_transition": "ØĨØ¸Ų‡Ø§Øą Ø§Ų†ØĒŲ‚Ø§Ų„ ØšØąØļ Ø§Ų„Ø´ØąØ§ØĻØ­", "show_supporter_badge": "Ø´Ø§ØąØŠ Ø§Ų„Ų…Ø¤ŲŠØ¯", "show_supporter_badge_description": "ØĨØ¸Ų‡Ø§Øą Ø´Ø§ØąØŠ Ø§Ų„Ų…Ø¤ŲŠØ¯", + "show_text_search_menu": "ØšØąØļ Ų‚Ø§ØĻŲ…ØŠ ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ ؁؊ Ø§Ų„Ų†Øĩ", "shuffle": "ØŽŲ„Øˇ", "sidebar": "Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„ØŦØ§Ų†Ø¨ŲŠ", "sidebar_display_description": "ØšØąØļ ØąØ§Ø¨Øˇ Ų„Ų„ØšØąØļ ؁؊ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„ØŦØ§Ų†Ø¨ŲŠ", @@ -1507,12 +1912,14 @@ "sort_created": "ØĒØ§ØąŲŠØŽ Ø§Ų„ØĨŲ†Ø´Ø§ØĄ", "sort_items": "ؚدد Ø§Ų„ØšŲ†Ø§ØĩØą", "sort_modified": "ØĒŲ… ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĒØ§ØąŲŠØŽ", + "sort_newest": "احدØĢ ØĩŲˆØąØŠ", "sort_oldest": "ØŖŲ‚Ø¯Ų… ØĩŲˆØąØŠ", "sort_people_by_similarity": "ØąØĒب Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø­ØŗØ¨ Ø§Ų„ØĒØ´Ø§Ø¨Ų‡", "sort_recent": "ØŖØ­Ø¯ØĢ ØĩŲˆØąØŠ", "sort_title": "Ø§Ų„ØšŲ†ŲˆØ§Ų†", "source": "Ø§Ų„Ų…ØĩØ¯Øą", "stack": "ØĒØŦŲ…ŲŠØš", + "stack_action_prompt": "{count} Ų…ŲƒØ¯ØŗØŠ", "stack_duplicates": "ØĒØŦŲ…ŲŠØš Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ", "stack_select_one_photo": "حدد ØĩŲˆØąØŠ ØąØĻŲŠØŗŲŠØŠ ŲˆØ§Ø­Ø¯ØŠ Ų„Ų„Ų…ØŦŲ…ŲˆØšØŠ", "stack_selected_photos": "ŲƒØ¯Øŗ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ", @@ -1520,16 +1927,20 @@ "stacktrace": "ØĒØĒŲ‘Ø¨ŲØš Ø§Ų„ØĒŲƒØ¯ŲŠØŗ", "start": "Ø§Ø¨Ø¯ØŖ", "start_date": "ØĒØ§ØąŲŠØŽ Ø§Ų„Ø¨Ø¯ØĄ", + "start_date_before_end_date": "؊ØŦب ØŖŲ† ŲŠŲƒŲˆŲ† ØĒØ§ØąŲŠØŽ Ø¨Ø¯ØĄ Ø§Ų„ŲØĒØąØŠ Ų‚Ø¨Ų„ ØĒØ§ØąŲŠØŽ Ų†Ų‡Ø§ŲŠØĒŲ‡Ø§", "state": "Ø§Ų„ŲˆŲ„Ø§ŲŠØŠ", "status": "Ø§Ų„Ø­Ø§Ų„ØŠ", + "stop_casting": "Ø§ŲŠŲ‚Ø§Ų Ø§Ų„Ø¨ØĢ", "stop_motion_photo": "ØĨŲŠŲ‚Ø§Ų Ø­ØąŲƒØŠ Ø§Ų„ØĩŲˆØąØŠ", "stop_photo_sharing": "ØĒŲˆŲ‚Ų ØšŲ† Ų…Ø´Ø§ØąŲƒØŠ ØĩŲˆØąŲƒØŸ", "stop_photo_sharing_description": "Ų„Ų† ؊ØĒŲ…ŲƒŲ† {partner} Ų…Ų† Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ ØĩŲˆØąŲƒ بؚد Ø§Ų„ØĸŲ†.", "stop_sharing_photos_with_user": "ØĒŲˆŲ‚Ų ØšŲ† Ų…Ø´Ø§ØąŲƒØŠ ØĩŲˆØąŲƒ Ų…Øš Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "storage": "Ų…ØŗØ§Ø­ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", - "storage_label": "ØĒØŗŲ…ŲŠØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", + "storage_label": "ØŗŲ…ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†", + "storage_quota": "Ø­ØĩØŠ Ø§Ų„ØŽØ˛Ų†", "storage_usage": "{used} Ų…Ų† {available} Ų…ŲØŗØĒØŽŲ’Ø¯Ų…", "submit": "ØĨØąØŗØ§Ų„", + "success": "ØĒŲ… Ø¨Ų†ØŦاح", "suggestions": "Ø§Ų‚ØĒØąØ§Ø­Ø§ØĒ", "sunrise_on_the_beach": "Ø´ØąŲˆŲ‚ Ø§Ų„Ø´Ų…Øŗ ØšŲ„Ų‰ Ø§Ų„Ø´Ø§ØˇØĻ", "support": "Ø§Ų„Ø¯ØšŲ…", @@ -1537,6 +1948,13 @@ "support_third_party_description": "ØĒŲ… Ø­Ø˛Ų… ØĒØĢØ¨ŲŠØĒ immich Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ Ø¨ŲˆØ§ØŗØˇØŠ ØŦŲ‡ØŠ ØŽØ§ØąØŦŲŠØŠ. Ų‚Ø¯ ØĒŲƒŲˆŲ† Ø§Ų„Ų…Ø´ŲƒŲ„Ø§ØĒ Ø§Ų„ØĒ؊ ØĒŲˆØ§ØŦŲ‡Ų‡Ø§ Ų†Ø§ØŦŲ…ØŠ ØšŲ† Ų‡Ø°Ų‡ Ø§Ų„Ø­Ø˛Ų…ØŠØŒ Ų„Ø°Ø§ ŲŠØąØŦŲ‰ ØˇØąØ­ Ø§Ų„Ų…Ø´ŲƒŲ„Ø§ØĒ Ų…ØšŲ‡Ų… ؁؊ Ø§Ų„Ų…Ų‚Ø§Ų… Ø§Ų„ØŖŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØąŲˆØ§Ø¨Øˇ ØŖØ¯Ų†Ø§Ų‡.", "swap_merge_direction": "ØĒØ¨Ø¯ŲŠŲ„ اØĒØŦØ§Ų‡ Ø§Ų„Ø¯Ų…ØŦ", "sync": "Ų…Ø˛Ø§Ų…Ų†ØŠ", + "sync_albums": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ", + "sync_albums_manual_subtitle": "Ų…Ø˛Ø§Ų…Ų†ØŠ ØŦŲ…ŲŠØš Ø§Ų„ŲØ¯ŲŠŲˆŲ‡Ø§ØĒ ŲˆØ§Ų„ØĩŲˆØą Ø§Ų„Ų…ØąŲŲˆØšØŠ Ø§Ų„Ų‰ Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„ØŽØ˛Ų† Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ø§Ų„Ų…ØŽØĒØ§ØąØŠ", + "sync_local": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ų…Ø­Ų„ŲŠØŠ", + "sync_remote": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ø¨ØšŲŠØ¯ØŠ", + "sync_status": "Ø­Ø§Ų„ØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ØĒØ˛Ø§Ų…Ų†", + "sync_status_subtitle": "ØšØąØļ ؈ØĨØ¯Ø§ØąØŠ Ų†Ø¸Ø§Ų… Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ØĒØ˛Ø§Ų…Ų†", + "sync_upload_album_setting_subtitle": "Ø§Ų†Ø´ØĻ ؈ Ø§ØąŲØš ØĩŲˆØąŲƒ ؈ ŲØ¯ŲŠŲˆŲ‡Ø§ØĒ؃ Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…Ø§ØĒ Ø§Ų„Ų…ØŽØĒØ§ØąØŠ ؁؊ Immich", "tag": "Ø§Ų„ØšŲ„Ø§Ų…ØŠ", "tag_assets": "ØŖØĩŲˆŲ„ Ø§Ų„ØšŲ„Ø§Ų…ØŠ", "tag_created": "ØĒŲ… ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØšŲ„Ø§Ų…ØŠ: {tag}", @@ -1546,13 +1964,20 @@ "tag_updated": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØšŲ„Ø§Ų…ØŠ: {tag}", "tagged_assets": "ØĒŲ… ؈ØļØš ØšŲ„Ø§Ų…ØŠ {count, plural, one {# asset} other {# assets}}", "tags": "Ø§Ų„ØšŲ„Ø§Ų…Ø§ØĒ", + "tap_to_run_job": "Ø§Ų†Ų‚Øą Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…Ų‡Ų…ØŠ", "template": "Ø§Ų„Ų†Ų…ŲˆØ°ØŦ", "theme": "Ų…Ø¸Ų‡Øą", "theme_selection": "ا؎ØĒŲŠØ§Øą Ø§Ų„ØŗŲ…ØŠ", "theme_selection_description": "Ų‚Ų… بØĒØšŲŠŲŠŲ† Ø§Ų„ØŗŲ…ØŠ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ØšŲ„Ų‰ Ø§Ų„Ų„ŲˆŲ† Ø§Ų„ŲØ§ØĒØ­ ØŖŲˆ Ø§Ų„Ø¯Ø§ŲƒŲ† Ø¨Ų†Ø§ØĄŲ‹ ØšŲ„Ų‰ ØĒ؁ØļŲŠŲ„Ø§ØĒ Ų†Ø¸Ø§Ų… Ø§Ų„Ų…ØĒØĩŲØ­ Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", "theme_setting_asset_list_storage_indicator_title": "ØšØąØļ Ų…Ø¤Ø´Øą Ø§Ų„ØĒØŽØ˛ŲŠŲ† ØšŲ„Ų‰ Ø¨Ų„Ø§Øˇ Ø§Ų„ØŖØĩŲˆŲ„", + "theme_setting_asset_list_tiles_per_row_title": "ؚدد Ø§Ų„Ø§ØĩŲˆŲ„ ؁؊ ŲƒŲ„ Øĩ؁({count})", + "theme_setting_colorful_interface_subtitle": "ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų„ŲˆŲ† Ø§Ų„ØŖØŗØ§ØŗŲŠ ØšŲ„Ų‰ Ø§Ų„ØŖØŗØˇØ­ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ.", + "theme_setting_colorful_interface_title": "ŲˆØ§ØŦŲ‡Ų‡ Ų…Ų„ŲˆŲ†ØŠ", "theme_setting_image_viewer_quality_subtitle": "اØļØ¨Øˇ ØŦŲˆØ¯ØŠ ØšØ§ØąØļ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØĒ؁ØĩŲŠŲ„ŲŠØŠ", "theme_setting_image_viewer_quality_title": "ØŦŲˆØ¯ØŠ ØšØ§ØąØļ Ø§Ų„ØĩŲˆØąØŠ", + "theme_setting_primary_color_subtitle": "ا؎ØĒØą Ų„ŲˆŲ† Ų„Ų„ØšŲ…Ų„ŲŠØ§ØĒ Ø§Ų„Ø§ØŗØ§ØŗŲŠØŠ ŲˆØ§Ų„Ų…ŲƒŲ…Ų„Ų‡.", + "theme_setting_primary_color_title": "Ø§Ų„Ų„ŲˆŲ† Ø§Ų„Ø§ØŗØ§ØŗŲŠ", + "theme_setting_system_primary_color_title": "Ø§ØŗØĒØŽØ¯Ų… Ų„ŲˆŲ† Ø§Ų„Ų†Ø¸Ø§Ų…", "theme_setting_system_theme_switch": "ØĒŲ„Ų‚Ø§ØĻ؊ (اØĒبؚ ØĨؚداد Ø§Ų„Ų†Ø¸Ø§Ų…)", "theme_setting_theme_subtitle": "ا؎ØĒØą ØĨؚداداØĒ Ų…Ø¸Ų‡Øą Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", "theme_setting_three_stage_loading_subtitle": "Ų‚Ø¯ ŲŠØ˛ŲŠØ¯ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ Ų…Ų† ØĢŲ„Ø§ØĢ Ų…ØąØ§Ø­Ų„ Ų…Ų† ØŖØ¯Ø§ØĄ Ø§Ų„ØĒØ­Ų…ŲŠŲ„ ŲˆŲ„ŲƒŲ†Ų‡ ŲŠØŗØ¨Ø¨ ØĒØ­Ų…ŲŠŲ„ Ø´Ø¨ŲƒØŠ ØŖØšŲ„Ų‰ Ø¨ŲƒØĢŲŠØą", @@ -1566,28 +1991,38 @@ "to_change_password": "ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "to_favorite": "ØĒ؁ØļŲŠŲ„", "to_login": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„", + "to_multi_select": "Ų„Ų„ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„Ų…ØĒؚدد", "to_parent": "Ø§Ų†ØĒŲ‚Ų„ ØĨŲ„Ų‰ Ø§Ų„ŲˆØ§Ų„Ø¯", + "to_select": "Ų„Ų„ØĒØ­Ø¯ŲŠØ¯", "to_trash": "Ø­Ø°Ų", "toggle_settings": "Ø§Ų„ØĨؚداداØĒ", "total": "Ø§Ų„ØĨØŦŲ…Ø§Ų„ŲŠ", "total_usage": "Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„ØĨØŦŲ…Ø§Ų„ŲŠ", "trash": "Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", + "trash_action_prompt": "{count} Ų†Ų‚Ų„ Ø§Ų„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_all": "Ų†Ų‚Ų„ Ø§Ų„ŲƒŲ„ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_count": "ØŗŲ„ØŠ Ø§Ų„Ų…Ø­Ų…Ų„Ø§ØĒ {count, number}", "trash_delete_asset": "Ø­Ø°Ų/Ų†Ų‚Ų„ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ ØĨŲ„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", + "trash_emptied": "ØŗØ¨ØŠ Ų…Ų‡Ų…Ų„Ø§ Ų…ŲØąØēØŠ", "trash_no_results_message": "ØŗØĒØ¸Ų‡Øą Ų‡Ų†Ø§ Ø§Ų„ØĩŲˆØą ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠ.", "trash_page_delete_all": "Ø­Ø°Ų Ø§Ų„ŲƒŲ„", "trash_page_empty_trash_dialog_content": "Ų‡Ų„ ØĒØąŲŠØ¯ ØĒŲØąŲŠØē ØŖØĩŲˆŲ„Ųƒ Ø§Ų„Ų…Ų‡Ų…Ų„ØŠØŸ ØŗØĒØĒŲ… ØĨØ˛Ø§Ų„ØŠ Ų‡Ø°Ų‡ Ø§Ų„ØšŲ†Ø§ØĩØą Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§ Ų…Ų† Ø§Ų„ØĒØˇØ¨ŲŠŲ‚", + "trash_page_info": "Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„Ų…Ų†Ų‚ŲˆŲ„ØŠ Ø§Ų„Ų‰ ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ ØŗŲŠØĒŲ… Ø­Ø°ŲŲ‡Ø§ Ø¨Ø´ŲƒŲ„ Ų†Ų‡Ø§ØĻ؊ بؚد {days} Ø§ŲŠØ§Ų…", "trash_page_no_assets": "Ų„Ø§ ØĒ؈ØŦد اØĩŲˆŲ„ ؁؊ ØŗŲ„Ų‡ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ", "trash_page_restore_all": "Ø§ØŗØĒؚاد؊ Ø§Ų„ŲƒŲ„", - "trash_page_select_assets_btn": "ا؎ØĒØą Ø§Ų„ØŖØĩŲˆŲ„ ", + "trash_page_select_assets_btn": "ا؎ØĒØą Ø§Ų„ØŖØĩŲˆŲ„", + "trash_page_title": "ØŗŲ„ØŠ Ø§Ų„Ų…Ų‡Ų…Ų„Ø§ØĒ ({count})", "trashed_items_will_be_permanently_deleted_after": "ØŗŲŠØĒŲ… Ø­Ø°ŲŲ Ø§Ų„ØšŲ†Ø§ØĩØą Ø§Ų„Ų…Ø­Ø°ŲˆŲØŠ Ų†ŲŲ‡Ø§ØĻŲŠŲ‹Ø§ بؚد {days, plural, one {# ŲŠŲˆŲ…} other {# ØŖŲŠØ§Ų… }}.", + "troubleshoot": "Ø§ØŗØĒŲƒØ´Ø§Ų Ø§Ų„Ų…Ø´Ø§ŲƒŲ„", "type": "Ø§Ų„Ų†ŲˆØš", - "unable_to_change_pin_code": "ØĒŲŲŠŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ ØēŲŠØą Ų…Ų…ŲƒŲ†", - "unable_to_setup_pin_code": "Ø§Ų†Ø´Ø§ØĄ Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ ØēŲŠØą Ų…Ų…ŲƒŲ†", + "unable_to_change_pin_code": "ØĒŲŲŠŲŠØą ØąŲ…Ø˛ PIN ØēŲŠØą Ų…Ų…ŲƒŲ†", + "unable_to_setup_pin_code": "Ø§Ų†Ø´Ø§ØĄ ØąŲ…Ø˛ PIN ØēŲŠØą Ų…Ų…ŲƒŲ†", "unarchive": "ØŖØŽØąØŦ Ų…Ų† Ø§Ų„ØŖØąØ´ŲŠŲ", + "unarchive_action_prompt": "{count} Ø§Ø˛ŲŠŲ„ Ų…Ų† Ø§Ų„Ø§ØąØ´ŲŠŲ", "unarchived_count": "{count, plural, other {ØēŲŠØą Ų…Ø¤ØąØ´ŲØŠ #}}", + "undo": "ØĒØąØ§ØŦØš", "unfavorite": "ØŖØ˛Ų„ Ø§Ų„ØĒ؁ØļŲŠŲ„", + "unfavorite_action_prompt": "{count} Ø§Ø˛ŲŠŲ„ Ų…Ų† Ø§Ų„Ų…ŲØļŲ„Ø§ØĒ", "unhide_person": "ØŖØ¸Ų‡Øą Ø§Ų„Ø´ØŽØĩ", "unknown": "ØēŲŠØą Ų…ØšØąŲˆŲ", "unknown_country": "Ø¨Ų„Ø¯ ØēŲŠØą Ų…ØšØąŲˆŲ", @@ -1603,29 +2038,44 @@ "unsaved_change": "ØĒØēŲŠŲŠØą ØēŲŠØą Ų…Ø­ŲŲˆØ¸", "unselect_all": "ØĨŲ„ØēØ§ØĄ ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„", "unselect_all_duplicates": "ØĨŲ„ØēØ§ØĄ ØĒØ­Ø¯ŲŠØ¯ ŲƒØ§ŲØŠ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ų…ŲƒØąØąØŠ", + "unselect_all_in": "ØĨŲ„ØēØ§ØĄ ØĒØ­Ø¯ŲŠØ¯ Ø§Ų„ŲƒŲ„ ؁؊ {group}", "unstack": "؁؃ Ø§Ų„ŲƒŲˆŲ…Ų‡", + "unstack_action_prompt": "ØĒŲ… Ø§Ø˛Ø§Ų„ØŠ ØĒŲƒØ¯ŲŠØŗ {count}", "unstacked_assets_count": "ØĒŲ… ØĨØŽØąØ§ØŦ {count, plural, one {# Ø§Ų„ØŖØĩŲ„} other {# Ø§Ų„ØŖØĩŲˆŲ„}} Ų…Ų† Ø§Ų„ØĒŲƒØ¯ŲŠØŗ", + "untagged": "ØēŲŠØą Ų…ŲØšŲŽŲ„ŲŽŲ‘Ų…", "up_next": "Ø§Ų„ØĒØ§Ų„ŲŠ", + "update_location_action_prompt": "ØĒØ­Ø¯ŲŠØĢ Ų…ŲˆŲ‚Øš {count} ØšŲ†Ø§ØĩØą Ų…Ø­Ø¯Ø¯ØŠ ØšŲ„Ų‰ Ø§Ų„Ų†Ø­Ųˆ Ø§Ų„ØĒØ§Ų„ŲŠ:", + "updated_at": "ØĒŲ… Ø§Ų„ØĒØ­Ø¯ŲŠØĢ", "updated_password": "ØĒŲ… ØĒØ­Ø¯ŲŠØĢ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą", "upload": "ØąŲØš", + "upload_action_prompt": "{count} ؅؄؁ ؁؊ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą Ų„Ų„ØąŲØš", "upload_concurrency": "Ø§Ų„ØąŲØš Ø§Ų„Ų…ØĒØ˛Ø§Ų…Ų†", + "upload_details": "ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„ØąŲØš", "upload_dialog_info": "Ų‡Ų„ ØĒØąŲŠØ¯ Ø§Ų„Ų†ØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠ Ų„Ų„ØŖØĩŲˆŲ„ (Ø§Ų„ØŖØĩŲˆŲ„) Ø§Ų„Ų…Ø­Ø¯Ø¯ØŠ ØĨŲ„Ų‰ Ø§Ų„ØŽØ§Ø¯Ų…ØŸ", "upload_dialog_title": "ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØŖØĩŲˆŲ„", "upload_errors": "ØĨ؃ØĒŲ…Ų„ Ø§Ų„ØąŲØš Ų…Øš {count, plural, one {# ØŽØˇØŖ} other {# ØŖØŽØˇØ§ØĄ}}, Ų‚Ų… بØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĩŲØ­ØŠ Ų„ØąØ¤ŲŠØŠ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ Ø§Ų„ØĒ؊ ØĒŲ… ØąŲØšŲ‡Ø§.", + "upload_finished": "ØĒŲ… Ø§Ų„Ø§Ų†ØĒŲ‡Ø§ØĄ Ų…Ų† Ø§Ų„ØąŲØš", "upload_progress": "Ų…ØĒØ¨Ų‚ŲŠØŠ {remaining, number} - Ų…ØšØ§Ų„ØŦØŠ {processed, number}/{total, number}", "upload_skipped_duplicates": "ØĒŲ… ØĒØŽØˇŲŠ {count, plural, one {# Ų…Ø­ØĒŲˆŲ‰ Ų…ŲƒØąØą} other {# Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ų…ŲƒØąØąØŠ }}", "upload_status_duplicates": "Ø§Ų„ØĒŲƒØąØ§ØąØ§ØĒ", "upload_status_errors": "Ø§Ų„ØŖØŽØˇØ§ØĄ", "upload_status_uploaded": "ØĒŲ… Ø§Ų„ØąŲØš", "upload_success": "ØĒŲ… Ø§Ų„ØąŲØš Ø¨Ų†ØŦاح، Ų‚Ų… بØĒØ­Ø¯ŲŠØĢ Ø§Ų„ØĩŲØ­ØŠ Ų„ØąØ¤ŲŠØŠ Ø§Ų„Ų…Ø­ØĒŲˆŲŠØ§ØĒ Ø§Ų„Ų…ØąŲŲˆØšØŠ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ.", + "upload_to_immich": "Ø§Ų„ØąŲØš Ø§Ų„Ų‰Immich ‎ ‏ ({count})", + "uploading": "ØŦØ§ØąŲŠ Ø§Ų„ØąŲØš", + "uploading_media": "ØąŲØš Ø§Ų„ŲˆØŗØ§ØĻØˇ", "url": "ØšŲ†ŲˆØ§Ų† URL", "usage": "Ø§Ų„Ø§ØŗØĒØŽØ¯Ø§Ų…", + "use_biometric": "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ø¨Ø§ŲŠŲˆŲ…ØĒØąŲŠ", + "use_current_connection": "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø§Ų„Ø­Ø§Ų„ŲŠ", "use_custom_date_range": "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ų†ØˇØ§Ų‚ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„Ų…ØŽØĩØĩ Ø¨Ø¯Ų„Ø§Ų‹ Ų…Ų† Ø°Ų„Ųƒ", "user": "Ų…ØŗØĒØŽØ¯Ų…", + "user_has_been_deleted": "Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ØĒŲ… Ø­Ø°ŲŲ‡.", "user_id": "Ų…ØšØąŲ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_liked": "Ų‚Ø§Ų… {user} Ø¨Ø§Ų„ØĨØšØŦاب {type, select, photo {Ø¨Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØąØŠ} video {Ø¨Ų‡Ø°Ø§ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ} asset {Ø¨Ų‡Ø°Ø§ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰} other {Ø¨Ų‡Ø§}}", - "user_pin_code_settings": "Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", - "user_pin_code_settings_description": "ØĒØēŲŠØą Ø§Ų„ØąŲ‚Ų… Ø§Ų„ØŗØąŲŠ", + "user_pin_code_settings": "ØąŲ…Ø˛ PIN", + "user_pin_code_settings_description": "ØĒØēŲŠØą ØąŲ…Ø˛ PIN", + "user_privacy": "ØŽØĩ؈ØĩŲŠØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "user_purchase_settings": "Ø§Ų„Ø´ØąØ§ØĄ", "user_purchase_settings_description": "ØĨØ¯Ø§ØąØŠ ØšŲ…Ų„ŲŠØŠ Ø§Ų„Ø´ØąØ§ØĄ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ųƒ", "user_role_set": "Ų‚Ų… بØĒØšŲŠŲŠŲ† {user} ŲƒŲ€ {role}", @@ -1634,8 +2084,10 @@ "user_usage_stats_description": "ØšØąØļ ØĨØ­ØĩاØĻŲŠØ§ØĒ Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø­ØŗØ§Ø¨", "username": "Ø§ØŗŲ… Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "users": "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†", + "users_added_to_album_count": "ØĒŲ… اØļØ§ŲØŠ{count, plural, one {# Ų…ØŗØĒØŽØ¯Ų…} other {# Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†}} Ø§Ų„Ų‰ Ø§Ų„Ø§Ų„Ø¨ŲˆŲ…", "utilities": "ØŖØ¯ŲˆØ§ØĒ", "validate": "ØĒØ­Ų‚Ų’Ų‚", + "validate_endpoint_error": "Ø§Ų„ØąØŦØ§ØĄ Ø§Ø¯ØŽØ§Ų„ ØšŲ†ŲˆØ§Ų† URL ØĩØ§Ų„Ø­", "variables": "Ø§Ų„Ų…ØĒØēŲŠØąØ§ØĒ", "version": "Ø§Ų„ØĨØĩØ¯Ø§Øą", "version_announcement_closing": "ØĩØ¯ŲŠŲ‚ŲƒØŒ ØŖŲ„ŲŠŲƒØŗ", @@ -1651,13 +2103,17 @@ "view_album": "ØšØąØļ Ø§Ų„ØŖŲ„Ø¨ŲˆŲ…", "view_all": "ØšØąØļ Ø§Ų„ŲƒŲ„", "view_all_users": "ØšØąØļ ŲƒØ§ŲØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†", + "view_details": "ØąØ¤ŲŠØŠ Ø§Ų„ØĒŲØ§ØĩŲŠŲ„", "view_in_timeline": "ØšØąØļ ؁؊ Ø§Ų„ØŦØ¯ŲˆŲ„ Ø§Ų„Ø˛Ų…Ų†ŲŠ", "view_link": "ØšØąØļ Ø§Ų„ØąØ§Ø¨Øˇ", "view_links": "ØšØąØļ Ø§Ų„ØąŲˆØ§Ø¨Øˇ", "view_name": "ØšØąØļ", "view_next_asset": "ØšØąØļ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ØĒØ§Ų„ŲŠ", "view_previous_asset": "ØšØąØļ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ØŗØ§Ø¨Ų‚", + "view_qr_code": "Â­ØšØąØļ ØąŲ…Ø˛ Ø§Ų„Ø§ØŗØĒØŦاب؊ Ø§Ų„ØŗØąŲŠØšØŠ", + "view_similar_photos": "ØšØąØļ ØĩŲˆØą Ų…Ø´Ø§Ø¨Ų‡ØŠ", "view_stack": "ØšØąØļ Ø§Ų„ØĒŲƒØ¯ŲŠØŗ", + "view_user": "ØšØąØļ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…", "viewer_remove_from_stack": "Ø­Ø°Ų Ų…Ų† Ø§Ų„ŲƒŲˆŲ…Ų‡ ØŖŲˆ Ø§Ų„Ų…ØŦŲ…ŲˆØšØŠ", "viewer_stack_use_as_main_asset": "Ø§ØŗØĒØŽØ¯Ų… ŲƒØŖØĩŲ„ ØąØĻŲŠØŗŲŠ", "viewer_unstack": "؁؃ Ø§Ų„ŲƒŲˆŲ…Ų‡", @@ -1667,11 +2123,13 @@ "week": "ØŖØŗØ¨ŲˆØš", "welcome": "Ų…ØąØ­Ø¨Ø§Ų‹", "welcome_to_immich": "Ų…ØąØ­Ø¨Ø§Ų‹ Ø¨Ųƒ ؁؊ Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Ø§ØŗŲ… Ø´Ø¨ŲƒØŠ Wi-Fi", + "wrong_pin_code": "ØąŲ…Ø˛ PIN ØŽØ§ØˇØĻ", "year": "ØŗŲ†ØŠ", "years_ago": "Ų…Ų†Ø° {years, plural, one {# ØŗŲ†ØŠ} other {# ØŗŲ†ŲˆØ§ØĒ}}", "yes": "Ų†ØšŲ…", "you_dont_have_any_shared_links": "Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØŖŲŠ ØąŲˆØ§Ø¨Øˇ Ų…Ø´ØĒØąŲƒØŠ", - "your_wifi_name": "Your WiFi name", - "zoom_image": "ØĒŲƒØ¨ŲŠØą Ø§Ų„ØĩŲˆØąØŠ" + "your_wifi_name": "Ø§ØŗŲ… Ø´Ø¨ŲƒØŠ Wi-Fi Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ", + "zoom_image": "ØĒŲƒØ¨ŲŠØą Ø§Ų„ØĩŲˆØąØŠ", + "zoom_to_bounds": "ØĒŲƒØ¨ŲŠØą Ø­ØĒŲ‰ Ø­Ø¯ŲˆØ¯ Ø§Ų„Ų…Ų†ØˇŲ‚ØŠ" } diff --git a/i18n/az.json b/i18n/az.json index 19ca4aa08d..53e7f55db6 100644 --- a/i18n/az.json +++ b/i18n/az.json @@ -1,39 +1,63 @@ { - "about": "Haqqinda", + "about": "HaqqÄąnda", "account": "Hesab", - "account_settings": "Hesab parametrləri", + "account_settings": "Hesab Parametrləri", "acknowledge": "Təsdiq et", "action": "Əməliyyat", + "action_common_update": "Yenilə", "actions": "Əməliyyatlar", "active": "Aktiv", "activity": "Fəaliyyət", + "activity_changed": "Fəaliyyət {enabled, select, true {aktivdir} other {aktiv deyil}}", "add": "Əlavə et", "add_a_description": "Təsviri əlavə et", "add_a_location": "Məkan əlavə et", "add_a_name": "Ad əlavə et", "add_a_title": "BaşlÄąq əlavə et", - "add_exclusion_pattern": "İstisna nÃŧmunəsi əlavə et", - "add_import_path": "Import yolunu əlavə et", - "add_location": "MəkanÄą əlavə et", + "add_birthday": "Doğum gÃŧnÃŧ əlavə et", + "add_endpoint": "Son nÃļqtə əlavə et", + "add_exclusion_pattern": "Ã‡Äąxarma nÃŧmunəsi əlavə et", + "add_import_path": "İdxal yolu əlavə et", + "add_location": "Məkan əlavə et", "add_more_users": "Daha çox istifadəçi əlavə et", "add_partner": "Partnyor əlavə et", "add_path": "Yol əlavə et", - "add_photos": "Şəkilləri əlavə et", - "add_to": "... əlavə et", - "add_to_album": "Albom əlavə et", + "add_photos": "Şəkillər əlavə et", + "add_tag": "Etiket əlavə et", + "add_to": "Bura əlavə etâ€Ļ", + "add_to_album": "Alboma əlavə et", + "add_to_album_bottom_sheet_added": "{album} albomuna əlavə edildi", + "add_to_album_bottom_sheet_already_exists": "ArtÄąq {album} albomunda var", + "add_to_album_bottom_sheet_some_local_assets": "Bəzi lokal resurslar alboma əlavə edilə bilmədi", + "add_to_album_toggle": "{album} ÃŧçÃŧn seçimi dəyişin", + "add_to_albums": "Albomlara əlavə et", + "add_to_albums_count": "({count}) albomlarÄąna əlavə et", "add_to_shared_album": "PaylaÅŸÄąlan alboma əlavə et", + "add_url": "URL əlavə et", "added_to_archive": "Arxivə əlavə edildi", "added_to_favorites": "Sevimlilələrə əlavə edildi", "added_to_favorites_count": "{count, number} şəkil sevimlilələrə əlavə edildi", "admin": { + "add_exclusion_pattern_description": "Ã‡Äąxarma nÃŧmunələri əlavə et. *, ** və ? istifadə edilərək globbing dəstəklənir. Hər hansÄą bir \"Raw\" adlÄą qovluqdakÄą bÃŧtÃŧn fayllarÄą gÃļrməməzlikdən gəlmək ÃŧçÃŧn **/Raw/** istifadə et. “.tif” ilə bitən bÃŧtÃŧn fayllarÄą gÃļrməməzlikdən gəlmək ÃŧçÃŧn **/*.tif istifadə et. Tam yolu gÃļrməməzlikdən gəlmək ÃŧçÃŧn /path/to/ignore/** istifadə et.", + "admin_user": "İdarəçi İstifadəçi", + "asset_offline_description": "Bu xarici kitabxana varlığı diskdə artÄąq tapÄąlmadÄą və zibil qutusuna kÃļçÃŧrÃŧldÃŧ. Əgər fayl kitabxana içərisində kÃļçÃŧrÃŧlÃŧbsə, zaman şkalanÄązÄą yeni uyğun gələn varlÄąq ÃŧçÃŧn yoxlayÄąn. Varlığı yenidən qaytarmaq ÃŧçÃŧn aşağıda verilmiş fayl yolunun Immich tərəfindən əlçatan olduğundan əmin olduqdan sonra kitabxananÄą skan edin.", "authentication_settings": "Səlahiyyətləndirmə parametrləri", "authentication_settings_description": "Şifrə, OAuth və digər səlahiyyətləndirmə parametrləri", "authentication_settings_disable_all": "BÃŧtÃŧn giriş etmə metodlarÄąnÄą sÃļndÃŧrmək istədiyinizdən əminsinizmi? Giriş etmə funksiyasÄą tamamilə sÃļndÃŧrÃŧləcəkdir.", "authentication_settings_reenable": "Yenidən aktiv etmək ÃŧçÃŧn Server Əmri -ni istifadə edin.", "background_task_job": "Arxa plan tapÅŸÄąrÄąqlarÄą", - "backup_database_enable_description": "Verilənlər bazasÄąnÄąn ehtiyat nÃŧsxələrini aktiv et", - "backup_settings": "Ehtiyat NÃŧsxə Parametrləri", + "backup_database": "Verilənlər bazasÄąnÄąn dump-ÄąnÄą yaradÄąn", + "backup_database_enable_description": "Verilənlər bazasÄąnÄąn artÄąq nÃŧsxələrini aktiv et", + "backup_keep_last_amount": "TutulmasÄą gərəkən nÃŧsxələrin sayÄą", + "backup_onboarding_1_description": "buludda və ya başqa fiziki yerdə saytdan kənar surət.", + "backup_onboarding_2_description": "mÃŧxtəlif cihazlarda yerli nÃŧsxələr. Bura əsas fayllar və həmin fayllarÄąn ehtiyat lokal nÃŧsxəsi daxildir.", + "backup_onboarding_3_description": "orijinal fayllar da daxil olmaqla məlumatlarÄąnÄązÄąn Ãŧmumi surətləri. Buraya 1 kənar nÃŧsxə və 2 lokal nÃŧsxə daxildir.", + "backup_onboarding_footer": "Immich-in ehtiyat nÃŧsxəsini Ã§Äąxarmaq haqqÄąnda ətraflÄą məlumat ÃŧçÃŧn sənədlərə mÃŧraciət edin.", + "backup_onboarding_parts_title": "3-2-1 ehtiyat nÃŧsxəsinə aşağıdakÄąlar daxildir:", + "backup_onboarding_title": "Ehtiyat surətlər", + "backup_settings": "BazanÄąn Dump Parametrləri", "backup_settings_description": "Verilənlər bazasÄąnÄąn ehtiyat nÃŧsxə parametrlərini idarə et", + "cleared_jobs": "{job} ÃŧçÃŧn tapÅŸÄąrÄąqlar silindi", "config_set_by_file": "Konfiqurasiya hal-hazÄąrda konfiqurasiya faylÄą ilə təyin olunub", "confirm_delete_library": "{library} kitabxanasÄąnÄą silmək istədiyinizdən əminmisiniz?", "confirm_email_below": "Təsdiqləmək ÃŧçÃŧn aşağıya {email} yazÄąn", @@ -53,7 +77,7 @@ "image_thumbnail_title": "Önizləmə parametrləri", "job_concurrency": "{job}paralellik", "job_created": "TapÅŸÄąrÄąq yaradÄąldÄą", - "job_not_concurrency_safe": "Bu tapÅŸÄąrÄąq parallel fəaliyyət ÃŧçÃŧn uyğun deyil", + "job_not_concurrency_safe": "Bu iş eyni vaxtda icra ÃŧçÃŧn təhlÃŧkəsiz deyil.", "job_settings": "TapÅŸÄąrÄąq parametrləri", "job_settings_description": "Parallel şəkildə fəaliyyət gÃļstərən tapÅŸÄąrÄąqlarÄą idarə et", "job_status": "TapÅŸÄąrÄąq statusu", @@ -84,5 +108,6 @@ "machine_learning_facial_recognition": "Üz TanÄąma", "machine_learning_facial_recognition_description": "Şəkillərdəki Ãŧzləri aşkarla, tanÄą və qruplaşdÄąr", "machine_learning_facial_recognition_model": "Üz tanÄąma modeli" - } + }, + "timeline": "Zaman şkalasÄą" } diff --git a/i18n/be.json b/i18n/be.json index 470030b6f6..7298e904c1 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -6,7 +6,7 @@ "action": "ДзĐĩŅĐŊĐŊĐĩ", "action_common_update": "АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ", "actions": "ДзĐĩŅĐŊĐŊŅ–", - "active": "АĐēŅ‚Ņ‹ŅžĐŊŅ‹", + "active": "АĐēŅ‚Ņ‹ŅžĐŊҋ҅", "activity": "АĐēŅ‚Ņ‹ŅžĐŊĐ°ŅŅ†ŅŒ", "activity_changed": "АĐēŅ‚Ņ‹ŅžĐŊĐ°ŅŅ†ŅŒ {enabled, select, true {҃ĐēĐģŅŽŅ‡Đ°ĐŊа} other {адĐēĐģŅŽŅ‡Đ°ĐŊа}}", "add": "Đ”Đ°Đ´Đ°Ņ†ŅŒ", @@ -14,6 +14,7 @@ "add_a_location": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐŧĐĩŅŅ†Đ°", "add_a_name": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ–ĐŧŅ", "add_a_title": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐˇĐ°ĐŗĐ°ĐģОваĐē", + "add_birthday": "Đ”Đ°Đ´Đ°Ņ†ŅŒ дСĐĩĐŊҌ ĐŊĐ°Ņ€Đ°Đ´ĐļŅĐŊĐŊŅ", "add_endpoint": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐēŅ€ĐžĐŋĐē҃ Đ´ĐžŅŅ‚ŅƒĐŋ҃", "add_exclusion_pattern": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đ˛Ņ‹ĐēĐģŅŽŅ‡ŅĐŊĐŊŅ", "add_import_path": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Ņƒ", @@ -22,10 +23,15 @@ "add_partner": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "add_path": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҈ĐģŅŅ…", "add_photos": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ„ĐžŅ‚Đ°", + "add_tag": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ‚ŅĐŗ", "add_to": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃â€Ļ", "add_to_album": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ аĐģŅŒĐąĐžĐŧ", "add_to_album_bottom_sheet_added": "ДададзĐĩĐŊа да {album}", "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐž СĐŊĐ°Ņ…ĐžĐ´ĐˇŅ–Ņ†Ņ†Đ° Ņž {album}", + "add_to_album_bottom_sheet_some_local_assets": "НĐĩĐēĐ°Ņ‚ĐžŅ€Ņ‹Ņ ĐģаĐēаĐģҌĐŊŅ‹Ņ аĐēŅ‚Ņ‹Đ˛Ņ‹ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ†ŅŒ ĐąŅ‹Ņ†ŅŒ дададСĐĩĐŊŅ‹ Ņž аĐģŅŒĐąĐžĐŧ", + "add_to_album_toggle": "ПĐĩŅ€Đ°ĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Đ˛Ņ‹ĐąĐ°Ņ€ Đ´ĐģŅ {album}", + "add_to_albums": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ аĐģŅŒĐąĐžĐŧŅ‹", + "add_to_albums_count": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ аĐģŅŒĐąĐžĐŧŅ‹ ({count})", "add_to_shared_album": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ҃ Đ°ĐŗŅƒĐģҌĐŊŅ‹ аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”Đ°Đ´Đ°Ņ†ŅŒ URL", "added_to_archive": "ДададзĐĩĐŊа Ņž Đ°Ņ€Ņ…Ņ–Ņž", @@ -33,34 +39,40 @@ "added_to_favorites_count": "ДададзĐĩĐŊа {count, number} да Đ°ĐąŅ€Đ°ĐŊĐ°ĐŗĐ°", "admin": { "add_exclusion_pattern_description": "Đ”Đ°Đ´Đ°ĐšŅ†Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ Đ˛Ņ‹ĐēĐģŅŽŅ‡ŅĐŊĐŊŅŅž. ĐŸĐ°Đ´Ņ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐĩŅ†Ņ†Đ° Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐŊĐŊĐĩ ҁҖĐŧваĐģĐ°Ņž * , ** Ņ– ?. Каб Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ ҃ҁĐĩ Ņ„Đ°ĐšĐģŅ‹ Ņž ĐģŅŽĐąĐžĐš Đ´Ņ‹Ņ€ŅĐēŅ‚ĐžŅ€Ņ‹Ņ– С ĐŊаСваК \"Raw\", Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐšŅ†Đĩ \"**/Raw/**\". Каб Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ ҃ҁĐĩ Ņ„Đ°ĐšĐģŅ‹, ŅĐēŅ–Ņ СаĐēаĐŊŅ‡Đ˛Đ°ŅŽŅ†Ņ†Đ° ĐŊа \".tif\", Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐšŅ†Đĩ \"**/.tif\". Каб Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊŅ‹ ҈ĐģŅŅ…, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐšŅ†Đĩ \"/path/to/ignore/**\".", + "admin_user": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€", "asset_offline_description": "Đ“ŅŅ‚Ņ‹ СĐŊĐĩ҈ĐŊŅ– ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅŅ‡ĐŊŅ‹ аĐēŅ‚Ņ‹Ņž йОĐģҌ҈ ĐŊĐĩ СĐŊОКдСĐĩĐŊŅ‹ ĐŊа Đ´Ņ‹ŅĐē҃ Ņ– ĐąŅ‹Ņž ĐŋĐĩŅ€Đ°ĐŧĐĩŅˆŅ‡Đ°ĐŊŅ‹ Ņž ҁĐŧĐĩŅ‚ĐŊŅ–Ņ†Ņƒ. КаĐģŅ– Ņ„Đ°ĐšĐģ ĐąŅ‹Ņž ĐŋĐĩŅ€Đ°ĐŧĐĩŅˆŅ‡Đ°ĐŊŅ‹ Ņž ĐŧĐĩĐļĐ°Ņ… ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–, ĐŋŅ€Đ°Đ˛ĐĩҀ҆Đĩ Đ˛Đ°ŅˆŅƒ Ņ…Ņ€ĐžĐŊŅ–Đē҃ Đ´ĐģŅ ĐŊĐžĐ˛Đ°ĐŗĐ° адĐŋавĐĩĐ´ĐŊĐ°ĐŗĐ° аĐēŅ‚Ņ‹Đ˛Đ°. Каб адĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ĐŗŅŅ‚Ņ‹ аĐēŅ‚Ņ‹Ņž, ĐŋĐĩŅ€Đ°ĐēаĐŊĐ°ĐšŅ†ĐĩŅŅ, ŅˆŅ‚Đž ҈ĐģŅŅ… да Ņ„Đ°ĐšĐģа ĐŊŅ–ĐļŅĐš Đ´Đ°ŅŅ‚ŅƒĐŋĐŊŅ‹ Đ´ĐģŅ Immich Ņ– Đ°Đ´ŅĐēаĐŊŅƒĐšŅ†Đĩ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃.", "authentication_settings": "НаĐģĐ°Đ´Ņ‹ ĐŋŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– ŅĐ°ĐŋŅ€Đ°ŅžĐ´ĐŊĐ°ŅŅ†Ņ–", "authentication_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŋĐ°Ņ€ĐžĐģŅĐŧŅ–, OAuth, Ņ– Ņ–ĐŊŅˆŅ‹Ņ ĐŊаĐģĐ°Đ´Ņ‹ ĐŋŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– ŅĐ°ĐŋŅ€Đ°ŅžĐ´ĐŊĐ°ŅŅ†Ņ–", "authentication_settings_disable_all": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ адĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ҃ҁĐĩ ҁĐŋĐžŅĐ°ĐąŅ‹ ĐģĐžĐŗŅ–ĐŊ҃? Đ›ĐžĐŗŅ–ĐŊ ĐąŅƒĐ´ĐˇĐĩ Ņ†Đ°ĐģĐēаĐŧ адĐēĐģŅŽŅ‡Đ°ĐŊŅ‹.", "authentication_settings_reenable": "Каб СĐŊĐžŅž ҃ĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐšŅ†Đĩ КаĐŧаĐŊĐ´Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "background_task_job": "ФОĐŊĐ°Đ˛Ņ‹Ņ СадаĐŊĐŊŅ–", - "backup_database": "Đ ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°Ņ ĐēĐžĐŋŅ–Ņ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅", + "backup_database": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛ŅƒŅŽ ĐēĐžĐŋŅ–ŅŽ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅", "backup_database_enable_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Ņ€ŅĐˇĐĩŅ€Đ˛Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅", "backup_keep_last_amount": "КоĐģҌĐēĐ°ŅŅ†ŅŒ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅ–Ņ… Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Ņ‹Ņ… ĐēĐžĐŋŅ–Đš Đ´ĐģŅ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊĐŊŅ", + "backup_onboarding_1_description": "СĐŊŅŅˆĐŊŅŅ ĐēĐžĐŋŅ–Ņ Ņž вОйĐģаĐē҃ айО Ņž Ņ–ĐŊŅˆŅ‹Đŧ Ņ„Ņ–ĐˇŅ–Ņ‡ĐŊŅ‹Đŧ ĐŧĐĩҁ҆ҋ.", + "backup_onboarding_2_description": "ĐģаĐēаĐģҌĐŊŅ‹Ņ ĐēĐžĐŋŅ–Ņ– ĐŊа Ņ–ĐŊŅˆŅ‹Ņ… ĐŋҀҋĐģĐ°Đ´Đ°Ņ…. Đ“ŅŅ‚Đ° ŅžĐēĐģŅŽŅ‡Đ°Đĩ Ņž ŅŅĐąĐĩ Đ°ŅĐŊĐžŅžĐŊŅ‹Ņ Ņ„Đ°ĐšĐģŅ‹ Ņ– ĐģаĐēаĐģҌĐŊŅƒŅŽ Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛ŅƒŅŽ ĐēĐžĐŋŅ–ŅŽ ĐŗŅŅ‚Ņ‹Ņ… Ņ„Đ°ĐšĐģĐ°Ņž.", + "backup_onboarding_parts_title": "Đ ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°Ņ ĐēĐžĐŋŅ–Ņ ÂĢ3-2-1Âģ ҃ĐēĐģŅŽŅ‡Đ°Đĩ Ņž ŅŅĐąĐĩ:", + "backup_onboarding_title": "Đ ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Ņ‹Ņ ĐēĐžĐŋŅ–Ņ–", "backup_settings": "НаĐģĐ°Đ´Ņ‹ Ņ€ŅĐˇĐĩŅ€Đ˛ĐžĐ˛Đ°ĐŗĐ° ĐēаĐŋŅ–ŅĐ˛Đ°ĐŊĐŊŅ", - "backup_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– даĐŧĐŋа ĐąĐ°ĐˇŅ‹ дадСĐĩĐŊҋ҅. Đ—Đ°ŅžĐ˛Đ°ĐŗĐ°: ĐŗŅŅ‚Ņ‹Ņ ĐˇĐ°Đ´Đ°Ņ‡Ņ‹ ĐŊĐĩ ĐēаĐŊŅ‚Ņ€Đ°ĐģŅŽŅŽŅ†Ņ†Đ°, Ņ– Ņž Đ˛Ņ‹ĐŋадĐē҃ ĐŊŅŅžĐ´Đ°Ņ‡Ņ‹ ĐŋавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ адĐŋŅ€Đ°ŅžĐģĐĩĐŊа ĐŊĐĩ ĐąŅƒĐ´ĐˇĐĩ.", + "backup_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– Ņ€ŅĐˇĐĩŅ€Đ˛Đ°Đ˛Đ°ĐŊĐŊŅ ĐąĐ°ĐˇŅ‹ даĐŊҋ҅.", "cleared_jobs": "ĐŅ‡Ņ‹ŅˆŅ‡Đ°ĐŊŅ‹ СадаĐŊĐŊŅ– Đ´ĐģŅ: {job}", - "config_set_by_file": "КаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ Ņž ĐˇĐ°Ņ€Đ°Đˇ ŅƒŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊа ĐŋŅ€Đ°Đˇ Ņ„Đ°ĐšĐģ ĐēаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ–", - "confirm_delete_library": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ {library} ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃?", + "config_set_by_file": "КаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ ĐˇĐ°Ņ€Đ°Đˇ ŅƒŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊа ĐŋŅ€Đ°Đˇ Ņ„Đ°ĐšĐģ ĐēаĐŊŅ„Ņ–ĐŗŅƒŅ€Đ°Ņ†Ņ‹Ņ–", + "confirm_delete_library": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃ {library}?", "confirm_delete_library_assets": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐŗŅŅ‚ŅƒŅŽ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃? Đ“ŅŅ‚Đ° ĐŋŅ€Ņ‹Đ˛ŅĐ´ĐˇĐĩ да Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊĐŊŅ {count, plural, one {# аĐēŅ‚Ņ‹Đ˛Ņƒ} other {ŅƒŅŅ–Ņ… # аĐēŅ‚Ņ‹Đ˛Đ°Ņž}}, ŅĐēŅ–Ņ СĐŧŅŅˆŅ‡Đ°ŅŽŅ†Ņ†Đ° Ņž Immich, Ņ– ĐŗŅŅ‚Đ° дСĐĩŅĐŊĐŊĐĩ ĐŊĐĩĐŧĐ°ĐŗŅ‡Ņ‹Đŧа ĐąŅƒĐ´ĐˇĐĩ адĐŧŅĐŊŅ–Ņ†ŅŒ. ФаКĐģŅ‹ ĐˇĐ°ŅŅ‚Đ°ĐŊŅƒŅ†Ņ†Đ° ĐŊа Đ´Ņ‹ŅĐē҃.", "confirm_email_below": "Каб ĐŋĐ°Ņ†Đ˛ĐĩŅ€Đ´ĐˇŅ–Ņ†ŅŒ, ŅƒĐ˛ŅĐ´ĐˇŅ–Ņ†Đĩ \"{email}\" ĐŊŅ–ĐļŅĐš", "confirm_reprocess_all_faces": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ ĐŋĐĩŅ€Đ°Đ°ĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°Ņ†ŅŒ ҃ҁĐĩ Ņ‚Đ˛Đ°Ņ€Ņ‹? Đ“ŅŅ‚Đ° Ņ‚Đ°ĐēŅĐ°Đŧа ĐŋŅ€Ņ‹Đ˛ŅĐ´ĐˇĐĩ да Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊĐŊŅ Ņ–ĐŧŅ ĐģŅŽĐ´ĐˇĐĩĐš.", "confirm_user_password_reset": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ Ņž ҂ҋĐŧ, ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ ҁĐēŅ–ĐŊŅƒŅ†ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ {user}?", + "confirm_user_pin_code_reset": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹ Ņž ҂ҋĐŧ, ŅˆŅ‚Đž ĐļадаĐĩ҆Đĩ ҁĐēŅ–ĐŊŅƒŅ†ŅŒ PIN-ĐēОд {user}?", "create_job": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ СадаĐŊĐŊĐĩ", "cron_expression": "Đ’Ņ‹Ņ€Đ°Đˇ Cron", - "cron_expression_description": "ĐŖŅŅ‚Đ°ĐģŅŽĐšŅ†Đĩ Ņ–ĐŊŅ‚ŅŅ€Đ˛Đ°Đģ ҁĐēаĐŊаваĐŊĐŊŅ, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ŅŽŅ‡Ņ‹ Ņ„Đ°Ņ€ĐŧĐ°Ņ‚ cron. ДĐģŅ Đ°Ņ‚Ņ€Ņ‹ĐŧаĐŊĐŊŅ Đ´Đ°Đ´Đ°Ņ‚ĐēОваК Ņ–ĐŊŅ„Đ°Ņ€ĐŧĐ°Ņ†Ņ‹Ņ–, ĐēаĐģŅ– ĐģĐ°ŅĐēа, ĐˇĐ˛ŅŅ€ĐŊҖ҆ĐĩŅŅ, ĐŊаĐŋҀҋĐēĐģад, да Crontab Guru", - "cron_expression_presets": "ĐŸŅ€Đ°Đ´ŅƒŅŅ‚Đ°ĐŊОвĐēŅ– Đ˛Ņ‹Ņ€Đ°ĐˇĐ°Ņž Cron", + "cron_expression_description": "Đ—Đ°Đ´Đ°ĐšŅ†Đĩ Ņ–ĐŊŅ‚ŅŅ€Đ˛Đ°Đģ ҁĐēаĐŊаваĐŊĐŊŅ, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ŅŽŅ‡Ņ‹ Ņ„Đ°Ņ€ĐŧĐ°Ņ‚ cron. ДĐģŅ Đ°Ņ‚Ņ€Ņ‹ĐŧаĐŊĐŊŅ Đ´Đ°Đ´Đ°Ņ‚ĐēОваК Ņ–ĐŊŅ„Đ°Ņ€ĐŧĐ°Ņ†Ņ‹Ņ–, ĐˇĐ˛ŅŅ€ĐŊҖ҆ĐĩŅŅ, ĐŊаĐŋҀҋĐēĐģад, да Crontab Guru", + "cron_expression_presets": "ĐŸŅ€Đ°Đ´ŅƒŅŅ‚Đ°ĐŊĐžŅžĐēŅ– Đ˛Ņ‹Ņ€Đ°ĐˇĐ°Ņž Cron", "disable_login": "АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ŅƒĐ˛Đ°Ņ…ĐžĐ´", "duplicate_detection_job_description": "ЗаĐŋŅƒŅŅ†Ņ–Ņ†ŅŒ ĐŧĐ°ŅˆŅ‹ĐŊĐŊаĐĩ ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊĐĩ ĐŊа аĐēŅ‚Ņ‹Đ˛Đ°Ņ… Đ´ĐģŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ ĐŋадОйĐŊҋ҅ Đ˛Ņ‹ŅŅž. ЗаĐģĐĩĐļŅ‹Ņ†ŅŒ ад Smart Search", "exclusion_pattern_description": "ШайĐģĐžĐŊŅ‹ Đ˛Ņ‹ĐēĐģŅŽŅ‡ŅĐŊĐŊŅ даСваĐģŅŅŽŅ†ŅŒ Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ Ņ„Đ°ĐšĐģŅ‹ Ņ– ĐŋаĐŋĐēŅ– ĐŋҀҋ ҁĐēаĐŊаваĐŊĐŊŅ– Đ˛Đ°ŅˆĐ°Đš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–. Đ“ŅŅ‚Đ° ĐēĐ°Ņ€Ņ‹ŅĐŊа, ĐēаĐģŅ– Ņž Đ˛Đ°Ņ Ņ‘ŅŅ†ŅŒ ĐŋаĐŋĐēŅ–, ŅĐēŅ–Ņ СĐŧŅŅˆŅ‡Đ°ŅŽŅ†ŅŒ Ņ„Đ°ĐšĐģŅ‹, ŅĐēŅ–Ņ Đ˛Ņ‹ ĐŊĐĩ Ņ…ĐžŅ‡Đ°Ņ†Đĩ Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ, ĐŊаĐŋҀҋĐēĐģад, Ņ„Đ°ĐšĐģŅ‹ RAW.", "external_library_management": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ СĐŊĐĩ҈ĐŊŅĐš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēаК", "face_detection": "Đ’Ņ‹ŅŅžĐģĐĩĐŊĐŊĐĩ Ņ‚Đ˛Đ°Ņ€Đ°Ņž", - "face_detection_description": "Đ’Ņ‹ŅŅžĐģŅŅ†ŅŒ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐŊа Ņ„ĐžŅ‚Đ°ĐˇĐ´Ņ‹ĐŧĐēĐ°Ņ… Ņ– Đ˛Ņ–Đ´ŅĐ° С даĐŋаĐŧĐžĐŗĐ°Đš ĐŧĐ°ŅˆŅ‹ĐŊĐŊĐ°ĐŗĐ° ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊŅ. ДĐģŅ Đ˛Ņ–Đ´ŅĐ° ŅžĐģŅ–Ņ‡Đ˛Đ°ĐĩŅ†Ņ†Đ° Ņ‚ĐžĐģҌĐēŅ– ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€Đ°. \"АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ\" (ĐŋĐĩŅ€Đ°)аĐŋŅ€Đ°Ņ†ĐžŅžĐ˛Đ°Đĩ ŅžŅĐĩ ĐŧĐĩĐ´Ņ‹Ņ. \"ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ\" Đ´Đ°Đ´Đ°Ņ‚ĐēОва Đ°Ņ‡Ņ‹ŅˆŅ‡Đ°Đĩ ŅžŅĐĩ ĐąŅĐŗŅƒŅ‡Ņ‹Ņ дадСĐĩĐŊŅ‹Ņ ĐŋŅ€Đ° Ņ‚Đ˛Đ°Ņ€Ņ‹. \"ĐĐ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ\" ŅŅ‚Đ°Đ˛Ņ–Ņ†ŅŒ ҃ Ņ‡Đ°Ņ€ĐŗŅƒ ĐŧĐĩĐ´Ņ‹Ņ, ŅĐēŅ–Ņ ŅŅˆŅ‡Ņ ĐŊĐĩ ĐąŅ‹ĐģŅ– аĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°ĐŊŅ‹Ņ. Đ’Ņ‹ŅŅžĐģĐĩĐŊŅ‹Ņ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐąŅƒĐ´ŅƒŅ†ŅŒ ĐŋĐ°ŅŅ‚Đ°ŅžĐģĐĩĐŊŅ‹ Ņž Ņ‡Đ°Ņ€ĐŗŅƒ Đ´ĐģŅ Ņ€Đ°ŅĐŋаСĐŊаваĐŊĐŊŅ Đ°ŅĐžĐą ĐŋĐ°ŅĐģŅ ĐˇĐ°Đ˛ŅŅ€ŅˆŅĐŊĐŊŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ Ņ‚Đ˛Đ°Ņ€Đ°Ņž, С ĐŗŅ€ŅƒĐŋаваĐŊĐŊĐĩĐŧ Ņ–Ņ… Đŋа ҖҁĐŊŅƒŅŽŅ‡Ņ‹Ņ… айО ĐŊĐžĐ˛Ņ‹Ņ… ĐģŅŽĐ´ĐˇŅŅ….", + "face_detection_description": "Đ’Ņ‹ŅŅžĐģŅŅ†ŅŒ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐŊа Ņ„ĐžŅ‚Đ°ĐˇĐ´Ņ‹ĐŧĐēĐ°Ņ… Ņ– Đ˛Ņ–Đ´ŅĐ° С даĐŋаĐŧĐžĐŗĐ°Đš ĐŧĐ°ŅˆŅ‹ĐŊĐŊĐ°ĐŗĐ° ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊŅ. ДĐģŅ Đ˛Ņ–Đ´ŅĐ° ŅžĐģŅ–Ņ‡Đ˛Đ°ĐĩŅ†Ņ†Đ° Ņ‚ĐžĐģҌĐēŅ– ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€Đ°. \"АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ\" (ĐŋĐĩŅ€Đ°)аĐŋŅ€Đ°Ņ†ĐžŅžĐ˛Đ°Đĩ ŅžŅĐĩ ĐŧĐĩĐ´Ņ‹Ņ. \"ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ\" Đ´Đ°Đ´Đ°Ņ‚ĐēОва Đ°Ņ‡Ņ‹ŅˆŅ‡Đ°Đĩ ŅžŅĐĩ ĐąŅĐŗŅƒŅ‡Ņ‹Ņ даĐŊŅ‹Ņ ĐŋŅ€Đ° Ņ‚Đ˛Đ°Ņ€Ņ‹. \"ĐĐ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ\" ŅŅ‚Đ°Đ˛Ņ–Ņ†ŅŒ ҃ Ņ‡Đ°Ņ€ĐŗŅƒ ĐŧĐĩĐ´Ņ‹Ņ, ŅĐēŅ–Ņ ŅŅˆŅ‡Ņ ĐŊĐĩ ĐąŅ‹ĐģŅ– аĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°ĐŊŅ‹Ņ. Đ’Ņ‹ŅŅžĐģĐĩĐŊŅ‹Ņ Ņ‚Đ˛Đ°Ņ€Ņ‹ ĐąŅƒĐ´ŅƒŅ†ŅŒ ĐŋĐ°ŅŅ‚Đ°ŅžĐģĐĩĐŊŅ‹ Ņž Ņ‡Đ°Ņ€ĐŗŅƒ Đ´ĐģŅ Ņ€Đ°ŅĐŋаСĐŊаваĐŊĐŊŅ Đ°ŅĐžĐą ĐŋĐ°ŅĐģŅ ĐˇĐ°Đ˛ŅŅ€ŅˆŅĐŊĐŊŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ Ņ‚Đ˛Đ°Ņ€Đ°Ņž, С ĐŗŅ€ŅƒĐŋаваĐŊĐŊĐĩĐŧ Ņ–Ņ… Đŋа ҖҁĐŊŅƒŅŽŅ‡Ņ‹Ņ… айО ĐŊĐžĐ˛Ņ‹Ņ… ĐģŅŽĐ´ĐˇŅŅ….", "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋĐ°Đ˛Đ°Ņ†ŅŒ Đ˛Ņ‹ŅŅžĐģĐĩĐŊŅ‹Ņ Ņ‚Đ˛Đ°Ņ€Ņ‹ Đŋа Đ°ŅĐžĐąĐ°Ņ…. Đ“ŅŅ‚Ņ‹ ŅŅ‚Đ°Đŋ Đ˛Ņ‹ĐēĐžĐŊваĐĩŅ†Ņ†Đ° ĐŋĐ°ŅĐģŅ ĐˇĐ°Đ˛ŅŅ€ŅˆŅĐŊĐŊŅ Đ˛Ņ‹ŅŅžĐģĐĩĐŊĐŊŅ Ņ‚Đ˛Đ°Ņ€Đ°Ņž. \"ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ\" (ĐŋĐ°ŅžŅ‚ĐžŅ€ĐŊа) ĐŋĐĩŅ€Đ°ĐŗŅ€ŅƒĐŋĐžŅžĐ˛Đ°Đĩ ŅžŅĐĩ Ņ‚Đ˛Đ°Ņ€Ņ‹. \"ĐĐ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ\" ŅŅ‚Đ°Đ˛Ņ–Ņ†ŅŒ ҃ Ņ‡Đ°Ņ€ĐŗŅƒ Ņ‚Đ˛Đ°Ņ€Ņ‹, ŅĐēŅ–Ņ ŅŅˆŅ‡Ņ ĐŊĐĩ ĐŋҀҋĐŋŅ–ŅĐ°ĐŊŅ‹Ņ да ŅĐēОК-ĐŊĐĩĐąŅƒĐ´ĐˇŅŒ Đ°ŅĐžĐąŅ‹.", "failed_job_command": "КаĐŧаĐŊда {command} ĐŊĐĩ Đ˛Ņ‹ĐēаĐŊаĐģĐ°ŅŅ Đ´ĐģŅ СадаĐŊĐŊŅ: {job}", "force_delete_user_warning": "ĐŸĐĐŸĐ¯Đ Đ­Đ”Đ–ĐĐĐĐ•: Đ“ŅŅ‚Đ° дСĐĩŅĐŊĐŊĐĩ ĐŊĐĩадĐēĐģадĐŊа Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа Ņ– ŅžŅĐĩ ай'ĐĩĐē҂ҋ. Đ“ŅŅ‚Đ° дСĐĩŅĐŊĐŊĐĩ ĐŊĐĩ ĐŧĐžĐļа ĐąŅ‹Ņ†ŅŒ Đ°Đ´Ņ€ĐžĐąĐģĐĩĐŊа Ņ– Ņ„Đ°ĐšĐģŅ‹ ĐŊĐĩĐŧĐ°ĐŗŅ‡Ņ‹Đŧа ĐąŅƒĐ´ĐˇĐĩ адĐŊĐ°Đ˛Ņ–Ņ†ŅŒ.", @@ -71,15 +83,395 @@ "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_preview_description": "Đ’Ņ–Đ´Đ°Ņ€Ņ‹Ņ ŅŅŅ€ŅĐ´ĐŊŅĐŗĐ° ĐŋаĐŧĐĩŅ€Ņƒ С Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹ĐŧŅ– ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊŅ‹ĐŧŅ–, Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŋҀҋ ĐŋŅ€Đ°ĐŗĐģŅĐ´ĐˇĐĩ Đ°ŅĐžĐąĐŊĐ°ĐŗĐ° Ņ€ŅŅŅƒŅ€ŅŅƒ Ņ– Đ´ĐģŅ ĐŧĐ°ŅˆŅ‹ĐŊĐŊĐ°ĐŗĐ° ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊŅ", + "image_preview_quality_description": "Đ¯ĐēĐ°ŅŅ†ŅŒ ĐŋŅ€Đ°ŅĐ˛Ņ‹ ад 1 да 100. Đ§Ņ‹Đŧ Đ˛Ņ‹ŅˆŅĐš, ҂ҋĐŧ ĐģĐĩĐŋ҈, аĐģĐĩ ĐŋҀҋ ĐŗŅŅ‚Ņ‹Đŧ ŅŅ‚Đ˛Đ°Ņ€Đ°ŅŽŅ†Ņ†Đ° Ņ„Đ°ĐšĐģŅ‹ йОĐģŅŒŅˆĐ°ĐŗĐ° ĐŋаĐŧĐĩŅ€Ņƒ Ņ– ĐŧĐžĐļа СĐŊŅ–ĐˇŅ–Ņ†Ņ†Đ° Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†ŅŒ Đ˛ĐžĐ´ĐŗŅƒĐē҃ ĐŋҀҋĐēĐģадаĐŊĐŊŅ. ĐŽŅŅ‚Đ°ĐŊĐžŅžĐēа ĐŊŅ–ĐˇĐēĐ°ĐŗĐ° СĐŊĐ°Ņ‡ŅĐŊĐŊŅ ĐŧĐžĐļа ĐŋĐ°ŅžĐŋĐģŅ‹Đ˛Đ°Ņ†ŅŒ ĐŊа ŅĐēĐ°ŅŅ†ŅŒ ĐŧĐ°ŅˆŅ‹ĐŊĐŊĐ°ĐŗĐ° ĐŊĐ°Đ˛ŅƒŅ‡Đ°ĐŊĐŊŅ.", "image_preview_title": "НаĐģĐ°Đ´Ņ‹ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐŗĐ° ĐŋŅ€Đ°ĐŗĐģŅĐ´Ņƒ", "image_quality": "Đ¯ĐēĐ°ŅŅ†ŅŒ", "image_resolution": "Đ Đ°ĐˇĐ´ĐˇŅĐģŅĐģҌĐŊĐ°ŅŅ†ŅŒ", "image_settings": "НаĐģĐ°Đ´Ņ‹ Đ˛Ņ–Đ´Đ°Ņ€Ņ‹ŅĐ°", - "image_settings_description": "ĐšŅ–Ņ€ŅƒĐšŅ†Đĩ ŅĐēĐ°ŅŅ†ŅŽ Ņ– Ņ€Đ°ĐˇĐ´ĐˇŅĐģŅĐģҌĐŊĐ°ŅŅ†ŅŽ ŅĐŗĐĩĐŊĐĩŅ€Ņ‹Ņ€Đ°Đ˛Đ°ĐŊҋ҅ Đ˛Ņ–Đ´Đ°Ņ€Ņ‹ŅĐ°Ņž" + "image_settings_description": "ĐšŅ–Ņ€ŅƒĐšŅ†Đĩ ŅĐēĐ°ŅŅ†ŅŽ Ņ– Ņ€Đ°ĐˇĐ´ĐˇŅĐģŅĐģҌĐŊĐ°ŅŅ†ŅŽ ŅĐŗĐĩĐŊĐĩŅ€Ņ‹Ņ€Đ°Đ˛Đ°ĐŊҋ҅ Đ˛Ņ–Đ´Đ°Ņ€Ņ‹ŅĐ°Ņž", + "image_thumbnail_title": "НаĐģĐ°Đ´Ņ‹ ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€", + "job_concurrency": "{job} ĐēаĐŊĐēŅƒŅ€ŅĐŊŅ‚ĐŊĐ°ŅŅ†ŅŒ", + "job_created": "ЗадаĐŊĐŊĐĩ ŅŅ‚Đ˛ĐžŅ€Đ°ĐŊа", + "job_not_concurrency_safe": "Đ“ŅŅ‚Đ° СадаĐŊĐŊĐĩ ĐŊĐĩĐąŅŅĐŋĐĩ҇ĐŊаĐĩ Đ´ĐģŅ ĐēаĐŊĐēŅƒŅ€ŅĐŊŅ‚ĐŊĐ°ĐŗĐ°(адĐŊĐ°Ņ‡Đ°ŅĐžĐ˛Đ°ĐŗĐ°, ĐŋĐ°Ņ€Đ°ĐģĐĩĐģҌĐŊĐ°ĐŗĐ°) Đ˛Ņ‹ĐēаĐŊаĐŊĐŊŅ.", + "job_settings": "НаĐģĐ°Đ´Ņ‹ СадаĐŊĐŊŅŅž", + "job_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°Ņ†ŅŒ ĐŊаĐģадаĐŧŅ– адĐŊĐ°Ņ‡Đ°ŅĐžĐ˛Đ°ĐŗĐ° (ĐŋĐ°Ņ€Đ°ĐģĐĩĐģҌĐŊĐ°ĐŗĐ°) Đ˛Ņ‹ĐēаĐŊаĐŊĐŊŅ СадаĐŊĐŊŅ", + "job_status": "ĐĄŅ‚Đ°ĐŊĐžĐ˛Ņ–ŅˆŅ‡Đ° СадаĐŊĐŊŅ", + "library_created": "ĐĄŅ‚Đ˛ĐžŅ€Đ°ĐŊа ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēа: {library}", + "library_deleted": "Đ‘Ņ–ĐąĐģŅ–ŅŅ‚ŅĐēа Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊа", + "library_scanning": "ĐĄĐēаĐŊаваĐŊĐŊĐĩ Đŋа Ņ€Đ°ŅĐēĐģадСĐĩ", + "library_scanning_description": "НаĐģĐ°Đ´ĐˇŅŒŅ†Đĩ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ҁĐēаĐŊаваĐŊĐŊŅ Đ˛Đ°ŅˆĐ°Đš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–", + "library_scanning_enable_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ҁĐēаĐŊаваĐŊĐŊĐĩ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ– Đŋа Ņ€Đ°ŅĐēĐģадСĐĩ", + "library_settings": "ЗĐŊĐĩ҈ĐŊŅŅ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēа", + "library_settings_description": "НаĐģĐ°Đ´ĐˇŅŒŅ†Đĩ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ СĐŊĐĩ҈ĐŊŅĐš ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–", + "library_tasks_description": "ĐĄĐēаĐŊĐ°Đ˛Đ°Ņ†ŅŒ СĐŊĐĩ҈ĐŊŅ–Ņ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ– ĐŊа ĐŊĐ°ŅŅžĐŊĐ°ŅŅ†ŅŒ ĐŊĐžĐ˛Ņ‹Ņ… Ņ–/айО СĐŧĐĩĐŊĐĩĐŊҋ҅ Ņ€ŅŅŅƒŅ€ŅĐ°Ņž", + "library_watching_enable_description": "ĐĐ°ĐˇŅ–Ņ€Đ°Ņ†ŅŒ Са СĐŧĐĩĐŊаĐŧŅ– Ņ„Đ°ĐšĐģĐ°Ņž ҃ СĐŊĐĩ҈ĐŊŅ–Ņ… ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēĐ°Ņ…", + "library_watching_settings": "ĐĄĐ°Ņ‡Ņ‹Ņ†ŅŒ Са ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēаК (ŅĐēҁĐŋĐĩҀҋĐŧĐĩĐŊŅ‚Đ°ĐģҌĐŊŅ‹)", + "library_watching_settings_description": "ĐŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊа ŅĐ°Ņ‡Ņ‹Ņ†ŅŒ Са СĐŧĐĩĐŊаĐŧŅ– Ņž Ņ„Đ°ĐšĐģĐ°Ņ…", + "logging_enable_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐļŅƒŅ€ĐŊаĐģа", + "logging_level_description": "КаĐģŅ– ҃ĐēĐģŅŽŅ‡Đ°ĐŊа, ŅĐēŅ– ŅžĐˇŅ€ĐžĐ˛ĐĩĐŊҌ ĐļŅƒŅ€ĐŊаĐģŅĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Ņ†ŅŒ.", + "logging_settings": "Đ’ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐļŅƒŅ€ĐŊаĐģа", + "machine_learning_clip_model": "CLIP ĐŧĐ°Đ´ŅĐģҌ", + "machine_learning_clip_model_description": "Назва CLIP ĐŧĐ°Đ´ŅĐģŅ– ĐŋаĐēаСаĐŊа Ņ‚ŅƒŅ‚. Đ—Đ˛ŅŅ€ĐŊҖ҆Đĩ ŅžĐ˛Đ°ĐŗŅƒ, ŅˆŅ‚Đž ĐŋҀҋ СĐŧĐĩĐŊĐĩ ĐŧĐ°Đ´ŅĐģŅ– ĐŊĐĩĐ°ĐąŅ…ĐžĐ´ĐŊа ĐŋĐ°ŅžŅ‚ĐžŅ€ĐŊа СаĐŋŅƒŅŅ†Ņ–Ņ†ŅŒ СадаĐŊĐŊĐĩ \"Smart Search\" Đ´ĐģŅ ŅžŅŅ–Ņ… Đ˛Ņ–Đ´Đ°Ņ€Ņ‹ŅĐ°Ņž.", + "machine_learning_duplicate_detection": "Đ’Ņ‹ŅŅžĐģĐĩĐŊĐŊĐĩ ĐŋадОйĐŊҋ҅", + "map_dark_style": "ĐĻŅ‘ĐŧĐŊŅ‹ ҁ҂ҋĐģҌ", + "map_enable_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Ņ„ŅƒĐŊĐē҆ҋҖ ĐēĐ°Ņ€Ņ‚Ņ‹", + "map_gps_settings": "НаĐģĐ°Đ´Ņ‹ ĐēĐ°Ņ€Ņ‚Ņ‹ Ņ– GPS", + "map_light_style": "ХвĐĩŅ‚ĐģŅ‹ ҁ҂ҋĐģҌ", + "map_settings": "ĐšĐ°Ņ€Ņ‚Đ°", + "map_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– ĐēĐ°Ņ€Ņ‚Ņ‹", + "map_style_description": "URL-Đ°Đ´Ņ€Đ°Ņ style.json Ņ‚ŅĐŧŅ‹ ĐēĐ°Ņ€Ņ‚Ņ‹", + "metadata_settings": "НаĐģĐ°Đ´Ņ‹ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊҋ҅", + "oauth_button_text": "ĐĸŅĐēҁ҂ ĐēĐŊĐžĐŋĐēŅ–", + "oauth_settings": "OAuth", + "refreshing_all_libraries": "АйĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ŅžŅŅ–Ņ… ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē", + "registration": "Đ ŅĐŗŅ–ŅŅ‚Ņ€Đ°Ņ†Ņ‹Ņ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€Đ°", + "registration_description": "Đ’Ņ‹ С'ŅŅžĐģŅĐĩ҆ĐĩŅŅ ĐŋĐĩŅ€ŅˆŅ‹Đŧ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēаĐŧ ŅŅ–ŅŅ‚ŅĐŧŅ‹, Ņ‚Đ°Đŧ҃ Đ˛Ņ‹ ĐąŅƒĐ´ĐˇĐĩ҆Đĩ ĐŋŅ€Ņ‹ĐˇĐŊĐ°Ņ‡Đ°ĐŊŅ‹ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€Đ°Đŧ. Đ’Ņ‹ ĐąŅƒĐ´ĐˇĐĩ҆Đĩ адĐēĐ°ĐˇĐ˛Đ°Ņ†ŅŒ Са адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ†Ņ‹ĐšĐŊŅ‹Ņ ĐˇĐ°Đ´Đ°Ņ‡Ņ‹, а Ņ‚Đ°ĐēŅĐ°Đŧа ŅŅ‚Đ˛Đ°Ņ€Đ°Ņ†ŅŒ ĐŊĐžĐ˛Ņ‹Ņ… ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēĐ°Ņž.", + "require_password_change_on_login": "ĐŸĐ°Ņ‚Ņ€Đ°ĐąĐ°Đ˛Đ°Ņ†ŅŒ СĐŧŅĐŊŅ–Ņ†ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋҀҋ ĐŋĐĩŅ€ŅˆŅ‹Đŧ ŅƒĐ˛Đ°Ņ…ĐžĐ´ĐˇĐĩ Ņž ŅŅ–ŅŅ‚ŅĐŧ҃", + "reset_settings_to_default": "ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ ĐŊаĐģĐ°Đ´Ņ‹ да ĐŋŅ€Đ°Đ´Đ˛Ņ‹ĐˇĐŊĐ°Ņ‡Đ°ĐŊҋ҅", + "reset_settings_to_recent_saved": "ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ ĐŊаĐģĐ°Đ´Ņ‹ да ĐŊŅĐ´Đ°ŅžĐŊа ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊҋ҅", + "scanning_library": "ĐĄĐēаĐŊŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐēŅ–", + "server_external_domain_settings": "ЗĐŊĐĩ҈ĐŊŅ– даĐŧĐĩĐŊ", + "server_settings": "НаĐģĐ°Đ´Ņ‹ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "server_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", + "server_welcome_message": "ĐŸŅ€Ņ‹Đ˛Ņ–Ņ‚Đ°ĐģҌĐŊаĐĩ ĐŋавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ", + "server_welcome_message_description": "ПавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ, ŅĐēĐžĐĩ адĐģŅŽŅŅ‚Ņ€ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŊа ŅŅ‚Đ°Ņ€ĐžĐŊ҆ҋ ŅžĐ˛Đ°Ņ…ĐžĐ´Ņƒ.", + "system_settings": "ĐĄŅ–ŅŅ‚ŅĐŧĐŊŅ‹Ņ ĐŊаĐģĐ°Đ´Ņ‹", + "tag_cleanup_job": "ĐŅ‡Ņ‹ŅŅ‚Đēа Ņ‚ŅĐŗĐ°Ņž", + "template_email_preview": "ПĐĩŅ€Đ°Đ´ĐŋŅ€Đ°ĐŗĐģŅĐ´", + "theme_settings": "НаĐģĐ°Đ´Ņ‹ Ņ‚ŅĐŧŅ‹", + "transcoding_acceleration_nvenc": "NVENC (ĐŋĐ°Ņ‚Ņ€Đ°ĐąŅƒĐĩŅ†Ņ†Đ° Đ˛Ņ–Đ´ŅĐ°ĐēĐ°Ņ€Ņ‚Đ° NVIDIA)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_containers": "ĐŸŅ€Ņ‹ĐŊŅŅ‚Ņ‹Ņ ĐēаĐŊŅ‚ŅĐšĐŊĐĩҀҋ", + "transcoding_accepted_video_codecs": "ĐŸŅ€Ņ‹ĐŊŅŅ‚Ņ‹Ņ Đ˛Ņ–Đ´ŅĐ°ĐēĐžĐ´ŅĐēŅ–", + "transcoding_advanced_options_description": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ, ŅĐēŅ–Ņ йОĐģŅŒŅˆĐ°ŅŅ†Ņ– ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēĐ°Ņž ĐŊĐĩ Ņ‚Ņ€ŅĐąĐ° СĐŧŅĐŊŅŅ†ŅŒ", + "transcoding_audio_codec": "ĐŅƒĐ´Ņ‹ŅĐēĐžĐ´ŅĐē", + "transcoding_encoding_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐēĐ°Đ´ĐˇŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅ", + "transcoding_video_codec": "Đ’Ņ–Đ´ŅĐ°ĐēĐžĐ´ŅĐē", + "trash_enabled_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Ņ„ŅƒĐŊĐē҆ҋҖ ҁĐŧĐĩŅ‚ĐŊҖ҆ҋ", + "trash_number_of_days": "КоĐģҌĐēĐ°ŅŅ†ŅŒ Đ´ĐˇŅ‘ĐŊ", + "trash_settings": "НаĐģĐ°Đ´Ņ‹ ҁĐŧĐĩŅ‚ĐŊҖ҆ҋ", + "trash_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– ҁĐŧĐĩŅ‚ĐŊҖ҆ҋ", + "user_cleanup_job": "ĐŅ‡Ņ‹ŅŅ‚Đēа ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "user_management": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēаĐŧŅ–", + "user_password_has_been_reset": "ĐŸĐ°Ņ€ĐžĐģҌ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа ĐąŅ‹Ņž ҁĐēŅ–ĐŊŅƒŅ‚Ņ‹:", + "user_password_reset_description": "Đ—Đ°Đ´Đ°ĐšŅ†Đĩ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đē҃ Ņ‡Đ°ŅĐžĐ˛Ņ‹ ĐŋĐ°Ņ€ĐžĐģҌ Ņ– ĐŋавĐĩдаĐŧҖ҆Đĩ ŅĐŧ҃, ŅˆŅ‚Đž ĐŋҀҋ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊŅ‹Đŧ ŅƒĐ˛Đ°Ņ…ĐžĐ´ĐˇĐĩ Ņž ŅŅ–ŅŅ‚ŅĐŧ҃ ŅĐŧ҃ Ņ‚Ņ€ŅĐąĐ° ĐąŅƒĐ´ĐˇĐĩ СĐŧŅĐŊŅ–Ņ†ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ.", + "user_restore_description": "ĐŖĐģŅ–ĐēĐžĐ˛Ņ‹ СаĐŋҖҁ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа {user} ĐąŅƒĐ´ĐˇĐĩ адĐŊĐžŅžĐģĐĩĐŊŅ‹.", + "user_settings": "НаĐģĐ°Đ´Ņ‹ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "user_settings_description": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ĐŊаĐģадаĐŧŅ– ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "user_successfully_removed": "ĐšĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đē {email} ĐąŅ‹Ņž ĐŋĐ°ŅĐŋŅŅ…ĐžĐ˛Đ° Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹.", + "version_check_enabled_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐŋŅ€Đ°Đ˛ĐĩŅ€Đē҃ вĐĩҀҁҖҖ", + "version_check_implications": "Đ¤ŅƒĐŊĐēŅ†Ņ‹Ņ ĐŋŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– вĐĩҀҁҖҖ ĐŋĐĩŅ€Ņ‹ŅĐ´Ņ‹Ņ‡ĐŊа ĐˇĐ˛ŅŅ€Ņ‚Đ°ĐĩŅ†Ņ†Đ° да github.com", + "version_check_settings": "ĐŸŅ€Đ°Đ˛ĐĩŅ€Đēа вĐĩҀҁҖҖ", + "version_check_settings_description": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ/адĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ аĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅ– ай ĐŊОваК вĐĩҀҁҖҖ" }, + "admin_email": "Đ­ĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊĐ°Ņ ĐŋĐžŅˆŅ‚Đ° адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€Đ°", + "admin_password": "ĐŸĐ°Ņ€ĐžĐģҌ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€Đ°", + "administration": "ĐšŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°Đŧ", + "advanced": "ĐŸĐ°ŅˆŅ‹Ņ€Đ°ĐŊŅ‹Ņ", + "advanced_settings_log_level_title": "ĐŖĐˇŅ€ĐžĐ˛ĐĩĐŊҌ Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ ĐļŅƒŅ€ĐŊаĐģа: {level}", + "advanced_settings_proxy_headers_title": "Đ—Đ°ĐŗĐ°ĐģĐžŅžĐēŅ– ĐŋŅ€ĐžĐēҁҖ", + "advanced_settings_tile_subtitle": "ĐŸĐ°ŅˆŅ‹Ņ€Đ°ĐŊŅ‹Ņ ĐŊаĐģĐ°Đ´Ņ‹ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "advanced_settings_troubleshooting_subtitle": "ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Đ´Đ°Đ´Đ°Ņ‚ĐēĐžĐ˛Ņ‹Ņ Ņ„ŅƒĐŊĐē҆ҋҖ Đ´ĐģŅ Đ˛Ņ‹ĐŋŅ€Đ°ŅžĐģĐĩĐŊĐŊŅ ĐŊĐĩĐŋаĐģадаĐē", + "advanced_settings_troubleshooting_title": "Đ’Ņ‹ĐŋŅ€Đ°ŅžĐģĐĩĐŊĐŊĐĩ ĐŊĐĩĐŋаĐģадаĐē", + "age_months": "ĐŖĐˇŅ€ĐžŅŅ‚ {months, plural, one {# ĐŧĐĩŅŅŅ†} few {# ĐŧĐĩŅŅŅ†Ņ‹} many {# ĐŧĐĩŅŅŅ†Đ°Ņž} other {# ĐŧĐĩŅŅŅ†Đ°Ņž}}", + "age_year_months": "ĐŖĐˇŅ€ĐžŅŅ‚ 1 ĐŗĐžĐ´, {months, plural, one {# ĐŧĐĩŅŅŅ†} few {# ĐŧĐĩŅŅŅ†Ņ‹} many {# ĐŧĐĩŅŅŅ†Đ°Ņž} other {# ĐŧĐĩŅŅŅ†Đ°Ņž}}", + "age_years": "{years, plural, other {ĐŖĐˇŅ€ĐžŅŅ‚ #}}", + "album_added": "АĐģŅŒĐąĐžĐŧ дададСĐĩĐŊŅ‹", + "album_cover_updated": "ВоĐēĐģадĐēа аĐģŅŒĐąĐžĐŧа айĐŊĐžŅžĐģĐĩĐŊа", + "album_delete_confirmation": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ аĐģŅŒĐąĐžĐŧ {album}?", + "album_delete_confirmation_description": "КаĐģŅ– ĐŗŅŅ‚Ņ‹ аĐģŅŒĐąĐžĐŧ Đ°ĐąĐ°ĐŗŅƒĐģĐĩĐŊŅ‹, Ņ–ĐŊŅˆŅ‹Ņ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēŅ– йОĐģҌ҈ ĐŊĐĩ СĐŧĐžĐŗŅƒŅ†ŅŒ Đ°Ņ‚Ņ€Ņ‹ĐŧĐ°Ņ†ŅŒ да ŅĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ.", + "album_deleted": "АĐģŅŒĐąĐžĐŧ Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹", + "album_info_card_backup_album_excluded": "ВĐĢКЛЮЧАНĐĢ", + "album_info_card_backup_album_included": "ĐŖĐšĐ›ĐŽĐ§ĐĐĐĢ", + "album_info_updated": "ІĐŊŅ„Đ°Ņ€ĐŧĐ°Ņ†Ņ‹Ņ ĐŋŅ€Đ° аĐģŅŒĐąĐžĐŧ айĐŊĐžŅžĐģĐĩĐŊа", + "album_leave": "ПаĐēŅ–ĐŊŅƒŅ†ŅŒ аĐģŅŒĐąĐžĐŧ?", + "album_leave_confirmation": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ ĐŋаĐēŅ–ĐŊŅƒŅ†ŅŒ {album}?", + "album_name": "Назва аĐģŅŒĐąĐžĐŧа", + "album_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ аĐģŅŒĐąĐžĐŧа", + "album_remove_user": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа?", + "album_remove_user_confirmation": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ {user}?", + "album_search_not_found": "Па Đ˛Đ°ŅˆŅ‹Đŧ СаĐŋҋ҆Đĩ ĐŊĐĩ СĐŊОКдСĐĩĐŊа аĐģŅŒĐąĐžĐŧĐ°Ņž", + "album_share_no_users": "ЗдаĐĩŅ†Ņ†Đ°, Đ˛Ņ‹ ĐŋĐ°Đ´ĐˇŅĐģŅ–ĐģŅ–ŅŅ ĐŗŅŅ‚Ņ‹Đŧ аĐģŅŒĐąĐžĐŧаĐŧ С ŅƒŅŅ–ĐŧŅ– ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēаĐŧŅ–, айО Ņž Đ˛Đ°Ņ ĐŊŅĐŧа ĐŊŅ–Đ˛ĐžĐ´ĐŊĐ°ĐŗĐ° ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа, С ŅĐēŅ–Đŧ ĐŧĐžĐļĐŊа ĐŋĐ°Đ´ĐˇŅĐģŅ–Ņ†Ņ†Đ°.", + "album_updated": "АĐģŅŒĐąĐžĐŧ айĐŊĐžŅžĐģĐĩĐŊŅ‹", + "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_title": "НĐĩ ŅžĐ´Đ°ĐģĐžŅŅ СĐŧŅĐŊŅ–Ņ†ŅŒ ĐŊĐ°ĐˇĐ˛Ņƒ аĐģŅŒĐąĐžĐŧа", + "album_viewer_appbar_share_leave": "ПаĐēŅ–ĐŊŅƒŅ†ŅŒ аĐģŅŒĐąĐžĐŧ", + "album_viewer_appbar_share_to": "ĐĐąĐ°ĐŗŅƒĐģŅ–Ņ†ŅŒ С", + "album_viewer_page_share_add_users": "Đ”Đ°Đ´Đ°Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēĐ°Ņž", + "album_with_link_access": "ДазвоĐģŅ–Ņ†ŅŒ ŅƒŅŅ–Đŧ, Ņ…Ņ‚Đž ĐŧаĐĩ ҁĐŋĐ°ŅŅ‹ĐģĐē҃, ĐąĐ°Ņ‡Ņ‹Ņ†ŅŒ Ņ„ĐžŅ‚Đ° Ņ– ĐģŅŽĐ´ĐˇĐĩĐš ҃ ĐŗŅŅ‚Ņ‹Đŧ аĐģŅŒĐąĐžĐŧĐĩ.", + "albums": "АĐģŅŒĐąĐžĐŧŅ‹", + "albums_count": "{count, plural, one {1 аĐģŅŒĐąĐžĐŧ} few {{count, number} аĐģŅŒĐąĐžĐŧŅ‹} many {{count, number} аĐģŅŒĐąĐžĐŧĐ°Ņž} other {{count, number} аĐģŅŒĐąĐžĐŧĐ°Ņž}}", + "albums_default_sort_order": "ĐŸŅ€Đ°Đ´Đ˛Ņ‹ĐˇĐŊĐ°Ņ‡Đ°ĐŊŅ‹ ĐŋĐ°Ņ€Đ°Đ´Đ°Đē ŅĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧĐ°Ņž", + "albums_on_device_count": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ĐŋҀҋĐģадСĐĩ ({count})", + "all": "ĐŖŅĐĩ", + "all_albums": "ĐŖŅĐĩ аĐģŅŒĐąĐžĐŧŅ‹", + "all_people": "ĐŖŅĐĩ ĐģŅŽĐ´ĐˇŅ–", + "all_videos": "ĐŖŅĐĩ Đ˛Ņ–Đ´ŅĐ°", + "allow_dark_mode": "ДазвоĐģŅ–Ņ†ŅŒ ҆ґĐŧĐŊŅ‹ Ņ€ŅĐļŅ‹Đŧ", + "allow_edits": "ДазвоĐģŅ–Ņ†ŅŒ Ņ€ŅĐ´Đ°ĐŗĐ°Đ˛Đ°ĐŊĐŊĐĩ", + "alt_text_qr_code": "Đ’Ņ–Đ´Đ°Ņ€Ņ‹Ņ QR-ĐēОда", + "anti_clockwise": "ĐĄŅƒĐŋŅ€Đ°Ņ†ŅŒ ĐŗĐ°Đ´ĐˇŅ–ĐŊĐŊŅ–ĐēаваК ŅŅ‚Ņ€ŅĐģĐēŅ–", + "api_key": "КĐģŅŽŅ‡ API", + "api_key_empty": "Назва ĐēĐģŅŽŅ‡Đ° API ĐŊĐĩ ĐŋĐ°Đ˛Ņ–ĐŊĐŊа ĐąŅ‹Ņ†ŅŒ ĐŋŅƒŅŅ‚ĐžĐš", + "api_keys": "КĐģŅŽŅ‡Ņ‹ API", + "app_bar_signout_dialog_content": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ Đ˛Ņ‹ĐšŅŅ†Ņ–?", + "app_bar_signout_dialog_ok": "ĐĸаĐē", + "app_bar_signout_dialog_title": "Đ’Ņ‹ĐšŅŅ†Ņ–", + "app_settings": "НаĐģĐ°Đ´Ņ‹ ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹", + "archive": "ĐŅ€Ņ…Ņ–Ņž", + "archive_page_title": "ĐŅ€Ņ…Ņ–Ņž ({count})", + "archive_size": "ПаĐŧĐĩŅ€ Đ°Ņ€Ņ…Ņ–Đ˛Đ°", + "are_these_the_same_person": "ĐĻŅ– ĐŗŅŅ‚Đ° Đ°Đ´ĐˇŅ–ĐŊ Ņ– Ņ‚ĐžĐš Đļа Ņ‡Đ°ĐģавĐĩĐē?", + "are_you_sure_to_do_this": "Đ’Ņ‹ ŅžĐŋŅŅžĐŊĐĩĐŊŅ‹, ŅˆŅ‚Đž Ņ…ĐžŅ‡Đ°Ņ†Đĩ ĐŗŅŅ‚Đ° ĐˇŅ€Đ°ĐąŅ–Ņ†ŅŒ?", + "asset_added_to_album": "ДададзĐĩĐŊа Ņž аĐģŅŒĐąĐžĐŧ", + "asset_adding_to_album": "ДадаваĐŊĐŊĐĩ Ņž аĐģŅŒĐąĐžĐŧâ€Ļ", + "asset_skipped": "ĐŸŅ€Đ°ĐŋŅƒŅˆŅ‡Đ°ĐŊа", + "asset_skipped_in_trash": "ĐŖ ҁĐŧĐĩŅ‚ĐŊҖ҆ҋ", + "asset_uploaded": "ЗаĐŋаĐŧĐŋаваĐŊа", + "asset_uploading": "ЗаĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩâ€Ļ", + "assets_were_part_of_albums_count": "{count, plural, one {АĐēŅ‚Ņ‹Ņž ҃ĐļĐž ĐąŅ‹Ņž} other {АĐēŅ‚Ņ‹Đ˛Ņ‹ ҃ĐļĐž ĐąŅ‹ĐģŅ–}} Ņ‡Đ°ŅŅ‚ĐēаК аĐģŅŒĐąĐžĐŧ҃", + "authorized_devices": "ĐŅžŅ‚Đ°Ņ€Ņ‹ĐˇĐ°Đ˛Đ°ĐŊŅ‹Ņ ĐŋҀҋĐģĐ°Đ´Ņ‹", + "automatic_endpoint_switching_subtitle": "ПадĐēĐģŅŽŅ‡Đ°Ņ†Ņ†Đ° ĐģаĐēаĐģҌĐŊа Đŋа Đ˛Ņ‹ĐģŅƒŅ‡Đ°ĐŊŅ‹Đŧ Wi-Fi, ĐēаĐģŅ– ĐŗŅŅ‚Đ° ĐŧĐ°ĐŗŅ‡Ņ‹Đŧа, Ņ– Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Ņ†ŅŒ аĐģŅŒŅ‚ŅŅ€ĐŊĐ°Ņ‚Ņ‹ŅžĐŊŅ‹Ņ ĐŋадĐēĐģŅŽŅ‡ŅĐŊĐŊŅ Ņž Ņ–ĐŊŅˆŅ‹Ņ… ĐŧĐĩŅŅ†Đ°Ņ…", + "automatic_endpoint_switching_title": "ĐŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊаĐĩ ĐŋĐĩŅ€Đ°ĐēĐģŅŽŅ‡ŅĐŊĐŊĐĩ URL", + "back": "Назад", + "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ĐŋҀҋĐģадСĐĩ ({count})", + "backup_all": "ĐŖŅĐĩ", + "backup_controller_page_background_wifi": "ĐĸĐžĐģҌĐēŅ– ĐŋŅ€Đ°Đˇ Wi-Fi", + "buy": "ĐšŅƒĐŋŅ–Ņ†ŅŒ Immich", + "cache_settings_clear_cache_button": "ĐŅ‡Ņ‹ŅŅ†Ņ–Ņ†ŅŒ ĐēŅŅˆ", + "cache_settings_tile_title": "ЛаĐēаĐģҌĐŊаĐĩ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°", + "cancel": "ĐĄĐēĐ°ŅĐ°Đ˛Đ°Ņ†ŅŒ", + "cancel_search": "ĐĄĐēĐ°ŅĐ°Đ˛Đ°Ņ†ŅŒ ĐŋĐžŅˆŅƒĐē", + "canceled": "ĐĄĐēĐ°ŅĐ°Đ˛Đ°ĐŊа", + "city": "Đ“ĐžŅ€Đ°Đ´", + "clear": "ĐŅ‡Ņ‹ŅŅ†Ņ–Ņ†ŅŒ", + "clear_all": "ĐŅ‡Ņ‹ŅŅ†Ņ–Ņ†ŅŒ ŅƒŅŅ‘", + "client_cert_dialog_msg_confirm": "ОК", + "client_cert_enter_password": "ĐŖĐ˛ŅĐ´ĐˇŅ–Ņ†Đĩ ĐŋĐ°Ņ€ĐžĐģҌ", + "client_cert_import": "ІĐŧĐŋĐ°Ņ€Ņ‚", + "close": "ЗаĐēŅ€Ņ‹Ņ†ŅŒ", + "collapse": "Đ—ĐŗĐ°Ņ€ĐŊŅƒŅ†ŅŒ", + "collapse_all": "Đ—ĐŗĐ°Ņ€ĐŊŅƒŅ†ŅŒ ŅƒŅŅ‘", + "color": "КоĐģĐĩŅ€", + "color_theme": "КоĐģĐĩŅ€Đ°Đ˛Đ°Ņ Ņ‚ŅĐŧа", + "continue": "ĐŸŅ€Đ°Ņ†ŅĐŗĐŊŅƒŅ†ŅŒ", + "control_bottom_app_bar_create_new_album": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ ĐŊĐžĐ˛Ņ‹ аĐģŅŒĐąĐžĐŧ", + "control_bottom_app_bar_delete_from_immich": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ С Immich", + "control_bottom_app_bar_delete_from_local": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ С ĐŋҀҋĐģĐ°Đ´Ņ‹", + "control_bottom_app_bar_edit_location": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐŧĐĩŅŅ†Đ°ĐˇĐŊĐ°Ņ…ĐžĐ´ĐļаĐŊĐŊĐĩ", + "country": "ĐšŅ€Đ°Ņ–ĐŊа", + "cover": "ВоĐēĐģадĐēа", + "covers": "ВоĐēĐģадĐēŅ–", + "create": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ", + "create_album": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ аĐģŅŒĐąĐžĐŧ", + "create_album_page_untitled": "БĐĩС ĐŊĐ°ĐˇĐ˛Ņ‹", + "create_library": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃", + "create_link": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐē҃", + "create_new_user": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ ĐŊĐžĐ˛Đ°ĐŗĐ° ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "create_tag": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ Ņ‚ŅĐŗ", + "create_user": "ĐĄŅ‚Đ˛Đ°Ņ€Ņ‹Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "dark": "ĐĻŅ‘ĐŧĐŊĐ°Ņ", + "day": "ДзĐĩĐŊҌ", + "delete": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ", + "delete_album": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ аĐģŅŒĐąĐžĐŧ", + "delete_dialog_ok_force": "ĐŖŅŅ‘ адĐŊĐž Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ", + "delete_dialog_title": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐŊĐ°ĐˇĐ°ŅžĐļĐ´Ņ‹", + "delete_face": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ Ņ‚Đ˛Đ°Ņ€", + "delete_key": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐģŅŽŅ‡", + "delete_library": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐąŅ–ĐąĐģŅ–ŅŅ‚ŅĐē҃", + "delete_link": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐē҃", + "delete_local_dialog_ok_force": "ĐŖŅŅ‘ адĐŊĐž Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ", + "delete_others": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ Ņ–ĐŊŅˆŅ‹Ņ", + "delete_tag": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ Ņ‚ŅĐŗ", + "delete_user": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "discord": "Discord", + "documentation": "ДаĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ‹Ņ", + "done": "Đ“Đ°Ņ‚ĐžĐ˛Đ°", + "download": "ĐĄĐŋаĐŧĐŋĐ°Đ˛Đ°Ņ†ŅŒ", + "download_canceled": "ĐĄĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ ҁĐēĐ°ŅĐ°Đ˛Đ°ĐŊа", + "download_complete": "ĐĄĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ СавĐĩŅ€ŅˆĐ°ĐŊа", + "download_enqueue": "ĐĄĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ дададСĐĩĐŊа Ņž Ņ‡Đ°Ņ€ĐŗŅƒ", + "downloading": "ĐĄĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ", + "edit": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ", + "edit_album": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ аĐģŅŒĐąĐžĐŧ", + "edit_avatar": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", + "edit_date": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ Đ´Đ°Ņ‚Ņƒ", + "edit_date_and_time": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ– Ņ‡Đ°Ņ", + "edit_description": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ аĐŋŅ–ŅĐ°ĐŊĐŊĐĩ", + "edit_description_prompt": "Đ’Ņ‹ĐąĐĩҀҋ҆Đĩ ĐŊОваĐĩ аĐŋŅ–ŅĐ°ĐŊĐŊĐĩ:", + "edit_faces": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ Ņ‚Đ˛Đ°Ņ€Ņ‹", + "edit_import_path": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Ņƒ", + "edit_import_paths": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ҈ĐģŅŅ…Ņ– Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Ņƒ", + "edit_key": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐēĐģŅŽŅ‡", + "edit_link": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐē҃", + "edit_location": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐŧĐĩŅŅ†Đ°ĐˇĐŊĐ°Ņ…ĐžĐ´ĐļаĐŊĐŊĐĩ", + "edit_location_dialog_title": "МĐĩŅŅ†Đ°ĐˇĐŊĐ°Ņ…ĐžĐ´ĐļаĐŊĐŊĐĩ", + "edit_name": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐŊĐ°ĐˇĐ˛Ņƒ", + "edit_people": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐģŅŽĐ´ĐˇĐĩĐš", + "edit_tag": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ Ņ‚ŅĐŗ", + "edit_title": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐˇĐ°ĐŗĐ°ĐģОваĐē", + "edit_user": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "edited": "ĐĐ´Ņ€ŅĐ´Đ°ĐŗĐ°Đ˛Đ°ĐŊа", + "editor": "Đ ŅĐ´Đ°ĐēŅ‚Đ°Ņ€", + "editor_close_without_save_prompt": "ЗĐŧĐĩĐŊŅ‹ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ†ŅŒ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊŅ‹", + "editor_close_without_save_title": "ЗаĐēŅ€Ņ‹Ņ†ŅŒ Ņ€ŅĐ´Đ°ĐēŅ‚Đ°Ņ€?", + "editor_crop_tool_h2_aspect_ratios": "ĐĄŅƒĐ°Đ´ĐŊĐžŅŅ–ĐŊŅ‹ йаĐēĐžŅž", + "editor_crop_tool_h2_rotation": "ĐŸĐ°Đ˛Đ°Ņ€ĐžŅ‚", + "error": "ПаĐŧŅ‹ĐģĐēа", + "error_saving_image": "ПаĐŧŅ‹ĐģĐēа: {error}", + "exif": "Exif", + "exif_bottom_sheet_description": "Đ”Đ°Đ´Đ°Ņ†ŅŒ аĐŋŅ–ŅĐ°ĐŊĐŊĐĩ...", + "favorite": "ĐŖ Đ°ĐąŅ€Đ°ĐŊŅ‹Đŧ", + "favorite_or_unfavorite_photo": "Đ”Đ°Đ´Đ°Ņ†ŅŒ айО Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ Ņ„ĐžŅ‚Đ° С Đ°ĐąŅ€Đ°ĐŊĐ°ĐŗĐ°", + "favorites": "ĐĐąŅ€Đ°ĐŊŅ‹Ņ", + "file_name": "Назва Ņ„Đ°ĐšĐģа", + "filename": "Назва Ņ„Đ°ĐšĐģа", + "filetype": "ĐĸŅ‹Đŋ Ņ„Đ°ĐšĐģа", + "filter": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€", + "forward": "НаĐŋĐĩŅ€Đ°Đ´", + "gcast_enabled": "Google Cast", + "general": "ĐĐŗŅƒĐģҌĐŊŅ‹Ņ", + "go_back": "Назад", + "go_to_folder": "ПĐĩŅ€Đ°ĐšŅŅ†Ņ– да ĐŋаĐŋĐēŅ–", + "hi_user": "Đ’Ņ–Ņ‚Đ°ĐĩĐŧ, {name} ({email})", + "hide_all_people": "ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ŅƒŅŅ–Ņ… ĐģŅŽĐ´ĐˇĐĩĐš", + "hide_gallery": "ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŗĐ°ĐģĐĩŅ€ŅŅŽ", + "hide_named_person": "ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ {name}", + "hide_password": "ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", + "hide_person": "ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Ņ‡Đ°ĐģавĐĩĐēа", + "image_viewer_page_state_provider_download_started": "ĐĄĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ ĐŋĐ°Ņ‡Đ°ĐģĐžŅŅ", + "immich_logo": "Đ›Đ°ĐŗĐ°Ņ‚Ņ‹Đŋ Immich", + "interval": { + "day_at_onepm": "КоĐļĐŊŅ‹ дСĐĩĐŊҌ а 13-Đš ĐŗĐ°Đ´ĐˇŅ–ĐŊĐĩ", + "hours": "{hours, plural, one {КоĐļĐŊŅƒŅŽ ĐŗĐ°Đ´ĐˇŅ–ĐŊ҃} few {КоĐļĐŊŅ‹Ņ {hours, number} ĐŗĐ°Đ´ĐˇŅ–ĐŊŅ‹} many {КоĐļĐŊŅ‹Ņ {hours, number} ĐŗĐ°Đ´ĐˇŅ–ĐŊ} other {КоĐļĐŊŅ‹Ņ {hours, number} ĐŗĐ°Đ´ĐˇŅ–ĐŊ}}", + "night_at_midnight": "КоĐļĐŊŅƒŅŽ ĐŊĐžŅ‡ аĐŋĐžŅžĐŊĐ°Ņ‡Ņ‹", + "night_at_twoam": "КоĐļĐŊŅƒŅŽ ĐŊĐžŅ‡ а 2-Đš ĐŗĐ°Đ´ĐˇŅ–ĐŊĐĩ" + }, + "language": "Мова", + "library": "Đ‘Ņ–ĐąĐģŅ–ŅŅ‚ŅĐēа", + "light": "ХвĐĩŅ‚ĐģĐ°Ņ", + "login_form_back_button_text": "Назад", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_password_hint": "ĐŋĐ°Ņ€ĐžĐģҌ", + "login_form_save_login": "Đ—Đ°ŅŅ‚Đ°Đ˛Đ°Ņ†Ņ†Đ° Ņž ŅŅ–ŅŅ‚ŅĐŧĐĩ", + "main_menu": "ГаĐģĐžŅžĐŊаĐĩ ĐŧĐĩĐŊŅŽ", + "map_location_dialog_yes": "ĐĸаĐē", + "map_settings_dark_mode": "ĐĻŅ‘ĐŧĐŊŅ‹ Ņ€ŅĐļŅ‹Đŧ", + "map_settings_date_range_option_day": "АĐŋĐžŅˆĐŊŅ–Ņ 24 ĐŗĐ°Đ´ĐˇŅ–ĐŊŅ‹", + "map_settings_date_range_option_days": "АĐŋĐžŅˆĐŊŅ–Ņ… Đ´ĐˇŅ‘ĐŊ: {days}", + "map_settings_date_range_option_year": "АĐŋĐžŅˆĐŊŅ– ĐŗĐžĐ´", + "map_settings_date_range_option_years": "АĐŋĐžŅˆĐŊŅ–Ņ… ĐŗĐžĐ´: {years}", + "map_settings_dialog_title": "НаĐģĐ°Đ´Ņ‹ ĐēĐ°Ņ€Ņ‚Ņ‹", + "map_settings_theme_settings": "ĐĸŅĐŧа ĐēĐ°Ņ€Ņ‚Ņ‹", + "menu": "МĐĩĐŊŅŽ", + "minute": "ĐĨĐ˛Ņ–ĐģŅ–ĐŊа", + "month": "МĐĩŅŅŅ†", + "monthly_title_text_date_format": "MMMM y", + "my_albums": "МаĐĩ аĐģŅŒĐąĐžĐŧŅ‹", + "name": "ІĐŧŅ", + "name_or_nickname": "ІĐŧŅ айО ĐŋҁĐĩŅžĐ´Đ°ĐŊŅ–Đŧ", + "next": "ДаĐģĐĩĐš", + "no": "НĐĩ", + "offline": "Па-Са ҁĐĩŅ‚ĐēаК", + "ok": "ОК", + "online": "ĐŖ ҁĐĩ҂҆ҋ", + "open": "АдĐēŅ€Ņ‹Ņ†ŅŒ", + "or": "айО", + "partner_list_user_photos": "Đ¤ĐžŅ‚Đ° ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа {user}", + "pause": "ĐŸŅ€Ņ‹ĐŋŅ‹ĐŊŅ–Ņ†ŅŒ", + "people": "Đ›ŅŽĐ´ĐˇŅ–", + "permission_onboarding_back": "Назад", + "permission_onboarding_continue_anyway": "ĐŖŅŅ‘ адĐŊĐž ĐŋŅ€Đ°Ņ†ŅĐŗĐŊŅƒŅ†ŅŒ", + "photos": "Đ¤ĐžŅ‚Đ°", + "photos_and_videos": "Đ¤ĐžŅ‚Đ° Ņ– Đ˛Ņ–Đ´ŅĐ°", + "place": "МĐĩŅŅ†Đ°", + "places": "МĐĩҁ҆ҋ", + "port": "ĐŸĐžŅ€Ņ‚", + "previous": "ПаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ", + "profile": "ĐŸŅ€ĐžŅ„Ņ–ĐģҌ", + "profile_drawer_app_logs": "Đ–ŅƒŅ€ĐŊаĐģŅ‹", + "profile_drawer_github": "GitHub", + "purchase_button_buy": "ĐšŅƒĐŋŅ–Ņ†ŅŒ", + "purchase_button_buy_immich": "ĐšŅƒĐŋŅ–Ņ†ŅŒ Immich", + "purchase_button_select": "Đ’Ņ‹ĐąŅ€Đ°Ņ†ŅŒ", + "readonly_mode_disabled": "Đ’Ņ‹ĐēĐģŅŽŅ‡Đ°ĐŊŅ‹ Ņ€ŅĐļŅ‹Đŧ Ņ‚ĐžĐģҌĐēŅ– Đ´ĐģŅ Ņ‡Ņ‹Ņ‚Đ°ĐŊĐŊŅ", + "readonly_mode_enabled": "ĐŖĐēĐģŅŽŅ‡Đ°ĐŊŅ‹ Ņ€ŅĐļŅ‹Đŧ Ņ‚ĐžĐģҌĐēŅ– Đ´ĐģŅ Ņ‡Ņ‹Ņ‚Đ°ĐŊĐŊŅ", + "reassign": "ПĐĩŅ€Đ°ĐŋŅ€Ņ‹ĐˇĐŊĐ°Ņ‡Ņ‹Ņ†ŅŒ", + "reassing_hint": "ĐŸŅ€Ņ‹ĐŋŅ–ŅĐ°Ņ†ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊŅ‹Ņ аĐēŅ‚Ņ‹Đ˛Ņ‹ ҖҁĐŊŅƒŅŽŅ‡Đ°Đš Đ°ŅĐžĐąĐĩ", + "recent": "ĐŅĐ´Đ°ŅžĐŊŅ–", + "recent-albums": "ĐŅĐ´Đ°ŅžĐŊŅ–Ņ аĐģŅŒĐąĐžĐŧŅ‹", + "recent_searches": "ĐŅĐ´Đ°ŅžĐŊŅ–Ņ ĐŋĐžŅˆŅƒĐēŅ–", + "recently_added": "ĐŅĐ´Đ°ŅžĐŊа дададСĐĩĐŊа", + "refresh_faces": "АйĐŊĐ°Đ˛Ņ–Ņ†ŅŒ Ņ‚Đ˛Đ°Ņ€Ņ‹", + "remove": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ", + "remove_from_album": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ С аĐģŅŒĐąĐžĐŧа", + "remove_from_favorites": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ С Đ°ĐąŅ€Đ°ĐŊҋ҅", + "remove_tag": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ Ņ‚ŅĐŗ", + "remove_url": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ URL-Đ°Đ´Ņ€Đ°Ņ", + "remove_user": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "rename": "ПĐĩŅ€Đ°ĐšĐŧĐĩĐŊĐ°Đ˛Đ°Ņ†ŅŒ", + "repository": "Đ ŅĐŋĐ°ĐˇŅ–Ņ‚ĐžŅ€Ņ‹Đš", + "reset": "ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ", + "reset_password": "ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", + "restore": "АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ", + "restore_all": "АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ŅƒŅŅ‘", + "restore_user": "АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", + "resume": "ĐŖĐˇĐŊĐ°Đ˛Ņ–Ņ†ŅŒ", + "role": "Đ ĐžĐģŅ", + "role_editor": "Đ ŅĐ´Đ°ĐēŅ‚Đ°Ņ€", + "role_viewer": "ГĐģŅĐ´Đ°Ņ‡", + "save": "Đ—Đ°Ņ…Đ°Đ˛Đ°Ņ†ŅŒ", + "save_to_gallery": "Đ—Đ°Ņ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ ĐŗĐ°ĐģĐĩŅ€ŅŅŽ", + "search_filter_date": "Đ”Đ°Ņ‚Đ°", + "search_filter_location": "МĐĩŅŅ†Đ°ĐˇĐŊĐ°Ņ…ĐžĐ´ĐļаĐŊĐŊĐĩ", + "search_filter_location_title": "Đ’Ņ‹ĐąĐĩҀҋ҆Đĩ ĐŧĐĩŅŅ†Đ°ĐˇĐŊĐ°Ņ…ĐžĐ´ĐļаĐŊĐŊĐĩ", + "search_filter_media_type": "ĐĸŅ‹Đŋ ĐŧĐĩĐ´Ņ‹Ņ", + "search_filter_media_type_title": "Đ’Ņ‹ĐąĐĩҀҋ҆Đĩ ҂ҋĐŋ ĐŧĐĩĐ´Ņ‹Ņ", + "search_page_screenshots": "Đ—Đ´Ņ‹ĐŧĐēŅ– ŅĐēŅ€Đ°ĐŊа", + "search_page_selfies": "ĐĄŅĐģ҄Җ", + "search_page_things": "Đ ŅŅ‡Ņ‹", + "search_page_your_map": "Đ’Đ°ŅˆĐ° ĐēĐ°Ņ€Ņ‚Đ°", + "second": "ĐĄĐĩĐē҃ĐŊда", + "send_message": "АдĐŋŅ€Đ°Đ˛Ņ–Ņ†ŅŒ ĐŋавĐĩдаĐŧĐģĐĩĐŊĐŊĐĩ", + "setting_languages_apply": "ĐŖĐļŅ‹Ņ†ŅŒ", + "setting_notifications_notify_never": "ĐŊŅ–ĐēĐžĐģŅ–", + "settings": "НаĐģĐ°Đ´Ņ‹", + "share_add_photos": "Đ”Đ°Đ´Đ°Ņ†ŅŒ Ņ„ĐžŅ‚Đ°", + "shared_album_section_people_title": "ЛЮДЗІ", + "shared_link_info_chip_metadata": "EXIF", + "sharing_page_empty_list": "ĐŸĐŖĐĄĐĸĐĢ ĐĄĐŸĐ†ĐĄ", + "sign_out": "Đ’Ņ‹ĐšŅŅ†Ņ–", + "sign_up": "Đ—Đ°Ņ€ŅĐŗŅ–ŅŅ‚Ņ€Đ°Đ˛Đ°Ņ†Ņ†Đ°", + "size": "ПаĐŧĐĩŅ€", + "sort_title": "Đ—Đ°ĐŗĐ°ĐģОваĐē", + "source": "ĐšŅ€Ņ‹ĐŊŅ–Ņ†Đ°", + "tag": "ĐĸŅĐŗ", + "tags": "ĐĸŅĐŗŅ–", + "theme": "ĐĸŅĐŧа", + "theme_selection": "Đ’Ņ‹ĐąĐ°Ņ€ Ņ‚ŅĐŧŅ‹", "timeline": "ĐĨŅ€ĐžĐŊŅ–Đēа", "total": "ĐŖŅŅĐŗĐž", + "trash": "ĐĄĐŧĐĩŅ‚ĐŊŅ–Ņ†Đ°", + "trash_page_delete_all": "Đ’Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ҃ҁĐĩ", + "trash_page_restore_all": "АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ҃ҁĐĩ", + "trash_page_title": "ĐĄĐŧĐĩŅ‚ĐŊŅ–Ņ†Đ° ({count})", + "type": "ĐĸŅ‹Đŋ", + "undo": "ĐĐ´Ņ€Đ°ĐąŅ–Ņ†ŅŒ", + "upload": "ЗаĐŋаĐŧĐŋĐ°Đ˛Đ°Ņ†ŅŒ", + "upload_status_errors": "ПаĐŧŅ‹ĐģĐēŅ–", + "uploading": "ЗаĐŋаĐŧĐŋĐžŅžĐ˛Đ°ĐŊĐŊĐĩ", + "url": "URL-Đ°Đ´Ņ€Đ°Ņ", "user": "ĐšĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đē", + "user_has_been_deleted": "Đ“ŅŅ‚Ņ‹ ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đē ĐąŅ‹Ņž Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹.", "user_id": "ID ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–Đēа", "user_purchase_settings": "ĐšŅƒĐŋĐģŅ", "user_purchase_settings_description": "ĐšŅ–Ņ€ŅƒĐšŅ†Đĩ ĐŋаĐē҃ĐŋĐēаĐŧŅ–", @@ -108,18 +500,18 @@ "view_all_users": "ĐŸŅ€Đ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ ҃ҁĐĩŅ… ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊŅ–ĐēĐ°Ņž", "view_in_timeline": "ĐŸĐ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ Ņ…Ņ€ĐžĐŊŅ–Đē҃", "view_links": "ĐŸŅ€Đ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐēŅ–", - "view_name": "ĐŸŅ€Đ°ĐŗĐģĐĩдСĐĩŅ†ŅŒ", + "view_name": "ĐŸŅ€Đ°ĐŗĐģŅĐ´", "view_next_asset": "ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊŅ‹ ай'ĐĩĐēŅ‚", "view_previous_asset": "ĐŸŅ€Đ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅ– ай'ĐĩĐēŅ‚", "view_stack": "ĐŸŅ€Đ°ĐŗĐģŅĐ´ ŅŅ‚ŅĐēа", - "visibility_changed": "Đ’Ņ–Đ´ĐˇŅ–ĐŧĐ°ŅŅ†ŅŒ СĐŧŅĐŊŅ–ĐģĐ°ŅŅ Đ´ĐģŅ {count, plural, one {# Ņ‡Đ°ĐģавĐĩĐē(-Đ°Ņž)} Đ°ŅŅ‚Đ°Ņ‚ĐŊŅ–Ņ… {# Ņ‡Đ°ĐģавĐĩĐē}}", + "visibility_changed": "Đ‘Đ°Ņ‡ĐŊĐ°ŅŅ†ŅŒ СĐŧŅĐŊŅ–ĐģĐ°ŅŅ Đ´ĐģŅ {count, plural, one {# Ņ‡Đ°ĐģавĐĩĐēа} other {# Ņ‡Đ°ĐģавĐĩĐē}}", "waiting": "ЧаĐēĐ°ŅŽŅ†ŅŒ", "warning": "ПаĐŋŅŅ€ŅĐ´ĐļаĐŊĐŊĐĩ", "week": "ĐĸŅ‹Đ´ĐˇĐĩĐŊҌ", "welcome": "Đ’Ņ–Ņ‚Đ°ĐĩĐŧ", "welcome_to_immich": "Đ’Ņ–Ņ‚Đ°ĐĩĐŧ ҃ Immich", "year": "Год", - "years_ago": "{years, plural, one {# ĐŗĐžĐ´} other {# ĐŗĐ°Đ´ĐžŅž}} Ņ‚Đ°Đŧ҃", + "years_ago": "{years, plural, one {# ĐŗĐžĐ´} few {# ĐŗĐ°Đ´Ņ‹} many {# ĐŗĐ°Đ´ĐžŅž} other {# ĐŗĐ°Đ´ĐžŅž}} Ņ‚Đ°Đŧ҃", "yes": "ĐĸаĐē", "you_dont_have_any_shared_links": "ĐŖ Đ˛Đ°Ņ ĐŊŅĐŧа Đ°ĐąĐ°ĐŗŅƒĐģĐĩĐŊҋ҅ ҁĐŋĐ°ŅŅ‹ĐģаĐē", "zoom_image": "ĐŸĐ°Đ˛ŅĐģŅ–Ņ‡Ņ‹Ņ†ŅŒ Đ˛Ņ–Đ´Đ°Ņ€Ņ‹Ņ" diff --git a/i18n/bg.json b/i18n/bg.json index 2df3dd927d..a0ab4d0a80 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -13,20 +13,25 @@ "add_a_description": "Добави ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "add_a_location": "Добави ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "add_a_name": "Добави иĐŧĐĩ", - "add_a_title": "ДобавĐĩŅ‚Đĩ ĐˇĐ°ĐŗĐģавиĐĩ", + "add_a_title": "Добaви ĐˇĐ°ĐŗĐģавиĐĩ", + "add_birthday": "Добави Đ´Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ", "add_endpoint": "Добави ĐēŅ€Đ°ĐšĐŊа Ņ‚ĐžŅ‡Đēа", "add_exclusion_pattern": "Добави ĐŧОдĐĩĐģ Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ", "add_import_path": "Добави ĐŋŅŠŅ‚ Са иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ", - "add_location": "ДобавĐĩŅ‚Đĩ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", - "add_more_users": "ДобавĐĩŅ‚Đĩ ĐžŅ‰Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", - "add_partner": "ДобавĐĩŅ‚Đĩ ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€", + "add_location": "Дoйави ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "add_more_users": "Добави ĐžŅ‰Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", + "add_partner": "Добави ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€", "add_path": "Добави ĐŋŅŠŅ‚", - "add_photos": "ДобавĐĩŅ‚Đĩ ҁĐŊиĐŧĐēи", + "add_photos": "Добави ҁĐŊиĐŧĐēи", "add_tag": "Добави ĐŧĐ°Ņ€ĐēĐĩŅ€", "add_to": "Добави ĐēҊĐŧâ€Ļ", "add_to_album": "Добави ĐēҊĐŧ аĐģĐąŅƒĐŧ", "add_to_album_bottom_sheet_added": "ДобавĐĩĐŊĐž в {album}", "add_to_album_bottom_sheet_already_exists": "ВĐĩ҇Đĩ Đĩ в {album}", + "add_to_album_bottom_sheet_some_local_assets": "ĐŅĐēОи ĐģĐžĐēаĐģĐŊи Ņ„Đ°ĐšĐģОвĐĩ ĐŊĐĩ ҃ҁĐŋŅŅ…Đ° да ҁĐĩ Đ´ĐžĐąĐ°Đ˛ŅŅ‚ ĐēҊĐŧ аĐģĐąŅƒĐŧа", + "add_to_album_toggle": "ĐĄĐŧĐĩĐŊĐĩŅ‚Đĩ Đ¸ĐˇĐąĐžŅ€Đ° Са {album}", + "add_to_albums": "Đ”ĐžĐąĐ°Đ˛ŅĐŊĐĩ в аĐģĐąŅƒĐŧи", + "add_to_albums_count": "Đ”ĐžĐąĐ°Đ˛ŅĐŊĐĩ в аĐģĐąŅƒĐŧи ({count})", "add_to_shared_album": "Добави ĐēҊĐŧ ҁĐŋОдĐĩĐģĐĩĐŊ аĐģĐąŅƒĐŧ", "add_url": "Добави URL", "added_to_archive": "ДобавĐĩĐŊĐž ĐēҊĐŧ Đ°Ņ€Ņ…Đ¸Đ˛Đ°", @@ -44,6 +49,13 @@ "backup_database": "ĐĄŅŠĐˇĐ´Đ°Đš Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа йаСа даĐŊĐŊи", "backup_database_enable_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", "backup_keep_last_amount": "Đ‘Ņ€ĐžĐš СаĐŋаСĐĩĐŊи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ", + "backup_onboarding_1_description": "ĐēĐžĐŋиĐĩ ĐŊа ОйĐģаĐēа иĐģи Đ´Ņ€ŅƒĐŗĐž Ņ„Đ¸ĐˇĐ¸Ņ‡ĐĩҁĐēĐž ĐŧŅŅŅ‚Đž.", + "backup_onboarding_2_description": "ĐģĐžĐēаĐģĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа Ņ€Đ°ĐˇĐģĐ¸Ņ‡ĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°. ĐĸОва вĐēĐģŅŽŅ‡Đ˛Đ° ĐžŅĐŊОвĐŊĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ и ĐģĐžĐēаĐģĐŊи Đ°Ņ€Ņ…Đ¸Đ˛Đ¸ ĐŊа Ņ‚ĐĩСи Ņ„Đ°ĐšĐģОвĐĩ.", + "backup_onboarding_3_description": "ĐžĐąŅ‰Đž ĐēĐžĐŋĐ¸Ņ ĐŊа Đ˛Đ°ŅˆĐ¸Ņ‚Đĩ даĐŊĐŊи, вĐēĐģŅŽŅ‡Đ¸Ņ‚ĐĩĐŊĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ. ĐĸОва вĐēĐģŅŽŅ‡Đ˛Đ° 1 ĐēĐžĐŋиĐĩ Đ¸ĐˇĐ˛ŅŠĐŊ ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ° и 2 ĐģĐžĐēаĐģĐŊи ĐēĐžĐŋĐ¸Ņ.", + "backup_onboarding_description": "За ĐŊадĐĩĐļĐ´ĐŊа ĐˇĐ°Ņ‰Đ¸Ņ‚Đ° ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ˛Đ°ĐŧĐĩ ŅŅ‚Ņ€Đ°Ņ‚ĐĩĐŗĐ¸ŅŅ‚Đ° 3-2-1. ĐŸŅ€Đ°Đ˛ĐĩŅ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐēаĐēŅ‚Đž ĐŊа ĐēĐ°Ņ‡ĐĩĐŊĐ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи/видĐĩа, Ņ‚Đ°Đēа и ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи ĐŊа Immich.", + "backup_onboarding_footer": "За ĐŋĐžĐ´Ņ€ĐžĐąĐŊа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ĐžŅ‚ĐŊĐžŅĐŊĐž Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž в Immich, ĐŧĐžĐģŅ виĐļŅ‚Đĩ в Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸ŅŅ‚Đ°.", + "backup_onboarding_parts_title": "ĐĄŅ‚Ņ€Đ°Ņ‚ĐĩĐŗĐ¸ŅŅ‚Đ° 3-2-1 вĐēĐģŅŽŅ‡Đ˛Đ°:", + "backup_onboarding_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ", "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐž ĐēĐžĐŋиĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи.", "cleared_jobs": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸ ĐžŅ‚ Ņ‚Đ¸Đŋ: {job}", @@ -112,6 +124,13 @@ "logging_enable_description": "ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа СаĐŋĐ¸Ņ (ĐģĐžĐŗĐžĐ˛Đĩ)", "logging_level_description": "ĐšĐžĐŗĐ°Ņ‚Đž Đĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž, ĐēаĐēвО ĐŊивО ĐŊа СаĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ да ҁĐĩ иСĐŋĐžĐģСва.", "logging_settings": "ЗаĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ", + "machine_learning_availability_checks": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēи Са ĐŊаĐģĐ¸Ņ‡ĐŊĐžŅŅ‚", + "machine_learning_availability_checks_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ и ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ ĐŊа ĐŊаĐģĐ¸Ņ‡ĐŊи ŅŅŠŅ€Đ˛ŅŠŅ€Đ¸ Са ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐž ĐžĐąŅƒŅ‡ĐĩĐŊиĐĩ", + "machine_learning_availability_checks_enabled": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи Са ĐŊаĐģĐ¸Ņ‡ĐŊĐžŅŅ‚", + "machine_learning_availability_checks_interval": "ИĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ĐŊа ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", + "machine_learning_availability_checks_interval_description": "Đ’Ņ€ĐĩĐŧĐĩ в ĐŧиĐģĐ¸ŅĐĩĐē҃ĐŊди ĐŧĐĩĐļĐ´Ņƒ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐēĐ¸Ņ‚Đĩ Са ĐŊаĐģĐ¸Ņ‡ĐŊĐžŅŅ‚", + "machine_learning_availability_checks_timeout": "Đ’Ņ€ĐĩĐŧĐĩ Са Đ¸ĐˇŅ‡Đ°ĐēваĐŊĐĩ ĐŊа ĐžŅ‚ĐŗĐžĐ˛ĐžŅ€", + "machine_learning_availability_checks_timeout_description": "Đ’Ņ€ĐĩĐŧĐĩ Са Đ¸ĐˇŅ‡Đ°ĐēваĐŊĐĩ ĐŊа ĐžŅ‚ĐŗĐžĐ˛ĐžŅ€ в ĐŧиĐģĐ¸ŅĐĩĐē҃ĐŊди ĐŋŅ€Đ¸ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēа ĐŊа ĐŊаĐģĐ¸Ņ‡ĐŊĐžŅŅ‚", "machine_learning_clip_model": "CLIP ĐŧОдĐĩĐģ", "machine_learning_clip_model_description": "ИĐŧĐĩŅ‚Đž ĐŊа CLIP ĐŧОдĐĩĐģа, ĐŋĐžŅĐžŅ‡ĐĩĐŊ Ņ‚ŅƒĐē. ИĐŧĐ°ĐšŅ‚Đĩ ĐŋŅ€Đĩдвид, ҇Đĩ ĐŋŅ€Đ¸ ĐŋŅ€ĐžĐŧŅĐŊа ĐŊа ĐŧОдĐĩĐģа Ņ‚Ņ€ŅĐąĐ˛Đ° да ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°Ņ‚Đĩ ĐžŅ‚ĐŊОвО ĐˇĐ°Đ´Đ°Ņ‡Đ°Ņ‚Đ° \"ИĐŊŅ‚ĐĩĐģĐ¸ĐŗĐĩĐŊŅ‚ĐŊĐž ĐĸŅŠŅ€ŅĐĩĐŊĐĩ\" Са Đ˛ŅĐ¸Ņ‡Đēи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ.", "machine_learning_duplicate_detection": "ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", @@ -166,6 +185,20 @@ "metadata_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊи", "migration_job": "ĐœĐ¸ĐŗŅ€Đ°Ņ†Đ¸Ņ", "migration_job_description": "ĐœĐ¸ĐŗŅ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸Ņ‚Đĩ Са ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ и ĐģĐ¸Ņ†Đ° ĐēҊĐŧ ĐŊаК-ĐŊĐžĐ˛Đ°Ņ‚Đ° ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ° ĐŊа ĐŋаĐŋĐēĐ¸Ņ‚Đĩ", + "nightly_tasks_cluster_faces_setting_description": "ИСĐŋҊĐģĐŊи Ņ€Đ°ĐˇĐŋОСĐŊаваĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đĩ Са ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ ĐŊОви ĐģĐ¸Ņ†Đ°", + "nightly_tasks_cluster_new_faces_setting": "РаСĐŋОСĐŊаваĐŊĐĩ ĐŊа ĐŊОви ĐģĐ¸Ņ†Đ°", + "nightly_tasks_database_cleanup_setting": "Đ—Đ°Đ´Đ°Ņ‡Đ¸ ĐŋĐž ĐŋĐžŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", + "nightly_tasks_database_cleanup_setting_description": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ŅŅ‚Đ°Ņ€Đ¸, ĐŊĐĩĐŊ҃ĐļĐŊи СаĐŋĐ¸ŅĐ¸ ĐžŅ‚ ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", + "nightly_tasks_generate_memories_setting": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋĐžĐŧĐĩĐŊи", + "nightly_tasks_generate_memories_setting_description": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОви ҁĐŋĐžĐŧĐĩĐŊи ĐžŅ‚ ŅŅŠŅ‰ĐĩŅŅ‚Đ˛ŅƒĐ˛Đ°Ņ‰Đ¸ ОйĐĩĐēŅ‚Đ¸", + "nightly_tasks_missing_thumbnails_setting": "ГĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģиĐŋŅĐ˛Đ°Ņ‰Đ¸ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸", + "nightly_tasks_missing_thumbnails_setting_description": "Đ”ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ОйĐĩĐēŅ‚Đ¸ ĐąĐĩС ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ° в ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ° Са ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°", + "nightly_tasks_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Са ĐŋŅ€ĐĩС ĐŊĐžŅ‰Ņ‚Đ°", + "nightly_tasks_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ¸Ņ‚Đĩ, иСĐŋҊĐģĐŊŅĐ˛Đ°ĐŊи ĐŋŅ€ĐĩС ĐŊĐžŅ‰Ņ‚Đ°", + "nightly_tasks_start_time_setting": "Đ’Ņ€ĐĩĐŧĐĩ Са ĐŊĐ°Ņ‡Đ°ĐģĐž", + "nightly_tasks_start_time_setting_description": "Đ’Ņ€ĐĩĐŧĐĩ, ĐēĐžĐŗĐ°Ņ‚Đž ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ҉Đĩ СаĐŋĐžŅ‡ĐŊĐĩ иСĐŋҊĐģĐŊĐĩĐŊиĐĩ ĐŊа ĐŊĐžŅ‰ĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸", + "nightly_tasks_sync_quota_usage_setting": "ĐšĐ˛ĐžŅ‚Đ° Са ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ", + "nightly_tasks_sync_quota_usage_setting_description": "ОбĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐēĐ˛ĐžŅ‚Đ°Ņ‚Đ° ҁĐŋĐžŅ€ĐĩĐ´ Ņ‚ĐĩĐēŅƒŅ‰ĐžŅ‚Đž ĐŋĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊиĐĩ", "no_paths_added": "ĐŅĐŧа дОйавĐĩĐŊи ĐŋŅŠŅ‚Đ¸Ņ‰Đ°", "no_pattern_added": "ĐŅĐŧа дОйавĐĩĐŊ ĐŧОдĐĩĐģ", "note_apply_storage_label_previous_assets": "ЗабĐĩĐģĐĩĐļĐēа: За да ĐŋŅ€Đ¸ĐģĐžĐļĐ¸Ņ‚Đĩ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐēҊĐŧ ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģĐŊĐž ĐēĐ°Ņ‡ĐĩĐŊи Ņ„Đ°ĐšĐģОвĐĩ, ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐšŅ‚Đĩ", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI Са ĐŧОйиĐģĐŊĐž ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ", "oauth_mobile_redirect_uri_override": "URI ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ Са ĐŧОйиĐģĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "oauth_mobile_redirect_uri_override_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đēа Са OAuth ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊĐĩ ĐŋОСвОĐģŅĐ˛Đ° Са ĐŧОйиĐģĐŊи URI идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸, ĐēĐ°Ņ‚Đž ''{callback}''", + "oauth_role_claim": "ĐŸĐžŅ‚Đ˛ŅŠŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŊа Ņ€ĐžĐģŅ", + "oauth_role_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅĐŊĐĩ ĐŊа адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊи ĐŋŅ€Đ°Đ˛Đ° ĐŋŅ€Đ¸ ĐŊаĐģĐ¸Ņ‡Đ¸Đĩ ĐŊа Ņ‚ĐžĐ˛Đ° ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļĐĩĐŊиĐĩ. ĐŸĐžŅ‚Đ˛ŅŠŅ€ĐļĐ´ĐĩĐŊиĐĩŅ‚Đž ĐŧĐžĐļĐĩ да иĐŧа ŅŅ‚ĐžĐšĐŊĐžŅŅ‚ 'user' иĐģи 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ˛Ņ…ĐžĐ´ ҁ OAuth", "oauth_settings_more_details": "За ĐŋОвĐĩ҇Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģĐŊĐžŅŅ‚Ņ‚Đ°, ҁĐĩ ĐŋĐžŅ‚ŅŠŅ€ŅĐĩŅ‚Đĩ в docs.", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Đ‘Ņ€ĐžĐš Đ´ĐŊи, в ĐēĐžĐ¸Ņ‚Đž Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ да ҁĐĩ ŅŅŠŅ…Ņ€Đ°ĐŊŅĐ˛Đ°Ņ‚ ĐŊа йОĐēĐģ҃Đēа, ĐŋŅ€Đĩди да ĐąŅŠĐ´Đ°Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸", "trash_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", "trash_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", + "unlink_all_oauth_accounts": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа Đ˛ŅĐ¸Ņ‡Đēи OAuth ĐŋŅ€ĐžŅ„Đ¸Đģи", + "unlink_all_oauth_accounts_description": "НĐĩ ĐˇĐ°ĐąŅ€Đ°Đ˛ŅĐšŅ‚Đĩ да ĐŋŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа Đ˛ŅĐ¸Ņ‡Đēи OAuth ĐŋŅ€ĐžŅ„Đ¸Đģи ĐŋŅ€Đĩди да ĐŧĐ¸ĐŗŅ€Đ¸Ņ€Đ°Ņ‚Đĩ ĐēҊĐŧ ĐŊОв Đ´ĐžŅŅ‚Đ°Đ˛Ņ‡Đ¸Đē.", + "unlink_all_oauth_accounts_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐžŅ‚ĐŋĐ¸ŅˆĐĩŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи OAuth ĐŋŅ€ĐžŅ„Đ¸Đģи? ĐĸОва ҉Đĩ ĐŊ҃ĐģĐ¸Ņ€Đ° OAuth ID Са Đ˛ŅĐĩĐēи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ и ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž.", "user_cleanup_job": "ĐŸĐžŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", "user_delete_delay": "{user} aĐēĐ°ŅƒĐŊŅ‚ŅŠŅ‚ и Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ ĐŋĐģаĐŊĐ¸Ņ€Đ°ĐŊи Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ҁĐģĐĩĐ´ {delay, plural, one {# Đ´ĐĩĐŊ} other {# Đ´ĐŊи}}.", "user_delete_delay_settings": "Đ—Đ°ĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ", @@ -364,13 +402,15 @@ "advanced_settings_prefer_remote_title": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°Đš Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "advanced_settings_proxy_headers_subtitle": "ДĐĩŅ„Đ¸ĐŊĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐēŅĐ¸ Ņ…ĐĩĐ´ŅŠŅ€Đ¸, ĐēĐžĐ¸Ņ‚Đž Immich Ņ‚Ņ€ŅĐąĐ˛Đ° да иСĐŋŅ€Đ°Ņ‰Đ° ҁ Đ˛ŅŅĐēа ĐŧŅ€ĐĩĐļОва ĐˇĐ°ŅĐ˛Đēа", "advanced_settings_proxy_headers_title": "ĐŸŅ€ĐžĐēŅĐ¸ Ņ…ĐĩĐ´ŅŠŅ€Đ¸", + "advanced_settings_readonly_mode_subtitle": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ° Ņ€ĐĩĐļиĐŧа \"ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ\", ĐŋŅ€Đ¸ ĐēĐžĐšŅ‚Đž ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Ņ€Đ°ĐˇĐŗĐģĐĩĐļдаĐŊи, ĐŊĐž ĐŊĐĩŅ‰Đ° ĐēĐ°Ņ‚Đž Đ¸ĐˇĐąĐžŅ€ ĐŊа ĐŊŅĐēĐžĐģĐēĐž Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ, ҁĐŋОдĐĩĐģŅĐŊĐĩ, Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ŅĐ° ĐˇĐ°ĐąŅ€Đ°ĐŊĐĩĐŊи. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ/Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩĐļиĐŧа ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ ŅŅ‚Đ°Đ˛Đ° ĐžŅ‚ ĐēĐ°Ņ€Ņ‚Đ¸ĐŊĐēĐ°Ņ‚Đ°-Đ°Đ˛Đ°Ņ‚Đ°Ņ€ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ĐžŅ‚ ĐžŅĐŊОвĐŊĐ¸Ņ ĐĩĐēŅ€Đ°ĐŊ", + "advanced_settings_readonly_mode_title": "Đ ĐĩĐļиĐŧ ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ", "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐžĐŋ҃ҁĐēа ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐēĐ°Ņ‚Đ° ĐŊа SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°. Đ˜ĐˇĐ¸ŅĐēва ҁĐĩ ĐŋŅ€Đ¸ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊи ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ¸.", "advanced_settings_self_signed_ssl_title": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊи SSL ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ¸", "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Đ¸ иĐģи Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОви ОйĐĩĐēŅ‚ ĐŊа Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž, ĐēĐžĐŗĐ°Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸ĐĩŅ‚Đž Đĩ Đ¸ĐˇĐ˛ŅŠŅ€ŅˆĐĩĐŊĐž ĐŋŅ€ĐĩС ҃ĐĩĐą-иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅĐ°", "advanced_settings_sync_remote_deletions_title": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŊа Đ´Đ¸ŅŅ‚Đ°ĐŊŅ†Đ¸ĐžĐŊĐŊи Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐ¸Ņ [ЕКСПЕРИМЕНĐĸАЛНО]", "advanced_settings_tile_subtitle": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "advanced_settings_troubleshooting_subtitle": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸ Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊи Đ˛ŅŠĐˇĐŧĐžĐļĐŊĐžŅŅ‚Đ¸ Са ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐąĐģĐĩĐŧи", - "advanced_settings_troubleshooting_title": "ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐąĐģĐĩĐŧи", + "advanced_settings_troubleshooting_title": "ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊe ĐŊа ĐŋŅ€ĐžĐąĐģĐĩĐŧи", "age_months": "Đ’ŅŠĐˇŅ€Đ°ŅŅ‚ {months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ¸}}", "age_year_months": "Đ’ŅŠĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´Đ¸ĐŊа, {months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ¸}}", "age_years": "{years, plural, other {ГодиĐŊа #}}", @@ -379,6 +419,7 @@ "album_cover_updated": "ОбĐģĐžĐļĐēĐ°Ņ‚Đ° ĐŊа аĐģĐąŅƒĐŧа Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа", "album_delete_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ аĐģĐąŅƒĐŧа {album}?", "album_delete_confirmation_description": "АĐēĐž Ņ‚ĐžĐˇĐ¸ аĐģĐąŅƒĐŧ Đĩ ҁĐŋОдĐĩĐģĐĩĐŊ, Đ´Ņ€ŅƒĐŗĐ¸ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи вĐĩ҇Đĩ ĐŊŅĐŧа да иĐŧĐ°Ņ‚ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ĐŊĐĩĐŗĐž.", + "album_deleted": "АĐģĐąŅƒĐŧа Đĩ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚", "album_info_card_backup_album_excluded": "ИЗКЛЮЧЕН", "album_info_card_backup_album_included": "ВКЛЮЧЕН", "album_info_updated": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŅ‚Đ° Са аĐģĐąŅƒĐŧа Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа", @@ -388,7 +429,9 @@ "album_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа аĐģĐąŅƒĐŧа", "album_remove_user": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ?", "album_remove_user_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩŅ‚Đĩ {user}?", + "album_search_not_found": "ĐŅĐŧа ĐŊаĐŧĐĩŅ€ĐĩĐŊи аĐģĐąŅƒĐŧи, ĐžŅ‚ĐŗĐžĐ˛Đ°Ņ€ŅŅ‰Đ¸ ĐŊа Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž ви", "album_share_no_users": "Đ˜ĐˇĐŗĐģĐĩĐļда, ҇Đĩ ҁ҂Đĩ ҁĐŋОдĐĩĐģиĐģи Ņ‚ĐžĐˇĐ¸ аĐģĐąŅƒĐŧ ҁ Đ˛ŅĐ¸Ņ‡Đēи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи иĐģи ĐŊŅĐŧĐ°Ņ‚Đĩ Đ´Ņ€ŅƒĐŗ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ, ҁ ĐēĐžĐŗĐžŅ‚Đž да ĐŗĐž ҁĐŋОдĐĩĐģĐ¸Ņ‚Đĩ.", + "album_summary": "ĐžĐąĐžĐąŅ‰ĐĩĐŊиĐĩ ĐŊа аĐģĐąŅƒĐŧа", "album_updated": "АĐģĐąŅƒĐŧŅŠŅ‚ Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ", "album_updated_setting_description": "ПоĐģŅƒŅ‡Đ°Đ˛Đ°ĐšŅ‚Đĩ иСвĐĩŅŅ‚Đ¸Đĩ ĐŋĐž иĐŧĐĩĐšĐģ, ĐēĐžĐŗĐ°Ņ‚Đž ҁĐŋОдĐĩĐģĐĩĐŊ аĐģĐąŅƒĐŧ иĐŧа ĐŊОви Ņ„Đ°ĐšĐģОвĐĩ", "album_user_left": "НаĐŋ҃ҁĐŊа {album}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Đ ĐĩĐ´ ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ Са ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧи", "albums_default_sort_order_description": "ĐŸŅŠŅ€Đ˛ĐžĐŊĐ°Ņ‡Đ°ĐģĐĩĐŊ Ņ€ĐĩĐ´ ĐŊа ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŋŅ€Đ¸ ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОв аĐģĐąŅƒĐŧ.", "albums_feature_description": "КоĐģĐĩĐēŅ†Đ¸Đ¸ ĐžŅ‚ ОйĐĩĐēŅ‚Đ¸, ĐēĐžĐ¸Ņ‚Đž ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ ҁĐŋОдĐĩĐģŅĐŊи ҁ Đ´Ņ€ŅƒĐŗĐ¸ ĐŋĐžŅ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи.", + "albums_on_device_count": "АĐģĐąŅƒĐŧи ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž ({count})", "all": "Đ’ŅĐ¸Ņ‡Đēи", "all_albums": "Đ’ŅĐ¸Ņ‡Đēи аĐģĐąŅƒĐŧи", "all_people": "Đ’ŅĐ¸Ņ‡Đēи Ņ…ĐžŅ€Đ°", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "ИСĐģĐĩС ĐžŅ‚ ĐŋŅ€ĐžŅ„Đ¸Đģа", "app_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đŧа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", "appears_in": "ИСĐģиСа в", + "apply_count": "ĐŸŅ€Đ¸ĐģĐžĐļи ({count, number})", "archive": "ĐŅ€Ņ…Đ¸Đ˛", + "archive_action_prompt": "{count} ŅĐ° дОйавĐĩĐŊи в ĐŅ€Ņ…Đ¸Đ˛Đ°", "archive_or_unarchive_photo": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ иĐģи Đ´ĐĩĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŊиĐŧĐēа", "archive_page_no_archived_assets": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ОйĐĩĐēŅ‚Đ¸ в Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "archive_page_title": "ĐŅ€Ņ…Đ¸Đ˛ ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "ĐŖŅĐŋĐĩ҈ĐŊĐž Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ ОйĐĩĐēŅ‚", "asset_skipped": "ĐŸŅ€ĐžĐŋ҃ҁĐŊĐ°Ņ‚Đž", "asset_skipped_in_trash": "В ĐēĐžŅˆŅ‡ĐĩŅ‚Đž", + "asset_trashed": "ОбĐĩĐēŅ‚ŅŠŅ‚ Đĩ Đ¸ĐˇŅ…Đ˛ŅŠŅ€ĐģĐĩĐŊ", + "asset_troubleshoot": "ПоĐŋŅ€Đ°Đ˛Đēа ĐŊа ĐŗŅ€Đĩ҈Đēи ҁ ОйĐĩĐēŅ‚Đ°", "asset_uploaded": "ĐšĐ°Ņ‡ĐĩĐŊĐž", "asset_uploading": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩâ€Ļ", "asset_viewer_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ¸ĐˇĐŗĐģĐĩĐ´", @@ -464,8 +512,9 @@ "assets": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "assets_added_count": "ДобавĐĩĐŊĐž {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "ДобавĐĩĐŊ(и) ŅĐ° {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}} в аĐģĐąŅƒĐŧа", - "assets_added_to_name_count": "ДобавĐĩĐŊ(и) ŅĐ° {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}} ĐēҊĐŧ {hasName, select, true {{name}} other {ĐŊОв аĐģĐąŅƒĐŧ}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {# ОйĐĩĐēŅ‚ Đĩ дОйавĐĩĐŊ} other {# ОйĐĩĐēŅ‚Đ° ŅĐ° дОйавĐĩĐŊи}} в {albumTotal, plural, one {# аĐģĐąŅƒĐŧ} other {# аĐģĐąŅƒĐŧа}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {ОбĐĩĐēŅ‚Đ° ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ дОйави} other {ОбĐĩĐēŅ‚Đ¸Ņ‚Đĩ ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ Đ´ĐžĐąĐ°Đ˛ŅŅ‚}} в аĐģĐąŅƒĐŧа", + "assets_cannot_be_added_to_albums": "{count, plural, one {ОйĐĩĐēŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ дОйавĐĩĐŊ} other {ОйĐĩĐēŅ‚Đ° ĐŊĐĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ дОйавĐĩĐŊи}} в ĐŊиĐēОК ĐžŅ‚ аĐģĐąŅƒĐŧĐ¸Ņ‚Đĩ", "assets_count": "{count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ°}}", "assets_deleted_permanently": "{count} ОйĐĩĐēŅ‚Đ° ŅĐ° Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ СавиĐŊĐ°ĐŗĐ¸", "assets_deleted_permanently_from_server": "{count} ОйĐĩĐēŅ‚Đ° ŅĐ° Đ¸ĐˇŅ‚Đ¸Ņ‚Đ¸ ĐžŅ‚ Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ° СавиĐŊĐ°ĐŗĐ¸", @@ -482,20 +531,25 @@ "assets_trashed_count": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ(и) ŅĐ° {count, plural, one {# Ņ„Đ°ĐšĐģ} other {# Ņ„Đ°ĐšĐģа}}", "assets_trashed_from_server": "{count} ОйĐĩĐēŅ‚Đ° ŅĐ° ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊи в ĐēĐžŅˆĐ° ĐŊа Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "assets_were_part_of_album_count": "{count, plural, one {ФаКĐģŅŠŅ‚ Đĩ} other {ФаКĐģОвĐĩŅ‚Đĩ ŅĐ°}} вĐĩ҇Đĩ Ņ‡Đ°ŅŅ‚ ĐžŅ‚ аĐģĐąŅƒĐŧа", + "assets_were_part_of_albums_count": "{count, plural, one {ОйĐĩĐēŅ‚ вĐĩ҇Đĩ Đĩ} other {ОйĐĩĐēŅ‚Đ° вĐĩ҇Đĩ ŅĐ°}} Ņ‡Đ°ŅŅ‚ ĐžŅ‚ аĐģĐąŅƒĐŧĐ¸Ņ‚Đĩ", "authorized_devices": "ĐŖĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ĐĩĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "automatic_endpoint_switching_subtitle": "ĐšĐžĐŗĐ°Ņ‚Đž Đĩ Đ´ĐžŅŅ‚ŅŠĐŋĐŊа, иСĐŋĐžĐģСваК ĐŋĐžŅĐžŅ‡ĐĩĐŊĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа, иĐŊĐ°Ņ‡Đĩ иСĐŋĐžĐģСваК аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊи Đ˛Ņ€ŅŠĐˇĐēи", "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа URL", "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊа ҁĐŧŅĐŊа ĐŊа ҁĐģаКдОвĐĩŅ‚Đĩ", "back": "Назад", "back_close_deselect": "Назад, ĐˇĐ°Ņ‚Đ˛Đ°Ņ€ŅĐŊĐĩ иĐģи ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа Đ¸ĐˇĐąĐžŅ€Đ°", + "background_backup_running_error": "ĐĄŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐž Đĩ Ņ„ĐžĐŊОвО Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ, ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ Đŋ҃ҁĐŊĐĩ Ņ€ŅŠŅ‡ĐŊĐž Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ", "background_location_permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ", "background_location_permission_content": "За да ĐŧĐžĐļĐĩ да ҇ĐĩŅ‚Đĩ иĐŧĐĩĐŊĐ°Ņ‚Đ° ĐŊа Wi-Fi ĐŧŅ€ĐĩĐļĐ¸Ņ‚Đĩ и да ĐŗĐ¸ ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ° ĐŋŅ€Đ¸ Ņ€Đ°ĐąĐžŅ‚Đ° Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ, Immich Ņ‚Ņ€ŅĐąĐ˛Đ° *виĐŊĐ°ĐŗĐ¸* да иĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ‚Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "background_options": "ОĐŋŅ†Đ¸Đ¸ Са Ņ„ĐžĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸", + "backup": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ", "backup_album_selection_page_albums_device": "АĐģĐąŅƒĐŧи ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž ({count})", "backup_album_selection_page_albums_tap": "ĐĐ°Ņ‚Đ¸ŅĐŊи Са да вĐēĐģŅŽŅ‡Đ¸Ņˆ, двОКĐŊĐž Са да иСĐēĐģŅŽŅ‡Đ¸Ņˆ", "backup_album_selection_page_assets_scatter": "ОбĐĩĐēŅ‚Đ¸Ņ‚Đĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ Ņ€Đ°ĐˇĐŋŅ€ŅŠŅĐŊĐ°Ņ‚Đ¸ в ĐŊŅĐēĐžĐģĐēĐž аĐģĐąŅƒĐŧа. По Ņ‚ĐžĐˇĐ¸ ĐŊĐ°Ņ‡Đ¸ĐŊ аĐģĐąŅƒĐŧĐ¸Ņ‚Đĩ ĐŧĐžĐŗĐ°Ņ‚ да ĐąŅŠĐ´Đ°Ņ‚ вĐēĐģŅŽŅ‡ĐĩĐŊи иĐģи иСĐēĐģŅŽŅ‡ĐĩĐŊи ĐŋĐž Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа ĐŋŅ€ĐžŅ†ĐĩŅĐ° ĐŊа Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ.", "backup_album_selection_page_select_albums": "Đ˜ĐˇĐąĐžŅ€ ĐŊа аĐģĐąŅƒĐŧи", "backup_album_selection_page_selection_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Đ¸ĐˇĐąŅ€Đ°ĐŊĐžŅ‚Đž", "backup_album_selection_page_total_assets": "ĐŖĐŊиĐēаĐģĐŊи ОйĐĩĐēŅ‚Đ¸ ĐžĐąŅ‰Đž", + "backup_albums_sync": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ‚Đĩ", "backup_all": "Đ’ŅĐ¸Ņ‡ĐēĐž", "backup_background_service_backup_failed_message": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ. Нов ĐžĐŋĐ¸Ņ‚â€Ļ", "backup_background_service_connection_failed_message": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐ˛ŅŠŅ€ĐˇĐ˛Đ°ĐŊĐĩ ĐēҊĐŧ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°. Нов ĐžĐŋĐ¸Ņ‚â€Ļ", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "ВĐēĐģŅŽŅ‡Đ¸ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ в аĐēŅ‚Đ¸Đ˛ĐĩĐŊ Ņ€ĐĩĐļиĐŧ", "backup_controller_page_uploading_file_info": "ИĐŊŅ„Đž Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸Ņ Ņ„Đ°ĐšĐģ", "backup_err_only_album": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩ ĐĩдиĐŊŅŅ‚Đ˛ĐĩĐŊĐ¸Ņ аĐģĐąŅƒĐŧ", - "backup_info_card_assets": "ОйĐĩĐēŅ‚Đ¸", + "backup_error_sync_failed": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Đĩ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊа. Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžŅ‚Đž ĐēĐžĐŋиĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ¸.", + "backup_info_card_assets": "ОйĐĩĐēŅ‚Đ°", "backup_manual_cancelled": "ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", "backup_manual_in_progress": "Đ’ŅŠŅ€Đ˛Đ¸ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ. ОĐŋĐ¸Ņ‚Đ°Đš ҁĐģĐĩĐ´ ĐŧаĐģĐēĐž", "backup_manual_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž", "backup_manual_title": "ĐĄŅŠŅŅ‚ĐžŅĐŊиĐĩ ĐŊа Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž", + "backup_options": "ОĐŋŅ†Đ¸Đ¸ Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ", "backup_options_page_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ", "backup_setting_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°Đš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ в аĐēŅ‚Đ¸Đ˛ĐĩĐŊ и Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ", + "backup_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", "backward": "Назад", "biometric_auth_enabled": "ВĐēĐģŅŽŅ‡ĐĩĐŊа йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", "biometric_locked_out": "ĐŅĐŧа Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ĐēĐĩŅˆĐ°", "cache_settings_clear_cache_button_title": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ˛Đ° ĐēĐĩŅˆĐ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž. ĐĸОва ҉Đĩ ĐŋОвĐģĐ¸ŅĐĩ ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž Đ´ĐžĐēĐ°Ņ‚Đž ĐēĐĩŅˆĐ° ĐŊĐĩ ĐąŅŠĐ´Đĩ ŅŅŠĐˇĐ´Đ°Đ´ĐĩĐŊ ĐžŅ‚ĐŊОвО.", "cache_settings_duplicated_assets_clear_button": "ИЗЧИСĐĸИ", - "cache_settings_duplicated_assets_subtitle": "ĐĄĐŊиĐŧĐēи и видĐĩа, ĐēĐžĐ¸Ņ‚Đž ŅĐ° в ЧĐĩŅ€ĐŊĐ¸Ņ ҁĐŋĐ¸ŅŅŠĐē ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", + "cache_settings_duplicated_assets_subtitle": "ĐĄĐŊиĐŧĐēи и видĐĩа, ĐēĐžĐ¸Ņ‚Đž ŅĐ° в ĐĄĐŋĐ¸ŅŅŠĐēа Са Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°ĐŊĐĩ ĐžŅ‚ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊи ОйĐĩĐēŅ‚Đ¸ ({count})", "cache_settings_statistics_album": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа ҁ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸", "cache_settings_statistics_full": "ĐŸŅŠĐģĐŊи Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", @@ -587,6 +644,7 @@ "cancel": "ĐžŅ‚ĐēаĐļи", "cancel_search": "ĐžŅ‚ĐŧĐĩĐŊи Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž", "canceled": "ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", + "canceling": "АĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊĐĩ", "cannot_merge_people": "НĐĩ ĐŧĐžĐļĐĩ да ОйĐĩдиĐŊŅĐ˛Đ° Ņ…ĐžŅ€Đ°", "cannot_undo_this_action": "НĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚Đĩ Ņ‚ĐžĐ˛Đ° Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", "cannot_update_the_description": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐž", @@ -609,6 +667,8 @@ "change_pin_code": "ĐĄĐŧĐĩĐŊи PIN ĐēОда", "change_your_password": "ĐŸŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ° ŅĐ¸", "changed_visibility_successfully": "ВидиĐŧĐžŅŅ‚Ņ‚Đ° Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊа ҃ҁĐŋĐĩ҈ĐŊĐž", + "charging": "ĐŸŅ€Đ¸ ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ", + "charging_requirement_mobile_backup": "ФОĐŊОвО Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ŅĐ°ĐŧĐž ĐŋŅ€Đ¸ ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž", "check_corrupt_asset_backup": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸ Са ĐŋĐžĐ˛Ņ€ĐĩĐ´ĐĩĐŊи Đ°Ņ€Ņ…Đ¸Đ˛ĐŊи ĐēĐžĐŋĐ¸Ņ", "check_corrupt_asset_backup_button": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸", "check_corrupt_asset_backup_description": "ИСĐŋҊĐģĐŊи Ņ‚Đ°ĐˇĐ¸ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēа ŅĐ°ĐŧĐž ĐŋŅ€Đ¸ Wi-Fi и ҁĐģĐĩĐ´ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ОйĐĩĐēŅ‚Đ¸. ĐŸŅ€ĐžŅ†ĐĩĐ´ŅƒŅ€Đ°Ņ‚Đ° ĐŧĐžĐļĐĩ да ĐŋŅ€ĐžĐ´ŅŠĐģĐļи ĐŊŅĐēĐžĐģĐēĐž ĐŧиĐŊŅƒŅ‚Đ¸.", @@ -618,6 +678,7 @@ "clear": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸", "clear_all": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ Đ˛ŅĐ¸Ņ‡ĐēĐž", "clear_all_recent_searches": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩŅ‚Đĩ Đ˛ŅĐ¸Ņ‡Đēи ҁĐēĐžŅ€ĐžŅˆĐŊи Ņ‚ŅŠŅ€ŅĐĩĐŊĐ¸Ņ", + "clear_file_cache": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ĐēĐĩŅˆĐ° ĐŊа Ņ„Đ°ĐšĐģОвĐĩŅ‚Đĩ", "clear_message": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ŅŅŠĐžĐąŅ‰ĐĩĐŊиĐĩŅ‚Đž", "clear_value": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Ņ‚Đ°", "client_cert_dialog_msg_confirm": "ОК", @@ -688,11 +749,13 @@ "create_new_user": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊОв ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "create_shared_album_page_share_add_assets": "ДОБАВИ ОБЕКĐĸИ", "create_shared_album_page_share_select_photos": "ИСйĐĩŅ€Đ¸ ҁĐŊиĐŧĐēи", + "create_shared_link": "ĐĄŅŠĐˇĐ´Đ°Đš ĐģиĐŊĐē Са ҁĐŋОдĐĩĐģŅĐŊĐĩ", "create_tag": "ĐĄŅŠĐˇĐ´Đ°Đš Ņ‚Đ°Đŗ", "create_tag_description": "ĐĄŅŠĐˇĐ´Đ°ĐšŅ‚Đĩ ĐŊОв Ņ‚Đ°Đŗ. За вĐģĐžĐļĐĩĐŊи Ņ‚Đ°ĐŗĐžĐ˛Đĩ, ĐŧĐžĐģŅ, Đ˛ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŋҊĐģĐŊĐ¸Ņ ĐŋŅŠŅ‚ ĐŊа Ņ‚Đ°ĐŗĐ°, вĐēĐģŅŽŅ‡Đ¸Ņ‚ĐĩĐģĐŊĐž ĐŊаĐēĐģĐžĐŊĐĩĐŊĐ¸Ņ‚Đĩ ҇ĐĩŅ€Ņ‚Đ¸.", "create_user": "ĐĄŅŠĐˇĐ´Đ°Đš ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "created": "ĐĄŅŠĐˇĐ´Đ°Đ´ĐĩĐŊĐž", "created_at": "ĐĄŅŠĐˇĐ´Đ°Đ´ĐĩĐŊ", + "creating_linked_albums": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи аĐģĐąŅƒĐŧи...", "crop": "Đ˜ĐˇŅ€ĐĩĐļи", "curated_object_page_title": "НĐĩŅ‰Đ°", "current_device": "ĐĸĐĩĐēŅƒŅ‰Đž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", @@ -700,10 +763,11 @@ "current_server_address": "ĐĐ°ŅŅ‚ĐžŅŅ‰ Đ°Đ´Ņ€Đĩҁ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "custom_locale": "ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ ĐģĐžĐēаĐģ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ¸ и Ņ‡Đ¸ŅĐģа в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚ ĐžŅ‚ ĐĩСиĐēа и Ņ€ĐĩĐŗĐ¸ĐžĐŊа", + "custom_url": "ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ URL Đ°Đ´Ņ€Đĩҁ", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "ĐĸҊĐŧĐĩĐŊ", - "darkTheme": "ĐŸŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐŊа Ņ‚ŅŠĐŧĐŊа Ņ‚ĐĩĐŧа", + "dark_theme": "ĐĸҊĐŧĐŊа Ņ‚ĐĩĐŧа", "date_after": "Đ”Đ°Ņ‚Đ° ҁĐģĐĩĐ´", "date_and_time": "Đ”Đ°Ņ‚Đ° и Ņ‡Đ°Ņ", "date_before": "Đ”Đ°Ņ‚Đ° ĐŋŅ€Đĩди", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ Đĩ СаĐŋĐ¸ŅĐ°ĐŊа ҃ҁĐŋĐĩ҈ĐŊĐž", "date_range": "ПĐĩŅ€Đ¸ĐžĐ´ ĐžŅ‚ Đ˛Ņ€ĐĩĐŧĐĩ", "day": "ДĐĩĐŊ", + "days": "ДĐŊи", "deduplicate_all": "ДĐĩĐ´ŅƒĐŋĐģиĐēĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи", "deduplication_criteria_1": "РаСĐŧĐĩŅ€ ĐŊа ҁĐŊиĐŧĐēĐ°Ņ‚Đ° в ĐąĐ°ĐšŅ‚ĐžĐ˛Đĩ", "deduplication_criteria_2": "Đ‘Ņ€ĐžĐš EXIF даĐŊĐŊи", @@ -719,6 +784,8 @@ "default_locale": "ЛоĐēаĐģĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ", "default_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ¸ и Ņ‡Đ¸ŅĐģа в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚ ĐžŅ‚ ĐĩСиĐēĐžĐ˛Đ°Ņ‚Đ° ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŊа ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°", "delete": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš", + "delete_action_confirmation_message": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚ĐžĐˇĐ¸ ОйĐĩĐēŅ‚? ĐĄĐģĐĩдва ĐŋŅ€ĐĩĐŧĐĩŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ОйĐĩĐēŅ‚Đ° в ĐēĐžŅˆĐ° Са ĐžŅ‚ĐŋĐ°Đ´ŅŠŅ†Đ¸ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° и ҉Đĩ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊиĐĩ ОйĐĩĐēŅ‚Đ° да ĐąŅŠĐ´Đĩ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚ ĐģĐžĐēаĐģĐŊĐž", + "delete_action_prompt": "{count} ŅĐ° Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸", "delete_album": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš аĐģĐąŅƒĐŧ", "delete_api_key_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚ĐžĐˇĐ¸ API ĐēĐģŅŽŅ‡?", "delete_dialog_alert": "ĐĸĐĩСи ОйĐĩĐēŅ‚Đ¸ ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ СавиĐŊĐ°ĐŗĐ¸ и ĐžŅ‚ Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ° и ĐžŅ‚ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž", @@ -732,19 +799,23 @@ "delete_key": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐēĐģŅŽŅ‡", "delete_library": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "delete_link": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐģиĐŊĐē", + "delete_local_action_prompt": "{count} ŅĐ° Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ ĐģĐžĐēаĐģĐŊĐž", "delete_local_dialog_ok_backed_up_only": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐģĐžĐēаĐģĐŊĐž ŅĐ°ĐŧĐž Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ", "delete_local_dialog_ok_force": "Đ’ŅŠĐŋŅ€ĐĩĐēи Ņ‚ĐžĐ˛Đ° Đ¸ĐˇŅ‚Ņ€Đ¸Đš", "delete_others": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐžŅŅ‚Đ°ĐŊаĐģĐ¸Ņ‚Đĩ", + "delete_permanently": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž", + "delete_permanently_action_prompt": "{count} Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž", "delete_shared_link": "Đ˜ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊ ĐģиĐŊĐē", "delete_shared_link_dialog_title": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ҁĐŋОдĐĩĐģĐĩĐŊĐ°Ņ‚Đ° Đ˛Ņ€ŅŠĐˇĐēа", "delete_tag": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš Ņ‚Đ°Đŗ", - "delete_tag_confirmation_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚Đ°Đŗ {tagName}?", + "delete_tag_confirmation_prompt": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚Đ°ĐŗĐ° {tagName}?", "delete_user": "Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "deleted_shared_link": "Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚ ҁĐŋОдĐĩĐģĐĩĐŊ ĐģиĐŊĐē", "deletes_missing_assets": "Đ˜ĐˇŅ‚Ņ€Đ¸Đ˛Đ° Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ĐģиĐŋŅĐ˛Đ°Ņ‚ ĐŊа Đ´Đ¸ŅĐēа", "description": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩ", "description_input_hint_text": "Добави ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ...", - "description_input_submit_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž. За ĐŋĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ виĐļ в Đ´ĐŊĐĩвĐŊиĐēа", + "description_input_submit_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž. За ĐŋĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ виĐļŅ‚Đĩ в Đ´ĐŊĐĩвĐŊиĐēа", + "deselect_all": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи Đ¸ĐˇĐąĐžŅ€Đ° ĐžŅ‚ Đ˛ŅĐ¸Ņ‡Đēи", "details": "ДĐĩŅ‚Đ°ĐšĐģи", "direction": "ĐŸĐžŅĐžĐēа", "disabled": "ИСĐēĐģŅŽŅ‡ĐĩĐŊĐž", @@ -762,6 +833,7 @@ "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ", "done": "Đ“ĐžŅ‚ĐžĐ˛Đž", "download": "Đ˜ĐˇŅ‚ĐĩĐŗĐģи", + "download_action_prompt": "Đ—Đ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа {count} ОйĐĩĐēŅ‚Đ°", "download_canceled": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž Đĩ ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", "download_complete": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ¸", "download_enqueue": "Đ˜ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩŅ‚Đž Đĩ дОйавĐĩĐŊĐž в ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ°", @@ -788,8 +860,12 @@ "edit": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ", "edit_album": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧ", "edit_avatar": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ°Đ˛Đ°Ņ‚Đ°Ņ€", + "edit_birthday": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ€ĐžĐļĐ´ĐĩĐŊ Đ´ĐĩĐŊ", "edit_date": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ°", "edit_date_and_time": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ´Đ°Ņ‚Đ° и Ņ‡Đ°Ņ", + "edit_date_and_time_action_prompt": "{count} Đ´Đ°Ņ‚Đ° и Đ˛Ņ€ĐĩĐŧĐĩ ŅĐ° Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊи", + "edit_date_and_time_by_offset": "ĐŸŅ€ĐžĐŧŅĐŊа ĐŊа Đ´Đ°Ņ‚Đ°Ņ‚Đ° ҇ҀĐĩС ĐžŅ‚ĐŧĐĩŅŅ‚Đ˛Đ°ĐŊĐĩ", + "edit_date_and_time_by_offset_interval": "Нов ĐŋĐĩŅ€Đ¸ĐžĐ´ ĐžŅ‚ Đ˛Ņ€ĐĩĐŧĐĩ: {from} - {to}", "edit_description": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°Đš ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "edit_description_prompt": "МоĐģŅ, иСйĐĩŅ€Đ¸ ĐŊОвО ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ:", "edit_exclusion_pattern": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ŅˆĐ°ĐąĐģĐžĐŊ Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ", @@ -799,6 +875,7 @@ "edit_key": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐēĐģŅŽŅ‡", "edit_link": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģиĐŊĐē", "edit_location": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", + "edit_location_action_prompt": "{count} ĐģĐžĐēĐ°Ņ†Đ¸Đ¸ ŅĐ° Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊи", "edit_location_dialog_title": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "edit_name": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа иĐŧĐĩ", "edit_people": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", @@ -817,6 +894,7 @@ "empty_trash": "ИСĐŋŅ€Đ°ĐˇĐ˛Đ°ĐŊĐĩ ĐŊа ĐēĐžŅˆ", "empty_trash_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иСĐŋŅ€Đ°ĐˇĐŊĐ¸Ņ‚Đĩ ĐēĐžŅˆŅ‡ĐĩŅ‚Đž? ĐĸОва ҉Đĩ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩ Đ˛ŅĐ¸Ņ‡ĐēĐž в ĐēĐžŅˆŅ‡ĐĩŅ‚Đž Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž ĐžŅ‚ Immich.\nНĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚Đĩ Ņ‚ĐžĐ˛Đ° Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", "enable": "ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ", + "enable_backup": "ВĐēĐģŅŽŅ‡Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžŅ‚Đž ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊĐĩ", "enable_biometric_auth_description": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ Đ˛Đ°ŅˆĐ¸Ņ PIN ĐēОд, Са да Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚Đĩ йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ", "enabled": "ВĐēĐģŅŽŅ‡ĐĩĐŊĐž", "end_date": "ĐšŅ€Đ°ĐšĐŊа Đ´Đ°Ņ‚Đ°", @@ -827,7 +905,9 @@ "error": "Đ“Ņ€Đĩ҈Đēа", "error_change_sort_album": "НĐĩ҃ҁĐŋĐĩ҈ĐŊа ĐŋŅ€ĐžĐŧŅĐŊа ĐŊа Ņ€Đĩда ĐŊа ŅĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧ", "error_delete_face": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đĩ ĐžŅ‚ аĐēŅ‚Đ¸Đ˛Đ°", + "error_getting_places": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ŅŅŠĐąĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧĐĩŅŅ‚Đ°Ņ‚Đ°", "error_loading_image": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž", + "error_loading_partners": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€Đ¸: {error}", "error_saving_image": "Đ“Ņ€Đĩ҈Đēа: {error}", "error_tag_face_bounding_box": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ĐžŅ‚ĐąĐĩĐģŅĐˇĐ˛Đ°ĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đĩ - ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸ ĐŊа Ņ€Đ°ĐŧĐēĐ°Ņ‚Đ°", "error_title": "Đ“Ņ€Đĩ҈Đēа - ĐŊĐĩŅ‰Đž ҁĐĩ ĐžĐąŅŠŅ€Đēа", @@ -851,7 +931,7 @@ "error_selecting_all_assets": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ¸ĐˇĐąĐžŅ€Đ° ĐŊа Đ˛ŅĐ¸Ņ‡Đēи Ņ„Đ°ĐšĐģОвĐĩ", "exclusion_pattern_already_exists": "ĐĸОСи ĐŧОдĐĩĐģ Са иСĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ вĐĩ҇Đĩ ŅŅŠŅ‰ĐĩŅŅ‚Đ˛ŅƒĐ˛Đ°.", "failed_to_create_album": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧ", - "failed_to_create_shared_link": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", + "failed_to_create_shared_link": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋoĐ´ĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", "failed_to_edit_shared_link": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", "failed_to_get_people": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", "failed_to_keep_this_delete_others": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž СаĐŋаСваĐŊĐĩ ĐŊа Ņ‚ĐžĐˇĐ¸ ОйĐĩĐēŅ‚ и Đ¸ĐˇŅ‚Ņ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐžŅŅ‚Đ°ĐŊаĐģĐ¸Ņ‚Đĩ ОйĐĩĐēŅ‚Đ¸", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа иСвĐĩŅŅ‚Đ¸Ņ", "failed_to_load_people": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", "failed_to_remove_product_key": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚ĐžĐ˛Đ¸Ņ ĐēĐģŅŽŅ‡", + "failed_to_reset_pin_code": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ПИН ĐēОда", "failed_to_stack_assets": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžĐ´Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ОйĐĩĐēŅ‚Đ¸", "failed_to_unstack_assets": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐžĐ´Ņ€ĐĩĐ´ĐąĐ°Ņ‚Đ° ĐŊа ОйĐĩĐēŅ‚Đ¸", "failed_to_update_notification_status": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅŠŅŅ‚ĐžŅĐŊиĐĩŅ‚Đž ĐŊа иСвĐĩŅŅ‚Đ¸ŅŅ‚Đ°", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# ĐŋŅŠŅ‚} other {# ĐŋŅŠŅ‚Đ¸Ņ‰Đ°}} ĐŊĐĩ ĐŋŅ€ĐĩĐŧиĐŊĐ°Ņ…Đ° ваĐģĐ¸Đ´Đ°Ņ†Đ¸Ņ", "profile_picture_transparent_pixels": "ĐŸŅ€ĐžŅ„Đ¸ĐģĐŊĐ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи ĐŊĐĩ ĐŧĐžĐŗĐ°Ņ‚ да иĐŧĐ°Ņ‚ ĐŋŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊи ĐŋиĐēҁĐĩĐģи. МоĐģŅ, ŅƒĐ˛ĐĩĐģĐ¸Ņ‡ĐĩŅ‚Đĩ и/иĐģи ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩŅ‚Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž.", "quota_higher_than_disk_size": "ЗададĐĩĐŊа Đĩ ĐēĐ˛ĐžŅ‚Đ°, ĐŋĐž-ĐŗĐžĐģŅĐŧа ĐžŅ‚ Ņ€Đ°ĐˇĐŧĐĩŅ€Đ° ĐŊа Đ´Đ¸ŅĐēа", + "something_went_wrong": "НĐĩŅ‰Đž ҁĐĩ ĐžĐąŅŠŅ€Đēа", "unable_to_add_album_users": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи в аĐģĐąŅƒĐŧ", "unable_to_add_assets_to_shared_link": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ОйĐĩĐēŅ‚Đ¸ в ҁĐŋОдĐĩĐģĐĩĐŊ ĐģиĐŊĐē", "unable_to_add_comment": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž Đ´ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Добави ОĐŋĐ¸ŅĐ°ĐŊиĐĩ...", + "exif_bottom_sheet_description_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "exif_bottom_sheet_details": "ПОДРОБНОСĐĸИ", "exif_bottom_sheet_location": "ĐœĐ¯ĐĄĐĸО", "exif_bottom_sheet_people": "ĐĨОРА", "exif_bottom_sheet_person_add_person": "Добави иĐŧĐĩ", - "exif_bottom_sheet_person_age_months": "Đ’ŅŠĐˇŅ€Đ°ŅŅ‚ {months} ĐŧĐĩҁĐĩŅ†Đ°", - "exif_bottom_sheet_person_age_year_months": "Đ’ŅŠĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´Đ¸ĐŊа и {months} ĐŧĐĩҁĐĩŅ†Đ°", - "exif_bottom_sheet_person_age_years": "Đ’ŅŠĐˇŅ€Đ°ŅŅ‚ {years}", "exit_slideshow": "Đ˜ĐˇŅ…ĐžĐ´ ĐžŅ‚ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒŅ‚Đž", "expand_all": "Đ Đ°ĐˇŅˆĐ¸Ņ€Đ¸ Đ˛ŅĐ¸Ņ‡Đēи", "experimental_settings_new_asset_list_subtitle": "В Ņ€Đ°ĐˇĐ˛Đ¸Ņ‚Đ¸Đĩ", @@ -973,6 +1053,8 @@ "explorer": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "export": "ЕĐēҁĐŋĐžŅ€Ņ‚", "export_as_json": "ЕĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐēĐ°Ņ‚Đž JSON", + "export_database": "ЕĐēҁĐŋĐžŅ€Ņ‚ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи", + "export_database_description": "ЕĐēҁĐŋĐžŅ€Ņ‚ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи SQLite", "extension": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", "external": "Đ’ŅŠĐŊ҈ĐŊĐž", "external_libraries": "Đ’ŅŠĐŊ҈ĐŊи йийĐģĐ¸ĐžŅ‚ĐĩĐēи", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "failed_to_load_folder": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ĐŋаĐŋĐēа", "favorite": "Đ›ŅŽĐąĐ¸Đŧ", + "favorite_action_prompt": "{count} ŅĐ° дОйавĐĩĐŊи в Đ›ŅŽĐąĐ¸Đŧи", "favorite_or_unfavorite_photo": "Добави иĐģи ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊи ҁĐŊиĐŧĐēа ĐžŅ‚ Đ›ŅŽĐąĐ¸Đŧи", "favorites": "Đ›ŅŽĐąĐ¸Đŧи", "favorites_page_no_favorites": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ĐģŅŽĐąĐ¸Đŧи ОйĐĩĐēŅ‚Đ¸", "feature_photo_updated": "ĐŸŅ€ĐĩĐ´ŅŅ‚Đ°Đ˛Đ¸Ņ‚ĐĩĐģĐŊĐ°Ņ‚Đ° ҁĐŊиĐŧĐēа Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊа", "features": "Đ¤ŅƒĐŊĐēŅ†Đ¸Đ¸", + "features_in_development": "Đ¤ŅƒĐŊĐēŅ†Đ¸Đ¸ в ĐŋŅ€ĐžŅ†Đĩҁ ĐŊа Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đēа", "features_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", "file_name": "ИĐŧĐĩ ĐŊа Ņ„Đ°ĐšĐģа", "file_name_or_extension": "ИĐŧĐĩ ĐŊа Ņ„Đ°ĐšĐģ иĐģи Ņ€Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", @@ -998,21 +1082,26 @@ "filter_people": "ФиĐģŅ‚Ņ€Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ°", "filter_places": "ФиĐģŅ‚ŅŠŅ€ ĐŋĐž ĐŧŅŅŅ‚Đž", "find_them_fast": "НаĐŧĐĩŅ€ĐĩŅ‚Đĩ ĐŗĐ¸ ĐąŅŠŅ€ĐˇĐž ĐŋĐž иĐŧĐĩ ҁ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", + "first": "ĐŸŅŠŅ€Đ˛Đ¸", "fix_incorrect_match": "ПоĐŋŅ€Đ°Đ˛ŅĐŊĐĩ ĐŊа ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐž ŅŅŠĐ˛ĐŋадĐĩĐŊиĐĩ", "folder": "ПаĐŋĐēа", "folder_not_found": "ПаĐŋĐēĐ°Ņ‚Đ° ĐŊĐĩ Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊа", "folders": "ПаĐŋĐēи", "folders_feature_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐļдаĐŊĐĩ ĐŊа ĐŋаĐŋĐēĐ°Ņ‚Đ° Са ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ и видĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ в Ņ„Đ°ĐšĐģĐžĐ˛Đ°Ņ‚Đ° ŅĐ¸ŅŅ‚ĐĩĐŧа", + "forgot_pin_code_question": "Đ—Đ°ĐąŅ€Đ°Đ˛Đ¸Đģи ҁ҂Đĩ ŅĐ˛ĐžŅ ПИН ĐēОд?", "forward": "НаĐŋŅ€ĐĩĐ´", "gcast_enabled": "Google Cast", "gcast_enabled_description": "За да Ņ€Đ°ĐąĐžŅ‚Đ¸ Ņ‚Đ°ĐˇĐ¸ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ĐˇĐ°Ņ€ĐĩĐļда Đ˛ŅŠĐŊ҈ĐŊи Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐžŅ‚ Google.", "general": "ĐžĐąŅ‰Đ¸", + "geolocation_instruction_location": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ОйĐĩĐēŅ‚ ҁ GPS ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸ Са да иСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ Ņ‚ŅŅ… иĐģи иСйĐĩŅ€ĐĩŅ‚Đĩ ĐŧŅŅŅ‚Đž Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐŊĐž ĐžŅ‚ ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ°", "get_help": "ПоĐŧĐžŅ‰", "get_wifiname_error": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ°Đ˛Đ°ĐŊĐĩ иĐŧĐĩŅ‚Đž ĐŊа Wi-Fi ĐŧŅ€ĐĩĐļĐ°Ņ‚Đ°. МоĐģŅ, ŅƒĐąĐĩĐ´ĐĩŅ‚Đĩ ҁĐĩ, ҇Đĩ ŅĐ° ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩĐŊи ĐŊ҃ĐļĐŊĐ¸Ņ‚Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž и иĐŧа Đ˛Ņ€ŅŠĐˇĐēа ҁ Wi-Fi", "getting_started": "КаĐē да СаĐŋĐžŅ‡ĐŊĐĩĐŧ", "go_back": "Đ’Ņ€ŅŠŅ‰Đ°ĐŊĐĩ ĐŊаСад", "go_to_folder": "ĐžŅ‚Đ¸Đ´Đ¸ в ĐŋаĐŋĐēĐ°Ņ‚Đ°", "go_to_search": "ĐŸŅ€ĐĩĐŧиĐŊаваĐŊĐĩ ĐēҊĐŧ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", + "gps": "GPS ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸", + "gps_missing": "ĐŅĐŧа GPS ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸", "grant_permission": "Дай Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", "group_albums_by": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Đš аĐģĐąŅƒĐŧ ĐŋĐž...", "group_country": "Đ“Ņ€ŅƒĐŋĐ¸Ņ€Đ°Đš ĐŋĐž Đ´ŅŠŅ€Đļава", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "ВĐēĐģŅŽŅ‡Đ¸ Ņ‚Đ°ĐēŅ‚Đ¸ĐģĐŊа ĐžĐąŅ€Đ°Ņ‚ĐŊа Đ˛Ņ€ŅŠĐˇĐēа", "haptic_feedback_title": "ĐĸаĐēŅ‚Đ¸ĐģĐŊа ĐžĐąŅ€Đ°Ņ‚ĐŊа Đ˛Ņ€ŅŠĐˇĐēа", "has_quota": "ЛиĐŧĐ¸Ņ‚", + "hash_asset": "ОбĐĩĐēŅ‚ ҁ Ņ…Đĩ҈", + "hashed_assets": "ĐĨĐĩŅˆĐ¸Ņ€Đ°ĐŊи ОйĐĩĐēŅ‚Đ¸", + "hashing": "ĐĨĐĩŅˆĐ¸Ņ€Đ°ĐŊĐĩ", "header_settings_add_header_tip": "Добави ĐˇĐ°ĐŗĐģавиĐĩ", "header_settings_field_validator_msg": "НĐĩĐ´ĐžĐŋŅƒŅŅ‚Đ¸ĐŧĐž Đĩ да ĐŊŅĐŧа ŅŅ‚ĐžĐšĐŊĐžŅŅ‚", "header_settings_header_name_input": "ИĐŧĐĩ ĐŊа ĐˇĐ°ĐŗĐģавиĐĩŅ‚Đž", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "МоĐļĐĩ да ĐēĐ°Ņ‡Đ˛Đ°Ņ‚Đĩ ĐŧаĐēŅĐ¸Đŧ҃Đŧ 30 ОйĐĩĐēŅ‚Đ° ĐĩĐ´ĐŊĐžĐ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐž, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ", "host": "ĐĨĐžŅŅ‚", "hour": "Đ§Đ°Ņ", + "hours": "Đ§Đ°ŅĐ°", "id": "ID", + "idle": "БĐĩСдĐĩĐšŅŅ‚Đ˛Đ¸Đĩ", "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐŊи ҁĐŊиĐŧĐēи ĐžŅ‚ iCloud", "ignore_icloud_photos_description": "ĐĄĐŊиĐŧĐēи, ĐēĐžĐ¸Ņ‚Đž ŅĐ° СаĐŋаСĐĩĐŊи в iCloud ĐŊŅĐŧа да ҁĐĩ ĐēĐ°Ņ‡Đ˛Đ°Ņ‚ в Immich ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "image": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", @@ -1112,10 +1206,13 @@ "language_no_results_title": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ĐĩĐˇĐ¸Ņ†Đ¸", "language_search_hint": "ĐĸŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐĩĐˇĐ¸Ņ†Đ¸...", "language_setting_description": "ИСйĐĩŅ€ĐĩŅ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊ ĐĩСиĐē", + "large_files": "ГоĐģĐĩĐŧи Ņ„Đ°ĐšĐģОвĐĩ", + "last": "ĐŸĐžŅĐģĐĩĐ´ĐĩĐŊ", "last_seen": "ĐŸĐžŅĐģĐĩĐ´ĐŊĐž Đ˛Đ¸Đ´ŅĐŊĐž", "latest_version": "ĐŸĐžŅĐģĐĩĐ´ĐŊа вĐĩŅ€ŅĐ¸Ņ", "latitude": "Đ¨Đ¸Ņ€Đ¸ĐŊа", "leave": "ИСĐģĐĩС", + "leave_album": "НаĐŋ҃ҁĐēаĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧа", "lens_model": "МодĐĩĐģ ĐģĐĩŅ‰Đ°", "let_others_respond": "ПозвоĐģĐĩŅ‚Đĩ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ‚Đĩ да ĐžŅ‚ĐŗĐžĐ˛ĐžŅ€ŅŅ‚", "level": "Ниво", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "Đ”Đ°Ņ‚Đ° ĐŊа ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ", "library_page_sort_last_modified": "ĐŸĐžŅĐģĐĩĐ´ĐŊа ĐŋŅ€ĐžĐŧŅĐŊа", "library_page_sort_title": "Đ—Đ°ĐŗĐģавиĐĩ ĐŊа аĐģĐąŅƒĐŧа", + "licenses": "Đ›Đ¸Ņ†ĐĩĐŊСи", "light": "ХвĐĩŅ‚ĐģĐž", + "like": "ĐĨĐ°Ņ€ĐĩŅĐ°ĐšŅ‚Đĩ", "like_deleted": "ĐšĐ°Ņ‚Đž Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚", "link_motion_video": "ЛиĐŊĐē ĐēҊĐŧ видĐĩĐž", - "link_options": "ОĐŋŅ†Đ¸Đ¸ ĐŊа ĐģиĐŊĐē Са ҁĐŋОдĐĩĐģŅĐŊĐĩ", "link_to_oauth": "ЛиĐŊĐē ĐēҊĐŧ OAuth", "linked_oauth_account": "ĐĄĐ˛ŅŠŅ€ĐˇĐ°ĐŊ OAuth аĐēĐ°ŅƒĐŊŅ‚", "list": "Đ›Đ¸ŅŅ‚", "loading": "Đ—Đ°Ņ€ĐĩĐļдаĐŊĐĩ", "loading_search_results_failed": "Đ—Đ°Ņ€ĐĩĐļдаĐŊĐĩŅ‚Đž ĐŊа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž Đĩ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", + "local": "ЛоĐēаĐģĐŊĐž", "local_asset_cast_failed": "НĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋŅ€Đĩдава ОйĐĩĐēŅ‚, ĐēĐžĐšŅ‚Đž ĐžŅ‰Đĩ ĐŊĐĩ Đĩ ĐēĐ°Ņ‡ĐĩĐŊ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "local_assets": "ЛоĐēаĐģĐŊи ОйĐĩĐēŅ‚Đ¸", + "local_media_summary": "ĐžĐąĐžĐąŅ‰ĐĩĐŊиĐĩ ĐŊа ĐģĐžĐēаĐģĐŊĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ", "local_network": "ЛоĐēаĐģĐŊа ĐŧŅ€ĐĩĐļа", "local_network_sheet_info": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ҉Đĩ ҁĐĩ ŅĐ˛ŅŠŅ€ĐļĐĩ ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ĐŊа Ņ‚ĐžĐˇĐ¸ URL, ĐēĐžĐŗĐ°Ņ‚Đž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž Đĩ ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊĐž ĐēҊĐŧ СададĐĩĐŊĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа", "location_permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ Са ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐĩĐžĐŗŅ€Đ°Ņ„ŅĐēа Đ´ŅŠĐģĐļиĐŊа Ņ‚ŅƒĐē", "lock": "ЗаĐēĐģŅŽŅ‡Đ¸", "locked_folder": "ЗаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа", + "log_detail_title": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ ĐžŅ‚ Đ´ĐŊĐĩвĐŊиĐēа", "log_out": "ИСĐģиСаĐŊĐĩ", "log_out_all_devices": "ИСĐģиСаĐŊĐĩ ҁ Đ˛ŅĐ¸Ņ‡Đēи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "logged_in_as": "ВĐŋĐ¸ŅĐ°ĐŊ ĐēĐ°Ņ‚Đž {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž ОйĐŊОвĐĩĐŊа ĐŋĐ°Ņ€ĐžĐģа", "logout_all_device_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иСĐģĐĩСĐĩŅ‚Đĩ ĐžŅ‚ Đ˛ŅĐ¸Ņ‡Đēи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°?", "logout_this_device_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да иСĐģĐĩСĐĩŅ‚Đĩ ĐžŅ‚ Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž?", + "logs": "ДĐŊĐĩвĐŊиĐē", "longitude": "Đ”ŅŠĐģĐļиĐŊа", "look": "Đ˜ĐˇĐŗĐģĐĩĐ´", "loop_videos": "ĐŸĐžĐ˛Ņ‚Đ°Ņ€ŅĐŊĐĩ ĐŊа видĐĩĐ°Ņ‚Đ°", @@ -1185,6 +1288,7 @@ "main_branch_warning": "ИСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ вĐĩŅ€ŅĐ¸Ņ Са Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸Ņ†Đ¸, ŅĐ¸ĐģĐŊĐž ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ˛Đ°ĐŧĐĩ да иСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģĐŊа вĐĩŅ€ŅĐ¸Ņ!", "main_menu": "ГĐģавĐŊĐž ĐŧĐĩĐŊŅŽ", "make": "ĐœĐ°Ņ€Đēа", + "manage_geolocation": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŅ‚Đ°", "manage_shared_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", "manage_sharing_with_partners": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģŅĐŊĐĩŅ‚Đž ҁ ĐŋĐ°Ņ€Ņ‚ĐŊŅŒĐžŅ€Đ¸", "manage_the_app_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", @@ -1193,8 +1297,7 @@ "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа вĐģĐĩСĐģĐ¸Ņ‚Đĩ в ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "manage_your_oauth_connection": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа OAuth Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ°", "map": "ĐšĐ°Ņ€Ņ‚Đ°", - "map_assets_in_bound": "{count} ҁĐŊиĐŧĐēи", - "map_assets_in_bounds": "{count} ҁĐŊиĐŧĐēи", + "map_assets_in_bounds": "{count, plural, =0 {ĐŅĐŧа ҁĐŊиĐŧĐēи} one {# ҁĐŊиĐŧĐēа} other {# ҁĐŊиĐŧĐēи}}", "map_cannot_get_user_location": "НĐĩ ĐŧĐžĐļĐ°Ņ… да ĐŋĐžĐģŅƒŅ‡Đ° ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "ИСĐŋĐžĐģСваК Ņ‚ĐžĐ˛Đ° ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -1202,7 +1305,6 @@ "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_title": "ĐžŅ‚ĐēаСаĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "map_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ°", @@ -1221,6 +1323,7 @@ "mark_as_read": "ĐœĐ°Ņ€ĐēĐ¸Ņ€Đ°Đš ĐēĐ°Ņ‚Đž ҇ĐĩŅ‚ĐĩĐŊĐž", "marked_all_as_read": "Đ’ŅĐ¸Ņ‡Đēи ĐŧĐ°Ņ€ĐēĐ¸Ņ€Đ°ĐŊи ĐēĐ°Ņ‚Đž ĐŋŅ€ĐžŅ‡ĐĩŅ‚ĐĩĐŊи", "matches": "ĐĄŅŠĐ˛ĐŋадĐĩĐŊĐ¸Ņ", + "matching_assets": "ĐĄŅŠĐ˛ĐŋĐ°Đ´Đ°Ņ‰Đ¸ ОйĐĩĐēŅ‚Đ¸", "media_type": "Вид ĐŧĐĩĐ´Đ¸Ņ", "memories": "ĐĄĐŋĐžĐŧĐĩĐŊи", "memories_all_caught_up": "ĐĸОва Đĩ Đ˛ŅĐ¸Ņ‡ĐēĐž Са Đ´ĐŊĐĩҁ", @@ -1239,13 +1342,15 @@ "merged_people_count": "ĐĄĐģŅŅ‚ {count, plural, one {# Ņ‡ĐžĐ˛ĐĩĐē} other {# Ņ‡ĐžĐ˛ĐĩĐēа}}", "minimize": "МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ", "minute": "МиĐŊŅƒŅ‚Đ°", + "minutes": "МиĐŊŅƒŅ‚Đ¸", "missing": "ЛиĐŋŅĐ˛Đ°Ņ‰Đ¸", "model": "МодĐĩĐģ", "month": "МĐĩҁĐĩ҆", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM Đŗ", "more": "ĐžŅ‰Đĩ", "move": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸", "move_off_locked_folder": "ИСвади ĐžŅ‚ СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа", + "move_to_lock_folder_action_prompt": "{count} ŅĐ° дОйавĐĩĐŊи в СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа", "move_to_locked_folder": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ в СаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа", "move_to_locked_folder_confirmation": "ĐĸĐĩСи ҁĐŊиĐŧĐēи и видĐĩа ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ ĐžŅ‚ Đ˛ŅĐ¸Ņ‡Đēи аĐģĐąŅƒĐŧи и ҉Đĩ ŅĐ° Đ´ĐžŅŅ‚ŅŠĐŋĐŊи ŅĐ°ĐŧĐž в СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа", "moved_to_archive": "{count, plural, one {# ОйĐĩĐēŅ‚ Đĩ ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊ} many {# ОйĐĩĐēŅ‚Đ° ŅĐ° ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊи} other {# ОйĐĩĐēŅ‚Đ° ŅĐ° ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊи}} в Đ°Ņ€Ņ…Đ¸Đ˛Đ°", @@ -1257,6 +1362,10 @@ "my_albums": "Мои аĐģĐąŅƒĐŧи", "name": "ИĐŧĐĩ", "name_or_nickname": "ИĐŧĐĩ иĐģи ĐŋŅ€ŅĐēĐžŅ€", + "network_requirement_photos_upload": "ИСĐŋĐžĐģСваК ĐŧОйиĐģĐŊи даĐŊĐŊи Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ҁĐŊиĐŧĐēи", + "network_requirement_videos_upload": "ИСĐŋĐžĐģСваК ĐŧОйиĐģĐŊи даĐŊĐŊи Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа видĐĩĐž", + "network_requirements": "Đ˜ĐˇĐ¸ŅĐēваĐŊĐ¸Ņ ĐēҊĐŧ ĐŧŅ€ĐĩĐļĐ°Ņ‚Đ°", + "network_requirements_updated": "ĐœŅ€ĐĩĐļĐžĐ˛Đ¸Ņ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐ° ĐŋŅ€ĐžĐŧĐĩĐŊĐĩĐŊи, ĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ° Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ", "networking_settings": "ĐœŅ€ĐĩĐļа", "networking_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са Đ˛Ņ€ŅŠĐˇĐēа ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "never": "НиĐēĐžĐŗĐ°", @@ -1266,6 +1375,7 @@ "new_person": "Нов Ņ‡ĐžĐ˛ĐĩĐē", "new_pin_code": "Нов PIN ĐēОд", "new_pin_code_subtitle": "ĐĸОва Đĩ ĐŋŅŠŅ€Đ˛Đ¸ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž СаĐēĐģŅŽŅ‡ĐĩĐŊа ĐŋаĐŋĐēа. ĐĄŅŠĐˇĐ´Đ°ĐšŅ‚Đĩ PIN ĐēОд Са ĐˇĐ°Ņ‰Đ¸Ņ‚ĐĩĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Ņ‚Đ°ĐˇĐ¸ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°", + "new_timeline": "Нова Đ˛Ņ€ĐĩĐŧĐĩва ĐģиĐŊĐ¸Ņ", "new_user_created": "ĐĄŅŠĐˇĐ´Đ°Đ´ĐĩĐŊ ĐŊОв ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "new_version_available": "НАЛИЧНА НОВА Đ’Đ•Đ ĐĄĐ˜Đ¯", "newest_first": "Най-ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŋŅŠŅ€Đ˛Đ¸", @@ -1279,19 +1389,25 @@ "no_assets_message": "КЛИКНЕĐĸЕ, ЗА ДА КАЧИĐĸЕ ПĐĒРВАĐĸА ХИ СНИМКА", "no_assets_to_show": "ĐŅĐŧа ОйĐĩĐēŅ‚Đ¸ Са ĐŋĐžĐēаСваĐŊĐĩ", "no_cast_devices_found": "ĐŅĐŧа ĐŊаĐŧĐĩŅ€ĐĩĐŊи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° Са ĐŋŅ€ĐĩдаваĐŊĐĩ", + "no_checksum_local": "ЛиĐŋŅĐ˛Đ°Ņ‚ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊи ҁ҃Đŧи - ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋĐžĐģŅƒŅ‡Đ°Ņ‚ ĐģĐžĐēаĐģĐŊи ОйĐĩĐēŅ‚Đ¸", + "no_checksum_remote": "ЛиĐŋŅĐ˛Đ°Ņ‚ ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊи ҁ҃Đŧи - ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋĐžĐģŅƒŅ‡Đ°Ņ‚ ОйĐĩĐēŅ‚Đ¸ ĐžŅ‚ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "no_duplicates_found": "НĐĩ ĐąŅŅ…Đ° ĐžŅ‚ĐēŅ€Đ¸Ņ‚Đ¸ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸.", "no_exif_info_available": "ĐŅĐŧа exif иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ", "no_explore_results_message": "ĐšĐ°Ņ‡ĐĩŅ‚Đĩ ĐžŅ‰Đĩ ҁĐŊиĐŧĐēи, Са да Ņ€Đ°ĐˇĐŗĐģĐĩĐ´Đ°Ņ‚Đĩ ĐēĐžĐģĐĩĐēŅ†Đ¸ŅŅ‚Đ° ŅĐ¸.", "no_favorites_message": "ДобавĐĩŅ‚Đĩ в ĐģŅŽĐąĐ¸Đŧи, Са да ĐŊаĐŧĐ¸Ņ€Đ°Ņ‚Đĩ ĐąŅŠŅ€ĐˇĐž ĐŊаК-Đ´ĐžĐąŅ€Đ¸Ņ‚Đĩ ŅĐ¸ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ", "no_libraries_message": "ĐĄŅŠĐˇĐ´Đ°ĐšŅ‚Đĩ Đ˛ŅŠĐŊ҈ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа Са да Ņ€Đ°ĐˇĐŗĐģĐĩĐļĐ´Đ°Ņ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ", + "no_local_assets_found": "НĐĩ Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊ ĐģĐžĐēаĐģĐĩĐŊ ОйĐĩĐēŅ‚ ҁ Ņ‚Đ°Đēава ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊа ҁ҃Đŧа", "no_locked_photos_message": "ĐĄĐŊиĐŧĐēĐ¸Ņ‚Đĩ и видĐĩĐ°Ņ‚Đ° в СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸ и ĐŊĐĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚ ĐŋŅ€Đ¸ Ņ€Đ°ĐˇĐŗĐģĐĩĐļдаĐŊĐĩ ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°.", "no_name": "БĐĩС иĐŧĐĩ", "no_notifications": "ĐŅĐŧа иСвĐĩŅŅ‚Đ¸Ņ", "no_people_found": "НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ŅŅŠĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ°Ņ‰Đ¸ Ņ…ĐžŅ€Đ°", "no_places": "ĐŅĐŧа ĐŧĐĩŅŅ‚Đ°", + "no_remote_assets_found": "НĐĩ Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊ ОйĐĩĐēŅ‚ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ҁ Ņ‚Đ°Đēава ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐŊа ҁ҃Đŧа", "no_results": "ĐŅĐŧа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸", "no_results_description": "ОĐŋĐ¸Ņ‚Đ°ĐšŅ‚Đĩ ҁҊҁ ŅĐ¸ĐŊĐžĐŊиĐŧ иĐģи ĐŋĐž-ĐžĐąŅ‰Đ° ĐēĐģŅŽŅ‡ĐžĐ˛Đ° Đ´ŅƒĐŧа", "no_shared_albums_message": "ĐĄŅŠĐˇĐ´Đ°ĐšŅ‚Đĩ аĐģĐąŅƒĐŧ, Са да ҁĐŋОдĐĩĐģŅŅ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩĐžĐēĐģиĐŋОвĐĩ ҁ Ņ…ĐžŅ€Đ°Ņ‚Đ° в ĐŧŅ€ĐĩĐļĐ°Ņ‚Đ° ŅĐ¸", + "no_uploads_in_progress": "ĐŅĐŧа ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ в ĐŧĐžĐŧĐĩĐŊŅ‚Đ°", + "not_available": "НĐĩĐŊаĐģĐ¸Ņ‡ĐŊĐž", "not_in_any_album": "НĐĩ Đĩ в ĐŊиĐēОК аĐģĐąŅƒĐŧ", "not_selected": "НĐĩ Đĩ Đ¸ĐˇĐąŅ€Đ°ĐŊĐž", "note_apply_storage_label_to_previously_uploaded assets": "ЗабĐĩĐģĐĩĐļĐēа: За да ĐŋŅ€Đ¸ĐģĐžĐļĐ¸Ņ‚Đĩ ĐĩŅ‚Đ¸ĐēĐĩŅ‚Đ° Са ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐēҊĐŧ ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģĐŊĐž ĐēĐ°Ņ‡ĐĩĐŊи аĐēŅ‚Đ¸Đ˛Đ¸, ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐšŅ‚Đĩ", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģĐŊа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са Immich", "offline": "ĐžŅ„ĐģаКĐŊ", + "offset": "ĐžŅ‚ĐŧĐĩŅŅ‚Đ˛Đ°ĐŊĐĩ", "ok": "Đ”ĐžĐąŅ€Đĩ", "oldest_first": "Най-ŅŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ĐŋŅŠŅ€Đ˛Đ¸", "on_this_device": "На Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "ĐžŅ‚Đ˛Đ°Ņ€Đ¸ Ņ„Đ¸ĐģŅ‚Ņ€Đ¸Ņ‚Đĩ Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ", "options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "or": "иĐģи", + "organize_into_albums": "Organitzar per àlbums", + "organize_into_albums_description": "Posar les fotos existents dins dels àlbums fent servir la configuraciÃŗ de sincronitzaciÃŗ", "organize_your_library": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛Đ°ŅˆĐ°Ņ‚Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "original": "ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ", "other": "Đ”Ņ€ŅƒĐŗĐ¸", "other_devices": "Đ”Ņ€ŅƒĐŗĐ¸ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "other_entities": "Đ”Ņ€ŅƒĐŗĐ¸ ОйĐĩĐēŅ‚Đ¸", "other_variables": "Đ”Ņ€ŅƒĐŗĐ¸ ĐŋŅ€ĐžĐŧĐĩĐŊĐģиви", "owned": "ĐœĐžĐ¸Ņ‚Đĩ", "owner": "ĐĄĐžĐąŅŅ‚Đ˛ĐĩĐŊиĐē", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ. За да ĐŧĐžĐļĐĩ Immich да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ° и ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ° ĐŗĐ°ĐģĐĩŅ€Đ¸ŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩŅ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ҁĐŊиĐŧĐēи и видĐĩĐž в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ.", "permission_onboarding_request": "Immich ҁĐĩ ĐŊ҃ĐļдаĐĩ ĐžŅ‚ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ Са ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ҁĐŊиĐŧĐēи и видĐĩĐž.", "person": "ЧОвĐĩĐē", + "person_age_months": "{months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ°}}", + "person_age_year_months": "1 ĐŗĐžĐ´Đ¸ĐŊа и {months, plural, one {# ĐŧĐĩҁĐĩ҆} other {# ĐŧĐĩҁĐĩŅ†Đ°}}", + "person_age_years": "{years, plural, other {# ĐŗĐžĐ´Đ¸ĐŊи}}", "person_birthdate": "Đ”Đ°Ņ‚Đ° ĐŊа Ņ€Đ°ĐļдаĐŊĐĩ {date}", "person_hidden": "{name}{hidden, select, true { (ҁĐēŅ€Đ¸Ņ‚)} other {}}", "photo_shared_all_users": "Đ˜ĐˇĐŗĐģĐĩĐļда, ҇Đĩ ҁ҂Đĩ ҁĐŋОдĐĩĐģиĐģи ҁĐŊиĐŧĐēĐ¸Ņ‚Đĩ ŅĐ¸ ҁ Đ˛ŅĐ¸Ņ‡Đēи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи иĐģи ĐŊŅĐŧĐ°Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи, ҁ ĐēĐžĐ¸Ņ‚Đž да ҁĐŋОдĐĩĐģŅŅ‚Đĩ.", @@ -1406,6 +1529,7 @@ "port": "ĐŸĐžŅ€Ņ‚", "preferences_settings_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ¸ŅŅ‚Đ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž", "preferences_settings_title": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐ¸Ņ", + "preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа", "preset": "ШайĐģĐžĐŊ", "preview": "ĐŸŅ€ĐĩĐŗĐģĐĩди", "previous": "ĐŸŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž", @@ -1418,12 +1542,13 @@ "privacy": "ПовĐĩŅ€Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚", "profile": "ĐŸŅ€ĐžŅ„Đ¸Đģ", "profile_drawer_app_logs": "ДĐŊĐĩвĐŊиĐē", - "profile_drawer_client_out_of_date_major": "МобиĐģĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģĐž. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°Đš Đ´Đž ĐŊаК-ĐŊĐžĐ˛Đ°Ņ‚Đ° ĐžŅĐŊОвĐŊа вĐĩŅ€ŅĐ¸Ņ.", - "profile_drawer_client_out_of_date_minor": "МобиĐģĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģĐž. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°Đš Đ´Đž ĐŊаК-ĐŊĐžĐ˛Đ°Ņ‚Đ° вĐĩŅ€ŅĐ¸Ņ.", + "profile_drawer_client_out_of_date_major": "МобиĐģĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģĐž. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐšŅ‚Đĩ Đ´Đž ĐŊаК-ĐŊĐžĐ˛Đ°Ņ‚Đ° ĐžŅĐŊОвĐŊа вĐĩŅ€ŅĐ¸Ņ.", + "profile_drawer_client_out_of_date_minor": "МобиĐģĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģĐž. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐšŅ‚Đĩ Đ´Đž ĐŊаК-ĐŊĐžĐ˛Đ°Ņ‚Đ° вĐĩŅ€ŅĐ¸Ņ.", "profile_drawer_client_server_up_to_date": "КĐģиĐĩĐŊŅ‚Đ° и ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ŅĐ° ОйĐŊОвĐĩĐŊи", "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "ВĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģа. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°Đš ĐŋĐžĐŊĐĩ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° ĐŗĐģавĐŊа вĐĩŅ€ŅĐ¸Ņ.", - "profile_drawer_server_out_of_date_minor": "ВĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģа. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°Đš Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° вĐĩŅ€ŅĐ¸Ņ.", + "profile_drawer_readonly_mode": "Đ ĐĩĐļиĐŧа ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ Đĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ. ĐĄ Đ´ŅŠĐģĐŗĐž ĐŊĐ°Ņ‚Đ¸ŅĐēаĐŊĐĩ Đ˛ŅŠŅ€Ņ…Ņƒ ĐēĐ°Ņ€Ņ‚Đ¸ĐēĐ°Ņ‚Đ°-Đ°Đ˛Đ°Ņ‚Đ°Ņ€ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ ҉Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ.", + "profile_drawer_server_out_of_date_major": "ВĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģа. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐšŅ‚Đĩ ĐŋĐžĐŊĐĩ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° ĐŗĐģавĐŊа вĐĩŅ€ŅĐ¸Ņ.", + "profile_drawer_server_out_of_date_minor": "ВĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đĩ ĐžŅŅ‚Đ°Ņ€ŅĐģа. МоĐģŅ, аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐšŅ‚Đĩ Đ´Đž ĐŋĐžŅĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° вĐĩŅ€ŅĐ¸Ņ.", "profile_image_of_user": "ĐŸŅ€ĐžŅ„Đ¸ĐģĐŊа ҁĐŊиĐŧĐēа ĐŊа {user}", "profile_picture_set": "ĐŸŅ€ĐžŅ„Đ¸ĐģĐŊĐ°Ņ‚Đ° ҁĐŊиĐŧĐēа Đĩ ҁĐģĐžĐļĐĩĐŊа.", "public_album": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐĩĐŊ аĐģĐąŅƒĐŧ", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŊа ĐŋĐžĐ´Đ´Ņ€ŅŠĐļĐŊиĐē", "purchase_server_title": "ĐĄŅŠŅ€Đ˛ŅŠŅ€", "purchase_settings_server_activated": "ĐŸŅ€ĐžĐ´ŅƒĐēŅ‚ĐžĐ˛Đ¸ŅŅ‚ ĐēĐģŅŽŅ‡ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° ҁĐĩ ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ° ĐžŅ‚ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", + "query_asset_id": "Buscar item per ID", + "queue_status": "В ĐžĐŋĐ°ŅˆĐēа {count} ĐžŅ‚ {total}", "rating": "ĐžŅ†ĐĩĐŊĐēа ҁҊҁ СвĐĩСди", "rating_clear": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ¸ ĐžŅ†ĐĩĐŊĐēĐ°Ņ‚Đ°", "rating_count": "{count, plural, one {# СвĐĩСда} other {# СвĐĩСди}}", "rating_description": "ПоĐēаĐļи EXIF ĐžŅ†ĐĩĐŊĐēĐ°Ņ‚Đ° в ĐŋаĐŊĐĩĐģа ҁ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ", "reaction_options": "Đ˜ĐˇĐąĐžŅ€ ĐŊа Ņ€ĐĩаĐēŅ†Đ¸Ņ", "read_changelog": "ĐŸŅ€ĐžŅ‡ĐĩŅ‚Đ¸ ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ", + "readonly_mode_disabled": "Đ ĐĩĐļиĐŧа ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ", + "readonly_mode_enabled": "Đ ĐĩĐļиĐŧа ŅĐ°ĐŧĐž Са ҇ĐĩŅ‚ĐĩĐŊĐĩ Đĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ", + "ready_for_upload": "Đ“ĐžŅ‚ĐžĐ˛Đž Са ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", "reassign": "ĐŸŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ°Đ˛Đ°ĐŊĐĩ", "reassigned_assets_to_existing_person": "ĐŸŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊи {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}} ĐŊа {name, select, null {ŅŅŠŅ‰ĐĩŅŅ‚Đ˛ŅƒĐ˛Đ°Ņ‰ Ņ‡ĐžĐ˛ĐĩĐē} other {{name}}}", "reassigned_assets_to_new_person": "ĐŸŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊи {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}} ĐŊа ĐŊОв Ņ‡ĐžĐ˛ĐĩĐē", @@ -1488,6 +1618,9 @@ "refreshing_faces": "ОĐŋŅ€ĐĩҁĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐģĐ¸Ņ†Đ°Ņ‚Đ°", "refreshing_metadata": "ОĐŋŅ€ĐĩҁĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊĐ¸Ņ‚Đĩ", "regenerating_thumbnails": "ĐŸŅ€ĐĩŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸Ņ‚Đĩ", + "remote": "На ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "remote_assets": "ОбĐĩĐēŅ‚Đ¸ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "remote_media_summary": "ĐžĐąĐžĐąŅ‰ĐĩĐŊиĐĩ ĐŊа ĐŧĐĩдиКĐŊĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", "remove": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи", "remove_assets_album_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩŅ‚Đĩ {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}} ĐžŅ‚ аĐģĐąŅƒĐŧа?", "remove_assets_shared_link_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐĩŅ‚Đĩ {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°}} ĐžŅ‚ Ņ‚ĐžĐˇĐ¸ ҁĐŋĐžĐĩĐ´ĐĩĐģĐĩĐŊ ĐģиĐŊĐē?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи СададĐĩĐŊĐ¸Ņ диаĐŋаСОĐŊ ĐžŅ‚ Đ´Đ°Ņ‚Đ¸", "remove_deleted_assets": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи Đ˜ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸Ņ‚Đĩ ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "remove_from_album": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ĐžŅ‚ аĐģĐąŅƒĐŧа", + "remove_from_album_action_prompt": "{count} ŅĐ° ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ ĐžŅ‚ аĐģĐąŅƒĐŧа", "remove_from_favorites": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ĐžŅ‚ Đ›ŅŽĐąĐ¸Đŧи", + "remove_from_lock_folder_action_prompt": "{count} ŅĐ° ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ ĐžŅ‚ СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа", "remove_from_locked_folder": "ĐœĐ°Ņ…ĐŊи ĐžŅ‚ СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа", "remove_from_locked_folder_confirmation": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ŅĐ¸, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ Ņ‚ĐĩСи ҁĐŊиĐŧĐēи и видĐĩа да ĐąŅŠĐ´Đ°Ņ‚ иСвадĐĩĐŊи ĐžŅ‚ СаĐēĐģŅŽŅ‡ĐĩĐŊĐ°Ņ‚Đ° ĐŋаĐŋĐēа? ĐĸĐĩ ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ видиĐŧи в йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°.", "remove_from_shared_link": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи ĐžŅ‚ ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ ĐģиĐŊĐē", @@ -1523,19 +1658,29 @@ "reset_password": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", "reset_people_visibility": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа видиĐŧĐžŅŅ‚Ņ‚Đ° ĐŊа Ņ…ĐžŅ€Đ°Ņ‚Đ°", "reset_pin_code": "ĐŅƒĐģĐ¸Ņ€Đ°Đš PIN ĐēОда", + "reset_pin_code_description": "АĐēĐž ҁ҂Đĩ ŅĐ¸ ĐˇĐ°ĐąŅ€Đ°Đ˛Đ¸Đģи ПИН ĐēОда, ĐŧĐžĐļĐĩ да ҁĐĩ ĐžĐąŅŠŅ€ĐŊĐĩŅ‚Đĩ ĐēҊĐŧ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Са да ĐŗĐž ĐŊ҃ĐģĐ¸Ņ€Đ°", + "reset_pin_code_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž ĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊ ПИН ĐēОд", + "reset_pin_code_with_password": "ĐĄ Đ˛Đ°ŅˆĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа ĐŧĐžĐļĐĩŅ‚Đĩ виĐŊĐ°ĐŗĐ¸ да ĐŊ҃ĐģĐ¸Ņ€Đ°Ņ‚Đĩ ŅĐ˛ĐžŅ ПИН ĐēОд", + "reset_sqlite": "ĐŅƒĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи SQLite", + "reset_sqlite_confirmation": "ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŊ҃ĐģĐ¸Ņ€Đ°Ņ‚Đĩ ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи SQLite? ĐŠĐĩ Ņ‚Ņ€ŅĐąĐ˛Đ° да иСĐģĐĩСĐĩŅ‚Đĩ ĐžŅ‚ ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ° и да ҁĐĩ вĐŋĐ¸ŅˆĐĩŅ‚Đĩ ĐžŅ‚ĐŊОвО Са ĐŊОва ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŊа даĐŊĐŊĐ¸Ņ‚Đĩ", + "reset_sqlite_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž ĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° даĐŊĐŊи SQLite", "reset_to_default": "Đ’Ņ€ŅŠŅ‰Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐąŅ€Đ¸Ņ‡ĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", "resolve_duplicates": "Đ ĐĩŅˆĐ¸ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸Ņ‚Đĩ", "resolved_all_duplicates": "Đ’ŅĐ¸Ņ‡Đēи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸ ŅĐ° Ņ€Đĩ҈ĐĩĐŊи", "restore": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ", "restore_all": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОви Đ˛ŅĐ¸Ņ‡Đēи", + "restore_trash_action_prompt": "{count} Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊи ĐžŅ‚ ĐēĐžŅˆĐ°", "restore_user": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОви ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ", "restored_asset": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", "resume": "ĐŸŅ€ĐžĐ´ŅŠĐģĐļаваĐŊĐĩ", + "resume_paused_jobs": "ĐŸŅ€ĐžĐ´ŅŠĐģĐļи иСĐŋҊĐģĐŊĐĩĐŊиĐĩŅ‚Đž ĐŊа {count, plural, one {# ĐˇĐ°Đ´Đ°Ņ‡Đ°} other {# ĐˇĐ°Đ´Đ°Ņ‡Đ¸}}", "retry_upload": "ОĐŋĐ¸Ņ‚Đ°Đš ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž ĐžŅ‚ĐŊОвО", "review_duplicates": "Đ Đ°ĐˇĐŗĐģĐĩдаК Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸Ņ‚Đĩ", + "review_large_files": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŗĐžĐģĐĩĐŧи Ņ„Đ°ĐšĐģОвĐĩ", "role": "Đ ĐžĐģŅ", "role_editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "role_viewer": "Đ—Ņ€Đ¸Ņ‚ĐĩĐģ", + "running": "ИСĐŋҊĐģĐŊŅĐ˛Đ°ĐŊĐĩ", "save": "ЗаĐŋаСи", "save_to_gallery": "ЗаĐŋаСи в ĐŗĐ°ĐģĐĩŅ€Đ¸ŅŅ‚Đ°", "saved_api_key": "ЗаĐŋаСĐĩĐŊ API Key", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа аĐģĐąŅƒĐŧ ĐŊĐĩ ĐąĐĩ ҃ҁĐŋĐĩ҈ĐŊĐž", "selected": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐž", "selected_count": "{count, plural, other {# Đ¸ĐˇĐąŅ€Đ°ĐŊи}}", + "selected_gps_coordinates": "Đ˜ĐˇĐąŅ€Đ°ĐŊи GPS ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸", "send_message": "ИСĐŋŅ€Đ°Ņ‚ĐĩŅ‚Đĩ ŅŅŠĐžĐąŅ‰ĐĩĐŊиĐĩ", "send_welcome_email": "ИСĐŋŅ€Đ°Ņ‚ĐĩŅ‚Đĩ иĐŧĐĩĐšĐģ Са Đ´ĐžĐąŅ€Đĩ Đ´ĐžŅˆĐģи", "server_endpoint": "ĐĐ´Ņ€Đĩҁ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", @@ -1667,6 +1813,7 @@ "settings_saved": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ŅĐ° СаĐŋаСĐĩĐŊи", "setup_pin_code": "Задай PIN ĐēОд", "share": "ĐĄĐŋОдĐĩĐģŅĐŊĐĩ", + "share_action_prompt": "{count} ҁĐŋОдĐĩĐģĐĩĐŊи ОйĐĩĐēŅ‚Đ°", "share_add_photos": "Добави ҁĐŊиĐŧĐēи", "share_assets_selected": "{count} Đ¸ĐˇĐąŅ€Đ°ĐŊи", "share_dialog_preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "КоĐŋĐ¸Ņ€Đ°ĐŊĐž в ĐēĐģиĐŋĐąĐžŅ€Đ´Đ°", "shared_link_clipboard_text": "Đ’Ņ€ŅŠĐˇĐēа: {link}\nĐŸĐ°Ņ€ĐžĐģа: {password}", "shared_link_create_error": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", + "shared_link_custom_url_description": "Đ”ĐžŅŅ‚ŅŠĐŋĐĩŅ‚Đĩ ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ ĐģиĐŊĐē ҁ ĐŋĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ URL Đ°Đ´Ņ€Đĩҁ", "shared_link_edit_description_hint": "Đ’ŅŠĐ˛Đĩди ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊĐžŅ‚Đž", "shared_link_edit_expire_after_option_day": "1 Đ´ĐĩĐŊ", "shared_link_edit_expire_after_option_days": "{count} Đ´ĐŊи", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ‚Đĩ Đ˛Ņ€ŅŠĐˇĐēи", "shared_link_options": "ОĐŋŅ†Đ¸Đ¸ Са ҁĐŋОдĐĩĐģĐĩĐŊа Đ˛Ņ€ŅŠĐˇĐēа", + "shared_link_password_description": "Đ˜ĐˇĐ¸ŅĐēваĐŊĐĩ ĐŊа ĐŋĐ°Ņ€ĐžĐģа Са Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ ĐģиĐŊĐē", "shared_links": "ĐĄĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", "shared_links_description": "ĐĄĐŋОдĐĩĐģи ҁĐŊиĐŧĐēи и видĐĩа ҁ ĐģиĐŊĐē", "shared_photos_and_videos_count": "{assetCount, plural, other {# ҁĐŋОдĐĩĐģĐĩĐŊи ҁĐŊиĐŧĐēи и видĐĩа.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "ПоĐēаĐļи ĐŋŅ€ĐĩŅ…ĐžĐ´Đ° ĐŊа ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒŅ‚Đž", "show_supporter_badge": "ЗĐŊĐ°Ņ‡Đēа ĐŋĐžĐ´Đ´Ņ€ŅŠĐļĐŊиĐē", "show_supporter_badge_description": "ПоĐēаĐļи СĐŊĐ°Ņ‡Đēа ĐŋĐžĐ´Đ´Ņ€ŅŠĐļĐŊиĐē", + "show_text_search_menu": "ПоĐēаĐļи ĐŧĐĩĐŊŅŽŅ‚Đž Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа Ņ‚ĐĩĐēҁ҂", "shuffle": "Đ Đ°ĐˇĐąŅŠŅ€ĐēваĐŊĐĩ", "sidebar": "ĐĄŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊа ĐģĐĩĐŊŅ‚Đ°", "sidebar_display_description": "ПоĐēаСваĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēа ĐēҊĐŧ Đ¸ĐˇĐŗĐģĐĩда в ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊĐ°Ņ‚Đ° ĐģĐĩĐŊŅ‚Đ°", @@ -1762,12 +1912,14 @@ "sort_created": "Đ”Đ°Ņ‚Đ° ĐŊа ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ", "sort_items": "Đ‘Ņ€ĐžĐš ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "sort_modified": "Đ”Đ°Ņ‚Đ° ĐŊа ĐŋŅ€ĐžĐŧŅĐŊа", + "sort_newest": "Най-ĐŊОви ҁĐŊиĐŧĐēи", "sort_oldest": "Най-ŅŅ‚Đ°Ņ€Đ°Ņ‚Đ° ҁĐŊиĐŧĐēа", "sort_people_by_similarity": "ĐĄĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ…ĐžŅ€Đ° ĐŋĐž ĐŋŅ€Đ¸ĐģиĐēа", "sort_recent": "Най-ĐŊĐžĐ˛Đ°Ņ‚Đ° ҁĐŊиĐŧĐēа", "sort_title": "Đ—Đ°ĐŗĐģавиĐĩ", "source": "Код", "stack": "ĐĄŅŠĐąĐĩŅ€Đ¸", + "stack_action_prompt": "{count} ŅĐ° ĐŗŅ€ŅƒĐŋĐ¸Ņ€Đ°ĐŊи", "stack_duplicates": "ĐŸĐžĐ´Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", "stack_select_one_photo": "ИСйĐĩŅ€Đ¸ ĐĩĐ´ĐŊа ĐŗĐģавĐŊа ҁĐŊиĐŧĐēа Са ŅŅŠĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ ҁĐŊиĐŧĐēи", "stack_selected_photos": "ĐŸĐžĐ´Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа Đ¸ĐˇĐąŅ€Đ°ĐŊи ҁĐŊиĐŧĐēи", @@ -1775,6 +1927,7 @@ "stacktrace": "ĐĄĐģĐĩда ĐŊа ŅŅŠĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ", "start": "ĐĄŅ‚Đ°Ņ€Ņ‚", "start_date": "ĐĐ°Ņ‡Đ°ĐģĐŊа Đ´Đ°Ņ‚Đ°", + "start_date_before_end_date": "ĐĐ°Ņ‡Đ°ĐģĐŊĐ°Ņ‚Đ° Đ´Đ°Ņ‚Đ° Ņ‚Ņ€ŅĐąĐ˛Đ° да ĐąŅŠĐ´Đĩ ĐŋŅ€Đĩди ĐēŅ€Đ°ĐšĐŊĐ°Ņ‚Đ° Đ´Đ°Ņ‚Đ°", "state": "ĐŠĐ°Ņ‚", "status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ", "stop_casting": "ĐĄĐŋŅ€Đ¸ ĐŋŅ€ĐĩдаваĐŊĐĩŅ‚Đž", @@ -1787,6 +1940,7 @@ "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° ĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩŅ‚Đž", "storage_usage": "ИСĐŋĐžĐģСваĐŊи {used} ĐžŅ‚ {available}", "submit": "ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "success": "ĐŖŅĐŋĐĩ҈ĐŊĐž", "suggestions": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", "sunrise_on_the_beach": "Đ˜ĐˇĐŗŅ€Đĩв ĐŊа ĐŋĐģаĐļа", "support": "ĐŸĐžĐ´Đ´Ņ€ŅŠĐļĐēа", @@ -1796,6 +1950,10 @@ "sync": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ", "sync_albums": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа аĐģĐąŅƒĐŧи", "sync_albums_manual_subtitle": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°Đš Đ˛ŅĐ¸Ņ‡Đēи ĐˇĐ°Ņ€ĐĩĐ´ĐĩĐŊи видĐĩа и ҁĐŊиĐŧĐēи в Đ¸ĐˇĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛ĐŊи аĐģĐąŅƒĐŧи", + "sync_local": "ЛоĐēаĐģĐŊа ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ", + "sync_remote": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ҁҊҁ ŅŅŠŅ€Đ˛ŅŠŅ€Đ°", + "sync_status": "ĐĄŅŠŅŅ‚ĐžŅĐŊиĐĩ ĐŊа ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°", + "sync_status_subtitle": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ и ҃ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ° Са ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ", "sync_upload_album_setting_subtitle": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ и ĐˇĐ°Ņ€ĐĩĐļĐ´Đ°ĐšŅ‚Đĩ ҁĐŊиĐŧĐēи и видĐĩа в Đ¸ĐˇĐąŅ€Đ°ĐŊи аĐģĐąŅƒĐŧи в Immich", "tag": "ĐĸĐ°Đŗ", "tag_assets": "ĐĸĐ°ĐŗĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", @@ -1806,6 +1964,7 @@ "tag_updated": "АĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ ĐĩŅ‚Đ¸ĐēĐĩŅ‚: {tag}", "tagged_assets": "ĐĸĐ°ĐŗĐŊĐ°Ņ‚Đ¸ {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", "tags": "Đ•Ņ‚Đ¸ĐēĐĩŅ‚", + "tap_to_run_job": "ДоĐēĐžŅĐŊĐĩŅ‚Đĩ, Са да ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°Ņ‚Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ°Ņ‚Đ°", "template": "ШайĐģĐžĐŊ", "theme": "ĐĸĐĩĐŧа", "theme_selection": "Đ˜ĐˇĐąĐžŅ€ ĐŊа Ņ‚ĐĩĐŧа", @@ -1832,12 +1991,15 @@ "to_change_password": "ĐŸŅ€ĐžĐŧŅĐŊа ĐŊа ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°", "to_favorite": "Đ›ŅŽĐąĐ¸Đŧ", "to_login": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ", + "to_multi_select": "Са Đ¸ĐˇĐąĐžŅ€ ĐŊа ĐŊŅĐēĐžĐģĐēĐž", "to_parent": "ĐžŅ‚Đ¸Đ´Đ¸ ĐēҊĐŧ Ņ€ĐžĐ´Đ¸Ņ‚ĐĩĐģҁĐēĐ¸Ņ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", + "to_select": "Са Đ¸ĐˇĐąĐžŅ€", "to_trash": "ĐšĐžŅˆŅ‡Đĩ", "toggle_settings": "ĐŸŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ", "total": "ĐžĐąŅ‰Đž", "total_usage": "ĐžĐąŅ‰Đž иСĐŋĐžĐģСваĐŊĐž", "trash": "ĐšĐžŅˆŅ‡Đĩ", + "trash_action_prompt": "{count} ŅĐ° ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊи в ĐēĐžŅˆĐ°", "trash_all": "Đ˜ĐˇŅ…Đ˛ŅŠŅ€Đģи Đ˛ŅĐ¸Ņ‡Đēи", "trash_count": "В ĐšĐžŅˆŅ‡ĐĩŅ‚Đž {count, number}", "trash_delete_asset": "ВĐēĐ°Ņ€Đ°Đš в ĐšĐžŅˆŅ‡ĐĩŅ‚Đž/Đ˜ĐˇŅ‚Ņ€Đ¸Đš ĐĩĐģĐĩĐŧĐĩĐŊŅ‚", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "ИСйĐĩŅ€Đ¸ ОйĐĩĐēŅ‚Đ¸", "trash_page_title": "В ĐēĐžŅˆĐ° ({count})", "trashed_items_will_be_permanently_deleted_after": "Đ˜ĐˇŅ…Đ˛ŅŠŅ€ĐģĐĩĐŊĐ¸Ņ‚Đĩ в ĐēĐžŅˆŅ‡ĐĩŅ‚Đž ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đ¸ Са ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž ҁĐģĐĩĐ´ {days, plural, one {# Đ´ĐĩĐŊ} other {# Đ´ĐŊи}}.", + "troubleshoot": "ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐąĐģĐĩĐŧи", "type": "ĐĸиĐŋ", "unable_to_change_pin_code": "НĐĩĐ˛ŅŠĐˇĐŧĐžĐļĐŊа ĐŋŅ€ĐžĐŧŅĐŊа ĐŊа PIN ĐēОда", "unable_to_setup_pin_code": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž СадаваĐŊĐĩ ĐŊа PIN ĐēОда", "unarchive": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Đš", + "unarchive_action_prompt": "{count} ŅĐ° ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ ĐžŅ‚ ĐŅ€Ņ…Đ¸Đ˛Đ°", "unarchived_count": "{count, plural, other {НĐĩĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊи #}}", "undo": "ĐžŅ‚ĐŧĐĩĐŊи", "unfavorite": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐžŅ‚ ĐģŅŽĐąĐ¸ĐŧĐ¸Ņ‚Đĩ", + "unfavorite_action_prompt": "{count} ŅĐ° ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ¸ ĐžŅ‚ Đ›ŅŽĐąĐ¸Đŧи", "unhide_person": "ПоĐēаĐļи ĐžŅ‚ĐŊОвО Ņ‡ĐžĐ˛ĐĩĐēа", "unknown": "НĐĩиСвĐĩҁ҂ĐŊĐž", "unknown_country": "НĐĩĐŋОСĐŊĐ°Ņ‚Đ° Đ”ŅŠŅ€Đļава", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "ĐžŅ‚ ĐŧĐ°Ņ€ĐēĐ¸Ņ€Đ°Đš Đ˛ŅĐ¸Ņ‡Đēи Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", "unselect_all_in": "ĐŸŅ€ĐĩĐŧĐ°Ņ…ĐŊи Đ¸ĐˇĐąĐžŅ€Đ° ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ĐžŅ‚ ĐŗŅ€ŅƒĐŋĐ°Ņ‚Đ° {group}", "unstack": "РаСĐēĐ°Ņ‡Đ¸", + "unstack_action_prompt": "{count} ŅĐ° Ņ€Đ°ĐˇĐŗŅ€ŅƒĐŋĐ¸Ņ€Đ°ĐŊи", "unstacked_assets_count": "РаСĐēĐ°Ņ‡ĐĩĐŊи {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", + "untagged": "НĐĩĐŧĐ°Ņ€ĐēĐ¸Ņ€Đ°ĐŊи", "up_next": "ĐĄĐģĐĩĐ´Đ˛Đ°Ņ‰", + "update_location_action_prompt": "ОбĐŊОви ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸Ņ‚Đĩ ĐŊа {count} Đ¸ĐˇĐąŅ€Đ°ĐŊи ОйĐĩĐēŅ‚Đ° ҁ:", "updated_at": "ОбĐŊОвĐĩĐŊĐž", "updated_password": "ĐŸĐ°Ņ€ĐžĐģĐ°Ņ‚Đ° Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа", "upload": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", + "upload_action_prompt": "{count} ĐŊа ĐžĐŋĐ°ŅˆĐēа Са ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩ", "upload_concurrency": "ĐŖŅĐŋĐžŅ€ĐĩĐ´ĐŊи ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐ¸Ņ", + "upload_details": "ДĐĩŅ‚Đ°ĐšĐģи Са ĐēĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž", "upload_dialog_info": "Đ˜ŅĐēĐ°Ņ‚Đĩ Đģи да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа ŅŅŠŅ€Đ˛ŅŠŅ€Đ° Đ¸ĐˇĐąŅ€Đ°ĐŊĐ¸Ņ‚Đĩ ОйĐĩĐēŅ‚Đ¸?", "upload_dialog_title": "ĐšĐ°Ņ‡Đ¸ ОйĐĩĐēŅ‚", "upload_errors": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ĐˇĐ°Đ˛ŅŠŅˆĐĩĐŊĐž ҁ {count, plural, one {# ĐŗŅ€Đĩ҈Đēа} other {# ĐŗŅ€Đĩ҈Đēи}}, ОйĐŊОвĐĩŅ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ° Са да Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸.", + "upload_finished": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ¸", "upload_progress": "ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ {remaining, number} - ĐžĐąŅ€Đ°ĐąĐžŅ‚ĐĩĐŊи {processed, number}/{total, number}", "upload_skipped_duplicates": "ĐŸŅ€ĐĩҁĐēĐžŅ‡ĐĩĐŊи {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# Đ´ŅƒĐąĐģĐ¸Ņ€Đ°ĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}}", "upload_status_duplicates": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸", @@ -1892,6 +2063,7 @@ "upload_success": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ҃ҁĐŋĐĩ҈ĐŊĐž, ĐžĐŋŅ€ĐĩҁĐŊĐĩŅ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ°, Са да Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Đ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģОвĐĩ.", "upload_to_immich": "КазваĐŊĐĩ в Immich ({count})", "uploading": "ĐšĐ°Ņ‡Đ˛Đ°ĐŧĐĩ", + "uploading_media": "ĐšĐ°Ņ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐŧĐĩдиКĐŊи Ņ„Đ°ĐšĐģОвĐĩ", "url": "URL", "usage": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊиĐĩ", "use_biometric": "ИСĐŋĐžĐģСваК йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸ĐēĐ°Ņ‚Đ° Са иСĐŋĐžĐģСваĐŊĐĩŅ‚Đž ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ°", "username": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēĐž иĐŧĐĩ", "users": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", + "users_added_to_album_count": "{count, plural, one {ДобавĐĩĐŊ Đĩ # ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģ} other {ДобавĐĩĐŊи ŅĐ° # ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ}} ĐŊа аĐģĐąŅƒĐŧа", "utilities": "ИĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚Đ¸", "validate": "ВаĐģĐ¸Đ´Đ¸Ņ€Đ°ĐŊĐĩ", "validate_endpoint_error": "МоĐģŅ, Đ˛ŅŠĐ˛Đĩди ĐŋŅ€Đ°Đ˛Đ¸ĐģĐĩĐŊ URL", @@ -1930,6 +2103,7 @@ "view_album": "Đ Đ°ĐˇĐŗĐģĐĩдаК аĐģĐąŅƒĐŧа", "view_all": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи", "view_all_users": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа Đ˛ŅĐ¸Ņ‡Đēи ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи", + "view_details": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ Са Đ¸ĐˇĐŗĐģĐĩда", "view_in_timeline": "ПоĐēаĐļи Đ˛ŅŠĐ˛ Đ˛Ņ€ĐĩĐŧĐĩва ĐģиĐŊĐ¸Ņ", "view_link": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ°", "view_links": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа Đ˛Ņ€ŅŠĐˇĐēĐ¸Ņ‚Đĩ", @@ -1937,6 +2111,7 @@ "view_next_asset": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ¸Ņ Ņ„Đ°ĐšĐģ", "view_previous_asset": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐ¸Ņ Ņ„Đ°ĐšĐģ", "view_qr_code": "ВиĐļ QR ĐēОда", + "view_similar_photos": "ВиĐļ ĐŋОдОйĐŊи ҁĐŊиĐŧĐēи", "view_stack": "ПоĐēаĐļи в ҁ҂ĐĩĐē", "view_user": "ВиĐļ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģŅ", "viewer_remove_from_stack": "ĐŸŅ€ĐĩĐŧĐ°Ņ…Đ˛Đ°ĐŊĐĩ ĐžŅ‚ ĐžĐŋĐ°ŅˆĐēĐ°Ņ‚Đ°", @@ -1955,5 +2130,6 @@ "yes": "Да", "you_dont_have_any_shared_links": "ĐŅĐŧĐ°Ņ‚Đĩ ҁĐŋОдĐĩĐģĐĩĐŊи Đ˛Ņ€ŅŠĐˇĐēи", "your_wifi_name": "Đ’Đ°ŅˆĐ°Ņ‚Đ° Wi-Fi ĐŧŅ€ĐĩĐļа", - "zoom_image": "ĐŖĐ˛ĐĩĐģĐ¸Ņ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž" + "zoom_image": "ĐŖĐ˛ĐĩĐģĐ¸Ņ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩŅ‚Đž", + "zoom_to_bounds": "ĐŸŅ€Đ¸ĐąĐģиĐļи Đ´Đž ŅŅŠĐąĐ¸Ņ€Đ°ĐŊĐĩ в ĐŗŅ€Đ°ĐŊĐ¸Ņ†Đ¸Ņ‚Đĩ" } diff --git a/i18n/bi.json b/i18n/bi.json index fff8196e75..58c84f95d9 100644 --- a/i18n/bi.json +++ b/i18n/bi.json @@ -14,5 +14,10 @@ "add_exclusion_pattern": "Putem wan paten wae hemi karem aot", "add_import_path": "Putem wan pat blo import", "add_location": "Putem wan place blo hem", - "add_more_users": "Putem mor man" + "add_more_users": "Putem mor man", + "readonly_mode_enabled": "Mod blo yu no save janjem i on", + "reassigned_assets_to_new_person": "Janjem{count, plural, one {# asset} other {# assets}} blo nu man", + "reassing_hint": "janjem ol sumtin yu bin joos i go blo wan man", + "recent-albums": "album i no old tu mas", + "recent_searches": "lukabout wea i no old tu mas" } diff --git a/i18n/bn.json b/i18n/bn.json index ed108652b7..004b584d3c 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -8,12 +8,124 @@ "actions": "āĻ•āĻ°ā§āĻŽ", "active": "āϏāϚāϞ", "activity": "āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ", + "activity_changed": "āĻāĻ•āϟāĻŋāĻ­āĻŋāϟāĻŋ āĻāĻ–āύ {enabled, select, true {āϚāĻžāϞ⧁} other {āĻŦāĻ¨ā§āϧ}} āφāϛ⧇", "add": "āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_description": "āĻāĻ•āϟāĻŋ āĻŦāĻŋāĻŦāϰāĻŖ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_location": "āĻāĻ•āϟāĻŋ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_name": "āĻāĻ•āϟāĻŋ āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_a_title": "āĻāĻ•āϟāĻŋ āĻļāĻŋāϰ⧋āύāĻžāĻŽ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_birthday": "āĻāĻ•āϟāĻŋ āϜāĻ¨ā§āĻŽāĻĻāĻŋāύ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_endpoint": "āĻāĻ¨ā§āĻĄāĻĒāϝāĻŧ⧇āĻ¨ā§āϟ āϝ⧋āĻ— āĻ•āϰ⧁āύ", "add_exclusion_pattern": "āĻŦāĻšāĻŋāĻ°ā§āĻ­ā§‚āϤāĻ•āϰāĻŖ āύāĻŽā§āύāĻž", - "add_url": "āϞāĻŋāĻ™ā§āĻ• āϝ⧋āĻ— āĻ•āϰ⧁āύ" + "add_import_path": "āχāĻŽāĻĒā§‹āĻ°ā§āϟ āĻ•āϰāĻžāϰ āĻĒāĻžāĻĨ āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_location": "āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_more_users": "āφāϰ⧋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_partner": "āĻ…āĻ‚āĻļā§€āĻĻāĻžāϰ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_path": "āĻĒāĻžāĻĨ āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_photos": "āĻ›āĻŦāĻŋ āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_tag": "āĻŸā§āϝāĻžāĻ— āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ", + "add_to": "āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύâ€Ļ", + "add_to_album": "āĻāϞāĻŦāĻžāĻŽ āĻ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_to_album_bottom_sheet_added": "{album} āĻ āϝ⧋āĻ— āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇", + "add_to_album_bottom_sheet_already_exists": "{album} āĻ āφāϗ⧇ āĻĨ⧇āϕ⧇āχ āφāϛ⧇", + "add_to_album_toggle": "{album} - āĻāϰ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ", + "add_to_albums": "āĻ…ā§āϝāĻžāϞāĻŦāĻžāĻŽā§‡ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_to_albums_count": "āĻ…ā§āϝāĻžāϞāĻŦāĻžāĻŽā§‡ āϝ⧋āĻ— āĻ•āϰ⧁āύ ({count})", + "add_to_shared_album": "āĻļ⧇āϝāĻŧāĻžāϰ āĻ•āϰāĻž āĻ…ā§āϝāĻžāϞāĻŦāĻžāĻŽā§‡ āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "add_url": "āϞāĻŋāĻ™ā§āĻ• āϝ⧋āĻ— āĻ•āϰ⧁āύ", + "added_to_archive": "āφāĻ°ā§āĻ•āĻžāχāĻ­ āĻ āϝ⧋āĻ— āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇", + "added_to_favorites": "āĻĢ⧇āĻ­āĻžāϰāĻŋāĻŸā§‡ āϝ⧋āĻ— āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇", + "added_to_favorites_count": "āĻĒāĻ›āĻ¨ā§āĻĻ⧇āϰ āϤāĻžāϞāĻŋāĻ•āĻžā§Ÿ {count, number} āϝ⧋āĻ— āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇", + "admin": { + "add_exclusion_pattern_description": "āĻāĻ•ā§āϏāĻ•ā§āϞ⧁āĻļāύ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āϝ⧋āĻ— āĻ•āϰ⧁āύāĨ¤ *, **, āĻāĻŦāĻ‚ ? āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻ—ā§āϞ⧋āĻŦāĻŋāĻ‚ āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦāĨ¤ \"Raw\" āύāĻžāĻŽā§‡āϰ āϝ⧇āϕ⧋āύ⧋ āĻĄāĻŋāϰ⧇āĻ•ā§āϟāϰāĻŋāϤ⧇ āĻĨāĻžāĻ•āĻž āϏāĻŽāĻ¸ā§āϤ āĻĢāĻžāχāϞ āĻŦāĻžāĻĻ āĻĻāĻŋāϤ⧇ \"**/Raw/**\" āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤ \".tif\" āĻĻāĻŋāϝāĻŧ⧇ āĻļ⧇āώ āĻšāĻ“āϝāĻŧāĻž āϏāĻŽāĻ¸ā§āϤ āĻĢāĻžāχāϞ āĻŦāĻžāĻĻ āĻĻāĻŋāϤ⧇ \"**/*.tif\" āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤ āĻāĻ•āϟāĻŋ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻžāĻĨ āĻŦāĻžāĻĻ āĻĻāĻŋāϤ⧇, \"/path/to/ignore/**\" āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤", + "admin_user": "āĻāĻĄāĻŽāĻŋāύ āχāωāϜāĻžāϰ", + "asset_offline_description": "āĻāχ āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āϏāĻŽā§āĻĒāĻĻāϟāĻŋ āφāϰ āĻĄāĻŋāĻ¸ā§āϕ⧇ āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāĻšā§āϛ⧇ āύāĻž āĻāĻŦāĻ‚ āĻŸā§āĻ°ā§āϝāĻžāĻļ⧇ āϏāϰāĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇āĨ¤ āϝāĻĻāĻŋ āĻĢāĻžāχāϞāϟāĻŋ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āϏāϰāĻžāύ⧋ āĻšāϝāĻŧ⧇ āĻĨāĻžāϕ⧇, āϤāĻžāĻšāϞ⧇ āύāϤ⧁āύ āϏāĻ‚āĻļā§āϞāĻŋāĻˇā§āϟ āϏāĻŽā§āĻĒāĻĻ⧇āϰ āϜāĻ¨ā§āϝ āφāĻĒāύāĻžāϰ āϟāĻžāχāĻŽāϞāĻžāχāύ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰ⧁āύāĨ¤ āĻāχ āϏāĻŽā§āĻĒāĻĻāϟāĻŋ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰāϤ⧇, āĻĻāϝāĻŧāĻž āĻ•āϰ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧁āύ āϝ⧇ āύ⧀āĻšā§‡āϰ āĻĢāĻžāχāϞ āĻĒāĻžāĻĨāϟāĻŋ Immich āĻĻā§āĻŦāĻžāϰāĻž āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇ āĻāĻŦāĻ‚ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋāϟāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰ⧁āύāĨ¤", + "authentication_settings": "āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāĻŖ āϏ⧇āϟāĻŋāĻ‚āϏ", + "authentication_settings_description": "āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ, OAuth āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāĻŖ āϏ⧇āϟāĻŋāĻ‚āϏ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύ", + "authentication_settings_disable_all": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ āϏāĻŽāĻ¸ā§āϤ āϞāĻ—āχāύ āĻĒāĻĻā§āϧāϤāĻŋ āĻ…āĻ•ā§āώāĻŽ āĻ•āϰāϤ⧇ āϚāĻžāύ? āϞāĻ—āχāύ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖāϰ⧂āĻĒ⧇ āĻ…āĻ•ā§āώāĻŽ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤", + "authentication_settings_reenable": "āĻĒ⧁āύāϰāĻžāϝāĻŧ āϏāĻ•ā§āώāĻŽ āĻ•āϰāϤ⧇, āĻāĻ•āϟāĻŋ āϏāĻžāĻ°ā§āĻ­āĻžāϰ āĻ•āĻŽāĻžāĻ¨ā§āĻĄ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧁āύāĨ¤", + "background_task_job": "āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄ āϟāĻžāĻ¸ā§āĻ•", + "backup_database": "āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻĄāĻžāĻŽā§āĻĒ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ", + "backup_database_enable_description": "āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻĄāĻžāĻŽā§āĻĒ āϏāĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰ⧁āύ", + "backup_keep_last_amount": "āφāϗ⧇āϰ āĻĄāĻžāĻŽā§āĻĒ⧇āϰ āĻĒāϰāĻŋāĻŽāĻžāĻŖ āϰāĻžāĻ–āĻž āĻšāĻŦ⧇", + "backup_onboarding_1_description": "āĻ…āĻĢāϏāĻžāχāϟ āĻ•āĻĒāĻŋ āĻ•ā§āϞāĻžāωāĻĄā§‡ āĻ…āĻĨāĻŦāĻž āĻ…āĻ¨ā§āϝ āϕ⧋āύāĻ“ āϭ⧌āϤ āĻ¸ā§āĻĨāĻžāύ⧇āĨ¤", + "backup_onboarding_2_description": "āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āĻĄāĻŋāĻ­āĻžāχāϏ⧇ āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧ āĻ•āĻĒāĻŋāĨ¤ āĻāϰ āĻŽāĻ§ā§āϝ⧇ āϰāϝāĻŧ⧇āϛ⧇ āĻĒā§āϰāϧāĻžāύ āĻĢāĻžāχāϞ āĻāĻŦāĻ‚ āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧāĻ­āĻžāĻŦ⧇ āϏ⧇āχ āĻĢāĻžāχāϞāϗ⧁āϞāĻŋāϰ āĻŦā§āϝāĻžāĻ•āφāĻĒāĨ¤", + "backup_onboarding_3_description": "āĻŽā§‚āϞ āĻĢāĻžāχāϞ āϏāĻš āφāĻĒāύāĻžāϰ āĻĄā§‡āϟāĻžāϰ āĻŽā§‹āϟ āĻ•āĻĒāĻŋāĨ¤ āĻāϰ āĻŽāĻ§ā§āϝ⧇ āϰāϝāĻŧ⧇āϛ⧇ ā§§āϟāĻŋ āĻ…āĻĢāϏāĻžāχāϟ āĻ•āĻĒāĻŋ āĻāĻŦāĻ‚ ⧍āϟāĻŋ āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧ āĻ•āĻĒāĻŋāĨ¤", + "backup_onboarding_description": "āφāĻĒāύāĻžāϰ āĻĄā§‡āϟāĻž āϏ⧁āϰāĻ•ā§āώāĻŋāϤ āϰāĻžāĻ–āĻžāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ 3-2-1 āĻŦā§āϝāĻžāĻ•āφāĻĒ āĻ•ā§ŒāĻļāϞ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āĻāĻ•āϟāĻŋ āĻŦāĻŋāĻ¸ā§āϤ⧃āϤ āĻŦā§āϝāĻžāĻ•āφāĻĒ āϏāĻŽāĻžāϧāĻžāύ⧇āϰ āϜāĻ¨ā§āϝ āφāĻĒāύāĻžāϰ āφāĻĒāϞ⧋āĻĄ āĻ•āϰāĻž āĻĢāĻŸā§‹/āĻ­āĻŋāĻĄāĻŋāĻ“āϗ⧁āϞāĻŋāϰ āĻ•āĻĒāĻŋ āĻāĻŦāĻ‚ Immich āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āϰāĻžāĻ–āĻž āωāϚāĻŋāϤāĨ¤", + "backup_onboarding_footer": "Immich āĻāϰ āĻŦā§āϝāĻžāĻ•āφāĻĒ āύ⧇āĻ“ā§ŸāĻžāϰ āĻŦāĻŋāĻˇā§Ÿā§‡ āφāϰāĻ“ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ, āĻ…āύ⧁āĻ—ā§āϰāĻš āĻ•āϰ⧇ āĻĄāϕ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻļāύ āĻĻ⧇āϖ⧁āύāĨ¤", + "backup_onboarding_parts_title": "ā§Š-⧍-ā§§ āĻŦā§āϝāĻžāĻ•āφāĻĒ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϰāϝāĻŧ⧇āϛ⧇:", + "backup_onboarding_title": "āĻŦā§āϝāĻžāĻ•āφāĻĒ", + "backup_settings": "āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻĄāĻžāĻŽā§āĻĒ āϏ⧇āϟāĻŋāĻ‚āϏ", + "backup_settings_description": "āĻĄāĻžāϟāĻžāĻŦ⧇āϏ āĻĄāĻžāĻŽā§āĻĒ āϏ⧇āϟāĻŋāĻ‚āϏ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύāĨ¤", + "cleared_jobs": "{job} āĻāϰ āϜāĻ¨ā§āϝ jobs āĻ–āĻžāϞāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇", + "config_set_by_file": "āĻ•āύāĻĢāĻŋāĻ— āĻŦāĻ°ā§āϤāĻŽāĻžāύ⧇ āĻāĻ•āϟāĻŋ āĻ•āύāĻĢāĻŋāĻ— āĻĢāĻžāχāϞ āĻĻā§āĻŦāĻžāϰāĻž āϏ⧇āϟ āĻ•āϰāĻž āφāϛ⧇", + "confirm_delete_library": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ {library} āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻŽā§āϛ⧇ āĻĢ⧇āϞāϤ⧇ āϚāĻžāύ?", + "confirm_delete_library_assets": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ āĻāχ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋāϟāĻŋ āĻŽā§āϛ⧇ āĻĢ⧇āϞāϤ⧇ āϚāĻžāύ? āĻāϟāĻŋ Immich āĻĨ⧇āϕ⧇ {count, plural, one {# contained asset} other {all # contained asset}} āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻŦ⧇ āĻāĻŦāĻ‚ āĻĒā§‚āĻ°ā§āĻŦāĻžāĻŦāĻ¸ā§āĻĨāĻžāϝāĻŧ āĻĢ⧇āϰāĻžāύ⧋ āϝāĻžāĻŦ⧇ āύāĻžāĨ¤ āĻĢāĻžāχāϞāϗ⧁āϞāĻŋ āĻĄāĻŋāĻ¸ā§āϕ⧇ āĻĨāĻžāĻ•āĻŦ⧇āĨ¤", + "confirm_email_below": "āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰāϤ⧇, āύāĻŋāĻšā§‡ \"{email}\" āϟāĻžāχāĻĒ āĻ•āϰ⧁āύ", + "confirm_reprocess_all_faces": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ āϏāĻŽāĻ¸ā§āϤ āĻŽā§āĻ– āĻĒ⧁āύāϰāĻžāϝāĻŧ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻ•āϰāϤ⧇ āϚāĻžāύ? āĻāϟāĻŋ āύāĻžāĻŽāϝ⧁āĻ•ā§āϤ āĻŦā§āϝāĻ•ā§āϤāĻŋāĻĻ⧇āϰāĻ“ āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻŦ⧇āĨ¤", + "confirm_user_password_reset": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ {user} āĻāϰ āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āϰāĻŋāϏ⧇āϟ āĻ•āϰāϤ⧇ āϚāĻžāύ?", + "confirm_user_pin_code_reset": "āφāĻĒāύāĻŋ āĻ•āĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āϝ⧇ āφāĻĒāύāĻŋ {user} āĻāϰ āĻĒāĻŋāύ āϕ⧋āĻĄ āϰāĻŋāϏ⧇āϟ āĻ•āϰāϤ⧇ āϚāĻžāύ?", + "create_job": "job āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ", + "cron_expression": "āĻ•ā§āϰ⧋āύ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ", + "cron_expression_description": "āĻ•ā§āϰ⧋āύ āĻĢāĻ°ā§āĻŽā§āϝāĻžāϟ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻ¸ā§āĻ•ā§āϝāĻžāύāĻŋāĻ‚ āĻŦā§āϝāĻŦāϧāĻžāύ āϏ⧇āϟ āĻ•āϰ⧁āύāĨ¤ āφāϰāĻ“ āϤāĻĨā§āϝ⧇āϰ āϜāĻ¨ā§āϝ āĻĻāϝāĻŧāĻž āĻ•āϰ⧇ āĻĻ⧇āϖ⧁āύ āϝ⧇āĻŽāύ Crontab Guru", + "cron_expression_presets": "āĻ•ā§āϰ⧋āύ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻĒā§āϰāĻŋāϏ⧇āϟ", + "disable_login": "āϞāĻ—āχāύ āĻ…āĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ", + "duplicate_detection_job_description": "āĻ…āύ⧁āϰ⧂āĻĒ āĻ›āĻŦāĻŋ āϏāύāĻžāĻ•ā§āϤ āĻ•āϰāϤ⧇ āϏāĻŽā§āĻĒāĻĻāϗ⧁āϞāĻŋāϤ⧇ āĻŽā§‡āĻļāĻŋāύ āϞāĻžāĻ°ā§āύāĻŋāĻ‚ āϚāĻžāϞāĻžāύāĨ¤ āĻ¸ā§āĻŽāĻžāĻ°ā§āϟ āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ⧇āϰ āωāĻĒāϰ āύāĻŋāĻ°ā§āĻ­āϰ āĻ•āϰ⧇", + "exclusion_pattern_description": "āĻāĻ•ā§āϏāĻ•ā§āϞ⧁āĻļāύ āĻĒā§āϝāĻžāϟāĻžāĻ°ā§āύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āφāĻĒāύāĻŋ āφāĻĒāύāĻžāϰ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻĢāĻžāχāϞ āĻāĻŦāĻ‚ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰāϗ⧁āϞāĻŋāϕ⧇ āωāĻĒ⧇āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύāĨ¤ āϝāĻĻāĻŋ āφāĻĒāύāĻžāϰ āĻāĻŽāύ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ āĻĨāĻžāϕ⧇ āϝ⧇āĻ–āĻžāύ⧇ āĻāĻŽāύ āĻĢāĻžāχāϞ āĻĨāĻžāϕ⧇ āϝāĻž āφāĻĒāύāĻŋ āφāĻŽāĻĻāĻžāύāĻŋ āĻ•āϰāϤ⧇ āϚāĻžāύ āύāĻž, āϝ⧇āĻŽāύ RAW āĻĢāĻžāχāϞāĨ¤", + "external_library_management": "āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āĻ—ā§āϰāĻ¨ā§āĻĨāĻžāĻ—āĻžāϰ āĻŦā§āϝāĻŦāĻ¸ā§āĻĨāĻžāĻĒāύāĻž", + "face_detection": "āĻŽā§āĻ– āϏāύāĻžāĻ•ā§āϤāĻ•āϰāĻŖ", + "face_detection_description": "āĻŽā§‡āĻļāĻŋāύ āϞāĻžāĻ°ā§āύāĻŋāĻ‚ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻ…ā§āϝāĻžāϏ⧇āĻŸā§‡ āĻĨāĻžāĻ•āĻž āĻŽā§āĻ–/āĻšā§‡āĻšāĻžāϰāĻž āϗ⧁āϞāĻŋ āϏāύāĻžāĻ•ā§āϤ āĻ•āϰ⧁āύāĨ¤ āĻ­āĻŋāĻĄāĻŋāĻ“ āϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ, āĻļ⧁āϧ⧁āĻŽāĻžāĻ¤ā§āϰ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ āĻŦāĻŋāĻŦ⧇āϚāύāĻž āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ \"āϰāĻŋāĻĢā§āϰ⧇āĻļ\" (āĻĒ⧁āύāϰāĻžāϝāĻŧ) āϏāĻŽāĻ¸ā§āϤ āĻ…ā§āϝāĻžāϏ⧇āϟ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻ•āϰ⧇āĨ¤ \"āϰāĻŋāϏ⧇āϟ\" āĻ•āϰāĻžāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤāĻ­āĻžāĻŦ⧇ āϏāĻŽāĻ¸ā§āϤ āĻŦāĻ°ā§āϤāĻŽāĻžāύ āĻŽā§āϖ⧇āϰ āĻĄā§‡āϟāĻž āϏāĻžāĻĢ āĻ•āϰ⧇āĨ¤ \"āĻ…āύ⧁āĻĒāĻ¸ā§āĻĨāĻŋāϤ\" āĻ…ā§āϝāĻžāϏ⧇āϟāϗ⧁āϞāĻŋāϕ⧇ āϏāĻžāϰāĻŋāĻŦāĻĻā§āϧ āĻ•āϰ⧇ āϝāĻž āĻāĻ–āύāĻ“ āĻĒā§āϰāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻ•āϰāĻž āĻšāϝāĻŧāύāĻŋāĨ¤ āϏāύāĻžāĻ•ā§āϤ āĻ•āϰāĻž āĻŽā§āĻ–āϗ⧁āϞāĻŋāϕ⧇ āĻĢ⧇āϏāĻŋāϝāĻŧāĻžāϞ āϰāĻŋāĻ•āĻ—āύāĻŋāĻļāύ⧇āϰ āϜāĻ¨ā§āϝ āϏāĻžāϰāĻŋāĻŦāĻĻā§āϧ āĻ•āϰāĻž āĻšāĻŦ⧇, āĻĢ⧇āϏāĻŋāϝāĻŧāĻžāϞ āĻĄāĻŋāĻŸā§‡āĻ•āĻļāύ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ“āϝāĻŧāĻžāϰ āĻĒāϰ⧇, āĻŦāĻŋāĻĻā§āϝāĻŽāĻžāύ āĻŦāĻž āύāϤ⧁āύ āĻŦā§āϝāĻ•ā§āϤāĻŋāĻĻ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻ—ā§‹āĻˇā§āĻ ā§€āĻŦāĻĻā§āϧ āĻ•āϰ⧇āĨ¤", + "facial_recognition_job_description": "āĻļāύāĻžāĻ•ā§āϤ āĻ•āϰāĻž āĻŽā§āĻ–āϗ⧁āϞāĻŋāϕ⧇ āĻŽāĻžāύ⧁āώ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āĻ—ā§‹āĻˇā§āĻ ā§€āϭ⧁āĻ•ā§āϤ/āĻ—ā§āϰ⧁āĻĒ āĻ•āϰ⧁āύāĨ¤ āĻŽā§āĻ– āϏāύāĻžāĻ•ā§āϤāĻ•āϰāĻŖ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ“āϝāĻŧāĻžāϰ āĻĒāϰ⧇ āĻāχ āϧāĻžāĻĒāϟāĻŋ āϚāϞ⧇āĨ¤ \"āϰāĻŋāϏ⧇āϟ\" (āĻĒ⧁āύāϰāĻžāϝāĻŧ) āϏāĻŽāĻ¸ā§āϤ āĻŽā§āĻ–āϕ⧇ āĻ•ā§āϞāĻžāĻ¸ā§āϟāĻžāϰ āĻ•āϰ⧇āĨ¤ \"āĻ…āύ⧁āĻĒāĻ¸ā§āĻĨāĻŋāϤ/āĻŽāĻŋāϏāĻŋāĻ‚\" āĻŽā§āĻ–āϗ⧁āϞāĻŋāϕ⧇ āϏāĻžāϰāĻŋāϤ⧇ āϰāĻžāϖ⧇ āϝ⧇āϗ⧁āϞ⧋ āϕ⧋āύāĻ“ āĻŦā§āϝāĻ•ā§āϤāĻŋāϕ⧇ āĻāϏāĻžāχāύ/āĻŦāϰāĻžāĻĻā§āĻĻ āĻ•āϰāĻž āĻšāϝāĻŧāύāĻŋāĨ¤", + "failed_job_command": "āĻ•āĻŽāĻžāĻ¨ā§āĻĄ {command} āĻ•āĻžāĻœā§‡āϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻ°ā§āĻĨ āĻšāϝāĻŧ⧇āϛ⧇: {job}", + "force_delete_user_warning": "āϏāϤāĻ°ā§āĻ•āϤāĻž: āĻāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āĻāĻŦāĻ‚ āϏāĻŽāĻ¸ā§āϤ āϏāĻŽā§āĻĒāĻĻ āĻ…āĻŦāĻŋāϞāĻŽā§āĻŦ⧇ āϏāϰāĻŋāϝāĻŧ⧇ āĻĢ⧇āϞāĻŦ⧇āĨ¤ āĻāϟāĻŋ āĻĒā§‚āĻ°ā§āĻŦāĻžāĻŦāĻ¸ā§āĻĨāĻžāϝāĻŧ āĻĢ⧇āϰāĻžāύ⧋ āϝāĻžāĻŦ⧇ āύāĻž āĻāĻŦāĻ‚ āĻĢāĻžāχāϞāϗ⧁āϞāĻŋ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰāĻž āϝāĻžāĻŦ⧇ āύāĻžāĨ¤", + "image_format": "āĻĢāϰāĻŽā§āϝāĻžāϟ", + "image_format_description": "WebP JPEG āĻāϰ āϤ⧁āϞāύāĻžā§Ÿ āϛ⧋āϟ āĻĢāĻžāχāϞ āϤ⧈āϰāĻŋ āĻ•āϰ⧇, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻāύāϕ⧋āĻĄ āĻ•āϰāϤ⧇ āϧ⧀āϰāĨ¤", + "image_fullsize_description": "āϜ⧁āĻŽ āχāύ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻ¸ā§āĻŸā§āϰāĻŋāĻĒāĻĄ āĻŽā§‡āϟāĻžāĻĄā§‡āϟāĻž āϏāĻš āĻĒā§‚āĻ°ā§āĻŖ āφāĻ•āĻžāϰ⧇āϰ āĻ›āĻŦāĻŋ", + "image_fullsize_enabled": "āĻĒā§‚āĻ°ā§āĻŖ-āφāĻ•āĻžāϰ⧇āϰ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ", + "image_fullsize_enabled_description": "āĻ“āϝāĻŧ⧇āĻŦ-āĻŦāĻžāĻ¨ā§āϧāĻŦ āύāϝāĻŧ āĻāĻŽāύ āĻĢāĻ°ā§āĻŽā§āϝāĻžāĻŸā§‡āϰ āϜāĻ¨ā§āϝ āĻĒā§‚āĻ°ā§āĻŖ-āφāĻ•āĻžāϰ⧇āϰ āĻ›āĻŦāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύāĨ¤ \"āĻāĻŽāĻŦ⧇āĻĄā§‡āĻĄ āĻĒā§āϰāĻŋāĻ­āĻŋāω āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰ⧁āύ\" āϏāĻ•ā§āώāĻŽ āĻ•āϰāĻž āĻĨāĻžāĻ•āϞ⧇, āϰ⧂āĻĒāĻžāĻ¨ā§āϤāϰ āĻ›āĻžāĻĄāĻŧāĻžāχ āĻāĻŽāĻŦ⧇āĻĄā§‡āĻĄ āĻĒā§āϰāĻŋāĻ­āĻŋāω āϏāϰāĻžāϏāϰāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ JPEG-āĻāϰ āĻŽāϤ⧋ āĻ“āϝāĻŧ⧇āĻŦ-āĻŦāĻžāĻ¨ā§āϧāĻŦ āĻĢāĻ°ā§āĻŽā§āϝāĻžāϟāϗ⧁āϞāĻŋāϕ⧇ āĻĒā§āϰāĻ­āĻžāĻŦāĻŋāϤ āĻ•āϰ⧇ āύāĻžāĨ¤", + "image_fullsize_quality_description": "āĻĒā§‚āĻ°ā§āĻŖ-āφāĻ•āĻžāϰ⧇āϰ āĻ›āĻŦāĻŋāϰ āĻŽāĻžāύ ā§§-ā§§ā§Ļā§ĻāĨ¤ āωāĻšā§āϚāϤāϰ āĻšāϞ⧇ āĻ­āĻžāϞ⧋, āĻ•āĻŋāĻ¨ā§āϤ⧁ āφāϰāĻ“ āĻŦāĻĄāĻŧ āĻĢāĻžāχāϞ āϤ⧈āϰāĻŋ āĻšāϝāĻŧāĨ¤", + "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 āĻ›āĻŦāĻŋāϗ⧁āϞ⧋ āϰāϙ⧇āϰ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻā§œāĻžāϤ⧇ sRGB āĻšāĻŋāϏ⧇āĻŦ⧇āχ āϰāĻžāĻ–āĻž āĻšāĻŦ⧇āĨ¤", + "image_preview_description": "āĻ¸ā§āĻŸā§āϰāĻŋāĻĒāĻĄ āĻŽā§‡āϟāĻžāĻĄā§‡āϟāĻž āϏāĻš āĻŽāĻžāĻāĻžāϰāĻŋ āφāĻ•āĻžāϰ⧇āϰ āĻ›āĻŦāĻŋ, āĻāĻ•āϟāĻŋ āĻāĻ•āĻ• āϏāĻŽā§āĻĒāĻĻ āĻĻ⧇āĻ–āĻžāϰ āϏāĻŽāϝāĻŧ āĻāĻŦāĻ‚ āĻŽā§‡āĻļāĻŋāύ āϞāĻžāĻ°ā§āύāĻŋāĻ‚āϝāĻŧ⧇āϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻšāϝāĻŧ", + "image_preview_quality_description": "ā§§-ā§§ā§Ļā§Ļ āĻāϰ āĻŽāĻ§ā§āϝ⧇ āĻĒā§āϰāĻŋāĻ­āĻŋāω āϕ⧋āϝāĻŧāĻžāϞāĻŋāϟāĻŋāĨ¤ āĻŦ⧇āĻļāĻŋ āĻšāϞ⧇ āĻ­āĻžāϞ⧋, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻŦāĻĄāĻŧ āĻĢāĻžāχāϞ āϤ⧈āϰāĻŋ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāĻļā§€āϞāϤāĻž āĻ•āĻŽāĻžāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻ•āĻŽ āĻŽāĻžāύ āϏ⧇āϟ āĻ•āϰāϞ⧇ āĻŽā§‡āĻļāĻŋāύ āϞāĻžāĻ°ā§āύāĻŋāĻ‚ āϕ⧋āϝāĻŧāĻžāϞāĻŋāϟāĻŋāϰ āωāĻĒāϰ āĻĒā§āϰāĻ­āĻžāĻŦ āĻĒāĻĄāĻŧāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤", + "image_preview_title": "āĻĒā§āϰāĻŋāĻ­āĻŋāω āϏ⧇āϟāĻŋāĻ‚āϏ", + "image_quality": "āϗ⧁āĻŖāĻŽāĻžāύ", + "image_resolution": "āϰ⧇āĻœā§‹āϞāĻŋāωāĻļāύ", + "image_resolution_description": "āωāĻšā§āϚ āϰ⧇āĻœā§‹āϞāĻŋāωāĻļāύ⧇āϰ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āφāϰāĻ“ āĻŦāĻŋāĻ¸ā§āϤāĻžāϰāĻŋāϤ āϤāĻĨā§āϝ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻāύāϕ⧋āĻĄ āĻ•āϰāϤ⧇ āĻŦ⧇āĻļāĻŋ āϏāĻŽāϝāĻŧ āϞāĻžāϗ⧇, āĻĢāĻžāχāϞ⧇āϰ āφāĻ•āĻžāϰ āĻŦāĻĄāĻŧ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāĻļā§€āϞāϤāĻž āĻ•āĻŽāĻžāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤", + "image_settings": "āϚāĻŋāĻ¤ā§āϰ āϏ⧇āϟāĻŋāĻ‚āϏ", + "image_settings_description": "āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻ›āĻŦāĻŋāϰ āĻŽāĻžāύ āĻāĻŦāĻ‚ āϰ⧇āĻœā§‹āϞāĻŋāωāĻļāύ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύ", + "image_thumbnail_description": "āĻŽā§‡āϟāĻžāĻĄā§‡āϟāĻž āĻŦāĻžāĻĻ āĻĻ⧇āĻ“ā§ŸāĻž āϛ⧋āϟ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ, āĻŽā§‚āϞ āϟāĻžāχāĻŽāϞāĻžāχāύ⧇āϰ āĻŽāϤ⧋ āĻ›āĻŦāĻŋāϰ āĻ—ā§āϰ⧁āĻĒ āĻĻ⧇āĻ–āĻžāϰ āϏāĻŽāϝāĻŧ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻšā§Ÿ", + "image_thumbnail_quality_description": "āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ⧇āϰ āĻŽāĻžāύ ā§§-ā§§ā§Ļā§ĻāĨ¤ āĻŦ⧇āĻļāĻŋ āĻšāϞ⧇ āĻ­āĻžāϞ⧋, āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻŦāĻĄāĻŧ āĻĢāĻžāχāϞ āϤ⧈āϰāĻŋ āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻžāĻļā§€āϞāϤāĻž āĻ•āĻŽāĻžāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤", + "image_thumbnail_title": "āĻĨāĻžāĻŽā§āĻŦāύ⧇āϞ āϏ⧇āϟāĻŋāĻ‚āϏ", + "job_concurrency": "{job} āĻ•āύāĻ•āĻžāϰ⧇āĻ¨ā§āϏāĻŋ", + "job_created": "Job āϤ⧈āϰāĻŋ āĻšāϝāĻŧ⧇āϛ⧇", + "job_not_concurrency_safe": "āĻāχ āĻ•āĻžāϜāϟāĻŋ āϏāĻŽāĻžāĻ¨ā§āϤāϰāĻžāϞāĻ­āĻžāĻŦ⧇ āϚāĻžāϞāĻžāύ⧋ āύāĻŋāϰāĻžāĻĒāĻĻ āύ⧟", + "job_settings": "āĻ•āĻžāĻœā§‡āϰ āϏ⧇āϟāĻŋāĻ‚āϏ", + "job_settings_description": "āĻ•āĻžāĻœā§‡āϰ āϏāĻŽāĻžāĻ¨ā§āϤāϰāĻžāϞāϤāĻž āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύ", + "job_status": "āϚāĻžāĻ•āϰāĻŋāϰ āĻ…āĻŦāĻ¸ā§āĻĨāĻž", + "jobs_delayed": "{jobCount, plural, other {# āĻŦāĻŋāϞāĻŽā§āĻŦāĻŋāϤ}}", + "jobs_failed": "{jobCount, plural, other {# āĻŦā§āϝāĻ°ā§āĻĨ}}", + "library_created": "āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āσ {library}", + "library_deleted": "āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻž āĻšāϝāĻŧ⧇āϛ⧇", + "library_import_path_description": "āχāĻŽā§āĻĒā§‹āĻ°ā§āϟ/āϝ⧋āĻ— āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āĻāĻ•āϟāĻŋ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ•āϰ⧁āύāĨ¤ āϏāĻžāĻŦāĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ āϏāĻš āĻāχ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰāϟāĻŋ āĻ›āĻŦāĻŋ āĻāĻŦāĻ‚ āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āϜāĻ¨ā§āϝ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤", + "library_scanning": "āĻĒāĻ°ā§āϝāĻžāϝāĻŧāĻ•ā§āϰāĻŽāĻŋāĻ• āĻ¸ā§āĻ•ā§āϝāĻžāύāĻŋāĻ‚", + "library_scanning_description": "āĻĒāĻ°ā§āϝāĻžāϝāĻŧāĻ•ā§āϰāĻŽāĻŋāĻ• āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύāĻŋāĻ‚ āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ āĻ•āϰ⧁āύ", + "library_scanning_enable_description": "āĻĒāĻ°ā§āϝāĻžāϝāĻŧāĻ•ā§āϰāĻŽāĻŋāĻ• āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύāĻŋāĻ‚ āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ", + "library_settings": "āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ", + "library_settings_description": "āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āϏ⧇āϟāĻŋāĻ‚āϏ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύ", + "library_tasks_description": "āύāϤ⧁āύ āĻāĻŦāĻ‚/āĻ…āĻĨāĻŦāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āϏāĻŽā§āĻĒāĻĻ⧇āϰ āϜāĻ¨ā§āϝ āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰ⧁āύ", + "library_watching_enable_description": "āĻĢāĻžāχāϞ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ⧇āϰ āϜāĻ¨ā§āϝ āĻŦāĻšāĻŋāϰāĻžāĻ—āϤ āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋāϗ⧁āϞāĻŋ āĻĻ⧇āϖ⧁āύ", + "library_watching_settings": "āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻĻ⧇āĻ–āĻž (āĻĒāϰ⧀āĻ•ā§āώāĻžāĻŽā§‚āϞāĻ•)", + "library_watching_settings_description": "āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻĢāĻžāχāϞāϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āύāϜāϰ āϰāĻžāϖ⧁āύ", + "logging_enable_description": "āϞāĻ—āĻŋāĻ‚ āĻāύāĻžāĻŦāϞ/āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ", + "logging_level_description": "āϏāĻ•ā§āϰāĻŋāϝāĻŧ āĻĨāĻžāĻ•āĻžāĻ•āĻžāϞ⧀āύ, āϕ⧋āύ āϞāĻ— āĻ¸ā§āϤāϰ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇āĨ¤", + "logging_settings": "āϞāĻ—āĻŋāĻ‚", + "machine_learning_clip_model": "CLIP āĻŽāĻĄā§‡āϞ", + "machine_learning_clip_model_description": "āĻāĻ–āĻžāύ⧇ āϤāĻžāϞāĻŋāĻ•āĻžāϭ⧁āĻ•ā§āϤ āĻāĻ•āϟāĻŋ CLIP āĻŽāĻĄā§‡āϞ⧇āϰ āύāĻžāĻŽāĨ¤ āĻŽāύ⧇ āϰāĻžāĻ–āĻŦ⧇āύ, āĻŽāĻĄā§‡āϞ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ⧇āϰ āĻĒāϰ āϏāĻŦ āĻ›āĻŦāĻŋāϰ āϜāĻ¨ā§āϝ āĻ…āĻŦāĻļā§āϝāχ ‘Smart Search’ āĻ•āĻžāϜāϟāĻŋ āφāĻŦāĻžāϰ āϚāĻžāϞāĻžāϤ⧇ āĻšāĻŦ⧇āĨ¤", + "machine_learning_duplicate_detection": "āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āϏāύāĻžāĻ•ā§āϤāĻ•āϰāĻŖ", + "machine_learning_duplicate_detection_enabled": "āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻļāύāĻžāĻ•ā§āϤāĻ•āϰāĻŖ āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ" + } } diff --git a/i18n/ca.json b/i18n/ca.json index 5c53311a02..5f3267f7ca 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -2,7 +2,7 @@ "about": "Quant a", "account": "Compte", "account_settings": "ConfiguraciÃŗ del compte", - "acknowledge": "D'acord", + "acknowledge": "Base de coneixement", "action": "AcciÃŗ", "action_common_update": "Actualitzar", "actions": "Accions", @@ -14,6 +14,7 @@ "add_a_location": "Afegiu una ubicaciÃŗ", "add_a_name": "Afegir un nom", "add_a_title": "Afegir un títol", + "add_birthday": "Afegeix la data de naixement", "add_endpoint": "afegir endpoint", "add_exclusion_pattern": "Afegir un patrÃŗ d'exclusiÃŗ", "add_import_path": "Afegir una ruta d'importaciÃŗ", @@ -27,9 +28,13 @@ "add_to_album": "Afegir a un l'àlbum", "add_to_album_bottom_sheet_added": "Afegit a {album}", "add_to_album_bottom_sheet_already_exists": "Ja està a {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns recursos locals no s'han pogut afegir a l'àlbum", + "add_to_album_toggle": "Commutar selecciÃŗ de {album}", + "add_to_albums": "Afegir als àlbums", + "add_to_albums_count": "Afegir als àlbums ({count})", "add_to_shared_album": "Afegir a un àlbum compartit", "add_url": "Afegir URL", - "added_to_archive": "Afegit als arxivats", + "added_to_archive": "Afegir a l'arxiu", "added_to_favorites": "Afegit als preferits", "added_to_favorites_count": "{count, number} afegits als preferits", "admin": { @@ -44,6 +49,13 @@ "backup_database": "Fer un bolcat de la base de dades", "backup_database_enable_description": "Habilitar bolcat de la base de dades", "backup_keep_last_amount": "Quantitat de bolcats anteriors per conservar", + "backup_onboarding_1_description": "cÃ˛pia externa al nÃēvol o en una altra ubicaciÃŗ física.", + "backup_onboarding_2_description": "cÃ˛pies locals en diferents dispositius. AixÃ˛ inclou els fitxers principals i una cÃ˛pia de seguretat d'aquests fitxers localment.", + "backup_onboarding_3_description": "cÃ˛pies totals de les vostres dades, inclosos els fitxers originals. AixÃ˛ inclou 1 cÃ˛pia externa i 2 cÃ˛pies locals.", + "backup_onboarding_description": "Es recomana una estratègia de cÃ˛pia de seguretat 3-2-1 per protegir les vostres dades. Hauríeu de conservar cÃ˛pies de les vostres fotos/vídeos penjats, així com de la base de dades Immich per obtenir una soluciÃŗ de cÃ˛pia de seguretat completa.", + "backup_onboarding_footer": "Per obtenir mÊs informaciÃŗ sobre com fer cÃ˛pies de seguretat d'Immich, consulteu la documentation.", + "backup_onboarding_parts_title": "Una cÃ˛pia de seguretat 3-2-1 inclou:", + "backup_onboarding_title": "CÃ˛pies de seguretat", "backup_settings": "ConfiguraciÃŗ dels bolcats", "backup_settings_description": "Gestionar la configuraciÃŗ de bolcats de la base de dades. Nota: els treballs no es monitoritzen ni es notifiquen els errors.", "cleared_jobs": "Tasques esborrades per a: {job}", @@ -73,10 +85,10 @@ "image_fullsize_enabled": "Activa la generaciÃŗ d'imatges a tamany complet", "image_fullsize_enabled_description": "Genera imatges a tamany complet per formats no compatibles amb la web. Quan \"Prefereix vista prèvia incrustada\" està activat, les vistes prèvies incrustades s'utilitzen directament sense conversiÃŗ. No afecta els formats compatibles amb la web com JPEG.", "image_fullsize_quality_description": "De 1 a 100, qualitat de l'imatge a tamany complet. Un valor mÊs alt Ês millor, perÃ˛ resulta en fitxers de major tamany.", - "image_fullsize_title": "ConfiguraciÃŗ d'imatges a tamany complet", + "image_fullsize_title": "ConfiguraciÃŗ de les imatges a tamany complet", "image_prefer_embedded_preview": "Prefereix vista prèvia incrustada", "image_prefer_embedded_preview_setting_description": "Empra vista prèvia incrustada en les fotografies RAW com a entrada per al processament d'imatge, quan sigui possible. Aquesta acciÃŗ pot produir colors mÊs acurats en algunes imatges, perÃ˛ la qualitat de la vista prèvia depèn de la càmera i la imatge pot tenir mÊs artefactes de compressiÃŗ.", - "image_prefer_wide_gamut": "Prefereix àmplia gamma", + "image_prefer_wide_gamut": "Prefereix la gamma àmplia", "image_prefer_wide_gamut_setting_description": "Uitlitza Display P3 per a les miniatures. AixÃ˛ preserva mÊs bÊ la vitalitat de les imatges amb espais de color àmplis, perÃ˛ les imatges es poden veure diferent en aparells antics amb una versiÃŗ antiga del navegador. Les imatges sRGB romandran com a sRGB per a evitar canvis de color.", "image_preview_description": "Imatge de mida mitjana amb metadades eliminades, que s'utilitza quan es visualitza un sol recurs i per a l'aprenentatge automàtic", "image_preview_quality_description": "Vista prèvia de la qualitat de l'1 al 100. MÊs alt Ês millor, perÃ˛ produeix fitxers mÊs grans i pot reduir la capacitat de resposta de l'aplicaciÃŗ. Establir un valor baix pot afectar la qualitat de l'aprenentatge automàtic.", @@ -84,11 +96,11 @@ "image_quality": "Qualitat", "image_resolution": "ResoluciÃŗ", "image_resolution_description": "Les resolucions mÊs altes poden conservar mÊs detalls perÃ˛ triguen mÊs a codificar-se, tenen mides de fitxer mÊs grans i poden reduir la capacitat de resposta de l'aplicaciÃŗ.", - "image_settings": "ConfiguraciÃŗ d'imatges", + "image_settings": "ConfiguraciÃŗ de les imatges", "image_settings_description": "Gestiona la qualitat i resoluciÃŗ de les imatges generades", "image_thumbnail_description": "Miniatura petita amb metadades eliminades, que s'utilitza quan es visualitzen grups de fotos com la línia de temps principal", "image_thumbnail_quality_description": "Qualitat de miniatura d'1 a 100. MÊs alt Ês millor, perÃ˛ produeix fitxers mÊs grans i pot reduir la capacitat de resposta de l'aplicaciÃŗ.", - "image_thumbnail_title": "ConfiguraciÃŗ de miniatures", + "image_thumbnail_title": "ConfiguraciÃŗ de les miniatures", "job_concurrency": "{job} simultàniament", "job_created": "Tasca creada", "job_not_concurrency_safe": "Aquesta tasca no Ês segura per a la conconcurrència.", @@ -112,6 +124,13 @@ "logging_enable_description": "Habilitar el registrament", "logging_level_description": "Quan està habilitat, quin nivell de registre es vol emprar.", "logging_settings": "Registre", + "machine_learning_availability_checks": "Comprovacions de disponibilitat", + "machine_learning_availability_checks_description": "Detectar i preferir automàticament els servidors d'aprenentatge automàtic disponibles", + "machine_learning_availability_checks_enabled": "Habilita les comprovacions de disponibilitat", + "machine_learning_availability_checks_interval": "Interval de comprovaciÃŗ", + "machine_learning_availability_checks_interval_description": "Interval en mil¡lisegons entre comprovacions de disponibilitat", + "machine_learning_availability_checks_timeout": "Temps d'espera de la sol¡licitud", + "machine_learning_availability_checks_timeout_description": "Temps d'espera en mil¡lisegons per a les comprovacions de disponibilitat", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "El nom d'un model CLIP que apareix a aquí. Tingues en compte que has de tornar a executar la cerca intel¡ligent per a totes les imatges quan es canvia de model.", "machine_learning_duplicate_detection": "DetecciÃŗ de duplicats", @@ -166,6 +185,20 @@ "metadata_settings_description": "Administrar la configuraciÃŗ de les metadades", "migration_job": "MigraciÃŗ", "migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes", + "nightly_tasks_cluster_faces_setting_description": "Executar el reconeixement facial en cares recentment detectades", + "nightly_tasks_cluster_new_faces_setting": "Agrupa cares noves", + "nightly_tasks_database_cleanup_setting": "Tasques de neteja de la base de dades", + "nightly_tasks_database_cleanup_setting_description": "Netegeu les dades antigues i caducades de la base de dades", + "nightly_tasks_generate_memories_setting": "Generar memÃ˛ries", + "nightly_tasks_generate_memories_setting_description": "Crear nous records a partir de les fotos penjades", + "nightly_tasks_missing_thumbnails_setting": "Generar les miniatures restants", + "nightly_tasks_missing_thumbnails_setting_description": "Posar en cua les fotos penjades sense miniatures per a la generaciÃŗ de la seva miniatura", + "nightly_tasks_settings": "ConfiguraciÃŗ de les tasques nocturnes", + "nightly_tasks_settings_description": "Gestionar les tasques nocturnes", + "nightly_tasks_start_time_setting": "Hora d'inici", + "nightly_tasks_start_time_setting_description": "Hora en què el servidor comença a executar les tasques nocturnes", + "nightly_tasks_sync_quota_usage_setting": "Sincronitzar l'Ãēs de la quota", + "nightly_tasks_sync_quota_usage_setting_description": "Actualitzar la quota d'emmagatzematge de l'usuari segons l'Ãēs actual", "no_paths_added": "No s'ha afegit cap ruta", "no_pattern_added": "Cap patrÃŗ aplicat", "note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI de redirecciÃŗ mÃ˛bil", "oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecciÃŗ mÃ˛bil", "oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mÃ˛bil, com ara ''{callback}''", + "oauth_role_claim": "ConcessiÃŗ de rol", + "oauth_role_claim_description": "Atorgar accÊs d'administrador automàticament segons la presència d'aquesta concessiÃŗ. La concessiÃŗ pot ser 'usuari' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestiona la configuraciÃŗ de l'inici de sessiÃŗ OAuth", "oauth_settings_more_details": "Per a mÊs detalls sobre aquesta funcionalitat, consulteu la documentaciÃŗ.", @@ -244,6 +279,7 @@ "storage_template_migration_info": "Les extensions es convertiran a minÃēscules. Els canvis de plantilla nomÊs s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la {job}.", "storage_template_migration_job": "Tasca de migraciÃŗ de la plantilla d'emmagatzematge", "storage_template_more_details": "Per obtenir mÊs detalls sobre aquesta funciÃŗ, consulteu la Storage Template i les seves implications", + "storage_template_onboarding_description_v2": "Un cop habilitada, aquesta funciÃŗ organitzarà automàticament els fitxers a partir d'una plantilla definida per l'usuari. Per a mÊs informaciÃŗ, podeu consultar la documentaciÃŗ.", "storage_template_path_length": "Límit aproximat de longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla d'emmagatzematge", "storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats", @@ -330,6 +366,9 @@ "trash_number_of_days_description": "Nombre de dies per mantenir els recursos a la paperera abans de suprimir-los permanentment", "trash_settings": "ConfiguraciÃŗ de la paperera", "trash_settings_description": "Gestiona la configuraciÃŗ de la paperera", + "unlink_all_oauth_accounts": "Desvincula tots els comptes d'OAuth", + "unlink_all_oauth_accounts_description": "Recorda desvincular tots els comptes d'OAuth abans de migrar a un proveïdor nou.", + "unlink_all_oauth_accounts_prompt": "Estàs segur que vols desvincular tots els comptes d'OAuth? AixÃ˛ restablirà l'identificador d'OAuth per a cada usuari i no es pot tornar enrere.", "user_cleanup_job": "Neteja d'usuari", "user_delete_delay": "El compte i els recursos de {user} es programaran per a la supressiÃŗ permanent en {delay, plural, one {# dia} other {# dies}}.", "user_delete_delay_settings": "Retard de la supressiÃŗ", @@ -359,10 +398,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opciÃŗ per filtrar els continguts multimèdia durant la sincronitzaciÃŗ segons criteris alternatius. NomÊs proveu-ho si teniu problemes amb l'aplicaciÃŗ per detectar tots els àlbums.", "advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronitzaciÃŗ d'àlbums de dispositius alternatius", "advanced_settings_log_level_title": "Nivell de registre: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositius sÃŗn molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositius sÃŗn molt lents en carregar miniatures dels elements locals. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", "advanced_settings_prefer_remote_title": "Prefereix imatges remotes", "advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol¡licitud de xarxa", "advanced_settings_proxy_headers_title": "Capçaleres de proxy", + "advanced_settings_readonly_mode_subtitle": "Habilita el nomÊs de lectura mode on les fotos poden ser nomÊs vist, a coses els agrada seleccionant imatges mÃēltiples, compartint, càsting, elimina Ês tot discapacitat. Habilita/Desactiva nomÊs de lectura via avatar d'usuari des de la pantalla major", + "advanced_settings_readonly_mode_title": "Mode de nomÊs lectura", "advanced_settings_self_signed_ssl_subtitle": "Omet la verificaciÃŗ del certificat SSL del servidor. Requerit per a certificats autosignats.", "advanced_settings_self_signed_ssl_title": "Permet certificats SSL autosignats", "advanced_settings_sync_remote_deletions_subtitle": "Suprimeix o restaura automàticament un actiu en aquest dispositiu quan es realitzi aquesta acciÃŗ al web", @@ -378,6 +419,7 @@ "album_cover_updated": "Portada de l'àlbum actualitzada", "album_delete_confirmation": "Esteu segur que voleu suprimir l'àlbum {album}?", "album_delete_confirmation_description": "Si aquest àlbum es comparteix, els altres usuaris ja no podran accedir-hi.", + "album_deleted": "S'ha suprimit l'àlbum", "album_info_card_backup_album_excluded": "Exclosos", "album_info_card_backup_album_included": "Inclosos", "album_info_updated": "InformaciÃŗ de l'àlbum actualitzada", @@ -387,7 +429,9 @@ "album_options": "Opcions de l'àlbum", "album_remove_user": "Eliminar l'usuari?", "album_remove_user_confirmation": "Esteu segurs que voleu eliminar {user}?", + "album_search_not_found": "No s'ha trobat cap àlbum que coincideixi amb la teva cerca", "album_share_no_users": "Sembla que has compartit aquest àlbum amb tots els usuaris o no tens cap usuari amb qui compartir-ho.", + "album_summary": "Resum de l'àlbum", "album_updated": "Àlbum actualitzat", "album_updated_setting_description": "Rep una notificaciÃŗ per correu electrÃ˛nic quan un àlbum compartit tingui recursos nous", "album_user_left": "Surt de {album}", @@ -406,6 +450,7 @@ "albums_default_sort_order": "Ordre per defecte de l'àlbum", "albums_default_sort_order_description": "Ordre de classificaciÃŗ inicial dels recursos al crear àlbums nous.", "albums_feature_description": "Col¡leccions d'actius que es poden compartir amb altres usuaris.", + "albums_on_device_count": "Àlbums al dispositiu ({count})", "all": "Tots", "all_albums": "Tots els àlbum", "all_people": "Tota la gent", @@ -425,7 +470,9 @@ "app_bar_signout_dialog_title": "Tanca la sessiÃŗ", "app_settings": "ConfiguraciÃŗ de l'app", "appears_in": "Apareix a", + "apply_count": "Aplicar ({count, number})", "archive": "Arxiu", + "archive_action_prompt": "{count} afegit a Arxiu", "archive_or_unarchive_photo": "Arxivar o desarxivar fotografia", "archive_page_no_archived_assets": "No s'ha trobat res arxivat", "archive_page_title": "Arxiu({count})", @@ -456,6 +503,8 @@ "asset_restored_successfully": "Element recuperat correctament", "asset_skipped": "Saltat", "asset_skipped_in_trash": "A la paperera", + "asset_trashed": "Recurs a la paperera", + "asset_troubleshoot": "DiagnÃ˛stic de l'element", "asset_uploaded": "Carregat", "asset_uploading": "S'està carregantâ€Ļ", "asset_viewer_settings_subtitle": "Gestiona la configuraciÃŗ del visualitzador de la galeria", @@ -463,8 +512,9 @@ "assets": "Elements", "assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}", "assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum", - "assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {{name}} other {new album}}", + "assets_added_to_albums_count": "Afegits {assetTotal, plural, one {# recurs} other {# recursos}} a {albumTotal, plural, one {# album} other {# albums}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum", + "assets_cannot_be_added_to_albums": "{count, plural, one {El recurs} other {Els recursos}} no poden ser afegits a cap dels àlbums", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", "assets_deleted_permanently": "{count} element(s) esborrats permanentment", "assets_deleted_permanently_from_server": "{count} element(s) esborrats permanentment del servidor d'Immich", @@ -481,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# element enviat} other {# elements enviats}} a la paperera", "assets_trashed_from_server": "{count} element(s) enviat a la paperera del servidor d'Immich", "assets_were_part_of_album_count": "{count, plural, one {L'element ja Ês} other {Els elements ja sÃŗn}} part de l'àlbum", + "assets_were_part_of_albums_count": "{count, plural, one {El recurs ja formava} other {Els recursos ja formaven}} part dels àlbums", "authorized_devices": "Dispositius autoritzats", "automatic_endpoint_switching_subtitle": "Connecteu-vos localment a travÊs de la Wi-Fi designada quan estigui disponible i utilitzeu connexions alternatives en altres llocs", "automatic_endpoint_switching_title": "Canvi automàtic d'URL", "autoplay_slideshow": "Reprodueix automàticament les diapositives", "back": "Enrere", "back_close_deselect": "Tornar, tancar o anul¡lar la selecciÃŗ", + "background_backup_running_error": "La cÃ˛pia de seguretat en segon pla s'està executant actualment, no es pot iniciar la cÃ˛pia de seguretat manual", "background_location_permission": "Permís d'ubicaciÃŗ en segon pla", "background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accÊs a la ubicaciÃŗ precisa perquè l'aplicaciÃŗ pugui llegir el nom de la xarxa Wi-Fi", + "background_options": "Opcions en segon pla", + "backup": "CÃ˛pia", "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})", "backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure", "backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procÊs de cÃ˛pia de seguretat.", "backup_album_selection_page_select_albums": "Selecciona àlbums", "backup_album_selection_page_selection_info": "InformaciÃŗ de la selecciÃŗ", "backup_album_selection_page_total_assets": "Total d'elements Ãēnics", + "backup_albums_sync": "SincronitzaciÃŗ d'àlbums de cÃ˛pia de seguretat", "backup_all": "Tots", "backup_background_service_backup_failed_message": "No s'ha pogut copiar els elements. Tornant a intentarâ€Ļ", "backup_background_service_connection_failed_message": "No s'ha pogut connectar al servidor. Tornant a intentarâ€Ļ", @@ -544,13 +599,16 @@ "backup_controller_page_turn_on": "Activa la cÃ˛pia de seguretat", "backup_controller_page_uploading_file_info": "S'està pujant la informaciÃŗ del fitxer", "backup_err_only_album": "No es pot eliminar l'Ãēnic àlbum", + "backup_error_sync_failed": "SincronitzaciÃŗ malament, No es pot fer backup.", "backup_info_card_assets": "elements", "backup_manual_cancelled": "Cancel¡lat", "backup_manual_in_progress": "La pujada ja està en curs. Torneu-ho a provar mÊs tard", "backup_manual_success": "Èxit", "backup_manual_title": "Estat de pujada", + "backup_options": "Opcions de CÃ˛pia de Seguretat", "backup_options_page_title": "Opcions de cÃ˛pia de seguretat", "backup_setting_subtitle": "Gestiona la configuraciÃŗ de càrrega en segon pla i en primer pla", + "backup_settings_subtitle": "Administra la configuraciÃŗ de pujada", "backward": "Enrere", "biometric_auth_enabled": "AutentificaciÃŗ biomètrica activada", "biometric_locked_out": "Esteu bloquejats fora de l'autenticaciÃŗ biomètrica", @@ -569,7 +627,7 @@ "cache_settings_clear_cache_button": "Neteja la memÃ˛ria cau", "cache_settings_clear_cache_button_title": "Neteja la memÃ˛ria cau de l'aplicaciÃŗ. AixÃ˛ impactarà significativament el rendiment fins que la memÃ˛ria cau es torni a reconstruir.", "cache_settings_duplicated_assets_clear_button": "NETEJA", - "cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista negra de l'aplicaciÃŗ", + "cache_settings_duplicated_assets_subtitle": "Fotos i vídeos que estan a la llista ignorada de l'aplicaciÃŗ", "cache_settings_duplicated_assets_title": "Elements duplicats ({count})", "cache_settings_statistics_album": "Miniatures de la biblioteca", "cache_settings_statistics_full": "Imatges completes", @@ -586,6 +644,7 @@ "cancel": "Cancel¡la", "cancel_search": "Cancel¡la la cerca", "canceled": "Cancel¡lat", + "canceling": "Cancel¡lant", "cannot_merge_people": "No es pot fusionar gent", "cannot_undo_this_action": "Aquesta acciÃŗ no es pot desfer!", "cannot_update_the_description": "No es pot actualitzar la descripciÃŗ", @@ -608,6 +667,8 @@ "change_pin_code": "Canviar el codi PIN", "change_your_password": "Canvia la teva contrasenya", "changed_visibility_successfully": "Visibilitat canviada amb èxit", + "charging": "Carregant", + "charging_requirement_mobile_backup": "La cÃ˛pia de seguretat en segon pla requereix que el dispositiu estigui carregant", "check_corrupt_asset_backup": "Comprovar les cÃ˛pies de seguretat corruptes", "check_corrupt_asset_backup_button": "Realitzar comprovaciÃŗ", "check_corrupt_asset_backup_description": "Executeu aquesta comprovaciÃŗ nomÊs mitjançant Wi-Fi i un cop s'hagi fet una cÃ˛pia de seguretat de tots els actius. El procediment pot trigar uns minuts.", @@ -617,6 +678,7 @@ "clear": "Buida", "clear_all": "Neteja-ho tot", "clear_all_recent_searches": "Esborra totes les cerques recents", + "clear_file_cache": "Buida la memÃ˛ria cau de fitxers", "clear_message": "Neteja el missatge", "clear_value": "Neteja el valor", "client_cert_dialog_msg_confirm": "OK", @@ -687,11 +749,13 @@ "create_new_user": "Crea un usuari nou", "create_shared_album_page_share_add_assets": "AFEGEIX ELEMENTS", "create_shared_album_page_share_select_photos": "Escull fotografies", + "create_shared_link": "Crea un enllaç compartit", "create_tag": "Crear etiqueta", "create_tag_description": "Crear una nova etiqueta. Per les etiquetes aniuades, escriu la ruta comperta de l'etiqueta, incloses les barres diagonals.", "create_user": "Crea un usuari", "created": "Creat", "created_at": "Creat", + "creating_linked_albums": "Creant àlbums enllaçats...", "crop": "Retalla", "curated_object_page_title": "Coses", "current_device": "Dispositiu actual", @@ -699,10 +763,11 @@ "current_server_address": "Adreça actual del servidor", "custom_locale": "LocalitzaciÃŗ personalitzada", "custom_locale_description": "Format de dates i nÃēmeros segons la llengua i regiÃŗ", + "custom_url": "URL personalitzada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Fosc", - "darkTheme": "Activa/desactiva el tema fosc", + "dark_theme": "Canviar a tema fosc", "date_after": "Data posterior a", "date_and_time": "Data i hora", "date_before": "Data anterior a", @@ -710,6 +775,7 @@ "date_of_birth_saved": "Data de naixement guardada amb èxit", "date_range": "Interval de dates", "day": "Dia", + "days": "Dies", "deduplicate_all": "Desduplica-ho tot", "deduplication_criteria_1": "Mida d'imatge en bytes", "deduplication_criteria_2": "Quantitat de dades EXIF", @@ -718,6 +784,8 @@ "default_locale": "LocalitzaciÃŗ predeterminada", "default_locale_description": "Format de dates i nÃēmeros segons la configuraciÃŗ del navegador", "delete": "Esborra", + "delete_action_confirmation_message": "Segur que vols eliminar aquest recurs? Aquesta acciÃŗ el mourà a la paperera del servidor, i et preguntarà si el vols eliminar localment", + "delete_action_prompt": "{count} eliminats", "delete_album": "Esborra l'àlbum", "delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?", "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu", @@ -731,9 +799,12 @@ "delete_key": "Suprimeix la clau", "delete_library": "Suprimeix la Llibreria", "delete_link": "Esborra l'enllaç", + "delete_local_action_prompt": "{count} eliminats localment", "delete_local_dialog_ok_backed_up_only": "Esborrar nomÊs les que tinguin cÃ˛pia de seguretat", "delete_local_dialog_ok_force": "Suprimeix de totes maneres", "delete_others": "Suprimeix altres", + "delete_permanently": "Eliminar permanentment", + "delete_permanently_action_prompt": "{count} eliminats permanentment", "delete_shared_link": "Odstranit sdílenÃŊ odkaz", "delete_shared_link_dialog_title": "Suprimeix l'enllaç compartit", "delete_tag": "Eliminar etiqueta", @@ -744,6 +815,7 @@ "description": "DescripciÃŗ", "description_input_hint_text": "Afegeix descripciÃŗ...", "description_input_submit_error": "S'ha produït un error en actualitzar la descripciÃŗ, comproveu el registre per a mÊs detalls", + "deselect_all": "Deseleccionar Tots", "details": "Detalls", "direction": "DirecciÃŗ", "disabled": "Desactivat", @@ -761,6 +833,7 @@ "documentation": "DocumentaciÃŗ", "done": "Fet", "download": "Descarregar", + "download_action_prompt": "Baixant {count} recursos", "download_canceled": "Descàrrega cancel¡lada", "download_complete": "Descàrrega completada", "download_enqueue": "Descàrrega en cua", @@ -787,8 +860,12 @@ "edit": "Editar", "edit_album": "Edita l'àlbum", "edit_avatar": "Edita l'avatar", + "edit_birthday": "Editar aniversari", "edit_date": "Edita la data", "edit_date_and_time": "Edita data i hora", + "edit_date_and_time_action_prompt": "{count} dates i hores editades", + "edit_date_and_time_by_offset": "Canviar data mitjançant diferència", + "edit_date_and_time_by_offset_interval": "Nou rang de dates: {from}-{to}", "edit_description": "Edita la descripciÃŗ", "edit_description_prompt": "Si us plau, selecciona una nova descripciÃŗ:", "edit_exclusion_pattern": "Edita patrÃŗ d'exclusiÃŗ", @@ -798,6 +875,7 @@ "edit_key": "Edita clau", "edit_link": "Edita enllaç", "edit_location": "Edita ubicaciÃŗ", + "edit_location_action_prompt": "{count} ubicacions editades", "edit_location_dialog_title": "UbicaciÃŗ", "edit_name": "Edita el nom", "edit_people": "Edita la gent", @@ -816,6 +894,7 @@ "empty_trash": "Buidar la paperera", "empty_trash_confirmation": "Esteu segur que voleu buidar la paperera? AixÃ˛ eliminarà tots els recursos a la paperera permanentment d'Immich.\nNo podeu desfer aquesta acciÃŗ!", "enable": "Activar", + "enable_backup": "CÃ˛pia de Seguretat", "enable_biometric_auth_description": "Introduïu el codi PIN per a habilitar l'autenticaciÃŗ biomètrica", "enabled": "Activat", "end_date": "Data final", @@ -826,7 +905,9 @@ "error": "Error", "error_change_sort_album": "No s'ha pogut canviar l'ordre d'ordenaciÃŗ dels àlbums", "error_delete_face": "Error esborrant cara de les cares reconegudes", + "error_getting_places": "S'ha produït un error en obtenir els llocs", "error_loading_image": "Error carregant la imatge", + "error_loading_partners": "No s'han pogut carregar les parelles: {error}", "error_saving_image": "Error: {error}", "error_tag_face_bounding_box": "Error a l'etiquetar la cara - no s'han pogut obtenir les coordenades de l'àrea", "error_title": "Error - Quelcom ha anat malament", @@ -859,6 +940,7 @@ "failed_to_load_notifications": "Error en carregar les notificacions", "failed_to_load_people": "No s'han pogut carregar les persones", "failed_to_remove_product_key": "No s'ha pogut eliminar la clau del producte", + "failed_to_reset_pin_code": "No s'ha pogut reiniciar el codi PIN", "failed_to_stack_assets": "No s'han pogut apilar els elements", "failed_to_unstack_assets": "No s'han pogut desapilar els elements", "failed_to_update_notification_status": "Error en actualitzar l'estat de les notificacions", @@ -867,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# ruta} other {# rutes}} no ha pogut validar", "profile_picture_transparent_pixels": "Les fotos de perfil no poden tenir píxels transparents. Per favor, feu zoom in, mogueu la imatge o ambdues.", "quota_higher_than_disk_size": "Heu establert una quota mÊs gran que la mida de disc", + "something_went_wrong": "Alguna cosa ha anat malament", "unable_to_add_album_users": "No es poden afegir usuaris a l'àlbum", "unable_to_add_assets_to_shared_link": "No s'han pogut afegir els elements a l'enllaç compartit", "unable_to_add_comment": "No es pot afegir el comentari", @@ -952,13 +1035,11 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Afegeix descripciÃŗ...", + "exif_bottom_sheet_description_error": "No s'ha pogut actualitzar la descripciÃŗ", "exif_bottom_sheet_details": "DETALLS", "exif_bottom_sheet_location": "UBICACIÓ", "exif_bottom_sheet_people": "PERSONES", "exif_bottom_sheet_person_add_person": "Afegir nom", - "exif_bottom_sheet_person_age_months": "Edat {months} mesos", - "exif_bottom_sheet_person_age_year_months": "Edat 1 any, {months} mesos", - "exif_bottom_sheet_person_age_years": "Edat {years}", "exit_slideshow": "Surt de la presentaciÃŗ de diapositives", "expand_all": "Ampliar-ho tot", "experimental_settings_new_asset_list_subtitle": "Treball en curs", @@ -972,6 +1053,8 @@ "explorer": "Explorador", "export": "Exporta", "export_as_json": "Exportar com a JSON", + "export_database": "Exportar base de dades", + "export_database_description": "Exportar la base de dades SQLite", "extension": "ExtensiÃŗ", "external": "Extern", "external_libraries": "Llibreries externes", @@ -983,11 +1066,13 @@ "failed_to_load_assets": "Error carregant recursos", "failed_to_load_folder": "No s'ha pogut carregar la carpeta", "favorite": "Preferit", + "favorite_action_prompt": "{count} afegit a Favorits", "favorite_or_unfavorite_photo": "Foto preferida o no preferida", "favorites": "Preferits", "favorites_page_no_favorites": "No s'han trobat preferits", "feature_photo_updated": "Foto destacada actualitzada", "features": "Característiques", + "features_in_development": "Funcions en desenvolupament", "features_setting_description": "Administrar les funcions de l'aplicaciÃŗ", "file_name": "Nom de l'arxiu", "file_name_or_extension": "Nom de l'arxiu o extensiÃŗ", @@ -997,21 +1082,26 @@ "filter_people": "Filtra persones", "filter_places": "Filtrar per llocs", "find_them_fast": "Trobeu-los ràpidament pel nom amb la cerca", + "first": "Primer", "fix_incorrect_match": "Corregiu la coincidència incorrecta", "folder": "Carpeta", "folder_not_found": "Carpeta no trobada", "folders": "Carpetes", "folders_feature_description": "Explorar la vista de carpetes per les fotos i vídeos del sistema d'arxius", + "forgot_pin_code_question": "Has oblidat el teu PIN?", "forward": "Endavant", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Aquesta funciÃŗ carrega recursos externs de Google per funcionar.", "general": "General", + "geolocation_instruction_location": "Fes click en un element amb coordinades GPS per utilitzar la seva ubicaciÃŗ o selecciona una ubicaciÃŗ des del mapa", "get_help": "Aconseguir ajuda", "get_wifiname_error": "No s'ha pogut obtenir el nom de la Wi-Fi. Assegureu-vos que heu concedit els permisos necessaris i que esteu connectat a una xarxa Wi-Fi", "getting_started": "Començant", "go_back": "Torna", "go_to_folder": "Anar al directori", "go_to_search": "VÊs a cercar", + "gps": "GPS", + "gps_missing": "Sense GPS", "grant_permission": "Concedir permís", "group_albums_by": "Agrupa àlbums per...", "group_country": "Agrupar per país", @@ -1022,6 +1112,9 @@ "haptic_feedback_switch": "Activa la resposta hàptica", "haptic_feedback_title": "Resposta Hàptica", "has_quota": "Quota", + "hash_asset": "Hash del recurs", + "hashed_assets": "Recursos amb hash", + "hashing": "Generant el hash", "header_settings_add_header_tip": "Afegeix Capçalera", "header_settings_field_validator_msg": "El valor no pot estar buit", "header_settings_header_name_input": "Nom de la capçalera", @@ -1053,7 +1146,9 @@ "home_page_upload_err_limit": "NomÊs es poden pujar un màxim de 30 elements alhora, ometent", "host": "AmfitriÃŗ", "hour": "Hora", + "hours": "Hores", "id": "ID", + "idle": "En espera", "ignore_icloud_photos": "Ignora fotos d'iCloud", "ignore_icloud_photos_description": "Les fotos emmagatzemades a iCloud no es penjaran al servidor Immich", "image": "Imatge", @@ -1111,10 +1206,13 @@ "language_no_results_title": "No s'han trobat idiomes", "language_search_hint": "Cerca idiomes...", "language_setting_description": "Seleccioneu el vostre idioma", + "large_files": "Fitxers Grans", + "last": "Últim", "last_seen": "Vist per Ãēltim cop", "latest_version": "Última versiÃŗ", "latitude": "Latitud", "leave": "Marxar", + "leave_album": "Abandonar àlbum", "lens_model": "Model de lents", "let_others_respond": "Deixa que els altres responguin", "level": "Nivell", @@ -1122,20 +1220,24 @@ "library_options": "Opcions de biblioteca", "library_page_device_albums": "Àlbums al Dispositiu", "library_page_new_album": "Nou àlbum", - "library_page_sort_asset_count": "Nombre d'elements", + "library_page_sort_asset_count": "Quantitat d'elements", "library_page_sort_created": "Creat mÊs recentment", "library_page_sort_last_modified": "Darrera modificaciÃŗ", "library_page_sort_title": "Títol de l'àlbum", + "licenses": "Llicències", "light": "Llum", + "like": "M'agrada", "like_deleted": "M'agrada suprimit", "link_motion_video": "Enllaçar vídeo en moviment", - "link_options": "Opcions d'enllaç", "link_to_oauth": "Enllaç a OAuth", "linked_oauth_account": "Compte OAuth enllaçat", "list": "Llista", "loading": "Carregant", "loading_search_results_failed": "No s'han pogut carregar els resultats de la cerca", + "local": "Local", "local_asset_cast_failed": "No es pot convertir un actiu que no s'ha penjat al servidor", + "local_assets": "Recursos Locals", + "local_media_summary": "Resum de Mitjans Locals", "local_network": "Xarxa local", "local_network_sheet_info": "L'aplicaciÃŗ es connectarà al servidor mitjançant aquest URL quan utilitzeu la xarxa Wi-Fi especificada", "location_permission": "Permís d'ubicaciÃŗ", @@ -1147,8 +1249,10 @@ "location_picker_longitude_hint": "Introdueix aquí la longitud", "lock": "Bloqueja", "locked_folder": "Carpeta bloquejada", + "log_detail_title": "Detall de Registres", "log_out": "Tanca la sessiÃŗ", "log_out_all_devices": "Tanqueu la sessiÃŗ de tots els dispositius", + "logged_in_as": "SessiÃŗ iniciada com a {user}", "logged_out_all_devices": "S'ha tancat la sessiÃŗ de tots els dispositius", "logged_out_device": "Dispositiu tancat", "login": "Iniciar sessiÃŗ", @@ -1157,7 +1261,7 @@ "login_form_back_button_text": "Enrere", "login_form_email_hint": "elteu@correu.cat", "login_form_endpoint_hint": "http://ip-del-servidor:port", - "login_form_endpoint_url": "URL del servidor", + "login_form_endpoint_url": "URL del punt final del servidor", "login_form_err_http": "Especifica http:// o https://", "login_form_err_invalid_email": "Adreça de correu electrÃ˛nic no vàlida", "login_form_err_invalid_url": "URL no vàlid", @@ -1176,6 +1280,7 @@ "login_password_changed_success": "La contrasenya s'ha canviat correctament", "logout_all_device_confirmation": "Esteu segur que voleu tancar la sessiÃŗ de tots els dispositius?", "logout_this_device_confirmation": "Esteu segur que voleu tancar la sessiÃŗ d'aquest dispositiu?", + "logs": "Registres", "longitude": "Longitud", "look": "Aspecte", "loop_videos": "Vídeos en bucle", @@ -1183,6 +1288,7 @@ "main_branch_warning": "Esteu utilitzant una versiÃŗ en desenvolupament; Recomanem fer servir una versiÃŗ publicada!", "main_menu": "MenÃē principal", "make": "Fabricant", + "manage_geolocation": "Gestioneu la vostra ubicaciÃŗ", "manage_shared_links": "Administrar enllaços compartits", "manage_sharing_with_partners": "Gestiona la comparticiÃŗ amb els companys", "manage_the_app_settings": "Gestioneu la configuraciÃŗ de l'aplicaciÃŗ", @@ -1191,8 +1297,7 @@ "manage_your_devices": "Gestioneu els vostres dispositius connectats", "manage_your_oauth_connection": "Gestioneu la vostra connexiÃŗ OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, =0 {No hi ha fotos en aquesta àrea} one {# foto} other {# fotos}}", "map_cannot_get_user_location": "No es pot obtenir la ubicaciÃŗ de l'usuari", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Utilitzar aquesta ubicaciÃŗ", @@ -1200,7 +1305,6 @@ "map_location_service_disabled_title": "Servei de localitzaciÃŗ desactivat", "map_marker_for_images": "Marcador de mapa per a imatges fetes a {city}, {country}", "map_marker_with_image": "Marcador de mapa amb imatge", - "map_no_assets_in_bounds": "No hi ha fotos en aquesta zona", "map_no_location_permission_content": "Es necessita el permís de localitzaciÃŗ per mostrar els elements de la teva ubicaciÃŗ actual. Vols permetre-ho ara?", "map_no_location_permission_title": "Permís de localitzaciÃŗ denegat", "map_settings": "Paràmetres de mapa", @@ -1219,6 +1323,7 @@ "mark_as_read": "Marcar com ha llegit", "marked_all_as_read": "Marcat tot com a llegit", "matches": "Coincidències", + "matching_assets": "Recursos Coincidents", "media_type": "Tipus de mitjà", "memories": "Records", "memories_all_caught_up": "Posat al dia", @@ -1237,6 +1342,7 @@ "merged_people_count": "Combinades {count, plural, one {# persona} other {# persones}}", "minimize": "Minimitza", "minute": "Minut", + "minutes": "Minuts", "missing": "Restants", "model": "Model", "month": "Mes", @@ -1244,6 +1350,7 @@ "more": "MÊs", "move": "Moure", "move_off_locked_folder": "Moure fora de la carpeta bloquejada", + "move_to_lock_folder_action_prompt": "{count} afegides a la carpeta protegida", "move_to_locked_folder": "Moure a la carpeta bloquejada", "move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i nomÊs podran ser vistes des de la carpeta bloquejada", "moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu", @@ -1255,6 +1362,10 @@ "my_albums": "Els meus àlbums", "name": "Nom", "name_or_nickname": "Nom o sobrenom", + "network_requirement_photos_upload": "Fes servir dades mÃ˛bils per a cÃ˛pies de seguretat de fotos", + "network_requirement_videos_upload": "Fes servir dades mÃ˛bils per a cÃ˛pies de seguretat de videos", + "network_requirements": "Requeriments de Xarxa", + "network_requirements_updated": "Han canviat els requeriments de xarxa, reiniciant la cua", "networking_settings": "Xarxes", "networking_subtitle": "Gestiona la configuraciÃŗ del endpoint del servidor", "never": "Mai", @@ -1264,6 +1375,7 @@ "new_person": "Persona nova", "new_pin_code": "Nou codi PIN", "new_pin_code_subtitle": "Aquesta Ês la primera vegada que accedeixes a la carpeta bloquejada. Crea una codi PIN i accedeix de manera segura a aquesta pàgina", + "new_timeline": "Nova Línia de Temps", "new_user_created": "Nou usuari creat", "new_version_available": "NOVA VERSIÓ DISPONIBLE", "newest_first": "El mÊs nou primer", @@ -1277,19 +1389,25 @@ "no_assets_message": "FEU CLIC PER PUJAR LA VOSTRA PRIMERA FOTO", "no_assets_to_show": "No hi ha elements per mostrar", "no_cast_devices_found": "No s'han trobat dispositius per transmetre", + "no_checksum_local": "Cap checksum disponible - no s'han pogut carregar els recursos locals", + "no_checksum_remote": "Cap checksum disponible - no s'ha pogut obtenir el recurs remot", "no_duplicates_found": "No s'han trobat duplicats.", "no_exif_info_available": "No hi ha informaciÃŗ d'exif disponible", "no_explore_results_message": "Penja mÊs fotos per explorar la teva col¡lecciÃŗ.", "no_favorites_message": "Afegiu preferits per trobar les millors fotos i vídeos a l'instant", "no_libraries_message": "Creeu una llibreria externa per veure les vostres fotos i vídeos", + "no_local_assets_found": "No s'ha trobat cap recurs local amb aquest checksum", "no_locked_photos_message": "Les fotos i vídeos d'aquesta carpeta estan ocultes, i no es mostraran a mesura que navegues o cerques a la teva biblioteca.", "no_name": "Sense nom", "no_notifications": "No hi ha notificacions", "no_people_found": "No s'han trobat coincidències de persones", "no_places": "No hi ha llocs", + "no_remote_assets_found": "No s'ha trobat cap recurs remot amb aquest checksum", "no_results": "Sense resultats", "no_results_description": "Proveu un sinÃ˛nim o una paraula clau mÊs general", "no_shared_albums_message": "Creeu un àlbum per compartir fotos i vídeos amb persones a la vostra xarxa", + "no_uploads_in_progress": "Cap pujada en progrÊs", + "not_available": "N/A", "not_in_any_album": "En cap àlbum", "not_selected": "No seleccionat", "note_apply_storage_label_to_previously_uploaded assets": "Nota: per aplicar l'etiqueta d'emmagatzematge als actius penjats anteriorment, executeu el", @@ -1305,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Recursos oficials d'Immich", "offline": "Fora de línia", + "offset": "Diferència", "ok": "D'acord", "oldest_first": "El mÊs vell primer", "on_this_device": "En aquest dispositiu", @@ -1323,10 +1442,13 @@ "open_the_search_filters": "Obriu els filtres de cerca", "options": "Opcions", "or": "o", + "organize_into_albums": "Organitzar en àlbums", + "organize_into_albums_description": "Posar fotos existents en àlbums utilitzant la configuraciÃŗ de sincronitzaciÃŗ actual", "organize_your_library": "Organitzeu la llibreria", "original": "original", "other": "Altres", "other_devices": "Altres dispositius", + "other_entities": "Altres entitats", "other_variables": "Altres variables", "owned": "Propi", "owner": "Propietari", @@ -1381,6 +1503,9 @@ "permission_onboarding_permission_limited": "Permís limitat. Per a permetre que Immich faci cÃ˛pies de seguretat i gestioni tota la col¡lecciÃŗ de la galeria, concediu permisos de fotos i vídeos a ConfiguraciÃŗ.", "permission_onboarding_request": "Immich requereix permís per veure les vostres fotos i vídeos.", "person": "Persona", + "person_age_months": "{months, plural, one {# mes} other {# mesos}} d'antiguitat", + "person_age_year_months": "1 any, {months, plural, one {# mes} other {# mesos}} d'antiguitat", + "person_age_years": "{years, plural, other {# anys}} d'antiguitat", "person_birthdate": "Nascut a {date}", "person_hidden": "{name}{hidden, select, true { (ocultat)} other {}}", "photo_shared_all_users": "Sembla que has compartit les teves fotos amb tots els usuaris o no tens cap usuari amb qui compartir-les.", @@ -1404,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Gestiona les preferències de l'aplicaciÃŗ", "preferences_settings_title": "Preferències", + "preparing": "Preparant", "preset": "Preestablert", "preview": "PrevisualitzaciÃŗ", "previous": "Anterior", @@ -1418,8 +1544,9 @@ "profile_drawer_app_logs": "Registres", "profile_drawer_client_out_of_date_major": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ major.", "profile_drawer_client_out_of_date_minor": "L'aplicaciÃŗ mÃ˛bil està desactualitzada. Si us plau, actualitzeu a l'Ãēltima versiÃŗ menor.", - "profile_drawer_client_server_up_to_date": "El Client i el Servidor estan actualitzats", + "profile_drawer_client_server_up_to_date": "El client i el servidor estan actualitzats", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Mode nomÊs lectura. Feu pulsaciÃŗ llarga a la icona de l'avatar d'usuari per sortir.", "profile_drawer_server_out_of_date_major": "El servidor està desactualitzat. Si us plau, actualitzeu a l'Ãēltima versiÃŗ major.", "profile_drawer_server_out_of_date_minor": "El servidor està desactualitzat. Si us plau, actualitzeu a l'Ãēltima versiÃŗ menor.", "profile_image_of_user": "Imatge de perfil de {user}", @@ -1458,12 +1585,17 @@ "purchase_server_description_2": "Estat del contribuent", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "La clau de producte del servidor la gestiona l'administrador", + "query_asset_id": "Consulta d'identificaciÃŗ d'actius", + "queue_status": "En cua {count}/{total}", "rating": "ValoraciÃŗ", "rating_clear": "Esborrar valoraciÃŗ", "rating_count": "{count, plural, one {# estrella} other {# estrelles}}", "rating_description": "Mostrar la valoraciÃŗ EXIF al panell d'informaciÃŗ", "reaction_options": "Opcions de reacciÃŗ", "read_changelog": "Llegeix el registre de canvis", + "readonly_mode_disabled": "Mode de nomÊs lectura desactivat", + "readonly_mode_enabled": "Mode de nomÊs lectura activat", + "ready_for_upload": "Llest per a pujar", "reassign": "Reassignar", "reassigned_assets_to_existing_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a {name, select, null {una persona existent} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {S'ha reassignat # recurs} other {S'han reassignat # recursos}} a una persona nova", @@ -1486,6 +1618,9 @@ "refreshing_faces": "Refrescant cares", "refreshing_metadata": "Actualitzant les metadades", "regenerating_thumbnails": "Regenerant les miniatures", + "remote": "Remot", + "remote_assets": "Recursos Remots", + "remote_media_summary": "Resum de Mitjans Remots", "remove": "Eliminar", "remove_assets_album_confirmation": "Confirmes que vols eliminar {count, plural, one {# recurs} other {# recursos}} de l'àlbum?", "remove_assets_shared_link_confirmation": "Esteu segur que voleu eliminar {count, plural, one {# recurs} other {# recursos}} d'aquest enllaç compartit?", @@ -1493,7 +1628,9 @@ "remove_custom_date_range": "Elimina l'interval de dates personalitzat", "remove_deleted_assets": "Suprimeix fitxers fora de línia", "remove_from_album": "Treu de l'àlbum", + "remove_from_album_action_prompt": "{count} eliminats de l'àlbum", "remove_from_favorites": "Eliminar dels preferits", + "remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida", "remove_from_locked_folder": "Elimina de la carpeta bloquejada", "remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.", "remove_from_shared_link": "Eliminar de l'enllaç compartit", @@ -1521,19 +1658,29 @@ "reset_password": "Restablir contrasenya", "reset_people_visibility": "Restablir la visibilitat de les persones", "reset_pin_code": "Restablir el codi PIN", + "reset_pin_code_description": "Si has oblidat el teu codi PIN, pots contactar amb l'administrador del servidor per a reiniciar-lo", + "reset_pin_code_success": "Codi PIN reiniciat correctament", + "reset_pin_code_with_password": "Sempre pots reiniciar el codi PIN amb la teva contrasenya", + "reset_sqlite": "Reiniciar base de dades SQLite", + "reset_sqlite_confirmation": "Segur que vols reiniciar la base de dades SQLite? Hauràs de tancar la sessiÃŗ i tornar a accedir per a resincronitzar les dades", + "reset_sqlite_success": "S'ha reiniciat la base de dades correctament", "reset_to_default": "Restableix els valors predeterminats", "resolve_duplicates": "Resoldre duplicats", "resolved_all_duplicates": "Tots els duplicats resolts", "restore": "Recupera", "restore_all": "Restaurar-ho tot", + "restore_trash_action_prompt": "{count} recuperats de la paperera", "restore_user": "Restaurar l'usuari", "restored_asset": "Element restaurat", "resume": "Reprendre", + "resume_paused_jobs": "Reprèn {count, plural, one {# treball pausat} other {# treballs pausats}}", "retry_upload": "Torna a provar de pujar", "review_duplicates": "Revisar duplicats", + "review_large_files": "Revisar fitxers grans", "role": "Rol", "role_editor": "Editor", "role_viewer": "Visor", + "running": "En execuciÃŗ", "save": "Desa", "save_to_gallery": "Desa a galeria", "saved_api_key": "Clau d'API guardada", @@ -1606,6 +1753,7 @@ "select_album_cover": "Seleccionar la portada de l'àlbum", "select_all": "Selecciona-ho tot", "select_all_duplicates": "Seleccioneu tots els duplicats", + "select_all_in": "Selecciona tot en {group}", "select_avatar_color": "Tria color de l'avatar", "select_face": "Selecciona cara", "select_featured_photo": "Selecciona foto principal", @@ -1619,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Error al crear l'àlbum", "selected": "Seleccionat", "selected_count": "{count, plural, one {# seleccionat} other {# seleccionats}}", + "selected_gps_coordinates": "Seleccio de coordinades GPS", "send_message": "Envia missatge", "send_welcome_email": "Envia correu de benvinguda", "server_endpoint": "Endpoint de Servidor", @@ -1664,6 +1813,7 @@ "settings_saved": "ConfiguraciÃŗ desada", "setup_pin_code": "Configurar un codi PIN", "share": "Comparteix", + "share_action_prompt": "Compartits {count} recursos", "share_add_photos": "Afegeix fotografies", "share_assets_selected": "{count} seleccionats", "share_dialog_preparing": "S'està preparant...", @@ -1685,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "S'ha copiat al porta-retalls", "shared_link_clipboard_text": "Enllaç: {link}\nContrasenya: {password}", "shared_link_create_error": "S'ha produït un error en crear l'enllaç compartit", + "shared_link_custom_url_description": "Accedeix a aquest enllaç compartit amb una URL personalitzada", "shared_link_edit_description_hint": "Introduïu la descripciÃŗ de comparticiÃŗ", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dies", @@ -1710,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestiona els enllaços compartits", "shared_link_options": "Opcions d'enllaços compartits", + "shared_link_password_description": "Requereix una contrasenya per accedir a aquest enllaç compartit", "shared_links": "Enllaços compartits", "shared_links_description": "Comparteix fotos i vídeos amb un enllaç", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotos i vídeos compartits.}}", @@ -1744,6 +1896,7 @@ "show_slideshow_transition": "Mostra la transiciÃŗ de la presentaciÃŗ de diapositives", "show_supporter_badge": "Insígnia de contribuent", "show_supporter_badge_description": "Mostra una insígnia de contributor", + "show_text_search_menu": "Mostra el menÃē de cerca amb text", "shuffle": "Mescla", "sidebar": "Barra lateral", "sidebar_display_description": "Mostra un enllaç a la vista a la barra lateral", @@ -1757,14 +1910,16 @@ "slideshow_settings": "ConfiguraciÃŗ de diapositives", "sort_albums_by": "Ordena àlbums per...", "sort_created": "Data de creaciÃŗ", - "sort_items": "Nombre d'elements", + "sort_items": "Quantitat d'elements", "sort_modified": "Data de modificaciÃŗ", + "sort_newest": "Foto mÊs nova", "sort_oldest": "Foto mÊs antiga", "sort_people_by_similarity": "Ordenar personar per semblança", "sort_recent": "Foto mÊs recent", "sort_title": "Títol", "source": "Font", "stack": "Apila", + "stack_action_prompt": "{count} apilats", "stack_duplicates": "Aplica duplicats", "stack_select_one_photo": "Selecciona una imatge principal per la pila", "stack_selected_photos": "Apila les fotos seleccionades", @@ -1772,6 +1927,7 @@ "stacktrace": "Traça de pila", "start": "Inicia", "start_date": "Data inicial", + "start_date_before_end_date": "La data d'inici ha de ser abans de la data de fi", "state": "RegiÃŗ", "status": "Estat", "stop_casting": "Atura la transmisiÃŗ", @@ -1784,6 +1940,7 @@ "storage_quota": "Quota d'emmagatzematge", "storage_usage": "{used} de {available} en Ãēs", "submit": "Envia", + "success": "Amb èxit", "suggestions": "Suggeriments", "sunrise_on_the_beach": "Albada a la platja", "support": "Suport", @@ -1793,6 +1950,10 @@ "sync": "Sincronitza", "sync_albums": "Sincronitzar àlbums", "sync_albums_manual_subtitle": "Sincronitza tots els vídeos i fotos penjats amb els àlbums de cÃ˛pia de seguretat seleccionats", + "sync_local": "Sincronitza Local", + "sync_remote": "Sincronitza Remot", + "sync_status": "Estat de sincronitzaciÃŗ", + "sync_status_subtitle": "Observa i administra el sistema de sincronitzaciÃŗ", "sync_upload_album_setting_subtitle": "Creeu i pugeu les seves fotos i vídeos als àlbums seleccionats a Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar actius", @@ -1803,6 +1964,7 @@ "tag_updated": "Etiqueta actualizada: {tag}", "tagged_assets": "{count, plural, one {#Etiquetat} other {#Etiquetats}} {count, plural, one {# actiu} other {# actius}}", "tags": "Etiquetes", + "tap_to_run_job": "Toca per executar el treball", "template": "Plantilla", "theme": "Tema", "theme_selection": "SelecciÃŗ de tema", @@ -1829,12 +1991,15 @@ "to_change_password": "Canviar la contrasenya", "to_favorite": "Prefereix", "to_login": "Iniciar sessiÃŗ", + "to_multi_select": "per multi-seleccionar", "to_parent": "Anar als pares", + "to_select": "per seleccionar", "to_trash": "Paperera", "toggle_settings": "Canvia configuraciÃŗ", "total": "Total", "total_usage": "Ús total", "trash": "Paperera", + "trash_action_prompt": "{count} mogudes a la brossa", "trash_all": "Envia-ho tot a la paperera", "trash_count": "Paperera {count, number}", "trash_delete_asset": "Esborra/Elimina element", @@ -1848,13 +2013,16 @@ "trash_page_select_assets_btn": "Selecciona elements", "trash_page_title": "Paperera ({count})", "trashed_items_will_be_permanently_deleted_after": "Els elements que s'enviïn a la paperera s'eliminaran permanentment desprÊs de {days, plural, one {# dia} other {# dies}}.", + "troubleshoot": "SoluciÃŗ de problemes", "type": "Tipus", "unable_to_change_pin_code": "No es pot canviar el codi PIN", "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", + "unarchive_action_prompt": "{count} eliminades de l'arxiu", "unarchived_count": "{count, plural, other {# elements desarxivats}}", "undo": "Desfer", "unfavorite": "Reverteix preferit", + "unfavorite_action_prompt": "{count} eliminades de preferits", "unhide_person": "Mostra persona", "unknown": "Desconegut", "unknown_country": "País Desconegut", @@ -1870,16 +2038,23 @@ "unsaved_change": "Canvi no desat", "unselect_all": "Deselecciona-ho tot", "unselect_all_duplicates": "Desmarqueu tots els duplicats", + "unselect_all_in": "Desseleccionar tots els elements de {group}", "unstack": "Desapila", + "unstack_action_prompt": "{count} sense apilar", "unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}", + "untagged": "Sense etiqueta", "up_next": "PrÃ˛xim", + "update_location_action_prompt": "Actualitza la ubicaciÃŗ de {count} elements seleccionats amb:", "updated_at": "Actualitzat", "updated_password": "Contrasenya actualitzada", "upload": "Pujar", + "upload_action_prompt": "{count} a la cua per a pujar", "upload_concurrency": "Concurrència de pujades", + "upload_details": "Detalls de la Pujada", "upload_dialog_info": "Vols fer cÃ˛pia de seguretat dels elements seleccionats al servidor?", "upload_dialog_title": "Puja elements", "upload_errors": "Càrrega completada amb {count, plural, one {# error} other {# errors}}, actualitzeu la pàgina per veure els nous elements carregats.", + "upload_finished": "Pujada finalitzada", "upload_progress": "Restant {remaining, number} - Processat {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {S'ha omès # recurs duplicat} other {S'han omès # recursos duplicats}}", "upload_status_duplicates": "Duplicats", @@ -1888,6 +2063,7 @@ "upload_success": "Pujada correcta, actualitza la pàgina per veure nous recursos de pujada.", "upload_to_immich": "Puja a Immich ({count})", "uploading": "Pujant", + "uploading_media": "Pujant mitjans", "url": "URL", "usage": "Ús", "use_biometric": "Empra biometria", @@ -1908,6 +2084,7 @@ "user_usage_stats_description": "Veure les estadístiques d'Ãēs del compte", "username": "Nom d'usuari", "users": "Usuaris", + "users_added_to_album_count": "{count, plural, one {S'ha afegit # usuari} other {S'han afegit # usuaris}} a l'àlbum", "utilities": "Utilitats", "validate": "Valida", "validate_endpoint_error": "Per favor introdueix un URL vàlid", @@ -1926,6 +2103,7 @@ "view_album": "Veure l'àlbum", "view_all": "Veure tot", "view_all_users": "Mostra tot els usuaris", + "view_details": "Veure Detalls", "view_in_timeline": "Mostrar a la línia de temps", "view_link": "Veure enllaç", "view_links": "Mostra enllaços", @@ -1933,6 +2111,7 @@ "view_next_asset": "Mostra el segÃŧent element", "view_previous_asset": "Mostra l'element anterior", "view_qr_code": "Veure codi QR", + "view_similar_photos": "Veure fotos similars", "view_stack": "Veure la pila", "view_user": "Veure Usuari", "viewer_remove_from_stack": "Elimina de la pila", @@ -1951,5 +2130,6 @@ "yes": "Sí", "you_dont_have_any_shared_links": "No tens cap enllaç compartit", "your_wifi_name": "Nom del teu Wi-Fi", - "zoom_image": "Ampliar Imatge" + "zoom_image": "Ampliar Imatge", + "zoom_to_bounds": "Amplia als límits" } diff --git a/i18n/cs.json b/i18n/cs.json index 82e72cab46..4382629f89 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -14,6 +14,7 @@ "add_a_location": "Přidat polohu", "add_a_name": "Přidat jmÊno", "add_a_title": "Přidat nÃĄzev", + "add_birthday": "Přidat datum narození", "add_endpoint": "Přidat koncovÃŊ bod", "add_exclusion_pattern": "Přidat vzor vyloučení", "add_import_path": "Přidat cestu importu", @@ -27,6 +28,10 @@ "add_to_album": "Přidat do alba", "add_to_album_bottom_sheet_added": "PřidÃĄno do {album}", "add_to_album_bottom_sheet_already_exists": "Je jiÅž v {album}", + "add_to_album_bottom_sheet_some_local_assets": "NěkterÃĄ místní aktiva nebylo moÅžnÊ přidat do alba", + "add_to_album_toggle": "Přepnout vÃŊběr pro {album}", + "add_to_albums": "Přidat do alb", + "add_to_albums_count": "Přidat do alb ({count})", "add_to_shared_album": "Přidat do sdílenÊho alba", "add_url": "Přidat URL", "added_to_archive": "PřidÃĄno do archivu", @@ -44,7 +49,14 @@ "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_onboarding_1_description": "kopie v cloudu nebo na jinÊm fyzickÊm místě.", + "backup_onboarding_2_description": "místní kopie na různÃŊch zařízeních. To zahrnuje hlavní soubory a jejich místní zÃĄlohu.", + "backup_onboarding_3_description": "kompletní kopie vaÅĄich dat, včetně původních souborů. To zahrnuje 1 kopii na jinÊm místě a 2 místní kopie.", + "backup_onboarding_description": "K ochraně vaÅĄich dat doporučujeme strategii zÃĄlohovÃĄní 3-2-1. Pro komplexní zÃĄlohovÃĄní byste měli uchovÃĄvat kopie nahranÃŊch fotografií/videí i databÃĄze Immich.", + "backup_onboarding_footer": "DalÅĄÃ­ informace o zÃĄlohovÃĄní Immiche naleznete v dokumentaci.", + "backup_onboarding_parts_title": "ZÃĄloha 3-2-1 zahrnuje:", + "backup_onboarding_title": "ZÃĄlohy", + "backup_settings": "ZÃĄlohovÃĄní databÃĄze", "backup_settings_description": "SprÃĄva nastavení vÃŊpisu databÃĄze.", "cleared_jobs": "HotovÊ Ãēlohy pro: {job}", "config_set_by_file": "Konfigurace je aktuÃĄlně provÃĄděna konfiguračním souborem", @@ -112,7 +124,14 @@ "logging_enable_description": "Povolit protokolovÃĄní", "logging_level_description": "KdyÅž je povoleno, jakou Ãēroveň protokolu pouŞít.", "logging_settings": "ProtokolovÃĄní", - "machine_learning_clip_model": "CLIP model", + "machine_learning_availability_checks": "Kontroly dostupnosti", + "machine_learning_availability_checks_description": "Automaticky zvolit a preferovat dostupnÊ servery strojovÊho učení", + "machine_learning_availability_checks_enabled": "Povolit kontroly dostupnosti", + "machine_learning_availability_checks_interval": "Interval kontrol", + "machine_learning_availability_checks_interval_description": "Interval v milisekundÃĄch mezi kontrolami dostupnosti", + "machine_learning_availability_checks_timeout": "VyprÅĄení poÅžadavku", + "machine_learning_availability_checks_timeout_description": "ČasovÊ vyprÅĄení poÅžadavku v milisekundÃĄch u kontrol dostupnosti", + "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "NÃĄzev CLIP modelu je uvedenÃŊ zde. Pamatujte, Åže při změně modelu je nutnÊ znovu spustit Ãēlohu 'ChytrÊ vyhledÃĄvÃĄní' pro vÅĄechny obrÃĄzky.", "machine_learning_duplicate_detection": "Kontrola duplicit", "machine_learning_duplicate_detection_enabled": "Povolit kontrolu duplicit", @@ -166,6 +185,20 @@ "metadata_settings_description": "SprÃĄva nastavení metadat", "migration_job": "Migrace", "migration_job_description": "Migrace miniatur snímků a obličejů do nejnovějÅĄÃ­ struktury sloÅžek", + "nightly_tasks_cluster_faces_setting_description": "Spustit rozpoznÃĄvÃĄní obličeje na nově nalezenÃŊch obličejích", + "nightly_tasks_cluster_new_faces_setting": "Seskupit novÊ tvÃĄÅ™e", + "nightly_tasks_database_cleanup_setting": "Úlohy čiÅĄtění databÃĄze", + "nightly_tasks_database_cleanup_setting_description": "Vyčistit databÃĄzi od starÃŊch dat, jejichÅž platnost vyprÅĄela", + "nightly_tasks_generate_memories_setting": "VytvÃĄÅ™ení vzpomínek", + "nightly_tasks_generate_memories_setting_description": "VytvÃĄÅ™ení novÃŊch vzpomínek z poloÅžek", + "nightly_tasks_missing_thumbnails_setting": "Generovat chybějící miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Řadit poloÅžky bez miniatur do fronty pro generovÃĄní miniatur", + "nightly_tasks_settings": "Noční Ãēlohy", + "nightly_tasks_settings_description": "SprÃĄva nočních Ãēkolů", + "nightly_tasks_start_time_setting": "Čas zahÃĄjení", + "nightly_tasks_start_time_setting_description": "Čas, kdy server spustí noční Ãēlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizace vyuÅžití kvÃŗty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovat kvÃŗtu ÃēloÅžiÅĄtě uÅživatele na zÃĄkladě aktuÃĄlního vyuÅžití", "no_paths_added": "Nebyly přidÃĄny ÅžÃĄdnÊ cesty", "no_pattern_added": "Nebyl přidÃĄn ÅžÃĄdnÃŊ vzor", "note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Å títku ÃēloÅžiÅĄtě na dříve nahranÊ poloÅžky spusÅĨte", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobilní přesměrovÃĄní URI", "oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrovÃĄní URI", "oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''", + "oauth_role_claim": "Deklarace Role", + "oauth_role_claim_description": "Automaticky udělit přístup sprÃĄvce na zÃĄkladě přítomnosti tÊto deklarace. Deklarace můŞe mít hodnotu 'user' nebo 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "SprÃĄva nastavení OAuth přihlÃĄÅĄení", "oauth_settings_more_details": "DalÅĄÃ­ podrobnosti o tÊto funkci naleznete v dokumentaci.", @@ -230,7 +265,7 @@ "server_settings_description": "SprÃĄva nastavení serveru", "server_welcome_message": "Uvítací zprÃĄva", "server_welcome_message_description": "ZprÃĄva, kterÃĄ se zobrazí na přihlaÅĄovací strÃĄnce.", - "sidecar_job": "Sidecar metadata", + "sidecar_job": "Postranní metadata", "sidecar_job_description": "ObjevovÃĄní nebo synchronizace sidecar metadat ze systÊmu souborů", "slideshow_duration_description": "Počet sekund pro zobrazení kaÅždÊho obrÃĄzku", "smart_search_job_description": "StrojovÊ učení na objektech pro podporu inteligentního vyhledÃĄvÃĄní", @@ -305,7 +340,7 @@ "transcoding_policy_description": "Nastavte po překÃŗdovÃĄní videa", "transcoding_preferred_hardware_device": "PreferovanÊ hardwarovÊ zařízení", "transcoding_preferred_hardware_device_description": "Platí pouze pro VAAPI a QSV. Nastaví dri uzel pouÅžitÃŊ pro hardwarovÊ překÃŗdovÃĄní.", - "transcoding_preset_preset": "Preset (-preset)", + "transcoding_preset_preset": "Předvolba (-preset)", "transcoding_preset_preset_description": "Rychlost komprese. PomalejÅĄÃ­ předvolby vytvÃĄÅ™ejí menÅĄÃ­ soubory a zvyÅĄují kvalitu při dosaÅžení určitÊho datovÊho toku. VP9 ignoruje rychlosti vyÅĄÅĄÃ­ neÅž 'faster'.", "transcoding_reference_frames": "Referenční snímky", "transcoding_reference_frames_description": "Počet referenčních snímků při kompresi danÊho snímku. VyÅĄÅĄÃ­ hodnoty zvyÅĄují Ãēčinnost komprese, ale zpomalují kÃŗdovÃĄní. Hodnota 0 toto nastavuje automaticky.", @@ -314,11 +349,11 @@ "transcoding_settings_description": "SprÃĄva rozliÅĄení a kÃŗdovÃĄní videosouborů", "transcoding_target_resolution": "CílovÊ rozliÅĄení", "transcoding_target_resolution_description": "VyÅĄÅĄÃ­ rozliÅĄení mohou zachovat více detailů, ale jejich kÃŗdovÃĄní trvÃĄ dÊle, mají větÅĄÃ­ velikost souboru a mohou sníŞit odezvu aplikace.", - "transcoding_temporal_aq": "Temporal AQ", + "transcoding_temporal_aq": "ČasovÊ AQ", "transcoding_temporal_aq_description": "Platí pouze pro NVENC. ZvyÅĄuje kvalitu scÊn s vysokÃŊm počtem detailů a malÃŊm počtem pohybů. Nemusí bÃŊt kompatibilní se starÅĄÃ­mi zařízeními.", "transcoding_threads": "VlÃĄkna", "transcoding_threads_description": "VyÅĄÅĄÃ­ hodnoty vedou k rychlejÅĄÃ­mu kÃŗdovÃĄní, ale ponechÃĄvají serveru mÊně prostoru pro zpracovÃĄní jinÃŊch Ãēloh. Tato hodnota by neměla bÃŊt vyÅĄÅĄÃ­ neÅž počet jader procesoru. Maximalizuje vyuÅžití, pokud je nastavena na 0.", - "transcoding_tone_mapping": "Tone-mapping", + "transcoding_tone_mapping": "MapovÃĄní tÃŗnů", "transcoding_tone_mapping_description": "SnaŞí se zachovat vzhled videí HDR při převodu na SDR. KaÅždÃŊ algoritmus dělÃĄ různÊ kompromisy v oblasti barev, detailů a jasu. Hable zachovÃĄvÃĄ detaily, Mobius zachovÃĄvÃĄ barvy a Reinhard zachovÃĄvÃĄ jas.", "transcoding_transcode_policy": "ZÃĄsady překÃŗdovÃĄní", "transcoding_transcode_policy_description": "ZÃĄsady, kdy mÃĄ bÃŊt video překÃŗdovÃĄno. Videa HDR budou překÃŗdovÃĄna vÅždy (kromě případů, kdy je překÃŗdovÃĄní zakÃĄzÃĄno).", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Počet dní, po kterÊ je třeba poloÅžku ponechat v koÅĄi, neÅž bude trvale odstraněna", "trash_settings": "KoÅĄ", "trash_settings_description": "SprÃĄva nastavení koÅĄe", + "unlink_all_oauth_accounts": "Odpojit vÅĄechny Ãēčty OAuth", + "unlink_all_oauth_accounts_description": "Nezapomeňte odpojit vÅĄechny OAuth Ãēčty před přechodem k novÊmu poskytovateli.", + "unlink_all_oauth_accounts_prompt": "Opravdu chcete odpojit vÅĄechny Ãēčty OAuth? Tím se resetuje ID OAuth pro kaÅždÊho uÅživatele a tento Ãēkon nelze vrÃĄtit zpět.", "user_cleanup_job": "PromazÃĄní uÅživatelů", "user_delete_delay": "Účet a poloÅžky uÅživatele {user} budou trvale smazÃĄny za {delay, plural, one {# den} few {# dny} other {# dní}}.", "user_delete_delay_settings": "OdloÅžení odstranění", @@ -360,10 +398,12 @@ "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í: {level}", - "advanced_settings_prefer_remote_subtitle": "U některÃŊch zařízení je načítÃĄní miniatur z prostředků v zařízení velmi pomalÊ. Aktivujte toto nastavení, aby se místo toho načítaly vzdÃĄlenÊ obrÃĄzky.", + "advanced_settings_prefer_remote_subtitle": "U některÃŊch zařízení je načítÃĄní miniatur z lokÃĄlních prostředků velmi pomalÊ. Aktivujte toto nastavení, aby se místo toho načítaly vzdÃĄlenÊ obrÃĄzky.", "advanced_settings_prefer_remote_title": "Preferovat vzdÃĄlenÊ obrÃĄzky", "advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, kterÊ by měl Immich odesílat s kaÅždÃŊm síÅĨovÃŊm poÅžadavkem", "advanced_settings_proxy_headers_title": "Proxy hlavičky", + "advanced_settings_readonly_mode_subtitle": "Povoluje reÅžim pouze pro čtení, ve kterÊm lze fotografie pouze prohlíŞet, ale funkce jako vÃŊběr více obrÃĄzků, sdílení, přenos, mazÃĄní jsou zakÃĄzÃĄny. Povolení/zakÃĄzÃĄní reÅžimu pouze pro čtení pomocí avatara uÅživatele na hlavní obrazovce", + "advanced_settings_readonly_mode_title": "ReÅžim pouze pro čtení", "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", @@ -379,6 +419,7 @@ "album_cover_updated": "Obal alba aktualizovÃĄn", "album_delete_confirmation": "Opravdu chcete album {album} odstranit?", "album_delete_confirmation_description": "Pokud je toto album sdíleno, ostatní uÅživatelÊ k němu jiÅž nebudou mít přístup.", + "album_deleted": "Album smazÃĄno", "album_info_card_backup_album_excluded": "VYLOUČENO", "album_info_card_backup_album_included": "ZAHRNUTO", "album_info_updated": "Informace o albu aktualizovÃĄny", @@ -388,7 +429,9 @@ "album_options": "MoÅžnosti alba", "album_remove_user": "Odebrat uÅživatele?", "album_remove_user_confirmation": "Opravdu chcete odebrat uÅživatele {user}?", + "album_search_not_found": "Nebyla nalezena ÅžÃĄdnÃĄ alba odpovídající vaÅĄemu hledÃĄní", "album_share_no_users": "Zřejmě jste toto album sdíleli se vÅĄemi uÅživateli, nebo nemÃĄte ÅžÃĄdnÊho uÅživatele, se kterÃŊm byste ho mohli sdílet.", + "album_summary": "Souhrn alba", "album_updated": "Album aktualizovÃĄno", "album_updated_setting_description": "DostÃĄvat e-mailovÃĄ oznÃĄmení o novÃŊch poloÅžkÃĄch sdílenÊho alba", "album_user_left": "Opustil {album}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "VÃŊchozí řazení alb", "albums_default_sort_order_description": "VÃŊchozí řazení poloÅžek při vytvÃĄÅ™ení novÃŊch alb.", "albums_feature_description": "Sbírky poloÅžek, kterÊ lze sdílet s ostatními uÅživateli.", + "albums_on_device_count": "Alba v zařízení ({count})", "all": "VÅĄe", "all_albums": "VÅĄechna alba", "all_people": "VÅĄichni lidÊ", @@ -426,8 +470,10 @@ "app_bar_signout_dialog_title": "OdhlÃĄsit se", "app_settings": "Aplikace", "appears_in": "Vyskytuje se v", + "apply_count": "PouŞít ({count, number})", "archive": "Archiv", - "archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku", + "archive_action_prompt": "{count} přidanÃŊch do archivu", + "archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu", "archive_page_no_archived_assets": "Nebyla nalezena ÅžÃĄdnÃĄ archivovanÃĄ mÊdia", "archive_page_title": "Archiv ({count})", "archive_size": "Velikost archivu", @@ -453,10 +499,12 @@ "asset_list_settings_subtitle": "Nastavení rozloÅžení mříŞky fotografií", "asset_list_settings_title": "MříŞka fotografií", "asset_offline": "Offline poloÅžka", - "asset_offline_description": "Toto externí poloÅžka se jiÅž na disku nenachÃĄzí. ObraÅĨte se na Immich sprÃĄvce a poÅžÃĄdejte o pomoc.", + "asset_offline_description": "Toto externí poloÅžka se jiÅž na disku nenachÃĄzí. ObraÅĨte se na sprÃĄvce Immich a poÅžÃĄdejte o pomoc.", "asset_restored_successfully": "PoloÅžka ÃēspÄ›ÅĄně obnovena", "asset_skipped": "Přeskočeno", "asset_skipped_in_trash": "V koÅĄi", + "asset_trashed": "PoloÅžka vyhozena", + "asset_troubleshoot": "ŘeÅĄení problÊmů s poloÅžkami", "asset_uploaded": "NahrÃĄno", "asset_uploading": "NahrÃĄvÃĄníâ€Ļ", "asset_viewer_settings_subtitle": "SprÃĄva nastavení prohlíŞeče galerie", @@ -464,14 +512,15 @@ "assets": "PoloÅžky", "assets_added_count": "{count, plural, one {PřidÃĄna # poloÅžka} few {PřidÃĄny # poloÅžky} other {PřidÃĄno # poloÅžek}}", "assets_added_to_album_count": "Do alba {count, plural, one {byla přidÃĄna # poloÅžka} few {byly přidÃĄny # poloÅžky} other {bylo přidÃĄno # poloÅžek}}", - "assets_added_to_name_count": "{count, plural, one {PřidÃĄna # poloÅžka} few {PřidÃĄny # poloÅžky} other {PřidÃĄno # poloÅžek}} do {hasName, select, true {alba {name}} other {novÊho alba}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {PřidÃĄna # poloÅžka} few{PřidÃĄny # poloÅžky} other {PřidÃĄno # poloÅžek}} do {albumTotal, plural, one {# alba} other {# alb}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {PoloÅžku} other {PoloÅžky}} nelze přidat do alba", + "assets_cannot_be_added_to_albums": "{count, plural, one {PoloÅžku} other {PoloÅžky}} nelze přidat do ÅžÃĄdnÊho z alb", "assets_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžek}}", "assets_deleted_permanently": "{count} poloÅžek trvale odstraněno", "assets_deleted_permanently_from_server": "{count} poloÅžek trvale odstraněno z Immich serveru", "assets_downloaded_failed": "{count, plural, one {StaÅžen # soubor - {error} souborů selhalo} few {StaÅženy # soubory - {error} souborů selhalo} other {StaÅženo # souborů - {error} souborů selhalo}}", "assets_downloaded_successfully": "{count, plural, one {ÚspÄ›ÅĄně staÅžen # soubor} few {ÚspÄ›ÅĄně staÅženy # soubory} other {ÚspÄ›ÅĄně staÅženo # souborů}}", - "assets_moved_to_trash_count": "Do koÅĄe {count, plural, one {přesunuta # poloÅžka} few {přesunuty # poloÅžky} other {přesunuto # poloÅžek}}", + "assets_moved_to_trash_count": "{count, plural, one {# poloÅžka přesunuta} few {# poloÅžky přesunuty} other {# poloÅžek přesunuto}} do koÅĄe", "assets_permanently_deleted_count": "Trvale {count, plural, one {smazÃĄna # poloÅžka} few {smazÃĄny # poloÅžky} other {smazÃĄno # poloÅžek}}", "assets_removed_count": "{count, plural, one {Odstraněna # poloÅžka} few {Odstraněny # poloÅžky} other {Odstraněno # poloÅžek}}", "assets_removed_permanently_from_device": "{count} poloÅžek trvale odstraněno z vaÅĄeho zařízení", @@ -482,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {Vyhozena # poloÅžka} few {Vyhozeny # poloÅžky} other {Vyhozeno # poloÅžek}}", "assets_trashed_from_server": "{count} poloÅžek vyhozeno do koÅĄe na Immich serveru", "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka byla} other {PoloÅžky byly}} souÄÃĄstí alba", + "assets_were_part_of_albums_count": "{count, plural, one {PoloÅžka jiÅž byla} other {PoloÅžky jiÅž byly}} souÄÃĄstí alb", "authorized_devices": "AutorizovanÃĄ zařízení", "automatic_endpoint_switching_subtitle": "Připojit se místně přes určenou Wi-Fi, pokud je k dispozici, a pouŞívat alternativní připojení jinde", "automatic_endpoint_switching_title": "AutomatickÊ přepínÃĄní URL", "autoplay_slideshow": "AutomatickÊ přehrÃĄvÃĄní prezentace", "back": "Zpět", "back_close_deselect": "Zpět, zavřít nebo zruÅĄit vÃŊběr", + "background_backup_running_error": "PrÃĄvě probíhÃĄ zÃĄlohovÃĄní na pozadí, nelze spustit ruční zÃĄlohovÃĄní", "background_location_permission": "Povolení polohy na pozadí", "background_location_permission_content": "Aby bylo moÅžnÊ přepínat sítě při běhu na pozadí, musí mít Immich *vÅždy* přístup k přesnÊ poloze, aby mohl zjistit nÃĄzev Wi-Fi sítě", + "background_options": "MoÅžnosti běhu na pozadí", + "backup": "ZÃĄloha", "backup_album_selection_page_albums_device": "Alba v zařízení ({count})", "backup_album_selection_page_albums_tap": "Klepnutím na poloÅžku ji zahrnete, opětovnÃŊm klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "PoloÅžky mohou bÃŊt roztrouÅĄeny ve více albech. To umoŞňuje zahrnout nebo vyloučit alba během procesu zÃĄlohovÃĄní.", "backup_album_selection_page_select_albums": "VybranÃĄ alba", "backup_album_selection_page_selection_info": "Informace o vÃŊběru", "backup_album_selection_page_total_assets": "CelkovÃŊ počet jedinečnÃŊch poloÅžek", + "backup_albums_sync": "Synchronizace zÃĄlohovanÃŊch alb", "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â€Ļ", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Povolit zÃĄlohovÃĄní na popředí", "backup_controller_page_uploading_file_info": "Informace o nahranÊm souboru", "backup_err_only_album": "Nelze odstranit jedinÊ vybranÊ album", + "backup_error_sync_failed": "Synchronizace selhala. Nelze zpracovat zÃĄlohu.", "backup_info_card_assets": "poloÅžek", "backup_manual_cancelled": "ZruÅĄeno", "backup_manual_in_progress": "NahrÃĄvÃĄní jiÅž probíhÃĄ. Zkuste to znovu později", "backup_manual_success": "Úspěch", "backup_manual_title": "Stav nahrÃĄvÃĄní", + "backup_options": "MoÅžnosti zÃĄlohovÃĄní", "backup_options_page_title": "Nastavení zÃĄloh", "backup_setting_subtitle": "SprÃĄva nastavení zÃĄlohovÃĄní na pozadí a na popředí", + "backup_settings_subtitle": "SprÃĄva nastavení nahrÃĄvÃĄní", "backward": "PozpÃĄtku", "biometric_auth_enabled": "BiometrickÊ ověřovÃĄní je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrickÊho ověřovÃĄní", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "Vymazat vyrovnÃĄvací paměÅĨ", "cache_settings_clear_cache_button_title": "VymaÅže vyrovnÃĄvací paměÅĨ aplikace. To vÃŊrazně ovlivní vÃŊkon aplikace, dokud se vyrovnÃĄvací paměÅĨ neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", - "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, kterÊ aplikace zařadila na černou listinu", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, kterÊ aplikace ignoruje", "cache_settings_duplicated_assets_title": "Duplicitní poloÅžky ({count})", "cache_settings_statistics_album": "Knihovna nÃĄhledů", "cache_settings_statistics_full": "Kompletní fotografie", @@ -587,6 +644,7 @@ "cancel": "ZruÅĄit", "cancel_search": "ZruÅĄit vyhledÃĄvÃĄní", "canceled": "ZruÅĄeno", + "canceling": "RuÅĄení", "cannot_merge_people": "Nelze sloučit osoby", "cannot_undo_this_action": "Tuto akci nelze vrÃĄtit zpět!", "cannot_update_the_description": "Nelze aktualizovat popis", @@ -609,6 +667,8 @@ "change_pin_code": "Změnit PIN kÃŗd", "change_your_password": "Změna vaÅĄeho hesla", "changed_visibility_successfully": "Změna viditelnosti proběhla ÃēspÄ›ÅĄně", + "charging": "Nabíjení", + "charging_requirement_mobile_backup": "ZÃĄlohovÃĄní na pozadí vyÅžaduje, aby bylo zařízení nabíjeno", "check_corrupt_asset_backup": "Kontrola poÅĄkozenÃŊch zÃĄloh poloÅžek", "check_corrupt_asset_backup_button": "ProvÊst kontrolu", "check_corrupt_asset_backup_description": "Tuto kontrolu provÃĄdějte pouze přes Wi-Fi a po zÃĄlohovÃĄní vÅĄech prostředků. Takto operace můŞe trvat několik minut.", @@ -618,6 +678,7 @@ "clear": "Vymazat", "clear_all": "Vymazat vÅĄe", "clear_all_recent_searches": "Vymazat vÅĄechna nedÃĄvnÃĄ vyhledÃĄvÃĄní", + "clear_file_cache": "Vymazat mezipaměÅĨ souborů", "clear_message": "Vymazat zprÃĄvu", "clear_value": "Vymazat hodnotu", "client_cert_dialog_msg_confirm": "OK", @@ -688,11 +749,13 @@ "create_new_user": "Vytvořit novÊho uÅživatele", "create_shared_album_page_share_add_assets": "PŘIDAT POLOÅŊKY", "create_shared_album_page_share_select_photos": "Vybrat fotografie", + "create_shared_link": "Vytvořit sdílenÃŊ odkaz", "create_tag": "Vytvořit značku", "create_tag_description": "Vytvoření novÊ značky. U vnořenÃŊch značek zadejte celou cestu ke značce včetně dopřednÃŊch lomítek.", "create_user": "Vytvořit uÅživatele", "created": "Vytvořeno", "created_at": "Vytvořeno", + "creating_linked_albums": "VytvÃĄÅ™ení propojenÃŊch alb...", "crop": "Oříznout", "curated_object_page_title": "Věci", "current_device": "SoučasnÊ zařízení", @@ -700,10 +763,11 @@ "current_server_address": "AktuÃĄlní adresa serveru", "custom_locale": "Vlastní lokalizace", "custom_locale_description": "FormÃĄtovat datumy a čísla podle jazyka a oblasti", + "custom_url": "Vlastní URL", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "TmavÃŊ", - "darkTheme": "Přepnout tmavÃŊ motiv", + "dark_theme": "Přepnout tmavÃŊ motiv", "date_after": "Datum po", "date_and_time": "Datum a čas", "date_before": "Datum před", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Datum narození ÃēspÄ›ÅĄně uloÅženo", "date_range": "Rozsah dat", "day": "Den", + "days": "Dnů", "deduplicate_all": "Odstranit vÅĄechny duplicity", "deduplication_criteria_1": "Velikost obrÃĄzku v bajtech", "deduplication_criteria_2": "Počet EXIF dat", @@ -719,6 +784,8 @@ "default_locale": "VÃŊchozí jazyk", "default_locale_description": "FormÃĄtovat datumy a čísla podle místního prostředí prohlíŞeče", "delete": "Smazat", + "delete_action_confirmation_message": "Opravdu chcete odstranit tuto poloÅžku? Tato akce přesune poloÅžku do serverovÊho koÅĄe a zeptÃĄ se vÃĄs, zda ji chcete odstranit lokÃĄlně", + "delete_action_prompt": "{count} smazÃĄno", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto poloÅžky budou trvale smazÃĄny z aplikace Immich i z vaÅĄeho zařízení", @@ -732,9 +799,12 @@ "delete_key": "Smazat klíč", "delete_library": "Smazat knihovnu", "delete_link": "Smazat odkaz", + "delete_local_action_prompt": "{count} smazÃĄno lokÃĄlně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zÃĄlohovanÊ", "delete_local_dialog_ok_force": "Přesto smazat", - "delete_others": "Odstranit ostatní", + "delete_others": "Smazat ostatní", + "delete_permanently": "Trvale smazat", + "delete_permanently_action_prompt": "{count} trvale smazÃĄno", "delete_shared_link": "Smazat sdílenÃŊ odkaz", "delete_shared_link_dialog_title": "Odstranit sdílenÃŊ odkaz", "delete_tag": "Smazat značku", @@ -745,6 +815,7 @@ "description": "Popis", "description_input_hint_text": "Přidat popis...", "description_input_submit_error": "Chyba aktualizace popisu, dalÅĄÃ­ podrobnosti najdete v logu", + "deselect_all": "ZruÅĄit vÃŊběr vÅĄech", "details": "Podrobnosti", "direction": "Směr", "disabled": "ZakÃĄzÃĄno", @@ -762,6 +833,7 @@ "documentation": "Dokumentace", "done": "Hotovo", "download": "StÃĄhnout", + "download_action_prompt": "StahovÃĄní {count} poloÅžek", "download_canceled": "StahovÃĄní zruÅĄeno", "download_complete": "StahovÃĄní kompletní", "download_enqueue": "StahovÃĄní ve frontě", @@ -788,8 +860,12 @@ "edit": "Upravit", "edit_album": "Upravit album", "edit_avatar": "Upravit avatar", + "edit_birthday": "Upravit datum narození", "edit_date": "Upravit datum", "edit_date_and_time": "Upravit datum a čas", + "edit_date_and_time_action_prompt": "{count} časovÃŊch Ãēdajů upraveno", + "edit_date_and_time_by_offset": "Posunout datum", + "edit_date_and_time_by_offset_interval": "NovÃŊ rozsah dat: {from} – {to}", "edit_description": "Upravit popis", "edit_description_prompt": "Vyberte novÃŊ popis:", "edit_exclusion_pattern": "Upravit vzor vyloučení", @@ -799,6 +875,7 @@ "edit_key": "Upravit klíč", "edit_link": "Upravit odkaz", "edit_location": "Upravit polohu", + "edit_location_action_prompt": "{count} upravenÃŊch poloh", "edit_location_dialog_title": "Poloha", "edit_name": "Upravit jmÊno", "edit_people": "Upravit lidi", @@ -817,6 +894,7 @@ "empty_trash": "VyprÃĄzdnit koÅĄ", "empty_trash_confirmation": "Opravdu chcete vysypat koÅĄ? Tím se z Immiche trvale odstraní vÅĄechny poloÅžky v koÅĄi.\nTuto akci nelze vrÃĄtit zpět!", "enable": "Povolit", + "enable_backup": "Povolit zÃĄlohovÃĄní", "enable_biometric_auth_description": "Zadejte vÃĄÅĄ PIN kÃŗd pro povolení biometrickÊho ověřovÃĄní", "enabled": "Povoleno", "end_date": "KonečnÊ datum", @@ -827,7 +905,9 @@ "error": "Chyba", "error_change_sort_album": "Nepodařilo se změnit pořadí alba", "error_delete_face": "Chyba při odstraňovÃĄní obličeje z poloÅžky", + "error_getting_places": "Chyba při zjiÅĄÅĨovÃĄní míst", "error_loading_image": "Chyba při načítÃĄní obrÃĄzku", + "error_loading_partners": "Chyba při načítÃĄní partnerů: {error}", "error_saving_image": "Chyba: {error}", "error_tag_face_bounding_box": "Chyba při označovÃĄní obličeje - nelze získat souřadnice ohraničujícího rÃĄmečku", "error_title": "Chyba - Něco se pokazilo", @@ -860,14 +940,16 @@ "failed_to_load_notifications": "Nepodařilo se načíst oznÃĄmení", "failed_to_load_people": "Chyba načítÃĄní osob", "failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu", + "failed_to_reset_pin_code": "Nepodařilo se resetovat PIN kÃŗd", "failed_to_stack_assets": "Nepodařilo se seskupit poloÅžky", - "failed_to_unstack_assets": "Nepodařilo se rozloÅžit poloÅžky", + "failed_to_unstack_assets": "Nepodařilo se zruÅĄit seskupení poloÅžek", "failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznÃĄmení", "import_path_already_exists": "Tato cesta importu jiÅž existuje.", "incorrect_email_or_password": "NesprÃĄvnÃŊ e-mail nebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta neproÅĄla} few {# cesty neproÅĄly} other {# cest neproÅĄlo}} kontrolou", "profile_picture_transparent_pixels": "ProfilovÊ obrÃĄzky nemohou mít průhlednÊ pixely. ObrÃĄzek si prosím zvětÅĄete nebo posuňte.", "quota_higher_than_disk_size": "Nastavili jste kvÃŗtu vyÅĄÅĄÃ­, neÅž je velikost disku", + "something_went_wrong": "Něco se pokazilo", "unable_to_add_album_users": "Nelze přidat uÅživatele do alba", "unable_to_add_assets_to_shared_link": "Nelze přidat poloÅžky do sdílenÊho odkazu", "unable_to_add_comment": "Nelze přidat komentÃĄÅ™", @@ -876,7 +958,7 @@ "unable_to_add_partners": "Nelze přidat partnery", "unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit poloÅžku z} other {přidat poloÅžku do}} archivu", "unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit poloÅžku} other {zruÅĄit oblíbení poloÅžky}}", - "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}", + "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odebrat z archivu}}", "unable_to_change_album_user_role": "Nelze změnit roli uÅživatele alba", "unable_to_change_date": "Nelze změnit datum", "unable_to_change_description": "Nelze změnit popis", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Přidat popis...", + "exif_bottom_sheet_description_error": "Chyba při aktualizaci popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "LIDÉ", "exif_bottom_sheet_person_add_person": "Přidat jmÊno", - "exif_bottom_sheet_person_age_months": "{months} měsíců", - "exif_bottom_sheet_person_age_year_months": "1 rok a {months} měsíců", - "exif_bottom_sheet_person_age_years": "{years} let", "exit_slideshow": "Ukončit prezentaci", "expand_all": "Rozbalit vÅĄe", "experimental_settings_new_asset_list_subtitle": "ZpracovÃĄvÃĄm", @@ -973,6 +1053,8 @@ "explorer": "Průzkumník", "export": "Export", "export_as_json": "Exportovat jako JSON", + "export_database": "Exportovat databÃĄzi", + "export_database_description": "Exportovat databÃĄzi SQLite", "extension": "Přípona", "external": "Externí", "external_libraries": "Externí knihovny", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Nepodařilo se načíst poloÅžky", "failed_to_load_folder": "Nepodařilo se načíst sloÅžku", "favorite": "Oblíbit", + "favorite_action_prompt": "{count} přidÃĄno do OblíbenÃŊch", "favorite_or_unfavorite_photo": "Oblíbit nebo zruÅĄit oblíbení fotky", "favorites": "OblíbenÊ", "favorites_page_no_favorites": "Nebyla nalezena ÅžÃĄdnÃĄ oblíbenÃĄ mÊdia", "feature_photo_updated": "Hlavní fotka aktualizovÃĄna", "features": "Funkce", + "features_in_development": "Funkce ve vÃŊvoji", "features_setting_description": "SprÃĄva funkcí aplikace", "file_name": "NÃĄzev souboru", "file_name_or_extension": "NÃĄzev nebo přípona souboru", @@ -998,21 +1082,26 @@ "filter_people": "Filtrovat lidi", "filter_places": "Filtrovat místa", "find_them_fast": "Najděte je rychle vyhledÃĄním jejich jmÊna", + "first": "První", "fix_incorrect_match": "Opravit nesprÃĄvnou shodu", "folder": "SloÅžka", "folder_not_found": "SloÅžka nebyla nalezena", "folders": "SloÅžky", "folders_feature_description": "ProchÃĄzení zobrazení sloÅžek s fotografiemi a videi v souborovÊm systÊmu", + "forgot_pin_code_question": "Zapomněli jste PIN?", "forward": "Dopředu", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Tato funkce načítÃĄ externí zdroje z Googlu, aby mohla fungovat.", "general": "ObecnÊ", + "geolocation_instruction_location": "Klikněte na poloÅžku s GPS souřadnicemi, abyste mohli pouŞít její polohu, nebo vyberte polohu přímo z mapy", "get_help": "Získat pomoc", "get_wifiname_error": "Nepodařilo se získat nÃĄzev Wi-Fi. Zkontrolujte, zda jste udělili potřebnÃĄ oprÃĄvnění a zda jste připojeni k Wi-Fi síti", "getting_started": "ZačínÃĄme", "go_back": "Přejít zpět", "go_to_folder": "Přejít do sloÅžky", "go_to_search": "Přejít na vyhledÃĄvÃĄní", + "gps": "GPS", + "gps_missing": "Bez GPS", "grant_permission": "Udělit oprÃĄvnění", "group_albums_by": "Seskupit alba podle...", "group_country": "Seskupit podle země", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu", "haptic_feedback_title": "DotykovÃĄ zpětnÃĄ vazba", "has_quota": "MÃĄ kvÃŗtu", + "hash_asset": "Hash poloÅžky", + "hashed_assets": "HashovanÊ poloÅžky", + "hashing": "HashovÃĄní", "header_settings_add_header_tip": "Přidat hlavičku", "header_settings_field_validator_msg": "Hodnota nemůŞe bÃŊt prÃĄzdnÃĄ", "header_settings_header_name_input": "NÃĄzev hlavičky", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "Lze nahrÃĄt nejvÃŊÅĄe 30 poloÅžek najednou, přeskakuji", "host": "Hostitel", "hour": "Hodina", + "hours": "Hodin", "id": "ID", + "idle": "Nečinnost", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uloÅženÊ na iCloudu se nebudou nahrÃĄvat na Immich server", "image": "ObrÃĄzek", @@ -1112,10 +1206,13 @@ "language_no_results_title": "Nebyly nalezeny ÅžÃĄdnÊ jazyky", "language_search_hint": "Vyhledat jazyk...", "language_setting_description": "Vyberte upřednostňovanÃŊ jazyk", + "large_files": "VelkÊ soubory", + "last": "Poslední", "last_seen": "Naposledy viděno", "latest_version": "NejnovějÅĄÃ­ verze", "latitude": "ZeměpisnÃĄ ÅĄÃ­Å™ka", "leave": "Opustit", + "leave_album": "Opustit album", "lens_model": "Model objektivu", "let_others_respond": "Nechte ostatní reagovat", "level": "Úroveň", @@ -1127,27 +1224,32 @@ "library_page_sort_created": "Naposledy vytvořenÊ", "library_page_sort_last_modified": "Naposledy upraveno", "library_page_sort_title": "Podle nÃĄzvu alba", + "licenses": "Licence", "light": "SvětlÃŊ", - "like_deleted": "Lajk smazÃĄn", + "like": "Líbí se mi", + "like_deleted": "Oblíbení smazÃĄno", "link_motion_video": "Připojit pohyblivÊ video", - "link_options": "MoÅžnosti odkazu", "link_to_oauth": "Propojit s OAuth", "linked_oauth_account": "PropojenÃŊ OAuth Ãēčet", "list": "Seznam", "loading": "NačítÃĄní", "loading_search_results_failed": "NačítÃĄní vÃŊsledků vyhledÃĄvÃĄní se nezdařilo", + "local": "Místní", "local_asset_cast_failed": "Nelze odeslat poloÅžku, kterÃĄ není nahranÃĄ na serveru", + "local_assets": "Místní poloÅžky", + "local_media_summary": "Souhrn místních mÊdií", "local_network": "Místní síÅĨ", "local_network_sheet_info": "Aplikace se při pouÅžití zadanÊ sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "OprÃĄvnění polohy", "location_permission_content": "Aby bylo moÅžnÊ pouŞívat funkci automatickÊho přepínÃĄní, potřebuje Immich oprÃĄvnění k přesnÊ poloze, aby mohl přečíst nÃĄzev aktuÃĄlní sítě Wi-Fi", - "location_picker_choose_on_map": "Vyberte na mapě", + "location_picker_choose_on_map": "Vybrat na mapě", "location_picker_latitude_error": "Zadejte platnou zeměpisnou ÅĄÃ­Å™ku", "location_picker_latitude_hint": "Zadejte vlastní zeměpisnou ÅĄÃ­Å™ku", "location_picker_longitude_error": "Zadejte platnou zeměpisnou dÊlku", "location_picker_longitude_hint": "Zadejte vlastní zeměpisnou dÊlku", "lock": "Zamknout", "locked_folder": "UzamčenÃĄ sloÅžka", + "log_detail_title": "Podrobnosti protokolu", "log_out": "OdhlÃĄsit", "log_out_all_devices": "OdhlÃĄsit vÅĄechna zařízení", "logged_in_as": "PřihlÃĄÅĄen jako {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Heslo bylo ÃēspÄ›ÅĄně aktualizovÃĄno", "logout_all_device_confirmation": "Opravdu chcete odhlÃĄsit vÅĄechna zařízení?", "logout_this_device_confirmation": "Opravdu chcete odhlÃĄsit toto zařízení?", + "logs": "Protokoly", "longitude": "ZeměpisnÃĄ dÊlka", "look": "Zobrazení", "loop_videos": "Videa ve smyčce", @@ -1185,6 +1288,7 @@ "main_branch_warning": "PouŞívÃĄte vÃŊvojovou verzi; důrazně doporučujeme pouŞívat verzi z vydÃĄní!", "main_menu": "Hlavní nabídka", "make": "VÃŊrobce", + "manage_geolocation": "Spravovat polohu", "manage_shared_links": "Spravovat sdílenÊ odkazy", "manage_sharing_with_partners": "SprÃĄva sdílení s partnery", "manage_the_app_settings": "SprÃĄva nastavení aplikace", @@ -1193,8 +1297,7 @@ "manage_your_devices": "SprÃĄva přihlÃĄÅĄenÃŊch zařízení", "manage_your_oauth_connection": "SprÃĄva OAuth propojení", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotek", + "map_assets_in_bounds": "{count, plural, =0 {ÅŊÃĄdnÃĄ fotka v tÊto oblasti} one {# fotka} few{# fotky} other {# fotek}}", "map_cannot_get_user_location": "Nelze zjistit polohu uÅživatele", "map_location_dialog_yes": "Ano", "map_location_picker_page_use_location": "PouŞít tuto polohu", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "SluÅžba určovÃĄní polohy je zakÃĄzÃĄna", "map_marker_for_images": "Značka na mapě pro snímky pořízenÊ v {city}, {country}", "map_marker_with_image": "Značka mapy s obrÃĄzkem", - "map_no_assets_in_bounds": "ÅŊÃĄdnÊ fotografie v tÊto oblasti", "map_no_location_permission_content": "OprÃĄvnění polohy je nutnÊ pro zobrazení fotek z vaÅĄÃ­ aktuÃĄlní polohy. Chcete oprÃĄvnění nyní povolit?", "map_no_location_permission_title": "OprÃĄvnění polohy zamítnuto", "map_settings": "Nastavení mapy", @@ -1221,6 +1323,7 @@ "mark_as_read": "Označit jako přečtenÊ", "marked_all_as_read": "VÅĄe označeno jako přečtenÊ", "matches": "Shody", + "matching_assets": "Odpovídající poloÅžky", "media_type": "Typ mÊdia", "memories": "Vzpomínky", "memories_all_caught_up": "To je vÅĄechno", @@ -1239,6 +1342,7 @@ "merged_people_count": "{count, plural, one {Sloučena # osoba} few {Sloučeny # osoby} other {Sloučeno # lidí}}", "minimize": "Minimalizovat", "minute": "Minuta", + "minutes": "Minut", "missing": "Chybějící", "model": "Model", "month": "Měsíc", @@ -1246,10 +1350,11 @@ "more": "Více", "move": "Přesunout", "move_off_locked_folder": "Přesunout z uzamčenÊ sloÅžky", + "move_to_lock_folder_action_prompt": "{count} přidanÃŊch do uzamčenÊ sloÅžky", "move_to_locked_folder": "Přesunout do uzamčenÊ sloÅžky", "move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze vÅĄech alb a bude je moÅžnÊ zobrazit pouze v uzamčenÊ sloÅžce", - "moved_to_archive": "{count, plural, one {Přesunuta # poloÅžka} few {Přesunuty # poloÅžky} other {Přesunuto # poloÅžek}} do archivu", - "moved_to_library": "{count, plural, one {Přesunuta # poloÅžka} few {Přesunuty # poloÅžky} other {Přesunuto # poloÅžek}} do knihovny", + "moved_to_archive": "{count, plural, one {# poloÅžka přesunuta} few {# poloÅžky přesunuty} other {# poloÅžek přesunuto}} do archivu", + "moved_to_library": "{count, plural, one {# poloÅžka přesunuta} few {# poloÅžky přesunuty} other {# poloÅžek přesunuto}} do knihovny", "moved_to_trash": "Přesunuto do koÅĄe", "multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum poloÅžek pouze pro čtení, přeskakuji", "multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu poloÅžek pouze pro čtení, přeskakuji", @@ -1257,6 +1362,10 @@ "my_albums": "Moje alba", "name": "JmÊno", "name_or_nickname": "JmÊno nebo přezdívka", + "network_requirement_photos_upload": "Pro zÃĄlohovÃĄní fotografií pouŞívat mobilní data", + "network_requirement_videos_upload": "Pro zÃĄlohovÃĄní videí pouŞívat mobilní data", + "network_requirements": "PoÅžadavky na síÅĨ", + "network_requirements_updated": "PoÅžadavky na síÅĨ se změnily, fronta zÃĄlohovÃĄní se vytvoří znovu", "networking_settings": "SíÅĨ", "networking_subtitle": "SprÃĄva nastavení koncovÊho bodu serveru", "never": "Nikdy", @@ -1266,6 +1375,7 @@ "new_person": "NovÃĄ osoba", "new_pin_code": "NovÃŊ PIN kÃŗd", "new_pin_code_subtitle": "PoprvÊ přistupujete k uzamčenÊ sloÅžce. Vytvořte si kÃŗd PIN pro bezpečnÃŊ přístup na tuto strÃĄnku", + "new_timeline": "NovÃĄ časovÃĄ osa", "new_user_created": "Vytvořen novÃŊ uÅživatel", "new_version_available": "NOVÁ VERZE K DISPOZICI", "newest_first": "NejnovějÅĄÃ­ první", @@ -1279,19 +1389,25 @@ "no_assets_message": "KLIKNĚTE PRO NAHRÁNÍ PRVNÍ FOTOGRAFIE", "no_assets_to_show": "ÅŊÃĄdnÊ poloÅžky k zobrazení", "no_cast_devices_found": "Nebyla nalezena ÅžÃĄdnÃĄ zařízení", + "no_checksum_local": "Není k dispozici kontrolní součet - nelze načíst místní poloÅžky", + "no_checksum_remote": "Není k dispozici kontrolní součet - nelze načíst vzdÃĄlenou poloÅžku", "no_duplicates_found": "Nebyly nalezeny ÅžÃĄdnÊ duplicity.", "no_exif_info_available": "Exif není k dispozici", "no_explore_results_message": "Nahrajte dalÅĄÃ­ fotografie a prozkoumejte svou sbírku.", "no_favorites_message": "Přidejte si oblíbenÊ poloÅžky a rychle najděte svÊ nejlepÅĄÃ­ obrÃĄzky a videa", "no_libraries_message": "Vytvořte si externí knihovnu pro zobrazení fotografií a videí", + "no_local_assets_found": "Nebyly nalezeny ÅžÃĄdnÊ místní poloÅžky s tímto kontrolním součtem", "no_locked_photos_message": "Fotky a videa v uzamčenÊ sloÅžce jsou skrytÊ a při prochÃĄzení nebo vyhledÃĄvÃĄní v knihovně se nezobrazují.", "no_name": "Bez jmÊna", "no_notifications": "ÅŊÃĄdnÃĄ oznÃĄmení", "no_people_found": "Nebyli nalezeni ÅžÃĄdní odpovídající lidÊ", "no_places": "ÅŊÃĄdnÃĄ místa", + "no_remote_assets_found": "Nebyly nalezeny ÅžÃĄdnÊ vzdÃĄlenÊ poloÅžky s tímto kontrolním součtem", "no_results": "ÅŊÃĄdnÊ vÃŊsledky", "no_results_description": "Zkuste pouŞít synonymum nebo obecnějÅĄÃ­ klíčovÊ slovo", "no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve svÊ síti", + "no_uploads_in_progress": "NeprobíhÃĄ ÅžÃĄdnÊ nahrÃĄvÃĄní", + "not_available": "Není k dispozici", "not_in_any_album": "Bez alba", "not_selected": "Není vybrÃĄno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li pouŞít ÅĄtítek ÃēloÅžiÅĄtě na dříve nahranÊ poloÅžky, spusÅĨte příkaz", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "OficiÃĄlní zdroje Immich", "offline": "Offline", + "offset": "Posun", "ok": "Ok", "oldest_first": "NejstarÅĄÃ­ první", "on_this_device": "V tomto zařízení", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Otevřít vyhledÃĄvací filtry", "options": "MoÅžnosti", "or": "nebo", + "organize_into_albums": "Organizovat do alb", + "organize_into_albums_description": "Umístit existující fotky do alb s pouÅžitím aktuÃĄlního nastavení synchronizace", "organize_your_library": "UspoÅ™ÃĄdejte si knihovnu", "original": "originÃĄl", "other": "Ostatní", "other_devices": "Ostatní zařízení", + "other_entities": "Ostatní entity", "other_variables": "DalÅĄÃ­ proměnnÊ", "owned": "Vlastní", "owner": "Vlastník", @@ -1369,7 +1489,7 @@ "permanent_deletion_warning_setting_description": "Zobrazit varovÃĄní při trvalÊm odstranění poloÅžek", "permanently_delete": "Trvale odstranit", "permanently_delete_assets_count": "Trvale smazat {count, plural, one {poloÅžku} other {poloÅžky}}", - "permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tuto poloÅžku} few {tyto # poloÅžky} other {těchto # poloÅžek}}? Tím {count, plural, one {ji takÊ odstraníte z jejích} other {je takÊ odstraníte z jejich}} alb.", + "permanently_delete_assets_prompt": "Opravdu chcete trvale smazat {count, plural, one {tento soubor?} other {tyto # soubory?}} Tím se takÊ odstraní {count, plural, one {z jeho} other {z jejich}} alba.", "permanently_deleted_asset": "PoloÅžka trvale odstraněna", "permanently_deleted_assets_count": "{count, plural, one {PoloÅžka trvale vymazÃĄna} other {PoloÅžky trvale vymazÃĄny}}", "permission": "OprÃĄvnění", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Přístup omezen. Chcete-li pouŞívat Immich k zÃĄlohovÃĄní a sprÃĄvě celÊ vaÅĄÃ­ kolekce galerií, povolte v nastavení přístup k fotkÃĄm a videím.", "permission_onboarding_request": "Immich potřebuje přístup k zobrazení vaÅĄich fotek a videí.", "person": "Osoba", + "person_age_months": "{months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}", + "person_age_year_months": "1 rok a {months, plural, one {# měsíc} few {# měsíce} other {# měsíců}}", + "person_age_years": "{years, plural, one {# rok} few {# roky} other {# let}}", "person_birthdate": "Narozen(a) {date}", "person_hidden": "{name}{hidden, select, true { (skryto)} other {}}", "photo_shared_all_users": "VypadÃĄ to, Åže jste fotky sdíleli se vÅĄemi uÅživateli, nebo nemÃĄte ÅžÃĄdnÊho uÅživatele, se kterÃŊm byste je mohli sdílet.", @@ -1406,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "SprÃĄva předvoleb aplikace", "preferences_settings_title": "Předvolby", + "preparing": "Příprava", "preset": "Přednastavení", "preview": "NÃĄhled", "previous": "Předchozí", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Mobilní aplikace je zastaralÃĄ. Aktualizujte ji na nejnovějÅĄÃ­ verzi.", "profile_drawer_client_server_up_to_date": "Klient a server jsou aktuÃĄlní", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "ReÅžim jen pro čtení. Ukončíte ho dlouhÃŊm podrÅžením ikony avataru.", "profile_drawer_server_out_of_date_major": "Server je zastaralÃŊ. Aktualizujte na nejnovějÅĄÃ­ hlavní verzi.", "profile_drawer_server_out_of_date_minor": "Server je zastaralÃŊ. Aktualizujte je na nejnovějÅĄÃ­ verzi.", "profile_image_of_user": "ProfilovÃŊ obrÃĄzek uÅživatele {user}", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Stav podporovatele", "purchase_server_title": "Server", "purchase_settings_server_activated": "ProduktovÃŊ klíč serveru spravuje sprÃĄvce", + "query_asset_id": "ID poloÅžky dotazu", + "queue_status": "Ve frontě {count}/{total}", "rating": "Hodnocení hvězdičkami", "rating_clear": "Vyčistit hodnocení", "rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}", "rating_description": "Zobrazit EXIF hodnocení v informačním panelu", "reaction_options": "MoÅžnosti reakce", "read_changelog": "Přečtěte si seznam změn", + "readonly_mode_disabled": "ReÅžim pouze pro čtení je deaktivovÃĄn", + "readonly_mode_enabled": "ReÅžim pouze pro čtení povolen", + "ready_for_upload": "Připraveno k nahrÃĄní", "reassign": "Přeřadit", "reassigned_assets_to_existing_person": "Přeřadit {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžek}} na {name, select, null {existující osobu} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {Přeřazena # poloÅžka} few {Přeřazeny # poloÅžky} other {Přeřazeno # poloÅžek}} na novou osobu", @@ -1488,6 +1618,9 @@ "refreshing_faces": "ObnovovÃĄní obličejů", "refreshing_metadata": "ObnovovÃĄní metadat", "regenerating_thumbnails": "Regenerace miniatur", + "remote": "VzdÃĄlenÃŊ", + "remote_assets": "VzdÃĄlenÊ poloÅžky", + "remote_media_summary": "Souhrn vzdÃĄlenÃŊch mÊdií", "remove": "Odstranit", "remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžek}}?", "remove_assets_shared_link_confirmation": "Opravdu chcete ze sdílenÊho odkazu odstranit {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžek}}?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Odstranit vlastní rozsah datumů", "remove_deleted_assets": "Odstranit offline soubory", "remove_from_album": "Odstranit z alba", + "remove_from_album_action_prompt": "{count} odstraněnÃŊch z alba", "remove_from_favorites": "Odstranit z oblíbenÃŊch", + "remove_from_lock_folder_action_prompt": "{count} odebranÃŊch z uzamčenÊ sloÅžky", "remove_from_locked_folder": "Odstranit z uzamčenÊ sloÅžky", "remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčenÊ sloÅžky? Budou viditelnÊ ve vaÅĄÃ­ knihovně.", "remove_from_shared_link": "Odstranit ze sdílenÊho odkazu", @@ -1523,25 +1658,35 @@ "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", "reset_pin_code": "Resetovat PIN kÃŗd", + "reset_pin_code_description": "Pokud jste zapomněli svůj PIN kÃŗd, obraÅĨte se na sprÃĄvce serveru pro jeho resetovÃĄní", + "reset_pin_code_success": "PIN kÃŗd ÃēspÄ›ÅĄně resetovÃĄn", + "reset_pin_code_with_password": "Svůj PIN kÃŗd můŞete vÅždy resetovat pomocí hesla", + "reset_sqlite": "Obnovit databÃĄzi SQLite", + "reset_sqlite_confirmation": "Jste si jisti, Åže chcete obnovit databÃĄzi SQLite? Pro opětovnou synchronizaci dat se budete muset odhlÃĄsit a znovu přihlÃĄsit", + "reset_sqlite_success": "Obnovení SQLite databÃĄze proběhlo ÃēspÄ›ÅĄně", "reset_to_default": "Obnovit vÃŊchozí nastavení", "resolve_duplicates": "VyřeÅĄit duplicity", "resolved_all_duplicates": "VyřeÅĄeny vÅĄechny duplicity", "restore": "Obnovit", "restore_all": "Obnovit vÅĄe", + "restore_trash_action_prompt": "{count} obnoveno z koÅĄe", "restore_user": "Obnovit uÅživatele", "restored_asset": "PoloÅžka obnovena", "resume": "Pokračovat", + "resume_paused_jobs": "Pokračovat {count, plural, one {v # pozastavenÊ Ãēloze} few {ve # pozastavenÃŊch ÃēlohÃĄch} other {v # pozastavenÃŊch ÃēlohÃĄch}}", "retry_upload": "OpakovÃĄní nahrÃĄvÃĄní", "review_duplicates": "Kontrola duplicit", + "review_large_files": "Kontrola velkÃŊch souborů", "role": "Role", "role_editor": "Editor", "role_viewer": "DivÃĄk", + "running": "ProbíhÃĄ", "save": "UloÅžit", "save_to_gallery": "UloÅžit do galerie", "saved_api_key": "API klíč uloÅžen", "saved_profile": "Profil uloÅžen", "saved_settings": "Nastavení uloÅženo", - "say_something": "Řekněte něco", + "say_something": "NapiÅĄte něco", "scaffold_body_error_occurred": "DoÅĄlo k chybě", "scan_all_libraries": "Prohledat vÅĄechny knihovny", "scan_library": "Prohledat", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album", "selected": "VybrÃĄno", "selected_count": "{count, plural, one {# vybranÃŊ} few {# vybranÊ} other {# vybranÃŊch}}", + "selected_gps_coordinates": "VybranÊ GPS souřadnice", "send_message": "Odeslat zprÃĄvu", "send_welcome_email": "Poslat uvítací e-mail", "server_endpoint": "KoncovÃŊ bod serveru", @@ -1667,6 +1813,7 @@ "settings_saved": "Nastavení uloÅženo", "setup_pin_code": "Nastavení PIN kÃŗdu", "share": "Sdílet", + "share_action_prompt": "Sdíleno {count} poloÅžek", "share_add_photos": "Přidat fotografie", "share_assets_selected": "{count} vybrÃĄno", "share_dialog_preparing": "Připravuji...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "ZkopírovÃĄno do schrÃĄnky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Chyba při vytvÃĄÅ™ení sdílenÊho odkazu", + "shared_link_custom_url_description": "Přístup k tomuto sdílenÊmu odkazu pomocí vlastního URL", "shared_link_edit_description_hint": "Zadejte popis sdílení", "shared_link_edit_expire_after_option_day": "1 den", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovat sdílenÊ odkazy", "shared_link_options": "MoÅžnosti sdílenÊho odkazu", + "shared_link_password_description": "VyÅžadovat heslo pro přístup k tomuto sdílenÊmu odkazu", "shared_links": "SdílenÊ odkazy", "shared_links_description": "Sdílet fotky a videa pomocí odkazu", "shared_photos_and_videos_count": "{assetCount, plural, one {# sdílenÃĄ fotografie a video.} few {# sdílenÊ fotografie a videa.} other {# sdílenÃŊch fotografií a videí.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Zobrazit přechod prezentace", "show_supporter_badge": "Odznak podporovatele", "show_supporter_badge_description": "Zobrazit odznak podporovatele", + "show_text_search_menu": "Zobrazit nabídku pro vyhledÃĄvÃĄní textu", "shuffle": "NÃĄhodnÃŊ vÃŊběr", "sidebar": "Postranní panel", "sidebar_display_description": "Zobrazení odkazu na zobrazení v postranním panelu", @@ -1762,12 +1912,14 @@ "sort_created": "Datum vytvoření", "sort_items": "Počet poloÅžek", "sort_modified": "Datum modifikace", + "sort_newest": "NejnovějÅĄÃ­ fotka", "sort_oldest": "NejstarÅĄÃ­ fotka", "sort_people_by_similarity": "Seřadit lidi podle podobnosti", "sort_recent": "NejnovějÅĄÃ­ fotka", "sort_title": "NÃĄzev alba", "source": "Zdroj", "stack": "Seskupit", + "stack_action_prompt": "{count} seskupeno", "stack_duplicates": "Seskupit duplicity", "stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení", "stack_selected_photos": "Seskupení vybranÃŊch fotografií", @@ -1775,6 +1927,7 @@ "stacktrace": "VÃŊpis zÃĄsobníku", "start": "Start", "start_date": "PoÄÃĄteční datum", + "start_date_before_end_date": "PoÄÃĄteční datum se musí nachÃĄzet před konečnÃŊm datem", "state": "StÃĄt", "status": "Stav", "stop_casting": "Zastavit odesílÃĄní", @@ -1787,6 +1940,7 @@ "storage_quota": "KvÃŗta ÃēloÅžiÅĄtě", "storage_usage": "VyuÅžito {used} z {available}", "submit": "Odeslat", + "success": "Úspěch", "suggestions": "NÃĄvrhy", "sunrise_on_the_beach": "VÃŊchod slunce na plÃĄÅži", "support": "Podpora", @@ -1796,6 +1950,10 @@ "sync": "Synchronizovat", "sync_albums": "Synchronizovat alba", "sync_albums_manual_subtitle": "Synchronizovat vÅĄechna nahranÃĄ videa a fotografie do vybranÃŊch zÃĄloÅžních alb", + "sync_local": "Synchronizovat místní", + "sync_remote": "Synchronizovat vzdÃĄlenÊ", + "sync_status": "Stav synchronizace", + "sync_status_subtitle": "Zobrazit a spravovat synchronizační systÊm", "sync_upload_album_setting_subtitle": "Vytvořit a nahrÃĄt fotografie a videa do vybranÃŊch alb na Immich", "tag": "Značka", "tag_assets": "Přiřadit značku", @@ -1806,6 +1964,7 @@ "tag_updated": "AktualizovÃĄna značka: {tag}", "tagged_assets": "Přiřazena značka {count, plural, one {# poloÅžce} other {# poloÅžkÃĄm}}", "tags": "Značky", + "tap_to_run_job": "Klepnutím na spustíte Ãēlohu", "template": "Å ablona", "theme": "Motiv", "theme_selection": "VÃŊběr motivu", @@ -1818,7 +1977,7 @@ "theme_setting_image_viewer_quality_title": "Kvalita prohlíŞeče obrÃĄzků", "theme_setting_primary_color_subtitle": "Zvolte barvu pro hlavní akce a zvÃŊraznění.", "theme_setting_primary_color_title": "Hlavní barva", - "theme_setting_system_primary_color_title": "PouÅžití systÊmovÊ barvy", + "theme_setting_system_primary_color_title": "PouŞít systÊmovou barvu", "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ě", @@ -1832,12 +1991,15 @@ "to_change_password": "Změnit heslo", "to_favorite": "Oblíbit", "to_login": "PřihlÃĄsit", + "to_multi_select": "na vícenÃĄsobnÃŊ vÃŊběr", "to_parent": "Přejít k rodiči", + "to_select": "vybrat", "to_trash": "Vyhodit", "toggle_settings": "Přepnout nastavení", "total": "Celkem", "total_usage": "CelkovÊ vyuÅžití", "trash": "KoÅĄ", + "trash_action_prompt": "{count} přesunutÃŊch do koÅĄe", "trash_all": "Vyhodit vÅĄe", "trash_count": "Vyhodit {count, number}", "trash_delete_asset": "Vyhodit/Smazat poloÅžku", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Vybrat poloÅžky", "trash_page_title": "KoÅĄ ({count})", "trashed_items_will_be_permanently_deleted_after": "SmazanÊ poloÅžky budou trvale odstraněny po {days, plural, one {# dni} other {# dnech}}.", + "troubleshoot": "Diagnostika", "type": "Typ", "unable_to_change_pin_code": "Nelze změnit PIN kÃŗd", "unable_to_setup_pin_code": "Nelze nastavit PIN kÃŗd", - "unarchive": "Odarchivovat", + "unarchive": "Odebrat z archivu", + "unarchive_action_prompt": "{count} odstraněnÃŊch z archivu", "unarchived_count": "{count, plural, one {OdarchivovÃĄna #} few {OdarchivovÃĄny #} other {OdarchivovÃĄno #}}", "undo": "VrÃĄtit zpět", "unfavorite": "ZruÅĄit oblíbení", + "unfavorite_action_prompt": "{count} odstraněnÃŊch z oblíbenÃŊch", "unhide_person": "ZruÅĄit skrytí osoby", "unknown": "NeznÃĄmÃŊ", "unknown_country": "NeznÃĄmÃĄ země", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "ZruÅĄit vÃŊběr vÅĄech duplicit", "unselect_all_in": "ZruÅĄit vÃŊběr ve skupině {group}", "unstack": "ZruÅĄit seskupení", - "unstacked_assets_count": "{count, plural, one {RozloÅženÃĄ # poloÅžka} few {RozloÅženÊ # poloÅžky} other {RozloÅženÃŊch # poloÅžiek}}", + "unstack_action_prompt": "{count} seskupenÃŊch zruÅĄeno", + "unstacked_assets_count": "{count, plural, one {RozloÅženÃĄ # poloÅžka} few {RozloÅženÊ # poloÅžky} other {RozloÅženÃŊch # poloÅžek}}", + "untagged": "Neoznačeno", "up_next": "To je prozatím vÅĄe", + "update_location_action_prompt": "Aktualizovat polohu {count} vybranÃŊch poloÅžek pomocí:", "updated_at": "AktualizovÃĄno", "updated_password": "Heslo aktualizovÃĄno", "upload": "NahrÃĄt", + "upload_action_prompt": "{count} ve frontě pro nahrÃĄní", "upload_concurrency": "SouběŞnost nahrÃĄvÃĄní", + "upload_details": "Detaily nahrÃĄvÃĄní", "upload_dialog_info": "Chcete zÃĄlohovat vybranÊ poloÅžky na server?", "upload_dialog_title": "NahrÃĄt poloÅžku", "upload_errors": "NahrÃĄvÃĄní bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte strÃĄnku pro zobrazení novÃŊch poloÅžek.", + "upload_finished": "NahrÃĄvÃĄní dokončeno", "upload_progress": "ZbÃŊvÃĄ {remaining, number} - ZpracovÃĄno {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Přeskočena # duplicitní poloÅžka} few {Přeskočeny # duplicitní poloÅžky} other {Přeskočeno # duplicitních poloÅžek}}", "upload_status_duplicates": "Duplicity", @@ -1892,6 +2063,7 @@ "upload_success": "NahrÃĄní proběhlo ÃēspÄ›ÅĄně, obnovením strÃĄnky se zobrazí nově nahranÊ poloÅžky.", "upload_to_immich": "NahrÃĄt do Immich ({count})", "uploading": "NahrÃĄvÃĄní", + "uploading_media": "NahrÃĄvÃĄní mÊdií", "url": "URL", "usage": "VyuÅžití", "use_biometric": "PouŞít biometrickÊ Ãēdaje", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Zobrazit statistiky pouŞívÃĄní Ãēčtu", "username": "UÅživateleskÊ jmÊno", "users": "UÅživatelÊ", + "users_added_to_album_count": "{count, plural, one {PřidÃĄn # uÅživatel} few {PřidÃĄny # uÅživatelÊ} other {PřidÃĄno # uÅživatelů}} do alba", "utilities": "NÃĄstroje", "validate": "Ověřit", "validate_endpoint_error": "Zadejte platnÊ URL", @@ -1920,7 +2093,7 @@ "version_announcement_closing": "VÃĄÅĄ přítel Alex", "version_announcement_message": "Ahoj! K dispozici je novÃĄ verze aplikace Immich. Věnujte prosím chvíli přečtení poznÃĄmek k vydÃĄní a ujistěte se, Åže je vaÅĄe nastavení aktuÃĄlní, abyste předeÅĄli případnÃŊm chybnÃŊm konfiguracím, zejmÊna pokud pouŞívÃĄte WatchTower nebo jinÃŊ mechanismus, kterÃŊ se starÃĄ o automatickou aktualizaci instance aplikace Immich.", "version_history": "Historie verzí", - "version_history_item": "NainstalovÃĄno {version} dne {date}", + "version_history_item": "Verze {version} nainstalovÃĄna dne {date}", "video": "Video", "video_hover_setting": "PřehrÃĄvat miniaturu videa po najetí myÅĄÃ­", "video_hover_setting_description": "PřehrÃĄt miniaturu videa při najetí myÅĄÃ­ na poloÅžku. I kdyÅž je přehrÃĄvÃĄní vypnuto, lze jej spustit najetím na ikonu přehrÃĄvÃĄní.", @@ -1930,6 +2103,7 @@ "view_album": "Zobrazit album", "view_all": "Zobrazit vÅĄe", "view_all_users": "Zobrazit vÅĄechny uÅživatele", + "view_details": "Zobrazit podrobnosti", "view_in_timeline": "Zobrazit na časovÊ ose", "view_link": "Zobrazit odkaz", "view_links": "Zobrazit odkazy", @@ -1937,11 +2111,12 @@ "view_next_asset": "Zobrazit dalÅĄÃ­ poloÅžku", "view_previous_asset": "Zobrazit předchozí poloÅžku", "view_qr_code": "Zobrazit QR kÃŗd", + "view_similar_photos": "Zobrazit podobnÊ fotky", "view_stack": "Zobrazit seskupení", "view_user": "Zobrazit uÅživatele", "viewer_remove_from_stack": "Odstranit ze zÃĄsobníku", "viewer_stack_use_as_main_asset": "PouŞít jako hlavní poloÅžku", - "viewer_unstack": "Rozbalit zÃĄsobník", + "viewer_unstack": "ZruÅĄit zÃĄsobník", "visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}", "waiting": "Čekající", "warning": "Upozornění", @@ -1955,5 +2130,6 @@ "yes": "Ano", "you_dont_have_any_shared_links": "NemÃĄte ÅžÃĄdnÊ sdílenÊ odkazy", "your_wifi_name": "NÃĄzev vaÅĄÃ­ Wi-Fi", - "zoom_image": "ZvětÅĄit obrÃĄzek" + "zoom_image": "ZvětÅĄit obrÃĄzek", + "zoom_to_bounds": "PřiblíŞit na okraje" } diff --git a/i18n/cv.json b/i18n/cv.json index 89f71c9632..fe5bb3c2fc 100644 --- a/i18n/cv.json +++ b/i18n/cv.json @@ -4,6 +4,7 @@ "account_settings": "Đ¨ŅƒŅ‚Đ° ŌĢҋҀĐŊи Ķ—ĐŊĐĩŅ€ĐģĐĩĐŊĶŗ", "acknowledge": "Ã‡Đ¸Ņ€Ķ—ĐŋĐģĐĩŅ‚", "action": "Ķ–Ã§ĐģĐĩĐŊи", + "action_common_update": "ŌĒĶ—ĐŊĐĩŅ‚", "actions": "Ķ–ŌĢҁĐĩĐŧ", "active": "ĐĨĐ°ŅŅ‚Đ°Ņ€", "activity": "ĐĨĐ°ŅŅ‚Đ°Ņ€Đģ͑҅", @@ -13,6 +14,8 @@ "add_a_location": "Đ’Ņ‹Ņ€Ķ‘ĐŊ Ņ…ŅƒŅˆ", "add_a_name": "Đ¯Ņ‚ĐŊĐĩ Ņ…ŅƒŅˆ", "add_a_title": "Đ¯Ņ‚ Ņ…ŅƒŅˆ", + "add_birthday": "ŌĒŅƒŅ€Đ°ĐģĐŊĶ‘ Đē҃ĐŊ Ņ…ŅƒŅˆĶ‘Ņ€", + "add_endpoint": "Đ’Ķ—ŌĢĶ—ĐŧĐģĶ— ĐŋĶ‘ĐŊ҇͑ Ņ…ŅƒŅˆĐ°Ņ€", "add_exclusion_pattern": "ĐšĶ‘ĐģĐ°Ņ€ŅĐ° ĐŋĶ‘Ņ€Đ°Ņ…ĐŧаĐģĐģи ĐšĶ—Ņ€ĐēĐĩ Ņ…ŅƒŅˆ", "add_import_path": "ИĐŧĐŋĐžŅ€Ņ‚ ŌĢ҃ĐģĐŊĐĩ Ņ…ŅƒŅˆ", "add_location": "Đ’Ņ‹Ņ€Ķ‘ĐŊ Ņ…ŅƒŅˆ", @@ -20,6 +23,7 @@ "add_partner": "ĐœĶ‘ŅˆĶ‘Ņ€ Ņ…ŅƒŅˆ", "add_path": "ŌĒ҃ĐģĐŊĐĩ Ņ…ŅƒŅˆ", "add_photos": "ĐĄĶ‘ĐŊĶŗĐēĐĩҀ҇͗ĐēҁĐĩĐŧ Ņ…ŅƒŅˆ", + "add_tag": "ĐĸĐĩĐŗ Ņ…ŅƒŅˆ", "add_to": "ĐœĶ—ĐŊ Ņ‚Đĩ Đŋ҃ĐģиĐŊ Ņ…ŅƒŅˆâ€Ļ", "add_to_album": "АĐģŅŒĐąĐžĐŧа Ņ…ŅƒŅˆ", "add_to_shared_album": "ĐŸĶ—Ņ€ĐģĐĩŅ…Đ¸ аĐģŅŒĐąĐžĐŧа Ņ…ŅƒŅˆ", @@ -28,9 +32,13 @@ "added_to_favorites": "ĐĄŅƒĐšĐģĐ°ŅĐ° иĐģĐŊиĐŊĐĩ Ņ…ŅƒŅˆĐŊĶ‘", "added_to_favorites_count": "ĐĄŅƒĐšĐģĐ°ŅĐ° иĐģĐŊиĐŊĐĩ {count, number} Ņ…ŅƒŅˆĐŊĶ‘", "admin": { + "admin_user": "ĐŖŅĶ‘ŌĢ Đ°Đ´ĐŧиĐŊ", "asset_offline_description": "БибĐģĐ¸ĐžŅ‚ĐĩĐēĶ‘ĐŊ ŌĢаĐē Ņ‚ŅƒĐģĐ°Ņˆ Ņ„Đ°ĐšĐģĐŊĐĩ Đ´Đ¸ŅĐēŅ€Đ° ŅƒŅ€Ķ‘Ņ… Ņ‚ŅƒĐŋаКĐŧаĐŊ, ĐēĐ°Ņ€ŌĢиĐŊĐēĐēаĐŊа Đē҃ŌĢĐ°Ņ€ĐŊĶ‘. ЕĐŊ҇ĐĩĐŊ Ņ‚Đĩ Ņ„Đ°ĐšĐģа Đ˛ŅƒĐģĐ°Đ˛Ķ‘Ņˆ Ķ‘ŅˆĐŊĐĩ Đē҃ŌĢĐ°Ņ€ĐŊĶ‘ Đŋ҃ĐģŅĐ°ĐŊ, Ņ‚Đ¸Đ˛Ķ—ŌĢĐģĶ— ŌĢĶ—ĐŊĶ— Ņ€ĐĩŅŅƒŅ€Ņ Ņ‚ŅƒĐŋĐ°Ņ Ņ‚ĐĩҁĐĩĐŊ Ņ…Ķ‘Đ˛Ķ‘Ņ€Ķ‘ĐŊ Đ˛Ķ‘Ņ…Ķ‘Ņ‚Đģ͑҅ ҈ĐēаĐģĶ‘ĐŊа ҂͗Ҁ͗ҁĐģĶ—Ņ€. ŌĒаĐē Ņ„Đ°ĐšĐģа ŌĢĶ—ĐŊĶ—Ņ€ĐĩĐŊ ҇͗Ҁ҂Đĩҁ Ņ‚ĐĩҁĐĩĐŊ Ņ„Đ°ĐšĐģ ĐŋĐ°Ņ‚ĐŊĐĩ ĐēаКĐŧаĐģĐģи ŌĢ҃Đģа Immich ваĐģĐģи Đ°ŅĐģĐ°Ņ€Đ°Ņ… ŌĢĐ¸Ņ‚ĐĩŅ€ĐŊиĐŊĐĩ ĐēŅƒŅ€ŅĐ° Ķ—ĐŊĐĩĐŊĶ—Ņ€, йийĐģĐ¸ĐžŅ‚ĐĩĐēĶ‘ĐŊа ҁĐēаĐŊĐĩŅ€ĐģаĐŊиĐŊĐĩ ĐŋŅƒŅ€ĐŊĶ‘ŌĢĐģ͑Ҁ.", + "authentication_settings": "ĐŅƒŅ‚Ņ‚ĐĩĐŊŅ‚Đ¸Ņ…Đ˛Đ¸ĐēĐ°Ņ‚ŅĐ¸ Ķ—ĐŊĐĩŅ€ĐģĐĩĐŊͺҁĐĩĐŧ", "authentication_settings_disable_all": "Đ­ŅĐ¸Ņ€ ĐēĶ—ĐŧĐĩĐģĐģи ĐŋŅƒŅ€ ĐŧĐĩҁĐģĐĩ҂ҁĐĩĐŊĐĩ Ņ‚Đĩ Ņ‡Đ°Ņ€ŅĐ° ĐģĐ°Ņ€Ņ‚Đ°ŅŅˆĶ‘ĐŊ Ņ‚ĐĩҁĐĩ ŅˆŅƒŅ‚ĐģĐ°Ņ‚Ķ‘Ņ€-и? ĐšĶ—ĐŧĐĩĐģĐģи ŅˆĶ‘Ņ‚Ķ‘Đēа Đŋ͗҂͗ĐŧĐŋĐĩŅ… ҃ŌĢаŌĢŌĢĶ—.", "background_task_job": "ĐšŅƒŅ€ÄƒĐŊĐŧаĐŊ Ķ—ŌĢҁĐĩĐŧ", + "backup_database": "ПĕĐģĕĐŧ ĐŋŅƒŅ…ĐŧĐ°Ņ‡Ä• Ņ‚ŅƒŅĐ°", + "backup_onboarding_title": "ĐĄŅ‹Ņ…Đģ͑҅ ĐēĐžĐŋĐŋĐ¸ŅĐĩĐŧ", "cleared_jobs": "Ķ–ŌĢҁĐĩĐŊĐĩ Ņ‚Đ°ŅĐ°Ņ‚ĐŊĶ‘:{job}", "confirm_email_below": "ŌĒĐ¸Ņ€Ķ—ĐŋĐģĐĩŅ‚Đĩҁ Ņ‚ĐĩҁĐĩĐŊ, Đ°ŅĐģĐ°Ņ€Đ°Ņ… ÂĢ{email}Âģ Đē͗Ҁ҂͗Ҁ", "confirm_reprocess_all_faces": "ĐŸĶ—Ņ‚Ķ—Đŧ ҁ͑ĐŊ͗ҁĐĩĐŊĐĩ Ņ‚ĐĩĐŋĶ—Ņ€ Ņ…ŅƒŅ‚ ĐŋаĐģĶ‘Ņ€Ņ‚Đ°Ņ ĐēиĐģĐĩŅ‚ Ņ‚ĐĩҁĐĩ ŅˆĐ°ĐŊĐ°Ņ‚Ķ‘Ņ€-и? ŌĒĐ°Đ˛Ķ‘ĐŊ ĐŋĐĩĐēĐĩŅ… ŅŅ‚ŅĐĩĐŊĐĩ ĐŋŅƒŅ€ ŌĢŅ‹ĐŊŅ€Đ°ĐŊ Ņ‚Đ° Ņ…ŅƒŅ€Đ°Ņ‚Ķ—ŌĢ.", @@ -45,6 +53,8 @@ "image_preview_title": "МаĐģŅ‚Đ°ĐŊĐģĶ‘Ņ…Đ° Đŋ͑҅ĐŧаĐģĐģи Ķ—ĐŊĐĩŅ€ĐģĐĩĐ˛ŅĐĩĐŧ", "image_quality": "ĐŸĐ°Ņ…Đ°Đģ͑҅", "image_resolution": "ВиŌĢĐĩ", + "image_settings": "ĐĄĶ‘ĐŊĶŗĐēĐĩҀ҇͗Đē Ķ—ĐŊĐĩŅ€ĐģĐĩĐŊͺҁĐĩĐŧ", + "image_thumbnail_title": "ĐŸĶ—Ņ‡Ķ—Đē ĶŗĐēĐĩҀ҇͗ĐēҁĐĩĐŊ Ķ—ĐŊĐĩŅ€ĐģĐĩĐŊͺҁĐĩĐŧ", "map_gps_settings": "ĐšĐ°Ņ€Ņ‚Ņ‚Ķ‘ Ņ‚Đ°Ņ‚Đ° GPS ĕĐŊĐĩŅ€ĐģĐĩĐŊĕвĕ", "map_gps_settings_description": "ĐšĐ°Ņ€Ņ‚Ņ‚Ķ‘Đŋа GPS (ĐēĐ°ŅĐģĐģа ĐŗĐĩĐžŅŽĐŧĐģаĐŊи) Ķ—ĐŊĐĩŅ€ĐģĐĩĐŊĐ¸ŅĐĩĐŊĐĩ ĐšĶ—Ņ€ĐēĐĩĐģĐĩҁĐĩ ҂͑Ҁ͑Ҁ", "map_settings": "ĐšĐ°Ņ€Ņ‚Ņ‚Ķ‘" diff --git a/i18n/da.json b/i18n/da.json index 3273a2d553..c7aabf2b3c 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -1,8 +1,8 @@ { - "about": "Om", + "about": "Om os", "account": "Konto", "account_settings": "Kontoindstillinger", - "acknowledge": "Godkend", + "acknowledge": "Accepter", "action": "Handling", "action_common_update": "Opdater", "actions": "Handlinger", @@ -14,6 +14,7 @@ "add_a_location": "Tilføj en placering", "add_a_name": "Tilføj et navn", "add_a_title": "Tilføj en titel", + "add_birthday": "Tilføj en fødselsdag", "add_endpoint": "Tilføj endepunkt", "add_exclusion_pattern": "Tilføj udelukkelsesmønster", "add_import_path": "Tilføj importsti", @@ -27,13 +28,18 @@ "add_to_album": "Tilføj til album", "add_to_album_bottom_sheet_added": "Tilføjet til {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Nogle lokale elementer kunne ikke føjes til albummet", + "add_to_album_toggle": "Skift selektion for {album}", + "add_to_albums": "Tilføj til albummer", + "add_to_albums_count": "Tilføj til albummer({count})", "add_to_shared_album": "Tilføj til delt album", "add_url": "Tilføj URL", "added_to_archive": "Tilføjet til arkiv", "added_to_favorites": "Tilføjet til favoritter", - "added_to_favorites_count": "Tilføjet {count, number} til favoritter", + "added_to_favorites_count": "Tilføjede {count, number} til favoritter", "admin": { "add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjÃĻlp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter pÃĨ \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".", + "admin_user": "Administratorbruger", "asset_offline_description": "Denne eksterne biblioteksressource findes ikke lÃĻngere pÃĨ disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgÃĨs af Immich og scanne biblioteket.", "authentication_settings": "Godkendelsesindstillinger", "authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger", @@ -43,6 +49,13 @@ "backup_database": "Lav Database Dump", "backup_database_enable_description": "SlÃĨ database-backup til", "backup_keep_last_amount": "MÃĻngde af tidligere backups, der skal gemmes", + "backup_onboarding_1_description": "kopi pÃĨ en anden fysisk lokation eller i skyen.", + "backup_onboarding_2_description": "lokale kopier pÃĨ separate enheder. Dette inkluderer de originale filer og en lokal backup af disse.", + "backup_onboarding_3_description": "kopier af din data i alt, inklusiv de originale filer. Dette inkluderer 1 kopi pÃĨ en anden fysisk lokation, og 2 lokale kopier.", + "backup_onboarding_description": "En 3-2-1 backup strategy anbefales for at beskytte dine data. En altomfattende backupløsning skulle gerne have kopier af dine uploadede billeder og videoer, samt Immich databasen.", + "backup_onboarding_footer": "Referer venligst til dokumentationen for mere information om hvordan Immich backes op.", + "backup_onboarding_parts_title": "En 3-2-1 backup inkluderer:", + "backup_onboarding_title": "Backupper", "backup_settings": "Database Backup-indstillinger", "backup_settings_description": "Administrer backupindstillinger for database.", "cleared_jobs": "Ryddet jobs til: {job}", @@ -111,16 +124,23 @@ "logging_enable_description": "Aktiver logning", "logging_level_description": "NÃĨr slÃĨet til, hvilket logniveau, der skal bruges.", "logging_settings": "Logning", + "machine_learning_availability_checks": "TilgÃĻngelighedstjek", + "machine_learning_availability_checks_description": "Opdag og foretrÃĻk automatisk tilgÃĻngelige maskinlÃĻringsservere", + "machine_learning_availability_checks_enabled": "AktivÊr tilgÃĻngelighedstjek", + "machine_learning_availability_checks_interval": "Kontroller interval", + "machine_learning_availability_checks_interval_description": "Interval i millisekunder mellem tilgÃĻngelighedstjeks", + "machine_learning_availability_checks_timeout": "Timeout pÃĨ anmodning", + "machine_learning_availability_checks_timeout_description": "Timeout i millisekunder pÃĨ tilgÃĻngelighedstjeks", "machine_learning_clip_model": "CLIP-model", "machine_learning_clip_model_description": "Navnet pÃĨ CLIP-modellen pÃĨ listen her. BemÃĻrk at du skal genkøre \"Smart Søgning\"-jobbet for alle billeder, hvis du skifter model.", "machine_learning_duplicate_detection": "Dubletdetektion", - "machine_learning_duplicate_detection_enabled": "Aktiver duplikatdetektion", - "machine_learning_duplicate_detection_enabled_description": "NÃĨr slÃĨet fra, vil nøjagtigt identiske mediefiler blive de-duplikerede.", - "machine_learning_duplicate_detection_setting_description": "Brug CLIP-indlejringer til at finde sandsynlige duplikater", + "machine_learning_duplicate_detection_enabled": "Aktiver dubletdetektion", + "machine_learning_duplicate_detection_enabled_description": "NÃĨr slÃĨet fra, vil nøjagtigt identiske mediefiler stadig blive de-duplikerede.", + "machine_learning_duplicate_detection_setting_description": "Brug CLIP-indlejringer til at finde sandsynlige dubletter", "machine_learning_enabled": "AktivÊr maskinlÃĻring", "machine_learning_enabled_description": "Hvis deaktiveret, vil alle ML-funktioner blive deaktiveret uanset nedenstÃĨende indstillinger.", "machine_learning_facial_recognition": "Ansigtsgenkendelse", - "machine_learning_facial_recognition_description": "Registrer, genkend og grupper ansigter i billeder", + "machine_learning_facial_recognition_description": "Opdag, genkend og gruppÊr ansigter i billeder", "machine_learning_facial_recognition_model": "Ansigtsgenkendelsesmodel", "machine_learning_facial_recognition_model_description": "Modellerne er listet i faldende størrelsesorden. Større modeller er langsommere og bruger mere hukommelse, men giver bedre resultater. BemÃĻrk, at du skal køre ansigtsopdagelsesopgaven igen for alle billeder, nÃĨr du ÃĻndrer en model.", "machine_learning_facial_recognition_setting": "AktivÊr ansigtgenkendelse", @@ -165,6 +185,20 @@ "metadata_settings_description": "HÃĨndtÊr metadataindstillinger", "migration_job": "Migrering", "migration_job_description": "MigrÊr miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", + "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse pÃĨ nye ansigter", + "nightly_tasks_cluster_new_faces_setting": "GruppÊr nye ansigter", + "nightly_tasks_database_cleanup_setting": "Databaseoprydning opgaver", + "nightly_tasks_database_cleanup_setting_description": "Ryd op i gamle, udløbne data fra databasen", + "nightly_tasks_generate_memories_setting": "Generer minder", + "nightly_tasks_generate_memories_setting_description": "Skab nye minder ud fra dine medier", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniaturebilleder", + "nightly_tasks_missing_thumbnails_setting_description": "SÃĻt medier uden thumbnails i kø til generering af thumbnails", + "nightly_tasks_settings": "Indstillinger for opgaver om natten", + "nightly_tasks_settings_description": "AdministrÊr opgaver om natten", + "nightly_tasks_start_time_setting": "Starttidspunkt", + "nightly_tasks_start_time_setting_description": "Tidspunktet hvor serveren begynder at køre opgaver om natten", + "nightly_tasks_sync_quota_usage_setting": "SynkronisÊr kvoteforbrug", + "nightly_tasks_sync_quota_usage_setting_description": "Opdater brugerens lagerkvote baseret pÃĨ aktuelt forbrug", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "BemÃĻrk: For at anvende LagringsmÃĻrkatet pÃĨ tidligere uploadede mediefiler, kør", @@ -195,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobilomdiregerings-URL", "oauth_mobile_redirect_uri_override": "TilsidesÃĻttelse af mobil omdiregerings-URL", "oauth_mobile_redirect_uri_override_description": "Aktiver, nÃĨr OAuth-udbyderen ikke tillader en mobil URI, som ''{callback}''", + "oauth_role_claim": "Rolle attribut", + "oauth_role_claim_description": "Tildel automatisk admin adgang pÃĨ basis af forekomst af denne pÃĨstand. DÊn kan vÃĻre enten 'user' eller 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer OAuth login-indstillinger", "oauth_settings_more_details": "LÃĻs flere detaljer om funktionen i dokumentationen.", @@ -203,7 +239,7 @@ "oauth_storage_quota_claim": "Lagringskvotefordring", "oauth_storage_quota_claim_description": "SÃĻt automatisk brugeres lagringskvote til denne nye fordrings vÃĻrdi.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", - "oauth_storage_quota_default_description": "Kvote i GiB som bruges, nÃĨr der ikke bliver oplyst en fordring (Indtast 0 for uendelig kvote).", + "oauth_storage_quota_default_description": "Kvote i GiB som bruges, nÃĨr der ikke bliver oplyst en fordring.", "oauth_timeout": "Forespørgslen udløb", "oauth_timeout_description": "Udløbstid for forespørgsel i milisekunder", "password_enable_description": "Log ind med email og adgangskode", @@ -243,6 +279,7 @@ "storage_template_migration_info": "Lager-skabelonen vil konvertere alle filendelser til smÃĨ bogstaver. SkabelonÃĻndringer vil kun gÃĻlde for nye mediefiler. For at anvende skabelonen retroaktivt pÃĨ tidligere uploadede mediefiler skal du køre {job}.", "storage_template_migration_job": "Lager Skabelon Migreringsjob", "storage_template_more_details": "For flere detaljer om denne funktion, referer til Lager Skabelonen og dens implikationer", + "storage_template_onboarding_description_v2": "NÃĨr aktiveret, sÃĨ vil denne funktion auto-organisere filer pÃĨ grundlag af en brugerdefineret skabelon. For nÃĻrmere, se dokumentation.", "storage_template_path_length": "AnslÃĨet sti-lÃĻngde begrÃĻnsning {length, number}/{limit, number}", "storage_template_settings": "Lagringsskabelon", "storage_template_settings_description": "Administrer mappestrukturen og filnavnet for den uploadede mediefil", @@ -329,6 +366,9 @@ "trash_number_of_days_description": "Antal dage aktiver i skraldespanden skal beholdes inden de fjernes permanent", "trash_settings": "Skraldeindstillinger", "trash_settings_description": "AdministrÊr skraldeindstillinger", + "unlink_all_oauth_accounts": "OphÃĻv link til alle OAuth konti", + "unlink_all_oauth_accounts_description": "Husk at fjerne linket til alle OAuth konti før du migrerer til en ny udbyder.", + "unlink_all_oauth_accounts_prompt": "Er du sikker pÃĨ, at du vil ophÃĻve link til alle OAuth konti? Dette vil nulstille OAuth ID for hver bruger og kan ikke fortrydes.", "user_cleanup_job": "Bruger-oprydning", "user_delete_delay": "{user}'s konto og mediefiler vil blive planlagt til permanent sletning om {delay, plural, one {# dag} other {# dage}}.", "user_delete_delay_settings": "Slet forsinkelse", @@ -358,10 +398,12 @@ "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_enable_alternate_media_filter_title": "[EKSPERIMENTEL] Brug alternativ enheds album synkroniserings filter", "advanced_settings_log_level_title": "Logniveau: {level}", - "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_subtitle": "Nogle enheder er meget lang tid om at indlÃĻse miniaturebilleder af lokale elementer. Aktiver denne indstilling for at indlÃĻse elementer fra serveren i stedet.", "advanced_settings_prefer_remote_title": "ForetrÃĻk elementer pÃĨ serveren", "advanced_settings_proxy_headers_subtitle": "Definer proxy headers Immich skal sende med hver netvÃĻrks forespørgsel", - "advanced_settings_proxy_headers_title": "Proxy Headers", + "advanced_settings_proxy_headers_title": "Proxy headere", + "advanced_settings_readonly_mode_subtitle": "Aktiverer skrivebeskyttet tilstand, hvor billederne alene kan vises. Ting som at vÃĻlge flere billeder, dele, caste og slette er alle deaktiveret. Aktiver skrivebeskyttet tilstand via en bruger avatar fra hovedskÃĻrmen", + "advanced_settings_readonly_mode_title": "Skrivebeskyttet tilstand", "advanced_settings_self_signed_ssl_subtitle": "Spring verificering af SSL-certifikat over for serverens endelokation. KrÃĻves for selvsignerede certifikater.", "advanced_settings_self_signed_ssl_title": "Tillad selvsignerede certifikater", "advanced_settings_sync_remote_deletions_subtitle": "Slet eller gendan automatisk en mediefil pÃĨ denne enhed, nÃĨr denne handling foretages pÃĨ Immich webinterface", @@ -377,6 +419,7 @@ "album_cover_updated": "Albumcover opdateret", "album_delete_confirmation": "Er du sikker pÃĨ at du vil slette albummet {album}?", "album_delete_confirmation_description": "Hvis dette album er delt, vil andre brugere ikke lÃĻngere kunne fÃĨ adgang til det.", + "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERET", "album_info_card_backup_album_included": "INKLUDERET", "album_info_updated": "Albuminfo opdateret", @@ -386,7 +429,9 @@ "album_options": "Albumindstillinger", "album_remove_user": "Fjern bruger?", "album_remove_user_confirmation": "Er du sikker pÃĨ at du vil fjerne {user}?", + "album_search_not_found": "Ingen album fundet som matcher din søgning", "album_share_no_users": "Det ser ud til at du har delt denne album med alle brugere, eller du har ikke nogen brugere til at dele med.", + "album_summary": "Albumoversigt", "album_updated": "Album opdateret", "album_updated_setting_description": "Modtag en emailnotifikation nÃĨr et delt album fÃĨr nye mediefiler", "album_user_left": "Forlod {album}", @@ -405,6 +450,7 @@ "albums_default_sort_order": "Standard album sortering", "albums_default_sort_order_description": "GrundlÃĻggende sortering ved oprettelse af nyt album.", "albums_feature_description": "Samling af billeder der kan deles med andre brugere.", + "albums_on_device_count": "Albummer pÃĨ enheden ({count})", "all": "Alt", "all_albums": "Alle albummer", "all_people": "Alle personer", @@ -424,7 +470,9 @@ "app_bar_signout_dialog_title": "Log ud", "app_settings": "Appindstillinger", "appears_in": "OptrÃĻder i", + "apply_count": "Brug ({count, number})", "archive": "Arkiv", + "archive_action_prompt": "{count} føjet til arkiv", "archive_or_unarchive_photo": "ArkivÊr eller dearkivÊr billede", "archive_page_no_archived_assets": "Ingen arkiverede elementer blev fundet", "archive_page_title": "ArkivÊr ({count})", @@ -441,11 +489,11 @@ "asset_description_updated": "Mediefilsbeskrivelse er blevet opdateret", "asset_filename_is_offline": "Mediefil {filename} er offline", "asset_has_unassigned_faces": "Aktivet har ikke-tildelte ansigter", - "asset_hashing": "Hashingâ€Ļ", + "asset_hashing": "Hasherâ€Ļ", "asset_list_group_by_sub_title": "GruppÊr efter", "asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout", "asset_list_layout_settings_group_automatically": "Automatisk", - "asset_list_layout_settings_group_by": "GruppÊr elementer pr.", + "asset_list_layout_settings_group_by": "Grupper elementer efter", "asset_list_layout_settings_group_by_month_day": "MÃĨned + dag", "asset_list_layout_sub_title": "Udseende", "asset_list_settings_subtitle": "Indstillinger for billedgitterlayout", @@ -455,15 +503,18 @@ "asset_restored_successfully": "Elementet blev gendannet succesfuldt", "asset_skipped": "Sprunget over", "asset_skipped_in_trash": "I skraldespand", + "asset_trashed": "Objekt kasseret", + "asset_troubleshoot": "Fejlsøg pÃĨ objekt", "asset_uploaded": "Uploadet", "asset_uploading": "Uploaderâ€Ļ", "asset_viewer_settings_subtitle": "Administrer indstillinger for gallerifremviser", "asset_viewer_settings_title": "Billedviser", - "assets": "elementer", + "assets": "Objekter", "assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}", "assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet", - "assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {{name}} other {nyt album}}", + "assets_added_to_albums_count": "Tilføjet {assetTotal, plural, one {# asset} other {# assets}} til {albumTotal, plural, one {# album} other {# albums}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan ikke føjes til i nogen af albummerne", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", "assets_deleted_permanently_from_server": "{count} element(er) blev fjernet permanent fra Immich serveren", @@ -480,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# mediefil} other {# mediefiler}} smidt i papirkurven", "assets_trashed_from_server": "{count} element(er) blev smidt i Immich serverens papirkurv", "assets_were_part_of_album_count": "mediefil{count, plural, one {mediefil} other {mediefiler}} er allerede en del af albummet", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} er allerede en del af albummerne", "authorized_devices": "Tilladte enheder", "automatic_endpoint_switching_subtitle": "Forbind lokalt over det anviste WiFi, nÃĨr det er tilgÃĻngeligt og brug alternative forbindelser andre stÃĻder", "automatic_endpoint_switching_title": "Automatisk skift af URL", "autoplay_slideshow": "Afspil slideshow automatisk", "back": "Tilbage", "back_close_deselect": "Tilbage, luk eller fravÃĻlg", + "background_backup_running_error": "Backup kører lige nu i baggrund; kan ikke starte manuel backup", "background_location_permission": "Tilladelse til baggrundsplacering", "background_location_permission_content": "For at skifte netvÃĻrk, nÃĨr appen kører i baggrunden, skal Immich *altid* have prÃĻcis placeringsadgang, sÃĨ appen kan lÃĻse WiFi-netvÃĻrkets navn", + "background_options": "Baggrundsmuligheder", + "backup": "Sikkerhedskopier", "backup_album_selection_page_albums_device": "Albummer pÃĨ enheden ({count})", "backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere", "backup_album_selection_page_assets_scatter": "Elementer kan vÃĻre spredt pÃĨ tvÃĻrs af flere albummer. Albummer kan sÃĨledes inkluderes eller udelukkes under sikkerhedskopieringsprocessen.", "backup_album_selection_page_select_albums": "VÃĻlg albummer", "backup_album_selection_page_selection_info": "Oplysninger om valgte", "backup_album_selection_page_total_assets": "Samlede unikke elementer", + "backup_albums_sync": "Synkronisering af backupalbums", "backup_all": "Alt", "backup_background_service_backup_failed_message": "Sikkerhedskopiering af elementer fejlede. Forsøger igenâ€Ļ", "backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igenâ€Ļ", @@ -532,7 +588,7 @@ "backup_controller_page_none_selected": "Ingen valgte", "backup_controller_page_remainder": "TilbagevÃĻrende", "backup_controller_page_remainder_sub": "TilbagevÃĻrende billeder og albummer, at sikkerhedskopiere, fra valgte", - "backup_controller_page_server_storage": "Serverlager", + "backup_controller_page_server_storage": "Serverplads", "backup_controller_page_start_backup": "Start sikkerhedskopiering", "backup_controller_page_status_off": "Sikkerhedskopiering er slÃĨet fra", "backup_controller_page_status_on": "Sikkerhedskopiering er slÃĨet til", @@ -543,13 +599,16 @@ "backup_controller_page_turn_on": "SlÃĨ sikkerhedskopiering til", "backup_controller_page_uploading_file_info": "Uploader filinformation", "backup_err_only_album": "Kan ikke slette det eneste album", - "backup_info_card_assets": "elementer", + "backup_error_sync_failed": "Synkroniseringen mislykkedes. Sikkerhedskopieringen kunne ikke behandles.", + "backup_info_card_assets": "objekter", "backup_manual_cancelled": "Annulleret", "backup_manual_in_progress": "Upload er allerede undervejs. Prøv igen efter noget tid", "backup_manual_success": "Succes", "backup_manual_title": "Uploadstatus", + "backup_options": "Backup indstillinger", "backup_options_page_title": "Backupindstillinger", "backup_setting_subtitle": "Administrer indstillnger for upload i forgrund og baggrund", + "backup_settings_subtitle": "HÃĨndtere upload indstillinger", "backward": "BaglÃĻns", "biometric_auth_enabled": "Biometrisk adgangskontrol slÃĨet til", "biometric_locked_out": "Du er lÃĨst ude af biometrisk adgangskontrol", @@ -568,7 +627,7 @@ "cache_settings_clear_cache_button": "Fjern cache", "cache_settings_clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad pÃĨvirke appens ydeevne indtil cachen er genopbygget.", "cache_settings_duplicated_assets_clear_button": "RYD", - "cache_settings_duplicated_assets_subtitle": "Billeder og videoer der er sortlistet af appen", + "cache_settings_duplicated_assets_subtitle": "Billeder og videoer der er ignoreres af appen", "cache_settings_duplicated_assets_title": "Dublikerede elementer ({count})", "cache_settings_statistics_album": "Biblioteksminiaturer", "cache_settings_statistics_full": "Fulde billeder", @@ -585,10 +644,11 @@ "cancel": "AnnullÊr", "cancel_search": "AnnullÊr søgning", "canceled": "Annulleret", + "canceling": "Annullerer", "cannot_merge_people": "Kan ikke sammenflette personer", "cannot_undo_this_action": "Du kan ikke fortryde denne handling!", "cannot_update_the_description": "Kan ikke opdatere beskrivelsen", - "cast": "Cast", + "cast": "Caste", "cast_description": "Konfigurer tilgÃĻngelige cast destinationer", "change_date": "Ændr dato", "change_description": "Beskrivelse af ÃĻndringer", @@ -607,6 +667,8 @@ "change_pin_code": "Skift PIN kode", "change_your_password": "Skift dit kodeord", "changed_visibility_successfully": "Synlighed blev ÃĻndret", + "charging": "Lader", + "charging_requirement_mobile_backup": "Baggrundsbackup krÃĻver, at enheden er tilsluttet oplader", "check_corrupt_asset_backup": "Tjek for korrupte sikkerhedskopier af elementer", "check_corrupt_asset_backup_button": "Foretag kontrol", "check_corrupt_asset_backup_description": "Kør kun denne kontrol via Wi-Fi, og nÃĨr alle elementer er blevet sikkerhedskopieret. Proceduren kan tage et par minutter.", @@ -616,6 +678,7 @@ "clear": "Ryd", "clear_all": "Ryd alle", "clear_all_recent_searches": "Ryd alle seneste søgninger", + "clear_file_cache": "Ryd filcache", "clear_message": "Ryd bedsked", "clear_value": "Ryd vÃĻrdi", "client_cert_dialog_msg_confirm": "OK", @@ -686,11 +749,13 @@ "create_new_user": "Opret ny bruger", "create_shared_album_page_share_add_assets": "TILFØJ ELEMENT", "create_shared_album_page_share_select_photos": "VÃĻlg Billeder", + "create_shared_link": "Opret delt link", "create_tag": "Opret tag", "create_tag_description": "Opret et nyt tag. For indlejrede tags skal du indtaste den fulde sti til tagget inklusive skrÃĨstreger.", "create_user": "Opret bruger", "created": "Oprettet", "created_at": "Oprettet", + "creating_linked_albums": "Opretter sammenkÃĻdede albums...", "crop": "BeskÃĻr", "curated_object_page_title": "Ting", "current_device": "NuvÃĻrende enhed", @@ -698,10 +763,11 @@ "current_server_address": "NuvÃĻrende serveraddresse", "custom_locale": "Brugerdefineret lokale", "custom_locale_description": "FormatÊr datoer og tal baseret pÃĨ sproget og regionen", + "custom_url": "Tilpasset URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mørk", - "darkTheme": "Skift til mørkt tema", + "dark_theme": "Skift til mørkt tema", "date_after": "Dato efter", "date_and_time": "Dato og klokkeslÃĻt", "date_before": "Dato før", @@ -709,6 +775,7 @@ "date_of_birth_saved": "Fødselsdatoen blev gemt korrekt", "date_range": "Datointerval", "day": "Dag", + "days": "Dage", "deduplicate_all": "Kopier alle", "deduplication_criteria_1": "Billedstørrelse i bytes", "deduplication_criteria_2": "Antal EXIF-data", @@ -717,6 +784,8 @@ "default_locale": "Standardlokalitet", "default_locale_description": "FormatÊr datoer og tal baseret pÃĨ din browsers regions indstillinger", "delete": "Slet", + "delete_action_confirmation_message": "Er du sikker pÃĨ, at du vil slette dette objekt? Denne handling vil flytte objektet til serverens papirkurv, og vil spørge dig, om du vil slette den lokalt", + "delete_action_prompt": "{count} slettet", "delete_album": "Slet album", "delete_api_key_prompt": "Er du sikker pÃĨ, at du vil slette denne API-nøgle?", "delete_dialog_alert": "Disse elementer vil blive slettet permanent fra Immich og din enhed", @@ -730,9 +799,12 @@ "delete_key": "Slet nøgle", "delete_library": "Slet bibliotek", "delete_link": "Slet link", + "delete_local_action_prompt": "{count} slettet lokalt", "delete_local_dialog_ok_backed_up_only": "Slet kun backup", "delete_local_dialog_ok_force": "Slet alligevel", "delete_others": "Slet andre", + "delete_permanently": "Slet permanent", + "delete_permanently_action_prompt": "{count} slettet permanent", "delete_shared_link": "Slet delt link", "delete_shared_link_dialog_title": "Slet delt link", "delete_tag": "Slet tag", @@ -743,6 +815,7 @@ "description": "Beskrivelse", "description_input_hint_text": "Tilføj en beskrivelse...", "description_input_submit_error": "Fejl med at opdatere beskrivelsen. Tjek loggen for flere detaljer", + "deselect_all": "AfmarkÊr alt", "details": "DETALJER", "direction": "Retning", "disabled": "Deaktiveret", @@ -760,6 +833,7 @@ "documentation": "Dokumentation", "done": "FÃĻrdig", "download": "Hent", + "download_action_prompt": "Downloader {count} objekter", "download_canceled": "Download annulleret", "download_complete": "Download fuldført", "download_enqueue": "Donload sat i kø", @@ -786,8 +860,12 @@ "edit": "Rediger", "edit_album": "RedigÊr album", "edit_avatar": "RedigÊr avatar", + "edit_birthday": "Rediger fødselsdag", "edit_date": "RedigÊr dato", "edit_date_and_time": "RedigÊr dato og tid", + "edit_date_and_time_action_prompt": "{count} dato og tid redigeret", + "edit_date_and_time_by_offset": "Forskyde dato med offset", + "edit_date_and_time_by_offset_interval": "Nyt datointerval: {from} - {to}", "edit_description": "Rediger beskrivelse", "edit_description_prompt": "VÃĻlg venligst en ny beskrivelse:", "edit_exclusion_pattern": "RedigÊr udelukkelsesmønster", @@ -797,6 +875,7 @@ "edit_key": "RedigÊr nøgle", "edit_link": "Rediger link", "edit_location": "Rediger placering", + "edit_location_action_prompt": "{count} geolokation redigeret", "edit_location_dialog_title": "Placering", "edit_name": "Rediger navn", "edit_people": "RedigÊr personer", @@ -815,6 +894,7 @@ "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker pÃĨ, at du vil tømme papirkurven? Dette vil fjerne alle objekter i papirkurven permanent fra Immich.\nDu kan ikke fortryde denne handling!", "enable": "AktivÊr", + "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Indtast din PIN kode for at slÃĨ biometrisk adgangskontrol til", "enabled": "Aktiveret", "end_date": "Slutdato", @@ -825,7 +905,9 @@ "error": "Fejl", "error_change_sort_album": "Ændring af sorteringsrÃĻkkefølgen mislykkedes", "error_delete_face": "Fejl ved sletning af ansigt fra mediefil", + "error_getting_places": "Fejl ved hentning af steder", "error_loading_image": "Fejl ved indlÃĻsning af billede", + "error_loading_partners": "Fejl ved indlÃĻsning af partnere: {error}", "error_saving_image": "Fejl: {error}", "error_tag_face_bounding_box": "Fejl ved tagging af ansigt - kan ikke finde koordinator for afgrÃĻnsningskasse", "error_title": "Fejl - Noget gik galt", @@ -858,6 +940,7 @@ "failed_to_load_notifications": "Kunne ikke indlÃĻse notifikationer", "failed_to_load_people": "IndlÃĻsning af personer mislykkedes", "failed_to_remove_product_key": "Fjernelse af produktnøgle mislykkedes", + "failed_to_reset_pin_code": "Kunne ikke resette PIN-koden", "failed_to_stack_assets": "Det lykkedes ikke at stable mediefiler", "failed_to_unstack_assets": "Det lykkedes ikke at fjerne gruperingen af mediefiler", "failed_to_update_notification_status": "Kunne ikke uploade notifikations status", @@ -866,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# sti} other {# stier}} slog fejl ved validering", "profile_picture_transparent_pixels": "Profilbilleder kan ikke have gennemsigtige pixels. Zoom venligst ind og/eller flyt billedet.", "quota_higher_than_disk_size": "Du har sat en kvote der er større end disken", + "something_went_wrong": "Noget gik galt", "unable_to_add_album_users": "Ikke i stand til at tilføje brugere til album", "unable_to_add_assets_to_shared_link": "Kan ikke tilføje mediefiler til det delte link", "unable_to_add_comment": "Ikke i stand til at tilføje kommentar", @@ -951,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Tilføj beskrivelse...", + "exif_bottom_sheet_description_error": "Fejl ved opdatering af beskrivelsen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "LOKATION", "exif_bottom_sheet_people": "PERSONER", "exif_bottom_sheet_person_add_person": "Tilføj navn", - "exif_bottom_sheet_person_age_months": "Alder {months} mÃĨned(er)", - "exif_bottom_sheet_person_age_year_months": "Alder 1 ÃĨr, {months} mÃĨned(er)", - "exif_bottom_sheet_person_age_years": "Alder {years}", "exit_slideshow": "Afslut slideshow", "expand_all": "Udvid alle", "experimental_settings_new_asset_list_subtitle": "Under udarbejdelse", @@ -971,6 +1053,8 @@ "explorer": "Udforske", "export": "EksportÊr", "export_as_json": "EksportÊr som JSON", + "export_database": "Eksporter database", + "export_database_description": "Eksporter SQLite databasen", "extension": "Udvidelse", "external": "Ekstern", "external_libraries": "Eksterne biblioteker", @@ -982,11 +1066,13 @@ "failed_to_load_assets": "Kunne ikke indlÃĻse mediefiler", "failed_to_load_folder": "Kunne ikke indlÃĻse mappe", "favorite": "Favorit", + "favorite_action_prompt": "{count} føjet til favoritter", "favorite_or_unfavorite_photo": "Tilføj eller fjern fra yndlingsbilleder", "favorites": "Favoritter", "favorites_page_no_favorites": "Ingen favoritter blev fundet", "feature_photo_updated": "Forsidebillede uploadet", "features": "Funktioner", + "features_in_development": "Funktioner under udvikling", "features_setting_description": "Administrer app-funktioner", "file_name": "Filnavn", "file_name_or_extension": "Filnavn eller filtype", @@ -996,21 +1082,26 @@ "filter_people": "FiltrÊr personer", "filter_places": "Filtrer steder", "find_them_fast": "Find dem hurtigt med søgning via navn", + "first": "Første", "fix_incorrect_match": "Fix forkert match", "folder": "Mappe", "folder_not_found": "Mappe ikke fundet", "folders": "Mapper", "folders_feature_description": "Gennemse mappevisningen efter fotos og videoer pÃĨ filsystemet", + "forgot_pin_code_question": "Har du glemt PIN-koden?", "forward": "Fremad", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Denne funktion indlÃĻser eksterne ressourcer fra Google for at virke.", "general": "Generel", + "geolocation_instruction_location": "Klik pÃĨ et objekt med GPS-koordinater for at bruge dettes position, eller vÃĻlg position direkte pÃĨ kortet", "get_help": "FÃĨ hjÃĻlp", "get_wifiname_error": "Kunne ikke hente Wi-Fi-navn. Sørg for, at du har givet de nødvendige tilladelser og er forbundet til et Wi-Fi-netvÃĻrk", "getting_started": "Kom godt i gang", "go_back": "GÃĨ tilbage", "go_to_folder": "GÃĨ til mappe", "go_to_search": "GÃĨ til søgning", + "gps": "GPS", + "gps_missing": "Ingen GPS", "grant_permission": "Giv tilladelse", "group_albums_by": "GruppÊr albummer efter...", "group_country": "GruppÊr efter land", @@ -1021,6 +1112,9 @@ "haptic_feedback_switch": "SlÃĨ haptisk feedback til", "haptic_feedback_title": "Haptisk feedback", "has_quota": "Har kvote", + "hash_asset": "Hash objekter", + "hashed_assets": "Hashede objekter", + "hashing": "Hasher", "header_settings_add_header_tip": "Tilføj Header", "header_settings_field_validator_msg": "VÃĻrdi kan ikke vÃĻre tom", "header_settings_header_name_input": "Header navn", @@ -1043,7 +1137,7 @@ "home_page_building_timeline": "Bygger tidslinjen", "home_page_delete_err_partner": "Kan endnu ikke slette partners elementer. Springer over", "home_page_delete_remote_err_local": "Lokale elementer i fjernsletningssektion. Springer over", - "home_page_favorite_err_local": "Kan endnu ikke gøre lokale elementer til favoritter, springer over.", + "home_page_favorite_err_local": "Det er ikke muligt at gøre lokale elementer til favoritter endnu, springer over", "home_page_favorite_err_partner": "Kan endnu ikke tilføje partners elementer som favoritter. Springer over", "home_page_first_time_notice": "Hvis det er din første gang i appen, bedes du vÃĻlge en sikkerhedskopi af albummer sÃĨ tidlinjen kan blive fyldt med billeder og videoer fra albummerne", "home_page_locked_error_local": "Kan ikke flytte lokale mediefiler til lÃĨst mappe, springer over", @@ -1052,7 +1146,9 @@ "home_page_upload_err_limit": "Det er kun muligt at lave sikkerhedskopi af 30 elementer ad gangen. Springer over", "host": "Host", "hour": "Time", + "hours": "Timer", "id": "ID", + "idle": "Inaktiv", "ignore_icloud_photos": "Ignorer iCloud-billeder", "ignore_icloud_photos_description": "Billeder der er gemt pÃĨ iCloud vil ikke blive uploadet til Immich-serveren", "image": "Billede", @@ -1110,10 +1206,13 @@ "language_no_results_title": "Ingen sprog fundet", "language_search_hint": "VÃĻlg sprog...", "language_setting_description": "VÃĻlg dit foretrukne sprog", + "large_files": "Store filer", + "last": "Sidste", "last_seen": "Sidst set", "latest_version": "Seneste version", "latitude": "Breddegrad", "leave": "Forlad", + "leave_album": "Forlad album", "lens_model": "Objektivmodel", "let_others_respond": "Lad andre svare", "level": "Niveau", @@ -1125,16 +1224,20 @@ "library_page_sort_created": "Senest oprettet", "library_page_sort_last_modified": "Sidst redigeret", "library_page_sort_title": "Albumtitel", + "licenses": "Licenser", "light": "Lys", + "like": "Synes om", "like_deleted": "Ligesom slettet", "link_motion_video": "Link bevÃĻgelsesvideo", - "link_options": "Link-indstillinger", "link_to_oauth": "Link til OAuth", "linked_oauth_account": "Tilsluttet OAuth-konto", "list": "Liste", "loading": "IndlÃĻser", "loading_search_results_failed": "IndlÃĻsning af søgeresultater fejlede", + "local": "Lokal", "local_asset_cast_failed": "Kan ikke caste et aktiv, der ikke er uploadet til serveren", + "local_assets": "Lokale objekter", + "local_media_summary": "Opsummering af lokale media", "local_network": "Lokalt netvÃĻrk", "local_network_sheet_info": "Appen vil oprette forbindelse til serveren via denne URL, nÃĨr du bruger det angivne WiFi-netvÃĻrk", "location_permission": "Tilladelse til placering", @@ -1146,8 +1249,10 @@ "location_picker_longitude_hint": "Indtast din lÃĻngdegrad her", "lock": "LÃĨs", "locked_folder": "LÃĨst mappe", + "log_detail_title": "Logdetaljer", "log_out": "Log ud", "log_out_all_devices": "Log ud af alle enheder", + "logged_in_as": "Logget ind som {user}", "logged_out_all_devices": "Logget ud af alle enheder", "logged_out_device": "Logget ud af enhed", "login": "Log ind", @@ -1156,7 +1261,7 @@ "login_form_back_button_text": "Tilbage", "login_form_email_hint": "din-e-mail@e-mail.com", "login_form_endpoint_hint": "http://din-server-ip:port", - "login_form_endpoint_url": "Server Endpoint URL", + "login_form_endpoint_url": "Server endepunkt URL", "login_form_err_http": "Angiv venligst http:// eller https://", "login_form_err_invalid_email": "Ugyldig e-mail", "login_form_err_invalid_url": "Ugyldig webadresse", @@ -1175,13 +1280,15 @@ "login_password_changed_success": "Kodeordet blev opdateret", "logout_all_device_confirmation": "Er du sikker pÃĨ, at du vil logge ud af alle enheder?", "logout_this_device_confirmation": "Er du sikker pÃĨ, at du vil logge denne enhed ud?", - "longitude": "LÃĻngde", + "logs": "Logs", + "longitude": "LÃĻngdegrad", "look": "Kig", "loop_videos": "Gentag videoer", "loop_videos_description": "AktivÊr for at genafspille videoer automatisk i detaljeret visning.", "main_branch_warning": "Du bruger en udviklingsversion; vi anbefaler kraftigt at bruge en udgivelsesversion!", "main_menu": "Hovedmenu", "make": "Producent", + "manage_geolocation": "Administrer placering", "manage_shared_links": "HÃĨndter delte links", "manage_sharing_with_partners": "AdministrÊr deling med partnere", "manage_the_app_settings": "Administrer appindstillinger", @@ -1190,8 +1297,7 @@ "manage_your_devices": "AdministrÊr dine enheder der er logget ind", "manage_your_oauth_connection": "AdministrÊr din OAuth-tilslutning", "map": "Kort", - "map_assets_in_bound": "{count} billede", - "map_assets_in_bounds": "{count} billeder", + "map_assets_in_bounds": "{count, plural, =0 {Ingen billeder i dette omrÃĨde} one {# billede} other {# billeder}}", "map_cannot_get_user_location": "Kan ikke finde brugerens placering", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Brug denne placering", @@ -1199,7 +1305,6 @@ "map_location_service_disabled_title": "Placeringstjenesten er deaktiveret", "map_marker_for_images": "Kortmarkør for billeder taget i {city}, {country}", "map_marker_with_image": "Kortmarkør med billede", - "map_no_assets_in_bounds": "Der er ingen billeder i dette omrÃĨde", "map_no_location_permission_content": "Der krÃĻves tilladelse til placeringen for at vise elementer fra din nuvÃĻrende placering. Vil du give tilladelse?", "map_no_location_permission_title": "Placeringstilladelse blev afvist", "map_settings": "Kortindstillinger", @@ -1218,6 +1323,7 @@ "mark_as_read": "Marker som lÃĻst", "marked_all_as_read": "Markerede alle som lÃĻst", "matches": "Parringer", + "matching_assets": "Matchende objekter", "media_type": "Medietype", "memories": "Minder", "memories_all_caught_up": "Ajour", @@ -1236,6 +1342,7 @@ "merged_people_count": "{count, plural, one {# person} other {# personer}} lagt sammen", "minimize": "MinimÊr", "minute": "Minut", + "minutes": "Minutter", "missing": "Mangler", "model": "Model", "month": "MÃĨned", @@ -1243,6 +1350,7 @@ "more": "Mere", "move": "Flyt", "move_off_locked_folder": "Flyt ud af lÃĨst mappe", + "move_to_lock_folder_action_prompt": "{count} føjet til i den lÃĨste mappe", "move_to_locked_folder": "Flyt til lÃĨst mappe", "move_to_locked_folder_confirmation": "Disse billeder og videoer vil blive fjernet fra alle albums, og vil kun vÃĻre synlig fra den lÃĨste mappe", "moved_to_archive": "Flyttede {count, plural, one {# mediefil} other {# mediefiler}} til arkivet", @@ -1254,6 +1362,10 @@ "my_albums": "Mine albummer", "name": "Navn", "name_or_nickname": "Navn eller kÃĻlenavn", + "network_requirement_photos_upload": "Benyt mobildatanettet for at sikkerhedskopiere dine fotos", + "network_requirement_videos_upload": "Benyt mobildatanettet for at sikkerhedskopiere dine videoer", + "network_requirements": "NetvÃĻrkskrav", + "network_requirements_updated": "NetvÃĻrkskravene er ÃĻndret, backup-køen nulstilles", "networking_settings": "NetvÃĻrk", "networking_subtitle": "Administrer serverens endepunktindstillinger", "never": "aldrig", @@ -1263,6 +1375,7 @@ "new_person": "Ny person", "new_pin_code": "Ny PIN kode", "new_pin_code_subtitle": "Dette er første gang du tilgÃĨr den lÃĨste mappe. Lav en PIN kode for sikkert at tilgÃĨ denne side", + "new_timeline": "Ny tidslinje", "new_user_created": "Ny bruger oprettet", "new_version_available": "NY VERSION TILGÆNGELIG", "newest_first": "Nyeste først", @@ -1276,19 +1389,25 @@ "no_assets_message": "KLIK FOR AT UPLOADE DIT FØRSTE BILLEDE", "no_assets_to_show": "Ingen elementer at vise", "no_cast_devices_found": "Ingen Cast-enheder fundet", + "no_checksum_local": "Ingen checksum tilgÃĻngelig – kan ikke hente lokale objekter", + "no_checksum_remote": "Ingen checksum tilgÃĻngelig – kan ikke hente eksterne objekter", "no_duplicates_found": "Ingen duplikater fundet.", "no_exif_info_available": "Ingen tilgÃĻngelig exif information", "no_explore_results_message": "Upload flere billeder for at udforske din samling.", "no_favorites_message": "Tilføj favoritter for hurtigt at finde dine bedst billeder og videoer", "no_libraries_message": "Opret et eksternt bibliotek for at se dine billeder og videoer", + "no_local_assets_found": "Ingen lokale objekter fundet med denne checksum", "no_locked_photos_message": "Billeder og videoer i den lÃĨste mappe er skjulte og vil ikke blive vist i dit bibliotek.", "no_name": "Intet navn", "no_notifications": "Ingen notifikationer", "no_people_found": "Ingen tilsvarende personer fundet", "no_places": "Ingen steder", + "no_remote_assets_found": "Ingen eksterne objekter fundet med denne checksum", "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller et mere generelt søgeord", "no_shared_albums_message": "Opret et album for at dele billeder og videoer med personer i dit netvÃĻrk", + "no_uploads_in_progress": "Ingen upload i gang", + "not_available": "ikke tilgÃĻngelig", "not_in_any_album": "Ikke i noget album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "BemÃĻrk: For at anvende LagringsmÃĻrkat pÃĨ tidligere uploadede medier, kør", @@ -1304,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Officielle Immich-ressourcer", "offline": "Offline", + "offset": "Forskydning", "ok": "Ok", "oldest_first": "Ældste først", "on_this_device": "PÃĨ denne enhed", @@ -1322,14 +1442,17 @@ "open_the_search_filters": "Åbn søgefiltre", "options": "Handlinger", "or": "eller", + "organize_into_albums": "Organiser i album", + "organize_into_albums_description": "SÃĻt eksisterende billeder i albummer ved hjÃĻlp af aktuelle synkroniseringsindstillinger", "organize_your_library": "OrganisÊr dit bibliotek", "original": "original", "other": "Andet", "other_devices": "Andre enheder", + "other_entities": "Andre enheder", "other_variables": "Andre variable", "owned": "Egne", "owner": "Ejer", - "partner": "Partner", + "partner": "Partnerpartner", "partner_can_access": "{partner} kan tilgÃĨ", "partner_can_access_assets": "Alle dine billeder og videoer, bortset fra dem i Arkivet og Slettet", "partner_can_access_location": "Stedet, hvor dine billeder blev taget", @@ -1379,7 +1502,10 @@ "permission_onboarding_permission_granted": "Tilladelse givet! Du er nu klar.", "permission_onboarding_permission_limited": "Tilladelse begrÃĻnset. For at lade Immich lave sikkerhedskopi og styre hele dit galleri, skal der gives tilladelse til billeder og videoer i indstillinger.", "permission_onboarding_request": "Immich krÃĻver tilliadelse til at se dine billeder og videoer.", - "person": "Person", + "person": "Personperson", + "person_age_months": "{months, plural, one {# month} other {# months}} gammel", + "person_age_year_months": "1 ÃĨr, {months, plural, one {# month} other {# months}} gammel", + "person_age_years": "{years, plural, other {# years}} gammel", "person_birthdate": "Født den {date}", "person_hidden": "{name}{hidden, select, true { (skjult)} other {}}", "photo_shared_all_users": "Det ser ud til, at du har delt dine billeder med alle brugere, eller ogsÃĨ har du ikke nogen bruger at dele med.", @@ -1403,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Administrer app-prÃĻferencer", "preferences_settings_title": "PrÃĻferencer", + "preparing": "Forberedelse", "preset": "Forudindstilling", "preview": "ForhÃĨndsvisning", "previous": "Forrige", @@ -1419,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Mobilapp er forÃĻldet. Opdater venligst til den nyeste mindre version.", "profile_drawer_client_server_up_to_date": "Klient og server er ajour", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Skrivebeskyttet tilstand aktiveret. Lang tryk pÃĨ bruger avatar ikonet for at afslutte.", "profile_drawer_server_out_of_date_major": "Server er forÃĻldet. Opdater venligst til den nyeste større version.", "profile_drawer_server_out_of_date_minor": "Server er forÃĻldet. Opdater venligst til den nyeste mindre version.", "profile_image_of_user": "Profilbillede af {user}", @@ -1454,15 +1582,20 @@ "purchase_remove_server_product_key": "Fjern serverens produktnøgle", "purchase_remove_server_product_key_prompt": "Er du sikker pÃĨ, at du vil fjerne serverproduktnøglen?", "purchase_server_description_1": "For hele serveren", - "purchase_server_description_2": "Supporter status", + "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serverens produktnøgle administreres af administratoren", + "query_asset_id": "Forespørgsels Asset ID", + "queue_status": "Kø {count}/{total}", "rating": "Stjernebedømmelse", "rating_clear": "Nulstil vurdering", "rating_count": "{count, plural, one {# stjerne} other {# stjerner}}", "rating_description": "Vis EXIF-klassificeringen i infopanelet", "reaction_options": "Reaktionsindstillinger", "read_changelog": "LÃĻs ÃĻndringslog", + "readonly_mode_disabled": "Skrivebeskyttet tilstand deaktiveret", + "readonly_mode_enabled": "Skrivebeskyttet tilstand aktiveret", + "ready_for_upload": "Klar til upload", "reassign": "Gentildel", "reassigned_assets_to_existing_person": "{count, plural, one {# mediefil} other {# mediefiler}} er blevet gentildelt til {name, select, null {en eksisterende person} other {{name}}}", "reassigned_assets_to_new_person": "Gentildelt {count, plural, one {# aktiv} other {# aktiver}} til en ny person", @@ -1485,6 +1618,9 @@ "refreshing_faces": "Opdaterer ansigter", "refreshing_metadata": "Opdaterer metadata", "regenerating_thumbnails": "Regenererer forhÃĨndsvisninger", + "remote": "Eksternt", + "remote_assets": "Eksterne objekter", + "remote_media_summary": "Oversigt over eksterne media", "remove": "Fjern", "remove_assets_album_confirmation": "Er du sikker pÃĨ, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra albummet?", "remove_assets_shared_link_confirmation": "Er du sikker pÃĨ, at du vil fjerne {count, plural, one {# aktiv} other {# aktiver}} fra dette delte link?", @@ -1492,7 +1628,9 @@ "remove_custom_date_range": "Fjern tilpasset datointerval", "remove_deleted_assets": "Fjern slettede mediefiler", "remove_from_album": "Fjern fra album", + "remove_from_album_action_prompt": "{count} fjernet fra albummet", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_lock_folder_action_prompt": "{count} fjernet fra den lÃĨste mappe", "remove_from_locked_folder": "Fjern fra lÃĨst mappe", "remove_from_locked_folder_confirmation": "Er du sikker pÃĨ at du vil flytte disse billeder og videoer ud af den lÃĨste mappe? De vil vÃĻre synlige i dit bibliotek.", "remove_from_shared_link": "Fjern fra delt link", @@ -1520,19 +1658,29 @@ "reset_password": "Nulstil adgangskode", "reset_people_visibility": "Nulstil personsynlighed", "reset_pin_code": "Nulstil PIN kode", + "reset_pin_code_description": "Hvis du har glemt din PIN-kode, kan du kontakte serveradministratoren for at fÃĨ den stillet tilbage", + "reset_pin_code_success": "PIN-koden er stillet tilbage", + "reset_pin_code_with_password": "Du kan altid nulstille din PIN-kode med dit password", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Er du sikker pÃĨ, at du vil nulstille SQLite databasen? Du er nødt til at logge ud og ind igen for at gensynkronisere dine data", + "reset_sqlite_success": "Vellykket reset af SQLite databasen", "reset_to_default": "Nulstil til standard", "resolve_duplicates": "Løs dubletter", "resolved_all_duplicates": "Alle dubletter løst", "restore": "Gendan", "restore_all": "Gendan alle", + "restore_trash_action_prompt": "{count} genskabt fra papirkurven", "restore_user": "Gendan bruger", "restored_asset": "Gendannet mediefilen", "resume": "Genoptag", + "resume_paused_jobs": "FortsÃĻt {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Forsøg upload igen", "review_duplicates": "GennemgÃĨ dubletter", + "review_large_files": "GennemgÃĨ store filer", "role": "Rolle", "role_editor": "Redaktør", "role_viewer": "Seer", + "running": "Kører", "save": "Gem", "save_to_gallery": "Gem til galleri", "saved_api_key": "Gemt API-nøgle", @@ -1605,6 +1753,7 @@ "select_album_cover": "VÃĻlg albumcover", "select_all": "VÃĻlg alle", "select_all_duplicates": "VÃĻlg alle dubletter", + "select_all_in": "VÃĻlg alt i {group}", "select_avatar_color": "VÃĻlg avatarfarve", "select_face": "VÃĻlg ansigt", "select_featured_photo": "VÃĻlg forsidebillede", @@ -1618,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Fejlede i at oprette et nyt album", "selected": "Valgt", "selected_count": "{count, plural, one {# valgt} other {# valgte}}", + "selected_gps_coordinates": "Udvalgte GPS Koordinater", "send_message": "Send besked", "send_welcome_email": "Send velkomstemail", "server_endpoint": "Server endepunkt", @@ -1643,7 +1793,7 @@ "setting_image_viewer_preview_title": "IndlÃĻs forhÃĨndsvisning af billedet", "setting_image_viewer_title": "Billeder", "setting_languages_apply": "Anvend", - "setting_languages_subtitle": "Ændrer app-sprog", + "setting_languages_subtitle": "Ændr app-sprog", "setting_notifications_notify_failures_grace_period": "Giv besked om fejl med sikkerhedskopiering i baggrunden: {duration}", "setting_notifications_notify_hours": "{count} timer", "setting_notifications_notify_immediately": "med det samme", @@ -1655,7 +1805,7 @@ "setting_notifications_subtitle": "Tilpas dine notifikationsprÃĻferencer", "setting_notifications_total_progress_subtitle": "Samlet uploadstatus (fÃĻrdige/samlet antal elementer)", "setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus", - "setting_video_viewer_looping_title": "Looping", + "setting_video_viewer_looping_title": "Looper", "setting_video_viewer_original_video_subtitle": "NÃĨr der streames video fra serveren, afspil da den originale selv nÃĨr en omkodet udgave er tilgÃĻngelig. Kan føre til buffering. Videoer, der er tilgÃĻngelige lokalt, afspilles i original kvalitet uanset denne indstilling.", "setting_video_viewer_original_video_title": "Tving original video", "settings": "Indstillinger", @@ -1663,6 +1813,7 @@ "settings_saved": "Indstillinger er gemt", "setup_pin_code": "SÃĻt in PIN kode", "share": "Del", + "share_action_prompt": "Delte {count} objekter", "share_add_photos": "Tilføj billeder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder...", @@ -1684,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Kopieret til udklipsholderen", "shared_link_clipboard_text": "Link: {link}\nAdgangskode: {password}", "shared_link_create_error": "Der opstod en fejl i oprettelsen af et delt link", + "shared_link_custom_url_description": "Adgang til dette delte link med en selvdefineret URL", "shared_link_edit_description_hint": "Indtast beskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dage", @@ -1709,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "HÃĨndter delte links", "shared_link_options": "Muligheder for delt link", + "shared_link_password_description": "KrÃĻv et kodeord for at fÃĨ adgang til dette delte link", "shared_links": "Delte links", "shared_links_description": "Del billeder og videoer med et link", "shared_photos_and_videos_count": "{assetCount, plural, other {# delte billeder & videoer.}}", @@ -1743,6 +1896,7 @@ "show_slideshow_transition": "Vis overgang til diasshow", "show_supporter_badge": "SupportermÃĻrke", "show_supporter_badge_description": "Vis et supportermÃĻrke", + "show_text_search_menu": "Vis tekstsøgningsmenu", "shuffle": "Bland", "sidebar": "SidebjÃĻlke", "sidebar_display_description": "Vis et link til visningen i sidebjÃĻlken", @@ -1758,12 +1912,14 @@ "sort_created": "Dato oprettet", "sort_items": "Antal genstande", "sort_modified": "Ændret dato", + "sort_newest": "Nyeste foto", "sort_oldest": "Ældste foto", "sort_people_by_similarity": "Sorter efter personer der ligner hinanden", "sort_recent": "Seneste foto", "sort_title": "Titel", "source": "Kilde", "stack": "Stak", + "stack_action_prompt": "{count} stakket", "stack_duplicates": "Stak dubletter", "stack_select_one_photo": "VÃĻlg Êt hovedbillede til stakken", "stack_selected_photos": "Stak valgte billeder", @@ -1771,9 +1927,10 @@ "stacktrace": "Stacktrace", "start": "Start", "start_date": "Startdato", + "start_date_before_end_date": "Startdato skal ligge før slutdato", "state": "Stat", "status": "Status", - "stop_casting": "Stop casting", + "stop_casting": "Stop støbning", "stop_motion_photo": "Stopmotionbillede", "stop_photo_sharing": "Stop med at dele dine billeder?", "stop_photo_sharing_description": "{partner} vil ikke lÃĻngere kunne tilgÃĨ dine billeder.", @@ -1783,6 +1940,7 @@ "storage_quota": "Lagringskvota", "storage_usage": "{used} ud af {available} brugt", "submit": "Indsend", + "success": "Vellykket", "suggestions": "Anbefalinger", "sunrise_on_the_beach": "Solopgang pÃĨ stranden", "support": "Support", @@ -1792,6 +1950,10 @@ "sync": "SynkronisÊr", "sync_albums": "Synkroniser albummer", "sync_albums_manual_subtitle": "Synkroniser alle uploadet billeder og videoer til de valgte backupalbummer", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", + "sync_status": "Synkroniserings Status", + "sync_status_subtitle": "Se og administrÊr synkroniseringssystemet", "sync_upload_album_setting_subtitle": "Opret og upload dine billeder og videoer til de valgte albummer i Immich", "tag": "Tag", "tag_assets": "Tag mediefiler", @@ -1802,6 +1964,7 @@ "tag_updated": "Opdateret tag: {tag}", "tagged_assets": "Tagget {count, plural, one {# aktiv} other {# aktiver}}", "tags": "Tags", + "tap_to_run_job": "Tryk for at køre jobbet", "template": "Skabelon", "theme": "Tema", "theme_selection": "Temavalg", @@ -1828,12 +1991,15 @@ "to_change_password": "Skift adgangskode", "to_favorite": "Gør til favorit", "to_login": "Login", + "to_multi_select": "For at vÃĻlge flere", "to_parent": "GÃĨ op", + "to_select": "for at vÃĻlge", "to_trash": "Papirkurv", "toggle_settings": "SlÃĨ indstillinger til eller fra", "total": "Total", "total_usage": "Samlet forbrug", "trash": "Papirkurv", + "trash_action_prompt": "{count} flyttet til papirkurven", "trash_all": "Smid alle ud", "trash_count": "Slet {count, number}", "trash_delete_asset": "Flyt mediefil til Papirkurv", @@ -1847,13 +2013,16 @@ "trash_page_select_assets_btn": "VÃĻlg elementer", "trash_page_title": "Papirkurv ({count})", "trashed_items_will_be_permanently_deleted_after": "Mediefiler i skraldespanden vil blive slettet permanent efter {days, plural, one {# dag} other {# dage}}.", + "troubleshoot": "Fejlfinding", "type": "Type", "unable_to_change_pin_code": "Kunne ikke ÃĻndre PIN kode", "unable_to_setup_pin_code": "Kunne ikke sÃĻtte PIN kode", "unarchive": "AfakivÊr", + "unarchive_action_prompt": "{count} slettet fra Arkiv", "unarchived_count": "{count, plural, other {Uarkiveret #}}", "undo": "Fortryd", "unfavorite": "Fjern favorit", + "unfavorite_action_prompt": "{count} slettet fra Favoritter", "unhide_person": "Stop med at skjule person", "unknown": "Ukendt", "unknown_country": "Ukendt land", @@ -1869,16 +2038,23 @@ "unsaved_change": "Ændring, der ikke er gemt", "unselect_all": "FravÃĻlg alle", "unselect_all_duplicates": "Fjern markeringen af alle dubletter", + "unselect_all_in": "AfmarkÊr alle i {group}", "unstack": "Fjern fra stak", + "unstack_action_prompt": "{count} ustakket", "unstacked_assets_count": "Ikke-stablet {count, plural, one {# aktiv} other {# aktiver}}", + "untagged": "UmÃĻrket", "up_next": "NÃĻste", + "update_location_action_prompt": "Opdater lokationen for {count} valgte objekter med:", "updated_at": "Opdateret", "updated_password": "Opdaterede adgangskode", "upload": "Upload", + "upload_action_prompt": "{count} i kø til upload", "upload_concurrency": "Upload samtidighed", + "upload_details": "Upload detaljer", "upload_dialog_info": "Vil du sikkerhedskopiere de(t) valgte element(er) til serveren?", "upload_dialog_title": "Upload element", "upload_errors": "Upload afsluttet med {count, plural, one {# fejl} other {# fejl}}. Opdater siden for at se nye uploadaktiver.", + "upload_finished": "Upload fuldført", "upload_progress": "Resterende {remaining, number} - Behandlet {processed, number}/{total, number}", "upload_skipped_duplicates": "Sprang over {count, plural, one {# duplet aktiv} other {# duplikerede aktiver}}", "upload_status_duplicates": "Dubletter", @@ -1887,6 +2063,7 @@ "upload_success": "Upload gennemført. Opdater siden for at se nye uploadaktiver.", "upload_to_immich": "Upload til Immich ({count})", "uploading": "Uploader", + "uploading_media": "Uploader media", "url": "URL", "usage": "Forbrug", "use_biometric": "Brug biometrisk", @@ -1907,6 +2084,7 @@ "user_usage_stats_description": "Vis konto anvendelsesstatistik", "username": "Brugernavn", "users": "Brugere", + "users_added_to_album_count": "Føjet {count, plural, one {# bruker} other {# brukere}} til albummet", "utilities": "VÃĻrktøjer", "validate": "ValidÊr", "validate_endpoint_error": "Indtast en gyldig URL", @@ -1925,6 +2103,7 @@ "view_album": "Se album", "view_all": "Se alle", "view_all_users": "Se alle brugere", + "view_details": "Vis detaljer", "view_in_timeline": "Se pÃĨ tidslinjen", "view_link": "Vis Link", "view_links": "Vis links", @@ -1932,6 +2111,7 @@ "view_next_asset": "Se nÃĻste medie", "view_previous_asset": "Se forrige medie", "view_qr_code": "Vis QR kode", + "view_similar_photos": "Se lignende billeder", "view_stack": "Vis stak", "view_user": "Vis bruger", "viewer_remove_from_stack": "Fjern fra stak", @@ -1950,5 +2130,6 @@ "yes": "Ja", "you_dont_have_any_shared_links": "Du har ikke nogen delte links", "your_wifi_name": "Dit Wi-Fi navn", - "zoom_image": "Zoom billede" + "zoom_image": "Zoom billede", + "zoom_to_bounds": "Zoom til grÃĻnserne" } diff --git a/i18n/de.json b/i18n/de.json index bbcd9c569c..69e36be8e9 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -14,10 +14,11 @@ "add_a_location": "Standort hinzufÃŧgen", "add_a_name": "Name hinzufÃŧgen", "add_a_title": "Titel hinzufÃŧgen", + "add_birthday": "Geburtsdatum hinzufÃŧgen", "add_endpoint": "Endpunkt hinzufÃŧgen", "add_exclusion_pattern": "Ausschlussmuster hinzufÃŧgen", "add_import_path": "Importpfad hinzufÃŧgen", - "add_location": "Ort hinzufÃŧgen", + "add_location": "Standort hinzufÃŧgen", "add_more_users": "Weitere Nutzer hinzufÃŧgen", "add_partner": "Partner hinzufÃŧgen", "add_path": "Pfad hinzufÃŧgen", @@ -27,6 +28,10 @@ "add_to_album": "Zu Album hinzufÃŧgen", "add_to_album_bottom_sheet_added": "Zu {album} hinzugefÃŧgt", "add_to_album_bottom_sheet_already_exists": "Bereits in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Einige lokale Dateien konnten nicht zum Album hinzugefÃŧgt werden", + "add_to_album_toggle": "Auswahl umschalten fÃŧr {album}", + "add_to_albums": "Zu Alben hinzufÃŧgen", + "add_to_albums_count": "Zu Alben hinzufÃŧgen ({count})", "add_to_shared_album": "Zu geteiltem Album hinzufÃŧgen", "add_url": "URL hinzufÃŧgen", "added_to_archive": "Zum Archiv hinzugefÃŧgt", @@ -41,9 +46,16 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbanksicherung regelmäßig erstellen", + "backup_database": "Datenbanksicherung erstellen", "backup_database_enable_description": "Datenbank regelmäßig sichern", "backup_keep_last_amount": "Anzahl der aufzubewahrenden frÃŧheren Backups", + "backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.", + "backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehÃļren die Hauptdateien und eine lokale Sicherung dieser Dateien.", + "backup_onboarding_3_description": "3 komplette Kopien deiner Daten, inkl. der Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopie.", + "backup_onboarding_description": "Eine 3-2-1 Backup-Strategie wird empfohlen, um deine Daten zu schÃŧtzen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Backup-LÃļsung zu haben.", + "backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der Dokumentation.", + "backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:", + "backup_onboarding_title": "Backups", "backup_settings": "Einstellungen fÃŧr Datenbanksicherung", "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht Ãŧberwacht und du wirst nicht Ãŧber Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurÃŧckgesetzt: {job}", @@ -56,9 +68,9 @@ "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurÃŧcksetzen mÃļchtest?", "create_job": "Aufgabe erstellen", "cron_expression": "Cron Zeitangabe", - "cron_expression_description": "Setze ein Intervall fÃŧr die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der Crontab Guru", + "cron_expression_description": "Setze ein Intervall fÃŧr die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", "cron_expression_presets": "NÃŧtzliche Zeitangaben fÃŧr Cron", - "disable_login": "Login deaktvieren", + "disable_login": "Login deaktivieren", "duplicate_detection_job_description": "Diese Aufgabe fÃŧhrt das maschinelle Lernen fÃŧr jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern kÃļnnen Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nÃŧtzlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren mÃļchtest, wie z. B. RAW-Dateien.", "external_library_management": "Verwaltung externer Bibliotheken", @@ -112,6 +124,13 @@ "logging_enable_description": "Aktiviere Logging", "logging_level_description": "Wenn aktiviert, welches Log-Level genutzt wird.", "logging_settings": "Protokollierung", + "machine_learning_availability_checks": "VerfÃŧgbarkeitschecks", + "machine_learning_availability_checks_description": "Erkenne und bevorzuge verfÃŧgbare Machine Learning Servers", + "machine_learning_availability_checks_enabled": "VerfÃŧgbarkeitschecks einschalten", + "machine_learning_availability_checks_interval": "ÜberprÃŧfungsinterval", + "machine_learning_availability_checks_interval_description": "Interval in Millisekunden zwischen VerfÃŧgbarkeitschecks", + "machine_learning_availability_checks_timeout": "AnfragenzeitÃŧberschreitung", + "machine_learning_availability_checks_timeout_description": "ZeitÃŧberschreitung in Millisekunden fÃŧr VerfÃŧgbarkeitschecks", "machine_learning_clip_model": "CLIP-Modell", "machine_learning_clip_model_description": "Der Name eines CLIP-Modells, welches hier aufgefÃŧhrt ist. Beachte, dass du die Aufgabe \"Intelligente Suche\" fÃŧr alle Bilder erneut ausfÃŧhren musst, wenn du das Modell wechselst.", "machine_learning_duplicate_detection": "Duplikaterkennung", @@ -120,7 +139,7 @@ "machine_learning_duplicate_detection_setting_description": "Verwendung von CLIP-Embeddings zum Erkennen mÃļglicher Duplikate", "machine_learning_enabled": "Maschinelles Lernen aktivieren", "machine_learning_enabled_description": "Wenn diese Option deaktiviert ist, werden alle ML-Funktionen unabhängig von den unten aufgefÃŧhrten Einstellungen deaktiviert.", - "machine_learning_facial_recognition": "Gesichtsidentifikation", + "machine_learning_facial_recognition": "Gesichtsidentifizierung", "machine_learning_facial_recognition_description": "Erkenne, identifiziere und gruppiere Gesichter in Bildern", "machine_learning_facial_recognition_model": "Gesichtserkennungs-Modell", "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer GrÃļße aufgefÃŧhrt. GrÃļßere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du die Gesichtserkennungsaufgabe fÃŧr alle Bilder neu starten musst, wenn du ein Modell änderst.", @@ -166,6 +185,20 @@ "metadata_settings_description": "Metadaten-Einstellungen verwalten", "migration_job": "Migration", "migration_job_description": "Diese Aufgabe migriert Miniaturansichten fÃŧr Dateien und Gesichter in die neueste Ordnerstruktur", + "nightly_tasks_cluster_faces_setting_description": "Gesichtsidentifikation auf neu erkannten Gesichtern ausfÃŧhren", + "nightly_tasks_cluster_new_faces_setting": "Neue Gesichter gruppieren", + "nightly_tasks_database_cleanup_setting": "Datenbankbereinigungs-Aufgaben", + "nightly_tasks_database_cleanup_setting_description": "Alte, abgelaufene Daten aus der Datenbank bereinigen", + "nightly_tasks_generate_memories_setting": "Erinnerungen generieren", + "nightly_tasks_generate_memories_setting_description": "Neue Erinnerungen aus Dateien erstellen", + "nightly_tasks_missing_thumbnails_setting": "Fehlende Miniaturansichten generieren", + "nightly_tasks_missing_thumbnails_setting_description": "Dateien ohne Miniaturansicht in die Warteschlange zur Miniaturansicht-Generierung hinzufÃŧgen", + "nightly_tasks_settings": "Einstellungen fÃŧr nächtliche Aufgaben", + "nightly_tasks_settings_description": "Nächtliche Aufgaben verwalten", + "nightly_tasks_start_time_setting": "Startzeit", + "nightly_tasks_start_time_setting_description": "Die Zeit, zu der der Server mit der AusfÃŧhrung der nächtlichen Aufgaben beginnt", + "nightly_tasks_sync_quota_usage_setting": "Kontingentnutzung synchronisieren", + "nightly_tasks_sync_quota_usage_setting_description": "Benutzerspeicherkontingent basierend auf der aktuellen Nutzung aktualisieren", "no_paths_added": "Keine Pfade hinzugefÃŧgt", "no_pattern_added": "Kein Ausschlussmuster hinzugefÃŧgt", "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobile Umleitungs-URI", "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI Ãŧberschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt", + "oauth_role_claim": "Rollen-Claim", + "oauth_role_claim_description": "Gewähre automatisch Admin-Zugriff basierend auf dem Vorhandensein dieses Claims. Der Claim kann entweder 'user' oder 'admin' sein.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", @@ -244,6 +279,7 @@ "storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur fÃŧr neue Dateien. Um die Vorlage rÃŧckwirkend auf bereits hochgeladene Assets anzuwenden, fÃŧhre den {job} aus.", "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", + "storage_template_onboarding_description_v2": "Wenn aktiviert, werden Dateien automatisch nach einer benutzerdefinierten Vorlage organisiert. FÃŧr mehr Informationen siehe die Dokumentation.", "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", @@ -330,6 +366,9 @@ "trash_number_of_days_description": "Anzahl der Tage, welche die Objekte im Papierkorb verbleiben, bevor sie endgÃŧltig entfernt werden", "trash_settings": "Papierkorbeinstellungen", "trash_settings_description": "Papierkorbeinstellungen verwalten", + "unlink_all_oauth_accounts": "Aus allen OAuth Konten ausloggen", + "unlink_all_oauth_accounts_description": "Denken Sie daran, alle OAuth Konten zu deaktivieren, bevor Sie zu einem neuen Anbieter migrieren.", + "unlink_all_oauth_accounts_prompt": "Sind Sie sich sicher, dass Sie alle OAuth Konten deaktivieren mÃļchten? Diese Aktion kann nicht rÃŧckgängig gemacht werden und wird außerdem die OAuth ID aller Benutzer zurÃŧcksetzen.", "user_cleanup_job": "Benutzer aufräumen", "user_delete_delay": "Das Konto und die Dateien von {user} werden in {delay, plural, one {einem Tag} other {# Tagen}} fÃŧr eine permanente LÃļschung geplant.", "user_delete_delay_settings": "VerzÃļgerung fÃŧr das LÃļschen von Benutzern", @@ -359,14 +398,16 @@ "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: {level}", - "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_subtitle": "Einige Geräte sind sehr langsam beim Laden von lokalen Vorschaubildern. 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_readonly_mode_subtitle": "Aktiviert den schreibgeschÃŧtzten Modus, in dem die Fotos nur angezeigt werden kÃļnnen. Funktionen wie das Auswählen mehrerer Bilder, das Teilen, das Übertragen und das LÃļschen sind deaktiviert. Aktivieren/Deaktiviere den schreibgeschÃŧtzten Modus Ãŧber den Benutzer-Avatar auf dem Hauptbildschirm", + "advanced_settings_readonly_mode_title": "SchreibgeschÃŧtzter Modus", "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_sync_remote_deletions_title": "Mit Server-LÃļschungen synchronisieren [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -378,6 +419,7 @@ "album_cover_updated": "Album-Cover aktualisiert", "album_delete_confirmation": "Bist du sicher, dass du das Album {album} lÃļschen willst?", "album_delete_confirmation_description": "Falls dieses Album geteilt wurde, kÃļnnen andere Benutzer nicht mehr darauf zugreifen.", + "album_deleted": "Album gelÃļscht", "album_info_card_backup_album_excluded": "AUSGESCHLOSSEN", "album_info_card_backup_album_included": "EINGESCHLOSSEN", "album_info_updated": "Album-Infos aktualisiert", @@ -387,7 +429,9 @@ "album_options": "Albumoptionen", "album_remove_user": "Nutzer entfernen?", "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", + "album_search_not_found": "Keine Alben gefunden, die zur Suche passen", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", + "album_summary": "Album Zusammenfassung", "album_updated": "Album aktualisiert", "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", "album_user_left": "{album} verlassen", @@ -406,6 +450,7 @@ "albums_default_sort_order": "Standard Album Sortierung", "albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.", "albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden kÃļnnen.", + "albums_on_device_count": "Alben auf dem Gerät ({count})", "all": "Alle", "all_albums": "Alle Alben", "all_people": "Alle Personen", @@ -425,7 +470,9 @@ "app_bar_signout_dialog_title": "Abmelden", "app_settings": "App-Einstellungen", "appears_in": "Erscheint in", + "apply_count": "Anwenden ({count, number})", "archive": "Archiv", + "archive_action_prompt": "{count} zum Archiv hinzugefÃŧgt", "archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben", "archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden", "archive_page_title": "Archiv ({count})", @@ -456,15 +503,18 @@ "asset_restored_successfully": "Datei erfolgreich wiederhergestellt", "asset_skipped": "Übersprungen", "asset_skipped_in_trash": "Im Papierkorb", + "asset_trashed": "Datei GelÃļscht", + "asset_troubleshoot": "Datei Fehlerbehebung", "asset_uploaded": "Hochgeladen", "asset_uploading": "Hochladenâ€Ļ", - "asset_viewer_settings_subtitle": "Verwaltung der Einstellungen fÃŧr den Galerie-Viewer", + "asset_viewer_settings_subtitle": "Verwaltung der Einstellungen fÃŧr die Fotoanzeige", "asset_viewer_settings_title": "Fotoanzeige", "assets": "Dateien", "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefÃŧgt", "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefÃŧgt", - "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefÃŧgt", + "assets_added_to_albums_count": "{assetTotal, plural, one {# Datei} other {# Dateien}} zu {albumTotal, plural, one {# Album} other {# Alben}} hinzugefÃŧgt", "assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien kÃļnnen}} nicht zum Album hinzugefÃŧgt werden", + "assets_cannot_be_added_to_albums": "{count, plural, one {Datei kann} other {Dateien kÃļnnen}} nicht zu den Alben hinzugefÃŧgt werden", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_deleted_permanently": "{count} Element(e) permanent gelÃļscht", "assets_deleted_permanently_from_server": "{count} Element(e) permanent vom Immich-Server gelÃļscht", @@ -481,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# Datei} other {# Dateien}} in den Papierkorb verschoben", "assets_trashed_from_server": "{count} Element(e) vom Immich-Server gelÃļscht", "assets_were_part_of_album_count": "{count, plural, one {# Datei ist} other {# Dateien sind}} bereits im Album vorhanden", + "assets_were_part_of_albums_count": "{count, plural, one {Datei war} other {Dateien waren}} bereits in den Alben", "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_title": "Automatische URL-Umschaltung", "autoplay_slideshow": "Automatische Diashow", "back": "ZurÃŧck", "back_close_deselect": "ZurÃŧck, Schließen oder Abwählen", + "background_backup_running_error": "Hintergrund Sicherung läuft, kann manuelle Sicherung nicht starten", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu kÃļnnen, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", + "background_options": "Hintergrund Optionen", + "backup": "Sicherung", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) kÃļnnen sich Ãŧber mehrere Alben verteilen. Daher kÃļnnen diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", "backup_album_selection_page_select_albums": "Alben auswählen", "backup_album_selection_page_selection_info": "Information", "backup_album_selection_page_total_assets": "Elemente", + "backup_albums_sync": "Synchronisation von Alben beim Backup", "backup_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â€Ļ", @@ -544,13 +599,16 @@ "backup_controller_page_turn_on": "Sicherung im Vordergrund einschalten", "backup_controller_page_uploading_file_info": "Informationen", "backup_err_only_album": "Das einzige Album kann nicht entfernt werden", + "backup_error_sync_failed": "Synchronisierung fehlgeschlagen. Backup kann nicht verarbeitet werden.", "backup_info_card_assets": "Elemente", "backup_manual_cancelled": "Abgebrochen", "backup_manual_in_progress": "Sicherung läuft bereits. Bitte versuche es später erneut", "backup_manual_success": "Erfolgreich", "backup_manual_title": "Sicherungsstatus", + "backup_options": "Backup-Optionen", "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", + "backup_settings_subtitle": "Upload-Einstellungen verwalten", "backward": "RÃŧckwärts", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", @@ -586,6 +644,7 @@ "cancel": "Abbrechen", "cancel_search": "Suche abbrechen", "canceled": "Abgebrochen", + "canceling": "Abbrechen", "cannot_merge_people": "Personen kÃļnnen nicht zusammengefÃŧhrt werden", "cannot_undo_this_action": "Diese Aktion kann nicht rÃŧckgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", @@ -595,7 +654,7 @@ "change_description": "Beschreibung anpassen", "change_display_order": "Anzeigereihenfolge ändern", "change_expiration_time": "Verfallszeitpunkt ändern", - "change_location": "Ort ändern", + "change_location": "Standort ändern", "change_name": "Name ändern", "change_name_successfully": "Name wurde erfolgreich geändert", "change_password": "Passwort ändern", @@ -608,6 +667,8 @@ "change_pin_code": "PIN Code ändern", "change_your_password": "Ändere dein Passwort", "changed_visibility_successfully": "Die Sichtbarkeit wurde erfolgreich geändert", + "charging": "Aufladen", + "charging_requirement_mobile_backup": "Backup im Hintergrund erfordert Aufladen des Geräts", "check_corrupt_asset_backup": "Auf beschädigte Asset-Backups ÃŧberprÃŧfen", "check_corrupt_asset_backup_button": "ÜberprÃŧfung durchfÃŧhren", "check_corrupt_asset_backup_description": "FÃŧhre diese PrÃŧfung nur mit aktivierten WLAN durch, nachdem alle Dateien gesichert worden sind. Dieser Vorgang kann ein paar Minuten dauern.", @@ -617,6 +678,7 @@ "clear": "Leeren", "clear_all": "Alles leeren", "clear_all_recent_searches": "Alle letzten Suchvorgänge lÃļschen", + "clear_file_cache": "Dateien-Cache leeren", "clear_message": "Nachrichten leeren", "clear_value": "Wert leeren", "client_cert_dialog_msg_confirm": "Ok", @@ -657,7 +719,7 @@ "control_bottom_app_bar_create_new_album": "Neues Album erstellen", "control_bottom_app_bar_delete_from_immich": "Aus Immich lÃļschen", "control_bottom_app_bar_delete_from_local": "Vom Gerät lÃļschen", - "control_bottom_app_bar_edit_location": "Ort bearbeiten", + "control_bottom_app_bar_edit_location": "Standort bearbeiten", "control_bottom_app_bar_edit_time": "Datum und Uhrzeit bearbeiten", "control_bottom_app_bar_share_link": "Link teilen", "control_bottom_app_bar_share_to": "Teilen mit", @@ -687,11 +749,13 @@ "create_new_user": "Neuen Nutzer erstellen", "create_shared_album_page_share_add_assets": "INHALTE HINZUFÜGEN", "create_shared_album_page_share_select_photos": "Fotos auswählen", + "create_shared_link": "Geteilten Link erstellen", "create_tag": "Tag erstellen", "create_tag_description": "Erstelle einen neuen Tag. FÃŧr verschachtelte Tags, gib den gesamten Pfad inklusive Schrägstrich an.", "create_user": "Nutzer erstellen", "created": "Erstellt", "created_at": "Erstellt", + "creating_linked_albums": "Erstelle verknÃŧpfte Alben...", "crop": "Zuschneiden", "curated_object_page_title": "Dinge", "current_device": "Aktuelles Gerät", @@ -699,10 +763,11 @@ "current_server_address": "Aktuelle Serveradresse", "custom_locale": "Benutzerdefinierte Sprache", "custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren", + "custom_url": "Benutzerdefinierte URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", - "darkTheme": "Dunkles Theme umschalten", + "dark_theme": "Dunkle Ansicht umschalten", "date_after": "Datum nach", "date_and_time": "Datum und Zeit", "date_before": "Datum vor", @@ -710,6 +775,7 @@ "date_of_birth_saved": "Das Geburtsdatum wurde erfolgreich gespeichert", "date_range": "Datumsbereich", "day": "Tag", + "days": "Tage", "deduplicate_all": "Alle Duplikate entfernen", "deduplication_criteria_1": "BildgrÃļße in Bytes", "deduplication_criteria_2": "Anzahl der EXIF-Daten", @@ -718,6 +784,8 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "LÃļschen", + "delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt lÃļschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal lÃļschen willst", + "delete_action_prompt": "{count} gelÃļscht", "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", @@ -731,9 +799,12 @@ "delete_key": "SchlÃŧssel lÃļschen", "delete_library": "Bibliothek lÃļschen", "delete_link": "Link lÃļschen", + "delete_local_action_prompt": "{count} lokal gelÃļscht", "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte lÃļschen", "delete_local_dialog_ok_force": "Trotzdem lÃļschen", "delete_others": "Andere lÃļschen", + "delete_permanently": "EndgÃŧltig lÃļschen", + "delete_permanently_action_prompt": "{count} endgÃŧltig gelÃļscht", "delete_shared_link": "geteilten Link lÃļschen", "delete_shared_link_dialog_title": "Geteilten Link lÃļschen", "delete_tag": "Tag lÃļschen", @@ -744,6 +815,7 @@ "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", + "deselect_all": "Alle abwählen", "details": "Details", "direction": "Richtung", "disabled": "Deaktiviert", @@ -761,6 +833,7 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", + "download_action_prompt": "Herunterladen von {count} Dateien", "download_canceled": "Download abgebrochen", "download_complete": "Download vollständig", "download_enqueue": "Download in die Warteschlange gesetzt", @@ -787,8 +860,12 @@ "edit": "Bearbeiten", "edit_album": "Album bearbeiten", "edit_avatar": "Avatar bearbeiten", + "edit_birthday": "Geburtsdatum bearbeiten", "edit_date": "Datum bearbeiten", "edit_date_and_time": "Datum und Uhrzeit bearbeiten", + "edit_date_and_time_action_prompt": "{count} Daten und Zeiten geändert", + "edit_date_and_time_by_offset": "Datum ändern um Versatz", + "edit_date_and_time_by_offset_interval": "Neuer Datumsbereich: {from} - {to}", "edit_description": "Beschreibung bearbeiten", "edit_description_prompt": "Bitte wähle eine neue Beschreibung:", "edit_exclusion_pattern": "Ausschlussmuster bearbeiten", @@ -798,7 +875,8 @@ "edit_key": "SchlÃŧssel bearbeiten", "edit_link": "Link bearbeiten", "edit_location": "Standort bearbeiten", - "edit_location_dialog_title": "Ort bearbeiten", + "edit_location_action_prompt": "{count} Geolokationen angepasst", + "edit_location_dialog_title": "Standort bearbeiten", "edit_name": "Name bearbeiten", "edit_people": "Personen bearbeiten", "edit_tag": "Tag bearbeiten", @@ -816,6 +894,7 @@ "empty_trash": "Papierkorb leeren", "empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgÃŧltig aus Immich und kann nicht rÃŧckgängig gemacht werden!", "enable": "Aktivieren", + "enable_backup": "Sicherung aktivieren", "enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren", "enabled": "Aktiviert", "end_date": "Enddatum", @@ -826,7 +905,9 @@ "error": "Fehler", "error_change_sort_album": "Ändern der Anzeigereihenfolge fehlgeschlagen", "error_delete_face": "Fehler beim LÃļschen des Gesichts", + "error_getting_places": "Fehler beim Abrufen der Orte", "error_loading_image": "Fehler beim Laden des Bildes", + "error_loading_partners": "Fehler beim Laden der Partner: {error}", "error_saving_image": "Fehler: {error}", "error_tag_face_bounding_box": "Fehler beim Markieren des Gesichts - Begrenzungen kÃļnnen nicht abgerufen werden", "error_title": "Fehler - Etwas ist schief gelaufen", @@ -859,6 +940,7 @@ "failed_to_load_notifications": "Fehler beim Laden der Benachrichtigungen", "failed_to_load_people": "Fehler beim Laden von Personen", "failed_to_remove_product_key": "Fehler beim Entfernen des ProduktschlÃŧssels", + "failed_to_reset_pin_code": "ZurÃŧcksetzen des PIN Codes fehlgeschlagen", "failed_to_stack_assets": "Dateien konnten nicht gestapelt werden", "failed_to_unstack_assets": "Dateien konnten nicht entstapelt werden", "failed_to_update_notification_status": "Benachrichtigungsstatus aktualisieren fehlgeschlagen", @@ -867,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# Pfad konnte} other {# Pfade konnten}} nicht validiert werden", "profile_picture_transparent_pixels": "Profilbilder dÃŧrfen keine transparenten Pixel haben. Bitte zoome heran und/oder verschiebe das Bild.", "quota_higher_than_disk_size": "Dein festgelegtes Kontingent ist grÃļßer als der verfÃŧgbare Speicher", + "something_went_wrong": "Ein Fehler ist eingetreten", "unable_to_add_album_users": "Benutzer konnten nicht zum Album hinzugefÃŧgt werden", "unable_to_add_assets_to_shared_link": "Datei konnte nicht zum geteilten Link hinzugefÃŧgt werden", "unable_to_add_comment": "Es kann kein Kommentar hinzufÃŧgt werden", @@ -880,7 +963,7 @@ "unable_to_change_date": "Datum kann nicht verändert werden", "unable_to_change_description": "Ändern der Beschreibung nicht mÃļglich", "unable_to_change_favorite": "Es konnte der Favoritenstatus fÃŧr diese Datei nicht geändert werden", - "unable_to_change_location": "Ort kann nicht verändert werden", + "unable_to_change_location": "Standort kann nicht verändert werden", "unable_to_change_password": "Passwort konnte nicht geändert werden", "unable_to_change_visibility": "Sichtbarkeit von {count, plural, one {einer Person} other {# Personen}} konnte nicht geändert werden", "unable_to_complete_oauth_login": "OAuth-Anmeldung konnte nicht abgeschlossen werden", @@ -944,7 +1027,7 @@ "unable_to_update_album_cover": "Album-Cover konnte nicht aktualisiert werden", "unable_to_update_album_info": "Album-Info konnte nicht aktualisiert werden", "unable_to_update_library": "Die Bibliothek konnte nicht aktualisiert werden", - "unable_to_update_location": "Der Ort konnte nicht aktualisiert werden", + "unable_to_update_location": "Der Standort konnte nicht aktualisiert werden", "unable_to_update_settings": "Die Einstellungen konnten nicht aktualisiert werden", "unable_to_update_timeline_display_status": "Status der Zeitleistenanzeige konnte nicht aktualisiert werden", "unable_to_update_user": "Der Nutzer konnte nicht aktualisiert werden", @@ -952,13 +1035,11 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Beschreibung hinzufÃŧgen...", + "exif_bottom_sheet_description_error": "Fehler bei der Aktualisierung der Beschreibung", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", "exif_bottom_sheet_people": "PERSONEN", "exif_bottom_sheet_person_add_person": "Namen hinzufÃŧgen", - "exif_bottom_sheet_person_age_months": "{months} Monate alt", - "exif_bottom_sheet_person_age_year_months": "1 Jahr, {months} Monate alt", - "exif_bottom_sheet_person_age_years": "Alter {years}", "exit_slideshow": "Diashow beenden", "expand_all": "Alle aufklappen", "experimental_settings_new_asset_list_subtitle": "In Arbeit", @@ -972,6 +1053,8 @@ "explorer": "Datei-Explorer", "export": "Exportieren", "export_as_json": "Als JSON exportieren", + "export_database": "Datenbank exportieren", + "export_database_description": "Exportiert die SQLite Datenbank", "extension": "Erweiterung", "external": "Extern", "external_libraries": "Externe Bibliotheken", @@ -983,11 +1066,13 @@ "failed_to_load_assets": "Laden der Assets fehlgeschlagen", "failed_to_load_folder": "Fehler beim Laden des Ordners", "favorite": "Favorit", + "favorite_action_prompt": "{count} zu den Favoriten hinzugefÃŧgt", "favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto", "favorites": "Favoriten", "favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden", "feature_photo_updated": "Profilbild aktualisiert", "features": "Funktionen", + "features_in_development": "Feature in Entwicklung", "features_setting_description": "Funktionen der App verwalten", "file_name": "Dateiname", "file_name_or_extension": "Dateiname oder -erweiterung", @@ -997,21 +1082,26 @@ "filter_people": "Personen filtern", "filter_places": "Orte filtern", "find_them_fast": "Finde sie schneller mit der Suche nach Namen", + "first": "Erste", "fix_incorrect_match": "Fehlerhafte Übereinstimmung beheben", "folder": "Ordner", "folder_not_found": "Ordner nicht gefunden", "folders": "Ordner", "folders_feature_description": "Durchsuchen der Ordneransicht fÃŧr Fotos und Videos im Dateisystem", + "forgot_pin_code_question": "PIN Code vergessen?", "forward": "Vorwärts", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Diese Funktion lädt externe Quellen von Google, um zu funktionieren.", "general": "Allgemein", + "geolocation_instruction_location": "Klicke auf eine Datei mit GPS Koordinaten um diesen Standort zu verwenden oder wähle einen Standort direkt auf der Karte", "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", "getting_started": "Erste Schritte", "go_back": "ZurÃŧck", "go_to_folder": "Gehe zu Ordner", "go_to_search": "Zur Suche gehen", + "gps": "GPS", + "gps_missing": "Kein GPS", "grant_permission": "Erlaubnis gewähren", "group_albums_by": "Alben gruppieren nach...", "group_country": "Nach Land gruppieren", @@ -1022,6 +1112,9 @@ "haptic_feedback_switch": "Haptisches Feedback aktivieren", "haptic_feedback_title": "Haptisches Feedback", "has_quota": "Kontingent", + "hash_asset": "Dateihash", + "hashed_assets": "Gehashte Dateien", + "hashing": "Hashen", "header_settings_add_header_tip": "Header hinzufÃŧgen", "header_settings_field_validator_msg": "Der Wert darf nicht leer sein", "header_settings_header_name_input": "Header-Name", @@ -1043,17 +1136,19 @@ "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_delete_remote_err_local": "Lokale Elemente in der Auswahl zum Entfernen von Remote-Elementen, Überspringe", "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_first_time_notice": "Wenn Sie die App zum ersten Mal verwenden, wählen Sie bitte ein Album zur Sicherung aus, damit die Zeitachse mit Fotos und Videos gefÃŧllt werden kann", "home_page_locked_error_local": "Lokale Dateien kÃļnnen nicht in den gesperrten Ordner verschoben werden, Ãŧberspringe", "home_page_locked_error_partner": "Dateien von Partnern kÃļnnen nicht in den gesperrten Ordner verschoben werden, Ãŧberspringe", "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", "host": "Host", "hour": "Stunde", + "hours": "Stunden", "id": "ID", + "idle": "Untätig", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1111,10 +1206,13 @@ "language_no_results_title": "Keine Sprachen gefunden", "language_search_hint": "Sprachen durchsuchen...", "language_setting_description": "Wähle deine bevorzugte Sprache", + "large_files": "Große Dateien", + "last": "Letzte", "last_seen": "Zuletzt gesehen", - "latest_version": "Aktuellste Version", + "latest_version": "Aktuelle Version", "latitude": "Breitengrad", "leave": "Verlassen", + "leave_album": "Album verlassen", "lens_model": "Objektivmodell", "let_others_respond": "Antworten zulassen", "level": "Level", @@ -1126,16 +1224,20 @@ "library_page_sort_created": "Zuletzt erstellt", "library_page_sort_last_modified": "Zuletzt bearbeitet", "library_page_sort_title": "Titel des Albums", + "licenses": "Lizenzen", "light": "Hell", + "like": "Gefällt mir", "like_deleted": "Like gelÃļscht", "link_motion_video": "Bewegungsvideo verknÃŧpfen", - "link_options": "Link-Optionen", "link_to_oauth": "Mit OAuth verknÃŧpfen", "linked_oauth_account": "VerknÃŧpftes OAuth-Konto", "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local": "Lokal", "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", + "local_assets": "Lokale Dateien", + "local_media_summary": "Zusammenfassung der lokalen Medien", "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt Ãŧber diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", @@ -1147,6 +1249,7 @@ "location_picker_longitude_hint": "Längengrad eingeben", "lock": "Sperren", "locked_folder": "Gesperrter Ordner", + "log_detail_title": "Protokoll Details", "log_out": "Abmelden", "log_out_all_devices": "Alle Geräte abmelden", "logged_in_as": "Angemeldet als {user}", @@ -1177,6 +1280,7 @@ "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?", + "logs": "Protokolle", "longitude": "Längengrad", "look": "Erscheinungsbild", "loop_videos": "Loop-Videos", @@ -1184,6 +1288,7 @@ "main_branch_warning": "Du benutzt eine Entwicklungsversion. Wir empfehlen dringend, eine Release-Version zu verwenden!", "main_menu": "HauptmenÃŧ", "make": "Marke", + "manage_geolocation": "Standort verwalten", "manage_shared_links": "Freigegebene Links verwalten", "manage_sharing_with_partners": "Gemeinsame Nutzung mit Partnern verwalten", "manage_the_app_settings": "App-Einstellungen verwalten", @@ -1192,8 +1297,7 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-VerknÃŧpfung verwalten", "map": "Karte", - "map_assets_in_bound": "{count} Foto", - "map_assets_in_bounds": "{count} Fotos", + "map_assets_in_bounds": "{count, plural, =0 {Keine Fotos in diesem Gebiet} one {# Foto} other {# Fotos}}", "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", @@ -1201,7 +1305,6 @@ "map_location_service_disabled_title": "Ortungsdienste deaktiviert", "map_marker_for_images": "Kartenmarkierung fÃŧr Bilder, die in {city}, {country} aufgenommen wurden", "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_settings": "Karteneinstellungen", @@ -1214,12 +1317,13 @@ "map_settings_include_show_archived": "Archivierte anzeigen", "map_settings_include_show_partners": "Partner einbeziehen", "map_settings_only_show_favorites": "Nur Favoriten anzeigen", - "map_settings_theme_settings": "Karten Design", + "map_settings_theme_settings": "Kartendesign", "map_zoom_to_see_photos": "Ansicht verkleinern um Fotos zu sehen", "mark_all_as_read": "Alle als gelesen markieren", "mark_as_read": "Als gelesen markieren", "marked_all_as_read": "Alle als gelesen markiert", "matches": "Treffer", + "matching_assets": "Passende Dateien", "media_type": "Medientyp", "memories": "Erinnerungen", "memories_all_caught_up": "Alles aufgeholt", @@ -1238,6 +1342,7 @@ "merged_people_count": "{count, plural, one {# Person} other {# Personen}} zusammengefÃŧgt", "minimize": "Minimieren", "minute": "Minute", + "minutes": "Minuten", "missing": "Fehlende", "model": "Modell", "month": "Monat", @@ -1245,6 +1350,7 @@ "more": "Mehr", "move": "Verschieben", "move_off_locked_folder": "Aus dem gesperrten Ordner verschieben", + "move_to_lock_folder_action_prompt": "{count} zum gesperrten Ordner hinzugefÃŧgt", "move_to_locked_folder": "In den gesperrten Ordner verschieben", "move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und kÃļnnen nur noch im gesperrten Ordner angezeigt werden", "moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert", @@ -1256,6 +1362,10 @@ "my_albums": "Meine Alben", "name": "Name", "name_or_nickname": "Name oder Nickname", + "network_requirement_photos_upload": "Mobile Daten verwenden, um Fotos zu sichern", + "network_requirement_videos_upload": "Mobile Daten verwenden, um Videos zu sichern", + "network_requirements": "Anforderungen ans Netzwerk", + "network_requirements_updated": "Netzwerk-Abhängigkeiten haben sich geändert, Backup-Warteschlange wird zurÃŧckgesetzt", "networking_settings": "Netzwerk", "networking_subtitle": "Verwaltung von Server-Endpunkt-Einstellungen", "never": "Niemals", @@ -1265,6 +1375,7 @@ "new_person": "Neue Person", "new_pin_code": "Neuer PIN Code", "new_pin_code_subtitle": "Dies ist dein erster Zugriff auf den gesperrten Ordner. Erstelle einen PIN Code fÃŧr den sicheren Zugriff auf diese Seite", + "new_timeline": "Neue Zeitleiste", "new_user_created": "Neuer Benutzer wurde erstellt", "new_version_available": "NEUE VERSION VERFÜGBAR", "newest_first": "Neueste zuerst", @@ -1278,19 +1389,25 @@ "no_assets_message": "KLICKE, UM DEIN ERSTES FOTO HOCHZULADEN", "no_assets_to_show": "Keine Vorschau vorhanden", "no_cast_devices_found": "Keine Geräte zum Übertragen gefunden", + "no_checksum_local": "PrÃŧfsumme nicht verfÃŧgbar - kann lokale Datei/en nicht laden", + "no_checksum_remote": "PrÃŧfsumme nicht verfÃŧgbar - kann entfernte Datei/en nicht laden", "no_duplicates_found": "Es wurden keine Duplikate gefunden.", "no_exif_info_available": "Keine EXIF-Informationen vorhanden", "no_explore_results_message": "Lade weitere Fotos hoch, um deine Sammlung zu erkunden.", "no_favorites_message": "FÃŧge Favoriten hinzu, um deine besten Bilder und Videos schnell zu finden", "no_libraries_message": "Eine externe Bibliothek erstellen, um deine Fotos und Videos anzusehen", + "no_local_assets_found": "Keine lokale Datei mit dieser PrÃŧfsumme gefunden", "no_locked_photos_message": "Fotos und Videos im gesperrten Ordner sind versteckt und werden nicht angezeigt, wenn du deine Bibliothek durchsuchst.", "no_name": "Kein Name", "no_notifications": "Keine Benachrichtigungen", "no_people_found": "Keine passenden Personen gefunden", "no_places": "Keine Orte", + "no_remote_assets_found": "Keine entfernten Dateien mit dieser PrÃŧfsumme gefunden", "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", + "no_uploads_in_progress": "Kein Upload in Bearbeitung", + "not_available": "N/A", "not_in_any_album": "In keinem Album", "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", @@ -1306,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Offizielle Immich Quellen", "offline": "Offline", + "offset": "Verschiebung", "ok": "Ok", "oldest_first": "Älteste zuerst", "on_this_device": "Auf diesem Gerät", @@ -1324,10 +1442,13 @@ "open_the_search_filters": "Die Suchfilter Ãļffnen", "options": "Optionen", "or": "oder", + "organize_into_albums": "In Alben organisieren", + "organize_into_albums_description": "Aktuelle Synchronisationseinstellungen verwenden, um existierende Fotos in Alben zu laden", "organize_your_library": "Organisiere deine Bibliothek", "original": "Original", "other": "Sonstiges", "other_devices": "Andere Geräte", + "other_entities": "Andere Entitäten", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1382,6 +1503,9 @@ "permission_onboarding_permission_limited": "Berechtigungen unzureichend. Um Immich das Sichern von ganzen Sammlungen zu ermÃļglichen, muss der Zugriff auf alle Fotos und Videos in den Einstellungen erlaubt werden.", "permission_onboarding_request": "Immich benÃļtigt Berechtigung um auf deine Fotos und Videos zuzugreifen.", "person": "Person", + "person_age_months": "{months, plural, one {# month} other {# months}} alt", + "person_age_year_months": "1 Jahr, {months, plural, one {# month} other {# months}} alt", + "person_age_years": "{years, plural, one {# Jahr} other {# Jahre}} alt", "person_birthdate": "Geboren am {date}", "person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}", "photo_shared_all_users": "Es sieht so aus, als hättest du deine Fotos mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", @@ -1405,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "App-Einstellungen verwalten", "preferences_settings_title": "Voreinstellungen", + "preparing": "Vorbereiten", "preset": "Voreinstellung", "preview": "Vorschau", "previous": "Vorherige", @@ -1421,6 +1546,7 @@ "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- und Server-Versionen sind aktuell", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "SchreibgeschÃŧtzter Modus aktiviert. Halte das Benutzer-Avatar-Symbol gedrÃŧckt, um den Modus zu verlassen.", "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.", "profile_image_of_user": "Profilbild von {user}", @@ -1446,25 +1572,30 @@ "purchase_license_subtitle": "Kaufe Immich, um die fortlaufende Entwicklung zu unterstÃŧtzen", "purchase_lifetime_description": "Lebenslange GÃŧltigkeit", "purchase_option_title": "KAUFOPTIONEN", - "purchase_panel_info_1": "Die Entwicklung von Immich erfordert viel Zeit und MÃŧhe, und wir haben Vollzeit-Entwickler, die daran arbeiten es mÃļglichst perfekt zu machen. Unser Ziel ist es, dass Open-Source-Software und moralische Geschäftsmethoden zu einer nachhaltigen Einkommensquelle fÃŧr Entwickler werden und ein datenschutzfreundliches Ökosystem mit echten Alternativen zu ausbeuterischen Cloud-Diensten geschaffen wird.", + "purchase_panel_info_1": "Die Entwicklung von Immich erfordert viel Zeit und MÃŧhe und wir haben Vollzeit-Entwickler, die daran arbeiten Immich mÃļglichst perfekt zu machen. Unser Ziel ist es, Open-Source-Software und ethische Geschäftspraktiken zu einer verlässlichen Einkommensquelle fÃŧr Entwickler zu machen und ein datenschutzfreundliches Ökosystem mit echten Alternativen zu ausbeuterischen Cloud-Diensten zu schaffen.", "purchase_panel_info_2": "Weil wir uns dagegen entschieden haben, eine Bezahlschranke einzusetzen, wird dieser Kauf keine zusätzlichen Funktionen in Immich freischalten. Wir verlassen uns auf Nutzende wie dich, um die Entwicklung von Immich zu unterstÃŧtzen.", "purchase_panel_title": "Das Projekt unterstÃŧtzen", "purchase_per_server": "Pro Server", "purchase_per_user": "Pro Benutzer", "purchase_remove_product_key": "ProduktschlÃŧssel entfernen", - "purchase_remove_product_key_prompt": "Sicher, dass der ProduktschlÃŧssel entfernt werden soll?", + "purchase_remove_product_key_prompt": "Bist Du sicher, dass der ProduktschlÃŧssel entfernt werden soll?", "purchase_remove_server_product_key": "Server-ProduktschlÃŧssel entfernen", "purchase_remove_server_product_key_prompt": "Sicher, dass der Server-ProduktschlÃŧssel entfernt werden soll?", "purchase_server_description_1": "FÃŧr den gesamten Server", "purchase_server_description_2": "UnterstÃŧtzerstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Der Server-ProduktschlÃŧssel wird durch den Administrator verwaltet", + "query_asset_id": "Datei-ID abfragen", + "queue_status": "Warteschlange {count}/{total}", "rating": "Bewertung", "rating_clear": "Bewertung lÃļschen", "rating_count": "{count, plural, one {# Stern} other {# Sterne}}", "rating_description": "Stellt die EXIF-Bewertung im Informationsbereich dar", "reaction_options": "ReaktionsmÃļglichkeiten", "read_changelog": "Changelog lesen", + "readonly_mode_disabled": "SchreibgeschÃŧtzter Modus deaktiviert", + "readonly_mode_enabled": "SchreibgeschÃŧtzter Modus aktiviert", + "ready_for_upload": "Bereit zum Hochladen", "reassign": "Neu zuweisen", "reassigned_assets_to_existing_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} {name, select, null {einer vorhandenen Person} other {{name}}} zugewiesen", "reassigned_assets_to_new_person": "{count, plural, one {# Datei wurde} other {# Dateien wurden}} einer neuen Person zugewiesen", @@ -1487,6 +1618,9 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", + "remote": "Server", + "remote_assets": "Server-Dateien", + "remote_media_summary": "Zusammenfassung der entfernten Medien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1494,7 +1628,9 @@ "remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen", "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", + "remove_from_album_action_prompt": "{count} vom Album entfernt", "remove_from_favorites": "Aus Favoriten entfernen", + "remove_from_lock_folder_action_prompt": "{count} aus dem gesperrten Ordner entfernt", "remove_from_locked_folder": "Aus gesperrtem Ordner entfernen", "remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen mÃļchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.", "remove_from_shared_link": "Aus geteiltem Link entfernen", @@ -1522,19 +1658,29 @@ "reset_password": "Passwort zurÃŧcksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurÃŧcksetzen", "reset_pin_code": "PIN Code zurÃŧcksetzen", + "reset_pin_code_description": "Falls du deinen PIN Code vergessen hast, wende dich an deinen Immich-Administrator um ihn zurÃŧcksetzen zu lassen", + "reset_pin_code_success": "PIN Code erfolgreich zurÃŧckgesetzt", + "reset_pin_code_with_password": "Mit deinem Passwort kannst du jederzeit deinen PIN Code zurÃŧcksetzen", + "reset_sqlite": "SQLite Datenbank zurÃŧcksetzen", + "reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurÃŧcksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren", + "reset_sqlite_success": "SQLite Datenbank erfolgreich zurÃŧckgesetzt", "reset_to_default": "Auf Standard zurÃŧcksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelÃļst", "restore": "Wiederherstellen", "restore_all": "Alle wiederherstellen", + "restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt", "restore_user": "Nutzer wiederherstellen", "restored_asset": "Datei wiederhergestellt", "resume": "Fortsetzen", + "resume_paused_jobs": "{count, plural, one {# Aufgabe fortsetzen } other {# Aufgaben fortsetzen}}", "retry_upload": "Upload wiederholen", "review_duplicates": "Duplikate ÃŧberprÃŧfen", + "review_large_files": "Große Dateien ÃŧberprÃŧfen", "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", + "running": "Läuft", "save": "Speichern", "save_to_gallery": "In Galerie speichern", "saved_api_key": "API-SchlÃŧssel wurde gespeichert", @@ -1621,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden", "selected": "Ausgewählt", "selected_count": "{count, plural, other {# ausgewählt}}", + "selected_gps_coordinates": "Ausgewählte GPS-Koordinaten", "send_message": "Nachricht senden", "send_welcome_email": "BegrÃŧssungsmail senden", "server_endpoint": "Server-Endpunkt", @@ -1658,7 +1805,7 @@ "setting_notifications_subtitle": "Benachrichtigungen anpassen", "setting_notifications_total_progress_subtitle": "Gesamter Upload-Fortschritt (abgeschlossen/Anzahl Elemente)", "setting_notifications_total_progress_title": "Zeige den Gesamtfortschritt der Hintergrundsicherung", - "setting_video_viewer_looping_title": "Schleife / Looping", + "setting_video_viewer_looping_title": "Video-Wiederholung", "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", @@ -1666,6 +1813,7 @@ "settings_saved": "Einstellungen gespeichert", "setup_pin_code": "Einen PIN Code festlegen", "share": "Teilen", + "share_action_prompt": "{count} Dateien geteilt", "share_add_photos": "Fotos hinzufÃŧgen", "share_assets_selected": "{count} ausgewählt", "share_dialog_preparing": "Vorbereiten...", @@ -1675,7 +1823,7 @@ "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", - "shared_album_section_people_action_leave": "Album verlassen", + "shared_album_section_people_action_leave": "Benutzer vom Album entfernen", "shared_album_section_people_action_remove_user": "Benutzer von Album entfernen", "shared_album_section_people_title": "PERSONEN", "shared_by": "Geteilt von", @@ -1687,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Link kopiert", "shared_link_clipboard_text": "Link: {link}\nPasswort: {password}", "shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe", + "shared_link_custom_url_description": "Greife Ãŧber eine benutzerdefinierte URL auf diesen Freigabelink zu", "shared_link_edit_description_hint": "Beschreibung eingeben", "shared_link_edit_expire_after_option_day": "1 Tag", "shared_link_edit_expire_after_option_days": "{count} Tagen", @@ -1712,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", "shared_link_options": "Optionen fÃŧr geteilten Link", + "shared_link_password_description": "FÃŧr den Zugriff auf diesen freigegebenen Link ist ein Passwort erforderlich", "shared_links": "Geteilte Links", "shared_links_description": "Teile Fotos und Videos mit einem Link", "shared_photos_and_videos_count": "{assetCount, plural, one {# geteiltes Foto oder Video.} other {# geteilte Fotos & Videos.}}", @@ -1746,6 +1896,7 @@ "show_slideshow_transition": "Slideshow-Übergang anzeigen", "show_supporter_badge": "UnterstÃŧtzerabzeichen", "show_supporter_badge_description": "Zeige UnterstÃŧtzerabzeichen", + "show_text_search_menu": "Zeige MenÃŧ fÃŧr Textsuche", "shuffle": "Durchmischen", "sidebar": "Seitenleiste", "sidebar_display_description": "Zeige einen Link zu der Ansicht in der Seitenleiste an", @@ -1761,12 +1912,14 @@ "sort_created": "Erstellungsdatum", "sort_items": "Anzahl der Einträge", "sort_modified": "Änderungsdatum", + "sort_newest": "Neuestes Foto", "sort_oldest": "Ältestes Foto", "sort_people_by_similarity": "Personen nach Ähnlichkeit sortieren", "sort_recent": "Neustes Foto", "sort_title": "Titel", "source": "Quellcode", "stack": "Stapel", + "stack_action_prompt": "{count} gestapelt", "stack_duplicates": "Duplikate stapeln", "stack_select_one_photo": "Hauptfoto fÃŧr den Stapel auswählen", "stack_selected_photos": "Ausgewählte Fotos stapeln", @@ -1774,6 +1927,7 @@ "stacktrace": "Stapelaufgaben", "start": "Starten", "start_date": "Anfangsdatum", + "start_date_before_end_date": "Anfangsdatum muss vor dem Enddatum liegen", "state": "Bundesland / Provinz", "status": "Status", "stop_casting": "Übertragung stoppen", @@ -1786,6 +1940,7 @@ "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", + "success": "Erfolgreich", "suggestions": "Vorschläge", "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "UnterstÃŧtzung", @@ -1795,6 +1950,10 @@ "sync": "Synchronisieren", "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", + "sync_local": "Lokal synchronisieren", + "sync_remote": "mit Server synchronisieren", + "sync_status": "Synchronisierungstatus", + "sync_status_subtitle": "Synchronisierungssystem anzeigen und bearbeiten", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1805,11 +1964,12 @@ "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", "tags": "Tags", + "tap_to_run_job": "Tippen, um den Job zu starten", "template": "Vorlage", "theme": "Theme", "theme_selection": "Themenauswahl", "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_storage_indicator_title": "Fortschrittsbalken der Sicherung auf dem Vorschaubild", "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})", "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", @@ -1831,12 +1991,15 @@ "to_change_password": "Passwort ändern", "to_favorite": "Zu Favoriten hinzufÃŧgen", "to_login": "Anmelden", + "to_multi_select": "zur Mehrfachauswahl", "to_parent": "Gehe zum Übergeordneten", + "to_select": "zum Auswählen", "to_trash": "In den Papierkorb verschieben", "toggle_settings": "Einstellungen umschalten", "total": "Gesamt", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", + "trash_action_prompt": "{count} in den Papierkorb verschoben", "trash_all": "Alle lÃļschen", "trash_count": "Papierkorb {count, number}", "trash_delete_asset": "Datei lÃļschen/in den Papierkorb verschieben", @@ -1850,13 +2013,16 @@ "trash_page_select_assets_btn": "Elemente auswählen", "trash_page_title": "Papierkorb ({count})", "trashed_items_will_be_permanently_deleted_after": "GelÃļschte Objekte werden nach {days, plural, one {# Tag} other {# Tagen}} endgÃŧltig gelÃļscht.", + "troubleshoot": "Fehler beheben", "type": "Typ", "unable_to_change_pin_code": "PIN Code konnte nicht geändert werden", "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", + "unarchive_action_prompt": "{count} aus dem Archiv entfernt", "unarchived_count": "{count, plural, other {# entarchiviert}}", "undo": "RÃŧckgängig", "unfavorite": "Entfavorisieren", + "unfavorite_action_prompt": "{count} aus den Favoriten entfernt", "unhide_person": "Person einblenden", "unknown": "Unbekannt", "unknown_country": "Unbekanntes Land", @@ -1874,15 +2040,21 @@ "unselect_all_duplicates": "Alle Duplikate abwählen", "unselect_all_in": "Alle in {group} abwählen", "unstack": "Entstapeln", + "unstack_action_prompt": "{count} entstapelt", "unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt", + "untagged": "Ohne Tag", "up_next": "Weiter", + "update_location_action_prompt": "Aktualsiere den Ort von {count} ausgewählten Dateien mit:", "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", + "upload_action_prompt": "{count} in der Warteschlange fÃŧr Upload", "upload_concurrency": "Parallelität beim Hochladen", + "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", + "upload_finished": "Upload fertig", "upload_progress": "{remaining, number} verbleibend - {processed, number}/{total, number} verarbeitet", "upload_skipped_duplicates": "{count, plural, one {# doppelte Datei} other {# doppelte Dateien}} ausgelassen", "upload_status_duplicates": "Duplikate", @@ -1891,6 +2063,7 @@ "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", + "uploading_media": "Medien werden hochgeladen", "url": "URL", "usage": "Verwendung", "use_biometric": "Biometrie verwenden", @@ -1911,6 +2084,7 @@ "user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen", "username": "Nutzername", "users": "Benutzer", + "users_added_to_album_count": "{count, plural, one {# Benutzer} other {# Benutzer}} zum Album hinzugefÃŧgt", "utilities": "Hilfsmittel", "validate": "Validieren", "validate_endpoint_error": "Bitte gib eine gÃŧltige URL ein", @@ -1929,6 +2103,7 @@ "view_album": "Album anzeigen", "view_all": "Alles anzeigen", "view_all_users": "Alle Nutzer anzeigen", + "view_details": "Details ansehen", "view_in_timeline": "In Zeitleiste anzeigen", "view_link": "Link anzeigen", "view_links": "Links anzeigen", @@ -1936,6 +2111,7 @@ "view_next_asset": "Nächste Datei anzeigen", "view_previous_asset": "Vorherige Datei anzeigen", "view_qr_code": "QR code anzeigen", + "view_similar_photos": "Zeige ähnliche Fotos an", "view_stack": "Stapel anzeigen", "view_user": "Benutzer anzeigen", "viewer_remove_from_stack": "Aus Stapel entfernen", @@ -1954,5 +2130,6 @@ "yes": "Ja", "you_dont_have_any_shared_links": "Du hast keine geteilten Links", "your_wifi_name": "Dein WLAN-Name", - "zoom_image": "Bild vergrÃļßern" + "zoom_image": "Bild vergrÃļßern", + "zoom_to_bounds": "In die Grenzen zoomen" } diff --git a/i18n/el.json b/i18n/el.json index 62d0481ec6..6492ea0370 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -14,6 +14,7 @@ "add_a_location": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎ¯ÎąĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "add_a_name": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ έÎŊÎą ΌÎŊÎŋÎŧÎą", "add_a_title": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„Î¯Ī„ÎģÎŋĪ…", + "add_birthday": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ Ī„ÎˇÎŊ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŗÎĩÎŊÎĩθÎģÎ¯Ī‰ÎŊ", "add_endpoint": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī„ÎĩÎģΚÎēÎŋĪ ĪƒÎˇÎŧÎĩίÎŋĪ…", "add_exclusion_pattern": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎŋĪ„Î¯Î˛ÎŋĪ… ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ", "add_import_path": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŧÎŋÎŊÎŋĪ€ÎąĪ„ÎšÎŋĪ ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽĪ‚", @@ -27,6 +28,10 @@ "add_to_album": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΃Îĩ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "add_to_album_bottom_sheet_added": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ĪƒĪ„Îŋ {album}", "add_to_album_bottom_sheet_already_exists": "Ήδη ĪƒĪ„Îŋ {album}", + "add_to_album_bottom_sheet_some_local_assets": "ÎŸĪÎšĪƒÎŧέÎŊÎŋΚ Ī„ÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą δÎĩÎŊ ÎŧĪ€ĪŒĪÎĩĪƒÎąÎŊ ÎŊÎą ΀΁ÎŋĪƒĪ„ÎĩθÎŋĪÎŊ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "add_to_album_toggle": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚ ÎŗÎšÎą Ī„Îŋ {album}", + "add_to_albums": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "add_to_albums_count": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ({count})", "add_to_shared_album": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΃Îĩ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "add_url": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "added_to_archive": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ĪƒĪ„Îŋ ÎąĪĪ‡ÎĩίÎŋ", @@ -44,6 +49,13 @@ "backup_database": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą Dump βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", "backup_database_enable_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ dumps βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", "backup_keep_last_amount": "ΠÎŋĪƒĪŒĪ„ÎˇĪ„Îą ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊΉÎŊ dumps Ī€ÎŋĪ… Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Î´ÎšÎąĪ„ÎˇĪÎˇÎ¸ÎŋĪÎŊ", + "backup_onboarding_1_description": "ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎĩÎēĪ„ĪŒĪ‚ ÎĩÎŗÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚, ÎĩÎ¯Ī„Îĩ ĪƒĪ„Îŋ cloud ÎĩÎ¯Ī„Îĩ ΃Îĩ ÎŦÎģÎģΡ Ī†Ī…ĪƒÎšÎēÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą.", + "backup_onboarding_2_description": "Ī„ÎŋĪ€ÎšÎēÎŦ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ΃Îĩ Î´ÎšÎąĪ†Îŋ΁ÎĩĪ„ÎšÎēÎ­Ī‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚. Î‘Ī…Ī„ĪŒ Ī€ÎĩĪÎšÎģÎąÎŧβÎŦÎŊÎĩΚ Ī„Îą ÎēĪĪÎšÎą ÎąĪĪ‡ÎĩÎ¯Îą ÎēιΚ έÎŊÎą Ī„ÎŋĪ€ÎšÎēΌ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎąĪ…Ī„ĪŽÎŊ ΄ΉÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ.", + "backup_onboarding_3_description": "ĪƒĪ…ÎŊÎŋÎģΚÎēÎŦ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ΄ΉÎŊ δÎĩδÎŋÎŧέÎŊΉÎŊ ĪƒÎąĪ‚, ĪƒĪ…ÎŧĪ€ÎĩĪÎšÎģÎąÎŧβιÎŊÎŋÎŧέÎŊΉÎŊ ΄ΉÎŊ ÎąĪĪ‡ÎšÎēĪŽÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ. Î‘Ī…Ī„ĪŒ Ī€ÎĩĪÎšÎģÎąÎŧβÎŦÎŊÎĩΚ 1 ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎĩÎēĪ„ĪŒĪ‚ ÎĩÎŗÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚ (offsite) ÎēιΚ 2 Ī„ÎŋĪ€ÎšÎēÎŦ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą.", + "backup_onboarding_description": "ÎŖĪ…ÎŊÎšĪƒĪ„ÎŦĪ„ÎąÎš Ρ ĪƒĪ„ĪÎąĪ„ÎˇÎŗÎšÎēÎŽ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ 3-2-1 ÎŗÎšÎą Ī„ÎˇÎŊ ΀΁ÎŋĪƒĪ„ÎąĪƒÎ¯Îą ΄ΉÎŊ δÎĩδÎŋÎŧέÎŊΉÎŊ ĪƒÎąĪ‚. Θι Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Î´ÎšÎąĪ„ÎˇĪÎĩÎ¯Ī„Îĩ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ΄ΉÎŊ ÎąÎŊÎĩÎ˛ÎąĪƒÎŧέÎŊΉÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ/Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚, ÎēÎąÎ¸ĪŽĪ‚ ÎēιΚ Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ Ī„ÎŋĪ… Immich, ÎŗÎšÎą ÎŧΚι ÎŋÎģÎŋÎēÎģÎˇĪĪ‰ÎŧέÎŊΡ ÎģĪĪƒÎˇ backup.", + "backup_onboarding_footer": "Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ĪƒĪ‡ÎĩĪ„ÎšÎēÎŦ ÎŧÎĩ Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī„ÎŋĪ… Immich, ÎąÎŊÎąĪ„ĪÎ­ÎžÎĩ ĪƒĪ„ÎŋÎŊ ÎŋÎ´ÎˇÎŗĪŒ Ī„ÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇĪ‚.", + "backup_onboarding_parts_title": "ΈÎŊÎą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ Ī„ĪĪ€ÎŋĪ… 3-2-1 Ī€ÎĩĪÎšÎģÎąÎŧβÎŦÎŊÎĩΚ:", + "backup_onboarding_title": "ΑÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ dump βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", "backup_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ dump Ī„ÎˇĪ‚ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ.", "cleared_jobs": "ΕÎēÎēÎąÎ¸ÎąĪÎ¯ĪƒĪ„ÎˇÎēÎąÎŊ ÎŋΚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎŗÎšÎą: {job}", @@ -112,6 +124,13 @@ "logging_enable_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚ ĪƒĪ…ÎŧβÎŦÎŊ΄ΉÎŊ", "logging_level_description": "ΤÎŋ ÎĩĪ€Î¯Ī€ÎĩδÎŋ ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚ ĪƒĪ…ÎŧβÎŦÎŊ΄ΉÎŊ Ī€ÎŋĪ… θι ÎĩĪ†ÎąĪÎŧÎŋĪƒĪ„Îĩί, ĪŒĪ„ÎąÎŊ ÎąĪ…Ī„ÎŽ ÎĩίÎŊιΚ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊΡ.", "logging_settings": "ÎšÎąĪ„ÎąÎŗĪÎąĪ†ÎŽ ÎŖĪ…ÎŧβÎŦÎŊ΄ΉÎŊ", + "machine_learning_availability_checks": "ΈÎģÎĩÎŗĪ‡ÎŋΚ δΚιθÎĩĪƒÎšÎŧĪŒĪ„ÎˇĪ„ÎąĪ‚", + "machine_learning_availability_checks_description": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„ÎŋĪ‚ ÎąÎŊÎ¯Ī‡ÎŊÎĩĪ…ĪƒÎˇ ÎēιΚ ΀΁ÎŋĪ„Î¯ÎŧÎˇĪƒÎˇ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΉÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ĪŽÎŊ ÎŧÎˇĪ‡ÎąÎŊΚÎēÎŽĪ‚ ÎŧÎŦÎ¸ÎˇĪƒÎˇĪ‚", + "machine_learning_availability_checks_enabled": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎĩÎģÎ­ÎŗĪ‡Ī‰ÎŊ δΚιθÎĩĪƒÎšÎŧĪŒĪ„ÎˇĪ„ÎąĪ‚", + "machine_learning_availability_checks_interval": "ΔιÎŦĪƒĪ„ÎˇÎŧÎą ÎĩÎģÎ­ÎŗĪ‡ÎŋĪ…", + "machine_learning_availability_checks_interval_description": "ΔιÎŦĪƒĪ„ÎˇÎŧÎą ΃Îĩ Ī‡ÎšÎģΚÎŋĪƒĪ„ÎŦ δÎĩĪ…Ī„Îĩ΁ÎŋÎģÎ­Ī€Ī„ÎŋĪ… ÎŧÎĩĪ„ÎąÎžĪ ΄ΉÎŊ ÎĩÎģÎ­ÎŗĪ‡Ī‰ÎŊ δΚιθÎĩĪƒÎšÎŧĪŒĪ„ÎˇĪ„ÎąĪ‚", + "machine_learning_availability_checks_timeout": "Î‘Î¯Ī„ÎˇÎŧÎą ·΁ÎŋÎŊΚÎēÎŋĪ ÎŋĪÎ¯ÎŋĪ… ÎģÎŽÎžÎˇĪ‚", + "machine_learning_availability_checks_timeout_description": "Î§ĪÎŋÎŊΚÎēΌ ĪŒĪÎšÎŋ ΃Îĩ Ī‡ÎšÎģΚÎŋĪƒĪ„ÎŦ δÎĩĪ…Ī„Îĩ΁ÎŋÎģÎ­Ī€Ī„ÎŋĪ… ÎŗÎšÎą ÎĩÎģÎ­ÎŗĪ‡ÎŋĪ…Ī‚ δΚιθÎĩĪƒÎšÎŧĪŒĪ„ÎˇĪ„ÎąĪ‚", "machine_learning_clip_model": "ΜÎŋÎŊĪ„Î­ÎģÎŋ CLIP", "machine_learning_clip_model_description": "ΤÎŋ ΌÎŊÎŋÎŧÎą ÎĩÎŊĪŒĪ‚ ÎŧÎŋÎŊĪ„Î­ÎģÎŋĪ… CLIP Ī€ÎŋĪ… ÎąÎŊÎąĪ†Î­ĪÎĩĪ„ÎąÎš ÎĩÎ´ĪŽ. ÎŖÎˇÎŧÎĩÎšĪŽĪƒĪ„Îĩ ĪŒĪ„Îš Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą ÎĩĪ€ÎąÎŊÎĩÎēĪ„ÎĩÎģÎ­ĪƒÎĩĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪÎŗÎąĪƒÎ¯Îą 'ÎˆÎžĪ…Ī€ÎŊΡ ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ' ÎŗÎšÎą ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚ ÎŧÎĩĪ„ÎŦ Ī„ÎˇÎŊ ÎąÎģÎģÎąÎŗÎŽ ÎŧÎŋÎŊĪ„Î­ÎģÎŋĪ….", "machine_learning_duplicate_detection": "ΕÎŊĪ„ÎŋĪ€ÎšĪƒÎŧĪŒĪ‚ Î”ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", @@ -166,6 +185,20 @@ "metadata_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎˇĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ÎŧÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊΉÎŊ", "migration_job": "ΜÎĩĪ„ÎąĪ†Îŋ΁ÎŦ δÎĩδÎŋÎŧέÎŊΉÎŊ (Migration)", "migration_job_description": "ΜÎĩĪ„ÎąĪ†Îŋ΁ÎŦ ΄ΉÎŊ ÎĩΚÎēÎŋÎŊÎšÎ´Î¯Ī‰ÎŊ ÎŗÎšÎą ÎąĪĪ‡ÎĩÎ¯Îą ÎēιΚ Ī€ĪĪŒĪƒĪ‰Ī€Îą ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ δÎŋÎŧÎŽ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ", + "nightly_tasks_cluster_faces_setting_description": "ΕÎēĪ„Î­ÎģÎĩĪƒÎˇ ÎąÎŊÎąÎŗÎŊĪŽĪÎšĪƒÎˇĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… ΃Îĩ ÎŊέι ÎąÎŊÎšĪ‡ÎŊÎĩĪ…ÎŧέÎŊÎą Ī€ĪĪŒĪƒĪ‰Ī€Îą", + "nightly_tasks_cluster_new_faces_setting": "ΟÎŧιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŊÎ­Ī‰ÎŊ ΀΁ÎŋĪƒĪŽĪ€Ī‰ÎŊ", + "nightly_tasks_database_cleanup_setting": "Î•ĪÎŗÎąĪƒÎ¯ÎĩĪ‚ ÎēÎąÎ¸ÎąĪÎšĪƒÎŧÎŋĪ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "nightly_tasks_database_cleanup_setting_description": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ Ī€ÎąÎģÎšĪŽÎŊ ÎēιΚ ÎģÎˇÎŗÎŧέÎŊΉÎŊ δÎĩδÎŋÎŧέÎŊΉÎŊ ÎąĪ€ĪŒ Ī„Îˇ βÎŦĪƒÎˇ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "nightly_tasks_generate_memories_setting": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊÎąÎŧÎŊÎŽĪƒÎĩΉÎŊ", + "nightly_tasks_generate_memories_setting_description": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊÎ­Ī‰ÎŊ ÎąÎŊÎąÎŧÎŊÎŽĪƒÎĩΉÎŊ ÎąĪ€ĪŒ ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą", + "nightly_tasks_missing_thumbnails_setting": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎĩÎģÎģÎĩÎšĪ€ĪŒÎŊ΄ΉÎŊ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", + "nightly_tasks_missing_thumbnails_setting_description": "ΤÎŋĪ€ÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ĪƒĪ„Îˇ Îŋ΅΁ÎŦ ΄ΉÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ Ī‡Ī‰ĪÎ¯Ī‚ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎŗÎšÎą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", + "nightly_tasks_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎŗÎšÎą Ī„ÎšĪ‚ ÎŊ΅·΄ÎĩĪÎšÎŊÎ­Ī‚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚", + "nightly_tasks_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎŊ΅·΄ÎĩĪÎšÎŊĪŽÎŊ ÎĩĪÎŗÎąĪƒÎšĪŽÎŊ", + "nightly_tasks_start_time_setting": "ÎĪÎą έÎŊÎąĪÎžÎˇĪ‚", + "nightly_tasks_start_time_setting_description": "Η ĪŽĪÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎŋĪ€ÎŋÎ¯Îą Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ΞÎĩÎēΚÎŊÎŦÎĩΚ ÎŊÎą ÎĩÎēĪ„ÎĩÎģÎĩί Ī„ÎšĪ‚ ÎŊ΅·΄ÎĩĪÎšÎŊÎ­Ī‚ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚", + "nightly_tasks_sync_quota_usage_setting": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚ Ī‡ĪŽĪÎŋĪ…", + "nightly_tasks_sync_quota_usage_setting_description": "ΕÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎŋĪ… Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋĪ… Ī‡ĪŽĪÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ, ÎŧÎĩ βÎŦĪƒÎˇ Ī„ÎˇÎŊ Ī„ĪÎ­Ī‡ÎŋĪ…ĪƒÎą Ī‡ĪÎŽĪƒÎˇ", "no_paths_added": "ΔÎĩÎŊ ΀΁ÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ Î´ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚", "no_pattern_added": "ΔÎĩÎŊ ΀΁ÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ ÎŧÎŋĪ„Î¯Î˛Îŋ", "note_apply_storage_label_previous_assets": "ÎŖÎˇÎŧÎĩÎ¯Ī‰ĪƒÎˇ: Για ÎŊÎą ÎĩĪ†ÎąĪÎŧÎŋĪƒĪ„Îĩί Ρ Î•Ī„ÎšÎēÎ­Ī„Îą Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ ΃Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… ÎĩÎ¯Ī‡ÎąÎŊ ÎąÎŊÎąĪĪ„ÎˇÎ¸Îĩί Ī€ÎąÎģÎąÎšĪŒĪ„ÎĩĪÎą, ÎĩÎēĪ„Î­ÎģÎĩ΃Îĩ Ī„Îŋ", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI ΑÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", "oauth_mobile_redirect_uri_override": "Î ĪÎŋĪƒĪ€Î­ÎģÎąĪƒÎˇ URI ÎąÎŊÎąÎēÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇĪ‚ ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ Ī„ÎˇÎģÎ­Ī†Ī‰ÎŊÎą", "oauth_mobile_redirect_uri_override_description": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„Îŋ ĪŒĪ„ÎąÎŊ Îŋ Ī€ÎŦ΁Îŋ·ÎŋĪ‚ OAuth δÎĩÎŊ ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎĩΚ ÎŧΚι URI ÎŗÎšÎą ÎēΚÎŊÎˇĪ„ÎŦ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ ''{callback}''", + "oauth_role_claim": "ΑÎŊÎŦθÎĩĪƒÎˇ ΁ΌÎģÎŋĪ…", + "oauth_role_claim_description": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ Ī€ÎąĪÎąĪ‡ĪŽĪÎˇĪƒÎˇ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ ÎŧÎĩ βÎŦĪƒÎˇ Ī„ÎˇÎŊ ĪĪ€ÎąĪÎžÎˇ ÎąĪ…Ī„ÎŽĪ‚ Ī„ÎˇĪ‚ ÎąÎŊÎŦθÎĩĪƒÎˇĪ‚. Η ÎąÎŊÎŦθÎĩĪƒÎˇ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎĩίÎŊιΚ ÎĩÎ¯Ī„Îĩ 'Ī‡ĪÎŽĪƒĪ„ÎˇĪ‚' ÎĩÎ¯Ī„Îĩ 'Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽĪ‚'.", "oauth_settings": "OAuth", "oauth_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ OAuth", "oauth_settings_more_details": "Για Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ ĪƒĪ‡ÎĩĪ„ÎšÎēÎŦ ÎŧÎĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą, ÎąÎŊÎąĪ„ĪÎ­ÎžĪ„Îĩ ĪƒĪ„ÎˇÎŊ Ī„ÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇ.", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ΡÎŧÎĩĪĪŽÎŊ Ī€ÎąĪÎąÎŧÎŋÎŊÎŽĪ‚ ΄ΉÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ, Ī€ĪÎšÎŊ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŽ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ Ī„ÎŋĪ…Ī‚", "trash_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ΚÎŦδÎŋĪ… Î‘Ī€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "trash_settings_description": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸Î¯ĪƒÎĩΉÎŊ ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", + "unlink_all_oauth_accounts": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧĪŽÎŊ OAuth", + "unlink_all_oauth_accounts_description": "ΜηÎŊ ΞÎĩ·ÎŦ΃ÎĩĪ„Îĩ ÎŊÎą ÎąĪ€ÎŋĪƒĪ…ÎŊÎ´Î­ĪƒÎĩĪ„Îĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪĪ‚ OAuth Ī€ĪÎšÎŊ ÎŧÎĩĪ„ÎąÎ˛ÎĩÎ¯Ī„Îĩ ΃Îĩ ÎŊέÎŋ Ī€ÎŦ΁Îŋ·Îŋ.", + "unlink_all_oauth_accounts_prompt": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ€ÎŋĪƒĪ…ÎŊÎ´Î­ĪƒÎĩĪ„Îĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪĪ‚ OAuth; Î‘Ī…Ī„ĪŒ θι ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩΚ Ī„Îŋ OAuth ID ÎŗÎšÎą ÎēÎŦθÎĩ Ī‡ĪÎŽĪƒĪ„Îˇ ÎēιΚ δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎŊÎąÎšĪÎĩθÎĩί.", "user_cleanup_job": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ", "user_delete_delay": "Ο ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧĪŒĪ‚ ÎēιΚ Ī„Îą ÎąĪĪ‡ÎĩÎ¯Îą Ī„ÎŋĪ…/Ī„ÎˇĪ‚ {user} θι ΀΁ÎŋÎŗĪÎąÎŧÎŧÎąĪ„ÎšĪƒĪ„ÎŋĪÎŊ ÎŗÎšÎą ÎŋĪÎšĪƒĪ„ÎšÎēÎŽ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ΃Îĩ {delay, plural, one {# ΡÎŧÎ­ĪÎą} other {# ΡÎŧÎ­ĪÎĩĪ‚}}.", "user_delete_delay_settings": "ÎšÎąÎ¸Ī…ĪƒĪ„Î­ĪÎˇĪƒÎˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚", @@ -360,10 +398,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ ÎŗÎšÎą ÎŊÎą Ī†ÎšÎģ΄΁ÎŦ΁ÎĩĪ„Îĩ Ī„Îą ÎŧÎ­ĪƒÎą ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇĪ‚ ÎēÎąĪ„ÎŦ Ī„ÎŋÎŊ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧΌ ÎŧÎĩ βÎŦĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēÎŦ ÎēĪÎšĪ„ÎŽĪÎšÎą. ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ Î´Ī…ÎŊÎąĪ„ĪŒĪ„ÎˇĪ„Îą ÎŧΌÎŊÎŋ ÎąÎŊ Î­Ī‡ÎĩĪ„Îĩ ΀΁ÎŋβÎģÎŽÎŧÎąĪ„Îą ÎŧÎĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ Ī€ÎŋĪ… ÎĩÎŊĪ„ÎŋĪ€Î¯ÎļÎĩΚ ΌÎģÎą Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", "advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Î§ĪÎŽĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēÎŋĪ Ī†Î¯Îģ΄΁ÎŋĪ… ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚", "advanced_settings_log_level_title": "Î•Ī€Î¯Ī€ÎĩδÎŋ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚: {level}", - "advanced_settings_prefer_remote_subtitle": "ΜÎĩĪÎšÎēÎ­Ī‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ÎąĪÎŗÎŋĪÎŊ Ī€ÎŋÎģĪ ÎŊÎą ΆÎŋĪĪ„ĪŽĪƒÎŋĪ…ÎŊ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎąĪ€ĪŒ ÎąĪĪ‡ÎĩÎ¯Îą ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ. ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ ÎŗÎšÎą ÎŊÎą ΆÎŋĪĪ„ĪŽÎŊÎŋÎŊĪ„ÎąÎš ÎąÎŊĪ„Î¯ ÎąĪ…Ī„ÎŋĪ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎĩĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚.", + "advanced_settings_prefer_remote_subtitle": "ΜÎĩĪÎšÎēÎ­Ī‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ÎąĪÎŗÎŋĪÎŊ Ī€ÎŋÎģĪ ÎŊÎą ΆÎŋĪĪ„ĪŽĪƒÎŋĪ…ÎŊ ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎąĪ€ĪŒ Ī„ÎŋĪ€ÎšÎēÎŦ ÎąĪĪ‡ÎĩÎ¯Îą. ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪĪÎ¸ÎŧÎšĪƒÎˇ ÎŗÎšÎą ÎŊÎą ΆÎŋĪĪ„ĪŽÎŊÎŋÎŊĪ„ÎąÎš ÎąÎŊĪ„Î¯ ÎąĪ…Ī„ÎŋĪ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎĩĪ‚ ÎĩΚÎēΌÎŊÎĩĪ‚.", "advanced_settings_prefer_remote_title": "Î ĪÎŋĪ„Î¯ÎŧÎˇĪƒÎˇ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊΉÎŊ ÎĩΚÎēΌÎŊΉÎŊ", "advanced_settings_proxy_headers_subtitle": "ΚαθÎŋĪÎšĪƒÎŧĪŒĪ‚ ÎēÎĩĪ†ÎąÎģÎ¯Î´Ī‰ÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎĩ΃ÎŋÎģÎŦÎ˛ÎˇĪƒÎˇĪ‚ Ī€ÎŋĪ… Ī„Îŋ Immich Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą ĪƒĪ„Î­ÎģÎŊÎĩΚ ÎŧÎĩ ÎēÎŦθÎĩ ÎąÎ¯Ī„ÎˇÎŧÎą δΚÎēĪ„ĪÎŋĪ…", "advanced_settings_proxy_headers_title": "ΚÎĩĪ†ÎąÎģίδÎĩĪ‚ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎĩ΃ÎŋÎģÎŦÎ˛ÎˇĪƒÎˇĪ‚", + "advanced_settings_readonly_mode_subtitle": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚÎĩί Ī„Îˇ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ, ĪŒĪ€ÎŋĪ… ÎŋΚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŧΌÎŊÎŋ ÎŊÎą ΀΁ÎŋβÎģΡθÎŋĪÎŊ. ΕÎŊÎ­ĪÎŗÎĩΚÎĩĪ‚ ĪŒĪ€Ī‰Ī‚ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ Ī€ÎŋÎģÎģĪŽÎŊ ÎĩΚÎēΌÎŊΉÎŊ, ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ, ÎąĪ€ÎŋĪƒĪ„ÎŋÎģÎŽ (casting) ÎēιΚ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎĩίÎŊιΚ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎĩĪ‚. Η ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ/ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ Ī„ÎˇĪ‚ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ ÎŗÎ¯ÎŊÎĩĪ„ÎąÎš ÎŧÎ­ĪƒĪ‰ Ī„ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ÎēÎĩÎŊĪ„ĪÎšÎēÎŽ ÎŋÎ¸ĪŒÎŊΡ", + "advanced_settings_readonly_mode_title": "ΛÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ", "advanced_settings_self_signed_ssl_subtitle": "Î ÎąĪÎąÎēÎŦÎŧ΀΄ÎĩΚ Ī„ÎŋÎŊ έÎģÎĩÎŗĪ‡Îŋ Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŋĪ SSL Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ. Î‘Ī€ÎąĪÎąÎ¯Ī„ÎˇĪ„Îŋ ÎŗÎšÎą ÎąĪ…Ī„Îŋ-Ī…Ī€ÎŋÎŗÎĩÎŗĪÎąÎŧÎŧέÎŊÎą Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŦ.", "advanced_settings_self_signed_ssl_title": "Να ÎĩĪ€ÎšĪ„ĪÎ­Ī€ÎŋÎŊĪ„ÎąÎš ÎąĪ…Ī„Îŋ-Ī…Ī€ÎŋÎŗÎĩÎŗĪÎąÎŧÎŧέÎŊÎą Ī€ÎšĪƒĪ„ÎŋĪ€ÎŋÎšÎˇĪ„ÎšÎēÎŦ SSL", "advanced_settings_sync_remote_deletions_subtitle": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ Î´ÎšÎąÎŗĪÎąĪ†ÎŽ ÎŽ ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎĩÎŊĪŒĪ‚ Ī€ÎĩĪÎšÎŋĪ…ĪƒÎšÎąÎēÎŋĪ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ, ĪŒĪ„ÎąÎŊ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι ÎąĪ…Ī„ÎŽ Ī€ĪÎąÎŗÎŧÎąĪ„ÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš ĪƒĪ„Îŋ Î´ÎšÎąÎ´Î¯Îē΄΅Îŋ", @@ -379,6 +419,7 @@ "album_cover_updated": "ΤÎŋ ÎĩÎžĪŽĪ†Ī…ÎģÎģÎŋ Ī„ÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ, ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "album_delete_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ {album};", "album_delete_confirmation_description": "ΕÎŦÎŊ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎĩίÎŊιΚ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ, ÎŋΚ ÎŦÎģÎģÎŋΚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ δÎĩÎŊ θι ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą Î­Ī‡ÎŋĪ…ÎŊ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ.", + "album_deleted": "ΤÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ", "album_info_card_backup_album_excluded": "ΕΞΑΙΡΟÎĨΜΕΝΟ", "album_info_card_backup_album_included": "ÎŖÎĨΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΟ", "album_info_updated": "Οι Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ Ī„ÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ, ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎąÎŊ", @@ -388,7 +429,9 @@ "album_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "album_remove_user": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ Ī‡ĪÎŽĪƒĪ„Îˇ;", "album_remove_user_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ Ī„ÎŋÎŊ/Ī„ÎˇÎŊ {user};", + "album_search_not_found": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Ī€ÎŋĪ… ÎŊÎą Ī„ÎąÎšĪÎšÎŦÎļÎŋĪ…ÎŊ ÎŧÎĩ Ī„ÎˇÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎŽ ĪƒÎąĪ‚", "album_share_no_users": "ÎĻÎąÎ¯ÎŊÎĩĪ„ÎąÎš ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩΚ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ΃Îĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŽ δÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŗÎšÎą ÎŊÎą Ī„Îŋ ÎēÎŋΚÎŊÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ.", + "album_summary": "ΠÎĩĪÎ¯ÎģÎˇĪˆÎˇ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "album_updated": "ΤÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ, ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "album_updated_setting_description": "ΛÎŦβÎĩĪ„Îĩ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŧÎ­ĪƒĪ‰ email ĪŒĪ„ÎąÎŊ έÎŊÎą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Î­Ī‡ÎĩΚ ÎŊέι ÎąĪĪ‡ÎĩÎ¯Îą", "album_user_left": "Î‘Ī€ÎŋĪ‡Ī‰ĪÎŽĪƒÎąĪ„Îĩ ÎąĪ€ĪŒ Ī„Îŋ {album}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Î ĪÎŋÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊΡ Ī„ÎąÎžÎšÎŊΌÎŧÎˇĪƒÎˇ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "albums_default_sort_order_description": "Î‘ĪĪ‡ÎšÎēÎŽ Ī„ÎąÎžÎšÎŊΌÎŧÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊÎ­Ī‰ÎŊ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ.", "albums_feature_description": "ÎŖĪ…ÎģÎģÎŋÎŗÎ­Ī‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ Ī€ÎŋĪ… ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎēÎŋΚÎŊÎŋĪ€ÎŋΚΡθÎŋĪÎŊ ΃Îĩ ÎŦÎģÎģÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚.", + "albums_on_device_count": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ({count})", "all": "ΌÎģÎą", "all_albums": "ΌÎģÎą Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "all_people": "ΌÎģÎą Ī„Îą ÎŦĪ„ÎŋÎŧÎą", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ", "app_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "appears_in": "ΕÎŧĪ†ÎąÎŊίÎļÎĩĪ„ÎąÎš ΃Îĩ", + "apply_count": "Î•Ī†ÎąĪÎŧÎŋÎŗÎŽ ({count, number})", "archive": "Î‘ĪĪ‡ÎĩίÎŋ", + "archive_action_prompt": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ {count} ĪƒĪ„Îŋ Î‘ĪĪ‡ÎĩίÎŋ", "archive_or_unarchive_photo": "Î‘ĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ÎŽ ÎąĪ€ÎŋÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎąĪ‚", "archive_page_no_archived_assets": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎˇÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "archive_page_title": "Î‘ĪĪ‡ÎĩίÎŋ ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "ΤÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎąĪ€ÎŋÎēÎąĪ„ÎąĪƒĪ„ÎŦθΡÎēÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", "asset_skipped": "Î ÎąĪÎąÎģÎĩÎ¯Ī†Î¸ÎˇÎēÎĩ", "asset_skipped_in_trash": "ÎŖĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", + "asset_trashed": "ΤÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ", + "asset_troubleshoot": "ΑÎŊĪ„ÎšÎŧÎĩĪ„ĪŽĪ€ÎšĪƒÎˇ ΀΁ÎŋβÎģÎŽÎŧÎąĪ„ÎŋĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "asset_uploaded": "ΑÎŊÎĩβÎŦĪƒĪ„ÎˇÎēÎĩ", "asset_uploading": "ΑÎŊÎĩβÎŦÎļÎĩĪ„ÎąÎšâ€Ļ", "asset_viewer_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ΀΁ÎŋβÎŋÎģÎŽĪ‚ ĪƒĪ…ÎģÎģÎŋÎŗÎŽĪ‚", @@ -464,8 +512,9 @@ "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 {{name}} other {ÎŊέÎŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ}}", + "assets_added_to_albums_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ {assetTotal, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}} ΃Îĩ {albumTotal, plural, one {# ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ} other {# ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {ÎŖĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {ÎŖĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ΀΁ÎŋĪƒĪ„ÎĩθÎŋĪÎŊ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "assets_cannot_be_added_to_albums": "ΔÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ΀΁ÎŋĪƒĪ„ÎĩθÎĩί ÎēÎąÎŊέÎŊÎą {count, plural, one {ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} ΃Îĩ ÎēÎąÎŊέÎŊÎą ÎąĪ€ĪŒ Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "assets_count": "{count, plural, one {# ÎąĪĪ‡ÎĩίÎŋ} other {# ÎąĪĪ‡ÎĩÎ¯Îą}}", "assets_deleted_permanently": "{count} Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", "assets_deleted_permanently_from_server": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", @@ -482,20 +531,25 @@ "assets_trashed_count": "ΜÎĩĪ„ÎąÎēΚÎŊ. ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŦ΄ΉÎŊ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", "assets_trashed_from_server": "{count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†Î­ĪÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ÎąĪ€ĪŒ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", "assets_were_part_of_album_count": "{count, plural, one {ΤÎŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎąÎŊÎŽÎēÎĩΚ} other {Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎąÎŊÎŽÎēÎŋĪ…ÎŊ}} ΎδΡ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "assets_were_part_of_albums_count": "ΤÎŋ/Îą {count, plural, one {ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎŽĪ„ÎąÎŊ} other {ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŽĪ„ÎąÎŊ}} ΎδΡ ÎŧÎ­ĪÎŋĪ‚ ΄ΉÎŊ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "authorized_devices": "ΕξÎŋĪ…ĪƒÎšÎŋδÎŋĪ„ÎˇÎŧέÎŊÎĩĪ‚ ÎŖĪ…ĪƒÎēÎĩĪ…Î­Ī‚", "automatic_endpoint_switching_subtitle": "ÎŖĪÎŊδÎĩĪƒÎˇ Ī„ÎŋĪ€ÎšÎēÎŦ ÎŧÎ­ĪƒĪ‰ Ī„ÎŋĪ… ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋĪ… Wi-Fi ĪŒĪ„ÎąÎŊ ÎĩίÎŊιΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ ÎēιΚ Ī‡ĪÎŽĪƒÎˇ ÎĩÎŊÎąÎģÎģÎąÎēĪ„ÎšÎēĪŽÎŊ ĪƒĪ…ÎŊÎ´Î­ĪƒÎĩΉÎŊ ÎąÎģÎģÎŋĪ", "automatic_endpoint_switching_title": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎĩÎŊÎąÎģÎģÎąÎŗÎŽ URL", "autoplay_slideshow": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ÎąÎŊÎąĪ€ÎąĪÎąÎŗĪ‰ÎŗÎŽ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇĪ‚", "back": "Î Î¯ĪƒĪ‰", "back_close_deselect": "Î Î¯ĪƒĪ‰, ÎēÎģÎĩÎ¯ĪƒÎšÎŧÎŋ ÎŽ ÎąĪ€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ", + "background_backup_running_error": "Η δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎŦÎģÎĩÎšÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎĩÎēĪ„ÎĩÎģÎĩÎ¯Ī„ÎąÎš ΎδΡ, δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ΞÎĩÎēΚÎŊÎŽĪƒÎĩĪ„Îĩ ·ÎĩÎšĪÎŋÎēίÎŊÎˇĪ„Îŋ ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎŦÎģÎĩÎšÎąĪ‚", "background_location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ", "background_location_permission_content": "ΤÎŋ Immich ÎŗÎšÎą ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎģÎģÎŦÎļÎĩΚ δίÎēĪ„Ī…Îą ĪŒĪ„ÎąÎŊ Ī„ĪÎ­Ī‡ÎĩΚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ, Ī€ĪÎ­Ī€ÎĩΚ *Ī€ÎŦÎŊĪ„Îą* ÎŊÎą Î­Ī‡ÎĩΚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ĪƒĪ„ÎˇÎŊ ÎąÎēĪÎšÎ˛ÎŽ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą ĪŽĪƒĪ„Îĩ Ρ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ ÎŊÎą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą δΚιβÎŦÎļÎĩΚ Ī„Îŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… δΚÎēĪ„ĪÎŋĪ… Wi-Fi", + "background_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ Ī€ÎąĪÎąĪƒÎēΡÎŊίÎŋĪ…", + "backup": "ΑÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_album_selection_page_albums_device": "ΆÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ({count})", "backup_album_selection_page_albums_tap": "ΠÎŦĪ„ÎˇÎŧÎą ÎŗÎšÎą ĪƒĪ…ÎŧĪ€ÎĩĪÎ¯ÎģÎˇĪˆÎˇ, Î´ÎšĪ€ÎģΌ Ī€ÎŦĪ„ÎˇÎŧÎą ÎŗÎšÎą ÎĩÎžÎąÎ¯ĪÎĩĪƒÎˇ", "backup_album_selection_page_assets_scatter": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪƒÎēÎŋĪĪ€ÎšĪƒĪ„ÎŋĪÎŊ ΃Îĩ Ī€ÎŋÎģÎģÎŦ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ. ÎˆĪ„ĪƒÎš, Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą Ī€ÎĩĪÎšÎģÎˇĪ†Î¸ÎŋĪÎŊ ÎŽ ÎŊÎą ÎĩÎžÎąÎšĪÎĩθÎŋĪÎŊ ÎēÎąĪ„ÎŦ Ī„Îˇ δΚιδΚÎēÎąĪƒÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚.", "backup_album_selection_page_select_albums": "Î•Ī€ÎšÎģÎŋÎŗÎŽ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "backup_album_selection_page_selection_info": "ΠÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎŽĪ‚", "backup_album_selection_page_total_assets": "ÎŖĪ…ÎŊÎŋÎģΚÎēÎŦ ÎŧÎŋÎŊιδΚÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", + "backup_albums_sync": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "backup_all": "ΌÎģÎą", "backup_background_service_backup_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇâ€Ļ", "backup_background_service_connection_failed_message": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ ÎŧÎĩ Ī„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ. Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇâ€Ļ", @@ -521,7 +575,7 @@ "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": "ΑÎŊĪ„Î¯ÎŗĪÎąĪ†Îŋ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_controller_page_backup_selected": "Î•Ī€ÎšÎģÎĩÎŗÎŧέÎŊÎą: ", "backup_controller_page_backup_sub": "ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŗÎšÎą Ī„Îą ÎŋĪ€ÎŋÎ¯Îą Î­Ī‡ÎŋĪ…ÎŊ δΡÎŧΚÎŋĪ…ĪÎŗÎˇÎ¸Îĩί ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_controller_page_created": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ ĪƒĪ„ÎšĪ‚: {date}", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ", "backup_controller_page_uploading_file_info": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī€ÎģÎˇĪÎŋΆÎŋĪÎšĪŽÎŊ ÎąĪĪ‡ÎĩίÎŋĪ…", "backup_err_only_album": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇ Ī„ÎŋĪ… ÎŧÎŋÎŊιδΚÎēÎŋĪ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "backup_error_sync_failed": "Ο ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ. ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎĩĪ€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Ī„ÎŋĪ… ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚.", "backup_info_card_assets": "ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "backup_manual_cancelled": "ΑÎēĪ…ĪĪŽÎ¸ÎˇÎēÎĩ", "backup_manual_in_progress": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ΃Îĩ ÎĩΞέÎģΚΞΡ. ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ ÎąĪÎŗĪŒĪ„ÎĩĪÎą", "backup_manual_success": "Î•Ī€ÎšĪ„Ī…Ī‡Î¯Îą", "backup_manual_title": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚", + "backup_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_options_page_title": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "backup_setting_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎēιΚ ĪƒĪ„Îŋ ΀΁Îŋ΃ÎēÎŽÎŊΚÎŋ", + "backup_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΄ΉÎŊ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚", "backward": "Î ĪÎŋĪ‚ Ī„Îą Ī€Î¯ĪƒĪ‰", "biometric_auth_enabled": "ΒιÎŋÎŧÎĩĪ„ĪÎšÎēÎŽ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ", "biometric_locked_out": "Î•Î¯ĪƒĪ„Îĩ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋΚ ÎĩÎēĪ„ĪŒĪ‚ Ī„ÎˇĪ‚ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽĪ‚ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", @@ -587,6 +644,7 @@ "cancel": "ΑÎēĪĪĪ‰ĪƒÎˇ", "cancel_search": "ΑÎēĪĪĪ‰ĪƒÎˇ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", "canceled": "ΑÎē΅΁ΉÎŧέÎŊÎŋ", + "canceling": "ΑÎēĪ…ĪĪŽÎŊÎĩĪ„ÎąÎš", "cannot_merge_people": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ĪƒĪ…ÎŗĪ‡ĪŽÎŊÎĩĪ…ĪƒÎˇ ÎąĪ„ĪŒÎŧΉÎŊ", "cannot_undo_this_action": "ΔÎĩÎŊ ÎŧĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩÎŊÎ­ĪÎŗÎĩΚι!", "cannot_update_the_description": "Î‘Î´ĪÎŊÎąĪ„Îˇ Ρ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", @@ -609,6 +667,8 @@ "change_pin_code": "ΑÎģÎģÎąÎŗÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "change_your_password": "ΑÎģÎģÎŦÎžĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ ĪƒÎąĪ‚", "changed_visibility_successfully": "Η ΀΁ÎŋβÎŋÎģÎŽ, ÎŦÎģÎģιΞÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", + "charging": "ÎĻĪŒĪĪ„ÎšĪƒÎˇ", + "charging_requirement_mobile_backup": "Η δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎŦÎģÎĩÎšÎąĪ‚ ĪƒĪ„Îŋ Ī€ÎąĪÎąĪƒÎēÎŽÎŊΚÎŋ ÎąĪ€ÎąÎšĪ„Îĩί Ρ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ÎŊÎą ΆÎŋĪĪ„Î¯ÎļÎĩΚ", "check_corrupt_asset_backup": "ΈÎģÎĩÎŗĪ‡ÎŋĪ‚ ÎŗÎšÎą ÎēÎąĪ„ÎĩĪƒĪ„ĪÎąÎŧÎŧέÎŊÎą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "check_corrupt_asset_backup_button": "ΕÎēĪ„Î­ÎģÎĩĪƒÎˇ ÎĩÎģÎ­ÎŗĪ‡ÎŋĪ…", "check_corrupt_asset_backup_description": "ΕÎēĪ„Î­ÎģÎĩ΃Îĩ ÎąĪ…Ī„ĪŒÎŊ Ī„ÎŋÎŊ έÎģÎĩÎŗĪ‡Îŋ ÎŧΌÎŊÎŋ ÎŧÎ­ĪƒĪ‰ Wi-Fi ÎēιΚ ÎąĪ†ÎŋĪ Î­Ī‡ÎŋĪ…ÎŊ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„Îĩί ΌÎģÎą Ī„Îą ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ. Η δΚιδΚÎēÎąĪƒÎ¯Îą ÎŧĪ€Îŋ΁Îĩί ÎŊÎą Î´ÎšÎąĪÎēÎ­ĪƒÎĩΚ ÎŧÎĩĪÎšÎēÎŦ ÎģÎĩ΀΄ÎŦ.", @@ -618,6 +678,7 @@ "clear": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ", "clear_all": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ ΌÎģΉÎŊ", "clear_all_recent_searches": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ Ī€ĪĪŒĪƒĪ†ÎąĪ„Ī‰ÎŊ ÎąÎŊÎąÎļÎˇĪ„ÎŽĪƒÎĩΉÎŊ", + "clear_file_cache": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ Ī„ÎˇĪ‚ Î ĪÎŋĪƒĪ‰ĪÎšÎŊÎŽĪ‚ ΜÎŊÎŽÎŧÎˇĪ‚ Î‘ĪĪ‡ÎĩÎ¯Ī‰ÎŊ", "clear_message": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ ÎŧΡÎŊĪÎŧÎąĪ„ÎŋĪ‚", "clear_value": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ Ī„ÎšÎŧÎŽĪ‚", "client_cert_dialog_msg_confirm": "ΟΚ", @@ -688,11 +749,13 @@ "create_new_user": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊέÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", "create_shared_album_page_share_add_assets": "Î ÎĄÎŸÎŖÎ˜Î—ÎšÎ— ÎŖÎ¤ÎŸÎ™Î§Î•Î™ÎŠÎ", "create_shared_album_page_share_select_photos": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎĻΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", + "create_shared_link": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "create_tag": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚", "create_tag_description": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎŊÎ­ÎąĪ‚ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚. Για Ī„ÎšĪ‚ έÎŊθÎĩĪ„ÎĩĪ‚ ÎĩĪ„ÎšÎēÎ­Ī„ÎĩĪ‚, Ī€ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„Îˇ Ī€ÎģÎŽĪÎˇ Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ Ī„ÎˇĪ‚, ĪƒĪ…ÎŧĪ€ÎĩĪÎšÎģÎąÎŧβιÎŊÎŋÎŧέÎŊΉÎŊ ΄ΉÎŊ ÎēÎŦθÎĩ΄ΉÎŊ Î´ÎšÎąĪ‡Ī‰ĪÎšĪƒĪ„ÎšÎēĪŽÎŊ.", "create_user": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą Ī‡ĪÎŽĪƒĪ„Îˇ", "created": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ", "created_at": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ", + "creating_linked_albums": "ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊΉÎŊ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ...", "crop": "Î‘Ī€ÎŋÎēÎŋĪ€ÎŽ", "curated_object_page_title": "Î ĪÎŦÎŗÎŧÎąĪ„Îą", "current_device": "Î¤ĪÎ­Ī‡ÎŋĪ…ĪƒÎą ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", @@ -700,10 +763,11 @@ "current_server_address": "Î¤ĪÎ­Ī‡ÎŋĪ…ĪƒÎą δΚÎĩĪÎ¸Ī…ÎŊĪƒÎˇ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "custom_locale": "Î ĪÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΡ ΤÎŋĪ€ÎšÎēÎŽ ÎĄĪÎ¸ÎŧÎšĪƒÎˇ", "custom_locale_description": "ΜÎŋ΁ΆÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎšĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊίÎĩĪ‚ ÎēιΚ Ī„ÎŋĪ…Ī‚ ÎąĪÎšÎ¸ÎŧÎŋĪĪ‚, ĪƒĪÎŧΆΉÎŊÎą ÎŧÎĩ Ī„Îˇ ÎŗÎģĪŽĪƒĪƒÎą ÎēιΚ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŋĪ‡ÎŽ", + "custom_url": "Î ĪÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΡ δΚÎĩĪÎ¸Ī…ÎŊĪƒÎˇ URL", "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "ÎŖÎēÎŋĪĪÎŋ", - "darkTheme": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ΃ÎēÎŋĪĪÎŋĪ… θέÎŧÎąĪ„ÎŋĪ‚", + "dark_theme": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ΃ÎēÎŋĪ„ÎĩΚÎŊÎŽĪ‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇĪ‚", "date_after": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŧÎĩĪ„ÎŦ", "date_and_time": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎēιΚ ĪŽĪÎą", "date_before": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą Ī€ĪÎšÎŊ", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Η ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎŗÎ­ÎŊÎŊÎˇĪƒÎˇĪ‚ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡ĪŽĪ‚", "date_range": "Î•ĪĪÎŋĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎšĪŽÎŊ", "day": "ΗÎŧÎ­ĪÎą", + "days": "ΗÎŧÎ­ĪÎĩĪ‚", "deduplicate_all": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", "deduplication_criteria_1": "ÎœÎ­ÎŗÎĩθÎŋĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚ ΃Îĩ byte", "deduplication_criteria_2": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ EXIF", @@ -719,6 +784,8 @@ "default_locale": "Î ĪÎŋÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊΡ ΤÎŋĪ€ÎšÎēÎŽ ÎĄĪÎ¸ÎŧÎšĪƒÎˇ", "default_locale_description": "ΜÎŋ΁ΆÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ Ī„ÎšĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊίÎĩĪ‚ ÎēιΚ Ī„ÎŋĪ…Ī‚ ÎąĪÎšÎ¸ÎŧÎŋĪĪ‚ ÎŧÎĩ βÎŦĪƒÎˇ Ī„ÎˇÎŊ Ī„ÎŋĪ€ÎšÎēÎŽ ĪĪÎ¸ÎŧÎšĪƒÎˇ Ī„ÎŋĪ… ΀΁ÎŋÎŗĪÎŦÎŧÎŧÎąĪ„ÎŋĪ‚ Ī€ÎĩĪÎšÎŽÎŗÎˇĪƒÎŽĪ‚ ĪƒÎąĪ‚", "delete": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ", + "delete_action_confirmation_message": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ ÎąĪĪ‡ÎĩίÎŋ; Î‘Ī…Ī„ÎŽ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι θι Ī„Îŋ ÎŧÎĩĪ„ÎąÎēΚÎŊÎŽĪƒÎĩΚ ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎēιΚ θι ÎĩÎŧĪ†ÎąÎŊÎšĪƒĪ„Îĩί ÎŧÎŽÎŊĪ…ÎŧÎą ÎŗÎšÎą Ī„Îŋ ÎąÎŊ θέÎģÎĩĪ„Îĩ ÎŊÎą Ī„Îŋ Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎēιΚ Ī„ÎŋĪ€ÎšÎēÎŦ", + "delete_action_prompt": "{count} Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ", "delete_album": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "delete_api_key_prompt": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ ÎąĪ…Ī„ĪŒ ÎēÎģÎĩΚδί API;", "delete_dialog_alert": "Î‘Ī…Ī„ÎŦ Ī„Îą ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎąĪ€ĪŒ Ī„Îŋ Immich ÎēιΚ ÎąĪ€ĪŒ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ĪƒÎąĪ‚", @@ -732,9 +799,12 @@ "delete_key": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎēÎģÎĩΚδΚÎŋĪ", "delete_library": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΒιβÎģΚÎŋθΎÎēÎˇĪ‚", "delete_link": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", + "delete_local_action_prompt": "{count} Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎąÎŊ Ī„ÎŋĪ€ÎšÎēÎŦ", "delete_local_dialog_ok_backed_up_only": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎŧΌÎŊÎŋ ΄ΉÎŊ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "delete_local_dialog_ok_force": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ĪŒĪ€Ī‰Ī‚ ÎēιΚ ÎŊÎą Î­Ī‡ÎĩΚ", "delete_others": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ Ī…Ī€ÎŋÎģÎŋÎ¯Ī€Ī‰ÎŊ", + "delete_permanently": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", + "delete_permanently_action_prompt": "{count} Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ", "delete_shared_link": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "delete_shared_link_dialog_title": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΚÎŋΚÎŊÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋĪ… ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "delete_tag": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ÎĩĪ„ÎšÎēÎ­Ī„ÎąĪ‚", @@ -745,6 +815,7 @@ "description": "ΠÎĩĪÎšÎŗĪÎąĪ†ÎŽ", "description_input_hint_text": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚...", "description_input_submit_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚, ÎĩÎģÎ­ÎŗÎžĪ„Îĩ Ī„Îŋ ÎąĪĪ‡ÎĩίÎŋ ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚ ÎŗÎšÎą Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎģÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚", + "deselect_all": "ΑÎēĪĪĪ‰ĪƒÎˇ ΌÎģΉÎŊ ΄ΉÎŊ ÎĩĪ€ÎšÎģÎŋÎŗĪŽÎŊ", "details": "ΛÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚", "direction": "ÎšÎąĪ„ÎĩĪÎ¸Ī…ÎŊĪƒÎˇ", "disabled": "Î‘Ī€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", @@ -762,6 +833,7 @@ "documentation": "ΤÎĩÎēÎŧÎˇĪÎ¯Ī‰ĪƒÎˇ", "done": "ÎˆÎŗÎšÎŊÎĩ", "download": "Î›ÎŽĪˆÎˇ", + "download_action_prompt": "ÎšÎąĪ„Î­Î˛ÎąĪƒÎŧÎą {count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "download_canceled": "Η ÎģÎŽĪˆÎˇ ÎąÎēĪ…ĪĪŽÎ¸ÎˇÎēÎĩ", "download_complete": "Η ÎģÎŽĪˆÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ", "download_enqueue": "Η ÎģÎŽĪˆÎˇ Ī„Î­Î¸ÎˇÎēÎĩ ΃Îĩ Îŋ΅΁ÎŦ", @@ -788,8 +860,12 @@ "edit": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą", "edit_album": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "edit_avatar": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŦÎ˛ÎąĪ„ÎąĪ", + "edit_birthday": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŗÎĩÎŊÎĩθÎģÎ¯Ī‰ÎŊ", "edit_date": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚", "edit_date_and_time": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚ ÎēιΚ ĪŽĪÎąĪ‚", + "edit_date_and_time_action_prompt": "{count} ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎēιΚ ĪŽĪÎą ÎĩĪ€ÎĩΞÎĩĪÎŗÎŦĪƒĪ„ÎˇÎēÎąÎŊ", + "edit_date_and_time_by_offset": "ΑÎģÎģÎąÎŗÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯ÎąĪ‚ ÎŧÎĩ ÎŧÎĩĪ„ÎąĪ„ĪŒĪ€ÎšĪƒÎˇ", + "edit_date_and_time_by_offset_interval": "ΝέÎŋ ÎĩĪĪÎŋĪ‚ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎšĪŽÎŊ: {from} - {to}", "edit_description": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "edit_description_prompt": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩĪ€ÎšÎģÎ­ÎžĪ„Îĩ ÎŊέι Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽ:", "edit_exclusion_pattern": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŧÎŋĪ„Î¯Î˛ÎŋĪ… ÎąĪ€ÎŋÎēÎģÎĩÎšĪƒÎŧÎŋĪ", @@ -799,6 +875,7 @@ "edit_key": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎēÎģÎĩΚδΚÎŋĪ", "edit_link": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "edit_location": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", + "edit_location_action_prompt": "Î•Ī€ÎĩΞÎĩĪÎŗÎŦĪƒĪ„ÎˇÎēÎąÎŊ {count} Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎĩĪ‚", "edit_location_dialog_title": "ΤÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", "edit_name": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", "edit_people": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą ÎąĪ„ĪŒÎŧΉÎŊ", @@ -817,6 +894,7 @@ "empty_trash": "ΆδÎĩÎšÎąĪƒÎŧÎą ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "empty_trash_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ÎŋĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ιδÎĩΚÎŦ΃ÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ; Î‘Ī…Ī„ĪŒ θι ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩΚ ÎŧΌÎŊΚÎŧÎą ΌÎģÎą Ī„Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī„ÎŋĪ… ÎēÎŦδÎŋĪ… ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ Ī„ÎŋĪ… Immich. \nÎ‘Ī…Ī„ÎŽ Ρ ÎĩÎŊÎ­ĪÎŗÎĩΚι δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎąÎŊÎąÎšĪÎĩθÎĩί!", "enable": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", + "enable_backup": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąÎŊĪ„ÎšÎŗĪÎŦΆÎŋĪ… ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "enable_biometric_auth_description": "Î•ÎšĪƒÎŦÎŗÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚ ÎŗÎšÎą ÎŊÎą ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ Ī„ÎˇÎŊ βΚÎŋÎŧÎĩĪ„ĪÎšÎēÎŽ Ī„ÎąĪ…Ī„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "enabled": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ", "end_date": "ΤÎĩÎģΚÎēÎŽ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą", @@ -827,7 +905,9 @@ "error": "ÎŖĪ†ÎŦÎģÎŧÎą", "error_change_sort_album": "Î‘Ī€Î­Ī„Ī…Ī‡Îĩ Ρ ÎąÎģÎģÎąÎŗÎŽ ΃ÎĩÎšĪÎŦĪ‚ Ī„ÎŋĪ… ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "error_delete_face": "ÎŖĪ†ÎŦÎģÎŧÎą Î´ÎšÎąÎŗĪÎąĪ†ÎŽĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… ÎąĪ€ĪŒ Ī„Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ", + "error_getting_places": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎąÎŊÎŦÎēĪ„ÎˇĪƒÎˇ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎšĪŽÎŊ", "error_loading_image": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī„ÎˇĪ‚ ÎĩΚÎēΌÎŊÎąĪ‚", + "error_loading_partners": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ ĪƒĪ…ÎŊÎĩĪÎŗÎąĪ„ĪŽÎŊ: {error}", "error_saving_image": "ÎŖĪ†ÎŦÎģÎŧÎą: {error}", "error_tag_face_bounding_box": "ÎŖĪ†ÎŦÎģÎŧÎą ÎĩĪ€ÎšĪƒÎŽÎŧÎąÎŊĪƒÎˇĪ‚ ΀΁ÎŋĪƒĪŽĪ€ÎŋĪ… - δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎģÎˇĪ†Î¸ÎŋĪÎŊ ÎŋΚ ĪƒĪ…ÎŊĪ„ÎĩĪ„ÎąÎŗÎŧέÎŊÎĩĪ‚ Ī„ÎŋĪ… Ī€ÎģÎąÎšĪƒÎ¯ÎŋĪ… ÎŋĪÎšÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚", "error_title": "ÎŖĪ†ÎŦÎģÎŧÎą - ΚÎŦĪ„Îš Ī€ÎŽÎŗÎĩ ĪƒĪ„ĪÎąÎ˛ÎŦ", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ÎĩΚδÎŋĪ€ÎŋÎšÎŽĪƒÎĩΉÎŊ", "failed_to_load_people": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ÎąĪ„ĪŒÎŧΉÎŊ", "failed_to_remove_product_key": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎąĪ†ÎąÎ¯ĪÎĩĪƒÎˇĪ‚ ÎēÎģÎĩΚδΚÎŋĪ ΀΁ÎŋΊΌÎŊĪ„ÎŋĪ‚", + "failed_to_reset_pin_code": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦĪ‚ Ī„ÎŋĪ… PIN", "failed_to_stack_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪ„ÎˇÎŊ ĪƒĪ…ÎŧĪ€Î¯ÎĩĪƒÎˇ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "failed_to_unstack_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ĪƒĪ„ÎˇÎŊ ÎąĪ€ÎŋĪƒĪ…ÎŧĪ€Î¯ÎĩĪƒÎˇ ΄ΉÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "failed_to_update_notification_status": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇĪ‚ Ī„ÎˇĪ‚ ÎēÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇĪ‚ ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# Î´ÎšÎąÎ´ĪÎŋÎŧÎŽ} other {# Î´ÎšÎąÎ´ĪÎŋÎŧÎ­Ī‚}} ÎąĪ€Î­Ī„Ī…Ī‡ÎąÎŊ ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎēĪĪĪ‰ĪƒÎˇ", "profile_picture_transparent_pixels": "Οι ÎĩΚÎēΌÎŊÎĩĪ‚ ΀΁ÎŋĪ†Î¯Îģ δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą Î­Ī‡ÎŋĪ…ÎŊ Î´ÎšÎąĪ†ÎąÎŊÎŽ ÎĩΚÎēÎŋÎŊÎŋĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą. Î ÎąĪÎąÎēÎąÎģĪŽ ÎŧÎĩÎŗÎĩÎ¸ĪÎŊÎĩĪ„Îĩ ÎŽ/ÎēιΚ ÎŧÎĩĪ„ÎąÎēΚÎŊÎŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎĩΚÎēΌÎŊÎą.", "quota_higher_than_disk_size": "ÎˆĪ‡ÎĩĪ„Îĩ ÎŋĪÎ¯ĪƒÎĩΚ έÎŊÎą ĪŒĪÎšÎŋ, ÎŧÎĩÎŗÎąÎģĪĪ„Îĩ΁Îŋ ÎąĪ€ĪŒ Ī„Îŋ ÎŧÎ­ÎŗÎĩθÎŋĪ‚ Ī„ÎŋĪ… Î´Î¯ĪƒÎēÎŋĪ…", + "something_went_wrong": "ΚÎŦĪ„Îš Ī€ÎŽÎŗÎĩ ĪƒĪ„ĪÎąÎ˛ÎŦ", "unable_to_add_album_users": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ΀΁ÎŋĪƒÎ¸ÎŽÎēÎˇĪ‚ Ī‡ĪÎŽĪƒĪ„Îˇ ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "unable_to_add_assets_to_shared_link": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ΀΁ÎŋĪƒÎ¸ÎŽÎēÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… ĪƒĪ„ÎŋÎŊ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ", "unable_to_add_comment": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ΀΁ÎŋĪƒÎ¸ÎŽÎēÎˇĪ‚ ĪƒĪ‡ÎŋÎģίÎŋĪ…", @@ -953,13 +1035,11 @@ }, "exif": "ΜÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊÎą Exif", "exif_bottom_sheet_description": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΠÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚...", + "exif_bottom_sheet_description_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎˇĪ‚ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽĪ‚", "exif_bottom_sheet_details": "Î›Î•Î Î¤ÎŸÎœÎ•ÎĄÎ•Î™Î•ÎŖ", "exif_bottom_sheet_location": "Î¤ÎŸÎ ÎŸÎ˜Î•ÎŖÎ™Î‘", "exif_bottom_sheet_people": "ΑΤΟΜΑ", "exif_bottom_sheet_person_add_person": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚", - "exif_bottom_sheet_person_age_months": "ΗÎģΚÎēÎ¯Îą {months} ÎŧÎŽÎŊÎĩĪ‚", - "exif_bottom_sheet_person_age_year_months": "ΗÎģΚÎēÎ¯Îą 1 Î­Ī„ÎŋĪ…Ī‚, {months} ÎŧΡÎŊĪŽÎŊ", - "exif_bottom_sheet_person_age_years": "ΗÎģΚÎēÎ¯Îą {years}", "exit_slideshow": "ΈΞÎŋδÎŋĪ‚ ÎąĪ€ĪŒ Ī„ÎˇÎŊ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇ", "expand_all": "ΑÎŊÎŦĪ€Ī„Ī…ÎžÎˇ ΌÎģΉÎŊ", "experimental_settings_new_asset_list_subtitle": "ÎŖÎĩ ÎĩΞέÎģΚΞΡ", @@ -973,6 +1053,8 @@ "explorer": "ΠÎĩĪÎšÎˇÎŗÎˇĪ„ÎŽĪ‚", "export": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ", "export_as_json": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ Ή΂ JSON", + "export_database": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "export_database_description": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ Ī„ÎˇĪ‚ SQLite βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", "extension": "Î•Ī€Î­ÎēĪ„ÎąĪƒÎˇ", "external": "Î•ÎžĪ‰Ī„ÎĩĪÎšÎēĪŒĪ‚", "external_libraries": "Î•ÎžĪ‰Ī„ÎĩĪÎšÎēÎ­Ī‚ βΚβÎģΚÎŋθΎÎēÎĩĪ‚", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "failed_to_load_folder": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą Ī†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚ Ī†ÎąÎēέÎģÎŋĪ…", "favorite": "Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎŋ", + "favorite_action_prompt": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ {count} ĪƒĪ„Îą Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "favorite_or_unfavorite_photo": "ÎŸĪÎ¯ĪƒĪ„Îĩ ÎŧÎ¯Îą ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą Ή΂ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊΡ ÎŽ ÎąĪ†ÎąÎšĪÎ­ĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎąĪ€ĪŒ Ī„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "favorites": "Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "favorites_page_no_favorites": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "feature_photo_updated": "Η ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "features": "Î§ÎąĪÎąÎēĪ„ÎˇĪÎšĪƒĪ„ÎšÎēÎŦ", + "features_in_development": "ΛÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯ÎĩĪ‚ Ī…Ī€ĪŒ ΑÎŊÎŦĪ€Ī„Ī…ÎžÎˇ", "features_setting_description": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îą Ī‡ÎąĪÎąÎēĪ„ÎˇĪÎšĪƒĪ„ÎšÎēÎŦ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "file_name": "ΌÎŊÎŋÎŧÎą ÎąĪĪ‡ÎĩίÎŋĪ…", "file_name_or_extension": "ΌÎŊÎŋÎŧÎą ÎąĪĪ‡ÎĩίÎŋĪ… ÎŽ ÎĩĪ€Î­ÎēĪ„ÎąĪƒÎˇ", @@ -998,21 +1082,26 @@ "filter_people": "ÎĻΚÎģ΄΁ÎŦĪÎšĪƒÎŧÎą ÎąĪ„ĪŒÎŧΉÎŊ", "filter_places": "ÎĻΚÎģ΄΁ÎŦĪÎšĪƒÎŧÎą Ī„ÎŋĪ€ÎŋθÎĩĪƒÎšĪŽÎŊ", "find_them_fast": "Î’ĪÎĩÎ¯Ī„Îĩ Ī„ÎŋĪ…Ī‚ ÎŗĪÎŽÎŗÎŋĪÎą ÎŧÎĩ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ ΌÎŊÎŋÎŧÎą", + "first": "Î‘ĪĪ‡ÎšÎēÎŦ", "fix_incorrect_match": "Î”ÎšĪŒĪÎ¸Ī‰ĪƒÎˇ ÎģÎąÎŊÎ¸ÎąĪƒÎŧέÎŊÎˇĪ‚ ÎąÎŊĪ„ÎšĪƒĪ„ÎŋÎ¯Ī‡ÎšĪƒÎˇĪ‚", "folder": "ÎĻÎŦÎēÎĩÎģÎŋĪ‚", "folder_not_found": "Ο ΆÎŦÎēÎĩÎģÎŋĪ‚ δÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎĩ", "folders": "ÎĻÎŦÎēÎĩÎģÎŋΚ", "folders_feature_description": "ΠÎĩĪÎšÎŽÎŗÎˇĪƒÎˇ ĪƒĪ„ÎˇÎŊ ΀΁ÎŋβÎŋÎģÎŽ Ī†ÎąÎēέÎģÎŋĪ… ÎŗÎšÎą Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„Îŋ ĪƒĪĪƒĪ„ÎˇÎŧÎą ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ", + "forgot_pin_code_question": "ΞÎĩ·ÎŦĪƒÎąĪ„Îĩ Ī„Îŋ PIN;", "forward": "Î ĪÎŋĪ‚ Ī„Îą ÎĩÎŧĪ€ĪĪŒĪ‚", "gcast_enabled": "ΜÎĩĪ„ÎŦδÎŋĪƒÎˇ Ī€ÎĩĪÎšÎĩ·ÎŋÎŧέÎŊÎŋĪ… Google Cast", "gcast_enabled_description": "Î‘Ī…Ī„ĪŒ Ī„Îŋ Ī‡ÎąĪÎąÎēĪ„ÎˇĪÎšĪƒĪ„ÎšÎēΌ ΆÎŋĪĪ„ĪŽÎŊÎĩΚ ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŋĪĪ‚ Ī€ĪŒĪÎŋĪ…Ī‚ ÎąĪ€ĪŒ Ī„Îˇ Google ÎŗÎšÎą ÎŊÎą ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎŽĪƒÎĩΚ.", "general": "ΓÎĩÎŊΚÎēÎŦ", + "geolocation_instruction_location": "ΚÎŦÎŊÎĩ ÎēÎģΚÎē ΃Îĩ έÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ ÎŧÎĩ ĪƒĪ…ÎŊĪ„ÎĩĪ„ÎąÎŗÎŧέÎŊÎĩĪ‚ GPS ÎŗÎšÎą ÎŊÎą Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚ Ī„ÎˇÎŊ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą Ī„ÎŋĪ…, ÎŽ ÎĩĪ€Î¯ÎģÎĩΞÎĩ ÎąĪ€ÎĩĪ…Î¸ÎĩÎ¯ÎąĪ‚ ÎŧΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą ÎąĪ€ĪŒ Ī„ÎŋÎŊ ·ÎŦĪĪ„Îˇ", "get_help": "Î–ÎˇĪ„ÎŽĪƒĪ„Îĩ βÎŋΎθÎĩΚι", "get_wifiname_error": "ΔÎĩÎŊ ÎŽĪ„ÎąÎŊ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎģÎŽĪˆÎˇ Ī„ÎŋĪ… ÎŋÎŊΌÎŧÎąĪ„ÎŋĪ‚ Wi-Fi. ΒÎĩÎ˛ÎąÎšĪ‰Î¸ÎĩÎ¯Ī„Îĩ ĪŒĪ„Îš Î­Ī‡ÎĩĪ„Îĩ Î´ĪŽĪƒÎĩΚ Ī„ÎšĪ‚ ÎąĪ€ÎąĪÎąÎ¯Ī„ÎˇĪ„ÎĩĪ‚ ÎŦδÎĩΚÎĩĪ‚ ÎēιΚ ĪŒĪ„Îš ÎĩÎ¯ĪƒĪ„Îĩ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋΚ ΃Îĩ δίÎē΄΅Îŋ Wi-Fi", "getting_started": "ΞÎĩÎēΚÎŊĪŽÎŊĪ„ÎąĪ‚", "go_back": "Î ÎˇÎŗÎąÎ¯ÎŊÎĩĪ„Îĩ Ī€Î¯ĪƒĪ‰", "go_to_folder": "ΜÎĩĪ„ÎŦÎ˛ÎąĪƒÎˇ ĪƒĪ„Îŋ ΆÎŦÎēÎĩÎģÎŋ", "go_to_search": "Î ÎˇÎŗÎąÎ¯ÎŊÎĩĪ„Îĩ ĪƒĪ„ÎˇÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ", + "gps": "GPS", + "gps_missing": "Î§Ī‰ĪÎ¯Ī‚ GPS", "grant_permission": "Î•Ī€ÎšĪ„ĪÎ­ĪˆĪ„Îĩ Ī„ÎˇÎŊ ÎŦδÎĩΚι", "group_albums_by": "ΟÎŧιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎēÎąĪ„ÎŦ...", "group_country": "ΟÎŧιδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎēÎąĪ„ÎŦ Ī‡ĪŽĪÎą", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "ΕÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ÎąĪ€Ī„ÎšÎēÎŽĪ‚ ÎąÎŊÎŦÎ´ĪÎąĪƒÎˇĪ‚", "haptic_feedback_title": "Î‘Ī€Ī„ÎšÎēÎŽ ΑÎŊÎŦÎ´ĪÎąĪƒÎˇ", "has_quota": "ÎˆĪ‡ÎĩΚ Ī€ÎŋĪƒĪŒĪƒĪ„Ī‰ĪƒÎˇ", + "hash_asset": "ÎšÎąĪ„ÎąÎēÎĩ΁ÎŧÎąĪ„ÎšĪƒÎŧĪŒĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", + "hashed_assets": "ÎšÎąĪ„ÎąÎēÎĩ΁ÎŧÎąĪ„ÎšĪƒÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", + "hashing": "ÎšÎąĪ„ÎąÎēÎĩ΁ÎŧÎąĪ„ÎšĪƒÎŧĪŒĪ‚", "header_settings_add_header_tip": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΚÎĩĪ†ÎąÎģÎ¯Î´ÎąĪ‚", "header_settings_field_validator_msg": "Η Ī„ÎšÎŧÎŽ δÎĩÎŊ ÎŧĪ€Îŋ΁Îĩί ÎŊÎą ÎĩίÎŊιΚ ÎēÎĩÎŊÎŽ", "header_settings_header_name_input": "ΌÎŊÎŋÎŧÎą ÎēÎĩĪ†ÎąÎģÎ¯Î´ÎąĪ‚", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎąÎŊÎĩβÎŦ΃ÎĩĪ„Îĩ ÎŧΌÎŊÎŋ 30 ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎēÎŦθÎĩ ΆÎŋ΁ÎŦ, Ī€ÎąĪÎąÎģÎĩÎ¯Ī€ÎĩĪ„ÎąÎš", "host": "ÎĻΚÎģÎŋΞÎĩÎŊÎ¯Îą", "hour": "ÎĪÎą", + "hours": "ÎĪÎĩĪ‚", "id": "ID", + "idle": "Î‘Î´ĪÎŦÎŊÎĩΚι", "ignore_icloud_photos": "Î‘ÎŗÎŊÎŋÎŽĪƒĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ iCloud", "ignore_icloud_photos_description": "Οι ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ Ī€ÎŋĪ… ÎĩίÎŊιΚ ÎąĪ€ÎŋθΡÎēÎĩĪ…ÎŧέÎŊÎĩĪ‚ ĪƒĪ„Îŋ iCloud δÎĩÎŊ θι ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸ÎŋĪÎŊ ĪƒĪ„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ Immich", "image": "ΕιÎēΌÎŊÎą", @@ -1112,10 +1206,13 @@ "language_no_results_title": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎŗÎģĪŽĪƒĪƒÎĩĪ‚", "language_search_hint": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ÎŗÎģĪ‰ĪƒĪƒĪŽÎŊ...", "language_setting_description": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ Ī„Îˇ ÎŗÎģĪŽĪƒĪƒÎą Ī€ÎŋĪ… ΀΁ÎŋĪ„ÎšÎŧÎŦĪ„Îĩ", + "large_files": "ΜÎĩÎŗÎŦÎģÎą Î‘ĪĪ‡ÎĩÎ¯Îą", + "last": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą", "last_seen": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą ΀΁ÎŋβÎŋÎģÎŽ", "latest_version": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą ΈÎēδÎŋĪƒÎˇ", "latitude": "ΓÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ Ī€ÎģÎŦĪ„ÎŋĪ‚", "leave": "Î•ÎŗÎēÎąĪ„ÎŦÎģÎĩÎšĪˆÎˇ", + "leave_album": "Î‘Ī€ÎŋĪ‡ĪŽĪÎˇĪƒÎˇ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "lens_model": "ΜÎŋÎŊĪ„Î­ÎģÎŋ Ī†ÎąÎēÎŋĪ", "let_others_respond": "Î•Ī€Î­Ī„ĪÎĩΈÎĩ ΃Îĩ ÎŦÎģÎģÎŋĪ…Ī‚ ÎŊÎą ÎąĪ€ÎąÎŊĪ„ÎŽĪƒÎŋĪ…ÎŊ", "level": "Î•Ī€Î¯Ī€ÎĩδÎŋ", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚", "library_page_sort_last_modified": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯Îą ΄΁ÎŋĪ€ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "library_page_sort_title": "Î¤Î¯Ī„ÎģÎŋĪ‚ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "licenses": "ΆδÎĩΚÎĩĪ‚", "light": "ÎĻΉ΄ÎĩΚÎŊΌ", + "like": "ΜÎŋĪ… ÎąĪÎ­ĪƒÎĩΚ", "like_deleted": "ΤÎŋ \"ÎŧÎŋĪ… ÎąĪÎ­ĪƒÎĩΚ\" Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ", "link_motion_video": "ÎŖĪÎŊδÎĩ΃Îĩ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎēίÎŊÎˇĪƒÎˇĪ‚", - "link_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", "link_to_oauth": "ÎŖĪÎŊδÎĩĪƒÎˇ ĪƒĪ„ÎŋÎŊ OAuth", "linked_oauth_account": "Ο OAuth ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧĪŒĪ‚ ĪƒĪ…ÎŊδέθΡÎēÎĩ", "list": "Î›Î¯ĪƒĪ„Îą", "loading": "ÎĻĪŒĪĪ„Ī‰ĪƒÎˇ", "loading_search_results_failed": "Η Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎąĪ€ÎŋĪ„ÎĩÎģÎĩ΃ÎŧÎŦ΄ΉÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚ ÎąĪ€Î­Ī„Ī…Ī‡Îĩ", + "local": "ΤÎŋĪ€ÎšÎēÎŦ", "local_asset_cast_failed": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ… Ī€ÎŋĪ… δÎĩÎŊ Î­Ī‡ÎĩΚ ÎąÎŊέβÎĩΚ ĪƒĪ„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", + "local_assets": "ΤÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", + "local_media_summary": "ΠÎĩĪÎ¯ÎģÎˇĪˆÎˇ Ī„ÎŋĪ€ÎšÎēĪŽÎŊ Ī€ÎŋÎģĪ…ÎŧÎ­ĪƒĪ‰ÎŊ", "local_network": "ΤÎŋĪ€ÎšÎēΌ δίÎē΄΅Îŋ", "local_network_sheet_info": "Η ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ θι ĪƒĪ…ÎŊδÎĩθÎĩί ÎŧÎĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŧÎ­ĪƒĪ‰ ÎąĪ…Ī„ÎŋĪ Ī„ÎŋĪ… URL ĪŒĪ„ÎąÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„ÎąÎš Ī„Îŋ ÎēιθÎŋĪÎšĪƒÎŧέÎŊÎŋ δίÎē΄΅Îŋ Wi-Fi", "location_permission": "ΆδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ ÎĩÎ´ĪŽ Ī„Îŋ ÎŗÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ ĪƒÎąĪ‚ ÎŧÎŽÎēÎŋĪ‚", "lock": "ΚÎģÎĩÎ¯Î´Ī‰ÎŧÎą", "locked_folder": "ΚÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋĪ‚ ΆÎŦÎēÎĩÎģÎŋĪ‚", + "log_detail_title": "ΛÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚι ÎēÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚", "log_out": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ", "log_out_all_devices": "Î‘Ī€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ ÎąĪ€ĪŒ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ÎŖĪ…ĪƒÎēÎĩĪ…Î­Ī‚", "logged_in_as": "ÎŖĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎŋĪ‚ Ή΂ {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Ο ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", "logout_all_device_confirmation": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ€ÎŋĪƒĪ…ÎŊδÎĩθÎĩÎ¯Ī„Îĩ ÎąĪ€ĪŒ ΌÎģÎĩĪ‚ Ī„ÎšĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚;", "logout_this_device_confirmation": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ€ÎŋĪƒĪ…ÎŊδÎĩθÎĩÎ¯Ī„Îĩ ÎąĪ€ĪŒ ÎąĪ…Ī„ÎŽÎŊ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ;", + "logs": "ÎšÎąĪ„ÎąÎŗĪÎąĪ†Î­Ī‚", "longitude": "ΓÎĩĪ‰ÎŗĪÎąĪ†ÎšÎēΌ ÎŧÎŽÎēÎŋĪ‚", "look": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ", "loop_videos": "Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ Î˛Î¯ÎŊĪ„ÎĩÎŋ", @@ -1185,6 +1288,7 @@ "main_branch_warning": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋΚÎĩÎ¯Ī„Îĩ ÎŧΚι έÎēδÎŋĪƒÎˇ ΃Îĩ ÎąÎŊÎŦĪ€Ī„Ī…ÎžÎˇÎ‡ ĪƒĪ…ÎŊÎšĪƒĪ„ÎŋĪÎŧÎĩ ÎąÎŊÎĩĪ€ÎšĪ†ĪÎģÎąÎēĪ„Îą Ī„Îˇ Ī‡ĪÎŽĪƒÎˇ ÎŧÎšÎąĪ‚ Ī„ÎĩÎģΚÎēÎŽĪ‚ έÎēδÎŋĪƒÎˇĪ‚!", "main_menu": "ÎšĪĪÎšÎŋ ÎŧÎĩÎŊÎŋĪ", "make": "ÎšÎąĪ„ÎąĪƒÎēÎĩĪ…ÎąĪƒĪ„ÎŽĪ‚", + "manage_geolocation": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚", "manage_shared_links": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Ī‰ÎŊ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧΉÎŊ", "manage_sharing_with_partners": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎˇÎŊ ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ ÎŧÎĩ ĪƒĪ…ÎŊÎĩĪÎŗÎŦĪ„ÎĩĪ‚", "manage_the_app_settings": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ĪƒĪ…ÎŊδÎĩδÎĩÎŧέÎŊÎĩĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ĪƒÎąĪ‚", "manage_your_oauth_connection": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„Îˇ ĪƒĪÎŊδÎĩĪƒÎŽ ĪƒÎąĪ‚ OAuth", "map": "ΧÎŦĪĪ„ÎˇĪ‚", - "map_assets_in_bound": "{count} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", - "map_assets_in_bounds": "{count} ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚", + "map_assets_in_bounds": "{count, plural, =0 {ΚαÎŧÎ¯Îą ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŋĪ‡ÎŽ} one {# ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą} other {# ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚}}", "map_cannot_get_user_location": "ΔÎĩÎŊ ÎĩίÎŊιΚ Î´Ī…ÎŊÎąĪ„ÎŽ Ρ ÎģÎŽĪˆÎˇ Ī„ÎˇĪ‚ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ", "map_location_dialog_yes": "Ναι", "map_location_picker_page_use_location": "Î§ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", @@ -1202,7 +1305,6 @@ "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_title": "Η ÎŦδÎĩΚι Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ÎąĪ€ÎŋĪĪÎ¯Ī†Î¸ÎˇÎēÎĩ", "map_settings": "ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ·ÎŦĪĪ„Îˇ", @@ -1221,6 +1323,7 @@ "mark_as_read": "Î•Ī€ÎšĪƒÎŽÎŧÎąÎŊĪƒÎˇ Ή΂ ÎąÎŊÎąÎŗÎŊĪ‰ĪƒÎŧέÎŊÎŋ", "marked_all_as_read": "ΌÎģÎą ÎĩĪ€ÎšĪƒÎˇÎŧÎŦÎŊθΡÎēÎąÎŊ Ή΂ ÎąÎŊÎąÎŗÎŊĪ‰ĪƒÎŧέÎŊÎą", "matches": "ΑÎŊĪ„ÎšĪƒĪ„ÎŋÎšĪ‡Î¯ÎĩĪ‚", + "matching_assets": "ΑÎŊĪ„ÎšĪƒĪ„ÎŋÎšĪ‡Î¯Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "media_type": "Î¤ĪĪ€ÎŋĪ‚ Ī€ÎŋÎģĪ…ÎŧÎ­ĪƒÎŋĪ…", "memories": "ΑÎŊÎąÎŧÎŊÎŽĪƒÎĩÎšĪ‚", "memories_all_caught_up": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧέÎŊÎą", @@ -1239,6 +1342,7 @@ "merged_people_count": "ÎˆÎŗÎšÎŊÎĩ ĪƒĪ…ÎŗĪ‡ĪŽÎŊÎĩĪ…ĪƒÎˇ {count, plural, one {# ÎąĪ„ĪŒÎŧÎŋĪ…} other {# ÎąĪ„ĪŒÎŧΉÎŊ}}", "minimize": "ΕÎģÎąĪ‡ÎšĪƒĪ„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "minute": "ΛÎĩĪ€Ī„ĪŒ", + "minutes": "ΛÎĩ΀΄ÎŦ", "missing": "ÎŒĪƒÎą ΛÎĩÎ¯Ī€ÎŋĪ…ÎŊ", "model": "ΜÎŋÎŊĪ„Î­ÎģÎŋ", "month": "ΜήÎŊÎąĪ‚", @@ -1246,6 +1350,7 @@ "more": "ΠÎĩĪÎšĪƒĪƒĪŒĪ„ÎĩĪÎą", "move": "ΜÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇ", "move_off_locked_folder": "ΜÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇ Î­ÎžĪ‰ ÎąĪ€ĪŒ Ī„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", + "move_to_lock_folder_action_prompt": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎąÎŊ {count} ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "move_to_locked_folder": "ΜÎĩĪ„ÎąÎēίÎŊÎˇĪƒÎˇ ΃Îĩ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "move_to_locked_folder_confirmation": "Î‘Ī…Ī„Î­Ī‚ ÎŋΚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ θι ÎąĪ†ÎąÎšĪÎĩθÎŋĪÎŊ ÎąĪ€ĪŒ ΌÎģÎą Ī„Îą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎēιΚ θι ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ΀΁ÎŋβÎģΡθÎŋĪÎŊ ÎŧΌÎŊÎŋ ÎąĪ€ĪŒ Ī„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "moved_to_archive": "ΜÎĩĪ„ÎąÎēΚÎŊΎθΡÎēÎąÎŊ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} ĪƒĪ„Îŋ ÎąĪĪ‡ÎĩίÎŋ", @@ -1257,6 +1362,10 @@ "my_albums": "Τι ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŧÎŋĪ…", "name": "ΌÎŊÎŋÎŧÎą", "name_or_nickname": "ΌÎŊÎŋÎŧÎą ÎŽ ΈÎĩĪ…Î´ĪŽÎŊĪ…ÎŧÎŋ", + "network_requirement_photos_upload": "Î§ĪÎŽĪƒÎˇ δÎĩδÎŋÎŧέÎŊΉÎŊ ÎēΚÎŊÎˇĪ„ÎŽĪ‚ Ī„ÎˇÎģÎĩΆΉÎŊÎ¯ÎąĪ‚ ÎŗÎšÎą Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", + "network_requirement_videos_upload": "Î§ĪÎŽĪƒÎˇ δÎĩδÎŋÎŧέÎŊΉÎŊ ÎēΚÎŊÎˇĪ„ÎŽĪ‚ Ī„ÎˇÎģÎĩΆΉÎŊÎ¯ÎąĪ‚ ÎŗÎšÎą Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ΄ΉÎŊ Î˛Î¯ÎŊĪ„ÎĩÎŋ", + "network_requirements": "Î‘Ī€ÎąÎšĪ„ÎŽĪƒÎĩÎšĪ‚ Î”Ī…ÎēĪ„Î¯ÎŋĪ…", + "network_requirements_updated": "Οι ÎąĪ€ÎąÎšĪ„ÎŽĪƒÎĩÎšĪ‚ δΚÎēĪ„ĪÎŋĪ… ÎŦÎģÎģιΞιÎŊ, ÎŗÎ¯ÎŊÎĩĪ„ÎąÎš ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ Ī„ÎˇĪ‚ Îŋ΅΁ÎŦĪ‚ ÎąÎŊĪ„ÎšÎŗĪÎŦΆΉÎŊ ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚", "networking_settings": "ΔιÎēĪ„ĪĪ‰ĪƒÎˇ", "networking_subtitle": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ Ī„ÎĩÎģΚÎēĪŽÎŊ ĪƒÎˇÎŧÎĩÎ¯Ī‰ÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ", "never": "ΠÎŋĪ„Î­", @@ -1266,6 +1375,7 @@ "new_person": "ΝέÎŋ ÎŦĪ„ÎŋÎŧÎŋ", "new_pin_code": "ΝέÎŋĪ‚ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ PIN", "new_pin_code_subtitle": "Î‘Ī…Ī„ÎŽ ÎĩίÎŊιΚ Ρ Ī€ĪĪŽĪ„Îˇ ΆÎŋ΁ÎŦ Ī€ÎŋĪ… ÎąĪ€ÎŋÎēĪ„ÎŦĪ„Îĩ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ. ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ έÎŊÎąÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ÎŗÎšÎą ÎąĪƒĪ†ÎąÎģÎŽ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ΃Îĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ΃ÎĩÎģÎ¯Î´Îą", + "new_timeline": "ΝέÎŋ Î§ĪÎŋÎŊÎŋÎģĪŒÎŗÎšÎŋ", "new_user_created": "Ο ÎŊέÎŋĪ‚ Ī‡ĪÎŽĪƒĪ„ÎˇĪ‚ δΡÎŧΚÎŋĪ…ĪÎŗÎŽÎ¸ÎˇÎēÎĩ", "new_version_available": "Î”Î™Î‘Î˜Î•ÎŖÎ™ÎœÎ— ΝΕΑ Î•ÎšÎ”ÎŸÎŖÎ—", "newest_first": "Τι ÎŊÎĩĪŒĪ„ÎĩĪÎą Ī€ĪĪŽĪ„Îą", @@ -1279,19 +1389,25 @@ "no_assets_message": "ΚΑΝΤΕ ΚΛΙΚ ΓΙΑ ΝΑ Î‘ÎÎ•Î’Î‘ÎŖÎ•Î¤Î• ΤΗΝ ΠΡΩΤΗ ÎŖÎ‘ÎŖ ÎĻΩΤΟΓΡΑÎĻΙΑ", "no_assets_to_show": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎŋĪ…ÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ΀΁ÎŋĪ‚ ÎĩÎŧΆÎŦÎŊÎšĪƒÎˇ", "no_cast_devices_found": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚", + "no_checksum_local": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎĩΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ checksum ÎŗÎšÎą έÎģÎĩÎŗĪ‡Îŋ ÎąÎēÎĩĪÎąÎšĪŒĪ„ÎˇĪ„ÎąĪ‚ – δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎąÎŊÎąÎēĪ„ÎˇÎ¸ÎŋĪÎŊ Ī„Îą Ī„ÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", + "no_checksum_remote": "ΔÎĩÎŊ Ī…Ī€ÎŦ΁·ÎĩΚ Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ checksum ÎŗÎšÎą έÎģÎĩÎŗĪ‡Îŋ ÎąÎēÎĩĪÎąÎšĪŒĪ„ÎˇĪ„ÎąĪ‚ – δÎĩÎŊ ÎŧĪ€Îŋ΁ÎŋĪÎŊ ÎŊÎą ÎąÎŊÎąÎēĪ„ÎˇÎ¸ÎŋĪÎŊ Ī„Îą ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "no_duplicates_found": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą.", "no_exif_info_available": "ΚαÎŧÎ¯Îą Ī€ÎģÎˇĪÎŋΆÎŋĪÎ¯Îą exif Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ", "no_explore_results_message": "ΑÎŊÎĩβÎŦĪƒĪ„Îĩ Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎŗÎšÎą ÎŊÎą Ī€ÎĩĪÎšÎˇÎŗÎˇÎ¸ÎĩÎ¯Ī„Îĩ ĪƒĪ„Îˇ ĪƒĪ…ÎģÎģÎŋÎŗÎŽ ĪƒÎąĪ‚.", "no_favorites_message": "Î ĪÎŋĪƒÎ¸Î­ĪƒĪ„Îĩ ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą ÎŗÎšÎą ÎŊÎą Î˛ĪÎĩÎ¯Ī„Îĩ ÎŗĪÎŽÎŗÎŋĪÎą Ī„ÎšĪ‚ ÎēÎąÎģĪĪ„Îĩ΁ÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚", "no_libraries_message": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ ÎŧΚι ÎĩÎžĪ‰Ī„ÎĩĪÎšÎēÎŽ βΚβÎģΚÎŋθΎÎēΡ ÎŗÎšÎą ÎŊÎą ΀΁ÎŋβÎŦÎģÎĩĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚", + "no_local_assets_found": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ Ī„ÎŋĪ€ÎšÎēÎŦ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ checksum", "no_locked_photos_message": "Οι ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ, ÎĩίÎŊιΚ Îē΁΅ÎŧÎŧέÎŊÎĩĪ‚ ÎēιΚ δÎĩÎŊ θι ÎĩÎŧĪ†ÎąÎŊίÎļÎŋÎŊĪ„ÎąÎš ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŽÎŗÎˇĪƒÎˇ ÎŽ Ī„ÎˇÎŊ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ĪƒĪ„Îˇ βΚβÎģΚÎŋθΎÎēΡ ĪƒÎąĪ‚.", "no_name": "Î§Ī‰ĪÎ¯Ī‚ ΌÎŊÎŋÎŧÎą", "no_notifications": "ΚαÎŧÎ¯Îą ÎĩΚδÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", "no_people_found": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎŦĪ„ÎŋÎŧÎą Ī€ÎŋĪ… ÎŊÎą Ī„ÎąÎšĪÎšÎŦÎļÎŋĪ…ÎŊ", "no_places": "ΚαÎŧÎ¯Îą Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯Îą", + "no_remote_assets_found": "ΔÎĩÎŊ Î˛ĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩ ÎąĪ…Ī„ĪŒ Ī„Îŋ checksum", "no_results": "ΚαÎŊέÎŊÎą ÎąĪ€ÎŋĪ„Î­ÎģÎĩ΃ÎŧÎą", "no_results_description": "ΔÎŋÎēΚÎŧÎŦĪƒĪ„Îĩ έÎŊÎą ĪƒĪ…ÎŊĪŽÎŊĪ…ÎŧÎŋ ÎŽ Ī€ÎšÎŋ ÎŗÎĩÎŊΚÎēÎŽ ÎģέΞΡ-ÎēÎģÎĩΚδί", "no_shared_albums_message": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ έÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ÎŗÎšÎą ÎŊÎą ÎŧÎŋÎšĪÎŦÎļÎĩĪƒĪ„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŧÎĩ ÎŦĪ„ÎŋÎŧÎą ĪƒĪ„Îŋ δίÎēĪ„Ī…ĪŒ ĪƒÎąĪ‚", + "no_uploads_in_progress": "ΚαÎŧÎ¯Îą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ΃Îĩ ÎĩΞέÎģΚΞΡ", + "not_available": "Μ/Δ (Μη Î”ÎšÎąÎ¸Î­ĪƒÎšÎŧÎŋ)", "not_in_any_album": "ÎŖÎĩ ÎēÎąÎŊέÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "not_selected": "ΔÎĩÎŊ ÎĩĪ€ÎšÎģÎ­Ī‡Î¸ÎˇÎēÎĩ", "note_apply_storage_label_to_previously_uploaded assets": "ÎŖÎˇÎŧÎĩÎ¯Ī‰ĪƒÎˇ: Για ÎŊÎą ÎĩĪ†ÎąĪÎŧΌ΃ÎĩĪ„Îĩ Ī„ÎˇÎŊ Î•Ī„ÎšÎēÎ­Ī„Îą Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇĪ‚ ΃Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î­Ī‡ÎŋĪ…ÎŊ ÎŧÎĩĪ„ÎąĪ†ÎŋĪĪ„Ī‰Î¸Îĩί ΀΁ÎŋÎˇÎŗÎŋĪ…ÎŧέÎŊΉ΂, ÎĩÎēĪ„ÎĩÎģÎ­ĪƒĪ„Îĩ Ī„Îŋ", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Î•Ī€Î¯ĪƒÎˇÎŧÎŋΚ Î ĪŒĪÎŋΚ Ī„ÎŋĪ… Immich", "offline": "ΕÎēĪ„ĪŒĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚", + "offset": "ΜÎĩĪ„ÎąĪ„ĪŒĪ€ÎšĪƒÎˇ", "ok": "ÎˆÎŗÎšÎŊÎĩ", "oldest_first": "Τι Ī€ÎąÎģÎąÎšĪŒĪ„ÎĩĪÎą Ī€ĪĪŽĪ„Îą", "on_this_device": "ÎŖÎĩ ÎąĪ…Ī„ÎŽ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "ΑÎŊÎŋÎ¯ÎžĪ„Îĩ Ī„Îą Ī†Î¯ÎģĪ„ĪÎą ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚", "options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚", "or": "ÎŽ", + "organize_into_albums": "ÎŸĪÎŗÎŦÎŊĪ‰ĪƒÎˇ ΃Îĩ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "organize_into_albums_description": "ΤÎŋĪ€ÎŋθÎĩĪ„ÎĩÎ¯ĪƒĪ„Îĩ Ī„ÎšĪ‚ Ī…Ī€ÎŦ΁·ÎŋĪ…ĪƒÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ΃Îĩ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšĪŽÎŊĪ„ÎąĪ‚ Ī„ÎšĪ‚ Ī„ĪÎ­Ī‡ÎŋĪ…ĪƒÎĩĪ‚ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ", "organize_your_library": "ÎŸĪÎŗÎąÎŊĪŽĪƒĪ„Îĩ Ī„Îˇ βΚβÎģΚÎŋθΎÎēΡ ĪƒÎąĪ‚", "original": "Ī€ĪĪ‰Ī„ĪŒĪ„Ī…Ī€Îŋ", "other": "ΆÎģÎģÎĩĪ‚", "other_devices": "ΆÎģÎģÎĩĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…Î­Ī‚", + "other_entities": "ΆÎģÎģÎĩĪ‚ ÎŋÎŊĪ„ĪŒĪ„ÎˇĪ„ÎĩĪ‚", "other_variables": "ΆÎģÎģÎĩĪ‚ ÎŧÎĩĪ„ÎąÎ˛ÎģÎˇĪ„Î­Ī‚", "owned": "ΔιÎēÎŦ ÎŧÎŋĪ…", "owner": "ΚÎŦĪ„Îŋ·ÎŋĪ‚", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "ΠÎĩĪÎšÎŋĪÎšĪƒÎŧέÎŊΡ ÎŦδÎĩΚι. Για ÎŊÎą ÎĩĪ€ÎšĪ„ĪÎ­ĪˆÎĩĪ„Îĩ ĪƒĪ„Îŋ Immich ÎŊÎą δΡÎŧΚÎŋĪ…ĪÎŗÎĩί ÎąÎŊĪ„Î¯ÎŗĪÎąĪ†Îą ÎąĪƒĪ†ÎąÎģÎĩÎ¯ÎąĪ‚ ÎēιΚ ÎŊÎą Î´ÎšÎąĪ‡ÎĩÎšĪÎ¯ÎļÎĩĪ„ÎąÎš ÎŋÎģΌÎēÎģÎˇĪÎˇ Ī„Îˇ ĪƒĪ…ÎģÎģÎŋÎŗÎŽ ĪƒÎąĪ‚, Ī€ÎąĪÎąĪ‡Ī‰ĪÎŽĪƒĪ„Îĩ ÎŦδÎĩΚÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ĪƒĪ„ÎšĪ‚ ÎĄĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚.", "permission_onboarding_request": "ΤÎŋ Immich ÎąĪ€ÎąÎšĪ„Îĩί ÎŦδÎĩΚι Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎĩÎšĪ‚ ĪƒĪ„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚.", "person": "Î†Ī„ÎŋÎŧÎŋ", + "person_age_months": "{months, plural, one {# ÎŧÎŽÎŊÎąĪ‚} other {# ÎŧÎŽÎŊÎĩĪ‚}} Ī€ÎąÎģΚÎŦ", + "person_age_year_months": "1 Ī‡ĪĪŒÎŊÎŋĪ‚, {months, plural, one {# ÎŧÎŽÎŊÎąĪ‚} other {# ÎŧÎŽÎŊÎĩĪ‚}} Ī€ÎąÎģΚÎŦ", + "person_age_years": "{years, plural, other {# Ī‡ĪĪŒÎŊΚι}} Ī€ÎąÎģΚÎŦ", "person_birthdate": "ΓÎĩÎŊÎŊΡθÎĩÎ¯Ī‚ ĪƒĪ„ÎšĪ‚ {date}", "person_hidden": "{name}{hidden, select, true { (ÎēĪĪ…Ī†ĪŒ)} other {}}", "photo_shared_all_users": "ÎĻÎąÎ¯ÎŊÎĩĪ„ÎąÎš ĪŒĪ„Îš ÎŧÎŋÎšĪÎąĪƒĪ„ÎŽÎēÎąĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ĪƒÎąĪ‚ ÎŧÎĩ ΌÎģÎŋĪ…Ī‚ Ī„ÎŋĪ…Ī‚ Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚ ÎŽ δÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ ÎēÎąÎŊέÎŊÎąÎŊ Ī‡ĪÎŽĪƒĪ„Îˇ ÎŗÎšÎą ÎēÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ.", @@ -1406,6 +1529,7 @@ "port": "Î˜ĪĪÎą", "preferences_settings_subtitle": "Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎĩÎ¯Ī„Îĩ Ī„ÎšĪ‚ ΀΁ÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚ Ī„ÎˇĪ‚ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽĪ‚", "preferences_settings_title": "Î ĪÎŋĪ„ÎšÎŧÎŽĪƒÎĩÎšĪ‚", + "preparing": "Î ĪÎŋÎĩĪ„ÎŋΚÎŧÎąĪƒÎ¯Îą", "preset": "Î ĪÎŋÎēιθÎŋĪÎšĪƒÎŧέÎŊΡ ĪĪÎ¸ÎŧÎšĪƒÎˇ", "preview": "Î ĪÎŋÎĩĪ€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ", "previous": "Î ĪÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎŋ", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ δÎĩĪ…Ī„Îĩ΁ÎĩĪÎŋĪ…ĪƒÎą έÎēδÎŋĪƒÎˇ.", "profile_drawer_client_server_up_to_date": "Ο Ī€ÎĩÎģÎŦĪ„ÎˇĪ‚ ÎēιΚ Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚ ÎĩίÎŊιΚ ÎĩÎŊΡÎŧÎĩ΁ΉÎŧέÎŊÎŋΚ", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Η ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ. ÎšĪÎąĪ„ÎŽĪƒĪ„Îĩ Ī€ÎąĪ„ÎˇÎŧέÎŊÎŋ Ī„Îŋ ÎĩΚÎēÎŋÎŊίδΚÎŋ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ ÎŗÎšÎą ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ.", "profile_drawer_server_out_of_date_major": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ ÎēĪĪÎšÎą έÎēδÎŋĪƒÎˇ.", "profile_drawer_server_out_of_date_minor": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎŊΡÎŧÎĩĪĪŽĪƒĪ„Îĩ Ī„ÎŋÎŊ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ δÎĩĪ…Ī„Îĩ΁ÎĩĪÎŋĪ…ĪƒÎą έÎēδÎŋĪƒÎˇ.", "profile_image_of_user": "ΕιÎēΌÎŊÎą ΀΁ÎŋĪ†Î¯Îģ Ī„ÎŋĪ… Ī‡ĪÎŽĪƒĪ„Îˇ {user}", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ Ī…Ī€ÎŋĪƒĪ„ÎˇĪÎšÎēĪ„ÎŽ", "purchase_server_title": "ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽĪ‚", "purchase_settings_server_activated": "Η Î´ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī„ÎŋĪ… ÎēÎģÎĩΚδΚÎŋĪ ΀΁ÎŋΊΌÎŊĪ„ÎŋĪ‚ Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŗÎ¯ÎŊÎĩĪ„ÎąÎš ÎąĪ€ĪŒ Ī„ÎŋÎŊ Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ", + "query_asset_id": "ΑÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇ ID ÎŖĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", + "queue_status": "ΤÎŋĪ€ÎŋÎ¸Î­Ī„ÎˇĪƒÎˇ ĪƒĪ„Îˇ Îŋ΅΁ÎŦ {count} ÎąĪ€ĪŒ {total}", "rating": "ΑξιÎŋÎģĪŒÎŗÎˇĪƒÎˇ ÎŧÎĩ ÎąĪƒĪ„Î­ĪÎšÎą", "rating_clear": "ΕÎēÎēιθÎŦĪÎšĪƒÎˇ ιΞΚÎŋÎģĪŒÎŗÎˇĪƒÎˇĪ‚", "rating_count": "{count, plural, one {# ÎąĪƒĪ„Î­ĪÎš} other {# ÎąĪƒĪ„Î­ĪÎšÎą}}", "rating_description": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ Ī„ÎˇĪ‚ ιΞΚÎŋÎģĪŒÎŗÎˇĪƒÎˇĪ‚ EXIF ĪƒĪ„ÎŋÎŊ Ī€Î¯ÎŊÎąÎēÎą Ī€ÎģÎˇĪÎŋΆÎŋĪÎšĪŽÎŊ", "reaction_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎąÎŊĪ„Î¯Î´ĪÎąĪƒÎˇĪ‚", "read_changelog": "ΔιαβÎŦĪƒĪ„Îĩ Ī„Îŋ Î‘ĪĪ‡ÎĩίÎŋ ÎšÎąĪ„ÎąÎŗĪÎąĪ†ÎŽĪ‚ ΑÎģÎģÎąÎŗĪŽÎŊ", + "readonly_mode_disabled": "Η ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ ÎąĪ€ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ", + "readonly_mode_enabled": "Η ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą ÎŧΌÎŊÎŋ-ÎŗÎšÎą-ÎąÎŊÎŦÎŗÎŊĪ‰ĪƒÎˇ ÎĩÎŊÎĩĪÎŗÎŋĪ€ÎŋΚΎθΡÎēÎĩ", + "ready_for_upload": "ÎˆĪ„ÎŋΚÎŧÎŋ ÎŗÎšÎą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", "reassign": "ΑÎŊÎŦθÎĩĪƒÎˇ", "reassigned_assets_to_existing_person": "Η ÎąÎŊÎŦθÎĩĪƒÎˇ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋĪ…} other {# ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ}} ĪƒĪ„ÎŋÎŊ/ĪƒĪ„ÎˇÎŊ {name, select, null {έÎŊÎąÎŊ/ÎŧÎ¯Îą Ī…Ī€ÎŦ΁·ÎŋÎŊĪ„Îą/ÎŋĪ…ĪƒÎą Ī‡ĪÎŽĪƒĪ„Îˇ} other {{name}}}", "reassigned_assets_to_new_person": "Η ÎąÎŊÎŦθÎĩĪƒÎˇ {count, plural, one {# ÎąĪĪ‡ÎĩίÎŋĪ…} other {# ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ}} ΃Îĩ ÎŊέÎŋ ÎŦĪ„ÎŋÎŧÎŋ", @@ -1488,6 +1618,9 @@ "refreshing_faces": "ΑÎŊÎąÎŊÎĩĪŽÎŊÎŋÎŊĪ„ÎąÎš Ī€ĪĪŒĪƒĪ‰Ī€Îą", "refreshing_metadata": "Τι ÎŧÎĩĪ„ÎąÎ´ÎĩδÎŋÎŧέÎŊÎą ÎąÎŊÎąÎŊÎĩĪŽÎŊÎŋÎŊĪ„ÎąÎš", "regenerating_thumbnails": "Οι ÎŧΚÎē΁ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎąÎŊÎąÎŗÎĩÎŊÎŊĪŽÎŊĪ„ÎąÎš", + "remote": "Î‘Ī€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎŋĪ‚", + "remote_assets": "Î‘Ī€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", + "remote_media_summary": "ΠÎĩĪÎ¯ÎģÎˇĪˆÎˇ ÎąĪ€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊΉÎŊ Ī€ÎŋÎģĪ…ÎŧÎ­ĪƒĪ‰ÎŊ", "remove": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ", "remove_assets_album_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ;", "remove_assets_shared_link_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎąĪ†ÎąÎšĪÎ­ĪƒÎĩĪ„Îĩ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}} ÎąĪ€ĪŒ ÎąĪ…Ī„ĪŒÎŊ Ī„ÎŋÎŊ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ;", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊÎˇĪ‚ ·΁ÎŋÎŊΚÎēÎŽĪ‚ Ī€ÎĩĪÎšĪŒÎ´ÎŋĪ…", "remove_deleted_assets": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ΔιÎĩÎŗĪÎąÎŧÎŧέÎŊΉÎŊ ÎŖĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "remove_from_album": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "remove_from_album_action_prompt": "{count} ÎąĪ†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ĪŒ Ī„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "remove_from_favorites": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒ Ī„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", + "remove_from_lock_folder_action_prompt": "{count} ÎąĪ†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ĪŒ Ī„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "remove_from_locked_folder": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ", "remove_from_locked_folder_confirmation": "Î•Î¯ĪƒĪ„Îĩ ĪƒÎ¯ÎŗÎŋ΅΁ÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą ÎŧÎĩĪ„ÎąÎēΚÎŊÎŽĪƒÎĩĪ„Îĩ ÎąĪ…Ī„Î­Ī‚ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎąĪ€ĪŒ Ī„ÎŋÎŊ ÎēÎģÎĩÎšÎ´Ī‰ÎŧέÎŊÎŋ ΆÎŦÎēÎĩÎģÎŋ; Θι ÎĩίÎŊιΚ Ī€ÎģέÎŋÎŊ ÎŋĪÎąĪ„Î­Ī‚ ĪƒĪ„Îˇ βΚβÎģΚÎŋθΎÎēΡ ĪƒÎąĪ‚.", "remove_from_shared_link": "Î‘Ī†ÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒ Ī„ÎŋÎŊ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ", @@ -1523,19 +1658,29 @@ "reset_password": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", "reset_people_visibility": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ΀΁ÎŋβÎŋÎģÎŽĪ‚ ÎąĪ„ĪŒÎŧΉÎŊ", "reset_pin_code": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", + "reset_pin_code_description": "ΑÎŊ ΞÎĩ·ÎŦĪƒÎąĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN ĪƒÎąĪ‚, ÎŧĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ ÎŊÎą ÎĩĪ€ÎšÎēÎŋΚÎŊΉÎŊÎŽĪƒÎĩĪ„Îĩ ÎŧÎĩ Ī„ÎŋÎŊ Î´ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ Ī„ÎŋĪ… δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ ÎŗÎšÎą ÎŊÎą Ī„ÎŋÎŊ ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩΚ", + "reset_pin_code_success": "Ο ÎēĪ‰Î´ÎšÎēĪŒĪ‚ PIN ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎ¸ÎˇÎēÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡ĪŽĪ‚", + "reset_pin_code_with_password": "ÎœĪ€Îŋ΁ÎĩÎ¯Ī„Îĩ Ī€ÎŦÎŊĪ„Îą ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩĪ„Îĩ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ PIN Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšĪŽÎŊĪ„ÎąĪ‚ Ī„ÎŋÎŊ ÎēĪ‰Î´ÎšÎēΌ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎŽĪ‚ ĪƒÎąĪ‚", + "reset_sqlite": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ SQLite βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ", + "reset_sqlite_confirmation": "Î•Î¯ĪƒÎąÎš ĪƒÎ¯ÎŗÎŋ΅΁ÎŋĪ‚ ĪŒĪ„Îš θέÎģÎĩÎšĪ‚ ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎĩÎšĪ‚ Ī„Îˇ βÎŦĪƒÎˇ δÎĩδÎŋÎŧέÎŊΉÎŊ SQLite; Θι ·΁ÎĩÎšÎąĪƒĪ„Îĩί ÎŊÎą ÎēÎŦÎŊÎĩÎšĪ‚ ÎąĪ€ÎŋĪƒĪÎŊδÎĩĪƒÎˇ ÎēιΚ ÎĩĪ€ÎąÎŊÎąĪƒĪÎŊδÎĩĪƒÎˇ ÎŗÎšÎą ÎŊÎą ÎĩĪ€ÎąÎŊÎąĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎ¯ĪƒÎĩÎšĪ‚ Ī„Îą δÎĩδÎŋÎŧέÎŊÎą", + "reset_sqlite_success": "Η ÎĩĪ€ÎąÎŊÎąĪ†Îŋ΁ÎŦ Ī„ÎˇĪ‚ SQLite βÎŦĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ ÎŧÎĩ ÎĩĪ€ÎšĪ„Ī…Ī‡Î¯Îą", "reset_to_default": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ĪƒĪ„ÎšĪ‚ ΀΁ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚", "resolve_duplicates": "Î•Ī€Î¯ÎģĪ…ĪƒÎˇ Î´ÎšĪ€ÎģÎŋĪ„ĪĪ€Ī‰ÎŊ", "resolved_all_duplicates": "Î•Ī€ÎšÎģĪÎ¸ÎˇÎēÎąÎŊ ΌÎģÎą Ī„Îą Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą", "restore": "ΑÎŊÎŦÎēĪ„ÎˇĪƒÎˇ", "restore_all": "ΑÎŊÎŦÎēĪ„ÎˇĪƒÎˇ ΌÎģΉÎŊ", + "restore_trash_action_prompt": "{count} ÎąÎŊÎąÎēĪ„ÎŽÎ¸ÎˇÎēÎąÎŊ ÎąĪ€ĪŒ Ī„Îą ÎąĪ€ÎŋĪĪÎ¯ÎŧÎŧÎąĪ„Îą", "restore_user": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ Ī‡ĪÎŽĪƒĪ„Îˇ", "restored_asset": "ΑÎŊÎąÎēĪ„ÎŽÎ¸ÎˇÎēÎĩ Ī„Îŋ ÎąĪĪ‡ÎĩίÎŋ", "resume": "ÎŖĪ…ÎŊÎ­Ī‡ÎšĪƒÎˇ", + "resume_paused_jobs": "ÎŖĪ…ÎŊÎ­Ī‡ÎšĪƒÎˇ {count, plural, one {# ΃Îĩ Ī€ÎąĪĪƒÎˇ ÎĩĪÎŗÎąĪƒÎ¯Îą} other {# ΃Îĩ Ī€ÎąĪĪƒÎˇ ÎĩĪÎŗÎąĪƒÎ¯ÎĩĪ‚}}", "retry_upload": "Î•Ī€ÎąÎŊÎŦÎģÎˇĪˆÎˇ ÎąÎŊÎĩβÎŦ΃ÎŧÎąĪ„ÎŋĪ‚", "review_duplicates": "Î ĪÎŋβÎŋÎģÎŽ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", + "review_large_files": "Î•Ī€ÎšĪƒÎēĪŒĪ€ÎˇĪƒÎˇ ÎŧÎĩÎŗÎŦÎģΉÎŊ ÎąĪĪ‡ÎĩÎ¯Ī‰ÎŊ", "role": "ÎĄĪŒÎģÎŋĪ‚", "role_editor": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒĪ„ÎŽĪ‚", "role_viewer": "ΘÎĩÎąĪ„ÎŽĪ‚", + "running": "ÎŖÎĩ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą", "save": "Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇ", "save_to_gallery": "Î‘Ī€ÎŋθΎÎēÎĩĪ…ĪƒÎˇ ĪƒĪ„Îˇ ĪƒĪ…ÎģÎģÎŋÎŗÎŽ", "saved_api_key": "Î‘Ī€ÎŋθΡÎēÎĩĪ…ÎŧέÎŊÎŋ API key", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Î‘Ī€ÎŋĪ„Ī…Ī‡Î¯Îą δΡÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚ ÎŦÎģĪ€ÎŋĪ…Îŧ", "selected": "Î•Ī€ÎšÎģÎĩÎŗÎŧέÎŊÎŋΚ", "selected_count": "{count, plural, other {# ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎŋΚ}}", + "selected_gps_coordinates": "Î•Ī€ÎšÎģÎĩÎŗÎŧέÎŊÎĩĪ‚ ĪƒĪ…ÎŊĪ„ÎĩĪ„ÎąÎŗÎŧέÎŊÎĩĪ‚ GPS", "send_message": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎģÎŽ ÎŧΡÎŊĪÎŧÎąĪ„ÎŋĪ‚", "send_welcome_email": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎģÎŽ email ÎēÎąÎģĪ‰ĪƒÎŋĪÎ¯ĪƒÎŧÎąĪ„ÎŋĪ‚", "server_endpoint": "ΤÎĩÎģΚÎēΌ ĪƒÎˇÎŧÎĩίÎŋ ΔιαÎēÎŋÎŧÎšĪƒĪ„ÎŽ", @@ -1667,6 +1813,7 @@ "settings_saved": "Οι ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩÎšĪ‚ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎąÎŊ", "setup_pin_code": "ÎĄĪÎ¸ÎŧÎšĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "share": "ΚÎŋΚÎŊÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ", + "share_action_prompt": "ΚÎŋΚÎŊÎŽ Ī‡ĪÎŽĪƒÎˇ {count} ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", "share_add_photos": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", "share_assets_selected": "{count} ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą", "share_dialog_preparing": "Î ĪÎŋÎĩĪ„ÎŋΚÎŧÎąĪƒÎ¯Îą...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "ΑÎŊĪ„ÎšÎŗĪÎŦĪ†ÎˇÎēÎĩ ĪƒĪ„Îŋ Ī€ĪĪŒĪ‡ÎĩÎšĪÎŋ", "shared_link_clipboard_text": "ÎŖĪÎŊδÎĩ΃ÎŧÎŋĪ‚: {link}\nÎšĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚: {password}", "shared_link_create_error": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„Îˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", + "shared_link_custom_url_description": "Î‘Ī€ÎŋÎēĪ„ÎŽĪƒĪ„Îĩ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ΃Îĩ ÎąĪ…Ī„ĪŒÎŊ Ī„ÎŋÎŊ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Îŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ ÎŧÎĩ ÎŧΚι ΀΁ÎŋĪƒÎąĪÎŧÎŋ΃ÎŧέÎŊΡ δΚÎĩĪÎ¸Ī…ÎŊĪƒÎˇ URL", "shared_link_edit_description_hint": "Î•ÎšĪƒÎąÎŗÎŦÎŗÎĩĪ„Îĩ Ī„ÎˇÎŊ Ī€ÎĩĪÎšÎŗĪÎąĪ†ÎŽ Ī„ÎˇĪ‚ ÎēÎŋΚÎŊÎŽĪ‚ Ī‡ĪÎŽĪƒÎˇĪ‚", "shared_link_edit_expire_after_option_day": "1 ΡÎŧÎ­ĪÎą", "shared_link_edit_expire_after_option_days": "{count} ΡÎŧÎ­ĪÎĩĪ‚", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„Ī‰ÎŊ ÎŖĪ…ÎŊÎ´Î­ĪƒÎŧΉÎŊ", "shared_link_options": "Î•Ī€ÎšÎģÎŋÎŗÎ­Ī‚ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ… ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…", + "shared_link_password_description": "Î‘Ī€ÎąÎ¯Ī„ÎˇĪƒÎˇ ÎēĪ‰Î´ÎšÎēÎŋĪ ÎŗÎšÎą Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇ ĪƒĪ„ÎŋÎŊ ÎēÎŋΚÎŊÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ", "shared_links": "ΚÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋΚ ĪƒĪÎŊδÎĩ΃ÎŧÎŋΚ", "shared_links_description": "ΜÎŋÎšĪÎąĪƒĪ„ÎĩÎ¯Ī„Îĩ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎŧÎĩ ĪƒĪÎŊδÎĩ΃ÎŧÎŋ", "shared_photos_and_videos_count": "{assetCount, plural, other {# ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎĩĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ & Î˛Î¯ÎŊĪ„ÎĩÎŋ.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ÎŧÎĩĪ„ÎŦÎ˛ÎąĪƒÎˇĪ‚ Ī€ÎąĪÎŋĪ…ĪƒÎ¯ÎąĪƒÎˇĪ‚", "show_supporter_badge": "ÎŖÎŽÎŧÎą Ī…Ī€ÎŋĪƒĪ„ÎˇĪÎšÎēĪ„ÎŽ", "show_supporter_badge_description": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ĪƒÎŽÎŧÎąĪ„ÎŋĪ‚ Ī…Ī€ÎŋĪƒĪ„ÎˇĪÎšÎēĪ„ÎŽ", + "show_text_search_menu": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ÎŧÎĩÎŊÎŋĪ ÎąÎŊÎąÎļÎŽĪ„ÎˇĪƒÎˇĪ‚ ÎēÎĩΚÎŧέÎŊÎŋĪ…", "shuffle": "ΑÎŊÎŦÎŧÎĩΚΞΡ", "sidebar": "ΠÎģÎąĪŠÎŊÎŽ ÎŧĪ€ÎŦĪÎą", "sidebar_display_description": "ΕÎŧΆÎŦÎŊÎšĪƒÎˇ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ… ÎŗÎšÎą ΀΁ÎŋβÎŋÎģÎŽ ĪƒĪ„ÎˇÎŊ Ī€ÎģÎąĪŠÎŊÎŽ ÎŧĪ€ÎŦĪÎą", @@ -1762,12 +1912,14 @@ "sort_created": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ΔηÎŧΚÎŋĪ…ĪÎŗÎ¯ÎąĪ‚", "sort_items": "Î‘ĪÎšÎ¸ÎŧĪŒĪ‚ ÎąÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊΉÎŊ", "sort_modified": "ΗÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ΄΁ÎŋĪ€ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇĪ‚", + "sort_newest": "ΝÎĩĪŒĪ„ÎĩĪÎˇ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", "sort_oldest": "Η Ī€ÎšÎŋ Ī€ÎąÎģΚÎŦ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", "sort_people_by_similarity": "ΤιΞΚÎŊΌÎŧÎˇĪƒÎˇ ÎąĪ„ĪŒÎŧΉÎŊ ÎēÎąĪ„ÎŦ ÎŋÎŧÎŋÎšĪŒĪ„ÎˇĪ„Îą", "sort_recent": "Η Ī€ÎšÎŋ Ī€ĪĪŒĪƒĪ†ÎąĪ„Îˇ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą", "sort_title": "Î¤Î¯Ī„ÎģÎŋĪ‚", "source": "Î ÎˇÎŗÎŽ", "stack": "ÎŖĪ„ÎŋÎ¯Î˛Îą", + "stack_action_prompt": "{count} ĪƒĪ…ĪƒĪƒĪ‰ĪÎĩĪĪ„ÎˇÎēÎąÎŊ", "stack_duplicates": "ÎŖĪ„ÎŋÎ¯Î˛ÎąÎžÎˇ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", "stack_select_one_photo": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎŧΚι ÎēĪĪÎšÎą ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯Îą ÎŗÎšÎą Ī„Îˇ ĪƒĪ„ÎŋÎ¯Î˛ÎąÎžÎˇ", "stack_selected_photos": "ÎŖĪ„ÎŋÎ¯Î˛ÎąÎŗÎŧÎą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊΉÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", @@ -1775,6 +1927,7 @@ "stacktrace": "ÎšÎąĪ„ÎąÎŗĪÎąĪ†ÎŽ ĪƒĪ„ÎŋÎ¯Î˛ÎąĪ‚", "start": "ΈÎŊÎąĪÎžÎˇ", "start_date": "Î‘Ī€ĪŒ", + "start_date_before_end_date": "Η ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą έÎŊÎąĪÎžÎˇĪ‚ Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą ÎĩίÎŊιΚ Ī€ĪÎšÎŊ ÎąĪ€ĪŒ Ī„ÎˇÎŊ ΡÎŧÎĩ΁ÎŋÎŧΡÎŊÎ¯Îą ÎģÎŽÎžÎˇĪ‚", "state": "ΝÎŋÎŧĪŒĪ‚", "status": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ", "stop_casting": "ΔιαÎēÎŋĪ€ÎŽ ÎŧÎĩĪ„ÎŦδÎŋĪƒÎˇĪ‚", @@ -1787,6 +1940,7 @@ "storage_quota": "ΠÎŋ΃ÎŋĪƒĪ„ĪŒ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„ÎšÎēÎŋĪ Ī‡ĪŽĪÎŋĪ…", "storage_usage": "{used} ÎąĪ€ĪŒ {available} ΃Îĩ Ī‡ĪÎŽĪƒÎˇ", "submit": "ÎĨĪ€ÎŋβÎŋÎģÎŽ", + "success": "Î•Ī€ÎšĪ„Ī…Ī‡Î¯Îą", "suggestions": "Î ĪÎŋĪ„ÎŦ΃ÎĩÎšĪ‚", "sunrise_on_the_beach": "ΗÎģΚÎŋÎ˛ÎąĪƒÎ¯ÎģÎĩÎŧÎą ĪƒĪ„ÎˇÎŊ Ī€ÎąĪÎąÎģÎ¯Îą", "support": "ÎĨĪ€ÎŋĪƒĪ„ÎŽĪÎšÎžÎˇ", @@ -1796,6 +1950,10 @@ "sync": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚", "sync_albums": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "sync_albums_manual_subtitle": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎ¯ĪƒĪ„Îĩ ΌÎģÎą Ī„Îą ÎŧÎĩĪ„ÎąĪ†Îŋ΁΄ΉÎŧέÎŊÎą Î˛Î¯ÎŊĪ„ÎĩÎŋ ÎēιΚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎŧÎĩ Ī„Îą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą ÎĩΆÎĩÎ´ĪÎšÎēÎŦ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", + "sync_local": "ΤÎŋĪ€ÎšÎēĪŒĪ‚ ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚", + "sync_remote": "Î‘Ī€ÎŋÎŧÎąÎēĪĪ…ĪƒÎŧέÎŊÎŋĪ‚ ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚", + "sync_status": "ÎšÎąĪ„ÎŦĪƒĪ„ÎąĪƒÎˇ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ", + "sync_status_subtitle": "Î ĪÎŋβÎŋÎģÎŽ ÎēιΚ Î´ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ Ī„ÎŋĪ… ĪƒĪ…ĪƒĪ„ÎŽÎŧÎąĪ„ÎŋĪ‚ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧÎŋĪ", "sync_upload_album_setting_subtitle": "ΔηÎŧΚÎŋĪ…ĪÎŗÎŽĪƒĪ„Îĩ ÎēιΚ ÎąÎŊÎĩβÎŦĪƒĪ„Îĩ Ī„ÎšĪ‚ ΆΉ΄ÎŋÎŗĪÎąĪ†Î¯ÎĩĪ‚ ÎēιΚ Ī„Îą Î˛Î¯ÎŊĪ„ÎĩΌ ĪƒÎąĪ‚ ĪƒĪ„Îą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ ĪƒĪ„Îŋ Immich", "tag": "Î•Ī„ÎšÎēÎ­Ī„Îą", "tag_assets": "Î•Ī„ÎšÎēÎĩĪ„ÎŋĪ€ÎŋÎ¯ÎˇĪƒÎˇ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", @@ -1806,6 +1964,7 @@ "tag_updated": "ΕÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ Ρ ÎĩĪ„ÎšÎēÎ­Ī„Îą: {tag}", "tagged_assets": "Î•Ī„ÎšÎēÎĩĪ„ÎŋĪ€ÎŋΚΡÎŧέÎŊÎŋ/Îą {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", "tags": "Î•Ī„ÎšÎēÎ­Ī„ÎĩĪ‚", + "tap_to_run_job": "Î ÎąĪ„ÎŽĪƒĪ„Îĩ ÎŗÎšÎą ÎŊÎą ΞÎĩÎēΚÎŊÎŽĪƒÎĩΚ Ρ ÎĩĪÎŗÎąĪƒÎ¯Îą", "template": "Î ĪĪŒĪ„Ī…Ī€Îŋ", "theme": "ΘέÎŧÎą", "theme_selection": "Î•Ī€ÎšÎģÎŋÎŗÎŽ θέÎŧÎąĪ„ÎŋĪ‚", @@ -1832,12 +1991,15 @@ "to_change_password": "ΑÎģÎģÎąÎŗÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚", "to_favorite": "Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎŋ", "to_login": "Î•Î¯ĪƒÎŋδÎŋĪ‚", + "to_multi_select": "ÎŗÎšÎą Ī€ÎŋÎģÎģÎąĪ€ÎģÎŽ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ", "to_parent": "ΜÎĩĪ„ÎąÎ˛ÎĩÎ¯Ī„Îĩ ĪƒĪ„Îŋ ÎŗÎŋÎŊΚÎēΌ ΆÎŦÎēÎĩÎģÎŋ", + "to_select": "ÎŗÎšÎą ÎĩĪ€ÎšÎģÎŋÎŗÎŽ", "to_trash": "ΚÎŦδÎŋĪ‚ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", "toggle_settings": "ΕÎŊÎąÎģÎģÎąÎŗÎŽ ĪĪ…Î¸ÎŧÎ¯ĪƒÎĩΉÎŊ", "total": "ÎŖĪÎŊÎŋÎģÎŋ", "total_usage": "ÎŖĪ…ÎŊÎŋÎģΚÎēΡ Ī‡ĪÎˇĪƒÎˇ", "trash": "ΚÎŦδÎŋĪ‚ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ", + "trash_action_prompt": "{count} ÎŧÎĩĪ„ÎąÎēΚÎŊΎθΡÎēÎąÎŊ ĪƒĪ„Îą ÎąĪ€ÎŋĪĪÎ¯ÎŧÎŧÎąĪ„Îą", "trash_all": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΌÎģΉÎŊ", "trash_count": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ {count, number}", "trash_delete_asset": "Î”ÎšÎąÎŗĪÎąĪ†ÎŽ/ÎŸĪÎšĪƒĪ„. Î”ÎšÎąÎŗĪÎąĪ†ÎŽ ΑÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊÎŋĪ…", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą", "trash_page_title": "ΚÎŦδÎŋĪ‚ Î‘Ī€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ ({count})", "trashed_items_will_be_permanently_deleted_after": "Τι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą Ī€ÎŋĪ… Î˛ĪÎ¯ĪƒÎēÎŋÎŊĪ„ÎąÎš ĪƒĪ„ÎŋÎŊ ÎēÎŦδÎŋ ÎąĪ€ÎŋĪĪÎšÎŧÎŧÎŦ΄ΉÎŊ θι Î´ÎšÎąÎŗĪÎąĪ†ÎŋĪÎŊ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ ÎŧÎĩĪ„ÎŦ ÎąĪ€ĪŒ {days, plural, one {# ΡÎŧÎ­ĪÎą} other {# ΡÎŧÎ­ĪÎĩĪ‚}}.", + "troubleshoot": "Î•Ī€Î¯ÎģĪ…ĪƒÎˇ ΀΁ÎŋβÎģΡÎŧÎŦ΄ΉÎŊ", "type": "Î¤ĪĪ€ÎŋĪ‚", "unable_to_change_pin_code": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ÎąÎģÎģÎąÎŗÎŽĪ‚ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "unable_to_setup_pin_code": "Î‘Î´Ī…ÎŊÎąÎŧÎ¯Îą ĪĪÎ¸ÎŧÎšĪƒÎˇĪ‚ ÎēĪ‰Î´ÎšÎēÎŋĪ PIN", "unarchive": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪĪ‡ÎĩΚÎŋÎ¸Î­Ī„ÎˇĪƒÎˇĪ‚", + "unarchive_action_prompt": "{count} ÎąĪ†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ĪŒ Ī„Îŋ Î‘ĪĪ‡ÎĩίÎŋ", "unarchived_count": "{count, plural, other {Î‘ĪĪ‡ÎĩΚÎŋθÎĩĪ„ÎŽĪƒÎĩÎšĪ‚ ÎąÎŊÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ #}}", "undo": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ", "unfavorite": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ÎąĪ€ĪŒ Ī„Îą ÎąÎŗÎąĪ€ÎˇÎŧέÎŊÎą", + "unfavorite_action_prompt": "{count} ÎąĪ†ÎąÎšĪÎ­Î¸ÎˇÎēÎąÎŊ ÎąĪ€ĪŒ Ī„Îą Î‘ÎŗÎąĪ€ÎˇÎŧέÎŊÎą", "unhide_person": "ΑÎŊÎąÎ¯ĪÎĩĪƒÎˇ ÎąĪ€ĪŒÎēĪĪ…ĪˆÎˇĪ‚ ÎąĪ„ĪŒÎŧÎŋĪ…", "unknown": "Î†ÎŗÎŊĪ‰ĪƒĪ„Îŋ", "unknown_country": "Î†ÎŗÎŊĪ‰ĪƒĪ„Îˇ Î§ĪŽĪÎą", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ΄ΉÎŊ Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Ī‰ÎŊ", "unselect_all_in": "Î‘Ī€ÎŋÎĩĪ€ÎšÎģÎŋÎŗÎŽ ΌÎģΉÎŊ ĪƒĪ„Îŋ {group}", "unstack": "Î‘Ī€ÎŋĪƒĪ„ÎŋÎ¯Î˛ÎąÎžÎˇ", + "unstack_action_prompt": "{count} ÎąĪ€ÎŋĪƒĪ…ĪƒĪƒĪ‰ĪÎĩĪĪ„ÎˇÎēÎąÎŊ", "unstacked_assets_count": "Î‘Ī€ÎŋĪƒĪ„ÎŋΚβÎŦÎžÎąĪ„Îĩ {count, plural, one {# ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", + "untagged": "Î§Ī‰ĪÎ¯Ī‚ ÎĩĪ„ÎšÎēÎ­Ī„Îą", "up_next": "ΑÎēÎŋÎģÎŋĪ…Î¸Îĩί", + "update_location_action_prompt": "ΕÎŊΡÎŧÎ­ĪĪ‰ĪƒÎˇ Ī„ÎŋĪ€ÎŋθÎĩĪƒÎ¯ÎąĪ‚ ÎŗÎšÎą {count} ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩ:", "updated_at": "ΕÎŊΡÎŧÎĩ΁ΉÎŧέÎŊÎŋ", "updated_password": "Ο ÎēĪ‰Î´ÎšÎēĪŒĪ‚ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚ ÎĩÎŊΡÎŧÎĩĪĪŽÎ¸ÎˇÎēÎĩ", "upload": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", + "upload_action_prompt": "{count} Ī„ÎŋĪ€ÎŋθÎĩĪ„ÎŽÎ¸ÎˇÎēÎąÎŊ ĪƒĪ„ÎˇÎŊ Îŋ΅΁ÎŦ ÎŗÎšÎą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", "upload_concurrency": "Î¤ÎąĪ…Ī„ĪŒĪ‡ĪÎŋÎŊΡ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ", + "upload_details": "ΛÎĩ΀΄ÎŋÎŧÎ­ĪÎĩΚÎĩĪ‚ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚", "upload_dialog_info": "ΘέÎģÎĩĪ„Îĩ ÎŊÎą ÎąÎŊĪ„ÎšÎŗĪÎŦΈÎĩĪ„Îĩ (ÎēÎŦÎŊÎĩĪ„Îĩ backup) Ī„Îą ÎĩĪ€ÎšÎģÎĩÎŗÎŧέÎŊo(Îą) ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ(Îą) ĪƒĪ„Îŋ δΚιÎēÎŋÎŧÎšĪƒĪ„ÎŽ;", "upload_dialog_title": "ΑÎŊÎ­Î˛ÎąĪƒÎŧÎą ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "upload_errors": "Η ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ ÎŧÎĩ {count, plural, one {# ĪƒĪ†ÎŦÎģÎŧÎą} other {# ĪƒĪ†ÎŦÎģÎŧÎąĪ„Îą}}, ÎąÎŊÎąÎŊÎĩĪŽĪƒĪ„Îĩ Ī„Îˇ ΃ÎĩÎģÎ¯Î´Îą ÎŗÎšÎą ÎŊÎą δÎĩÎ¯Ī„Îĩ ÎŊέι ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚.", + "upload_finished": "ΟÎģÎŋÎēÎģÎŽĪĪ‰ĪƒÎˇ ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇĪ‚", "upload_progress": "Î‘Ī€ÎŋÎŧέÎŊÎŋĪ…ÎŊ {remaining, number} - ΟÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎąÎŊ {processed, number}/{total, number}", "upload_skipped_duplicates": "Î ÎąĪÎąÎģÎĩÎ¯Ī†Î¸ÎˇÎēÎąÎŊ {count, plural, one {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îŋ ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋ} other {# Î´ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą}}", "upload_status_duplicates": "Î”ÎšĪ€ÎģĪŒĪ„Ī…Ī€Îą", @@ -1892,6 +2063,7 @@ "upload_success": "Η ÎŧÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ÎŋÎģÎŋÎēÎģÎˇĪĪŽÎ¸ÎˇÎēÎĩ, ÎąÎŊÎąÎŊÎĩĪŽĪƒĪ„Îĩ Ī„Îˇ ΃ÎĩÎģÎ¯Î´Îą ÎŗÎšÎą ÎŊÎą δÎĩÎ¯Ī„Îĩ Ī„Îą ÎŊέι ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎą.", "upload_to_immich": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ ĪƒĪ„Îŋ Immich ({count})", "uploading": "ΜÎĩĪ„ÎąĪ†ÎŋĪĪ„ĪŽÎŊÎĩĪ„ÎąÎš", + "uploading_media": "ΜÎĩĪ„ÎąĪ†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī€ÎŋÎģĪ…ÎŧÎ­ĪƒĪ‰ÎŊ", "url": "URL", "usage": "Î§ĪÎŽĪƒÎˇ", "use_biometric": "Î§ĪÎŽĪƒÎˇ βΚÎŋÎŧÎĩĪ„ĪÎšÎēĪŽÎŊ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Î ĪÎŋβÎŋÎģÎŽ ĪƒĪ„ÎąĪ„ÎšĪƒĪ„ÎšÎēĪŽÎŊ Ī‡ĪÎŽĪƒÎˇĪ‚ ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ", "username": "ΌÎŊÎŋÎŧÎą Î§ĪÎŽĪƒĪ„Îˇ", "users": "Î§ĪÎŽĪƒĪ„ÎĩĪ‚", + "users_added_to_album_count": "Î ĪÎŋĪƒĪ„Î­Î¸ÎˇÎēÎĩ {count, plural, one {# Ī‡ĪÎŽĪƒĪ„ÎˇĪ‚} other {# Ī‡ĪÎŽĪƒĪ„ÎĩĪ‚}} ĪƒĪ„Îŋ ÎŦÎģÎŧĪ€ÎŋĪ…Îŧ", "utilities": "ΒÎŋÎˇÎ¸ÎˇĪ„ÎšÎēÎŦ ΀΁ÎŋÎŗĪÎŦÎŧÎŧÎąĪ„Îą", "validate": "Î•Ī€ÎšÎēĪĪĪ‰ĪƒÎˇ", "validate_endpoint_error": "Î ÎąĪÎąÎēÎąÎģĪŽ ÎĩÎšĪƒÎŦÎŗÎĩĪ„Îĩ έÎŊÎą Î­ÎŗÎē΅΁Îŋ URL", @@ -1930,6 +2103,7 @@ "view_album": "Î ĪÎŋβÎŋÎģÎŽ ΆÎģÎŧĪ€ÎŋĪ…Îŧ", "view_all": "Î ĪÎŋβÎŋÎģÎŽ ΌÎģΉÎŊ", "view_all_users": "Î ĪÎŋβÎŋÎģÎŽ ΌÎģΉÎŊ ΄ΉÎŊ Ī‡ĪÎˇĪƒĪ„ĪŽÎŊ", + "view_details": "Î ĪÎŋβÎŋÎģÎŽ ΛÎĩ΀΄ÎŋÎŧÎĩ΁ÎĩÎšĪŽÎŊ", "view_in_timeline": "Î ĪÎŋβÎŋÎģÎŽ ĪƒĪ„Îŋ ·΁ÎŋÎŊÎŋδΚÎŦÎŗĪÎąÎŧÎŧÎą", "view_link": "Î ĪÎŋβÎŋÎģÎŽ ĪƒĪÎŊδÎĩ΃ÎŧÎŋĪ…", "view_links": "Î ĪÎŋβÎŋÎģÎŽ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧΉÎŊ", @@ -1937,6 +2111,7 @@ "view_next_asset": "Î ĪÎŋβÎŋÎģÎŽ ÎĩĪ€ĪŒÎŧÎĩÎŊÎŋĪ… ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "view_previous_asset": "Î ĪÎŋβÎŋÎģÎŽ ΀΁ÎŋÎˇÎŗÎŋĪÎŧÎĩÎŊÎŋĪ… ĪƒĪ„ÎŋÎšĪ‡ÎĩίÎŋĪ…", "view_qr_code": "Î ĪÎŋβÎŋÎģÎŽ ÎēĪ‰Î´ÎšÎēÎŋĪ QR", + "view_similar_photos": "Î ĪÎŋβÎŋÎģÎŽ Ī€ÎąĪĪŒÎŧÎŋÎšĪ‰ÎŊ ΆΉ΄ÎŋÎŗĪÎąĪ†ÎšĪŽÎŊ", "view_stack": "Î ĪÎŋβÎŋÎģÎŽ Ī„ÎˇĪ‚ ĪƒĪ„ÎŋÎ¯Î˛ÎąĪ‚", "view_user": "Î ĪÎŋβÎŋÎģÎŽ Î§ĪÎŽĪƒĪ„Îˇ", "viewer_remove_from_stack": "ÎšÎąĪ„ÎŦĪÎŗÎˇĪƒÎˇ ÎąĪ€ĪŒ Ī„Îˇ ÎŖĪ„ÎŋÎ¯Î˛Îą", @@ -1955,5 +2130,6 @@ "yes": "Ναι", "you_dont_have_any_shared_links": "ΔÎĩÎŊ Î­Ī‡ÎĩĪ„Îĩ ÎēÎŋΚÎŊĪŒĪ‡ĪÎˇĪƒĪ„ÎŋĪ…Ī‚ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ…Ī‚", "your_wifi_name": "ΤÎŋ ΌÎŊÎŋÎŧÎą Ī„ÎŋĪ… Wi-Fi ĪƒÎąĪ‚", - "zoom_image": "ΖÎŋĪ…Îŧ ΕιÎēΌÎŊÎąĪ‚" + "zoom_image": "ΖÎŋĪ…Îŧ ΕιÎēΌÎŊÎąĪ‚", + "zoom_to_bounds": "Î•ĪƒĪ„Î¯ÎąĪƒÎˇ ĪƒĪ„Îą ĪŒĪÎšÎą" } diff --git a/i18n/en.json b/i18n/en.json index bda2fee4fb..9fe1b1e26c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,6 +14,7 @@ "add_a_location": "Add a location", "add_a_name": "Add a name", "add_a_title": "Add a title", + "add_birthday": "Add a birthday", "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Add exclusion pattern", "add_import_path": "Add import path", @@ -27,7 +28,12 @@ "add_to_album": "Add to album", "add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_already_exists": "Already in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Some local assets could not be added to album", + "add_to_album_toggle": "Toggle selection for {album}", + "add_to_albums": "Add to albums", + "add_to_albums_count": "Add to albums ({count})", "add_to_shared_album": "Add to shared album", + "add_upload_to_stack": "Add upload to stack", "add_url": "Add URL", "added_to_archive": "Added to archive", "added_to_favorites": "Added to favorites", @@ -44,6 +50,13 @@ "backup_database": "Create Database Dump", "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_onboarding_1_description": "offsite copy in the cloud or at another physical location.", + "backup_onboarding_2_description": "local copies on different devices. This includes the main files and a backup of those files locally.", + "backup_onboarding_3_description": "total copies of your data, including the original files. This includes 1 offsite copy and 2 local copies.", + "backup_onboarding_description": "A 3-2-1 backup strategy is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution.", + "backup_onboarding_footer": "For more information about backing up Immich, please refer to the documentation.", + "backup_onboarding_parts_title": "A 3-2-1 backup includes:", + "backup_onboarding_title": "Backups", "backup_settings": "Database Dump Settings", "backup_settings_description": "Manage database dump settings.", "cleared_jobs": "Cleared jobs for: {job}", @@ -112,6 +125,13 @@ "logging_enable_description": "Enable logging", "logging_level_description": "When enabled, what log level to use.", "logging_settings": "Logging", + "machine_learning_availability_checks": "Availability checks", + "machine_learning_availability_checks_description": "Automatically detect and prefer available machine learning servers", + "machine_learning_availability_checks_enabled": "Enable availability checks", + "machine_learning_availability_checks_interval": "Check interval", + "machine_learning_availability_checks_interval_description": "Interval in milliseconds between availability checks", + "machine_learning_availability_checks_timeout": "Request timeout", + "machine_learning_availability_checks_timeout_description": "Timeout in milliseconds for availability checks", "machine_learning_clip_model": "CLIP model", "machine_learning_clip_model_description": "The name of a CLIP model listed here. Note that you must re-run the 'Smart Search' job for all images upon changing a model.", "machine_learning_duplicate_detection": "Duplicate Detection", @@ -347,6 +367,9 @@ "trash_number_of_days_description": "Number of days to keep the assets in trash before permanently removing them", "trash_settings": "Trash Settings", "trash_settings_description": "Manage trash settings", + "unlink_all_oauth_accounts": "Unlink all OAuth accounts", + "unlink_all_oauth_accounts_description": "Remember to unlink all OAuth accounts before migrating to a new provider.", + "unlink_all_oauth_accounts_prompt": "Are you sure you want to unlink all OAuth accounts? This will reset the OAuth ID for each user and cannot be undone.", "user_cleanup_job": "User cleanup", "user_delete_delay": "{user}'s account and assets will be scheduled for permanent deletion in {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Delete delay", @@ -373,8 +396,6 @@ "admin_password": "Admin Password", "administration": "Administration", "advanced": "Advanced", - "advanced_settings_beta_timeline_subtitle": "Try the new app experience", - "advanced_settings_beta_timeline_title": "Beta Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter", "advanced_settings_log_level_title": "Log level: {level}", @@ -382,6 +403,8 @@ "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_readonly_mode_subtitle": "Enables the read-only mode where the photos can be only viewed, things like selecting multiple images, sharing, casting, delete are all disabled. Enable/Disable read-only via user avatar from the main screen", + "advanced_settings_readonly_mode_title": "Read-only Mode", "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": "Automatically delete or restore an asset on this device when that action is taken on the web", @@ -397,6 +420,7 @@ "album_cover_updated": "Album cover updated", "album_delete_confirmation": "Are you sure you want to delete the album {album}?", "album_delete_confirmation_description": "If this album is shared, other users will not be able to access it anymore.", + "album_deleted": "Album deleted", "album_info_card_backup_album_excluded": "EXCLUDED", "album_info_card_backup_album_included": "INCLUDED", "album_info_updated": "Album info updated", @@ -408,6 +432,7 @@ "album_remove_user_confirmation": "Are you sure you want to remove {user}?", "album_search_not_found": "No albums found matching your search", "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.", + "album_summary": "Album summary", "album_updated": "Album updated", "album_updated_setting_description": "Receive an email notification when a shared album has new assets", "album_user_left": "Left {album}", @@ -446,6 +471,7 @@ "app_bar_signout_dialog_title": "Sign out", "app_settings": "App Settings", "appears_in": "Appears in", + "apply_count": "Apply ({count, number})", "archive": "Archive", "archive_action_prompt": "{count} added to Archive", "archive_or_unarchive_photo": "Archive or unarchive photo", @@ -478,6 +504,8 @@ "asset_restored_successfully": "Asset restored successfully", "asset_skipped": "Skipped", "asset_skipped_in_trash": "In trash", + "asset_trashed": "Asset trashed", + "asset_troubleshoot": "Asset Troubleshoot", "asset_uploaded": "Uploaded", "asset_uploading": "Uploadingâ€Ļ", "asset_viewer_settings_subtitle": "Manage your gallery viewer settings", @@ -485,7 +513,9 @@ "assets": "Assets", "assets_added_count": "Added {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Added {count, plural, one {# asset} other {# assets}} to the album", + "assets_added_to_albums_count": "Added {assetTotal, plural, one {# asset} other {# assets}} to {albumTotal, plural, one {# album} other {# albums}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} cannot be added to the album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} cannot be added to any of the albums", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) deleted permanently", "assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Immich server", @@ -502,20 +532,25 @@ "assets_trashed_count": "Trashed {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} asset(s) trashed from the Immich server", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} already part of the album", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} already part of the albums", "authorized_devices": "Authorized Devices", "automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere", "automatic_endpoint_switching_title": "Automatic URL switching", "autoplay_slideshow": "Autoplay slideshow", "back": "Back", "back_close_deselect": "Back, close, or deselect", + "background_backup_running_error": "Background backup is currently running, cannot start manual backup", "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_options": "Background Options", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albums on device ({count})", "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_albums_sync": "Backup albums synchronization", "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â€Ļ", @@ -565,13 +600,16 @@ "backup_controller_page_turn_on": "Turn on foreground backup", "backup_controller_page_uploading_file_info": "Uploading file info", "backup_err_only_album": "Cannot remove the only album", + "backup_error_sync_failed": "Sync failed. Cannot process backup.", "backup_info_card_assets": "assets", "backup_manual_cancelled": "Cancelled", "backup_manual_in_progress": "Upload already in progress. Try after sometime", "backup_manual_success": "Success", "backup_manual_title": "Upload status", + "backup_options": "Backup Options", "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_settings_subtitle": "Manage upload settings", "backward": "Backward", "biometric_auth_enabled": "Biometric authentication enabled", "biometric_locked_out": "You are locked out of biometric authentication", @@ -590,7 +628,7 @@ "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_subtitle": "Photos and videos that are ignore listed by the app", "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})", "cache_settings_statistics_album": "Library thumbnails", "cache_settings_statistics_full": "Full images", @@ -630,6 +668,8 @@ "change_pin_code": "Change PIN code", "change_your_password": "Change your password", "changed_visibility_successfully": "Changed visibility successfully", + "charging": "Charging", + "charging_requirement_mobile_backup": "Background backup requires the device to be charging", "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.", @@ -639,6 +679,7 @@ "clear": "Clear", "clear_all": "Clear all", "clear_all_recent_searches": "Clear all recent searches", + "clear_file_cache": "Clear File Cache", "clear_message": "Clear message", "clear_value": "Clear value", "client_cert_dialog_msg_confirm": "OK", @@ -709,11 +750,13 @@ "create_new_user": "Create new user", "create_shared_album_page_share_add_assets": "ADD ASSETS", "create_shared_album_page_share_select_photos": "Select Photos", + "create_shared_link": "Create shared link", "create_tag": "Create tag", "create_tag_description": "Create a new tag. For nested tags, please enter the full path of the tag including forward slashes.", "create_user": "Create user", "created": "Created", "created_at": "Created", + "creating_linked_albums": "Creating linked albums...", "crop": "Crop", "curated_object_page_title": "Things", "current_device": "Current device", @@ -721,6 +764,7 @@ "current_server_address": "Current server address", "custom_locale": "Custom Locale", "custom_locale_description": "Format dates and numbers based on the language and the region", + "custom_url": "Custom URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Dark", @@ -732,6 +776,7 @@ "date_of_birth_saved": "Date of birth saved successfully", "date_range": "Date range", "day": "Day", + "days": "Days", "deduplicate_all": "Deduplicate All", "deduplication_criteria_1": "Image size in bytes", "deduplication_criteria_2": "Count of EXIF data", @@ -740,7 +785,8 @@ "default_locale": "Default Locale", "default_locale_description": "Format dates and numbers based on your browser locale", "delete": "Delete", - "delete_action_prompt": "{count} deleted permanently", + "delete_action_confirmation_message": "Are you sure you want to delete this asset? This action will move the asset to the server's trash and will prompt if you want to delete it locally", + "delete_action_prompt": "{count} deleted", "delete_album": "Delete album", "delete_api_key_prompt": "Are you sure you want to delete this API key?", "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", @@ -758,6 +804,8 @@ "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", "delete_local_dialog_ok_force": "Delete Anyway", "delete_others": "Delete others", + "delete_permanently": "Delete permanently", + "delete_permanently_action_prompt": "{count} deleted permanently", "delete_shared_link": "Delete shared link", "delete_shared_link_dialog_title": "Delete Shared Link", "delete_tag": "Delete tag", @@ -813,8 +861,12 @@ "edit": "Edit", "edit_album": "Edit album", "edit_avatar": "Edit avatar", + "edit_birthday": "Edit birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", + "edit_date_and_time_action_prompt": "{count} date and time edited", + "edit_date_and_time_by_offset": "Change date by offset", + "edit_date_and_time_by_offset_interval": "New date range: {from} - {to}", "edit_description": "Edit description", "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", @@ -854,7 +906,9 @@ "error": "Error", "error_change_sort_album": "Failed to change album sort order", "error_delete_face": "Error deleting face from asset", + "error_getting_places": "Error getting places", "error_loading_image": "Error loading image", + "error_loading_partners": "Error loading partners: {error}", "error_saving_image": "Error: {error}", "error_tag_face_bounding_box": "Error tagging face - cannot get bounding box coordinates", "error_title": "Error - Something went wrong", @@ -887,6 +941,7 @@ "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_reset_pin_code": "Failed to reset PIN code", "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", @@ -895,6 +950,7 @@ "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation", "profile_picture_transparent_pixels": "Profile pictures cannot have transparent pixels. Please zoom in and/or move the image.", "quota_higher_than_disk_size": "You set a quota higher than the disk size", + "something_went_wrong": "Something went wrong", "unable_to_add_album_users": "Unable to add users to album", "unable_to_add_assets_to_shared_link": "Unable to add assets to shared link", "unable_to_add_comment": "Unable to add comment", @@ -980,13 +1036,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Add Description...", + "exif_bottom_sheet_description_error": "Error updating description", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATION", "exif_bottom_sheet_people": "PEOPLE", "exif_bottom_sheet_person_add_person": "Add name", - "exif_bottom_sheet_person_age_months": "Age {months} months", - "exif_bottom_sheet_person_age_year_months": "Age 1 year, {months} months", - "exif_bottom_sheet_person_age_years": "Age {years}", "exit_slideshow": "Exit Slideshow", "expand_all": "Expand all", "experimental_settings_new_asset_list_subtitle": "Work in progress", @@ -1000,6 +1054,8 @@ "explorer": "Explorer", "export": "Export", "export_as_json": "Export as JSON", + "export_database": "Export Database", + "export_database_description": "Export the SQLite database", "extension": "Extension", "external": "External", "external_libraries": "External Libraries", @@ -1017,6 +1073,7 @@ "favorites_page_no_favorites": "No favorite assets found", "feature_photo_updated": "Feature photo updated", "features": "Features", + "features_in_development": "Features in Development", "features_setting_description": "Manage the app features", "file_name": "File name", "file_name_or_extension": "File name or extension", @@ -1026,21 +1083,26 @@ "filter_people": "Filter people", "filter_places": "Filter places", "find_them_fast": "Find them fast by name with search", + "first": "First", "fix_incorrect_match": "Fix incorrect match", "folder": "Folder", "folder_not_found": "Folder not found", "folders": "Folders", "folders_feature_description": "Browsing the folder view for the photos and videos on the file system", + "forgot_pin_code_question": "Forgot your PIN?", "forward": "Forward", "gcast_enabled": "Google Cast", "gcast_enabled_description": "This feature loads external resources from Google in order to work.", "general": "General", + "geolocation_instruction_location": "Click on an asset with GPS coordinates to use its location, or select a location directly from the map", "get_help": "Get Help", "get_wifiname_error": "Could not get Wi-Fi name. Make sure you have granted the necessary permissions and are connected to a Wi-Fi network", "getting_started": "Getting Started", "go_back": "Go back", "go_to_folder": "Go to folder", "go_to_search": "Go to search", + "gps": "GPS", + "gps_missing": "No GPS", "grant_permission": "Grant permission", "group_albums_by": "Group albums by...", "group_country": "Group by country", @@ -1051,6 +1113,9 @@ "haptic_feedback_switch": "Enable haptic feedback", "haptic_feedback_title": "Haptic Feedback", "has_quota": "Has quota", + "hash_asset": "Hash asset", + "hashed_assets": "Hashed assets", + "hashing": "Hashing", "header_settings_add_header_tip": "Add Header", "header_settings_field_validator_msg": "Value cannot be empty", "header_settings_header_name_input": "Header name", @@ -1082,7 +1147,9 @@ "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "host": "Host", "hour": "Hour", + "hours": "Hours", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Image", @@ -1140,10 +1207,13 @@ "language_no_results_title": "No languages found", "language_search_hint": "Search languages...", "language_setting_description": "Select your preferred language", + "large_files": "Large Files", + "last": "Last", "last_seen": "Last seen", "latest_version": "Latest Version", "latitude": "Latitude", "leave": "Leave", + "leave_album": "Leave album", "lens_model": "Lens model", "let_others_respond": "Let others respond", "level": "Level", @@ -1157,15 +1227,18 @@ "library_page_sort_title": "Album title", "licenses": "Licenses", "light": "Light", + "like": "Like", "like_deleted": "Like deleted", "link_motion_video": "Link motion video", - "link_options": "Link options", "link_to_oauth": "Link to OAuth", "linked_oauth_account": "Linked OAuth account", "list": "List", "loading": "Loading", "loading_search_results_failed": "Loading search results failed", + "local": "Local", "local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server", + "local_assets": "Local Assets", + "local_media_summary": "Local Media Summary", "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", @@ -1177,6 +1250,7 @@ "location_picker_longitude_hint": "Enter your longitude here", "lock": "Lock", "locked_folder": "Locked Folder", + "log_detail_title": "Log Detail", "log_out": "Log out", "log_out_all_devices": "Log Out All Devices", "logged_in_as": "Logged in as {user}", @@ -1207,6 +1281,7 @@ "login_password_changed_success": "Password updated successfully", "logout_all_device_confirmation": "Are you sure you want to log out all devices?", "logout_this_device_confirmation": "Are you sure you want to log out this device?", + "logs": "Logs", "longitude": "Longitude", "look": "Look", "loop_videos": "Loop videos", @@ -1214,6 +1289,7 @@ "main_branch_warning": "You're using a development version; we strongly recommend using a release version!", "main_menu": "Main menu", "make": "Make", + "manage_geolocation": "Manage location", "manage_shared_links": "Manage shared links", "manage_sharing_with_partners": "Manage sharing with partners", "manage_the_app_settings": "Manage the app settings", @@ -1222,8 +1298,7 @@ "manage_your_devices": "Manage your logged-in devices", "manage_your_oauth_connection": "Manage your OAuth connection", "map": "Map", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, =0 {No photos in this area} one {# photo} other {# 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", @@ -1231,7 +1306,6 @@ "map_location_service_disabled_title": "Location Service disabled", "map_marker_for_images": "Map marker for images taken in {city}, {country}", "map_marker_with_image": "Map marker with image", - "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": "Map settings", @@ -1250,6 +1324,7 @@ "mark_as_read": "Mark as read", "marked_all_as_read": "Marked all as read", "matches": "Matches", + "matching_assets": "Matching Assets", "media_type": "Media type", "memories": "Memories", "memories_all_caught_up": "All caught up", @@ -1268,6 +1343,7 @@ "merged_people_count": "Merged {count, plural, one {# person} other {# people}}", "minimize": "Minimize", "minute": "Minute", + "minutes": "Minutes", "missing": "Missing", "model": "Model", "month": "Month", @@ -1287,6 +1363,10 @@ "my_albums": "My albums", "name": "Name", "name_or_nickname": "Name or nickname", + "network_requirement_photos_upload": "Use cellular data to backup photos", + "network_requirement_videos_upload": "Use cellular data to backup videos", + "network_requirements": "Network Requirements", + "network_requirements_updated": "Network requirements changed, resetting backup queue", "networking_settings": "Networking", "networking_subtitle": "Manage the server endpoint settings", "never": "Never", @@ -1296,6 +1376,7 @@ "new_person": "New person", "new_pin_code": "New PIN code", "new_pin_code_subtitle": "This is your first time accessing the locked folder. Create a PIN code to securely access this page", + "new_timeline": "New Timeline", "new_user_created": "New user created", "new_version_available": "NEW VERSION AVAILABLE", "newest_first": "Newest first", @@ -1309,19 +1390,25 @@ "no_assets_message": "CLICK TO UPLOAD YOUR FIRST PHOTO", "no_assets_to_show": "No assets to show", "no_cast_devices_found": "No cast devices found", + "no_checksum_local": "No checksum available - cannot fetch local assets", + "no_checksum_remote": "No checksum available - cannot fetch remote asset", "no_duplicates_found": "No duplicates were found.", "no_exif_info_available": "No exif info available", "no_explore_results_message": "Upload more photos to explore your collection.", "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_local_assets_found": "No local assets found with this checksum", "no_locked_photos_message": "Photos and videos in the locked folder are hidden and won't show up as you browse or search your library.", "no_name": "No Name", "no_notifications": "No notifications", "no_people_found": "No matching people found", "no_places": "No places", + "no_remote_assets_found": "No remote assets found with this checksum", "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", + "no_uploads_in_progress": "No uploads in progress", + "not_available": "N/A", "not_in_any_album": "Not in any album", "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", @@ -1337,6 +1424,7 @@ "oauth": "OAuth", "official_immich_resources": "Official Immich Resources", "offline": "Offline", + "offset": "Offset", "ok": "Ok", "oldest_first": "Oldest first", "on_this_device": "On this device", @@ -1355,10 +1443,13 @@ "open_the_search_filters": "Open the search filters", "options": "Options", "or": "or", + "organize_into_albums": "Organize into albums", + "organize_into_albums_description": "Put existing photos into albums using current sync settings", "organize_your_library": "Organize your library", "original": "original", "other": "Other", "other_devices": "Other devices", + "other_entities": "Other entities", "other_variables": "Other variables", "owned": "Owned", "owner": "Owner", @@ -1413,6 +1504,9 @@ "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.", "person": "Person", + "person_age_months": "{months, plural, one {# month} other {# months}} old", + "person_age_year_months": "1 year, {months, plural, one {# month} other {# months}} old", + "person_age_years": "{years, plural, other {# years}} old", "person_birthdate": "Born on {date}", "person_hidden": "{name}{hidden, select, true { (hidden)} other {}}", "photo_shared_all_users": "Looks like you shared your photos with all users or you don't have any user to share with.", @@ -1436,6 +1530,7 @@ "port": "Port", "preferences_settings_subtitle": "Manage the app's preferences", "preferences_settings_title": "Preferences", + "preparing": "Preparing", "preset": "Preset", "preview": "Preview", "previous": "Previous", @@ -1452,6 +1547,7 @@ "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", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Read-only mode enabled. Long-press the user avatar icon to exit.", "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.", "profile_image_of_user": "Profile image of {user}", @@ -1490,6 +1586,7 @@ "purchase_server_description_2": "Supporter status", "purchase_server_title": "Server", "purchase_settings_server_activated": "The server product key is managed by the admin", + "query_asset_id": "Query Asset ID", "queue_status": "Queuing {count}/{total}", "rating": "Star rating", "rating_clear": "Clear rating", @@ -1497,6 +1594,9 @@ "rating_description": "Display the EXIF rating in the info panel", "reaction_options": "Reaction options", "read_changelog": "Read Changelog", + "readonly_mode_disabled": "Read-only mode disabled", + "readonly_mode_enabled": "Read-only mode enabled", + "ready_for_upload": "Ready for upload", "reassign": "Reassign", "reassigned_assets_to_existing_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", "reassigned_assets_to_new_person": "Re-assigned {count, plural, one {# asset} other {# assets}} to a new person", @@ -1519,6 +1619,9 @@ "refreshing_faces": "Refreshing faces", "refreshing_metadata": "Refreshing metadata", "regenerating_thumbnails": "Regenerating thumbnails", + "remote": "Remote", + "remote_assets": "Remote Assets", + "remote_media_summary": "Remote Media Summary", "remove": "Remove", "remove_album": "Remove album", "remove_assets_album_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from the album?", @@ -1557,19 +1660,29 @@ "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", "reset_pin_code": "Reset PIN code", + "reset_pin_code_description": "If you forgot your PIN code, you can contact the server administrator to reset it", + "reset_pin_code_success": "Successfully reset PIN code", + "reset_pin_code_with_password": "You can always reset your PIN code with your password", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Are you sure you want to reset the SQLite database? You will need to log out and log in again to resync the data", + "reset_sqlite_success": "Successfully reset the SQLite database", "reset_to_default": "Reset to default", "resolve_duplicates": "Resolve duplicates", "resolved_all_duplicates": "Resolved all duplicates", "restore": "Restore", "restore_all": "Restore all", + "restore_trash_action_prompt": "{count} restored from trash", "restore_user": "Restore user", "restored_asset": "Restored asset", "resume": "Resume", + "resume_paused_jobs": "Resume {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Retry upload", "review_duplicates": "Review duplicates", + "review_large_files": "Review large files", "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", + "running": "Running", "save": "Save", "save_to_gallery": "Save to gallery", "saved_api_key": "Saved API Key", @@ -1656,6 +1769,7 @@ "select_user_for_sharing_page_err_album": "Failed to create album", "selected": "Selected", "selected_count": "{count, plural, other {# selected}}", + "selected_gps_coordinates": "Selected GPS Coordinates", "send_message": "Send message", "send_welcome_email": "Send welcome email", "server_endpoint": "Server Endpoint", @@ -1723,6 +1837,7 @@ "shared_link_clipboard_copied_massage": "Copied to clipboard", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Error while creating shared link", + "shared_link_custom_url_description": "Access this shared link with a custom URL", "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": "{count} days", @@ -1748,6 +1863,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Manage Shared links", "shared_link_options": "Shared link options", + "shared_link_password_description": "Require a password to access this shared link", "shared_links": "Shared links", "shared_links_description": "Share photos and videos with a link", "shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}", @@ -1782,6 +1898,7 @@ "show_slideshow_transition": "Show slideshow transition", "show_supporter_badge": "Supporter badge", "show_supporter_badge_description": "Show a supporter badge", + "show_text_search_menu": "Show text search menu", "shuffle": "Shuffle", "sidebar": "Sidebar", "sidebar_display_description": "Display a link to the view in the sidebar", @@ -1797,6 +1914,7 @@ "sort_created": "Date created", "sort_items": "Number of items", "sort_modified": "Date modified", + "sort_newest": "Newest photo", "sort_oldest": "Oldest photo", "sort_people_by_similarity": "Sort people by similarity", "sort_recent": "Most recent photo", @@ -1811,6 +1929,7 @@ "stacktrace": "Stacktrace", "start": "Start", "start_date": "Start date", + "start_date_before_end_date": "Start date must be before end date", "state": "State", "status": "Status", "stop_casting": "Stop casting", @@ -1823,6 +1942,7 @@ "storage_quota": "Storage Quota", "storage_usage": "{used} of {available} used", "submit": "Submit", + "success": "Success", "suggestions": "Suggestions", "sunrise_on_the_beach": "Sunrise on the beach", "support": "Support", @@ -1832,6 +1952,10 @@ "sync": "Sync", "sync_albums": "Sync albums", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", + "sync_local": "Sync Local", + "sync_remote": "Sync Remote", + "sync_status": "Sync Status", + "sync_status_subtitle": "View and manage the sync system", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Tag", "tag_assets": "Tag assets", @@ -1842,6 +1966,7 @@ "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", + "tap_to_run_job": "Tap to run job", "template": "Template", "theme": "Theme", "theme_selection": "Theme selection", @@ -1868,7 +1993,9 @@ "to_change_password": "Change password", "to_favorite": "Favorite", "to_login": "Login", + "to_multi_select": "to multi-select", "to_parent": "Go to parent", + "to_select": "to select", "to_trash": "Trash", "toggle_settings": "Toggle settings", "total": "Total", @@ -1888,6 +2015,7 @@ "trash_page_select_assets_btn": "Select assets", "trash_page_title": "Trash ({count})", "trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.", + "troubleshoot": "Troubleshoot", "type": "Type", "unable_to_change_pin_code": "Unable to change PIN code", "unable_to_setup_pin_code": "Unable to setup PIN code", @@ -1918,14 +2046,17 @@ "unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}", "untagged": "Untagged", "up_next": "Up next", + "update_location_action_prompt": "Update the location of {count} selected assets with:", "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", + "upload_action_prompt": "{count} queued for upload", "upload_concurrency": "Upload concurrency", "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", + "upload_finished": "Upload finished", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicates", @@ -1934,6 +2065,7 @@ "upload_success": "Upload success, refresh the page to see new upload assets.", "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", + "uploading_media": "Uploading media", "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", @@ -1981,6 +2113,7 @@ "view_next_asset": "View next asset", "view_previous_asset": "View previous asset", "view_qr_code": "View QR code", + "view_similar_photos": "View similar photos", "view_stack": "View Stack", "view_user": "View User", "viewer_remove_from_stack": "Remove from Stack", @@ -1999,5 +2132,6 @@ "yes": "Yes", "you_dont_have_any_shared_links": "You don't have any shared links", "your_wifi_name": "Your Wi-Fi name", - "zoom_image": "Zoom Image" + "zoom_image": "Zoom Image", + "zoom_to_bounds": "Zoom to bounds" } diff --git a/i18n/es.json b/i18n/es.json index 7bfdc678df..626ae0540c 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -2,7 +2,7 @@ "about": "Acerca de", "account": "Cuenta", "account_settings": "Ajustes de la cuenta", - "acknowledge": "De acuerdo", + "acknowledge": "Aceptar", "action": "AcciÃŗn", "action_common_update": "Actualizar", "actions": "Acciones", @@ -14,36 +14,48 @@ "add_a_location": "Agregar ubicaciÃŗn", "add_a_name": "Agregar nombre", "add_a_title": "Agregar título", - "add_endpoint": "AÃąadir endpoint", + "add_birthday": "Agregar un cumpleaÃąos", + "add_endpoint": "Agregar endpoint", "add_exclusion_pattern": "Agregar patrÃŗn de exclusiÃŗn", "add_import_path": "Agregar ruta de importaciÃŗn", "add_location": "Agregar ubicaciÃŗn", "add_more_users": "Agregar mÃĄs usuarios", "add_partner": "Agregar compaÃąero", - "add_path": "Agregar carpeta", + "add_path": "Agregar ruta", "add_photos": "Agregar fotos", "add_tag": "Agregar etiqueta", "add_to": "Agregar aâ€Ļ", "add_to_album": "Incluir en ÃĄlbum", "add_to_album_bottom_sheet_added": "Agregado a {album}", "add_to_album_bottom_sheet_already_exists": "Ya se encuentra en {album}", + "add_to_album_bottom_sheet_some_local_assets": "Algunos recursos locales no se pudieron aÃąadir al ÃĄlbum", + "add_to_album_toggle": "Alternar selecciÃŗn para el {album}", + "add_to_albums": "Incluir en ÃĄlbumes", + "add_to_albums_count": "Incluir en {count} ÃĄlbumes", "add_to_shared_album": "Incluir en ÃĄlbum compartido", - "add_url": "AÃąadir URL", + "add_url": "Agregar URL", "added_to_archive": "Agregado al Archivado", "added_to_favorites": "Agregado a favoritos", "added_to_favorites_count": "Agregado {count, number} a favoritos", "admin": { "add_exclusion_pattern_description": "Agrega patrones de exclusiÃŗn. Puedes utilizar los caracteres *, ** y ? (globbing). Ejemplos: para ignorar todos los archivos en cualquier directorio llamado \"Raw\", utiliza \"**/Raw/**\". Para ignorar todos los archivos que terminan en \".tif\", utiliza \"**/*.tif\". Para ignorar una ruta absoluta, utiliza \"/carpeta/a/ignorar/**\".", - "admin_user": "Usuario admin", + "admin_user": "Usuario administrador", "asset_offline_description": "Este recurso externo de la biblioteca ya no se encuentra en el disco y se ha movido a la papelera. Si el archivo se moviÃŗ dentro de la biblioteca, comprueba la línea temporal para el nuevo recurso correspondiente. Para restaurar este recurso, asegÃērate de que Immich puede acceder a la siguiente ruta de archivo y escanear la biblioteca.", "authentication_settings": "ParÃĄmetros de autenticaciÃŗn", "authentication_settings_description": "Gestionar contraseÃąas, OAuth y otros parÃĄmetros de autenticaciÃŗn", "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 Comando del servidor.", "background_task_job": "Tareas en segundo plano", - "backup_database": "Crear volcado de base de datos", - "backup_database_enable_description": "Activar volcado de base de datos", + "backup_database": "Crear volcado de la base de datos", + "backup_database_enable_description": "Activar volcados de la base de datos", "backup_keep_last_amount": "Cantidad de volcados previos a mantener", + "backup_onboarding_1_description": "Copia en un lugar externo, en la nube u otra ubicaciÃŗn física.", + "backup_onboarding_2_description": "copias locales en diferentes dispositivos. Incluye los archivos principales y una copia de seguridad local de dichos archivos.", + "backup_onboarding_3_description": "copias totales de tu data, incluyendo los archivos originales. Incluye 1 copia fuera de sitio y 2 copias locales.", + "backup_onboarding_description": "Una estrategia de copia de seguridad 3-2-1 es recomendada para proteger tu data. Deberías mantener tanto copias de tus fotos/videos subidos como de la base de datos de Immich para tener una soluciÃŗn de copia de seguridad integral.", + "backup_onboarding_footer": "Para obtener mÃĄs informaciÃŗn sobre cÃŗmo hacer una copia de seguridad de Immich, consulta la documentaciÃŗn.", + "backup_onboarding_parts_title": "Una copia de seguridad 3-2-1 incluye:", + "backup_onboarding_title": "Copias de seguridad", "backup_settings": "Ajustes de volcado de base de datos", "backup_settings_description": "Administrar configuraciÃŗn de volcado de base de datos.", "cleared_jobs": "Trabajos borrados para: {job}", @@ -53,7 +65,7 @@ "confirm_email_below": "Para confirmar, escribe \"{email}\" a continuaciÃŗn", "confirm_reprocess_all_faces": "ÂŋEstÃĄs seguro de que deseas reprocesar todas las caras? Esto borrarÃĄ a todas las personas que nombraste.", "confirm_user_password_reset": "ÂŋEstÃĄs seguro de que quieres restablecer la contraseÃąa de {user}?", - "confirm_user_pin_code_reset": "EstÃĄ seguro de que quiere restablecer el PIN de {user}?", + "confirm_user_pin_code_reset": "ÂŋSeguro que quieres restablecer el PIN de {user}?", "create_job": "Crear trabajo", "cron_expression": "ExpresiÃŗn CRON", "cron_expression_description": "Establece el intervalo de escaneo utilizando el formato CRON. Para mÃĄs informaciÃŗn puedes consultar, por ejemplo, Crontab Guru", @@ -63,8 +75,8 @@ "exclusion_pattern_description": "Los patrones de exclusiÃŗn te permiten ignorar archivos y carpetas al escanear tu biblioteca. Es Ãētil si tienes carpetas que contienen archivos que no deseas importar, por ejemplo archivos RAW.", "external_library_management": "GestiÃŗn de bibliotecas externas", "face_detection": "DetecciÃŗn de caras", - "face_detection_description": "Detecta las caras en los activos mediante aprendizaje automÃĄtico. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesarÃĄ todos los elementos. \"Restablecer\" borra ademÃĄs todos los datos de caras actuales. \"Falta\" pone en cola los elementos que aÃēn no se han procesado. Las caras detectadas se pondrÃĄn en cola para el reconocimiento facial una vez finalizada la detecciÃŗn, agrupÃĄndolos en personas existentes o nuevas.", - "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se ejecuta una vez finalizada la detecciÃŗn de caras. \"Restablecer\" (re)agrupa todas las caras. \"Falta\" pone en cola las caras que no tienen asignada una persona.", + "face_detection_description": "Detecta las caras en los elementos mediante aprendizaje automÃĄtico. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesarÃĄ todos los elementos. \"Restablecer\" borra ademÃĄs todos los datos de caras actuales. \"Faltante\" pone en cola los elementos que aÃēn no se han procesado. Las caras detectadas se pondrÃĄn en cola para el reconocimiento facial una vez finalizada la detecciÃŗn, agrupÃĄndolos en personas existentes o nuevas.", + "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se realiza despuÊs de completar la detecciÃŗn de caras. \"Restablecer\" (re)agrupa todas las caras. \"Faltante\" pone en cola las caras que no tienen una persona asignada.", "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "force_delete_user_warning": "CUIDADO: Esta acciÃŗn eliminarÃĄ inmediatamente el usuario y todos los elementos. Esta accion no se puede deshacer y los archivos no pueden ser recuperados.", "image_format": "Formato", @@ -80,7 +92,7 @@ "image_prefer_wide_gamut_setting_description": "Usar \"Display P3\" para las miniaturas. Preserva mejor la vivacidad de las imÃĄgenes con espacios de color amplios pero las imÃĄgenes pueden aparecer de manera diferente en dispositivos antiguos con una versiÃŗn antigua del navegador. Las imÃĄgenes sRGB se mantienen como sRGB para evitar cambios de color.", "image_preview_description": "Imagen de tamaÃąo mediano con metadatos eliminados. Es utilizado al visualizar un solo activo y para el aprendizaje automÃĄtico", "image_preview_quality_description": "Calidad de vista previa de 1 a 100. Es mejor cuanto mÃĄs alta sea la calidad pero genera archivos mÃĄs grandes y puede reducir la capacidad de respuesta de la aplicaciÃŗn. Establecer un valor bajo puede afectar la calidad del aprendizaje automÃĄtico.", - "image_preview_title": "Ajustes de la vista previa", + "image_preview_title": "Ajustes de las vistas previas", "image_quality": "Calidad", "image_resolution": "ResoluciÃŗn", "image_resolution_description": "Las resoluciones mÃĄs altas pueden conservar mÃĄs detalles pero requieren mÃĄs tiempo para codificar, tienen tamaÃąos de archivo mÃĄs grandes y pueden afectar la capacidad de respuesta de la aplicaciÃŗn.", @@ -105,13 +117,20 @@ "library_scanning_enable_description": "Activar el escaneo periÃŗdico de la biblioteca", "library_settings": "Biblioteca externa", "library_settings_description": "Administrar configuraciÃŗn biblioteca externa", - "library_tasks_description": "Buscar archivos nuevos o modificados en bibliotecas externas", + "library_tasks_description": "Buscar elementos nuevos o modificados en bibliotecas externas", "library_watching_enable_description": "Vigilar las bibliotecas externas para detectar cambios en los archivos", "library_watching_settings": "Vigilancia de la biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Vigilar automaticamente en busca de archivos modificados", "logging_enable_description": "Habilitar registro", "logging_level_description": "Indica el nivel de registro a utilizar cuando estÃĄ habilitado.", "logging_settings": "Registro", + "machine_learning_availability_checks": "Comprobaciones de disponibilidad", + "machine_learning_availability_checks_description": "AutomÃĄticamente detectar y preferir servidores de machine learning disponibles", + "machine_learning_availability_checks_enabled": "Habilitar comprobaciones de disponibilidad", + "machine_learning_availability_checks_interval": "Intervalo de comprobaciÃŗn", + "machine_learning_availability_checks_interval_description": "Intervalo en milisegundos entre las comprobaciones de disponibilidad", + "machine_learning_availability_checks_timeout": "Tiempo de espera de solicitud", + "machine_learning_availability_checks_timeout_description": "Tiempo de espera en milisegundos para comprobaciones de disponibilidad", "machine_learning_clip_model": "Modelo CLIP (Contrastive Language-Image Pre-Training)", "machine_learning_clip_model_description": "El nombre de un modelo CLIP listado aquí. TendrÃĄs que relanzar el trabajo 'BÃēsqueda Inteligente' para todos los elementos al cambiar de modelo.", "machine_learning_duplicate_detection": "DetecciÃŗn de duplicados", @@ -152,12 +171,12 @@ "map_manage_reverse_geocoding_settings": "Gestionar los ajustes de la geocodificaciÃŗn inversa", "map_reverse_geocoding": "GeocodificaciÃŗn inversa", "map_reverse_geocoding_enable_description": "Activar geocodificaciÃŗn inversa", - "map_reverse_geocoding_settings": "Ajustes GeocodificaciÃŗn Inversa", + "map_reverse_geocoding_settings": "Ajustes de la geocodificaciÃŗn inversa", "map_settings": "Mapa", "map_settings_description": "Administrar la configuraciÃŗn del mapa", "map_style_description": "DirecciÃŗn URL a un tema de mapa (style.json)", - "memory_cleanup_job": "Limpieza de memoria", - "memory_generate_job": "GeneraciÃŗn de memoria", + "memory_cleanup_job": "Limpieza de recuerdos", + "memory_generate_job": "GeneraciÃŗn de recuerdos", "metadata_extraction_job": "ExtracciÃŗn de metadatos", "metadata_extraction_job_description": "Extraer informaciÃŗn de metadatos de cada activo, como GPS, caras y resoluciÃŗn", "metadata_faces_import_setting": "Activar importaciÃŗn de caras", @@ -166,9 +185,23 @@ "metadata_settings_description": "Administrar la configuraciÃŗn de metadatos", "migration_job": "MigraciÃŗn", "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 Almacenamiento a un elemento anteriormente cargado, lanza el", + "nightly_tasks_cluster_faces_setting_description": "Ejecutar reconocimiento facial en caras detectadas recientemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar caras nuevas", + "nightly_tasks_database_cleanup_setting": "Tareas de limpieza de base de datos", + "nightly_tasks_database_cleanup_setting_description": "Limpiar datos antiguos y caducados de la base de datos", + "nightly_tasks_generate_memories_setting": "Generar recuerdos", + "nightly_tasks_generate_memories_setting_description": "Crear nuevos recuerdos a partir de activos", + "nightly_tasks_missing_thumbnails_setting": "Generar miniaturas faltantes", + "nightly_tasks_missing_thumbnails_setting_description": "Poner en cola a activos sin miniaturas para la generaciÃŗn de miniaturas", + "nightly_tasks_settings": "ConfiguraciÃŗn de Tareas Nocturnas", + "nightly_tasks_settings_description": "Gestionar Tareas Nocturnas", + "nightly_tasks_start_time_setting": "Tiempo de inicio", + "nightly_tasks_start_time_setting_description": "El tiempo cuando el servidor comienza a ejecutar las tareas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Uso de la cuota de sincronizaciÃŗn", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, segÃēn el uso actual", + "no_paths_added": "No se han agregado rutas", + "no_pattern_added": "No se han agregado patrones", + "note_apply_storage_label_previous_assets": "Nota: Para aplicar la etiqueta de almacenamiento a los elementos que ya se subieron, ejecuta la", "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 \". AsegÃērate de utilizar una direcciÃŗn desde la que puedas enviar correos electrÃŗnicos.", @@ -196,26 +229,28 @@ "oauth_mobile_redirect_uri": "URI de redireccionamiento mÃŗvil", "oauth_mobile_redirect_uri_override": "Sobreescribir URI de redirecciÃŗn mÃŗvil", "oauth_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI mÃŗvil, como ''{callback}''", + "oauth_role_claim": "ConcesiÃŗn de rol", + "oauth_role_claim_description": "Otorgar acceso de administrador automÃĄticamente segÃēn la presencia de esta concesiÃŗn. La concesiÃŗn puede tener \"usuario\" o \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Administrar la configuraciÃŗn de inicio de sesiÃŗn de OAuth", "oauth_settings_more_details": "Para mÃĄs detalles acerca de esta característica, consulte la documentaciÃŗn.", - "oauth_storage_label_claim": "PeticiÃŗn de etiqueta de almacenamiento", - "oauth_storage_label_claim_description": "Establece la etiqueta del almacenamiento del usuario automÃĄticamente a este valor reclamado.", - "oauth_storage_quota_claim": "Reclamar quota de almacenamiento", - "oauth_storage_quota_claim_description": "Establezca automÃĄticamente la cuota de almacenamiento del usuario al valor de esta solicitud.", + "oauth_storage_label_claim": "Solicitud de etiqueta de almacenamiento", + "oauth_storage_label_claim_description": "Fijar la etiqueta de almacenamiento del usuario automÃĄticamente al valor solicitado.", + "oauth_storage_quota_claim": "Cuota de almacenamiento solicitada", + "oauth_storage_quota_claim_description": "Fijar la cuota de almacenamiento del usuario automÃĄticamente al valor solicitado.", "oauth_storage_quota_default": "Cuota de almacenamiento predeterminada (GiB)", - "oauth_storage_quota_default_description": "Cuota en GiB que se utilizarÃĄ cuando no se proporcione ninguna por defecto.", - "oauth_timeout": "ExpiraciÃŗn de solicitud", + "oauth_storage_quota_default_description": "Cuota (en GiB) que se usarÃĄ cuando no se solicite un valor específico.", + "oauth_timeout": "Tiempo de espera agotado para la solicitud", "oauth_timeout_description": "Tiempo de espera de solicitudes en milisegundos", "password_enable_description": "Iniciar sesiÃŗn con correo electrÃŗnico y contraseÃąa", "password_settings": "ContraseÃąa de Acceso", "password_settings_description": "Administrar la configuraciÃŗn de inicio de sesiÃŗn con contraseÃąa", "paths_validated_successfully": "Todas las carpetas se han validado satisfactoriamente", "person_cleanup_job": "Limpieza de personas", - "quota_size_gib": "TamaÃąo de Quota (GiB)", + "quota_size_gib": "TamaÃąo de la cuota (GiB)", "refreshing_all_libraries": "Actualizar todas las bibliotecas", "registration": "Registrar administrador", - "registration_description": "Dado que eres el primer usuario del sistema, se te asignarÃĄ como Admin y serÃĄs responsable de las tareas administrativas, y de crear a los usuarios adicionales.", + "registration_description": "Dado que eres el primer usuario del sistema, se te designarÃĄ como administrador, tendrÃĄs a tu cargo las tareas administrativas y deberÃĄs crear los demÃĄs usuarios.", "require_password_change_on_login": "Requerir que el usuario cambie la contraseÃąa en el primer inicio de sesiÃŗn", "reset_settings_to_default": "Restablecer la configuraciÃŗn predeterminada", "reset_settings_to_recent_saved": "Restablecer la configuraciÃŗn a la configuraciÃŗn guardada recientemente", @@ -225,7 +260,7 @@ "server_external_domain_settings": "Dominio externo", "server_external_domain_settings_description": "Dominio para enlaces pÃēblicos compartidos, incluidos http(s)://", "server_public_users": "Usuarios pÃēblicos", - "server_public_users_description": "Todos los usuarios (nombre y correo electrÃŗnico) aparecen en la lista cuando se aÃąade un usuario a los ÃĄlbumes compartidos. Si se desactiva, la lista de usuarios sÃŗlo estarÃĄ disponible para los usuarios administradores.", + "server_public_users_description": "Cuando se agrega un usuario a los ÃĄlbumes compartidos, todos los usuarios aparecen en una lista con su nombre y su correo electrÃŗnico. Si deshabilita esta opciÃŗn, solo los administradores podrÃĄn ver la lista de usuarios.", "server_settings": "ConfiguraciÃŗn del servidor", "server_settings_description": "Administrar la configuraciÃŗn del servidor", "server_welcome_message": "Mensaje de bienvenida", @@ -247,9 +282,9 @@ "storage_template_onboarding_description_v2": "Al habilitar esta funciÃŗn, los archivos se organizarÃĄn automÃĄticamente segÃēn la plantilla definida por el usuario. Para mÃĄs informaciÃŗn, consulte la documentaciÃŗn.", "storage_template_path_length": "Límite aproximado de la longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administrar 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 subido", "storage_template_user_label": "{label} es la etiqueta de almacenamiento del usuario", - "system_settings": "Ajustes del Sistema", + "system_settings": "Ajustes del sistema", "tag_cleanup_job": "Limpieza de etiquetas", "template_email_available_tags": "Puede utilizar las siguientes variables en su plantilla: {tags}", "template_email_if_empty": "Si la plantilla estÃĄ vacía, se utilizarÃĄ el correo electrÃŗnico predeterminado.", @@ -262,7 +297,7 @@ "template_settings_description": "Gestione plantillas personalizadas para las notificaciones", "theme_custom_css_settings": "CSS Personalizado", "theme_custom_css_settings_description": "Las Hojas de Estilo (CSS) permiten personalizar el diseÃąo de Immich.", - "theme_settings": "Ajustes Tema", + "theme_settings": "Ajustes del tema", "theme_settings_description": "Gestionar la personalizaciÃŗn de la interfaz web de Immich", "thumbnail_generation_job": "Generar Miniaturas", "thumbnail_generation_job_description": "Genere miniaturas grandes, pequeÃąas y borrosas para cada archivo, así como miniaturas para cada persona", @@ -306,7 +341,7 @@ "transcoding_preferred_hardware_device": "Dispositivo de hardware preferido", "transcoding_preferred_hardware_device_description": "Se aplica Ãēnicamente a VAAPI y QSV. Establece el nodo dri utilizado para la transcodificaciÃŗn de hardware.", "transcoding_preset_preset": "ConfiguraciÃŗn predefinida (-preset)", - "transcoding_preset_preset_description": "Velocidad de compresiÃŗn. Los preajustes mÃĄs lentos producen archivos mÃĄs pequeÃąos, y aumentan la calidad cuando se apunta a una determinada tasa de bits. VP9 ignora las velocidades superiores a 'mÃĄs rÃĄpido'.", + "transcoding_preset_preset_description": "Velocidad de compresiÃŗn. Los preajustes mÃĄs lentos producen archivos mÃĄs pequeÃąos y aumentan la calidad cuando se apunta a una tasa de bits determinada. VP9 ignora las velocidades superiores al valor \"faster\" (\"mÃĄs rÃĄpido\").", "transcoding_reference_frames": "Frames de referencia", "transcoding_reference_frames_description": "El nÃēmero de fotogramas a los que hacer referencia al comprimir un fotograma determinado. Los valores mÃĄs altos mejoran la eficiencia de la compresiÃŗn, pero ralentizan la codificaciÃŗn. 0 establece este valor automÃĄticamente.", "transcoding_required_description": "SÃŗlo vídeos que no estÊn en un formato soportado", @@ -325,19 +360,22 @@ "transcoding_two_pass_encoding": "CodificaciÃŗn en dos pasadas", "transcoding_two_pass_encoding_setting_description": "Transcodifica en dos pasadas para producir vídeos mejor codificados. Cuando la velocidad de bits mÃĄxima estÃĄ habilitada (es necesaria para que funcione con H.264 y HEVC), este modo utiliza un rango de velocidad de bits basado en la velocidad de bits mÃĄxima e ignora CRF. Para VP9, se puede utilizar CRF si la tasa de bits mÃĄxima estÃĄ deshabilitada.", "transcoding_video_codec": "CÃŗdecs de Video", - "transcoding_video_codec_description": "VP9 tiene alta eficiencia y compatibilidad web, pero lleva mÃĄs tiempo transcodificarlo. HEVC funciona de manera similar, pero tiene menor compatibilidad web. H.264 es ampliamente compatible y se transcodifica rÃĄpidamente, pero produce archivos mucho mÃĄs grandes. AV1 es el cÃŗdec mÃĄs eficiente pero carece de soporte en dispositivos mÃĄs antiguos.", + "transcoding_video_codec_description": "VP9 tiene alta eficiencia y compatibilidad web, pero lleva mucho tiempo transcodificarlo. HEVC ofrece un rendimiento similar, pero tiene menor compatibilidad web. H.264 es ampliamente compatible y se transcodifica muy rÃĄpido, pero los archivos producidos son mucho mÃĄs grandes. AV1 es el cÃŗdec mÃĄs eficiente, pero no es compatible con los dispositivos mÃĄs antiguos.", "trash_enabled_description": "Habilitar papelera", "trash_number_of_days": "NÃēmero de días", "trash_number_of_days_description": "NÃēmero de días para mantener los archivos en la papelera antes de eliminarlos permanentemente", "trash_settings": "ConfiguraciÃŗn papelera", "trash_settings_description": "Administrar la configuraciÃŗn de la papelera", + "unlink_all_oauth_accounts": "Desvincular todas las cuentas de OAuth", + "unlink_all_oauth_accounts_description": "Recuerda desvincular todas las cuentas de OAuth antes de migrar a un proveedor nuevo.", + "unlink_all_oauth_accounts_prompt": "ÂŋSeguro que deseas desvincular todas las cuentas de OAuth? Se restablecerÃĄ el id. de OAuth de cada usuario. La acciÃŗn no se podrÃĄ deshacer.", "user_cleanup_job": "Limpieza de usuarios", "user_delete_delay": "La cuenta {user} 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", "user_delete_delay_settings_description": "NÃēmero de días despuÊs de la eliminaciÃŗn para eliminar permanentemente la cuenta y los activos de un usuario. El trabajo de eliminaciÃŗn de usuarios se ejecuta a medianoche para comprobar si hay usuarios que estÊn listos para su eliminaciÃŗn. Los cambios a esta configuraciÃŗn se evaluarÃĄn en la prÃŗxima ejecuciÃŗn.", "user_delete_immediately": "La cuenta {user} y los archivos se pondrÃĄn en cola para su eliminaciÃŗn permanente inmediatamente.", "user_delete_immediately_checkbox": "Poner en cola la eliminaciÃŗn inmediata de usuarios y elementos", - "user_details": "Detalles de Usuario", + "user_details": "Detalles del usuario", "user_management": "GestiÃŗn de usuarios", "user_password_has_been_reset": "La contraseÃąa del usuario ha sido restablecida:", "user_password_reset_description": "Proporcione una contraseÃąa temporal al usuario e infÃŗrmele que deberÃĄ cambiar la contraseÃąa en su prÃŗximo inicio de sesiÃŗn.", @@ -353,17 +391,19 @@ "video_conversion_job": "Transcodificar vídeos", "video_conversion_job_description": "Transcodifique vídeos para una mayor compatibilidad con navegadores y dispositivos" }, - "admin_email": "Correo ElectrÃŗnico del Administrador", - "admin_password": "ContraseÃąa del Administrador", + "admin_email": "Correo electrÃŗnico del administrador", + "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: {level}", - "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_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los archivos locales. 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": "Cabeceras Proxy", + "advanced_settings_readonly_mode_subtitle": "Habilita el modo de solo lectura donde las fotografías sÃŗlo pueden ser vistas, funciones como seleccionar mÃēltiples imÃĄgenes, compartir, transmitir, eliminar son deshabilitadas. Habilita/Deshabilita solo lectura vía el avatar del usuario en la pantalla principal", + "advanced_settings_readonly_mode_title": "Modo Solo lectura", "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", @@ -374,11 +414,12 @@ "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}}", - "album_added": "Álbum aÃąadido", + "album_added": "Álbum agregado", "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", "album_delete_confirmation": "ÂŋEstÃĄs seguro de que deseas eliminar el ÃĄlbum {album}?", "album_delete_confirmation_description": "Si este ÃĄlbum se comparte, otros usuarios ya no podrÃĄn acceder a Êl.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUIDOS", "album_info_card_backup_album_included": "INCLUIDOS", "album_info_updated": "InformaciÃŗn del ÃĄlbum actualizada", @@ -388,7 +429,9 @@ "album_options": "Opciones del Album", "album_remove_user": "ÂŋEliminar usuario?", "album_remove_user_confirmation": "ÂŋEstÃĄs seguro de que quieres eliminar a {user}?", + "album_search_not_found": "No se encontraron ÃĄlbumes que coincidan con tu bÃēsqueda", "album_share_no_users": "Parece que has compartido este ÃĄlbum con todos los usuarios o no tienes ningÃēn usuario con quien compartirlo.", + "album_summary": "Resumen del ÃĄlbum", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificaciÃŗn por correo electrÃŗnico cuando un ÃĄlbum compartido tenga nuevos archivos", "album_user_left": "Salida {album}", @@ -401,20 +444,21 @@ "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.", + "album_with_link_access": "Permite que cualquiera que tenga este enlace vea las fotos y las personas del ÃĄlbum.", "albums": "Álbumes", - "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbumes}}", + "albums_count": "{count, plural, one {{count, number} ÃĄlbum} other {{count, number} ÃĄlbumes}}", "albums_default_sort_order": "OrdenaciÃŗn por defecto de los ÃĄlbumes", - "albums_default_sort_order_description": "Orden de clasificaciÃŗn inicial de los activos al crear nuevos ÃĄlbumes.", - "albums_feature_description": "Colecciones de activos que pueden compartirse con otros usuarios.", + "albums_default_sort_order_description": "Orden de clasificaciÃŗn inicial de los recursos al crear nuevos ÃĄlbumes.", + "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", + "albums_on_device_count": "Álbumes en el dispositivo ({count})", "all": "Todos", - "all_albums": "Todos los albums", + "all_albums": "Todos los ÃĄlbumes", "all_people": "Todas las personas", "all_videos": "Todos los videos", "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 subir al usuario publico", + "allow_public_user_to_download": "Permitir descargas a los usuarios pÃēblicos", + "allow_public_user_to_upload": "Permitir subir fotos a los usuarios pÃēblicos", "alt_text_qr_code": "CÃŗdigo QR", "anti_clockwise": "En sentido antihorario", "api_key": "Clave API", @@ -424,9 +468,11 @@ "app_bar_signout_dialog_content": "ÂŋEstÃĄs seguro que quieres cerrar sesiÃŗn?", "app_bar_signout_dialog_ok": "Sí", "app_bar_signout_dialog_title": "Cerrar sesiÃŗn", - "app_settings": "Ajustes de Aplicacion", + "app_settings": "Ajustes de la aplicacion", "appears_in": "Aparece en", + "apply_count": "Aplicar ({count, number})", "archive": "Archivo", + "archive_action_prompt": "{count} agregado(s) al archivo", "archive_or_unarchive_photo": "Archivar o restaurar foto", "archive_page_no_archived_assets": "No se encontraron elementos archivados", "archive_page_title": "Archivo ({count})", @@ -438,8 +484,8 @@ "are_you_sure_to_do_this": "ÂŋEstas seguro de que quieres hacer esto?", "asset_action_delete_err_read_only": "No se pueden borrar el archivo(s) de solo lectura, omitiendo", "asset_action_share_err_offline": "No se pudo obtener el archivo(s) sin conexiÃŗn, omitiendo", - "asset_added_to_album": "AÃąadido al ÃĄlbum", - "asset_adding_to_album": "AÃąadiendo al ÃĄlbumâ€Ļ", + "asset_added_to_album": "Agregado al ÃĄlbum", + "asset_adding_to_album": "Agregando al ÃĄlbumâ€Ļ", "asset_description_updated": "La descripciÃŗn del elemento ha sido actualizada", "asset_filename_is_offline": "El archivo {filename} estÃĄ offline", "asset_has_unassigned_faces": "El archivo no tiene rostros asignados", @@ -457,20 +503,23 @@ "asset_restored_successfully": "Elementos restaurados exitosamente", "asset_skipped": "Omitido", "asset_skipped_in_trash": "En la papelera", + "asset_trashed": "Elemento eliminado", + "asset_troubleshoot": "DiagnÃŗstico del elemento", "asset_uploaded": "Subido", "asset_uploading": "Subiendoâ€Ļ", "asset_viewer_settings_subtitle": "Administra las configuracioens de tu visor de fotos", "asset_viewer_settings_title": "Visor de Archivos", "assets": "elementos", - "assets_added_count": "AÃąadido {count, plural, one {# asset} other {# assets}}", - "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 {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no pueden ser aÃąadidos al album", + "assets_added_count": "{count, plural, one {# elemento agregado} other {# elementos agregados}}", + "assets_added_to_album_count": "{count, plural, one {# elemento agregado} other {# elementos agregados}} al ÃĄlbum", + "assets_added_to_albums_count": "{assetTotal, plural, one {# agregado} other {# agregados}} {albumTotal, plural, one {# al ÃĄlbum} other {# a los ÃĄlbumes}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {El elemento no se puede agregar al ÃĄlbum} other {Los elementos no se pueden agregar al ÃĄlbum}}", + "assets_cannot_be_added_to_albums": "{count, plural, one {El elemento} other {Los elementos}} no se {count, plural, one {puede} other {pueden}} agregar a ninguno de los ÃĄlbumes", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich", - "assets_downloaded_failed": "{count, plural, one {Descargado archivo # - {error} archivo fallido} other {Descargados # archivos - {error} archivos fallidos}}", - "assets_downloaded_successfully": "{count, plural, one {Archivo # descargado correctamente} other {Archivos # descargados correctamente}}", + "assets_downloaded_failed": "{count, plural, one {# archivo descargado - {error} archivo fallido} other {# archivos descargados - {error} archivos fallidos}}", + "assets_downloaded_successfully": "{count, plural, one {# archivo descargado exitosamente} other {# archivos descargados exitosamente}}", "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}}", @@ -482,20 +531,25 @@ "assets_trashed_count": "Borrado {count, plural, one {# elemento} other {# elementos}}", "assets_trashed_from_server": "{count} 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", + "assets_were_part_of_albums_count": "{count, plural, one {El elemento ya es} other {Los elementos ya son}} parte de los ÃĄlbumes", "authorized_devices": "Dispositivos Autorizados", "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", "autoplay_slideshow": "PresentaciÃŗn con reproducciÃŗn automÃĄtica", "back": "AtrÃĄs", "back_close_deselect": "AtrÃĄs, cerrar o anular la selecciÃŗn", + "background_backup_running_error": "Ya se estÃĄ ejecutando la copia de seguridad en segundo plano, no se puede iniciar la copia de seguridad manual", "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", + "background_options": "Opciones de segundo plano", + "backup": "Copia de Seguridad", "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({count})", "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.", - "backup_album_selection_page_select_albums": "Seleccionar Álbumes", + "backup_album_selection_page_select_albums": "Seleccionar ÃĄlbumes", "backup_album_selection_page_selection_info": "InformaciÃŗn sobre la SelecciÃŗn", "backup_album_selection_page_total_assets": "Total de elementos Ãēnicos", + "backup_albums_sync": "SincronizaciÃŗn de ÃĄlbumes de respaldo", "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â€Ļ", @@ -529,7 +583,7 @@ "backup_controller_page_excluded": "Excluido: ", "backup_controller_page_failed": "Fallidos ({count})", "backup_controller_page_filename": "Nombre del archivo: {filename} [{size}]", - "backup_controller_page_id": "ID: {id}", + "backup_controller_page_id": "Id.: {id}", "backup_controller_page_info": "InformaciÃŗn de la Copia de Seguridad", "backup_controller_page_none_selected": "Ninguno seleccionado", "backup_controller_page_remainder": "Restante", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Activar la copia de seguridad", "backup_controller_page_uploading_file_info": "Subiendo informaciÃŗn del archivo", "backup_err_only_album": "No se puede eliminar el Ãēnico ÃĄlbum", + "backup_error_sync_failed": "SincronizaciÃŗn fallÃŗ. No es posible procesar la copia de seguridad.", "backup_info_card_assets": "elementos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Subida ya en progreso. Vuelve a intentarlo mÃĄs tarde", "backup_manual_success": "Éxito", "backup_manual_title": "Estado de la subida", + "backup_options": "Opciones de copia de seguridad", "backup_options_page_title": "Opciones de Copia de Seguridad", "backup_setting_subtitle": "Administra las configuraciones de respaldo en segundo y primer plano", + "backup_settings_subtitle": "Configura las opciones de subida", "backward": "Retroceder", "biometric_auth_enabled": "AutentificaciÃŗn biomÊtrica habilitada", "biometric_locked_out": "EstÃĄs bloqueado de la autentificaciÃŗn biomÊtrica", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "Borrar cachÊ", "cache_settings_clear_cache_button_title": "Borra la cachÊ de la aplicaciÃŗn. Esto afectarÃĄ significativamente el rendimiento de la aplicaciÃŗn hasta que se reconstruya la cachÊ.", "cache_settings_duplicated_assets_clear_button": "LIMPIAR", - "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos en la lista negra de la app", + "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos ignorados por la aplicaciÃŗn", "cache_settings_duplicated_assets_title": "Elementos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas de la biblioteca", "cache_settings_statistics_full": "ImÃĄgenes completas", @@ -587,6 +644,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar bÃēsqueda", "canceled": "Cancelado", + "canceling": "Cancelando", "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", @@ -599,25 +657,28 @@ "change_location": "Cambiar ubicaciÃŗn", "change_name": "Cambiar nombre", "change_name_successfully": "Nombre cambiado exitosamente", - "change_password": "Cambiar ContraseÃąa", + "change_password": "Cambiar contraseÃąa", "change_password_description": "Esta es la primera vez que inicia sesiÃŗn en el sistema o se ha realizado una solicitud para cambiar su contraseÃąa. Por favor ingrese la nueva contraseÃąa a continuaciÃŗn.", - "change_password_form_confirm_password": "Confirmar ContraseÃąa", + "change_password_form_confirm_password": "Confirmar contraseÃąa", "change_password_form_description": "Hola {name},\n\nEsta es la primera vez que inicias sesiÃŗn en el sistema o se ha solicitado cambiar tu contraseÃąa. Por favor, introduce la nueva contraseÃąa a continuaciÃŗn.", - "change_password_form_new_password": "Nueva ContraseÃąa", + "change_password_form_new_password": "Nueva contraseÃąa", "change_password_form_password_mismatch": "Las contraseÃąas no coinciden", "change_password_form_reenter_new_password": "Vuelve a ingresar la nueva contraseÃąa", "change_pin_code": "Cambiar PIN", "change_your_password": "Cambia tu contraseÃąa", "changed_visibility_successfully": "Visibilidad cambiada correctamente", + "charging": "Cargando", + "charging_requirement_mobile_backup": "La copia de seguridad en segundo plano requiere que el dispositivo se estÊ cargando", "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", + "choose_matching_people_to_merge": "Elija ocurrencias duplicadas de la misma persona para fusionar", "city": "Ciudad", "clear": "Limpiar", "clear_all": "Limpiar todo", "clear_all_recent_searches": "Borrar bÃēsquedas recientes", + "clear_file_cache": "Limpiar la cachÊ de archivos", "clear_message": "Limpiar mensaje", "clear_value": "Limpiar valor", "client_cert_dialog_msg_confirm": "OK", @@ -639,14 +700,14 @@ "comments_and_likes": "Comentarios y me gusta", "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.", + "common_server_error": "Por favor, comprueba 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": "Completado", "confirm": "Confirmar", - "confirm_admin_password": "Confirmar ContraseÃąa de Administrador", + "confirm_admin_password": "Confirmar contraseÃąa del administrador", "confirm_delete_face": "ÂŋEstÃĄs seguro que deseas eliminar la cara de {name} del archivo?", "confirm_delete_shared_link": "ÂŋEstÃĄs seguro de que deseas eliminar este enlace compartido?", "confirm_keep_this_delete_others": "Todos los demÃĄs activos de la pila se eliminarÃĄn excepto este activo. ÂŋEstÃĄ seguro de que quiere continuar?", - "confirm_new_pin_code": "Confirmar nuevo pin", + "confirm_new_pin_code": "Confirmar nuevo PIN", "confirm_password": "Confirmar contraseÃąa", "confirm_tag_face": "ÂŋQuieres etiquetar esta cara como {name}?", "confirm_tag_face_unnamed": "ÂŋQuieres etiquetar esta cara?", @@ -687,12 +748,14 @@ "create_new_person_hint": "Asignar los archivos seleccionados a una nueva persona", "create_new_user": "Crear nuevo usuario", "create_shared_album_page_share_add_assets": "AGREGAR ELEMENTOS", - "create_shared_album_page_share_select_photos": "Seleccionar Fotos", + "create_shared_album_page_share_select_photos": "Seleccionar fotos", + "create_shared_link": "Crear un enlace compartido", "create_tag": "Crear etiqueta", "create_tag_description": "Crear una nueva etiqueta. Para las etiquetas anidadas, ingresa la ruta completa de la etiqueta, incluidas las barras diagonales.", "create_user": "Crear usuario", "created": "Creado", "created_at": "Creado", + "creating_linked_albums": "Creando ÃĄlbumes vinculados...", "crop": "Recortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo actual", @@ -700,10 +763,11 @@ "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", + "custom_url": "URL personalizada", "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", - "darkTheme": "Activar tema oscuro", + "dark_theme": "Alternar tema oscuro", "date_after": "Fecha posterior", "date_and_time": "Fecha y Hora", "date_before": "Fecha anterior", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Guardada con Êxito la fecha de nacimiento", "date_range": "Rango de fechas", "day": "Día", + "days": "Días", "deduplicate_all": "Deduplicar todo", "deduplication_criteria_1": "TamaÃąo de imagen en bytes", "deduplication_criteria_2": "Conteo de datos EXIF", @@ -719,12 +784,14 @@ "default_locale": "ConfiguraciÃŗn regional predeterminada", "default_locale_description": "Formatee fechas y nÃēmeros segÃēn la configuraciÃŗn regional de su navegador", "delete": "Eliminar", + "delete_action_confirmation_message": "ÂŋEstÃĄ seguro que desea eliminar este archivo? Esta acciÃŗn lo moverÃĄ a la papelera del servidor y le preguntarÃĄ si desea eliminarlo localmente", + "delete_action_prompt": "{count} eliminados", "delete_album": "Eliminar ÃĄlbum", "delete_api_key_prompt": "ÂŋEstÃĄ seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serÃĄn eliminados permanentemente de Immich y de tu dispositivo", - "delete_dialog_alert_local": "Estas imÃĄgenes van a ser borradas de tu dispositivo, pero seguirÃĄn disponibles en el servidor Immich", - "delete_dialog_alert_local_non_backed_up": "Algunas de las imÃĄgenes no tienen copia de seguridad y serÃĄn borradas de forma permanente de tu dispositivo", - "delete_dialog_alert_remote": "Estas imÃĄgenes van a ser borradas de forma permanente del servidor Immich", + "delete_dialog_alert_local": "Estos elementos se eliminarÃĄn permanente de tu dispositivo pero seguirÃĄn disponibles en el servidor de Immich", + "delete_dialog_alert_local_non_backed_up": "Algunos de los elementos no tienen copia de seguridad en Immich y serÃĄn borrados permanentemente de tu dispositivo", + "delete_dialog_alert_remote": "Estas imÃĄgenes van a ser borradas permanentemente del servidor de Immich", "delete_dialog_ok_force": "Borrar de todos modos", "delete_dialog_title": "Eliminar Permanentemente", "delete_duplicates_confirmation": "ÂŋEstÃĄ seguro de que desea eliminar permanentemente estos duplicados?", @@ -732,9 +799,12 @@ "delete_key": "Eliminar clave", "delete_library": "Eliminar biblioteca", "delete_link": "Eliminar enlace", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar enlace compartido", "delete_shared_link_dialog_title": "Eliminar enlace compartido", "delete_tag": "Eliminar etiqueta", @@ -744,11 +814,13 @@ "deletes_missing_assets": "Elimina archivos que faltan en el disco duro", "description": "DescripciÃŗn", "description_input_hint_text": "Agregar descripciÃŗn...", - "description_input_submit_error": "Error al actualizar la descripciÃŗn, verifica el registro para obtener mÃĄs detalles", + "description_input_submit_error": "Error al actualizar la descripciÃŗn, comprueba el registro para obtener mÃĄs detalles", + "deselect_all": "Deseleccionar Todo", "details": "Detalles", "direction": "DirecciÃŗn", "disabled": "Deshabilitado", "disallow_edits": "Bloquear ediciÃŗn", + "discord": "Discord", "discover": "Descubrir", "discovered_devices": "Dispositivos descubiertos", "dismiss_all_errors": "Descartar todos los errores", @@ -761,6 +833,7 @@ "documentation": "DocumentaciÃŗn", "done": "Hecho", "download": "Descargar", + "download_action_prompt": "Descargando {count} archivos", "download_canceled": "Descarga cancelada", "download_complete": "Descarga completada", "download_enqueue": "Descarga en cola", @@ -787,8 +860,12 @@ "edit": "Editar", "edit_album": "Editar album", "edit_avatar": "Editar avatar", + "edit_birthday": "Editar cumpleaÃąos", "edit_date": "Editar fecha", "edit_date_and_time": "Editar fecha y hora", + "edit_date_and_time_action_prompt": "{count} fecha y hora editadas", + "edit_date_and_time_by_offset": "Cambiar fecha usando una desviaciÃŗn", + "edit_date_and_time_by_offset_interval": "Nuevo intervalo de fechas: {from} - {to}", "edit_description": "Editar descripciÃŗn", "edit_description_prompt": "Por favor selecciona una nueva descripciÃŗn:", "edit_exclusion_pattern": "Editar patrÃŗn de exclusiÃŗn", @@ -798,6 +875,7 @@ "edit_key": "Editar clave", "edit_link": "Editar enlace", "edit_location": "Editar ubicaciÃŗn", + "edit_location_action_prompt": "{count} ubicaciones actualizadas", "edit_location_dialog_title": "UbicaciÃŗn", "edit_name": "Cambiar nombre", "edit_people": "Editar persona", @@ -816,19 +894,22 @@ "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", + "enable_backup": "Habilitar Copia de Seguridad", "enable_biometric_auth_description": "Introduce tu cÃŗdigo PIN para habilitar la autentificaciÃŗn biomÊtrica", "enabled": "Habilitado", "end_date": "Fecha final", - "enqueued": "AÃąadido a la cola", + "enqueued": "Agregado a la cola", "enter_wifi_name": "Introduce el nombre Wi-Fi", "enter_your_pin_code": "Introduce tu cÃŗdigo PIN", - "enter_your_pin_code_subtitle": "Introduce tu cÃŗdigo PIN para acceder a la carpeta bloqueada", + "enter_your_pin_code_subtitle": "Introduce tu cÃŗdigo PIN para acceder a la carpeta protegida", "error": "Error", "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_getting_places": "Error obteniendo lugares", "error_loading_image": "Error al cargar la imagen", + "error_loading_partners": "Error al cargar compaÃąeros: {error}", "error_saving_image": "Error: {error}", - "error_tag_face_bounding_box": "Error etiquetando cara - no se pueden obtener las coordenadas del marco delimitante", + "error_tag_face_bounding_box": "Error al etiquetar la cara: no se pueden obtener las coordenadas del marco", "error_title": "Error: algo saliÃŗ mal", "errors": { "cannot_navigate_next_asset": "No puedes navegar al siguiente archivo", @@ -841,8 +922,8 @@ "cant_get_number_of_comments": "No se puede obtener la cantidad de comentarios", "cant_search_people": "No se puede buscar a personas", "cant_search_places": "No se pueden buscar lugares", - "error_adding_assets_to_album": "Error al aÃąadir archivos al ÃĄlbum", - "error_adding_users_to_album": "Error al aÃąadir usuarios al ÃĄlbum", + "error_adding_assets_to_album": "Error al agregar los elementos al ÃĄlbum", + "error_adding_users_to_album": "Error al agregar los usuarios al ÃĄlbum", "error_deleting_shared_user": "Error al eliminar usuario compartido", "error_downloading": "Error al descargar {filename}", "error_hiding_buy_button": "Error al ocultar el botÃŗn de compra", @@ -859,6 +940,7 @@ "failed_to_load_notifications": "Error al cargar las notificaciones", "failed_to_load_people": "Error al cargar a los usuarios", "failed_to_remove_product_key": "No se pudo eliminar la clave del producto", + "failed_to_reset_pin_code": "No se pudo restablecer el cÃŗdigo PIN", "failed_to_stack_assets": "No se pudieron agrupar los archivos", "failed_to_unstack_assets": "Error al desagrupar los archivos", "failed_to_update_notification_status": "Error al actualizar el estado de la notificaciÃŗn", @@ -867,15 +949,16 @@ "paths_validation_failed": "FallÃŗ la validaciÃŗn en {paths, plural, one {# carpeta} other {# carpetas}}", "profile_picture_transparent_pixels": "Las imÃĄgenes de perfil no pueden tener píxeles transparentes. Por favor amplíe y/o mueva la imagen.", "quota_higher_than_disk_size": "Se ha establecido una cuota superior al tamaÃąo del disco", + "something_went_wrong": "Algo saliÃŗ mal", "unable_to_add_album_users": "No se pueden agregar usuarios al ÃĄlbum", "unable_to_add_assets_to_shared_link": "No se pueden agregar archivos al enlace compartido", "unable_to_add_comment": "No se puede agregar comentario", "unable_to_add_exclusion_pattern": "No se puede agregar el patrÃŗn de exclusiÃŗn", - "unable_to_add_import_path": "No se puede aÃąadir la ruta de importaciÃŗn", - "unable_to_add_partners": "No se pueden aÃąadir invitados", + "unable_to_add_import_path": "No se puede agregar la ruta de importaciÃŗn", + "unable_to_add_partners": "No se pueden agregar compaÃąeros", "unable_to_add_remove_archive": "No se puede archivar {archived, select, true {remove asset from} other {add asset to}}", - "unable_to_add_remove_favorites": "AÃąade {favorite, select, true {add asset to} other {remove asset from}} a favoritos", - "unable_to_archive_unarchive": "AÃąade a {archived, select, true {archive} other {unarchive}}", + "unable_to_add_remove_favorites": "{favorite, select, true {No se pudo agregar el elemento a los favoritos} other {No se pudo eliminar el elemento de los favoritos}}", + "unable_to_archive_unarchive": "{archived, select, true {No se pudo agregar el elemento al archivo} other {No se pudo quitar el elemento del archivo}}", "unable_to_change_album_user_role": "No se puede cambiar la funciÃŗn del usuario del ÃĄlbum", "unable_to_change_date": "No se puede cambiar la fecha", "unable_to_change_description": "Imposible cambiar la descripciÃŗn", @@ -951,43 +1034,45 @@ "unable_to_upload_file": "Error al subir el archivo" }, "exif": "EXIF", - "exif_bottom_sheet_description": "Agregar DescripciÃŗn...", + "exif_bottom_sheet_description": "Agregar descripciÃŗnâ€Ļ", + "exif_bottom_sheet_description_error": "Error al actualizar la descripciÃŗn", "exif_bottom_sheet_details": "DETALLES", "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_months": "Edad {months} meses", - "exif_bottom_sheet_person_age_year_months": "Edad 1 aÃąo, {months} meses", - "exif_bottom_sheet_person_age_years": "Edad {years}", + "exif_bottom_sheet_person_add_person": "Agregar nombre", "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 propia responsabilidad!", "experimental_settings_title": "Experimental", - "expire_after": "Expirar despuÊs de", + "expire_after": "Caducar despuÊs de", "expired": "Caducado", - "expires_date": "Expira el {date}", + "expires_date": "Caduca el {date}", "explore": "Explorar", "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", + "export_database": "Exportar Base de Datos", + "export_database_description": "Exportar la Base de Datos SQLite", "extension": "ExtensiÃŗn", "external": "Externo", - "external_libraries": "Bibliotecas Externas", + "external_libraries": "Bibliotecas externas", "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", + "external_network_sheet_info": "Cuando no tengas conexiÃŗn con tu red Wi-Fi preferida, la aplicaciÃŗn se conectarÃĄ al servidor utilizando la primera de las URL siguientes a la que pueda acceder, empezando de arriba hacia abajo", "face_unassigned": "Sin asignar", "failed": "Fallido", "failed_to_authenticate": "Fallo al autentificar", "failed_to_load_assets": "Error al cargar los activos", "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", + "favorite_action_prompt": "{count} agregado(s) a Favoritos", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "No se encontraron elementos marcados como favoritos", "feature_photo_updated": "Foto destacada actualizada", "features": "Características", + "features_in_development": "Funciones en Desarrollo", "features_setting_description": "Administrar las funciones de la aplicaciÃŗn", "file_name": "Nombre de archivo", "file_name_or_extension": "Nombre del archivo o extensiÃŗn", @@ -997,23 +1082,28 @@ "filter_people": "Filtrar personas", "filter_places": "Filtrar lugares", "find_them_fast": "EncuÊntrelos rÃĄpidamente por nombre con la bÃēsqueda", + "first": "Primero", "fix_incorrect_match": "Corregir coincidencia incorrecta", "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", + "forgot_pin_code_question": "ÂŋOlvidaste tu cÃŗdigo PIN?", + "forward": "Avanzar", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Esta funcionalidad carga recursos externos desde Google para poder funcionar.", "general": "General", + "geolocation_instruction_location": "Da click en un asset con coordenadas GPS para usar su ubicacion, o selecciona una ubicacion directamente en el mapa", "get_help": "Solicitar ayuda", "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", + "gps": "GPS", + "gps_missing": "Sin GPS", "grant_permission": "Conceder permiso", - "group_albums_by": "Agrupar albums por...", + "group_albums_by": "Agrupar ÃĄlbumes por...", "group_country": "Agrupar por país", "group_no": "Sin agrupaciÃŗn", "group_owner": "Agrupar por propietario", @@ -1021,8 +1111,11 @@ "group_year": "Agrupar por aÃąo", "haptic_feedback_switch": "Activar respuesta hÃĄptica", "haptic_feedback_title": "Respuesta HÃĄptica", - "has_quota": "Su cuota", - "header_settings_add_header_tip": "AÃąadir cabecera", + "has_quota": "Cuota asignada", + "hash_asset": "Generar hash del archivo", + "hashed_assets": "Archivos con hash generado", + "hashing": "Generando hash", + "header_settings_add_header_tip": "Agregar cabecera", "header_settings_field_validator_msg": "El valor no puede estar vacío", "header_settings_header_name_input": "Nombre de la cabecera", "header_settings_header_value_input": "Valor de la cabecera", @@ -1037,23 +1130,25 @@ "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": "Se aÃąadieron {added} elementos al ÃĄlbum {album}.", + "home_page_add_to_album_success": "Se agregaron {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", + "home_page_archive_err_partner": "No se pueden archivar los elementos de un compaÃąero; omitiendo", "home_page_building_timeline": "Construyendo la línea de tiempo", - "home_page_delete_err_partner": "No se pueden eliminar elementos de un compaÃąero, omitiendo", + "home_page_delete_err_partner": "No se pueden eliminar los elementos de un compaÃąero; omitiendo", "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_favorite_err_partner": "AÃēn no se pueden marcar los elementos de un compaÃąero como favoritos; omitiendo", "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_locked_error_local": "Imposible mover archivos locales a carpeta bloqueada, saltando", - "home_page_locked_error_partner": "Imposible mover los archivos del compaÃąero a carpeta bloqueada, obviando", + "home_page_locked_error_local": "No se pueden mover archivos locales a una carpeta protegida; omitiendo", + "home_page_locked_error_partner": "No se pueden mover los elementos de un compaÃąero a una carpeta protegida; omitiendo", "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", "hour": "Hora", - "id": "ID", + "hours": "Horas", + "id": "Id.", + "idle": "Inactivo", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirÃĄn a Immich", "image": "Imagen", @@ -1079,7 +1174,7 @@ "in_archive": "En archivo", "include_archived": "Incluir archivados", "include_shared_albums": "Incluir ÃĄlbumes compartidos", - "include_shared_partner_assets": "Incluir archivos compartidos de invitados", + "include_shared_partner_assets": "Incluir elementos compartidos por compaÃąeros", "individual_share": "Compartir individualmente", "individual_shares": "Acciones individuales", "info": "InformaciÃŗn", @@ -1111,10 +1206,13 @@ "language_no_results_title": "No se han encontrado idiomas", "language_search_hint": "Buscar idiomas...", "language_setting_description": "Selecciona tu idioma preferido", + "large_files": "Archivos Grandes", + "last": "Último", "last_seen": "Ultima vez visto", "latest_version": "Última versiÃŗn", "latitude": "Latitud", "leave": "Abandonar", + "leave_album": "Abandonar ÃĄlbum", "lens_model": "Modelo de objetivo", "let_others_respond": "Permitir que otros respondan", "level": "Nivel", @@ -1126,16 +1224,20 @@ "library_page_sort_created": "Creado mÃĄs recientemente", "library_page_sort_last_modified": "Última modificaciÃŗn", "library_page_sort_title": "Título del ÃĄlbum", + "licenses": "Licencias", "light": "Claro", + "like": "Me gusta", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", - "link_options": "Opciones de enlace", "link_to_oauth": "Enlace a OAuth", "linked_oauth_account": "Cuenta OAuth vinculada", - "list": "Listar", + "list": "Lista", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la bÃēsqueda", - "local_asset_cast_failed": "No se puede emitir un activo que no estÃĄ cargado en el servidor", + "local": "Local", + "local_asset_cast_failed": "No es posible transmitir un recurso que no estÃĄ subido al servidor", + "local_assets": "Archivos Locales", + "local_media_summary": "Resumen de Medios Locales", "local_network": "Red local", "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", @@ -1146,7 +1248,8 @@ "location_picker_longitude_error": "Introduce una longitud vÃĄlida", "location_picker_longitude_hint": "Introduce tu longitud aquí", "lock": "Bloquear", - "locked_folder": "Carpeta bloqueada", + "locked_folder": "Carpeta protegida", + "log_detail_title": "Detalle del registro", "log_out": "Cerrar sesiÃŗn", "log_out_all_devices": "Cerrar sesiÃŗn en todos los dispositivos", "logged_in_as": "SesiÃŗn iniciada como {user}", @@ -1154,17 +1257,17 @@ "logged_out_device": "Dispositivo desconectado", "login": "Inicio de sesiÃŗn", "login_disabled": "El inicio de sesiÃŗn ha sido desactivado", - "login_form_api_exception": "ExcepciÃŗn producida por API. Por favor, verifica el URL del servidor e intÊntalo de nuevo.", + "login_form_api_exception": "ExcepciÃŗn producida por API. Por favor, comprueba el URL del servidor e intÊntalo de nuevo.", "login_form_back_button_text": "AtrÃĄs", "login_form_email_hint": "tucorreo@correo.com", "login_form_endpoint_hint": "http://tu-ip-de-servidor:puerto", - "login_form_endpoint_url": "URL del servidor", + "login_form_endpoint_url": "Enlace del punto de acceso (endpoint) del servidor", "login_form_err_http": "Por favor, especifique http:// o https://", "login_form_err_invalid_email": "Correo electrÃŗnico no vÃĄlido", "login_form_err_invalid_url": "URL no vÃĄlida", "login_form_err_leading_whitespace": "Espacio en blanco inicial", "login_form_err_trailing_whitespace": "Espacio en blanco al final", - "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_config": "Error al iniciar sesiÃŗn con OAuth, comprueba 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 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.", @@ -1177,6 +1280,7 @@ "login_password_changed_success": "ContraseÃąa cambiado con Êxito", "logout_all_device_confirmation": "ÂŋEstÃĄs seguro de que quieres cerrar sesiÃŗn en todos los dispositivos?", "logout_this_device_confirmation": "ÂŋEstÃĄs seguro de que quieres cerrar sesiÃŗn en este dispositivo?", + "logs": "Registros", "longitude": "Longitud", "look": "Mirar", "loop_videos": "Vídeos en bucle", @@ -1184,16 +1288,16 @@ "main_branch_warning": "EstÃĄ utilizando una versiÃŗn de desarrollo; ÂĄle recomendamos encarecidamente que utilice una versiÃŗn de lanzamiento!", "main_menu": "MenÃē principal", "make": "Marca", + "manage_geolocation": "Administrar ubicaciÃŗn", "manage_shared_links": "Administrar enlaces compartidos", - "manage_sharing_with_partners": "Administrar el uso compartido con invitados", + "manage_sharing_with_partners": "Gestionar el uso compartido con compaÃąeros", "manage_the_app_settings": "Administrar la configuraciÃŗn de la aplicaciÃŗn", "manage_your_account": "Gestiona tu cuenta", "manage_your_api_keys": "Administre sus claves API", "manage_your_devices": "Administre sus dispositivos conectados", "manage_your_oauth_connection": "Administra tu conexiÃŗn OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, =0 {No hay fotos en esta ÃĄrea} one {# foto} other {# fotos}}", "map_cannot_get_user_location": "No se pudo obtener la posiciÃŗn del usuario", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Usar esta ubicaciÃŗn", @@ -1201,25 +1305,25 @@ "map_location_service_disabled_title": "Servicios de ubicaciÃŗn desactivados", "map_marker_for_images": "Marcador de mapa para imÃĄgenes tomadas en {city}, {country}", "map_marker_with_image": "Marcador de mapa con imagen", - "map_no_assets_in_bounds": "No hay fotos en esta zona", "map_no_location_permission_content": "Se necesitan permisos de ubicaciÃŗn para mostrar elementos de tu ubicaciÃŗn actual. ÂŋDeseas activarlos ahora?", "map_no_location_permission_title": "Permisos de ubicaciÃŗn denegados", - "map_settings": "Ajustes mapa", + "map_settings": "Ajustes del mapa", "map_settings_dark_mode": "Modo oscuro", "map_settings_date_range_option_day": "Últimas 24 horas", "map_settings_date_range_option_days": "Últimos {days} días", "map_settings_date_range_option_year": "Último aÃąo", "map_settings_date_range_option_years": "Últimos {years} aÃąos", - "map_settings_dialog_title": "Ajustes mapa", + "map_settings_dialog_title": "Ajustes del mapa", "map_settings_include_show_archived": "Incluir archivados", - "map_settings_include_show_partners": "Incluir Parejas", + "map_settings_include_show_partners": "Incluir compaÃąeros", "map_settings_only_show_favorites": "Mostrar solo favoritas", "map_settings_theme_settings": "Apariencia del Mapa", "map_zoom_to_see_photos": "Alejar para ver fotos", - "mark_all_as_read": "Marcar todos como leídos", + "mark_all_as_read": "Marcar todas como leídas", "mark_as_read": "Marcar como leído", "marked_all_as_read": "Todos marcados como leídos", "matches": "Coincidencias", + "matching_assets": "Elementos Coincidentes", "media_type": "Tipo de medio", "memories": "Recuerdos", "memories_all_caught_up": "Puesto al día", @@ -1238,24 +1342,30 @@ "merged_people_count": "Fusionada {count, plural, one {# persona} other {# personas}}", "minimize": "Minimizar", "minute": "Minuto", - "missing": "Perdido", + "minutes": "Minutos", + "missing": "Faltante", "model": "Modelo", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM a", "more": "Mas", "move": "Mover", - "move_off_locked_folder": "Mover fuera de la carpeta protegida", + "move_off_locked_folder": "Sacar de la carpeta protegida", + "move_to_lock_folder_action_prompt": "{count} agregado(s) a la carpeta protegida", "move_to_locked_folder": "Mover a la carpeta protegida", - "move_to_locked_folder_confirmation": "Estas fotos y vídeos serÃĄn eliminados de todos los ÃĄlbumes y sÃŗlo podrÃĄn ser vistos desde la carpeta protegida", + "move_to_locked_folder_confirmation": "Estas fotos y vídeos se eliminarÃĄn de todos los ÃĄlbumes; solo se podrÃĄn ver en la carpeta protegida", "moved_to_archive": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a archivo", "moved_to_library": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a biblioteca", "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 editar la ubicaciÃŗn de activos de solo lectura, omitiendo", "mute_memories": "Silenciar Recuerdos", - "my_albums": "Mis albums", + "my_albums": "Mis ÃĄlbumes", "name": "Nombre", "name_or_nickname": "Nombre o apodo", + "network_requirement_photos_upload": "Usar datos mÃŗviles para crear una copia de seguridad de las fotos", + "network_requirement_videos_upload": "Usar datos mÃŗviles para crear una copia de seguridad de los videos", + "network_requirements": "Requisitos de red", + "network_requirements_updated": "Los requisitos de red han cambiado, reiniciando la cola de copias de seguridad", "networking_settings": "Red", "networking_subtitle": "Configuraciones de acceso por URL al servidor", "never": "Nunca", @@ -1264,7 +1374,8 @@ "new_password": "Nueva contraseÃąa", "new_person": "Nueva persona", "new_pin_code": "Nuevo PIN", - "new_pin_code_subtitle": "Esta es tu primera vez accediendo a la carpeta protegida. Crea un PIN seguro para acceder a esta pÃĄgina", + "new_pin_code_subtitle": "Esta es la primera vez que accedes a la carpeta protegida. Crea un cÃŗdigo PIN seguro para acceder a esta pÃĄgina", + "new_timeline": "Nueva Línea de tiempo", "new_user_created": "Nuevo usuario creado", "new_version_available": "NUEVA VERSIÓN DISPONIBLE", "newest_first": "El mÃĄs reciente primero", @@ -1277,23 +1388,29 @@ "no_archived_assets_message": "Archive fotos y videos para ocultarlos de su vista de Fotos", "no_assets_message": "HAZ CLIC PARA SUBIR TU PRIMERA FOTO", "no_assets_to_show": "No hay elementos a mostrar", - "no_cast_devices_found": "Dispositivos de difusiÃŗn no encontrados", + "no_cast_devices_found": "No se encontraron dispositivos de transmisiÃŗn", + "no_checksum_local": "Suma de verificaciÃŗn no disponible. No se pueden obtener los elementos locales", + "no_checksum_remote": "Suma de verificaciÃŗn no disponible. No se puede obtener el elemento remoto", "no_duplicates_found": "No se encontraron duplicados.", "no_exif_info_available": "No hay informaciÃŗn exif disponible", "no_explore_results_message": "Sube mÃĄs fotos para explorar tu colecciÃŗn.", "no_favorites_message": "Agregue favoritos para encontrar rÃĄpidamente sus mejores fotos y videos", "no_libraries_message": "Crea una biblioteca externa para ver tus fotos y vídeos", - "no_locked_photos_message": "Fotos y vídeos en la carpeta protegida estÃĄn ocultos y no se mostrarÃĄn en las bÃēsquedas de tu librería.", + "no_local_assets_found": "No se encontraron elementos locales con esta suma de comprobaciÃŗn", + "no_locked_photos_message": "Las fotos y los vídeos de la carpeta protegida se mantienen ocultos; no aparecerÃĄn cuando veas o busques elementos en tu biblioteca.", "no_name": "Sin nombre", "no_notifications": "Ninguna notificaciÃŗn", "no_people_found": "No se encontraron personas coincidentes", "no_places": "Sin lugares", + "no_remote_assets_found": "No se encontraron elementos remotos con esta suma de comprobaciÃŗn", "no_results": "Sin resultados", "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", + "no_uploads_in_progress": "No hay cargas en progreso", + "not_available": "N/D", "not_in_any_album": "Sin ÃĄlbum", "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", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos que ya se subieron, ejecute la", "notes": "Notas", "nothing_here_yet": "Sin nada aÃēn", "notification_permission_dialog_content": "Para activar las notificaciones, ve a ConfiguraciÃŗn y selecciona permitir.", @@ -1306,13 +1423,14 @@ "oauth": "OAuth", "official_immich_resources": "Recursos oficiales de Immich", "offline": "Desconectado", + "offset": "DesviaciÃŗn", "ok": "Sí", "oldest_first": "Los mÃĄs antiguos primero", "on_this_device": "En este dispositivo", "onboarding": "Incorporando", "onboarding_locale_description": "Selecciona tu idioma preferido. PodrÃĄs cambiarlo despuÊs desde tu configuraciÃŗn.", - "onboarding_privacy_description": "Las siguientes funciones (opcionales) dependen de servicios externos y pueden desactivarse en cualquier momento desde los ajustes.", - "onboarding_server_welcome_description": "Empecemos a configurar tu instancia con algunos ajustes comunes.", + "onboarding_privacy_description": "Las siguientes funciones, que son opcionales, utilizan servicios externos. Puedes deshabilitarlas mediante los ajustes en cualquier momento.", + "onboarding_server_welcome_description": "Empecemos a configurar tu instancia fijando algunos ajustes comunes.", "onboarding_theme_description": "Elija un color de tema para su instancia. Puedes cambiar esto mÃĄs tarde en tu configuraciÃŗn.", "onboarding_user_welcome_description": "ÂĄEmpecemos!", "onboarding_welcome_user": "Bienvenido, {user}", @@ -1324,27 +1442,30 @@ "open_the_search_filters": "Abre los filtros de bÃēsqueda", "options": "Opciones", "or": "o", + "organize_into_albums": "Organizar en ÃĄlbumes", + "organize_into_albums_description": "AÃąade fotos existentes en ÃĄlbumes usando la configuraciÃŗn actual de sincronizaciÃŗn", "organize_your_library": "Organiza tu biblioteca", "original": "original", "other": "Otro", - "other_devices": "Otro dispositivo", + "other_devices": "Otros dispositivos", + "other_entities": "Otras entidades", "other_variables": "Otras variables", - "owned": "Propio", + "owned": "Propios", "owner": "Propietario", - "partner": "Invitado", - "partner_can_access": "{partner} puede acceder", + "partner": "CompaÃąero", + "partner_can_access": "{partner} tiene acceso", "partner_can_access_assets": "Todas tus fotos y vídeos excepto los Archivados y Eliminados", "partner_can_access_location": "UbicaciÃŗn donde fueron realizadas tus fotos", "partner_list_user_photos": "Fotos de {user}", "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": "No se pudo aÃąadir el socio", + "partner_page_partner_add_failed": "No se pudo agregar el compaÃąero", "partner_page_select_partner": "Seleccionar compaÃąero", "partner_page_shared_to_title": "Compartido con", "partner_page_stop_sharing_content": "{partner} ya no podrÃĄ acceder a tus fotos.", - "partner_sharing": "Compartir con invitados", - "partners": "Invitados", + "partner_sharing": "Compartir con compaÃąeros", + "partners": "CompaÃąeros", "password": "ContraseÃąa", "password_does_not_match": "Las contraseÃąas no coinciden", "password_required": "ContraseÃąa requerida", @@ -1382,11 +1503,14 @@ "permission_onboarding_permission_limited": "Permiso limitado. Para permitir que Immich haga copia de seguridad y gestione toda tu colecciÃŗn de galería, concede permisos de fotos y videos en ConfiguraciÃŗn.", "permission_onboarding_request": "Immich requiere permiso para ver tus fotos y videos.", "person": "Persona", + "person_age_months": "hace {months, plural, one {# mes} other {# meses}}", + "person_age_year_months": "1 aÃąo y {months, plural, one {# mes} other {# meses}}", + "person_age_years": "{years, plural, other {# aÃąos}}", "person_birthdate": "Nacido el {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", "photo_shared_all_users": "Parece que compartiste tus fotos con todos los usuarios o no tienes ningÃēn usuario con quien compartirlas.", "photos": "Fotos", - "photos_and_videos": "Fotos y Videos", + "photos_and_videos": "Fotos y Vídeos", "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Fotos}}", "photos_from_previous_years": "Fotos de aÃąos anteriores", "pick_a_location": "Elige una ubicaciÃŗn", @@ -1405,6 +1529,7 @@ "port": "Puerto", "preferences_settings_subtitle": "Configuraciones de la aplicaciÃŗn", "preferences_settings_title": "Preferencias", + "preparing": "Preparando", "preset": "Preestablecido", "preview": "Posterior", "previous": "Anterior", @@ -1421,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "La app estÃĄ desactualizada. Por favor actualiza a la Ãēltima versiÃŗn menor.", "profile_drawer_client_server_up_to_date": "Cliente y Servidor estÃĄn actualizados", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Modo Solo lectura habilitado. MantÊn pulsado el icono del avatar del usuario para salir.", "profile_drawer_server_out_of_date_major": "El servidor estÃĄ desactualizado. Por favor actualiza a la Ãēltima versiÃŗn principal.", "profile_drawer_server_out_of_date_minor": "El servidor estÃĄ desactualizado. Por favor actualiza a la Ãēltima versiÃŗn menor.", "profile_image_of_user": "Foto de perfil de {user}", @@ -1459,12 +1585,17 @@ "purchase_server_description_2": "Estado del soporte", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "La clave del producto del servidor la administra el administrador", + "query_asset_id": "Consultar ID de elemento", + "queue_status": "Poniendo en cola {count}/{total}", "rating": "ValoraciÃŗn", "rating_clear": "Borrar calificaciÃŗn", "rating_count": "{count, plural, one {# estrella} other {# estrellas}}", "rating_description": "Mostrar la clasificaciÃŗn exif en el panel de informaciÃŗn", "reaction_options": "Opciones de reacciÃŗn", "read_changelog": "Leer registro de cambios", + "readonly_mode_disabled": "Modo Solo lectura deshabilitado", + "readonly_mode_enabled": "Modo Solo lectura habilitado", + "ready_for_upload": "Listo para subir", "reassign": "Reasignar", "reassigned_assets_to_existing_person": "Reasignado {count, plural, one {# elemento} other {# elementos}} a {name, select, null {una persona existente} other {{name}}}", "reassigned_assets_to_new_person": "Reasignado {count, plural, one {# elemento} other {# elementos}} a un nuevo usuario", @@ -1487,6 +1618,9 @@ "refreshing_faces": "Recargando caras", "refreshing_metadata": "Recargando metadatos", "regenerating_thumbnails": "Recargando miniaturas", + "remote": "Remoto", + "remote_assets": "Elementos remotos", + "remote_media_summary": "Resumen de Medios Remotos", "remove": "Eliminar", "remove_assets_album_confirmation": "ÂŋEstÃĄs seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del ÃĄlbum?", "remove_assets_shared_link_confirmation": "ÂŋEstÃĄs seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del enlace compartido?", @@ -1494,12 +1628,14 @@ "remove_custom_date_range": "Eliminar intervalo de fechas personalizado", "remove_deleted_assets": "Eliminar archivos sin conexiÃŗn", "remove_from_album": "Eliminar del ÃĄlbum", + "remove_from_album_action_prompt": "{count} eliminado del ÃĄlbum", "remove_from_favorites": "Quitar de favoritos", + "remove_from_lock_folder_action_prompt": "{count} eliminado(s) de la carpeta protegida", "remove_from_locked_folder": "Eliminar de la carpeta protegida", - "remove_from_locked_folder_confirmation": "ÂŋEstÃĄs seguro de que deseas mover estas fotos y vídeos fuera de la carpeta protegida? SerÃĄn visibles en tu biblioteca.", + "remove_from_locked_folder_confirmation": "ÂŋSeguro que deseas sacar estas fotos y vídeos de la carpeta protegida? Si continÃēas, los elementos serÃĄn visibles en tu biblioteca.", "remove_from_shared_link": "Eliminar desde enlace compartido", - "remove_memory": "Quitar memoria", - "remove_photo_from_memory": "Quitar foto de esta memoria", + "remove_memory": "Quitar recuerdo", + "remove_photo_from_memory": "Quitar foto de este recuerdo", "remove_tag": "Quitar etiqueta", "remove_url": "Eliminar URL", "remove_user": "Eliminar usuario", @@ -1507,8 +1643,8 @@ "removed_from_archive": "Eliminado del archivo", "removed_from_favorites": "Eliminado de favoritos", "removed_from_favorites_count": "{count, plural, other {Eliminados #}} de favoritos", - "removed_memory": "Memoria eliminada", - "removed_photo_from_memory": "Se ha eliminado la foto de la memoria", + "removed_memory": "Recuerdo eliminado", + "removed_photo_from_memory": "Foto eliminada del recuerdo", "removed_tagged_assets": "Etiqueta eliminada de {count, plural, one {# activo} other {# activos}}", "rename": "Renombrar", "repair": "Reparar", @@ -1518,23 +1654,33 @@ "require_password": "ContraseÃąa requerida", "require_user_to_change_password_on_first_login": "Requerir que el usuario cambie la contraseÃąa en el primer inicio de sesiÃŗn", "rescan": "Volver a escanear", - "reset": "Reiniciar", + "reset": "Restablecer", "reset_password": "Restablecer la contraseÃąa", "reset_people_visibility": "Restablecer la visibilidad de las personas", "reset_pin_code": "Restablecer PIN", + "reset_pin_code_description": "Si olvidaste tu cÃŗdigo PIN, puedes comunicarte con el administrador del servidor para restablecerlo", + "reset_pin_code_success": "CÃŗdigo PIN restablecido correctamente", + "reset_pin_code_with_password": "Siempre puedes restablecer tu cÃŗdigo PIN usando tu contraseÃąa", + "reset_sqlite": "Restablecer la Base de Datos SQLite", + "reset_sqlite_confirmation": "ÂŋEstÃĄs seguro que deseas restablecer la base de datos SQLite? DeberÃĄs cerrar sesiÃŗn y volver a iniciarla para resincronizar los datos", + "reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite", "reset_to_default": "Restablecer los valores predeterminados", "resolve_duplicates": "Resolver duplicados", "resolved_all_duplicates": "Todos los duplicados resueltos", "restore": "Restaurar", "restore_all": "Restaurar todo", + "restore_trash_action_prompt": "{count} restaurado de la papelera", "restore_user": "Restaurar usuario", "restored_asset": "Archivo restaurado", "resume": "Continuar", + "resume_paused_jobs": "Reanudar {count, plural, one {# tarea en pausa} other {# tareas en pausa}}", "retry_upload": "Reintentar subida", "review_duplicates": "Revisar duplicados", + "review_large_files": "Revisar archivos grandes", "role": "Rol", "role_editor": "Editor", "role_viewer": "Visor", + "running": "En ejecuciÃŗn", "save": "Guardar", "save_to_gallery": "Guardado en la galería", "saved_api_key": "Clave API guardada", @@ -1547,7 +1693,7 @@ "scan_settings": "ConfiguraciÃŗn de escaneo", "scanning_for_album": "Buscando ÃĄlbum...", "search": "Buscar", - "search_albums": "Buscar ÃĄlbums", + "search_albums": "Buscar ÃĄlbumes", "search_by_context": "Buscar por contexto", "search_by_description": "Buscar por descripciÃŗn", "search_by_description_example": "Día de senderismo en Sapa", @@ -1558,17 +1704,17 @@ "search_city": "Buscar ciudad...", "search_country": "Buscar país...", "search_filter_apply": "Aplicar filtros", - "search_filter_camera_title": "Elige tipo de cÃĄmara", + "search_filter_camera_title": "Elegir tipo de cÃĄmara", "search_filter_date": "Fecha", "search_filter_date_interval": "{start} al {end}", - "search_filter_date_title": "Selecciona un intervalo de fechas", + "search_filter_date_title": "Seleccionar un intervalo de fechas", "search_filter_display_option_not_in_album": "No en ÃĄlbum", "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", - "search_filter_media_type_title": "Selecciona el tipo de archivo", + "search_filter_media_type_title": "Seleccionar el tipo de archivo", "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", @@ -1591,23 +1737,23 @@ "search_people": "Buscar personas", "search_places": "Buscar lugar", "search_rating": "Buscar por calificaciÃŗn...", - "search_result_page_new_search_hint": "Nueva Busqueda", - "search_settings": "Ajustes de la bÃēsqueda", + "search_result_page_new_search_hint": "Nueva BÃēsqueda", + "search_settings": "Ajustes de bÃēsqueda", "search_state": "Buscar regiÃŗn/estado...", "search_suggestion_list_smart_search_hint_1": "La bÃēsqueda inteligente estÃĄ habilitada por defecto, para buscar metadatos utiliza esta sintaxis ", "search_suggestion_list_smart_search_hint_2": "m:tu-tÊrmino-de-bÃēsqueda", - "search_tags": "Buscando etiquetas...", + "search_tags": "Buscar etiquetas...", "search_timezone": "Buscar zona horaria...", "search_type": "Tipo de bÃēsqueda", "search_your_photos": "Busca tus fotos", "searching_locales": "Buscando sitios...", "second": "Segundo", "see_all_people": "Ver todas las personas", - "select": "Selecciona", + "select": "Seleccionar", "select_album_cover": "Seleccionar portada del ÃĄlbum", "select_all": "Seleccionar todo", "select_all_duplicates": "Seleccionar todos los duplicados", - "select_all_in": "Selecciona todos en {group}", + "select_all_in": "Seleccionar todos en {group}", "select_avatar_color": "Seleccionar color del avatar", "select_face": "Seleccionar cara", "select_featured_photo": "Seleccionar foto principal", @@ -1621,11 +1767,12 @@ "select_user_for_sharing_page_err_album": "Fallo al crear el ÃĄlbum", "selected": "Seleccionado", "selected_count": "{count, plural, one {# seleccionado} other {# seleccionados}}", + "selected_gps_coordinates": "Coordenadas GPS seleccionadas", "send_message": "Enviar mensaje", "send_welcome_email": "Enviar correo de bienvenida", "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_info_box_server_url": "Enlace del servidor", "server_offline": "Servidor desconectado", "server_online": "Servidor en línea", "server_privacy": "Privacidad del Servidor", @@ -1638,7 +1785,7 @@ "set_date_of_birth": "Establecer fecha de nacimiento", "set_profile_picture": "Establecer foto de perfil", "set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa", - "set_stack_primary_asset": "Establecer como activo principal", + "set_stack_primary_asset": "Establecer como recurso principal", "setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeÃąa, luego carga la vista previa de tamaÃąo mediano (si estÃĄ habilitada), finalmente carga la original (si estÃĄ habilitada).", "setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resoluciÃŗn original (ÂĄmuy grande!). Deshabilitar para reducir el consumo de datos (de red y cachÊ).", "setting_image_viewer_original_title": "Cargar imagen original", @@ -1666,11 +1813,12 @@ "settings_saved": "Ajustes guardados", "setup_pin_code": "Establecer un PIN", "share": "Compartir", + "share_action_prompt": "{count} recursos compartidos", "share_add_photos": "Agregar fotos", "share_assets_selected": "{count} seleccionado(s)", "share_dialog_preparing": "Preparando...", "share_link": "Compartir Enlace", - "shared": "Compartido", + "shared": "Compartidos", "shared_album_activities_input_disable": "Los comentarios estÃĄn deshabilitados", "shared_album_activity_remove_content": "ÂŋDeseas eliminar esta actividad?", "shared_album_activity_remove_title": "Eliminar Actividad", @@ -1687,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Copiado al portapapeles", "shared_link_clipboard_text": "Enlace: {link}\nContraseÃąa: {password}", "shared_link_create_error": "Error creando el enlace compartido", + "shared_link_custom_url_description": "Acceder a este enlace compartido con una URL personalizada", "shared_link_edit_description_hint": "Introduce la descripciÃŗn del enlace", "shared_link_edit_expire_after_option_day": "1 día", "shared_link_edit_expire_after_option_days": "{count} días", @@ -1712,21 +1861,22 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrar enlaces compartidos", "shared_link_options": "Opciones de enlaces compartidos", + "shared_link_password_description": "Requerir una contraseÃąa para acceder a este enlace compartido", "shared_links": "Enlaces compartidos", "shared_links_description": "Comparte fotos y vídeos con un enlace", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos y vídeos compartidos.}}", "shared_with_me": "Compartidos conmigo", "shared_with_partner": "Compartido con {partner}", - "sharing": "Compartido", + "sharing": "Compartidos", "sharing_enter_password": "Por favor, introduce la contraseÃąa para ver esta pÃĄgina.", "sharing_page_album": "Álbumes compartidos", "sharing_page_description": "Crea ÃĄlbumes compartidos para compartir fotos y vídeos con las personas de tu red.", "sharing_page_empty_list": "LISTA VACIA", "sharing_sidebar_description": "Muestra un enlace a \"Compartido\" en el menÃē lateral", "sharing_silver_appbar_create_shared_album": "Crear un ÃĄlbum compartido", - "sharing_silver_appbar_share_partner": "Compartir con el compaÃąero", + "sharing_silver_appbar_share_partner": "Compartir con compaÃąero", "shift_to_permanent_delete": "presiona ⇧ para eliminar permanentemente el archivo", - "show_album_options": "Mostrar ajustes del ÃĄlbum", + "show_album_options": "Mostrar opciones del ÃĄlbum", "show_albums": "Mostrar ÃĄlbumes", "show_all_people": "Mostrar todas las personas", "show_and_hide_people": "Mostrar y ocultar personas", @@ -1746,6 +1896,7 @@ "show_slideshow_transition": "Mostrar la transiciÃŗn de las diapositivas", "show_supporter_badge": "Insignia de colaborador", "show_supporter_badge_description": "Mostrar una insignia de colaborador", + "show_text_search_menu": "Mostrar el menÃē de bÃēsqueda", "shuffle": "Modo aleatorio", "sidebar": "Barra lateral", "sidebar_display_description": "Muestra un enlace a la vista en la barra lateral", @@ -1755,18 +1906,20 @@ "skip_to_content": "Saltar al contenido", "skip_to_folders": "Ir a las carpetas", "skip_to_tags": "Ir a las etiquetas", - "slideshow": "Diapositivas", + "slideshow": "Pase de diapositivas", "slideshow_settings": "Ajustes de diapositivas", - "sort_albums_by": "Ordenar ÃĄlbumes por...", + "sort_albums_by": "Ordenar ÃĄlbumes porâ€Ļ", "sort_created": "Fecha de creaciÃŗn", "sort_items": "NÃēmero de archivos", "sort_modified": "Fecha de modificaciÃŗn", + "sort_newest": "Foto mÃĄs nueva", "sort_oldest": "Foto mÃĄs antigua", "sort_people_by_similarity": "Ordenar personas por similitud", "sort_recent": "Foto mÃĄs reciente", "sort_title": "Título", - "source": "Origen", + "source": "Fuente", "stack": "Apilar", + "stack_action_prompt": "{count} apilados", "stack_duplicates": "Apilar duplicados", "stack_select_one_photo": "Selecciona una imagen principal para la pila", "stack_selected_photos": "Apilar fotos seleccionadas", @@ -1774,27 +1927,33 @@ "stacktrace": "Seguimiento de pila", "start": "Inicio", "start_date": "Fecha de inicio", + "start_date_before_end_date": "Fecha de inicio debe ser antes de fecha final", "state": "Estado", "status": "Estado", - "stop_casting": "Parar difusiÃŗn", + "stop_casting": "Detener transmisiÃŗn", "stop_motion_photo": "Parar foto en movimiento", "stop_photo_sharing": "ÂŋDejar de compartir tus fotos?", "stop_photo_sharing_description": "{partner} ya no podrÃĄ acceder a tus fotos.", "stop_sharing_photos_with_user": "Deja de compartir tus fotos con este usuario", "storage": "Espacio de almacenamiento", "storage_label": "Etiqueta de almacenamiento", - "storage_quota": "Cuota de Almacenamiento", + "storage_quota": "Cuota de almacenamiento", "storage_usage": "{used} de {available} en uso", "submit": "Enviar", + "success": "Éxito", "suggestions": "Sugerencias", "sunrise_on_the_beach": "Amanecer en la playa", "support": "Soporte", "support_and_feedback": "Soporte y comentarios", - "support_third_party_description": "Su instalaciÃŗn de immich fue empaquetada por un tercero. Los problemas que experimenta pueden ser causados por ese paquete, así que por favor plantee problemas con ellos en primer lugar usando los enlaces inferiores.", + "support_third_party_description": "Esta instalaciÃŗn de Immich fue empaquetada por un tercero. Los problemas actuales pueden ser ocasionados por ese paquete; por favor, discuta sus inconvenientes con el empaquetador antes de usar los enlaces de abajo.", "swap_merge_direction": "Alternar direcciÃŗn de mezcla", "sync": "Sincronizar", "sync_albums": "Sincronizar ÃĄlbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los ÃĄlbumes seleccionados a respaldar", + "sync_local": "SincronizaciÃŗn Local", + "sync_remote": "SincronizaciÃŗn Remota", + "sync_status": "Estado de la sincronizaciÃŗn", + "sync_status_subtitle": "Ver y gestionar el estado de la sincronizaciÃŗn", "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los ÃĄlbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", @@ -1805,6 +1964,7 @@ "tag_updated": "Etiqueta actualizada: {tag}", "tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}", "tags": "Etiquetas", + "tap_to_run_job": "Toca para ejecutar la tarea", "template": "Plantilla", "theme": "Tema", "theme_selection": "SelecciÃŗn de tema", @@ -1831,12 +1991,15 @@ "to_change_password": "Cambiar contraseÃąa", "to_favorite": "A los favoritos", "to_login": "Iniciar SesiÃŗn", + "to_multi_select": "para multi selecciÃŗn", "to_parent": "Ir a los padres", + "to_select": "para seleccionar", "to_trash": "Descartar", "toggle_settings": "Alternar ajustes", "total": "Total", "total_usage": "Uso total", "trash": "Papelera", + "trash_action_prompt": "{count} movidos a la papelera", "trash_all": "Descartar todo", "trash_count": "Descartar {count, number}", "trash_delete_asset": "Borrar/Eliminar archivo", @@ -1850,18 +2013,21 @@ "trash_page_select_assets_btn": "Seleccionar elementos", "trash_page_title": "Papelera ({count})", "trashed_items_will_be_permanently_deleted_after": "Los elementos en la papelera serÃĄn eliminados permanentemente tras {days, plural, one {# día} other {# días}}.", + "troubleshoot": "Solucionar problemas", "type": "Tipo", "unable_to_change_pin_code": "No se ha podido cambiar el PIN", "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", + "unarchive_action_prompt": "{count} eliminados del archivo", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", "undo": "Deshacer", "unfavorite": "Retirar favorito", + "unfavorite_action_prompt": "{count} eliminados de favoritos", "unhide_person": "Mostrar persona", "unknown": "Desconocido", "unknown_country": "País desconocido", "unknown_year": "AÃąo desconocido", - "unlimited": "Ilimitado", + "unlimited": "Sin límites", "unlink_motion_video": "Desvincular vídeo en movimiento", "unlink_oauth": "Desvincular OAuth", "unlinked_oauth_account": "Cuenta OAuth desconectada", @@ -1874,15 +2040,21 @@ "unselect_all_duplicates": "Deseleccionar todos los duplicados", "unselect_all_in": "Deselecciona todos en {group}", "unstack": "Desapilar", + "unstack_action_prompt": "{count} desapilado(s)", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", + "untagged": "Sin etiqueta", "up_next": "A continuaciÃŗn", + "update_location_action_prompt": "Actualiza la ubicaciÃŗn de {count} assets seleccionados con:", "updated_at": "Actualizado", "updated_password": "ContraseÃąa actualizada", "upload": "Subir", + "upload_action_prompt": "{count} en cola para carga", "upload_concurrency": "Subidas simultÃĄneas", + "upload_details": "Cargar Detalles", "upload_dialog_info": "ÂŋQuieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la pÃĄgina para ver los nuevos recursos de la subida.", + "upload_finished": "Carga finalizada", "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", @@ -1891,6 +2063,7 @@ "upload_success": "Subida realizada correctamente, actualice la pÃĄgina para ver los nuevos recursos de subida.", "upload_to_immich": "Subir a Immich ({count})", "uploading": "Subiendo", + "uploading_media": "Subiendo medios", "url": "URL", "usage": "Uso", "use_biometric": "Uso biomÊtrico", @@ -1898,7 +2071,7 @@ "use_custom_date_range": "Usa un intervalo de fechas personalizado", "user": "Usuario", "user_has_been_deleted": "Este usuario ha sido eliminado.", - "user_id": "ID de usuario", + "user_id": "Id. de usuario", "user_liked": "{user} le gustÃŗ {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", "user_pin_code_settings": "PIN", "user_pin_code_settings_description": "Gestione su PIN", @@ -1911,6 +2084,7 @@ "user_usage_stats_description": "Ver estadísticas de uso de la cuenta", "username": "Nombre de usuario", "users": "Usuarios", + "users_added_to_album_count": "{count, plural, one {# usuario agregado} other {# usuarios agregados}} al ÃĄlbum", "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduce una URL vÃĄlida", @@ -1929,13 +2103,15 @@ "view_album": "Ver Álbum", "view_all": "Ver todas", "view_all_users": "Mostrar todos los usuarios", - "view_in_timeline": "Mostrar en la línea de tiempo", + "view_details": "Ver Detalles", + "view_in_timeline": "Ver en la línea de tiempo", "view_link": "Ver enlace", "view_links": "Mostrar enlaces", "view_name": "Ver", "view_next_asset": "Mostrar siguiente elemento", "view_previous_asset": "Mostrar elemento anterior", "view_qr_code": "Ver cÃŗdigo QR", + "view_similar_photos": "Ver fotografías similares", "view_stack": "Ver Pila", "view_user": "Ver Usuario", "viewer_remove_from_stack": "Quitar de la pila", @@ -1954,5 +2130,6 @@ "yes": "Sí", "you_dont_have_any_shared_links": "No tienes ningÃēn enlace compartido", "your_wifi_name": "El nombre de tu Wi-Fi", - "zoom_image": "Acercar Imagen" + "zoom_image": "Acercar Imagen", + "zoom_to_bounds": "Ajustar a los límites" } diff --git a/i18n/et.json b/i18n/et.json index a2b17c6138..d451606dae 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -14,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_birthday": "Lisa sÃŧnnipäev", "add_endpoint": "Lisa lÃĩpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", @@ -27,46 +28,57 @@ "add_to_album": "Lisa albumisse", "add_to_album_bottom_sheet_added": "Lisatud albumisse {album}", "add_to_album_bottom_sheet_already_exists": "On juba albumis {album}", + "add_to_album_bottom_sheet_some_local_assets": "KÃĩiki lokaalseid Ãŧksuseid ei Ãĩnnestunud albumisse lisada", + "add_to_album_toggle": "Muuda albumi {album} valikut", + "add_to_albums": "Lisa albumitesse", + "add_to_albums_count": "Lisa albumitesse ({count})", "add_to_shared_album": "Lisa jagatud albumisse", "add_url": "Lisa URL", "added_to_archive": "Lisatud arhiivi", "added_to_favorites": "Lisatud lemmikutesse", "added_to_favorites_count": "{count, number} pilti lisatud lemmikutesse", "admin": { - "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. KÃĩikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". KÃĩikide .tif failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/path/to/ignore/**\".", + "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. KÃĩikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". KÃĩikide \".tif\" lÃĩpuga failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/tee/mida/ignoreerida/**\".", "admin_user": "Administraator", - "asset_offline_description": "Seda välise kogu Ãŧksust ei leitud kettalt ning see liigutati prÃŧgikasti. Kui faili asukoht kogu siseselt muutus, leiad vastava uue Ãŧksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", + "asset_offline_description": "Seda välise kogu Ãŧksust ei leitud kettalt ning see liigutati prÃŧgikasti. Kui faili asukoht muutus kogu siseselt, leiad vastava uue Ãŧksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", "authentication_settings": "Autentimise seaded", - "authentication_settings_description": "Halda parooli, OAuth ja muid autentimise seadeid", + "authentication_settings_description": "Halda parooli, OAuth'i ja muid autentimise seadeid", "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 serveri käsku.", "background_task_job": "Tausttegumid", "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_onboarding_1_description": "asukohaväline koopia pilves vÃĩi teises fÃŧÃŧsilises asukohas.", + "backup_onboarding_2_description": "lokaalset koopiat erinevatel seadmetel. See hÃĩlmab pÃĩhifaile ja nende failide lokaalsed varundust.", + "backup_onboarding_3_description": "koopiat su andmetest, kaasa arvatud originaalfailid. See hÃĩlmab Ãŧht asukohavälist ja kaht lokaalset koopiat.", + "backup_onboarding_description": "Andmete kaitsmiseks on soovituslik 3-2-1 varundusstrateegia. PÃĩhjaliku varunduse jaoks peaksid talletama koopiaid nii oma Ãŧleslaaditud fotodest ja videotest kui ka Immich'i andmebaasist.", + "backup_onboarding_footer": "Rohkem informatsiooni Immich'i varundamise kohta leiad dokumentatsioonist.", + "backup_onboarding_parts_title": "3-2-1 varundus hÃĩlmab:", + "backup_onboarding_title": "Varukoopiad", "backup_settings": "Andmebaasi tÃĩmmiste seaded", "backup_settings_description": "Halda andmebaasi tÃĩmmiste seadeid.", "cleared_jobs": "TÃļÃļted eemaldatud: {job}", - "config_set_by_file": "Konfiguratsioon on määratud konfifaili abil", + "config_set_by_file": "Konfiguratsioon on määratud konfiguratsioonifaili abil", "confirm_delete_library": "Kas oled kindel, et soovid kustutada {library} kogu?", - "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv Ãŧksus} other {kÃĩik # sisalduvat Ãŧksust}} Immich'ist ning seda ei saa tagasi vÃĩtta. Failid jäävad kettale alles.", + "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv Ãŧksus} other {kÃĩik # sisalduvat Ãŧksust}} Immich'ist ning seda toimingut ei saa tagasi vÃĩtta. Failid jäävad kettale alles.", "confirm_email_below": "Kinnitamiseks sisesta allpool \"{email}\"", "confirm_reprocess_all_faces": "Kas oled kindel, et soovid kÃĩik näod uuesti tÃļÃļdelda? See eemaldab kÃĩik nimega isikud.", "confirm_user_password_reset": "Kas oled kindel, et soovid kasutaja {user} parooli lähtestada?", "confirm_user_pin_code_reset": "Kas oled kindel, et soovid kasutaja {user} PIN-koodi lähtestada?", "create_job": "Lisa tÃļÃļde", "cron_expression": "Cron avaldis", - "cron_expression_description": "Sea skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", + "cron_expression_description": "Määra skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", "cron_expression_presets": "Eelseadistatud cron avaldised", "disable_login": "Keela sisselogimine", "duplicate_detection_job_description": "Rakenda Ãŧksustele masinÃĩpet, et leida sarnaseid pilte. Kasutab nutiotsingut", - "exclusion_pattern_description": "Välistamismustrid vÃĩimaldavad ignoreerida faile ja kaustu kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", + "exclusion_pattern_description": "Välistamismustrid vÃĩimaldavad ignoreerida faile ja kaustu selle kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", "external_library_management": "Väliste kogude haldus", "face_detection": "Näoavastus", - "face_detection_description": "Avasta Ãŧksustest nägusid masinÃĩppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" tÃļÃļtleb kÃĩik Ãŧksused uuesti. \"Lähtesta\" kustutab lisaks kÃĩik seni leitud näed. \"Puuduvad\" vÃĩtab ette Ãŧksused, mida pole veel tÃļÃļdeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks vÃĩi uuteks isikuteks.", + "face_detection_description": "Avasta Ãŧksustest nägusid masinÃĩppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" tÃļÃļtleb kÃĩik Ãŧksused uuesti. \"Lähtesta\" kustutab lisaks kÃĩik seni leitud näod. \"Puuduvad\" vÃĩtab ette Ãŧksused, mida pole veel tÃļÃļdeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks vÃĩi uuteks isikuteks.", "facial_recognition_job_description": "Grupeeri avastatud näod inimesteks. See samm käivitub siis, kui näoavastus on lÃĩppenud. \"Lähtesta\" grupeerib kÃĩik näod uuesti. \"Puuduvad\" vÃĩtab ette näod, mida pole isikuga seostatud.", "failed_job_command": "Käsk {command} ebaÃĩnnestus tÃļÃļtes: {job}", - "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kÃĩik Ãŧksused. Seda ei saa tagasi vÃĩtta ja faile ei saa taastada.", + "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kÃĩik tema Ãŧksused. Toimingut ei saa tagasi vÃĩtta ja faile ei saa taastada.", "image_format": "Formaat", "image_format_description": "WebP failid on väiksemad kui JPEG, aga kodeerimine on aeglasem.", "image_fullsize_description": "TäismÃĩÃĩdus pilt ilma metaandmeteta, kasutatakse sisse suumimisel", @@ -77,9 +89,9 @@ "image_prefer_embedded_preview": "Eelista manustatud eelvaadet", "image_prefer_embedded_preview_setting_description": "Kasuta pilditÃļÃļtluse sisendina vÃĩimalusel RAW fotodesse manustatud eelvaateid. See vÃĩib mÃĩnede piltide puhul anda tulemuseks täpsemad värvid, aga eelvaate kvaliteet sÃĩltub konkreetsest kaamerast ning pildis vÃĩib olla rohkem tihendusmÃŧra.", "image_prefer_wide_gamut": "Eelista laia värvigammat", - "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, aga vanematel seadmetel ja vanemate brauseritega vÃĩivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", + "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, kuid vanematel seadmetel ja vanemate brauseritega vÃĩivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", "image_preview_description": "Keskmise suurusega pilt ilma metaandmeteta, kasutusel Ãŧksiku Ãŧksuse vaatamise ja masinÃĩppe jaoks", - "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. KÃĩrgem väärtus on parem, aga tekitab suuremaid faile ning vÃĩib mÃĩjutada rakenduse tÃļÃļkiirust. Madala väärtuse seadmine vÃĩib mÃĩjutada masinÃĩppe kvaliteeti.", + "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. KÃĩrgem väärtus on parem, aga tekitab suuremaid faile ning vÃĩib mÃĩjutada rakenduse tÃļÃļkiirust. Madal väärtus vÃĩib mÃĩjutada masinÃĩppe kvaliteeti.", "image_preview_title": "Eelvaate seaded", "image_quality": "Kvaliteet", "image_resolution": "Resolutsioon", @@ -92,7 +104,7 @@ "job_concurrency": "{job} samaaegsus", "job_created": "TÃļÃļde lisatud", "job_not_concurrency_safe": "Seda tÃļÃļdet pole ohutu samaaegselt käivitada.", - "job_settings": "TÃļÃļte seaded", + "job_settings": "TÃļÃļdete seaded", "job_settings_description": "Halda tÃļÃļdete samaaegsust", "job_status": "TÃļÃļte seisund", "jobs_delayed": "{jobCount, plural, other {# edasi lÃŧkatud}}", @@ -112,6 +124,13 @@ "logging_enable_description": "Luba logimine", "logging_level_description": "Kui lubatud, millist logimistaset kasutada.", "logging_settings": "Logimine", + "machine_learning_availability_checks": "Saadavuskontrollid", + "machine_learning_availability_checks_description": "Tuvasta ja eelista automaatselt saadavalolevaid masinÃĩppeservereid", + "machine_learning_availability_checks_enabled": "Luba saadavuskontrollid", + "machine_learning_availability_checks_interval": "Kontrolli intervall", + "machine_learning_availability_checks_interval_description": "Saadavuskontrollide intervall millisekundites", + "machine_learning_availability_checks_timeout": "Päringu ajalÃĩpp", + "machine_learning_availability_checks_timeout_description": "Saadavuskontrollide ajalÃĩpp millisekundites", "machine_learning_clip_model": "CLIP mudel", "machine_learning_clip_model_description": "CLIP mudeli nimi, mis on loetletud siin. Pane tähele, et mudeli muutmisel pead kÃĩigi piltide peal nutiotsingu tÃļÃļte uuesti käivitama.", "machine_learning_duplicate_detection": "Duplikaatide leidmine", @@ -166,6 +185,20 @@ "metadata_settings_description": "Halda metaandmete seadeid", "migration_job": "Migratsioon", "migration_job_description": "Migreeri Ãŧksuste ja nägude pisipildid uusimale kaustastruktuurile", + "nightly_tasks_cluster_faces_setting_description": "Käivita värskelt avastatud nägudel näotuvastus", + "nightly_tasks_cluster_new_faces_setting": "Grupeeri uued näod", + "nightly_tasks_database_cleanup_setting": "Andmebaasi puhastuse tegumid", + "nightly_tasks_database_cleanup_setting_description": "Eemalda andmebaasist vanad, aegunud andmed", + "nightly_tasks_generate_memories_setting": "Genereeri mälestused", + "nightly_tasks_generate_memories_setting_description": "Loo Ãŧksustest uued mälestused", + "nightly_tasks_missing_thumbnails_setting": "Genereeri puuduvad pisipildid", + "nightly_tasks_missing_thumbnails_setting_description": "Suuna ilma pisipiltideta Ãŧksused pisipiltide genereerimisele", + "nightly_tasks_settings": "Öiste tegumite seaded", + "nightly_tasks_settings_description": "Halda Ãļiseid tegumeid", + "nightly_tasks_start_time_setting": "Algusaeg", + "nightly_tasks_start_time_setting_description": "Aeg, millal server alustab Ãļiste tegumite käivitamist", + "nightly_tasks_sync_quota_usage_setting": "SÃŧnkrooni kvoodikasutus", + "nightly_tasks_sync_quota_usage_setting_description": "Uuenda kasutaja talletuskvoot jooksva kasutuse alusel", "no_paths_added": "Ühtegi teed pole", "no_pattern_added": "Mustreid ei ole", "note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem Ãŧleslaaditud Ãŧksustele, käivita", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobiilne Ãŧmbersuunamise URI", "oauth_mobile_redirect_uri_override": "Mobiilse Ãŧmbersuunamise URI Ãŧlekirjutamine", "oauth_mobile_redirect_uri_override_description": "LÃŧlita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks ''{callback}''", + "oauth_role_claim": "Rolli väide", + "oauth_role_claim_description": "Anna selle väite olemasolul automaatselt administraatori ligipääs. Väite väärtus vÃĩib olla 'user' vÃĩi 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Päevade arv, kui kaua hoida Ãŧksusi prÃŧgikastis enne nende lÃĩplikku kustutamist", "trash_settings": "PrÃŧgikasti seaded", "trash_settings_description": "Halda prÃŧgikasti seadeid", + "unlink_all_oauth_accounts": "Eemalda kÃĩik OAuth kontod", + "unlink_all_oauth_accounts_description": "Ära unusta enne teenusepakkuja vahetamist kÃĩik OAuth kontod eemaldada.", + "unlink_all_oauth_accounts_prompt": "Kas oled kindel, et soovid kÃĩik OAuth kontod eemaldada? See lähtestab iga kasutaja OAuth ID ja seda tegevust ei saa tagasi vÃĩtta.", "user_cleanup_job": "Kasutajate korrastamine", "user_delete_delay": "Kasutaja {user} konto ja Ãŧksuste lÃĩplik kustutamine on planeeritud {delay, plural, one {# päeva} other {# päeva}} pärast.", "user_delete_delay_settings": "Kustutamise viivitus", @@ -360,10 +398,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sÃŧnkroonimise ajal Ãŧksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kÃĩigi albumite tuvastamisega.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sÃŧnkroonimise filtrit", "advanced_settings_log_level_title": "Logimistase: {level}", - "advanced_settings_prefer_remote_subtitle": "MÃĩned seadmed laadivad seadmes olevate Ãŧksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", + "advanced_settings_prefer_remote_subtitle": "MÃĩned seadmed laadivad lokaalsete Ãŧksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", "advanced_settings_prefer_remote_title": "Eelista kaugpilte", "advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma", "advanced_settings_proxy_headers_title": "Vaheserveri päised", + "advanced_settings_readonly_mode_subtitle": "LÃŧlitab sisse kirjutuskaitsereÅžiimi, milles saab fotosid ainult vaadata ning toimingud nagu mitme pildi valimine, jagamine, edastamine ja kustutamine on keelatud. LÃŧlita kirjutuskaitsereÅžiim sisse/välja pÃĩhiekraanil oleva avatari kaudu", + "advanced_settings_readonly_mode_title": "KirjutuskaitsereÅžiim", "advanced_settings_self_signed_ssl_subtitle": "Jätab serveri lÃĩpp-punkti SSL-sertifikaadi kontrolli vahele. NÃĩutud endasigneeritud sertifikaatide jaoks.", "advanced_settings_self_signed_ssl_title": "Luba endasigneeritud SSL-sertifikaadid", "advanced_settings_sync_remote_deletions_subtitle": "Kustuta vÃĩi taasta Ãŧksus selles seadmes automaatself, kui sama tegevus toimub veebis", @@ -379,6 +419,7 @@ "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_deleted": "Album kustutatud", "album_info_card_backup_album_excluded": "VÄLJA JÄETUD", "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", @@ -388,7 +429,9 @@ "album_options": "Albumi valikud", "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", + "album_search_not_found": "Otsingule vastavaid albumeid ei leitud", "album_share_no_users": "Paistab, et oled seda albumit kÃĩikide kasutajatega jaganud, vÃĩi pole Ãŧhtegi kasutajat, kellega jagada.", + "album_summary": "Albumi kokkuvÃĩte", "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}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Vaikimisi albumi järjestus", "albums_default_sort_order_description": "Uute albumite lisamisel Ãŧksuste esialgne järjekord.", "albums_feature_description": "Üksuste kollektsioonid, mida saab teiste kasutajatega jagada.", + "albums_on_device_count": "Albumid seadmel ({count})", "all": "KÃĩik", "all_albums": "KÃĩik albumid", "all_people": "KÃĩik isikud", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Logi välja", "app_settings": "Rakenduse seaded", "appears_in": "Albumid", + "apply_count": "Rakenda ({count, number})", "archive": "Arhiiv", + "archive_action_prompt": "{count} lisatud arhiivi", "archive_or_unarchive_photo": "Arhiveeri vÃĩi taasta foto", "archive_page_no_archived_assets": "Arhiveeritud Ãŧksuseid ei leitud", "archive_page_title": "Arhiveeri ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "Üksus edukalt taastatud", "asset_skipped": "Vahele jäetud", "asset_skipped_in_trash": "PrÃŧgikastis", + "asset_trashed": "Üksus liigutatud prÃŧgikasti", + "asset_troubleshoot": "Üksuse tÃĩrkeotsing", "asset_uploaded": "Üleslaaditud", "asset_uploading": "Üleslaadimineâ€Ļ", "asset_viewer_settings_subtitle": "Halda galeriivaaturi seadeid", @@ -464,8 +512,9 @@ "assets": "Üksused", "assets_added_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} lisatud", "assets_added_to_album_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} albumisse lisatud", - "assets_added_to_name_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} lisatud {hasName, select, true {albumisse {name}} other {uude albumisse}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {# Ãŧksus} other {# Ãŧksust}} lisatud {albumTotal, plural, one {# albumisse} other {# albumisse}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada", + "assets_cannot_be_added_to_albums": "{count, plural, one {Üksust} other {Üksuseid}} ei saa lisada Ãŧhtegi albumisse", "assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", "assets_deleted_permanently": "{count} Ãŧksus(t) jäädavalt kustutatud", "assets_deleted_permanently_from_server": "{count} Ãŧksus(t) Immich'i serverist jäädavalt kustutatud", @@ -482,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud prÃŧgikasti", "assets_trashed_from_server": "{count} Ãŧksus(t) liigutatud Immich'i serveris prÃŧgikasti", "assets_were_part_of_album_count": "{count, plural, one {Üksus oli} other {Üksused olid}} juba osa albumist", + "assets_were_part_of_albums_count": "{count, plural, one {Üksus oli} other {Üksused olid}} juba nendes albumites", "authorized_devices": "Autoriseeritud seadmed", "automatic_endpoint_switching_subtitle": "Ühendu lokaalselt Ãŧle valitud WiFi-vÃĩrgu, kui see on saadaval, ja kasuta mujal alternatiivseid Ãŧhendusi", "automatic_endpoint_switching_title": "Automaatne URL-i ÃŧmberlÃŧlitamine", "autoplay_slideshow": "Esita slaidiesitlus automaatselt", "back": "Tagasi", "back_close_deselect": "Tagasi, sulge vÃĩi tÃŧhista valik", + "background_backup_running_error": "Taustvarundus on käimas, ei saa käsitsi varundust alustada", "background_location_permission": "Taustal asukoha luba", "background_location_permission_content": "Et taustal tÃļÃļtades vÃĩrguÃŧhendust vahetada, peab Immich'il *alati* olema täpse asukoha luba, et rakendus saaks WiFi-vÃĩrgu nime lugeda", + "background_options": "Taustavalikud", + "backup": "Varundamine", "backup_album_selection_page_albums_device": "Albumid seadmel ({count})", "backup_album_selection_page_albums_tap": "Puuduta kaasamiseks, topeltpuuduta välistamiseks", "backup_album_selection_page_assets_scatter": "Üksused vÃĩivad olla jaotatud mitme albumi vahel. Seega saab albumeid varundamise protsessi kaasata vÃĩi välistada.", "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_albums_sync": "Varundusalbumite sÃŧnkroniseerimine", "backup_all": "KÃĩik", "backup_background_service_backup_failed_message": "Üksuste varundamine ebaÃĩnnestus. Uuesti proovimineâ€Ļ", "backup_background_service_connection_failed_message": "Serveriga Ãŧhendumine ebaÃĩnnestus. Uuesti proovimineâ€Ļ", @@ -550,8 +604,10 @@ "backup_manual_in_progress": "Üleslaadimine juba käib. Proovi hiljem uuesti", "backup_manual_success": "Õnnestus", "backup_manual_title": "Üleslaadimise staatus", + "backup_options": "Varunduse valikud", "backup_options_page_title": "Varundamise valikud", "backup_setting_subtitle": "Halda taustal ja esiplaanil Ãŧleslaadimise seadeid", + "backup_settings_subtitle": "Halda Ãŧleslaadimise seadeid", "backward": "Tagasi", "biometric_auth_enabled": "Biomeetriline autentimine lubatud", "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", @@ -570,7 +626,7 @@ "cache_settings_clear_cache_button": "TÃŧhjenda puhver", "cache_settings_clear_cache_button_title": "TÃŧhjendab rakenduse puhvri. See mÃĩjutab oluliselt rakenduse jÃĩudlust, kuni puhver uuesti täidetakse.", "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", - "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt ignoreeritud", "cache_settings_duplicated_assets_title": "Dubleeritud Ãŧksused ({count})", "cache_settings_statistics_album": "Kogu pisipildid", "cache_settings_statistics_full": "TäismÃĩÃĩdus pildid", @@ -587,6 +643,7 @@ "cancel": "Katkesta", "cancel_search": "Katkesta otsing", "canceled": "TÃŧhistatud", + "canceling": "TÃŧhistamine", "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", @@ -609,6 +666,8 @@ "change_pin_code": "Muuda PIN-koodi", "change_your_password": "Muuda oma parooli", "changed_visibility_successfully": "Nähtavus muudetud", + "charging": "Laadimine", + "charging_requirement_mobile_backup": "Taustal varundus vajab, et seade oleks laadimas", "check_corrupt_asset_backup": "Otsi riknenud Ãŧksuste varukoopiaid", "check_corrupt_asset_backup_button": "Teosta kontroll", "check_corrupt_asset_backup_description": "Käivita see kontroll ainult WiFi-vÃĩrgus ja siis, kui kÃĩik Ãŧksused on varundatud. See protseduur vÃĩib kesta mÃĩne minuti.", @@ -618,6 +677,7 @@ "clear": "TÃŧhjenda", "clear_all": "TÃŧhjenda kÃĩik", "clear_all_recent_searches": "TÃŧhjenda hiljutised otsingud", + "clear_file_cache": "TÃŧhjenda failipuhver", "clear_message": "TÃŧhjenda sÃĩnum", "clear_value": "TÃŧhjenda väärtus", "client_cert_dialog_msg_confirm": "OK", @@ -688,11 +748,13 @@ "create_new_user": "Lisa uus kasutaja", "create_shared_album_page_share_add_assets": "LISA ÜKSUSEID", "create_shared_album_page_share_select_photos": "Vali fotod", + "create_shared_link": "Loo jagatud link", "create_tag": "Lisa silt", "create_tag_description": "Lisa uus silt. Pesastatud siltide jaoks sisesta täielik tee koos kaldkriipsudega.", "create_user": "Lisa kasutaja", "created": "Lisatud", "created_at": "Lisatud", + "creating_linked_albums": "Lingitud albumite loomine...", "crop": "Kärpimine", "curated_object_page_title": "Asjad", "current_device": "Praegune seade", @@ -700,10 +762,11 @@ "current_server_address": "Praegune serveri aadress", "custom_locale": "Kohandatud lokaat", "custom_locale_description": "Vorminda kuupäevad ja arvud vastavalt keelele ja regioonile", + "custom_url": "Kohandatud URL", "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", - "darkTheme": "LÃŧlita tume teema", + "dark_theme": "LÃŧlita tume teema", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", @@ -711,6 +774,7 @@ "date_of_birth_saved": "SÃŧnnikuupäev salvestatud", "date_range": "Kuupäevavahemik", "day": "Päev", + "days": "Päeva", "deduplicate_all": "Dedubleeri kÃĩik", "deduplication_criteria_1": "Pildi suurus baitides", "deduplication_criteria_2": "EXIF andmete hulk", @@ -719,6 +783,8 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", + "delete_action_confirmation_message": "Kas oled kindel, et soovid selle Ãŧksuse kustutada? See toiming liigutab Ãŧksuse serveri prÃŧgikasti ja kÃŧsib, kas soovid selle lokaalselt kustutada", + "delete_action_prompt": "{count} kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API vÃĩtme kustutada?", "delete_dialog_alert": "Need Ãŧksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -732,9 +798,12 @@ "delete_key": "Kustuta vÃĩti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_action_prompt": "{count} kustutatud lokaalselt", "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", + "delete_permanently": "Kustuta jäädavalt", + "delete_permanently_action_prompt": "{count} jäädavalt kustutatud", "delete_shared_link": "Kustuta jagatud link", "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", @@ -745,6 +814,7 @@ "description": "Kirjeldus", "description_input_hint_text": "Lisa kirjeldus...", "description_input_submit_error": "Viga kirjelduse muutmisel, rohkem infot leiad logist", + "deselect_all": "Eemalda kÃĩik valikust", "details": "Üksikasjad", "direction": "Suund", "disabled": "Välja lÃŧlitatud", @@ -762,6 +832,7 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_action_prompt": "{count} Ãŧksust laaditakse alla", "download_canceled": "Allalaadimine katkestatud", "download_complete": "Allalaadimine lÃĩpetatud", "download_enqueue": "Allalaadimine ootel", @@ -776,11 +847,11 @@ "download_settings_description": "Halda Ãŧksuste allalaadimise seadeid", "download_started": "Allalaadimine alustatud", "download_sucess": "Allalaadimine Ãĩnnestus", - "download_sucess_android": "Meediumid laaditi alla kataloogi DCIM/Immich", + "download_sucess_android": "Üksused laaditi alla kataloogi DCIM/Immich", "download_waiting_to_retry": "Uuesti proovimise ootel", "downloading": "Allalaadimine", "downloading_asset_filename": "Üksuse {filename} allalaadimine", - "downloading_media": "Meediumi allalaadimine", + "downloading_media": "Üksuste allalaadimine", "drop_files_to_upload": "Failide Ãŧleslaadimiseks sikuta need ÃŧkskÃĩik kuhu", "duplicates": "Duplikaadid", "duplicates_description": "Lahenda iga grupp, valides duplikaadid, kui neid on", @@ -788,8 +859,12 @@ "edit": "Muuda", "edit_album": "Muuda albumit", "edit_avatar": "Muuda avatari", + "edit_birthday": "Muuda sÃŧnnipäeva", "edit_date": "Muuda kuupäeva", "edit_date_and_time": "Muuda kuupäeva ja kellaaega", + "edit_date_and_time_action_prompt": "{count} päev ja kellaaeg muudetud", + "edit_date_and_time_by_offset": "Nihuta kuupäeva", + "edit_date_and_time_by_offset_interval": "Uus kuupäevavahemik: {from} - {to}", "edit_description": "Muuda kirjeldust", "edit_description_prompt": "Palun vali uus kirjeldus:", "edit_exclusion_pattern": "Muuda välistamismustrit", @@ -799,6 +874,7 @@ "edit_key": "Muuda vÃĩtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_action_prompt": "{count} asukoht muudetud", "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", @@ -817,6 +893,7 @@ "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", + "enable_backup": "Luba varundus", "enable_biometric_auth_description": "Biomeetrilise autentimise lubamiseks sisesta oma PIN-kood", "enabled": "Lubatud", "end_date": "LÃĩppkuupäev", @@ -827,7 +904,9 @@ "error": "Viga", "error_change_sort_album": "Albumi sorteerimisjärjestuse muutmine ebaÃĩnnestus", "error_delete_face": "Viga näo kustutamisel", + "error_getting_places": "Viga kohtade pärimisel", "error_loading_image": "Viga pildi laadimisel", + "error_loading_partners": "Viga partnerite laadimisel: {error}", "error_saving_image": "Viga: {error}", "error_tag_face_bounding_box": "Viga näo sildistamisel - Ãŧmbritseva kasti koordinaate ei Ãĩnnestunud leida", "error_title": "Viga - midagi läks valesti", @@ -860,6 +939,7 @@ "failed_to_load_notifications": "Teavituste laadimine ebaÃĩnnestus", "failed_to_load_people": "Isikute laadimine ebaÃĩnnestus", "failed_to_remove_product_key": "TootevÃĩtme eemaldamine ebaÃĩnnestus", + "failed_to_reset_pin_code": "PIN-koodi lähestamine ebaÃĩnnestus", "failed_to_stack_assets": "Üksuste virnastamine ebaÃĩnnestus", "failed_to_unstack_assets": "Üksuste eraldamine ebaÃĩnnestus", "failed_to_update_notification_status": "Teavituste seisundi uuendamine ebaÃĩnnestus", @@ -868,6 +948,7 @@ "paths_validation_failed": "{paths, plural, one {# tee} other {# teed}} ei valideerunud", "profile_picture_transparent_pixels": "Profiilipildis ei tohi olla läbipaistvaid piksleid. Palun suumi sisse ja/vÃĩi liiguta pilti.", "quota_higher_than_disk_size": "Määratud kvoot on suurem kui kettamaht", + "something_went_wrong": "Midagi läks valesti", "unable_to_add_album_users": "Kasutajate lisamine albumisse ebaÃĩnnestus", "unable_to_add_assets_to_shared_link": "Üksuste jagatud lingile lisamine ebaÃĩnnestus", "unable_to_add_comment": "Kommentaari lisamine ebaÃĩnnestus", @@ -953,13 +1034,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_description_error": "Viga kirjelduse muutmisel", "exif_bottom_sheet_details": "ÜKSIKASJAD", "exif_bottom_sheet_location": "ASUKOHT", "exif_bottom_sheet_people": "ISIKUD", "exif_bottom_sheet_person_add_person": "Lisa nimi", - "exif_bottom_sheet_person_age_months": "Vanus {months} kuud", - "exif_bottom_sheet_person_age_year_months": "Vanus 1 aasta, {months} kuud", - "exif_bottom_sheet_person_age_years": "Vanus {years}", "exit_slideshow": "Sulge slaidiesitlus", "expand_all": "Näita kÃĩik", "experimental_settings_new_asset_list_subtitle": "TÃļÃļs", @@ -973,6 +1052,8 @@ "explorer": "Brauser", "export": "Ekspordi", "export_as_json": "Ekspordi JSON-formaati", + "export_database": "Ekspordi andmebaas", + "export_database_description": "Ekspordi SQLite andmebaas", "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", @@ -984,11 +1065,13 @@ "failed_to_load_assets": "Üksuste laadimine ebaÃĩnnestus", "failed_to_load_folder": "Kausta laadimine ebaÃĩnnestus", "favorite": "Lemmik", + "favorite_action_prompt": "{count} lisatud lemmikutesse", "favorite_or_unfavorite_photo": "Lisa foto lemmikutesse vÃĩi eemalda lemmikutest", "favorites": "Lemmikud", "favorites_page_no_favorites": "Lemmikuid Ãŧksuseid ei leitud", "feature_photo_updated": "EsiletÃĩstetud foto muudetud", "features": "Funktsioonid", + "features_in_development": "Arendusjärgus olevad funktsioonid", "features_setting_description": "Halda rakenduse funktsioone", "file_name": "Failinimi", "file_name_or_extension": "Failinimi vÃĩi -laiend", @@ -998,21 +1081,26 @@ "filter_people": "Filtreeri isikuid", "filter_places": "Filtreeri kohti", "find_them_fast": "Leia teda kiiresti nime järgi otsides", + "first": "Esimene", "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", + "forgot_pin_code_question": "Unustasid oma PIN-koodi?", "forward": "Edasi", "gcast_enabled": "Google Cast", "gcast_enabled_description": "See funktsionaalsus laadib tÃļÃļtamiseks Google'st väliseid ressursse.", "general": "Üldine", + "geolocation_instruction_location": "KlÃĩpsa GPS-koordinaatidega Ãŧksusel, et kasutada selle asukohta, vÃĩi vali asukoht otse kaardilt", "get_help": "KÃŧsi abi", "get_wifiname_error": "WiFi-vÃĩrgu nime ei Ãĩnnestunud lugeda. Veendu, et oled andnud vajalikud load ja oled WiFi-vÃĩrguga Ãŧhendatud", "getting_started": "Alustamine", "go_back": "Tagasi", "go_to_folder": "Mine kausta", "go_to_search": "Otsingusse", + "gps": "GPS", + "gps_missing": "GPS puudub", "grant_permission": "Anna luba", "group_albums_by": "Grupeeri albumid...", "group_country": "Grupeeri riigi kaupa", @@ -1023,6 +1111,9 @@ "haptic_feedback_switch": "Luba haptiline tagasiside", "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "hash_asset": "Arvuta Ãŧksuse räsi", + "hashed_assets": "Räsiga Ãŧksused", + "hashing": "Räsi arvutamine", "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", @@ -1054,7 +1145,9 @@ "home_page_upload_err_limit": "Korraga saab Ãŧles laadida ainult 30 Ãŧksust, jäetakse vahele", "host": "Host", "hour": "Tund", + "hours": "Tundi", "id": "ID", + "idle": "JÃĩude", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita Ãŧles Immich'i serverisse", "image": "Pilt", @@ -1099,7 +1192,7 @@ "ios_debug_info_no_processes_queued": "Taustaprotsesse pole järjekorras", "ios_debug_info_no_sync_yet": "Taustal sÃŧnkroonimise tÃļÃļde pole veel käinud", "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprotsess järjekorras} other {{count} taustaprotsessi järjekorras}}", - "ios_debug_info_processing_ran_at": "TÃļÃļtlemine käis {dateTime}", + "ios_debug_info_processing_ran_at": "TÃļÃļtlemine toimus {dateTime}", "items_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}}", "jobs": "TÃļÃļted", "keep": "Jäta alles", @@ -1112,10 +1205,13 @@ "language_no_results_title": "Ühtegi keelt ei leitud", "language_search_hint": "Otsi keeli...", "language_setting_description": "Vali oma eelistatud keel", + "large_files": "Suured failid", + "last": "Viimane", "last_seen": "Viimati nähtud", "latest_version": "Uusim versioon", "latitude": "Laiuskraad", "leave": "Lahku", + "leave_album": "Lahku albumist", "lens_model": "Läätse mudel", "let_others_respond": "Luba teistel vastata", "level": "Tase", @@ -1127,16 +1223,20 @@ "library_page_sort_created": "Loomise aeg", "library_page_sort_last_modified": "Viimase muutmise aeg", "library_page_sort_title": "Albumi pealkiri", + "licenses": "Litsentsid", "light": "Hele", + "like": "Meeldib", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", - "link_options": "Lingi valikud", "link_to_oauth": "Ühenda OAuth", "linked_oauth_account": "OAuth konto Ãŧhendatud", "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaÃĩnnestus", + "local": "Lokaalsed", "local_asset_cast_failed": "Ei saa edastada Ãŧksust, mis pole serverisse Ãŧles laaditud", + "local_assets": "Lokaalsed Ãŧksused", + "local_media_summary": "Lokaalsete Ãŧksuste kokkuvÃĩte", "local_network": "Kohalik vÃĩrk", "local_network_sheet_info": "Rakendus Ãŧhendub valitud Wi-Fi vÃĩrgus olles serveriga selle URL-i kaudu", "location_permission": "Asukoha luba", @@ -1148,6 +1248,7 @@ "location_picker_longitude_hint": "Sisesta pikkuskraad siia", "lock": "Lukusta", "locked_folder": "Lukustatud kaust", + "log_detail_title": "Logi detailid", "log_out": "Logi välja", "log_out_all_devices": "Logi kÃĩigist seadmetest välja", "logged_in_as": "Logitud sisse kasutajana {user}", @@ -1178,6 +1279,7 @@ "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?", + "logs": "Logid", "longitude": "Pikkuskraad", "look": "Välimus", "loop_videos": "Taasesita videod", @@ -1185,6 +1287,7 @@ "main_branch_warning": "Sa kasutad arendusversiooni; soovitame tungivalt kasutada väljalaskeversiooni!", "main_menu": "PeamenÃŧÃŧ", "make": "Mark", + "manage_geolocation": "Halda asukohta", "manage_shared_links": "Halda jagatud linke", "manage_sharing_with_partners": "Halda partneritega jagamist", "manage_the_app_settings": "Halda rakenduse seadeid", @@ -1193,8 +1296,7 @@ "manage_your_devices": "Halda oma autenditud seadmeid", "manage_your_oauth_connection": "Halda oma OAuth Ãŧhendust", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotot", + "map_assets_in_bounds": "{count, plural, =0 {Selles piirkonnas fotosid pole} one {# foto} other {# fotot}}", "map_cannot_get_user_location": "Ei saa kasutaja asukohta tuvastada", "map_location_dialog_yes": "Jah", "map_location_picker_page_use_location": "Kasuta seda asukohta", @@ -1202,7 +1304,6 @@ "map_location_service_disabled_title": "Asukoha teenus keelatud", "map_marker_for_images": "Kaardimarker kohas {city}, {country} tehtud piltide jaoks", "map_marker_with_image": "Kaardimarker pildiga", - "map_no_assets_in_bounds": "Selles piirkonnas ei ole fotosid", "map_no_location_permission_content": "Praeguse asukoha Ãŧksuste kuvamiseks on vaja asukoha luba. Kas soovid seda praegu lubada?", "map_no_location_permission_title": "Asukoha luba keelatud", "map_settings": "Kaardi seaded", @@ -1221,6 +1322,7 @@ "mark_as_read": "Märgi loetuks", "marked_all_as_read": "KÃĩik märgiti loetuks", "matches": "Ühtivad failid", + "matching_assets": "Ühtivad Ãŧksused", "media_type": "Meediumi tÃŧÃŧp", "memories": "Mälestused", "memories_all_caught_up": "Ongi kÃĩik", @@ -1239,6 +1341,7 @@ "merged_people_count": "Ühendatud {count, plural, one {# isik} other {# isikut}}", "minimize": "Minimeeri", "minute": "Minut", + "minutes": "Minutit", "missing": "Puuduvad", "model": "Mudel", "month": "Kuu", @@ -1246,6 +1349,7 @@ "more": "Rohkem", "move": "Liiguta", "move_off_locked_folder": "Liiguta lukustatud kaustast välja", + "move_to_lock_folder_action_prompt": "{count} lisatud lukustatud kausta", "move_to_locked_folder": "Liiguta lukustatud kausta", "move_to_locked_folder_confirmation": "Need fotod ja videod eemaldatakse kÃĩigist albumitest ning nad on nähtavad ainult lukustatud kaustas", "moved_to_archive": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} liigutatud arhiivi", @@ -1257,6 +1361,10 @@ "my_albums": "Minu albumid", "name": "Nimi", "name_or_nickname": "Nimi vÃĩi hÃŧÃŧdnimi", + "network_requirement_photos_upload": "Kasuta fotode varundamiseks mobiilset andmesidet", + "network_requirement_videos_upload": "Kasuta videote varundamiseks mobiilset andmesidet", + "network_requirements": "VÃĩrgu nÃĩuded", + "network_requirements_updated": "VÃĩrgu nÃĩuded muutusid, varundamise järjekord lähtestatakse", "networking_settings": "VÃĩrguÃŧhendus", "networking_subtitle": "Halda serveri lÃĩpp-punkti seadeid", "never": "Mitte kunagi", @@ -1266,6 +1374,7 @@ "new_person": "Uus isik", "new_pin_code": "Uus PIN-kood", "new_pin_code_subtitle": "See on sul esimene kord lukustatud kausta kasutada. Turvaliseks ligipääsuks loo PIN-kood", + "new_timeline": "Uus ajajoon", "new_user_created": "Uus kasutaja lisatud", "new_version_available": "UUS VERSIOON SAADAVAL", "newest_first": "Uuemad eespool", @@ -1279,19 +1388,24 @@ "no_assets_message": "KLIKI ESIMESE FOTO ÜLESLAADIMISEKS", "no_assets_to_show": "Pole Ãŧksuseid, mida kuvada", "no_cast_devices_found": "Edastamise seadmeid ei leitud", + "no_checksum_local": "Kontrollsumma pole saadaval - lokaalse Ãŧksuse pärimine ebaÃĩnnestus", + "no_checksum_remote": "Kontrollsumma pole saadaval - kaugÃŧksuse pärimine ebaÃĩnnestus", "no_duplicates_found": "Ühtegi duplikaati ei leitud.", "no_exif_info_available": "Exif info pole saadaval", "no_explore_results_message": "Oma kogu avastamiseks laadi Ãŧles rohkem fotosid.", "no_favorites_message": "Lisa lemmikud, et oma parimaid fotosid ja videosid kiiresti leida", "no_libraries_message": "Lisa väline kogu oma fotode ja videote vaatamiseks", + "no_local_assets_found": "Selle kontrollsummaga lokaalseid Ãŧksuseid ei leitud", "no_locked_photos_message": "Lukustatud kaustas olevad fotod ja videod on peidetud ning need pole kogu sirvimisel ja otsimisel nähtavad.", "no_name": "Nimetu", "no_notifications": "Teavitusi pole", "no_people_found": "Kattuvaid isikuid ei leitud", "no_places": "Kohti ei ole", + "no_remote_assets_found": "Selle kontrollsummaga kaugÃŧksuseid ei leitud", "no_results": "Vasteid pole", "no_results_description": "Proovi sÃŧnonÃŧÃŧmi vÃĩi Ãŧldisemat märksÃĩna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", + "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", "not_in_any_album": "Pole Ãŧheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem Ãŧleslaaditud Ãŧksustele, käivita", @@ -1307,6 +1421,7 @@ "oauth": "OAuth", "official_immich_resources": "Ametlikud Immich'i ressursid", "offline": "Ühendus puudub", + "offset": "Nihe", "ok": "OK", "oldest_first": "Vanemad eespool", "on_this_device": "Sellel seadmel", @@ -1325,10 +1440,13 @@ "open_the_search_filters": "Ava otsingufiltrid", "options": "Valikud", "or": "vÃĩi", + "organize_into_albums": "Organiseeri albumitesse", + "organize_into_albums_description": "Pane olemasolevad fotod albumitesse, kasutades jooksvaid sÃŧnkroonimise seadeid", "organize_your_library": "Korrasta oma kogu", "original": "originaal", "other": "Muud", "other_devices": "Muud seadmed", + "other_entities": "Muud objektid", "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", @@ -1383,6 +1501,9 @@ "permission_onboarding_permission_limited": "Piiratud luba. Et Immich saaks tervet su galeriid varundada ja hallata, anna Seadetes luba fotodele ja videotele.", "permission_onboarding_request": "Immich'il on vaja luba su fotode ja videote vaatamiseks.", "person": "Isik", + "person_age_months": "{months, plural, one {# kuu} other {# kuud}} vana", + "person_age_year_months": "1 aasta {months, plural, one {# kuu} other {# kuud}} vana", + "person_age_years": "{years, plural, other {# aastat}} vana", "person_birthdate": "SÃŧndinud {date}", "person_hidden": "{name}{hidden, select, true { (peidetud)} other {}}", "photo_shared_all_users": "Paistab, et oled oma fotosid kÃĩigi kasutajatega jaganud, vÃĩi pole Ãŧhtegi kasutajat, kellega jagada.", @@ -1406,6 +1527,7 @@ "port": "Port", "preferences_settings_subtitle": "Halda rakenduse eelistusi", "preferences_settings_title": "Eelistused", + "preparing": "Ettevalmistamine", "preset": "Eelseadistus", "preview": "Eelvaade", "previous": "Eelmine", @@ -1422,6 +1544,7 @@ "profile_drawer_client_out_of_date_minor": "Mobiilirakendus on aegunud. Palun uuenda uusimale väikesele versioonile.", "profile_drawer_client_server_up_to_date": "Klient ja server on uuendatud", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "KirjutuskaitsereÅžiim sisse lÃŧlitatud. Väljumiseks puuduta pikalt avatari ikooni.", "profile_drawer_server_out_of_date_major": "Server on aegunud. Palun uuenda uusimale suurele versioonile.", "profile_drawer_server_out_of_date_minor": "Server on aegunud. Palun uuenda uusimale väikesele versioonile.", "profile_image_of_user": "Kasutaja {user} profiilipilt", @@ -1460,12 +1583,16 @@ "purchase_server_description_2": "Toetaja staatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevÃĩtit haldab administraator", + "queue_status": "Järjekorras {count}/{total}", "rating": "Hinnang", "rating_clear": "TÃŧhjenda hinnang", "rating_count": "{count, plural, one {# tärn} other {# tärni}}", "rating_description": "Kuva infopaneelis EXIF hinnangut", "reaction_options": "Reaktsiooni valikud", "read_changelog": "Vaata muudatuste Ãŧlevaadet", + "readonly_mode_disabled": "KirjutuskaitsereÅžiim välja lÃŧlitatud", + "readonly_mode_enabled": "KirjutuskaitsereÅžiim sisse lÃŧlitatud", + "ready_for_upload": "Valmis Ãŧleslaadimiseks", "reassign": "Määra uuesti", "reassigned_assets_to_existing_person": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} seostatud {name, select, null {olemasoleva isikuga} other {isikuga {name}}}", "reassigned_assets_to_new_person": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} seostatud uue isikuga", @@ -1488,6 +1615,9 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", + "remote": "Serveris", + "remote_assets": "KaugÃŧksused", + "remote_media_summary": "KaugÃŧksuste kokkuvÃĩte", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# Ãŧksuse} other {# Ãŧksust}} albumist eemaldada?", "remove_assets_shared_link_confirmation": "Kas oled kindel, et soovid eemaldada {count, plural, one {# Ãŧksuse} other {# Ãŧksust}} sellelt jagatud lingilt?", @@ -1495,7 +1625,9 @@ "remove_custom_date_range": "Eemalda kohandatud kuupäevavahemik", "remove_deleted_assets": "Eemalda kustutatud Ãŧksused", "remove_from_album": "Eemalda albumist", + "remove_from_album_action_prompt": "{count} eemaldatud albumist", "remove_from_favorites": "Eemalda lemmikutest", + "remove_from_lock_folder_action_prompt": "{count} eemaldatud lukustatud kaustast", "remove_from_locked_folder": "Eemalda lukustatud kaustast", "remove_from_locked_folder_confirmation": "Kas oled kindel, et soovid need fotod ja videod lukustatud kaustast välja liigutada? Need muutuvad su kogus nähtavaks.", "remove_from_shared_link": "Eemalda jagatud lingist", @@ -1523,19 +1655,29 @@ "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", "reset_pin_code": "Lähtesta PIN-kood", + "reset_pin_code_description": "Kui unustasid oma PIN-koodi, vÃĩta selle lähtestamiseks Ãŧhendust serveri administraatoriga", + "reset_pin_code_success": "PIN-kood edukalt lähtestatud", + "reset_pin_code_with_password": "Saad oma PIN-koodi alati oma parooli abil lähtestada", + "reset_sqlite": "Lähtesta SQLite andmebaas", + "reset_sqlite_confirmation": "Kas oled kindel, et soovid SQLite andmebaasi lähtestada? Andmete uuesti sÃŧnkroonimiseks pead välja ja jälle sisse logima", + "reset_sqlite_success": "SQLite andmebaas edukalt lähtestatud", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "KÃĩik duplikaadid lahendatud", "restore": "Taasta", "restore_all": "Taasta kÃĩik", + "restore_trash_action_prompt": "{count} prÃŧgikastust taastatud", "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", + "resume_paused_jobs": "Jätka {count, plural, one {# peatatud tÃļÃļde} other {# peatatud tÃļÃļdet}}", "retry_upload": "Proovi Ãŧleslaadimist uuesti", "review_duplicates": "Vaata duplikaadid läbi", + "review_large_files": "Vaata suured failid läbi", "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", + "running": "Käimas", "save": "Salvesta", "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API vÃĩti salvestatud", @@ -1622,6 +1764,7 @@ "select_user_for_sharing_page_err_album": "Albumi lisamine ebaÃĩnnestus", "selected": "Valitud", "selected_count": "{count, plural, other {# valitud}}", + "selected_gps_coordinates": "Valitud GPS-koordinaadid", "send_message": "Saada sÃĩnum", "send_welcome_email": "Saada tervituskiri", "server_endpoint": "Serveri lÃĩpp-punkt", @@ -1667,6 +1810,7 @@ "settings_saved": "Seaded salvestatud", "setup_pin_code": "Seadista PIN-kood", "share": "Jaga", + "share_action_prompt": "Jagatud {count} Ãŧksust", "share_add_photos": "Lisa fotosid", "share_assets_selected": "{count} valitud", "share_dialog_preparing": "Ettevalmistamine...", @@ -1688,6 +1832,7 @@ "shared_link_clipboard_copied_massage": "Kopeeritud lÃĩikelauale", "shared_link_clipboard_text": "Link: {link}\nParool: {password}", "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_custom_url_description": "Ligipääs jagatud lingile kohandatud URL-i kaudu", "shared_link_edit_description_hint": "Sisesta jagatud lingi kirjeldus", "shared_link_edit_expire_after_option_day": "1 päev", "shared_link_edit_expire_after_option_days": "{count} päeva", @@ -1713,6 +1858,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", + "shared_link_password_description": "NÃĩua jagatud lingile ligi pääsemiseks parooli", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", @@ -1747,6 +1893,7 @@ "show_slideshow_transition": "Kuva slaidiesitluse Ãŧleminekud", "show_supporter_badge": "Toetaja märk", "show_supporter_badge_description": "Kuva toetaja märki", + "show_text_search_menu": "Kuva tekstiotsingu menÃŧÃŧd", "shuffle": "Juhuslik", "sidebar": "KÃŧlgmenÃŧÃŧ", "sidebar_display_description": "Kuva kÃŧlgmenÃŧÃŧs linki vaatele", @@ -1762,12 +1909,14 @@ "sort_created": "Loomise aeg", "sort_items": "Üksuste arv", "sort_modified": "Muutmise aeg", + "sort_newest": "Uusim foto", "sort_oldest": "Vanim foto", "sort_people_by_similarity": "Sorteeri isikud sarnasuse järgi", "sort_recent": "Uusim foto", "sort_title": "Pealkiri", "source": "Lähtekood", "stack": "Virnasta", + "stack_action_prompt": "{count} virnastatud", "stack_duplicates": "Virnasta duplikaadid", "stack_select_one_photo": "Vali virnale kaanefoto", "stack_selected_photos": "Virnasta valitud fotod", @@ -1775,6 +1924,7 @@ "stacktrace": "Pinujälg", "start": "Alusta", "start_date": "Alguskuupäev", + "start_date_before_end_date": "Alguskuupäev peab olema varasem kui lÃĩppkuupäev", "state": "Osariik", "status": "Staatus", "stop_casting": "LÃĩpeta edastamine", @@ -1787,6 +1937,7 @@ "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", + "success": "Õnnestus", "suggestions": "Soovitused", "sunrise_on_the_beach": "PäikesetÃĩus rannal", "support": "Tugi", @@ -1796,6 +1947,10 @@ "sync": "SÃŧnkrooni", "sync_albums": "SÃŧnkrooni albumid", "sync_albums_manual_subtitle": "SÃŧnkrooni kÃĩik Ãŧleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_local": "SÃŧnkrooni lokaalsed Ãŧksused", + "sync_remote": "SÃŧnkrooni kaugÃŧksused", + "sync_status": "SÃŧnkroonimise staatus", + "sync_status_subtitle": "Vaata ja halda sÃŧnkroonimissÃŧsteemi", "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod Ãŧles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista Ãŧksuseid", @@ -1806,6 +1961,7 @@ "tag_updated": "Muudetud silt: {tag}", "tagged_assets": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} sildistatud", "tags": "Sildid", + "tap_to_run_job": "Puuduta tÃļÃļte käivitamiseks", "template": "Mall", "theme": "Teema", "theme_selection": "Teema valik", @@ -1832,12 +1988,15 @@ "to_change_password": "Muuda parool", "to_favorite": "Lemmik", "to_login": "Logi sisse", + "to_multi_select": "vali mitu", "to_parent": "Tase Ãŧles", + "to_select": "vali", "to_trash": "PrÃŧgikasti", "toggle_settings": "Kuva/peida seaded", "total": "Kokku", "total_usage": "Kogukasutus", "trash": "PrÃŧgikast", + "trash_action_prompt": "{count} liigutatud prÃŧgikasti", "trash_all": "KÃĩik prÃŧgikasti", "trash_count": "Liiguta {count, number} prÃŧgikasti", "trash_delete_asset": "Kustuta Ãŧksus", @@ -1851,13 +2010,16 @@ "trash_page_select_assets_btn": "Vali Ãŧksused", "trash_page_title": "PrÃŧgikast ({count})", "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.", + "troubleshoot": "TÃĩrkeotsing", "type": "TÃŧÃŧp", "unable_to_change_pin_code": "PIN-koodi muutmine ebaÃĩnnestus", "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaÃĩnnestus", "unarchive": "Taasta arhiivist", + "unarchive_action_prompt": "{count} eemaldatud arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", "undo": "VÃĩta tagasi", "unfavorite": "Eemalda lemmikutest", + "unfavorite_action_prompt": "{count} eemaldatud lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", "unknown_country": "Tundmatu riik", @@ -1875,15 +2037,21 @@ "unselect_all_duplicates": "Ära vali duplikaate", "unselect_all_in": "Ära vali Ãŧhtegi grupis {group}", "unstack": "Eralda", + "unstack_action_prompt": "{count} eraldatud", "unstacked_assets_count": "{count, plural, one {# Ãŧksus} other {# Ãŧksust}} eraldatud", + "untagged": "Sildistamata", "up_next": "Järgmine", + "update_location_action_prompt": "Uuenda {count} valitud Ãŧksuse asukoht:", "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi Ãŧles", + "upload_action_prompt": "{count} Ãŧleslaadimise ootel", "upload_concurrency": "Üleslaadimise samaaegsus", + "upload_details": "Üleslaadimise Ãŧksikasjad", "upload_dialog_info": "Kas soovid valitud Ãŧksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse Ãŧleslaadimine", "upload_errors": "Üleslaadimine lÃĩpetatud {count, plural, one {# veaga} other {# veaga}}, uute Ãŧksuste nägemiseks värskenda lehte.", + "upload_finished": "Üleslaadimine lÃĩpetatud", "upload_progress": "Ootel {remaining, number} - TÃļÃļdeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud Ãŧksus} other {# dubleeritud Ãŧksust}} vahele jäetud", "upload_status_duplicates": "Duplikaadid", @@ -1892,6 +2060,7 @@ "upload_success": "Üleslaadimine Ãĩnnestus, uute Ãŧksuste nägemiseks värskenda lehte.", "upload_to_immich": "Laadi Immich'isse ({count})", "uploading": "Üleslaadimine", + "uploading_media": "Üksuste Ãŧleslaadimine", "url": "URL", "usage": "Kasutus", "use_biometric": "Kasuta biomeetriat", @@ -1912,6 +2081,7 @@ "user_usage_stats_description": "Vaata konto kasutuse statistikat", "username": "Kasutajanimi", "users": "Kasutajad", + "users_added_to_album_count": "{count, plural, one {# kasutaja} other {# kasutajat}} lisatud albumisse", "utilities": "TÃļÃļriistad", "validate": "Valideeri", "validate_endpoint_error": "Sisesta korrektne URL", @@ -1930,6 +2100,7 @@ "view_album": "Vaata albumit", "view_all": "Vaata kÃĩiki", "view_all_users": "Vaata kÃĩiki kasutajaid", + "view_details": "Vaata Ãŧksikasju", "view_in_timeline": "Vaata ajajoonel", "view_link": "Vaata linki", "view_links": "Vaata linke", @@ -1937,6 +2108,7 @@ "view_next_asset": "Vaata järgmist Ãŧksust", "view_previous_asset": "Vaata eelmist Ãŧksust", "view_qr_code": "Vaata QR-koodi", + "view_similar_photos": "Vaata sarnaseid fotosid", "view_stack": "Vaata virna", "view_user": "Vaata kasutajat", "viewer_remove_from_stack": "Eemalda virnast", @@ -1955,5 +2127,6 @@ "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" + "zoom_image": "Suumi pilti", + "zoom_to_bounds": "Suumi piiridesse" } diff --git a/i18n/eu.json b/i18n/eu.json index f7f695a2d2..bb16f126d6 100644 --- a/i18n/eu.json +++ b/i18n/eu.json @@ -1,17 +1,105 @@ { - "active": "Martxan", + "about": "Honi buruz", + "account": "Kontua", + "account_settings": "Kontuaren Ezarpenak", + "acknowledge": "Onartu", + "action": "Ekintza", + "action_common_update": "Eguneratu", + "actions": "Ekintzak", + "active": "Aktibo", + "activity": "Jarduera", + "activity_changed": "Jarduera {enabled, select, true {ezarrita dago} other {ez dago ezarrita}}", "add": "Gehitu", "add_a_description": "Azalpena gehitu", + "add_a_location": "Kokapena gehitu", "add_a_name": "Izena gehitu", "add_a_title": "Izenburua gehitu", + "add_birthday": "Urtebetetzea gehitu", + "add_endpoint": "Endpoint-a gehitu", + "add_exclusion_pattern": "Bazterketa eredua gehitu", + "add_import_path": "Inportazio bidea gehitu", + "add_location": "Kokapena gehitu", "add_more_users": "Erabiltzaile gehiago gehitu", + "add_partner": "Kidea gehitu", + "add_path": "Bidea gehitu", "add_photos": "Argazkiak gehitu", + "add_tag": "Etiketa gehitu", + "add_to": "Hona gehituâ€Ļ", "add_to_album": "Albumera gehitu", + "add_to_album_bottom_sheet_added": "{album} -(e)ra gehitu", "add_to_album_bottom_sheet_already_exists": "Dagoeneko {album} albumenean", + "add_to_albums": "Albumetara gehitu", + "add_to_albums_count": "Albumetara gehitu ({count})", "add_to_shared_album": "Gehitu partekatutako albumera", "add_url": "URL-a gehitu", + "added_to_archive": "Artxibategira gehituta", "added_to_favorites": "Faboritoetara gehituta", + "added_to_favorites_count": "{count, number} faboritoetara gehituta", "admin": { - "image_quality": "Kalitatea" - } + "add_exclusion_pattern_description": "Gehitu baztertze patroiak. *, ** eta ? karakterak erabil ditzazkezu (globbing). Adibideak: \"Raw\" izeneko edozein direktorioko fitxategi guztiak baztertzeko, erabili \"**/Raw/**\". \".tif\" amaitzen diren fitxategi guztiak baztertzeko, erabili \"**/*.tif\". Bide absolutu bat baztertzeko, erabili \"/baztertu/beharreko/bidea/**\".", + "admin_user": "Administradore erabiltzailea", + "authentication_settings": "Segurtasun Ezarpenak", + "authentication_settings_description": "Kudeatu pasahitza, OAuth edo beste segurtasun konfigurazio bat", + "authentication_settings_disable_all": "Seguru zaude saioa hasteko modu guztiak desgaitu nahi dituzula? Saioa hastea guztiz desgaitua izango da.", + "authentication_settings_reenable": "Berriro gaitzeko, erabili Server Command.", + "background_task_job": "Atzealdeko Lanak", + "backup_onboarding_footer": "Immich-en babes kopiei buruzko informazio gehiago nahi baduzu, mesedez irakurri dokumentazioa.", + "backup_onboarding_title": "Babes Kopiak", + "confirm_delete_library": "Seguru zaude {library} ezabatu nahi duzula?", + "confirm_email_below": "Konfirmatzeko, idatzi \"{email}\" azpian", + "confirm_reprocess_all_faces": "Seguru zaude aurpegi guztiak berriro prozesatu nahi dituzula? Erabakiak jendearen izenak ere borratuko ditu.", + "confirm_user_password_reset": "Seguru zaude {user}-ren pasahitza berrezarri nahi duzula?", + "confirm_user_pin_code_reset": "Seguru zaude {user}-ren PIN kodea berrezarri nahi duzula?", + "create_job": "Gehitu zeregina", + "disable_login": "Desgaitu saio hastea", + "face_detection": "Aurpegi detekzioa", + "failed_job_command": "{command} komandoak hutsegin du {job} zereginerako", + "image_format": "Formatua", + "image_format_description": "WebP ereduak JPEG baino fitxategi txikiagoak sortzen ditu, baina motelagoa da kodifikatzen.", + "image_preview_title": "Aurreikusiaen Konfigurazioa", + "image_quality": "Kalitatea", + "image_settings": "Argazkien Konfigurazioa", + "image_thumbnail_title": "Argazki Txikien Konfigurazioa", + "job_created": "Zeregina sortuta", + "job_settings": "Zereginaren konfigurazioa", + "job_status": "Zereginaren Egoera", + "logging_enable_description": "Gaitu erregistroak", + "logging_level_description": "Erregistroak gaituta daudenean, nolako erregistro maila erabili.", + "logging_settings": "Erregistroak", + "machine_learning_duplicate_detection": "Bizkoizketa Detekzioa", + "machine_learning_duplicate_detection_enabled": "Gaitu bikoizketa detekezioa", + "machine_learning_facial_recognition": "Aurpegi-Ezagutza", + "machine_learning_facial_recognition_description": "Detektatu, ezagutu eta aurpegiak banatu argazkietan", + "machine_learning_facial_recognition_model": "Aurpegi-Ezagutza eredua", + "machine_learning_facial_recognition_setting": "Aurpegi-Ezagutza Gaitu", + "machine_learning_smart_search_enabled": "Gaitu bilaketa arina", + "manage_log_settings": "Kudeatu erregistroen konfigurazioa", + "map_dark_style": "Beltz estiloa", + "map_gps_settings": "Mapa eta GPS Konfigurazioa", + "map_light_style": "Zuri estiloa", + "map_settings": "Mapa", + "metadata_faces_import_setting": "Gaitu aurpegien inportazioa", + "metadata_settings": "Metadata Konfigurazioa", + "metadata_settings_description": "Kudeatu metadaten konfigurazioa", + "migration_job": "Migrazio" + }, + "advanced_settings_readonly_mode_title": "Irakurri-bakarrik Modua", + "apply_count": "Ezarri ({count, number})", + "assets_added_to_albums_count": "Gehituta {assetTotal, plural, one {# asset} other {# assets}} to {albumTotal, plural, one {# album} other {# albums}}", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} ezin izan da albumetara gehitu", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} dagoeneko albumean dago", + "first": "Lehenengo ÂĢLehenikÂģ", + "gps": "GPS", + "gps_missing": "Ez dago GPS", + "last": "Azkena", + "like": "Gustoko", + "manage_geolocation": "Kudeatu kokapena", + "organize_into_albums": "Albumetan antolatu", + "query_asset_id": "Aztertu aukeratutako ID-a", + "readonly_mode_disabled": "Irakurri-bakarrik modua desgaituta", + "readonly_mode_enabled": "Irakurri-bakarrik modua gaituta", + "selected_gps_coordinates": "GPS Koordenadak Aukeratuta", + "sort_newest": "Argazkirik berriena", + "to_select": "aukeratzeko", + "view_similar_photos": "Ikusi antzeko argazkiak" } diff --git a/i18n/fa.json b/i18n/fa.json index 84857479b3..76f8d956fc 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -13,6 +13,7 @@ "add_a_location": "Ø§ŲØ˛ŲˆØ¯Ų† یڊ Ų…ÚŠØ§Ų†", "add_a_name": "Ø§ŲØ˛ŲˆØ¯Ų† Ų†Ø§Ų…", "add_a_title": "Ø§ŲØ˛ŲˆØ¯Ų† ØšŲ†ŲˆØ§Ų†", + "add_birthday": "Ø§ŲØ˛ŲˆØ¯Ų† ØĒØ§ØąÛŒØŽ ØĒŲˆŲ„Ø¯", "add_exclusion_pattern": "Ø§ŲØ˛ŲˆØ¯Ų† Ø§Ų„Ú¯ŲˆÛŒ Ø§ØŗØĒØĢŲ†Ø§", "add_import_path": "Ø§ŲØ˛ŲˆØ¯Ų† Ų…ØŗÛŒØą ŲˆØąŲˆØ¯ÛŒ", "add_location": "Ø§ŲØ˛ŲˆØ¯Ų† Ų…ÚŠØ§Ų†", @@ -22,10 +23,13 @@ "add_photos": "Ø§ŲØ˛ŲˆØ¯Ų† ØšÚŠØŗ Ų‡Ø§", "add_to": "Ø§ŲØ˛ŲˆØ¯Ų† Ø¨Ų‡ â€Ļ", "add_to_album": "Ø§ŲØ˛ŲˆØ¯Ų† Ø¨Ų‡ ØĸŲ„Ø¨ŲˆŲ…", + "add_to_album_bottom_sheet_added": "Ø¨Ų‡ ØĸŲ„Ø¨ŲˆŲ… {album} اØļØ§ŲŲ‡ شد", + "add_to_album_bottom_sheet_already_exists": "Ų‚Ø¨Ų„Ø§ Ø¯Øą ØĸŲ„Ø¨ŲˆŲ… {album} Ų…ŲˆØŦŲˆØ¯ Ø§ØŗØĒ", + "add_to_album_bottom_sheet_some_local_assets": "Ø¨ØąØŽÛŒ Ø§Ø˛ Ų…Ø­ØĒŲˆØ§Ų‡Ø§ÛŒ Ų…Ø­Ų„ÛŒ ØąØ§ Ų†Ø´Ø¯ Ø¨Ų‡ ØĸŲ„Ø¨ŲˆŲ… اØļØ§ŲŲ‡ ÚŠØąØ¯", "add_to_shared_album": "Ø§ŲØ˛ŲˆØ¯Ų† Ø¨Ų‡ ØĸŲ„Ø¨ŲˆŲ… اشØĒØąØ§ÚŠÛŒ", "added_to_archive": "Ø¨Ų‡ ØĸØąØ´ÛŒŲˆ اØļØ§ŲŲ‡ شد", "added_to_favorites": "Ø¨Ų‡ ØšŲ„Ø§Ų‚Ų‡ Ų…Ų†Ø¯ÛŒ Ų‡Ø§ اØļØ§ŲŲ‡ شد", - "added_to_favorites_count": "{count} ØĒا اØļØ§ŲŲ‡ شد Ø¨Ų‡ ØšŲ„Ø§Ų‚Ų‡ Ų…Ų†Ø¯ÛŒ Ų‡Ø§", + "added_to_favorites_count": "{count, number} ØĒا Ø¨Ų‡ ØšŲ„Ø§Ų‚Ų‡ Ų…Ų†Ø¯ÛŒ Ų‡Ø§ اØļØ§ŲŲ‡ شد", "admin": { "add_exclusion_pattern_description": "Ø§Ų„Ú¯ŲˆŲ‡Ø§ÛŒ Ø§ØŗØĒØĢŲ†Ø§ ØąØ§ اØļØ§ŲŲ‡ ÚŠŲ†ÛŒØ¯. ŲžØ´ØĒÛŒØ¨Ø§Ų†ÛŒ Ø§Ø˛ Ú¯Ų„Ø§Ø¨ÛŒŲ†Ú¯ با Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø§Ø˛ *, ** ؈ ? ؈ØŦŲˆØ¯ Ø¯Ø§ØąØ¯. Ø¨ØąØ§ÛŒ Ų†Ø§Ø¯ÛŒØ¯Ų‡ Ú¯ØąŲØĒŲ† ØĒŲ…Ø§Ų… ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ Ø¯Øą Ų‡Øą Ø¯Ø§ÛŒØąÚŠØĒŲˆØąÛŒ با Ų†Ø§Ų… \"Raw\"، Ø§Ø˛ \"**/Raw/**\" Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯. Ø¨ØąØ§ÛŒ Ų†Ø§Ø¯ÛŒØ¯Ų‡ Ú¯ØąŲØĒŲ† ØĒŲ…Ø§Ų… ŲØ§ÛŒŲ„â€ŒŲ‡Ø§ÛŒÛŒ ÚŠŲ‡ با \".tif\" ŲžØ§ÛŒØ§Ų† Ų…ÛŒâ€ŒÛŒØ§Ø¨Ų†Ø¯ØŒ Ø§Ø˛ \"**/*.tif\" Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯. Ø¨ØąØ§ÛŒ Ų†Ø§Ø¯ÛŒØ¯Ų‡ Ú¯ØąŲØĒŲ† یڊ Ų…ØŗÛŒØą Ų…ØˇŲ„Ų‚ØŒ Ø§Ø˛ \"/path/to/ignore/**\" Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯.", "authentication_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ø§Ø­ØąØ§Ø˛ Ų‡ŲˆÛŒØĒ", @@ -471,7 +475,6 @@ "library": "ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡", "library_options": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ÚŠØĒØ§Ø¨ØŽØ§Ų†Ų‡", "light": "ØąŲˆØ´Ų†", - "link_options": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ Ų„ÛŒŲ†ÚŠ", "link_to_oauth": "اØĒØĩØ§Ų„ Ø¨Ų‡ OAuth", "linked_oauth_account": "Ø­ØŗØ§Ø¨ OAuth Ų…ØĒØĩŲ„ Ø´Ø¯Ų‡", "list": "Ų„ÛŒØŗØĒ", @@ -493,7 +496,6 @@ "manage_your_devices": "Ų…Ø¯ÛŒØąÛŒØĒ Ø¯ØŗØĒÚ¯Ø§Ų‡â€ŒŲ‡Ø§ÛŒ Ų…ØĒØĩŲ„", "manage_your_oauth_connection": "Ų…Ø¯ÛŒØąÛŒØĒ اØĒØĩØ§Ų„ OAuth", "map": "Ų†Ų‚Ø´Ų‡", - "map_assets_in_bound": "{count} ØšÚŠØŗ", "map_assets_in_bounds": "{count} ØšÚŠØŗ Ų‡Ø§", "map_cannot_get_user_location": "Ų…ŲˆŲ‚ØšÛŒØĒ Ų…ÚŠØ§Ų†ÛŒ Ø¯Øą Ø¯ØŗØĒØąØŗ Ų†ÛŒØŗØĒ", "map_location_dialog_yes": "Ø¨Ų„Ų‡", @@ -502,7 +504,6 @@ "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_title": "Ø¯ØŗØĒØąØŗÛŒ Ø¨Ų‡ Ų…ŲˆŲ‚ØšÛŒØĒ Ø´Ų…Ø§ ŲØšØ§Ų„ Ų†ÛŒØŗØĒ", "map_settings": "ØĒŲ†Ø¸ÛŒŲ…Ø§ØĒ Ų†Ų‚Ø´Ų‡", diff --git a/i18n/fi.json b/i18n/fi.json index f41d0cf631..769b528f4c 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -14,6 +14,7 @@ "add_a_location": "Lisää sijainti", "add_a_name": "Lisää nimi", "add_a_title": "Lisää otsikko", + "add_birthday": "Lisää syntymäpäivä", "add_endpoint": "Lisää päätepiste", "add_exclusion_pattern": "Lisää poissulkemismalli", "add_import_path": "Lisää tuontipolku", @@ -27,6 +28,9 @@ "add_to_album": "Lisää albumiin", "add_to_album_bottom_sheet_added": "Lisätty albumiin {album}", "add_to_album_bottom_sheet_already_exists": "Kohde on jo albumissa {album}", + "add_to_album_toggle": "Vaihda albumin {album} valintaa", + "add_to_albums": "Lisää albumeihin", + "add_to_albums_count": "Lisää albumeihin ({count})", "add_to_shared_album": "Lisää jaettuun albumiin", "add_url": "Lisää URL", "added_to_archive": "Lisätty arkistoon", @@ -44,6 +48,13 @@ "backup_database": "Luo tietokantavedos", "backup_database_enable_description": "Ota tietokantavedokset käyttÃļÃļn", "backup_keep_last_amount": "Säilytettävien tietokantavedosten määrä", + "backup_onboarding_1_description": "Kopio pilvipalvelussa tai toisessa fyysisessä sijainnissa.", + "backup_onboarding_2_description": "Paikalliset kopiot eri laitteilla. Tämä sisältää sekä alkuperäiset tiedostot että niiden varmuuskopiot paikallisesti.", + "backup_onboarding_3_description": "Kopiot tiedoistasi, mukaan lukien alkuperäiset tiedostot. Tähän sisältyy yksi etäsijainnissa oleva kopio ja kaksi paikallista kopiota.", + "backup_onboarding_description": "Suosittelemme 3-2-1-varmuuskopiointistrategiaa tietojesi suojaamiseksi. Säilytä kopiot sekä ladatuista valokuvista ja videoista että Immichin tietokannasta kattavaa varmuuskopiointiratkaisua varten.", + "backup_onboarding_footer": "Lisätietoja Immich varmuuskopioinnista lÃļydät dokumentaatiosta.", + "backup_onboarding_parts_title": "3-2-1-varmuuskopio sisältää:", + "backup_onboarding_title": "Varmuuskopiot", "backup_settings": "Tietokantavedosten asetukset", "backup_settings_description": "Hallitse tietokannan vedosasetuksia.", "cleared_jobs": "TyÃļn {job} tehtävät tyhjennetty", @@ -112,6 +123,13 @@ "logging_enable_description": "Ota lokikirjaus käyttÃļÃļn", "logging_level_description": "Kun käytÃļssä, mitä lokituksen tasoa käytetään.", "logging_settings": "Lokit", + "machine_learning_availability_checks": "Saatavuustarkastukset", + "machine_learning_availability_checks_description": "Automaattisesti havaitse ja suosi vapaita koneoppimisen palvelimia", + "machine_learning_availability_checks_enabled": "Laita päälle saatavuus tarkistukset", + "machine_learning_availability_checks_interval": "Tarkastusväli", + "machine_learning_availability_checks_interval_description": "Aikaväli millisekunneissa saavutettavuus tarkistuksille", + "machine_learning_availability_checks_timeout": "PyynnÃļn aikakatkaisu", + "machine_learning_availability_checks_timeout_description": "Aikakatkaisu aika millisekunneissa saatavuus tarkistuksille", "machine_learning_clip_model": "CLIP-malli", "machine_learning_clip_model_description": "Käytettävän CLIP-mallin nimi toimivien mallien listasta. Huomaa että sinun täytyy suorittaa \"Älykäs etsintä\"-tyÃļ uudelleen vaihdettuasi käytettävää mallia.", "machine_learning_duplicate_detection": "Kaksoiskappaleiden tunnistus", @@ -166,6 +184,20 @@ "metadata_settings_description": "Hallitse metatietoja", "migration_job": "Migraatio", "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", + "nightly_tasks_cluster_faces_setting_description": "Aja kasvojen tunnistus uusiin tunnistettuihin kasvoihin", + "nightly_tasks_cluster_new_faces_setting": "Kokoa uudet kasvot", + "nightly_tasks_database_cleanup_setting": "Tietokannan puhdistuksen tehtävät", + "nightly_tasks_database_cleanup_setting_description": "Siivoa vanhentunut data tietokannasta", + "nightly_tasks_generate_memories_setting": "Luo muistoja", + "nightly_tasks_generate_memories_setting_description": "Luo kohteista uusia muistoja", + "nightly_tasks_missing_thumbnails_setting": "Luo puuttuvat pikkukuvat", + "nightly_tasks_missing_thumbnails_setting_description": "Laita ilman pikkukuvia olevat kohteet jonoon pikkukuvien luontia varten", + "nightly_tasks_settings": "YÃļllisten tehtävien asetukset", + "nightly_tasks_settings_description": "Hallitse yÃļllisiä tehtäviä", + "nightly_tasks_start_time_setting": "Aloitusaika", + "nightly_tasks_start_time_setting_description": "Aika jolloin palvelin aloittaa yÃļllisten tehtävien ajon", + "nightly_tasks_sync_quota_usage_setting": "SynkronointikiintiÃļn käyttÃļ", + "nightly_tasks_sync_quota_usage_setting_description": "Päivitä käyttäjän tallennustilan kiintiÃļ nykyisen käytÃļn mukaan", "no_paths_added": "Polkuja ei asetettu", "no_pattern_added": "Kaavoja ei lisättynä", "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", @@ -196,6 +228,8 @@ "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override_description": "Ota käyttÃļÃļn kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", + "oauth_role_claim": "Roolin vaatimus", + "oauth_role_claim_description": "Salli pääkäyttäjän pääsyoikeus automaattisesti tämän vaatimuksen perusteella. Vaatimus voi sisältää, joko 'käyttäjän' tai 'pääkäyttäjän'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", @@ -244,6 +278,7 @@ "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyÃļ", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", + "storage_template_onboarding_description_v2": "Päälle kytkettynä, toiminto järjestestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -330,6 +365,9 @@ "trash_number_of_days_description": "Kuinka monta päivää aineistoja pidetään roskakorissa ennen pysyvää poistamista", "trash_settings": "Roskakorin asetukset", "trash_settings_description": "Hallitse roskakoriasetuksia", + "unlink_all_oauth_accounts": "Irrota kaikki OAuth-tilit", + "unlink_all_oauth_accounts_description": "Muista irrottaa kaikki OAuth-tilit ennen uuteen palveluntarjoajaan siirtymistä.", + "unlink_all_oauth_accounts_prompt": "Haluatko varmasti irrottaa kaikki OAuth-tilit? Tämä nollaa OAuth-tunnistautumisen kaikille käyttäjille eikä sitä voi perua.", "user_cleanup_job": "Käyttäjien puhdistus", "user_delete_delay": "Käyttäjän {user} tili ja aineistot aikataulutetaan poistettavaksi ajan kuluttua: {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Poiston viive", @@ -359,10 +397,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Käytä tätä vaihtoehtoa suodattaaksesi mediaa synkronoinnin aikana vaihtoehtoisten kriteerien perusteella. Kokeile tätä vain, jos sovelluksessa on ongelmia kaikkien albumien tunnistamisessa.", "advanced_settings_enable_alternate_media_filter_title": "[KOKEELLINEN] Käytä vaihtoehtoisen laitteen albumin synkronointisuodatinta", "advanced_settings_log_level_title": "Kirjaustaso: {level}", - "advanced_settings_prefer_remote_subtitle": "Jotkut laitteet ovat erittäin hitaita lataamaan esikatselukuvia laitteen kohteista. Aktivoi tämä asetus käyttääksesi etäkuvia.", + "advanced_settings_prefer_remote_subtitle": "Jotkut laitteet ovat erittäin hitaita lataamaan esikatselukuvia paikallisista kohteista. Aktivoi tämä asetus käyttääksesi etäkuvia.", "advanced_settings_prefer_remote_title": "Suosi etäkuvia", "advanced_settings_proxy_headers_subtitle": "Määritä välityspalvelimen otsikot(proxy headers), jotka Immichin tulisi lähettää jokaisen verkkopyynnÃļn mukana", "advanced_settings_proxy_headers_title": "Välityspalvelimen otsikot", + "advanced_settings_readonly_mode_subtitle": "Aktivoi vain luku -tilan, jolloin valokuvia voi ainoastaan selata. Toiminnot kuten useiden kuvien valitseminen, jakaminen, siirtäminen toistolaitteelle ja poistaminen ovat pois käytÃļstä. Laita vain luku -tila päälle tai pois päältä päävalikon käyttäjäkuvakkeesta", + "advanced_settings_readonly_mode_title": "Vain luku -tila", "advanced_settings_self_signed_ssl_subtitle": "Ohita SSL sertifikaattivarmennus palvelimen päätepisteellä. Vaaditaan self-signed -sertifikaateissa.", "advanced_settings_self_signed_ssl_title": "Salli self-signed SSL -sertifikaatit", "advanced_settings_sync_remote_deletions_subtitle": "Poista tai palauta kohde automaattisesti tällä laitteella, kun kyseinen toiminto suoritetaan verkossa", @@ -378,6 +418,7 @@ "album_cover_updated": "Albumin kansikuva päivitetty", "album_delete_confirmation": "Haluatko varmasti poistaa albumin {album}?", "album_delete_confirmation_description": "Jos albumi on jaettu, muut eivät pääse siihen enää.", + "album_deleted": "Albumi poistettu", "album_info_card_backup_album_excluded": "JÄTETTY POIS", "album_info_card_backup_album_included": "SISÄLLYTETTY", "album_info_updated": "Albumin tiedot päivitetty", @@ -387,7 +428,9 @@ "album_options": "Albumin asetukset", "album_remove_user": "Poista käyttäjä?", "album_remove_user_confirmation": "Oletko varma että haluat poistaa {user}?", + "album_search_not_found": "Haullasi ei lÃļytynyt yhtään albumia", "album_share_no_users": "Näyttää että olet jakanut tämän albumin kaikkien kanssa, tai sinulla ei ole käyttäjiä joille jakaa.", + "album_summary": "Albumi tiivistelmä", "album_updated": "Albumi päivitetty", "album_updated_setting_description": "Saa sähkÃļpostia kun jaetussa albumissa on uutta sisältÃļä", "album_user_left": "Poistuttiin albumista {album}", @@ -403,6 +446,10 @@ "album_with_link_access": "Anna kenen tahansa nähdä linkin kautta tämän albumin valokuvat ja henkilÃļt.", "albums": "Albumit", "albums_count": "{count, plural, one {{count, number} albumi} other {{count, number} albumia}}", + "albums_default_sort_order": "Albumin oletuslajittelujärjestys", + "albums_default_sort_order_description": "Kohteiden ensisijainen lajittelujärjestys uusia albumeja luotaessa.", + "albums_feature_description": "Kokoelma kohteita, jotka voidaan jakaa muille käyttäjille.", + "albums_on_device_count": "({count}) albumia laitteella", "all": "Kaikki", "all_albums": "Kaikki albumit", "all_people": "Kaikki henkilÃļt", @@ -422,7 +469,9 @@ "app_bar_signout_dialog_title": "Kirjaudu ulos", "app_settings": "Sovellusasetukset", "appears_in": "Esiintyy albumeissa", + "apply_count": "Aseta {count, number}", "archive": "Arkisto", + "archive_action_prompt": "{count} lisätty arkistoon", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_page_no_archived_assets": "Arkistoituja kohteita ei lÃļytynyt", "archive_page_title": "Arkisto ({count})", @@ -453,6 +502,7 @@ "asset_restored_successfully": "Kohde palautettu onnistuneesti", "asset_skipped": "Ohitettu", "asset_skipped_in_trash": "Roskakorissa", + "asset_trashed": "Kohde poistettu", "asset_uploaded": "Lähetetty", "asset_uploading": "Ladataanâ€Ļ", "asset_viewer_settings_subtitle": "Galleriakatseluohjelman asetusten hallinta", @@ -460,10 +510,14 @@ "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", - "assets_added_to_name_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}} {hasName, select, true {{name}} other {uuteen albumiin}}", + "assets_added_to_albums_count": "Lisätty {assetTotal, plural, one {# aineisto} other {# aaineistoa}} {albumTotal, plural, one {# albumiin} other {# albumeihin}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin", + "assets_cannot_be_added_to_albums": "{count, plural, one {Aineisto} other {Aineistoa}} ei voi lisätä mihinkään albumiin", "assets_count": "{count, plural, one {# media} other {# mediaa}}", "assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi", "assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta", + "assets_downloaded_failed": "{count, plural, one {Ladattu # tiedosto - {error} tiedosto epäonnistui} other {ladattu # tiedostoa - {error} tiedostot epäonnistuivat}}", + "assets_downloaded_successfully": "{count, plural, one {Ladattu # tiedosto onnistuneesti} other {Ladattu # tiedostoa onnistuneesti}}", "assets_moved_to_trash_count": "Siirretty {count, plural, one {# media} other {# mediaa}} roskakoriin", "assets_permanently_deleted_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "assets_removed_count": "{count, plural, one {# media} other {# mediaa}} poistettu", @@ -475,13 +529,18 @@ "assets_trashed_count": "{count, plural, one {# media} other {# mediaa}} siirretty roskakoriin", "assets_trashed_from_server": "{count} kohdetta siirretty roskakoriin Immich-palvelimelta", "assets_were_part_of_album_count": "{count, plural, one {Media oli} other {Mediat olivat}} jo albumissa", + "assets_were_part_of_albums_count": "{count, plural, one {Aineisto} other {Aineistoa}} oli jo albumeissa", "authorized_devices": "Valtuutetut laitteet", "automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla", "automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto", + "autoplay_slideshow": "Toista diaesitys automaattisesti", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", + "background_backup_running_error": "Tausta varmuuskopiointi on aktiivinen, ei voida aloittaa manuaalista varmuuskopiointia", "background_location_permission": "Taustasijainnin käyttÃļoikeus", "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "background_options": "Tausta valinnat", + "backup": "Varmuuskopiointi", "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", "backup_album_selection_page_assets_scatter": "Kohteet voivat olla hajaantuneina useisiin albumeihin. Albumeita voidaan sisällyttää varmuuskopiointiin tai jättää siitä pois.", @@ -542,8 +601,10 @@ "backup_manual_in_progress": "Lähetys palvelimelle on jo käynnissä. Kokeile myÃļhemmin uudelleen", "backup_manual_success": "Onnistui", "backup_manual_title": "Lähetyksen tila", + "backup_options": "Varmuuskopiointiasetukset", "backup_options_page_title": "Varmuuskopioinnin asetukset", "backup_setting_subtitle": "Hallinnoi aktiivisia ja taustalla olevia lähetysasetuksia", + "backup_settings_subtitle": "Hallitse lähetysasetuksia", "backward": "Taaksepäin", "biometric_auth_enabled": "Biometrinen tunnistautuminen käytÃļssä", "biometric_locked_out": "Sinulta on evätty pääsy biometriseen tunnistautumiseen", @@ -562,7 +623,7 @@ "cache_settings_clear_cache_button": "Tyhjennä välimuisti", "cache_settings_clear_cache_button_title": "Tyhjennä sovelluksen välimuisti. Tämä vaikuttaa merkittävästi sovelluksen suorituskykyyn, kunnes välimuisti on rakennettu uudelleen.", "cache_settings_duplicated_assets_clear_button": "Tyhjennä", - "cache_settings_duplicated_assets_subtitle": "Sovelluksen mustalle listalle merkitsemät valokuvat ja videot", + "cache_settings_duplicated_assets_subtitle": "Sovelluksen sivuutettavaksi merkitsemät valokuvat ja videot", "cache_settings_duplicated_assets_title": "Kaksoiskappaleet ({count})", "cache_settings_statistics_album": "Kirjaston esikatselukuvat", "cache_settings_statistics_full": "Täysikokoiset kuvat", @@ -579,10 +640,12 @@ "cancel": "Peruuta", "cancel_search": "Peru haku", "canceled": "Peruutettu", + "canceling": "Peruutetaan", "cannot_merge_people": "Ihmisiä ei voitu yhdistää", "cannot_undo_this_action": "Et voi perua tätä toimintoa!", "cannot_update_the_description": "Kuvausta ei voi päivittää", - "cast": "Lähettää", + "cast": "Suoratoisto", + "cast_description": "Määritä saatavilla olevat suoratoistopalvelut", "change_date": "Vaihda päiväys", "change_description": "Muuta kuvausta", "change_display_order": "Muuta näyttÃļjärjestystä", @@ -609,6 +672,7 @@ "clear": "Tyhjennä", "clear_all": "Tyhjennä kaikki", "clear_all_recent_searches": "Tyhjennä viimeisimmät haut", + "clear_file_cache": "Tyhjennä tiedostovälimuisti", "clear_message": "Tyhjennä viesti", "clear_value": "Tyhjää arvo", "client_cert_dialog_msg_confirm": "OK", @@ -641,6 +705,7 @@ "confirm_password": "Vahvista salasana", "confirm_tag_face": "Haluatko merkitä nämä kasvot nimellä {name}?", "confirm_tag_face_unnamed": "MerkitäänkÃļ nämä kasvot?", + "connected_device": "Yhdistetty laite", "connected_to": "Yhdistetty", "contain": "Mahduta", "context": "Konteksti", @@ -678,6 +743,7 @@ "create_new_user": "Luo uusi käyttäjä", "create_shared_album_page_share_add_assets": "LISÄÄ KOHTEITA", "create_shared_album_page_share_select_photos": "Valitse kuvat", + "create_shared_link": "Luo jakolinkki", "create_tag": "Luo tunniste", "create_tag_description": "Luo uusi tunniste. Sisäkkäisiä tunnisteita varten syÃļtä tunnisteen täydellinen polku kauttaviivat mukaan luettuna.", "create_user": "Luo käyttäjä", @@ -690,9 +756,11 @@ "current_server_address": "Nykyinen palvelinosoite", "custom_locale": "Muokatut maa-asetukset", "custom_locale_description": "Muotoile päivämäärät ja numerot perustuen alueen kieleen", + "custom_url": "Mukautettu URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tumma", + "dark_theme": "Vaihda tumma teema", "date_after": "Päivämäärän jälkeen", "date_and_time": "Päivämäärä ja aika", "date_before": "Päivä ennen", @@ -700,6 +768,7 @@ "date_of_birth_saved": "Syntymäaika tallennettu", "date_range": "Päivämäärän rajaus", "day": "Päivä", + "days": "Päivää", "deduplicate_all": "Poista kaikkien kaksoiskappaleet", "deduplication_criteria_1": "Kuvan koko tavuina", "deduplication_criteria_2": "EXIF-datan määrä", @@ -708,6 +777,8 @@ "default_locale": "Oletuskieliasetus", "default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan", "delete": "Poista", + "delete_action_confirmation_message": "Haluatko varmasti poistaa tämän aineiston? Tämä toiminto siirtää aineiston palvelimen roskakoriin ja kysyy, haluatko poistaa sen myÃļs paikallisesti", + "delete_action_prompt": "{count} poistettu", "delete_album": "Poista albumi", "delete_api_key_prompt": "Haluatko varmasti poistaa tämän API-avaimen?", "delete_dialog_alert": "Nämä kohteet poistetaan pysyvästi Immich:stä ja laitteeltasi", @@ -721,9 +792,12 @@ "delete_key": "Poista avain", "delete_library": "Poista kirjasto", "delete_link": "Poista linkki", + "delete_local_action_prompt": "{count} poistettu paikallisesti", "delete_local_dialog_ok_backed_up_only": "Poista vain varmuuskopioidut", "delete_local_dialog_ok_force": "Poista kuitenkin", "delete_others": "Poista muut", + "delete_permanently": "Poista pysyvästi", + "delete_permanently_action_prompt": "{count} poistettu pysyvästi", "delete_shared_link": "Poista jaettu linkki", "delete_shared_link_dialog_title": "Poista jaettu linkki", "delete_tag": "Poista tunniste", @@ -734,22 +808,25 @@ "description": "Kuvaus", "description_input_hint_text": "Lisää kuvaus...", "description_input_submit_error": "Virhe kuvauksen päivittämisessä, tarkista lisätiedot lokista", + "deselect_all": "Poista valinnat", "details": "Tiedot", "direction": "Suunta", "disabled": "Poistettu käytÃļstä", "disallow_edits": "Älä salli muokkauksia", "discord": "Discord", "discover": "Tutki", + "discovered_devices": "LÃļydetyt laitteet", "dismiss_all_errors": "Sivuuta kaikki virheet", "dismiss_error": "Sivuuta virhe", "display_options": "NäyttÃļasetukset", "display_order": "NäyttÃļjärjestys", "display_original_photos": "Näytä alkuperäiset kuvat", - "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva peukalokuvan sijasta kun alkuperäinen aineisto on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", + "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva esikatselukuvan sijasta, kun alkuperäinen kuva on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", "do_not_show_again": "Älä näytä tätä enää", "documentation": "Dokumentaatio", "done": "Valmis", "download": "Lataa", + "download_action_prompt": "Ladataan {count} kohdetta", "download_canceled": "Lataus peruutettu", "download_complete": "Lataus valmis", "download_enqueue": "Latausjonossa", @@ -776,8 +853,12 @@ "edit": "Muokkaa", "edit_album": "Muokkaa albumia", "edit_avatar": "Muokkaa avataria", + "edit_birthday": "Muokkaa syntymäpäivää", "edit_date": "Muokkaa päiväystä", "edit_date_and_time": "Muokkaa päivämäärää ja kellonaikaa", + "edit_date_and_time_action_prompt": "{count} päivämäärää ja aikaa muutettu", + "edit_date_and_time_by_offset": "Muuta päivämäärää siirtymällä", + "edit_date_and_time_by_offset_interval": "Uusi päivämääräväli: {from} - {to}", "edit_description": "Muokkaa kuvausta", "edit_description_prompt": "Valitse uusi kuvaus:", "edit_exclusion_pattern": "Muokkaa poissulkemismallia", @@ -787,6 +868,7 @@ "edit_key": "Muokkaa avainta", "edit_link": "Muokkaa linkkiä", "edit_location": "Muokkaa sijaintia", + "edit_location_action_prompt": "{count} sijaintia muokattu", "edit_location_dialog_title": "Sijainti", "edit_name": "Muokkaa nimeä", "edit_people": "Muokkaa henkilÃļitä", @@ -794,7 +876,7 @@ "edit_title": "Muokkaa otsikkoa", "edit_user": "Muokkaa käyttäjää", "edited": "Muokattu", - "editor": "Editori", + "editor": "Muokkaaja", "editor_close_without_save_prompt": "Muutoksia ei tallenneta", "editor_close_without_save_title": "Suljetaanko editori?", "editor_crop_tool_h2_aspect_ratios": "Kuvasuhteet", @@ -805,6 +887,7 @@ "empty_trash": "Tyhjennä roskakori", "empty_trash_confirmation": "Haluatko varmasti tyhjentää roskakorin? Tämä poistaa pysyvästi kaikki tiedostot Immich:stä.\nToimintoa ei voi perua!", "enable": "Ota käyttÃļÃļn", + "enable_backup": "Ota varmuuskopiointi käyttÃļÃļn", "enable_biometric_auth_description": "SyÃļtä PIN-koodisi ottaaksesi biometrisen tunnistautumisen käyttÃļÃļn", "enabled": "KäytÃļssä", "end_date": "Päättymispäivä", @@ -848,6 +931,7 @@ "failed_to_load_notifications": "Ilmoitusten lataaminen epäonnistui", "failed_to_load_people": "HenkilÃļiden lataus epäonnistui", "failed_to_remove_product_key": "Tuoteavaimen poistaminen epäonnistui", + "failed_to_reset_pin_code": "PIN-koodin nollaus epäonnistui", "failed_to_stack_assets": "Medioiden pinoaminen epäonnistui", "failed_to_unstack_assets": "Medioiden pinoamisen purku epäonnistui", "failed_to_update_notification_status": "Ilmoituksen tilan päivittäminen epäonnistui", @@ -856,6 +940,7 @@ "paths_validation_failed": "{paths, plural, one {# polun} other {# polun}} validointi epäonnistui", "profile_picture_transparent_pixels": "Profiilikuvassa ei voi olla läpinäkyviä pikseleitä. Zoomaa lähemmäs ja/tai siirrä kuvaa.", "quota_higher_than_disk_size": "Asettamasi kiintiÃļ on suurempi kuin levyn koko", + "something_went_wrong": "Jotain meni pieleen", "unable_to_add_album_users": "Käyttäjiä ei voi lisätä albumiin", "unable_to_add_assets_to_shared_link": "Medioiden lisääminen jaettuun linkkiin epäonnistui", "unable_to_add_comment": "Kommentin lisääminen epäonnistui", @@ -941,13 +1026,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Lisää kuvausâ€Ļ", + "exif_bottom_sheet_description_error": "Kuvauksen muuttaminen epäonnistui", "exif_bottom_sheet_details": "TIEDOT", "exif_bottom_sheet_location": "SIJAINTI", "exif_bottom_sheet_people": "IHMISET", "exif_bottom_sheet_person_add_person": "Lisää nimi", - "exif_bottom_sheet_person_age_months": "Ikä {months} kuukautta", - "exif_bottom_sheet_person_age_year_months": "Ikä 1 vuosi, {months} kuukautta", - "exif_bottom_sheet_person_age_years": "Ikä {years}", "exit_slideshow": "Poistu diaesityksestä", "expand_all": "Laajenna kaikki", "experimental_settings_new_asset_list_subtitle": "TyÃļn alla", @@ -961,6 +1044,8 @@ "explorer": "Selain", "export": "Vie", "export_as_json": "Vie JSON-muodossa", + "export_database": "Vie tietokanta", + "export_database_description": "Vie SQLite-tietokanta", "extension": "Tiedostopääte", "external": "Ulkoisesta", "external_libraries": "Ulkoiset kirjastot", @@ -972,6 +1057,7 @@ "failed_to_load_assets": "Kohteiden lataus epäonnistui", "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", + "favorite_action_prompt": "{count} lisätty suosikkeihin", "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", "favorites": "Suosikit", "favorites_page_no_favorites": "Suosikkikohteita ei lÃļytynyt", @@ -986,15 +1072,18 @@ "filter_people": "Suodata henkilÃļt", "filter_places": "Suodata paikkoja", "find_them_fast": "LÃļydä nopeasti hakemalla nimellä", + "first": "Ensimmäinen", "fix_incorrect_match": "Korjaa virheellinen osuma", "folder": "Kansio", "folder_not_found": "Kansiota ei lÃļytynyt", "folders": "Kansiot", "folders_feature_description": "Käytetään kansionäkymää valokuvien ja videoiden selaamiseen järjestelmässä", + "forgot_pin_code_question": "Unohditko PIN-koodisi?", "forward": "Eteenpäin", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Ominaisuus lataa ulkoisia resursseja Googlelta toimiakseen.", "general": "Yleinen", + "geolocation_instruction_location": "Napsauta kuvaa, jossa on GPS-koordinaatit, käyttääksesi sen sijaintia, tai valitse sijainti suoraan kartalta", "get_help": "Hae apua", "get_wifiname_error": "Wi-Fi-verkon nimen hakeminen epäonnistui. Varmista, että olet myÃļntänyt tarvittavat käyttÃļoikeudet ja että olet yhteydessä Wi-Fi-verkkoon", "getting_started": "Aloittaminen", @@ -1011,6 +1100,9 @@ "haptic_feedback_switch": "Ota haptinen palaute käyttÃļÃļn", "haptic_feedback_title": "Haptinen palaute", "has_quota": "On kiintiÃļ", + "hash_asset": "Tiivistetty kohde", + "hashed_assets": "Tiivistettyä kohdetta", + "hashing": "Tiivistys", "header_settings_add_header_tip": "Lisää otsikko", "header_settings_field_validator_msg": "Arvo ei voi olla tyhjä", "header_settings_header_name_input": "Otsikon nimi", @@ -1042,7 +1134,9 @@ "home_page_upload_err_limit": "Voit lähettää palvelimelle enintään 30 kohdetta kerrallaan, ohitetaan", "host": "Isäntä", "hour": "Tunti", + "hours": "Tunnit", "id": "ID", + "idle": "Toimeton", "ignore_icloud_photos": "Ohita iCloud-kuvat", "ignore_icloud_photos_description": "iCloudiin tallennettuja kuvia ei ladata Immich-palvelimelle", "image": "Kuva", @@ -1086,6 +1180,8 @@ "ios_debug_info_last_sync_at": "Viimeisin synkronisointi {dateTime}", "ios_debug_info_no_processes_queued": "Ei taustaprosesseja jonossa", "ios_debug_info_no_sync_yet": "Taustasynkronisointia ei ole suoritettu vielä", + "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprosessi jonossa} other {{count} taustaprosessia jonossa}}", + "ios_debug_info_processing_ran_at": "Prosessi valmistui {dateTime}", "items_count": "{count, plural, one {# kpl} other {# kpl}}", "jobs": "Taustatehtävät", "keep": "Säilytä", @@ -1094,11 +1190,17 @@ "kept_this_deleted_others": "Tämä kohde säilytettiin. {count, plural, one {# asset} other {# assets}} poistettiin", "keyboard_shortcuts": "Pikanäppäimet", "language": "Kieli", + "language_no_results_subtitle": "Yritä säätää hakuehtoja", + "language_no_results_title": "Kieliä ei lÃļydetty", + "language_search_hint": "Etsi kieliä...", "language_setting_description": "Valitse suosimasi kieli", + "large_files": "Suuret tiedostot", + "last": "Viimeinen", "last_seen": "Viimeksi nähty", "latest_version": "Viimeisin versio", "latitude": "Leveysaste", - "leave": "Lähde", + "leave": "Poistu", + "leave_album": "Poistu albumista", "lens_model": "Objektiivin malli", "let_others_respond": "Anna muiden vastata", "level": "Taso", @@ -1110,15 +1212,19 @@ "library_page_sort_created": "Viimeisin luotu", "library_page_sort_last_modified": "Viimeksi muokattu", "library_page_sort_title": "Albumin otsikko", + "licenses": "Lisenssit", "light": "Vaalea", + "like": "Tykkää", "like_deleted": "Tykkäys poistettu", "link_motion_video": "Linkitä liikevideo", - "link_options": "Linkin asetukset", "link_to_oauth": "Linkki OAuth", "linked_oauth_account": "Linkitetty OAuth-tili", "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", + "local": "Paikallinen", + "local_asset_cast_failed": "Kohdetta, joka ei ole ladattuna palvelimelle, ei voida striimata", + "local_assets": "Paikalliset kohteet", "local_network": "Lähiverkko", "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", "location_permission": "Sijainnin käyttÃļoikeus", @@ -1132,6 +1238,7 @@ "locked_folder": "Lukittu kansio", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", + "logged_in_as": "Kirjautunut käyttäjänä {user}", "logged_out_all_devices": "Kaikki laitteet kirjattu ulos", "logged_out_device": "Laite kirjattu ulos", "login": "Kirjaudu", @@ -1174,8 +1281,7 @@ "manage_your_devices": "Hallitse sisäänkirjautuneita laitteitasi", "manage_your_oauth_connection": "Hallitse OAuth-yhteyttäsi", "map": "Kartta", - "map_assets_in_bound": "{count} kuva", - "map_assets_in_bounds": "{count} kuvaa", + "map_assets_in_bounds": "{count, plural, =0 {Ei kuvia tällä alueella} one {# kuva} other {# kuvaa}}", "map_cannot_get_user_location": "Käyttäjän sijaintia ei voitu määrittää", "map_location_dialog_yes": "Kyllä", "map_location_picker_page_use_location": "Käytä tätä sijaintia", @@ -1183,7 +1289,6 @@ "map_location_service_disabled_title": "Paikannuspalvelu pois päältä", "map_marker_for_images": "Karttamarkerointi kuville, jotka on otettu kaupungissa {city}, maassa {country}", "map_marker_with_image": "Karttamarkerointi kuvalla", - "map_no_assets_in_bounds": "Ei kuvia tällä alueella", "map_no_location_permission_content": "Paikannuslupa tarvitaan, jotta nykyisen sijainnin kohteita voidaan näyttää. Haluatko sallia pääsyn sijaintiin?", "map_no_location_permission_title": "Paikannuslupa estetty", "map_settings": "Kartta-asetukset", @@ -1220,13 +1325,15 @@ "merged_people_count": "{count, plural, one {# HenkilÃļ} other {# henkilÃļä}} yhdistetty", "minimize": "PIenennä", "minute": "Minuutti", - "missing": "Puuttuu", + "minutes": "Minuutit", + "missing": "Puuttuvat", "model": "Malli", "month": "Kuukauden mukaan", "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", "move": "Siirrä", "move_off_locked_folder": "Siirrä pois lukitusta kansiosta", + "move_to_lock_folder_action_prompt": "{count} lisätty lukittuun kansioon", "move_to_locked_folder": "Siirrä lukittuun kansioon", "move_to_locked_folder_confirmation": "Nämä kuvat ja videot poistetaan kaikista albumeista, ja ne ovat nähtävissä vain lukitussa kansiossa", "moved_to_archive": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} arkistoon", @@ -1238,6 +1345,9 @@ "my_albums": "Omat albumit", "name": "Nimi", "name_or_nickname": "Nimi tai lempinimi", + "network_requirement_photos_upload": "Käytä mobiiliverkkoa kuvien varmuuskopioimiseksi", + "network_requirement_videos_upload": "Käytä mobiiliverkkoa videoiden varmuuskopioimiseksi", + "network_requirements_updated": "Verkkovaatimukset muuttuivat, nollataan varmuuskopiointijono", "networking_settings": "Verkko", "networking_subtitle": "Hallitse palvelinasetuksia", "never": "ei koskaan", @@ -1259,6 +1369,7 @@ "no_archived_assets_message": "Arkistoi kuvia ja videoita piilottaaksesi ne kuvat näkymästä", "no_assets_message": "NAPAUTA LATAAKSESI ENSIMMÄISEN KUVASI", "no_assets_to_show": "Ei näytettäviä kohteita", + "no_cast_devices_found": "Cast-laitteita ei lÃļytynyt", "no_duplicates_found": "Kaksoiskappaleita ei lÃļytynyt.", "no_exif_info_available": "EXIF-tietoa ei saatavilla", "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", @@ -1272,6 +1383,7 @@ "no_results": "Ei tuloksia", "no_results_description": "Kokeile synonyymiä tai yleisempää avainsanaa", "no_shared_albums_message": "Luo albumi, jotta voit jakaa kuvia ja videoita toisille", + "no_uploads_in_progress": "Ei käynnissä olevia latauksia", "not_in_any_album": "Ei yhdessäkään albumissa", "not_selected": "Ei valittu", "note_apply_storage_label_to_previously_uploaded assets": "Huom: Jotta voit soveltaa tallennustunnistetta aiemmin ladattuihin kohteisiin, suorita", @@ -1287,12 +1399,16 @@ "oauth": "OAuth", "official_immich_resources": "Viralliset Immich-resurssit", "offline": "Offline", + "offset": "Ero", "ok": "Ok", "oldest_first": "Vanhin ensin", "on_this_device": "Laitteella", "onboarding": "KäyttÃļÃļnotto", - "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin, ja ne voidaan poistaa käytÃļstä milloin tahansa hallinta asetuksista.", + "onboarding_locale_description": "Valitse haluamasi kieli. Voit muuttaa kieliasetuksia myÃļhemmin asetuksista.", + "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin ja ne voidaan milloin tahansa poistaa käytÃļstä asetuksista.", + "onboarding_server_welcome_description": "Määritellään seuraavaksi järjestelmäsi muutamalla yleisellä asetuksella.", "onboarding_theme_description": "Valitse väriteema istunnollesi. Voit muuttaa tämän myÃļhemmin asetuksistasi.", + "onboarding_user_welcome_description": "Aloitetaan!", "onboarding_welcome_user": "Tervetuloa {user}", "online": "Online", "only_favorites": "Vain suosikit", @@ -1306,6 +1422,7 @@ "original": "alkuperäinen", "other": "Muut", "other_devices": "Toiset laitteet", + "other_entities": "Muut entiteetit", "other_variables": "Muut muuttujat", "owned": "Omistettu", "owner": "Omistaja", @@ -1360,6 +1477,9 @@ "permission_onboarding_permission_limited": "Rajoitettu käyttÃļoikeus. Salliaksesi Immichin varmuuskopioida ja hallita koko kuvakirjastoasi, myÃļnnä oikeus kuviin ja videoihin asetuksista.", "permission_onboarding_request": "Immich vaatii käyttÃļoikeuden kuvien ja videoiden käyttämiseen.", "person": "HenkilÃļ", + "person_age_months": "{months, plural, one {# kuukauden} other {# kuukauden}} ikäinen", + "person_age_year_months": "1 vuosi ja {months, plural, one {# kuukauden} other {# kuukautta}} vanha", + "person_age_years": "{years, plural, other {# vuotta}} vanha", "person_birthdate": "Syntynyt {date}", "person_hidden": "{name}{hidden, select, true { (piilotettu)} other {}}", "photo_shared_all_users": "Näyttää että olet jakanut kuvasi kaikkien käyttäjien kanssa, tai sinulla ei ole käyttäjää kenelle jakaa.", @@ -1392,7 +1512,7 @@ "previous_or_next_photo": "Kuva seuraava/edellinen", "previous_or_next_year": "Vuosi seuraava/edellinen", "primary": "Ensisijainen", - "privacy": "Yksityisyys", + "privacy": "Tietosuoja", "profile": "Profiili", "profile_drawer_app_logs": "Lokit", "profile_drawer_client_out_of_date_major": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", @@ -1437,6 +1557,7 @@ "purchase_server_description_2": "Tukijan tila", "purchase_server_title": "Palvelin", "purchase_settings_server_activated": "Palvelimen tuoteavainta hallinnoi ylläpitäjä", + "queue_status": "Jonossa {count}/{total}", "rating": "Tähtiarvostelu", "rating_clear": "Tyhjennä arvostelu", "rating_count": "{count, plural, one {# tähti} other {# tähteä}}", @@ -1465,6 +1586,8 @@ "refreshing_faces": "Päivitetään kasvoja", "refreshing_metadata": "Päivitetään metadata", "regenerating_thumbnails": "Regeneroidaan pikkukuvia", + "remote": "Etä", + "remote_assets": "Etäkohteet", "remove": "Poista", "remove_assets_album_confirmation": "Haluatko varmasti poistaa {count, plural, one {# median} other {# mediaa}} albumista?", "remove_assets_shared_link_confirmation": "Haluatko varmasti poistaa {count, plural, one {# median} other {# mediaa}} tästä jakolinkistä?", @@ -1472,12 +1595,15 @@ "remove_custom_date_range": "Poista aikaväliltä", "remove_deleted_assets": "Poista Offline-tiedostot", "remove_from_album": "Poista albumista", + "remove_from_album_action_prompt": "{count} poistettu albumista", "remove_from_favorites": "Poista suosikeista", + "remove_from_lock_folder_action_prompt": "{count} poistettu lukitusta albumista", "remove_from_locked_folder": "Poista lukitusta kansiosta", "remove_from_locked_folder_confirmation": "Haluatko varmasti siirtää nämä kuvat ja videot pois lukitusta kansiosta? Ne näkyvät sen jälkeen kirjastossasi.", "remove_from_shared_link": "Poista jakolinkistä", "remove_memory": "Tyhjennä muisti", "remove_photo_from_memory": "Poista kuva muistista", + "remove_tag": "Poista tunniste", "remove_url": "Poista URL", "remove_user": "Poista käyttäjä", "removed_api_key": "API-avain {name} poistettu", @@ -1499,19 +1625,28 @@ "reset_password": "Nollaa salasana", "reset_people_visibility": "Nollaa henkilÃļiden näkyvyysasetukset", "reset_pin_code": "Nollaa PIN-koodi", + "reset_pin_code_description": "Jos olet unohtanut PIN-koodisi, ole yhteydessä järjestelmänvalvojaan", + "reset_pin_code_success": "PIN-koodi nollattu onnistuneesti", + "reset_pin_code_with_password": "Voit aina nollata PIN-koodisi salasanan avulla", + "reset_sqlite": "Nollaa SQLite Tietokanta", + "reset_sqlite_confirmation": "Haluatko varmasti nollata SQLite tietokannan? Sinun tulee kirjautua sovelluksesta ulos ja takaisin sisään uudelleensynkronoidaksesi datan", + "reset_sqlite_success": "SQLite Tietokanta nollattu onnistuneesti", "reset_to_default": "Palauta oletusasetukset", "resolve_duplicates": "Ratkaise kaksoiskappaleet", "resolved_all_duplicates": "Kaikki kaksoiskappaleet selvitetty", "restore": "Palauta", "restore_all": "Palauta kaikki", + "restore_trash_action_prompt": "{count} palautettu roskakorista", "restore_user": "Palauta käyttäjä", "restored_asset": "Palautettu media", "resume": "Jatka", "retry_upload": "Yritä latausta uudelleen", "review_duplicates": "Tarkastele kaksoiskappaleita", + "review_large_files": "Tarkista suuret tiedostot", "role": "Rooli", - "role_editor": "Editori", - "role_viewer": "Toistin", + "role_editor": "Muokkaaja", + "role_viewer": "Katsoja", + "running": "Käynnissä", "save": "Tallenna", "save_to_gallery": "Tallenna galleriaan", "saved_api_key": "API-avain tallennettu", @@ -1584,6 +1719,7 @@ "select_album_cover": "Valitse albmin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", + "select_all_in": "Valitse kaikki {group}", "select_avatar_color": "Valitse avatarin väri", "select_face": "Valitse kasvo", "select_featured_photo": "Valitse esittelykuva", @@ -1604,6 +1740,7 @@ "server_info_box_server_url": "Palvelimen URL-osoite", "server_offline": "Palvelin Offline-tilassa", "server_online": "Palvelin Online-tilassa", + "server_privacy": "Palvelimen tietosuoja", "server_stats": "Palvelimen tilastot", "server_version": "Palvelimen versio", "set": "Aseta", @@ -1613,6 +1750,7 @@ "set_date_of_birth": "Aseta syntymäaika", "set_profile_picture": "Aseta profiilikuva", "set_slideshow_to_fullscreen": "Näytä diaesitys koko ruudulla", + "set_stack_primary_asset": "Aseta pääkohteeksi", "setting_image_viewer_help": "Kuvaa katseltaessa ensin ladataan pikkukuva, sitten keskilaatuinen pikkukuva (jos käytÃļssä) ja lopuksi alkuperäinen (jos käytÃļssä).", "setting_image_viewer_original_subtitle": "Ota käyttÃļÃļn ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytÃļstä vähentääksesi datan käyttÃļä (sekä verkossa että laitteen välimuistissa).", "setting_image_viewer_original_title": "Lataa alkuperäinen kuva", @@ -1640,6 +1778,7 @@ "settings_saved": "Asetukset tallennettu", "setup_pin_code": "Määritä PIN-koodi", "share": "Jaa", + "share_action_prompt": "Jaettu {count} kohdetta", "share_add_photos": "Lisää kuvia", "share_assets_selected": "{count} valittu", "share_dialog_preparing": "Valmistellaan...", @@ -1661,6 +1800,7 @@ "shared_link_clipboard_copied_massage": "Kopioitu leikepÃļydältä", "shared_link_clipboard_text": "Linkki: {link}\nSalasana: {password}", "shared_link_create_error": "Jaetun linkin luomisessa tapahtui virhe", + "shared_link_custom_url_description": "Avaa tämä jaettu linkki mukautetulla URL-osoitteella", "shared_link_edit_description_hint": "Lisää jaon kuvaus", "shared_link_edit_expire_after_option_day": "1 päivä", "shared_link_edit_expire_after_option_days": "{count} päivää", @@ -1686,6 +1826,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Hallitse jaettuja linkkejä", "shared_link_options": "Jaetun linkin vaihtoehdot", + "shared_link_password_description": "Vaadi salasana tämän jakolinkin käyttämiseksi", "shared_links": "Jaetut linkit", "shared_links_description": "Jaa kuvia ja videoita linkin avulla", "shared_photos_and_videos_count": "{assetCount, plural, other {# jaettua kuvaa ja videota.}}", @@ -1735,12 +1876,14 @@ "sort_created": "Luontipäivä", "sort_items": "Tietueiden määrä", "sort_modified": "Muokkauspäivä", + "sort_newest": "Uusin kuva", "sort_oldest": "Vanhin kuva", "sort_people_by_similarity": "Lajittele ihmiset samankaltaisuuden mukaan", "sort_recent": "Tuorein kuva", "sort_title": "Otsikko", "source": "Lähdekoodi", "stack": "Pinoa", + "stack_action_prompt": "{count} pinottu", "stack_duplicates": "Pinoa kaksoiskappaleet", "stack_select_one_photo": "Valitse yksi pääkuva pinolle", "stack_selected_photos": "Pinoa valitut kuvat", @@ -1750,6 +1893,7 @@ "start_date": "Alkupäivä", "state": "Maakunta", "status": "Tila", + "stop_casting": "Lopeta suoratoisto", "stop_motion_photo": "Pysäytä liikkuva kuva", "stop_photo_sharing": "Lopetetaanko kuvien jakaminen?", "stop_photo_sharing_description": "{partner} ei enää pääse kuviisi.", @@ -1759,6 +1903,7 @@ "storage_quota": "TallennuskiintiÃļ", "storage_usage": "{used} / {available} käytetty", "submit": "Lähetä", + "success": "Onnistui", "suggestions": "Ehdotukset", "sunrise_on_the_beach": "Auringonnousu rannalla", "support": "Tuki", @@ -1768,8 +1913,10 @@ "sync": "Synkronoi", "sync_albums": "Synkronoi albumit", "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", + "sync_local": "Synkronoi paikallinen", + "sync_remote": "Synkronoi etä", "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", - "tag": "Lisää tunniste", + "tag": "Tunniste", "tag_assets": "Lisää tunnisteita", "tag_created": "Luotu tunniste: {tag}", "tag_feature_description": "Selaa valokuvia ja videoita, jotka on ryhmitelty loogisten tunnisteotsikoiden mukaan", @@ -1778,6 +1925,7 @@ "tag_updated": "Päivitetty tunniste: {tag}", "tagged_assets": "Tunnistettu {count, plural, one {# kohde} other {# kohdetta}}", "tags": "Tunnisteet", + "tap_to_run_job": "Napauta suorittaaksesi tehtävän", "template": "Nimeämismalli", "theme": "Teema", "theme_selection": "Teeman valinta", @@ -1810,6 +1958,7 @@ "total": "Yhteensä", "total_usage": "KäyttÃļ yhteensä", "trash": "Roskakori", + "trash_action_prompt": "{count} siirretty roskakoriin", "trash_all": "Vie kaikki roskakoriin", "trash_count": "Roskakori {count, number}", "trash_delete_asset": "Poista / vie roskakoriin", @@ -1827,8 +1976,11 @@ "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", "unarchive": "Palauta arkistosta", + "unarchive_action_prompt": "{count} poistettu arkistosta", "unarchived_count": "{count, plural, other {# poistettu arkistosta}}", + "undo": "Kumoa", "unfavorite": "Poista suosikeista", + "unfavorite_action_prompt": "{count} poistettu suosikeista", "unhide_person": "Poista henkilÃļ piilosta", "unknown": "Tuntematon", "unknown_country": "Tuntematon maa", @@ -1844,16 +1996,22 @@ "unsaved_change": "Tallentamaton muutos", "unselect_all": "Poista valinnat", "unselect_all_duplicates": "Poista kaikkien kaksoiskappaleiden valinta", + "unselect_all_in": "Poista kaikki valinnat {group}", "unstack": "Pura pino", + "unstack_action_prompt": "{count} purettu pinosta", "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", + "untagged": "Ilman tunnistetta", "up_next": "Seuraavaksi", "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", "upload": "Siirrä palvelimelle", + "upload_action_prompt": "{count} jonossa lähetystä varten", "upload_concurrency": "Latausten samanaikaisuus", + "upload_details": "Lähetyksen tiedot", "upload_dialog_info": "Haluatko varmuuskopioida valitut kohteet palvelimelle?", "upload_dialog_title": "Lähetä kohde", "upload_errors": "Lataus valmistui {count, plural, one {# virheen} other {# virheen}} kanssa. Päivitä sivu nähdäksesi ladatut tiedot.", + "upload_finished": "Lähetys valmistui", "upload_progress": "Jäljellä {remaining, number} - Käsitelty {processed, number}/{total, number}", "upload_skipped_duplicates": "Ohitettiin {count, plural, one {# kaksoiskappale} other {# kaksoiskappaletta}}", "upload_status_duplicates": "Kaksoiskappaleet", @@ -1861,7 +2019,8 @@ "upload_status_uploaded": "Ladattu", "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", "upload_to_immich": "Lähetä Immichiin ({count})", - "uploading": "Lähettään", + "uploading": "Lähettää", + "uploading_media": "Lähetetään mediaa", "url": "URL", "usage": "KäyttÃļ", "use_biometric": "Käytä biometriikkaa", @@ -1873,6 +2032,7 @@ "user_liked": "{user} tykkäsi {type, select, photo {kuvasta} video {videosta} asset {mediasta} other {tästä}}", "user_pin_code_settings": "PIN-koodi", "user_pin_code_settings_description": "Hallinnoi PIN-koodiasi", + "user_privacy": "Käyttäjän tietosuoja", "user_purchase_settings": "Osta", "user_purchase_settings_description": "Hallitse ostostasi", "user_role_set": "Tee käyttäjästä {user} {role}", @@ -1881,6 +2041,7 @@ "user_usage_stats_description": "Näytä tilin käyttÃļtilastot", "username": "Käyttäjänimi", "users": "Käyttäjät", + "users_added_to_album_count": "{count, plural, one {# käyttäjä} other {# käyttäjää}} lisätty albumiin", "utilities": "Apuohjelmat", "validate": "Validoi", "validate_endpoint_error": "Anna kelvollinen URL-osoite", @@ -1899,6 +2060,7 @@ "view_album": "Näytä albumi", "view_all": "Näytä kaikki", "view_all_users": "Näytä kaikki käyttäjät", + "view_details": "Näytä tiedot", "view_in_timeline": "Näytä aikajanalla", "view_link": "Näytä linkki", "view_links": "Näytä linkit", diff --git a/i18n/fil.json b/i18n/fil.json index 12e74f7bad..12e6086064 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -14,10 +14,13 @@ "add_a_location": "Dagdagan ng lugar", "add_a_name": "Dagdagan ng pangalan", "add_a_title": "Dagdagan ng pamagat", + "add_endpoint": "Dagdagan ng dulo", "add_location": "Magdagdag ng lugar", "add_more_users": "Magdagdag ng mga user", "add_partner": "Magdagdag ng kasangga", + "add_path": "Magdagdag ng path", "add_photos": "Magdagdag ng litrato", + "add_tag": "Magdagdag ng tag", "add_to": "Idagdag saâ€Ļ", "add_to_album": "Idagdag sa album", "add_to_album_bottom_sheet_added": "Naidagdag sa {album}", diff --git a/i18n/fr.json b/i18n/fr.json index bf57469944..df0e9d1cc0 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -8,16 +8,17 @@ "actions": "Actions", "active": "En cours", "activity": "ActivitÊ", - "activity_changed": "ActivitÊ {enabled, select, true {autorisÊe} other {interdite}}", + "activity_changed": "ActivitÊ {enabled, select, true {activÊe} other {dÊsactivÊe}}", "add": "Ajouter", "add_a_description": "Ajouter une description", "add_a_location": "Ajouter une localisation", "add_a_name": "Ajouter un nom", "add_a_title": "Ajouter un titre", + "add_birthday": "Ajouter un anniversaire", "add_endpoint": "Ajouter une adresse", "add_exclusion_pattern": "Ajouter un schÊma d'exclusion", "add_import_path": "Ajouter un chemin à importer", - "add_location": "Ajouter un lieu", + "add_location": "Ajouter une localisation", "add_more_users": "Ajouter plus d'utilisateurs", "add_partner": "Ajouter un partenaire", "add_path": "Ajouter un chemin", @@ -27,6 +28,10 @@ "add_to_album": "Ajouter à l'album", "add_to_album_bottom_sheet_added": "AjoutÊ à {album}", "add_to_album_bottom_sheet_already_exists": "DÊjà dans {album}", + "add_to_album_bottom_sheet_some_local_assets": "Certains mÊdias n'ont pas pu ÃĒtre ajoutÊs à l'album", + "add_to_album_toggle": "Basculer la sÊlection pour {album}", + "add_to_albums": "Ajouter aux albums", + "add_to_albums_count": "Ajouter aux albums ({count})", "add_to_shared_album": "Ajouter à l'album partagÊ", "add_url": "Ajouter l'URL", "added_to_archive": "AjoutÊ à l'archive", @@ -44,6 +49,13 @@ "backup_database": "CrÊation d'une image de la base de donnÊes", "backup_database_enable_description": "Activer la crÊation d'images de la base de donnÊes", "backup_keep_last_amount": "Nombre d'images à conserver", + "backup_onboarding_1_description": "copie hors site dans le cloud ou sur un site distant.", + "backup_onboarding_2_description": "copies locales sur diffÊrents appareils. Cela inclut les fichiers principaux ainsi qu'une sauvegarde locale de ces fichiers.", + "backup_onboarding_3_description": "copies total de vos donnÊes, incluant les fichiers originaux. Cela inclut 1 copie hors site ainsi que 2 copies locales.", + "backup_onboarding_description": "Une stratÊgie de sauvegarde 3-2-1 est recommandÊ pour protÊger vos donnÊes. Vous devriez conserver des copies de vos photos/vidÊos tÊlÊversÊs ainsi que de la base de donnÊes d'Immich pour une solution de sauvegarde cohÊrente.", + "backup_onboarding_footer": "Pour plus d'information sur la sauvegarde d'Immich, merci de vous rÊfÊrer à la documentation.", + "backup_onboarding_parts_title": "Une sauvegarde 3-2-1 inclut :", + "backup_onboarding_title": "Sauvegardes", "backup_settings": "Paramètres de crÊation d'images de la base de donnÊes", "backup_settings_description": "GÊrer les paramètres de crÊation d'images de la base de donnÊes.", "cleared_jobs": "TÃĸches supprimÊes pour : {job}", @@ -112,6 +124,13 @@ "logging_enable_description": "Activer la journalisation", "logging_level_description": "Niveau de journalisation lorsque cette option est activÊe.", "logging_settings": "Journalisation", + "machine_learning_availability_checks": "VÊrifications de disponibilitÊ", + "machine_learning_availability_checks_description": "DÊtecte automatiquement et choisit les serveurs d'apprentissage machine disponibles", + "machine_learning_availability_checks_enabled": "Activer les vÊrifications de disponibilitÊ", + "machine_learning_availability_checks_interval": "Intervalle de vÊrification", + "machine_learning_availability_checks_interval_description": "Intervalle en millisecondes entre les vÊrifications de disponibilitÊ", + "machine_learning_availability_checks_timeout": "DÊlai d'expiration de la requÃĒte", + "machine_learning_availability_checks_timeout_description": "DÊlai d'expiration en millisecondes pour les vÊrifications de disponibilitÊ", "machine_learning_clip_model": "Modèle de langage CLIP", "machine_learning_clip_model_description": "Le nom d'un modèle CLIP listÊ ici. Notez que vous devez rÊexÊcuter la tÃĸche 'Recherche intelligente' pour toutes les images après avoir changÊ de modèle.", "machine_learning_duplicate_detection": "DÊtection des doublons", @@ -166,6 +185,20 @@ "metadata_settings_description": "Gestion des paramètres de mÊtadonnÊes", "migration_job": "Migration", "migration_job_description": "Migration des miniatures pour les mÊdias et les visages vers la dernière structure de dossiers", + "nightly_tasks_cluster_faces_setting_description": "DÊmarrer la reconnaissance faciale sur les visages nouvellement dÊtectÊs", + "nightly_tasks_cluster_new_faces_setting": "Regrouper les nouveaux visages", + "nightly_tasks_database_cleanup_setting": "TÃĸches de nettoyage de la base de donnÊes", + "nightly_tasks_database_cleanup_setting_description": "Nettoyage ancien, donnÊes de la base de donnÊes expirÊes", + "nightly_tasks_generate_memories_setting": "GÊnÊrer les souvenirs", + "nightly_tasks_generate_memories_setting_description": "CrÊer des souvenirs à partir des ÊlÊments", + "nightly_tasks_missing_thumbnails_setting": "GÊnÊrer les miniatures manquantes", + "nightly_tasks_missing_thumbnails_setting_description": "Mettre en file d'attente les ÊlÊments sans miniature pour la crÊation de miniature", + "nightly_tasks_settings": "Paramètres des tÃĸches de nuit", + "nightly_tasks_settings_description": "GÊrer les tÃĸches de nuit", + "nightly_tasks_start_time_setting": "Heure de dÊmarrage", + "nightly_tasks_start_time_setting_description": "Heure à laquelle le serveur commence à exÊcuter les tÃĸches de nuit", + "nightly_tasks_sync_quota_usage_setting": "Synchroniser les quota d'usage", + "nightly_tasks_sync_quota_usage_setting_description": "Mettre à jour les quota d'usage de l'utilisateur, en se basant sur l'utilisation actuelle", "no_paths_added": "Aucun chemin n'a ÊtÊ ajoutÊ", "no_pattern_added": "Aucun schÊma d'exclusion n'a ÊtÊ ajoutÊ", "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'Êtiquette de stockage à des mÊdias prÊcÊdemment envoyÊs, exÊcutez", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI de redirection mobile", "oauth_mobile_redirect_uri_override": "Remplacer l'URI de redirection mobile", "oauth_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme ''{callback}''", + "oauth_role_claim": "Attribut de rôle", + "oauth_role_claim_description": "Donne automatiquement un accès en tant qu'admin, en se basant sur la prÊsence de cet attribut. L'attribut peut avoir soit 'user' (utilisateur) soit 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "GÊrer les paramètres de connexion OAuth", "oauth_settings_more_details": "Pour plus de dÊtails sur cette fonctionnalitÊ, consultez ce lien.", @@ -244,7 +279,7 @@ "storage_template_migration_info": "L'enregistrement des modèles va convertir toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux mÊdias. Pour appliquer rÊtroactivement le modèle aux mÊdias prÊcÊdemment envoyÊs, exÊcutez la tÃĸche {job}.", "storage_template_migration_job": "TÃĸche de migration du modèle de stockage", "storage_template_more_details": "Pour plus de dÊtails sur cette fonctionnalitÊ, reportez-vous au Modèle de stockage et à ses implications", - "storage_template_onboarding_description_v2": "Quand elle est activÊe, cette fonctionnalitÊ organise automatiquement les fichiers, sur base d'un modèle dÊfini par l'utilisateur. Pour plus d'informations, se rÊpÊter à la documentation.", + "storage_template_onboarding_description_v2": "Quand elle est activÊe, cette fonctionnalitÊ organise automatiquement les fichiers, sur base d'un modèle dÊfini par l'utilisateur. Pour plus d'informations, se rÊfÊrer à la documentation.", "storage_template_path_length": "Limite approximative de la longueur du chemin : {length, number}/{limit, number}", "storage_template_settings": "Modèle de stockage", "storage_template_settings_description": "GÊrer la structure des dossiers et le nom des fichiers du mÊdia envoyÊ", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Nombre de jours de rÊtention des mÊdias dans la corbeille avant leur suppression dÊfinitive", "trash_settings": "Corbeille", "trash_settings_description": "GÊrer les paramètres de la corbeille", + "unlink_all_oauth_accounts": "DÊlier tous les comptes OAuth", + "unlink_all_oauth_accounts_description": "Pensez à dÊlier tous les comptes OAuth avant de migrer vers un nouveau fournisseur.", + "unlink_all_oauth_accounts_prompt": "Êtes-vous sÃģr de vouloir dÊlier tous les comptes OAuth ? Cela va rÊinitialiser l'identifiant OAuth de chaque utilisateur et est irrÊvocable.", "user_cleanup_job": "Nettoyage des utilisateurs", "user_delete_delay": "La suppression dÊfinitive du compte et des mÊdias de {user} sera programmÊe dans {delay, plural, one {# jour} other {# jours}}.", "user_delete_delay_settings": "DÊlai de suppression", @@ -357,13 +395,15 @@ "admin_password": "Mot de passe Admin", "administration": "Administration", "advanced": "AvancÊ", - "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_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 tous les albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPÉRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", "advanced_settings_log_level_title": "Niveau de journalisation : {level}", - "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_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources locales. 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_readonly_mode_subtitle": "Active le mode lecture seule, oÚ les photos peuvent seulement ÃĒtre visualisÊes, et les actions comme les sÊlections multiples, le partage, la diffusion, la suppression sont dÊsactivÊes. Activer/dÊsactiver la lecture seule via l'image de l'utilisateur depuis l'Êcran d'accueil", + "advanced_settings_readonly_mode_title": "Mode lecture seule", "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", @@ -379,6 +419,7 @@ "album_cover_updated": "Couverture de l'album mise à jour", "album_delete_confirmation": "Êtes-vous sÃģr de vouloir supprimer l'album {album} ?", "album_delete_confirmation_description": "Si cet album est partagÊ, les autres utilisateurs ne pourront plus y accÊder.", + "album_deleted": "Album supprimÊ", "album_info_card_backup_album_excluded": "EXCLUS", "album_info_card_backup_album_included": "INCLUS", "album_info_updated": "DÊtails de l'album mis à jour", @@ -388,7 +429,9 @@ "album_options": "Options de l'album", "album_remove_user": "Supprimer l'utilisateur ?", "album_remove_user_confirmation": "Êtes-vous sÃģr de vouloir supprimer {user} ?", + "album_search_not_found": "Aucun album trouvÊ ne correspond à votre recherche", "album_share_no_users": "Il semble que vous ayez partagÊ cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", + "album_summary": "RÊsumÊ de l'album", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagÊ a de nouveaux mÊdias", "album_user_left": "{album} quittÊ", @@ -407,14 +450,15 @@ "albums_default_sort_order": "Ordre de tri par dÊfaut des albums", "albums_default_sort_order_description": "Ordre de tri des mÊdias pour les nouveaux albums crÊÊs.", "albums_feature_description": "Bibliothèques de mÊdias pouvant ÃĒtre partagÊs avec d'autres utilisateurs.", + "albums_on_device_count": "Album sur l'appareil ({count})", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", "all_videos": "Toutes les vidÊos", "allow_dark_mode": "Autoriser le mode sombre", "allow_edits": "Autoriser les modifications", - "allow_public_user_to_download": "Permettre aux utilisateurs non connectÊs de tÊlÊcharger", - "allow_public_user_to_upload": "Autoriser l'envoi aux utilisateurs non connectÊs", + "allow_public_user_to_download": "Permettre le tÊlÊchargement par des utilisateurs non connectÊs", + "allow_public_user_to_upload": "Permettre l'envoi par des utilisateurs non connectÊs", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "ClÊ API", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Se dÊconnecter", "app_settings": "Paramètres de l'application", "appears_in": "ApparaÃŽt dans", - "archive": "Archiver", + "apply_count": "Appliquer ({count, number})", + "archive": "Archive", + "archive_action_prompt": "{count} ajoutÊ(s) à l'archive", "archive_or_unarchive_photo": "Archiver ou dÊsarchiver une photo", "archive_page_no_archived_assets": "Aucun ÊlÊment archivÊ n'a ÊtÊ trouvÊ", "archive_page_title": "Archiver ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "ÉlÊment restaurÊ avec succès", "asset_skipped": "SautÊ", "asset_skipped_in_trash": "À la corbeille", + "asset_trashed": "MÊdia mis à la corbeille", + "asset_troubleshoot": "DÊpannage de mÊdia", "asset_uploaded": "EnvoyÊ", "asset_uploading": "Envoiâ€Ļ", "asset_viewer_settings_subtitle": "Modifier les paramètres du visualiseur photos", @@ -464,8 +512,9 @@ "assets": "MÊdias", "assets_added_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}}", "assets_added_to_album_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}} à l'album", - "assets_added_to_name_count": "{count, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}} à {hasName, select, true {{name}} other {new album}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {# mÊdia ajoutÊ} other {# mÊdias ajoutÊs}} à {albumTotal, plural, one {# album} other {# albums}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Le mÊdia ne peut pas ÃĒtre ajoutÊ} other {Les mÊdias ne peuvent pas ÃĒtre ajoutÊs}} à l'album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Le mÊdia ne peut ÃĒtre ajoutÊ} other {Les mÊdias ne peuvent ÃĒtre ajoutÊs}} à aucun des albums", "assets_count": "{count, plural, one {# mÊdia} other {# mÊdias}}", "assets_deleted_permanently": "{count} mÊdia(s) supprimÊ(s) dÊfinitivement", "assets_deleted_permanently_from_server": "{count} mÊdia(s) supprimÊ(s) dÊfinitivement du serveur Immich", @@ -482,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# mÊdia} other {# mÊdias}} mis à la corbeille", "assets_trashed_from_server": "{count} mÊdia(s) dÊplacÊ(s) vers la corbeille du serveur Immich", "assets_were_part_of_album_count": "{count, plural, one {Un mÊdia est} other {Des mÊdias sont}} dÊjà dans l'album", + "assets_were_part_of_albums_count": "{count, plural, one {Le mÊdia Êtait dÊjà prÊsent} other {Les mÊdias Êtaient dÊjà prÊsents}} dans les albums", "authorized_devices": "Appareils autorisÊs", "automatic_endpoint_switching_subtitle": "Se connecter localement lorsque connectÊ au WI-FI spÊcifiÊ mais utiliser une adresse alternative lorsque connectÊ à un autre rÊseau", "automatic_endpoint_switching_title": "Changement automatique d'adresse", "autoplay_slideshow": "Lecture automatique d'un diaporama", "back": "Retour", "back_close_deselect": "Retournez en arrière, fermez ou dÊsÊlectionnez", + "background_backup_running_error": "La sauvegarde en tÃĸche de fond est actuellement en cours, impossible de dÊmarrer une sauvegarde manuelle", "background_location_permission": "Permission de localisation en arrière plan", "background_location_permission_content": "Afin de pouvoir changer d'adresse en arrière plan, Immich doit avoir *en permanence* accès à la localisation prÊcise, afin d'accÊder au le nom du rÊseau Wi-Fi utilisÊ", + "background_options": "Options d'arrière-plan", + "backup": "Sauvegarde", "backup_album_selection_page_albums_device": "Albums sur l'appareil ({count})", "backup_album_selection_page_albums_tap": "Tapez pour inclure, tapez deux fois pour exclure", "backup_album_selection_page_assets_scatter": "Les ÊlÊments peuvent ÃĒtre rÊpartis sur plusieurs albums. De ce fait, les albums peuvent ÃĒtre inclus ou exclus pendant le processus de sauvegarde.", "backup_album_selection_page_select_albums": "SÊlectionner les albums", "backup_album_selection_page_selection_info": "Informations sur la sÊlection", "backup_album_selection_page_total_assets": "Total des ÊlÊments uniques", + "backup_albums_sync": "Sauvegarde de la synchronisation des albums", "backup_all": "Tout", "backup_background_service_backup_failed_message": "Échec de la sauvegarde des mÊdias. Nouvelle tentativeâ€Ļ", "backup_background_service_connection_failed_message": "Impossible de se connecter au serveur. Nouvelle tentativeâ€Ļ", @@ -542,16 +596,19 @@ "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", - "backup_controller_page_turn_on": "Activer la sauvegarde", + "backup_controller_page_turn_on": "Activer la sauvegarde au premier plan", "backup_controller_page_uploading_file_info": "Envoi des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", + "backup_error_sync_failed": "Échec de la synchronisation. Impossible d'exÊcuter la sauvegarde.", "backup_info_card_assets": "ÊlÊments", "backup_manual_cancelled": "AnnulÊ", "backup_manual_in_progress": "Envoi dÊjà en cours. RÊessayez plus tard", "backup_manual_success": "Succès", "backup_manual_title": "Statut de l'envoi", + "backup_options": "Options de sauvegarde", "backup_options_page_title": "Options de sauvegarde", "backup_setting_subtitle": "Ajuster les paramètres d'envoi au premier et en arrière-plan", + "backup_settings_subtitle": "GÊrer les paramètres de tÊlÊversement", "backward": "Arrière", "biometric_auth_enabled": "Authentification biomÊtrique activÊe", "biometric_locked_out": "L'authentification biomÊtrique est verrouillÊ", @@ -570,7 +627,7 @@ "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_subtitle": "Photos et vidÊos qui sont ignorÊes par l'application", "cache_settings_duplicated_assets_title": "MÊdias dupliquÊs ({count})", "cache_settings_statistics_album": "Miniatures de la bibliothèque", "cache_settings_statistics_full": "Images complètes", @@ -587,6 +644,7 @@ "cancel": "Annuler", "cancel_search": "Annuler la recherche", "canceled": "AnnulÊ", + "canceling": "Annulation", "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", @@ -609,6 +667,8 @@ "change_pin_code": "Changer le code PIN", "change_your_password": "Changer votre mot de passe", "changed_visibility_successfully": "VisibilitÊ modifiÊe avec succès", + "charging": "En charge", + "charging_requirement_mobile_backup": "La sauvegarde en tÃĸche de fond nÊcessite que l'appareil soit en charge", "check_corrupt_asset_backup": "VÊrifier la corruption des ÊlÊments enregistrÊs", "check_corrupt_asset_backup_button": "VÊrifier", "check_corrupt_asset_backup_description": "Lancer cette vÊrification uniquement lorsque connectÊ à un rÊseau Wi-Fi et que tout le contenu a ÊtÊ enregistrÊ. Cette procÊdure peut durer plusieurs minutes.", @@ -618,6 +678,7 @@ "clear": "Effacer", "clear_all": "Effacer tout", "clear_all_recent_searches": "Supprimer les recherches rÊcentes", + "clear_file_cache": "Vider le fichier de cache", "clear_message": "Effacer le message", "clear_value": "Effacer la valeur", "client_cert_dialog_msg_confirm": "D'accord", @@ -636,18 +697,18 @@ "color_theme": "Thème de couleur", "comment_deleted": "Commentaire supprimÊ", "comment_options": "Options des commentaires", - "comments_and_likes": "Commentaires et \"j'aime\"", + "comments_and_likes": "Commentaires et \"J'aime\"", "comments_are_disabled": "Les commentaires sont dÊsactivÊs", "common_create_new_album": "CrÊer un nouvel album", "common_server_error": "Veuillez vÊrifier votre connexion rÊseau, vous assurer que le serveur est accessible et que les versions de l'application et du serveur sont compatibles.", "completed": "ComplÊtÊ", - "confirm": "Confirmer", - "confirm_admin_password": "Confirmer le mot de passe Admin", + "confirm": "Confirmez", + "confirm_admin_password": "Confirmez le mot de passe Admin", "confirm_delete_face": "Êtes-vous sÃģr de vouloir supprimer le visage de {name} du mÊdia ?", "confirm_delete_shared_link": "Voulez-vous vraiment supprimer ce lien partagÊ ?", "confirm_keep_this_delete_others": "Tous les autres mÊdias dans la pile seront supprimÊs sauf celui-ci. Êtes-vous sÃģr de vouloir continuer ?", - "confirm_new_pin_code": "Confirmer le nouveau code PIN", - "confirm_password": "Confirmer le mot de passe", + "confirm_new_pin_code": "Confirmez le nouveau code PIN", + "confirm_password": "Confirmez le mot de passe", "confirm_tag_face": "Voulez-vous identifier ce visage en tant que {name} ?", "confirm_tag_face_unnamed": "Voulez-vous identifier ce visage ?", "connected_device": "Appareil connectÊ", @@ -688,11 +749,13 @@ "create_new_user": "CrÊer un nouvel utilisateur", "create_shared_album_page_share_add_assets": "AJOUTER DES ÉLÉMENTS", "create_shared_album_page_share_select_photos": "SÊlectionner les photos", + "create_shared_link": "CrÊer un lien partagÊ", "create_tag": "CrÊer une Êtiquette", "create_tag_description": "CrÊer une nouvelle Êtiquette. Pour les Êtiquettes imbriquÊes, veuillez entrer le chemin complet de l'Êtiquette, y compris les caractères \"/\".", "create_user": "CrÊer un utilisateur", "created": "CrÊÊ", "created_at": "CrÊÊ à", + "creating_linked_albums": "CrÊation des albums liÊs...", "crop": "Recadrer", "curated_object_page_title": "Objets", "current_device": "Appareil actuel", @@ -700,10 +763,11 @@ "current_server_address": "Adresse actuelle du serveur", "custom_locale": "Paramètres rÊgionaux personnalisÊs", "custom_locale_description": "Afficher les dates et nombres en fonction des paramètres rÊgionaux", + "custom_url": "URL personnalisÊe", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", - "darkTheme": "Basculer sur le thème sombre", + "dark_theme": "Activer le thème sombre", "date_after": "Date après", "date_and_time": "Date et heure", "date_before": "Date avant", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Date de naissance enregistrÊe avec succès", "date_range": "Plage de dates", "day": "Jour", + "days": "Jours", "deduplicate_all": "DÊdupliquer tout", "deduplication_criteria_1": "Taille de l'image en octets", "deduplication_criteria_2": "Nombre de donnÊes EXIF", @@ -719,6 +784,8 @@ "default_locale": "RÊgion par dÊfaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", + "delete_action_confirmation_message": "Êtes-vous sÃģr de vouloir supprimer ce mÊdia ? Cela dÊplacera le mÊdia dans la poubelle du serveur et vous demandera si vous voulez le supprimer localement", + "delete_action_prompt": "{count} supprimÊ(s)", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clÊ API ?", "delete_dialog_alert": "Ces mÊdias seront dÊfinitivement supprimÊs de Immich et de votre appareil", @@ -732,9 +799,12 @@ "delete_key": "Supprimer la clÊ", "delete_library": "Supprimer la bibliothèque", "delete_link": "Supprimer le lien", + "delete_local_action_prompt": "{count} supprimÊ(s) localement", "delete_local_dialog_ok_backed_up_only": "Suppression des donnÊes sauvegardÊes uniquement", "delete_local_dialog_ok_force": "Supprimer tout de mÃĒme", "delete_others": "Supprimer les autres", + "delete_permanently": "Supprimer dÊfinitivement", + "delete_permanently_action_prompt": "{count} supprimÊ(s) dÊfinitivement", "delete_shared_link": "Supprimer le lien partagÊ", "delete_shared_link_dialog_title": "Supprimer le lien partagÊ", "delete_tag": "Supprimer l'Êtiquette", @@ -745,6 +815,7 @@ "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vÊrifier le journal pour plus de dÊtails", + "deselect_all": "Tout dÊsÊlectionner", "details": "DÊtails", "direction": "Ordre", "disabled": "DÊsactivÊ", @@ -762,6 +833,7 @@ "documentation": "Documentation", "done": "TerminÊ", "download": "TÊlÊcharger", + "download_action_prompt": "TÊlÊchargement de {count} ÊlÊments", "download_canceled": "TÊlÊchargement annulÊ", "download_complete": "TÊlÊchargement terminÊ", "download_enqueue": "TÊlÊchargement en attente", @@ -788,8 +860,12 @@ "edit": "Modifier", "edit_album": "Modifier l'album", "edit_avatar": "Modifier l'avatar", + "edit_birthday": "Modifier l'anniversaire", "edit_date": "Modifier la date", "edit_date_and_time": "Modifier la date et l'heure", + "edit_date_and_time_action_prompt": "{count} modifiÊ(s) sur leur date et heure", + "edit_date_and_time_by_offset": "Ajouter un dÊcalage à la date", + "edit_date_and_time_by_offset_interval": "Nouvelle plage de date : {from} - {to}", "edit_description": "Modifier la description", "edit_description_prompt": "Choisir une nouvelle description :", "edit_exclusion_pattern": "Modifier le schÊma d'exclusion", @@ -799,6 +875,7 @@ "edit_key": "Modifier la clÊ", "edit_link": "Modifier le lien", "edit_location": "Modifier la localisation", + "edit_location_action_prompt": "{count} localisation(s) mise(s) à jour", "edit_location_dialog_title": "Localisation", "edit_name": "Modifier le nom", "edit_people": "Modifier les personnes", @@ -817,17 +894,20 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sÃģr de vouloir vider la corbeille ? Cela supprimera dÊfinitivement de Immich tous les mÊdias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_backup": "Activer la sauvegarde", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biomÊtrique", "enabled": "ActivÊ", "end_date": "Date de fin", - "enqueued": "Mis en file", + "enqueued": "Mis en file d'attente", "enter_wifi_name": "Entrez le nom du rÊseau wifi", "enter_your_pin_code": "Entrez votre code PIN", "enter_your_pin_code_subtitle": "Entrez votre code PIN pour accÊder au dossier verrouillÊ", "error": "Erreur", "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_getting_places": "Erreur à la rÊcupÊration des lieux", "error_loading_image": "Erreur de chargement de l'image", + "error_loading_partners": "Erreur de rÊcupÊration des partenaires : {error}", "error_saving_image": "Erreur : {error}", "error_tag_face_bounding_box": "Erreur lors de l'identification de visage - impossible de rÊcupÊrer les coordonnÊes du cadre entourant le visage", "error_title": "Erreur - Quelque chose s'est mal passÊ", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "Erreur de rÊcupÊration des notifications", "failed_to_load_people": "Impossible de charger les personnes", "failed_to_remove_product_key": "Échec de suppression de la clÊ du produit", + "failed_to_reset_pin_code": "Échec de la rÊinitialisation du code PIN", "failed_to_stack_assets": "Impossible d'empiler les mÊdias", "failed_to_unstack_assets": "Impossible de dÊpiler les mÊdias", "failed_to_update_notification_status": "Erreur de mise à jour du statut des notifications", @@ -868,6 +949,7 @@ "paths_validation_failed": "Validation ÊchouÊe pour {paths, plural, one {# un chemin} other {# plusieurs chemins}}", "profile_picture_transparent_pixels": "Les images de profil ne peuvent pas avoir de pixels transparents. Veuillez agrandir et/ou dÊplacer l'image.", "quota_higher_than_disk_size": "Le quota saisi est supÊrieur à l'espace disponible", + "something_went_wrong": "Une erreur est survenue", "unable_to_add_album_users": "Impossible d'ajouter des utilisateurs à l'album", "unable_to_add_assets_to_shared_link": "Impossible d'ajouter des mÊdias au lien partagÊ", "unable_to_add_comment": "Impossible d'ajouter un commentaire", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Ajouter une description...", + "exif_bottom_sheet_description_error": "Erreur de mise à jour de la description", "exif_bottom_sheet_details": "DÉTAILS", "exif_bottom_sheet_location": "LOCALISATION", "exif_bottom_sheet_people": "PERSONNES", "exif_bottom_sheet_person_add_person": "Ajouter un nom", - "exif_bottom_sheet_person_age_months": "Âge {months} mois", - "exif_bottom_sheet_person_age_year_months": "Âge 1 an, {months} mois", - "exif_bottom_sheet_person_age_years": "Âge {years}", "exit_slideshow": "Quitter le diaporama", "expand_all": "Tout dÊvelopper", "experimental_settings_new_asset_list_subtitle": "En cours de dÊveloppement", @@ -973,6 +1053,8 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", + "export_database": "Exporter la base de donnÊes", + "export_database_description": "Exporter la base de donnÊes SQLite", "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Échec du chargement des ressources", "failed_to_load_folder": "Échec de chargement du dossier", "favorite": "Favori", + "favorite_action_prompt": "{count} ajoutÊ(s) aux Favoris", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", "favorites_page_no_favorites": "Aucun ÊlÊment favori n'a ÊtÊ trouvÊ", "feature_photo_updated": "Photo de la personne mise à jour", "features": "FonctionnalitÊs", + "features_in_development": "FonctionnalitÊs en dÊveloppement", "features_setting_description": "GÊrer les fonctionnalitÊs de l'application", "file_name": "Nom du fichier", "file_name_or_extension": "Nom du fichier ou extension", @@ -998,21 +1082,26 @@ "filter_people": "Filtrer les personnes", "filter_places": "Filtrer par lieu", "find_them_fast": "Pour les retrouver rapidement par leur nom", + "first": "Premier", "fix_incorrect_match": "Corriger une association incorrecte", "folder": "Dossier", "folder_not_found": "Dossier introuvable", "folders": "Dossiers", "folders_feature_description": "Parcourir l'affichage par dossiers pour les photos et les vidÊos sur le système de fichiers", + "forgot_pin_code_question": "Code PIN oubliÊ ?", "forward": "Avant", "gcast_enabled": "Diffusion Google Cast", "gcast_enabled_description": "Cette fonctionnalitÊ charge des ressources externes depuis Google pour fonctionner.", "general": "GÊnÊral", + "geolocation_instruction_location": "Cliquez sur un mÊdia avec des coordonnÊes GPS pour utiliser sa localisation, ou bien sÊlectionnez une localisation directement sur la carte", "get_help": "Obtenir de l'aide", "get_wifiname_error": "Impossible d'obtenir le nom du rÊseau wifi. Assurez-vous d'avoir donnÊ les permissions nÊcessaires à l'application et que vous ÃĒtes connectÊ à un rÊseau wifi", "getting_started": "Commencer", "go_back": "Retour", "go_to_folder": "Dossier", "go_to_search": "Faire une recherche", + "gps": "GPS", + "gps_missing": "Pas de GPS", "grant_permission": "Accorder les permissions", "group_albums_by": "Grouper les albums par...", "group_country": "Grouper par pays", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Activer le retour haptique", "haptic_feedback_title": "Retour haptique", "has_quota": "Quota", + "hash_asset": "Hasher le mÊdia", + "hashed_assets": "MÊdia hashÊs", + "hashing": "Hash", "header_settings_add_header_tip": "Ajouter un en-tÃĒte", "header_settings_field_validator_msg": "Cette valeur ne peut pas ÃĒtre vide", "header_settings_header_name_input": "Nom de l'en-tÃĒte", @@ -1036,7 +1128,7 @@ "hide_password": "Masquer le mot de passe", "hide_person": "Masquer la personne", "hide_unnamed_people": "Cacher les personnes non nommÊes", - "home_page_add_to_album_conflicts": "{added} ÊlÊments ajoutÊs à l'album {album}. Les ÊlÊments {failed} sont dÊjà dans l'album.", + "home_page_add_to_album_conflicts": "{added} ÊlÊments ajoutÊs à l'album {album}. {failed} ÊlÊments sont dÊjà dans l'album.", "home_page_add_to_album_err_local": "Impossible d'ajouter des mÊdias locaux aux albums, ils sont ignorÊs", "home_page_add_to_album_success": "{added} ÊlÊments ajoutÊs à l'album {album}.", "home_page_album_err_partner": "Impossible d'ajouter des mÊdias d'un partenaire à un album, ils sont ignorÊs", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "Impossible d'envoyer plus de 30 mÊdias en mÃĒme temps, demande ignorÊe", "host": "Hôte", "hour": "Heure", + "hours": "Heures", "id": "ID", + "idle": "Inactif", "ignore_icloud_photos": "Ignorer les photos iCloud", "ignore_icloud_photos_description": "Les photos stockÊes sur iCloud ne seront pas envoyÊes sur le serveur Immich", "image": "Image", @@ -1112,10 +1206,13 @@ "language_no_results_title": "Aucune langue trouvÊe", "language_search_hint": "Recherche de langues...", "language_setting_description": "SÊlectionnez votre langue prÊfÊrÊe", + "large_files": "Fichiers volumineux", + "last": "Dernier", "last_seen": "Dernièrement utilisÊ", "latest_version": "Dernière version", "latitude": "Latitude", "leave": "Quitter", + "leave_album": "Quitter l'album", "lens_model": "Modèle d'objectif", "let_others_respond": "Laisser les autres rÊagir", "level": "Niveau", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "CrÊations les plus rÊcentes", "library_page_sort_last_modified": "Dernière modification", "library_page_sort_title": "Titre de l'album", + "licenses": "Licences", "light": "Clair", - "like_deleted": "RÊaction ÂĢ j'aime Âģ supprimÊe", + "like": "J'aime", + "like_deleted": "RÊaction ÂĢ J'aime Âģ supprimÊe", "link_motion_video": "Lier la photo animÊe", - "link_options": "Options de lien", "link_to_oauth": "Lien au service OAuth", "linked_oauth_account": "Compte OAuth rattachÊ", "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des rÊsultats ÊchouÊ", + "local": "Local", "local_asset_cast_failed": "Impossible de caster un mÊdia qui n'a pas envoyÊ vers le serveur", + "local_assets": "MÊdia locaux", + "local_media_summary": "RÊsumÊ du mÊdia local", "local_network": "RÊseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connectÊ à ce rÊseau Wi-Fi", "location_permission": "Autorisation de localisation", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Saisir la longitude ici", "lock": "Verrouiller", "locked_folder": "Dossier verrouillÊ", + "log_detail_title": "Niveau de journalisation", "log_out": "Se dÊconnecter", "log_out_all_devices": "DÊconnecter tous les appareils", "logged_in_as": "ConnectÊ en tant que {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Mot de passe mis à jour avec succès", "logout_all_device_confirmation": "Êtes-vous sÃģr de vouloir dÊconnecter tous les appareils ?", "logout_this_device_confirmation": "Êtes-vous sÃģr de vouloir dÊconnecter cet appareil ?", + "logs": "Journaux", "longitude": "Longitude", "look": "Regarder", "loop_videos": "VidÊos en boucle", @@ -1185,6 +1288,7 @@ "main_branch_warning": "Vous utilisez une version de dÊveloppement. Nous vous recommandons fortement d'utiliser une version stable !", "main_menu": "Menu principal", "make": "Marque", + "manage_geolocation": "GÊrer la localisation", "manage_shared_links": "GÊrer les liens partagÊs", "manage_sharing_with_partners": "GÊrer le partage avec les partenaires", "manage_the_app_settings": "GÊrer les paramètres de l'application", @@ -1193,8 +1297,7 @@ "manage_your_devices": "GÊrer vos appareils", "manage_your_oauth_connection": "GÊrer votre connexion OAuth", "map": "Carte", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, =0 {Aucune photo dans cette zone} one {# photo} other {# photos}}", "map_cannot_get_user_location": "Impossible d'obtenir la localisation de l'utilisateur", "map_location_dialog_yes": "Oui", "map_location_picker_page_use_location": "Utiliser ma position", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Service de localisation dÊsactivÊ", "map_marker_for_images": "Marqueur de carte pour les images prises à {city}, {country}", "map_marker_with_image": "Marqueur de carte avec image", - "map_no_assets_in_bounds": "Pas de photos dans cette zone", "map_no_location_permission_content": "L'autorisation de localisation est nÊcessaire pour afficher les mÊdias de votre emplacement actuel. Souhaitez-vous l'autoriser maintenant ?", "map_no_location_permission_title": "Permission de localisation refusÊe", "map_settings": "Paramètres de la carte", @@ -1221,6 +1323,7 @@ "mark_as_read": "Marquer comme lu", "marked_all_as_read": "Tout a ÊtÊ marquÊ comme lu", "matches": "Correspondances", + "matching_assets": "MÊdias correspondants", "media_type": "Type de mÊdia", "memories": "Souvenirs", "memories_all_caught_up": "Vous avez tout vu", @@ -1239,6 +1342,7 @@ "merged_people_count": "{count, plural, one {# personne fusionnÊe} other {# personnes fusionnÊes}}", "minimize": "RÊduire", "minute": "Minute", + "minutes": "Minutes", "missing": "Manquant", "model": "Modèle", "month": "Mois", @@ -1246,8 +1350,9 @@ "more": "Plus", "move": "DÊplacer", "move_off_locked_folder": "DÊplacer en dehors du dossier verrouillÊ", + "move_to_lock_folder_action_prompt": "{count} ajoutÊ(s) au dossier verrouillÊ", "move_to_locked_folder": "DÊplacer dans le dossier verrouillÊ", - "move_to_locked_folder_confirmation": "Ces photos et vidÊos seront retirÊs de tout les albums et ne seront visibles que dans le dossier verrouillÊ", + "move_to_locked_folder_confirmation": "Ces photos et vidÊos seront retirÊes de tous les albums et ne seront visibles que dans le dossier verrouillÊ", "moved_to_archive": "{count, plural, one {# ÊlÊment dÊplacÊ} other {# ÊlÊments dÊplacÊs}} vers les archives", "moved_to_library": "{count, plural, one {# ÊlÊment dÊplacÊ} other {# ÊlÊments dÊplacÊs}} vers la bibliothèque", "moved_to_trash": "DÊplacÊ dans la corbeille", @@ -1257,6 +1362,10 @@ "my_albums": "Mes albums", "name": "Nom", "name_or_nickname": "Nom ou surnom", + "network_requirement_photos_upload": "Utiliser les donnÊes mobile pour sauvegarder les photos", + "network_requirement_videos_upload": "Utiliser les donnÊes mobile pour sauvegarder les vidÊos", + "network_requirements": "PrÊrequis rÊseau", + "network_requirements_updated": "Contraintes rÊseau modifiÊes, file d'attente de sauvegarde rÊinitialisÊe", "networking_settings": "RÊseau", "networking_subtitle": "GÊrer les adresses du serveur", "never": "Jamais", @@ -1266,6 +1375,7 @@ "new_person": "Nouvelle personne", "new_pin_code": "Nouveau code PIN", "new_pin_code_subtitle": "C'est votre premier accès au dossier verrouillÊ. CrÊez un code PIN pour sÊcuriser l'accès à cette page", + "new_timeline": "Nouvelle vue chronologique", "new_user_created": "Nouvel utilisateur crÊÊ", "new_version_available": "NOUVELLE VERSION DISPONIBLE", "newest_first": "RÊcents en premier", @@ -1279,19 +1389,25 @@ "no_assets_message": "CLIQUEZ POUR ENVOYER VOTRE PREMIÈRE PHOTO", "no_assets_to_show": "Aucun ÊlÊment à afficher", "no_cast_devices_found": "Aucun appareil de diffusion trouvÊ", + "no_checksum_local": "Aucune empreinte numerique disponible - impossible de rÊcupÊrer les mÊdias locaux", + "no_checksum_remote": "Aucune empreinte numÊrique disponible - impossible de rÊcupÊrer les mÊdias distants", "no_duplicates_found": "Aucun doublon n'a ÊtÊ trouvÊ.", "no_exif_info_available": "Aucune information exif disponible", "no_explore_results_message": "Envoyez plus de photos pour explorer votre bibliothèque.", "no_favorites_message": "Ajouter des photos et vidÊos à vos favoris pour les retrouver plus rapidement", "no_libraries_message": "CrÊer une bibliothèque externe pour voir vos photos et vidÊos dans un autre espace de stockage", + "no_local_assets_found": "Aucun mÊdia local trouvÊ avec cette empreinte numerique", "no_locked_photos_message": "Les photos et vidÊos du dossier verrouillÊ sont masquÊs et ne s'afficheront pas dans votre galerie ou la recherche.", "no_name": "Pas de nom", "no_notifications": "Pas de notification", "no_people_found": "Aucune personne correspondante trouvÊe", "no_places": "Pas de lieu", + "no_remote_assets_found": "Aucun mÊdia distant trouvÊ avec cette empreinte numerique", "no_results": "Aucun rÊsultat", "no_results_description": "Essayez un synonyme ou un mot-clÊ plus gÊnÊral", "no_shared_albums_message": "CrÊer un album pour partager vos photos et vidÊos avec les personnes de votre rÊseau", + "no_uploads_in_progress": "Pas d'envoi en cours", + "not_available": "N/A", "not_in_any_album": "Dans aucun album", "not_selected": "Non sÊlectionnÊ", "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'Êtiquette de stockage aux mÊdias prÊcÊdemment envoyÊs, exÊcutez", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Ressources Immich officielles", "offline": "Hors ligne", + "offset": "DÊcalage", "ok": "OK", "oldest_first": "Anciens en premier", "on_this_device": "Sur cet appareil", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Ouvrir les filtres de recherche", "options": "Options", "or": "ou", + "organize_into_albums": "Organiser dans des albums", + "organize_into_albums_description": "Mettre les photos existantes dans des albums en utilisant les paramètres de synchronisation actuels", "organize_your_library": "Organiser votre bibliothèque", "original": "original", "other": "Autre", "other_devices": "Autres appareils", + "other_entities": "Autres entitÊs", "other_variables": "Autres variables", "owned": "PossÊdÊ", "owner": "PropriÊtaire", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Permission limitÊe. Pour permettre à Immich de sauvegarder et de gÊrer l'ensemble de votre bibliothèque, accordez l'autorisation pour les photos et vidÊos dans les Paramètres.", "permission_onboarding_request": "Immich nÊcessite l'autorisation d'accÊder à vos photos et vidÊos.", "person": "Personne", + "person_age_months": "{months, plural, one {# mois} other {# mois}}", + "person_age_year_months": "1 an, {months, plural, one {# mois} other {# mois}}", + "person_age_years": "{years, plural, other {# ans}}", "person_birthdate": "NÊ(e) le {date}", "person_hidden": "{name}{hidden, select, true { (cachÊ)} other {}}", "photo_shared_all_users": "Il semble que vous ayez partagÊ vos photos avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec qui les partager.", @@ -1390,7 +1513,7 @@ "photos_and_videos": "Photos et vidÊos", "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos des annÊes prÊcÊdentes", - "pick_a_location": "Choisissez un lieu", + "pick_a_location": "Choisissez une localisation", "pin_code_changed_successfully": "Code PIN changÊ avec succès", "pin_code_reset_successfully": "RÊinitialisation du code PIN rÊussie", "pin_code_setup_successfully": "DÊfinition du code PIN rÊussie", @@ -1406,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "GÊrer les prÊfÊrences de l'application", "preferences_settings_title": "PrÊfÊrences", + "preparing": "PrÊparation", "preset": "PrÊrÊglage", "preview": "Aperçu", "previous": "PrÊcÊdent", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "L'application mobile est obsolète. Veuillez effectuer la mise à jour vers la dernière version mineure.", "profile_drawer_client_server_up_to_date": "Le client et le serveur sont à jour", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Mode lecture seule activÊ. Faites un appui long sur l'image de l'utilisateur pour quitter.", "profile_drawer_server_out_of_date_major": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version majeure.", "profile_drawer_server_out_of_date_minor": "Le serveur est obsolète. Veuillez mettre à jour vers la dernière version mineure.", "profile_image_of_user": "Image de profil de {user}", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clÊ du produit pour le Serveur est gÊrÊe par l'administrateur", + "query_asset_id": "Obtenir l'ID du mÊdia", + "queue_status": "{count}/{total} en file d'attente", "rating": "Étoile d'Êvaluation", "rating_clear": "Effacer l'Êvaluation", "rating_count": "{count, plural, one {# Êtoile} other {# Êtoiles}}", "rating_description": "Afficher l'Êvaluation EXIF dans le panneau d'information", "reaction_options": "Options de rÊaction", "read_changelog": "Lire les changements", + "readonly_mode_disabled": "Mode lecture seule dÊsactivÊ", + "readonly_mode_enabled": "Mode lecture seule activÊ", + "ready_for_upload": "TÊlÊchargement prÃĒt", "reassign": "RÊattribuer", "reassigned_assets_to_existing_person": "{count, plural, one {# mÊdia rÊattribuÊ} other {# mÊdias rÊattribuÊs}} à {name, select, null {une personne existante} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {# mÊdia rÊattribuÊ} other {# mÊdias rÊattribuÊs}} à une nouvelle personne", @@ -1488,6 +1618,9 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des mÊtadonnÊes", "regenerating_thumbnails": "RegÊnÊration des miniatures", + "remote": "À distance", + "remote_assets": "MÊdia à distance", + "remote_media_summary": "RÊsumÊ du mÊdia distant", "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Ê ?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Supprimer la plage de date personnalisÊe", "remove_deleted_assets": "Supprimer les fichiers hors ligne", "remove_from_album": "Supprimer de l'album", + "remove_from_album_action_prompt": "{count} supprimÊ(s) de l'album", "remove_from_favorites": "Supprimer des favoris", + "remove_from_lock_folder_action_prompt": "{count} supprimÊ(s) du dossier verrouillÊ", "remove_from_locked_folder": "Supprimer du dossier verrouillÊ", "remove_from_locked_folder_confirmation": "Êtes vous sÃģr de vouloir dÊplacer ces photos et vidÊos en dehors du dossier verrouillÊ ? Elles seront visibles dans votre galerie.", "remove_from_shared_link": "Supprimer des liens partagÊs", @@ -1510,7 +1645,7 @@ "removed_from_favorites_count": "{count, plural, one {# supprimÊ} other {# supprimÊs}} des favoris", "removed_memory": "Souvenir supprimÊ", "removed_photo_from_memory": "Photo supprimÊe du souvenir", - "removed_tagged_assets": "Tag supprimÊ de {count, plural, one {# mÊdia} other {# mÊdias}}", + "removed_tagged_assets": "Étiquette supprimÊe de {count, plural, one {# mÊdia} other {# mÊdias}}", "rename": "Renommer", "repair": "RÊparer", "repair_no_results_message": "Les fichiers non importÊs ou absents s'afficheront ici", @@ -1523,19 +1658,29 @@ "reset_password": "RÊinitialiser le mot de passe", "reset_people_visibility": "RÊinitialiser la visibilitÊ des personnes", "reset_pin_code": "RÊinitialiser le code PIN", + "reset_pin_code_description": "Si vous avez oubliÊ votre code PIN, vous devez contacter l'administrateur du serveur pour le rÊinitialiser", + "reset_pin_code_success": "Code PIN rÊinitialisÊ avec succès", + "reset_pin_code_with_password": "Vous pouvez toujours rÊinitialiser le code PIN avec votre mot de passe", + "reset_sqlite": "RÊinitialiser la base de donnÊes SQLite", + "reset_sqlite_confirmation": "Êtes-vous certain de vouloir rÊinitialiser la base de donnÊes SQLite ? Vous devrez vous dÊconnecter puis vous reconnecter à nouveau pour resynchroniser les donnÊes", + "reset_sqlite_success": "La base de donnÊes SQLite à ÊtÊ rÊinitialisÊ avec succès", "reset_to_default": "RÊtablir les valeurs par dÊfaut", "resolve_duplicates": "RÊsoudre les doublons", "resolved_all_duplicates": "RÊsolution de tous les doublons", "restore": "Restaurer", "restore_all": "Tout restaurer", + "restore_trash_action_prompt": "{count} restaurÊ de la corbeille", "restore_user": "Restaurer l'utilisateur", "restored_asset": "MÊdia restaurÊ", "resume": "Reprendre", + "resume_paused_jobs": "Reprendre {count, plural, one {la tÃĸche en cours} other {les # tÃĸches en cours}}", "retry_upload": "RÊessayer l'envoi", "review_duplicates": "Consulter les doublons", + "review_large_files": "Consulter les fichiers volumineux", "role": "Rôle", "role_editor": "Éditeur", - "role_viewer": "Visionneuse", + "role_viewer": "Visionneur", + "running": "En cours", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "ClÊ API sauvegardÊe", @@ -1566,8 +1711,8 @@ "search_filter_display_option_not_in_album": "Pas dans un album", "search_filter_display_options": "Options d'affichage", "search_filter_filename": "Recherche par nom de fichier", - "search_filter_location": "Lieu", - "search_filter_location_title": "SÊlectionner un lieu", + "search_filter_location": "Localisation", + "search_filter_location_title": "SÊlectionner une localisation", "search_filter_media_type": "Type de mÊdia", "search_filter_media_type_title": "SÊlectionner type de mÊdia", "search_filter_people_title": "SÊlectionner une personne", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Échec de la crÊation de l'album", "selected": "SÊlectionnÊ", "selected_count": "{count, plural, one {# sÊlectionnÊ} other {# sÊlectionnÊs}}", + "selected_gps_coordinates": "CoordonnÊes GPS sÊlectionnÊes", "send_message": "Envoyer un message", "send_welcome_email": "Envoyer un courriel de bienvenue", "server_endpoint": "Adresse du serveur", @@ -1667,6 +1813,7 @@ "settings_saved": "Paramètres sauvegardÊs", "setup_pin_code": "DÊfinir un code PIN", "share": "Partager", + "share_action_prompt": "{count} ÊlÊments partagÊs", "share_add_photos": "Ajouter des photos", "share_assets_selected": "{count} sÊlectionnÊ(s)", "share_dialog_preparing": "PrÊparation...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "CopiÊ dans le presse-papier", "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", "shared_link_create_error": "Erreur pendant la crÊation du lien partagÊ", + "shared_link_custom_url_description": "AccÊder à ce lien partagÊ avec une URL personnalisÊe", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", "shared_link_edit_expire_after_option_days": "{count} jours", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "GÊrer les liens partagÊs", "shared_link_options": "Options de lien partagÊ", + "shared_link_password_description": "Demander un mot de passe pour accÊder à ce lien partagÊ", "shared_links": "Liens partagÊs", "shared_links_description": "Partager les photos et vidÊos via un lien", "shared_photos_and_videos_count": "{assetCount, plural, other {# photos et vidÊos partagÊes.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Afficher la transition du diaporama", "show_supporter_badge": "Badge de contributeur", "show_supporter_badge_description": "Afficher le badge de contributeur", + "show_text_search_menu": "Afficher le menu de recherche de texte", "shuffle": "MÊlanger", "sidebar": "Barre latÊrale", "sidebar_display_description": "Afficher un lien vers la vue dans la barre latÊrale", @@ -1762,12 +1912,14 @@ "sort_created": "Date de crÊation", "sort_items": "Nombre d'ÊlÊments", "sort_modified": "Date de modification", + "sort_newest": "Photo la plus rÊcente", "sort_oldest": "Photo la plus ancienne", "sort_people_by_similarity": "Trier les personnes par similitude", "sort_recent": "Photo la plus rÊcente", "sort_title": "Titre", "source": "Source", "stack": "Empiler", + "stack_action_prompt": "{count} empilÊ(s)", "stack_duplicates": "Empiler les doublons", "stack_select_one_photo": "SÊlectionnez une photo principale pour la pile", "stack_selected_photos": "Empiler les photos sÊlectionnÊes", @@ -1775,6 +1927,7 @@ "stacktrace": "Trace de la pile", "start": "Commencer", "start_date": "Date de dÊbut", + "start_date_before_end_date": "La date de dÊbut doit ÃĒtre avant la date de fin", "state": "RÊgion", "status": "Statut", "stop_casting": "ArrÃĒter la diffusion", @@ -1787,6 +1940,7 @@ "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisÊ", "submit": "Soumettre", + "success": "RÊussi", "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", "support": "Soutenir", @@ -1796,6 +1950,10 @@ "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", "sync_albums_manual_subtitle": "Synchroniser toutes les vidÊos et photos envoyÊes dans les albums sÊlectionnÊs", + "sync_local": "Synchronisation locale", + "sync_remote": "Synchronisation à distance", + "sync_status": "Statut de synchronisation", + "sync_status_subtitle": "Consulter et gÊrer le système de synchronisation", "sync_upload_album_setting_subtitle": "CrÊez et envoyez vos photos et vidÊos dans les albums sÊlectionnÊs sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les mÊdias", @@ -1806,6 +1964,7 @@ "tag_updated": "Étiquette mise à jour : {tag}", "tagged_assets": "Étiquette ajoutÊe à {count, plural, one {# mÊdia} other {# mÊdias}}", "tags": "Étiquettes", + "tap_to_run_job": "Appuyez pour dÊmarrer la tÃĸche", "template": "Modèle", "theme": "Thème", "theme_selection": "SÊlection du thème", @@ -1832,12 +1991,15 @@ "to_change_password": "Modifier le mot de passe", "to_favorite": "Ajouter aux favoris", "to_login": "Se connecter", + "to_multi_select": "pour faire une sÊlection multiple", "to_parent": "Aller au dossier parent", + "to_select": "pour faire une sÊlection", "to_trash": "Corbeille", "toggle_settings": "Inverser les paramètres", "total": "Total", "total_usage": "Utilisation globale", "trash": "Corbeille", + "trash_action_prompt": "{count} mÊdia(s) mis à la corbeille", "trash_all": "Tout supprimer", "trash_count": "Corbeille {count, number}", "trash_delete_asset": "Mettre à la corbeille/Supprimer un mÊdia", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "SÊlectionner les ÊlÊments", "trash_page_title": "Corbeille ({count})", "trashed_items_will_be_permanently_deleted_after": "Les ÊlÊments dans la corbeille seront supprimÊs dÊfinitivement après {days, plural, one {# jour} other {# jours}}.", + "troubleshoot": "DÊpannage", "type": "Type", "unable_to_change_pin_code": "Impossible de changer le code PIN", "unable_to_setup_pin_code": "Impossible de dÊfinir le code PIN", "unarchive": "DÊsarchiver", + "unarchive_action_prompt": "{count} supprimÊ(s) de l'archive", "unarchived_count": "{count, plural, one {# supprimÊ} other {# supprimÊs}} de l'archive", "undo": "Annuler", "unfavorite": "Enlever des favoris", + "unfavorite_action_prompt": "{count} supprimÊ(s) des favoris", "unhide_person": "Afficher la personne", "unknown": "Inconnu", "unknown_country": "Pays non connu", @@ -1874,16 +2039,22 @@ "unselect_all": "Annuler la sÊlection", "unselect_all_duplicates": "DÊsÊlectionner tous les doublons", "unselect_all_in": "Tout dÊsÊlectionner dans {group}", - "unstack": "DÊsempiler", + "unstack": "DÊpiler", + "unstack_action_prompt": "{count} dÊpilÊ(s)", "unstacked_assets_count": "{count, plural, one {# mÊdia dÊpilÊ} other {# mÊdias dÊpilÊs}}", + "untagged": "Sans Êtiquette", "up_next": "Suite", + "update_location_action_prompt": "Mettre à jour la localisation des {count} mÊdias sÊlectionnÊs avec :", "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", + "upload_action_prompt": "{count} en attente d'envoi", "upload_concurrency": "Envois simultanÊs", + "upload_details": "DÊtails des envois", "upload_dialog_info": "Voulez-vous sauvegarder la sÊlection vers le serveur ?", "upload_dialog_title": "Envoyer le mÊdia", "upload_errors": "L'envoi s'est complÊtÊ avec {count, plural, one {# erreur} other {# erreurs}}. RafraÃŽchissez la page pour voir les nouveaux mÊdias envoyÊs.", + "upload_finished": "Envoi fini", "upload_progress": "{remaining, number} restant(s) - {processed, number} traitÊ(s)/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# doublon ignorÊ} other {# doublons ignorÊs}}", "upload_status_duplicates": "Doublons", @@ -1892,6 +2063,7 @@ "upload_success": "Envoi rÊussi. RafraÃŽchissez la page pour voir les nouveaux mÊdias envoyÊs.", "upload_to_immich": "Envoyer vers Immich ({count})", "uploading": "Envoi", + "uploading_media": "Envoi du mÊdia", "url": "URL", "usage": "Utilisation", "use_biometric": "Utiliser l'authentification biomÊtrique", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Voir les statistiques d'utilisation du compte", "username": "Nom d'utilisateur", "users": "Utilisateurs", + "users_added_to_album_count": "{count, plural, one {# utilisateur ajoutÊ} other {# utilisateurs ajoutÊs}} à l'album", "utilities": "Utilitaires", "validate": "Valider", "validate_endpoint_error": "Merci d'entrer un lien valide", @@ -1930,6 +2103,7 @@ "view_album": "Afficher l'album", "view_all": "Voir tout", "view_all_users": "Voir tous les utilisateurs", + "view_details": "Voir les dÊtails", "view_in_timeline": "Voir dans la vue chronologique", "view_link": "Voir le lien", "view_links": "Voir les liens", @@ -1937,11 +2111,12 @@ "view_next_asset": "Voir le mÊdia suivant", "view_previous_asset": "Voir le mÊdia prÊcÊdent", "view_qr_code": "Voir le QR code", + "view_similar_photos": "Afficher les photos similaires", "view_stack": "Afficher la pile", "view_user": "Voir l'utilisateur", "viewer_remove_from_stack": "Retirer de la pile", "viewer_stack_use_as_main_asset": "Utiliser comme ÊlÊment principal", - "viewer_unstack": "DÊsempiler", + "viewer_unstack": "DÊpiler", "visibility_changed": "VisibilitÊ changÊe pour {count, plural, one {# personne} other {# personnes}}", "waiting": "En attente", "warning": "Attention", @@ -1955,5 +2130,6 @@ "yes": "Oui", "you_dont_have_any_shared_links": "Vous n'avez aucun lien partagÊ", "your_wifi_name": "Nom du rÊseau wifi", - "zoom_image": "Zoomer" + "zoom_image": "Zoomer", + "zoom_to_bounds": "Zoom sur la zone" } diff --git a/i18n/gl.json b/i18n/gl.json index 558cc48900..b6a59fadc7 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -14,6 +14,7 @@ "add_a_location": "Engadir unha ubicaciÃŗn", "add_a_name": "Engadir un nome", "add_a_title": "Engadir un título", + "add_birthday": "Engadir cumpleanos", "add_endpoint": "Engadir endpoint", "add_exclusion_pattern": "Engadir patrÃŗn de exclusiÃŗn", "add_import_path": "Engadir ruta de importaciÃŗn", @@ -22,10 +23,14 @@ "add_partner": "Engadir compaÃąeiro/a", "add_path": "Engadir ruta", "add_photos": "Engadir fotos", + "add_tag": "Engadir etiqueta", "add_to": "Engadir aâ€Ļ", "add_to_album": "Engadir ao ÃĄlbum", "add_to_album_bottom_sheet_added": "Engadido a {album}", "add_to_album_bottom_sheet_already_exists": "Xa estÃĄ en {album}", + "add_to_album_toggle": "Alternar selecciÃŗn para o {album}", + "add_to_albums": "Engadir en ÃĄlbums", + "add_to_albums_count": "Engadir a {count} ÃĄlbums", "add_to_shared_album": "Engadir ao ÃĄlbum compartido", "add_url": "Engadir URL", "added_to_archive": "Engadido ao arquivo", @@ -33,17 +38,25 @@ "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/**\".", + "admin_user": "Usuario administrador", "asset_offline_description": "Este activo da biblioteca externa xa non se atopa no disco e moveuse ao lixo. Se o ficheiro se moveu dentro da biblioteca, comproba a tÃēa liÃąa de tempo para o novo activo correspondente. Para restaurar este activo, asegÃērate de que Immich poida acceder ÃĄ ruta do ficheiro a continuaciÃŗn e escanee a biblioteca.", "authentication_settings": "ConfiguraciÃŗn de autenticaciÃŗn", "authentication_settings_description": "Xestionar contrasinal, OAuth e outras configuraciÃŗns de autenticaciÃŗn", "authentication_settings_disable_all": "EstÃĄs seguro de que queres desactivar todos os mÊtodos de inicio de sesiÃŗn? O inicio de sesiÃŗn desactivarase completamente.", "authentication_settings_reenable": "Para reactivalo, use un Comando de servidor.", "background_task_job": "Tarefas en segundo plano", - "backup_database": "Copia de seguridade da base de datos", - "backup_database_enable_description": "Activar copias de seguridade da base de datos", + "backup_database": "Crear un vertedoiro de base de datos", + "backup_database_enable_description": "Activar o vertedoiro de copias de seguridade da base de datos", "backup_keep_last_amount": "Cantidade de copias de seguridade anteriores a conservar", + "backup_onboarding_1_description": "Copia no exterior na nube ou noutra localizaciÃŗn física.", + "backup_onboarding_2_description": "Copias locais en diferentes dispositivos. Isto inclue os arquivos principais e as copias de esos arquivos localmente.", + "backup_onboarding_3_description": "copias totais da tua informaciÃŗn, incluindo os arquivos orixinais. Isto inclue 1 copia externa e 2 copias locais.", + "backup_onboarding_description": "Unha estratexia de copia 3-2-1 Ê recomendada para protexer os teus datos. Deberías gardar copias das tÃēas fotos/videos subidas así como da base de datos de Immich como unha soluciÃŗn de seguridade.", + "backup_onboarding_footer": "Pra mÃĄis informaciÃŗn sobre copias de seguridade de Immich, por favor use a seguinte ligazÃŗn de documentaciÃŗn.", + "backup_onboarding_parts_title": "Unha copia de seguridade 3-2-1 inclue:", + "backup_onboarding_title": "Copia de seguridade", "backup_settings": "ConfiguraciÃŗn da copia de seguridade", - "backup_settings_description": "Xestionar a configuraciÃŗn da copia de seguridade da base de datos", + "backup_settings_description": "Xestionar a configuraciÃŗn do volcado da base de datos", "cleared_jobs": "Traballos borrados para: {job}", "config_set_by_file": "A configuraciÃŗn establÊcese actualmente mediante un ficheiro de configuraciÃŗn", "confirm_delete_library": "EstÃĄs seguro de que queres eliminar a biblioteca {library}?", @@ -110,6 +123,13 @@ "logging_enable_description": "Activar rexistro", "logging_level_description": "Cando estea activado, que nivel de rexistro usar.", "logging_settings": "Rexistro", + "machine_learning_availability_checks": "ComprobaciÃŗns de dispoÃąibilidade", + "machine_learning_availability_checks_description": "Detectar automÃĄticamente e preferir servidores de aprendizaxe profunda dispoÃąibles", + "machine_learning_availability_checks_enabled": "Activar comprobaciÃŗns de dispoÃąibilidade", + "machine_learning_availability_checks_interval": "Intervalo de comprobaciÃŗn", + "machine_learning_availability_checks_interval_description": "Intervalo en milisegundos entre comprobaciÃŗns de dispoÃąibilidade", + "machine_learning_availability_checks_timeout": "Tempo de espera da solicitude", + "machine_learning_availability_checks_timeout_description": "Tempo de espera en milisegundos para as comprobaciÃŗn de dispoÃąibilidade", "machine_learning_clip_model": "Modelo CLIP", "machine_learning_clip_model_description": "O nome dun modelo CLIP listado aquí. Ten en conta que debe volver executar o traballo 'Busca Intelixente' para todas as imaxes ao cambiar un modelo.", "machine_learning_duplicate_detection": "DetecciÃŗn de duplicados", @@ -164,6 +184,19 @@ "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", + "nightly_tasks_cluster_faces_setting_description": "Executar recoÃąecemento facial nas novas caras detectadas", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novas caras", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de datos", + "nightly_tasks_database_cleanup_setting_description": "Limpar informaciÃŗn vella e obsoleta da base de datos", + "nightly_tasks_generate_memories_setting": "Xerar memorias", + "nightly_tasks_generate_memories_setting_description": "Crear novas memorias dende os recursos", + "nightly_tasks_missing_thumbnails_setting": "Xerar as miniaturas que faltan", + "nightly_tasks_missing_thumbnails_setting_description": "Encolar arquivos sin miniaturas para a xeraciÃŗn das miniaturas", + "nightly_tasks_settings": "ConfiguraciÃŗn das tarefas nocturnas", + "nightly_tasks_settings_description": "Administrar as tarefas nocturnas", + "nightly_tasks_start_time_setting": "Tempo de inicio", + "nightly_tasks_start_time_setting_description": "O tempo no que o servidor comeza a executar as tarefas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Sincronizar uso de cuota", "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", @@ -455,7 +488,6 @@ "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 {{name}} other {novo ÃĄlbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", @@ -477,6 +509,7 @@ "back_close_deselect": "AtrÃĄs, pechar ou deseleccionar", "background_location_permission": "Permiso de ubicaciÃŗn en segundo plano", "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso ÃĄ ubicaciÃŗn precisa para que a aplicaciÃŗn poida ler o nome da rede wifi", + "backup": "Copia de Seguridade", "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios ÃĄlbums. Polo tanto, os ÃĄlbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", @@ -916,9 +949,6 @@ "exif_bottom_sheet_location": "UBICACIÓN", "exif_bottom_sheet_people": "PERSOAS", "exif_bottom_sheet_person_add_person": "Engadir nome", - "exif_bottom_sheet_person_age_months": "Idade {months} meses", - "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", - "exif_bottom_sheet_person_age_years": "Idade {years}", "exit_slideshow": "Saír da PresentaciÃŗn", "expand_all": "Expandir todo", "experimental_settings_new_asset_list_subtitle": "Traballo en progreso", @@ -1071,7 +1101,6 @@ "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", @@ -1130,7 +1159,6 @@ "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": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "Non se pode obter a ubicaciÃŗn do usuario", "map_location_dialog_yes": "Si", @@ -1139,7 +1167,6 @@ "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", diff --git a/i18n/he.json b/i18n/he.json index 737c307f31..8138679cdd 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -14,6 +14,7 @@ "add_a_location": "הוספ×Ē ×ž×™×§×•×", "add_a_name": "הוספ×Ē ×Š×", "add_a_title": "הוספ×Ē ×›×•×Ēר×Ē", + "add_birthday": "הוספ×Ē ×™×•× הולד×Ē", "add_endpoint": "×”×•×Ą×Ŗ נקוד×Ē ×§×Ļה", "add_exclusion_pattern": "הוספ×Ē ×“×¤×•×Ą החרגה", "add_import_path": "הוספ×Ē × ×Ēיב יבוא", @@ -27,6 +28,10 @@ "add_to_album": "הוספה לאלבום", "add_to_album_bottom_sheet_added": "× ×•×Ą×Ŗ ל {album}", "add_to_album_bottom_sheet_already_exists": "כבר ב {album}", + "add_to_album_bottom_sheet_some_local_assets": "לא ני×Ēן היה ×œ×”×•×Ą×™×Ŗ חלק מהקב×Ļים המקומיים לאלבום", + "add_to_album_toggle": "החלפ×Ē ×ž×Ļב בחירה ×ĸבור {album}", + "add_to_albums": "הוספה לאלבומים", + "add_to_albums_count": "×”×•×Ą×Ŗ ({count}) לאלבום", "add_to_shared_album": "הוספה לאלבום משו×Ē×Ŗ", "add_url": "הוספ×Ē ×§×™×Š×•×¨", "added_to_archive": "× ×•×Ą×Ŗ לארכיון", @@ -34,6 +39,7 @@ "added_to_favorites_count": "{count, number} נוספו למו×ĸדפים", "admin": { "add_exclusion_pattern_description": "הוספ×Ē ×“×¤×•×Ą×™ החרגה. × ×Ēמכ×Ē ×”×Ēאמ×Ē ×“×¤×•×Ą×™× באמ×Ļ×ĸו×Ē *, ** ו-?. כדי לה×Ē×ĸלם מכל הקב×Ļים ב×Ēיקיה כלשהי בשם \"Raw\", יש להש×Ēמ׊ ב \"**/Raw/**\". כדי לה×Ē×ĸלם מכל הקב×Ļים המס×Ēיימים ב \"tif.\", יש להש×Ēמ׊ ב \"tif.*/**\". כדי לה×Ē×ĸלם מנ×Ēיב מוחלט, יש להש×Ēמ׊ ב \"**/× ×Ēיב/לה×Ē×ĸלמו×Ē\".", + "admin_user": "מנהל מ×ĸרכ×Ē", "asset_offline_description": "×Ēמונה מספרייה חי×Ļוני×Ē ×–×• לא נמ×Ļא×Ē ×™×•×Ēר בדיסק והו×ĸברה לאשפה. אם הקוב×Ĩ הו×ĸבר מ×Ēוך הספרייה, נא לבדוק א×Ē ×Ļיר הזמן שלך ×ĸבור ה×Ēמונה המקבילה החדש. כדי לשחזר ×Ēמונה זו, נא לוודא ׊-Immich יכול לגש×Ē ××œ × ×Ēיב הקוב×Ĩ למטה ולסרוק מחדש א×Ē ×”×Ą×¤×¨×™×™×”.", "authentication_settings": "הגדרו×Ē ×”×Ēחברו×Ē", "authentication_settings_description": "ניהול סיסמה, OAuth, והגדרו×Ē ×”×Ēחברו×Ē ××—×¨×•×Ē", @@ -43,6 +49,13 @@ "backup_database": "גיבוי מסד × ×Ēונים", "backup_database_enable_description": "אפ׊ר גיבויי מסד × ×Ēונים", "backup_keep_last_amount": "כמו×Ē ×Š×œ גיבויים קודמים שיש לשמור", + "backup_onboarding_1_description": "ה×ĸ×Ē×§ ב×ĸנן או במיקום פיזי אחר מחו×Ĩ למקום השר×Ē.", + "backup_onboarding_2_description": "ה×ĸ×Ēקים מקומיים במכשירים שונים. זה כולל א×Ē ×”×§×‘×Ļים הראשיים וגיבוי של הקב×Ļים האלה באופן מקומי.", + "backup_onboarding_3_description": "סך כל הה×ĸ×Ēקים של הנ×Ēונים שלך, כולל הקב×Ļים המקוריים. זה כולל ה×ĸ×Ē×§ אחד מחו×Ĩ למקום השר×Ē ×•×Š× ×™ ה×ĸ×Ēקים מקומיים.", + "backup_onboarding_description": "אסטרטגיי×Ē ×’×™×‘×•×™ 3-2-1 הינה מומל×Ļ×Ē ×ĸל מנ×Ē ×œ×”×’×Ÿ ×ĸל הנ×Ēונים שלך. ×ĸליך להשאיר ה×ĸ×Ēקים של ×Ēמונו×Ē/סרטונים שהו×ĸלו כמו גם א×Ē ×ž×Ą×“ הנ×Ēונים של Immich ×ĸבור פ×Ēרון גיבוי ×ž×§×™×Ŗ.", + "backup_onboarding_footer": "×ĸבור מיד×ĸ × ×•×Ą×Ŗ ×ĸל גיבוי Immich, נא לפנו×Ē ××œ ה×Ēי×ĸוד.", + "backup_onboarding_parts_title": "גיבוי 3-2-1 כולל:", + "backup_onboarding_title": "גיבויים", "backup_settings": "הגדרו×Ē ×’×™×‘×•×™", "backup_settings_description": "ניהול הגדרו×Ē ×’×™×‘×•×™ מסד × ×Ēונים.", "cleared_jobs": "נוקו משימו×Ē ×ĸבור: {job}", @@ -111,6 +124,13 @@ "logging_enable_description": "אפ׊ר רישום ביומן", "logging_level_description": "כאשר פו×ĸל, באיזה רמ×Ē ×™×•×ž×Ÿ ל×Ē×ĸד.", "logging_settings": "רישום ביומן", + "machine_learning_availability_checks": "בדיק×Ē ×–×ž×™× ×•×Ē", + "machine_learning_availability_checks_description": "זהה ו×Ē×ĸדת אוטומטי×Ē ×Š×¨×Ēי למיד×Ē ×ž×›×•× ×” זמינים", + "machine_learning_availability_checks_enabled": "הפ×ĸל×Ē ×‘×“×™×§×•×Ē ×–×ž×™× ×•×Ē", + "machine_learning_availability_checks_interval": "×Ēזמון בדיקה", + "machine_learning_availability_checks_interval_description": "מרווח זמן במילישניו×Ē ×‘×™×Ÿ בדיקו×Ē ×–×ž×™× ×•×Ē", + "machine_learning_availability_checks_timeout": "פץק זמן לבקשה", + "machine_learning_availability_checks_timeout_description": "פץק זמן במילישניו×Ē ×ĸבור בדיקו×Ē ×–×ž×™× ×•×Ē", "machine_learning_clip_model": "מודל CLIP", "machine_learning_clip_model_description": "שמו של מודל CLIP רשום כאן. שים לב ׊×ĸליך להפ×ĸיל מחדש א×Ē ×”×ž×Š×™×ž×” 'חיפוש חכם' ×ĸבור כל ה×Ēמונו×Ē ×‘×ĸ×Ē ×Š×™× ×•×™ מודל.", "machine_learning_duplicate_detection": "אי×Ēור כפילויו×Ē", @@ -155,16 +175,30 @@ "map_settings": "מפה", "map_settings_description": "ניהול הגדרו×Ē ×ž×¤×”", "map_style_description": "כ×Ēוב×Ē ××Ēר ל×ĸרכ×Ē × ×•×Š× של מפה style.json", - "memory_cleanup_job": "ניקוי זיכרון", - "memory_generate_job": "י×Ļיר×Ē ×–×™×›×¨×•×Ÿ", + "memory_cleanup_job": "ניקוי זיכרון (היום לפני..)", + "memory_generate_job": "י×Ļיר×Ē ×–×™×›×¨×•×Ÿ (היום לפני..)", "metadata_extraction_job": "חל×Ĩ מטא-× ×Ēונים", "metadata_extraction_job_description": "חל×Ĩ מטא-× ×Ēונים מכל ×Ēמונה, כגון GPS, פנים ורזולו×Ļיה", "metadata_faces_import_setting": "אפ׊ר יבוא פנים", "metadata_faces_import_setting_description": "יבא פנים מנ×Ēוני EXIF של ×Ēמונה ומקב×Ļים נלווים", "metadata_settings": "הגדרו×Ē ×ž×˜×-× ×Ēונים", - "metadata_settings_description": "ניהול הגדרו×Ē ×ž×˜×-× ×Ēונים", - "migration_job": "ה×ĸברה", + "metadata_settings_description": "ניהול הגדרו×Ē metadata", + "migration_job": "נדידה", "migration_job_description": "ה×ĸבר ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ ×Ēמונו×Ē ×•×¤× ×™× למבנה ה×Ēיקיו×Ē ×”×ĸדכני ביו×Ēר", + "nightly_tasks_cluster_faces_setting_description": "ב×Ļ×ĸ זיהוי פנים ×ĸבור פר×Ļופים שזוהו לאחרונה", + "nightly_tasks_cluster_new_faces_setting": "קב×Ĩ פנים חדשו×Ē", + "nightly_tasks_database_cleanup_setting": "משימו×Ē ×Ēחזוקה וניקוי של מסד הנ×Ēונים", + "nightly_tasks_database_cleanup_setting_description": "נקה × ×Ēונים ישנים שפג ×Ēוקפם ממסד הנ×Ēונים", + "nightly_tasks_generate_memories_setting": "י×Ļיר×Ē ×–×›×¨×•× ×•×Ē", + "nightly_tasks_generate_memories_setting_description": "×Ļור זכרונו×Ē ×—×“×Š×™× מה×Ēמונו×Ē ×Š×œ×š", + "nightly_tasks_missing_thumbnails_setting": "×Ļור ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×—×Ą×¨×•×Ē", + "nightly_tasks_missing_thumbnails_setting_description": "×”×•×Ą×Ŗ ל×Ēור קב×Ļים ללא ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×œ×™×Ļירה של ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", + "nightly_tasks_settings": "הגדרו×Ē ×Š×œ משימו×Ē ×œ×™×œ×™×•×Ē", + "nightly_tasks_settings_description": "נהל משימו×Ē ×œ×™×œ×™×•×Ē", + "nightly_tasks_start_time_setting": "זמן ה×Ēחלה", + "nightly_tasks_start_time_setting_description": "הש×ĸה שבה השר×Ē ×ž×Ēחיל להרי×Ĩ א×Ē ×”×ž×Š×™×ž×•×Ē ×”×œ×™×œ×™×•×Ē", + "nightly_tasks_sync_quota_usage_setting": "סנכרון מכס×Ē ×Š×™×ž×•×Š", + "nightly_tasks_sync_quota_usage_setting_description": "×ĸדכן א×Ē ×ž×›×Ą×Ē ×”××—×Ą×•×Ÿ של המש×Ēמ׊ בה×Ēאם לשימוש הנוכחי", "no_paths_added": "לא נוספו × ×Ēיבים", "no_pattern_added": "לא נוספה ×Ēבני×Ē", "note_apply_storage_label_previous_assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē", @@ -195,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "×ĸקיפ×Ē URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override_description": "אפ׊ר כאשר ץפק OAuth לא מאפ׊ר כ×Ēוב×Ē URI לנייד, כמו ''{callback}''", + "oauth_role_claim": "דריש×Ē ×Ēפקיד", + "oauth_role_claim_description": "ה×ĸ× ×§ גיש×Ē ×ž× ×”×œ באופן אוטומטי אם ×Ēבי×ĸה זו קיימ×Ē. ×ĸרך ה×Ēבי×ĸה יכול להיו×Ē 'user' או 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרו×Ē ×”×Ēחברו×Ē ×ĸם OAuth", "oauth_settings_more_details": "למיד×ĸ × ×•×Ą×Ŗ אודו×Ē ×Ēכונה זו, בדוק א×Ē ×”×Ēי×ĸוד.", @@ -203,7 +239,7 @@ "oauth_storage_quota_claim": "דריש×Ē ×ž×›×Ą×Ē ××—×Ą×•×Ÿ", "oauth_storage_quota_claim_description": "הגדר אוטומטי×Ē ××Ē ×ž×›×Ą×Ē ×”××—×Ą×•×Ÿ של המש×Ēמ׊ ל×ĸרך של דרישה זו.", "oauth_storage_quota_default": "מכס×Ē ××—×Ą×•×Ÿ בריר×Ē ×ž×—×“×œ (GiB)", - "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופק×Ē ×“×¨×™×Š×” (הזן 0 ×ĸבור מכסה בל×Ēי מוגבל×Ē).", + "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופק×Ē ×“×¨×™×Š×”.", "oauth_timeout": "הבקשה נכשלה – הזמן הק×Ļוב הס×Ēיים", "oauth_timeout_description": "זמן ×§×Ļוב לבקשו×Ē (במילישניו×Ē)", "password_enable_description": "ה×Ēחבר ×ĸם דוא\"ל וסיסמה", @@ -243,6 +279,7 @@ "storage_template_migration_info": "×Ēבני×Ē ×”××—×Ą×•×Ÿ ×Ēמיר א×Ē ×›×œ ההרחבו×Ē ×œ××•×Ēיו×Ē ×§×˜× ×•×Ē. שינויים ב×Ēבני×Ē ×™×—×•×œ×• רק ×ĸל ×Ēמונו×Ē ×—×“×Š×•×Ē. כדי להחיל באופן רטרואקטיבי א×Ē ×”×Ēבני×Ē ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē {job}.", "storage_template_migration_job": "משימ×Ē ×”×ĸבר×Ē ×Ēבני×Ē ××—×Ą×•×Ÿ", "storage_template_more_details": "לפרטים נוספים אודו×Ē ×Ēכונה זו, ×ĸיין ב×Ēבני×Ē ×”××—×Ą×•×Ÿ ובהשלכו×Ēיה", + "storage_template_onboarding_description_v2": "כאשר פי×Ļ’ר זה מופ×ĸל, הקב×Ļים יאורגנו אוטומטי×Ē ×œ×¤×™ ×Ēבני×Ē ×Š×”×•×’×“×¨×” ×ĸל ידי המש×Ēמ׊. למיד×ĸ × ×•×Ą×Ŗ, ×ĸיין ב־×Ēי×ĸוד.", "storage_template_path_length": "מגבל×Ē ××•×¨×š × ×Ēיב משו×ĸר×Ē: {length, number}/{limit, number}", "storage_template_settings": "×Ēבני×Ē ××—×Ą×•×Ÿ", "storage_template_settings_description": "ניהול מבנה ה×Ēיקיו×Ē ×•××Ē ×Š× הקוב×Ĩ של ה×Ēמונה שהו×ĸל×Ēה", @@ -329,6 +366,9 @@ "trash_number_of_days_description": "מספר הימים לשמירה של ×Ēמונו×Ē ×‘××Š×¤×” לפני הסר×Ēם ל×Ļמי×Ēו×Ē", "trash_settings": "הגדרו×Ē ×”××Š×¤×”", "trash_settings_description": "ניהול הגדרו×Ē ×”××Š×¤×”", + "unlink_all_oauth_accounts": "ביטול קישור כל חשבונו×Ē OAuth", + "unlink_all_oauth_accounts_description": "זכור לבטל א×Ē ×”×§×™×Š×•×¨ של כל חשבונו×Ē ×”-OAuth לפני ההגירה לספק חדש.", + "unlink_all_oauth_accounts_prompt": "האם באמ×Ē ×‘×¨×Ļונך לבטל א×Ē ×”×§×™×Š×•×¨ של כל חשבונו×Ē ×”-OAuth? פ×ĸולה זו ×Ēאפץ א×Ē ×ž×–×”×” ה-OAuth ×ĸבור כל מ׊×Ēמ׊ ואינה ני×Ē× ×Ē ×œ×‘×™×˜×•×œ.", "user_cleanup_job": "ניקוי מ׊×Ēמשים", "user_delete_delay": "החשבון וה×Ēמונו×Ē ×Š×œ {user} י×Ēוזמנו למחיקה ל×Ļמי×Ēו×Ē ×‘×ĸוד {delay, plural, one {יום #} other {# ימים}}.", "user_delete_delay_settings": "×ĸיכוב מחיקה", @@ -358,10 +398,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "הש×Ēמ׊ באפשרו×Ē ×–×• כדי לסנן מדיה במהלך הסנכרון לפי קריטריונים חלופיים. מומל×Ĩ להש×Ēמ׊ בזה רק אם יש ב×ĸיה בזיהוי כל האלבומים באפליק×Ļיה.", "advanced_settings_enable_alternate_media_filter_title": "[ניסיוני] הש×Ēמ׊ במסנן סנכרון אלבום חלופי שמבכשיר", "advanced_settings_log_level_title": "רמ×Ē ×¨×™×Š×•× ביומן: {level}", - "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לט×ĸינה של ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ž×Ēמונו×Ē ×Š×‘×ž×›×Š×™×¨. הפ×ĸל הגדרה זו כדי לט×ĸון ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē ×‘×ž×§×•×.", + "advanced_settings_prefer_remote_subtitle": "במכשירים מסוימים ט×ĸינ×Ē ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×ž×§×‘×Ļים מקומיים ×ĸלולה להיו×Ē ××™×˜×™×Ē ×‘×ž×™×•×—×“. הפ×ĸל הגדרה זו כדי לט×ĸון ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē ×‘×ž×§×•× זא×Ē.", "advanced_settings_prefer_remote_title": "ה×ĸדת ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē", "advanced_settings_proxy_headers_subtitle": "הגדר proxy headers שהיישום ×Ļריך לשלוח ×ĸם כל בקש×Ē ×¨×Š×Ē", "advanced_settings_proxy_headers_title": "כו×Ēרו×Ē ×¤×¨×•×§×Ą×™", + "advanced_settings_readonly_mode_subtitle": "מאפ׊ר א×Ē ×ž×Ļב לקריאה בלבד בו ה×Ēמונו×Ē × ×™×Ēנו×Ē ×œ×Ļפייה בלבד, דברים כמו בחיר×Ē ×Ēמונו×Ē ×ž×¨×•×‘×•×Ē, שי×Ēות, שידור, מחיקה הם כולם מושב×Ēים. אפ׊ר/השב×Ē ×ž×Ļב לקריאה בלבד באמ×Ļ×ĸו×Ē ×™×Ļגן המש×Ēמ׊ מהמסך הראשי", + "advanced_settings_readonly_mode_title": "מ×Ļב לקריאה בלבד", "advanced_settings_self_signed_ssl_subtitle": "מדלג ×ĸל אימו×Ē ×Ē×ĸוד×Ē SSL ×ĸבור נקוד×Ē ×”×§×Ļה של השר×Ē. דרוש ×ĸבור ×Ē×ĸודו×Ē ×‘×—×Ēימה ×ĸ×Ļמי×Ē.", "advanced_settings_self_signed_ssl_title": "ה×Ēר ×Ē×ĸודו×Ē SSL בח×Ēימה ×ĸ×Ļמי×Ē", "advanced_settings_sync_remote_deletions_subtitle": "מחק או שחזר ×Ēמונה במכשיר זה באופן אוטומטי כאשר פ×ĸולה זו × ×ĸשי×Ē ×‘×“×¤×“×¤×Ÿ", @@ -377,6 +419,7 @@ "album_cover_updated": "×ĸטיפ×Ē ×”××œ×‘×•× ×ĸודכנה", "album_delete_confirmation": "האם באמ×Ē ×‘×¨×Ļונך למחוק א×Ē ×”××œ×‘×•× {album}?", "album_delete_confirmation_description": "אם האלבום הזה משו×Ē×Ŗ, מ׊×Ēמשים אחרים לא יוכלו לגש×Ē ××œ×™×• יו×Ēר.", + "album_deleted": "אלבום נמחק", "album_info_card_backup_album_excluded": "הוחרגו", "album_info_card_backup_album_included": "נכללו", "album_info_updated": "מיד×ĸ האלבום ×ĸודכן", @@ -386,7 +429,9 @@ "album_options": "אפשרויו×Ē ×”××œ×‘×•×", "album_remove_user": "להסיר מ׊×Ēמ׊?", "album_remove_user_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר א×Ē {user}?", + "album_search_not_found": "לא נמ×Ļאו אלבומים ה×Ēואמים לחיפוש שלך", "album_share_no_users": "נראה ששי×Ēפ×Ē ××Ē ×”××œ×‘×•× הזה ×ĸם כל המש×Ēמשים או שאין לך את מ׊×Ēמ׊ לש×Ē×Ŗ אי×Ēו.", + "album_summary": "×Ē×§×Ļיר אלבום", "album_updated": "אלבום ×ĸודכן", "album_updated_setting_description": "קבל הוד×ĸ×Ē ×“×•×\"ל כאשר לאלבום משו×Ē×Ŗ יש ×Ēמונו×Ē ×—×“×Š×•×Ē", "album_user_left": "×ĸזב א×Ē {album}", @@ -405,6 +450,7 @@ "albums_default_sort_order": "סדר מיון אלבומים בריר×Ē ×ž×—×“×œ", "albums_default_sort_order_description": "סדר מיון ×Ēמונו×Ē ×¨××Š×•× ×™ ב×ĸ×Ē ×™×Ļיר×Ē ××œ×‘×•×ž×™× חדשים.", "albums_feature_description": "אוספים של ×Ēמונו×Ē ××Š×¨ ני×Ēנים לשי×Ēות ×ĸם מ׊×Ēמשים אחרים.", + "albums_on_device_count": "אלבומים במכשיר ({count})", "all": "הכל", "all_albums": "כל האלבומים", "all_people": "כל האנשים", @@ -424,7 +470,9 @@ "app_bar_signout_dialog_title": "ה×Ē× ×Ē×§", "app_settings": "הגדרו×Ē ×™×™×Š×•×", "appears_in": "מופי×ĸ ב", + "apply_count": "החל ({count, number})", "archive": "ארכיון", + "archive_action_prompt": "{count} נוספו לארכיון", "archive_or_unarchive_photo": "ה×ĸבר ×Ēמונה לארכיון או הו×Ļא או×Ēה מ׊ם", "archive_page_no_archived_assets": "לא נמ×Ļאו ×Ēמונו×Ē ×‘××¨×›×™×•×Ÿ", "archive_page_title": "בארכיון ({count})", @@ -455,15 +503,18 @@ "asset_restored_successfully": "×Ēמונה שוחזרה בה×Ļלחה", "asset_skipped": "דילג", "asset_skipped_in_trash": "באשפה", + "asset_trashed": "ה×Ēמונה הו×ĸברה לאשפה", + "asset_troubleshoot": "פ×Ēרון ב×ĸיו×Ē ×‘×Ēמונו×Ē", "asset_uploaded": "הו×ĸלה", "asset_uploading": "מ×ĸלהâ€Ļ", "asset_viewer_settings_subtitle": "ניהול הגדרו×Ē ×ž×Ļיג הגלריה שלך", "asset_viewer_settings_title": "מ×Ļיג ה×Ēמונו×Ē", - "assets": "×Ēמונו×Ē", + "assets": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™×", "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 {{name}} other {אלבום חדש}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {× ×•×Ą×Ŗ פריט #} other {נוספו # פריטים}} אל {albumTotal, plural, one {אלבום #} other {# אלבומים}}", "assets_cannot_be_added_to_album_count": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ א×Ē ×”{count, plural, one {×Ēמונה} other {×Ēמונו×Ē}} לאלבום", + "assets_cannot_be_added_to_albums": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ {count, plural, one {פריט} other {פריטים}} לאת אחד מהאלבומים", "assets_count": "{count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "assets_deleted_permanently": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē", "assets_deleted_permanently_from_server": "{count} ×Ēמונו×Ē × ×ž×—×§×• ל×Ļמי×Ēו×Ē ×ž×Š×¨×Ē ×”-Immich", @@ -480,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {×Ēמונה # הושלכה} other {# ×Ēמונו×Ē ×”×•×Š×œ×›×•}} לאשפה", "assets_trashed_from_server": "{count} ×Ēמונו×Ē ×”×•×ĸברו לאשפה מהשר×Ē", "assets_were_part_of_album_count": "{count, plural, one {×Ēמונה היי×Ēה} other {×Ēמונו×Ē ×”×™×•}} כבר חלק מהאלבום", + "assets_were_part_of_albums_count": "{count, plural, one {הפריט כבר היה} other {הפריטים כבר היו}} חלק מהאלבומים", "authorized_devices": "מכשירים מורשים", "automatic_endpoint_switching_subtitle": "ה×Ēחבר מקומי×Ē ×“×¨×š אינטרנט אלחוטי יי×ĸודי כאשר זמין והש×Ēמ׊ בחיבורים חלופיים במקומו×Ē ××—×¨×™×", "automatic_endpoint_switching_title": "החלפ×Ē ×›×Ēוב×Ē ××•×˜×•×ž×˜×™×Ē", "autoplay_slideshow": "מ×Ļג×Ē ×Ēמונו×Ē ××•×˜×•×ž×˜×™×Ē", "back": "חזרה", "back_close_deselect": "חזור, סגור, או בטל בחירה", + "background_backup_running_error": "גיבוי ברק×ĸ פו×ĸל כ×ĸ×Ē, לא ני×Ēן להפ×ĸיל גיבוי ידני", "background_location_permission": "הרשא×Ē ×ž×™×§×•× ברק×ĸ", "background_location_permission_content": "כדי ×œ×”×—×œ×™×Ŗ ר׊×Ēו×Ē ×‘×ĸ×Ē ×¨×™×Ļה ברק×ĸ, היישום ×Ļריך *×Ēמיד* גישה למיקום מדויק ×ĸל מנ×Ē ×œ×§×¨×•× א×Ē ×”×Š× של ר׊×Ē ×”××™× ×˜×¨× ×˜ האלחוטי", + "background_options": "אפשרויו×Ē ×¨×§×ĸ", + "backup": "גיבוי", "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פ×ĸמיים כדי להחריג", "backup_album_selection_page_assets_scatter": "×Ēמונו×Ē ×™×›×•×œ×•×Ē ×œ×”×Ēפזר ×ĸל פני אלבומים מרובים. לפיכך, ני×Ēן לכלול או להחריג אלבומים במהלך ×Ēהליך הגיבוי.", "backup_album_selection_page_select_albums": "בחיר×Ē ××œ×‘×•×ž×™×", "backup_album_selection_page_selection_info": "פרטי בחירה", "backup_album_selection_page_total_assets": "סה״כ ×Ēמונו×Ē ×™×™×—×•×“×™×•×Ē", + "backup_albums_sync": "סנכרון אלבומי גיבוי", "backup_all": "הכל", "backup_background_service_backup_failed_message": "נכשל בגיבוי ×Ēמונו×Ē. מנסה שובâ€Ļ", "backup_background_service_connection_failed_message": "נכשל בה×Ēחברו×Ē ×œ×Š×¨×Ē. מנסה שובâ€Ļ", @@ -532,24 +588,27 @@ "backup_controller_page_none_selected": "אין בחירה", "backup_controller_page_remainder": "בהמ×Ēנה לגיבוי", "backup_controller_page_remainder_sub": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× הנו×Ēרים לגיבוי מ×Ēוך בחירה", - "backup_controller_page_server_storage": "אחסון ׊ר×Ē", + "backup_controller_page_server_storage": "אחסון בשר×Ē", "backup_controller_page_start_backup": "ה×Ēחל גיבוי", "backup_controller_page_status_off": "גיבוי חזי×Ē ××•×˜×•×ž×˜×™ כבוי", "backup_controller_page_status_on": "גיבוי חזי×Ē ××•×˜×•×ž×˜×™ מופ×ĸל", - "backup_controller_page_storage_format": "{total} מ×Ēוך {used} בשימוש", + "backup_controller_page_storage_format": "{used}מ×Ēוך {total} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבו×Ē", "backup_controller_page_total_sub": "כל ה×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזי×Ē", "backup_controller_page_turn_on": "הפ×ĸל גיבוי חזי×Ē", "backup_controller_page_uploading_file_info": "מ×ĸלה מיד×ĸ ×ĸל הקוב×Ĩ", "backup_err_only_album": "לא ני×Ēן להסיר א×Ē ×”××œ×‘×•× היחיד", + "backup_error_sync_failed": "הסינכרון נכשל. לא ני×Ēן להשלים א×Ē ×”×’×™×‘×•×™.", "backup_info_card_assets": "×Ēמונו×Ē", "backup_manual_cancelled": "בוטל", "backup_manual_in_progress": "ה×ĸלאה כבר ב×Ēהליך. נסה אחרי זמן מה", "backup_manual_success": "ה×Ļלחה", "backup_manual_title": "מ×Ļב ה×ĸלאה", + "backup_options": "אפשרויו×Ē ×’×™×‘×•×™", "backup_options_page_title": "אפשרויו×Ē ×’×™×‘×•×™", "backup_setting_subtitle": "ניהול הגדרו×Ē ×”×ĸלא×Ē ×¨×§×ĸ וחזי×Ē", + "backup_settings_subtitle": "נהל הגדרו×Ē ×”×ĸלאה", "backward": "אחורה", "biometric_auth_enabled": "אימו×Ē ×‘×™×•×ž×˜×¨×™ הופ×ĸל", "biometric_locked_out": "גישה לאימו×Ē ×”×‘×™×•×ž×˜×¨×™ נחסמה", @@ -568,7 +627,7 @@ "cache_settings_clear_cache_button": "ניקוי מטמון", "cache_settings_clear_cache_button_title": "מנקה א×Ē ×”×ž×˜×ž×•×Ÿ של היישום. זה ישפי×ĸ באופן מ׊מ×ĸו×Ēי ×ĸל הבי×Ļו×ĸים של היישום ×ĸד שהמטמון מ×Ēמלא מחדש.", "cache_settings_duplicated_assets_clear_button": "נקה", - "cache_settings_duplicated_assets_subtitle": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ׊נמ×Ļאים ברשימה השחורה של היישום", + "cache_settings_duplicated_assets_subtitle": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ׊נמ×Ļאים ברשימ×Ē ×”×”×Ē×ĸלמו×Ē ×Š×œ האפליק×Ļיה", "cache_settings_duplicated_assets_title": "({count}) ×Ēמונו×Ē ×ž×Š×•×›×¤×œ×•×Ē", "cache_settings_statistics_album": "×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē ×Š×œ ספרייה", "cache_settings_statistics_full": "×Ēמונו×Ē ×ž×œ××•×Ē", @@ -585,6 +644,7 @@ "cancel": "ביטול", "cancel_search": "ביטול חיפוש", "canceled": "בוטל", + "canceling": "מבטל", "cannot_merge_people": "לא ני×Ēן למזג אנשים", "cannot_undo_this_action": "אין באפשרו×Ēך לבטל א×Ē ×”×¤×ĸולה הזו!", "cannot_update_the_description": "לא ני×Ēן ל×ĸדכן א×Ē ×”×Ēיאור", @@ -607,6 +667,8 @@ "change_pin_code": "שנה קוד PIN", "change_your_password": "×”×—×œ×Ŗ א×Ē ×”×Ą×™×Ą×ž×” שלך", "changed_visibility_successfully": "הנראו×Ē ×Š×•× ×Ēה בה×Ļלחה", + "charging": "טו×ĸן", + "charging_requirement_mobile_backup": "גיבוי ברק×ĸ דורש ט×ĸינה של המכשיר", "check_corrupt_asset_backup": "בדוק גיבויים פגומים של ×Ēמונו×Ē", "check_corrupt_asset_backup_button": "ב×Ļ×ĸ בדיקה", "check_corrupt_asset_backup_description": "הר×Ĩ בדיקה זו רק ×ĸל Wi-Fi ולאחר שכל ה×Ēמונו×Ē ×’×•×‘×•. ההליך ×ĸשוי לקח×Ē ×›×ž×” דקו×Ē.", @@ -616,6 +678,7 @@ "clear": "נקה", "clear_all": "נקה הכל", "clear_all_recent_searches": "נקה א×Ē ×›×œ החיפושים האחרונים", + "clear_file_cache": "נקה קב×Ļי מטמון", "clear_message": "נקה הוד×ĸה", "clear_value": "נקה ×ĸרך", "client_cert_dialog_msg_confirm": "בסדר", @@ -686,11 +749,13 @@ "create_new_user": "×Ļור מ׊×Ēמ׊ חדש", "create_shared_album_page_share_add_assets": "×”×•×Ą×Ŗ ×Ēמונו×Ē", "create_shared_album_page_share_select_photos": "בחיר×Ē ×Ēמונו×Ē", + "create_shared_link": "×Ļור קישור משו×Ē×Ŗ", "create_tag": "×Ļור ×Ēג", "create_tag_description": "×Ļור ×Ēג חדש. ×ĸבור ×Ēגים מקוננים, נא להזין א×Ē ×”× ×Ēיב המלא של ה×Ēג כולל קווים נטויים.", "create_user": "×Ļור מ׊×Ēמ׊", "created": "נו×Ļר", "created_at": "נו×Ļר", + "creating_linked_albums": "יו×Ļר אלבומים מקושרים...", "crop": "ח×Ēוך", "curated_object_page_title": "דברים", "current_device": "מכשיר נוכחי", @@ -698,10 +763,11 @@ "current_server_address": "כ×Ēוב×Ē ×Š×¨×Ē × ×•×›×—×™×Ē", "custom_locale": "אזור שפה מו×Ēאם אישי×Ē", "custom_locale_description": "×ĸ×Ļב ×Ēאריכים ומספרים ×ĸל סמך השפה והאזור", + "custom_url": "קישור מו×Ēאם אישי×Ē", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", - "darkTheme": "החלפה למ×Ļב חושך", + "dark_theme": "הפ×ĸל/כבה מ×Ļב כהה", "date_after": "×Ēאריך אחרי", "date_and_time": "×Ēאריך וש×ĸה", "date_before": "×Ēאריך לפני", @@ -709,14 +775,17 @@ "date_of_birth_saved": "×Ēאריך לידה נ׊מר בה×Ļלחה", "date_range": "טווח ×Ēאריכים", "day": "יום", + "days": "ימים", "deduplicate_all": "ביטול כל הכפילויו×Ē", "deduplication_criteria_1": "גודל ×Ēמונה בב×Ēים", - "deduplication_criteria_2": "ספיר×Ē × ×Ēוני EXIF", + "deduplication_criteria_2": "כמו×Ē × ×Ēוני EXIF", "deduplication_info": "מיד×ĸ ×ĸל ביטול כפילויו×Ē", "deduplication_info_description": "כדי לבחור מרא׊ ×Ēמונו×Ē ×‘××•×¤×Ÿ אוטומטי ולהסיר כפילויו×Ē ×‘×›×ž×•×Ē ×’×“×•×œ×”, אנו מס×Ēכלים ×ĸל:", "default_locale": "׊פ×Ē ×‘×¨×™×¨×Ē ×ž×—×“×œ", "default_locale_description": "פורמט ×Ēאריכים ומספרים מבוסס ׊פ×Ē ×”×“×¤×“×¤×Ÿ שלך", "delete": "מחק", + "delete_action_confirmation_message": "האם א×Ēה בטוח שבר×Ļונך למחוק א×Ē ×”×Ēמונה הזא×Ē? פ×ĸולה זו ×Ē×ĸביר או×Ēו לאשפה של השר×Ē, ו×Ēשאל אם בר×Ļונך למחוק או×Ēו גם מהמכשיר המקומי", + "delete_action_prompt": "{count} נמחקו", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם א×Ēה בטוח שבר×Ļונך למחוק מפ×Ēח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו ל×Ļמי×Ēו×Ē ×ž×”×Š×¨×Ē ×•×ž×”×ž×›×Š×™×¨ שלך", @@ -730,9 +799,12 @@ "delete_key": "מחק מפ×Ēח", "delete_library": "מחק ספרייה", "delete_link": "מחק קישור", + "delete_local_action_prompt": "{count} נמחקו באופן מקומי", "delete_local_dialog_ok_backed_up_only": "מחק א×Ē ×ž×” שמגובה בלבד", "delete_local_dialog_ok_force": "מחק בכל זא×Ē", "delete_others": "מחק אחרים", + "delete_permanently": "מחק ל×Ļמי×Ēו×Ē", + "delete_permanently_action_prompt": "{count} נמחקו ל×Ļמי×Ēו×Ē", "delete_shared_link": "מחק קישור משו×Ē×Ŗ", "delete_shared_link_dialog_title": "מחק קישור משו×Ē×Ŗ", "delete_tag": "מחק ×Ēג", @@ -743,6 +815,7 @@ "description": "×Ēיאור", "description_input_hint_text": "×”×•×Ą×Ŗ ×Ēיאור...", "description_input_submit_error": "שגיאה ב×ĸדכון ×Ēיאור, בדוק א×Ē ×”×™×•×ž×Ÿ לפרטים נוספים", + "deselect_all": "בטל הכל", "details": "פרטים", "direction": "כיוון", "disabled": "מושב×Ē", @@ -760,6 +833,7 @@ "documentation": "×Ēי×ĸוד", "done": "סיום", "download": "הורדה", + "download_action_prompt": "מוריד {count} ×Ēמונו×Ē", "download_canceled": "הורדה בוטלה", "download_complete": "הורדה הושלמה", "download_enqueue": "הורדה נוספה ל×Ēור", @@ -785,9 +859,13 @@ "duration": "משך זמן", "edit": "×ĸרוך", "edit_album": "×ĸרוך אלבום", - "edit_avatar": "×ĸרוך ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", + "edit_avatar": "×ĸרוך י×Ļגן", + "edit_birthday": "×ĸריכ×Ē ×™×•× הולד×Ē", "edit_date": "×ĸרוך ×Ēאריך", "edit_date_and_time": "×ĸרוך ×Ēאריך וש×ĸה", + "edit_date_and_time_action_prompt": "{count} ×Ēאריך וש×ĸה × ×ĸרכו", + "edit_date_and_time_by_offset": "שינוי ×Ēאריך לפי קיזוז", + "edit_date_and_time_by_offset_interval": "טווח ×Ēאריכים חדש: {from} ×ĸד {to}", "edit_description": "×ĸרוך ×Ēיאור", "edit_description_prompt": "אנא בחר ×Ēיאור חדש:", "edit_exclusion_pattern": "×ĸרוך דפוס החרגה", @@ -797,6 +875,7 @@ "edit_key": "×ĸרוך מפ×Ēח", "edit_link": "×ĸרוך קישור", "edit_location": "×ĸרוך מיקום", + "edit_location_action_prompt": "{count} מיקומים × ×ĸרכו", "edit_location_dialog_title": "מיקום", "edit_name": "×ĸרוך ׊ם", "edit_people": "×ĸרוך אנשים", @@ -815,6 +894,7 @@ "empty_trash": "רוקן אשפה", "empty_trash_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לרוקן א×Ē ×”××Š×¤×”? זה יסיר ל×Ļמי×Ēו×Ē ××Ē ×›×œ ה×Ēמונו×Ē ×ž×”××Š×¤×” של השר×Ē.\nאין באפשרו×Ēך לבטל פ×ĸולה זו!", "enable": "אפ׊ר", + "enable_backup": "הפ×ĸל גיבוי", "enable_biometric_auth_description": "הזן א×Ē ×§×•×“ ה־PIN שלך כדי להפ×ĸיל אימו×Ē ×‘×™×•×ž×˜×¨×™", "enabled": "מופ×ĸל", "end_date": "×Ēאריך סיום", @@ -825,7 +905,9 @@ "error": "שגיאה", "error_change_sort_album": "שינוי סדר מיון אלבום נכשל", "error_delete_face": "שגיאה במחיק×Ē ×¤× ×™× מ×Ēמונה", + "error_getting_places": "שגיאה בקבל×Ē ×ž×§×•×ž×•×Ē", "error_loading_image": "שגיאה בט×ĸינ×Ē ×”×Ēמונה", + "error_loading_partners": "שגיאה בט×ĸינ×Ē ×Š×•×Ēפים: {error}", "error_saving_image": "שגיאה: {error}", "error_tag_face_bounding_box": "שגיאה ב×Ēיוג הפנים – לא ני×Ēן לקבל א×Ē ×§×•××•×¨×“×™× ×˜×•×Ē ×”×ž×Ą×’×¨×Ē", "error_title": "שגיאה - משהו הש×Ēבש", @@ -858,6 +940,7 @@ "failed_to_load_notifications": "איר×ĸה שגיאה ב×ĸ×Ē ×˜×ĸינ×Ē ×”×”×Ēראו×Ē", "failed_to_load_people": "נכשל באחזור אנשים", "failed_to_remove_product_key": "הסר×Ē ×ž×¤×Ēח מו×Ļר נכשלה", + "failed_to_reset_pin_code": "איפוס קוד PIN נכשל", "failed_to_stack_assets": "י×Ļיר×Ē ×ĸרימ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", "failed_to_unstack_assets": "ביטול ×ĸרימ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", "failed_to_update_notification_status": "שגיאה ב×ĸדכון הה×Ēראה", @@ -866,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {× ×Ēיב # נכשל} other {# × ×Ēיבים נכשלו}} אימו×Ē", "profile_picture_transparent_pixels": "×Ēמונו×Ē ×¤×¨×•×¤×™×œ אינן יכולו×Ē ×œ×›×œ×•×œ פיקסלים שקופים. נא להגדיל ו/או להזיז א×Ē ×”×Ēמונה.", "quota_higher_than_disk_size": "הגדר×Ē ×ž×›×Ą×” גבוהה יו×Ēר מגודל הדיסק", + "something_went_wrong": "משהו הש×Ēבש", "unable_to_add_album_users": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ מ׊×Ēמשים לאלבום", "unable_to_add_assets_to_shared_link": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēמונו×Ē ×œ×§×™×Š×•×¨ משו×Ē×Ŗ", "unable_to_add_comment": "לא ני×Ēן ×œ×”×•×Ą×™×Ŗ ×Ēגובה", @@ -951,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "×”×•×Ą×Ŗ ×Ēיאור...", + "exif_bottom_sheet_description_error": "שגיאה ב×ĸדכון ה×Ēיאור", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", "exif_bottom_sheet_people": "אנשים", "exif_bottom_sheet_person_add_person": "×”×•×Ą×Ŗ ׊ם", - "exif_bottom_sheet_person_age_months": "גיל {months} חודשים", - "exif_bottom_sheet_person_age_year_months": "גיל שנה ו-{months} חודשים", - "exif_bottom_sheet_person_age_years": "גיל {years}", "exit_slideshow": "×Ļא ממ×Ļג×Ē ×Š×§×•×¤×™×•×Ē", "expand_all": "הרחב הכל", "experimental_settings_new_asset_list_subtitle": "×ĸבודה ב×Ēהליך", @@ -971,6 +1053,8 @@ "explorer": "סייר", "export": "יי×Ļוא", "export_as_json": "יי×Ļוא כ-JSON", + "export_database": "יי×Ļא מסד × ×Ēונים", + "export_database_description": "יי×Ļא מסד × ×Ēונים SQL", "extension": "סיומ×Ē", "external": "חי×Ļוני", "external_libraries": "ספריו×Ē ×—×™×Ļוניו×Ē", @@ -982,11 +1066,13 @@ "failed_to_load_assets": "ט×ĸינ×Ē ×Ēמונו×Ē × ×›×Š×œ×”", "failed_to_load_folder": "ט×ĸינ×Ē ×Ēיקיה נכשלה", "favorite": "מו×ĸדת", + "favorite_action_prompt": "{count} נוספו למו×ĸדפים", "favorite_or_unfavorite_photo": "×”×•×Ą×Ŗ או הסר ×Ēמונה מהמו×ĸדפים", "favorites": "מו×ĸדפים", "favorites_page_no_favorites": "לא נמ×Ļאו ×Ēמונו×Ē ×ž×•×ĸדפים", "feature_photo_updated": "×Ēמונה מיי×Ļג×Ē ×ĸודכנה", "features": "×Ēכונו×Ē", + "features_in_development": "×Ēכונו×Ē ×‘×¤×™×Ēוח", "features_setting_description": "ניהול ×Ēכונו×Ē ×”×™×™×Š×•×", "file_name": "׊ם הקוב×Ĩ", "file_name_or_extension": "׊ם קוב×Ĩ או סיומ×Ē", @@ -996,21 +1082,26 @@ "filter_people": "סנן אנשים", "filter_places": "סינון מקומו×Ē", "find_them_fast": "מ×Ļא או×Ēם מהר לפי ׊ם ×ĸם חיפוש", + "first": "ראשון", "fix_incorrect_match": "×Ēקן ה×Ēאמה שגויה", "folder": "×Ēיקיה", "folder_not_found": "×Ēיקיה לא נמ×Ļאה", "folders": "×Ēיקיו×Ē", "folders_feature_description": "×ĸיון ב×Ē×Ļוג×Ē ×”×Ēיקייה ×ĸבור ה×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שבמ×ĸרכ×Ē ×”×§×‘×Ļים", + "forgot_pin_code_question": "שחכ×Ē ××Ē ×”-PIN שלך?", "forward": "קדימה", "gcast_enabled": "Google Cast", "gcast_enabled_description": "×Ēכונה זא×Ē ×˜×•×ĸ× ×Ē ×ž×Š××‘×™× חי×Ļוניים מגוגל בכדי לפ×ĸול.", "general": "כללי", + "geolocation_instruction_location": "לח×Ĩ ×ĸל פריט ×ĸם קואורדינטו×Ē GPS כדי להש×Ēמ׊ במיקומו, או בחר מיקום ישירו×Ē ×ž×”×ž×¤×”", "get_help": "קבל ×ĸזרה", "get_wifiname_error": "לא היה ני×Ēן לקבל א×Ē ×Š× האינטרנט האלחוטי שלך. יש לודא שה×ĸ× ×§×Ē ××Ē ×”×”×¨×Š××•×Ē ×”×“×¨×•×Š×•×Ē ×•×Š××Ē/ה מחובר/×Ē ×œ×¨×Š×Ē ××™× ×˜×¨× ×˜ אלחוטי", "getting_started": "×Ēחיל×Ē ×”×ĸבודה", "go_back": "חזור", "go_to_folder": "×ĸבור ל×Ēיקיה", "go_to_search": "×ĸבור לחיפוש", + "gps": "GPS", + "gps_missing": "אין GPS", "grant_permission": "לה×ĸניק הרשאה", "group_albums_by": "קב×Ĩ אלבומים לפי..", "group_country": "קב×Ĩ לפי מדינה", @@ -1021,6 +1112,9 @@ "haptic_feedback_switch": "אפ׊ר משוב ברטט", "haptic_feedback_title": "משוב ברטט", "has_quota": "יש מכסה", + "hash_asset": "גבב פריט", + "hashed_assets": "×Ēמונו×Ē ×ž×’×•×‘×‘×•×Ē", + "hashing": "מגבב", "header_settings_add_header_tip": "×”×•×Ą×Ŗ כו×Ēר×Ē", "header_settings_field_validator_msg": "×ĸרך אינו יכול להיו×Ē ×¨×™×§", "header_settings_header_name_input": "׊ם כו×Ēר×Ē", @@ -1052,7 +1146,9 @@ "home_page_upload_err_limit": "ני×Ēן לה×ĸלו×Ē ×¨×§ מקסימום של 30 ×Ēמונו×Ē ×‘×›×œ פ×ĸם, מדלג", "host": "מארח", "hour": "׊×ĸה", + "hours": "׊×ĸו×Ē", "id": "מזהה", + "idle": "במ×Ļב ץרק", "ignore_icloud_photos": "ה×Ē×ĸלם מ×Ēמונו×Ē iCloud", "ignore_icloud_photos_description": "×Ēמונו×Ē ×Š×ž××•×—×Ą× ×•×Ē ×‘-iCloud לא יו×ĸלו לשר×Ē", "image": "×Ēמונה", @@ -1110,10 +1206,13 @@ "language_no_results_title": "לא נמ×Ļאה שפה", "language_search_hint": "חפש שפו×Ē...", "language_setting_description": "בחר א×Ē ×”×Š×¤×” המו×ĸדפ×Ē ×ĸליך", + "large_files": "קב×Ļים גדולים", + "last": "אחרון", "last_seen": "נראה לאחרונה", "latest_version": "גרסה ×ĸדכני×Ē ×‘×™×•×Ēר", "latitude": "קו רוחב", "leave": "ל×ĸזוב", + "leave_album": "×ĸזיב×Ē ××œ×‘×•×", "lens_model": "דגם ×ĸדשה", "let_others_respond": "אפ׊ר לאחרים להגיב", "level": "רמה", @@ -1125,16 +1224,20 @@ "library_page_sort_created": "×Ēאריך י×Ļירה", "library_page_sort_last_modified": "שונה לאחרונה", "library_page_sort_title": "כו×Ēר×Ē ××œ×‘×•×", + "licenses": "רישיונו×Ē", "light": "בהיר", + "like": "אהב×Ēי", "like_deleted": "לייק נמחק", "link_motion_video": "ק׊ר סרטון ×Ēנו×ĸה", - "link_options": "אפשרויו×Ē ×§×™×Š×•×¨", "link_to_oauth": "קישור ל-OAuth", "linked_oauth_account": "חשבון OAuth מקושר", "list": "רשימה", "loading": "טו×ĸן", "loading_search_results_failed": "ט×ĸינ×Ē ×Ēו×Ļאו×Ē ×”×—×™×¤×•×Š נכשלה", + "local": "מקומי", "local_asset_cast_failed": "לא ני×Ēן לשדר ×Ēמונה שלא הו×ĸל×Ēה לשר×Ē", + "local_assets": "×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē", + "local_media_summary": "סיכום של מדיה מקומי×Ē", "local_network": "ר׊×Ē ×ž×§×•×ž×™×Ē", "local_network_sheet_info": "היישום י×Ēחבר לשר×Ē ×“×¨×š הכ×Ēוב×Ē ×”×–××Ē ×›××Š×¨ מ׊×Ēמשים ברש×Ē ×”××™× ×˜×¨× ×˜ האלחוטי ׊מ×Ļוינ×Ē", "location_permission": "הרשא×Ē ×ž×™×§×•×", @@ -1146,8 +1249,10 @@ "location_picker_longitude_hint": "הזן א×Ē ×§×• האורך שלך כאן", "lock": "× ×ĸל", "locked_folder": "×Ēיקיה × ×ĸולה", + "log_detail_title": "פרטי יומן", "log_out": "ה×Ē× ×Ē×§", "log_out_all_devices": "ה×Ē× ×Ē×§ מכל המכשירים", + "logged_in_as": "מחובר כ {user}", "logged_out_all_devices": "מנו×Ē×§ מכל המכשירים", "logged_out_device": "מכשיר מנו×Ē×§", "login": "כניסה", @@ -1175,6 +1280,7 @@ "login_password_changed_success": "סיסמה ×ĸודכנה בה×Ļלחה", "logout_all_device_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לה×Ē× ×Ē×§ מכל המכשירים?", "logout_this_device_confirmation": "האם באמ×Ē ×‘×¨×Ļונך לה×Ē× ×Ē×§ מהמכשיר הזה?", + "logs": "יומנים", "longitude": "קו אורך", "look": "מראה", "loop_videos": "הפ×ĸלה חוזר×Ē ×Š×œ סרטונים", @@ -1182,6 +1288,7 @@ "main_branch_warning": "הגרסה המו×Ē×§× ×Ē ×”×™× גרס×Ē ×¤×™×Ēוח; אנחנו ממלי×Ļים בחום להש×Ēמ׊ בגרסה י×Ļיבה!", "main_menu": "×Ēפריט ראשי", "make": "×Ēו×Ļר×Ē", + "manage_geolocation": "נהל מיקום", "manage_shared_links": "ניהול קישורים משו×Ēפים", "manage_sharing_with_partners": "ניהול שי×Ēות ×ĸם שו×Ēפים", "manage_the_app_settings": "ניהול הגדרו×Ē ×”××¤×œ×™×§×Ļיה", @@ -1190,8 +1297,7 @@ "manage_your_devices": "ניהול המכשירים המחוברים שלך", "manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך", "map": "מפה", - "map_assets_in_bound": "×Ēמונה {count}", - "map_assets_in_bounds": "{count} ×Ēמונו×Ē", + "map_assets_in_bounds": "{count, plural, =0 {אין ×Ēמונו×Ē ×‘××–×•×¨ זה} one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "map_cannot_get_user_location": "לא ני×Ēן לקבו×ĸ א×Ē ×ž×™×§×•× המש×Ēמ׊", "map_location_dialog_yes": "כן", "map_location_picker_page_use_location": "הש×Ēמ׊ במיקום הזה", @@ -1199,7 +1305,6 @@ "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_title": "הרשאה למיקום נדח×Ēה", "map_settings": "הגדרו×Ē ×ž×¤×”", @@ -1218,6 +1323,7 @@ "mark_as_read": "סמן כנקרא", "marked_all_as_read": "כל הה×Ēראו×Ē ×Ą×•×ž× ×• כנקראו", "matches": "ה×Ēאמו×Ē", + "matching_assets": "×Ēמונו×Ē ×Ēואמו×Ē", "media_type": "סוג מדיה", "memories": "זכרונו×Ē", "memories_all_caught_up": "ראי×Ē ×”×›×œ", @@ -1225,7 +1331,7 @@ "memories_setting_description": "נהל א×Ē ×ž×” שרואים בזכרונו×Ē ×Š×œ×š", "memories_start_over": "ה×Ēחל מחדש", "memories_swipe_to_close": "החלק למ×ĸלה כדי לסגור", - "memory": "זיכרון", + "memory": "זיכרון (היום לפני..)", "memory_lane_title": "מ׊×ĸול הזיכרונו×Ē {title}", "menu": "×Ēפריט", "merge": "מזג", @@ -1236,6 +1342,7 @@ "merged_people_count": "{count, plural, one {אדם # מוזג} other {# אנשים מוזגו}}", "minimize": "מז×ĸר", "minute": "דקה", + "minutes": "דקו×Ē", "missing": "חסרים", "model": "דגם", "month": "חודש", @@ -1243,6 +1350,7 @@ "more": "×ĸוד", "move": "ה×ĸבר", "move_off_locked_folder": "הו×Ļאה מה×Ēיקייה הנ×ĸולה", + "move_to_lock_folder_action_prompt": "{count} נוספו ל×Ēיקייה הנ×ĸולה", "move_to_locked_folder": "ה×ĸבר ל×Ēיקיה הנ×ĸולה", "move_to_locked_folder_confirmation": "ה×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× האלו יוסרו מכל האלבומים, ויהיו מו×Ļגים רק ב×Ēיקיה הנ×ĸולה", "moved_to_archive": "{count, plural, one {הו×ĸברה ×Ēמונה # } other {# ×Ēמונו×Ē ×”×•×ĸברו}} לארכיון", @@ -1254,6 +1362,10 @@ "my_albums": "האלבומים שלי", "name": "׊ם", "name_or_nickname": "׊ם או כינוי", + "network_requirement_photos_upload": "הש×Ēמ׊ בנ×Ēונים ניידים לגיבוי ×Ēמונו×Ē", + "network_requirement_videos_upload": "הש×Ēמ׊ בנ×Ēונים ניידים לגיבוי סרטונים", + "network_requirements": "דרישו×Ē ×¨×Š×Ē", + "network_requirements_updated": "דרישו×Ē ×”×¨×Š×Ē ×”×Š×Ēנו, ×Ēור הגיבוי אופס", "networking_settings": "ר׊×Ē", "networking_subtitle": "ניהול הגדרו×Ē × ×§×•×“×Ē ×§×Ļה ׊ר×Ē", "never": "את פ×ĸם", @@ -1263,6 +1375,7 @@ "new_person": "אדם חדש", "new_pin_code": "קוד PIN חדש", "new_pin_code_subtitle": "זא×Ē ×”×¤×ĸם הראשונה שנכנס×Ē ×œ×Ēיקיה הנ×ĸולה. ×Ļור קוד PIN כדי לאבטח א×Ē ×”×’×™×Š×” ×œ×“×Ŗ זה", + "new_timeline": "×Ļיר הזמן החדש", "new_user_created": "מ׊×Ēמ׊ חדש נו×Ļר", "new_version_available": "גרסה חדשה זמינה", "newest_first": "החדש ביו×Ēר ראשון", @@ -1276,19 +1389,25 @@ "no_assets_message": "לח×Ĩ כדי לה×ĸלו×Ē ××Ē ×”×Ēמונה הראשונה שלך", "no_assets_to_show": "אין ×Ēמונו×Ē ×œ×”×Ļגה", "no_cast_devices_found": "לא נמ×Ļאו מכשירי שידור", + "no_checksum_local": "אין Checksum זמין - לא ני×Ēן לאחזר ×Ēמונו×Ē ×ž×§×•×ž×™×•×Ē", + "no_checksum_remote": "אין Checksum זמין - לא ני×Ēן לאחזר ×Ēמונו×Ē ×ž×”×Š×¨×Ē", "no_duplicates_found": "לא נמ×Ļאו כפילויו×Ē.", "no_exif_info_available": "אין מיד×ĸ זמין ×ĸל מטא-× ×Ēונים (exif)", "no_explore_results_message": "ה×ĸלה ×Ēמונו×Ē × ×•×Ą×¤×•×Ē ×›×“×™ לחקור א×Ē ×”××•×Ą×Ŗ שלך.", "no_favorites_message": "×”×•×Ą×Ŗ מו×ĸדפים כדי למ×Ļוא במהירו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× הכי טובים שלך", "no_libraries_message": "×Ļור ספרייה חי×Ļוני×Ē ×›×“×™ לראו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שלך", + "no_local_assets_found": "לא נמ×Ļאו ×Ēמונו×Ē ×ĸם Checksum זהה", "no_locked_photos_message": "×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ב×Ēיקייה הנ×ĸולה מוס×Ēרים ולא יופי×ĸו בזמן הגלישה או החיפוש בספרייה שלך.", "no_name": "אין ׊ם", "no_notifications": "אין ה×Ēראו×Ē", "no_people_found": "לא נמ×Ļאו אנשים ×Ēואמים", "no_places": "אין מקומו×Ē", + "no_remote_assets_found": "לא נמ×Ļאו ×Ēמונו×Ē ×‘×Š×¨×Ē ×ĸם Checksum זהה", "no_results": "אין ×Ēו×Ļאו×Ē", "no_results_description": "נסה להש×Ēמ׊ במילה נרדפ×Ē ××• במיל×Ē ×ž×¤×Ēח יו×Ēר כללי×Ē", "no_shared_albums_message": "×Ļור אלבום כדי לש×Ē×Ŗ ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ×ĸם אנשים ברש×Ē ×Š×œ×š", + "no_uploads_in_progress": "אין ה×ĸלאו×Ē ×‘×Ēהליך", + "not_available": "לא רלוונטי", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", "note_apply_storage_label_to_previously_uploaded assets": "ה×ĸרה: כדי להחיל א×Ē ×Ēווי×Ē ×”××—×Ą×•×Ÿ ×ĸל ×Ēמונו×Ē ×Š×”×•×ĸלו ב×ĸבר, הפ×ĸל א×Ē", @@ -1304,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "מקורו×Ē ×¨×Š×ž×™×™× של Immich", "offline": "לא מקוון", + "offset": "קיזוז", "ok": "בסדר", "oldest_first": "הישן ביו×Ēר ראשון", "on_this_device": "במכשיר הזה", @@ -1322,10 +1442,13 @@ "open_the_search_filters": "פ×Ēח א×Ē ×ž×Ą× × ×™ החיפוש", "options": "אפשרויו×Ē", "or": "או", + "organize_into_albums": "ארגן ב×Ēוך אלבומים", + "organize_into_albums_description": "שים ×Ēמונו×Ē ×§×™×™×ž×•×Ē ×‘×Ēוך אלבומים באמ×Ļ×ĸו×Ē ×”×’×“×¨×•×Ē ×”×Ą× ×›×¨×•×Ÿ הנוכחיו×Ē", "organize_your_library": "ארגן א×Ē ×”×Ą×¤×¨×™×™×” שלך", "original": "מקורי", "other": "אחר", "other_devices": "מכשירים אחרים", + "other_entities": "ישויו×Ē ××—×¨×•×Ē", "other_variables": "מ׊×Ēנים אחרים", "owned": "בב×ĸלו×Ē", "owner": "ב×ĸלים", @@ -1380,6 +1503,9 @@ "permission_onboarding_permission_limited": "הרשאה מוגבל×Ē. כדי ל×Ē×Ē ×œ×™×™×Š×•× לגבו×Ē ×•×œ× ×”×œ א×Ē ×›×œ ××•×Ą×Ŗ הגלריה שלך, ה×ĸ× ×§ הרשאה ל×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× בהגדרו×Ē.", "permission_onboarding_request": "היישום דורש הרשאה כדי לראו×Ē ××Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× שלך.", "person": "אדם", + "person_age_months": "{months, plural, one {חודש #} other {# חודשים}}", + "person_age_year_months": "בגיל שנה ו{months, plural, one {חודש #} other {# חודשים}}", + "person_age_years": "בגיל {years, plural, other {# שנים}}", "person_birthdate": "נולד ב×Ēאריך {date}", "person_hidden": "{name}{hidden, select, true { (מוס×Ēר)} other {}}", "photo_shared_all_users": "נראה ששי×Ēפ×Ē ××Ē ×”×Ēמונו×Ē ×Š×œ×š ×ĸם כל המש×Ēמשים או שאין לך את מ׊×Ēמ׊ לש×Ē×Ŗ אי×Ēו.", @@ -1403,6 +1529,7 @@ "port": "י×Ļיאה", "preferences_settings_subtitle": "ניהול ה×ĸדפו×Ē ×™×™×Š×•×", "preferences_settings_title": "ה×ĸדפו×Ē", + "preparing": "בהכנה", "preset": "הגדרו×Ē ×§×‘×•×ĸו×Ē ×ž×¨××Š", "preview": "×Ē×Ļוגה מקדימה", "previous": "הקודם", @@ -1419,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "גרס×Ē ×”×™×™×Š×•× לנייד מיושנ×Ē. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”.", "profile_drawer_client_server_up_to_date": "היישום והשר×Ē ×ž×ĸודכנים", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "מ×Ļב לקריאה בלבד מופ×ĸל. לח×Ĩ לחי×Ļה ארוכה ×ĸל סמל הי×Ļגן של המש×Ēמ׊ כדי ל×Ļא×Ē.", "profile_drawer_server_out_of_date_major": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה הראשי×Ē ×”××—×¨×•× ×”.", "profile_drawer_server_out_of_date_minor": "השר×Ē ××™× ×• מ×ĸודכן. נא ל×ĸדכן לגרסה המשני×Ē ×”××—×¨×•× ×”.", "profile_image_of_user": "×Ēמונ×Ē ×¤×¨×•×¤×™×œ של {user}", @@ -1457,16 +1585,21 @@ "purchase_server_description_2": "מ×ĸמד ×Ēומך", "purchase_server_title": "׊ר×Ē", "purchase_settings_server_activated": "מפ×Ēח המו×Ļר של השר×Ē ×ž× ×•×”×œ ×ĸל ידי מנהל המ×ĸרכ×Ē", + "query_asset_id": "שאיל×Ēה ×ĸל מזהה הפריט", + "queue_status": "{count} מ×Ēוך {total} ×ĸומדים ב×Ēור", "rating": "דירוג כוכב", "rating_clear": "נקה דירוג", "rating_count": "{count, plural, one {כוכב #} other {# כוכבים}}", "rating_description": "ה×Ļג א×Ē ×“×™×¨×•×’ ה-EXIF בלוח המיד×ĸ", "reaction_options": "אפשרויו×Ē ×”×’×‘×”", "read_changelog": "קרא א×Ē ×™×•×ž×Ÿ השינויים", - "reassign": "הק×Ļה מחדש", + "readonly_mode_disabled": "מ×Ļב לקריאה בלבד מושב×Ē", + "readonly_mode_enabled": "מ×Ļב לקריאה בלבד מופ×ĸל", + "ready_for_upload": "מוכן לה×ĸלאה", + "reassign": "הק×Ļאה מחדש", "reassigned_assets_to_existing_person": "{count, plural, one {×Ēמונה # הוק×Ļ×Ēה} other {# ×Ēמונו×Ē ×”×•×§×Ļו}} מחדש אל {name, select, null {אדם קיים} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {×Ēמונה # הוק×Ļ×Ēה} other {# ×Ēמונו×Ē ×”×•×§×Ļו}} מחדש לאדם חדש", - "reassing_hint": "הק×Ļה ×Ēמונו×Ē ×Š× ×‘×—×¨×• לאדם קיים", + "reassing_hint": "הק×Ļא×Ē ×Ēמונו×Ē ×Š× ×‘×—×¨×• לאדם קיים", "recent": "חדש", "recent-albums": "אלבומים אחרונים", "recent_searches": "חיפושים אחרונים", @@ -1474,25 +1607,30 @@ "recently_added_page_title": "× ×•×Ą×Ŗ לאחרונה", "recently_taken": "×Ļולם לאחרונה", "recently_taken_page_title": "×Ļולם לאחרונה", - "refresh": "ר×ĸנן", - "refresh_encoded_videos": "ר×ĸנן סרטונים מקודדים", - "refresh_faces": "ר×ĸנן פנים", - "refresh_metadata": "ר×ĸנן מטא-× ×Ēונים", - "refresh_thumbnails": "ר×ĸנן ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", + "refresh": "ר×ĸנון", + "refresh_encoded_videos": "ר×ĸנון סרטונים מקודדים", + "refresh_faces": "ר×ĸנון פנים", + "refresh_metadata": "ר×ĸנון מטא-× ×Ēונים", + "refresh_thumbnails": "ר×ĸנון ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", "refreshed": "רו×ĸנן", "refreshes_every_file": "קורא מחדש א×Ē ×›×œ הקב×Ļים הקיימים והחדשים", "refreshing_encoded_video": "מר×ĸנן סרטון מקודד", "refreshing_faces": "מר×ĸנן פר×Ļופים", "refreshing_metadata": "מר×ĸנן מטא-× ×Ēונים", "regenerating_thumbnails": "מחדש ×Ēמונו×Ē ×ž×ž×•×–×ĸרו×Ē", - "remove": "הסר", + "remote": "מרוחק", + "remote_assets": "×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē", + "remote_media_summary": "×Ē×§×Ļיר ×Ēמונו×Ē ×ž×¨×•×—×§×•×Ē", + "remove": "הסרה", "remove_assets_album_confirmation": "האם באמ×Ē ×‘×¨×Ļונך להסיר {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}} מהאלבום?", - "remove_assets_shared_link_confirmation": "האם א×Ēה בטוח שבר×Ļונך להסיר {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}} מהקישור המשו×Ē×Ŗ הזה?", + "remove_assets_shared_link_confirmation": "האם בר×Ļונך להסיר {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}} מהקישור המשו×Ē×Ŗ הזה?", "remove_assets_title": "להסיר ×Ēמונו×Ē?", - "remove_custom_date_range": "הסר טווח ×Ēאריכים מו×Ēאם", - "remove_deleted_assets": "הסר קב×Ļים לא מקוונים", - "remove_from_album": "הסר מאלבום", - "remove_from_favorites": "הסר מהמו×ĸדפים", + "remove_custom_date_range": "הסר×Ē ×˜×•×•×— ×Ēאריכים מו×Ēאם", + "remove_deleted_assets": "הסר×Ē ×§×‘×Ļים לא מקוונים", + "remove_from_album": "הסרה מאלבום", + "remove_from_album_action_prompt": "{count} הוסרו מהאלבום", + "remove_from_favorites": "הסרה מהמו×ĸדפים", + "remove_from_lock_folder_action_prompt": "{count} הוסרו מה×Ēיקייה הנ×ĸולה", "remove_from_locked_folder": "הסר מה×Ēיקייה הנ×ĸולה", "remove_from_locked_folder_confirmation": "האם א×Ēה בטוח שבר×Ļונך לה×ĸביר א×Ē ×”×Ēמונו×Ē ×•×”×Ą×¨×˜×•× ×™× האלה מחו×Ĩ ל×Ēיקייה הנ×ĸולה? הם יהיו מו×Ļגים בספרייה שלך.", "remove_from_shared_link": "הסר מקישור משו×Ē×Ŗ", @@ -1520,19 +1658,29 @@ "reset_password": "איפוס סיסמה", "reset_people_visibility": "אפץ א×Ē × ×¨××•×Ē ×”×× ×Š×™×", "reset_pin_code": "אפץ קוד PIN", + "reset_pin_code_description": "אם שחכ×Ē ××Ē ×§×•×“ ה-PIN שלך, באפשרו×Ēך לי×Ļור ק׊ר ×ĸם מנהל השר×Ē ×›×“×™ לאפס או×Ēו", + "reset_pin_code_success": "קוד ה-PIN אופס בה×Ļלחה", + "reset_pin_code_with_password": "באפשרו×Ēך ×Ēמיד לאפס א×Ē ×§×•×“ ה-PIN שלך ×ĸם הסיסמה שלך", + "reset_sqlite": "אפץ א×Ē ×ž×Ą×“ הנ×Ēונים SQLite", + "reset_sqlite_confirmation": "האם א×Ēה בטוח שבר×Ļונך לאפס א×Ē ×ž×Ą×“ הנ×Ēונים SQLite? יהיה ×ĸליך לה×Ē× ×Ē×§ ולה×Ēחבר מחדש כדי לסנכרן א×Ē ×”× ×Ēונים מחדש", + "reset_sqlite_success": "איפוס מסד הנ×Ēונים SQLite בו×Ļ×ĸ בה×Ļלחה", "reset_to_default": "אפץ לבריר×Ē ×ž×—×“×œ", "resolve_duplicates": "פ×Ēור כפילויו×Ē", "resolved_all_duplicates": "כל הכפילויו×Ē × ×¤×Ēרו", "restore": "שחזר", "restore_all": "שחזר הכל", + "restore_trash_action_prompt": "{count} שוחזרו מהשאפה", "restore_user": "שחזר מ׊×Ēמ׊", "restored_asset": "ה×Ēמונה שוחזרה", "resume": "המשך", + "resume_paused_jobs": "המשך {count, plural, one {×ĸבודה # שהופסקה} other {# ×ĸבודו×Ē ×Š×”×•×¤×Ą×§×•}}", "retry_upload": "נסה שוב לה×ĸלו×Ē", "review_duplicates": "בדוק כפילויו×Ē", + "review_large_files": "×Ļפייה בקב×Ļים גדולים", "role": "×Ēפקיד", "role_editor": "×ĸורך", "role_viewer": "×Ļופה", + "running": "פו×ĸל", "save": "שמור", "save_to_gallery": "שמור לגלריה", "saved_api_key": "מפ×Ēח API שמור", @@ -1605,7 +1753,8 @@ "select_album_cover": "בחר ×ĸטיפ×Ē ××œ×‘×•×", "select_all": "בחר הכל", "select_all_duplicates": "בחר א×Ē ×›×œ הכפילויו×Ē", - "select_avatar_color": "בחר ×Ļב×ĸ ×Ēמונ×Ē ×¤×¨×•×¤×™×œ", + "select_all_in": "בחר הכול ב×Ēוך {group}", + "select_avatar_color": "בחר ×Ļב×ĸ י×Ļגן", "select_face": "בחר פנים", "select_featured_photo": "בחר ×Ēמונה מיי×Ļג×Ē", "select_from_computer": "בחר מהמחשב", @@ -1618,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "י×Ļיר×Ē ××œ×‘×•× נכשלה", "selected": "נבחרו", "selected_count": "{count, plural, other {# נבחרו}}", + "selected_gps_coordinates": "קואורדינטו×Ē GPS שנבחרו", "send_message": "שלח הוד×ĸה", "send_welcome_email": "שלח דוא\"ל קבל×Ē ×¤× ×™×", "server_endpoint": "נקוד×Ē ×§×Ļה ׊ר×Ē", @@ -1663,6 +1813,7 @@ "settings_saved": "ההגדרו×Ē × ×Š×ž×¨×•", "setup_pin_code": "הגדר קוד PIN", "share": "׊×Ē×Ŗ", + "share_action_prompt": "שו×Ēפו {count} ×Ēמונו×Ē", "share_add_photos": "×”×•×Ą×Ŗ ×Ēמונו×Ē", "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", @@ -1684,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "הו×ĸ×Ē×§ ללוח", "shared_link_clipboard_text": "קישור: {password}\nסיסמה: {link}", "shared_link_create_error": "שגיאה בי×Ļיר×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ", + "shared_link_custom_url_description": "גש לקישור ששו×Ē×Ŗ באמ×Ļ×ĸו×Ē ×›×Ēוב×Ē URL מו×Ēאמ×Ē ××™×Š×™×Ē", "shared_link_edit_description_hint": "הכנס א×Ē ×Ēיאור השי×Ēות", "shared_link_edit_expire_after_option_day": "1 יום", "shared_link_edit_expire_after_option_days": "{count} ימים", @@ -1709,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ניהול קישורים משו×Ēפים", "shared_link_options": "אפשרויו×Ē ×§×™×Š×•×¨ משו×Ē×Ŗ", + "shared_link_password_description": "דרוש סיסמה כדי לגש×Ē ×œ×§×™×Š×•×¨ המשו×Ē×Ŗ הזה", "shared_links": "קישורים משו×Ēפים", "shared_links_description": "׊×Ē×Ŗ ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× ×ĸם קישור", "shared_photos_and_videos_count": "{assetCount, plural, other {# ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× משו×Ēפים.}}", @@ -1743,6 +1896,7 @@ "show_slideshow_transition": "ה×Ļג מ×ĸבר מ×Ļג×Ē", "show_supporter_badge": "×Ēג ×Ēומך", "show_supporter_badge_description": "ה×Ļג ×Ēג ×Ēומך", + "show_text_search_menu": "ה×Ļג ×Ēפריט חיפוש טקץט", "shuffle": "×ĸרבוב", "sidebar": "סרגל ×Ļד", "sidebar_display_description": "ה×Ļג קישור ל×Ē×Ļוגה בסרגל ה×Ļד", @@ -1758,12 +1912,14 @@ "sort_created": "×Ēאריך י×Ļירה", "sort_items": "מספר פריטים", "sort_modified": "×Ēאריך שינוי", + "sort_newest": "×Ēמונה הכי חדשה", "sort_oldest": "×Ēמונה הכי ישנה", "sort_people_by_similarity": "מיין אנשים לפי דמיון", "sort_recent": "×Ēמונה אחרונה ביו×Ēר", "sort_title": "כו×Ēר×Ē", "source": "קוד מקור", "stack": "×ĸרימה", + "stack_action_prompt": "{count} קוב×Ļו", "stack_duplicates": "×Ļור ×ĸרימ×Ē ×›×¤×™×œ×•×™×•×Ē", "stack_select_one_photo": "בחר ×Ēמונה ראשי×Ē ××—×Ē ×ĸבור ה×ĸרימה", "stack_selected_photos": "×Ļור ×ĸרימ×Ē ×Ēמונו×Ē × ×‘×—×¨×•×Ē", @@ -1771,6 +1927,7 @@ "stacktrace": "Stack trace", "start": "ה×Ēחל", "start_date": "×Ēאריך ה×Ēחלה", + "start_date_before_end_date": "×Ēאריך הה×Ēחלה חייב להיו×Ē ×œ×¤× ×™ ×Ēאריך הסיום", "state": "מדינה", "status": "מ×Ļב", "stop_casting": "הפסק×Ē ×Š×™×“×•×¨", @@ -1783,8 +1940,9 @@ "storage_quota": "מכס×Ē ×”××—×Ą×•×Ÿ", "storage_usage": "{used} בשימוש מ×Ēוך {available}", "submit": "שלח", + "success": "בו×Ļ×ĸ בה×Ļלחה", "suggestions": "ה×Ļ×ĸו×Ē", - "sunrise_on_the_beach": "Sunrise on the beach (מומל×Ĩ לחפש באנגלי×Ē ×œ×Ēו×Ļאו×Ē ×˜×•×‘×•×Ē ×™×•×Ēר)", + "sunrise_on_the_beach": "שקי×ĸה בחות", "support": "×Ēמיכה", "support_and_feedback": "×Ēמיכה & משוב", "support_third_party_description": "ה×Ē×§× ×Ē ×”-Immich שלך נארזה ×ĸל ידי ×Ļד שלישי. ב×ĸיו×Ē ×Š××Ēה חווה ×ĸשויו×Ē ×œ×”×™×’×¨× ×ĸל ידי חבילה זו, אז בבקשה ×Ē×ĸלה ב×ĸיו×Ē ××™×Ēם ראשי×Ē ×›×œ באמ×Ļ×ĸו×Ē ×”×§×™×Š×•×¨×™× למטה.", @@ -1792,6 +1950,10 @@ "sync": "סנכרן", "sync_albums": "סנכרן אלבומים", "sync_albums_manual_subtitle": "סנכרן א×Ē ×›×œ הסרטונים וה×Ēמונו×Ē ×Š×”×•×ĸלו לאלבומי הגיבוי שנבחרו", + "sync_local": "סנכרן מקומי", + "sync_remote": "סנכרן נקוד×Ē ×§×Ļה מרוחק×Ē", + "sync_status": "סנכרן מ×Ļב", + "sync_status_subtitle": "ה×Ļג ונהל א×Ē ×ž×ĸרכ×Ē ×”×Ą× ×›×¨×•×Ÿ", "sync_upload_album_setting_subtitle": "×Ļור וה×ĸלה ×Ēמונו×Ē ×•×Ą×¨×˜×•× ×™× שלך לאלבומים שנבחרו ביישום", "tag": "×Ēג", "tag_assets": "×Ēיוג ×Ēמונו×Ē", @@ -1802,6 +1964,7 @@ "tag_updated": "×Ēג מ×ĸודכן: {tag}", "tagged_assets": "×Ēויגו {count, plural, one {×Ēמונה #} other {# ×Ēמונו×Ē}}", "tags": "×Ēגים", + "tap_to_run_job": "לח×Ĩ ×ĸל מנ×Ē ×œ×”×¤×ĸיל משימה", "template": "×Ēבני×Ē", "theme": "×ĸרכ×Ē × ×•×Š×", "theme_selection": "בחיר×Ē ×ĸרכ×Ē × ×•×Š×", @@ -1828,12 +1991,15 @@ "to_change_password": "שנה סיסמה", "to_favorite": "מו×ĸדת", "to_login": "כניסה", + "to_multi_select": "לבחור מרובו×Ē", "to_parent": "לך להורה", + "to_select": "לבחור", "to_trash": "אשפה", "toggle_settings": "×”×—×œ×Ŗ מ×Ļב הגדרו×Ē", "total": "סה\"כ", "total_usage": "שימוש כולל", "trash": "אשפה", + "trash_action_prompt": "{count} הו×ĸברו לאשפה", "trash_all": "ה×ĸבר הכל לאשפה", "trash_count": "ה×ĸבר לאשפה {count, number}", "trash_delete_asset": "ה×ĸבר לאשפה/מחק ×Ēמונה", @@ -1847,13 +2013,16 @@ "trash_page_select_assets_btn": "בחר ×Ēמונו×Ē", "trash_page_title": "אשפה ({count})", "trashed_items_will_be_permanently_deleted_after": "פריטים באשפה ימחקו ל×Ļמי×Ēו×Ē ×œ××—×¨ {days, plural, one {יום #} other {# ימים}}.", + "troubleshoot": "פ×Ēור ב×ĸיו×Ē", "type": "סוג", "unable_to_change_pin_code": "לא ני×Ēן לשנו×Ē ××Ē ×§×•×“ ה PIN", "unable_to_setup_pin_code": "לא ני×Ēן להגדיר קוד PIN", "unarchive": "הו×Ļא מארכיון", + "unarchive_action_prompt": "{count} הוסרו מהארכיון", "unarchived_count": "{count, plural, other {# הו×Ļאו מהארכיון}}", "undo": "לבטל", "unfavorite": "לא מו×ĸדת", + "unfavorite_action_prompt": "{count} הוסרו מהמו×ĸדפים", "unhide_person": "בטל הס×Ēר×Ē ××“×", "unknown": "לא ידו×ĸ", "unknown_country": "מדינה לא ידו×ĸה", @@ -1869,16 +2038,23 @@ "unsaved_change": "שינוי לא נ׊מר", "unselect_all": "בטל בחירה בהכל", "unselect_all_duplicates": "בטל בחיר×Ē ×›×œ הכפילויו×Ē", + "unselect_all_in": "בטל א×Ē ×”×‘×—×™×¨×” של הכל ב {group}", "unstack": "בטל ×ĸרימה", + "unstack_action_prompt": "{count} הופרדו", "unstacked_assets_count": "{count, plural, one {×Ēמונה # הוסרה} other {# ×Ēמונו×Ē ×”×•×Ą×¨×•}} מה×ĸרימה", + "untagged": "לא מ×Ēיוגים", "up_next": "הבא ב×Ēור", + "update_location_action_prompt": "×ĸדכן א×Ē ×”×ž×™×§×•× של {count} פריטים שנבחרו ×ĸם:", "updated_at": "×ĸודכן", "updated_password": "סיסמה ×ĸודכנה", "upload": "ה×ĸלאה", + "upload_action_prompt": "{count} נוספו ל×Ēור לה×ĸלאה", "upload_concurrency": "בו-זמניו×Ē ×Š×œ ה×ĸלאה", + "upload_details": "פרטי ה×ĸלאה", "upload_dialog_info": "האם בר×Ļונך לגבו×Ē ××Ē ×”×Ēמונו×Ē ×Š× ×‘×—×¨×• לשר×Ē?", "upload_dialog_title": "ה×ĸלא×Ē ×Ēמונה", "upload_errors": "ה×ĸלאה הושלמה ×ĸם {count, plural, one {שגיאה #} other {# שגיאו×Ē}}, ר×ĸנן א×Ē ×”×“×Ŗ כדי לראו×Ē ×Ēמונו×Ē ×Š×”×•×ĸלו.", + "upload_finished": "ה×ĸלאה הס×Ēיימה", "upload_progress": "נו×Ēרו {remaining, number} - טופלו {processed, number}/{total, number}", "upload_skipped_duplicates": "דילג ×ĸל {count, plural, one {×Ēמונה כפולה #} other {# ×Ēמונו×Ē ×›×¤×•×œ×•×Ē}}", "upload_status_duplicates": "כפילויו×Ē", @@ -1887,6 +2063,7 @@ "upload_success": "הה×ĸלאה בו×Ļ×ĸה בה×Ļלחה. ר×ĸנן א×Ē ×”×“×Ŗ כדי ל×Ļפו×Ē ×‘×Ēמונו×Ē ×Š×”×•×ĸלו.", "upload_to_immich": "ה×ĸלה לשר×Ē ({count})", "uploading": "מ×ĸלה", + "uploading_media": "מ×ĸלה מדיה", "url": "URL", "usage": "שימוש", "use_biometric": "הש×Ēמ׊ באימו×Ē ×‘×™×•×ž×˜×¨×™", @@ -1907,6 +2084,7 @@ "user_usage_stats_description": "ה×Ļג סטטיסטיקו×Ē ×Š×™×ž×•×Š בחשבון", "username": "׊ם מ׊×Ēמ׊", "users": "מ׊×Ēמשים", + "users_added_to_album_count": "נוספו {count, plural, one {מ׊×Ēמ׊ #} other {# מ׊×Ēמשים}} לאלבום", "utilities": "כלים", "validate": "לאמ×Ē", "validate_endpoint_error": "נא להזין כ×Ēוב×Ē ×Ēקני×Ē", @@ -1925,6 +2103,7 @@ "view_album": "ה×Ļג אלבום", "view_all": "ה×Ļג הכל", "view_all_users": "ה×Ļג א×Ē ×›×œ המש×Ēמשים", + "view_details": "ה×Ļג פרטים", "view_in_timeline": "ראה ב×Ļיר הזמן", "view_link": "ה×Ļג קישור", "view_links": "ה×Ļג קישורים", @@ -1932,6 +2111,7 @@ "view_next_asset": "ה×Ļג א×Ē ×”×Ēמונה הבאה", "view_previous_asset": "ה×Ļג א×Ē ×”×Ēמונה הקודמ×Ē", "view_qr_code": "ה×Ļג ברקוד", + "view_similar_photos": "ה×Ļג ×Ēמונו×Ē ×“×•×ž×•×Ē", "view_stack": "ה×Ļג ×ĸרימה", "view_user": "ה×Ļג מ׊×Ēמ׊", "viewer_remove_from_stack": "הסר מ×ĸרימה", @@ -1950,5 +2130,6 @@ "yes": "כן", "you_dont_have_any_shared_links": "אין לך קישורים משו×Ēפים", "your_wifi_name": "׊ם אינטרנט אלחוטי שלך", - "zoom_image": "זום ל×Ēמונה" + "zoom_image": "זום ל×Ēמונה", + "zoom_to_bounds": "ה×Ēמקד באזור" } diff --git a/i18n/hi.json b/i18n/hi.json index abb1552154..385a85c2e2 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -14,6 +14,7 @@ "add_a_location": "ā¤ā¤• ⤏āĨā¤Ĩā¤žā¤¨ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_a_name": "ā¤¨ā¤žā¤Ž ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_a_title": "ā¤ā¤• ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_birthday": "⤅ā¤Ē⤍āĨ‡ ⤜⤍āĨā¤Žā¤Ļā¤ŋ⤍ ā¤•ā¤ž ⤉⤞āĨā¤˛āĨ‡ā¤– ⤕⤰āĨ‡ā¤‚", "add_endpoint": "endpoint ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_exclusion_pattern": "⤅ā¤Ēā¤ĩā¤žā¤Ļ ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", @@ -22,6 +23,7 @@ "add_partner": "⤜āĨ‹ā¤Ąā¤ŧāĨ€ā¤Ļā¤žā¤° ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_path": "ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "add_tag": "⤚ā¤ŋā¤šāĨā¤¨ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "add_to": "ā¤‡ā¤¸ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚â€Ļ", "add_to_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "add_to_album_bottom_sheet_added": "{album} ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ‡ā¤‚", @@ -33,6 +35,7 @@ "added_to_favorites_count": "ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ {count, number} ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "admin": { "add_exclusion_pattern_description": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚. *, **, ⤔⤰ ? ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤕āĨ‡ ⤗āĨā¤˛āĨ‹ā¤Ŧā¤ŋ⤂⤗ ā¤•ā¤°ā¤¨ā¤ž ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ā¤šāĨˆāĨ¤ \"Raw\" ā¤¨ā¤žā¤Žā¤• ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋā¤•ā¤ž ⤕āĨ€ ⤏⤭āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"**/Raw/**\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ \".tif\" ⤏āĨ‡ ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ ā¤šāĨ‹ā¤¨āĨ‡ ā¤ĩā¤žā¤˛āĨ€ ⤏⤭āĨ€ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"**/*.tif\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ ⤕ā¤ŋ⤏āĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤Ēā¤Ĩ ⤕āĨ‹ ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, \"/path/to/ignore/**\" ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤", + "admin_user": "ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž", "asset_offline_description": "ā¤¯ā¤š ā¤Ŧā¤žā¤šā¤°āĨ€ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤ā¤¸āĨ‡ā¤Ÿ ⤅ā¤Ŧ ā¤Ąā¤ŋ⤏āĨā¤• ā¤Ē⤰ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ ⤔⤰ ⤇⤏āĨ‡ ⤟āĨā¤°āĨˆā¤ļ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤ ⤝ā¤Ļā¤ŋ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤕āĨ‹ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‡ ⤭āĨ€ā¤¤ā¤° ā¤•ā¤šāĨ€ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤Ĩā¤ž, ⤤āĨ‹ ⤍⤈ ⤏⤂ā¤Ŧ⤂⤧ā¤ŋ⤤ ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤅ā¤Ē⤍āĨ€ ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚āĨ¤ ⤇⤏ ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤ĩā¤žā¤Ē⤏ ā¤Ēā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨƒā¤Ēā¤¯ā¤ž ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤍āĨ€ā¤šāĨ‡ ā¤Ļā¤ŋā¤ ā¤—ā¤ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤Ēā¤Ĩ ⤕āĨ‹ ā¤‡ā¤ŽāĨā¤Žā¤ŋ⤚ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤ā¤•āĨā¤¸āĨ‡ā¤¸ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆ ⤔⤰ ā¤Ģā¤ŋ⤰ ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ⤏āĨā¤•āĨˆā¤¨ ⤕⤰āĨ‡ā¤‚āĨ¤", "authentication_settings": "ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "authentication_settings_description": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą, OAuth ⤔⤰ ⤅⤍āĨā¤¯ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", @@ -42,8 +45,15 @@ "backup_database": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ā¤Ŧā¤¨ā¤žā¤ā¤‚", "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "⤰⤖⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ēā¤ŋ⤛⤞āĨ‡ ā¤Ąā¤‚ā¤Ē ⤕āĨ€ ā¤Žā¤žā¤¤āĨā¤°ā¤ž", + "backup_onboarding_1_description": "⤕āĨā¤˛ā¤žā¤‰ā¤Ą ā¤ŽāĨ‡ā¤‚ ā¤¯ā¤ž ⤕ā¤ŋ⤏āĨ€ ⤅⤍āĨā¤¯ ⤭āĨŒā¤¤ā¤ŋ⤕ ⤏āĨā¤Ĩā¤žā¤¨ ā¤Ē⤰ ⤑ā¤Ģā¤¸ā¤žā¤‡ā¤Ÿ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤Ēā¤ŋāĨ¤", + "backup_onboarding_2_description": "ā¤ĩā¤ŋ⤭ā¤ŋ⤍āĨā¤¨ ⤉ā¤Ē⤕⤰⤪āĨ‹ā¤‚ ā¤Ē⤰ ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¯ā¤žā¤āĨ¤ ā¤‡ā¤¸ā¤ŽāĨ‡ā¤‚ ā¤ŽāĨā¤–āĨā¤¯ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ⤔⤰ ⤉⤍ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ā¤•ā¤ž ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤šāĨˆāĨ¤", + "backup_onboarding_3_description": "⤆ā¤Ē⤕āĨ‡ ā¤ĄāĨ‡ā¤Ÿā¤ž ⤕āĨ€ ⤕āĨā¤˛ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¯ā¤žā¤, ⤜ā¤ŋā¤¨ā¤ŽāĨ‡ā¤‚ ā¤ŽāĨ‚⤞ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ⤭āĨ€ ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤šāĨˆā¤‚āĨ¤ ā¤‡ā¤¸ā¤ŽāĨ‡ā¤‚ 1 ⤑ā¤Ģā¤ŧā¤¸ā¤žā¤‡ā¤Ÿ ā¤ĒāĨā¤°ā¤¤ā¤ŋ ⤔⤰ 2 ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¯ā¤žā¤ ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤šāĨˆā¤‚āĨ¤", + "backup_onboarding_description": "⤅ā¤Ē⤍āĨ‡ ā¤ĄāĨ‡ā¤Ÿā¤ž ⤕āĨ€ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ž ⤕āĨ‡ ⤞ā¤ŋā¤ 3-2-1 ā¤ŦāĨˆā¤•⤅ā¤Ē ⤰⤪⤍āĨ€ā¤¤ā¤ŋ ⤕āĨ€ ⤏ā¤ŋā¤Ģā¤žā¤°ā¤ŋā¤ļ ⤕āĨ€ ā¤œā¤žā¤¤āĨ€ ā¤šāĨˆāĨ¤ ā¤ā¤• ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤¸ā¤Žā¤žā¤§ā¤žā¤¨ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤆ā¤Ē⤕āĨ‹ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹/ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¯ā¤žā¤ ⤔⤰ Immich ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤ĻāĨ‹ā¤¨āĨ‹ā¤‚ ⤕āĨ‹ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ā¤°ā¤–ā¤¨ā¤ž ā¤šā¤žā¤šā¤ŋā¤āĨ¤", + "backup_onboarding_footer": "Immich ā¤•ā¤ž ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨āĨ‡ ⤏āĨ‡ ⤏⤂ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ĒāĨā¤°ā¤˛āĨ‡ā¤–⤍ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚āĨ¤", + "backup_onboarding_parts_title": "3-2-1 ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ŽāĨ‡ā¤‚ ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤šāĨˆā¤‚:", + "backup_onboarding_title": "ā¤ŦāĨˆā¤•⤅ā¤ĒāĨā¤¸", "backup_settings": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", - "backup_settings_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚āĨ¤ ⤧āĨā¤¯ā¤žā¤¨ ā¤ĻāĨ‡ā¤‚: ⤇⤍ ā¤•ā¤žā¤°āĨā¤¯āĨ‹ā¤‚ ⤕āĨ€ ⤍ā¤ŋā¤—ā¤°ā¤žā¤¨āĨ€ ā¤¨ā¤šāĨ€ā¤‚ ⤕āĨ€ ā¤œā¤žā¤¤āĨ€ ā¤šāĨˆ ⤔⤰ ā¤ĩā¤ŋā¤Ģā¤˛ā¤¤ā¤ž ⤕āĨ€ ⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ ā¤ŽāĨ‡ā¤‚ ⤆ā¤Ē⤕āĨ‹ ⤏āĨ‚ā¤šā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤žāĨ¤", + "backup_settings_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚āĨ¤", "cleared_jobs": "{job}: ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤•ā¤žā¤°āĨā¤¯ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰ ā¤Ļā¤ŋā¤ ā¤—ā¤", "config_set_by_file": "Config ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ā¤ŽāĨ‡ā¤‚ ā¤ā¤• config ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤏āĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", "confirm_delete_library": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {library} ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", @@ -164,12 +174,26 @@ "metadata_settings_description": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "migration_job": "ā¤ĒāĨā¤°ā¤ĩā¤žā¤¸", "migration_job_description": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤔⤰ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ‹ ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ā¤ŽāĨ‡ā¤‚ ā¤Žā¤žā¤‡ā¤—āĨā¤°āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_cluster_faces_setting_description": "ā¤¨ā¤ ā¤Ēā¤šā¤šā¤žā¤¨āĨ‡ ā¤—ā¤ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ā¤Ē⤰ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ€ ā¤Ēā¤šā¤šā¤žā¤¨ ā¤šā¤˛ā¤žā¤ā¤", + "nightly_tasks_cluster_new_faces_setting": "ā¤¨ā¤ ⤚āĨ‡ā¤šā¤°āĨ‹ā¤‚ ⤕āĨ‹ ā¤¸ā¤ŽāĨ‚ā¤š ā¤ŽāĨ‡ā¤‚ ā¤ļā¤žā¤Žā¤ŋ⤞ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_database_cleanup_setting": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤕āĨā¤˛āĨ€ā¤¨ā¤…ā¤Ē ā¤•ā¤žā¤°āĨā¤¯", + "nightly_tasks_database_cleanup_setting_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤏āĨ‡ ā¤ĒāĨā¤°ā¤žā¤¨ā¤ž, ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ ā¤šāĨ‹ ⤚āĨā¤•ā¤ž ā¤ĄāĨ‡ā¤Ÿā¤ž ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_generate_memories_setting": "ā¤¯ā¤žā¤ĻāĨ‡ā¤‚ ⤉⤤āĨā¤Ē⤍āĨā¤¨ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_generate_memories_setting_description": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤏āĨ‡ ⤍⤈ ā¤¯ā¤žā¤ĻāĨ‡ā¤‚ ā¤Ŧā¤¨ā¤žā¤ā¤", + "nightly_tasks_missing_thumbnails_setting": "ā¤—ā¤žā¤¯ā¤Ŧ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤉⤤āĨā¤Ē⤍āĨā¤¨ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_missing_thumbnails_setting_description": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤍ā¤ŋ⤰āĨā¤Žā¤žā¤Ŗ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ‡ ā¤Ŧā¤ŋā¤¨ā¤ž ā¤•ā¤¤ā¤žā¤°ā¤Ŧā¤ĻāĨā¤§ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤", + "nightly_tasks_settings": "ā¤°ā¤žā¤¤āĨā¤°ā¤ŋā¤•ā¤žā¤˛āĨ€ā¤¨ ā¤•ā¤žā¤°āĨā¤¯ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", + "nightly_tasks_settings_description": "ā¤°ā¤žā¤¤āĨā¤°ā¤ŋā¤•ā¤žā¤˛āĨ€ā¤¨ ā¤•ā¤žā¤°āĨā¤¯āĨ‹ā¤‚ ā¤•ā¤ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍ ⤕⤰āĨ‡ā¤‚", + "nightly_tasks_start_time_setting": "ā¤¸ā¤Žā¤¯ ā¤ļāĨā¤°āĨ‚", + "nightly_tasks_start_time_setting_description": "ā¤ĩā¤š ā¤¸ā¤Žā¤¯ ⤜ā¤Ŧ ⤏⤰āĨā¤ĩ⤰ ā¤°ā¤žā¤¤āĨā¤°ā¤ŋā¤•ā¤žā¤˛āĨ€ā¤¨ ā¤•ā¤žā¤°āĨā¤¯ ā¤šā¤˛ā¤žā¤¨ā¤ž ā¤ļāĨā¤°āĨ‚ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆ", + "nightly_tasks_sync_quota_usage_setting": "⤏ā¤ŋ⤂⤕ ⤕āĨ‹ā¤Ÿā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—", + "nightly_tasks_sync_quota_usage_setting_description": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤉ā¤Ē⤝āĨ‹ā¤— ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", "no_paths_added": "⤕āĨ‹ā¤ˆ ā¤Ēā¤Ĩ ā¤¨ā¤šāĨ€ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "no_pattern_added": "⤕āĨ‹ā¤ˆ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ā¤Ąā¤žā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "note_apply_storage_label_previous_assets": "⤍āĨ‹ā¤Ÿ: ā¤Ēā¤šā¤˛āĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤šā¤˛ā¤žā¤ā¤", "note_cannot_be_changed_later": "⤍āĨ‹ā¤Ÿ: ⤇⤏āĨ‡ ā¤Ŧā¤žā¤Ļ ā¤ŽāĨ‡ā¤‚ ā¤Ŧā¤Ļā¤˛ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž!", "notification_email_from_address": "⤇⤏ ā¤Ē⤤āĨ‡ ⤏āĨ‡", - "notification_email_from_address_description": "ā¤ĒāĨā¤°āĨ‡ā¤ˇā¤• ā¤•ā¤ž ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤¤ā¤ž, ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ⤕āĨ‡ ⤞ā¤ŋā¤: \"ā¤‡ā¤Žā¤ŋ⤚ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤏⤰āĨā¤ĩ⤰ \"", + "notification_email_from_address_description": "ā¤ĒāĨā¤°āĨ‡ā¤ˇā¤• ā¤•ā¤ž ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤¤ā¤ž, ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗ ⤕āĨ‡ ⤞ā¤ŋā¤: \"ā¤‡ā¤Žā¤ŋ⤚ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤏⤰āĨā¤ĩ⤰ \"āĨ¤ ā¤¯ā¤š ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤆ā¤Ē ⤉⤏āĨ€ ā¤Ē⤤āĨ‡ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚ ⤜ā¤ŋ⤏⤏āĨ‡ ⤆ā¤Ē⤕āĨ‹ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤭āĨ‡ā¤œā¤¨āĨ‡ ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤šāĨˆāĨ¤", "notification_email_host_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏⤰āĨā¤ĩ⤰ ā¤•ā¤ž ā¤šāĨ‹ā¤¸āĨā¤Ÿ (⤉ā¤Ļā¤ž. smtp.immitch.app)", "notification_email_ignore_certificate_errors": "ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ⤤āĨā¤°āĨā¤Ÿā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ⤧āĨā¤¯ā¤žā¤¨ ⤍ ā¤ĻāĨ‡ā¤‚", "notification_email_ignore_certificate_errors_description": "⤟āĨ€ā¤ā¤˛ā¤ā¤¸ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ⤏⤤āĨā¤¯ā¤žā¤Ē⤍ ⤤āĨā¤°āĨā¤Ÿā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ⤧āĨā¤¯ā¤žā¤¨ ⤍ ā¤ĻāĨ‡ā¤‚ (⤅⤍āĨā¤ļ⤂⤏ā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚)", @@ -193,7 +217,9 @@ "oauth_enable_description": "OAuth ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚", "oauth_mobile_redirect_uri": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ", "oauth_mobile_redirect_uri_override": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ ⤓ā¤ĩā¤°ā¤°ā¤žā¤‡ā¤Ą", - "oauth_mobile_redirect_uri_override_description": "⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚ ⤜ā¤Ŧ 'app.immitch:/' ā¤ā¤• ā¤…ā¤Žā¤žā¤¨āĨā¤¯ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ ⤝āĨ‚ā¤†ā¤°ā¤†ā¤ˆ ā¤šāĨ‹āĨ¤", + "oauth_mobile_redirect_uri_override_description": "⤜ā¤Ŧ OAuth ā¤ĒāĨā¤°ā¤Ļā¤žā¤¤ā¤ž ⤕ā¤ŋ⤏āĨ€ ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ URI, ⤜āĨˆā¤¸āĨ‡ ''{callback}'' ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ā¤¨ā¤šāĨ€ā¤‚ ā¤ĻāĨ‡ā¤¤ā¤ž, ⤤ā¤Ŧ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚", + "oauth_role_claim": "⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž ā¤•ā¤ž ā¤Ļā¤žā¤ĩā¤ž", + "oauth_role_claim_description": "⤇⤏ ā¤Ļā¤žā¤ĩāĨ‡ ⤕āĨ€ ⤉ā¤Ē⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕ ā¤Ēā¤šāĨā¤ā¤š ā¤ĒāĨā¤°ā¤Ļā¤žā¤¨ ⤕⤰āĨ‡ā¤‚āĨ¤ ā¤Ļā¤žā¤ĩāĨ‡ ā¤ŽāĨ‡ā¤‚ '⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž' ā¤¯ā¤ž 'ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤕' ā¤šāĨ‹ ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", "oauth_settings": "⤓⤑ā¤Ĩ", "oauth_settings_description": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "oauth_settings_more_details": "⤇⤏ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤕āĨ‡ ā¤Ŧā¤žā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚ ā¤ĄāĨ‰ā¤•āĨā¤¸āĨ¤", @@ -202,7 +228,7 @@ "oauth_storage_quota_claim": "ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ā¤•ā¤ž ā¤Ļā¤žā¤ĩā¤ž", "oauth_storage_quota_claim_description": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‡ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ⤕āĨ‹ ⤇⤏ ā¤Ļā¤žā¤ĩāĨ‡ ⤕āĨ‡ ā¤ŽāĨ‚⤞āĨā¤¯ ā¤Ē⤰ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚āĨ¤", "oauth_storage_quota_default": "ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž (GiB)", - "oauth_storage_quota_default_description": "GiB ā¤ŽāĨ‡ā¤‚ ⤕āĨ‹ā¤Ÿā¤ž ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤤ā¤Ŧ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž ⤜ā¤Ŧ ⤕āĨ‹ā¤ˆ ā¤Ļā¤žā¤ĩā¤ž ā¤ĒāĨā¤°ā¤Ļā¤žā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨ‹ (⤅⤏āĨ€ā¤Žā¤ŋ⤤ ⤕āĨ‹ā¤Ÿā¤ž ⤕āĨ‡ ⤞ā¤ŋā¤ 0 ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚)āĨ¤", + "oauth_storage_quota_default_description": "GiB ā¤ŽāĨ‡ā¤‚ ⤕āĨ‹ā¤Ÿā¤ž ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤤ā¤Ŧ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž ⤜ā¤Ŧ ⤕āĨ‹ā¤ˆ ā¤Ļā¤žā¤ĩā¤ž ā¤ĒāĨā¤°ā¤Ļā¤žā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨ‹ āĨ¤", "oauth_timeout": "ā¤ŦāĨā¤°āĨ‡ā¤• ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§", "oauth_timeout_description": "⤅⤍āĨā¤°āĨ‹ā¤§āĨ‹ā¤‚ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤¸ā¤Žā¤¯-⤏āĨ€ā¤Žā¤ž ā¤Žā¤ŋ⤞āĨ€ā¤¸āĨ‡ā¤•ā¤‚ā¤Ą ā¤ŽāĨ‡ā¤‚", "password_enable_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤔⤰ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰āĨ‡ā¤‚", @@ -242,6 +268,7 @@ "storage_template_migration_info": "⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤏⤭āĨ€ ā¤ā¤•āĨā¤¸ā¤ŸāĨ‡ā¤‚ā¤ļ⤍ ⤕āĨ‹ ⤞āĨ‹ā¤…⤰⤕āĨ‡ā¤¸ ā¤ŽāĨ‡ā¤‚ ā¤Ŧā¤Ļ⤞ ā¤ĻāĨ‡ā¤—ā¤žāĨ¤ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ā¤ŽāĨ‡ā¤‚ ⤕ā¤ŋā¤ ā¤—ā¤ ā¤Ŧā¤Ļā¤˛ā¤žā¤ĩ ⤏ā¤ŋ⤰āĨā¤Ģā¤ŧ ⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ā¤˛ā¤žā¤—āĨ‚ ā¤šāĨ‹ā¤‚⤗āĨ‡āĨ¤ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤Ēā¤šā¤˛āĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤Ē⤰ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩāĨā¤¯ā¤žā¤ĒāĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, {job} ā¤šā¤˛ā¤žā¤ā¤āĨ¤", "storage_template_migration_job": "⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ā¤Žā¤žā¤‡ā¤—āĨā¤°āĨ‡ā¤ļ⤍ ā¤•ā¤žā¤°āĨā¤¯", "storage_template_more_details": "⤇⤏ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤕āĨ‡ ā¤Ŧā¤žā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚ ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤔⤰ ⤇⤏⤕āĨ‡ ⤆ā¤ļ⤝", + "storage_template_onboarding_description_v2": "⤏⤕āĨā¤ˇā¤Ž ā¤šāĨ‹ā¤¨āĨ‡ ā¤Ē⤰, ā¤¯ā¤š ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž-⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‹ā¤‚ ⤕āĨ‹ ⤏āĨā¤ĩ⤤⤃ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ŋ⤤ ⤕⤰āĨ‡ā¤—āĨ€āĨ¤ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤Ļ⤏āĨā¤¤ā¤žā¤ĩāĨ‡ā¤œā¤ŧāĨ€ā¤•⤰⤪ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚āĨ¤", "storage_template_path_length": "⤅⤍āĨā¤Žā¤žā¤¨ā¤ŋ⤤ ā¤Ēā¤Ĩ ⤞⤂ā¤Ŧā¤žā¤ˆ ⤏āĨ€ā¤Žā¤ž: {length, number}/{limit, number}", "storage_template_settings": "ā¤­ā¤‚ā¤Ąā¤žā¤°ā¤Ŗ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ", "storage_template_settings_description": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ€ ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ⤔⤰ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤¨ā¤žā¤Ž ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", @@ -357,7 +384,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "⤏ā¤ŋ⤂⤕ ⤕āĨ‡ ā¤ĻāĨŒā¤°ā¤žā¤¨ ā¤ĩāĨˆā¤•⤞āĨā¤Ēā¤ŋ⤕ ā¤Žā¤žā¤¨ā¤Ļā¤‚ā¤ĄāĨ‹ā¤‚ ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤Ģā¤ŧā¤ŋ⤞āĨā¤Ÿā¤° ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤇⤏ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚āĨ¤ ⤇⤏āĨ‡ ⤕āĨ‡ā¤ĩ⤞ ⤤⤭āĨ€ ā¤†ā¤œā¤ŧā¤Žā¤žā¤ā¤ ⤜ā¤Ŧ ⤆ā¤Ē⤕āĨ‹ ⤐ā¤Ē ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤏⤭āĨ€ ā¤ā¤˛āĨā¤Ŧā¤ŽāĨ‹ā¤‚ ā¤•ā¤ž ā¤Ēā¤¤ā¤ž ā¤˛ā¤—ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ā¤šāĨ‹āĨ¤", "advanced_settings_enable_alternate_media_filter_title": "[ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤—ā¤žā¤¤āĨā¤Žā¤•] ā¤ĩāĨˆā¤•⤞āĨā¤Ēā¤ŋ⤕ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏ā¤ŋ⤂⤕ ā¤Ģā¤ŧā¤ŋ⤞āĨā¤Ÿā¤° ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚", "advanced_settings_log_level_title": "⤞āĨ‰ā¤— ⤏āĨā¤¤ā¤°:{level}", - "advanced_settings_prefer_remote_subtitle": "⤕āĨā¤› ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļ ā¤ā¤¸āĨ‡ā¤Ÿ ⤏āĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤•ā¤žā¤Ģā¤ŧāĨ€ ā¤¸ā¤Žā¤¯ ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆāĨ¤ ⤇⤏⤕āĨ‡ ā¤Ŧā¤œā¤žā¤¯ ⤰ā¤ŋā¤ŽāĨ‹ā¤Ÿ ā¤‡ā¤ŽāĨ‡ā¤œ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤇⤏ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ⤕āĨ‹ ⤏⤕āĨā¤°ā¤ŋ⤝ ⤕⤰āĨ‡ā¤‚āĨ¤", + "advanced_settings_prefer_remote_subtitle": "⤕āĨā¤› ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ā¤ā¤¸āĨ‡ā¤Ÿ ⤏āĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ŧā¤šāĨā¤¤ ⤧āĨ€ā¤ŽāĨ‡ ā¤šāĨ‹ā¤¤āĨ‡ ā¤šāĨˆā¤‚āĨ¤ ⤇⤏⤕āĨ‡ ā¤Ŧā¤œā¤žā¤¯, ā¤ĻāĨ‚⤰⤏āĨā¤Ĩ ā¤‡ā¤ŽāĨ‡ā¤œ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤇⤏ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ⤕āĨ‹ ⤏⤕āĨā¤°ā¤ŋ⤝ ⤕⤰āĨ‡ā¤‚āĨ¤", "advanced_settings_prefer_remote_title": "ā¤ĻāĨ‚⤰⤏āĨā¤Ĩ ⤛ā¤ĩā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋā¤•ā¤¤ā¤ž ā¤ĻāĨ‡ā¤‚", "advanced_settings_proxy_headers_subtitle": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ⤅⤍āĨā¤°āĨ‹ā¤§ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤‡ā¤ŽāĨā¤Žā¤ŋ⤚ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤭āĨ‡ā¤œāĨ‡ ā¤œā¤žā¤¨āĨ‡ ā¤ĩā¤žā¤˛āĨ‡ ā¤ĒāĨā¤°āĨ‰ā¤•āĨā¤¸āĨ€ ā¤šāĨ‡ā¤Ąā¤° ⤕āĨ‹ ā¤Ē⤰ā¤ŋā¤­ā¤žā¤ˇā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "advanced_settings_proxy_headers_title": "ā¤ĒāĨā¤°āĨ‰ā¤•āĨā¤¸āĨ€ ā¤šāĨ‡ā¤Ąā¤°", @@ -376,6 +403,7 @@ "album_cover_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕ā¤ĩ⤰ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "album_delete_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ā¤ā¤˛āĨā¤Ŧā¤Ž {album} ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "album_delete_confirmation_description": "⤝ā¤Ļā¤ŋ ā¤¯ā¤š ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ, ⤤āĨ‹ ⤅⤍āĨā¤¯ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤇⤏āĨ‡ ā¤ā¤•āĨā¤¸āĨ‡ā¤¸ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ā¤Ēā¤žā¤ā¤‚ā¤—āĨ‡āĨ¤", + "album_deleted": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "album_info_card_backup_album_excluded": "⤛āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", "album_info_card_backup_album_included": "ā¤ļā¤žā¤Žā¤ŋ⤞", "album_info_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕āĨ€ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕āĨ€ ā¤—ā¤ˆ", @@ -385,6 +413,7 @@ "album_options": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "album_remove_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤žā¤ā¤‚?", "album_remove_user_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {user} ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "album_search_not_found": "⤆ā¤Ē⤕āĨ€ ⤖āĨ‹ā¤œ ⤏āĨ‡ ā¤ŽāĨ‡ā¤˛ ā¤–ā¤žā¤¤ā¤ž ⤕āĨ‹ā¤ˆ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋā¤˛ā¤ž", "album_share_no_users": "ā¤ā¤¸ā¤ž ā¤˛ā¤—ā¤¤ā¤ž ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē⤍āĨ‡ ā¤¯ā¤š ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏⤭āĨ€ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤šāĨˆ ā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ā¤¸ā¤žā¤ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕āĨ‹ā¤ˆ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆāĨ¤", "album_updated": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "album_updated_setting_description": "⤜ā¤Ŧ ⤕ā¤ŋ⤏āĨ€ ā¤¸ā¤žā¤ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤ ā¤šāĨ‹ā¤‚ ⤤āĨ‹ ā¤ā¤• ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰āĨ‡ā¤‚", @@ -400,7 +429,11 @@ "album_viewer_page_share_add_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", "album_with_link_access": "⤞ā¤ŋ⤂⤕ ā¤ĩā¤žā¤˛āĨ‡ ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤇⤏ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤍āĨ‡ ā¤ĻāĨ‡ā¤‚āĨ¤", "albums": "ā¤ā¤˛ā¤Ŧā¤Ž", - "albums_count": "{⤗ā¤ŋ⤍⤤āĨ€, ā¤Ŧā¤šāĨā¤ĩ⤚⤍, ā¤ā¤• {{count, number} ā¤ā¤˛āĨā¤Ŧā¤Ž} ⤅⤍āĨā¤¯ {{count, number} ā¤ā¤˛āĨā¤Ŧā¤Ž}}", + "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}", + "albums_default_sort_order": "ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏āĨ‰ā¤°āĨā¤Ÿ ⤕āĨā¤°ā¤Ž", + "albums_default_sort_order_description": "⤍⤝āĨ‡ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤¤āĨ‡ ā¤¸ā¤Žā¤¯ ⤆⤰⤂⤭ā¤ŋ⤕ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤏āĨ‰ā¤°āĨā¤Ÿ ⤕āĨā¤°ā¤ŽāĨ¤", + "albums_feature_description": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ⤏⤂⤗āĨā¤°ā¤š ⤜ā¤ŋ⤏āĨ‡ ⤅⤍āĨā¤¯ ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‡ ā¤¸ā¤žā¤Ĩ ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", + "albums_on_device_count": "ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰ ā¤ā¤˛āĨā¤Ŧā¤Ž ({count})", "all": "⤏⤭āĨ€", "all_albums": "⤏⤭āĨ€ ā¤ā¤˛ā¤Ŧā¤Ž", "all_people": "⤏⤭āĨ€ ⤞āĨ‹ā¤—", @@ -421,13 +454,14 @@ "app_settings": "ā¤ā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ļ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗", "appears_in": "ā¤ĒāĨā¤°ā¤•ā¤Ÿ ā¤šāĨ‹ā¤¤ā¤ž ā¤šāĨˆ", "archive": "⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯", + "archive_action_prompt": "{count} ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤š ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", "archive_or_unarchive_photo": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ā¤¯ā¤ž ⤅⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕⤰āĨ‡ā¤‚", "archive_page_no_archived_assets": "⤕āĨ‹ā¤ˆ ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋ⤞āĨ€", "archive_page_title": "ā¤ĒāĨā¤°ā¤žā¤˛āĨ‡ā¤– ({count})", "archive_size": "ā¤ĒāĨā¤°ā¤žā¤˛āĨ‡ā¤– ā¤†ā¤•ā¤žā¤°", "archive_size_description": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏⤂⤗āĨā¤°ā¤š ā¤†ā¤•ā¤žā¤° ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰ ⤕⤰āĨ‡ā¤‚ (GiB ā¤ŽāĨ‡ā¤‚)", "archived": "⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤", - "archived_count": "{ā¤—ā¤Ŗā¤¨ā¤ž, ā¤Ŧā¤šāĨā¤ĩ⤚⤍, ⤅⤍āĨā¤¯ {⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ #}}", + "archived_count": "{count, plural, other {# ⤏⤂⤗āĨā¤°ā¤šāĨ€ā¤¤ ⤕ā¤ŋā¤ ā¤—ā¤}", "are_these_the_same_person": "⤕āĨā¤¯ā¤ž ⤝āĨ‡ ā¤ĩā¤šāĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ā¤šāĨˆā¤‚?", "are_you_sure_to_do_this": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤¸āĨā¤¤ā¤ĩ ā¤ŽāĨ‡ā¤‚ ⤇⤏āĨ‡ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "asset_action_delete_err_read_only": "⤕āĨ‡ā¤ĩ⤞ ā¤Ēā¤ĸā¤ŧ⤍āĨ‡ ⤝āĨ‹ā¤—āĨā¤¯ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(⤓⤂) ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¯ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž, ⤛āĨ‹ā¤Ąā¤ŧā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆ", @@ -440,73 +474,227 @@ "asset_hashing": "ā¤šāĨˆā¤ļā¤ŋ⤂⤗...āĨ¤", "asset_list_group_by_sub_title": "ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤¸ā¤ŽāĨ‚ā¤š ā¤Ŧā¤¨ā¤žā¤ā¤‚", "asset_list_layout_settings_dynamic_layout_title": "⤗⤤ā¤ŋā¤ļāĨ€ā¤˛ ⤞āĨ‡ā¤†ā¤‰ā¤Ÿ", + "asset_list_layout_settings_group_automatically": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤", + "asset_list_layout_settings_group_by": "ā¤¸ā¤ŽāĨ‚ā¤š ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž", + "asset_list_layout_settings_group_by_month_day": "ā¤Žā¤šāĨ€ā¤¨ā¤ž + ā¤Ļā¤ŋ⤍", + "asset_list_layout_sub_title": "⤞āĨ‡ā¤†ā¤‰ā¤Ÿ", + "asset_list_settings_subtitle": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤗āĨā¤°ā¤ŋā¤Ą ⤞āĨ‡ā¤†ā¤‰ā¤Ÿ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", + "asset_list_settings_title": "⤚ā¤ŋ⤤āĨā¤° ⤕āĨ€ ā¤œā¤žā¤˛āĨ€", "asset_offline": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨", "asset_offline_description": "ā¤¯ā¤š ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤑ā¤Ģā¤ŧā¤˛ā¤žā¤‡ā¤¨ ā¤šāĨˆāĨ¤", "asset_restored_successfully": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", "asset_skipped": "⤛āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "asset_skipped_in_trash": "ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚", "asset_uploaded": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕ā¤ŋā¤ ā¤—ā¤", - "asset_uploading": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆ..āĨ¤", + "asset_uploading": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆâ€Ļ", + "asset_viewer_settings_subtitle": "⤅ā¤Ē⤍āĨ€ ⤗āĨˆā¤˛ā¤°āĨ€ ā¤ĩāĨā¤¯āĨ‚⤅⤰ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "asset_viewer_settings_title": "ā¤ā¤¸āĨ‡ā¤Ÿ ā¤ĩāĨā¤¯āĨ‚⤅⤰", "assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤‚", + "assets_added_count": "{count, plural, one {# asset} other {# assets}} ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "assets_added_to_album_count": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ {count, plural, one {# asset} other {# assets}} ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¨ā¤šāĨ€ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", + "assets_count": "{count, plural, one {# ā¤†ā¤‡ā¤Ÿā¤Ž} other {# ā¤†ā¤‡ā¤Ÿā¤ŽāĨā¤¸}}", "assets_deleted_permanently": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", "assets_deleted_permanently_from_server": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", + "assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}", + "assets_downloaded_successfully": "{count, plural, one {# ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆ} other {# ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕āĨ€ ā¤—ā¤ˆā¤‚}}", + "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} ⤕āĨ‹ ⤟āĨā¤°āĨˆā¤ļ ā¤ŽāĨ‡ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤¯ā¤ž ā¤—ā¤¯ā¤ž", + "assets_permanently_deleted_count": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž {count, plural, one {# asset} other {# assets}}", + "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "assets_removed_permanently_from_device": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆā¤‚", - "assets_restore_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤅ā¤Ē⤍āĨ€ ⤏⤭āĨ€ ⤍⤎āĨā¤Ÿ ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡!", + "assets_restore_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤅ā¤Ē⤍āĨ€ ⤏⤭āĨ€ ⤍⤎āĨā¤Ÿ ⤕āĨ€ ā¤—ā¤ˆ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡āĨ¤", + "assets_restored_count": "ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ {count, plural, one {# asset} other {# assets}}", "assets_restored_successfully": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ€ ā¤—ā¤ˆā¤‚", "assets_trashed": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", + "assets_trashed_count": "⤟āĨā¤°āĨˆā¤ļ ⤕āĨ€ ā¤—ā¤ˆ {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ(ā¤¯ā¤žā¤) ā¤‡ā¤Žā¤ŋ⤚ ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ā¤•ā¤šā¤°āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛āĨ€ ā¤—ā¤ˆā¤‚", + "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}}ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤•ā¤ž ā¤Ēā¤šā¤˛āĨ‡ ⤏āĨ‡ ā¤šāĨ€ ā¤šā¤ŋ⤏āĨā¤¸ā¤ž ā¤ĨāĨ‡", "authorized_devices": "⤅⤧ā¤ŋ⤕āĨƒā¤¤ ⤉ā¤Ē⤕⤰⤪", + "automatic_endpoint_switching_subtitle": "⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤šāĨ‹ā¤¨āĨ‡ ā¤Ē⤰ ⤍ā¤ŋ⤰āĨā¤Ļā¤ŋ⤎āĨā¤Ÿ ā¤ĩā¤žā¤ˆ-ā¤Ģā¤žā¤ˆ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰āĨ‡ā¤‚ ⤔⤰ ⤅⤍āĨā¤¯ā¤¤āĨā¤° ā¤ĩāĨˆā¤•⤞āĨā¤Ēā¤ŋ⤕ ⤕⤍āĨ‡ā¤•āĨā¤ļ⤍ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚", + "automatic_endpoint_switching_title": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ URL ⤏āĨā¤ĩā¤ŋ⤚ā¤ŋ⤂⤗", + "autoplay_slideshow": "ā¤‘ā¤ŸāĨ‹ā¤ĒāĨā¤˛āĨ‡ ⤏āĨā¤˛ā¤žā¤‡ā¤Ą ā¤ļāĨ‹", "back": "ā¤ĩā¤žā¤Ē⤏", "back_close_deselect": "ā¤ĩā¤žā¤Ē⤏ ā¤œā¤žā¤ā¤, ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚, ā¤¯ā¤ž ā¤…ā¤šā¤¯ā¤¨ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", - "backup_controller_page_background_wifi": "Only on WiFi", + "background_location_permission": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤏āĨā¤Ĩā¤žā¤¨ ⤅⤍āĨā¤Žā¤¤ā¤ŋ", + "background_location_permission_content": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŽāĨ‡ā¤‚ ⤚⤞⤤āĨ‡ ā¤¸ā¤Žā¤¯ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ā¤Ŧā¤Ļ⤞⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, Immich ⤕āĨ‡ ā¤Ēā¤žā¤¸ *ā¤šā¤ŽāĨ‡ā¤ļā¤ž* ⤏⤟āĨ€ā¤• ⤏āĨā¤Ĩā¤žā¤¨ ⤤⤕ ā¤Ēā¤šāĨā¤‚ā¤š ā¤šāĨ‹ā¤¨āĨ€ ā¤šā¤žā¤šā¤ŋā¤ ā¤¤ā¤žā¤•ā¤ŋ ⤐ā¤Ē ā¤ĩā¤žā¤ˆ-ā¤Ģā¤žā¤ˆ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ā¤•ā¤ž ā¤¨ā¤žā¤Ž ā¤Ēā¤ĸā¤ŧ ⤏⤕āĨ‡", + "backup": "ā¤ŦāĨˆā¤•⤅ā¤Ē", + "backup_album_selection_page_albums_device": "ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰ ā¤ā¤˛āĨā¤Ŧā¤Ž ({count})", + "backup_album_selection_page_albums_tap": "ā¤ļā¤žā¤Žā¤ŋ⤞ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤟āĨˆā¤Ē ⤕⤰āĨ‡ā¤‚, ā¤Ŧā¤žā¤šā¤° ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ąā¤Ŧ⤞ ⤟āĨˆā¤Ē ⤕⤰āĨ‡ā¤‚", + "backup_album_selection_page_assets_scatter": "ā¤ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤ˆ ā¤ā¤˛āĨā¤Ŧā¤ŽāĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ā¤Ŧā¤ŋ⤖⤰āĨ‡ ā¤šāĨ‹ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚āĨ¤ ⤇⤏⤞ā¤ŋā¤, ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‡ ā¤ĻāĨŒā¤°ā¤žā¤¨ ā¤ā¤˛āĨā¤Ŧā¤ŽāĨ‹ā¤‚ ⤕āĨ‹ ā¤ļā¤žā¤Žā¤ŋ⤞ ā¤¯ā¤ž ā¤Ŧā¤žā¤šā¤° ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž ā¤šāĨˆāĨ¤", + "backup_album_selection_page_select_albums": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤚āĨā¤¨āĨ‡ā¤‚", + "backup_album_selection_page_selection_info": "⤚⤝⤍ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€", + "backup_album_selection_page_total_assets": "⤕āĨā¤˛ ⤅ā¤ĻāĨā¤ĩā¤ŋ⤤āĨ€ā¤¯ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤", + "backup_all": "⤏⤭āĨ€", + "backup_background_service_backup_failed_message": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞. ā¤ĒāĨā¤¨ā¤ƒ ā¤ĒāĨā¤°ā¤¯ā¤žā¤¸ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆâ€Ļ", + "backup_background_service_connection_failed_message": "⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞. ā¤ĒāĨā¤¨ā¤ƒ ā¤ĒāĨā¤°ā¤¯ā¤žā¤¸ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤°ā¤šā¤ž ā¤šāĨˆâ€Ļ", + "backup_background_service_current_upload_notification": "{filename} ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆ", + "backup_background_service_default_notification": "⤍⤈ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ā¤œā¤žā¤‚ā¤š ⤕āĨ€ ā¤œā¤ž ā¤°ā¤šāĨ€ ā¤šāĨˆâ€Ļ", + "backup_background_service_error_title": "ā¤ŦāĨˆā¤•⤅ā¤Ē ⤤āĨā¤°āĨā¤Ÿā¤ŋ", + "backup_background_service_in_progress_notification": "⤅ā¤Ē⤍āĨ€ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨ā¤žâ€Ļ", + "backup_background_service_upload_failure_notification": "{filename} ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", + "backup_controller_page_albums": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ā¤˛āĨā¤Ŧā¤Ž", + "backup_controller_page_background_app_refresh_disabled_content": "ā¤ŦāĨˆā¤•⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ > ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ > ā¤ŦāĨˆā¤•⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤐ā¤Ē ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤ŽāĨ‡ā¤‚ ā¤ŦāĨˆā¤•⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤐ā¤Ē ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚āĨ¤", + "backup_controller_page_background_app_refresh_disabled_title": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤐ā¤Ē ⤰āĨ€ā¤Ģā¤ŧāĨā¤°āĨ‡ā¤ļ ⤅⤕āĨā¤ˇā¤Ž", + "backup_controller_page_background_app_refresh_enable_button_text": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤Ē⤰ ā¤œā¤žā¤ā¤", + "backup_controller_page_background_battery_info_link": "⤕āĨˆā¤¸āĨ‡ ā¤ŽāĨā¤āĨ‡ ā¤Ļā¤ŋā¤–ā¤žā¤“", + "backup_controller_page_background_battery_info_message": "⤏⤰āĨā¤ĩāĨ‹ā¤¤āĨā¤¤ā¤Ž ā¤ŦāĨˆā¤•⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨˆā¤•⤅ā¤Ē ⤅⤍āĨā¤­ā¤ĩ ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨƒā¤Ēā¤¯ā¤ž Immich ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ŦāĨˆā¤•⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ ⤕āĨ‹ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ĩā¤žā¤˛āĨ‡ ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ā¤ŦāĨˆā¤Ÿā¤°āĨ€ ⤑ā¤ĒāĨā¤Ÿā¤ŋā¤Žā¤žā¤‡ā¤œā¤ŧāĨ‡ā¤ļ⤍ ⤕āĨ‹ ⤅⤕āĨā¤ˇā¤Ž ⤕⤰āĨ‡ā¤‚āĨ¤\n\n⤚āĨ‚⤁⤕ā¤ŋ ā¤¯ā¤š ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸-ā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿ ā¤šāĨˆ, ⤇⤏⤞ā¤ŋā¤ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤅ā¤Ē⤍āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤍ā¤ŋ⤰āĨā¤Žā¤žā¤¤ā¤ž ⤏āĨ‡ ⤆ā¤ĩā¤ļāĨā¤¯ā¤• ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚āĨ¤", + "backup_controller_page_background_battery_info_ok": "⤠āĨ€ā¤•", + "backup_controller_page_background_battery_info_title": "ā¤ŦāĨˆā¤Ÿā¤°āĨ€ ⤅⤍āĨā¤•āĨ‚⤞⤍", + "backup_controller_page_background_charging": "⤕āĨ‡ā¤ĩ⤞ ā¤šā¤žā¤°āĨā¤œ ⤕⤰⤤āĨ‡ ā¤¸ā¤Žā¤¯", + "backup_controller_page_background_configure_error": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤏āĨ‡ā¤ĩā¤ž ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", + "backup_controller_page_background_delay": "⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤•ā¤ž ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĩā¤ŋ⤞⤂ā¤Ŧā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚: {duration}", + "backup_controller_page_background_description": "⤐ā¤Ē ⤖āĨ‹ā¤˛āĨ‡ ā¤Ŧā¤ŋā¤¨ā¤ž ⤕ā¤ŋ⤏āĨ€ ⤭āĨ€ ⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤•ā¤ž ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤏āĨ‡ā¤ĩā¤ž ā¤šā¤žā¤˛āĨ‚ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_background_is_off": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ā¤šāĨˆ", + "backup_controller_page_background_is_on": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤šāĨˆ", + "backup_controller_page_background_turn_off": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤏āĨ‡ā¤ĩā¤ž ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_background_turn_on": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤏āĨ‡ā¤ĩā¤ž ā¤šā¤žā¤˛āĨ‚ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_background_wifi": "⤕āĨ‡ā¤ĩ⤞ ā¤ĩā¤žā¤ˆ-ā¤Ģā¤ŧā¤žā¤ˆ ā¤Ē⤰", + "backup_controller_page_backup": "ā¤ŦāĨˆā¤•⤅ā¤Ē", + "backup_controller_page_backup_selected": "⤚⤝⤍ā¤ŋ⤤: ", + "backup_controller_page_backup_sub": "ā¤ŦāĨˆā¤•⤅ā¤Ē ⤕ā¤ŋā¤ ā¤—ā¤ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", + "backup_controller_page_created": "⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤ ⤤ā¤ŋā¤Ĩā¤ŋ: {date}", + "backup_controller_page_desc_backup": "⤐ā¤Ē ⤖āĨ‹ā¤˛ā¤¤āĨ‡ ā¤¸ā¤Žā¤¯ ⤏⤰āĨā¤ĩ⤰ ā¤Ē⤰ ⤍⤈ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ⤕⤰āĨ‡ā¤‚āĨ¤", + "backup_controller_page_excluded": "⤛āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž: ", + "backup_controller_page_failed": "ā¤ĩā¤ŋā¤Ģ⤞ ({count})", + "backup_controller_page_filename": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤¨ā¤žā¤Ž: {filename} [{size}]", + "backup_controller_page_id": "ā¤†ā¤ˆā¤ĄāĨ€: {id}", + "backup_controller_page_info": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€", + "backup_controller_page_none_selected": "⤕āĨ‹ā¤ˆ ⤭āĨ€ ⤚⤝⤍ā¤ŋ⤤ ā¤¨ā¤šāĨ€ā¤‚", + "backup_controller_page_remainder": "ā¤ļāĨ‡ā¤ˇ", + "backup_controller_page_remainder_sub": "⤚⤝⤍ ⤏āĨ‡ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ļāĨ‡ā¤ˇ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", + "backup_controller_page_server_storage": "⤏⤰āĨā¤ĩ⤰ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ", + "backup_controller_page_start_backup": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĒāĨā¤°ā¤žā¤°ā¤‚⤭ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_status_off": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ā¤šāĨˆ", + "backup_controller_page_status_on": "⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤šāĨˆ", + "backup_controller_page_storage_format": "{total} ā¤ŽāĨ‡ā¤‚ ⤏āĨ‡ {used} ⤉ā¤Ē⤝āĨ‹ā¤— ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", + "backup_controller_page_to_backup": "ā¤ŦāĨˆā¤•⤅ā¤Ē ⤕ā¤ŋā¤ ā¤œā¤žā¤¨āĨ‡ ā¤ĩā¤žā¤˛āĨ‡ ā¤ā¤˛āĨā¤Ŧā¤Ž", + "backup_controller_page_total_sub": "⤚⤝⤍ā¤ŋ⤤ ā¤ā¤˛āĨā¤Ŧā¤ŽāĨ‹ā¤‚ ⤏āĨ‡ ⤏⤭āĨ€ ⤅ā¤ĻāĨā¤ĩā¤ŋ⤤āĨ€ā¤¯ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", + "backup_controller_page_turn_off": "⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_turn_on": "⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ⤕⤰āĨ‡ā¤‚", + "backup_controller_page_uploading_file_info": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤¨ā¤ž", + "backup_err_only_album": "ā¤ā¤•ā¤Žā¤žā¤¤āĨā¤° ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤¨ā¤šāĨ€ā¤‚ ā¤šā¤Ÿā¤žā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", + "backup_info_card_assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ", + "backup_manual_cancelled": "⤰ā¤ĻāĨā¤Ļ", + "backup_manual_in_progress": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤Ēā¤šā¤˛āĨ‡ ⤏āĨ‡ ā¤šāĨ€ ā¤ĒāĨā¤°ā¤—⤤ā¤ŋ ā¤Ē⤰ ā¤šāĨˆāĨ¤ ⤕āĨā¤› ā¤ĻāĨ‡ā¤° ā¤Ŧā¤žā¤Ļ ā¤ĒāĨā¤°ā¤¯ā¤žā¤¸ ⤕⤰āĨ‡ā¤‚", + "backup_manual_success": "⤏ā¤Ģā¤˛ā¤¤ā¤ž", + "backup_manual_title": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ", + "backup_options": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", + "backup_options_page_title": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", + "backup_setting_subtitle": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ⤔⤰ ⤅⤗āĨā¤°ā¤­āĨ‚ā¤Žā¤ŋ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "backup_settings_subtitle": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸ ā¤¸ā¤‚ā¤­ā¤žā¤˛āĨ‡ā¤‚", "backward": "ā¤Ēā¤ŋā¤›ā¤˛ā¤ž", + "biometric_auth_enabled": "ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏⤕āĨā¤ˇā¤Ž", + "biometric_locked_out": "⤆ā¤Ē ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏āĨ‡ ā¤Ŧā¤žā¤šā¤° ā¤šāĨˆā¤‚", + "biometric_no_options": "⤕āĨ‹ā¤ˆ ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ", + "biometric_not_available": "⤇⤏ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ā¤Ē⤰ ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ", "birthdate_saved": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ", "birthdate_set_description": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ā¤•ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤— ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤕āĨ‡ ā¤¸ā¤Žā¤¯ ⤇⤏ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ€ ⤆⤝āĨ ⤕āĨ€ ā¤—ā¤Ŗā¤¨ā¤ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤žā¤¤ā¤ž ā¤šāĨˆāĨ¤", "blurred_background": "⤧āĨā¤‚⤧⤞āĨ€ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ", + "bugs_and_feature_requests": "ā¤Ŧ⤗ ⤔⤰ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§", "build": "⤍ā¤ŋ⤰āĨā¤Žā¤žā¤Ŗ", "build_image": "⤛ā¤ĩā¤ŋ ā¤Ŧā¤¨ā¤žā¤ā¤", + "bulk_delete_duplicates_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {count, plural, one {# duplicate asset} other {# duplicate assets}} ⤕āĨ‹ ā¤Ŧ⤞āĨā¤• ā¤ŽāĨ‡ā¤‚ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤇⤏⤏āĨ‡ ā¤šā¤° ⤗āĨā¤°āĨā¤Ē ⤕āĨ€ ⤏ā¤Ŧ⤏āĨ‡ ā¤Ŧā¤Ąā¤ŧāĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ŧ⤍āĨ€ ā¤°ā¤šāĨ‡ā¤—āĨ€ ⤔⤰ ā¤Ŧā¤žā¤•āĨ€ ⤏⤭āĨ€ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤šā¤ŽāĨ‡ā¤ļā¤ž ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤šā¤Ÿ ā¤œā¤žā¤ā¤ā¤—āĨ‡āĨ¤ ⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡!", + "bulk_keep_duplicates_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {count, plural, one {# duplicate asset} other {# duplicate assets}} ā¤°ā¤–ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤇⤏⤏āĨ‡ ā¤Ŧā¤ŋā¤¨ā¤ž ⤕āĨā¤› ā¤šā¤Ÿā¤žā¤ ⤏⤭āĨ€ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤗āĨā¤°āĨā¤Ē ā¤šā¤˛ ā¤šāĨ‹ ā¤œā¤žā¤ā¤ā¤—āĨ‡āĨ¤", + "bulk_trash_duplicates_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {count, plural, one {# duplicate asset} other {# duplicate assets}} ⤕āĨ‹ ā¤Ŧ⤞āĨā¤• ⤟āĨā¤°āĨˆā¤ļ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ⤇⤏⤏āĨ‡ ā¤šā¤° ⤗āĨā¤°āĨā¤Ē ⤕āĨ€ ⤏ā¤Ŧ⤏āĨ‡ ā¤Ŧā¤Ąā¤ŧāĨ€ ā¤ā¤¸āĨ‡ā¤Ÿ ā¤°ā¤šāĨ‡ā¤—āĨ€ ⤔⤰ ā¤Ŧā¤žā¤•āĨ€ ⤏⤭āĨ€ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤟āĨā¤°āĨˆā¤ļ ā¤šāĨ‹ ā¤œā¤žā¤ā¤ā¤—āĨ‡āĨ¤", "buy": "ā¤‡ā¤ŽāĨā¤ŽāĨ€ā¤š ⤖⤰āĨ€ā¤ĻāĨ‹", + "cache_settings_clear_cache_button": "⤕āĨˆā¤ļ ⤕āĨ‹ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰āĨ‡ā¤‚", + "cache_settings_clear_cache_button_title": "⤐ā¤Ē ā¤•ā¤ž ⤕āĨˆā¤ļ ā¤¸ā¤žā¤Ģā¤ŧ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤ ⤕āĨˆā¤ļ ⤕āĨ‡ ā¤ĻāĨ‹ā¤Ŧā¤žā¤°ā¤ž ā¤Ŧ⤍⤍āĨ‡ ⤤⤕, ā¤¯ā¤š ⤐ā¤Ē ⤕āĨ‡ ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ā¤Ē⤰ ā¤•ā¤žā¤Ģā¤ŧāĨ€ ⤅⤏⤰ ā¤Ąā¤žā¤˛āĨ‡ā¤—ā¤žāĨ¤", + "cache_settings_duplicated_assets_clear_button": "⤏āĨā¤Ē⤎āĨā¤Ÿ", + "cache_settings_duplicated_assets_subtitle": "⤐ā¤Ē ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ⤅⤍ā¤ĻāĨ‡ā¤–ā¤ž ⤕āĨ€ ā¤—ā¤ˆ ⤤⤏āĨā¤ĩāĨ€ā¤°āĨ‡ā¤‚ ⤔⤰ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", + "cache_settings_duplicated_assets_title": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤‚ ({count})", + "cache_settings_statistics_album": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_full": "ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤚ā¤ŋ⤤āĨā¤°", + "cache_settings_statistics_shared": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_thumbnail": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_title": "⤕āĨˆā¤ļ ⤉ā¤Ē⤝āĨ‹ā¤—", + "cache_settings_subtitle": "Immich ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ā¤ā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ļ⤍ ⤕āĨ‡ ⤕āĨˆā¤ļā¤ŋ⤂⤗ ā¤ĩāĨā¤¯ā¤ĩā¤šā¤žā¤° ⤕āĨ‹ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "cache_settings_tile_subtitle": "⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‡ ā¤ĩāĨā¤¯ā¤ĩā¤šā¤žā¤° ⤕āĨ‹ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "cache_settings_tile_title": "⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ", + "cache_settings_title": "⤕āĨˆā¤ļā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤¸", "camera": "⤕āĨˆā¤Žā¤°ā¤ž", "camera_brand": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ŦāĨā¤°ā¤žā¤‚ā¤Ą", "camera_model": "⤕āĨˆā¤Žā¤°ā¤ž ā¤ŽāĨ‰ā¤Ąā¤˛", "cancel": "⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤¨ā¤ž", "cancel_search": "⤖āĨ‹ā¤œ ⤰ā¤ĻāĨā¤Ļ ⤕⤰āĨ‡ā¤‚", + "canceled": "⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤¨ā¤ž", + "canceling": "⤰ā¤ĻāĨā¤Ļ ⤕⤰ ā¤°ā¤šā¤ž ā¤šāĨˆ", "cannot_merge_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ā¤•ā¤ž ā¤ĩā¤ŋ⤞⤝ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨ‹ ā¤¸ā¤•ā¤¤ā¤ž", "cannot_undo_this_action": "⤆ā¤Ē ⤇⤏ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‹ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡!", "cannot_update_the_description": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", + "cast": "ā¤ĸā¤žā¤˛ā¤¨ā¤ž", + "cast_description": "⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤•ā¤žā¤¸āĨā¤Ÿ ⤗⤂⤤ā¤ĩāĨā¤¯āĨ‹ā¤‚ ⤕āĨ‹ ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŧā¤ŋ⤗⤰ ⤕⤰āĨ‡ā¤‚", "change_date": "ā¤Ŧā¤Ļā¤˛ā¤žā¤ĩ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤•", + "change_description": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", + "change_display_order": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ⤕āĨā¤°ā¤Ž ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_expiration_time": "ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ā¤ŋ ā¤¸ā¤Žā¤¯ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_name": "ā¤¨ā¤žā¤Ž ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤¨ ⤕⤰āĨ‡ā¤‚", - "change_name_successfully": "ā¤¨ā¤žā¤Ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", + "change_name_successfully": "ā¤¨ā¤žā¤Ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ŧā¤Ļā¤˛ā¤ž ā¤—ā¤¯ā¤ž", "change_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_password_description": "ā¤¯ā¤š ā¤¯ā¤ž ⤤āĨ‹ ā¤Ēā¤šā¤˛āĨ€ ā¤Ŧā¤žā¤° ā¤šāĨˆ ⤜ā¤Ŧ ⤆ā¤Ē ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚ ā¤¯ā¤ž ⤆ā¤Ēā¤•ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤", + "change_password_form_confirm_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤕āĨ€ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕āĨ€ā¤œā¤ŋ⤝āĨ‡", + "change_password_form_description": "ā¤¨ā¤Žā¤¸āĨā¤¤āĨ‡ {name},\n\nā¤¯ā¤ž ⤤āĨ‹ ⤆ā¤Ē ā¤Ēā¤šā¤˛āĨ€ ā¤Ŧā¤žā¤° ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕⤰ ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚ ā¤¯ā¤ž ā¤Ģā¤ŋ⤰ ⤆ā¤Ēā¤•ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤•ā¤ž ⤅⤍āĨā¤°āĨ‹ā¤§ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍āĨ€ā¤šāĨ‡ ā¤¨ā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ąā¤žā¤˛āĨ‡ā¤‚āĨ¤", + "change_password_form_new_password": "ā¤¨ā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "change_password_form_password_mismatch": "ā¤¸ā¤žā¤‚ā¤•āĨ‡ā¤¤ā¤ŋ⤕ ā¤ļā¤ŦāĨā¤Ļ ā¤ŽāĨ‡ā¤˛ ā¤¨ā¤šāĨ€ā¤‚ ā¤–ā¤žā¤¤āĨ‡", + "change_password_form_reenter_new_password": "ā¤¨ā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤ĒāĨā¤¨ā¤ƒ ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚", + "change_pin_code": "ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "change_your_password": "⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "changed_visibility_successfully": "ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤ŋ⤤", + "check_corrupt_asset_backup": "ā¤ĻāĨ‚⤎ā¤ŋ⤤ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤ŦāĨˆā¤•⤅ā¤Ē ⤕āĨ€ ā¤œā¤žā¤ā¤š ⤕⤰āĨ‡ā¤‚", + "check_corrupt_asset_backup_button": "ā¤œā¤žā¤ā¤š ⤕⤰āĨ‡ā¤‚", + "check_corrupt_asset_backup_description": "ā¤¯ā¤š ā¤œā¤žā¤ā¤š ⤕āĨ‡ā¤ĩ⤞ ā¤ĩā¤žā¤ˆ-ā¤Ģā¤ŧā¤žā¤ˆ ā¤Ē⤰ ā¤šāĨ€ ⤕⤰āĨ‡ā¤‚ ⤔⤰ ⤏⤭āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ŦāĨˆā¤•⤅ā¤Ē ⤞āĨ‡ā¤¨āĨ‡ ⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ā¤šāĨ€ ⤕⤰āĨ‡ā¤‚āĨ¤ ⤇⤏ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤ŽāĨ‡ā¤‚ ⤕āĨā¤› ā¤Žā¤ŋ⤍⤟ ⤞⤗ ⤏⤕⤤āĨ‡ ā¤šāĨˆā¤‚āĨ¤", "check_logs": "⤞āĨ‰ā¤— ā¤œā¤žā¤‚ā¤šāĨ‡ā¤‚", "choose_matching_people_to_merge": "ā¤Žā¤°āĨā¤œ ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Žā¤ŋ⤞⤤āĨ‡-⤜āĨā¤˛ā¤¤āĨ‡ ⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤚āĨā¤¨āĨ‡ā¤‚", "city": "ā¤ļā¤šā¤°", "clear": "⤏āĨā¤Ē⤎āĨā¤Ÿ", "clear_all": "⤏⤭āĨ€ ā¤¸ā¤žā¤Ģ ⤕⤰āĨ‡ā¤‚", "clear_all_recent_searches": "⤏⤭āĨ€ ā¤šā¤žā¤˛ā¤ŋā¤¯ā¤ž ⤖āĨ‹ā¤œāĨ‡ā¤‚ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰āĨ‡ā¤‚", + "clear_file_cache": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤕āĨˆā¤ļ ā¤¸ā¤žā¤Ģā¤ŧ ⤕⤰āĨ‡ā¤‚", "clear_message": "⤏āĨā¤Ē⤎āĨā¤Ÿ ⤏⤂ā¤ĻāĨ‡ā¤ļ", "clear_value": "⤏āĨā¤Ē⤎āĨā¤Ÿ ā¤ŽāĨ‚⤞āĨā¤¯", + "client_cert_dialog_msg_confirm": "⤠āĨ€ā¤•", + "client_cert_enter_password": "ā¤Ēā¤žā¤¸ ā¤ĩ⤰āĨā¤Ą ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚", + "client_cert_import": "ā¤†ā¤¯ā¤žā¤¤", + "client_cert_import_success_msg": "⤕āĨā¤˛ā¤žā¤‡ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤†ā¤¯ā¤žā¤¤ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", + "client_cert_invalid_msg": "ā¤…ā¤Žā¤žā¤¨āĨā¤¯ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤¯ā¤ž ⤗⤞⤤ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "client_cert_remove_msg": "⤕āĨā¤˛ā¤žā¤‡ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ", + "client_cert_subtitle": "⤕āĨ‡ā¤ĩ⤞ PKCS12 (.p12, .pfx) ā¤Ģā¤ŧāĨ‰ā¤°āĨā¤ŽāĨˆā¤Ÿ ā¤•ā¤ž ā¤¸ā¤Žā¤°āĨā¤Ĩ⤍ ā¤•ā¤°ā¤¤ā¤ž ā¤šāĨˆāĨ¤ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤†ā¤¯ā¤žā¤¤/ā¤šā¤Ÿā¤žā¤ā¤ ⤕āĨ‡ā¤ĩ⤞ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ ā¤Ēā¤šā¤˛āĨ‡ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤šāĨˆā¤‚", + "client_cert_title": "SSL ⤕āĨā¤˛ā¤žā¤‡ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤°", + "clockwise": "ā¤Ļ⤕āĨā¤ˇā¤ŋā¤Ŗā¤žā¤ĩ⤰āĨā¤¤", "close": "ā¤Ŧ⤂ā¤Ļ", "collapse": "⤗ā¤ŋ⤰ ā¤œā¤žā¤¨ā¤ž", "collapse_all": "⤏⤭āĨ€ ⤕āĨ‹ ⤏⤂⤕āĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "color": "⤰⤂⤗", "color_theme": "⤰⤂⤗ ā¤ĨāĨ€ā¤Ž", "comment_deleted": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤—ā¤ˆ", "comment_options": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "comments_and_likes": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋā¤¯ā¤žā¤ ⤔⤰ ā¤Ē⤏⤂ā¤Ļ", "comments_are_disabled": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋā¤¯ā¤žā¤ ⤅⤕āĨā¤ˇā¤Ž ā¤šāĨˆā¤‚", + "common_create_new_album": "ā¤¨ā¤¯ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤ā¤", + "common_server_error": "⤕āĨƒā¤Ēā¤¯ā¤ž ⤅ā¤Ē⤍āĨ‡ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ⤕⤍āĨ‡ā¤•āĨā¤ļ⤍ ⤕āĨ€ ā¤œā¤žā¤‚ā¤š ⤕⤰āĨ‡ā¤‚, ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤏⤰āĨā¤ĩ⤰ ā¤Ēā¤šāĨā¤‚ā¤š ⤝āĨ‹ā¤—āĨā¤¯ ā¤šāĨˆ ⤔⤰ ⤐ā¤Ē/⤏⤰āĨā¤ĩ⤰ ⤏⤂⤏āĨā¤•⤰⤪ ⤏⤂⤗⤤ ā¤šāĨˆā¤‚āĨ¤", + "completed": "ā¤ĒāĨā¤°ā¤ž ā¤šāĨ‹ā¤¨ā¤ž", "confirm": "ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ", "confirm_admin_password": "ā¤ā¤Ąā¤Žā¤ŋ⤍ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤕āĨ€ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕⤰āĨ‡ā¤‚", + "confirm_delete_face": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ā¤ā¤¸āĨ‡ā¤Ÿ ⤏āĨ‡ {name} ⤚āĨ‡ā¤šā¤°ā¤ž ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "confirm_delete_shared_link": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "confirm_keep_this_delete_others": "⤇⤏ ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ⤛āĨ‹ā¤Ąā¤ŧ⤕⤰, ⤏āĨā¤ŸāĨˆā¤• ⤕āĨ€ ⤏⤭āĨ€ ⤅⤍āĨā¤¯ ā¤ā¤¸āĨ‡ā¤Ÿ ā¤šā¤Ÿā¤ž ā¤ĻāĨ€ ā¤œā¤žā¤ā¤ā¤—āĨ€āĨ¤ ⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ā¤œā¤žā¤°āĨ€ ā¤°ā¤–ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "confirm_new_pin_code": "ā¤¨ā¤ ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ⤕āĨ€ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕⤰āĨ‡ā¤‚", "confirm_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤕āĨ€ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ⤕āĨ€ā¤œā¤ŋ⤝āĨ‡", + "confirm_tag_face": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ⤇⤏ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ‹ {name} ⤕āĨ‡ ⤰āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ⤟āĨˆā¤— ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "confirm_tag_face_unnamed": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ⤇⤏ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ‹ ⤟āĨˆā¤— ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "connected_device": "⤕⤍āĨ‡ā¤•āĨā¤ŸāĨ‡ā¤Ą ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸", + "connected_to": "⤏āĨ‡ ⤜āĨā¤Ąā¤ŧā¤ž", "contain": "ā¤¸ā¤Žā¤žā¤šā¤ŋ⤤", "context": "⤏⤂ā¤Ļ⤰āĨā¤­", "continue": "ā¤œā¤žā¤°āĨ€", + "control_bottom_app_bar_create_new_album": "ā¤¨ā¤¯ā¤ž ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤ā¤", + "control_bottom_app_bar_delete_from_immich": "Immich ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ā¤‚", + "control_bottom_app_bar_delete_from_local": "ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ā¤‚", + "control_bottom_app_bar_edit_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "control_bottom_app_bar_edit_time": "ā¤¤ā¤žā¤°āĨ€ā¤– ⤔⤰ ā¤¸ā¤Žā¤¯ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "control_bottom_app_bar_share_link": "⤞ā¤ŋ⤂⤕ ā¤ļāĨ‡ā¤¯ā¤° ⤕⤰āĨ‡ā¤‚", + "control_bottom_app_bar_share_to": "ā¤¸ā¤žā¤ā¤ž ⤕⤰āĨ‡ā¤‚", + "control_bottom_app_bar_trash_from_immich": "⤟āĨā¤°āĨˆā¤ļ ā¤ŽāĨ‡ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤ā¤‚", "copied_image_to_clipboard": "⤛ā¤ĩā¤ŋ ⤕āĨ‹ ⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤕āĨ‰ā¤ĒāĨ€ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤žāĨ¤", "copied_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤍⤕⤞!", "copy_error": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤Ēā¤ŋ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", @@ -521,6 +709,7 @@ "covers": "⤆ā¤ĩ⤰⤪", "create": "⤤āĨˆā¤¯ā¤žā¤° ⤕⤰āĨ‡ā¤‚", "create_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤Ŧā¤¨ā¤žā¤“", + "create_album_page_untitled": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤•ā¤šāĨ€ā¤¨", "create_library": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_link": "⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_link_to_share": "ā¤ļāĨ‡ā¤¯ā¤° ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤ā¤‚", @@ -529,39 +718,80 @@ "create_new_person": "ā¤¨ā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ā¤Ŧā¤¨ā¤žā¤ā¤‚", "create_new_person_hint": "⤚⤝⤍ā¤ŋ⤤ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ā¤• ā¤¨ā¤ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤏āĨŒā¤‚ā¤ĒāĨ‡ā¤‚", "create_new_user": "ā¤¨ā¤¯ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ŧā¤¨ā¤žā¤ā¤‚", + "create_shared_album_page_share_add_assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", + "create_shared_album_page_share_select_photos": "ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ⤚āĨā¤¨āĨ‡ā¤‚", + "create_shared_link": "ā¤ļāĨ‡ā¤¯ā¤° ⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤ā¤", + "create_tag": "⤟āĨˆā¤— ā¤Ŧā¤¨ā¤žā¤ā¤", + "create_tag_description": "ā¤ā¤• ā¤¨ā¤¯ā¤ž ⤟āĨˆā¤— ā¤Ŧā¤¨ā¤žā¤ā¤āĨ¤ ⤍āĨ‡ā¤¸āĨā¤ŸāĨ‡ā¤Ą ⤟āĨˆā¤— ⤕āĨ‡ ⤞ā¤ŋā¤, ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤Ģā¤ŧāĨ‰ā¤°ā¤ĩ⤰āĨā¤Ą ⤏āĨā¤˛āĨˆā¤ļ ā¤¸ā¤šā¤ŋ⤤ ⤟āĨˆā¤— ā¤•ā¤ž ā¤ĒāĨ‚ā¤°ā¤ž ā¤Ēā¤Ĩ ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚āĨ¤", "create_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ā¤Ŧā¤¨ā¤žā¤‡ā¤¯āĨ‡", "created": "ā¤Ŧā¤¨ā¤žā¤¯ā¤ž", + "created_at": "ā¤Ŧā¤¨ā¤žā¤¯ā¤ž ā¤Ĩā¤ž", "crop": "ā¤›ā¤žā¤ā¤ŸāĨ‡ā¤‚", + "curated_object_page_title": "⤚āĨ€ā¤œā¤ŧāĨ‡ā¤‚", "current_device": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤉ā¤Ē⤕⤰⤪", + "current_pin_code": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą", + "current_server_address": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤏⤰āĨā¤ĩ⤰ ā¤Ēā¤¤ā¤ž", "custom_locale": "⤕⤏āĨā¤Ÿā¤Ž ⤞āĨ‹ā¤•āĨ‡ā¤˛", "custom_locale_description": "ā¤­ā¤žā¤ˇā¤ž ⤔⤰ ⤕āĨā¤ˇāĨ‡ā¤¤āĨā¤° ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ⤔⤰ ⤏⤂⤖āĨā¤¯ā¤žā¤ā¤ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "custom_url": "⤕⤏āĨā¤Ÿā¤Ž URL", + "daily_title_text_date": "⤈, ā¤ā¤Žā¤ā¤Žā¤ā¤Ž ā¤ĄāĨ€ā¤ĄāĨ€", + "daily_title_text_date_year": "⤈, ā¤ā¤Žā¤ā¤Žā¤ā¤Ž ā¤Ļā¤ŋ⤍, ā¤ĩ⤰āĨā¤ˇ", "dark": "ā¤Ąā¤žā¤°āĨā¤•", + "dark_theme": "ā¤Ąā¤žā¤°āĨā¤• ā¤ĨāĨ€ā¤Ž ⤟āĨ‰ā¤—⤞ ⤕⤰āĨ‡ā¤‚", "date_after": "⤇⤏⤕āĨ‡ ā¤Ŧā¤žā¤Ļ ⤕āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", "date_and_time": "⤤ā¤ŋā¤Ĩā¤ŋ ⤔⤰ ā¤¸ā¤Žā¤¯", "date_before": "ā¤Ēā¤šā¤˛āĨ‡ ⤕āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "date_format": "⤈, ā¤ā¤˛ā¤ā¤˛ā¤ā¤˛ ā¤ĄāĨ€, ā¤ĩā¤žā¤ˆ â€ĸ ā¤ā¤š:ā¤ā¤Žā¤ā¤Ž ā¤", "date_of_birth_saved": "⤜⤍āĨā¤Žā¤¤ā¤ŋā¤Ĩā¤ŋ ⤏ā¤Ģā¤˛ā¤¤ā¤žā¤ĒāĨ‚⤰āĨā¤ĩ⤕ ā¤¸ā¤šāĨ‡ā¤œāĨ€ ā¤—ā¤ˆ", "date_range": "⤤ā¤ŋā¤Ĩā¤ŋ ⤏āĨ€ā¤Žā¤ž", "day": "ā¤Ļā¤ŋ⤍", "deduplicate_all": "⤏⤭āĨ€ ⤕āĨ‹ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕⤰āĨ‡ā¤‚", + "deduplication_criteria_1": "⤛ā¤ĩā¤ŋ ā¤•ā¤ž ā¤†ā¤•ā¤žā¤° ā¤Ŧā¤žā¤‡ā¤ŸāĨā¤¸ ā¤ŽāĨ‡ā¤‚", + "deduplication_criteria_2": "EXIF ā¤ĄāĨ‡ā¤Ÿā¤ž ⤕āĨ€ ⤏⤂⤖āĨā¤¯ā¤ž", + "deduplication_info": "ā¤ĄāĨā¤ĒāĨā¤˛āĨ€ā¤•āĨ‡ā¤ļ⤍ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ€ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€", + "deduplication_info_description": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ⤏āĨā¤ĩā¤šā¤žā¤˛ā¤ŋ⤤ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤ĒāĨ‚⤰āĨā¤ĩ-⤚⤝⤍ ⤕⤰⤍āĨ‡ ⤔⤰ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤ĨāĨ‹ā¤• ā¤ŽāĨ‡ā¤‚ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤, ā¤šā¤Ž ⤍ā¤ŋā¤ŽāĨā¤¨ ā¤Ē⤰ ⤧āĨā¤¯ā¤žā¤¨ ā¤ĻāĨ‡ā¤¤āĨ‡ ā¤šāĨˆā¤‚:", "default_locale": "ā¤Ąā¤ŋā¤Ģā¤ŧāĨ‰ā¤˛āĨā¤Ÿ ⤏āĨā¤Ĩā¤žā¤¨", "default_locale_description": "⤅ā¤Ē⤍āĨ‡ ā¤ŦāĨā¤°ā¤žā¤‰ā¤œā¤ŧ⤰ ⤏āĨā¤Ĩā¤žā¤¨ ⤕āĨ‡ ā¤†ā¤§ā¤žā¤° ā¤Ē⤰ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ⤔⤰ ⤏⤂⤖āĨā¤¯ā¤žā¤ā¤ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "delete": "ā¤šā¤Ÿā¤žā¤ā¤", + "delete_action_confirmation_message": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤†ā¤‡ā¤Ÿā¤Ž ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ā¤¯ā¤š ā¤•ā¤žā¤°āĨā¤°ā¤ĩā¤žā¤ˆ ā¤†ā¤‡ā¤Ÿā¤Ž ⤕āĨ‹ ⤏⤰āĨā¤ĩ⤰ ⤕āĨ€ ⤟āĨā¤°āĨˆā¤ļ ā¤ŽāĨ‡ā¤‚ ⤞āĨ‡ ā¤œā¤žā¤ā¤—āĨ€ ⤔⤰ ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤ĒāĨā¤ˇāĨā¤Ÿā¤ŋ ā¤Žā¤žā¤‚ā¤—āĨ‡ā¤—āĨ€", + "delete_action_prompt": "{count} ā¤šā¤Ÿā¤žā¤ ā¤—ā¤", "delete_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤žā¤ā¤", "delete_api_key_prompt": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤏ ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨā¤‚ā¤œāĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "delete_dialog_alert": "⤝āĨ‡ ā¤†ā¤‡ā¤Ÿā¤Ž Immich ⤔⤰ ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤ ā¤œā¤žā¤ā¤‚ā¤—āĨ‡", + "delete_dialog_alert_local": "⤝āĨ‡ ā¤†ā¤‡ā¤Ÿā¤Ž ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤ ā¤œā¤žā¤ā¤‚ā¤—āĨ‡, ⤞āĨ‡ā¤•ā¤ŋ⤍ ā¤Ģā¤ŋ⤰ ⤭āĨ€ Immich ⤏⤰āĨā¤ĩ⤰ ā¤Ē⤰ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤°ā¤šāĨ‡ā¤‚⤗āĨ‡", + "delete_dialog_alert_local_non_backed_up": "⤕āĨā¤› ā¤†ā¤‡ā¤Ÿā¤Ž ā¤•ā¤ž Immich ā¤ŽāĨ‡ā¤‚ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤¨ā¤šāĨ€ā¤‚ ⤞ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ā¤šāĨˆ ⤔⤰ ⤉⤍āĨā¤šāĨ‡ā¤‚ ⤆ā¤Ē⤕āĨ‡ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤œā¤žā¤ā¤—ā¤ž", + "delete_dialog_alert_remote": "⤝āĨ‡ ā¤†ā¤‡ā¤Ÿā¤Ž Immich ⤏⤰āĨā¤ĩ⤰ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤ ā¤œā¤žā¤ā¤‚ā¤—āĨ‡", + "delete_dialog_ok_force": "ā¤Ģā¤ŋ⤰ ⤭āĨ€ ā¤šā¤Ÿā¤žā¤ā¤‚", + "delete_dialog_title": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ā¤", "delete_duplicates_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ ⤇⤍ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕āĨ‹ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", + "delete_face": "⤚āĨ‡ā¤šā¤°ā¤ž ā¤šā¤Ÿā¤žā¤ā¤‚", "delete_key": "⤕āĨā¤‚ā¤œāĨ€ ā¤šā¤Ÿā¤žā¤ā¤", "delete_library": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ā¤šā¤Ÿā¤žā¤ā¤", "delete_link": "⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤žā¤ā¤", + "delete_local_action_prompt": "{count} ⤏āĨā¤Ĩā¤žā¤¨āĨ€ā¤¯ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", + "delete_local_dialog_ok_backed_up_only": "⤕āĨ‡ā¤ĩ⤞ ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤Ÿā¤žā¤ā¤‚", + "delete_local_dialog_ok_force": "ā¤Ģā¤ŋ⤰ ⤭āĨ€ ā¤šā¤Ÿā¤žā¤ā¤‚", + "delete_others": "⤅⤍āĨā¤¯ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤ā¤", + "delete_permanently": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ā¤", + "delete_permanently_action_prompt": "{count} ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤ ā¤—ā¤", "delete_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤ā¤‚", "delete_shared_link_dialog_title": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤ ā¤—ā¤ ⤞ā¤ŋ⤂⤕ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤ā¤‚", + "delete_tag": "⤟āĨˆā¤— ā¤šā¤Ÿā¤žā¤ā¤‚", + "delete_tag_confirmation_prompt": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē ā¤ĩā¤žā¤•ā¤ˆ {tagName} ⤟āĨˆā¤— ā¤šā¤Ÿā¤žā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚?", "delete_user": "⤉ā¤Ē⤭āĨ‹ā¤•āĨā¤¤ā¤ž ā¤Žā¤ŋā¤Ÿā¤žā¤¯āĨ‡ā¤‚", "deleted_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", + "deletes_missing_assets": "ā¤Ąā¤ŋ⤏āĨā¤• ⤏āĨ‡ ā¤—ā¤žā¤¯ā¤Ŧ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¤ā¤ž ā¤šāĨˆ", "description": "ā¤ĩ⤰āĨā¤Ŗā¤¨", + "description_input_hint_text": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚..āĨ¤", + "description_input_submit_error": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰⤤āĨ‡ ā¤¸ā¤Žā¤¯ ⤤āĨā¤°āĨā¤Ÿā¤ŋ ā¤šāĨā¤ˆ, ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤞āĨ‰ā¤— ā¤ĻāĨ‡ā¤–āĨ‡ā¤‚", + "deselect_all": "⤏ā¤Ŧ⤕āĨ‹ ā¤…ā¤šā¤¯ā¤¨ā¤ŋ⤤ ⤕⤰āĨ‹", "details": "ā¤ĩā¤ŋā¤ĩ⤰⤪", "direction": "ā¤Ļā¤ŋā¤ļā¤ž", "disabled": "⤅⤕āĨā¤ˇā¤Ž", "disallow_edits": "⤏⤂ā¤Ēā¤žā¤Ļ⤍āĨ‹ā¤‚ ⤕āĨ€ ⤅⤍āĨā¤Žā¤¤ā¤ŋ ⤍ ā¤ĻāĨ‡ā¤‚", + "discord": "ā¤Ąā¤ŋ⤏⤕āĨ‰ā¤°āĨā¤Ą", "discover": "⤖āĨ‹ā¤œāĨ‡ā¤‚", + "discovered_devices": "⤖āĨ‹ā¤œāĨ‡ ā¤—ā¤ ⤉ā¤Ē⤕⤰⤪", "dismiss_all_errors": "⤏⤭āĨ€ ⤤āĨā¤°āĨā¤Ÿā¤ŋā¤¯ā¤žā¤ ⤖ā¤ŧā¤žā¤°ā¤ŋ⤜ ⤕⤰āĨ‡ā¤‚", "dismiss_error": "⤤āĨā¤°āĨā¤Ÿā¤ŋ ⤖ā¤ŧā¤žā¤°ā¤ŋ⤜ ⤕⤰āĨ‡ā¤‚", "display_options": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ⤚āĨā¤¨ā¤žā¤ĩ", @@ -569,14 +799,18 @@ "display_original_photos": "ā¤ŽāĨ‚⤞ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "display_original_photos_setting_description": "⤕ā¤ŋ⤏āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‹ ā¤ĻāĨ‡ā¤–⤤āĨ‡ ā¤¸ā¤Žā¤¯ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤕āĨ‡ ā¤Ŧā¤œā¤žā¤¯ ā¤ŽāĨ‚⤞ ⤤⤏āĨā¤ĩāĨ€ā¤° ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļā¤ŋ⤤ ā¤•ā¤°ā¤¨ā¤ž ā¤Ē⤏⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚ ⤜ā¤Ŧ ā¤ŽāĨ‚⤞ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤ĩāĨ‡ā¤Ŧ-⤏⤂⤗⤤ ā¤šāĨ‹āĨ¤", "do_not_show_again": "⤇⤏ ⤏⤂ā¤ĻāĨ‡ā¤ļ ⤕āĨ‹ ā¤ĻāĨā¤Ŧā¤žā¤°ā¤ž ā¤Žā¤¤ ā¤Ļā¤ŋā¤–ā¤žā¤¨ā¤ž", + "documentation": "ā¤ĒāĨā¤°ā¤˛āĨ‡ā¤–⤍", "done": "⤠āĨ€ā¤• ā¤šāĨˆ", "download": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰āĨ‡ā¤‚", + "download_action_prompt": "{count} ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤‚ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šāĨ€ ā¤šāĨˆā¤‚", "download_canceled": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤰ā¤ĻāĨā¤Ļ ⤕⤰ ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", "download_complete": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĒāĨ‚ā¤°ā¤ž", "download_enqueue": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤¤ā¤žā¤° ā¤ŽāĨ‡ā¤‚ ā¤šāĨˆ", "download_error": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "download_failed": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĩā¤ŋā¤Ģ⤞", "download_finished": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤", + "download_include_embedded_motion_videos": "ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤ĄāĨ‡ā¤Ą ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹", + "download_include_embedded_motion_videos_description": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤Ģā¤ŧāĨ‹ā¤ŸāĨ‹ ā¤ŽāĨ‡ā¤‚ ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ą ⤕ā¤ŋā¤ ā¤—ā¤ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤕āĨ‹ ā¤ā¤• ⤅⤞⤗ ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤕āĨ‡ ⤰āĨ‚ā¤Ē ā¤ŽāĨ‡ā¤‚ ā¤ļā¤žā¤Žā¤ŋ⤞ ⤕⤰āĨ‡ā¤‚", "download_notfound": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋā¤˛ā¤ž", "download_paused": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏āĨā¤Ĩ⤗ā¤ŋ⤤", "download_settings": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤°ā¤¨ā¤ž", @@ -586,6 +820,7 @@ "download_sucess_android": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž DCIM/Immich ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤šāĨ‹ ā¤—ā¤¯ā¤ž ā¤šāĨˆ", "download_waiting_to_retry": "ā¤ĒāĨā¤¨ā¤ƒ ā¤ĒāĨā¤°ā¤¯ā¤žā¤¸ ⤕⤰⤍āĨ‡ ā¤•ā¤ž ā¤‡ā¤‚ā¤¤ā¤œā¤žā¤° ⤕⤰ ā¤°ā¤šā¤ž ā¤šāĨˆ", "downloading": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą", + "downloading_asset_filename": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ {filename} ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šāĨ€ ā¤šāĨˆ", "downloading_media": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤šāĨ‹ ā¤°ā¤šā¤ž ā¤šāĨˆ", "drop_files_to_upload": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ģā¤ŧā¤žā¤‡ā¤˛āĨ‡ā¤‚ ā¤•ā¤šāĨ€ā¤‚ ⤭āĨ€ ⤛āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚", "duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ", @@ -594,8 +829,12 @@ "edit": "⤏⤂ā¤Ēā¤žā¤Ļ⤍ ā¤•ā¤°ā¤¨ā¤ž", "edit_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_avatar": "⤅ā¤ĩā¤¤ā¤žā¤° ⤕āĨ‹ ā¤ā¤Ąā¤ŋ⤟ ⤕⤰āĨ‡ā¤‚", + "edit_birthday": "⤜⤍āĨā¤Žā¤Ļā¤ŋ⤍ ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "edit_date": "⤏⤂ā¤Ēā¤žā¤Ļ⤍ ⤕āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", "edit_date_and_time": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ⤔⤰ ā¤¸ā¤Žā¤¯ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "edit_date_and_time_action_prompt": "{count} ā¤¤ā¤žā¤°āĨ€ā¤– ⤔⤰ ā¤¸ā¤Žā¤¯ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕ā¤ŋā¤ ā¤—ā¤", + "edit_description": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ā¤ĩ⤰āĨā¤Ŗā¤¨", + "edit_description_prompt": "⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ā¤• ā¤¨ā¤¯ā¤ž ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤚āĨā¤¨āĨ‡ā¤‚:", "edit_exclusion_pattern": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_faces": "⤚āĨ‡ā¤šā¤°āĨ‡ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", @@ -603,27 +842,47 @@ "edit_key": "⤕āĨā¤‚ā¤œāĨ€ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_link": "⤞ā¤ŋ⤂⤕ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "edit_location_action_prompt": "{count} ⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕ā¤ŋā¤ ā¤—ā¤", + "edit_location_dialog_title": "⤏āĨā¤Ĩā¤žā¤¨", "edit_name": "ā¤¨ā¤žā¤Ž ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", + "edit_tag": "⤟āĨˆā¤— ā¤Ŧā¤Ļ⤞āĨ‡ā¤‚", "edit_title": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚", "edit_user": "⤝āĨ‚ā¤œā¤° ⤕āĨ‹ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰āĨ‹", "edited": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤", + "editor": "⤏⤂ā¤Ēā¤žā¤Ļ⤕", + "editor_close_without_save_prompt": "ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤¨ ā¤¸ā¤šāĨ‡ā¤œāĨ‡ ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤žā¤ā¤ā¤—āĨ‡", + "editor_close_without_save_title": "⤏⤂ā¤Ēā¤žā¤Ļ⤕ ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‡ā¤‚?", + "editor_crop_tool_h2_aspect_ratios": "⤆⤏āĨā¤ĒāĨ‡ā¤•āĨā¤Ÿ ⤅⤍āĨā¤Ēā¤žā¤¤", + "editor_crop_tool_h2_rotation": "⤰āĨ‹ā¤ŸāĨ‡ā¤ļ⤍", "email": "ā¤ˆā¤ŽāĨ‡ā¤˛", + "email_notifications": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤", + "empty_folder": "ā¤¯ā¤š ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ā¤–ā¤žā¤˛āĨ€ ā¤šāĨˆ", "empty_trash": "⤕āĨ‚ā¤Ąā¤ŧāĨ‡ā¤Ļā¤žā¤¨ ā¤–ā¤žā¤˛āĨ€ ⤕⤰āĨ‡ā¤‚", "empty_trash_confirmation": "⤕āĨā¤¯ā¤ž ⤆ā¤Ē⤕āĨ‹ ⤝⤕āĨ€ā¤¨ ā¤šāĨˆ ⤕ā¤ŋ ⤆ā¤Ē ā¤•ā¤šā¤°ā¤ž ā¤–ā¤žā¤˛āĨ€ ā¤•ā¤°ā¤¨ā¤ž ā¤šā¤žā¤šā¤¤āĨ‡ ā¤šāĨˆā¤‚? ā¤¯ā¤š ā¤‡ā¤Žā¤ŋ⤚ ⤏āĨ‡ ⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤•ā¤šā¤°ā¤ž ā¤ŽāĨ‡ā¤‚ ⤏⤭āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤šā¤Ÿā¤ž ā¤ĻāĨ‡ā¤—ā¤žāĨ¤\n⤆ā¤Ē ⤇⤏ ā¤•ā¤žā¤°āĨā¤°ā¤ĩā¤žā¤ˆ ⤕āĨ‹ ā¤¨ā¤šāĨ€ā¤‚ ⤰āĨ‹ā¤• ⤏⤕⤤āĨ‡!", "enable": "⤏⤕āĨā¤ˇā¤Ž", + "enable_backup": "ā¤ŦāĨˆā¤•⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ⤕⤰āĨ‡ā¤‚", + "enable_biometric_auth_description": "ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ā¤Ļ⤰āĨā¤œ ⤕⤰āĨ‡ā¤‚", "enabled": "⤏⤕āĨā¤°ā¤ŋ⤝", "end_date": "⤅⤂⤤ā¤ŋā¤Ž ⤤ā¤ŋā¤Ĩā¤ŋ", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "ā¤•ā¤¤ā¤žā¤° ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧā¤ž ā¤—ā¤¯ā¤ž", + "enter_wifi_name": "Wi-Fi ā¤•ā¤ž ā¤¨ā¤žā¤Ž ⤞ā¤ŋ⤖āĨ‡ā¤‚", + "enter_your_pin_code": "⤅ā¤Ēā¤¨ā¤ž ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ā¤Ąā¤žā¤˛āĨ‡ā¤‚", + "enter_your_pin_code_subtitle": "⤞āĨ‰ā¤• ā¤Ģā¤ŧāĨ‹ā¤˛āĨā¤Ąā¤° ⤖āĨ‹ā¤˛ā¤¨āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "error": "⤗⤞⤤āĨ€", + "error_change_sort_album": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤•ā¤ž ⤕āĨā¤°ā¤Ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤏ā¤Ģ⤞ ā¤°ā¤šā¤ž", + "error_delete_face": "ā¤ā¤¸āĨ‡ā¤Ÿ ⤏āĨ‡ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ ā¤šāĨā¤ˆ", "error_loading_image": "⤛ā¤ĩā¤ŋ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", "error_saving_image": "⤤āĨā¤°āĨā¤Ÿā¤ŋ: {error}", + "error_tag_face_bounding_box": "⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨ‹ ⤟āĨˆā¤— ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ – ā¤Ŧā¤žā¤‰ā¤‚ā¤Ąā¤ŋ⤂⤗ ā¤ŦāĨ‰ā¤•āĨā¤¸ ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤žā¤‚ā¤• ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕āĨ‡", "error_title": "⤤āĨā¤°āĨā¤Ÿā¤ŋ - ⤕āĨā¤› ⤗⤞⤤ ā¤šāĨ‹ ā¤—ā¤¯ā¤ž", "errors": { "cannot_navigate_next_asset": "⤅⤗⤞āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ē⤰ ⤍āĨ‡ā¤ĩā¤ŋ⤗āĨ‡ā¤Ÿ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", "cannot_navigate_previous_asset": "ā¤Ēā¤ŋ⤛⤞āĨ€ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤Ē⤰ ⤍āĨ‡ā¤ĩā¤ŋ⤗āĨ‡ā¤Ÿ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", "cant_apply_changes": "ā¤Ē⤰ā¤ŋā¤ĩ⤰āĨā¤¤ā¤¨ ā¤˛ā¤žā¤—āĨ‚ ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡", + "cant_change_activity": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ ⤕āĨ‹ {enabled, select, true {⤅⤕āĨā¤ˇā¤Ž} other {⤏⤕āĨā¤ˇā¤Ž}} ā¤¨ā¤šāĨ€ā¤‚ ⤕⤰ ⤏⤕⤤āĨ‡", "cant_change_asset_favorite": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤Ŧā¤Ļā¤˛ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", + "cant_change_metadata_assets_count": "{count, plural, one {# ā¤†ā¤‡ā¤Ÿā¤Ž} other {# ā¤†ā¤‡ā¤Ÿā¤ŽāĨā¤¸}} ⤕āĨ‡ ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤕āĨ‹ ā¤Ŧā¤Ļ⤞ ā¤¨ā¤šāĨ€ā¤‚ ⤏⤕⤤āĨ‡", "cant_get_faces": "⤚āĨ‡ā¤šā¤°āĨ‡ ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋ⤞ ⤏⤕āĨ‡", "cant_get_number_of_comments": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤Žā¤ŋ⤞ ⤏⤕āĨ€", "cant_search_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤖āĨ‹ā¤œā¤ž ā¤¨ā¤šāĨ€ā¤‚ ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž", @@ -631,6 +890,7 @@ "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": "⤏⤭āĨ€ ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ⤚⤝⤍ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤤āĨā¤°āĨā¤Ÿā¤ŋ", @@ -639,14 +899,18 @@ "failed_to_create_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤Ŧā¤¨ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_edit_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_get_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ā¤Ēā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", + "failed_to_keep_this_delete_others": "⤇⤏ ā¤†ā¤‡ā¤Ÿā¤Ž ⤕āĨ‹ ⤰⤖⤍āĨ‡ ⤔⤰ ā¤Ŧā¤žā¤•āĨ€ ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ⤅⤏ā¤Ģ⤞ ā¤°ā¤šā¤ž", "failed_to_load_asset": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_load_assets": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋā¤¯ā¤žā¤ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", + "failed_to_load_notifications": "⤏āĨ‚ā¤šā¤¨ā¤žā¤ā¤ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_load_people": "⤞āĨ‹ā¤—āĨ‹ā¤‚ ⤕āĨ‹ ⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_remove_product_key": "⤉⤤āĨā¤Ēā¤žā¤Ļ ⤕āĨā¤‚ā¤œāĨ€ ⤍ā¤ŋā¤•ā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_stack_assets": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ĸāĨ‡ā¤° ā¤˛ā¤—ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "failed_to_unstack_assets": "ā¤Ē⤰ā¤ŋ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ā¤•ā¤ž ā¤ĸāĨ‡ā¤° ⤖āĨ‹ā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", + "failed_to_update_notification_status": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤕āĨ€ ⤏āĨā¤Ĩā¤ŋ⤤ā¤ŋ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "import_path_already_exists": "ā¤¯ā¤š ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ā¤Ēā¤šā¤˛āĨ‡ ⤏āĨ‡ ā¤ŽāĨŒā¤œāĨ‚ā¤Ļ ā¤šāĨˆāĨ¤", "incorrect_email_or_password": "⤗⤞⤤ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "paths_validation_failed": "{paths, plural, one {# ā¤Ēā¤Ĩ} other {# ā¤Ēā¤Ĩ}} ⤏⤤āĨā¤¯ā¤žā¤Ē⤍ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞ ā¤°ā¤šāĨ‡", "profile_picture_transparent_pixels": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤°āĨ‹ā¤‚ ā¤ŽāĨ‡ā¤‚ ā¤Ēā¤žā¤°ā¤Ļ⤰āĨā¤ļāĨ€ ā¤Ēā¤ŋ⤕āĨā¤¸āĨ‡ā¤˛ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨ‹ ⤏⤕⤤āĨ‡āĨ¤", "quota_higher_than_disk_size": "⤆ā¤Ē⤍āĨ‡ ā¤Ąā¤ŋ⤏āĨā¤• ā¤†ā¤•ā¤žā¤° ⤏āĨ‡ ⤅⤧ā¤ŋ⤕ ⤕āĨ‹ā¤Ÿā¤ž ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ⤕ā¤ŋā¤¯ā¤ž ā¤šāĨˆ", "unable_to_add_album_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤ŽāĨ‡ā¤‚ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -655,11 +919,16 @@ "unable_to_add_exclusion_pattern": "ā¤Ŧā¤šā¤ŋ⤎āĨā¤•⤰⤪ ā¤ĒāĨˆā¤Ÿā¤°āĨā¤¨ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Ēā¤Ĩ ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_add_partners": "ā¤¸ā¤žā¤āĨ‡ā¤Ļā¤žā¤° ā¤Ąā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_add_remove_archive": "{archived, select, true {ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤š ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ} other {ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ⤏⤂⤗āĨā¤°ā¤š ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ}}", + "unable_to_add_remove_favorites": "{favorite, select, true {ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤ŽāĨ‡ā¤‚ ⤜āĨ‹ā¤Ąā¤ŧ⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ} other {ā¤ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‹ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ}}", + "unable_to_archive_unarchive": "{archived, select, true {⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ} other {⤏⤂⤗āĨā¤°ā¤š ⤏āĨ‡ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ}}", "unable_to_change_album_user_role": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ€ ⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_date": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_change_description": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_favorite": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ē⤏⤂ā¤ĻāĨ€ā¤Ļā¤ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_change_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_change_visibility": "{count, plural, one {# ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ} other {# ⤞āĨ‹ā¤—}} ⤕āĨ€ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž ā¤Ŧā¤Ļ⤞⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_complete_oauth_login": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_connect": "⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_copy_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤Ē⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤¨ā¤šāĨ€ā¤‚ ⤕ā¤ŋā¤¯ā¤ž ā¤œā¤ž ā¤¸ā¤•ā¤¤ā¤ž, ⤏āĨā¤¨ā¤ŋā¤ļāĨā¤šā¤ŋ⤤ ⤕⤰āĨ‡ā¤‚ ⤕ā¤ŋ ⤆ā¤Ē https ⤕āĨ‡ ā¤Žā¤žā¤§āĨā¤¯ā¤Ž ⤏āĨ‡ ā¤ĒāĨ‡ā¤œ ⤤⤕ ā¤Ēā¤šāĨā¤‚ā¤š ā¤°ā¤šāĨ‡ ā¤šāĨˆā¤‚", @@ -683,11 +952,13 @@ "unable_to_get_comments_number": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_get_shared_link": "ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤ĩā¤ŋā¤Ģ⤞", "unable_to_hide_person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤛āĨā¤Ēā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_link_motion_video": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤞ā¤ŋ⤂⤕ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_link_oauth_account": "OAuth ā¤–ā¤žā¤¤ā¤ž ⤞ā¤ŋ⤂⤕ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_log_out_all_devices": "⤏⤭āĨ€ ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸āĨ‹ā¤‚ ⤕āĨ‹ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_log_out_device": "ā¤Ąā¤ŋā¤ĩā¤žā¤‡ā¤¸ ⤞āĨ‰ā¤— ā¤†ā¤‰ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_login_with_oauth": "OAuth ⤏āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_play_video": "ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ā¤šā¤˛ā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_reassign_assets_existing_person": "{ name, select, null {ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ ā¤ā¤• ā¤ŽāĨŒā¤œāĨ‚ā¤Ļā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤ƒ ā¤…ā¤¸ā¤žā¤‡ā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ} other {ā¤ā¤¸āĨ‡ā¤ŸāĨā¤¸ ⤕āĨ‹ {name} ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤ƒ ā¤…ā¤¸ā¤žā¤‡ā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ} }", "unable_to_reassign_assets_new_person": "⤕ā¤ŋ⤏āĨ€ ⤍⤝āĨ‡ ā¤ĩāĨā¤¯ā¤•āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ā¤ĒāĨā¤¨ā¤ƒ ⤏āĨŒā¤‚ā¤Ē⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_refresh_user": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤ž ⤕āĨ‹ ā¤¤ā¤žā¤œā¤ŧā¤ž ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_remove_album_users": "⤉ā¤Ē⤝āĨ‹ā¤—⤕⤰āĨā¤¤ā¤žā¤“⤂ ⤕āĨ‹ ā¤ā¤˛āĨā¤Ŧā¤Ž ⤏āĨ‡ ⤍ā¤ŋā¤•ā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -697,6 +968,7 @@ "unable_to_remove_partner": "ā¤Ēā¤žā¤°āĨā¤Ÿā¤¨ā¤° ⤕āĨ‹ ā¤šā¤Ÿā¤žā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_remove_reaction": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤍ā¤ŋā¤•ā¤žā¤˛ā¤¨āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_reset_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_reset_pin_code": "ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_resolve_duplicate": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤•ā¤ž ā¤¸ā¤Žā¤žā¤§ā¤žā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_restore_assets": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ⤝āĨ‹ā¤‚ ⤕āĨ‹ ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_restore_trash": "ā¤•ā¤šā¤°ā¤ž ā¤ĒāĨā¤¨ā¤°āĨā¤¸āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -714,6 +986,7 @@ "unable_to_submit_job": "ā¤•ā¤žā¤°āĨā¤¯ ā¤ĒāĨā¤°ā¤¸āĨā¤¤āĨā¤¤ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_trash_asset": "⤏⤂ā¤Ē⤤āĨā¤¤ā¤ŋ ⤕āĨ‹ ⤟āĨā¤°āĨˆā¤ļ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_unlink_account": "ā¤–ā¤žā¤¤ā¤ž ⤅⤍⤞ā¤ŋ⤂⤕ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", + "unable_to_unlink_motion_video": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĩāĨ€ā¤Ąā¤ŋ⤝āĨ‹ ⤅⤍⤞ā¤ŋ⤂⤕ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_update_album_cover": "ā¤ā¤˛āĨā¤Ŧā¤Ž ⤕ā¤ĩ⤰ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_update_album_info": "ā¤ā¤˛āĨā¤Ŧā¤Ž ā¤œā¤žā¤¨ā¤•ā¤žā¤°āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", "unable_to_update_library": "ā¤˛ā¤žā¤‡ā¤ŦāĨā¤°āĨ‡ā¤°āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ", @@ -724,6 +997,7 @@ "unable_to_upload_file": "ā¤Ģā¤žā¤‡ā¤˛ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤍āĨ‡ ā¤ŽāĨ‡ā¤‚ ā¤…ā¤¸ā¤Žā¤°āĨā¤Ĩ" }, "exif": "ā¤ā¤•āĨā¤¸ā¤ŋā¤Ģ", + "exif_bottom_sheet_description": "ā¤ĩā¤ŋā¤ĩ⤰⤪ ⤜āĨ‹ā¤Ąā¤ŧāĨ‡ā¤‚..āĨ¤", "exif_bottom_sheet_person_add_person": "ā¤¨ā¤žā¤Ž ā¤Ąā¤žā¤˛āĨ‡ā¤‚", "exit_slideshow": "⤏āĨā¤˛ā¤žā¤‡ā¤Ą ā¤ļāĨ‹ ⤏āĨ‡ ā¤Ŧā¤žā¤šā¤° ⤍ā¤ŋ⤕⤞āĨ‡ā¤‚", "expand_all": "⤏⤭āĨ€ ā¤•ā¤ž ā¤ĩā¤ŋ⤏āĨā¤¤ā¤žā¤°", @@ -814,7 +1088,6 @@ "library_options": "ā¤ĒāĨā¤¸āĨā¤¤ā¤•ā¤žā¤˛ā¤¯ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "light": "⤰āĨ‹ā¤ļ⤍āĨ€", "like_deleted": "⤜āĨˆā¤¸āĨ‡ ā¤šā¤Ÿā¤ž ā¤Ļā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž", - "link_options": "⤞ā¤ŋ⤂⤕ ā¤ĩā¤ŋ⤕⤞āĨā¤Ē", "link_to_oauth": "OAuth ⤏āĨ‡ ⤞ā¤ŋ⤂⤕ ⤕⤰āĨ‡ā¤‚", "linked_oauth_account": "⤞ā¤ŋ⤂⤕ ⤕ā¤ŋā¤¯ā¤ž ā¤—ā¤¯ā¤ž OAuth ā¤–ā¤žā¤¤ā¤ž", "list": "⤏āĨ‚ā¤šāĨ€", @@ -1273,6 +1546,7 @@ "year": "ā¤ĩ⤰āĨā¤ˇ", "yes": "ā¤šā¤žā¤", "you_dont_have_any_shared_links": "⤆ā¤Ē⤕āĨ‡ ā¤Ēā¤žā¤¸ ⤕āĨ‹ā¤ˆ ā¤¸ā¤žā¤ā¤ž ⤞ā¤ŋ⤂⤕ ā¤¨ā¤šāĨ€ā¤‚ ā¤šāĨˆ", - "your_wifi_name": "Your WiFi name", - "zoom_image": "⤛ā¤ĩā¤ŋ ⤜ā¤ŧāĨ‚ā¤Ž ⤕⤰āĨ‡ā¤‚" + "your_wifi_name": "⤆ā¤Ē⤕āĨ‡ ā¤ĩā¤žā¤ˆā¤Ģā¤žā¤ˆ ā¤•ā¤ž ā¤¨ā¤žā¤Ž", + "zoom_image": "⤛ā¤ĩā¤ŋ ⤜ā¤ŧāĨ‚ā¤Ž ⤕⤰āĨ‡ā¤‚", + "zoom_to_bounds": "⤏āĨ€ā¤Žā¤ž ⤤⤕ āĨ›āĨ‚ā¤Ž ⤕⤰āĨ‡ā¤‚" } diff --git a/i18n/hr.json b/i18n/hr.json index d2edfc9a8e..32331976c1 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -14,6 +14,7 @@ "add_a_location": "Dodaj lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rođendan", "add_endpoint": "Dodaj krajnju točnu", "add_exclusion_pattern": "Dodaj uzorak izuzimanja", "add_import_path": "Dodaj import folder", @@ -27,6 +28,9 @@ "add_to_album": "Dodaj u album", "add_to_album_bottom_sheet_added": "Dodano u {album}", "add_to_album_bottom_sheet_already_exists": "Već u {album}", + "add_to_album_toggle": "Uključi/isključi odabir za {album}", + "add_to_albums": "Dodaj u albume", + "add_to_albums_count": "Dodaj u albume ({count})", "add_to_shared_album": "Dodaj u dijeljeni album", "add_url": "Dodaj URL", "added_to_archive": "Dodano u arhivu", @@ -34,6 +38,7 @@ "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podrÅžano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Da biste zanemarili sve datoteke koje zavrÅĄavaju na \".tif\", koristite \"**/*.tif\". Da biste zanemarili apsolutni put, koristite \"/path/to/ignore/**\".", + "admin_user": "Administrator", "asset_offline_description": "Ovo sredstvo vanjske knjiÅžnice viÅĄe nije pronađeno na disku i premjeÅĄteno je u smeće. Ako je datoteka premjeÅĄtena unutar biblioteke, provjerite svoju vremensku traku za novo odgovarajuće sredstvo. Da biste vratili ovo sredstvo, provjerite moÅže li Immich pristupiti donjoj stazi datoteke i skenirajte biblioteku.", "authentication_settings": "Postavke autentikacije", "authentication_settings_description": "Uredi lozinku, OAuth, i druge postavke autentikacije", @@ -43,6 +48,13 @@ "backup_database": "Kreiraj sigurnosnu kopiju baze podataka", "backup_database_enable_description": "Omogućite sigurnosne kopije baze podataka", "backup_keep_last_amount": "Količina prethodnih sigurnosnih kopija za čuvanje", + "backup_onboarding_1_description": "kopija izvan lokacije u oblaku ili na drugoj fizičkoj lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različitim uređajima. To uključuje glavne datoteke i sigurnosnu kopiju tih datoteka lokalno.", + "backup_onboarding_3_description": "ukupne kopije vaÅĄih podataka, uključujući izvorne datoteke. To uključuje 1 kopiju izvan lokacije i 2 lokalne kopije.", + "backup_onboarding_description": "Preporučuje se 3-2-1 strategija sigurnosnog kopiranja za zaÅĄtitu vaÅĄih podataka. Trebali biste čuvati kopije svojih prenesenih fotografija/videozapisa kao i Immich bazu podataka za sveobuhvatno rjeÅĄenje sigurnosne kopije.", + "backup_onboarding_footer": "Za viÅĄe informacija o sigurnosnom kopiranju Immich, molimo pogledajte dokumentaciju.", + "backup_onboarding_parts_title": "3-2-1 sigurnosna kopija uključuje:", + "backup_onboarding_title": "Sigurnosne kopije", "backup_settings": "Postavke sigurnosne kopije", "backup_settings_description": "Upravljajte postavkama izvoza baze podataka.", "cleared_jobs": "Izbrisani poslovi za: {job}", @@ -165,6 +177,20 @@ "metadata_settings_description": "Upravljanje postavkama metapodataka", "migration_job": "Migracija", "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", + "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novootkrivenim licima", + "nightly_tasks_cluster_new_faces_setting": "Grupiraj nova lica", + "nightly_tasks_database_cleanup_setting": "Zadaci čiÅĄÄ‡enja baze podataka", + "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", + "nightly_tasks_generate_memories_setting": "Generiraj uspomene", + "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz sadrÅžaja", + "nightly_tasks_missing_thumbnails_setting": "Generiraj nedostajuće sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Stavi u red čekanja sadrÅžaje bez sličica za generiranje sličica", + "nightly_tasks_settings": "Postavke noćnih zadataka", + "nightly_tasks_settings_description": "Upravljanje noćnim zadacima", + "nightly_tasks_start_time_setting": "Vrijeme početka", + "nightly_tasks_start_time_setting_description": "Vrijeme pokretanja noćnih zadataka na posluÅžitelju", + "nightly_tasks_sync_quota_usage_setting": "Sinkroniziraj iskoriÅĄtenost kvote", + "nightly_tasks_sync_quota_usage_setting_description": "AÅžuriraj korisničku kvotu za pohranu na temelju trenutne potroÅĄnje", "no_paths_added": "Nema dodanih putanja", "no_pattern_added": "Nije dodan uzorak", "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", @@ -195,6 +221,8 @@ "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "Nadjačavanje URI-preusmjeravanja za mobilne uređaje", "oauth_mobile_redirect_uri_override_description": "Omogući kada pruÅžatelj OAuth ne dopuÅĄta mobilni URI, poput ''{callback}''", + "oauth_role_claim": "Dodjela uloge", + "oauth_role_claim_description": "Automatski dodijeli administratorski pristup na temelju prisutnosti ove tvrdnje. Tvrdnja moÅže sadrÅžavati ili 'user' ili 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje postavkama za prijavu kroz OAuth", "oauth_settings_more_details": "Za viÅĄe pojedinosti o ovoj značajci pogledajte uputstva.", @@ -203,7 +231,7 @@ "oauth_storage_quota_claim": "Zahtjev za kvotom pohrane", "oauth_storage_quota_claim_description": "Automatski postavite korisničku kvotu pohrane na vrijednost ovog zahtjeva.", "oauth_storage_quota_default": "Zadana kvota pohrane (GiB)", - "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva (unesite 0 za neograničenu kvotu).", + "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva", "oauth_timeout": "Istek vremena zahtjeva", "oauth_timeout_description": "Istek vremena zahtjeva je u milisekundama", "password_enable_description": "Prijava s email adresom i zaporkom", @@ -243,6 +271,7 @@ "storage_template_migration_info": "PredloÅžak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloÅĄka primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloÅĄka na prethodno prenesena sredstva, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije PredloÅĄka Pohrane", "storage_template_more_details": "Za viÅĄe pojedinosti o ovoj značajci pogledajte PredloÅžak pohrane i njegove implikacije", + "storage_template_onboarding_description_v2": "Kada je omogućena, ova će značajka automatski organizira datoteke prema predloÅĄku koji je definirao korisnik. Za viÅĄe informacija pogledajte dokumentaciju.", "storage_template_path_length": "PribliÅžno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "PredloÅžak pohrane", "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", @@ -329,6 +358,9 @@ "trash_number_of_days_description": "Broj dana za drÅžanje sredstava u smeću prije njihovog trajnog uklanjanja", "trash_settings": "Postavke Smeća", "trash_settings_description": "Upravljanje postavkama smeća", + "unlink_all_oauth_accounts": "Odspoji sve OAuth račune", + "unlink_all_oauth_accounts_description": "Zapamtite da odspojite sve OAuth račune prije prelaska na novog pruÅžatelja usluge.", + "unlink_all_oauth_accounts_prompt": "Jeste li sigurni da Åželite odspojiti sve OAuth račune? Ovo će resetirati OAuth ID za svakog korisnika i ne moÅže se poniÅĄtiti.", "user_cleanup_job": "ČiÅĄÄ‡enje korisnika", "user_delete_delay": "Račun i sredstva korisnika {user} bit će zakazani za trajno brisanje za {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "Brisanje odgode", @@ -358,7 +390,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. PokuÅĄajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", "advanced_settings_log_level_title": "Razina zapisivanja: {level}", - "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s resursa na uređaju. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih resursa. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mreÅžnim zahtjevom.", "advanced_settings_proxy_headers_title": "Proxy zaglavlja", @@ -377,6 +409,7 @@ "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_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZUZETO", "album_info_card_backup_album_included": "UKLJUČENO", "album_info_updated": "Podaci o albumu aÅžurirani", @@ -386,6 +419,7 @@ "album_options": "Opcije albuma", "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da Åželite ukloniti {user}?", + "album_search_not_found": "Nema albuma koji odgovaraju vaÅĄem pretraÅživanju", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", "album_updated": "Album aÅžuriran", "album_updated_setting_description": "Primite obavijest e-poÅĄtom kada dijeljeni album ima nova sredstva", @@ -405,6 +439,7 @@ "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", + "albums_on_device_count": "Albumi na uređaju ({count})", "all": "Sve", "all_albums": "Svi albumi", "all_people": "Svi ljudi", @@ -425,6 +460,7 @@ "app_settings": "Postavke Aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", + "archive_action_prompt": "{count} dodano u arhivu", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", "archive_page_no_archived_assets": "Nema arhiviranih resursa", "archive_page_title": "Arhiviraj ({count})", @@ -462,11 +498,14 @@ "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 {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", + "assets_added_to_albums_count": "Dodano je {assetTotal} datoteka u {albumTotal} albuma", + "assets_cannot_be_added_to_album_count": "{count, plural, one {SadrÅžaj se ne moÅže dodati u album} other {{count} sadrÅžaja se ne mogu dodati u album}}", + "assets_cannot_be_added_to_albums": "{count, plural, one {Datoteka se ne moÅže dodati ni u jedan album} few {Datoteke se ne mogu dodati ni u jedan album} other {Datoteka se ne moÅže dodati ni u jedan album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspjeÅĄno uklonjeni", "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich posluÅžitelja", + "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} other {Preuzeto je # datoteka – {error} datoteke nisu uspjele}}", + "assets_downloaded_successfully": "{count, plural, one {UspjeÅĄno preuzeta # datoteka} other {UspjeÅĄno preuzete # datoteke}}", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premjeÅĄteno u smeće", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", @@ -478,13 +517,16 @@ "assets_trashed_count": "Bačeno u smeće {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} resurs(i) premjeÅĄten(i) u smeće s Immich posluÅžitelja", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} već dio albuma", + "assets_were_part_of_albums_count": "{count, plural, one {Datoteka je već bila dio albuma} few {Datoteke su već bile dio albuma} other {Datoteka je već bila dio albuma}}", "authorized_devices": "OvlaÅĄteni Uređaji", "automatic_endpoint_switching_subtitle": "PoveÅžite se lokalno preko naznačene Wi-Fi mreÅže kada je dostupna i koristite alternativne veze na drugim lokacijama", "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", + "autoplay_slideshow": "Automatsko prikazivanje slajdova", "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": "Sigurnosna kopija", "backup_album_selection_page_albums_device": "Albumi na uređaju ({count})", "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.", @@ -527,7 +569,7 @@ "backup_controller_page_id": "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": "Preostalo", "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", @@ -545,8 +587,10 @@ "backup_manual_in_progress": "Slanje već u tijeku. PokÅĄuajte nakon nekog vremena", "backup_manual_success": "Uspijeh", "backup_manual_title": "Status slanja", + "backup_options": "Opcije sigurnosne kopije", "backup_options_page_title": "Opcije sigurnosnog kopiranja", "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", + "backup_settings_subtitle": "Upravljaj postavkama slanja", "backward": "Unazad", "biometric_auth_enabled": "Biometrijska autentikacija omogućena", "biometric_locked_out": "Zaključani ste iz biometrijske autentikacije", @@ -565,7 +609,7 @@ "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_subtitle": "Fotografije i videozapisi koje je aplikacija ignorira", "cache_settings_duplicated_assets_title": "Duplicirani resursi ({count})", "cache_settings_statistics_album": "Sličice biblioteke", "cache_settings_statistics_full": "Pune slike", @@ -582,6 +626,7 @@ "cancel": "OtkaÅži", "cancel_search": "OtkaÅži pretragu", "canceled": "Otkazano", + "canceling": "Otkazivanje", "cannot_merge_people": "Nije moguće spojiti osobe", "cannot_undo_this_action": "Ne moÅžete poniÅĄtiti ovu radnju!", "cannot_update_the_description": "Nije moguće aÅžurirati opis", @@ -613,6 +658,7 @@ "clear": "Očisti", "clear_all": "Očisti sve", "clear_all_recent_searches": "IzbriÅĄi sva nedavna pretraÅživanja", + "clear_file_cache": "Očisti predmemoriju datoteka", "clear_message": "Jasna poruka", "clear_value": "Očisti vrijednost", "client_cert_dialog_msg_confirm": "U redu", @@ -645,6 +691,7 @@ "confirm_password": "Potvrdite lozinku", "confirm_tag_face": "ÅŊelite li označiti ovo lice kao {name}?", "confirm_tag_face_unnamed": "ÅŊelite li označiti ovo lice?", + "connected_device": "Uređaj povezan", "connected_to": "Povezano s", "contain": "SadrÅži", "context": "Kontekst", @@ -682,6 +729,7 @@ "create_new_user": "Kreiraj novog korisnika", "create_shared_album_page_share_add_assets": "DODAJ STAVKE", "create_shared_album_page_share_select_photos": "Odaberi fotografije", + "create_shared_link": "Kreiraj dijeljeni link", "create_tag": "Stvori oznaku", "create_tag_description": "Napravite novu oznaku. Za ugnijeŞđene oznake unesite punu putanju oznake uključujući kose crte.", "create_user": "Stvori korisnika", @@ -694,9 +742,11 @@ "current_server_address": "Trenutna adresa posluÅžitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", + "custom_url": "Prilagođena URL adresa", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Tamno", + "dark_theme": "Prebaci tamnu temu", "date_after": "Datum nakon", "date_and_time": "Datum i Vrijeme", "date_before": "Datum prije", @@ -704,6 +754,7 @@ "date_of_birth_saved": "Datum rođenja uspjeÅĄno spremljen", "date_range": "Razdoblje", "day": "Dan", + "days": "Dani", "deduplicate_all": "Dedupliciraj Sve", "deduplication_criteria_1": "Veličina slike u bajtovima", "deduplication_criteria_2": "Broj EXIF podataka", @@ -712,6 +763,8 @@ "default_locale": "Zadana lokalizacija", "default_locale_description": "Oblikujte datume i brojeve na temelju jezika preglednika", "delete": "IzbriÅĄi", + "delete_action_confirmation_message": "Jeste li sigurni da Åželite izbrisati ovaj sadrÅžaj? Ova radnja će premjestiti sadrÅžaj u smeće na posluÅžitelju i upitat će vas Åželite li ga izbrisati i lokalno", + "delete_action_prompt": "{count} izbrisano", "delete_album": "IzbriÅĄi album", "delete_api_key_prompt": "Jeste li sigurni da Åželite izbrisati ovaj API ključ?", "delete_dialog_alert": "Ove stavke bit će trajno izbrisane iz Immicha i s vaÅĄeg uređaja", @@ -725,9 +778,12 @@ "delete_key": "Ključ za brisanje", "delete_library": "IzbriÅĄi knjiÅžnicu", "delete_link": "IzbriÅĄi poveznicu", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "IzbriÅĄi samo sigurnosno kopirane", "delete_local_dialog_ok_force": "IzbriÅĄi svejedno", "delete_others": "IzbriÅĄi druge", + "delete_permanently": "IzbriÅĄi trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "IzbriÅĄi dijeljenu poveznicu", "delete_shared_link_dialog_title": "IzbriÅĄi dijeljenu poveznicu", "delete_tag": "IzbriÅĄi oznaku", @@ -738,12 +794,14 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "PogreÅĄka pri aÅžuriranju opisa, provjerite zapisnik za viÅĄe detalja", + "deselect_all": "PoniÅĄti odabir svih", "details": "Detalji", "direction": "Smjer", "disabled": "Onemogućeno", "disallow_edits": "Zabrani izmjene", "discord": "Discord", "discover": "Otkrij", + "discovered_devices": "Otkriveni uređaji", "dismiss_all_errors": "Odbaci sve pogreÅĄke", "dismiss_error": "Odbaci pogreÅĄku", "display_options": "Mogućnosti prikaza", @@ -754,6 +812,7 @@ "documentation": "Dokumentacija", "done": "Gotovo", "download": "Preuzmi", + "download_action_prompt": "Preuzimanje {count} sadrÅžaja", "download_canceled": "Preuzimanje otkazano", "download_complete": "Preuzimanje zavrÅĄeno", "download_enqueue": "Preuzimanje dodano u red", @@ -780,8 +839,12 @@ "edit": "Izmjena", "edit_album": "Uredi album", "edit_avatar": "Uredi avatar", + "edit_birthday": "Izmijeni rođendan", "edit_date": "Uredi datum", "edit_date_and_time": "Uredite datum i vrijeme", + "edit_date_and_time_action_prompt": "{count} datuma i vremena uređeno", + "edit_date_and_time_by_offset": "Promijeni datum prema pomaku", + "edit_date_and_time_by_offset_interval": "Novi raspon datuma: {from} - {to}", "edit_description": "Uredi opis", "edit_description_prompt": "Molimo odaberite novi opis:", "edit_exclusion_pattern": "Uredi uzorak izuzimanja", @@ -791,6 +854,7 @@ "edit_key": "Ključ za uređivanje", "edit_link": "Uredi poveznicu", "edit_location": "Uredi lokaciju", + "edit_location_action_prompt": "{count} uređenih lokacija", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi ljude", @@ -809,6 +873,7 @@ "empty_trash": "Isprazni smeće", "empty_trash_confirmation": "Jeste li sigurni da Åželite isprazniti smeće? Time će se iz Immicha trajno ukloniti sva sredstva u otpadu.\nNe moÅžete poniÅĄtiti ovu radnju!", "enable": "Omogući", + "enable_backup": "Omogući sigurnosnu kopiju", "enable_biometric_auth_description": "Unesite svoj PIN kod za omogućavanje biometrijske autentikacije", "enabled": "Omogućeno", "end_date": "Datum zavrÅĄetka", @@ -852,6 +917,7 @@ "failed_to_load_notifications": "NeuspjeÅĄno učitavanje obavijesti", "failed_to_load_people": "Učitavanje ljudi nije uspjelo", "failed_to_remove_product_key": "Uklanjanje ključa proizvoda nije uspjelo", + "failed_to_reset_pin_code": "NeuspjeÅĄno resetiranje PIN koda", "failed_to_stack_assets": "Slaganje sredstava nije uspjelo", "failed_to_unstack_assets": "Nije uspjelo uklanjanje snopa sredstava", "failed_to_update_notification_status": "NeuspjeÅĄno aÅžuriranje statusa obavijesti", @@ -860,6 +926,7 @@ "paths_validation_failed": "{paths, plural, one {# putanja nije proÅĄla} other {# putanje nisu proÅĄle}} provjeru valjanosti", "profile_picture_transparent_pixels": "Profilne slike ne smiju imati prozirne piksele. Povećajte i/ili pomaknite sliku.", "quota_higher_than_disk_size": "Postavili ste kvotu veću od veličine diska", + "something_went_wrong": "NeÅĄto je poÅĄlo po zlu", "unable_to_add_album_users": "Nije moguće dodati korisnike u album", "unable_to_add_assets_to_shared_link": "Nije moguće dodati sredstva na dijeljenu poveznicu", "unable_to_add_comment": "Nije moguće dodati komentar", @@ -945,13 +1012,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis...", + "exif_bottom_sheet_description_error": "PogreÅĄka pri aÅžuriranju opisa", "exif_bottom_sheet_details": "DETALJI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSOBE", "exif_bottom_sheet_person_add_person": "Dodaj ime", - "exif_bottom_sheet_person_age_months": "Dob {months} mjeseci", - "exif_bottom_sheet_person_age_year_months": "Dob 1 godina, {months} mjeseci", - "exif_bottom_sheet_person_age_years": "Dob {years}", "exit_slideshow": "Izađi iz projekcije slideova", "expand_all": "ProÅĄiri sve", "experimental_settings_new_asset_list_subtitle": "Rad u tijeku", @@ -965,6 +1030,8 @@ "explorer": "PretraÅživač (Explorer)", "export": "Izvoz", "export_as_json": "Izvezi kao JSON", + "export_database": "Izvezi bazu podataka", + "export_database_description": "Izvezi SQLite bazu podataka", "extension": "ProÅĄirenje (Extension)", "external": "Vanjski", "external_libraries": "Vanjske Biblioteke", @@ -976,6 +1043,7 @@ "failed_to_load_assets": "Neuspjelo učitavanje stavki", "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", + "favorite_action_prompt": "{count} dodano u Omiljeno", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Omiljene", "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", @@ -990,11 +1058,13 @@ "filter_people": "Filtrirajte ljude", "filter_places": "Filtriraj mjesta", "find_them_fast": "Pronađite ih brzo po imenu pomoću pretraÅživanja", + "first": "Prvi", "fix_incorrect_match": "Ispravite netočno podudaranje", "folder": "Mapa", "folder_not_found": "Mapa nije pronađena", "folders": "Mape", "folders_feature_description": "Pregledavanje prikaza mape za fotografije i videozapise u sustavu datoteka", + "forgot_pin_code_question": "Zaboravili ste svoj PIN?", "forward": "Naprijed", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Ova značajka učitava vanjske resurse s Googlea kako bi radila.", @@ -1015,6 +1085,9 @@ "haptic_feedback_switch": "Omogući haptičku povratnu informaciju", "haptic_feedback_title": "Haptička povratna informacija", "has_quota": "Ima kvotu", + "hash_asset": "Hash sadrÅžaja", + "hashed_assets": "Hashirani sadrÅžaji", + "hashing": "Hashiranje", "header_settings_add_header_tip": "Dodaj zaglavlje", "header_settings_field_validator_msg": "Vrijednost ne moÅže biti prazna", "header_settings_header_name_input": "Naziv zaglavlja", @@ -1046,7 +1119,9 @@ "home_page_upload_err_limit": "Moguće je prenijeti najviÅĄe 30 stavki odjednom, preskačem", "host": "Domaćin", "hour": "Sat", + "hours": "Sati", "id": "ID", + "idle": "Neaktivan", "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", @@ -1100,11 +1175,17 @@ "kept_this_deleted_others": "ZadrÅžana je ova datoteka i izbrisano {count, plural, one {# datoteka} other {# datoteka}}", "keyboard_shortcuts": "Prečaci tipkovnice", "language": "Jezik", + "language_no_results_subtitle": "PokuÅĄajte prilagoditi pojam za pretraÅživanje", + "language_no_results_title": "Nisu pronađeni jezici", + "language_search_hint": "PretraÅži jezike...", "language_setting_description": "Odaberite Åželjeni jezik", + "large_files": "Velike datoteke", + "last": "Zadnji", "last_seen": "Zadnji put viđen", "latest_version": "Najnovija verzija", "latitude": "Zemljopisna ÅĄirina", "leave": "Izađi", + "leave_album": "Napusti album", "lens_model": "Model objektiva", "let_others_respond": "Dozvoli da drugi odgovore", "level": "Razina", @@ -1116,15 +1197,19 @@ "library_page_sort_created": "Datum kreiranja", "library_page_sort_last_modified": "Zadnja izmjena", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svjetlo", + "like": "Sviđa mi se", "like_deleted": "Like izbrisan", "link_motion_video": "PoveÅžite videozapis pokreta", - "link_options": "Opcije veze", "link_to_oauth": "Veza na OAuth", "linked_oauth_account": "Povezani OAuth račun", "list": "Popis", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretraÅživanja nije uspjelo", + "local": "Lokalno", + "local_asset_cast_failed": "Nije moguće reproducirati sadrÅžaj koji nije prenesen na posluÅžitelj", + "local_assets": "Lokalni sadrÅžaji", "local_network": "Lokalna mreÅža", "local_network_sheet_info": "Aplikacija će se povezati s posluÅžiteljem putem ovog URL-a kada koristi određenu Wi-Fi mreÅžu", "location_permission": "Dozvola za lokaciju", @@ -1138,13 +1223,14 @@ "locked_folder": "Zaključana Mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", + "logged_in_as": "Prijavljeni kao {user}", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", "login_disabled": "Prijava je onemogućena", "login_form_api_exception": "API iznimka. Provjerite URL posluÅžitelja i pokuÅĄajte ponovno.", "login_form_back_button_text": "Nazad", - "login_form_email_hint": "vasaemaiadresal@email.com", + "login_form_email_hint": "vasaemailadresa@email.com", "login_form_endpoint_hint": "http://vaÅĄ-server-ip:port", "login_form_endpoint_url": "URL krajnje točke posluÅžitelja", "login_form_err_http": "Molimo navedite http:// ili https://", @@ -1180,8 +1266,7 @@ "manage_your_devices": "Upravljajte uređajima na kojima ste prijavljeni", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Karta", - "map_assets_in_bound": "{count} fotografija", - "map_assets_in_bounds": "{count} fotografija", + "map_assets_in_bounds": "{count, plural, =0 {Nema fotografija na ovom području} one {# fotografija} few {#fotografije} other {# fotografija}}", "map_cannot_get_user_location": "Nije moguće dohvatiti lokaciju korisnika", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Koristi ovu lokaciju", @@ -1189,7 +1274,6 @@ "map_location_service_disabled_title": "Usluga lokacije onemogućena", "map_marker_for_images": "Oznaka karte za slike snimljene u {city}, {country}", "map_marker_with_image": "Oznaka karte sa slikom", - "map_no_assets_in_bounds": "Nema fotografija u ovom području", "map_no_location_permission_content": "Potrebno je dopuÅĄtenje za lokaciju kako bi se prikazale stavke s vaÅĄe trenutne lokacije. ÅŊelite li ga sada omogućiti?", "map_no_location_permission_title": "DopuÅĄtenje za lokaciju odbijeno", "map_settings": "Postavke karte", @@ -1226,6 +1310,7 @@ "merged_people_count": "{count, plural, one {# Spojena osoba} other {# Spojene osobe}}", "minimize": "Minimiziraj", "minute": "Minuta", + "minutes": "Minute", "missing": "Nedostaje", "model": "Model", "month": "Mjesec", @@ -1233,6 +1318,7 @@ "more": "ViÅĄe", "move": "Pomakni", "move_off_locked_folder": "Premjesti iz zaključane mape", + "move_to_lock_folder_action_prompt": "{count} dodano u zaključanu mapu", "move_to_locked_folder": "Premjesti u zaključanu mapu", "move_to_locked_folder_confirmation": "Ove fotografije i videozapis bit će uklonjeni iz svih albuma i bit će vidljivi samo iz zaključane mape", "moved_to_archive": "PremjeÅĄteno {count, plural, one {# resurs} other {# resursa}} u arhivu", @@ -1244,6 +1330,9 @@ "my_albums": "Moji albumi", "name": "Ime", "name_or_nickname": "Ime ili nadimak", + "network_requirement_photos_upload": "Koristi mobilne podatke za sigurnosno kopiranje fotografija", + "network_requirement_videos_upload": "Koristi mobilne podatke za sigurnosno kopiranje videozapisa", + "network_requirements_updated": "Zahtjevi za mreÅžu su se promijenili, red za sigurnosno kopiranje se resetira", "networking_settings": "UmreÅžavanje", "networking_subtitle": "Upravljajte postavkama krajnje točke posluÅžitelja", "never": "Nikada", @@ -1265,6 +1354,7 @@ "no_archived_assets_message": "Arhivirajte fotografije i videozapise kako biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA PRENESETE SVOJU PRVU FOTOGRAFIJU", "no_assets_to_show": "Nema stavki za prikaz", + "no_cast_devices_found": "Nisu pronađeni uređaji za reprodukciju", "no_duplicates_found": "Nisu pronađeni duplikati.", "no_exif_info_available": "Nema dostupnih exif podataka", "no_explore_results_message": "Prenesite viÅĄe fotografija da istraÅžite svoju zbirku.", @@ -1278,6 +1368,7 @@ "no_results": "Nema rezultata", "no_results_description": "PokuÅĄajte sa sinonimom ili općenitijom ključnom riječi", "no_shared_albums_message": "Stvorite album za dijeljenje fotografija i videozapisa s osobama u svojoj mreÅži", + "no_uploads_in_progress": "Nema aktivnih prijenosa", "not_in_any_album": "Ni u jednom albumu", "not_selected": "Nije odabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primijenili Oznaku za skladiÅĄtenje na prethodno prenesena sredstva, pokrenite", @@ -1293,12 +1384,16 @@ "oauth": "OAuth", "official_immich_resources": "SluÅžbeni Immich resursi", "offline": "Izvan mreÅže", + "offset": "Pomak", "ok": "Ok", "oldest_first": "Prvo najstarije", "on_this_device": "Na ovom uređaju", "onboarding": "Uključivanje (Onboarding)", - "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama administracije.", + "onboarding_locale_description": "Odaberite Åželjeni jezik. Kasnije ga moÅžete promijeniti u postavkama.", + "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama.", + "onboarding_server_welcome_description": "Postavimo vaÅĄu instancu s nekim uobičajenim postavkama.", "onboarding_theme_description": "Odaberite temu boja za svoj primjer. To moÅžete kasnije promijeniti u postavkama.", + "onboarding_user_welcome_description": "Počnimo!", "onboarding_welcome_user": "Dobro doÅĄli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", @@ -1312,6 +1407,7 @@ "original": "originalno", "other": "Ostalo", "other_devices": "Ostali uređaji", + "other_entities": "Ostali entiteti", "other_variables": "Ostale varijable", "owned": "VlasniÅĄtvo", "owner": "Vlasnik", @@ -1366,6 +1462,9 @@ "permission_onboarding_permission_limited": "DopuÅĄtenje ograničeno. Da biste Immichu dopustili sigurnosno kopiranje i upravljanje cijelom galerijom, dodijelite dopuÅĄtenja za fotografije i videozapise u Postavkama.", "permission_onboarding_request": "Immich zahtijeva dopuÅĄtenje za pregled vaÅĄih fotografija i videozapisa.", "person": "Osoba", + "person_age_months": "{months, plural, one {# mjesec} few {# mjeseca} other {# mjeseci}} staro", + "person_age_year_months": "1 godina, {months, plural, one {# mjesec} few {# mjeseca} other {# mjeseci}} staro", + "person_age_years": "{years, plural, few {# godine} other {# godina}} staro", "person_birthdate": "Rođen/a {date}", "person_hidden": "{name}{hidden, select, true { (skriveno)} other {}}", "photo_shared_all_users": "Čini se da ste svoje fotografije podijelili sa svim korisnicima ili nemate nijednog korisnika s kojim biste ih podijelili.", @@ -1443,6 +1542,7 @@ "purchase_server_description_2": "Status podupiratelja", "purchase_server_title": "PosluÅžitelj (Server)", "purchase_settings_server_activated": "Ključem proizvoda posluÅžitelja upravlja administrator", + "queue_status": "Stavljanje u red {count}/{total}", "rating": "Broj zvjezdica", "rating_clear": "ObriÅĄi ocjenu", "rating_count": "{count, plural, one {# zvijezda} other {# zvijezde}}", @@ -1471,6 +1571,8 @@ "refreshing_faces": "OsvjeÅžavanje lica", "refreshing_metadata": "OsvjeÅžavanje metapodataka", "regenerating_thumbnails": "Obnavljanje sličica", + "remote": "Udaljeno", + "remote_assets": "Udaljeni sadrÅžaji", "remove": "Ukloni", "remove_assets_album_confirmation": "Jeste li sigurni da Åželite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz albuma?", "remove_assets_shared_link_confirmation": "Jeste li sigurni da Åželite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz ove dijeljene veze?", @@ -1478,12 +1580,15 @@ "remove_custom_date_range": "Ukloni prilagođeni datumski raspon", "remove_deleted_assets": "Ukloni izbrisana sredstva", "remove_from_album": "Ukloni iz albuma", + "remove_from_album_action_prompt": "{count} uklonjeno iz albuma", "remove_from_favorites": "Ukloni iz favorita", + "remove_from_lock_folder_action_prompt": "{count} uklonjeno iz zaključane mape", "remove_from_locked_folder": "Ukloni iz zaključane mape", "remove_from_locked_folder_confirmation": "Jeste li sigurni da Åželite premjestiti ove fotografije i videozapise iz zaključane mape? Bit će vidljivi u vaÅĄoj biblioteci.", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", "remove_memory": "Ukloni uspomenu", "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", + "remove_tag": "Ukloni oznaku", "remove_url": "Ukloni URL", "remove_user": "Ukloni korisnika", "removed_api_key": "Uklonjen API ključ: {name}", @@ -1505,19 +1610,28 @@ "reset_password": "Resetiraj lozinku", "reset_people_visibility": "PoniÅĄti vidljivost ljudi", "reset_pin_code": "Resetiraj PIN kod", + "reset_pin_code_description": "Ako ste zaboravili svoj PIN kod, moÅžete kontaktirati administratora posluÅžitelja da ga resetira", + "reset_pin_code_success": "PIN kod je uspjeÅĄno resetiran", + "reset_pin_code_with_password": "Uvijek moÅžete resetirati svoj PIN kod pomoću svoje lozinke", + "reset_sqlite": "Resetiraj SQLite bazu podataka", + "reset_sqlite_confirmation": "Jeste li sigurni da Åželite resetirati SQLite bazu podataka? Morat ćete se odjaviti i ponovno prijaviti kako biste ponovno sinkronizirali podatke", + "reset_sqlite_success": "SQLite baza podataka je uspjeÅĄno resetirana", "reset_to_default": "Vrati na zadano", "resolve_duplicates": "RijeÅĄite duplikate", "resolved_all_duplicates": "RazrijeÅĄi sve duplikate", "restore": "Oporavi", "restore_all": "Oporavi sve", + "restore_trash_action_prompt": "{count} vraćeno iz smeća", "restore_user": "Vrati korisnika", "restored_asset": "Obnovljena datoteka", "resume": "Nastavi", "retry_upload": "Ponovi prijenos", "review_duplicates": "Pregledajte duplikate", + "review_large_files": "Pregledaj velike datoteke", "role": "Uloga", "role_editor": "Urednik", "role_viewer": "Gledatelj", + "running": "U tijeku", "save": "Spremi", "save_to_gallery": "Spremi u galeriju", "saved_api_key": "Spremljen API ključ", @@ -1590,6 +1704,7 @@ "select_album_cover": "Odaberite omot albuma", "select_all": "Odaberi sve", "select_all_duplicates": "Odaberi sve duplikate", + "select_all_in": "Odaberi sve u {group}", "select_avatar_color": "Odaberi boju avatara", "select_face": "Odaberi lice", "select_featured_photo": "Odaberi istaknutu fotografiju", @@ -1610,6 +1725,7 @@ "server_info_box_server_url": "URL posluÅžitelja", "server_offline": "Server izvan mreÅže", "server_online": "Server na mreÅži", + "server_privacy": "Privatnost posluÅžitelja", "server_stats": "Statistike servera", "server_version": "Verzija servera", "set": "Postavi", @@ -1619,6 +1735,7 @@ "set_date_of_birth": "Postavi datum rođenja", "set_profile_picture": "Postavi profilnu sliku", "set_slideshow_to_fullscreen": "Postavi prezentaciju na cijeli zaslon", + "set_stack_primary_asset": "Postavi kao glavni sadrÅžaj", "setting_image_viewer_help": "Preglednik detalja prvo učitava malu sličicu, zatim učitava pregled srednje veličine (ako je omogućen), te na kraju učitava original (ako je omogućen).", "setting_image_viewer_original_subtitle": "Omogućite za učitavanje originalne slike pune rezolucije (velika!). Onemogućite za smanjenje potroÅĄnje podataka (i mreÅžne i na predmemoriji uređaja).", "setting_image_viewer_original_title": "Učitaj originalnu sliku", @@ -1646,6 +1763,7 @@ "settings_saved": "Postavke su spremljene", "setup_pin_code": "Postavi PIN kod", "share": "Podijeli", + "share_action_prompt": "Podijeljeno {count} sadrÅžaja", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} odabrano", "share_dialog_preparing": "Priprema...", @@ -1667,6 +1785,7 @@ "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik", "shared_link_clipboard_text": "Poveznica: {link}\nLozinka: {password}", "shared_link_create_error": "PogreÅĄka pri kreiranju dijeljene poveznice", + "shared_link_custom_url_description": "Pristupite ovom dijeljenom linku pomoću prilagođene URL adrese", "shared_link_edit_description_hint": "Unesite opis dijeljenja", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dana", @@ -1692,6 +1811,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", "shared_link_options": "Opcije dijeljene poveznice", + "shared_link_password_description": "Zahtjevaj loziku za pristup ovom dijeljenom linku", "shared_links": "Dijeljene poveznice", "shared_links_description": "Podijelite fotografije i videozapise putem poveznice", "shared_photos_and_videos_count": "{assetCount, plural, =1 {# podijeljena fotografija ili videozapis.} few {# podijeljene fotografije i videozapisa.} other {# podijeljenih fotografija i videozapisa.}}", @@ -1741,12 +1861,14 @@ "sort_created": "Datum kreiranja", "sort_items": "Broj stavki", "sort_modified": "Datum izmjene", + "sort_newest": "Najnovija fotografija", "sort_oldest": "Najstarija fotografija", "sort_people_by_similarity": "Sortiraj osobe po sličnosti", "sort_recent": "Najnovija fotografija", "sort_title": "Naslov", "source": "Izvor", "stack": "SloÅži", + "stack_action_prompt": "{count} sloÅženo", "stack_duplicates": "SloÅži duplikate", "stack_select_one_photo": "Odaberi jednu glavnu fotografiju za slaganje", "stack_selected_photos": "SloÅži odabrane fotografije", @@ -1756,6 +1878,7 @@ "start_date": "Datum početka", "state": "Stanje", "status": "Status", + "stop_casting": "Zaustavi reprodukciju", "stop_motion_photo": "Zaustavi pokretnu fotografiju", "stop_photo_sharing": "Prestati dijeliti svoje fotografije?", "stop_photo_sharing_description": "{partner} viÅĄe neće moći pristupiti vaÅĄim fotografijama.", @@ -1765,6 +1888,7 @@ "storage_quota": "Kvota Pohrane", "storage_usage": "{used} od {available} iskoriÅĄteno", "submit": "PoÅĄalji", + "success": "Uspijeh", "suggestions": "Prijedlozi", "sunrise_on_the_beach": "Izlazak sunca na plaÅži", "support": "PodrÅĄka", @@ -1774,6 +1898,8 @@ "sync": "Sink.", "sync_albums": "Sinkroniziraj albume", "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", + "sync_local": "Sinkroniziraj lokalno", + "sync_remote": "Sinkroniziraj udaljeno", "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", "tag": "Oznaka", "tag_assets": "Označi stavke", @@ -1784,6 +1910,7 @@ "tag_updated": "AÅžurirana oznaka: {tag}", "tagged_assets": "Označena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", "tags": "Oznake", + "tap_to_run_job": "Dodirnite za pokretanje zadatka", "template": "PredloÅžak", "theme": "Tema", "theme_selection": "Izbor teme", @@ -1816,6 +1943,7 @@ "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Smeće", + "trash_action_prompt": "{count} premjeÅĄteno u smeće", "trash_all": "Stavi sve u smeće", "trash_count": "Smeće {count, number}", "trash_delete_asset": "Premjesti u smeće / IzbriÅĄi stavku", @@ -1833,8 +1961,11 @@ "unable_to_change_pin_code": "Nije moguće promijeniti PIN kod", "unable_to_setup_pin_code": "Nije moguće postaviti PIN kod", "unarchive": "PoniÅĄti arhiviranje", + "unarchive_action_prompt": "{count} uklonjeno iz arhive", "unarchived_count": "{count, plural, =1 {PoniÅĄteno arhiviranje #} few {PoniÅĄteno arhiviranje #} other {PoniÅĄteno arhiviranje #}}", + "undo": "PoniÅĄti", "unfavorite": "Ukloni iz omiljenih", + "unfavorite_action_prompt": "{count} uklonjeno iz favorita", "unhide_person": "PrikaÅži osobu", "unknown": "Nepoznato", "unknown_country": "Nepoznata drÅžava", @@ -1850,16 +1981,22 @@ "unsaved_change": "Nespremljena promjena", "unselect_all": "PoniÅĄti odabir svih", "unselect_all_duplicates": "PoniÅĄti odabir svih duplikata", + "unselect_all_in": "PoniÅĄti odabir svih u {group}", "unstack": "Razdvoji", + "unstack_action_prompt": "{count} razloÅženo", "unstacked_assets_count": "Razdvojena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "untagged": "Bez oznaka", "up_next": "Sljedeće", "updated_at": "AÅžurirano", "updated_password": "Lozinka aÅžurirana", "upload": "Prijenos", + "upload_action_prompt": "{count} u redu za prijenos", "upload_concurrency": "Istovremeni prijenosi", + "upload_details": "Detalji prijenosa", "upload_dialog_info": "ÅŊelite li sigurnosno kopirati odabranu stavku(e) na posluÅžitelj?", "upload_dialog_title": "Prenesi stavku", "upload_errors": "Prijenos zavrÅĄen s {count, plural, =1 {# greÅĄkom} few {# greÅĄke} other {# greÅĄaka}}, osvjeÅžite stranicu da biste vidjeli nove prenesene stavke.", + "upload_finished": "Prijenos zavrÅĄen", "upload_progress": "Preostalo {remaining, number} - Obrađeno {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočena {count, plural, =1 {# duplicirana stavka} few {# duplicirane stavke} other {# dupliciranih stavki}}", "upload_status_duplicates": "Duplikati", @@ -1868,6 +2005,7 @@ "upload_success": "Prijenos uspjeÅĄan, osvjeÅžite stranicu da biste vidjeli nove prenesene stavke.", "upload_to_immich": "Prenesi na Immich ({count})", "uploading": "Prijenos u tijeku", + "uploading_media": "Prijenos medija", "url": "URL", "usage": "KoriÅĄtenje", "use_biometric": "Koristi biometriju", @@ -1879,6 +2017,7 @@ "user_liked": "{user} je označio/la sviđa mi se {type, select, photo {ovu fotografiju} video {ovaj videozapis} asset {ovu stavku} other {to}}", "user_pin_code_settings": "PIN kod", "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", + "user_privacy": "Privatnost korisnika", "user_purchase_settings": "Kupnja", "user_purchase_settings_description": "Upravljajte svojom kupnjom", "user_role_set": "Postavi {user} kao {role}", @@ -1887,6 +2026,7 @@ "user_usage_stats_description": "Pregledajte statistiku koriÅĄtenja računa", "username": "Korisničko ime", "users": "Korisnici", + "users_added_to_album_count": "Dodan{o/a} {count, plural, one {# korisnik} few {# korisnika} other {# korisnika}} u album", "utilities": "Alati", "validate": "Provjeri valjanost", "validate_endpoint_error": "Molimo unesite valjanu URL adresu", @@ -1905,6 +2045,7 @@ "view_album": "PrikaÅži album", "view_all": "PrikaÅži sve", "view_all_users": "PrikaÅži sve korisnike", + "view_details": "PrikaÅži pojedinosti", "view_in_timeline": "PrikaÅži na vremenskoj crti", "view_link": "PrikaÅži poveznicu", "view_links": "PrikaÅži poveznice", diff --git a/i18n/hu.json b/i18n/hu.json index 22ec6f22a1..081bc12f99 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -14,6 +14,7 @@ "add_a_location": "Helyszín hozzÃĄadÃĄsa", "add_a_name": "NÊv megadÃĄsa", "add_a_title": "CímadÃĄs", + "add_birthday": "SzÃŧletÊsnap hozzÃĄadÃĄsa", "add_endpoint": "VÊgpont megadÃĄsa", "add_exclusion_pattern": "KihagyÃĄsi minta (pattern) hozzÃĄadÃĄsa", "add_import_path": "ImportÃĄlÃĄsi Ãētvonal hozzÃĄadÃĄsa", @@ -22,10 +23,15 @@ "add_partner": "Partner hozzÃĄadÃĄsa", "add_path": "ElÊrÊsi Ãētvonal megadÃĄsa", "add_photos": "FotÃŗk hozzÃĄadÃĄsa", + "add_tag": "Címke hozzÃĄadÃĄsa", "add_to": "HozzÃĄadÃĄs ideâ€Ļ", "add_to_album": "FelvÊtel albumba", "add_to_album_bottom_sheet_added": "HozzÃĄadva a(z) \"{album}\" albumhoz", "add_to_album_bottom_sheet_already_exists": "MÃĄr benne van a(z) \"{album}\" albumban", + "add_to_album_bottom_sheet_some_local_assets": "NÊhÃĄny helyi elem nem adhatÃŗ hozzÃĄ az albumhoz", + "add_to_album_toggle": "{album} kijelÃļlÊsÊnek vÃĄltÃĄsa", + "add_to_albums": "HozzÃĄadÃĄs albumokhoz", + "add_to_albums_count": "HozzÃĄadÃĄs albumokhoz ({count})", "add_to_shared_album": "FelvÊtel megosztott albumba", "add_url": "URL hozzÃĄadÃĄsa", "added_to_archive": "HozzÃĄadva az archívumhoz", @@ -33,6 +39,7 @@ "added_to_favorites_count": "{count, number} hozzÃĄadva a kedvencekhez", "admin": { "add_exclusion_pattern_description": "KihagyÃĄsi mintÃĄk (pattern) megadÃĄsa. A *, ** Ês ? helyettesítő karakterek engedÊlyezettek. Pl. a \"Raw\" kÃļnyvtÃĄrban tÃĄrolt Ãļsszes fÃĄjl kihagyÃĄsÃĄhoz hasznÃĄlhatÃŗ a \"**/Raw/**\". Minden \".tif\" fÃĄjl kihagyÃĄsa az Ãļsszes mappÃĄban: \"**/*.tif\". AbszolÃēt elÊrÊsi Ãētvonal kihagyÃĄsa: \"/kihagyni/kivant/mappa/**\".", + "admin_user": "Admin felhasznÃĄlÃŗ", "asset_offline_description": "Ez a kÃŧlső kÊptÃĄrban lÊvő elem mÃĄr nem talÃĄlhatÃŗ, ezÊrt a lomtÃĄrba kerÃŧlt. Ha a fÃĄjl a kÊptÃĄron belÃŧl lett ÃĄthelyezve, akkor ellenőrizd, hogy tovÃĄbbra is lÃĄthatÃŗ az idővonaladon. Az elem visszaÃĄllítÃĄsÃĄhoz győződj meg rÃŗla, hogy az alÃĄbbi mappa az Immich szÃĄmÃĄra elÊrhető, majd Ãējra fÊsÃŧld ÃĄt a kÊptÃĄrat.", "authentication_settings": "HitelesítÊsi beÃĄllítÃĄsok", "authentication_settings_description": "JelszÃŗ, OAuth Ês egyÊb hitelesítÊsi beÃĄllítÃĄsok kezelÊse", @@ -42,8 +49,15 @@ "backup_database": "AdatbÃĄzis lementÊse", "backup_database_enable_description": "AdatbÃĄzis mentÊsek engedÊlyezÊse", "backup_keep_last_amount": "Megőrizendő korÃĄbbi mentÊsek szÃĄma", + "backup_onboarding_1_description": "kÃŧlső tÃĄrolÃŗn, felhőben vagy mÃĄsik fizikai helyen tÃĄrolt mÃĄsolat.", + "backup_onboarding_2_description": "helyi mÃĄsolatok kÃŧlÃļnbÃļző eszkÃļzÃļkÃļn. BeleÊrtve a fő fÃĄjlokat Ês azok helyi biztonsÃĄgi mÃĄsolatÃĄt.", + "backup_onboarding_3_description": "az adatok Ãļsszes mÃĄsolata, beleÊrtve az eredetit is. Ez 1 kÃŧlső Ês 2 helyi mÃĄsolatot tartalmaz.", + "backup_onboarding_description": "Az adatok vÊdelme ÊrdekÊben a 3-2-1 biztonsÃĄgi mentÊsi stratÊgia alkalmazÃĄsa ajÃĄnlott. A teljes kÃļrÅą biztonsÃĄgi mentÊs ÊrdekÊben Êrdemes mÃĄsolatot kÊszíteni a feltÃļltÃļtt fÊnykÊpekről/videÃŗkrÃŗl, valamint az Immich adatbÃĄzisrÃŗl.", + "backup_onboarding_footer": "Az Immich biztonsÃĄgi mentÊsÊről tovÃĄbbi informÃĄciÃŗkat a dokumentÃĄciÃŗban talÃĄlsz.", + "backup_onboarding_parts_title": "A 3-2-1 biztonsÃĄgi mentÊs ezt tartalmazza:", + "backup_onboarding_title": "BiztonsÃĄgi mentÊsek", "backup_settings": "AdatbÃĄzis mentÊs beÃĄllítÃĄsai", - "backup_settings_description": "AdatbÃĄzis mentÊs beÃĄllítÃĄsainak kezelÊse. MegjegyzÊs: Ezek a feladatok nincsenek felÃŧgyelve, így nem kapsz ÊrtesítÊs meghiÃēsulÃĄs esetÊn.", + "backup_settings_description": "AdatbÃĄzis mentÊs beÃĄllítÃĄsainak kezelÊse.", "cleared_jobs": "{job}: feladatai tÃļrÃļlve", "config_set_by_file": "A konfigurÃĄciÃŗt jelenleg egy konfigurÃĄciÃŗs fÃĄjl ÃĄllítja be", "confirm_delete_library": "Biztosan ki szeretnÊd tÃļrÃļlni a {library} kÊptÃĄrat?", @@ -110,6 +124,13 @@ "logging_enable_description": "NaplÃŗzÃĄs engedÊlyezÊse", "logging_level_description": "Ha be van kapcsolva, milyen rÊszletessÊgÅą legyen a naplÃŗzÃĄs.", "logging_settings": "NaplÃŗzÃĄs", + "machine_learning_availability_checks": "ElÊrhetősÊg ellenőrzÊse", + "machine_learning_availability_checks_description": "Automatikusan keressen Ês vÃĄlasszon elÊrhető gÊpi tanulÃĄs szervereket", + "machine_learning_availability_checks_enabled": "ElÊrhetősÊg ellenőrzÊsÊnek bekapcsolÃĄsa", + "machine_learning_availability_checks_interval": "EllenőrzÊsi intervallum", + "machine_learning_availability_checks_interval_description": "ElÊrhetősÊg-ellenőrzÊsek kÃļzÃļtti kÊsleltetÊs milliszekundumban", + "machine_learning_availability_checks_timeout": "KÊrÊsek időkorlÃĄtja", + "machine_learning_availability_checks_timeout_description": "ElÊrhetősÊg-ellenőrzÊsek időkorlÃĄtja milliszekundumban", "machine_learning_clip_model": "CLIP modell", "machine_learning_clip_model_description": "Egy CLIP modell neve az itt felsoroltak kÃļzÃŧl. A modell megvÃĄltoztatÃĄsa utÃĄn Ãējra kell futtatni az 'Okos KeresÊs' feladatot minden kÊpre.", "machine_learning_duplicate_detection": "DuplikÃĄciÃŗk KeresÊse", @@ -138,7 +159,7 @@ "machine_learning_smart_search_description": "KÊpek szemantikai keresÊse CLIP beÃĄgyazÃĄsok segítsÊgÊvel", "machine_learning_smart_search_enabled": "Okos keresÊs engedÊlyezÊse", "machine_learning_smart_search_enabled_description": "Ha ki van kapcsolva, a kÊpek nem lesznek ÃĄtalakítva okos keresÊshez.", - "machine_learning_url_description": "GÊpi tanulÃĄs szerver URL címe. Ha tÃļbbi, mint egy URL van megadva, mindegyik szervert egyenkÊnt prÃŗbÃĄlja meg, amíg az egyik sikeresen nem vÃĄlaszol, sorrendben az elsőtől az utÃŗlsÃŗig. A nem vÃĄlaszolÃŗ szervereket ÃĄtmenetileg figyelmen kívÃŧl hagyja, amíg Ãējra online nem lesznek.", + "machine_learning_url_description": "GÊpi tanulÃĄs szerver URL címe. Ha tÃļbbi, mint egy URL van megadva, mindegyik szervert egyenkÊnt prÃŗbÃĄlja meg, amíg az egyik sikeresen nem vÃĄlaszol, sorrendben az elsőtől az utÃŗlsÃŗig. A nem elÊrhető szervereket ÃĄtmenetileg figyelmen kívÃŧl lesznek hagyva, amíg Ãējra online nem lesznek.", "manage_concurrency": "PÃĄrhuzamos Feladatok KezelÊse", "manage_log_settings": "NaplÃŗzÃĄsi beÃĄllítÃĄsok kezelÊse", "map_dark_style": "SÃļtÊt stílus", @@ -155,7 +176,7 @@ "map_settings_description": "TÊrkÊp beÃĄllítÃĄsok kezelÊse", "map_style_description": "Egy style.json tÊrkÊptÊmÃĄra mutatÃŗ URL cím", "memory_cleanup_job": "MemÃŗria takarítÃĄs", - "memory_generate_job": "EmlÊk generÃĄlÃĄlsa", + "memory_generate_job": "EmlÊkek generÃĄlÃĄsa", "metadata_extraction_job": "Metaadatok kinyerÊse", "metadata_extraction_job_description": "Metaadat informÃĄciÃŗk (pl. GPS, arcok Ês felbontÃĄs) kinyerÊse minden elemből", "metadata_faces_import_setting": "Arc importÃĄlÃĄs engedÊlyezÊse", @@ -164,12 +185,26 @@ "metadata_settings_description": "Metaadat beÃĄllítÃĄsok kezelÊse", "migration_job": "MigrÃĄlÃĄs", "migration_job_description": "Az elemek Ês arcok bÊlyegkÊpeinek migrÃĄlÃĄsa a legÃējabb mappastruktÃērÃĄba", + "nightly_tasks_cluster_faces_setting_description": "ArcfelismerÊs futtatÃĄsa az Ãējonnan ÊrzÊkelt arcokon", + "nightly_tasks_cluster_new_faces_setting": "Új arcok csoportosítÃĄsa", + "nightly_tasks_database_cleanup_setting": "AdatbÃĄzis-tisztítÃĄsi feladatok", + "nightly_tasks_database_cleanup_setting_description": "A rÊgi, lejÃĄrt adatok tÃļrlÊse az adatbÃĄzisbÃŗl", + "nightly_tasks_generate_memories_setting": "EmlÊkek generÃĄlÃĄsa", + "nightly_tasks_generate_memories_setting_description": "Új emlÊkek lÊtrehozÃĄsa elemekből", + "nightly_tasks_missing_thumbnails_setting": "HiÃĄnyzÃŗ indexkÊpek generÃĄlÃĄsa", + "nightly_tasks_missing_thumbnails_setting_description": "A bÊlyegkÊp nÊlkÃŧli elemek bÊlyegkÊpgenerÃĄlÃŗ vÃĄrÃŗlistÃĄra helyezÊse", + "nightly_tasks_settings": "Éjjeli Feladat BeÃĄllítÃĄsok", + "nightly_tasks_settings_description": "Éjjeli feladatok kezelÊse", + "nightly_tasks_start_time_setting": "Kezdőidő", + "nightly_tasks_start_time_setting_description": "Az az időpont, amikor a szerver elkezdi futtatni az Êjszakai feladatokat", + "nightly_tasks_sync_quota_usage_setting": "KvÃŗta hasznÃĄlat szinkronizÃĄlÃĄsa", + "nightly_tasks_sync_quota_usage_setting_description": "A felhasznÃĄlÃŗ kvÃŗtÃĄjÃĄnak frissítÊse az aktuÃĄlis tÃĄrhelyhasznÃĄlat alapjÃĄn", "no_paths_added": "Nincs megadva elÊrÊsi Ãētvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "MegjegyzÊs: Ha a korÃĄbban feltÃļltÃļtt elemekhez is szeretne TÃĄrhely CímkÊket tÃĄrsítani, akkor futtassa ezt", "note_cannot_be_changed_later": "FIGYELEM: ezt kÊsőbb nem lehet megvÃĄltoztatni!", "notification_email_from_address": "FeladÃŗ cím", - "notification_email_from_address_description": "KÃŧldő email címe, pÊldÃĄul: \"Immich FotÃŗszerver \"", + "notification_email_from_address_description": "KÃŧldő email címe, pÊldÃĄul: \"Immich FotÃŗszerver \". Figyelj hogy olyan címet adj meg ahonnan az email kÃŧldÊs engedÊlyezett.", "notification_email_host_description": "Email szerver kiszolgÃĄlÃŗja (pl. smtp.immich.app)", "notification_email_ignore_certificate_errors": "TanÃēsítvÃĄny hibÃĄk figyelmen kívÃŧl hagyÃĄsa", "notification_email_ignore_certificate_errors_description": "TLS tanÃēsítvÃĄny ÊrvÊnyessÊgi hibÃĄk figyelmen kívÃŧl hagyÃĄsa (nem ajÃĄnlott)", @@ -194,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobil ÃĄtirÃĄnyítÃĄsi URI", "oauth_mobile_redirect_uri_override": "Mobil ÃĄtirÃĄnyítÃĄsi URI felÃŧlírÃĄs", "oauth_mobile_redirect_uri_override_description": "EngedÊlyezd, ha az OAuth szolgÃĄltatÃŗ tiltja a mobil URI-t, mint pÊldÃĄul ''{callback}''", + "oauth_role_claim": "SzerepkÃļr kÊrelem", + "oauth_role_claim_description": "Automatikusan adjon rendszergazdai hozzÃĄfÊrÊst ennek az igÊnylÊsnek a jelenlÊte alapjÃĄn. A kÊrelem lehet „felhasznÃĄlÃŗâ€ vagy „rendszergazda”.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth bejelentkezÊsi beÃĄllítÃĄsok kezelÊse", "oauth_settings_more_details": "Erről a funkciÃŗrÃŗl tovÃĄbbi informÃĄciÃŗt a dokumentÃĄciÃŗban talÃĄlsz.", @@ -202,7 +239,7 @@ "oauth_storage_quota_claim": "TÃĄrhelykvÃŗta igÊnylÊse", "oauth_storage_quota_claim_description": "A felhasznÃĄlÃŗ tÃĄrhelykvÃŗtÃĄjÃĄnak automatikus beÃĄllítÃĄsa ennek az igÊnyeltre.", "oauth_storage_quota_default": "AlapÊrtelmezett tÃĄrhelykvÃŗta (GiB)", - "oauth_storage_quota_default_description": "AlapÊrtelmezett tÃĄrhely kvÃŗta GiB-ban, amennyiben a felhasznÃĄlÃŗ nem jelezte az igÊnyÊt (A korlÃĄtlan tÃĄrhelyhez 0-t adj meg).", + "oauth_storage_quota_default_description": "AlapÊrtelmezett tÃĄrhely kvÃŗta GiB-ban, amennyiben a felhasznÃĄlÃŗ nem jelezte az igÊnyÊt.", "oauth_timeout": "KÊrÊs időkorlÃĄtja", "oauth_timeout_description": "KÊrÊsek időkorlÃĄtja milliszekundumban", "password_enable_description": "BejelentkezÊs emaillel Ês jelszÃŗval", @@ -242,6 +279,7 @@ "storage_template_migration_info": "A sablon az Ãļsszes kiterjesztÊst kisbetÅąssÊ alakítja ÃĄt. A megvÃĄltozott sablon csak az Ãējonnan feltÃļltÃļtt elemekre vonatkozik. A korÃĄbbi elemek visszamenőleges ÃĄthelyezÊsÊhez ezt futtasd: {job}.", "storage_template_migration_job": "TÃĄrhely Sablon MigrÃĄciÃŗja", "storage_template_more_details": "TovÃĄbbi rÊszletekÊrt erről a funkciÃŗrÃŗl lÃĄsd a TÃĄrhely Sablon Ês annak kÃļvetkezmÊnyeit a dokumentÃĄciÃŗban", + "storage_template_onboarding_description_v2": "A funkciÃŗ engedÊlyezÊsÊvel automatikusan, a felhasznÃĄlÃŗ ÃĄltal definiÃĄlt sablon alapjÃĄn lesznek rendezve a fÃĄjlok. TÃļbb informÃĄciÃŗÃŠrt lÃĄsd a dokumentÃĄciÃŗt.", "storage_template_path_length": "Útvonal hozzÃĄvetőleges maximÃĄlis hossza: {length, number}{limit, number}", "storage_template_settings": "TÃĄrhely Sablon", "storage_template_settings_description": "A feltÃļltÃļtt elemek mappaszerkezetÊnek Ês fÃĄjl elnevezÊsÊnek kezelÊse", @@ -256,7 +294,7 @@ "template_email_update_album": "Album frissítve sablon", "template_email_welcome": "ÜdvÃļzlő email sablon", "template_settings": "ÉrtesítÊs sablon", - "template_settings_description": "EgyÊni sablonok kezelÊse az ÊrtesítÊsekhez.", + "template_settings_description": "EgyÊni sablonok kezelÊse az ÊrtesítÊsekhez", "theme_custom_css_settings": "Egyedi CSS", "theme_custom_css_settings_description": "CSS Stíluslapokkal az Immich stílusa megvÃĄltoztathatÃŗ.", "theme_settings": "TÊma BeÃĄllítÃĄsok", @@ -288,7 +326,7 @@ "transcoding_encoding_options": "EnkÃŗdolÃĄs beÃĄllítÃĄsok", "transcoding_encoding_options_description": "BeÃĄllíthatod az enkÃŗdolt videÃŗk kÃŗdolÃĄsi algoritmusÃĄt, felbontÃĄsÃĄt, minősÊgÊt Ês egyÊb beÃĄllítÃĄsait", "transcoding_hardware_acceleration": "Hardveres GyorsítÃĄs", - "transcoding_hardware_acceleration_description": "KísÊrleti funkciÃŗ. Sokkal gyorsabb, viszont azonos bitrÃĄtÃĄn is alacsonyabb minősÊghez vezet", + "transcoding_hardware_acceleration_description": "KísÊrleti funkciÃŗ: gyorsabb transzkÃŗdolÃĄs, viszont azonos bitrÃĄtÃĄn alacsonyabb minősÊghez vezethet", "transcoding_hardware_decoding": "Hardveres dekÃŗdolÃĄs", "transcoding_hardware_decoding_setting_description": "LehetővÊ teszi az egÊsz folyamat gyorsítÃĄsÃĄt a pusztÃĄn kÃŗdolÃĄs gyorsítÃĄsa helyett. Nem biztos, hogy minden videÃŗ esetÊn mÅąkÃļdik.", "transcoding_max_b_frames": "B-kÊpkockÃĄk maximum szÃĄma", @@ -328,12 +366,16 @@ "trash_number_of_days_description": "HÃĄny napig legyenek a lomtÃĄrban az elemek a vÊgleges tÃļrlÊs előtt", "trash_settings": "LomtÃĄr BeÃĄllítÃĄsok", "trash_settings_description": "LomtÃĄr beÃĄllítÃĄsok kezelÊse", + "unlink_all_oauth_accounts": "Összes OAuth-fiÃŗk szÊtkapcsolÃĄsa", + "unlink_all_oauth_accounts_description": "Ne felejtsd el, hogy az Ãēj szolgÃĄltatÃŗra valÃŗ ÃĄttÊrÊs előtt minden OAuth-fiÃŗk kapcsolatot meg kell szÃŧntetned.", + "unlink_all_oauth_accounts_prompt": "Biztosan meg szeretnÊd szÃŧntetni az Ãļsszes OAuth-fiÃŗk ÃļsszekapcsolÃĄsÃĄt? Ez minden felhasznÃĄlÃŗ OAuth-azonosítÃŗjÃĄt visszaÃĄllítja, Ês nem vonhatÃŗ vissza.", "user_cleanup_job": "FelhasznÃĄlÃŗk kipucolÃĄsa", "user_delete_delay": "{user} felhasznÃĄlÃŗi fiÃŗkja Ês elemei vÊglegesen tÃļrÃļlve lesznek {delay, plural, one {# nap} other {# nap}} mÃēlva.", "user_delete_delay_settings": "TÃļrlÊsi kÊsleltetÊs", "user_delete_delay_settings_description": "HÃĄny nappal az eltÃĄvolítÃĄs utÃĄn legyen vÊglegesen tÃļrÃļlve a felhasznÃĄlÃŗ fiÃŗkja Ês tÃĄrolt elemei. A vÊgleges tÃļrlÊs feladat minden ÊjfÊlkor fut le, hogy ellenőrizze, hogy van-e tÃļrlendő felhasznÃĄlÃŗ. Ez a beÃĄllítÃĄs a kÃļvetkező futtatÃĄs sorÃĄn lÊp Êletbe.", "user_delete_immediately": "{user} felhasznÃĄlÃŗja Ês Ãļsszes eleme azonnal sorba ÃĄllítÃĄsra kerÃŧl a vÊgleges tÃļrlÊshez .", "user_delete_immediately_checkbox": "FelhasznÃĄlÃŗ Ês tÃĄrolt elemeinek sorba ÃĄllítÃĄsa azonnali tÃļrlÊsre", + "user_details": "FelhasznÃĄlÃŗi adatok", "user_management": "FelhasznÃĄlÃŗk KezelÊse", "user_password_has_been_reset": "A felhasznÃĄlÃŗ jelszava megvÃĄltoztatÃĄsra kerÃŧlt:", "user_password_reset_description": "Juttasd el az ÃĄtmeneti jelszÃŗt a felhasznÃĄlÃŗhoz Ês tÃĄjÊkoztasd, hogy a kÃļvetkező belÊpÊsnÊl azt majd meg kell vÃĄltoztatnia.", @@ -349,19 +391,23 @@ "video_conversion_job": "VideÃŗk ÁtkÃŗdolÃĄsa", "video_conversion_job_description": "VideÃŗk ÃĄtkÃŗdolÃĄsa bÃļngÊszőkkel Ês eszkÃļzÃļkkel valÃŗ szÊleskÃļrÅą kompatibilitÃĄs ÊrdekÊben" }, + "admin_email": "Admin e-mail", "admin_password": "Admin JelszÃŗ", "administration": "AdminisztrÃĄciÃŗ", "advanced": "HaladÃŗ", "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beÃĄllítÃĄssal a szinkronizÃĄlÃĄs sorÃĄn alternatív kritÊriumok alapjÃĄn szÅąrheted a fÃĄjlokat. Csak akkor prÃŗbÃĄld ki, ha problÊmÃĄid vannak azzal, hogy az alkalmazÃĄs nem ismeri fel az Ãļsszes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszkÃļz album szinkronizÃĄlÃĄsi szÅąrő hasznÃĄlata", "advanced_settings_log_level_title": "NaplÃŗzÃĄs szintje: {level}", - "advanced_settings_prefer_remote_subtitle": "NÊhÃĄny eszkÃļz fÃĄjdalmasan lassan tÃļlti be az eszkÃļzÃļn lÊvő bÊlyegkÊpeket. Ez a beÃĄllítÃĄs inkÃĄbb a tÃĄvoli kÊpeket tÃļlti be helyettÃŧk.", + "advanced_settings_prefer_remote_subtitle": "NÊhÃĄny eszkÃļz fÃĄjdalmasan lassan tÃļlti be az eszkÃļzÃļn lÊvő indexkÊpeket. Ez a beÃĄllítÃĄs inkÃĄbb a tÃĄvoli kÊpeket (a szerverről) tÃļlti be helyettÃŧk.", "advanced_settings_prefer_remote_title": "TÃĄvoli kÊpek előnyben rÊszesítÊse", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejlÊceket, amiket az app elkÃŧldjÃļn minden hÃĄlÃŗzati kÊrÊsnÊl", "advanced_settings_proxy_headers_title": "Proxy FejlÊcek", + "advanced_settings_readonly_mode_subtitle": "Bekapcsol egy írÃĄsvÊdett mÃŗdot ahol csak fotÃŗkat nÊzni lehetsÊges, egyebek, mint tÃļbb kÊp kivÃĄlasztÃĄsa, megosztÃĄs, kivetítÊs Ês tÃļrlÊs ki vannak kapcsolva. Ki/bekapcsolhatÃŗ a felhasznÃĄlÃŗ ikonjÃĄrÃŗl a fő kÊpernyőn", + "advanced_settings_readonly_mode_title": "ÍrÃĄsvÊdett MÃŗd", "advanced_settings_self_signed_ssl_subtitle": "Nem ellenőrzi a szerver SSL tanÃēsítvÃĄnyÃĄt. ÖnalÃĄÃ­rt tanÃēsítvÃĄny esetÊn szÃŧksÊges beÃĄllítÃĄs.", "advanced_settings_self_signed_ssl_title": "ÖnalÃĄÃ­rt SSL tanÃēsítvÃĄnyok engedÊlyezÊse", "advanced_settings_sync_remote_deletions_subtitle": "Automatikusan tÃļrÃļlni vagy visszaÃĄllítani egy elemet ezen az eszkÃļzÃļn, ha az adott mÅąveletet a weben hajtottÃĄk vÊgre", + "advanced_settings_sync_remote_deletions_title": "TÃĄvoli tÃļrlÊsek szinkronizÃĄlÃĄsa [KÍSÉRLETI FUNKCIÓ]", "advanced_settings_tile_subtitle": "HaladÃŗ felhasznÃĄlÃŗi beÃĄllítÃĄsok", "advanced_settings_troubleshooting_subtitle": "TovÃĄbbi funkciÃŗk engedÊlyezÊse hibaelhÃĄrítÃĄs cÊljÃĄbÃŗl", "advanced_settings_troubleshooting_title": "HibaelhÃĄrítÃĄs", @@ -373,6 +419,7 @@ "album_cover_updated": "Album borítÃŗ frissítve", "album_delete_confirmation": "Biztos, hogy ki szeretnÊd tÃļrÃļlni a(z) {album} albumot?", "album_delete_confirmation_description": "Amennyiben ez egy megosztott album, a tÃļbbi felhasznÃĄlÃŗ sem fog tudni tÃļbbÊ hozzÃĄfÊrni.", + "album_deleted": "Album tÃļrÃļlve", "album_info_card_backup_album_excluded": "KIHAGYVA", "album_info_card_backup_album_included": "BELEÉRTVE", "album_info_updated": "Album infÃŗ frissítve", @@ -382,7 +429,9 @@ "album_options": "Album beÃĄllítÃĄsok", "album_remove_user": "FelhasznÃĄlÃŗ tÃļrlÊse?", "album_remove_user_confirmation": "Biztos, hogy el szeretnÊd tÃĄvolítani {user} felhasznÃĄlÃŗt?", + "album_search_not_found": "Nem talÃĄlhatÃŗ a keresÊsnek megfelelő album", "album_share_no_users": "Úgy tÅąnik, hogy mÃĄr minden felhasznÃĄlÃŗval megosztottad ezt az albumot, vagy nincs senki, akivel meg tudnÃĄd osztani.", + "album_summary": "Album ÃļsszefogalalÃŗ", "album_updated": "Album frissÃŧlt", "album_updated_setting_description": "KÃŧldjÃļn email Êrtesítőt, amikor egy megosztott albumhoz Ãēj elemeket adnak hozzÃĄ", "album_user_left": "KilÊptÊl a(z) {album} albumbÃŗl", @@ -398,6 +447,10 @@ "album_with_link_access": "A link birtokÃĄban bÃĄrki lÃĄthatja a fotÃŗkat Ês a szemÊlyeket ebben az albumban.", "albums": "Albumok", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "AlapÊrtelmezett album rendezÊs", + "albums_default_sort_order_description": "AlapÊrtelmezett sorrendezÊs Ãēj albumok lÊtrehozÃĄsÃĄnÃĄl.", + "albums_feature_description": "MÃĄsokkal megoszthatÃŗ elemek gyÅąjtemÊnye.", + "albums_on_device_count": "Albumok az eszkÃļzÃļn ({count})", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden szemÊly", @@ -417,7 +470,9 @@ "app_bar_signout_dialog_title": "KijelentkezÊs", "app_settings": "AlkalmazÃĄs BeÃĄllítÃĄsok", "appears_in": "Itt szerepel", + "apply_count": "Alkalmaz ({count, number})", "archive": "Archívum", + "archive_action_prompt": "{count} elem hozzÃĄadva az Archívumhoz", "archive_or_unarchive_photo": "FotÃŗ archivÃĄlÃĄsa vagy archivÃĄlÃĄsÃĄnak visszavonÃĄsa", "archive_page_no_archived_assets": "Nem talÃĄlhatÃŗ archivÃĄlt elem", "archive_page_title": "Archívum ({count})", @@ -448,6 +503,8 @@ "asset_restored_successfully": "Elem sikeresen helyreÃĄllítva", "asset_skipped": "Kihagyva", "asset_skipped_in_trash": "LomtÃĄrban", + "asset_trashed": "Elem lomtÃĄrba helyezve", + "asset_troubleshoot": "HibajavítÃĄs", "asset_uploaded": "FeltÃļltve", "asset_uploading": "FeltÃļltÊsâ€Ļ", "asset_viewer_settings_subtitle": "A kÊpnÊzegető beÃĄllítÃĄsainak kezelÊse", @@ -455,10 +512,14 @@ "assets": "Elemek", "assets_added_count": "{count, plural, other {# elem}} hozzÃĄadva", "assets_added_to_album_count": "{count, plural, other {# elem}} hozzÃĄadva az albumhoz", - "assets_added_to_name_count": "{count, plural, other {# elem}} hozzÃĄadva {hasName, select, true {a(z) {name}} other {az Ãēj}} albumhoz", + "assets_added_to_albums_count": "{assetTotal, plural, one {# elem} other {# elemek}} hozzÃĄadva {albumTotal, plural, one {# albumhoz} other {# albumokhoz}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Az elem} other {Az elemek}} nem adhatÃŗak hozzÃĄ az albumhoz", + "assets_cannot_be_added_to_albums": "Az {count, plural, one {elemet} other {elemeket}} nem lehet hozzÃĄadni egy albumhoz sem", "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem vÊglegesen tÃļrÃļlve", "assets_deleted_permanently_from_server": "{count} elem vÊglegesen tÃļrÃļlve az Immich szerverről", + "assets_downloaded_failed": "{count, plural, one {# fÃĄjl letÃļltve - {error} fÃĄjl sikertelen} other {# fÃĄjl letÃļltve - {error} fÃĄjl sikertelen}}", + "assets_downloaded_successfully": "{count, plural, one {# fÃĄjl sikeresen letÃļltve} other {# fÃĄjl sikeresen letÃļltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} ÃĄthelyezve a lomtÃĄrba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} vÊglegesen tÃļrÃļlve", "assets_removed_count": "{count, plural, other {# elem}} eltÃĄvolítva", @@ -470,24 +531,30 @@ "assets_trashed_count": "{count, plural, other {# elem}} a lomtÃĄrba helyezve", "assets_trashed_from_server": "{count} elem lomtÃĄrba helyezve az Immich szerveren", "assets_were_part_of_album_count": "{count, plural, other {# elem}} mÃĄr eleve szerepelt az albumban", + "assets_were_part_of_albums_count": "Az {count, plural, one {elem} other {elemek}} mÃĄr az hozzÃĄ lettek adva az albumhoz", "authorized_devices": "EngedÊlyezett EszkÃļzÃļk", "automatic_endpoint_switching_subtitle": "A megadott WiFi-n keresztÃŧl helyi hÃĄlÃŗzaton keresztÃŧl kapcsolÃŗdolik, egyÊbkÊnt az alternatív címeket hasznÃĄlja", "automatic_endpoint_switching_title": "Automatikus URL cím vÃĄltÃĄs", + "autoplay_slideshow": "Automatikus diavetítÊs", "back": "Vissza", "back_close_deselect": "Vissza, bezÃĄrÃĄs, vagy kijelÃļlÊs tÃļrlÊse", + "background_backup_running_error": "HÃĄttÊrben futÃŗ mentÊs folyamatban, kÊzi mentÊs nem indíthatÃŗ", "background_location_permission": "HÃĄttÊrben tÃļrtÊnő helymeghatÃĄrozÃĄsi engedÊly", "background_location_permission_content": "HÃĄlÃŗzatok automatikus vÃĄltÃĄsÃĄhoz az Immich-nek *mindenkÊppen* hozzÃĄ kell fÊrnie a pontos helyzethez, hogy az alkalmazÃĄs le tudja kÊrni a Wi-Fi hÃĄlÃŗzat nevÊt", + "background_options": "HÃĄttÊrbeli futÃĄs beÃĄllítÃĄsai", + "backup": "MentÊs", "backup_album_selection_page_albums_device": "Ezen az eszkÃļzÃļn lÊvő albumok ({count})", "backup_album_selection_page_albums_tap": "Koppints a hozzÃĄadÃĄshoz, duplÃĄn koppints az eltÃĄvolítÃĄshoz", "backup_album_selection_page_assets_scatter": "Egy elem tÃļbb albumban is lehet. EzÊrt a mentÊshez albumokat lehet hozzÃĄadni vagy azokat a mentÊsből kihagyni.", "backup_album_selection_page_select_albums": "VÃĄlassz albumokat", "backup_album_selection_page_selection_info": "ÖsszegzÊs", "backup_album_selection_page_total_assets": "Összes egyedi elem", + "backup_albums_sync": "Backup albumok szinkronizÃĄlÃĄsa", "backup_all": "Összes", - "backup_background_service_backup_failed_message": "Az elemek mentÊse sikertelen. ÚjraprÃŗbÃĄlkozÃĄs...", - "backup_background_service_connection_failed_message": "A szerverhez csatlakozÃĄs sikertelen. ÚjraprÃŗbÃĄlkozÃĄs...", + "backup_background_service_backup_failed_message": "Az elemek mentÊse sikertelen. ÚjraprÃŗbÃĄlkozÃĄsâ€Ļ", + "backup_background_service_connection_failed_message": "A szerverhez csatlakozÃĄs sikertelen. ÚjraprÃŗbÃĄlkozÃĄsâ€Ļ", "backup_background_service_current_upload_notification": "FeltÃļltÊs {filename}", - "backup_background_service_default_notification": "Új elemek ellenőrzÊse...", + "backup_background_service_default_notification": "Új elemek ellenőrzÊseâ€Ļ", "backup_background_service_error_title": "Hiba a mentÊs kÃļzben", "backup_background_service_in_progress_notification": "Elemek mentÊse folyamatbanâ€Ļ", "backup_background_service_upload_failure_notification": "A feltÃļltÊs sikertelen {filename}", @@ -497,6 +564,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "BeÃĄllítÃĄsok megnyitÃĄsa", "backup_controller_page_background_battery_info_link": "Mutasd meg hogyan", "backup_controller_page_background_battery_info_message": "A sikeres hÃĄttÊrben tÃļrtÊnő mentÊshez kÊrjÃŧk, tiltsd le az Immich akkumulÃĄtor optimalizÃĄlÃĄsÃĄt.\n\nMivel ezt a kÃŧlÃļnfÊle eszkÃļzÃļkÃļn mÃĄshogy kell, ezÊrt kÊrjÃŧk, az eszkÃļzÃļd gyÃĄrtÃŗjÃĄtÃŗl tudd meg, hogyan kell.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "AkkumulÃĄtor optimalizÃĄlÃĄs", "backup_controller_page_background_charging": "Csak tÃļltÊs kÃļzben", "backup_controller_page_background_configure_error": "A hÃĄttÊrszolgÃĄltatÃĄs beÃĄllítÃĄsa sikertelen", @@ -506,7 +574,7 @@ "backup_controller_page_background_is_on": "Automatikus mentÊs a hÃĄttÊrben be van kapcsolva", "backup_controller_page_background_turn_off": "HÃĄttÊrszolgÃĄltatÃĄs kikapcsolÃĄsa", "backup_controller_page_background_turn_on": "HÃĄttÊrszolgÃĄltatÃĄs bekapcsolÃĄsa", - "backup_controller_page_background_wifi": "Csak WiFi-n", + "backup_controller_page_background_wifi": "Csak Wi-Fi-n", "backup_controller_page_backup": "MentÊs", "backup_controller_page_backup_selected": "KivÃĄlasztva: ", "backup_controller_page_backup_sub": "Mentett fotÃŗk Ês videÃŗk", @@ -531,18 +599,26 @@ "backup_controller_page_turn_on": "ElőtÊrben mentÊs bekapcsolÃĄsa", "backup_controller_page_uploading_file_info": "FÃĄjl informÃĄciÃŗk feltÃļltÊse", "backup_err_only_album": "Az utolsÃŗ albumot nem tudod tÃļrÃļlni", + "backup_error_sync_failed": "A szinkronizÃĄlÃĄs nem sikerÃŧlt. A biztonsÃĄgi mentÊs nem elkÊszíthető.", "backup_info_card_assets": "elemek", "backup_manual_cancelled": "Megszakítva", "backup_manual_in_progress": "FeltÃļltÊs mÃĄr folyamatban. PrÃŗbÃĄld meg kÊsőbb", "backup_manual_success": "Sikeres", "backup_manual_title": "FeltÃļltÊs ÃĄllapota", + "backup_options": "BiztonsÃĄgi mentÊs beÃĄllítÃĄsai", "backup_options_page_title": "BiztonÃĄgi mentÊs beÃĄllítÃĄsai", "backup_setting_subtitle": "A hÃĄttÊrben Ês előtÊrben mentÊs beÃĄllítÃĄsainak kezelÊse", + "backup_settings_subtitle": "FeltÃļltÊs beÃĄllítÃĄsai", "backward": "Visszafele", + "biometric_auth_enabled": "Biometrikus azonosítÃĄs engedÊlyezve", + "biometric_locked_out": "Ki vagy zÃĄrva a biometrikus azonosítÃĄsbÃŗl", + "biometric_no_options": "Nincsen elÊrhető biometrikus azonosítÃĄs", + "biometric_not_available": "Biometrikus azonosítÃĄs ezen az eszkÃļzÃļn nem elÊrhető", "birthdate_saved": "SzÃŧletÊsnap elmentve", "birthdate_set_description": "A szÃŧletÊs napjÃĄt a rendszer arra hasznÃĄlja, hogy kiírja, hogy a fÊnykÊp kÊszítÊsekor a szemÊly hÃĄny Êves volt.", "blurred_background": "HomÃĄlyos hÃĄttÊr", "bugs_and_feature_requests": "HibabejelentÊs Ês Új FunkciÃŗ KÊrÊse", + "build": "FelÊpítÊs", "build_image": "Build KÊp", "bulk_delete_duplicates_confirmation": "Biztosan kitÃļrÃļlsz {count, plural, one {# duplikÃĄlt elemet} other {# duplikÃĄlt elemet}}? A mÅąvelet a legnagyobb mÊretÅą elemet tartja meg minden hasonlÃŗ csoportbÃŗl Ês minden mÃĄsik duplikÃĄlt elemet kitÃļrÃļl. Ez a mÅąvelet nem visszavonhatÃŗ!", "bulk_keep_duplicates_confirmation": "Biztosan meg szeretnÊl tartani {count, plural, other {# egyező elemet}}? Ez a mÅąvelet az elemek tÃļrlÊse nÊlkÃŧl megszÃŧnteti az Ãļsszes duplikÃĄlt csoportosítÃĄst.", @@ -551,7 +627,7 @@ "cache_settings_clear_cache_button": "GyorsítÃŗtÃĄr kiÃŧrítÊse", "cache_settings_clear_cache_button_title": "KiÃŧríti az alkalmazÃĄs gyorsítÃŗtÃĄrÃĄt. Ez jelentősen kihat az alkalmazÃĄs teljesítmÊnyÊre, amíg a gyorsítÃŗtÃĄr Ãējra nem ÊpÃŧl.", "cache_settings_duplicated_assets_clear_button": "KIÜRÍT", - "cache_settings_duplicated_assets_subtitle": "FotÃŗk Ês videÃŗk, amiket az alkalmazÃĄs fekete listÃĄra tett", + "cache_settings_duplicated_assets_subtitle": "FotÃŗk Ês videÃŗk, amiket az alkalmazÃĄs figyelmen kívÃŧl hagyott", "cache_settings_duplicated_assets_title": "DuplikÃĄlt Elemek ({count})", "cache_settings_statistics_album": "KÊptÃĄr bÊlyegkÊpei", "cache_settings_statistics_full": "Teljes mÊretÅą kÊpek", @@ -568,10 +644,14 @@ "cancel": "MÊgsem", "cancel_search": "KeresÊs megszakítÃĄsa", "canceled": "Megszakítva", + "canceling": "LemondÃĄs", "cannot_merge_people": "SzemÊlyek ÃļsszevonÃĄsa nem sikerÃŧlt", "cannot_undo_this_action": "Ez a mÅąvelet nem visszavonhatÃŗ!", "cannot_update_the_description": "A leírÃĄs megvÃĄltoztatÃĄsa nem sikerÃŧlt", + "cast": "KÃļzvetítÊs", + "cast_description": "KÃļzvetítÊsi cÊlok beÃĄllítÃĄsa", "change_date": "DÃĄtum vÃĄltoztatÃĄsa", + "change_description": "LeírÃĄs megvÃĄltoztatÃĄsa", "change_display_order": "MegjelenítÊsi sorrend megvÃĄltoztatÃĄsa", "change_expiration_time": "LejÃĄrati idő megvÃĄltoztatÃĄsa", "change_location": "Helyszín vÃĄltoztatÃĄsa", @@ -587,6 +667,8 @@ "change_pin_code": "PIN kÃŗd megvÃĄltoztatÃĄsa", "change_your_password": "Jelszavad megvÃĄltoztatÃĄsa", "changed_visibility_successfully": "LÃĄthatÃŗsÃĄg sikeresen megvÃĄltoztatva", + "charging": "TÃļltÊs", + "charging_requirement_mobile_backup": "HÃĄttÊrben mentÊshez szÃŧksÊges, hogy az eszkÃļz tÃļltőn legyen", "check_corrupt_asset_backup": "SÊrÃŧlt elemek keresÊse a mentÊsben", "check_corrupt_asset_backup_button": "EllenőrzÊs", "check_corrupt_asset_backup_description": "Ezt az ellenőtzÊst csak Wi-Fi hÃĄlÃŗzaton futtasd Ês csak akkot, ha mÃĄr az Ãļsszes elem feltÃļltÊsre kerÃŧlt. A folyamat nÊhÃĄny percig is eltarthat.", @@ -596,8 +678,10 @@ "clear": "KitÃļrÃļl", "clear_all": "Alaphelyzet", "clear_all_recent_searches": "LegutÃŗbbi keresÊsek tÃļrlÊse", + "clear_file_cache": "GyorsítÃŗtÃĄr tÃļrlÊse", "clear_message": "Üzenet tÃļrlÊse", "clear_value": "ÉrtÊk tÃļrlÊse", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "JelszÃŗ MegadÃĄsa", "client_cert_import": "ImportÃĄlÃĄs", "client_cert_import_success_msg": "Kliens tanÃēsítvÃĄny importÃĄlva", @@ -625,6 +709,10 @@ "confirm_keep_this_delete_others": "Minden mÃĄs elem a kÊszletben tÃļrlÊsre kerÃŧl, kivÊve ezt az elemet. Biztosan folytatni szeretnÊd?", "confirm_new_pin_code": "Új PIN kÃŗd megerősítÊse", "confirm_password": "JelszÃŗ megerősítÊse", + "confirm_tag_face": "SzeretnÊd ezt az arcot {name}-nak/nek megjelÃļlni?", + "confirm_tag_face_unnamed": "SzeretnÊd ezt az arcot megjelÃļlni?", + "connected_device": "Kapcsolt eszkÃļz", + "connected_to": "KapcsolÃŗdva", "contain": "BelÃŧl", "context": "Kontextus", "continue": "FolytatÃĄs", @@ -633,6 +721,7 @@ "control_bottom_app_bar_delete_from_local": "TÃļrlÊs az eszkÃļzről", "control_bottom_app_bar_edit_location": "Hely MÃŗdosítÃĄsa", "control_bottom_app_bar_edit_time": "DÃĄtum Ês Idő MÃŗdosítÃĄsa", + "control_bottom_app_bar_share_link": "Link megosztÃĄsa", "control_bottom_app_bar_share_to": "MegosztÃĄs Ide", "control_bottom_app_bar_trash_from_immich": "LomtÃĄrba Helyez", "copied_image_to_clipboard": "KÊp a vÃĄgÃŗlapra mÃĄsolva.", @@ -660,10 +749,13 @@ "create_new_user": "Új felhasznÃĄlÃŗ lÊtrehozÃĄsa", "create_shared_album_page_share_add_assets": "ELEMEK HOZZÁADÁSA", "create_shared_album_page_share_select_photos": "FotÃŗk vÃĄlasztÃĄsa", + "create_shared_link": "Megosztott link lÊtrehozÃĄsa", "create_tag": "Címke lÊtrehozÃĄsa", "create_tag_description": "Új címke lÊtrehozÃĄsa. BeÃĄgyazott címkÊk esetÊn add meg a címke teljes elÊrÊsi ÃētvonalÃĄt, beleÊrtve a perjeleket is.", "create_user": "FelhasznÃĄlÃŗ lÊtrehozÃĄsa", "created": "KÊszÃŧlt", + "created_at": "LÊtrehozva", + "creating_linked_albums": "Kapcsolt albumok lÊtrehozÃĄsa...", "crop": "KivÃĄgÃĄs", "curated_object_page_title": "Dolgok", "current_device": "Ez az eszkÃļz", @@ -671,9 +763,11 @@ "current_server_address": "Jelenlegi szerver cím", "custom_locale": "EgyÊni TerÃŧleti BeÃĄllítÃĄs", "custom_locale_description": "DÃĄtumok Ês szÃĄmok formÃĄzÃĄsa a nyelv Ês terÃŧlet szerint", + "custom_url": "Egyedi URL", "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "yyyy MMM dd (E)", "dark": "SÃļtÊt", + "dark_theme": "SÃļtÊt tÊma kapcsolÃĄsa", "date_after": "DÃĄtumtÃŗl", "date_and_time": "DÃĄtum Ês Idő", "date_before": "DÃĄtumig", @@ -681,6 +775,7 @@ "date_of_birth_saved": "SzÃŧletÊsnap sikeresen elmentve", "date_range": "DÃĄtum intervallum", "day": "Nap", + "days": "Napok", "deduplicate_all": "Az Összes DeduplikÃĄlÃĄsa", "deduplication_criteria_1": "KÊp mÊrete bÃĄjtokban", "deduplication_criteria_2": "EXIF adatok mennyisÊge", @@ -689,6 +784,8 @@ "default_locale": "AlapÊrtelmezett TerÃŧleti BeÃĄllítÃĄs", "default_locale_description": "DÃĄtumok Ês szÃĄmok formÃĄzÃĄsa a bÃļngÊsződ terÃŧleti beÃĄllítÃĄsa alapjÃĄn", "delete": "TÃļrlÊs", + "delete_action_confirmation_message": "Biztosan tÃļrÃļlni szeretnÊd ezt az elemet? Így az elem a szerver lomtÃĄrÃĄba kerÃŧl, Ês a megkÊrdezi, hogy tÃļrÃļlni szeretnÊd-e a helyi mÃĄsolatot is", + "delete_action_prompt": "{count} tÃļrÃļlve", "delete_album": "Album tÃļrlÊse", "delete_api_key_prompt": "Biztosan tÃļrÃļlni szeretnÊd ezt az API kulcsot?", "delete_dialog_alert": "Ezek az elemek vÊglegesen tÃļrÃļlve lesznek Immich-ről Ês az eszkÃļzÃļdről is", @@ -702,9 +799,12 @@ "delete_key": "Kulcs tÃļrlÊse", "delete_library": "KÊptÃĄr TÃļrlÊse", "delete_link": "Link tÃļrlÊse", + "delete_local_action_prompt": "{count} tÃļrÃļlve az eszkÃļzről", "delete_local_dialog_ok_backed_up_only": "Csak a BiztonsÃĄgi MentÊs TÃļrlÊse", "delete_local_dialog_ok_force": "TÃļrlÊs MindenkÊpp", "delete_others": "TÃļbbi tÃļrlÊse", + "delete_permanently": "TÃļrlÊs vÊglegesen", + "delete_permanently_action_prompt": "{count} tÃļrÃļlve vÊglegesen", "delete_shared_link": "Megosztott link tÃļrlÊse", "delete_shared_link_dialog_title": "Megosztott Link TÃļrlÊse", "delete_tag": "Címke tÃļrlÊse", @@ -715,11 +815,14 @@ "description": "LeírÃĄs", "description_input_hint_text": "LeírÃĄs hozzÃĄadÃĄsa...", "description_input_submit_error": "Nem sikerÃŧlt frissíteni a leírÃĄst. TovÃĄbbi informÃĄciÃŗÃŠrt kÊrjÃŧk, nÊzd meg az esemÊnynaplÃŗt", + "deselect_all": "KijelÃļlÊs megszÃŧntetÊs", "details": "RÊszletek", "direction": "IrÃĄny", "disabled": "Letiltott", "disallow_edits": "MÃŗdosítÃĄsok letiltÃĄsa", + "discord": "Discord", "discover": "Felfedez", + "discovered_devices": "Felfedezett eszkÃļzÃļk", "dismiss_all_errors": "Minden hiba elvetÊse", "dismiss_error": "Hiba elvetÊse", "display_options": "MegjelenítÊsi beÃĄllítÃĄsok", @@ -730,6 +833,7 @@ "documentation": "DokumentÃĄciÃŗ", "done": "KÊsz", "download": "LetÃļltÊs", + "download_action_prompt": "{count} elem letÃļltÊse", "download_canceled": "LetÃļltÊs megszakítva", "download_complete": "LetÃļltÊs kÊsz", "download_enqueue": "LetÃļltÊs sorba ÃĄllítva", @@ -744,8 +848,8 @@ "download_settings_description": "Elemek letÃļltÊsÊvel kapcsolatos beÃĄllítÃĄsok kezelÊse", "download_started": "LetÃļltÊs megkezdve", "download_sucess": "Sikeres letÃļltÊs", - "download_sucess_android": "MÊdia letÃļltve a DCIM/Immich mappÃĄba\n", - "download_waiting_to_retry": "VÃĄrakozÃĄs", + "download_sucess_android": "MÊdia letÃļltve a DCIM/Immich mappÃĄba", + "download_waiting_to_retry": "VÃĄrÃĄs az ÃējraprÃŗbÃĄlkozÃĄsra", "downloading": "LetÃļltÊs", "downloading_asset_filename": "{filename} elem letÃļltÊse", "downloading_media": "MÊdia letÃļltÊse", @@ -756,8 +860,14 @@ "edit": "SzerkesztÊs", "edit_album": "Album mÃŗdosítÃĄsa", "edit_avatar": "ProfilkÊp mÃŗdosítÃĄsa", + "edit_birthday": "SzÃŧletÊsnap szerkesztÊse", "edit_date": "DÃĄtum mÃŗdosítÃĄsa", "edit_date_and_time": "DÃĄtum Ês idő mÃŗdosítÃĄsa", + "edit_date_and_time_action_prompt": "{count} dÃĄtum Ês idő mÃŗdosítva", + "edit_date_and_time_by_offset": "DÃĄtum mÃŗdosítÃĄsa időeltolÃĄssal", + "edit_date_and_time_by_offset_interval": "Új dÃĄtumtartomÃĄny: {from} - {to}", + "edit_description": "LeírÃĄs szerkesztÊse", + "edit_description_prompt": "KÊrlek vÃĄlassz egy Ãēj leírÃĄst:", "edit_exclusion_pattern": "KizÃĄrÃĄsi minta (pattern) mÃŗdosítÃĄsa", "edit_faces": "Arcok mÃŗdosítÃĄsa", "edit_import_path": "ImportÃĄlÃĄsi Ãētvonal mÃŗdosítÃĄsa", @@ -765,6 +875,7 @@ "edit_key": "Kulcs mÃŗdosítÃĄsa", "edit_link": "Link mÃŗdosítÃĄsa", "edit_location": "Hely mÃŗdosítÃĄsa", + "edit_location_action_prompt": "{count} hely vÃĄltoztatva", "edit_location_dialog_title": "Hely", "edit_name": "NÊv mÃŗdosítÃĄsa", "edit_people": "SzemÊlyek mÃŗdosítÃĄsa", @@ -777,18 +888,28 @@ "editor_close_without_save_title": "Szerkesztő bezÃĄrÃĄsa?", "editor_crop_tool_h2_aspect_ratios": "OldalarÃĄnyok", "editor_crop_tool_h2_rotation": "ForgatÃĄs", + "email": "E-mail", + "email_notifications": "E-mail ÊrtesítÊsek", + "empty_folder": "Ez a mappa Ãŧres", "empty_trash": "LomtÃĄr ÃŧrítÊse", "empty_trash_confirmation": "Biztosan kiÃŧríted a lomtÃĄrat? Ez az Immich lomtÃĄrÃĄban lÊvő Ãļsszes elemet vÊglegesen tÃļrli.\nEz a mÅąvelet nem visszavonhatÃŗ!", "enable": "EngedÊlyezÊs", + "enable_backup": "BiztonsÃĄgi mentÊs bekapcsolÃĄsa", + "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítÃĄs engedÊlyezÊsÊhez", "enabled": "EngedÊlyezve", "end_date": "VÊg dÃĄtum", "enqueued": "Sorba ÃĄllítva", - "enter_wifi_name": "Add meg a WiFi hÃĄlÃŗzat nevÊt", + "enter_wifi_name": "Add meg a Wi-Fi hÃĄlÃŗzat nevÊt", + "enter_your_pin_code": "Add meg a jelszavad", + "enter_your_pin_code_subtitle": "Add meg a PIN kÃŗdodat a zÃĄrolt mappa megnyitÃĄsÃĄhoz", "error": "Hiba", "error_change_sort_album": "Album sorbarendezÊsÊnek megvÃĄltoztatÃĄsa sikertelen", "error_delete_face": "Hiba az arc tÃļrlÊse sorÃĄn", + "error_getting_places": "Hiba a helyek betÃļltÊsekor", "error_loading_image": "Hiba a kÊp betÃļltÊse kÃļzben", + "error_loading_partners": "Hiba a partnerek betÃļltÊsÊnÊl: {error}", "error_saving_image": "Hiba: {error}", + "error_tag_face_bounding_box": "Hiba az arc megjelÃļlÊse kÃļzben - nem elÊrhetőek a hatÃĄrolÃŗ koordinÃĄtÃĄk", "error_title": "Hiba - valami fÊlresikerÃŧlt", "errors": { "cannot_navigate_next_asset": "Nem lehet a kÃļvetkező elemhez navigÃĄlni", @@ -816,15 +937,19 @@ "failed_to_keep_this_delete_others": "Nem sikerÃŧlt megtartani ezt az elemet, Ês a tÃļbbi elemet tÃļrÃļlni", "failed_to_load_asset": "Elem betÃļltÊse sikertelen", "failed_to_load_assets": "Elemek betÃļltÊse sikertelen", + "failed_to_load_notifications": "ÉrtesítÊsek betÃļltÊse sikertelen", "failed_to_load_people": "SzemÊlyek betÃļltÊse sikertelen", "failed_to_remove_product_key": "TermÊkkulcs eltÃĄvolítÃĄsa sikertelen", + "failed_to_reset_pin_code": "PIN kÃŗd visszaÃĄllítÃĄsa sikertelen", "failed_to_stack_assets": "Elemek csoportosítÃĄsa sikertelen", "failed_to_unstack_assets": "Csoportosított elemek szÊtszedÊse sikertelen", + "failed_to_update_notification_status": "ÉrtesítÊs stÃĄtusz frissítÊse sikertelen", "import_path_already_exists": "Ez az importÃĄlÃĄsi Ãētvonal mÃĄr lÊtezik.", "incorrect_email_or_password": "Helytelen email vagy jelszÃŗ", "paths_validation_failed": "A(z) {paths, plural, one {# elÊrÊsi Ãētvonal} other {# elÊrÊsi Ãētvonal}} ÊrvÊnyesítÊse sikertelen", "profile_picture_transparent_pixels": "ProfilkÊpek nem tartalmazhatnak ÃĄtlÃĄtszÃŗ pixeleket. KÃļzelíts rÃĄ Ês/vagy mozgasd a kÊpet.", "quota_higher_than_disk_size": "Az elÊrhető lemezmÊretnÊl nagyobb kvÃŗtÃĄt ÃĄllítottÃĄl be", + "something_went_wrong": "Valami baj tÃļrtÊnt", "unable_to_add_album_users": "FelhasznÃĄlÃŗk albumhoz adÃĄsa sikertelen", "unable_to_add_assets_to_shared_link": "Elemeket megosztott linkhez adÃĄsa sikertelen", "unable_to_add_comment": "HozzÃĄszÃŗlÃĄs sikertelen", @@ -836,6 +961,7 @@ "unable_to_archive_unarchive": "Az elem {archived, select, true {archivÃĄlÃĄsa} other {kivÊtele az archívumbÃŗl}} sikertelen", "unable_to_change_album_user_role": "Az album felhasznÃĄlÃŗi jogkÃļrÊnek megvÃĄltoztatÃĄsa sikertelen", "unable_to_change_date": "DÃĄtum megvÃĄltoztatÃĄsa sikertelen", + "unable_to_change_description": "LeírÃĄs mÃŗdosítÃĄsa sikertelen", "unable_to_change_favorite": "Az elem kedvenc ÃĄllapotÃĄnak megvÃĄltoztatÃĄsa sikertelen", "unable_to_change_location": "Hely megvÃĄltoztatÃĄsa sikertelen", "unable_to_change_password": "JelszÃŗ megvÃĄltoztatÃĄsa sikertelen", @@ -879,6 +1005,7 @@ "unable_to_remove_partner": "Partner eltÃĄvolítÃĄsa sikertelen", "unable_to_remove_reaction": "ReakciÃŗ eltÃĄvolítÃĄsa sikertelen", "unable_to_reset_password": "JelszÃŗ visszaÃĄllítÃĄsa sikertelen", + "unable_to_reset_pin_code": "A PIN-kÃŗd visszaÃĄllítÃĄsa nem sikerÃŧlt", "unable_to_resolve_duplicate": "DuplikÃĄtum feloldÃĄsa sikertelen", "unable_to_restore_assets": "Elemek visszaÃĄllítÃĄsa sikertelen", "unable_to_restore_trash": "Az Ãļsszes elem visszaÃĄllítÃĄsa sikertelen", @@ -906,7 +1033,9 @@ "unable_to_update_user": "FelhasznÃĄlÃŗ mÃŗdosítÃĄsa sikertelen", "unable_to_upload_file": "FÃĄjlfeltÃļltÊs sikertelen" }, + "exif": "Exif", "exif_bottom_sheet_description": "LeírÃĄs HozzÃĄadÃĄsa...", + "exif_bottom_sheet_description_error": "Hiba a leírÃĄs frissítÊsekor", "exif_bottom_sheet_details": "RÉSZLETEK", "exif_bottom_sheet_location": "HELY", "exif_bottom_sheet_people": "EMBEREK", @@ -924,20 +1053,26 @@ "explorer": "BÃļngÊsző", "export": "ExportÃĄlÃĄs", "export_as_json": "ExportÃĄlÃĄs JSON formÃĄtumban", + "export_database": "AdatbÃĄzis ExportÃĄlÃĄsa", + "export_database_description": "Az SQLite adatbÃĄzis exportÃĄlÃĄsa", "extension": "KiterjesztÊs", "external": "KÃŧlső KÊptÃĄr", "external_libraries": "KÃŧlső KÊptÃĄrak", "external_network": "KÃŧlső hÃĄlÃŗzat", - "external_network_sheet_info": "Ha nem vagy a megadott WiFi hÃĄlÃŗzathoz csatlakozva, akkor az alkalmazÃĄs az alÃĄbbi URL címeken fogja elÊrni a szervert, fentről lefelÊ haladva", + "external_network_sheet_info": "Ha nem vagy a megadott Wi-Fi hÃĄlÃŗzathoz csatlakozva, akkor az alkalmazÃĄs az alÃĄbbi URL címeken fogja elÊrni a szervert, fentről lefelÊ haladva", "face_unassigned": "Nincs hozzÃĄrendelve", "failed": "Sikertelen", + "failed_to_authenticate": "AutentikÃĄciÃŗ sikertelen", "failed_to_load_assets": "Nem sikerÃŧlt betÃļlteni az elemeket", + "failed_to_load_folder": "Mappa betÃļltÊse sikertelen", "favorite": "Kedvenc", + "favorite_action_prompt": "{count} hozzÃĄadva a Kedvencekhez", "favorite_or_unfavorite_photo": "FotÃŗ kedvencnek jelÃļlÊse vagy annak visszavonÃĄsa", "favorites": "Kedvencek", "favorites_page_no_favorites": "Nem talÃĄlhatÃŗ kedvencnek jelÃļlt elem", "feature_photo_updated": "CímlapkÊp frissítve", "features": "Jellemzők", + "features_in_development": "Folyamatban lÊvő fejlesztÊsek", "features_setting_description": "Az alkalmazÃĄs jellemzőinek kezelÊse", "file_name": "FÃĄjlnÊv", "file_name_or_extension": "FÃĄjlnÊv vagy kiterjesztÊs", @@ -945,18 +1080,28 @@ "filetype": "FÃĄjltípus", "filter": "SzÅąrő", "filter_people": "SzemÊlyek szÅąrÊse", + "filter_places": "Helyszínek szÅąrÊse", "find_them_fast": "NÊv alapjÃĄn keresÊssel gyorsan megtalÃĄlhatÃŗak", + "first": "Első", "fix_incorrect_match": "HibÃĄs talÃĄlat javítÃĄsa", + "folder": "Mappa", + "folder_not_found": "Mappa nem talÃĄlhatÃŗ", "folders": "MappÃĄk", "folders_feature_description": "A fÃĄjlrendszerben lÊvő fÊnykÊpek Ês videÃŗk mappanÊzetben valÃŗ bÃļngÊszÊse", + "forgot_pin_code_question": "Elfelejtetted a PIN kÃŗdod?", "forward": "Előre", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ez a funkciÃŗ a Google-től tÃļlti be a mÅąkÃļdÊsÊhez szÃŧksÊges kÃŧlső adatokat.", "general": "ÁltalÃĄnos", + "geolocation_instruction_location": "Kattints egy elemre, amelynek ismert a helyszíne a pozíciÃŗ kivÃĄlasztÃĄsÃĄhoz, vagy vÃĄlassz a tÊrkÊpen", "get_help": "SegítsÊgkÊrÊs", - "get_wifiname_error": "Nem sikerÃŧlt lekÊrni a Wi-Fi nevÊt. Győződj meg rÃŗla, hogy megadtad a szÃŧksÊges engedÊlyeket Ês csatlakoztÃĄl egy Wi-Fi hÃĄlÃŗzathoz.", + "get_wifiname_error": "Nem sikerÃŧlt lekÊrni a Wi-Fi nevÊt. Győződj meg rÃŗla, hogy megadtad a szÃŧksÊges engedÊlyeket Ês csatlakoztÃĄl egy Wi-Fi hÃĄlÃŗzathoz", "getting_started": "Kezdő LÊpÊsek", "go_back": "VisszalÊpÊs", "go_to_folder": "UgrÃĄs a mappÃĄhoz", "go_to_search": "UgrÃĄs a keresÊshez", + "gps": "GPS", + "gps_missing": "Nincs GPS", "grant_permission": "EngedÊly megadÃĄsa", "group_albums_by": "Albumok csoportosítÃĄsa...", "group_country": "CsoportosítÃĄs orszÃĄg szerint", @@ -967,6 +1112,9 @@ "haptic_feedback_switch": "RezgÊses visszajelzÊs engedÊlyezÊse", "haptic_feedback_title": "RezgÊses VisszajelzÊs", "has_quota": "KvÃŗta", + "hash_asset": "Elem hash-elÊse", + "hashed_assets": "Hash-elt elemek", + "hashing": "Hash-elÊs folyamatban", "header_settings_add_header_tip": "FejlÊc HozzÃĄadÃĄsa", "header_settings_field_validator_msg": "Az ÊrtÊk nem lehet Ãŧres", "header_settings_header_name_input": "FejlÊc neve", @@ -981,9 +1129,9 @@ "hide_person": "SzemÊly elrejtÊse", "hide_unnamed_people": "NÊv nÊlkÃŧli szemÊlyek elrejtÊse", "home_page_add_to_album_conflicts": "{added} elem hozzÃĄadva a(z) \"{album}\" albumhoz. {failed} elem mÃĄr eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket mÊg nem lehet albumba tenni. Kihagyjuk.", + "home_page_add_to_album_err_local": "Helyi elemeket mÊg nem lehet albumba tenni, ki lesznek hagyva", "home_page_add_to_album_success": "{added} elem hozzÃĄadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "MÊg nem lehet a partner elemeit albumokhoz adni, Ãēghogy kihagyjuk.", + "home_page_album_err_partner": "MÊg nem lehet a partner elemeit albumokhoz adni, ki lesznek hagyva", "home_page_archive_err_local": "Helyi elemek archivÃĄlÃĄsa mÊg nem tÃĄmogatott, Ãēgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archivÃĄlni, Ãēgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal ÃļsszeÃĄllítÃĄsa", @@ -991,11 +1139,16 @@ "home_page_delete_remote_err_local": "Helyi elemek vannak tÃĄvoli tÃļrlÊsre kivÃĄlasztva, Ãēgyhogy ezeket kihagyjuk", "home_page_favorite_err_local": "Helyi elemeket mÊg nem lehet a kedvencek kÃļzÊ tenni, Ãēgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit mÊg nem lehet a kedvencek kÃļzÊ tenni, Ãēgyhogy ezeket kihagyjuk", - "home_page_first_time_notice": "Ha most hasznÃĄlod előszÃļr az alkalmazÃĄst, akkor ahhoz, hogy megjelenjenek a fotÃŗk Ês a videÃŗk az idővonaladon, ÃĄllítsd be, hogy melyik albumaidrÃŗl kÊszÃŧljÃļn biztonsÃĄgi mentÊs.", + "home_page_first_time_notice": "Ha most hasznÃĄlod előszÃļr az alkalmazÃĄst, a fotÃŗk Ês videÃŗk megjelenítÊsÊhez az idővonaladon, ÃĄllítsd be, hogy melyik albumaidrÃŗl kÊszÃŧljÃļn biztonsÃĄgi mentÊs", + "home_page_locked_error_local": "A Helyi elemek nem mozgathatÃŗak a zÃĄrolt mappÃĄba, ki lesznek hagyva", + "home_page_locked_error_partner": "Partner elemek nem mozgathatÃŗak a zÃĄrolt mappÃĄba, ÃĄtugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket kÊszíteni, Ãēgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltÃļlteni, Ãēgyhogy kihagyjuk", "host": "KiszolgÃĄlÃŗ", "hour": "Óra", + "hours": "ÓrÃĄk", + "id": "AzonosítÃŗ", + "idle": "ÜresjÃĄrat", "ignore_icloud_photos": "iCloud fotÃŗk figyelmen kívÃŧl hagyÃĄsa", "ignore_icloud_photos_description": "Az iCloud-ban tÃĄrolt fotÃŗk nem lesznek feltÃļltve az Immich szerverre", "image": "KÊp", @@ -1035,6 +1188,12 @@ "invalid_date_format": "ÉrvÊnytelen dÃĄtumformÃĄtum", "invite_people": "SzemÊlyek MeghívÃĄsa", "invite_to_album": "MeghívÃĄs az albumba", + "ios_debug_info_fetch_ran_at": "LetÃļltÊs futtatva {dateTime}", + "ios_debug_info_last_sync_at": "UtoljÃĄra szinkronizÃĄlva {dateTime}", + "ios_debug_info_no_processes_queued": "Nincs a sorban hÃĄttÊrfolyamat jelenleg", + "ios_debug_info_no_sync_yet": "MÊg nem futott szinkronizÃĄlÃŗ hÃĄttÊrfolyamat", + "ios_debug_info_processes_queued": "{count, plural, one {{count} hÃĄttÊrfolyamat előkÊszítve} other {{count} hÃĄttÊrfolyamat előkÊszítve}}", + "ios_debug_info_processing_ran_at": "A feldolgozÃĄs ekkor futott: {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1043,11 +1202,17 @@ "kept_this_deleted_others": "Ez az elem Ês a tÃļrÃļltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "BillentyÅąparancsok", "language": "Nyelv", + "language_no_results_subtitle": "PrÃŗbÃĄld mÃŗdosítani a szavaidat a keresÊsnÊl", + "language_no_results_title": "Nem talÃĄlhatÃŗ nyelv", + "language_search_hint": "Nyelvek keresÊse...", "language_setting_description": "VÃĄlaszd ki preferÃĄlt nyelvet", + "large_files": "Nagy FÃĄjlok", + "last": "UtolsÃŗ", "last_seen": "UtoljÃĄra lÃĄttuk", "latest_version": "Legfrissebb VerziÃŗ", "latitude": "SzÊlessÊg", "leave": "ElhagyÃĄs", + "leave_album": "Album elhagyÃĄsa", "lens_model": "Objektív modell", "let_others_respond": "MÃĄsok is reagÃĄlhatnak", "level": "Szint", @@ -1059,26 +1224,35 @@ "library_page_sort_created": "LÊtrehozÃĄs ideje", "library_page_sort_last_modified": "UtolsÃŗ mÃŗdosítÃĄs ideje", "library_page_sort_title": "Album címe", + "licenses": "Licencek", "light": "VilÃĄgos", + "like": "Tetszik", "like_deleted": "ReakciÃŗ tÃļrÃļlve", "link_motion_video": "Motion videÃŗ hozzÃĄrendelÊse", - "link_options": "Link beÃĄllítÃĄsok", "link_to_oauth": "CsatlakoztatÃĄs OAuth-hoz", "linked_oauth_account": "Csatlakoztatott OAuth felhasznÃĄlÃŗ", "list": "Lista", "loading": "BetÃļltÊs", "loading_search_results_failed": "KeresÊsi eredmÊnyek betÃļltÊse sikertelen", + "local": "Helyi", + "local_asset_cast_failed": "Nem lehet olyan elemet vetíteni, ami nincs a szerverre feltÃļltve", + "local_assets": "Helyi Elemek", + "local_media_summary": "Helyi mÊdia ÃļsszegzÊs", "local_network": "Helyi hÃĄlÃŗzat", "local_network_sheet_info": "Az alkalmazÊs ezen az URL címen fogja elÊrni a szervert, ha a megadott WiFi hÃĄlÃŗzathoz van csatlankozva", "location_permission": "HelymeghatÃĄrozÃĄsi engedÊly", - "location_permission_content": "HÃĄlÃŗzatok automatikus vÃĄltÃĄsÃĄhoz az Immich-nek *mindenkÊppen* hozzÃĄ kell fÊrnie a pontos helyzethez, hogy az alkalmazÃĄs le tudja kÊrni a Wi-Fi hÃĄlÃŗzat nevÊt", + "location_permission_content": "A HÃĄlÃŗzatok automatikus vÃĄltÃĄsÃĄhoz az Immich-nek szÃŧksÊge van a pontos helymeghatÃĄrozÃĄsra, hogy az alkalmazÃĄs le tudja kÊrni a Wi-Fi hÃĄlÃŗzat nevÊt", "location_picker_choose_on_map": "VÃĄlassz a tÊrkÊpen", "location_picker_latitude_error": "ÉrvÊnyes szÊlessÊgi kÃļrt írj be", "location_picker_latitude_hint": "Ide írd a szÊlessÊgi kÃļrt", "location_picker_longitude_error": "ÉrvÊnyes hosszÃēsÃĄgi kÃļrt írj be", "location_picker_longitude_hint": "Ide írd a hosszÃēsÃĄgi kÃļrt", + "lock": "ZÃĄrolÃĄs", + "locked_folder": "ZÃĄrolt mappa", + "log_detail_title": "NaplÃŗk rÊszletei", "log_out": "KijelentkezÊs", "log_out_all_devices": "KijelentkezÊs Minden EszkÃļzÃļn", + "logged_in_as": "BelÊpve: {user} nÊven", "logged_out_all_devices": "Minden eszkÃļz kijelentkeztetve", "logged_out_device": "EszkÃļz kijelentkeztetve", "login": "BejelentkezÊs", @@ -1093,7 +1267,7 @@ "login_form_err_invalid_url": "ÉrvÊnytelen cím", "login_form_err_leading_whitespace": "Az első karakter szÃŗkÃļz", "login_form_err_trailing_whitespace": "Az utolsÃŗ karakter szÃŗkÃļz", - "login_form_failed_get_oauth_server_config": "Nem sikerÃŧlt az OAuth bejelentkezÊs. Ellenőrizd a szerver címÊt.", + "login_form_failed_get_oauth_server_config": "Nem sikerÃŧlt az OAuth bejelentkezÊs. Ellenőrizd a szerver URL-t", "login_form_failed_get_oauth_server_disable": "OAuth bejelentkezÊs nem elÊrhető ezen a szerveren", "login_form_failed_login": "Hiba a bejelentkezÊs kÃļzben, ellenőrizd a szerver címÊt, az emailt Ês a jelszÃŗt", "login_form_handshake_exception": "SSL KÊzfogÃĄsi Hiba tÃļrÊnt. EngedÊlyezd az ÃļnalÃĄÃ­rt tanÃēsítvÊnyokat a beÃĄllítÃĄsokban, hogy ha ÃļnalÃĄÃ­rt tanÃēsítvÃĄnyt hasznÃĄlsz.", @@ -1106,6 +1280,7 @@ "login_password_changed_success": "JelszÃŗ sikeresen mÃŗdosítva", "logout_all_device_confirmation": "Biztos, hogy minden eszkÃļzÃļn ki szeretnÊl jelentkezni?", "logout_this_device_confirmation": "Biztos, hogy ki szeretnÊl jelentkezni ezen az eszkÃļzÃļn?", + "logs": "NaplÃŗk", "longitude": "HosszÃēsÃĄg", "look": "MegjelenítÊs", "loop_videos": "VideÃŗk ismÊtlÊse", @@ -1113,6 +1288,7 @@ "main_branch_warning": "Fejlesztői verziÃŗt hasznÃĄlsz. Javasoljuk a stabil verziÃŗ hasznÃĄlatÃĄt!", "main_menu": "FőmenÃŧ", "make": "GyÃĄrtÃŗ", + "manage_geolocation": "Helyadatok kezelÊse", "manage_shared_links": "MegosztÃĄsi linkek kezelÊse", "manage_sharing_with_partners": "Partnerekkel valÃŗ megosztÃĄs kezelÊse", "manage_the_app_settings": "AlkalmazÃĄs beÃĄllítÃĄsainak kezelÊse", @@ -1121,8 +1297,7 @@ "manage_your_devices": "Bejelentkezett eszkÃļzÃļk kezelÊse", "manage_your_oauth_connection": "OAuth kapcsolÃŗdÃĄs kezelÊse", "map": "TÊrkÊp", - "map_assets_in_bound": "{count} fotÃŗ", - "map_assets_in_bounds": "{count} fotÃŗ", + "map_assets_in_bounds": "{count, plural, =0 {Nincs fotÃŗ ezen a terÃŧleten} one {# fotÃŗ} other {# fotÃŗ}}", "map_cannot_get_user_location": "A helymeghatÃĄrozÃĄs nem sikerÃŧlt", "map_location_dialog_yes": "Igen", "map_location_picker_page_use_location": "KivÃĄlasztott hely hasznÃĄlata", @@ -1130,7 +1305,6 @@ "map_location_service_disabled_title": "HelymeghatÃĄrozÃĄs SzolgÃĄltatÃĄs letiltva", "map_marker_for_images": "{country}, {city} helyen kÊszÃŧlt kÊpek tÊrkÊpjelÃļlője", "map_marker_with_image": "TÊrkÊpjelÃļlő kÊppel", - "map_no_assets_in_bounds": "Nincsenek fotÃŗk a kÃļrnyÊken", "map_no_location_permission_content": "A helymeghatÃĄrozÃĄst engedÊlyezni kell a jelenlegi helyednÊl lÊvő elemek megjelenítÊsÊhez. SzeretnÊd most engedÊlyezni?", "map_no_location_permission_title": "HelymeghatÃĄrozÃĄs letiltva", "map_settings": "TÊrkÊp beÃĄllítÃĄsok", @@ -1145,7 +1319,11 @@ "map_settings_only_show_favorites": "Csak Kedvencek MutatÃĄsa", "map_settings_theme_settings": "TÊrkÊp TÊmÃĄja", "map_zoom_to_see_photos": "Kicsinyítsd, hogy lÃĄss fÊnykÊpeket", + "mark_all_as_read": "Összes megjelÃļlÊse olvasottkÊnt", + "mark_as_read": "MegjelÃļlÊs olvasottkÊnt", + "marked_all_as_read": "Összes megjelÃļlve olvasottkÊnt", "matches": "Azonosak", + "matching_assets": "KapcsolÃŗdÃŗ elemek", "media_type": "MÊdiatípus", "memories": "EmlÊkek", "memories_all_caught_up": "NaprakÊsz vagy", @@ -1164,11 +1342,19 @@ "merged_people_count": "Összevonva {count, plural, other {# szemÊly}}", "minimize": "KicsinyítÊs", "minute": "Perc", + "minutes": "Percek", "missing": "HiÃĄnyzÃŗk", "model": "Modell", "month": "HÃŗnap", "monthly_title_text_date_format": "y MMMM", "more": "TovÃĄbbiak", + "move": "ÁthelyezÊs", + "move_off_locked_folder": "ÁtmozgatÃĄs a zÃĄrolt mappÃĄbÃŗl", + "move_to_lock_folder_action_prompt": "{count} hozzÃĄadva a zÃĄrolt mappÃĄhoz", + "move_to_locked_folder": "ÁthelyezÊs a zÃĄrolt mappÃĄba", + "move_to_locked_folder_confirmation": "Ezek a kÊpek Ês videÃŗk az Ãļsszes albumbÃŗl kikerÃŧlnek, Ês csak a zÃĄrolt mappÃĄban lesznek elÊrhetőek", + "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archivÃĄlva", + "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} mÃĄsik kÃļnyvtÃĄrba kÃļltÃļztetve", "moved_to_trash": "Áthelyezve a lomtÃĄrba", "multiselect_grid_edit_date_time_err_read_only": "Csak-olvashatÃŗ elem(ek) dÃĄtuma nem mÃŗdosíthatÃŗ, ezÊrt kihagyjuk", "multiselect_grid_edit_gps_err_read_only": "Csak-olvashatÃŗ elem(ek) helye nem mÃŗdosíthatÃŗ, ezÊrt kihagyjuk", @@ -1176,6 +1362,10 @@ "my_albums": "SajÃĄt albumaim", "name": "NÊv", "name_or_nickname": "NÊv vagy becenÊv", + "network_requirement_photos_upload": "Mobil adatforgalmat hasznÃĄljon a fÊnykÊpek biztonsÃĄgi mentÊsÊhez", + "network_requirement_videos_upload": "Mobil adatforgalmat hasznÃĄljon a videÃŗk biztonsÃĄgi mentÊsÊhez", + "network_requirements": "HÃĄlÃŗzati kÃļvetelmÊnyek", + "network_requirements_updated": "A hÃĄlÃŗzat megvÃĄltozott, a biztonsÃĄgi mentÊsi sor visszaÃĄllítÃĄsa", "networking_settings": "HÃĄlÃŗzat", "networking_subtitle": "Szerver vÊgpont beÃĄllítÃĄsok kezelÊse", "never": "Soha", @@ -1184,6 +1374,8 @@ "new_password": "Új jelszÃŗ", "new_person": "Új szemÊly", "new_pin_code": "Új PIN kÃŗd", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zÃĄrolt mappÃĄt. Hozz lÊtre egy jelszÃŗt a mappa biztonsÃĄgos elÊrÊsÊhez", + "new_timeline": "Új idővonal", "new_user_created": "Új felhasznÃĄlÃŗ lÊtrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "LegÃējabb előszÃļr", @@ -1196,20 +1388,31 @@ "no_archived_assets_message": "ArchivÃĄld a fÊnykÊpeket Ês videÃŗkat, hogy elrejtsd azokat a KÊpek nÊzetből", "no_assets_message": "KATTINTS AZ ELSŐ FÉNYKÉP FELTÖLTÉSÉHEZ", "no_assets_to_show": "Nincs megjeleníthető elem", + "no_cast_devices_found": "Nem talÃĄlhatÃŗ eszkÃļz vetítÊshez", + "no_checksum_local": "Nincs elÊrhető ellenőrzőÃļsszeg - a helyi eszkÃļzÃļk nem kÊrhetők le", + "no_checksum_remote": "Nincs elÊrhető ellenőrzőÃļsszeg - a tÃĄvoli eszkÃļz nem kÊrhető le", "no_duplicates_found": "Nem talÃĄlhatÃŗk duplikÃĄtumok.", "no_exif_info_available": "Nincs elÊrhető Exif informÃĄciÃŗ", "no_explore_results_message": "TÃļlts fel tÃļbb kÊpet, hogy bÃļngÊszhesd a gyÅąjtemÊnyed.", "no_favorites_message": "Add hozzÃĄ a kedvencekhez, hogy gyorsan megtalÃĄld a legjobb kÊpeidet Ês videÃŗidat", "no_libraries_message": "Hozz lÊtre kÃŧlső kÊptÃĄrat a fÊnykÊpeid Ês videÃŗid megtekintÊsÊhez", + "no_local_assets_found": "Nem talÃĄlhatÃŗk helyi eszkÃļzÃļk ezzel az ellenőrzőÃļsszeggel", + "no_locked_photos_message": "A zÃĄrolt mappÃĄban elhelyezett fotÃŗk Ês videÃŗk rejtettek, Ês nem jelennek meg a kÃļnyvtÃĄrad bÃļngÊszÊse vagy keresÊse kÃļzben sem.", "no_name": "Nincs NÊv", + "no_notifications": "Nincsenek ÊrtesítÊsek", + "no_people_found": "Nem talÃĄlhatÃŗ szemÊly", "no_places": "Nincsenek helyek", + "no_remote_assets_found": "Nem talÃĄlhatÃŗk tÃĄvoli eszkÃļzÃļk ezzel az ellenőrzőÃļsszeggel", "no_results": "Nincs talÃĄlat", "no_results_description": "PrÃŗbÃĄlkozz szinonimÃĄkkal vagy ÃĄltalÃĄnosabb kulcsszavakkal", "no_shared_albums_message": "Hozz lÊtre egy Ãēj albumot, hogy megoszthasd fÊnykÊpeid Ês videÃŗid mÃĄsokkal", + "no_uploads_in_progress": "Nincs folyamatban lÊvő feltÃļltÊs", + "not_available": "N/A", "not_in_any_album": "Nincs albumban", "not_selected": "Nincs kivÃĄlasztva", "note_apply_storage_label_to_previously_uploaded assets": "MegjegyzÊs: a korÃĄbban feltÃļltÃļtt elemek TÃĄrhely CímkÊzÊsÊhez futtasd a(z)", "notes": "MegjegyzÊsek", + "nothing_here_yet": "MÊg semmi sincs itt", "notification_permission_dialog_content": "Az ÊrtesítÊsek bekapcsolÃĄsÃĄhoz a BeÃĄllítÃĄsok menÃŧben vÃĄlaszd ki az EngedÊlyezÊs-t.", "notification_permission_list_tile_content": "ÉrtesítÊsek engedÊlyezÊse.", "notification_permission_list_tile_enable_button": "ÉrtesítÊsek BekapcsolÃĄsa", @@ -1217,27 +1420,39 @@ "notification_toggle_setting_description": "Email ÊrtesítÊsek engedÊlyezÊse", "notifications": "ÉrtesítÊsek", "notifications_setting_description": "ÉrtesítÊsek kezelÊse", + "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich ForrÃĄsok", + "offline": "Nem elÊrhető (offline)", + "offset": "EltolÃĄs", "ok": "Rendben", "oldest_first": "LegrÊgebbi előszÃļr", "on_this_device": "Ezen az eszkÃļzÃļn", "onboarding": "Első lÊpÊsek", - "onboarding_privacy_description": "Az alÃĄbbi (nem kÃļtelező) funkciÃŗk kÃŧlsős szolgÃĄltatÃĄsokon alapulnak Ês bÃĄrmikor kikapcsolhatÃŗak az adminisztrÃĄciÃŗs beÃĄllítÃĄsokban.", + "onboarding_locale_description": "VÃĄlaszd ki a preferÃĄlt nyelved. Ezt kÊsőbb a beÃĄllítÃĄsokban bÃĄrmikor mÃŗdosíthatod.", + "onboarding_privacy_description": "Az alÃĄbbi (nem kÃļtelező) funkciÃŗk kÃŧlsős szolgÃĄltatÃĄsokon alapulnak Ês bÃĄrmikor kikapcsolhatÃŗak a beÃĄllítÃĄsokban.", + "onboarding_server_welcome_description": "Állítsuk be a pÊldÃĄnyodat pÃĄr alap beÃĄllítÃĄssal.", "onboarding_theme_description": "VÃĄlassz egy színtÊmÃĄt. Ezt bÃĄrmikor megvÃĄltoztathatod a beÃĄllítÃĄsokban.", + "onboarding_user_welcome_description": "KezdjÃŧnk bele!", "onboarding_welcome_user": "ÜdvÃļzÃļllek {user}", + "online": "Online (elÊrhető)", "only_favorites": "Csak kedvencek", + "open": "Nyitva", "open_in_map_view": "MegnyitÃĄs tÊrkÊp nÊzetben", "open_in_openstreetmap": "MegnyitÃĄs OpenStreetMap-ben", "open_the_search_filters": "KeresÊsi szÅąrők megnyitÃĄsa", "options": "BeÃĄllítÃĄsok", "or": "vagy", + "organize_into_albums": "Albumokba rendezÊs", + "organize_into_albums_description": "MeglÊvő fotÃŗk albumokba helyezÊse, a jelenlegi szinkronizÃĄciÃŗs beÃĄllítÃĄsok alapjÃĄn", "organize_your_library": "Rendszerezd a kÊptÃĄradat", "original": "eredeti", "other": "EgyÊb", "other_devices": "EgyÊb eszkÃļzÃļk", + "other_entities": "EgyÊb entitÃĄsok", "other_variables": "EgyÊb vÃĄltozÃŗk", "owned": "Tulajdonos", "owner": "Tulajdonos", + "partner": "Partner", "partner_can_access": "{partner} hozzÃĄfÊrhet", "partner_can_access_assets": "Minden fÊnykÊped Ês videÃŗd, kivÊve az ArchivÃĄltak Ês a TÃļrÃļltek", "partner_can_access_location": "A helyszín, ahol a fotÃŗkat kÊszítettÊk", @@ -1247,7 +1462,7 @@ "partner_page_no_more_users": "Nincs tÃļbb hozzÃĄadhatÃŗ felhasznÃĄlÃŗ", "partner_page_partner_add_failed": "Partner hozzÃĄadÃĄsa sikertelen", "partner_page_select_partner": "Partner kivÃĄlasztÃĄsa", - "partner_page_shared_to_title": "Megosztva: ", + "partner_page_shared_to_title": "Megosztva", "partner_page_stop_sharing_content": "{partner} nem fog tÃļbbÊ hozzÃĄfÊrni a fotÃŗidhoz.", "partner_sharing": "Partner MegosztÃĄs", "partners": "Partnerek", @@ -1277,6 +1492,8 @@ "permanently_delete_assets_prompt": "Biztos, hogy vÊglegesen tÃļrÃļlni {count, plural, one {szeretnÊd ezt az elemet} other {szeretnÊl # elemet}}? Ez el fogja tÃĄvolítani az {count, plural, one {elemet az albumokbÃŗl, amikben szerepel} other {elemeket az albumokbÃŗl, amikben szerepelnek}}.", "permanently_deleted_asset": "Elem vÊglegesen tÃļrÃļlve", "permanently_deleted_assets_count": "{count, plural, other {# elem}} vÊglegesen tÃļrÃļlve", + "permission": "JogosultsÃĄg", + "permission_empty": "A jogosultsÃĄg nem hagyhatÃŗ Ãŧresen", "permission_onboarding_back": "Vissza", "permission_onboarding_continue_anyway": "FolytatÃĄs mindenkÊpp", "permission_onboarding_get_started": "VÃĄgjunk bele", @@ -1284,8 +1501,11 @@ "permission_onboarding_permission_denied": "HozzÃĄfÊrÊs megtagadva. Az Immich hasznÃĄlatÃĄhoz engedÊlyezni kell a fotÃŗ Ês videÃŗ hozzÃĄfÊrÊst a BeÃĄllítÃĄsokban.", "permission_onboarding_permission_granted": "HozzÃĄfÊrÊs engedÊlyezve! Minden kÊszen ÃĄll.", "permission_onboarding_permission_limited": "KorlÃĄtozott hozzÃĄfÊrÊs. Ha szeretnÊd, hogy az Immich a teljes galÊria gyÅąjtemÊnyedet mentse Ês kezelje, akkor a BeÃĄllítÃĄsokban engedÊlyezd a fotÃŗ Ês videÃŗ jogosultsÃĄgokat.", - "permission_onboarding_request": "EngedÊlyezni kell, hogy az Immich hozzÃĄfÊrjen a kÊpeidhez Ês videÃŗidhoz", + "permission_onboarding_request": "EngedÊlyezni kell, hogy az Immich hozzÃĄfÊrjen a kÊpeidhez Ês videÃŗidhoz.", "person": "SzemÊly", + "person_age_months": "{months, plural, one {# hÃŗnapja} other {# hÃŗnapja}}", + "person_age_year_months": "Egy Êv Ês {months, plural, one {# hÃŗnapja} other {# hÃŗnapja}}", + "person_age_years": "{years, plural, other {# Êve}}", "person_birthdate": "SzÃŧletett: {date}", "person_hidden": "{name}{hidden, select, true { (rejtett)} other {}}", "photo_shared_all_users": "Úgy tÅąnik, hogy mÃĄr mindenkivel megosztottad a fÊnykÊpeidet, vagy nincs senki, akivel meg tudnÃĄd osztani.", @@ -1297,6 +1517,7 @@ "pin_code_changed_successfully": "Sikeres PIN kÃŗd vÃĄltoztatÃĄs", "pin_code_reset_successfully": "Sikeres PIN kÃŗd visszaÃĄllítÃĄs", "pin_code_setup_successfully": "Sikeres PIN kÃŗd beÃĄllítÃĄs", + "pin_verification": "PIN kÃŗd megerősítÊse", "place": "Hely", "places": "Helyek", "places_count": "{count, plural, one {{count, number} Helyszín} other {{count, number} Helyszín}}", @@ -1304,19 +1525,28 @@ "play_memories": "EmlÊkek lejÃĄtszÃĄsa", "play_motion_photo": "MozgÃŗkÊp lejÃĄtszÃĄsa", "play_or_pause_video": "VideÃŗ elindítÃĄsa vagy megÃĄllítÃĄsa", + "please_auth_to_access": "KÊrlek jelentkezz be a hozzÃĄfÊrÊshez", + "port": "Port", "preferences_settings_subtitle": "AlkalmazÃĄsbeÃĄllítÃĄsok kezelÊse", "preferences_settings_title": "BeÃĄllítÃĄsok", + "preparing": "ElőkÊszítÊs", "preset": "Sablon", "preview": "ElőnÊzet", "previous": "Előző", "previous_memory": "Előző emlÊk", - "previous_or_next_photo": "Előző vagy kÃļvetkező fotÃŗ", + "previous_or_next_day": "Nap előre/hÃĄtra", + "previous_or_next_month": "HÃŗnap előre/hÃĄtra", + "previous_or_next_photo": "FotÃŗ előre/hÃĄtra", + "previous_or_next_year": "Év előre/hÃĄtra", "primary": "Elsődleges", "privacy": "MagÃĄnszfÊra", + "profile": "Profil", "profile_drawer_app_logs": "NaplÃŗk", "profile_drawer_client_out_of_date_major": "A mobilalkalmazÃĄs elavult. KÊrjÃŧk, frissítsd a legfrisebb főverziÃŗra.", "profile_drawer_client_out_of_date_minor": "A mobilalkalmazÃĄs elavult. KÊrjÃŧk, frissítsd a legfrisebb alverziÃŗra.", "profile_drawer_client_server_up_to_date": "A Kliens Ês a Szerver is naprakÊsz", + "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Csak olvashatÃŗ mÃŗd engedÊlyezve. A kilÊpÊshez hosszan nyomja meg a felhasznÃĄlÃŗi avatar ikont.", "profile_drawer_server_out_of_date_major": "A szerver elavult. KÊrjÃŧk, frissítsd a legfrisebb főverziÃŗra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. KÊrjÃŧk, frissítsd a legfrisebb alverziÃŗra.", "profile_image_of_user": "{user} profilkÊpe", @@ -1355,12 +1585,17 @@ "purchase_server_description_2": "TÃĄmogatÃŗ stÃĄtusz", "purchase_server_title": "Szerver", "purchase_settings_server_activated": "A szerver termÊkkulcsot az admin kezeli", + "query_asset_id": "LekÊrdezÊsi eszkÃļz azonosítÃŗja", + "queue_status": "Feldolgozva {count}/{total}", "rating": "ÉrtÊkelÊs csillagokkal", "rating_clear": "ÉrtÊkelÊs tÃļrlÊse", "rating_count": "{count, plural, one {# csillag} other {# csillag}}", "rating_description": "Exif ÊrtÊkelÊs megjelenítÊse az infÃŗpanelen", "reaction_options": "ReakciÃŗ lehetősÊgek", "read_changelog": "VÃĄltozÃĄsnaplÃŗ ElolvasÃĄsa", + "readonly_mode_disabled": "Csak olvashatÃŗ mÃŗd kikapcsolva", + "readonly_mode_enabled": "Csak olvashatÃŗ mÃŗd bekapcsolva", + "ready_for_upload": "KÊszen ÃĄll a feltÃļltÊsre", "reassign": "HozzÃĄrendel", "reassigned_assets_to_existing_person": "{count, plural, other {# elem}} hozzÃĄrendelve{name, select, null { egy lÊtező szemÊlyhez} other {: {name}}}", "reassigned_assets_to_new_person": "{count, plural, other {# elem}} hozzÃĄrendelve egy Ãēj szemÊlyhez", @@ -1370,6 +1605,8 @@ "recent_searches": "LegutÃŗbbi keresÊsek", "recently_added": "NemrÊg hozzÃĄadott", "recently_added_page_title": "NemrÊg HozzÃĄadott", + "recently_taken": "NemrÊg kÊszített", + "recently_taken_page_title": "NemrÊg kÊszített", "refresh": "FrissítÊs", "refresh_encoded_videos": "ÁtkÃŗdolt videÃŗk frissítÊse", "refresh_faces": "Arcok frissítÊse", @@ -1381,6 +1618,9 @@ "refreshing_faces": "Arcok frissítÊse folyamatban", "refreshing_metadata": "Metaadatok frissítÊse folyamatban", "regenerating_thumbnails": "BÊlyegkÊpek ÃējragenerÃĄlÃĄsa folyamatban", + "remote": "TÃĄvoli", + "remote_assets": "TÃĄvoli Elemek", + "remote_media_summary": "TÃĄvoli mÊdiaÃļsszefoglalÃŗ", "remove": "EltÃĄvolítÃĄs", "remove_assets_album_confirmation": "Biztosan el szeretnÊl tÃĄvolítani {count, plural, one {# elemet} other {# elemet}} az albumbÃŗl?", "remove_assets_shared_link_confirmation": "Biztosan el szeretnÊl tÃĄvolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", @@ -1388,10 +1628,15 @@ "remove_custom_date_range": "EgyÊni időintervallum eltÃĄvolítÃĄsa", "remove_deleted_assets": "TÃļrÃļlt Elemek EltÃĄvolítÃĄsa", "remove_from_album": "EltÃĄvolítÃĄs az albumbÃŗl", + "remove_from_album_action_prompt": "{count} eltÃĄvolítva az albumbÃŗl", "remove_from_favorites": "EltÃĄvolítÃĄs a kedvencekből", + "remove_from_lock_folder_action_prompt": "{count} eltÃĄvolítva a zÃĄrolt mappÃĄbÃŗl", + "remove_from_locked_folder": "EltÃĄvolítÃĄs a zÃĄrolt mappÃĄbÃŗl", + "remove_from_locked_folder_confirmation": "Biztosan ki szeretnÊd venni ezeket a fotÃŗkat Ês videÃŗkat a zÃĄrolt mappÃĄbÃŗl? LÃĄthatÃŗak lesznek a kÃļnyvtÃĄradban.", "remove_from_shared_link": "EltÃĄvolítÃĄs a megosztott linkből", "remove_memory": "EmlÊk eltÃĄvolítÃĄsa", "remove_photo_from_memory": "KÊp eltÃĄvolítÃĄsa az emlÊkből", + "remove_tag": "Címke eltÃĄvolítÃĄsa", "remove_url": "URL eltÃĄvolítÃĄsa", "remove_user": "FelhasznÃĄlÃŗ eltÃĄvolítÃĄsa", "removed_api_key": "API Kulcs eltÃĄvolítva: {name}", @@ -1413,19 +1658,29 @@ "reset_password": "JelszÃŗ visszaÃĄllítÃĄsa", "reset_people_visibility": "SzemÊlyek lÃĄthatÃŗsÃĄgÃĄnak visszaÃĄllítÃĄsa", "reset_pin_code": "PIN kÃŗd visszaÃĄllítÃĄsa", + "reset_pin_code_description": "Ha elfelejtetted a PIN-kÃŗdod, vedd fel a kapcsolatot a szerver rendszergazdÃĄjÃĄval, hogy visszaÃĄllíthassa azt", + "reset_pin_code_success": "PIN kÃŗd sikeresen visszaÃĄllítva", + "reset_pin_code_with_password": "A PIN kÃŗdod mindig visszaÃĄllíthatod a jelszavaddal", + "reset_sqlite": "SQLite AdatbÃĄzis VisszaÃĄllítÃĄsa", + "reset_sqlite_confirmation": "Biztosan vissza szeretnÊd ÃĄllítani az SQLite adatbÃĄzist? Az adatok ÃējraszinkronizÃĄlÃĄsÃĄhoz ki kell jelentkezed, majd Ãējra be kell lÊpned", + "reset_sqlite_success": "SQLite adatbÃĄzis sikeresen visszaÃĄllítva", "reset_to_default": "VisszaÃĄllítÃĄs alapÃĄllapotba", "resolve_duplicates": "DuplikÃĄtumok feloldÃĄsa", "resolved_all_duplicates": "Minden duplikÃĄtum feloldÃĄsa", "restore": "VisszaÃĄllít", "restore_all": "Minden visszaÃĄllítÃĄsa", + "restore_trash_action_prompt": "{count} visszaÃĄllítva a lomtÃĄrbÃŗl", "restore_user": "FelhasznÃĄlÃŗ visszaÃĄllítÃĄsa", "restored_asset": "VisszaÃĄllított elem", "resume": "FolytatÃĄs", + "resume_paused_jobs": "FolytatÃĄs {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "FeltÃļltÊs ÃējraprÃŗbÃĄlÃĄsa", "review_duplicates": "DuplikÃĄtumok ÃĄttekintÊse", + "review_large_files": "Nagy fÃĄjlok ÃĄttekintÊse", "role": "JogkÃļr", "role_editor": "Szerkesztő", "role_viewer": "Megjelenítő", + "running": "FutÃŗ", "save": "MentÊs", "save_to_gallery": "MentÊs a galÊriÃĄba", "saved_api_key": "API Kulcs Elmentve", @@ -1483,9 +1738,9 @@ "search_places": "Helyek keresÊse", "search_rating": "KeresÊs ÊrtÊkelÊs szerint...", "search_result_page_new_search_hint": "Új KeresÊs", - "search_settings": "KeresÊsi beÃĄllítÃĄsok", + "search_settings": "BeÃĄllítÃĄsok keresÊse", "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...", @@ -1498,6 +1753,7 @@ "select_album_cover": "AlbumborítÃŗ kivÃĄlasztÃĄsa", "select_all": "Összes kijelÃļlÊse", "select_all_duplicates": "Minden duplikÃĄtum kijelÃļlÊse", + "select_all_in": "Összes kijelÃļlÊse itt: {group}", "select_avatar_color": "AvatÃĄr színÊnek vÃĄlasztÃĄsa", "select_face": "Arc kivÃĄlasztÃĄsa", "select_featured_photo": "AlapÊrtelmezett fÊnykÊp kivÃĄlasztÃĄsa", @@ -1505,11 +1761,13 @@ "select_keep_all": "'Megtart' kijelÃļlÊse", "select_library_owner": "VÃĄlaszd ki a kÊptÃĄr tulajdonosÃĄt", "select_new_face": "Új arc vÃĄlasztÃĄsa", + "select_person_to_tag": "VÃĄlassz ki egy szemÊlyt a megjelÃļlÊshez", "select_photos": "FotÃŗk vÃĄlasztÃĄsa", "select_trash_all": "'LomtÃĄr' kijelÃļlÊse", "select_user_for_sharing_page_err_album": "Az album lÊtrehozÃĄsa sikertelen", "selected": "KivÃĄlasztott", "selected_count": "{count, plural, other {# kivÃĄlasztva}}", + "selected_gps_coordinates": "KivÃĄlasztott GPS KordinÃĄtÃĄk", "send_message": "Üzenet kÃŧldÊse", "send_welcome_email": "ÜdvÃļzlő email kÃŧldÊse", "server_endpoint": "Szerver VÊgpont", @@ -1517,6 +1775,7 @@ "server_info_box_server_url": "Szerver Címe", "server_offline": "Szerver Nem ElÊrhető", "server_online": "Szerver ElÊrhető", + "server_privacy": "Szerver biztonsÃĄg", "server_stats": "Szerver StatisztikÃĄk", "server_version": "Szerver VerziÃŗ", "set": "BeÃĄllít", @@ -1526,6 +1785,7 @@ "set_date_of_birth": "SzÃŧletÊsi dÃĄtum beÃĄllítÃĄsa", "set_profile_picture": "ProfilkÊp beÃĄllítÃĄsa", "set_slideshow_to_fullscreen": "DiavetítÊs teljes kÊpernyőre ÃĄllítÃĄsa", + "set_stack_primary_asset": "BeÃĄllítÃĄs elsődleges elemkÊnt", "setting_image_viewer_help": "Az Elem Megjelenítő előszÃļr a kis bÊlyegkÊpet tÃļlti be, aztÃĄn a kÃļzepes mÊretÅą előnÊzetet (ha elÊrhető), vÊgÃŧl az eredetit (ha elÊrhető).", "setting_image_viewer_original_subtitle": "EngedÊlyezi az eredeti teljes felbontÃĄsÃē kÊp betÃļltÊsÊt (nagy!). Kikapcsolva csÃļkkenti az adathasznÃĄlatot (a neten Ês az eszkÃļz gyorsítÃŗtÃĄrÃĄn is).", "setting_image_viewer_original_title": "Eredeti kÊp betÃļltÊse", @@ -1553,9 +1813,11 @@ "settings_saved": "BeÃĄllítÃĄsok elmentve", "setup_pin_code": "PIN kÃŗd beÃĄllítÃĄsa", "share": "MegosztÃĄs", + "share_action_prompt": "{count} elem megosztva", "share_add_photos": "FotÃŗk hozzÃĄadÃĄsa", "share_assets_selected": "{count} kivÃĄlasztva", "share_dialog_preparing": "ElőkÊszítÊs...", + "share_link": "Link megosztÃĄsa", "shared": "Megosztva", "shared_album_activities_input_disable": "HozzÃĄszÃŗlÃĄsok kikapcsolva", "shared_album_activity_remove_content": "TÃļrÃļlni szeretnÊd ezt a tevÊkenysÊget?", @@ -1573,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "VÃĄgÃŗlapra mÃĄsolva", "shared_link_clipboard_text": "Link: {link}\nJelszÃŗ: {password}", "shared_link_create_error": "Hiba a megosztott link lÊtrehozÃĄsakor", + "shared_link_custom_url_description": "HozzÃĄfÊrÊs ehhez a megosztott linkhez egyedi URL címen keresztÃŧl", "shared_link_edit_description_hint": "Add meg a megosztÃĄs leírÃĄsÃĄt", "shared_link_edit_expire_after_option_day": "1 nap", "shared_link_edit_expire_after_option_days": "{count} nap", @@ -1595,8 +1858,10 @@ "shared_link_expires_second": "{count} mÃĄsodperc mÃēlva lejÃĄr", "shared_link_expires_seconds": "{count} mÃĄsodperc mÃēlva lejÃĄr", "shared_link_individual_shared": "EgyÊnileg megosztva", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Megosztott linkek kezelÊse", "shared_link_options": "Megosztott link beÃĄllítÃĄsai", + "shared_link_password_description": "JelszÃŗ megadÃĄsa szÃŧksÊges ehhez a megosztott linkhez", "shared_links": "Megosztott linkek", "shared_links_description": "FÊnykÊpek Ês videÃŗk megosztÃĄsa linkkel", "shared_photos_and_videos_count": "{assetCount, plural, other {# megosztott kÊp Ês videÃŗ.}}", @@ -1631,6 +1896,7 @@ "show_slideshow_transition": "VetítÊs ÃĄttÅąnÊsi effekt mutatÃĄsa", "show_supporter_badge": "TÃĄmogatÃŗ jelvÊny", "show_supporter_badge_description": "TÃĄmogatÃŗ jelvÊny mutatÃĄsa", + "show_text_search_menu": "Mutasd a szÃļvegkeresÊsi menÃŧt", "shuffle": "VÊletlenszerÅą", "sidebar": "OldalsÃĄv", "sidebar_display_description": "NÊzet link megjelenítÊse az oldalsÃĄvban", @@ -1646,28 +1912,35 @@ "sort_created": "LÊtrehozÃĄs dÃĄtuma", "sort_items": "Elemek szÃĄma", "sort_modified": "MÃŗdosítÃĄs dÃĄtuma", + "sort_newest": "LegÃējabb fotÃŗ", "sort_oldest": "LegrÊgebbi fÊnykÊp", "sort_people_by_similarity": "Emberek hasonlÃŗsÃĄg szerinti rendezÊse", "sort_recent": "LegÃējabb fÊnykÊp", "sort_title": "Cím", "source": "ForrÃĄs", "stack": "FotÃŗk csoportosítÃĄsa", + "stack_action_prompt": "{count} egymÃĄsra helyezve", "stack_duplicates": "DuplikÃĄtumok csoportosítÃĄsa", "stack_select_one_photo": "VÃĄlassz egy fő kÊpet a csoportbÃŗl", "stack_selected_photos": "KivÃĄlasztott fÊnykÊpek csoportosítÃĄsa", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", + "stacktrace": "Hiba leírÃĄsa", "start": "Elindít", "start_date": "Kezdő dÃĄtum", + "start_date_before_end_date": "A kezdeti dÃĄtumnak a befejezÊsi dÃĄtum előtt kell lennie", "state": "Megye/Állam", "status": "Állapot", + "stop_casting": "VetítÊs megszÃŧntetÊse", "stop_motion_photo": "MozgÃŗkÊp MegÃĄllítÃĄsa", "stop_photo_sharing": "FotÃŗid megosztÃĄsÃĄnak megszÃŧntetÊse?", "stop_photo_sharing_description": "{partner} mostantÃŗl nem fog tudni hozzÃĄfÊrni a fÊnykÊpeidhez.", "stop_sharing_photos_with_user": "FÊnykÊpeid megosztÃĄsÃĄnak megszÃŧntetÊse ezzel a felhasznÃĄlÃŗval", "storage": "TÃĄrhely", "storage_label": "TÃĄrhely címke", + "storage_quota": "TÃĄrhely kvÃŗta", "storage_usage": "{used}/{available} hasznÃĄlatban", "submit": "BekÃŧldÊs", + "success": "Siker", "suggestions": "Javaslatok", "sunrise_on_the_beach": "Napkelte a tengerparton", "support": "TÃĄmogatÃĄs", @@ -1677,6 +1950,10 @@ "sync": "SzinkronizÃĄlÃĄs", "sync_albums": "Albumok szinkronizÃĄlÃĄsa", "sync_albums_manual_subtitle": "Összes fotÃŗ Ês videÃŗ lÊtrehozÃĄsa Ês szinkronizÃĄlÃĄsa a kivÃĄlasztott Immich albumokba", + "sync_local": "Helyi SzinkronizÃĄlÃĄsa", + "sync_remote": "TÃĄvoli SzinkronizÃĄlÃĄsa", + "sync_status": "SzinkronizÃĄlÃĄs ÃĄllapota", + "sync_status_subtitle": "SzinkronizÃĄlÃĄs megtekintÊse Ês kezelÊse", "sync_upload_album_setting_subtitle": "FotÃŗk Ês videÃŗk lÊtrehozÃĄsa Ês szinkronizÃĄlÃĄsa a kivÃĄlasztott Immich albumba", "tag": "Címke", "tag_assets": "Elemek címkÊzÊse", @@ -1687,17 +1964,18 @@ "tag_updated": "Frissített címke: {tag}", "tagged_assets": "{count, plural, one {# elem} other {# elem}} felcímkÊzve", "tags": "CímkÊk", + "tap_to_run_job": "Érintsd meg a feladat futtatÃĄsÃĄhoz", "template": "Sablon", "theme": "TÊma", "theme_selection": "TÊmavÃĄlasztÃĄs", "theme_selection_description": "A bÃļngÊsző beÃĄllítÃĄsÃĄnak megfelelően automatikusan hasznÃĄljon vilÃĄgos vagy sÃļtÊt tÊmÃĄt", "theme_setting_asset_list_storage_indicator_title": "TÃĄrhely ikon mutatÃĄsa az elemeken", "theme_setting_asset_list_tiles_per_row_title": "Elemek szÃĄma soronkÊnt ({count})", - "theme_setting_colorful_interface_subtitle": "AlapÊrtelmezett szín hasznÃĄlata a hÃĄttÊrben lÊvő felÃŧletekhez", + "theme_setting_colorful_interface_subtitle": "AlapÊrtelmezett szín hasznÃĄlata a hÃĄttÊrben lÊvő felÃŧletekhez.", "theme_setting_colorful_interface_title": "Színes felhasznÃĄlÃŗi felÃŧlet", "theme_setting_image_viewer_quality_subtitle": "RÊszletes kÊpmegjelenítő minősÊgÊnek beÃĄllítÃĄsa", "theme_setting_image_viewer_quality_title": "KÊpmegjelenítő minősÊge", - "theme_setting_primary_color_subtitle": "VÃĄlassz egy színt az alapÊrtelmezett mÅąveletekhez Ês kiemelÊsekhez", + "theme_setting_primary_color_subtitle": "VÃĄlassz egy színt az alapÊrtelmezett mÅąveletekhez Ês kiemelÊsekhez.", "theme_setting_primary_color_title": "AlapÊrtelmezett szín", "theme_setting_system_primary_color_title": "Rendszerszínek hasznÃĄlata", "theme_setting_system_theme_switch": "Automatikus (kÃļveti a rendszer tÊmÃĄjÃĄt)", @@ -1713,12 +1991,15 @@ "to_change_password": "JelszÃŗ megvÃĄltoztatÃĄsa", "to_favorite": "Kedvenc", "to_login": "BejelentkezÊs", + "to_multi_select": "tÃļbb elem kivÃĄlasztÃĄsÃĄhoz", "to_parent": "Egy szinttel feljebb", + "to_select": "a kivÃĄlasztÃĄshoz", "to_trash": "LomtÃĄrba helyezÊs", "toggle_settings": "BeÃĄllítÃĄsok ÃĄtÃĄllítÃĄsa", "total": "Összesen", "total_usage": "Összesen hasznÃĄlatban", "trash": "LomtÃĄr", + "trash_action_prompt": "{count} lomtÃĄrba helyezve", "trash_all": "Mindet lomtÃĄrba", "trash_count": "{count, number} elem lomtÃĄrba helyezÊse", "trash_delete_asset": "Elem TÃļrlÊse / LomtÃĄrba HelyezÊse", @@ -1732,12 +2013,16 @@ "trash_page_select_assets_btn": "Elemek kivÃĄlasztÃĄsa", "trash_page_title": "LomtÃĄr ({count})", "trashed_items_will_be_permanently_deleted_after": "A lomtÃĄrban lÊvő elemek vÊglegesen tÃļrlÊsre kerÃŧlnek {days, plural, other {# nap}} mÃēlva.", + "troubleshoot": "HibaelhÃĄrítÃĄs", "type": "Típus", "unable_to_change_pin_code": "Sikertelen PIN kÃŗd vÃĄltoztatÃĄs", "unable_to_setup_pin_code": "Sikertelen PIN kÃŗd beÃĄllítÃĄs", "unarchive": "ArchívumbÃŗl kivesz", + "unarchive_action_prompt": "{count} eltÃĄvolítva az ArchívumbÃŗl", "unarchived_count": "{count, plural, other {# elem kivÊve az archívumbÃŗl}}", + "undo": "VisszavonÃĄs", "unfavorite": "Kedvenc kÃļzÃŧl kivesz", + "unfavorite_action_prompt": "{count} eltÃĄvolítva a Kedvencekből", "unhide_person": "Nem rejtett szemÊly", "unknown": "Ismeretlen", "unknown_country": "Ismeretlen orszÃĄg", @@ -1753,15 +2038,23 @@ "unsaved_change": "Nem mentett vÃĄltoztatÃĄs", "unselect_all": "KijelÃļlÊsek megszÃŧntetÊse", "unselect_all_duplicates": "DuplikÃĄtumok kijelÃļlÊsÊnek megszÃŧntetÊse", + "unselect_all_in": "KijelÃļlÊs megszÃŧntetÊse itt: {group}", "unstack": "Csoport SzÊtszedÊse", + "unstack_action_prompt": "{count} egymÃĄsra helyezÊs megszÃŧntetÊse", "unstacked_assets_count": "{count, plural, other {# elemből}} ÃĄllÃŗ csoport szÊtszedve", + "untagged": "Címke eltÃĄvolítva", "up_next": "KÃļvetkezik", + "update_location_action_prompt": "{count} elem pozíciÃŗjÃĄnak frissítÊse a kÃļvetkezővel:", + "updated_at": "Frissített", "updated_password": "JelszÃŗ megvÃĄltoztatva", "upload": "FeltÃļltÊs", + "upload_action_prompt": "{count} sorba rakva a feltÃļltÊshez", "upload_concurrency": "PÃĄrhuzamos feltÃļltÊs", + "upload_details": "FeltÃļltÊsi RÊszletek", "upload_dialog_info": "SzeretnÊl mentÊst kÊszíteni a kivÃĄlasztott elem(ek)ről a szerverre?", "upload_dialog_title": "Elem FeltÃļltÊse", "upload_errors": "FeltÃļltÊs befejezve {count, plural, other {# hibÃĄval}}, frissítsd az oldalt az Ãējonnan feltÃļltÃļtt elemek megtekintÊsÊhez.", + "upload_finished": "FeltÃļltÊs befejezve", "upload_progress": "HÃĄtra van {remaining, number} - Feldolgozva {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, other {# duplikÃĄtum}} kihagyva", "upload_status_duplicates": "DuplikÃĄtumok", @@ -1770,14 +2063,19 @@ "upload_success": "FeltÃļltÊs sikeres, frissítsd az oldalt az Ãējonnan feltÃļltÃļtt elemek megtekintÊsÊhez.", "upload_to_immich": "FeltÃļltÊs Immich-be ({count})", "uploading": "FeltÃļltÊs folyamatban", + "uploading_media": "MÊdia feltÃļltÊs folyamatban", + "url": "URL", "usage": "HasznÃĄlat", + "use_biometric": "Biometrikus azonosítÃĄs hasznÃĄlata", "use_current_connection": "Jelenlegi kapcsolat hasznÃĄlata", "use_custom_date_range": "Szabadon megadott időintervallum hasznÃĄlata", "user": "FelhasznÃĄlÃŗ", + "user_has_been_deleted": "Ez a felhasznÃĄlÃŗ tÃļrlÊsre kerÃŧlt.", "user_id": "FelhasznÃĄlÃŗ azonosítÃŗja", "user_liked": "{user} felhasznÃĄlÃŗnak {type, select, photo {ez a fÊnykÊp} video {ez a videÃŗ} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kÃŗd", "user_pin_code_settings_description": "PIN kÃŗd kezelÊse", + "user_privacy": "FelhasznÃĄlÃŗi adatvÊdelem", "user_purchase_settings": "MegvÃĄsÃĄrlÃĄs", "user_purchase_settings_description": "VÃĄsÃĄrlÃĄs kezelÊse", "user_role_set": "{user} felhasznÃĄlÃŗnak {role} jogkÃļr biztosítÃĄsa", @@ -1786,6 +2084,7 @@ "user_usage_stats_description": "FiÃŗk hasznÃĄlati statisztikÃĄinak megtekintÊse", "username": "FelhasznÃĄlÃŗnÊv", "users": "FelhasznÃĄlÃŗk", + "users_added_to_album_count": "{count, plural, one {# felhasznÃĄlÃŗ} other {# felhasznÃĄlÃŗ}} hozzÃĄadva az albumhoz", "utilities": "SegÊdeszkÃļzÃļk", "validate": "EllenőrzÊs", "validate_endpoint_error": "KÊrlek, ÊrvÊnyes URL címet adj meg", @@ -1804,6 +2103,7 @@ "view_album": "Album MegtekintÊse", "view_all": "Összes MegtekintÊse", "view_all_users": "Minden FelhasznÃĄlÃŗ MegtekintÊse", + "view_details": "RÊszletek MegtekintÊse", "view_in_timeline": "MegtekintÊs az idővonalon", "view_link": "Link megtekintÊse", "view_links": "Linkek megtekintÊse", @@ -1811,6 +2111,7 @@ "view_next_asset": "KÃļvetkező elem megtekintÊse", "view_previous_asset": "Előző elem megtekintÊse", "view_qr_code": "QR kÃŗd megtekintÊse", + "view_similar_photos": "HasonlÃŗ kÊpek keresÊse", "view_stack": "Csoport MegtekintÊse", "view_user": "FelhasznÃĄlÃŗ MegtekintÊse", "viewer_remove_from_stack": "EltÃĄvolít a CsoportbÃŗl", @@ -1822,12 +2123,13 @@ "week": "HÊt", "welcome": "ÜdvÃļzlÃŧnk", "welcome_to_immich": "ÜdvÃļzÃļl az Immich", - "wifi_name": "WiFi Neve", + "wifi_name": "Wi-Fi Neve", "wrong_pin_code": "HibÃĄs PIN kÃŗd", "year": "Év", "years_ago": "{years, plural, one {# Êvvel} other {# Êvvel}} ezelőtt", "yes": "Igen", "you_dont_have_any_shared_links": "Nincsenek megosztott linkjeid", - "your_wifi_name": "A WiFi hÃĄlÃŗzatod neve", - "zoom_image": "KÊp NagyítÃĄsa" + "your_wifi_name": "A Wi-Fi hÃĄlÃŗzatod neve", + "zoom_image": "KÊp NagyítÃĄsa", + "zoom_to_bounds": "NagyítÃĄs a hatÃĄrokhoz" } diff --git a/i18n/hy.json b/i18n/hy.json index 86cbcd5554..1b12eb1cf0 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -1,51 +1,121 @@ { "about": "Õ„ÕĄÕŊÕĢÕļ", + "account": "Õ€ÕĄÕˇÕĢÕž", + "account_settings": "Õ€ÕĄÕˇÕžÕĢ Õ¯ÕĄÖ€ÕŖÕĄÕžÕ¸Ö€Õ¸Ö‚Õ´ÕļÕĨր", + "acknowledge": "Ô¸ÕļդուÕļÕĨÕŦ", "action": "ÔŗÕ¸Ö€ÕŽÕ¸Õ˛Õ¸Ö‚ÕŠÕĩուÕļ", + "action_common_update": "ÔšÕĄÖ€Õ´ÕĄÖÕļÕĨÕŦ", + "actions": "ÔŗÕ¸Ö€ÕŽÕ¸Õ˛Õ¸Ö‚ÕŠÕĩուÕļÕļÕĨր", + "active": "ÔąÕ¯ÕŋÕĢÕž", "add": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ", + "add_a_description": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ Õ´ÕĨÕ¯ÕļÕĄÕĸÕĄÕļÕ¸Ö‚ÕŠÕĩուÕļ", "add_a_location": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŋÕĨÕ˛", "add_a_name": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕĄÕļուÕļ", + "add_a_title": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕžÕĨÖ€ÕĄÕŖÕĢր", + "add_birthday": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŽÕļÕļÕ¤ÕĩÕĄÕļ ÕĄÕ´ÕŊÕĄÕŠÕĢÕž", + "add_endpoint": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕžÕĨրÕģÕļÕĄÕ¯ÕĨÕŋ", "add_location": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŋÕĨÕ˛", + "add_more_users": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ Õ°ÕĄÕžÕĨÕŦÕĩÕĄÕŦ Ö…ÕŖÕŋÕĄÕŋÕĨրÕĨր", + "add_partner": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕŖÕ¸Ö€ÕŽÕ¨ÕļÕ¯ÕĨր", "add_photos": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "added_to_archive": "ÔąÕžÕĨÕŦÕĄÖÕžÕĨÕŦ Õ§ ÕĄÖ€Õ­ÕĢÕžÕ¸Ö‚Õ´", + "added_to_favorites": "ÔąÕžÕĨÕŦÕĄÖÕžÕĨÕŦ Õ§ Õ°ÕĄÕžÕĄÕŽÕļÕĨրում", + "admin": { + "authentication_settings": "ՆուÕĩÕļÕĄÕ¯ÕĄÕļÕĄÖÕ´ÕĄÕļ Õ¯ÕĄÖ€ÕŖÕĄÕžÕ¸Ö€Õ¸Ö‚Õ´ÕļÕĨր", + "authentication_settings_disable_all": "ՎÕŊÕŋÕĄÕžÕ° ÕĨք, որ ÖÕĄÕļÕ¯ÕĄÕļում ÕĨք ÕĄÕļÕģÕĄÕŋÕĨÕŦ մուÕŋÖ„ÕĄÕĩÕĢÕļ Õ´ÕĨÕŠÕ¸Õ¤ÕļÕĨրը։ ՄուÕŋքÕļ ÕĄÕ´ÕĸÕ¸Õ˛ÕģÕ¸Ö‚ÕŠÕĩÕĄÕ´Õĸ Õ¯ÕĄÕļÕģÕĄÕŋÕžÕĢ։" + }, "back": "ՀÕĨÕŋ", "backup_all": "Ô˛Õ¸ÕŦոր", "backup_controller_page_background_battery_info_link": "ՑուÕĩց Õŋուր ÕĢÕļÕšÕēÕĨÕŊ", "backup_controller_page_background_battery_info_ok": "ÔŧÕĄÕž", + "backup_controller_page_background_wifi": "ՄÕĢÕĄÕĩÕļ Wi-Fi Õ´ÕĢÕĄÖÕ¸Ö‚Õ´Õ¸Ö‚Õ´", + "backup_controller_page_created": "ՍÕŋÕĨÕ˛ÕŽÕžÕĨÕŦ Õ§ {date}֊ÕĢÕļ", + "backup_controller_page_uploading_file_info": "ՎÕĨրÕĸÕĨÕŧÕļÕžÕ¸Ö‚Õ´ Õ§ Ö†ÕĄÕĩÕŦÕĢ ÕŋÕĨÕ˛ÕĨÕ¯Õ¸Ö‚ÕŠÕĩուÕļÕļÕĨրը", + "biometric_auth_enabled": "ÔŋÕĨÕļÕŊÕĄÕšÕĄÖƒÕĄÕ¯ÕĄÕļ ÕļուÕĩÕļÕĄÕ¯ÕĄÕļցումը Õ´ÕĢÕĄÖÕžÕĄÕŽ Õ§", + "change_display_order": "ՓոփոխÕĨÕŦ ÖÕ¸Ö‚ÖÕĄÕ¤Ö€Õ´ÕĄÕļ Õ°ÕĄÕģÕ¸Ö€Õ¤ÕĄÕ¯ÕĄÕļÕ¸Ö‚ÕŠÕĩուÕļÕ¨", "change_location": "ՓոխÕĨÕŦ ÕŋÕĨÕ˛Õ¨", "change_name": "ՓոխÕĨÕŦ ÕĄÕļուÕļ", + "change_password_form_reenter_new_password": "ÔŋրկÕĢÕļ մուÕŋÖ„ÕĄÕŖÖ€ÕĨÕŦ Õļոր ÕŖÕĄÕ˛ÕŋÕļÕĄÕĸÕĄÕŧÕ¨", + "change_pin_code": "ՓոփոխÕĨÕŦ ՊÔģՆ Õ¯Õ¸Õ¤Õ¨", "city": "Õ”ÕĄÕ˛ÕĄÖ„", "client_cert_dialog_msg_confirm": "ÔŧÕĄÕž", + "client_cert_title": "SSL Õ°ÕĄÕŗÕĄÕ­Õ¸Ö€Õ¤ÕĢ Õ°ÕĄÕžÕĄÕŊÕŋÕĄÕŖÕĢր", "color": "ÔŗÕ¸Ö‚ÕĩÕļ", + "common_create_new_album": "ՍÕŋÕĨÕ˛ÕŽÕĨÕŦ Õļոր ÕĄÕŦÕĸÕ¸Õ´", + "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_trash_from_immich": "ՏÕĨÕ˛ÕĄÖƒÕ¸Õ­ÕĨÕŦ ÕĄÕ˛ÕĸÕĄÕ´ÕĄÕļ", "country": "ÔĩրկÕĢր", "create_new": "ՍՏÔĩÕ‚ÔžÔĩÔŧ ՆՈՐ", "create_new_person": "ՍÕŋÕĨÕ˛ÕŽÕĨÕŦ Õļոր ÕĄÕļÕą", "create_shared_album_page_share_select_photos": "Ô¸ÕļÕŋրÕĨ Õ†Õ¯ÕĄÖ€ÕļÕĨր", "curated_object_page_title": "Ô˛ÕĄÕļÕĨր", + "current_pin_code": "Տ՞ÕĩÕĄÕŦ ՊÔģՆ Õ¯Õ¸Õ¤Õ¨", + "current_server_address": "Ô¸ÕļÕŠÕĄÖÕĢÕ¯ ÕŊÕĨÖ€ÕžÕĨրÕĢ Õ°ÕĄÕŊցÕĨ", + "daily_title_text_date": "E, MMM dd", "dark": "Õ„Õ¸Ö‚ÕŠ", + "dark_theme": "ՄÕĢÕĄÖÕļÕĨÕŦ/ÕĄÕļÕģÕĄÕŋÕĨÕŦ Õ´Õ¸Ö‚ÕŖ ÕŋÕĨÕŊքը", "day": "Օր", "delete": "ՋÕļÕģÕĨÕŦ", + "delete_shared_link_dialog_title": "ՋÕļÕģÕĨÕŦ Õ¯ÕĢÕŊÕžÕĄÕŽ Õ°Õ˛Õ¸Ö‚Õ´Õ¨", + "download_notfound": "ՆÕĨրÕĸÕĨÕŧÕļÕžÕĄÕŽÕ¨ ÕšÕĢ Õ°ÕĄÕĩÕŋÕļÕĄÕĸÕĨÖ€ÕžÕĨÕŦ", "edit_location": "ՓոխÕĨÕŦ ÕŋÕĨÕ˛Õ¨", + "enter_wifi_name": "ՄուÕŋÖ„ÕĄÕŖÖ€ÕĨÕŦ Wi-Fi ÕĄÕļուÕļÕ¨", "exif_bottom_sheet_person_add_person": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕĄÕļուÕļ", - "exif_bottom_sheet_person_age_years": "ÕÕĄÖ€ÕĢք {years}", + "experimental_settings_new_asset_list_subtitle": "Ô¸ÕļÕŠÕĄÖÖ„Õ¸Ö‚Õ´ Õ§", + "failed_to_authenticate": "ՆուÕĩÕļÕĄÕ¯ÕĄÕļÕĄÖÕ¸Ö‚Õ´Õ¨ ÕąÕĄÕ­Õ¸Õ˛ÕžÕĨÕŦ Õ§", + "folder_not_found": "ÔšÕ˛ÕŠÕĄÕēÕĄÕļÕĄÕ¯Õ¨ ÕšÕĢ Õ°ÕĄÕĩÕŋÕļÕĄÕĸÕĨÖ€ÕžÕĨÕŦ", "hi_user": "Ô˛ÕĄÖ€ÕĨւ {name} ({email})", - "map_assets_in_bound": "{count} ÕļÕ¯ÕĄÖ€", + "ignore_icloud_photos": "ÔąÕļÕŋÕĨÕŊÕĨÕŦ iCloud֊ÕĢ ÕļÕ¯ÕĄÖ€ÕļÕĨրը", + "invalid_date_format": "ÔąÕļÕžÕĄÕžÕĨր ÕĄÕ´ÕŊÕĄÕŠÕžÕĢ ÕąÖ‡ÕĄÕšÕĄÖƒ", + "ios_debug_info_last_sync_at": "ՎÕĨրÕģÕĢÕļ ÕŠÕĄÖ€Õ´ÕĄÖÕ¸Ö‚Õ´Õ¨ {dateTime}֊ÕĢÕļ", + "language_no_results_title": "ÔŧÕĨÕĻուÕļÕĨր ÕšÕĨÕļ ÕŖÕŋÕļÕžÕĨÕŦ", + "library_page_device_albums": "ÕÕĄÖ€Ö„ÕĄÕžÕ¸Ö€Õ´ÕĄÕļ ÕĄÕŦÕĸÕ¸Õ´ÕļÕĨրը", + "location_picker_choose_on_map": "Ô¸ÕļÕŋրÕĨÕŦ Ö„ÕĄÖ€ÕŋÕĨÕĻÕĢ ÕžÖ€ÕĄ", + "login_form_endpoint_url": "ՍÕĨÖ€ÕžÕĨրÕĢ ÕžÕĨրÕģÕļÕĄÕ¯ÕĨÕŋÕĢ URL", + "login_form_save_login": "ՄÕļÕĄÕŦ մուÕŋÖ„ÕĄÕŖÖ€ÕžÕĄÕŽ", + "login_password_changed_success": "ÔŗÕĄÕ˛ÕŋÕļÕĄÕĸÕĄÕŧÕ¨ Õ°ÕĄÕģÕ¸Õ˛Õ¸Ö‚ÕŠÕĩÕĄÕ´Õĸ ÖƒÕ¸ÖƒÕ¸Õ­ÕžÕĨÕŦ Õ§", "map_assets_in_bounds": "{count} ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "map_location_picker_page_use_location": "Õ•ÕŖÕŋÕĄÕŖÕ¸Ö€ÕŽÕĨÕŦ ÕĄÕĩÕŊ ÕŋÕĨÕ˛Õ¨", + "map_location_service_disabled_title": "ՏÕĨÕ˛Õ¸Ö€Õ¸ÕˇÕ´ÕĄÕļ ÕŽÕĄÕŧÕĄÕĩÕ¸Ö‚ÕŠÕĩուÕļÕļÕĨրÕļ ÕĄÕļÕģÕĄÕŋÕžÕĄÕŽ ÕĨÕļ", + "map_no_location_permission_title": "ՏÕĨÕ˛Õ¸Ö€Õ¸ÕˇÕ´ÕĄÕļ ÕŠÕ¸Ö‚ÕĩÕŦÕŋÕžÕ¸Ö‚ÕŠÕĩուÕļÕ¨ Õ´ÕĨրÕĒÕžÕĄÕŽ Õ§", + "map_settings_date_range_option_day": "ՎÕĨրÕģÕĢÕļ 24 ÕĒÕĄÕ´Õ¸Ö‚Õ´", + "map_settings_date_range_option_days": "ՎÕĨրÕģÕĢÕļ {days} օրում", + "map_settings_date_range_option_years": "ՎÕĨրÕģÕĢÕļ {years} ÕŋÕĄÖ€Õ¸Ö‚Õ´", + "mark_as_read": "Õ†ÕˇÕĨÕŦ Õ¯ÕĄÖ€Õ¤ÕĄÖÕĄÕŽ", + "new_pin_code": "Նոր ՊÔģՆ Õ¯Õ¸Õ¤", + "on_this_device": "ÔąÕĩÕŊ ÕŊÕĄÖ€Ö„ÕĄÕžÕ¸Ö€Õ¸Ö‚Õ´Õ¸Ö‚Õ´", "partner_list_user_photos": "{}-ÕĢÕļ ÕļÕ¯ÕĄÖ€ÕļÕĨրը", + "permission_onboarding_go_to_settings": "ÔŗÕļÕĄÕŦ Õ¯ÕĄÖ€ÕŖÕĄÕžÕ¸Ö€Õ¸Ö‚Õ´ÕļÕĨր", "photos": "Õ†Õ¯ÕĄÖ€ÕļÕĨր", + "pin_verification": "ՊÔģՆ Õ¯Õ¸Õ¤ÕĢ ÕŊÕŋÕ¸Ö‚ÕŖÕ¸Ö‚Õ´", + "reset_pin_code": "ՎÕĨÖ€ÕĄÕ¯ÕĄÕļÕŖÕļÕĨÕŦ ՊÔģՆ Õ¯Õ¸Õ¤Õ¨", "save": "ÕŠÕĄÕ°ÕĨ", + "save_to_gallery": "ÕŠÕĄÕ°ÕēÕĄÕļÕĨÕŦ ÕēÕĄÕŋÕ¯ÕĨÖ€ÕĄÕŊÖ€ÕĄÕ°Õ¸Ö‚Õ´", "scan_library": "Õ†ÕĄÕĩÕĨ", "search": "ՓÕļÕŋրÕĨ", "search_city": "ՈրոÕļÕĨ Ö„ÕĄÕ˛ÕĄÖ„â€Ļ", + "search_filter_camera_title": "Ô¸ÕļÕŋրÕĨÕŦ ÕŋÕĨÕŊÕĄÕ­ÖÕĢÕ¯ÕĢ ÕŋÕĨÕŊÕĄÕ¯Õ¨", "search_filter_date": "ÔąÕ´ÕŊÕĄÕŠÕĢÕž", "search_filter_date_interval": "{start} Õ´ÕĢÕļÕšÕĨւ {end}", + "search_filter_display_option_not_in_album": "ÔąÕŦÕĸոմում ÕšÕ¯ÕĄ", "search_filter_location": "ՏÕĨÕ˛", "search_filter_location_title": "Ô¸ÕļÕŋրÕĨ ÕŋÕĨÕ˛", + "search_filter_media_type_title": "Ô¸ÕļÕŋրÕĨÕŦ Õ´ÕĨÕ¤ÕĢÕĄÕĩÕĢ ÕŋÕĨÕŊÕĄÕ¯Õ¨", + "search_no_more_result": "ÔąÕĩÕŦևÕŊ ÕĄÖ€Õ¤ÕĩուÕļքÕļÕĨր ÕšÕ¯ÕĄÕļ", "search_no_people": "ÕˆÕš Õ´ÕĢ ÕĄÕļÕą", "search_page_motion_photos": "Õ‡ÕĄÖ€ÕĒÕžÕ¸Õ˛ Õ†Õ¯ÕĄÖ€ÕļÕĨր", "select_photos": "Ô¸ÕļÕŋրÕĨ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "setting_image_viewer_original_title": "Ô˛ÕĨÕŧÕļÕĨÕŦ ÕĸÕļÕĄÕŖÕĢր ÕļÕ¯ÕĄÖ€Õ¨", + "setting_image_viewer_preview_title": "Ô˛ÕĨÕŧÕļÕĨÕŦ ÕļÕĄÕ­ÕĄÕ¤ÕĢÕŋÕ´ÕĄÕļ ÕļÕ¯ÕĄÖ€Õ¨", "setting_notifications_notify_never": "ÕĨրÕĸÕĨք", "setting_notifications_notify_seconds": "{count} ÕžÕĄÕĩրկÕĩÕĄÕļ", + "setting_video_viewer_original_video_title": "ՍÕŋÕĢÕēÕĨÕŦ ÕĸÕļÕĄÕŖÕĢր ÕŋÕĨÕŊÕĄÕ°Õ¸ÕŦÕ¸ÕžÕĄÕ¯Õ¨", "share_add_photos": "ÔąÕžÕĨÕŦÕĄÖÕļÕĨÕŦ ÕļÕ¯ÕĄÖ€ÕļÕĨր", + "shared_album_activities_input_disable": "ՄÕĨÕ¯ÕļÕĄÕĸÕĄÕļÕ¸Ö‚ÕŠÕĩուÕļÕļÕĨրÕļ ÕĄÕļÕģÕĄÕŋÕžÕĄÕŽ ÕĨÕļ", + "shared_link_clipboard_copied_massage": "ÕŠÕĄÕŋÕŗÕĨÕļÕĄÕ°ÕĄÕļÕžÕĄÕŽ Õ§", "shared_link_edit_expire_after_option_day": "1 օր", "shared_link_edit_expire_after_option_days": "{count} օր", "shared_link_edit_expire_after_option_hour": "1 ÕĒÕĄÕ´", @@ -54,8 +124,14 @@ "shared_link_edit_expire_after_option_minutes": "{count} րոÕēÕĨ", "shared_link_edit_expire_after_option_months": "{count} ÕĄÕ´ÕĢÕŊ", "shared_link_edit_expire_after_option_year": "{count} ÕŋÕĄÖ€ÕĢ", + "shared_link_manage_links": "ÔŋÕĄÕŧÕĄÕžÕĄÖ€ÕĨÕŦ Õ¯ÕĢÕŊÕžÕĄÕŽ Õ°Õ˛Õ¸Ö‚Õ´ÕļÕĨրը", + "shared_with_me": "ÔģÕļÕą Õ°ÕĨÕŋ Õ¯ÕĢÕŊÕžÕĄÕŽ", + "sharing_silver_appbar_create_shared_album": "Նոր Õ¯ÕĢÕŊÕžÕĄÕŽ ÕĄÕŦÕĸÕ¸Õ´", + "sharing_silver_appbar_share_partner": "ÔŋÕĢÕŊÕĨÕŦ ÕŖÕ¸Ö€ÕŽÕ¨ÕļÕ¯ÕĨրոÕģ Õ°ÕĨÕŋ", "sort_oldest": "ÔąÕ´ÕĨÕļÕĄÕ°ÕĢÕļ ÕļÕ¯ÕĄÖ€Õ¨", "sort_recent": "ÔąÕ´ÕĨÕļÕĄÕļոր ÕļÕ¯ÕĄÖ€Õ¨", + "theme_setting_image_viewer_quality_title": "Õ†Õ¯ÕĄÖ€ÕĢ Õ¤ÕĢÕŋÕ´ÕĄÕļ Õ¸Ö€ÕĄÕ¯", + "theme_setting_system_primary_color_title": "Õ•ÕŖÕŋÕĄÕŖÕ¸Ö€ÕŽÕĨÕŦ Õ°ÕĄÕ´ÕĄÕ¯ÕĄÖ€ÕŖÕĢ ÕŖÕ¸Ö‚ÕĩÕļÕ¨", "timezone": "ÔēÕĄÕ´ÕĄÕĩÕĢÕļ ÕŖÕ¸ÕŋÕĢ", "to_trash": "ÔąÕ˛Õĸ", "trash": "ÔąÕ˛Õĸ", @@ -65,9 +141,12 @@ "unknown_country": "ÔąÕļÕ°ÕĄÕĩÕŋ ÔĩրկÕĢր", "unknown_year": "ÔąÕļÕ°ÕĄÕĩÕŋ ÕÕĄÖ€ÕĢ", "upload_status_errors": "ÕÕ­ÕĄÕŦÕļÕĨր", + "use_current_connection": "Ö…ÕŖÕŋÕĄÕŖÕ¸Ö€ÕŽÕĨÕŦ ÕŋÕžÕĩÕĄÕŦ Õ¯ÕĄÕēÕ¨", "version_announcement_closing": "Քո Õ¨ÕļÕ¯ÕĨրը՝ ÔąÕŦÕĨքÕŊÕ¨", "week": "Õ‡ÕĄÕĸÕĄÕŠ", "welcome": "Ô˛ÕĄÖ€ÕĢ ÕŖÕĄÕŦուÕŊÕŋ", + "wrong_pin_code": "ÕÕ­ÕĄÕŦ ՊÔģՆ Õ¯Õ¸Õ¤", "year": "ÕÕĄÖ€ÕĢ", - "yes": "ÔąÕĩÕ¸" + "yes": "ÔąÕĩÕ¸", + "your_wifi_name": "ՁÕĨր Wi-Fi ÕĄÕļուÕļÕ¨" } diff --git a/i18n/id.json b/i18n/id.json index 4d15ef01e9..8dba86752e 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -2,18 +2,19 @@ "about": "Tentang", "account": "Akun", "account_settings": "Pengaturan Akun", - "acknowledge": "Pernyataan", + "acknowledge": "Mengerti", "action": "Tindakan", "action_common_update": "Perbarui", "actions": "Tindakan", "active": "Aktif", "activity": "Aktivitas", "activity_changed": "Aktivitas {enabled, select, true {diaktifkan} other {dinonaktifkan}}", - "add": "Tambah", - "add_a_description": "Tambahkan deskripsi", + "add": "Tambahkan", + "add_a_description": "Tambahkan sebuah deskripsi", "add_a_location": "Tambahkan lokasi", "add_a_name": "Tambahkan nama", "add_a_title": "Tambahkan judul", + "add_birthday": "Tambahkan Tanggal Lahir", "add_endpoint": "Tambahkan titik akhir", "add_exclusion_pattern": "Tambahkan pola pengecualian", "add_import_path": "Tambahkan jalur impor", @@ -27,6 +28,9 @@ "add_to_album": "Tambahkan ke album", "add_to_album_bottom_sheet_added": "Ditambahkan ke {album}", "add_to_album_bottom_sheet_already_exists": "Sudah ada di {album}", + "add_to_album_toggle": "Masukkan ke {album} / Batalkan dari {album}", + "add_to_albums": "Tambahkan ke album", + "add_to_albums_count": "Tambahkan ke album ({count})", "add_to_shared_album": "Tambahkan ke album terbagi", "add_url": "Tambahkan URL", "added_to_archive": "Ditambahkan ke arsip", @@ -34,6 +38,7 @@ "added_to_favorites_count": "Ditambahkan {count, number} ke favorit", "admin": { "add_exclusion_pattern_description": "Tambahkan pola pengecualian. Glob menggunakan *, **, dan ? didukung. Untuk mengabaikan semua berkas dalam direktori apa pun bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua berkas berakhiran dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan jalur absolut, gunakan \"/jalur/untuk/diabaikan/**\".", + "admin_user": "Pengguna Admin", "asset_offline_description": "Aset pustaka eksternal ini tidak ada di diska dan telah dipindahkan ke tempat sampah. Jika berkasnya dipindah dalam pustaka, periksa lini masa Anda untuk aset baru yang cocok. Untuk memulihkan aset ini, pastikan jalur berkas di bawah dapat diakses oleh Immich dan pindai pustaka.", "authentication_settings": "Pengaturan Autentikasi", "authentication_settings_description": "Kelola kata sandi, OAuth, dan pengaturan autentikasi lainnya", @@ -43,8 +48,15 @@ "backup_database": "Buat Cadangan Basis Data", "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", + "backup_onboarding_1_description": "Simpan salinan cadangan di awan atau di lokasi fisik terpisah.", + "backup_onboarding_2_description": "Salinan lokal diperangkat yang berbeda. Termasuk berkas utama dan cadangannya disimpan secara lokal.", + "backup_onboarding_3_description": "jumlah salinan data, termasuk berkas asli yang termasuk 1 salinan diluar dan 2 salinan lokal.", + "backup_onboarding_description": "Disarankan menggunakan metode pencadangan 3-2-1untuk melindungi data anda. Anda disarankan untuk menyimpan salinan foto/video serta basis data Immich untuk memastikan solusi pencadangan secara menyeluruh.", + "backup_onboarding_footer": "Untuk informasi lebih lanjut terkait pencadangan Immich, silahkan mengunjungi dokumentasi.", + "backup_onboarding_parts_title": "Metode pencadangan 3-2-1 meliputi:", + "backup_onboarding_title": "Cadangkan", "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.", + "backup_settings_description": "Kelola pengaturan pencadangan basis data.", "cleared_jobs": "Tugas terselesaikan untuk: {job}", "config_set_by_file": "Konfigurasi saat ini ditetapkan oleh berkas konfigurasi", "confirm_delete_library": "Apakah Anda yakin ingin menghapus pustaka {library}?", @@ -111,6 +123,13 @@ "logging_enable_description": "Aktifkan log", "logging_level_description": "Ketika diaktifkan, tingkat log apa yang digunakan.", "logging_settings": "Penulisan log", + "machine_learning_availability_checks": "Pemeriksaan ketersediaan", + "machine_learning_availability_checks_description": "Secara otomatis mendeteksi dan memprioritaskan server machine learning yang tersedia", + "machine_learning_availability_checks_enabled": "Aktifkan pemeriksaan ketersediaan", + "machine_learning_availability_checks_interval": "Interval pemeriksaan", + "machine_learning_availability_checks_interval_description": "Interval dalam milidetik antar pemeriksaan ketersediaan", + "machine_learning_availability_checks_timeout": "Batas waktu permintaan", + "machine_learning_availability_checks_timeout_description": "Batas waktu dalam milidetik untuk pemeriksaan ketersediaan", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Nama model CLIP yang didaftarkan di sini. Anda harus menjalankan ulang tugas 'Pencarian Otomatis' untuk semua gambar ketika mengganti model.", "machine_learning_duplicate_detection": "Deteksi Duplikat", @@ -165,12 +184,26 @@ "metadata_settings_description": "Kelola pengaturan metadata", "migration_job": "Migrasi", "migration_job_description": "Migrasikan gambar kecil untuk aset dan wajah ke struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", + "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", + "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, kadaluarsa dari database", + "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", + "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_missing_thumbnails_setting": "Membuat thumbnail yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Mengantrikan aset tanpa thumbnail untuk pembuatan thumbnail", + "nightly_tasks_settings": "Pengaturan Tugas Malam", + "nightly_tasks_settings_description": "Atur tugas malam", + "nightly_tasks_start_time_setting": "Waktu mulai", + "nightly_tasks_start_time_setting_description": "Waktu saat server mulai menjalankan tugas malam", + "nightly_tasks_sync_quota_usage_setting": "Sinkronisasi penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Pembaruan kuota penyimpanan pengguna, berdasarkan penggunaan sekarang", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \"", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email.", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", @@ -195,6 +228,8 @@ "oauth_mobile_redirect_uri": "URI pengalihan ponsel", "oauth_mobile_redirect_uri_override": "Penimpaan URI penerusan ponsel", "oauth_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti ''{callback}''", + "oauth_role_claim": "Klaim Peran", + "oauth_role_claim_description": "Secara otomatis memberikan akses admin berdasarkan keberadaan klaim ini. Klaim dapat berupa \"user\" atau \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", @@ -203,7 +238,7 @@ "oauth_storage_quota_claim": "Klaim kuota penyimpanan", "oauth_storage_quota_claim_description": "Atur kuota penyimpanan pengguna menjadi nilai klaim ini secara otomatis.", "oauth_storage_quota_default": "Kuota penyimpanan bawaan (GiB)", - "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan ketika tidak ada klaim yang disediakan (Masukkan 0 untuk kuota tidak terbatas).", + "oauth_storage_quota_default_description": "Kuota dalam GiB akan digunakan jika tidak ada klaim yang diberikan.", "oauth_timeout": "Waktu Permintaan Habis", "oauth_timeout_description": "Waktu habis untuk permintaan dalam milidetik", "password_enable_description": "Masuk dengan surel dan kata sandi", @@ -243,6 +278,7 @@ "storage_template_migration_info": "Templat penyimpanan akan mengubah semua ekstensi ke huruf kecil. Perubahan templat hanya akan diterapkan pada aset baru. Untuk menerapkan templat pada setiap aset yang sebelumnya telah diunggah, jalankan {job}.", "storage_template_migration_job": "Tugas Migrasi Templat Ruang Penyimpanan", "storage_template_more_details": "Untuk detail lebih lanjut tentang fitur ini, pergi ke Templat Penyimpanan dan kekurangannya", + "storage_template_onboarding_description_v2": "Saat diaktifkan, fitur ini akan mengatur file secara otomatis berdasarkan templat yang ditentukan pengguna. Untuk informasi selengkapnya, silakan lihat dokumentasi.", "storage_template_path_length": "Batas panjang jalur: {length, number}{limit, number}", "storage_template_settings": "Templat Penyimpanan", "storage_template_settings_description": "Kelola struktur folder dan nama berkas dari aset yang diunggah", @@ -257,7 +293,7 @@ "template_email_update_album": "Perbarui Templat Album", "template_email_welcome": "Templat surel selamat datang", "template_settings": "Templat Notifikasi", - "template_settings_description": "Kelola templat kustom untuk notifikasi.", + "template_settings_description": "Kelola templat kustom untuk notifikasi", "theme_custom_css_settings": "CSS Kustom", "theme_custom_css_settings_description": "CSS memungkinkan desain Immich untuk diubah.", "theme_settings": "Pengaturan Tema", @@ -289,7 +325,7 @@ "transcoding_encoding_options": "Opsi Pengodean", "transcoding_encoding_options_description": "Atur kodek, resolusi, kualitas dan opsi lainnya untuk video terenkode", "transcoding_hardware_acceleration": "Akselerasi Perangkat Keras", - "transcoding_hardware_acceleration_description": "Uji coba; lebih cepat, tetapi akan memiliki kualitas lebih rendah pada kecepatan bit yang sama", + "transcoding_hardware_acceleration_description": "Eksperimental: transcoding lebih cepat tetapi dapat menurunkan kualitas pada bitrate yang sama", "transcoding_hardware_decoding": "Dekode perangkat keras", "transcoding_hardware_decoding_setting_description": "Mengaktifkan akselerasi ujung ke ujung daripada hanya mengakselerasi pengodean. Mungkin tidak berfungsi pada semua video.", "transcoding_max_b_frames": "Bingkai B maksimum", @@ -329,6 +365,9 @@ "trash_number_of_days_description": "Jumlah hari untuk menyimpan aset dalam sampah sebelum dihapus secara permanen", "trash_settings": "Pengaturan Sampah", "trash_settings_description": "Kelola pengaturan sampah", + "unlink_all_oauth_accounts": "Putuskan tautan semua akun OAuth", + "unlink_all_oauth_accounts_description": "Pastikan untuk memutus tautan semua akun OAuth sebelum melakukan migrasi ke penyedia baru.", + "unlink_all_oauth_accounts_prompt": "Apakah Anda yakin ingin memutus tautan semua akun OAuth? Tindakan ini akan mengatur ulang ID OAuth untuk setiap pengguna dan tidak dapat dibatalkan.", "user_cleanup_job": "Pembersihan data pengguna", "user_delete_delay": "Akun dan aset {user} akan dijadwalkan untuk penghapusan permanen dalam {delay, plural, one {# hari} other {# hari}}.", "user_delete_delay_settings": "Jeda penghapusan", @@ -358,10 +397,19 @@ "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: {level}", - "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat gambar kecil dengan cepat. Menyalakan ini akan memuat gambar kecil dari server.", + "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat akan lambat memuat gambar kecil dari lokal. Menyalakan ini akan memuat gambar kecil dari peladen.", "advanced_settings_prefer_remote_title": "Prioritaskan gambar dari server", + "advanced_settings_proxy_headers_subtitle": "Tentukan header proxy yang harus dikirim Immich dengan setiap permintaan jaringan", + "advanced_settings_proxy_headers_title": "Tajuk Proksi", + "advanced_settings_readonly_mode_subtitle": "Mengaktifkan mode baca-saja, di mana foto hanya bisa dilihat. Fitur seperti memilih banyak foto, berbagi, cast, dan hapus akan dinonaktifkan. Mode baca-saja bisa diaktifkan/nonaktifkan lewat avatar pengguna di layar utama", + "advanced_settings_readonly_mode_title": "Mode Baca-Saja", + "advanced_settings_self_signed_ssl_subtitle": "Melewati verifikasi sertifikat SSL untuk titik akhir server. Diperlukan untuk sertifikat yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Izinkan sertifikat SSL yang ditandatangani sendiri", "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": "Pengaturan pengguna tingkat lanjut", + "advanced_settings_troubleshooting_subtitle": "Aktifkan fitur tambahan untuk pemecahan masalah", + "advanced_settings_troubleshooting_title": "Penelusuran masalah", "age_months": "Umur {months, plural, one {# bulan} other {# bulan}}", "age_year_months": "Umur 1 tahun, {months, plural, one {# bulan} other {# bulan}}", "age_years": "{years, plural, other {Umur #}}", @@ -370,6 +418,7 @@ "album_cover_updated": "Kover album diperbarui", "album_delete_confirmation": "Apakah Anda yakin ingin menghapus album {album}?", "album_delete_confirmation_description": "Jika album ini dibagikan, pengguna lain tidak akan dapat mengaksesnya lagi.", + "album_deleted": "Album dihapus", "album_info_card_backup_album_excluded": "Dikecualikan", "album_info_card_backup_album_included": "Terpilih", "album_info_updated": "Info album diperbarui", @@ -379,7 +428,9 @@ "album_options": "Opsi Album", "album_remove_user": "Keluarkan pengguna?", "album_remove_user_confirmation": "Apakah Anda yakin ingin mengeluarkan {user}?", + "album_search_not_found": "Tidak ada album yang ditemukan sesuai pencarian Anda", "album_share_no_users": "Sepertinya Anda telah membagikan album ini dengan semua pengguna atau tidak memiliki pengguna siapa pun untuk dibagikan.", + "album_summary": "Ringkasan album", "album_updated": "Album diperbarui", "album_updated_setting_description": "Terima notifikasi surel ketika album terbagi memiliki aset baru", "album_user_left": "Keluar dari {album}", @@ -395,6 +446,10 @@ "album_with_link_access": "Perbolehkan siapa pun dengan tautan melihat foto dan orang dalam album ini.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album}other {{count, number} Album}}", + "albums_default_sort_order": "Urutan album bawaan", + "albums_default_sort_order_description": "Urutan awal aset saat membuat album baru.", + "albums_feature_description": "Koleksi foto atau video yang dapat dibagikan kepada pengguna lain.", + "albums_on_device_count": "Album di perangkat ({count})", "all": "Semua", "all_albums": "Semua album", "all_people": "Semua orang", @@ -414,21 +469,27 @@ "app_bar_signout_dialog_title": "Keluar akun", "app_settings": "Pengaturan Aplikasi", "appears_in": "Muncul dalam", + "apply_count": "Terapkan ({count, number})", "archive": "Arsip", + "archive_action_prompt": "{count} telah ditambahkan ke Arsip", "archive_or_unarchive_photo": "Arsipkan atau batalkan pengarsipan foto", "archive_page_no_archived_assets": "Tidak ada aset diarsipkan", "archive_page_title": "Arsip ({count})", "archive_size": "Ukuran arsip", "archive_size_description": "Atur ukuran arsip untuk unduhan (dalam GiB)", + "archived": "Diarsipkan", "archived_count": "{count, plural, other {# terarsip}}", "are_these_the_same_person": "Apakah ini adalah orang yang sama?", "are_you_sure_to_do_this": "Apakah Anda yakin ingin melakukan ini?", + "asset_action_delete_err_read_only": "Tidak dapat menghapus aset yang bersifat hanya-baca, proses dilewati", + "asset_action_share_err_offline": "Tidak dapat mengambil aset luring, dilewati", "asset_added_to_album": "Telah ditambahkan ke album", "asset_adding_to_album": "Menambahkan ke albumâ€Ļ", "asset_description_updated": "Deskripsi aset telah diperbarui", "asset_filename_is_offline": "Aset {filename} sedang luring", "asset_has_unassigned_faces": "Aset memiliki wajah yang belum ditetapkan", "asset_hashing": "Memilahâ€Ļ", + "asset_list_group_by_sub_title": "Kelompokkan berdasarkan", "asset_list_layout_settings_dynamic_layout_title": "Tata dinamis", "asset_list_layout_settings_group_automatically": "Otomatis", "asset_list_layout_settings_group_by": "Kelompokkan aset berdasarkan", @@ -438,18 +499,26 @@ "asset_list_settings_title": "Grid Foto", "asset_offline": "Aset Luring", "asset_offline_description": "Aset eksternal ini tidak ada lagi di diska. Silakan hubungi administrator Immich Anda untuk bantuan.", + "asset_restored_successfully": "Aset telah berhasil dipulihkan", "asset_skipped": "Dilewati", "asset_skipped_in_trash": "Dalam sampah", + "asset_trashed": "Aset dibuang", + "asset_troubleshoot": "Troubleshoot Aset", "asset_uploaded": "Sudah diunggah", "asset_uploading": "Mengunggahâ€Ļ", + "asset_viewer_settings_subtitle": "Kelola pengaturan penampil galeri Anda", "asset_viewer_settings_title": "Penampil Aset", "assets": "Aset", "assets_added_count": "{count, plural, one {# aset} other {# aset}} ditambahkan", "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 {{name}} other {album baru}}", + "assets_added_to_albums_count": "Ditambahkan {assetTotal, plural, one {# aset} other {# aset}} ke {albumTotal, plural, one {# album} other {# album}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} tidak dapat ditambahkan ke album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Aset} other {Aset}} tidak dapat ditambahkan ke album mana pun", "assets_count": "{count, plural, one {# aset} other {# aset}}", "assets_deleted_permanently": "{count} aset dihapus secara permanen", "assets_deleted_permanently_from_server": "{count} aset dihapus secara permanen dari server Immich", + "assets_downloaded_failed": "{count, plural, one {# berkas diunduh - {error} berkas gagal} other {# berkas diunduh - {error} berkas gagal}}", + "assets_downloaded_successfully": "{count, plural, one {# berkas berhasil diunduh} other {# berkas berhasil diunduh}}", "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", "assets_removed_count": "{count, plural, one {# aset} other {# aset}} dihapus", @@ -461,24 +530,40 @@ "assets_trashed_count": "{count, plural, one {# aset} other {# aset}} dibuang ke sampah", "assets_trashed_from_server": "{count} aset dipindahkan ke sampah dari server Immich", "assets_were_part_of_album_count": "{count, plural, one {Aset telah} other {Aset telah}} menjadi bagian dari album", + "assets_were_part_of_albums_count": "{count, plural, one {Aset sudah} other {Aset sudah}} ada di album", "authorized_devices": "Perangkat Terautentikasi", + "automatic_endpoint_switching_subtitle": "Sambungkan secara lokal melalui Wi-Fi yang telah ditetapkan saat tersedia, dan gunakan koneksi alternatif lain", + "automatic_endpoint_switching_title": "Peralihan URL otomatis", + "autoplay_slideshow": "Putar otomatis tayangan slide", "back": "Kembali", "back_close_deselect": "Kembali, tutup, atau batalkan pemilihan", + "background_backup_running_error": "Cadangan latar belakang sedang berjalan, tidak dapat memulai cadangan manual", + "background_location_permission": "Izin lokasi latar belakang", + "background_location_permission_content": "Untuk beralih jaringan saat berjalan di latar belakang, Immich harus selalu memiliki akses lokasi akurat agar aplikasi dapat membaca nama jaringan Wi-Fi", + "background_options": "Opsi Latar Belakang", + "backup": "Cadangkan", "backup_album_selection_page_albums_device": "Album di perangkat ({count})", "backup_album_selection_page_albums_tap": "Sentuh untuk memilih, sentuh 2x untuk mengecualikan", "backup_album_selection_page_assets_scatter": "Aset dapat tersebar dalam banyak album. Sehingga album dapat dipilih atau dikecualikan saat proses pencadangan.", "backup_album_selection_page_select_albums": "Pilih album", "backup_album_selection_page_selection_info": "Info Pilihan", "backup_album_selection_page_total_assets": "Total aset unik", + "backup_albums_sync": "Sinkronisasi cadangan album", "backup_all": "Semua", "backup_background_service_backup_failed_message": "Gagal mencadangkan aset. Mencoba lagiâ€Ļ", "backup_background_service_connection_failed_message": "Koneksi ke server gagal. Mencoba ulangâ€Ļ", "backup_background_service_current_upload_notification": "Mengunggah {filename}", "backup_background_service_default_notification": "Memeriksa aset baruâ€Ļ", + "backup_background_service_error_title": "Galat cadangan", "backup_background_service_in_progress_notification": "Mencadangkan asetmuâ€Ļ", "backup_background_service_upload_failure_notification": "Gagal mengunggah {filename}", "backup_controller_page_albums": "Cadangkan album", + "backup_controller_page_background_app_refresh_disabled_content": "Aktifkan penyegaran aplikasi di latar belakang di Pengaturan > Umum > Penyegaran Aplikasi Latar Belakang untuk menggunakan pencadangan latar belakang.", + "backup_controller_page_background_app_refresh_disabled_title": "Penyegaran aplikasi latar belakang dinonaktifkan", "backup_controller_page_background_app_refresh_enable_button_text": "Ke setelan", + "backup_controller_page_background_battery_info_link": "Tunjukkan caranya", + "backup_controller_page_background_battery_info_message": "Untuk mendapatkan pengalaman pencadangan latar belakang yang optimal, nonaktifkan setiap pengaturan pengoptimalan baterai yang membatasi aktivitas latar belakang pada aplikasi Immich.\n\nKarena pengaturan ini bergantung pada jenis perangkat, silakan cari informasi yang relevan sesuai dengan produsen perangkat Anda.", + "backup_controller_page_background_battery_info_ok": "Ok", "backup_controller_page_background_battery_info_title": "Optimisasi baterai", "backup_controller_page_background_charging": "Hanya saat mengisi daya", "backup_controller_page_background_configure_error": "Gagal mengatur layanan latar belakang", @@ -518,7 +603,10 @@ "backup_manual_in_progress": "Dalam proses unggah. Coba lagi nanti", "backup_manual_success": "Sukses", "backup_manual_title": "Status unggah", + "backup_options": "Pilihan pengaturan pencadangan", "backup_options_page_title": "Setelan cadangan", + "backup_setting_subtitle": "Kelola pengaturan unggahan latar belakang dan latar depan", + "backup_settings_subtitle": "Kelola pengaturan unggahan", "backward": "Maju", "biometric_auth_enabled": "Autentikasi biometrik diaktifkan", "biometric_locked_out": "Anda terkunci oleh autentikasi biometrik", @@ -537,7 +625,7 @@ "cache_settings_clear_cache_button": "Hapus cache", "cache_settings_clear_cache_button_title": "Membersihkan cache aplikasi. Performa aplikasi akan terpengaruh hingga cache dibuat kembali.", "cache_settings_duplicated_assets_clear_button": "BERSIHKAN", - "cache_settings_duplicated_assets_subtitle": "Foto dan video yang masuk dalam daftar hitam oleh aplikasi", + "cache_settings_duplicated_assets_subtitle": "Foto dan video yang masuk dalam daftar abaikan oleh aplikasi", "cache_settings_duplicated_assets_title": "Aset Duplikat ({count})", "cache_settings_statistics_album": "Pustaka thumbnail", "cache_settings_statistics_full": "Gambar penuh", @@ -554,17 +642,19 @@ "cancel": "Batal", "cancel_search": "Batalkan pencarian", "canceled": "Dibatalkan", + "canceling": "Membatalkan", "cannot_merge_people": "Tidak dapat menggabungkan orang", "cannot_undo_this_action": "Anda tidak dapat mengurungkan tindakan ini!", "cannot_update_the_description": "Tidak dapat memperbarui deskripsi", "cast": "Pencerminan", + "cast_description": "Mengonfigurasi tujuan cast yang tersedia", "change_date": "Ubah tanggal", "change_description": "Ganti deskripsi", "change_display_order": "Mengubah urutan tampilan", "change_expiration_time": "Ubah waktu kedaluwarsa", "change_location": "Ubah lokasi", "change_name": "Ubah nama", - "change_name_successfully": "Perubahan nama berhasil", + "change_name_successfully": "Nama berhasil diubah", "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", @@ -575,6 +665,8 @@ "change_pin_code": "Ubah kode PIN", "change_your_password": "Ubah kata sandi Anda", "changed_visibility_successfully": "Keterlihatan berhasil diubah", + "charging": "Mengisi daya", + "charging_requirement_mobile_backup": "Cadangan latar belakang memerlukan perangkat dalam keadaan mengisi daya", "check_corrupt_asset_backup": "Periksa cadangan aset yang rusak", "check_corrupt_asset_backup_button": "Lakukan pemeriksaan", "check_corrupt_asset_backup_description": "Jalankan pemeriksaan ini hanya melalui Wi-Fi dan setelah semua aset dicadangkan. Prosedur ini mungkin memerlukan waktu beberapa menit.", @@ -584,6 +676,7 @@ "clear": "Hapus", "clear_all": "Hapus semua", "clear_all_recent_searches": "Hapus semua pencarian terakhir", + "clear_file_cache": "Hapus tembolok berkas", "clear_message": "Hapus pesan", "clear_value": "Hapus nilai", "client_cert_dialog_msg_confirm": "OK", @@ -614,6 +707,9 @@ "confirm_keep_this_delete_others": "Semua aset lain di dalam stack akan dihapus kecuali aset ini. Anda yakin untuk melanjutkan?", "confirm_new_pin_code": "Konfirmasi kode PIN baru", "confirm_password": "Konfirmasi kata sandi", + "confirm_tag_face": "Apakah Anda ingin menandai wajah ini sebagai {name}?", + "confirm_tag_face_unnamed": "Apakah Anda ingin menandai wajah ini?", + "connected_device": "Perangkat terhubung", "connected_to": "Tersambung ke", "contain": "Berisi", "context": "Konteks", @@ -651,11 +747,13 @@ "create_new_user": "Buat pengguna baru", "create_shared_album_page_share_add_assets": "TAMBAHKAN ASET", "create_shared_album_page_share_select_photos": "Pilih Foto", + "create_shared_link": "Membuat tautan yang dapat dibagikan", "create_tag": "Buat tag", "create_tag_description": "Buat tag baru. Untuk tag bersarang, harap input jalur tag secara lengkap termasuk tanda garis miring ke depan.", "create_user": "Buat pengguna", "created": "Dibuat", "created_at": "Dibuat", + "creating_linked_albums": "Membuat album tertaut...", "crop": "Pangkas", "curated_object_page_title": "Benda", "current_device": "Perangkat saat ini", @@ -663,9 +761,11 @@ "current_server_address": "Alamat server saat ini", "custom_locale": "Lokal Khusus", "custom_locale_description": "Format tanggal dan angka berdasarkan bahasa dan wilayah", + "custom_url": "URL Kustom", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "Gelap", + "dark_theme": "Nyalakan mode gelap", "date_after": "Tanggal setelah", "date_and_time": "Tanggal dan Waktu", "date_before": "Tanggal sebelum", @@ -673,6 +773,7 @@ "date_of_birth_saved": "Tanggal lahir berhasil disimpan", "date_range": "Jangka tanggal", "day": "Hari", + "days": "Hari", "deduplicate_all": "Hapus semua duplikat", "deduplication_criteria_1": "Ukuran gambar dalam bita", "deduplication_criteria_2": "Hitungan data EXIF", @@ -681,6 +782,8 @@ "default_locale": "Lokal Bawaan", "default_locale_description": "Format tanggal dan angka berdasarkan peramban lokal Anda", "delete": "Hapus", + "delete_action_confirmation_message": "Apakah Anda yakin ingin menghapus aset ini? Tindakan ini akan memindahkan aset ke tempat sampah pada server dan akan mengkonfirmasi apakah Anda ingin menghapusnya juga secara lokal", + "delete_action_prompt": "Sebanyak {count} item telah dihapus", "delete_album": "Hapus album", "delete_api_key_prompt": "Apakah Anda yakin ingin menghapus kunci API ini?", "delete_dialog_alert": "Item ini akan dihapus permanen dari Immich dan perangkat", @@ -694,9 +797,12 @@ "delete_key": "Hapus kunci", "delete_library": "Hapus Pustaka", "delete_link": "Hapus tautan", + "delete_local_action_prompt": "Sebanyak {count} item telah dihapus secara lokal", "delete_local_dialog_ok_backed_up_only": "Hapus hanya yang telah dicadangkan", "delete_local_dialog_ok_force": "Lanjutkan Hapus", "delete_others": "Hapus lainnya", + "delete_permanently": "Hapus permanen", + "delete_permanently_action_prompt": "Sebanyak {count} item telah dihapus secara permanen", "delete_shared_link": "Hapus tautan terbagi", "delete_shared_link_dialog_title": "Hapus Link Berbagi", "delete_tag": "Hapus tag", @@ -707,11 +813,14 @@ "description": "Deskripsi", "description_input_hint_text": "Tambah deskripsi...", "description_input_submit_error": "Terjadi kesalahan saat memperbarui deskripsi, periksa log untuk detail selengkapnya", + "deselect_all": "Batalkan semua pilihan", "details": "Detail", "direction": "Arah", "disabled": "Dinonaktifkan", "disallow_edits": "Jangan izinkan penyuntingan", + "discord": "Discord", "discover": "Jelajahi", + "discovered_devices": "Perangkat yang ditemukan", "dismiss_all_errors": "Abaikan semua kesalahan", "dismiss_error": "Abaikan kesalahan", "display_options": "Opsi penampilan", @@ -722,6 +831,7 @@ "documentation": "Dokumentasi", "done": "Selesai", "download": "Unduh", + "download_action_prompt": "Sedang mengunduh {count} aset", "download_canceled": "Unduhan dibatalkan", "download_complete": "Unduhan selesai", "download_enqueue": "Unduhan diantrikan", @@ -740,6 +850,7 @@ "download_waiting_to_retry": "Menunggu untuk mencoba lagi", "downloading": "Mengunduh", "downloading_asset_filename": "Mengunduh aset {filename}", + "downloading_media": "Mengunduh media", "drop_files_to_upload": "Lepaskan berkas di mana saja untuk mengunggah", "duplicates": "Duplikat", "duplicates_description": "Selesaikan setiap kelompok dengan menunjukkan mana, jika ada, yang merupakan duplikat", @@ -747,8 +858,12 @@ "edit": "Sunting", "edit_album": "Sunting album", "edit_avatar": "Sunting avatar", + "edit_birthday": "Sunting tanggal lahir", "edit_date": "Tanggal penyuntingan", "edit_date_and_time": "Sunting tanggal dan waktu", + "edit_date_and_time_action_prompt": "Sebanyak {count} tanggal dan waktu disunting", + "edit_date_and_time_by_offset": "Ubah tanggal berdasarkan selisih", + "edit_date_and_time_by_offset_interval": "Rentang tanggal baru: {from} - {to}", "edit_description": "Sunting deskripsi", "edit_description_prompt": "Silakan pilih deskripsi baru:", "edit_exclusion_pattern": "Sunting pola pengecualian", @@ -758,6 +873,7 @@ "edit_key": "Sunting kunci", "edit_link": "Sunting tautan", "edit_location": "Sunting lokasi", + "edit_location_action_prompt": "Sebanyak {count} lokasi telah disunting", "edit_location_dialog_title": "Lokasi", "edit_name": "Sunting nama", "edit_people": "Sunting orang", @@ -765,26 +881,33 @@ "edit_title": "Sunting Judul", "edit_user": "Sunting pengguna", "edited": "Disunting", + "editor": "Penyunting", "editor_close_without_save_prompt": "Perubahan tidak akan di simpan", "editor_close_without_save_title": "Tutup editor?", "editor_crop_tool_h2_aspect_ratios": "Perbandingan aspek", "editor_crop_tool_h2_rotation": "Rotasi", "email": "Surel", "email_notifications": "Notifikasi surel", - "empty_folder": "This folder is empty", + "empty_folder": "Folder ini kosong", "empty_trash": "Kosongkan sampah", "empty_trash_confirmation": "Apakah Anda yakin ingin mengosongkan sampah? Ini akan menghapus semua aset dalam sampah secara permanen dari Immich.\nAnda tidak dapat mengurungkan tindakan ini!", "enable": "Aktifkan", + "enable_backup": "Aktifkan Pencadangan", "enable_biometric_auth_description": "Masukkan kode PIN Anda untuk mengaktifkan autentikasi biometrik", "enabled": "Diaktifkan", "end_date": "Tanggal akhir", + "enqueued": "Diantrikan", "enter_wifi_name": "Masukkan nama Wi-Fi", "enter_your_pin_code": "Masukkan kode PIN Anda", "enter_your_pin_code_subtitle": "Masukkan kode PIN Anda untuk mengakses folder terkunci", "error": "Eror", + "error_change_sort_album": "Gagal mengubah urutan album", "error_delete_face": "Terjadi kesalahan menghapus wajah dari aset", + "error_getting_places": "Kesalahan saat mengambil lokasi", "error_loading_image": "Terjadi eror memuat gambar", + "error_loading_partners": "Kesalahan saat memuat partner: {error}", "error_saving_image": "Kesalahan: {error}", + "error_tag_face_bounding_box": "Galat saat memberi tag wajah – tidak dapat memperoleh koordinat kotak pembatas", "error_title": "Eror - Ada yang salah", "errors": { "cannot_navigate_next_asset": "Tidak dapat menuju ke aset berikutnya", @@ -815,6 +938,7 @@ "failed_to_load_notifications": "Gagal memuat notifikasi", "failed_to_load_people": "Gagal mengunggah orang", "failed_to_remove_product_key": "Gagal menghapus kunci produk", + "failed_to_reset_pin_code": "Gagal atur ulang kode PIN", "failed_to_stack_assets": "Gagal menumpuk aset", "failed_to_unstack_assets": "Gagal membatalkan penumpukan aset", "failed_to_update_notification_status": "Gagal membarui status notifikasi", @@ -823,6 +947,7 @@ "paths_validation_failed": "{paths, plural, one {# jalur} other {# jalur}} gagal validasi", "profile_picture_transparent_pixels": "Foto profil tidak dapat memiliki piksel transparan. Silakan perbesar dan/atau pindah posisi gambar.", "quota_higher_than_disk_size": "Anda menetapkan kuota lebih tinggi dari ukuran diska", + "something_went_wrong": "Terjadi kesalahan", "unable_to_add_album_users": "Tidak dapat menambahkan pengguna ke album", "unable_to_add_assets_to_shared_link": "Tidak dapat menambahkan aset ke tautan terbagi", "unable_to_add_comment": "Tidak dapat menambahkan komentar", @@ -908,13 +1033,11 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Tambahkan Deskripsi...", + "exif_bottom_sheet_description_error": "Galat saat memperbaharui deskripsi", "exif_bottom_sheet_details": "RINCIAN", "exif_bottom_sheet_location": "LOKASI", "exif_bottom_sheet_people": "ORANG", "exif_bottom_sheet_person_add_person": "Tambah nama", - "exif_bottom_sheet_person_age_months": "Umur {months} bulan", - "exif_bottom_sheet_person_age_year_months": "Umur 1 tahun, {months} bulan", - "exif_bottom_sheet_person_age_years": "Umur {years}", "exit_slideshow": "Keluar dari Salindia", "expand_all": "Buka semua", "experimental_settings_new_asset_list_subtitle": "Memproses", @@ -928,37 +1051,56 @@ "explorer": "Jelajah", "export": "Ekspor", "export_as_json": "Ekspor sebagai JSON", + "export_database": "Ekspor Database", + "export_database_description": "Ekspor Database SQLite", "extension": "Ekstensi", "external": "Eksternal", "external_libraries": "Pustaka Eksternal", + "external_network": "Jaringan Eksternal", "external_network_sheet_info": "Ketika tidak berada di jaringan Wi-Fi yang disukai, aplikasi akan terhubung ke server melalui URL pertama di bawah ini yang dapat dijangkaunya, mulai dari atas ke bawah", "face_unassigned": "Tidak ada nama", + "failed": "Gagal", "failed_to_authenticate": "Autentikasi gagal", "failed_to_load_assets": "Gagal memuat aset", + "failed_to_load_folder": "Gagal memuat berkas", "favorite": "Favorit", + "favorite_action_prompt": "{count} sudah ditambahkan kedalam Favorites", "favorite_or_unfavorite_photo": "Favorit atau batalkan pemfavoritan foto", "favorites": "Favorit", "favorites_page_no_favorites": "Tidak ada aset favorit", "feature_photo_updated": "Foto terfitur diperbarui", "features": "Fitur", + "features_in_development": "Fitur dalam Pengembangan", "features_setting_description": "Kelola fitur aplikasi", "file_name": "Nama berkas", "file_name_or_extension": "Nama berkas atau ekstensi", "filename": "Nama berkas", "filetype": "Jenis berkas", + "filter": "Filter", "filter_people": "Saring orang", "filter_places": "Saring tempat", "find_them_fast": "Temukan dengan cepat berdasarkan nama dengan pencarian", + "first": "Pertama", "fix_incorrect_match": "Perbaiki pencocokan salah", + "folder": "Berkas", + "folder_not_found": "Berkas tidak ditemukan", "folders": "Berkas", "folders_feature_description": "Menjelajahi tampilan folder untuk foto dan video pada sistem file", + "forgot_pin_code_question": "Lupa PIN?", "forward": "Maju", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Fitur ini memuat sumber daya eksternal dari Google agar dapat berfungsi.", "general": "Umum", + "geolocation_instruction_location": "Klik aset yang memiliki koordinat GPS untuk menggunakan lokasinya, atau pilih lokasi langsung dari peta", "get_help": "Dapatkan Bantuan", + "get_wifiname_error": "Tidak dapat mendapatkan nama Wi-Fi. Pastikan Anda telah memberikan izin yang diperlukan dan terhubung ke jaringan Wi-Fi", "getting_started": "Memulai", "go_back": "Kembali", "go_to_folder": "Pergi ke folder", "go_to_search": "Pergi ke pencarian", + "gps": "GPS", + "gps_missing": "Tidak ada GPS", + "grant_permission": "Izinkan", "group_albums_by": "Kelompokkan album berdasarkan...", "group_country": "Kelompokkan berdasarkan negara", "group_no": "Tidak ada pengelompokan", @@ -968,6 +1110,15 @@ "haptic_feedback_switch": "Nyalakan getar", "haptic_feedback_title": "Getar", "has_quota": "Memiliki kuota", + "hash_asset": "Aset Hash", + "hashed_assets": "Aset yang di-hash", + "hashing": "Proses Hash", + "header_settings_add_header_tip": "Tambahkan Header", + "header_settings_field_validator_msg": "Nilai tidak boleh kosong", + "header_settings_header_name_input": "Nama Header", + "header_settings_header_value_input": "Nilai Header", + "headers_settings_tile_subtitle": "Menentukan header proksi yang akan dikirimkan oleh aplikasi pada setiap permintaan jaringan", + "headers_settings_tile_title": "Header proksi kustom", "hi_user": "Hai {name} ({email})", "hide_all_people": "Sembunyikan semua orang", "hide_gallery": "Sembunyikan galeri", @@ -976,17 +1127,28 @@ "hide_person": "Sembunyikan orang", "hide_unnamed_people": "Sembunyikan orang tanpa nama", "home_page_add_to_album_conflicts": "Aset {added} telah ditambahkan ke album {album}. Aset {failed} sudah ada dalam album.", + "home_page_add_to_album_err_local": "Belum dapat menambahkan aset lokal ke album, dilewati", "home_page_add_to_album_success": "Aset {added} telah ditambahkan ke {album}.", + "home_page_album_err_partner": "Belum dapat menambahkan aset pasangan ke album, dilewati", + "home_page_archive_err_local": "Belum dapat mengarsipkan aset lokal, dilewati", + "home_page_archive_err_partner": "Tidak dapat mengarsipkan aset pasangan, dilewati", "home_page_building_timeline": "Memuat linimasa", + "home_page_delete_err_partner": "Tidak dapat menghapus aset pasangan, dilewati", + "home_page_delete_remote_err_local": "Aset lokal ada dalam pilihan hapus jarak jauh, dilewat", + "home_page_favorite_err_local": "Belum dapat menandai aset lokal sebagai favorit, dilewati", + "home_page_favorite_err_partner": "Belum dapat menandai aset pasangan sebagai favorit, dilewati", "home_page_first_time_notice": "Jika ini pertama kali Anda menggunakan aplikasi, pastikan untuk memiliki album untuk dicadangkan agar lini masa anda terisi oleh foto dan video dalam album", "home_page_locked_error_local": "Tidak dapat memindahkan aset lokal ke folder terkunci, lewati", "home_page_locked_error_partner": "Tidak dapat memindahkan aset partner ke folder terkunci, lewati", + "home_page_share_err_local": "Tidak bisa membagikan lokal aset melalui tautan, dilewati", "home_page_upload_err_limit": "Hanya dapat mengunggah maksimal 30 aset dalam satu waktu, melewatkan", "host": "Hos", "hour": "Jam", + "hours": "Jam", "id": "ID", - "ignore_icloud_photos": "Ignore iCloud photos", - "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", + "idle": "Siaga", + "ignore_icloud_photos": "Abaikan foto iCloud", + "ignore_icloud_photos_description": "Foto yang disimpan di iCloud tidak akan diunggah ke server Immich", "image": "Gambar", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} pada tanggal {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} diambil oleh {person1} pada {date}", @@ -998,8 +1160,10 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1} dan {person2} pada {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {person3} pada {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} diambil di {city}, {country} oleh {person1}, {person2}, dan {additionalCount, number} lainnya pada {date}", + "image_saved_successfully": "Gambar disimpan", "image_viewer_page_state_provider_download_started": "Unduh dimulai", "image_viewer_page_state_provider_download_success": "Unduh Sukses", + "image_viewer_page_state_provider_share_error": "Galat membagikan", "immich_logo": "Logo Immich", "immich_web_interface": "Antarmuka Web Immich", "import_from_json": "Impor dari JSON", @@ -1011,14 +1175,23 @@ "include_shared_partner_assets": "Termasuk aset terbagi dengan partner", "individual_share": "Bagikan individu", "individual_shares": "Pembagian individu", + "info": "Info", "interval": { "day_at_onepm": "Setiap hari pada 13.00", "hours": "Setiap {hours, plural, one {jam} other {{hours, number} jam}}", "night_at_midnight": "Setiap malam pada 00.00", "night_at_twoam": "Setiap malam pada 02.00" }, + "invalid_date": "Tanggal tidak valid", + "invalid_date_format": "Format Tanggal tidak valid", "invite_people": "Undang Orang", "invite_to_album": "Undang ke album", + "ios_debug_info_fetch_ran_at": "Pengambilan dijalankan {dateTime}", + "ios_debug_info_last_sync_at": "Sinkronisasi terakhir {dateTime}", + "ios_debug_info_no_processes_queued": "Tidak ada proses latar belakang dalam antrean", + "ios_debug_info_no_sync_yet": "Belum ada pekerjaan sinkronisasi latar belakang yang dijalankan", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proses latar belakang dalam antrean} other {{count} proses latar belakang dalam antrean}}", + "ios_debug_info_processing_ran_at": "Pemrosesan dijalankan {dateTime}", "items_count": "{count, plural, one {# item} other {# item}}", "jobs": "Tugas", "keep": "Simpan", @@ -1027,11 +1200,17 @@ "kept_this_deleted_others": "Aset ini dipertahankan dan {count, plural, one {# asset} other {# assets}} dihapus", "keyboard_shortcuts": "Pintasan papan ketik", "language": "Bahasa", + "language_no_results_subtitle": "Coba sesuaikan kata kunci pencarian Anda", + "language_no_results_title": "Bahasa tidak ditemukan", + "language_search_hint": "Mencari Bahasa...", "language_setting_description": "Pilih bahasa Anda yang disukai", + "large_files": "File Besar", + "last": "Terakhir", "last_seen": "Terakhir dilihat", "latest_version": "Versi Terkini", "latitude": "Lintang", "leave": "Tinggalkan", + "leave_album": "Keluar dari album", "lens_model": "Model lensa", "let_others_respond": "Biarkan orang lain merespons", "level": "Tingkat", @@ -1043,15 +1222,23 @@ "library_page_sort_created": "Tanggal dibuat", "library_page_sort_last_modified": "Terakhir diubah", "library_page_sort_title": "Judul album", + "licenses": "Lisensi", "light": "Terang", + "like": "Suka", "like_deleted": "Suka dihapus", "link_motion_video": "Tautan video gerak", - "link_options": "Opsi tautan", "link_to_oauth": "Tautkan ke OAuth", "linked_oauth_account": "Akun OAuth tertaut", "list": "Daftar", "loading": "Memuat", "loading_search_results_failed": "Pemuatan hasil pencarian gagal", + "local": "Lokal", + "local_asset_cast_failed": "Tidak dapat melakukan cast aset yang belum diunggah ke server", + "local_assets": "Aset Lokal", + "local_media_summary": "Ringkasan Media Lokal", + "local_network": "Jaringan Lokal", + "local_network_sheet_info": "Aplikasi akan terhubung ke server melalui URL ini saat menggunakan jaringan Wi-Fi yang ditentukan", + "location_permission": "Izin lokasi", "location_permission_content": "Untuk menggunakan fitur pengalihan otomatis, Immich memerlukan izin lokasi yang akurat agar dapat membaca nama jaringan Wi-Fi saat ini", "location_picker_choose_on_map": "Pilih di peta", "location_picker_latitude_error": "Masukkan lintang yang sah", @@ -1060,8 +1247,10 @@ "location_picker_longitude_hint": "Masukkan bujur di sini", "lock": "Kunci", "locked_folder": "Folder Terkunci", + "log_detail_title": "Detail Log", "log_out": "Log keluar", "log_out_all_devices": "Keluar dari Semua Perangkat", + "logged_in_as": "Masuk sebagai {user}", "logged_out_all_devices": "Semua perangkat telah dikeluarkan", "logged_out_device": "Perangkat telah keluar", "login": "Log masuk", @@ -1074,9 +1263,12 @@ "login_form_err_http": "Harap tentukan http:// atau https://", "login_form_err_invalid_email": "Email Tidak Valid", "login_form_err_invalid_url": "URL Tidak Valid", + "login_form_err_leading_whitespace": "Spasi awal", + "login_form_err_trailing_whitespace": "Spasi akhir", "login_form_failed_get_oauth_server_config": "Gagal logging menggunakan OAuth, periksa URL server", "login_form_failed_get_oauth_server_disable": "Fitur OAuth tidak tersedia di server ini", "login_form_failed_login": "Login gagal, periksa URL server, email dan kata sandi", + "login_form_handshake_exception": "Terjadi Handshake Exception dengan server. Aktifkan dukungan sertifikat swatanda di pengaturan jika Anda menggunakan sertifikat swatanda.", "login_form_password_hint": "sandi", "login_form_save_login": "Ingat saya", "login_form_server_empty": "Masukkan URL server.", @@ -1086,6 +1278,7 @@ "login_password_changed_success": "Sandi berhasil diperbarui", "logout_all_device_confirmation": "Apakah Anda yakin ingin keluar dari semua perangkat?", "logout_this_device_confirmation": "Apakah Anda yakin ingin mengeluarkan perangkat ini?", + "logs": "Log", "longitude": "Bujur", "look": "Tampilan", "loop_videos": "Ulangi video", @@ -1093,6 +1286,7 @@ "main_branch_warning": "Anda menggunakan versi pengembangan; kami sangat menyarankan menggunakan versi rilis!", "main_menu": "Menu utama", "make": "Merek", + "manage_geolocation": "Atur lokasi", "manage_shared_links": "Kelola tautan terbagi", "manage_sharing_with_partners": "Kelola pembagian dengan partner", "manage_the_app_settings": "Kelola pengaturan aplikasi", @@ -1101,8 +1295,7 @@ "manage_your_devices": "Kelola perangkat Anda yang masuk", "manage_your_oauth_connection": "Kelola koneksi OAuth Anda", "map": "Peta", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto", + "map_assets_in_bounds": "{count, plural, =0 {Tidak ada foto di area ini} one {# foto} other {# foto}}", "map_cannot_get_user_location": "Tidak dapat memeroleh lokasi pengguna", "map_location_dialog_yes": "Ya", "map_location_picker_page_use_location": "Gunakan lokasi ini", @@ -1110,7 +1303,6 @@ "map_location_service_disabled_title": "Layanan Lokasi nonaktif", "map_marker_for_images": "Penanda peta untuk gambar yang diambil di {city}, {country}", "map_marker_with_image": "Penanda peta dengan gambar", - "map_no_assets_in_bounds": "Tidak ada foto di area ini", "map_no_location_permission_content": "Izin lokasi diperlukan untuk menampilkan aset yang terletak di lokasi kamu. Apakah kamu ingin mengizinkan sekarang?", "map_no_location_permission_title": "Izin Lokasi ditolak", "map_settings": "Pengaturan peta", @@ -1120,6 +1312,8 @@ "map_settings_date_range_option_year": "1 tahun terakhir", "map_settings_date_range_option_years": "{years} tahun terakhir", "map_settings_dialog_title": "Pengaturan Peta", + "map_settings_include_show_archived": "Sertakan yang diarsipkan", + "map_settings_include_show_partners": "Sertakan Pasangan", "map_settings_only_show_favorites": "Tampilkan Hanya Favorit", "map_settings_theme_settings": "Tema Peta", "map_zoom_to_see_photos": "Perkecil untuk lihat foto", @@ -1127,6 +1321,7 @@ "mark_as_read": "Tandai sebagai telah dibaca", "marked_all_as_read": "Semua telah ditandai sebagai telah dibaca", "matches": "Cocokan", + "matching_assets": "Aset yang Cocok", "media_type": "Jenis media", "memories": "Kenangan", "memories_all_caught_up": "Semua telah dilihat", @@ -1136,6 +1331,7 @@ "memories_swipe_to_close": "Geser ke atas untuk menutup", "memory": "Kenangan", "memory_lane_title": "Jalur Kenangan {title}", + "menu": "Menu", "merge": "Gabungkan", "merge_people": "Gabungkan orang", "merge_people_limit": "Anda hanya dapat menggabungkan sampai 5 wajah sekaligus", @@ -1144,20 +1340,32 @@ "merged_people_count": "{count, plural, one {# orang} other {# orang}} digabung", "minimize": "Kecilkan", "minute": "Menit", + "minutes": "Menit", "missing": "Hilang", + "model": "Model", "month": "Bulan", + "monthly_title_text_date_format": "BBBB t", "more": "Lainnya", "move": "Pindah", - "move_off_locked_folder": "Dikeluarkan dari Folder Terkunci", - "move_to_locked_folder": "Dipindahkan ke Folder Terkunci", - "move_to_locked_folder_confirmation": "Foto dan video ini akan dihapus dari seluruh album, dan hanya dapat dilihat melalui Folder Terkunci", + "move_off_locked_folder": "Pindahkan dari folder terkunci", + "move_to_lock_folder_action_prompt": "{count} ditambahkan ke folder terkunci", + "move_to_locked_folder": "Pindahkan dari folder terkunci", + "move_to_locked_folder_confirmation": "Foto dan video ini akan dihapus dari semua album, dan hanya dapat dilihat dari folder terkunci", "moved_to_archive": "Dipindahkan {count, plural, one {# asset} other {# assets}} ke arsip", "moved_to_library": "Dipindahkan {count, plural, one {# asset} other {# assets}} ke pustaka", "moved_to_trash": "Dipindahkan ke sampah", + "multiselect_grid_edit_date_time_err_read_only": "Tidak dapat mengedit tanggal aset hanya-baca, dilewati", + "multiselect_grid_edit_gps_err_read_only": "Tidak dapat mengedit lokasi aset hanya-baca, dilewati", "mute_memories": "Nonaktifkan Kenangan", "my_albums": "Album saya", "name": "Nama", "name_or_nickname": "Nama atau nama panggilan", + "network_requirement_photos_upload": "Gunakan data seluler untuk cadangkan foto", + "network_requirement_videos_upload": "Gunakan data seluler untuk cadangkan video", + "network_requirements": "Persyaratan Jaringan", + "network_requirements_updated": "Persyaratan jaringan telah berubah, antrean pencadangan diatur ulang", + "networking_settings": "Jaringan", + "networking_subtitle": "Kelola pengaturan Endpoint server", "never": "Tidak pernah", "new_album": "Album baru", "new_api_key": "Kunci API Baru", @@ -1165,6 +1373,7 @@ "new_person": "Orang baru", "new_pin_code": "Kode PIN baru", "new_pin_code_subtitle": "Ini adalah akses pertama Anda ke folder terkunci. Buat kode PIN untuk mengamankan akses ke halaman ini", + "new_timeline": "Linimasa Baru", "new_user_created": "Pengguna baru dibuat", "new_version_available": "VERSI BARU TERSEDIA", "newest_first": "Terkini dahulu", @@ -1177,20 +1386,28 @@ "no_archived_assets_message": "Arsipkan foto dan video untuk menyembunyikannya dari tampilan Foto", "no_assets_message": "KLIK UNTUK MENGUNGGAH FOTO PERTAMA ANDA", "no_assets_to_show": "Tidak ada aset", + "no_cast_devices_found": "Tidak ada perangkat cast yang ditemukan", + "no_checksum_local": "Tidak ada checksum yang tersedia - tidak dapat mengambil aset lokal", + "no_checksum_remote": "Tidak ada checksum yang tersedia - tidak dapat mengambil aset jarak jauh", "no_duplicates_found": "Tidak ada duplikat yang ditemukan.", "no_exif_info_available": "Tidak ada info EXIF yang tersedia", "no_explore_results_message": "Unggah lebih banyak foto untuk menjelajahi koleksi Anda.", "no_favorites_message": "Tambahkan favorit untuk mencari foto dan video terbaik Anda dengan cepat", "no_libraries_message": "Buat pustaka eksternal untuk menampilkan foto dan video Anda", - "no_locked_photos_message": "Foto dan video di Folder Terkunci disembunyikan dan tidak akan muncul saat Anda menelusuri pustaka.", + "no_local_assets_found": "Tidak ada aset lokal yang ditemukan dengan checksum ini", + "no_locked_photos_message": "Foto dan video di folder terkunci disembunyikan dan tidak akan muncul saat Anda menelusuri atau mencari di pustaka.", "no_name": "Tidak Ada Nama", "no_notifications": "Tidak ada notifikasi", "no_people_found": "Orang tidak ditemukan", "no_places": "Tidak ada tempat", + "no_remote_assets_found": "Tidak ada aset jarak jauh yang ditemukan dengan checksum ini", "no_results": "Tidak ada hasil", "no_results_description": "Coba sinonim atau kata kunci yang lebih umum", "no_shared_albums_message": "Buat sebuah album untuk membagikan foto dan video dengan orang-orang dalam jaringan Anda", + "no_uploads_in_progress": "Tidak ada unggahan yang sedang berlangsung", + "not_available": "T/T", "not_in_any_album": "Tidak ada dalam album apa pun", + "not_selected": "Belum dipilih", "note_apply_storage_label_to_previously_uploaded assets": "Catatan: Untuk menerapkan Label Penyimpanan pada aset yang sebelumnya telah diunggah, jalankan", "notes": "Catatan", "nothing_here_yet": "Masih kosong", @@ -1201,13 +1418,19 @@ "notification_toggle_setting_description": "Aktifkan notifikasi surel", "notifications": "Notifikasi", "notifications_setting_description": "Kelola notifikasi", + "oauth": "OAuth", "official_immich_resources": "Sumber Daya Immich Resmi", "offline": "Luring", + "offset": "Ofset", "ok": "Oke", "oldest_first": "Terlawas dahulu", + "on_this_device": "Di perangkat ini", "onboarding": "Memulai", - "onboarding_privacy_description": "Fitur berikut (opsional) bergantung pada layanan eksternal, dan dapat dinonaktifkan kapan saja di pengaturan administrasi.", + "onboarding_locale_description": "Pilih bahasa pilihan Anda. Anda dapat mengubahnya nanti di pengaturan.", + "onboarding_privacy_description": "Fitur-fitur berikut ini (bersifat opsional) bergantung pada layanan eksternal dan dapat Anda nonaktifkan kapan saja melalui menu pengaturan.", + "onboarding_server_welcome_description": "Mari kita konfigurasikan instans Anda dengan sejumlah pengaturan umum.", "onboarding_theme_description": "Pilih tema warna untuk server Anda. Ini dapat diubah lagi dalam pengaturan Anda.", + "onboarding_user_welcome_description": "Ayo kita mulai!", "onboarding_welcome_user": "Selamat datang, {user}", "online": "Daring", "only_favorites": "Hanya favorit", @@ -1217,10 +1440,13 @@ "open_the_search_filters": "Buka saringan pencarian", "options": "Opsi", "or": "atau", + "organize_into_albums": "Atur ke dalam album", + "organize_into_albums_description": "Masukkan foto lama ke album sesuai pengaturan sinkronisasi", "organize_your_library": "Kelola pustaka Anda", "original": "asli", "other": "Lainnya", "other_devices": "Perangkat lain", + "other_entities": "Entitas lain", "other_variables": "Variabel lain", "owned": "Dimiliki", "owner": "Pemilik", @@ -1264,14 +1490,20 @@ "permanently_delete_assets_prompt": "Apakah Anda yakin untuk menghapus {count, plural, one {aset ini secara permanen?} other {sebanyak # aset-aset berikut secara permanen?}} Ini juga akan menghapus {count, plural, one {ini dari} other {semua dari}} album-albumnya.", "permanently_deleted_asset": "Aset dihapus secara permanen", "permanently_deleted_assets_count": "{count, plural, one {# aset} other {# aset}} dihapus secara permanen", + "permission": "Perizinan", + "permission_empty": "Bagian izin tidak boleh dibiarkan kosong", "permission_onboarding_back": "Kembali", "permission_onboarding_continue_anyway": "Lanjutkan saja", + "permission_onboarding_get_started": "Memulai", "permission_onboarding_go_to_settings": "Buka setelan", "permission_onboarding_permission_denied": "Izin ditolak. Untuk menggunakan Immich, berikan izin akses foto dan video di Setelan.", "permission_onboarding_permission_granted": "Izin diberikan! Semua sudah siap.", "permission_onboarding_permission_limited": "Izin dibatasi. Agai Immich dapat mencadangkan dan mengatur seluruh koleksi galeri, izinkan akses foto dan video pada Setelan.", "permission_onboarding_request": "Immich memerlukan izin untuk melihat foto dan video kamu.", "person": "Orang", + "person_age_months": "{months, plural, one {# bulan} other {# bulan}} old", + "person_age_year_months": "1 year, {months, plural, one {# bulan} other {# bulan}} old", + "person_age_years": "{years, plural, other {# tahun}} old", "person_birthdate": "Lahir pada {date}", "person_hidden": "{name}{hidden, select, true { (tersembunyi)} other {}}", "photo_shared_all_users": "Sepertinya Anda membagikan foto Anda dengan semua pengguna atau Anda tidak memiliki pengguna siapa pun untuk dibagikan.", @@ -1293,12 +1525,17 @@ "play_or_pause_video": "Putar atau jeda video", "please_auth_to_access": "Silakan autentikasi untuk mengakses", "port": "Porta", + "preferences_settings_subtitle": "Kelola preferensi aplikasi", "preferences_settings_title": "Preferensi", + "preparing": "Mempersiapkan", "preset": "Prasetel", "preview": "Pratinjau", "previous": "Sebelumnya", "previous_memory": "Kenangan sebelumnya", - "previous_or_next_photo": "Foto sebelumnya atau berikutnya", + "previous_or_next_day": "Hari berikutnya/sebelumnya", + "previous_or_next_month": "Bulan berikutnya/sebelumnya", + "previous_or_next_photo": "Foto berikutnya/sebelumnya", + "previous_or_next_year": "Tahun berikutnya/sebelumnya", "primary": "Utama", "privacy": "Privasi", "profile": "Profil", @@ -1306,6 +1543,8 @@ "profile_drawer_client_out_of_date_major": "Versi app seluler ini sudah kedaluwarsa. Silakan perbarui ke versi major terbaru.", "profile_drawer_client_out_of_date_minor": "Versi app seluler ini sudah kedaluwarsa. Silakan perbarui ke versi minor terbaru.", "profile_drawer_client_server_up_to_date": "Klien dan server menjalankan versi terbaru", + "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Mode baca-saja aktif. Tekan lama ikon avatar pengguna untuk keluar.", "profile_drawer_server_out_of_date_major": "Versi server ini telah kedaluwarsa. Silakan perbarui ke versi major terbaru.", "profile_drawer_server_out_of_date_minor": "Versi server ini telah kedaluwarsa. Silakan perbarui ke versi minor terbaru.", "profile_image_of_user": "Foto profil dari {user}", @@ -1332,8 +1571,9 @@ "purchase_lifetime_description": "Pembayaran seumur hidup", "purchase_option_title": "PILIHAN PEMBAYARAN", "purchase_panel_info_1": "Membangun Immich membutuhkan banyak waktu dan upaya, dan kami memiliki insinyur penuh waktu yang bekerja untuk membuatnya sebaik mungkin. Misi kami adalah agar perangkat lunak sumber terbuka dan praktik bisnis yang beretika menjadi sumber pendapatan yang berkelanjutan bagi para pengembang dan menciptakan ekosistem yang menghargai privasi dengan alternatif nyata untuk layanan cloud yang eksploitatif.", - "purchase_panel_info_2": "Karena kami berkomitmen untuk tidak menambahkan paywall, pembelian ini tidak akan memberi kamu fitur tambahan apa pun di Immich. Kami mengandalkan pengguna seperti kamu untuk mendukung pengembangan Immich yang sedang berlangsung.", + "purchase_panel_info_2": "Karena kami berkomitmen untuk tidak menambahkan batasan akses berbayar (paywall), pembelian ini tidak akan memberikan fitur tambahan apa pun di Immich. Kami mengandalkan dukungan dari pengguna seperti Anda untuk memastikan pengembangan Immich dapat terus berjalan.", "purchase_panel_title": "Dukung proyek ini", + "purchase_per_server": "Per server", "purchase_per_user": "Per pengguna", "purchase_remove_product_key": "Hapus Kunci Produk", "purchase_remove_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk?", @@ -1341,13 +1581,19 @@ "purchase_remove_server_product_key_prompt": "Apakah kamu yakin ingin menghapus kunci produk Server?", "purchase_server_description_1": "Untuk keseluruhan server", "purchase_server_description_2": "Status pendukung", + "purchase_server_title": "Server", "purchase_settings_server_activated": "Kunci produk server dikelola oleh admin", + "query_asset_id": "ID Aset Kueri", + "queue_status": "Antrian {count}/{total}", "rating": "Peringkat bintang", "rating_clear": "Hapus peringkat", "rating_count": "{count, plural, one {# peringkat} other {# peringkat}}", "rating_description": "Tampilkan peringkat EXIF pada panel info", "reaction_options": "Opsi reaksi", "read_changelog": "Baca Log Perubahan", + "readonly_mode_disabled": "Mode baca-saja dimatikan", + "readonly_mode_enabled": "Mode baca-saja diaktifkan", + "ready_for_upload": "Siap untuk mengunggah", "reassign": "Tetapkan ulang", "reassigned_assets_to_existing_person": "Menetapkan ulang {count, plural, one {# aset} other {# aset}} kepada {name, select, null {orang yang sudah ada} other {{name}}}", "reassigned_assets_to_new_person": "Menetapkan ulang {count, plural, one {# aset} other {# aset}} kepada orang baru", @@ -1355,6 +1601,7 @@ "recent": "Terkini", "recent-albums": "Album terkini", "recent_searches": "Pencarian terkini", + "recently_added": "Barusaja ditambahkan", "recently_added_page_title": "Baru Ditambahkan", "recently_taken": "Diambil terkini", "recently_taken_page_title": "Diambil Terkini", @@ -1369,6 +1616,9 @@ "refreshing_faces": "Menyegarkan wajah", "refreshing_metadata": "Menyegarkan metadata", "regenerating_thumbnails": "Membuat ulang gambar kecil", + "remote": "Jarak Jauh", + "remote_assets": "Aset Jarak Jauh", + "remote_media_summary": "Ringkasan Media Jarak Jauh", "remove": "Hapus", "remove_assets_album_confirmation": "Apakah Anda yakin ingin menghapus {count, plural, one {# aset} other {# aset}} dari album?", "remove_assets_shared_link_confirmation": "Apakah Anda yakin ingin menghapus {count, plural, one {# aset} other {# aset}} dari tautan terbagi ini?", @@ -1376,12 +1626,15 @@ "remove_custom_date_range": "Hapus jangka tanggal khusus", "remove_deleted_assets": "Hapus Berkas Luring", "remove_from_album": "Hapus dari album", + "remove_from_album_action_prompt": "{count} telah dihapus dari album", "remove_from_favorites": "Hapus dari favorit", - "remove_from_locked_folder": "Hapus dari Folder Terkunci", - "remove_from_locked_folder_confirmation": "Apakah Anda yakin ingin mengeluarkan foto dan video dari Folder Terkunci? Semua itu akan terlihat di pustaka Anda", + "remove_from_lock_folder_action_prompt": "{count} telah dihapus dari folder terkunci", + "remove_from_locked_folder": "Hapus dari folder terkunci", + "remove_from_locked_folder_confirmation": "Apakah Anda yakin ingin memindahkan foto dan video ini keluar dari folder terkunci? Setelah dipindahkan, item tersebut akan terlihat di pustaka Anda.", "remove_from_shared_link": "Hapus dari tautan terbagi", "remove_memory": "Hapus kenangan", "remove_photo_from_memory": "Hapus foto dari kenangan ini", + "remove_tag": "Hapus Tanda", "remove_url": "Hapus URL", "remove_user": "Keluarkan pengguna", "removed_api_key": "Kunci API Dihapus: {name}", @@ -1403,20 +1656,31 @@ "reset_password": "Atur ulang kata sandi", "reset_people_visibility": "Atur ulang keterlihatan orang", "reset_pin_code": "Reset kode PIN", + "reset_pin_code_description": "Jika Anda lupa kode PIN, Anda bisa menghubungi admin untuk atur ulang", + "reset_pin_code_success": "Berhasil atur ulang kode PIN", + "reset_pin_code_with_password": "Anda selalu dapat mengatur ulang kode PIN dengan kata sandi Anda", + "reset_sqlite": "Atur ulang basis data SQLite", + "reset_sqlite_confirmation": "Apakah Anda yakin ingin mengatur ulang basis data SQLite? Setelah tindakan ini, Anda harus keluar lalu masuk kembali untuk melakukan sinkronisasi ulang data", + "reset_sqlite_success": "Berhasil mengatur ulang basis data SQLite", "reset_to_default": "Atur ulang ke bawaan", "resolve_duplicates": "Mengatasi duplikat", "resolved_all_duplicates": "Semua duplikat terselesaikan", "restore": "Pulihkan", "restore_all": "Pulihkan semua", + "restore_trash_action_prompt": "Sebanyak {count} item telah dipulihkan dari tempat sampah", "restore_user": "Pulihkan pengguna", "restored_asset": "Aset dipulihkan", "resume": "Lanjutkan", + "resume_paused_jobs": "Lanjutkan {count, plural, one {# pekerjaan yang dijeda} other {# pekerjaan yang dijeda}}", "retry_upload": "Ulangi pengunggahan", "review_duplicates": "Pratinjau duplikat", + "review_large_files": "Meninjau berkas berukuran besar", "role": "Peran", "role_editor": "Penyunting", "role_viewer": "Penampil", + "running": "Berjalan", "save": "Simpan", + "save_to_gallery": "Simpan ke galeri", "saved_api_key": "Kunci API Tersimpan", "saved_profile": "Profil disimpan", "saved_settings": "Pengaturan disimpan", @@ -1438,18 +1702,33 @@ "search_city": "Cari kota...", "search_country": "Cari negara...", "search_filter_apply": "Terapkan filter", + "search_filter_camera_title": "Pilih tipe kamera", + "search_filter_date": "Tanggal", + "search_filter_date_interval": "{start} sampai dengan {end}", + "search_filter_date_title": "Pilih rentang tanggal", "search_filter_display_option_not_in_album": "Tidak dalam album", + "search_filter_display_options": "Opsi tampilan", + "search_filter_filename": "Cari berdasarkan nama file", + "search_filter_location": "Lokasi", + "search_filter_location_title": "Pilih Lokasi", + "search_filter_media_type": "Tipe Media", + "search_filter_media_type_title": "Pilih jenis media", + "search_filter_people_title": "Pilih orang", "search_for": "Cari", "search_for_existing_person": "Cari orang yang sudah ada", + "search_no_more_result": "Tidak ada hasil lagi", "search_no_people": "Tidak ada orang", "search_no_people_named": "Tidak ada orang bernama \"{name}\"", + "search_no_result": "Tidak ada hasil yang ditemukan, coba kata kunci atau kombinasi lain", "search_options": "Pilihan pencarian", "search_page_categories": "Kategori", "search_page_motion_photos": "Foto Bergerak", "search_page_no_objects": "Tidak Ada Info Objek", "search_page_no_places": "Tidak Ada Info Lokasi", "search_page_screenshots": "Tangkapan Layar", + "search_page_search_photos_videos": "Cari foto dan video Anda", "search_page_selfies": "Swafoto", + "search_page_things": "Objek", "search_page_view_all_button": "Lihat semua", "search_page_your_activity": "Aktivitasmu", "search_page_your_map": "Peta Anda", @@ -1472,6 +1751,7 @@ "select_album_cover": "Pilih kover album", "select_all": "Pilih semua", "select_all_duplicates": "Pilih semua duplikat", + "select_all_in": "Pilih semua di {group}", "select_avatar_color": "Pilih warna avatar", "select_face": "Pilih wajah", "select_featured_photo": "Pilih foto terfitur", @@ -1485,12 +1765,15 @@ "select_user_for_sharing_page_err_album": "Gagal membuat album", "selected": "Dipilih", "selected_count": "{count, plural, other {# dipilih}}", + "selected_gps_coordinates": "Koordinat GPS yang dipilih", "send_message": "Kirim pesan", "send_welcome_email": "Kirim surel selamat datang", + "server_endpoint": "Endpoint server", "server_info_box_app_version": "Versi App", "server_info_box_server_url": "URL Server", "server_offline": "Server Luring", "server_online": "Server Daring", + "server_privacy": "Privasi server", "server_stats": "Statistik Server", "server_version": "Versi Server", "set": "Atur", @@ -1500,12 +1783,15 @@ "set_date_of_birth": "Atur tanggal lahir", "set_profile_picture": "Tetapkan foto profil", "set_slideshow_to_fullscreen": "Atur Salindia ke layar penuh", + "set_stack_primary_asset": "Atur sebagai aset utama", + "setting_image_viewer_help": "Penampil detail akan terlebih dahulu memuat gambar mini berukuran kecil, kemudian memuat pratinjau berukuran sedang (apabila diaktifkan), dan akhirnya memuat berkas asli (apabila diaktifkan).", "setting_image_viewer_original_subtitle": "Aktifkan untuk memuat gambar asli dengan resolusi penuh (berukuran besar!). Nonaktifkan untuk mengurangi penggunaan data (baik jaringan maupun cache perangkat).", "setting_image_viewer_original_title": "Muat gambar kualitas asli", "setting_image_viewer_preview_subtitle": "Aktifkan untuk memuat gambar dengan resolusi sedang. Nonaktifkan jika ingin langsung memuat gambar asli atau hanya ingin memuat thumbnail.", "setting_image_viewer_preview_title": "Muat gambar preview", "setting_image_viewer_title": "Foto", "setting_languages_apply": "Terapkan", + "setting_languages_subtitle": "Mengubah bahasa pada aplikasi", "setting_notifications_notify_failures_grace_period": "Beritahu kegagalan cadangan latar belakang: {duration}", "setting_notifications_notify_hours": "{count} jam", "setting_notifications_notify_immediately": "segera", @@ -1515,17 +1801,24 @@ "setting_notifications_single_progress_subtitle": "Rincian info proses unggah setiap asset", "setting_notifications_single_progress_title": "Tampilkan rincian proses cadangkan latar belakang", "setting_notifications_subtitle": "Atur setelan notifikasi", + "setting_notifications_total_progress_subtitle": "Progres keseluruhan unggahan (selesai/total aset)", + "setting_notifications_total_progress_title": "Tampilkan progres total pencadangan latar belakang", "setting_video_viewer_looping_title": "Ulangi", + "setting_video_viewer_original_video_subtitle": "Ketika melakukan streaming video dari server, sistem akan memutar versi asli meskipun tersedia hasil transkode. Pengaturan ini dapat menyebabkan terjadinya buffering. Video yang tersedia secara lokal akan selalu diputar dalam kualitas asli tanpa terpengaruh oleh pengaturan ini.", + "setting_video_viewer_original_video_title": "Paksa video asli", "settings": "Pengaturan", "settings_require_restart": "Harap mulai ulang Immich untuk menerapkan pengaturan ini", "settings_saved": "Pengaturan disimpan", "setup_pin_code": "Pasang kode PIN", "share": "Bagikan", + "share_action_prompt": "Sebanyak {count} aset telah dibagikan", "share_add_photos": "Tambah foto", "share_assets_selected": "{count} dipilih", "share_dialog_preparing": "Menyiapkan...", "share_link": "Bagikan Link", "shared": "Dibagikan", + "shared_album_activities_input_disable": "Komentar dinonaktifkan", + "shared_album_activity_remove_content": "Apakah Anda ingin menghapus aktivitas ini?", "shared_album_activity_remove_title": "Hapus Aktivitas", "shared_album_section_people_action_error": "Gagal menghapus dari album", "shared_album_section_people_action_leave": "Hapus pengguna dari album", @@ -1540,6 +1833,7 @@ "shared_link_clipboard_copied_massage": "Tersalin ke papan klip", "shared_link_clipboard_text": "Tautan: {link}\nKata Sandi: {password}", "shared_link_create_error": "Terjadi kesalahan saat membuat link berbagi", + "shared_link_custom_url_description": "Akses tautan bersama ini dengan URL khusus", "shared_link_edit_description_hint": "Masukkan deskripsi link", "shared_link_edit_expire_after_option_day": "1 hari", "shared_link_edit_expire_after_option_days": "{count} hari", @@ -1561,11 +1855,15 @@ "shared_link_expires_never": "Tidak akan kedaluwarsa", "shared_link_expires_second": "Kedaluwarsa dalam {count} detik", "shared_link_expires_seconds": "Kedaluwarsa dalam {count} detik", + "shared_link_individual_shared": "Dibagikan secara individual", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Atur link berbagi", "shared_link_options": "Pilihan tautan bersama", + "shared_link_password_description": "Wajibkan kata sandi untuk mengakses tautan bersama ini", "shared_links": "Tautan terbagi", "shared_links_description": "Bagikan foto dan video dengan tautan", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video terbagi.}}", + "shared_with_me": "Dibagikan kepada saya", "shared_with_partner": "Dibagikan dengan {partner}", "sharing": "Pembagian", "sharing_enter_password": "Masukkan kata sandi untuk membuka tautan halaman ini.", @@ -1596,6 +1894,7 @@ "show_slideshow_transition": "Tampilkan transisi salindia", "show_supporter_badge": "Lencana suporter", "show_supporter_badge_description": "Tampilkan lencana suporter", + "show_text_search_menu": "Tampilkan menu pencarian teks", "shuffle": "Acak", "sidebar": "Bilah sisi", "sidebar_display_description": "Menampilkan tautan ke tampilan di bilah sisi", @@ -1611,12 +1910,14 @@ "sort_created": "Tanggal dibuat", "sort_items": "Jumlah item", "sort_modified": "Tanggal diubah", + "sort_newest": "Foto terbaru", "sort_oldest": "Foto terlawas", "sort_people_by_similarity": "Urutkan orang berdasarkan kemiripan", "sort_recent": "Foto paling terkini", "sort_title": "Judul", "source": "Sumber", "stack": "Tumpukan", + "stack_action_prompt": "{count} tumpukan", "stack_duplicates": "Stack duplikat", "stack_select_one_photo": "Pilih satu foto utama untuk stack", "stack_selected_photos": "Tumpuk foto terpilih", @@ -1624,7 +1925,10 @@ "stacktrace": "Jejak tumpukan", "start": "Mulai", "start_date": "Tanggal mulai", + "start_date_before_end_date": "Tanggal mulai harus sebelum tanggal akhir", "state": "Keadaan", + "status": "Status", + "stop_casting": "Hentikan cast", "stop_motion_photo": "Hentikan Foto Gerak", "stop_photo_sharing": "Berhenti membagikan foto Anda?", "stop_photo_sharing_description": "{partner} tidak akan dapat mengakses foto Anda lagi.", @@ -1634,6 +1938,7 @@ "storage_quota": "Kuota Penyimpanan", "storage_usage": "{used} dari {available} digunakan", "submit": "Kirim", + "success": "Sukses", "suggestions": "Saran", "sunrise_on_the_beach": "Matahari terbit di pantai", "support": "Dukungan", @@ -1641,6 +1946,14 @@ "support_third_party_description": "Pemasangan Immich Anda telah dipaketkan oleh pihak ketiga. Masalah yang Anda alami dapat disebabkan oleh paket tersebut, jadi silakan ajukan isu dengan masalah tersebut menggunakan tautan di bawah.", "swap_merge_direction": "Ganti arah penggabungan", "sync": "Sinkronisasikan", + "sync_albums": "Sinkronkan album", + "sync_albums_manual_subtitle": "Melakukan sinkronisasi semua video dan foto yang telah diunggah ke album cadangan yang dipilih", + "sync_local": "Sinkronkan lokal", + "sync_remote": "Sinkronkan jarak jauh", + "sync_status": "Status Sinkronisasi", + "sync_status_subtitle": "Lihat dan atur sistem sinkronisasi", + "sync_upload_album_setting_subtitle": "Membuat dan mengunggah foto serta video Anda ke album yang telah dipilih pada Immich", + "tag": "Label", "tag_assets": "Tag aset", "tag_created": "Tag yang di buat: {tag}", "tag_feature_description": "Menjelajahi foto dan video yang dikelompokkan berdasarkan topik tag logis", @@ -1649,16 +1962,24 @@ "tag_updated": "Tag yang diperbarui: {tag}", "tagged_assets": "Ditandai {count, plural, one {# aset} other {# aset}}", "tags": "Tag", + "tap_to_run_job": "Ketuk untuk menjalankan pekerjaan", "template": "Templat", "theme": "Tema", "theme_selection": "Pemilihan tema", "theme_selection_description": "Tetapkan tema ke terang atau gelap secara otomatis berdasarkan preferensi sistem peramban Anda", "theme_setting_asset_list_storage_indicator_title": "Tampilkan sisa penyimpanan", "theme_setting_asset_list_tiles_per_row_title": "Jumlah aset per baris ({count})", + "theme_setting_colorful_interface_subtitle": "Menerapkan warna utama ke permukaan latar belakang.", + "theme_setting_colorful_interface_title": "Antarmuka berwarna-warni", "theme_setting_image_viewer_quality_subtitle": "Atur kualitas dari penampil gambar", "theme_setting_image_viewer_quality_title": "Kualitas penampil gambar", + "theme_setting_primary_color_subtitle": "Pilihlah warna yang akan digunakan untuk tindakan utama dan elemen aksen.", + "theme_setting_primary_color_title": "Warna utama", + "theme_setting_system_primary_color_title": "Gunakan warna sistem", "theme_setting_system_theme_switch": "Otomatis (Ikuti pengaturan sistem)", "theme_setting_theme_subtitle": "Pilih setelan tema aplikasi", + "theme_setting_three_stage_loading_subtitle": "Pemuatan tiga tahap dapat meningkatkan performa pemuatan, namun akan menyebabkan beban jaringan meningkat secara signifikan", + "theme_setting_three_stage_loading_title": "Aktifkan pemuatan tiga tahap", "they_will_be_merged_together": "Mereka akan digabungkan bersama", "third_party_resources": "Sumber Daya Pihak Ketiga", "time_based_memories": "Kenangan berbasis waktu", @@ -1668,15 +1989,19 @@ "to_change_password": "Ubah kata sandi", "to_favorite": "Favorit", "to_login": "Log masuk", + "to_multi_select": "untuk memilih beberapa", "to_parent": "Ke induk", + "to_select": "untuk memilih", "to_trash": "Sampah", "toggle_settings": "Saklar pengaturan", "total": "Jumlah", "total_usage": "Jumlah penggunaan", "trash": "Sampah", + "trash_action_prompt": "Sebanyak {count} item telah dipindahkan ke tempat sampah", "trash_all": "Buang Semua", "trash_count": "Sampah {count, number}", "trash_delete_asset": "Hapus Aset", + "trash_emptied": "Tempat sampah telah dikosongkan", "trash_no_results_message": "Foto dan video di sampah akan muncul di sini.", "trash_page_delete_all": "Hapus Semua", "trash_page_empty_trash_dialog_content": "Apakah kamu ingin menghapus semua aset di sampah? Item tersebut akan dihapus secara permanen dari Immich", @@ -1686,12 +2011,16 @@ "trash_page_select_assets_btn": "Pilih aset", "trash_page_title": "Sampah ({count})", "trashed_items_will_be_permanently_deleted_after": "Item yang dibuang akan dihapus secara permanen setelah {days, plural, one {# hari} other {# hari}}.", + "troubleshoot": "Pemecahan Masalah", "type": "Jenis", "unable_to_change_pin_code": "Tidak dapat mengubah kode PIN", "unable_to_setup_pin_code": "Tidak dapat memasang kode PIN", "unarchive": "Keluarkan dari arsip", + "unarchive_action_prompt": "Sebanyak {count} item telah dihapus dari Arsip", "unarchived_count": "{count, plural, other {# dipindahkan dari arsip}}", + "undo": "Urungkan", "unfavorite": "Hapus favorit", + "unfavorite_action_prompt": "Sebanyak {count} item telah dihapus dari Favorit", "unhide_person": "Munculkan orang", "unknown": "Tidak diketahui", "unknown_country": "Negara Tidak Diketahui", @@ -1707,16 +2036,23 @@ "unsaved_change": "Perubahan belum disimpan", "unselect_all": "Batalkan semua pilihan", "unselect_all_duplicates": "Batal pilih semua duplikat", + "unselect_all_in": "Membatalkan semua pilihan dalam {group}", "unstack": "Batalkan penumpukan", + "unstack_action_prompt": "{count} Tidak dalam tumpukan", "unstacked_assets_count": "Penumpukan {count, plural, one {# aset} other {# aset}} dibatalkan", + "untagged": "Tidak ditandai", "up_next": "Berikutnya", + "update_location_action_prompt": "Perbarui lokasi {count} aset yang dipilih dengan:", "updated_at": "Diperbarui", "updated_password": "Kata sandi diperbarui", "upload": "Unggah", + "upload_action_prompt": "{count} antrian untuk diunggah", "upload_concurrency": "Konkurensi pengunggahan", + "upload_details": "Detil unggahan", "upload_dialog_info": "Apakah akan mencadangkan aset terpilih ke server?", "upload_dialog_title": "Unggah Aset", "upload_errors": "Unggahan selesai dengan {count, plural, one {# eror} other {# eror}}, muat ulang laman untuk melihat aset terunggah baru.", + "upload_finished": "Unggahan berhasil", "upload_progress": "Tersisa {remaining, number} - Di proses {processed, number}/{total, number}", "upload_skipped_duplicates": "Melewati {count, plural, one {# aset duplikat} other {# aset duplikat}}", "upload_status_duplicates": "Duplikat", @@ -1724,8 +2060,12 @@ "upload_status_uploaded": "Diunggah", "upload_success": "Pengunggahan berhasil, muat ulang laman untuk melihat aset terunggah yang baru.", "upload_to_immich": "Unggah ke Immich ({count})", + "uploading": "Mengunggah", + "uploading_media": "Mengunggah media", + "url": "URL", "usage": "Penggunaan", "use_biometric": "Gunakan biometrik", + "use_current_connection": "Gunakan koneksi saat ini", "use_custom_date_range": "Gunakan jangka tanggal khusus saja", "user": "Pengguna", "user_has_been_deleted": "Pengguna ini telah dihapus.", @@ -1733,6 +2073,7 @@ "user_liked": "{user} menyukai {type, select, photo {foto ini} video {tayangan ini} asset {aset ini} other {ini}}", "user_pin_code_settings": "Kode PIN", "user_pin_code_settings_description": "Atur kode PIN Anda", + "user_privacy": "Kerahasiaan pengguna", "user_purchase_settings": "Pembelian", "user_purchase_settings_description": "Atur pembelian kamu", "user_role_set": "Tetapkan {user} sebagai {role}", @@ -1741,14 +2082,17 @@ "user_usage_stats_description": "Tampilkan statistik penggunaan akun", "username": "Nama pengguna", "users": "Pengguna", + "users_added_to_album_count": "{count, plural, one {Menambahkan # pengguna ke album} other {Menambahkan # pengguna ke album}}", "utilities": "Peralatan", "validate": "Validasi", + "validate_endpoint_error": "Masukkan URL yang valid", "variables": "Variabel", "version": "Versi", "version_announcement_closing": "Temanmu, Alex", "version_announcement_message": "Hai! Versi baru Immich telah tersedia. Harap luangkan waktu untuk membaca catatan rilis untuk memastikan pengaturan Anda terkini untuk mencegah kesalahan konfigurasi, terutama jika Anda menggunakan WatchTower atau mekanisme apa pun yang menangani pembaruan server Immich secara otomatis.", "version_history": "Riwayat Versi", "version_history_item": "Terpasang {version} pada {date}", + "video": "Video", "video_hover_setting": "Putar gambar kecil video saat kursor di atas", "video_hover_setting_description": "Putar gambar kecil video ketika tetikus berada di atas item. Bahkan saat dinonaktifkan, pemutaran dapat dimulai dengan mengambang di atas ikon putar.", "videos": "Video", @@ -1757,6 +2101,7 @@ "view_album": "Tampilkan Album", "view_all": "Tampilkan Semua", "view_all_users": "Tampilkan semua pengguna", + "view_details": "Tampilkan detil", "view_in_timeline": "Lihat di timeline", "view_link": "Tampilkan tautan", "view_links": "Tampilkan tautan", @@ -1764,9 +2109,12 @@ "view_next_asset": "Tampilkan aset berikutnya", "view_previous_asset": "Tampilkan aset sebelumnya", "view_qr_code": "Tampilkan kode QR", + "view_similar_photos": "Lihat foto yang mirip", "view_stack": "Tampilkan Tumpukan", + "view_user": "Lihat Pengguna", "viewer_remove_from_stack": "Keluarkan dari Tumpukan", "viewer_stack_use_as_main_asset": "Gunakan sebagai aset utama", + "viewer_unstack": "Lepas tumpukan", "visibility_changed": "Keterlihatan diubah untuk {count, plural, one {# orang} other {# orang}}", "waiting": "Menunggu", "warning": "Peringatan", @@ -1780,5 +2128,6 @@ "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak memiliki tautan terbagi", "your_wifi_name": "Nama Wi-Fi Anda", - "zoom_image": "Perbesar Gambar" + "zoom_image": "Perbesar Gambar", + "zoom_to_bounds": "Perbesar ke batas" } diff --git a/i18n/it.json b/i18n/it.json index 07e19736a7..3f8d90eacc 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -14,19 +14,24 @@ "add_a_location": "Aggiungi una posizione", "add_a_name": "Aggiungi un nome", "add_a_title": "Aggiungi un titolo", - "add_endpoint": "Aggiungi endpoint", + "add_birthday": "Aggiungi un compleanno", + "add_endpoint": "Aggiungi un endpoint", "add_exclusion_pattern": "Aggiungi un pattern di esclusione", - "add_import_path": "Aggiungi un percorso di importazione", + "add_import_path": "Aggiungi un percorso per l’importazione", "add_location": "Aggiungi posizione", "add_more_users": "Aggiungi altri utenti", "add_partner": "Aggiungi partner", - "add_path": "Aggiungi percorso", + "add_path": "Aggiungi un percorso", "add_photos": "Aggiungi foto", "add_tag": "Aggiungi tag", "add_to": "Aggiungi aâ€Ļ", "add_to_album": "Aggiungi all'album", "add_to_album_bottom_sheet_added": "Aggiunto in {album}", "add_to_album_bottom_sheet_already_exists": "Già presente in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alcune risorse locali non possono essere aggiunte all'album", + "add_to_album_toggle": "Attiva/disattiva selezione per {album}", + "add_to_albums": "Aggiungi ad album", + "add_to_albums_count": "Aggiungi ad album ({count})", "add_to_shared_album": "Aggiungi ad album condiviso", "add_url": "Aggiungi URL", "added_to_archive": "Aggiunto all'archivio", @@ -44,6 +49,13 @@ "backup_database": "Crea Dump Database", "backup_database_enable_description": "Abilita i backup del database", "backup_keep_last_amount": "Numero di backup da mantenere", + "backup_onboarding_1_description": "copia offsite nel cloud o in un'altra sede fisica.", + "backup_onboarding_2_description": "copie locali su diversi dispositivi. CiÃ˛ include i file principali e un backup di tali file a livello locale.", + "backup_onboarding_3_description": "copie totali dei tuoi dati, compresi i file originali. CiÃ˛ include 1 copia offsite e 2 copie locali.", + "backup_onboarding_description": "Per proteggere i tuoi dati, è consigliato adottare una strategia di backup 3-2-1. Per una soluzione di backup completa, è consigliato conservare copie delle foto/video caricati e del database Immich.", + "backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consulta la documentazione.", + "backup_onboarding_parts_title": "Un backup 3-2-1 include:", + "backup_onboarding_title": "Backup", "backup_settings": "Impostazioni Dump database", "backup_settings_description": "Gestisci le impostazioni dei backup.", "cleared_jobs": "Cancellati i processi per: {job}", @@ -51,7 +63,7 @@ "confirm_delete_library": "Sei sicuro di voler cancellare la libreria {library}?", "confirm_delete_library_assets": "Sei sicuro di voler cancellare questa libreria? Questo cancellerà {count, plural, one {# asset} other {tutti e # gli assets}} da Immich senza possibilità di tornare indietro. I file non verranno cancellati.", "confirm_email_below": "Per confermare, scrivi \"{email}\" qui sotto", - "confirm_reprocess_all_faces": "Sei sicuro di voler riprocessare tutti i volti? Questo cancellerà tutte le persone nominate.", + "confirm_reprocess_all_faces": "Sei sicuro di voler riprocessare tutti i volti? Questo cancellerà anche tutte le persone associate.", "confirm_user_password_reset": "Sei sicuro di voler resettare la password di {user}?", "confirm_user_pin_code_reset": "Sicuro di voler resettare il codice PIN di {user}?", "create_job": "Crea Processo", @@ -68,27 +80,27 @@ "failed_job_command": "Il comando {command} è fallito per il processo: {job}", "force_delete_user_warning": "ATTENZIONE: Questo rimuoverà immediatamente l'utente e tutti i suoi assets. Non è possibile tornare indietro e i file non potranno essere recuperati.", "image_format": "Formato", - "image_format_description": "WebP produce file piÚ piccoli rispetto a JPEG, ma l'encoding è piÚ lento.", - "image_fullsize_description": "Le immagini con dimensioni reali senza metadati sono utilizzate durante lo zoom", - "image_fullsize_enabled": "Abilita la generazione delle immagini con dimensioni reali", - "image_fullsize_enabled_description": "Genera immagini con dimensioni reali per i formati non web-friendly. Quando \"Preferisci l'anteprima integrata\" è abilitata, le anteprime integrate saranno usate senza conversione. Non riguarda le immagini web-friendly come il JPEG.", - "image_fullsize_quality_description": "Qualità delle immagini con dimensioni reali da 1 a 100. PiÚ è alto il valore piÚ la qualità sarà alta come anche la grandezza dei file.", - "image_fullsize_title": "Impostazioni Immagini con dimensioni reali", + "image_format_description": "WebP produce file piÚ piccoli rispetto a JPEG, ma è piÚ lento da codificare.", + "image_fullsize_description": "Immagini a dimensioni reali senza metadati, sono utilizzate durante lo zoom", + "image_fullsize_enabled": "Abilita la generazione delle immagini a dimensioni reali", + "image_fullsize_enabled_description": "Genera immagini a dimensioni reali per i formati non web-friendly. Quando è abilitata l'opzione \"Preferisci l'anteprima integrata\", le anteprime integrate saranno utilizzate direttamente senza conversione. Non influisce sui formati web-friendly come JPEG.", + "image_fullsize_quality_description": "Qualità delle immagini a dimensioni reali da 1 a 100. Un valore piÚ alto è migliore ma produce file piÚ grandi.", + "image_fullsize_title": "Impostazioni delle immagini a dimensioni reali", "image_prefer_embedded_preview": "Preferisci l'anteprima integrata", "image_prefer_embedded_preview_setting_description": "Usa l'anteprima integrata nelle foto RAW come input per l'elaborazione delle immagini, se disponibile. Questo permette un miglioramento dei colori per alcune immagini, ma la qualità delle anteprime dipende dalla macchina fotografica. Inoltre le immagini potrebbero presentare artefatti di compressione.", "image_prefer_wide_gamut": "Preferisci gamut piÚ ampio", "image_prefer_wide_gamut_setting_description": "Usa lo spazio colore Display P3 per le anteprime. Questo aiuta a mantenere la vivacità delle immagini con spazi colore piÚ ampi, tuttavia potrebbe non mostrare correttamente le immagini con dispositivi e browser obsoleti. Le immagini sRGB vengono preservate per evitare alterazioni del colore.", - "image_preview_description": "Immagine di medie dimensioni con metadati eliminati, utilizzata durante la visualizzazione di una singola risorsa e per l'apprendimento automatico", - "image_preview_quality_description": "Qualità dell'anteprima da 1 a 100. Elevata è migliore ma produce file piÚ pesanti e puÃ˛ ridurre la reattività dell'app. Impostare un valore basso puÃ˛ influenzare negativamente la qualità del machine learning.", + "image_preview_description": "Immagine a media dimensione senza metadati, utilizzata durante la visualizzazione di una singola risorsa e per il machine learning", + "image_preview_quality_description": "Qualità dell'anteprima da 1 a 100. PiÚ alto è meglio ma produce file piÚ pesanti e puÃ˛ ridurre la reattività dell'app. Impostare un valore basso puÃ˛ influenzare negativamente la qualità del machine learning.", "image_preview_title": "Impostazioni dell'anteprima", "image_quality": "Qualità", "image_resolution": "Risoluzione", - "image_resolution_description": "Risoluzioni piÚ elevate possono preservare piÚ dettagli ma richiedere piÚ tempo per la codifica, avere dimensioni di file piÚ grandi e possono ridurre la reattività dell'app.", + "image_resolution_description": "Risoluzioni piÚ elevate possono preservare piÚ dettagli ma richiedere piÚ tempo per la codifica, avere dimensioni di file piÚ grandi e ridurre la reattività dell'app.", "image_settings": "Impostazioni delle immagini", "image_settings_description": "Gestisci qualità e risoluzione delle immagini generate", - "image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come la sequenza temporale principale", - "image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore piÚ alto è migliore, ma produce file piÚ grandi e puÃ˛ ridurre la reattività dell'app.", - "image_thumbnail_title": "Impostazioni della copertina", + "image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come nella galleria principale", + "image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore piÚ alto è migliore ma produce file piÚ grandi e puÃ˛ ridurre la reattività dell'app.", + "image_thumbnail_title": "Impostazioni delle miniature", "job_concurrency": "Concorrenza {job}", "job_created": "Processo creato", "job_not_concurrency_safe": "Questo processo non è eseguibile in maniera concorrente.", @@ -105,13 +117,20 @@ "library_scanning_enable_description": "Attiva la scansione periodica della libreria", "library_settings": "Libreria Esterna", "library_settings_description": "Gestisci le impostazioni della libreria esterna", - "library_tasks_description": "Scansiona le librerie esterne per i nuovi aggiornamenti", + "library_tasks_description": "Scansiona le librerie esterne per risorse nuove o modificate", "library_watching_enable_description": "Osserva le librerie esterne per cambiamenti", "library_watching_settings": "Osserva librerie (SPERIMENTALE)", "library_watching_settings_description": "Osserva automaticamente i cambiamenti dei file", "logging_enable_description": "Attiva il logging", "logging_level_description": "Quando attivato, che livello di log utilizzare.", "logging_settings": "Registro dei Log", + "machine_learning_availability_checks": "Verifiche di disponibilità", + "machine_learning_availability_checks_description": "Rileva automaticamente e usa i server di machine learning disponibili", + "machine_learning_availability_checks_enabled": "Attiva verifiche di disponibilità", + "machine_learning_availability_checks_interval": "Intervallo di verifica", + "machine_learning_availability_checks_interval_description": "Intervallo (ms) tra le verifiche di disponibilità", + "machine_learning_availability_checks_timeout": "Timeout richiesta", + "machine_learning_availability_checks_timeout_description": "Timeout (ms) per le verifiche di disponibilità", "machine_learning_clip_model": "Modello CLIP", "machine_learning_clip_model_description": "Il nome del modello CLIP mostrato qui. Nota che devi rieseguire il processo 'Ricerca Intelligente' per tutte le immagini al cambio del modello.", "machine_learning_duplicate_detection": "Rilevamento Duplicati", @@ -119,9 +138,9 @@ "machine_learning_duplicate_detection_enabled_description": "Se disattivo, risorse perfettamente identiche saranno comunque deduplicate.", "machine_learning_duplicate_detection_setting_description": "Utilizza i CLIP embeddings per trovare possibili duplicati", "machine_learning_enabled": "Attiva machine learning", - "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.", + "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le impostazioni sottostanti.", "machine_learning_facial_recognition": "Riconoscimento Facciale", - "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa facce nelle immagini", + "machine_learning_facial_recognition_description": "Rileva, riconosci e raggruppa volti nelle immagini", "machine_learning_facial_recognition_model": "Modello di riconoscimento facciale", "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli piÚ grandi sono piÚ lenti e utilizzano piÚ memoria, perÃ˛ producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", "machine_learning_facial_recognition_setting": "Attiva riconoscimento facciale", @@ -132,7 +151,7 @@ "machine_learning_max_recognition_distance_description": "La distanza massima tra due volti per essere considerati la stessa persona, che varia da 0 a 2. Abbassare questo valore puÃ˛ prevenire l'etichettatura di due persone come se fossero la stessa persona, mentre aumentarlo puÃ˛ prevenire l'etichettatura della stessa persona come se fossero due persone diverse. Nota che è piÚ facile unire due persone che separare una persona in due, quindi è preferibile mantenere una soglia piÚ bassa quando possibile.", "machine_learning_min_detection_score": "Punteggio minimo di rilevazione", "machine_learning_min_detection_score_description": "Punteggio di confidenza minimo per rilevare un volto, da 0 a 1. Valori piÚ bassi rileveranno piÚ volti, ma potrebbero generare risultati fasulli.", - "machine_learning_min_recognized_faces": "Minimo volti rilevati", + "machine_learning_min_recognized_faces": "Minimo numero di volti rilevati", "machine_learning_min_recognized_faces_description": "Il numero minimo di volti riconosciuti per creare una persona. Aumentando questo valore si rende il riconoscimento facciale piÚ preciso, ma aumenta la possibilità che un volto non venga assegnato a una persona.", "machine_learning_settings": "Impostazioni Machine Learning", "machine_learning_settings_description": "Gestisci le impostazioni e le funzionalità del machine learning", @@ -153,19 +172,33 @@ "map_reverse_geocoding": "Geocodifica inversa", "map_reverse_geocoding_enable_description": "Abilita geocodifica inversa", "map_reverse_geocoding_settings": "Impostazioni Geocodifica Inversa", - "map_settings": "Impostazioni Mappa e Posizione", + "map_settings": "Mappa", "map_settings_description": "Gestisci impostazioni mappa", "map_style_description": "URL per un tema della mappa style.json", - "memory_cleanup_job": "pulizia memoria", - "memory_generate_job": "Generazione della memoria", - "metadata_extraction_job": "Estrazione Metadata", - "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascun asset, ad esempio coordinate GPS, volti e risoluzione", + "memory_cleanup_job": "Pulizia dei vecchi Ricordi", + "memory_generate_job": "Generazione dei Ricordi", + "metadata_extraction_job": "Estrazione Metadati", + "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascuna risorsa, come coordinate GPS, volti e risoluzione", "metadata_faces_import_setting": "Abilita l'importazione dei volti", "metadata_faces_import_setting_description": "Importa i volti dai dati EXIF dell'immagine e dai file sidecar", "metadata_settings": "Impostazioni Metadati", "metadata_settings_description": "Gestisci le impostazioni dei metadati", "migration_job": "Migrazione", "migration_job_description": "Migra le anteprime per gli asset e volti alla struttura di cartelle piÚ recente", + "nightly_tasks_cluster_faces_setting_description": "Avvia riconoscimento facciale sui volti appena rilevati", + "nightly_tasks_cluster_new_faces_setting": "Raggruppa nuovi volti", + "nightly_tasks_database_cleanup_setting": "Processi di pulizia del database", + "nightly_tasks_database_cleanup_setting_description": "Ripulisci il database da file vecchi e scaduti", + "nightly_tasks_generate_memories_setting": "Genera Ricordi", + "nightly_tasks_generate_memories_setting_description": "Genera nuovi Ricordi a partire dalle risorse", + "nightly_tasks_missing_thumbnails_setting": "Genera anteprime mancanti", + "nightly_tasks_missing_thumbnails_setting_description": "Metti in coda le risorse senza miniatura per la generazione delle anteprime", + "nightly_tasks_settings": "Impostazioni delle attività notturne", + "nightly_tasks_settings_description": "Gestisci attività notturne", + "nightly_tasks_start_time_setting": "Orario di avvio", + "nightly_tasks_start_time_setting_description": "L'orario in cui il server fa partire le attività notturne", + "nightly_tasks_sync_quota_usage_setting": "Sincronizza la quota di utilizzo", + "nightly_tasks_sync_quota_usage_setting_description": "Aggiorna la quota di spazio dell'utente in base all'utilizzo corrente", "no_paths_added": "Nessun percorso aggiunto", "no_pattern_added": "Nessun pattern aggiunto", "note_apply_storage_label_previous_assets": "Nota: Per assegnare l'etichetta storage ad asset precedentemente caricati, esegui", @@ -174,10 +207,10 @@ "notification_email_from_address_description": "Indirizzo email del mittente, ad esempio: \"Immich Photo Server \". Assicurati di utilizzare un indirizzo da cui sei autorizzato a inviare email.", "notification_email_host_description": "Host del server email (es. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignora errori di certificato", - "notification_email_ignore_certificate_errors_description": "Ignora errori di validazione del certificato TLS (sconsigliato)", + "notification_email_ignore_certificate_errors_description": "Ignora errori TLS di validazione del certificato (sconsigliato)", "notification_email_password_description": "Password da usare per l'autenticazione con il server email", "notification_email_port_description": "Porta del server email (es. 25, 465, 587)", - "notification_email_sent_test_email_button": "Invia email di test e salva", + "notification_email_sent_test_email_button": "Invia email di prova e salva", "notification_email_setting_description": "Impostazioni per le notifiche via email", "notification_email_test_email": "Invia email di prova", "notification_email_test_email_failed": "Impossibile inviare email di prova, controlla i valori inseriti", @@ -193,12 +226,14 @@ "oauth_button_text": "Testo pulsante", "oauth_client_secret_description": "Richiesto se PKCE (Proof Key for Code Exchange) non è supportato dal provider OAuth", "oauth_enable_description": "Login con OAuth", - "oauth_mobile_redirect_uri": "URI reindirizzamento mobile", - "oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare", + "oauth_mobile_redirect_uri": "URI di reindirizzamento per app mobile", + "oauth_mobile_redirect_uri_override": "Sovrascrivi URI di reindirizzamento per app mobile", "oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''", + "oauth_role_claim": "Claim del ruolo", + "oauth_role_claim_description": "Concedi automaticamente l'accesso come amministratore in base alla presenza di questo claim. Il claim puÃ˛ essere 'user' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestisci impostazioni di login OAuth", - "oauth_settings_more_details": "Per piÚ dettagli riguardo a questa funzionalità, consulta la documentazione.", + "oauth_settings_more_details": "Per maggiori informazioni su questa funzionalità, consulta la documentazione.", "oauth_storage_label_claim": "Dichiarazione di ambito(claim) etichetta archiviazione", "oauth_storage_label_claim_description": "Imposta automaticamente l'etichetta dell'archiviazione dell'utente al valore di questa dichiarazione di ambito(claim).", "oauth_storage_quota_claim": "Dichiarazione di ambito(claim) limite archiviazione", @@ -213,7 +248,7 @@ "paths_validated_successfully": "Percorsi validati con successo", "person_cleanup_job": "Pulizia Persona", "quota_size_gib": "Dimensione Archiviazione (GiB)", - "refreshing_all_libraries": "Aggiorna tutte le librerie", + "refreshing_all_libraries": "Aggiornando tutte le librerie", "registration": "Registrazione amministratore", "registration_description": "PoichÊ sei il primo utente del sistema, sarai assegnato come Amministratore e sarai responsabile dei task amministrativi, e utenti aggiuntivi saranno creati da te.", "require_password_change_on_login": "Richiedi all'utente di cambiare password al primo accesso", @@ -242,11 +277,11 @@ "storage_template_migration": "Migrazione modello archiviazione", "storage_template_migration_description": "Applica il {template} attuale agli asset caricati in precedenza", "storage_template_migration_info": "Le modifiche al modello di archiviazione verranno applicate solo agli asset nuovi. Per applicare le modifiche retroattivamente esegui {job}.", - "storage_template_migration_job": "Processo Migrazione Modello di Archiviazione", - "storage_template_more_details": "Per maggiori informazioni riguardo a questa funzionalità, consulta il Modello Archiviazione e le sue conseguenze", + "storage_template_migration_job": "Processo di migrazione del Modello di Archiviazione", + "storage_template_more_details": "Per maggiori informazioni riguardo a questa funzionalità, consulta il Modello di Archiviazione e le sue conseguenze", "storage_template_onboarding_description_v2": "Se attiva, questa funzionalità organizzerà automaticamente i file utilizzando un modello definito dall'utente. Per maggiori informazioni, consultare la documentazione.", "storage_template_path_length": "Limite approssimativo lunghezza percorso: {length, number}/{limit, number}", - "storage_template_settings": "Modello Archiviazione", + "storage_template_settings": "Modello di Archiviazione", "storage_template_settings_description": "Gestisci la struttura delle cartelle e il nome degli asset caricati", "storage_template_user_label": "{label} è l'etichetta di archiviazione dell'utente", "system_settings": "Impostazioni di sistema", @@ -256,7 +291,7 @@ "template_email_invite_album": "Modello di invito all'album", "template_email_preview": "Anteprima", "template_email_settings": "Template Email", - "template_email_update_album": "Modello di aggiornamento dell'album", + "template_email_update_album": "Aggiorna template dell'album", "template_email_welcome": "Modello di email di benvenuto", "template_settings": "Templates Notifiche", "template_settings_description": "Gestisci i modelli personalizzati per le notifiche", @@ -264,8 +299,8 @@ "theme_custom_css_settings_description": "I Cascading Style Sheets (CSS) permettono di personalizzare l'interfaccia di Immich.", "theme_settings": "Impostazioni Tema", "theme_settings_description": "Gestisci la personalizzazione dell'interfaccia web di Immich", - "thumbnail_generation_job": "Generazione Miniature", - "thumbnail_generation_job_description": "Genera miniature grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", + "thumbnail_generation_job": "Genera Anteprime", + "thumbnail_generation_job_description": "Genera anteprime grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", "transcoding_acceleration_api": "API di accelerazione", "transcoding_acceleration_api_description": "L'API che interagirà con il tuo dispositivo per accelerare la transcodifica. Questa impostazione è \"best effort\": ripiegherà sulla transcodifica software in caso di fallimento. VP9 potrebbe funzionare o meno a seconda del tuo hardware.", "transcoding_acceleration_nvenc": "NVENC (richiede GPU NVIDIA)", @@ -273,11 +308,11 @@ "transcoding_acceleration_rkmpp": "RKMPP (Solo per SOC Rockchip)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Codifiche audio accettate", - "transcoding_accepted_audio_codecs_description": "Seleziona quali codifiche audio non devono essere trascodificate. Solo usato per alcune politiche di trascodifica.", + "transcoding_accepted_audio_codecs_description": "Seleziona quali codifiche audio non devono essere transcodificate. Solo usato per alcune politiche di transcodifica.", "transcoding_accepted_containers": "Contenitori accettati", "transcoding_accepted_containers_description": "Seleziona quali formati non hanno bisogno di essere remuxati in MP4. Usato solo per certe politiche di transcodifica.", "transcoding_accepted_video_codecs": "Codifiche video accettate", - "transcoding_accepted_video_codecs_description": "Seleziona quali codifiche video non devono essere trascodificate. Usato solo per alcune politiche di trascodifica.", + "transcoding_accepted_video_codecs_description": "Seleziona quali codifiche video non devono essere transcodificate. Usato solo per alcune politiche di transcodifica.", "transcoding_advanced_options_description": "Impostazioni che la maggior parte degli utenti non dovrebbero cambiare", "transcoding_audio_codec": "Codifica Audio", "transcoding_audio_codec_description": "Opus è l'opzione con la qualità piÚ alta, ma è meno compatibile con dispositivi o software vecchi.", @@ -310,7 +345,7 @@ "transcoding_reference_frames": "Frame di riferimento", "transcoding_reference_frames_description": "Il numero di frame da prendere in considerazione nel comprimere un determinato frame. Valori piÚ alti migliorano l'efficienza di compressione, ma rallentano la codifica. 0 imposta questo valore automaticamente.", "transcoding_required_description": "Solo video che non sono in un formato accettato", - "transcoding_settings": "Impostazioni Trascodifica Video", + "transcoding_settings": "Impostazioni Transcodifica Video", "transcoding_settings_description": "Gestisci quali video transcodificare e come processarli", "transcoding_target_resolution": "Risoluzione desiderata", "transcoding_target_resolution_description": "Risoluzioni piÚ elevate possono preservare piÚ dettagli ma richiedono piÚ tempo per la codifica, producono file di dimensioni maggiori e possono ridurre la reattività dell'applicazione.", @@ -321,16 +356,19 @@ "transcoding_tone_mapping": "Mappatura della tonalità", "transcoding_tone_mapping_description": "Tenta di preservare l'aspetto dei video HDR quando convertiti in SDR. Ciascun algoritmo fa diversi compromessi per colore, dettaglio e luminosità. Hable conserva il dettaglio, Mobius conserva il colore e Reinhard conserva la luminosità.", "transcoding_transcode_policy": "Politica di transcodifica", - "transcoding_transcode_policy_description": "Politica che determina quando un video deve essere trascodificato. I video HDR verranno sempre trascodificati (eccetto quando la trascodifica è disabilitata).", + "transcoding_transcode_policy_description": "Politica che determina quando un video deve essere transcodificato. I video HDR verranno sempre transcodificati (eccetto quando la transcodifica è disabilitata).", "transcoding_two_pass_encoding": "Codifica a due passaggi", - "transcoding_two_pass_encoding_setting_description": "Trascodifica in due passaggi per produrre video codificati migliori. Quando il bitrate massimo è abilitato (necessario affinchÊ funzioni con H.264 e HEVC), questa modalità utilizza un intervallo di bitrate basato sul bitrate massimo e ignora CRF. Per VP9, CRF puÃ˛ essere utilizzato se il bitrate massimo è disabilitato.", + "transcoding_two_pass_encoding_setting_description": "Transcodifica in due passaggi per produrre video codificati migliori. Quando il bitrate massimo è abilitato (necessario affinchÊ funzioni con H.264 e HEVC), questa modalità utilizza un intervallo di bitrate basato sul bitrate massimo e ignora CRF. Per VP9, CRF puÃ˛ essere utilizzato se il bitrate massimo è disabilitato.", "transcoding_video_codec": "Codec video", - "transcoding_video_codec_description": "VP9 ha alta efficienza e compatibilità web, ma richiede piÚ tempo per la trascodifica. HEVC ha prestazioni simili, ma una minore compatibilità web. H.264 è ampiamente compatibile e veloce da transcodificare, ma produce file molto piÚ grandi. AV1 è il codec piÚ efficiente, ma non è supportato sui dispositivi piÚ vecchi.", + "transcoding_video_codec_description": "VP9 ha alta efficienza e compatibilità web, ma richiede piÚ tempo per la transcodifica. HEVC ha prestazioni simili, ma una minore compatibilità web. H.264 è ampiamente compatibile e veloce da transcodificare, ma produce file molto piÚ grandi. AV1 è il codec piÚ efficiente, ma non è supportato sui dispositivi piÚ vecchi.", "trash_enabled_description": "Abilita Funzionalità Cestino", "trash_number_of_days": "Numero di giorni", "trash_number_of_days_description": "Numero di giorni per cui mantenere gli asset nel cestino prima di rimuoverli definitivamente", "trash_settings": "Impostazioni cestino", "trash_settings_description": "Gestisci impostazioni cestino", + "unlink_all_oauth_accounts": "Disconnetti tutti gli account OAuth", + "unlink_all_oauth_accounts_description": "Ricorda di scollegare tutti gli account OAuth prima di passare a un nuovo provider.", + "unlink_all_oauth_accounts_prompt": "Sei sicuro di voler scollegare tutti gli account OAuth? Questa operazione reimposterà l’ID OAuth per ogni utente e non potrà essere annullata.", "user_cleanup_job": "Pulizia Utente", "user_delete_delay": "L'account e gli asset dell'utente {user} verranno programmati per la cancellazione definitiva tra {delay, plural, one {# giorno} other {# giorni}}.", "user_delete_delay_settings": "Ritardo eliminazione", @@ -350,8 +388,8 @@ "version_check_implications": "La funzione di controllo della versione fa uso di una comunicazione periodica con github.com", "version_check_settings": "Controllo Versione", "version_check_settings_description": "Abilita/disabilita la notifica per nuove versioni", - "video_conversion_job": "Trascodifica video", - "video_conversion_job_description": "Trascodifica video per maggiore compatibilità con browser e dispositivi" + "video_conversion_job": "Transcodifica video", + "video_conversion_job_description": "Transcodifica video per maggiore compatibilità con browser e dispositivi" }, "admin_email": "Email Amministratore", "admin_password": "Password Amministratore", @@ -360,25 +398,28 @@ "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: {level}", - "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_subtitle": "Alcuni dispositivi sono estremamente lenti a caricare le miniature da risorse locali. Attiva questa impostazione per caricare invece le immagini remote.", "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": "Header Proxy", + "advanced_settings_readonly_mode_subtitle": "Abilita la modalità di sola lettura in cui le foto possono essere solo visualizzate, mentre funzioni come la selezione di piÚ immagini, la condivisione, la trasmissione e l'eliminazione sono tutte disabilitate. Abilita/Disabilita la sola lettura tramite l'avatar dell'utente dalla schermata principale", + "advanced_settings_readonly_mode_title": "Modalità di sola lettura", "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_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo quando 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_tile_subtitle": "Impostazioni avanzate dell'utente", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", "age_months": "Età {months, plural, one {# mese} other {# mesi}}", "age_year_months": "Età 1 anno, {months, plural, one {# mese} other {# mesi}}", - "age_years": "{years, plural, one {# anno} other {# anni}}", + "age_years": "{years, plural, other {Età #}}", "album_added": "Album aggiunto", - "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto a un album condiviso", + "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto ad un album condiviso", "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", + "album_deleted": "Album eliminato", "album_info_card_backup_album_excluded": "ESCLUSI", "album_info_card_backup_album_included": "INCLUSI", "album_info_updated": "Informazioni dell'album aggiornate", @@ -388,47 +429,52 @@ "album_options": "Impostazioni Album", "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", + "album_search_not_found": "Nessun album trovato corrispondente alla tua ricerca", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", + "album_summary": "Sommario Album", "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": "Non è stato possibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Non è stato possibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere elementi dall'album", + "album_viewer_appbar_share_err_title": "Non è stato possibile 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", "album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", - "albums_default_sort_order": "Ordinamento album predefinito", - "albums_default_sort_order_description": "Ordine iniziale degli asset alla creazione di nuovi album.", - "albums_feature_description": "Collezione di asset che possono essere condivisi con altri utenti.", + "albums_default_sort_order": "Ordinamento predefinito degli album", + "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", + "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", + "albums_on_device_count": "Album sul dispositivo ({count})", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", "all_videos": "Tutti i video", "allow_dark_mode": "Permetti Tema Scuro", - "allow_edits": "Permetti Modifiche", + "allow_edits": "Permetti modifiche", "allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare", "allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare", "alt_text_qr_code": "Immagine QR", "anti_clockwise": "Senso anti-orario", "api_key": "Chiave API", - "api_key_description": "Il valore verrà mostrato solo una volta. Assicurati di copiarlo prima di chiudere la finestra.", - "api_key_empty": "Il nome della chiave API non puÃ˛ essere vuoto", + "api_key_description": "Questo valore verrà mostrato una sola volta. Assicurati di copiarlo prima di chiudere la finestra.", + "api_key_empty": "Il nome della chiave API non dovrebbe essere vuoto", "api_keys": "Chiavi API", "app_bar_signout_dialog_content": "Sei sicuro di volerti disconnettere?", "app_bar_signout_dialog_ok": "Si", "app_bar_signout_dialog_title": "Disconnetti", "app_settings": "Impostazioni Applicazione", "appears_in": "Compare in", + "apply_count": "Applica ({count, number})", "archive": "Archivio", + "archive_action_prompt": "Aggiunti {count} elementi all'Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", - "archive_page_no_archived_assets": "Nessuna oggetto archiviato", + "archive_page_no_archived_assets": "Non è stato trovato nessun elemento archiviato", "archive_page_title": "Archivio ({count})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", @@ -440,64 +486,72 @@ "asset_action_share_err_offline": "Non è possibile recuperare le risorse offline, azione ignorata", "asset_added_to_album": "Aggiunto all'album", "asset_adding_to_album": "Aggiungendo all'albumâ€Ļ", - "asset_description_updated": "La descrizione del media è stata aggiornata", + "asset_description_updated": "La descrizione dell'elemento è stata aggiornata", "asset_filename_is_offline": "Il media {filename} è offline", "asset_has_unassigned_faces": "Il media ha dei volti non categorizzati", "asset_hashing": "Hashing in corso â€Ļ", "asset_list_group_by_sub_title": "Raggruppa per", "asset_list_layout_settings_dynamic_layout_title": "Layout dinamico", "asset_list_layout_settings_group_automatically": "Automatico", - "asset_list_layout_settings_group_by": "Raggruppa le risorse per", + "asset_list_layout_settings_group_by": "Raggruppa gli elementi per", "asset_list_layout_settings_group_by_month_day": "Mese + giorno", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Impostazion del layout della griglia delle foto", + "asset_list_settings_subtitle": "Impostazioni del layout della griglia delle foto", "asset_list_settings_title": "Griglia foto", - "asset_offline": "Risorsa Offline", - "asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.", - "asset_restored_successfully": "Asset ripristinato con successo", + "asset_offline": "Elemento Offline", + "asset_offline_description": "Questo elemento esterno non viene piÚ trovato sul disco. Contatta il tuo amministratore di Immich per assistenza.", + "asset_restored_successfully": "Elemento ripristinato con successo", "asset_skipped": "Saltato", "asset_skipped_in_trash": "Nel cestino", + "asset_trashed": "Asset cestinato", + "asset_troubleshoot": "Risoluzione dei problemi dell'asset", "asset_uploaded": "Caricato", "asset_uploading": "Caricamentoâ€Ļ", - "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore risorse", + "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore della galleria", "asset_viewer_settings_title": "Visualizzazione risorse", "assets": "Risorse", "assets_added_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}}", "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 {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {L'asset} other {Gli asset}} non possono essere aggiunti all'album", - "assets_count": "{count, plural, other {# asset}}", + "assets_added_to_albums_count": "Aggiunto {assetTotal, plural, one {# elemento} other {# elementi}} a {albumTotal, plural, one {# album} other {# album}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {L'elemento} other {Gli elementi}} non possono essere aggiunti all'album", + "assets_cannot_be_added_to_albums": "Non Ê stato possibile aggiungere {count, plural, one {l'elemento} other {gli elementi}} a nessun album", + "assets_count": "{count, plural, one {# elemento} other {# elementi}}", "assets_deleted_permanently": "{count} elementi cancellati definitivamente", "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", - "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riusciti} other {Scaricati # file - {error} file non riusciti}}", + "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riuscito} other {Scaricati # file - {error} file non riusciti}}", "assets_downloaded_successfully": "{count, plural, one {Scaricato # file con successo} other {Scaricati # file con successo}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", + "assets_moved_to_trash_count": "{count, plural, one {# elemento spostato} other {# elementi 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": "{count} elementi cancellati 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_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli elementi 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": "{count} elementi ripristinati", "assets_trashed": "{count} elementi cestinati", "assets_trashed_count": "{count, plural, one {Spostato # asset} other {Spostati # assets}} nel cestino", "assets_trashed_from_server": "{count} 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", + "assets_were_part_of_albums_count": "{count, plural, one {L'elemento fa} other {Gli elementi fanno}} già parte degli 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", + "automatic_endpoint_switching_subtitle": "Connetti localmente alla rete Wi-Fi specificata, se disponibile; altrimenti utilizza connessioni alternative", "automatic_endpoint_switching_title": "Cambio automatico di URL", "autoplay_slideshow": "Avvio automatico presentazione", "back": "Indietro", "back_close_deselect": "Indietro, chiudi o deseleziona", + "background_backup_running_error": "Il backup in background è attualmente in esecuzione, impossibile avviare il backup manuale", "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", + "background_options": "Opzioni sfondo", + "backup": "Backup", "backup_album_selection_page_albums_device": "Album sul dispositivo ({count})", "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", "backup_album_selection_page_total_assets": "Numero totale delle risorse", + "backup_albums_sync": "Sincronizzazione album di backup", "backup_all": "Tutti", - "backup_background_service_backup_failed_message": "Impossibile caricare i contenuti. Riprovoâ€Ļ", + "backup_background_service_backup_failed_message": "È stato impossibile fare il backup dei contenuti. Riprovoâ€Ļ", "backup_background_service_connection_failed_message": "Impossibile connettersi al server. Riprovoâ€Ļ", "backup_background_service_current_upload_notification": "Caricamento di {filename} in corso", "backup_background_service_default_notification": "Ricerca di nuovi contenutiâ€Ļ", @@ -506,7 +560,7 @@ "backup_background_service_upload_failure_notification": "Impossibile caricare {filename}", "backup_controller_page_albums": "Backup Album", "backup_controller_page_background_app_refresh_disabled_content": "Attiva l'aggiornamento dell'app in background in Impostazioni > Generale > Aggiorna app in background per utilizzare backup in background.", - "backup_controller_page_background_app_refresh_disabled_title": "Backup in background è disattivo", + "backup_controller_page_background_app_refresh_disabled_title": "Aggiornamento dell'app in background disattivo", "backup_controller_page_background_app_refresh_enable_button_text": "Vai alle impostazioni", "backup_controller_page_background_battery_info_link": "Mostrami come", "backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.", @@ -515,12 +569,12 @@ "backup_controller_page_background_charging": "Solo durante la ricarica", "backup_controller_page_background_configure_error": "Impossibile configurare i servizi in background", "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {duration}", - "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di tutti i nuovi contenuti senza la necessità di aprire l'app", - "backup_controller_page_background_is_off": "Backup automatico disattivato", - "backup_controller_page_background_is_on": "Backup automatico attivo", + "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di nuovi contenuti senza la necessità di aprire l'app", + "backup_controller_page_background_is_off": "Backup automatico in background disattivato", + "backup_controller_page_background_is_on": "Backup automatico in background attivo", "backup_controller_page_background_turn_off": "Disabilita servizi in background", "backup_controller_page_background_turn_on": "Abilita servizi in background", - "backup_controller_page_background_wifi": "Solo Wi-Fi", + "backup_controller_page_background_wifi": "Solo con Wi-Fi", "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Attiva backup", "backup_controller_page_uploading_file_info": "Caricamento informazioni file", "backup_err_only_album": "Non è possibile rimuovere l'unico album", + "backup_error_sync_failed": "Sincronizzazione non riuscita. Impossibile elaborare il backup.", "backup_info_card_assets": "risorse", "backup_manual_cancelled": "Annullato", "backup_manual_in_progress": "Caricamento già in corso. Riprova piÚ tardi", "backup_manual_success": "Successo", "backup_manual_title": "Stato del caricamento", + "backup_options": "Opzioni di Backup", "backup_options_page_title": "Opzioni di Backup", "backup_setting_subtitle": "Gestisci le impostazioni di upload in primo piano e in background", + "backup_settings_subtitle": "Gestisci le impostazioni di caricamento", "backward": "Indietro", "biometric_auth_enabled": "Autenticazione biometrica attivata", "biometric_locked_out": "Sei stato bloccato dall'autenticazione biometrica", @@ -562,7 +619,7 @@ "blurred_background": "Sfondo sfocato", "bugs_and_feature_requests": "Bug & Richieste di nuove funzionalità", "build": "Compilazione", - "build_image": "Compila Immagine", + "build_image": "Immagine Compilata", "bulk_delete_duplicates_confirmation": "Sei sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset piÚ pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati. Non puoi annullare questa operazione!", "bulk_keep_duplicates_confirmation": "Sei sicuro di voler tenere {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione risolverà tutti i gruppi duplicati senza cancellare nulla.", "bulk_trash_duplicates_confirmation": "Sei davvero sicuro di voler cancellare {count, plural, one {# asset duplicato} other {# assets duplicati}}? Questa operazione manterrà l'asset piÚ pesante di ogni gruppo e cancellerà permanentemente tutti gli altri duplicati.", @@ -587,6 +644,7 @@ "cancel": "Annulla", "cancel_search": "Annulla ricerca", "canceled": "Annullato", + "canceling": "Annullamento", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -609,6 +667,8 @@ "change_pin_code": "Cambia il codice PIN", "change_your_password": "Modifica la tua password", "changed_visibility_successfully": "Visibilità modificata con successo", + "charging": "In carica", + "charging_requirement_mobile_backup": "Il backup in background richiede che il dispositivo sia in carica", "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.", @@ -618,6 +678,7 @@ "clear": "Pulisci", "clear_all": "Pulisci tutto", "clear_all_recent_searches": "Rimuovi tutte le ricerche recenti", + "clear_file_cache": "Cancella la cache dei file", "clear_message": "Pulisci messaggio", "clear_value": "Pulisci valore", "client_cert_dialog_msg_confirm": "OK", @@ -639,7 +700,7 @@ "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.", + "common_server_error": "Verifica la connessione di rete, assicurati che il server sia raggiungibile e che le versioni dell’app e del server siano compatibili.", "completed": "Completato", "confirm": "Conferma", "confirm_admin_password": "Conferma password dell'amministratore", @@ -674,7 +735,7 @@ "copy_to_clipboard": "Copia negli appunti", "country": "Nazione", "cover": "Riempi la finestra", - "covers": "Copre", + "covers": "Copertine", "create": "Crea", "create_album": "Crea album", "create_album_page_untitled": "Senza titolo", @@ -688,11 +749,13 @@ "create_new_user": "Crea nuovo utente", "create_shared_album_page_share_add_assets": "AGGIUNGI OGGETTI", "create_shared_album_page_share_select_photos": "Seleziona foto", + "create_shared_link": "Crea link condiviso", "create_tag": "Crea tag", - "create_tag_description": "Crea un nuovo tag. Per i tag annidati, si prega di inserire il percorso completo del tag tra cui barre oblique.", + "create_tag_description": "Crea un nuovo tag. Per i tag nidificati, inserisci il percorso completo del tag includendo le barre oblique (/).", "create_user": "Crea utente", "created": "Creato", "created_at": "Creato il", + "creating_linked_albums": "Creazione di album collegati...", "crop": "Ritaglia", "curated_object_page_title": "Oggetti", "current_device": "Dispositivo attuale", @@ -700,18 +763,20 @@ "current_server_address": "Indirizzo del server in uso", "custom_locale": "Localizzazione personalizzata", "custom_locale_description": "Formatta data e numeri in base alla lingua e al paese", + "custom_url": "URL personalizzato", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", - "darkTheme": "Attiva/Disattiva tema scuro", - "date_after": "Data dopo", + "dark_theme": "Imposta tema scuro", + "date_after": "Dopo la data", "date_and_time": "Data e ora", - "date_before": "Data prima", + "date_before": "Prima della data", "date_format": "E, d LLL, y â€ĸ hh:mm", "date_of_birth_saved": "Data di nascita salvata con successo", "date_range": "Intervallo di date", "day": "Giorno", - "deduplicate_all": "Duplica Tutti", + "days": "Giorni", + "deduplicate_all": "Elimina tutti i doppioni", "deduplication_criteria_1": "Dimensione immagine in bytes", "deduplication_criteria_2": "Numero di dati EXIF", "deduplication_info": "Informazioni di deduplicazione", @@ -719,6 +784,8 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", + "delete_action_confirmation_message": "Vuoi davvero eliminare questo asset? Questa azione sposterà l'asset nel cestino del server e ti chiederà se desideri eliminarla localmente", + "delete_action_prompt": "{count} elementi eliminati", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -732,9 +799,12 @@ "delete_key": "Elimina chiave", "delete_library": "Elimina libreria", "delete_link": "Elimina link", + "delete_local_action_prompt": "{count} elementi rimossi in locale", "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", + "delete_permanently": "Elimina definitivamente", + "delete_permanently_action_prompt": "{count} eliminati definitivamente", "delete_shared_link": "Elimina link condiviso", "delete_shared_link_dialog_title": "Elimina link condiviso", "delete_tag": "Elimina tag", @@ -745,6 +815,7 @@ "description": "Descrizione", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "deselect_all": "Deseleziona Tutto", "details": "Dettagli", "direction": "Direzione", "disabled": "Disabilitato", @@ -762,6 +833,7 @@ "documentation": "Documentazione", "done": "Fatto", "download": "Scarica", + "download_action_prompt": "Scaricando {count} elementi", "download_canceled": "Download annullato", "download_complete": "Download completato", "download_enqueue": "Download in coda", @@ -788,8 +860,12 @@ "edit": "Modifica", "edit_album": "Modifica album", "edit_avatar": "Modifica avatar", + "edit_birthday": "Modifica compleanno", "edit_date": "Modifica data", "edit_date_and_time": "Modifica data e ora", + "edit_date_and_time_action_prompt": "{count} data e ora modificata", + "edit_date_and_time_by_offset": "Modifica data per offset", + "edit_date_and_time_by_offset_interval": "Nuovo intervallo di date: {from} - {to}", "edit_description": "Modifica la descrizione", "edit_description_prompt": "Selezionare una nuova descrizione:", "edit_exclusion_pattern": "Modifica pattern di esclusione", @@ -799,6 +875,7 @@ "edit_key": "Modifica chiave", "edit_link": "Modifica link", "edit_location": "Modifica posizione", + "edit_location_action_prompt": "{count} luoghi modificati", "edit_location_dialog_title": "Posizione", "edit_name": "Modifica nome", "edit_people": "Modifica persone", @@ -817,6 +894,7 @@ "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", + "enable_backup": "Abilita Backup", "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", @@ -827,7 +905,9 @@ "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_getting_places": "Errore durante il recupero dei luoghi", "error_loading_image": "Errore nel caricamento dell'immagine", + "error_loading_partners": "Errore durante il caricamento dei partner: {error}", "error_saving_image": "Errore: {error}", "error_tag_face_bounding_box": "Errore durante il tag del volto - impossibile ricavare le coordinate del riquadro", "error_title": "Errore - Qualcosa è andato storto", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "Errore nel caricamento delle notifiche", "failed_to_load_people": "Caricamento delle persone non riuscito", "failed_to_remove_product_key": "Rimozione del codice del prodotto fallita", + "failed_to_reset_pin_code": "Impossibile reimpostare il codice PIN", "failed_to_stack_assets": "Errore durante il raggruppamento degli assets", "failed_to_unstack_assets": "Errore durante la separazione degli assets", "failed_to_update_notification_status": "Aggiornamento stato notifiche fallito", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# percorso} other {# percorsi}} hanno fallito la validazione", "profile_picture_transparent_pixels": "Le foto profilo non possono avere pixel trasparenti. Riprova ingrandendo e/o muovendo l'immagine.", "quota_higher_than_disk_size": "Hai impostato un limite piÚ alto della dimensione del disco", + "something_went_wrong": "Qualcosa è andato storto", "unable_to_add_album_users": "Impossibile aggiungere utenti all'album", "unable_to_add_assets_to_shared_link": "Impossibile aggiungere gli assets al link condiviso", "unable_to_add_comment": "Impossibile aggiungere commento", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Aggiungi una descrizione...", + "exif_bottom_sheet_description_error": "Errore durante l'aggiornamento della descrizione", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", "exif_bottom_sheet_person_add_person": "Aggiungi nome", - "exif_bottom_sheet_person_age_months": "Età {months} mesi", - "exif_bottom_sheet_person_age_year_months": "Età 1 anno e {months} mesi", - "exif_bottom_sheet_person_age_years": "Età {years}", "exit_slideshow": "Esci dalla presentazione", "expand_all": "Espandi tutto", "experimental_settings_new_asset_list_subtitle": "Lavori in corso", @@ -973,6 +1053,8 @@ "explorer": "Esplora", "export": "Esporta", "export_as_json": "Esporta come JSON", + "export_database": "Esporta database", + "export_database_description": "Esporta il database SQLite", "extension": "Estensione", "external": "Esterno", "external_libraries": "Librerie esterne", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Impossibile caricare gli asset", "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", + "favorite_action_prompt": "{count} elementi aggiunti ai preferiti", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", "favorites_page_no_favorites": "Nessun preferito", "feature_photo_updated": "Foto in evidenza aggiornata", "features": "Funzionalità", + "features_in_development": "Funzionalità in fase di sviluppo", "features_setting_description": "Gestisci le funzionalità dell'app", "file_name": "Nome file", "file_name_or_extension": "Nome file o estensione", @@ -998,21 +1082,26 @@ "filter_people": "Filtra persone", "filter_places": "Filtra luoghi", "find_them_fast": "Trovale velocemente con la ricerca", + "first": "Primo", "fix_incorrect_match": "Correggi corrispondenza errata", "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", + "forgot_pin_code_question": "Hai dimenticato il tuo PIN?", "forward": "Avanti", "gcast_enabled": "Google Cast Abilitato", "gcast_enabled_description": "Questa funzione carica risorse esterne da Google per poter funzionare.", "general": "Generale", + "geolocation_instruction_location": "Fai clic su una risorsa con coordinate GPS per utilizzare la sua posizione oppure seleziona una posizione direttamente dalla mappa", "get_help": "Chiedi Aiuto", "get_wifiname_error": "Non sono riuscito a recuperare il nome della rete Wi-Fi. Accertati di aver concesso i permessi necessari e di essere connesso ad una rete Wi-Fi", "getting_started": "Iniziamo", "go_back": "Torna indietro", "go_to_folder": "Vai alla cartella", "go_to_search": "Vai alla ricerca", + "gps": "GPS", + "gps_missing": "No GPS", "grant_permission": "Concedi permesso", "group_albums_by": "Raggruppa album in base a...", "group_country": "Raggruppa per paese", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", + "hash_asset": "Risorsa hash", + "hashed_assets": "Risorse hash", + "hashing": "Hashing", "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", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "Puoi caricare al massimo 30 file per volta, ignora quelli in eccesso", "host": "Host", "hour": "Ora", + "hours": "Ore", "id": "ID", + "idle": "Inattivo", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", "image": "Immagine", @@ -1080,7 +1174,7 @@ "in_archive": "In archivio", "include_archived": "Includi Archiviati", "include_shared_albums": "Includi album condivisi", - "include_shared_partner_assets": "Includi asset condivisi del compagno", + "include_shared_partner_assets": "Includi elementi condivisi dai compagni", "individual_share": "Condivisione individuale", "individual_shares": "Condivisioni individuali", "info": "Info", @@ -1110,12 +1204,15 @@ "language": "Lingua", "language_no_results_subtitle": "Prova a cambiare i tuoi termini di ricerca", "language_no_results_title": "Linguaggi non trovati", - "language_search_hint": "Cerca linguaggi...", + "language_search_hint": "Cerca una lingua...", "language_setting_description": "Seleziona la tua lingua predefinita", + "large_files": "File pesanti", + "last": "Ultimo", "last_seen": "Ultimo accesso", "latest_version": "Ultima Versione", "latitude": "Latitudine", "leave": "Esci", + "leave_album": "Esci dall’album", "lens_model": "Modello lenti", "let_others_respond": "Permetti agli altri di rispondere", "level": "Livello", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "Data di creazione", "library_page_sort_last_modified": "Ultima modifica", "library_page_sort_title": "Titolo album", + "licenses": "Licenze", "light": "Chiaro", + "like": "Mi piace", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", - "link_options": "Impostazioni Collegamento", "link_to_oauth": "Collegamento a OAuth", "linked_oauth_account": "Account OAuth collegato", "list": "Lista", "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", + "local": "Locale", "local_asset_cast_failed": "Impossibile trasmettere una risorsa che non è caricata sul server", + "local_assets": "Risorsa locale", + "local_media_summary": "Riepilogo dei Media Locali", "local_network": "Rete locale", "local_network_sheet_info": "L'app si collegherà al server tramite questo URL quando è in uso la rete Wi-Fi specificata", "location_permission": "Permesso di localizzazione", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Inserisci la longitudine qui", "lock": "Rendi privato", "locked_folder": "Cartella Privata", + "log_detail_title": "Dettaglio dei Log", "log_out": "Esci", "log_out_all_devices": "Disconnetti tutti i dispositivi", "logged_in_as": "Effettuato l'accesso come {user}", @@ -1178,13 +1280,15 @@ "login_password_changed_success": "Password aggiornata con successo", "logout_all_device_confirmation": "Sei sicuro di volerti disconnettere da tutti i dispositivi?", "logout_this_device_confirmation": "Sei sicuro di volerti disconnettere da questo dispositivo?", + "logs": "Logs", "longitude": "Longitudine", "look": "Guarda", "loop_videos": "Riproduci video in loop", - "loop_videos_description": "Abilita per riprodurre automaticamente un video in loop nella vista dettagli.", + "loop_videos_description": "Abilita per riprodurre automaticamente un video in loop nel visualizzatore dei dettagli.", "main_branch_warning": "Stai utilizzando una versione di sviluppo. Ti consigliamo vivamente di utilizzare una versione di rilascio!", "main_menu": "Menu Principale", "make": "Produttore", + "manage_geolocation": "Gestisci posizione", "manage_shared_links": "Gestisci link condivisi", "manage_sharing_with_partners": "Gestisci la condivisione con i compagni", "manage_the_app_settings": "Gestisci le impostazioni dell'applicazione", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Gestisci i tuoi dispositivi collegati", "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto", + "map_assets_in_bounds": "{count, plural, =0 {Nessuna foto in quest’area} one {# foto} other {# foto}}", "map_cannot_get_user_location": "Non è possibile ottenere la posizione dell'utente", "map_location_dialog_yes": "Si", "map_location_picker_page_use_location": "Usa questa posizione", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Servizio Localizzazione disattivato", "map_marker_for_images": "Indicatore mappa per le immagini scattate in {city}, {country}", "map_marker_with_image": "Segnaposto con immagine", - "map_no_assets_in_bounds": "Nessuna foto in questa zona", "map_no_location_permission_content": "L'accesso alla posizione è necessario per visualizzare gli elementi per la tua posizione attuale. Vuoi consentirlo adesso?", "map_no_location_permission_title": "Autorizzazione Posizione negata", "map_settings": "Impostazioni Mappa", @@ -1221,6 +1323,7 @@ "mark_as_read": "Segna come letto", "marked_all_as_read": "Segnato tutto come letto", "matches": "Corrispondenze", + "matching_assets": "Assets Corrispondenti", "media_type": "Tipo Media", "memories": "Ricordi", "memories_all_caught_up": "Tutto a posto", @@ -1239,6 +1342,7 @@ "merged_people_count": "{count, plural, one {Unita # persona} other {Unite # persone}}", "minimize": "Minimizza", "minute": "Minuto", + "minutes": "Minuti", "missing": "Mancanti", "model": "Modello", "month": "Mese", @@ -1246,6 +1350,7 @@ "more": "Di piÚ", "move": "Sposta", "move_off_locked_folder": "Sposta al di fuori della cartella privata", + "move_to_lock_folder_action_prompt": "{count} elementi aggiunti alla cartella sicura", "move_to_locked_folder": "Sposta nella cartella privata", "move_to_locked_folder_confirmation": "Queste foto e video verranno rimossi da tutti gli album, e saranno visibili solo dalla cartella privata", "moved_to_archive": "Spostati {count, plural, one {# asset} other {# assets}} nell'archivio", @@ -1257,6 +1362,10 @@ "my_albums": "I miei album", "name": "Nome", "name_or_nickname": "Nome o soprannome", + "network_requirement_photos_upload": "Utilizza la connessione dati per il backup delle foto", + "network_requirement_videos_upload": "Utilizza la connessione dati per il backup dei video", + "network_requirements": "Requisiti di rete", + "network_requirements_updated": "Requisiti di rete modificati, coda di backup reimpostata", "networking_settings": "Rete", "networking_subtitle": "Gestisci le impostazioni riguardanti gli endpoint del server", "never": "Mai", @@ -1266,6 +1375,7 @@ "new_person": "Nuova persona", "new_pin_code": "Nuovo codice PIN", "new_pin_code_subtitle": "Questa è la prima volta che accedi alla cartella privata. Crea un codice PIN per accedere in modo sicuro a questa pagina", + "new_timeline": "Nuova Timeline", "new_user_created": "Nuovo utente creato", "new_version_available": "NUOVA VERSIONE DISPONIBILE", "newest_first": "Prima recenti", @@ -1279,19 +1389,25 @@ "no_assets_message": "CLICCA PER CARICARE LA TUA PRIMA FOTO", "no_assets_to_show": "Nessuna risorsa da mostrare", "no_cast_devices_found": "Nessun dispositivo di trasmissione trovato", + "no_checksum_local": "Nessun checksum disponibile: impossibile recuperare gli assets locali", + "no_checksum_remote": "Nessun checksum disponibile: impossibile recuperare l'asset remoto", "no_duplicates_found": "Nessun duplicato trovato.", "no_exif_info_available": "Nessuna informazione exif disponibile", "no_explore_results_message": "Carica piÚ foto per esplorare la tua collezione.", "no_favorites_message": "Aggiungi preferiti per trovare facilmente le tue migliori foto e video", "no_libraries_message": "Crea una libreria esterna per vedere le tue foto e i tuoi video", + "no_local_assets_found": "Nessun asset locale trovato con questo checksum", "no_locked_photos_message": "Le foto e i video nella cartella privata sono nascosti e non vengono visualizzati mentre navighi o cerchi nella tua libreria.", "no_name": "Nessun nome", "no_notifications": "Nessuna notifica", "no_people_found": "Nessuna persona trovata", "no_places": "Nessun posto", + "no_remote_assets_found": "Nessun asset remoto trovato con questo checksum", "no_results": "Nessun risultato", "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", + "no_uploads_in_progress": "Nessun upload in corso", + "not_available": "N/A", "not_in_any_album": "In nessun album", "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", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Risorse Ufficiali Immich", "offline": "Offline", + "offset": "Offset", "ok": "Ok", "oldest_first": "Prima vecchi", "on_this_device": "Su questo dispositivo", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Apri filtri di ricerca", "options": "Opzioni", "or": "o", + "organize_into_albums": "Organizza all'interno degli albums", + "organize_into_albums_description": "Inserisci le foto esistenti all'interno degli albums utilizzando le attuale impostazioni di sincronizzazione", "organize_your_library": "Organizza la tua libreria", "original": "originale", "other": "Altro", "other_devices": "Altri dispositivi", + "other_entities": "Altre entità", "other_variables": "Altre variabili", "owned": "Posseduti", "owner": "Proprietario", @@ -1383,29 +1503,33 @@ "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.", "person": "Persona", + "person_age_months": "{months, plural, one {# mese} other {# mesi}}", + "person_age_year_months": "1 anno e {months, plural, one {# mese} other {# mesi}}", + "person_age_years": "{years, plural, one {# anno} other {# anni}}", "person_birthdate": "Nato il {date}", "person_hidden": "{name}{hidden, select, true { (nascosto)} other {}}", - "photo_shared_all_users": "Sembra che tu abbia condiviso le foto con tutti gli utenti, oppure che non ci siano utenti con i quali condividerle.", + "photo_shared_all_users": "Sembra che tu abbia condiviso le tue foto con tutti gli utenti, oppure che tu non abbia alcun utente con cui condividerle.", "photos": "Foto", "photos_and_videos": "Foto & Video", "photos_count": "{count, plural, one {{count, number} Foto} other {{count, number} Foto}}", - "photos_from_previous_years": "Foto degli anni scorsi", + "photos_from_previous_years": "Foto dagli anni scorsi", "pick_a_location": "Scegli una posizione", - "pin_code_changed_successfully": "Codice PIN cambiato", + "pin_code_changed_successfully": "Codice PIN cambiato correttamente", "pin_code_reset_successfully": "Codice PIN resettato con successo", - "pin_code_setup_successfully": "Codice PIN cambiato con successo", + "pin_code_setup_successfully": "Codice PIN impostato correttamente", "pin_verification": "Verifica del codice PIN", - "place": "Posizione", + "place": "Luogo", "places": "Luoghi", - "places_count": "{count, plural, one {{count, number} Luogo} other {{count, number} Places}}", + "places_count": "{count, plural, one {{count, number} Posizione} other {{count, number} Posizioni}}", "play": "Riproduci", - "play_memories": "Avvia ricordi", - "play_motion_photo": "Avvia Foto in movimento", + "play_memories": "Riproduci ricordi", + "play_motion_photo": "Riproduci foto in movimento", "play_or_pause_video": "Avvia o metti in pausa il video", - "please_auth_to_access": "Si prega di autenticarsi per accedere", + "please_auth_to_access": "Autenticati per accedere", "port": "Porta", "preferences_settings_subtitle": "Gestisci le preferenze dell'app", "preferences_settings_title": "Preferenze", + "preparing": "Preparando", "preset": "Preimpostazione", "preview": "Anteprima", "previous": "Precedente", @@ -1418,18 +1542,19 @@ "privacy": "Privacy", "profile": "Profilo", "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_out_of_date_major": "L’app non è aggiornata. Aggiorna all’ultima versione principale.", + "profile_drawer_client_out_of_date_minor": "L'applicazione non è aggiornata. Aggiorna all'ultima versione minore.", "profile_drawer_client_server_up_to_date": "Client e server sono aggiornati", "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Il server non è aggiornato. Per favore aggiorna all'ultima versione principale.", - "profile_drawer_server_out_of_date_minor": "Il server non è aggiornato. Per favore aggiorna all'ultima versione minore.", + "profile_drawer_readonly_mode": "Modalità di sola lettura abilitata. Tieni premuto sull'avatar dell'utente per disabilitarla.", + "profile_drawer_server_out_of_date_major": "Il server non è aggiornato. Aggiorna all'ultima versione principale.", + "profile_drawer_server_out_of_date_minor": "Il server non è aggiornato. Aggiorna all'ultima versione minore.", "profile_image_of_user": "Immagine profilo di {user}", "profile_picture_set": "Foto profilo impostata.", "public_album": "Album pubblico", "public_share": "Condivisione Pubblica", "purchase_account_info": "Contributore", - "purchase_activated_subtitle": "Grazie per supportare Immich e i software open source", + "purchase_activated_subtitle": "Grazie per supportare Immich e il software open source", "purchase_activated_time": "Attivato il {date}", "purchase_activated_title": "La tua chiave è stata attivata con successo", "purchase_button_activate": "Attiva", @@ -1439,33 +1564,38 @@ "purchase_button_reminder": "Ricordamelo tra 30 giorni", "purchase_button_remove_key": "Rimuovi chiave", "purchase_button_select": "Seleziona", - "purchase_failed_activation": "Attivazione fallita! Controlla la tua e-mail per la chiave del prodotto corretta!", + "purchase_failed_activation": "Attivazione fallita! Controlla la tua email per la chiave prodotto corretta!", "purchase_individual_description_1": "Per un individuo", - "purchase_individual_description_2": "Stato di Contributore", + "purchase_individual_description_2": "Stato di Sostenitore", "purchase_individual_title": "Individuale", - "purchase_input_suggestion": "Hai una chiave del prodotto? Inseriscila qui sotto", + "purchase_input_suggestion": "Hai una chiave prodotto? Inseriscila qui sotto", "purchase_license_subtitle": "Acquista Immich per supportare lo sviluppo continuo del servizio", "purchase_lifetime_description": "Acquisto a vita", "purchase_option_title": "OPZIONI DI ACQUISTO", - "purchase_panel_info_1": "Costruire Immich richiede molto tempo e impegno, e abbiamo ingegneri a tempo pieno che lavorano per renderlo il migliore possibile. La nostra missione è fare in modo che i software open source e le pratiche aziendali etiche diventino una fonte di reddito sostenibile per gli sviluppatori e creare un ecosistema che rispetti la privacy, offrendo vere alternative ai servizi cloud sfruttatori.", + "purchase_panel_info_1": "Sviluppare Immich richiede molto tempo e impegno, e abbiamo ingegneri a tempo pieno che lavorano per renderlo il migliore possibile. La nostra missione è fare in modo che il software open source e pratiche commerciali etiche diventino una fonte di reddito sostenibile per gli sviluppatori, creando al contempo un ecosistema che rispetti la privacy, offrendo vere alternative ai servizi cloud sfruttatori.", "purchase_panel_info_2": "PoichÊ ci impegniamo a non aggiungere paywall, questo acquisto non ti garantirà funzionalità aggiuntive in Immich. Contiamo su utenti come te per supportare lo sviluppo continuo di Immich.", "purchase_panel_title": "Contribuisci al progetto", "purchase_per_server": "Per server", "purchase_per_user": "Per utente", - "purchase_remove_product_key": "Rimuovi la Chiave del Prodotto", - "purchase_remove_product_key_prompt": "Sei sicuro di voler rimuovere la chiave del prodotto?", + "purchase_remove_product_key": "Rimuovi la Chiave Prodotto", + "purchase_remove_product_key_prompt": "Sei sicuro di voler rimuovere la chiave prodotto?", "purchase_remove_server_product_key": "Rimuovi la chiave del prodotto per Server", "purchase_remove_server_product_key_prompt": "Sei sicuro di voler rimuovere la chiave del prodotto per Server?", "purchase_server_description_1": "Per l'intero server", "purchase_server_description_2": "Stato di Contributore", "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", + "query_asset_id": "Esegui una query sull'ID dell'asset", + "queue_status": "Messi in coda {count}/{total}", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", "rating_count": "{count, plural, one {# stella} other {# stelle}}", "rating_description": "Visualizza la valutazione EXIF nel pannello informazioni", "reaction_options": "Impostazioni Reazioni", "read_changelog": "Leggi Riepilogo Modifiche", + "readonly_mode_disabled": "Modalità di sola lettura disabilitata", + "readonly_mode_enabled": "Modalità di sola lettura abilitata", + "ready_for_upload": "Pronto per il caricamento", "reassign": "Riassegna", "reassigned_assets_to_existing_person": "{count, plural, one {Riassegnato # asset} other {Riassegnati # assets}} {name, select, null {ad una persona esistente} other {a {name}}}", "reassigned_assets_to_new_person": "{count, plural, one {Riassegnato # asset} other {Riassegnati # assets}} ad una nuova persona", @@ -1477,17 +1607,20 @@ "recently_added_page_title": "Aggiunti di recente", "recently_taken": "Scattate di recente", "recently_taken_page_title": "Scattate di Recente", - "refresh": "Aggiorna", + "refresh": "Ricarica", "refresh_encoded_videos": "Ricarica video codificati", - "refresh_faces": "Aggiorna facce", + "refresh_faces": "Aggiorna volti", "refresh_metadata": "Ricarica metadati", "refresh_thumbnails": "Ricarica anteprime", "refreshed": "Aggiornato", "refreshes_every_file": "Rilegge tutti i file esistenti e nuovi", "refreshing_encoded_video": "Ricaricando il video codificato", - "refreshing_faces": "Aggiorna Facce", + "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", + "remote": "Remoto", + "remote_assets": "Risorse remote", + "remote_media_summary": "Riepilogo dei Media Remoti", "remove": "Rimuovi", "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} dall'album?", "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} da questo link condiviso?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", + "remove_from_album_action_prompt": "{count} elementi rimossi dall'album", "remove_from_favorites": "Rimuovi dai preferiti", + "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", "remove_from_locked_folder_confirmation": "Sei sicuro di voler spostare queste foto e questi video dalla cartella privata? Diventeranno visibili nella vostra libreria.", "remove_from_shared_link": "Rimuovi dal link condiviso", @@ -1523,19 +1658,29 @@ "reset_password": "Ripristina password", "reset_people_visibility": "Ripristina visibilità persone", "reset_pin_code": "Resetta il codice PIN", + "reset_pin_code_description": "Se hai dimenticato il codice PIN, puoi contattare l’amministratore del server per reimpostarlo", + "reset_pin_code_success": "Codice PIN reimpostato con successo", + "reset_pin_code_with_password": "Puoi sempre reimpostare il codice PIN usando la tua password", + "reset_sqlite": "Resetta Database SQLite", + "reset_sqlite_confirmation": "Vuoi davvero reimpostare il database SQLite? Dovrai disconnetterti e riconnetterti per risincronizzare i dati", + "reset_sqlite_success": "Database SQLite reimpostato correttamente", "reset_to_default": "Ripristina i valori predefiniti", "resolve_duplicates": "Risolvi duplicati", "resolved_all_duplicates": "Tutti i duplicati sono stati risolti", "restore": "Ripristina", "restore_all": "Ripristina tutto", + "restore_trash_action_prompt": "{count} ripristinati dal cestino", "restore_user": "Ripristina utente", "restored_asset": "Asset ripristinato", "resume": "Riprendi", + "resume_paused_jobs": "Riprendi {count, plural, one {# processo in pausa} other {# i processi in pausa}}", "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", + "review_large_files": "Revisiona file pesanti", "role": "Ruolo", "role_editor": "Editor", "role_viewer": "Visualizzatore", + "running": "In esecuzione", "save": "Salva", "save_to_gallery": "Salva in galleria", "saved_api_key": "Chiave API salvata", @@ -1577,11 +1722,11 @@ "search_no_people": "Nessuna persona", "search_no_people_named": "Nessuna persona chiamate \"{name}\"", "search_no_result": "Nessun risultato trovato, prova con un termine o combinazione diversi", - "search_options": "Opzioni Ricerca", + "search_options": "Opzioni di ricerca", "search_page_categories": "Categoria", "search_page_motion_photos": "Foto in movimento", - "search_page_no_objects": "Nessuna informazione relativa all'oggetto disponibile", - "search_page_no_places": "Nessun informazione sul luogo disponibile", + "search_page_no_objects": "Nessuna informazione sugli oggetti disponibile", + "search_page_no_places": "Nessuna informazione sui luoghi disponibile", "search_page_screenshots": "Screenshot", "search_page_search_photos_videos": "Ricerca le tue foto e i tuoi video", "search_page_selfies": "Selfie", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Impossibile nel creare l'album", "selected": "Selezionato", "selected_count": "{count, plural, one {# selezionato} other {# selezionati}}", + "selected_gps_coordinates": "Coordinate GPS selezionate", "send_message": "Manda messaggio", "send_welcome_email": "Invia email di benvenuto", "server_endpoint": "Server endpoint", @@ -1641,7 +1787,7 @@ "set_slideshow_to_fullscreen": "Imposta presentazione a schermo intero", "set_stack_primary_asset": "Imposta come risorsa primaria", "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_subtitle": "Abilita il caricamento dell’immagine originale in alta risoluzione (dimensioni elevate). Disattiva per ridurre il consumo di dati, sia di rete che in cache locale.", "setting_image_viewer_original_title": "Carica l'immagine originale", "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", @@ -1657,7 +1803,7 @@ "setting_notifications_single_progress_subtitle": "Informazioni dettagliate sul caricamento della risorsa", "setting_notifications_single_progress_title": "Mostra avanzamento dettagliato del backup in background", "setting_notifications_subtitle": "Cambia le impostazioni di notifica", - "setting_notifications_total_progress_subtitle": "Progresso generale del caricamento (caricati / totali)", + "setting_notifications_total_progress_subtitle": "Avanzamento complessivo del caricamento (completati/risorse totali)", "setting_notifications_total_progress_title": "Mostra avanzamento del backup in background", "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "Quando riproduci un video dal server, riproduci l'originale anche se è disponibile una versione transcodificata. Questo potrebbe portare a buffering. I video disponibili localmente sono sempre riprodotti a qualità originale indipendentemente da questa impostazione.", @@ -1667,6 +1813,7 @@ "settings_saved": "Impostazioni salvate", "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", + "share_action_prompt": "Condivisi {count} elementi", "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparoâ€Ļ", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Copiato negli appunti", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Si è verificato un errore durante la creazione del link condiviso", + "shared_link_custom_url_description": "Accedi a questo link condiviso con un URL personalizzato", "shared_link_edit_description_hint": "Inserisci la descrizione della condivisione", "shared_link_edit_expire_after_option_day": "1 giorno", "shared_link_edit_expire_after_option_days": "{count} giorni", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", + "shared_link_password_description": "Imposta una password per questo link condiviso", "shared_links": "Link condivisi", "shared_links_description": "Condividi foto e video con un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video condivisi.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Mostra la transizione della presentazione", "show_supporter_badge": "Medaglia di Contributore", "show_supporter_badge_description": "Mostra la medaglia di contributore", + "show_text_search_menu": "Mostra il menu di ricerca del testo", "shuffle": "Casuale", "sidebar": "Barra laterale", "sidebar_display_description": "Visualizzare un link alla vista nella barra laterale", @@ -1762,19 +1912,22 @@ "sort_created": "Data creazione", "sort_items": "Numero di elementi", "sort_modified": "Data modifica", + "sort_newest": "Foto piÚ recente", "sort_oldest": "Foto piÚ vecchia", "sort_people_by_similarity": "Ordina persone per somiglianza", "sort_recent": "Foto piÚ recente", "sort_title": "Titolo", - "source": "Fonte", + "source": "Sorgente", "stack": "Raggruppa", + "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", - "stack_selected_photos": "Impila foto selezionate", + "stack_selected_photos": "Raggruppa foto selezionate", "stacked_assets_count": "{count, plural, one {Raggruppato # asset} other {Raggruppati # asset}}", "stacktrace": "Traccia dell'errore", "start": "Avvia", "start_date": "Data di inizio", + "start_date_before_end_date": "La data di inizio deve essere precedente alla data di fine", "state": "Provincia", "status": "Stato", "stop_casting": "Interrompi trasmissione", @@ -1787,6 +1940,7 @@ "storage_quota": "Limite Archiviazione", "storage_usage": "{used} di {available} utilizzati", "submit": "Invia", + "success": "Successo", "suggestions": "Suggerimenti", "sunrise_on_the_beach": "Tramonto sulla spiaggia", "support": "Supporto", @@ -1795,7 +1949,11 @@ "swap_merge_direction": "Scambia direzione di unione", "sync": "Sincronizza", "sync_albums": "Sincronizza album", - "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricati con gli album di backup selezionati", + "sync_local": "Sincronizza gli elementi locali", + "sync_remote": "Sincronizza gli elementi remoti", + "sync_status": "Stato di Sincronizzazione", + "sync_status_subtitle": "Visualizza e gestisci il sistema di sincronizzazione", "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag": "Tag", "tag_assets": "Tagga risorse", @@ -1806,6 +1964,7 @@ "tag_updated": "Tag {tag} aggiornata", "tagged_assets": "{count, plural, one {# asset etichettato} other {# asset etichettati}}", "tags": "Tag", + "tap_to_run_job": "Tocca per eseguire l'attività", "template": "Modello", "theme": "Tema", "theme_selection": "Selezione tema", @@ -1832,12 +1991,15 @@ "to_change_password": "Modifica password", "to_favorite": "Preferito", "to_login": "Accedi", + "to_multi_select": "per selezione multipla", "to_parent": "Sali di un livello", + "to_select": "per selezionare", "to_trash": "Cancella", "toggle_settings": "Attiva/disattiva impostazioni", "total": "Totale", "total_usage": "Utilizzo totale", "trash": "Cestino", + "trash_action_prompt": "{count} elementi spostati nel cestino", "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Seleziona elemento", "trash_page_title": "Cestino ({count})", "trashed_items_will_be_permanently_deleted_after": "Gli elementi cestinati saranno eliminati definitivamente dopo {days, plural, one {# giorno} other {# giorni}}.", + "troubleshoot": "Risoluzione dei problemi", "type": "Tipo", "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", + "unarchive_action_prompt": "{count} elementi rimossi dall'Archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", + "unfavorite_action_prompt": "{count} rimossi dai Favoriti", "unhide_person": "Mostra persona", "unknown": "Sconosciuto", "unknown_country": "Paese sconosciuto", @@ -1874,16 +2039,22 @@ "unselect_all": "Deseleziona tutto", "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unselect_all_in": "Deseleziona tutto in {group}", - "unstack": "Rimuovi dal gruppo", + "unstack": "Separa dal gruppo", + "unstack_action_prompt": "{count} separati", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", + "untagged": "Senza tag", "up_next": "Prossimo", + "update_location_action_prompt": "Aggiorna la posizione di {count} risorse selezionate con:", "updated_at": "Aggiornato il", "updated_password": "Password aggiornata", "upload": "Carica", + "upload_action_prompt": "{count} accodati per l'upload", "upload_concurrency": "Caricamenti contemporanei", + "upload_details": "Dettagli di caricamento", "upload_dialog_info": "Vuoi fare il backup sul server delle risorse selezionate?", "upload_dialog_title": "Carica file", "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli asset caricati.", + "upload_finished": "Upload terminato", "upload_progress": "Rimanenti {remaining, number} - Processati {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # asset duplicati}}", "upload_status_duplicates": "Duplicati", @@ -1892,6 +2063,7 @@ "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", "upload_to_immich": "Carica su Immich ({count})", "uploading": "Caricamento", + "uploading_media": "Caricando i media", "url": "URL", "usage": "Utilizzo", "use_biometric": "Usa biometrica", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Consulta le statistiche d'uso dell'account", "username": "Nome utente", "users": "Utenti", + "users_added_to_album_count": "Aggiunti {count, plural, one {# utente} other {# utenti}} all'album", "utilities": "Utilità", "validate": "Validazione", "validate_endpoint_error": "Inserisci un URL valido", @@ -1930,6 +2103,7 @@ "view_album": "Visualizza Album", "view_all": "Vedi tutto", "view_all_users": "Visualizza tutti gli utenti", + "view_details": "Visualizza Dettagli", "view_in_timeline": "Visualizza in timeline", "view_link": "Visualizza link", "view_links": "Visualizza i link", @@ -1937,11 +2111,12 @@ "view_next_asset": "Visualizza risorsa successiva", "view_previous_asset": "Visualizza risorsa precedente", "view_qr_code": "Visualizza Codice QR", + "view_similar_photos": "Visualizza le foto simili", "view_stack": "Visualizza Raggruppamento", "view_user": "Visualizza Utente", - "viewer_remove_from_stack": "Rimuovi dalla pila", + "viewer_remove_from_stack": "Rimuovi dal gruppo", "viewer_stack_use_as_main_asset": "Usa come risorsa principale", - "viewer_unstack": "Rimuovi dal gruppo", + "viewer_unstack": "Separa dal gruppo", "visibility_changed": "Visibilità modificata per {count, plural, one {# persona} other {# persone}}", "waiting": "In Attesa", "warning": "Attenzione", @@ -1952,8 +2127,9 @@ "wrong_pin_code": "Codice PIN errato", "year": "Anno", "years_ago": "{years, plural, one {# anno} other {# anni}} fa", - "yes": "Si", - "you_dont_have_any_shared_links": "Non è presente alcun link condiviso", + "yes": "SÃŦ", + "you_dont_have_any_shared_links": "Non hai nessun link condiviso", "your_wifi_name": "Nome della tua rete Wi-Fi", - "zoom_image": "Ingrandisci immagine" + "zoom_image": "Ingrandisci immagine", + "zoom_to_bounds": "Ingrandisci fino ai bordi" } diff --git a/i18n/ja.json b/i18n/ja.json index 1649f978d8..b03614d65c 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -14,6 +14,7 @@ "add_a_location": "場所をčŋŊ加", "add_a_name": "名前をčŋŊ加", "add_a_title": "ã‚ŋイトãƒĢをčŋŊ加", + "add_birthday": "čĒ•į”Ÿæ—Ĩã‚’č¨­åŽš", "add_endpoint": "ã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆã‚’čŋŊ加", "add_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’čŋŊ加", "add_import_path": "ã‚¤ãƒŗãƒãƒŧトパ゚をčŋŊ加", @@ -27,6 +28,9 @@ "add_to_album": "ã‚ĸãƒĢバムãĢčŋŊ加", "add_to_album_bottom_sheet_added": "{album}ãĢčŋŊ加", "add_to_album_bottom_sheet_already_exists": "{album}ãĢčŋŊ加済ãŋ", + "add_to_album_toggle": "{album}ぎ選択を切りæ›ŋえ", + "add_to_albums": "ã‚ĸãƒĢバムãĢčŋŊ加", + "add_to_albums_count": "{count}つぎã‚ĸãƒĢバムへčŋŊ加", "add_to_shared_album": "å…ąæœ‰ã‚ĸãƒĢバムãĢčŋŊ加", "add_url": "URLをčŋŊ加", "added_to_archive": "ã‚ĸãƒŧã‚ĢイブãĢしぞした", @@ -44,6 +48,13 @@ "backup_database": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップをäŊœæˆ", "backup_database_enable_description": "デãƒŧã‚ŋベãƒŧ゚バックã‚ĸップぎäŊœæˆã‚’有劚ãĢする", "backup_keep_last_amount": "過åŽģぎバックã‚ĸップぎäŋæŒæ•°", + "backup_onboarding_1_description": "クナã‚Ļãƒ‰ã‚’åˆŠį”¨ã—ãŸã‚Ēフã‚ĩã‚¤ãƒˆãŽã‚ŗãƒ”ãƒŧかåˆĨãŽį‰Šį†įš„ãĒ場所。", + "backup_onboarding_2_description": "åˆĨ々ぎデバイ゚上ぎロãƒŧã‚ĢãƒĢã‚ŗãƒ”ãƒŧã€‚ã“ã‚Œã¯ãƒĄã‚¤ãƒŗãƒ•ã‚Ąã‚¤ãƒĢやそぎロãƒŧã‚ĢãƒĢバックã‚ĸãƒƒãƒ—ãƒ•ã‚Ąã‚¤ãƒĢをåĢãŋぞす。", + "backup_onboarding_3_description": "あãĒたぎすずãĻぎデãƒŧã‚ŋ(1つぎã‚Ēフã‚ĩã‚¤ãƒˆã‚ŗãƒ”ãƒŧと2つぎロãƒŧã‚ĢãƒĢã‚ŗãƒ”ãƒŧをåĢむ)ãŽã‚ŗãƒ”ãƒŧ。", + "backup_onboarding_description": "デãƒŧã‚ŋäŋč­ˇãĢは3-2-1バックã‚ĸップæˆĻį•Ĩが推åĨ¨ã•れぞす。ã‚ĸップロãƒŧãƒ‰ã—ãŸå†™įœŸ/動į”ģãŽã‚ŗãƒ”ãƒŧãĢ加え、Immichデãƒŧã‚ŋベãƒŧã‚šãŽã‚ŗãƒ”ãƒŧもäŋæŒã™ã‚‹ã“とで包æ‹Ŧįš„ãĒバックã‚ĸップã‚ŊãƒĒãƒĨãƒŧã‚ˇãƒ§ãƒŗã‚’åŽŸįžã§ããžã™ã€‚", + "backup_onboarding_footer": "Immichぎバックã‚ĸップãĢé–ĸã™ã‚‹æƒ…å ąã¯ã€ãƒ‰ã‚­ãƒĨãƒĄãƒŗãƒ†ãƒŧã‚ˇãƒ§ãƒŗã‚’įĸēčĒã—ãĻください。", + "backup_onboarding_parts_title": "3-2-1バックã‚ĸップ:", + "backup_onboarding_title": "バックã‚ĸップ", "backup_settings": "デãƒŧã‚ŋベãƒŧ゚バックã‚ĸップäŊœæˆãŽč¨­åޚ", "backup_settings_description": "デãƒŧã‚ŋベãƒŧ゚ぎバックã‚ĸップäŊœæˆč¨­åŽšãŽįŽĄį† (こぎジョブはãƒĸニã‚ŋãƒĒãƒŗã‚°ã•ã‚Œãžã›ã‚“ã—ã€å¤ąæ•—ãŒį™ēį”Ÿã—ãĻもあãĒたãĢ通įŸĨãŒčĄŒãã“ã¨ã¯ã‚ã‚Šãžã›ã‚“)", "cleared_jobs": "{job}ぎジョブをクãƒĒã‚ĸしぞした", @@ -112,6 +123,13 @@ "logging_enable_description": "ログぎ有劚化", "logging_level_description": "有劚ãĒ場合ãĢäŊŋį”¨ã•ã‚Œã‚‹ãƒ­ã‚° ãƒŦベãƒĢ。", "logging_settings": "ログ", + "machine_learning_availability_checks": "å¯į”¨æ€§ãŽįĸēčĒ", + "machine_learning_availability_checks_description": "åˆŠį”¨å¯čƒŊãĒ抟æĸ°å­Ļįŋ’ぎã‚ĩãƒŧバãƒŧをč‡Ē動で検įŸĨしå„Ēå…ˆįš„ãĢäŊŋį”¨ã—ãžã™", + "machine_learning_availability_checks_enabled": "å¯į”¨æ€§ãƒã‚§ãƒƒã‚¯ã‚’æœ‰åŠšãĢする", + "machine_learning_availability_checks_interval": "チェックぎ間隔", + "machine_learning_availability_checks_interval_description": "å¯į”¨æ€§ãƒã‚§ãƒƒã‚¯ãŽé–“éš”īŧˆãƒŸãƒĒį§’å˜äŊīŧ‰", + "machine_learning_availability_checks_timeout": "ãƒĒクエ゚トã‚ŋイムã‚ĸã‚Ļト", + "machine_learning_availability_checks_timeout_description": "å¯į”¨æ€§ãƒã‚§ãƒƒã‚¯ãŽã‚ŋイムã‚ĸã‚Ļト時間īŧˆãƒŸãƒĒį§’å˜äŊīŧ‰", "machine_learning_clip_model": "ClipãƒĸデãƒĢ", "machine_learning_clip_model_description": "CLIP ãƒĸデãƒĢぎ名前はここãĢãƒĒ゚トされãĻいぞす。ãƒĸデãƒĢを変更した場合は、すずãĻãŽã‚¤ãƒĄãƒŧジãĢ寞しãĻ「゚マãƒŧト検į´ĸã€ã‚¸ãƒ§ãƒ–ã‚’å†åŽŸčĄŒã™ã‚‹åŋ…čĻãŒã‚ã‚Šãžã™ã€‚", "machine_learning_duplicate_detection": "é‡č¤‡æ¤œå‡ē", @@ -166,6 +184,20 @@ "metadata_settings_description": "ãƒĄã‚ŋデãƒŧã‚ŋãŽč¨­åŽšã‚’įŽĄį†ã—ãžã™", "migration_job": "マイグãƒŦãƒŧã‚ˇãƒ§ãƒŗ", "migration_job_description": "ã‚ĸã‚ģãƒƒãƒˆãŠã‚ˆãŗéĄ”ãŽã‚ĩムネイãƒĢを最新ぎフりãƒĢダ構造ãĢį§ģčĄŒã—ãžã™", + "nightly_tasks_cluster_faces_setting_description": "新しく検å‡ēã•ã‚ŒãŸéĄ”ãĢ寞しãĻ顔čĒč­˜ã‚’åŽŸčĄŒ", + "nightly_tasks_cluster_new_faces_setting": "新たãĒéĄ”ãŽã‚¯ãƒŠã‚šã‚ŋãƒŧ", + "nightly_tasks_database_cleanup_setting": "デãƒŧã‚ŋベãƒŧ゚ぎ掃除ã‚ŋ゚ク", + "nightly_tasks_database_cleanup_setting_description": "期限切れぎデãƒŧã‚ŋをデãƒŧã‚ŋベãƒŧ゚から削除", + "nightly_tasks_generate_memories_setting": "ãƒĄãƒĸãƒĒãƒŧã‚’į”Ÿæˆ", + "nightly_tasks_generate_memories_setting_description": "é …į›Žã‹ã‚‰æ–°ã—ã„ãƒĄãƒĸãƒĒãƒŧをäŊœæˆ", + "nightly_tasks_missing_thumbnails_setting": "æŦ ã‘ãĻいるã‚ĩムネイãƒĢã‚’į”Ÿæˆ", + "nightly_tasks_missing_thumbnails_setting_description": "ã‚ĩムネイãƒĢぎæŦ ã„ãĻã„ã‚‹é …į›Žã‚’ã‚ĩムネイãƒĢį”Ÿæˆã‚¸ãƒ§ãƒ–ãŽé †į•Ēåž…ãĄãĢする", + "nightly_tasks_settings": "æ¯Žæ™ŠčĄŒã†ã‚ŋã‚šã‚¯ãŽč¨­åŽš", + "nightly_tasks_settings_description": "æ¯Žæ™ŠčĄŒã†ã‚ŋã‚šã‚¯ãŽįŽĄį†", + "nightly_tasks_start_time_setting": "開始時間", + "nightly_tasks_start_time_setting_description": "こぎã‚ĩãƒŧバãƒŧãŒæ¯Žæ™ŠčĄŒã†ã‚ŋã‚šã‚¯ã‚’čĄŒã†æ™‚é–“", + "nightly_tasks_sync_quota_usage_setting": "剞åŊ“厚量ぎ同期", + "nightly_tasks_sync_quota_usage_setting_description": "ãƒĻãƒŧã‚ļãƒŧãŽįžåœ¨ãŽã‚šãƒˆãƒŦãƒŧジäŊŋᔍįŠļæŗãĢåŋœã˜ãĻ剞åŊ“厚量を更新", "no_paths_added": "パ゚がčŋŊ加されãĻいぞせん", "no_pattern_added": "パã‚ŋãƒŧãƒŗãŒčŋŊ加されãĻいぞせん", "note_apply_storage_label_previous_assets": "æŗ¨æ„: äģĨ前ãĢã‚ĸップロãƒŧドされたã‚ĸã‚ģットãĢ゚トãƒŦãƒŧジナベãƒĢã‚’éŠį”¨ã™ã‚‹ãĢは、äģĨä¸‹ã‚’åŽŸčĄŒã—ãĻください", @@ -196,6 +228,8 @@ "oauth_mobile_redirect_uri": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURI", "oauth_mobile_redirect_uri_override": "ãƒĸバイãƒĢᔍãƒĒダイãƒŦクトURIīŧˆä¸Šæ›¸ãīŧ‰", "oauth_mobile_redirect_uri_override_description": "''{callback}''ãĒお、ãƒĸバイãƒĢURIがOAuthプロバイダãƒŧãĢã‚ˆãŖãĻč¨ąå¯ã•ã‚ŒãĻいãĒい場合ãĢ有劚ãĢしãĻください", + "oauth_role_claim": "åŊščˇã‚’ä¸ģåŧĩする", + "oauth_role_claim_description": "įŽĄį†č€…ãĢãĒã‚‹ã¨į”ŗã—įĢ‹ãĻãŒčĄŒã‚ã‚ŒãŸå ´åˆã€č‡Ēå‹•įš„ãĢįŽĄį†č€…ã‚ĸクã‚ģ゚がそぎäēēãĢäģ˜ä¸Žã•れるようãĢしぞす。", "oauth_settings": "OAuth", "oauth_settings_description": "OAuthãƒ­ã‚°ã‚¤ãƒŗč¨­åŽšã‚’įŽĄį†ã—ãžã™", "oauth_settings_more_details": "こぎ抟čƒŊãŽčŠŗį´°ãĢついãĻは、ドキãƒĨãƒĄãƒŗãƒˆã‚’å‚į…§ã—ãĻください。", @@ -331,6 +365,9 @@ "trash_number_of_days_description": "ã‚ĸã‚ģットを厌全ãĢ削除する前ãĢごãŋįŽąãĢäŋįŽĄã—ãĻおくæ—Ĩ数", "trash_settings": "ごãŋįŽąãŽč¨­åŽš", "trash_settings_description": "ごãŋįŽąãŽč¨­åŽšã‚’įŽĄį†ã—ãžã™", + "unlink_all_oauth_accounts": "全ãĻぎOAuthã‚ĸã‚Ģã‚Ļãƒŗãƒˆã‚’ãƒĒãƒŗã‚¯č§Ŗé™¤", + "unlink_all_oauth_accounts_description": "新しいプロバイダãƒŧãĢį§ģčĄŒã™ã‚‹å‰ãĢ、すずãĻぎOAuthã‚ĸã‚Ģã‚ĻãƒŗãƒˆãŽãƒĒãƒŗã‚¯ã‚’č§Ŗé™¤ã—ãĻください。", + "unlink_all_oauth_accounts_prompt": "すずãĻぎOAuthã‚ĸã‚Ģã‚Ļãƒŗãƒˆã‚’č§Ŗé™¤ã—ãžã™ã‹īŧŸã“れãĢより、各ãƒĻãƒŧã‚ļãƒŧぎOAuth IDがãƒĒã‚ģットされ、元ãĢæˆģすことはできぞせん。", "user_cleanup_job": "ãƒĻãƒŧã‚ļãƒŧぎクãƒĒãƒŧãƒŗã‚ĸップ", "user_delete_delay": "{user}ぎã‚ĸã‚Ģã‚Ļãƒŗãƒˆã¨ã‚ĸã‚ģットは{delay, plural, one {#æ—Ĩ} other {#æ—Ĩ}}垌ãĢ厌全ãĢ削除されるようãĢäēˆåŽšã•ã‚Œãžã™ã€‚", "user_delete_delay_settings": "遅åģļ削除", @@ -360,10 +397,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "åˆĨぎåŸēæē–ãĢåž“ãŖãĻãƒĄãƒ‡ã‚Ŗã‚ĸãƒ•ã‚Ąã‚¤ãƒĢãĢãƒ•ã‚ŖãƒĢã‚ŋãƒŧをかけãĻã€åŒæœŸã‚’čĄŒã„ãžã™ã€‚ã‚ĸプãƒĒがすずãĻぎã‚ĸãƒĢバムをčĒ­ãŋčžŧんでくれãĒい場合ãĢぎãŋ、こぎ抟čƒŊをčŠĻしãĻください。", "advanced_settings_enable_alternate_media_filter_title": "[čŠĻ鍓運ᔍ] åˆĨぎデバイ゚ぎã‚ĸãƒĢãƒãƒ åŒæœŸãƒ•ã‚ŖãƒĢã‚ŋãƒŧをäŊŋį”¨ã™ã‚‹", "advanced_settings_log_level_title": "ログãƒŦベãƒĢ: {level}", - "advanced_settings_prefer_remote_subtitle": "デバイ゚ãĢã‚ˆãŖãĻは、デバイ゚上ãĢあるã‚ĩムネイãƒĢぎロãƒŧドãĢ非常ãĢ時間がかかることがありぞす。こぎã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’ãĢ有劚ãĢするäē‹ãĢより、ã‚ĩãƒŧバãƒŧã‹ã‚‰į›´æŽĨį”ģ像をロãƒŧドすることが可čƒŊです。", + "advanced_settings_prefer_remote_subtitle": "デバイ゚ãĢã‚ˆãŖãĻは、デバイ゚上ãĢあるã‚ĩムネイãƒĢぎロãƒŧドãĢ非常ãĢ時間がかかることがありぞす。こぎã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’æœ‰åŠšãĢするäē‹ãĢより、ã‚ĩãƒŧバãƒŧã‹ã‚‰į›´æŽĨį”ģ像をロãƒŧドすることが可čƒŊです。", "advanced_settings_prefer_remote_title": "ãƒĒãƒĸãƒŧトをå„Ē先する", "advanced_settings_proxy_headers_subtitle": "ãƒ—ãƒ­ã‚­ã‚ˇãƒ˜ãƒƒãƒ€ã‚’č¨­åŽšã™ã‚‹", "advanced_settings_proxy_headers_title": "ãƒ—ãƒ­ã‚­ã‚ˇãƒ˜ãƒƒãƒ€", + "advanced_settings_readonly_mode_subtitle": "čĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧドを有劚ãĢã™ã‚‹ã¨ã€å†™įœŸãŽč¤‡æ•°é¸æŠžã‚„ã€å…ąæœ‰ã€å‰Šé™¤ã€ã‚­ãƒŖã‚šãƒˆæŠŸčƒŊãŒį„ĄåŠšãĢãĒã‚Šãžã™ã€‚ãƒĄã‚¤ãƒŗã‚šã‚¯ãƒĒãƒŧãƒŗãŽãƒĻãƒŧã‚ļãƒŧã‚ĸバã‚ŋãƒŧから有劚īŧį„ĄåŠšã‚’åˆ‡ã‚Šæ›ŋえられぞす", + "advanced_settings_readonly_mode_title": "čĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧド", "advanced_settings_self_signed_ssl_subtitle": "SSLぎチェックを゚キップする。č‡ĒåˇąįŊ˛åč¨ŧ明書がåŋ…čĻã§ã™ã€‚", "advanced_settings_self_signed_ssl_title": "č‡ĒåˇąįŊ˛åč¨ŧæ˜Žæ›¸ã‚’č¨ąå¯ã™ã‚‹", "advanced_settings_sync_remote_deletions_subtitle": "Webでこぎ操äŊœã‚’čĄŒãŖãŸéš›ãĢ、č‡Ēå‹•įš„ãĢこぎデバイ゚上からåŊ“čОã‚ĸã‚ģットを削除ぞたは垊元する", @@ -379,6 +418,7 @@ "album_cover_updated": "ã‚ĸãƒĢバムã‚Ģバãƒŧ更新", "album_delete_confirmation": "ã‚ĸãƒĢバム{album}をæœŦåŊ“ãĢ削除しぞすか?", "album_delete_confirmation_description": "こぎã‚ĸãƒĢãƒãƒ ãŒå…ąæœ‰ã•ã‚ŒãĻいるãĒら、äģ–ぎãƒĻãƒŧã‚ļãƒŧもã‚ĸãƒĢバムãĢã‚ĸクã‚ģ゚できãĒくãĒりぞす。", + "album_deleted": "ã‚ĸãƒĢバムが削除されぞした", "album_info_card_backup_album_excluded": "除外中", "album_info_card_backup_album_included": "選択中", "album_info_updated": "ã‚ĸãƒĢãƒãƒ æƒ…å ąæ›´æ–°", @@ -388,7 +428,9 @@ "album_options": "ã‚ĸãƒĢãƒãƒ č¨­åŽš", "album_remove_user": "ãƒĻãƒŧã‚ļãƒŧを削除しぞすか?", "album_remove_user_confirmation": "æœŦåŊ“ãĢ{user}を削除しぞすか?", + "album_search_not_found": "検į´ĸãĢä¸€č‡´ã™ã‚‹ã‚ĸãƒĢバムがありぞせん", "album_share_no_users": "こぎã‚ĸãƒĢバムを全ãĻぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã—ãŸã‹ã€å…ąæœ‰ã™ã‚‹ãƒĻãƒŧã‚ļãƒŧがいãĒいようです。", + "album_summary": "ã‚ĸãƒĢバムぎぞとめ", "album_updated": "ã‚ĸãƒĢバム更新", "album_updated_setting_description": "å…ąæœ‰ã‚ĸãƒĢバムãĢ新しいã‚ĸã‚ģットがčŋŊ加されたとき通įŸĨを受け取る", "album_user_left": "{album} をåŽģりぞした", @@ -407,6 +449,7 @@ "albums_default_sort_order": "デフりãƒĢトぎã‚ĸãƒĢãƒãƒ čĄ¨į¤ē順", "albums_default_sort_order_description": "新čĻã‚ĸãƒĢバムäŊœæˆæ™‚ãŽåˆæœŸčĄ¨į¤ē順.", "albums_feature_description": "äģ–ぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã§ãã‚‹ã‚ĸã‚ģãƒƒãƒˆãŽã‚ŗãƒŦã‚¯ã‚ˇãƒ§ãƒŗ.", + "albums_on_device_count": "デバイ゚上ぎã‚ĸãƒĢバム ({count})", "all": "すずãĻ", "all_albums": "全ãĻぎã‚ĸãƒĢバム", "all_people": "全ãĻぎäēēį‰Š", @@ -426,7 +469,9 @@ "app_bar_signout_dialog_title": "ã‚ĩã‚¤ãƒŗã‚ĸã‚Ļト", "app_settings": "ã‚ĸプãƒĒč¨­åŽš", "appears_in": "これらãĢåĢぞれぞす", + "apply_count": "éŠį”¨ ({count, number})", "archive": "ã‚ĸãƒŧã‚Ģイブ", + "archive_action_prompt": "ã‚ĸãƒŧã‚ĢイブãĢ{count}é …į›ŽčŋŊ加しぞした", "archive_or_unarchive_photo": "å†™įœŸã‚’ã‚ĸãƒŧã‚Ģイブぞたはã‚ĸãƒŧã‚Ģã‚¤ãƒ–č§Ŗé™¤", "archive_page_no_archived_assets": "ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã—ãŸå†™įœŸãžãŸã¯ãƒ“ãƒ‡ã‚Ēがありぞせん", "archive_page_title": "ã‚ĸãƒŧã‚Ģイブ ({count})", @@ -457,6 +502,8 @@ "asset_restored_successfully": "垊元できぞした", "asset_skipped": "゚キップ済", "asset_skipped_in_trash": "ã‚´ãƒŸįŽąãŽä¸­", + "asset_trashed": "é …į›ŽãŒå‰Šé™¤ã•ã‚Œãžã—ãŸ", + "asset_troubleshoot": "é …į›Žã‚’ãƒˆãƒŠãƒ–ãƒĢã‚ˇãƒĨãƒŧã‡ŗ", "asset_uploaded": "ã‚ĸップロãƒŧド済", "asset_uploading": "ã‚ĸップロãƒŧド中â€Ļ", "asset_viewer_settings_subtitle": "ã‚ŽãƒŖãƒŠãƒĒãƒŧビãƒĨãƒŧã‚ĸãƒŧãĢé–ĸã™ã‚‹č¨­åŽš", @@ -464,8 +511,9 @@ "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 {{name}} other {新しいã‚ĸãƒĢバム}}ãĢčŋŊ加しぞした", + "assets_added_to_albums_count": "{assetTotal, plural, one {# é …į›Ž} other {# é …į›Ž}}を{albumTotal, plural, one {# つぎã‚ĸãƒĢバム} other {# つぎã‚ĸãƒĢバム}}ãĢčŋŊ加しぞした", "assets_cannot_be_added_to_album_count": "{count, plural, one {ã‚ĸã‚ģット} other {ã‚ĸã‚ģット}} はã‚ĸãƒĢバムãĢčŋŊ加できぞせん", + "assets_cannot_be_added_to_albums": "{count, plural, one {é …į›Ž} other {é …į›Ž}} をおぎã‚ĸãƒĢバムãĢもčŋŊ加できぞせんでした", "assets_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģット", "assets_deleted_permanently": "{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", "assets_deleted_permanently_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{count}é …į›Žã‚’åŽŒå…¨ãĢ削除しぞした", @@ -482,20 +530,25 @@ "assets_trashed_count": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットをごãŋįŽąãĢį§ģ動しぞした", "assets_trashed_from_server": "ã‚ĩãƒŧバãƒŧ上ぎ{count}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "assets_were_part_of_album_count": "{count, plural, one {個} other {個}}ぎã‚ĸã‚ģットはæ—ĸãĢã‚ĸãƒĢバムぎ一部です", + "assets_were_part_of_albums_count": "{count, plural, one {é …į›Žã¯} other {é …į›Žã¯}}すでãĢã‚ĸãƒĢバムãĢčŋŊ加されãĻいるもぎでした", "authorized_devices": "čĒå¯æ¸ˆãŋデバイ゚", "automatic_endpoint_switching_subtitle": "指厚されたWi-FiãĢæŽĨį™‚ぎãŋロãƒŧã‚ĢãƒĢæŽĨįļšã‚’čĄŒã„ã€äģ–ぎネットワãƒŧク下では通常通りぎæŽĨįļšã‚’čĄŒã„ãžã™", "automatic_endpoint_switching_title": "č‡Ē動URL切りæ›ŋえ", "autoplay_slideshow": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧをč‡Ēå‹•å†į”Ÿ", "back": "æˆģる", "back_close_deselect": "æˆģã‚‹ã€é–‰ã˜ã‚‹ã€é¸æŠžč§Ŗé™¤", + "background_backup_running_error": "バックグナã‚Ļãƒŗãƒ‰ãŽãƒãƒƒã‚¯ã‚ĸップがすでãĢčĄŒã‚ã‚ŒãĻいる最中です。そぎため、マニãƒĨã‚ĸãƒĢでぎバックã‚ĸップを開始することはできぞせん。", "background_location_permission": "バックグナã‚Ļãƒŗãƒ‰äŊįŊŽæƒ…å ąã‚ĸクã‚ģ゚", "background_location_permission_content": "æ­Ŗå¸¸ãĢWi-Fiぎ名前(SSID)ã‚’į˛åž—ã™ã‚‹ãĢはã‚ĸプãƒĒが常ãĢčŠŗį´°ãĒäŊįŊŽæƒ…å ąãĢã‚ĸクã‚ģ゚できるåŋ…čĻãŒã‚ã‚Šãžã™", + "background_options": "バックグナã‚Ļãƒŗãƒ‰ãŽå‹•äŊœã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", + "backup": "バックã‚ĸップ", "backup_album_selection_page_albums_device": "デバイ゚上ぎã‚ĸãƒĢバム({count})", "backup_album_selection_page_albums_tap": "ã‚ŋップで選択、ダブãƒĢã‚ŋップで除外", - "backup_album_selection_page_assets_scatter": "ã‚ĸãƒĢバムを選択ãƒģ除外しãĻバックã‚ĸãƒƒãƒ—ã™ã‚‹å†™įœŸã‚’é¸ãļ (åŒã˜å†™įœŸãŒč¤‡æ•°ãŽã‚ĸãƒĢバムãĢį™ģéŒ˛ã•ã‚ŒãĻいることがあるため)", + "backup_album_selection_page_assets_scatter": "ã‚ĸãƒĢバムを選択ãƒģ除外しãĻバックã‚ĸãƒƒãƒ—ã™ã‚‹å†™įœŸã‚’é¸ãļ。 (åŒã˜å†™įœŸãŒč¤‡æ•°ãŽã‚ĸãƒĢバムãĢį™ģéŒ˛ã•ã‚ŒãĻいることがあるため)", "backup_album_selection_page_select_albums": "ã‚ĸãƒĢバムを選択", "backup_album_selection_page_selection_info": "選択ãƒģ除外中ぎã‚ĸãƒĢバム", "backup_album_selection_page_total_assets": "選択されたã‚ĸãƒĢãƒãƒ ãŽå†™įœŸã¨å‹•į”ģぎ数", + "backup_albums_sync": "ã‚ĸãƒĢバム同期įŠļ態をバックã‚ĸップ", "backup_all": "すずãĻ", "backup_background_service_backup_failed_message": "ã‚ĸップロãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸã€‚ãƒĒトナイ中â€Ļ", "backup_background_service_connection_failed_message": "ã‚ĩãƒŧバãƒŧãĢæŽĨįļšã§ããžã›ã‚“。ãƒĒトナイ中â€Ļ", @@ -550,8 +603,10 @@ "backup_manual_in_progress": "ã‚ĸップロãƒŧãƒ‰ãŒé€˛čĄŒä¸­ã§ã™ã€‚åžŒã§ã‚‚ã†ä¸€åēĻčŠĻしãĻください", "backup_manual_success": "成功", "backup_manual_title": "ã‚ĸップロãƒŧドįŠļæŗ", + "backup_options": "バックã‚ĸップã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "backup_options_page_title": "バックã‚ĸップã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "backup_setting_subtitle": "ã‚ĸップロãƒŧドãĢé–ĸã™ã‚‹č¨­åŽš", + "backup_settings_subtitle": "ã‚ĸップロãƒŧãƒ‰č¨­åŽšã‚’įŽĄį†", "backward": "新しい斚へ", "biometric_auth_enabled": "į”ŸäŊ“čĒč¨ŧを有劚化しぞした", "biometric_locked_out": "į”ŸäŊ“čĒč¨ŧãĢより、ã‚ĸクã‚ģ゚できぞせん", @@ -587,6 +642,7 @@ "cancel": "ã‚­ãƒŖãƒŗã‚ģãƒĢ", "cancel_search": "検į´ĸã‚’ã‚­ãƒŖãƒŗã‚ģãƒĢ", "canceled": "ã‚­ãƒŖãƒŗã‚ģãƒĢされぞした", + "canceling": "ã‚­ãƒŖãƒŗã‚ģãƒĢ中", "cannot_merge_people": "äēēį‰Šã‚’įĩąåˆã§ããžã›ã‚“", "cannot_undo_this_action": "こぎ操äŊœã¯å…ƒãĢæˆģせぞせんīŧ", "cannot_update_the_description": "čĒŦ明を更新できぞせん", @@ -609,6 +665,8 @@ "change_pin_code": "PINã‚ŗãƒŧドを変更", "change_your_password": "パ゚ワãƒŧドを変更しぞす", "changed_visibility_successfully": "非表į¤ēč¨­åŽšã‚’æ­Ŗå¸¸ãĢ変更しぞした", + "charging": "充é›ģ中", + "charging_requirement_mobile_backup": "バックグナã‚Ļãƒŗãƒ‰ã§ãŽãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—ã‚’čĄŒã†ãŸã‚ãĢは、デバイ゚が充é›ģ中であるåŋ…čĻãŒã‚ã‚Šãžã™", "check_corrupt_asset_backup": "į ´æã•ã‚ŒãĻã„ã‚‹é …į›Žã‚’æŽĸす", "check_corrupt_asset_backup_button": "ãƒã‚§ãƒƒã‚¯ã‚’čĄŒã†", "check_corrupt_asset_backup_description": "å†™įœŸã‚„å‹•į”ģãĒおが全ãĻã‚ĸップロãƒŧドしįĩ‚えãĻからWi-FiãĢæŽĨį™‚ぎãŋãƒã‚§ãƒƒã‚¯ã‚’čĄŒãĒãŖãĻください。äŊœæĨ­ãŒåތäē†ã™ã‚‹ãĢは数分かかる場合がありぞす", @@ -618,6 +676,7 @@ "clear": "クãƒĒã‚ĸ", "clear_all": "全ãĻクãƒĒã‚ĸ", "clear_all_recent_searches": "全ãĻぎ最čŋ‘ぎ検į´ĸをクãƒĒã‚ĸ", + "clear_file_cache": "ãƒ•ã‚Ąã‚¤ãƒĢã‚­ãƒŖãƒƒã‚ˇãƒĨを削除", "clear_message": "ãƒĄãƒƒã‚ģãƒŧジをクãƒĒã‚ĸ", "clear_value": "値をクãƒĒã‚ĸ", "client_cert_dialog_msg_confirm": "äē†č§Ŗ", @@ -661,7 +720,7 @@ "control_bottom_app_bar_edit_location": "äŊįŊŽæƒ…å ąã‚’įˇ¨é›†", "control_bottom_app_bar_edit_time": "æ’ŽåŊąæ—Ĩæ™‚ã‚’įˇ¨é›†", "control_bottom_app_bar_share_link": "å…ąæœ‰ãƒĒãƒŗã‚¯", - "control_bottom_app_bar_share_to": "æŦĄãŽãƒĻãƒŧã‚ļãƒŧãĢå…ąæœ‰: ", + "control_bottom_app_bar_share_to": "æŦĄãŽãƒĻãƒŧã‚ļãƒŧãĢå…ąæœ‰:", "control_bottom_app_bar_trash_from_immich": "ã‚´ãƒŸįŽąãĢå…Ĩれる", "copied_image_to_clipboard": "į”ģ像をクãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧしぞした。", "copied_to_clipboard": "クãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧしぞした!", @@ -688,11 +747,13 @@ "create_new_user": "新čĻãƒĻãƒŧã‚ļãƒŧぎäŊœæˆ", "create_shared_album_page_share_add_assets": "å†™įœŸã‚’čŋŊ加", "create_shared_album_page_share_select_photos": "å†™įœŸã‚’é¸æŠž", + "create_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’äŊœæˆ", "create_tag": "ã‚ŋグをäŊœæˆã™ã‚‹", "create_tag_description": "ã‚ŋグをäŊœæˆã—ぞす。å…Ĩれ子構造ぎã‚ŋã‚°ã¯ã€ã¯ã˜ã‚ãŽã‚šãƒŠãƒƒã‚ˇãƒĨをåĢめた、ã‚ŋグぎ厌全ãĒパ゚をå…Ĩ力しãĻください。", "create_user": "ãƒĻãƒŧã‚ļãƒŧをäŊœæˆ", "created": "äŊœæˆ", "created_at": "äŊœæˆ:", + "creating_linked_albums": "ãƒĒãƒŗã‚¯ã•ã‚ŒãŸã‚ĸãƒĢバムをäŊœæˆä¸­ãƒģãƒģãƒģ", "crop": "クロップ", "curated_object_page_title": "čĸĢ写äŊ“", "current_device": "įžåœ¨ãŽãƒ‡ãƒã‚¤ã‚š", @@ -700,10 +761,11 @@ "current_server_address": "įžåœ¨ãŽã‚ĩãƒŧバãƒŧURL", "custom_locale": "ã‚Ģ゚ã‚ŋãƒ ãƒ­ã‚ąãƒŧãƒĢ", "custom_locale_description": "言čĒžã¨åœ°åŸŸãĢåŸēãĨいãĻæ—Ĩäģ˜ã¨æ•°å€¤ã‚’フりãƒŧマットしぞす", + "custom_url": "ã‚Ģ゚ã‚ŋムURL", "daily_title_text_date": "MM DD, EE", "daily_title_text_date_year": "yyyy MM DD, EE", "dark": "ダãƒŧクãƒĸãƒŧド", - "darkTheme": "ダãƒŧクãƒĸãƒŧドを切りæ›ŋえ", + "dark_theme": "ダãƒŧクãƒĸãƒŧド切りæ›ŋえ", "date_after": "こぎæ—ĨäģĨ降", "date_and_time": "æ—Ĩäģ˜ã¨æ™‚é–“", "date_before": "こぎæ—ĨäģĨ前", @@ -711,6 +773,7 @@ "date_of_birth_saved": "į”Ÿåš´æœˆæ—Ĩã¯æ­Ŗå¸¸ãĢäŋå­˜ã•れぞした", "date_range": "æ—Ĩäģ˜", "day": "ナイトãƒĸãƒŧド", + "days": "æ—Ĩ", "deduplicate_all": "全ãĻé‡č¤‡æŽ’é™¤", "deduplication_criteria_1": "バイト単äŊãŽį”ģ像ã‚ĩイã‚ē", "deduplication_criteria_2": "EXIFデãƒŧã‚ŋ数", @@ -719,6 +782,8 @@ "default_locale": "デフりãƒĢãƒˆãŽãƒ­ã‚ąãƒŧãƒĢ", "default_locale_description": "ブナã‚Ļã‚ļãŽãƒ­ã‚ąãƒŧãƒĢãĢåŸēãĨいãĻæ—Ĩäģ˜ã¨æ•°å€¤ã‚’フりãƒŧマットしぞす", "delete": "削除", + "delete_action_confirmation_message": "ã“ãŽé …į›Žã‚’å‰Šé™¤ã—ãžã™ã‹īŧŸãžãšã€ã“ãŽé …į›Žã¯ã‚ĩãƒŧバãƒŧä¸ŠãŽã‚´ãƒŸįŽąã¸į§ģ動されぞす。そぎ垌、あãĒたぎデバイ゚上から削除するかをæąēめãĻいただきぞす", + "delete_action_prompt": "{count}é …į›Žã‚’å‰Šé™¤ã—ãžã—ãŸ", "delete_album": "ã‚ĸãƒĢバムを削除", "delete_api_key_prompt": "æœŦåŊ“ãĢこぎAPI キãƒŧを削除しぞすか?", "delete_dialog_alert": "ã‚ĩãƒŧバãƒŧã¨ãƒ‡ãƒã‚¤ã‚šãŽä¸Ąæ–šã‹ã‚‰åŽŒå…¨ãĢ削除されぞす", @@ -732,9 +797,12 @@ "delete_key": "キãƒŧを削除", "delete_library": "ナイブナãƒĒを削除", "delete_link": "ãƒĒãƒŗã‚¯ã‚’å‰Šé™¤", + "delete_local_action_prompt": "{count}é …į›Žã‚’ãƒ‡ãƒã‚¤ã‚šã‹ã‚‰å‰Šé™¤ã—ãžã—ãŸ", "delete_local_dialog_ok_backed_up_only": "バックã‚ĸップ済ãŋぎãŋを削除", "delete_local_dialog_ok_force": "削除しぞす", "delete_others": "ãģかを削除", + "delete_permanently": "厌全ãĢ削除", + "delete_permanently_action_prompt": "{count}é …į›ŽãŒåŽŒå…¨ãĢ削除されぞした", "delete_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’æļˆã™", "delete_shared_link_dialog_title": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‚’æļˆã™", "delete_tag": "ã‚ŋグを削除する", @@ -745,6 +813,7 @@ "description": "æĻ‚čρæŦ„", "description_input_hint_text": "čĒŦ明をčŋŊ加", "description_input_submit_error": "čĒŦæ˜ŽãŽįˇ¨é›†ãĢå¤ąæ•—ã—ãžã—ãŸã€‚čŠŗį´°ã¯ãƒ­ã‚°ã‚’įĸēčĒã—ãĻください。", + "deselect_all": "すずãĻãŽé¸æŠžã‚’č§Ŗé™¤", "details": "čŠŗį´°", "direction": "斚向", "disabled": "į„ĄåŠš", @@ -762,6 +831,7 @@ "documentation": "ドキãƒĨãƒĄãƒŗãƒˆ", "done": "厌äē†", "download": "ダã‚Ļãƒŗãƒ­ãƒŧド", + "download_action_prompt": "{count}é …į›Žã‚’ãƒ€ã‚Ļãƒŗãƒ­ãƒŧド中", "download_canceled": "ダã‚Ļãƒŗãƒ­ãƒŧãƒ‰ãŒã‚­ãƒŖãƒŗã‚ģãƒĢされぞした", "download_complete": "ダã‚Ļãƒŗãƒ­ãƒŧド厌äē†", "download_enqueue": "ダã‚Ļãƒŗãƒ­ãƒŧド垅抟中", @@ -788,8 +858,12 @@ "edit": "ᎍ集", "edit_album": "ã‚ĸãƒĢãƒãƒ ã‚’įˇ¨é›†", "edit_avatar": "ã‚ĸバã‚ŋãƒŧã‚’įˇ¨é›†", + "edit_birthday": "čĒ•į”Ÿæ—Ĩã‚’įˇ¨é›†ã™ã‚‹", "edit_date": "æ—Ĩäģ˜ã‚’ᎍ集", "edit_date_and_time": "æ’ŽåŊąæ—Ĩæ™‚ã‚’įˇ¨é›†", + "edit_date_and_time_action_prompt": "{count} 個ぎã‚ĸイテムぎæ—Ĩæ™‚ãŒįˇ¨é›†ã•ã‚Œãžã—ãŸ", + "edit_date_and_time_by_offset": "ã‚Ēフã‚ģãƒƒãƒˆã‚’į”¨ã„ãĻæ—Ĩäģ˜ã‚’変更", + "edit_date_and_time_by_offset_interval": "新しいæ—Ĩäģ˜į¯„回: {from} - {to}", "edit_description": "čĒŦæ˜Žæ–‡ã‚’įˇ¨é›†", "edit_description_prompt": "新しいčĒŦ明文を選んでください:", "edit_exclusion_pattern": "除外パã‚ŋãƒŧãƒŗã‚’įˇ¨é›†", @@ -799,6 +873,7 @@ "edit_key": "キãƒŧã‚’įˇ¨é›†", "edit_link": "ãƒĒãƒŗã‚¯ã‚’įˇ¨é›†ã™ã‚‹", "edit_location": "äŊįŊŽæƒ…å ąã‚’įˇ¨é›†", + "edit_location_action_prompt": "{count}é …į›ŽãŽäŊįŊŽæƒ…å ąã‚’å¤‰æ›´ã—ãžã—ãŸ", "edit_location_dialog_title": "äŊįŊŽæƒ…å ą", "edit_name": "名前を変更", "edit_people": "äēēį‰Šã‚’įˇ¨é›†", @@ -817,6 +892,7 @@ "empty_trash": "ã‚´ãƒŸįŽąã‚’įŠēãĢする", "empty_trash_confirmation": "æœŦåŊ“ãĢã‚´ãƒŸįŽąã‚’įŠēãĢしぞすか? これãĢã‚ˆã‚Šã€ã‚´ãƒŸįŽąå†…ãŽã™ãšãĻぎã‚ĸã‚ģットが Immich から永䚅ãĢ削除されぞす。\nこぎ操äŊœã‚’å…ƒãĢæˆģすことはできぞせん!", "enable": "有劚化", + "enable_backup": "バックã‚ĸップを有劚化", "enable_biometric_auth_description": "į”ŸäŊ“čĒč¨ŧを有劚化するためãĢ、PINã‚ŗãƒŧドをå…Ĩ力しãĻください", "enabled": "有劚", "end_date": "įĩ‚ä熿—Ĩ", @@ -827,7 +903,9 @@ "error": "エナãƒŧ", "error_change_sort_album": "ã‚ĸãƒĢãƒãƒ ãŽčĄ¨į¤ē順ぎ変更ãĢå¤ąæ•—ã—ãžã—ãŸ", "error_delete_face": "ã‚ĸã‚ģãƒƒãƒˆã‹ã‚‰éĄ”ãŽå‰Šé™¤ãŒã§ããžã›ã‚“ã§ã—ãŸ", + "error_getting_places": "場所ぎ取垗ãĢå¤ąæ•—ã—ãžã—ãŸ", "error_loading_image": "į”ģ像ぎčĒ­ãŋčžŧãŋエナãƒŧ", + "error_loading_partners": "パãƒŧトナãƒŧぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—ã—ãžã—ãŸ: {error}", "error_saving_image": "エナãƒŧ: {error}", "error_tag_face_bounding_box": "éĄ”ãŽį™ģ錞ãĢå¤ąæ•—ã—ãžã—ãŸ - éĄ”ã‚’å›˛ã‚€å››č§’åŊĸぎåē§æ¨™å–åž—ãĢå¤ąæ•—", "error_title": "エナãƒŧ - å•éĄŒãŒį™ēį”Ÿã—ãžã—ãŸ", @@ -860,6 +938,7 @@ "failed_to_load_notifications": "通įŸĨぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_load_people": "äēēį‰Šã‚’čĒ­ãŋčžŧめぞせんでした", "failed_to_remove_product_key": "プロダクトキãƒŧを削除できぞせんでした", + "failed_to_reset_pin_code": "PINã‚ŗãƒŧドぎãƒĒã‚ģットãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_stack_assets": "ã‚ĸã‚ģットを゚ã‚ŋックできぞせんでした", "failed_to_unstack_assets": "ã‚ĸã‚ģットを゚ã‚ŋãƒƒã‚¯ã‹ã‚‰č§Ŗé™¤ã™ã‚‹ã“ã¨ãŒã§ããžã›ã‚“ã§ã—ãŸ", "failed_to_update_notification_status": "通įŸĨ゚テãƒŧã‚ŋ゚ぎ更新ãĢå¤ąæ•—ã—ãžã—ãŸ", @@ -868,6 +947,7 @@ "paths_validation_failed": "{paths, plural, one {#個} other {#個}}ぎパ゚ぎ検č¨ŧãĢå¤ąæ•—ã—ãžã—ãŸ", "profile_picture_transparent_pixels": "ãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢå†™įœŸãĢは透明ピクã‚ģãƒĢをåĢめることはできぞせん。į”ģåƒã‚’æ‹Ąå¤§/į¸Žå°ã—ãŸã‚Šį§ģ動しãĻください。", "quota_higher_than_disk_size": "ãƒ‡ã‚Ŗã‚šã‚¯åŽšé‡ã‚ˆã‚Šå¤§ãã„åŽšé‡ãŒæŒ‡åŽšã•ã‚Œãžã—ãŸ", + "something_went_wrong": "å•éĄŒãŒį™ēį”Ÿã—ãžã—ãŸ", "unable_to_add_album_users": "ãƒĻãƒŧã‚ļãƒŧをã‚ĸãƒĢバムãĢčŋŊ加できぞせん", "unable_to_add_assets_to_shared_link": "ã‚ĸã‚ģãƒƒãƒˆã‚’å…ąæœ‰ãƒĒãƒŗã‚¯ãĢčŋŊ加できぞせん", "unable_to_add_comment": "ã‚ŗãƒĄãƒŗãƒˆã‚’čŋŊ加できぞせん", @@ -953,13 +1033,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "čĒŦ明をčŋŊ加", + "exif_bottom_sheet_description_error": "čĒŦ明文をã‚ĸップデãƒŧトできぞせんでした", "exif_bottom_sheet_details": "čŠŗį´°", "exif_bottom_sheet_location": "æ’ŽåŊąå ´æ‰€", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "名前をčŋŊ加", - "exif_bottom_sheet_person_age_months": "į”ŸåžŒ{months}ãƒļ月", - "exif_bottom_sheet_person_age_year_months": "1æ­ŗ{months}ãƒļ月", - "exif_bottom_sheet_person_age_years": "{years}æ­ŗ", "exit_slideshow": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧをįĩ‚わる", "expand_all": "全ãĻåą•é–‹", "experimental_settings_new_asset_list_subtitle": "čŖŊäŊœé€”中 (WIP)", @@ -973,6 +1051,8 @@ "explorer": "æŽĸį´ĸ", "export": "エク゚ポãƒŧト", "export_as_json": "JSONとしãĻエク゚ポãƒŧト", + "export_database": "デãƒŧã‚ŋベãƒŧ゚をå‡ē力", + "export_database_description": "SQLiteデãƒŧã‚ŋベãƒŧ゚をå‡ē力", "extension": "æ‹Ąåŧĩ子", "external": "外部", "external_libraries": "外部ナイブナãƒĒ", @@ -984,11 +1064,13 @@ "failed_to_load_assets": "ã‚ĸã‚ģットぎロãƒŧドãĢå¤ąæ•—ã—ãžã—ãŸ", "failed_to_load_folder": "フりãƒĢダãƒŧぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—", "favorite": "お気ãĢå…Ĩり", + "favorite_action_prompt": "{count}é …į›Žã‚’ãŠæ°—ãĢå…ĨりãĢčŋŊ加しぞした", "favorite_or_unfavorite_photo": "å†™įœŸã‚’ãŠæ°—ãĢいりãĢį™ģéŒ˛ãžãŸã¯č§Ŗé™¤", "favorites": "お気ãĢå…Ĩり", "favorites_page_no_favorites": "お気ãĢå…Ĩりį™ģéŒ˛ã•ã‚ŒãŸé …į›ŽãŒã‚ã‚Šãžã›ã‚“", "feature_photo_updated": "äēēį‰Šį”ģ像が更新されぞした", "features": "抟čƒŊ", + "features_in_development": "開į™ē中ぎ抟čƒŊ", "features_setting_description": "ã‚ĸプãƒĒぎ抟čƒŊã‚’įŽĄį†ã™ã‚‹", "file_name": "ãƒ•ã‚Ąã‚¤ãƒĢ名", "file_name_or_extension": "ãƒ•ã‚Ąã‚¤ãƒĢåãžãŸã¯æ‹Ąåŧĩ子", @@ -998,21 +1080,26 @@ "filter_people": "äēēį‰Šã‚’įĩžã‚Ščžŧãŋ", "filter_places": "å ´æ‰€ã‚’ãƒ•ã‚ŖãƒĢã‚ŋãƒŧ", "find_them_fast": "名前で検į´ĸしãĻį´ æ—Šãį™ēčĻ‹", + "first": "はじめ", "fix_incorrect_match": "é–“é•ãŖãŸä¸€č‡´ã‚’äŋŽæ­Ŗ", "folder": "フりãƒĢダãƒŧ", "folder_not_found": "フりãƒĢダãƒŧがčĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸ", "folders": "フりãƒĢダ", "folders_feature_description": "ãƒ•ã‚Ąã‚¤ãƒĢã‚ˇã‚šãƒ†ãƒ ä¸ŠãŽå†™įœŸã¨å‹•į”ģぎフりãƒĢダビãƒĨãƒŧã‚’é–˛čĻ§ã™ã‚‹", + "forgot_pin_code_question": "PINをåŋ˜ã‚Œãžã—たか?", "forward": "前へ", "gcast_enabled": "Google Cast", "gcast_enabled_description": "こぎ抟čƒŊは動äŊœãŽãŸã‚ãĢGoogleぎãƒĒã‚Ŋãƒŧ゚をčĒ­ãŋčžŧãŋぞす。", "general": "一čˆŦ", + "geolocation_instruction_location": "äŊįŊŽæƒ…å ąäģ˜ããŽé …į›Žã‚’ã‚¯ãƒĒックしãĻ、そぎäŊįŊŽæƒ…å ąã‚’åˆŠį”¨ã—ãžã™ã€‚ã‚ã‚‹ã„ã¯ã€åœ°å›ŗä¸ŠãŽåœ°į‚šã‚’į›´æŽĨ選ãļことも可čƒŊです", "get_help": "åŠŠã‘ã‚’æą‚ã‚ã‚‹", "get_wifiname_error": "Wi-Fiぎ名前(SSID)がå…Ĩ手できぞせんでした。Wi-FiãĢįš‹ãŒãŖãĻるぎとåŋ…čρãĒæ¨Šé™ã‚’č¨ąå¯ã—ãŸã‹įĸēčĒã—ãĻください", "getting_started": "はじめる", "go_back": "æˆģる", "go_to_folder": "フりãƒĢダへ", "go_to_search": "検į´ĸへ", + "gps": "GPS", + "gps_missing": "GPSį„Ą", "grant_permission": "č¨ąå¯ã™ã‚‹", "group_albums_by": "これでã‚ĸãƒĢバムをグãƒĢãƒŧプ化â€Ļ", "group_country": "å›ŊでグãƒĢãƒŧプ化", @@ -1023,6 +1110,9 @@ "haptic_feedback_switch": "ãƒãƒ—ãƒ†ã‚Ŗãƒƒã‚¯ãƒ•ã‚Ŗãƒŧドバック", "haptic_feedback_title": "ãƒãƒ—ãƒ†ã‚Ŗãƒƒã‚¯ãƒ•ã‚Ŗãƒŧドバックを有劚ãĢする", "has_quota": "クりãƒŧã‚ŋ有り", + "hash_asset": "é …į›Žã‚’ãƒãƒƒã‚ˇãƒĨ化する", + "hashed_assets": "ãƒãƒƒã‚ˇãƒĨåŒ–ã•ã‚ŒãŸé …į›Ž", + "hashing": "ãƒãƒƒã‚ˇãƒĨ化", "header_settings_add_header_tip": "ヘッダをčŋŊ加", "header_settings_field_validator_msg": "ヘッダをįŠēį™ŊãĢはできぞせん", "header_settings_header_name_input": "ヘッダぎ名前", @@ -1054,7 +1144,9 @@ "home_page_upload_err_limit": "1回でã‚ĸップロãƒŧãƒ‰ã§ãã‚‹å†™įœŸãŽæ•°ã¯30枚です。゚キップしぞす", "host": "ポト", "hour": "時間", + "hours": "時間", "id": "ID", + "idle": "ã‚ĸイドãƒĒãƒŗã‚°", "ignore_icloud_photos": "iCloudä¸ŠãŽå†™įœŸã‚’ã‚šã‚­ãƒƒãƒ—", "ignore_icloud_photos_description": "iCloudãĢäŋå­˜æ¸ˆãŋãŽé …į›Žã‚’Immichã‚ĩãƒŧバãƒŧ上ãĢã‚ĸップロãƒŧドしぞせん", "image": "å†™įœŸ", @@ -1112,10 +1204,13 @@ "language_no_results_title": "言čĒžãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", "language_search_hint": "言čĒžã‚’æ¤œį´ĸ", "language_setting_description": "å„Ēå…ˆč¨€čĒžã‚’é¸æŠžã—ãĻください", + "large_files": "大きいã‚ĩイã‚ēãŽãƒ•ã‚Ąã‚¤ãƒĢ", + "last": "最垌", "last_seen": "最新ぎæ´ģ動", "latest_version": "最新バãƒŧã‚¸ãƒ§ãƒŗ", "latitude": "᎝åēĻ", "leave": "退å‡ē", + "leave_album": "ã‚ĸãƒĢバムから抜ける", "lens_model": "ãƒŦãƒŗã‚ēãƒĸデãƒĢ", "let_others_respond": "äģ–ぎãƒĻãƒŧã‚ļãƒŧぎčŋ”äŋĄã‚’č¨ąå¯ã™ã‚‹", "level": "ãƒŦベãƒĢ", @@ -1127,16 +1222,20 @@ "library_page_sort_created": "äŊœæˆæ—Ĩ時", "library_page_sort_last_modified": "最įĩ‚変更", "library_page_sort_title": "ã‚ĸãƒĢバム名", + "licenses": "ナイã‚ģãƒŗã‚š", "light": "ナイトãƒĸãƒŧド", + "like": "いいね", "like_deleted": "いいねが削除されぞした", "link_motion_video": "ãƒĸãƒŧã‚ˇãƒ§ãƒŗãƒ“ãƒ‡ã‚ĒぎãƒĒãƒŗã‚¯", - "link_options": "ãƒĒãƒŗã‚¯ãŽã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "link_to_oauth": "OAuthへãƒĒãƒŗã‚¯ã™ã‚‹", "linked_oauth_account": "ãƒĒãƒŗã‚¯ã•ã‚ŒãŸOAuthã‚ĸã‚Ģã‚Ļãƒŗãƒˆ", "list": "ãƒĒ゚ト", "loading": "čĒ­ãŋčžŧãŋ中", "loading_search_results_failed": "検į´ĸįĩæžœã‚’čĒ­ãŋčžŧめぞせんでした", + "local": "ロãƒŧã‚ĢãƒĢ", "local_asset_cast_failed": "ã‚ĩãƒŧバãƒŧãĢã‚ĸップロãƒŧドされãĻいãĒã„é …į›Žã¯ã‚­ãƒŖã‚šãƒˆã§ããžã›ã‚“", + "local_assets": "ロãƒŧã‚ĢãƒĢãŽé …į›Ž", + "local_media_summary": "ロãƒŧã‚ĢãƒĢãƒĄãƒ‡ã‚Ŗã‚ĸぎぞとめ", "local_network": "ロãƒŧã‚ĢãƒĢネットワãƒŧク", "local_network_sheet_info": "ã‚ĸプãƒĒは指厚されたWi-FiãĢįš‹ãŒãŖãĻいる時ã‚ĩãƒŧバãƒŧへぎæŽĨįļšã‚’ä¸‹č¨˜ãŽURLã§čĄŒã„ãžã™", "location_permission": "äŊįŊŽæƒ…å ąæ¨Šé™", @@ -1148,6 +1247,7 @@ "location_picker_longitude_hint": "įĩŒåēĻをå…Ĩ力", "lock": "ロック", "locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧ", + "log_detail_title": "ãƒ­ã‚°ãŽčŠŗį´°", "log_out": "ログã‚ĸã‚Ļト", "log_out_all_devices": "全ãĻぎデバイ゚からログã‚ĸã‚Ļト", "logged_in_as": "{user}としãĻãƒ­ã‚°ã‚¤ãƒŗä¸­", @@ -1178,6 +1278,7 @@ "login_password_changed_success": "パ゚ワãƒŧドぎ変更ãĢ成功", "logout_all_device_confirmation": "æœŦåŊ“ãĢ全ãĻぎデバイ゚からログã‚ĸã‚Ļトしぞすか?", "logout_this_device_confirmation": "æœŦåŊ“ãĢこぎデバイ゚からログã‚ĸã‚Ļトしぞすか?", + "logs": "ログ", "longitude": "įĩŒåēĻ", "look": "čĻ‹ãŸį›Ž", "loop_videos": "動į”ģをãƒĢãƒŧプ", @@ -1185,6 +1286,7 @@ "main_branch_warning": "開į™ēį‰ˆã‚’äŊŋãŖãĻいるようです。ãƒĒãƒĒãƒŧã‚šį‰ˆãŽäŊŋį”¨ã‚’åŧˇãæŽ¨åĨ¨ã—ぞす!", "main_menu": "ãƒĄã‚¤ãƒŗãƒĄãƒ‹ãƒĨãƒŧ", "make": "ãƒĄãƒŧã‚Ģãƒŧ", + "manage_geolocation": "äŊįŊŽæƒ…å ąã‚’įˇ¨é›†", "manage_shared_links": "å…ąæœ‰æ¸ˆãŋぎãƒĒãƒŗã‚¯ã‚’įŽĄį†", "manage_sharing_with_partners": "パãƒŧトナãƒŧã¨ãŽå…ąæœ‰ã‚’įŽĄį†ã—ãžã™", "manage_the_app_settings": "ã‚ĸプãƒĒãŽč¨­åŽšã‚’įŽĄį†ã—ãžã™", @@ -1193,8 +1295,7 @@ "manage_your_devices": "ãƒ­ã‚°ã‚¤ãƒŗãƒ‡ãƒã‚¤ã‚šã‚’įŽĄį†ã—ãžã™", "manage_your_oauth_connection": "OAuthæŽĨįļšã‚’įŽĄį†ã—ãžã™", "map": "åœ°å›ŗ", - "map_assets_in_bound": "{count}枚", - "map_assets_in_bounds": "{count}枚", + "map_assets_in_bounds": "{count, plural, =0 {ここãĢå†™įœŸã¯ã‚ã‚Šãžã›ã‚“} one {# 枚} other {# 枚}}", "map_cannot_get_user_location": "äŊįŊŽæƒ…å ąãŒã‚˛ãƒƒãƒˆã§ããžã›ã‚“", "map_location_dialog_yes": "はい", "map_location_picker_page_use_location": "こぎäŊįŊŽæƒ…å ąã‚’äŊŋう", @@ -1202,7 +1303,6 @@ "map_location_service_disabled_title": "äŊįŊŽæƒ…å ąãŒã‚Ēフです", "map_marker_for_images": "{country} {city}で撎åŊąã•ã‚ŒãŸå†™įœŸãŽåœ°å›ŗãƒžãƒŧã‚Ģãƒŧ", "map_marker_with_image": "į”ģåƒãŽåœ°å›ŗãƒžãƒŧã‚Ģãƒŧ", - "map_no_assets_in_bounds": "こぎエãƒĒã‚ĸãĢå†™įœŸã¯ã‚ã‚Šãžã›ã‚“", "map_no_location_permission_content": "įžåœ¨åœ°ãŽé …į›Žã‚’čĄ¨į¤ēするãĢはäŊįŊŽæƒ…å ąã¸ãŽã‚ĸクã‚ģ゚がåŋ…čĻã§ã™ã€‚č¨ąå¯ã—ãžã™ã‹īŧŸ", "map_no_location_permission_title": "äŊįŊŽæƒ…å ąã¸ãŽã‚ĸクã‚ģ゚が拒åĻされぞした", "map_settings": "ãƒžãƒƒãƒ—ãŽč¨­åŽš", @@ -1221,6 +1321,7 @@ "mark_as_read": "æ—ĸčĒ­ãĢする", "marked_all_as_read": "すずãĻæ—ĸčĒ­ãĢしぞした", "matches": "マッチ", + "matching_assets": "ä¸€č‡´ã™ã‚‹é …į›Ž", "media_type": "ãƒĄãƒ‡ã‚Ŗã‚ĸã‚ŋイプ", "memories": "ãƒĄãƒĸãƒĒãƒŧ", "memories_all_caught_up": "これで全部です", @@ -1239,6 +1340,7 @@ "merged_people_count": "{count, plural, one {#äēē} other {#äēē}}ぎäēēį‰Šã‚’įĩąåˆã—ぞした", "minimize": "最小化", "minute": "分", + "minutes": "分", "missing": "æŦ čŊ", "model": "ãƒĸデãƒĢ", "month": "月", @@ -1246,6 +1348,7 @@ "more": "ã‚‚ãŖã¨čĄ¨į¤ē", "move": "į§ģ動", "move_off_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧからå‡ēす", + "move_to_lock_folder_action_prompt": "{count}é …į›Žã‚’éĩäģ˜ããƒ•りãƒĢダãƒŧãĢčŋŊ加しぞした", "move_to_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧへį§ģ動", "move_to_locked_folder_confirmation": "ã“ã‚Œã‚‰ãŽå†™įœŸã‚„å‹•į”ģはすずãĻぎã‚ĸãƒĢバムから外され、éĩäģ˜ããƒ•りãƒĢダãƒŧ内でぎãŋ閲čĻ§å¯čƒŊãĢãĒりぞす", "moved_to_archive": "{count, plural, one {#} other {#}}é …į›Žã‚’ã‚ĸãƒŧã‚Ģイブしぞした", @@ -1257,6 +1360,10 @@ "my_albums": "į§ãŽã‚ĸãƒĢバム", "name": "名前", "name_or_nickname": "名前ぞたはニックネãƒŧム", + "network_requirement_photos_upload": "ãƒĸバイãƒĢ通äŋĄã‚’äŊŋį”¨ã—ãĻå†™įœŸãŽãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—ã‚’čĄŒã†", + "network_requirement_videos_upload": "ãƒĸバイãƒĢ通äŋĄã‚’äŊŋį”¨ã—ãĻ動į”ģぎバックã‚ĸãƒƒãƒ—ã‚’čĄŒã†", + "network_requirements": "ネットワãƒŧクぎčρäģļ", + "network_requirements_updated": "ネットワãƒŧã‚¯ãŽæĄäģļが変更されたため、バックã‚ĸップぎ順į•Ēåž…ãĄã‚’ãƒĒã‚ģットしぞす", "networking_settings": "ネットワãƒŧク", "networking_subtitle": "ã‚ĩãƒŧバãƒŧã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆãĢé–ĸã™ã‚‹č¨­åŽš", "never": "čĄŒã‚ãĒい", @@ -1266,6 +1373,7 @@ "new_person": "新しいäēēį‰Š", "new_pin_code": "新しいPINã‚ŗãƒŧド", "new_pin_code_subtitle": "éĩäģ˜ããƒ•りãƒĢダãƒŧã‚’åˆŠį”¨ã™ã‚‹ãŽãŒåˆã‚ãĻぎようです。PINã‚ŗãƒŧドをäŊœæˆã—ãĻください", + "new_timeline": "新たãĒã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗ", "new_user_created": "新しいãƒĻãƒŧã‚ļãƒŧがäŊœæˆã•れぞした", "new_version_available": "新しいバãƒŧã‚¸ãƒ§ãƒŗãŒåˆŠį”¨å¯čƒŊ", "newest_first": "最新順", @@ -1279,19 +1387,25 @@ "no_assets_message": "クãƒĒックしãĻæœ€åˆãŽå†™įœŸã‚’ã‚ĸップロãƒŧド", "no_assets_to_show": "襨į¤ēã™ã‚‹é …į›ŽãŒã‚ã‚Šãžã›ã‚“", "no_cast_devices_found": "ã‚­ãƒŖã‚šãƒˆå…ˆãŽãƒ‡ãƒã‚¤ã‚šãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", + "no_checksum_local": "チェックã‚ĩムがčĻ‹ã¤ã‹ã‚Šãžã›ã‚“ - ãƒ‡ãƒã‚¤ã‚šä¸ŠãŽé …į›Žã‚’å–åž—ã§ããĒいようです", + "no_checksum_remote": "チェックã‚ĩムがčĻ‹ã¤ã‹ã‚Šãžã›ã‚“ - ã‚ĩãƒŧバãƒŧä¸ŠãŽé …į›Žã‚’å–åž—ã§ããĒいようです", "no_duplicates_found": "é‡č¤‡ã¯čĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸã€‚", "no_exif_info_available": "exifæƒ…å ąãŒåˆŠį”¨ã§ããžã›ã‚“", "no_explore_results_message": "ã‚ŗãƒŦã‚¯ã‚ˇãƒ§ãƒŗã‚’æŽĸį´ĸするãĢはさらãĢå†™įœŸã‚’ã‚ĸップロãƒŧドしãĻください。", "no_favorites_message": "お気ãĢå…Ĩりį™ģéŒ˛ã™ã‚‹ã¨åĨŊきãĒå†™įœŸã‚„å‹•į”ģをすぐãĢčĻ‹ã¤ã‘ã‚‰ã‚Œãžã™", "no_libraries_message": "あãĒãŸãŽå†™įœŸã‚„å‹•į”ģã‚’čĄ¨į¤ēするためぎ外部ナイブナãƒĒをäŊœæˆã—ぞしょう", + "no_local_assets_found": "こぎチェックã‚ĩãƒ ãŽé …į›Žã¯ãƒ‡ãƒã‚¤ã‚šä¸ŠãĢ存在しぞせん", "no_locked_photos_message": "éĩäģ˜ããƒ•りãƒĢダãƒŧå†…ãŽå†™įœŸã‚„å‹•į”ģは通常ぎナイブナãƒĒãĢ襨į¤ēされãĒくãĒりぞす。", "no_name": "名前ãĒし", "no_notifications": "通įŸĨãĒし", "no_people_found": "ä¸€č‡´ã™ã‚‹äēēį‰ŠãŒčĻ‹ã¤ã‹ã‚Šãžã›ã‚“", "no_places": "場所ãĒし", + "no_remote_assets_found": "こぎチェックã‚ĩãƒ ãŽé …į›Žã¯ã‚ĩãƒŧバãƒŧ上ãĢ存在しぞせん", "no_results": "įĩæžœãŒã‚りぞせん", "no_results_description": "åŒįžŠčĒžã‚„ã‚ˆã‚Šä¸€čˆŦįš„ãĒキãƒŧワãƒŧドをčŠĻしãĻください", "no_shared_albums_message": "ã‚ĸãƒĢバムをäŊœæˆã—ãĻå†™įœŸã‚„å‹•į”ģã‚’å…ąæœ‰ã—ãžã—ã‚‡ã†", + "no_uploads_in_progress": "ã‚ĸップロãƒŧãƒ‰ã¯čĄŒã‚ã‚ŒãĻいぞせん", + "not_available": "éŠį”¨ãĒし", "not_in_any_album": "おぎã‚ĸãƒĢバムãĢもå…ĨãŖãĻいãĒい", "not_selected": "選択ãĒし", "note_apply_storage_label_to_previously_uploaded assets": "æŗ¨æ„: äģĨ前ãĢã‚ĸップロãƒŧドしたã‚ĸã‚ģットãĢ゚トãƒŦãƒŧジナベãƒĢã‚’éŠį”¨ã™ã‚‹ãĢはäģĨä¸‹ã‚’åŽŸčĄŒã—ãĻください", @@ -1307,6 +1421,7 @@ "oauth": "OAuth", "official_immich_resources": "å…ŦåŧImmichãƒĒã‚Ŋãƒŧ゚", "offline": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗ", + "offset": "ã‚Ēフã‚ģット", "ok": "äē†č§Ŗ", "oldest_first": "古い順", "on_this_device": "ãƒ‡ãƒã‚¤ã‚šä¸ŠãŽé …į›Ž", @@ -1325,10 +1440,13 @@ "open_the_search_filters": "検į´ĸãƒ•ã‚ŖãƒĢã‚ŋを開く", "options": "ã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", "or": "ぞたは", + "organize_into_albums": "ã‚ĸãƒĢバムãĢčŋŊ加しãĻæ•´į†ã™ã‚‹", + "organize_into_albums_description": "æ—ĸå­˜ãŽå†™įœŸã‚’ã€įžåœ¨ãŽåŒæœŸč¨­åŽšãĢåŸēãĨきã‚ĸãƒĢバムãĢčŋŊ加する", "organize_your_library": "ナイブナãƒĒã‚’æ•´į†", "original": "ã‚ĒãƒĒジナãƒĢ", "other": "そぎäģ–", "other_devices": "そぎäģ–ぎデバイ゚", + "other_entities": "äģ–ぎもぎ", "other_variables": "そぎäģ–ぎ変数", "owned": "所有中", "owner": "ã‚Ēãƒŧナãƒŧ", @@ -1342,7 +1460,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}はäģŠåžŒã‚ãĒãŸãŽå†™įœŸã¸ã‚ĸクã‚ģ゚できãĒくãĒりぞす", "partner_sharing": "パãƒŧãƒˆãƒŠã¨ãŽå…ąæœ‰", "partners": "パãƒŧトナãƒŧ", @@ -1383,6 +1501,9 @@ "permission_onboarding_permission_limited": "å†™įœŸã¸ãŽã‚ĸクã‚ģ゚がåˆļ限されãĻいぞす。ImmichãŒå†™įœŸãŽãƒãƒƒã‚¯ã‚ĸãƒƒãƒ—ã¨įŽĄį†ã‚’čĄŒã†ãĢã¯ã€ã‚ˇã‚šãƒ†ãƒ č¨­åŽšã‹ã‚‰å†™įœŸã¨å‹•į”ģぎã‚ĸクã‚ģ゚樊限を変更しãĻください。", "permission_onboarding_request": "Immichã¯å†™įœŸã¸ãŽã‚ĸクã‚ģã‚šč¨ąå¯ãŒåŋ…čĻã§ã™", "person": "äēēį‰Š", + "person_age_months": "į”ŸåžŒ {months, plural, one {# ãƒļ月} other {# ãƒļ月}}", + "person_age_year_months": "1 æ­ŗã¨, {months, plural, one {# ãƒļ月} other {# ãƒļ月}}", + "person_age_years": "{years, plural, other {# æ­ŗ}}", "person_birthdate": "{date}į”Ÿãžã‚Œ", "person_hidden": "{name}{hidden, select, true { (非表į¤ē)} other {}}", "photo_shared_all_users": "å†™įœŸã‚’ã™ãšãĻぎãƒĻãƒŧã‚ļãƒŧã¨å…ąæœ‰ã—ãŸã‹ã€å…ąæœ‰ã™ã‚‹ãƒĻãƒŧã‚ļãƒŧがいãĒいようです。", @@ -1406,6 +1527,7 @@ "port": "ポãƒŧトãƒŦãƒŧト", "preferences_settings_subtitle": "ã‚ĸプãƒĒãĢé–ĸã™ã‚‹č¨­åŽš", "preferences_settings_title": "č¨­åŽš", + "preparing": "æē–備中", "preset": "プãƒĒã‚ģット", "preview": "プãƒŦビãƒĨãƒŧ", "previous": "前", @@ -1422,6 +1544,7 @@ "profile_drawer_client_out_of_date_minor": "ã‚ĸプãƒĒが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_drawer_client_server_up_to_date": "すずãĻæœ€æ–°į‰ˆã§ã™", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "čĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧドが有劚です。ãƒĻãƒŧã‚ļãƒŧぎã‚ĸã‚¤ã‚ŗãƒŗã‚’é•ˇæŠŧししãĻčĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧãƒ‰ã‚’č§Ŗé™¤ã—ãĻください。", "profile_drawer_server_out_of_date_major": "ã‚ĩãƒŧバãƒŧが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_drawer_server_out_of_date_minor": "ã‚ĩãƒŧバãƒŧが更新されãĻぞせん。最新ぎバãƒŧã‚¸ãƒ§ãƒŗãĢ更新しãĻください", "profile_image_of_user": "{user} ãŽãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢį”ģ像", @@ -1460,12 +1583,17 @@ "purchase_server_description_2": "ã‚ĩポãƒŧã‚ŋãƒŧぎįŠļ態", "purchase_server_title": "ã‚ĩãƒŧバãƒŧ", "purchase_settings_server_activated": "ã‚ĩãƒŧバãƒŧぎプロダクトキãƒŧã¯įŽĄį†č€…ãĢįŽĄį†ã•ã‚ŒãĻいぞす", + "query_asset_id": "順į•Ēåž…ãĄãŽé …į›ŽID", + "queue_status": "順į•Ēåž…ãĄä¸­ {count}/{total}", "rating": "æ˜Ÿã§ãŽčŠ•äžĄ", "rating_clear": "čŠ•äžĄã‚’å–ã‚Šæļˆã™", "rating_count": "星{count, plural, one {#つ} other {#つ}}", "rating_description": "æƒ…å ąæŦ„ãĢEXIFãŽčŠ•äžĄã‚’čĄ¨į¤ē", "reaction_options": "ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗãŽé¸æŠž", "read_changelog": "変更åąĨ歴をčĒ­ã‚€", + "readonly_mode_disabled": "čĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧãƒ‰į„ĄåŠš", + "readonly_mode_enabled": "čĒ­ãŋå–ã‚Šå°‚į”¨ãƒĸãƒŧド有劚", + "ready_for_upload": "ã‚ĸップロãƒŧドæē–å‚™åތäē†", "reassign": "å†å‰˛ã‚ŠåŊ“ãĻ", "reassigned_assets_to_existing_person": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを{name, select, null {æ—ĸ存ぎäēēį‰Š} other {{name}}}ãĢå†å‰˛ã‚ŠåŊ“ãĻしぞした", "reassigned_assets_to_new_person": "{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを新しいäēēį‰ŠãĢå‰˛ã‚ŠåŊ“ãĻぞした", @@ -1488,6 +1616,9 @@ "refreshing_faces": "顔čĒč­˜ã‚’æ›´æ–°ä¸­", "refreshing_metadata": "ãƒĄã‚ŋデãƒŧã‚ŋを更新中", "regenerating_thumbnails": "ã‚ĩムネイãƒĢã‚’å†į”Ÿæˆä¸­", + "remote": "ãƒĒãƒĸãƒŧト", + "remote_assets": "ãƒĒãƒĸãƒŧãƒˆãŽé …į›Ž", + "remote_media_summary": "ã‚ĩãƒŧバãƒŧä¸ŠãŽãƒĄãƒ‡ã‚Ŗã‚ĸぞとめ", "remove": "削除", "remove_assets_album_confirmation": "æœŦåŊ“ãĢ{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットをã‚ĸãƒĢバムから削除しぞすか?", "remove_assets_shared_link_confirmation": "æœŦåŊ“ãĢã“ãŽå…ąæœ‰ãƒĒãƒŗã‚¯ã‹ã‚‰{count, plural, one {#個} other {#個}}ぎã‚ĸã‚ģットを削除しぞすか?", @@ -1495,7 +1626,9 @@ "remove_custom_date_range": "ã‚Ģ゚ã‚ŋムæ—Ĩäģ˜į¯„å›˛ã‚’å‰Šé™¤", "remove_deleted_assets": "ã‚Ēãƒ•ãƒŠã‚¤ãƒŗãŽã‚ĸã‚ģットを削除", "remove_from_album": "ã‚ĸãƒĢバムから削除", + "remove_from_album_action_prompt": "{count}é …į›ŽãŒã‚ĸãƒĢバムから除かれぞした", "remove_from_favorites": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", + "remove_from_lock_folder_action_prompt": "{count}é …į›Žã‚’éĩäģ˜ããƒ•りãƒĢダãƒŧからå‡ēしぞした", "remove_from_locked_folder": "éĩäģ˜ããƒ•りãƒĢダãƒŧから取り除く", "remove_from_locked_folder_confirmation": "é¸æŠžã—ãŸå†™įœŸãƒģ動į”ģをéĩäģ˜ããƒ•りãƒĢダãƒŧぎ外ãĢå‡ēしãĻよろしいですかīŧŸãƒŠã‚¤ãƒ–ナãƒĒãĢå†ãŗčĄ¨į¤ēされるようãĢãĒりぞす", "remove_from_shared_link": "å…ąæœ‰ãƒĒãƒŗã‚¯ã‹ã‚‰å‰Šé™¤", @@ -1523,19 +1656,29 @@ "reset_password": "パ゚ワãƒŧドをãƒĒã‚ģット", "reset_people_visibility": "äēēį‰ŠãŽéžčĄ¨į¤ēč¨­åŽšã‚’ãƒĒã‚ģット", "reset_pin_code": "PINã‚ŗãƒŧドをãƒĒã‚ģット", + "reset_pin_code_description": "PINã‚ŗãƒŧドをåŋ˜ã‚ŒãŸå ´åˆã¯ã€ã‚ĩãƒŧバãƒŧįŽĄį†č€…ãĢ逪įĩĄã—ãĻãƒĒã‚ģットできぞす", + "reset_pin_code_success": "æ­Ŗå¸¸ãĢPINã‚ŗãƒŧドをãƒĒã‚ģットしぞした", + "reset_pin_code_with_password": "PINã‚ŗãƒŧドはいつでもパ゚ワãƒŧドをäŊŋãŖãĻãƒĒã‚ģットできぞす", + "reset_sqlite": "SQLiteデãƒŧã‚ŋベãƒŧ゚をãƒĒã‚ģット", + "reset_sqlite_confirmation": "SQLiteをæœŦåŊ“ãĢãƒĒã‚ģットしぞすかīŧŸãƒ‡ãƒŧã‚ŋã‚’å†ãŗåŒæœŸã™ã‚‹ãŸã‚ãĢログã‚ĸã‚Ļãƒˆã—å†ãƒ­ã‚°ã‚¤ãƒŗã‚’ã™ã‚‹åŋ…čĻãŒã‚ã‚Šãžã™", + "reset_sqlite_success": "SQLiteデãƒŧã‚ŋベãƒŧ゚ぎãƒĒã‚ģットãĢ成功しぞした", "reset_to_default": "デフりãƒĢトãĢãƒĒã‚ģット", "resolve_duplicates": "é‡č¤‡ã‚’č§Ŗæąēする", "resolved_all_duplicates": "全ãĻãŽé‡č¤‡ã‚’č§Ŗæąēしぞした", "restore": "垊元", "restore_all": "全ãĻ垊元", + "restore_trash_action_prompt": "{count}é …į›ŽãŒã‚´ãƒŸįŽąã‹ã‚‰åžŠå…ƒã•ã‚Œãžã—ãŸ", "restore_user": "ãƒĻãƒŧã‚ļãƒŧを垊元", "restored_asset": "é …į›Žã‚’åžŠå…ƒã—ãžã—ãŸ", "resume": "再開", + "resume_paused_jobs": "再開: {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "ã‚ĸップロãƒŧドを再čŠĻ行", "review_duplicates": "é‡č¤‡ã‚’čĒŋæŸģ", + "review_large_files": "ã‚ĩイã‚ēぎ大きãĒãƒ•ã‚Ąã‚¤ãƒĢをčĻ‹ã‚‹", "role": "ロãƒŧãƒĢ", "role_editor": "ᎍ集者", "role_viewer": "閲čϧ者", + "running": "åŽŸčĄŒä¸­", "save": "äŋå­˜", "save_to_gallery": "ã‚ŽãƒŖãƒŠãƒĒãƒŧãĢäŋå­˜", "saved_api_key": "APIキãƒŧをäŋå­˜ã—ぞした", @@ -1622,11 +1765,12 @@ "select_user_for_sharing_page_err_album": "ã‚ĸãƒĢバムäŊœæˆãĢå¤ąæ•—", "selected": "選択済ãŋ", "selected_count": "{count, plural, other {#個選択済ãŋ}}", + "selected_gps_coordinates": "選択されたäŊįŊŽæƒ…å ą", "send_message": "ãƒĄãƒƒã‚ģãƒŧジを送äŋĄ", "send_welcome_email": "ã‚ĻェãƒĢã‚Ģãƒ ãƒĄãƒŧãƒĢを送äŋĄ", "server_endpoint": "ã‚ĩãƒŧバãƒŧã‚¨ãƒŗãƒ‰ãƒã‚¤ãƒŗãƒˆ", "server_info_box_app_version": "ã‚ĸプãƒĒぎバãƒŧã‚¸ãƒ§ãƒŗ", - "server_info_box_server_url": " ã‚ĩãƒŧバãƒŧぎURL", + "server_info_box_server_url": "ã‚ĩãƒŧバãƒŧぎURL", "server_offline": "ã‚ĩãƒŧバãƒŧがã‚Ēãƒ•ãƒŠã‚¤ãƒŗã§ã™", "server_online": "ã‚ĩãƒŧバãƒŧがã‚ĒãƒŗãƒŠã‚¤ãƒŗã§ã™", "server_privacy": "ã‚ĩãƒŧバãƒŧãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ", @@ -1667,6 +1811,7 @@ "settings_saved": "č¨­åŽšãŒäŋå­˜ã•れぞした", "setup_pin_code": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップ", "share": "å…ąæœ‰", + "share_action_prompt": "{count}é …į›Žã‚’å…ąæœ‰ã—ãžã—ãŸ", "share_add_photos": "å†™įœŸã‚’čŋŊ加", "share_assets_selected": "{count}選択中", "share_dialog_preparing": "æē–備中", @@ -1688,6 +1833,7 @@ "shared_link_clipboard_copied_massage": "クãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧしぞした", "shared_link_clipboard_text": "ãƒĒãƒŗã‚¯: {link}\nパ゚ワãƒŧド: {password}", "shared_link_create_error": "å…ąæœ‰į”¨ãŽãƒĒãƒŗã‚¯äŊœæˆæ™‚ãĢエナãƒŧがį™ēį”Ÿã—ãžã—ãŸ", + "shared_link_custom_url_description": "ã“ãŽå…ąæœ‰ãƒĒãƒŗã‚¯ãĢã‚Ģ゚ã‚ŋムURLでã‚ĸクã‚ģ゚", "shared_link_edit_description_hint": "æĻ‚čĻã‚’čŋŊ加", "shared_link_edit_expire_after_option_day": "1æ—Ĩ", "shared_link_edit_expire_after_option_days": "{count}æ—Ĩ", @@ -1713,6 +1859,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "å…ąæœ‰æ¸ˆãŋぎãƒĒãƒŗã‚¯ã‚’įŽĄį†", "shared_link_options": "å…ąæœ‰ãƒĒãƒŗã‚¯ãŽã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ", + "shared_link_password_description": "ã“ãŽå…ąæœ‰ãƒĒãƒŗã‚¯ãĢã‚ĸクã‚ģ゚する際ãĢパ゚ワãƒŧドをčĻæą‚ã™ã‚‹", "shared_links": "å…ąæœ‰ãƒĒãƒŗã‚¯", "shared_links_description": "å†™įœŸã‚„å‹•į”ģをãƒĒãƒŗã‚¯ã§å…ąæœ‰", "shared_photos_and_videos_count": "{assetCount, plural, other {#å€‹ãŽå…ąæœ‰ã•ã‚ŒãŸå†™įœŸã¨å‹•į”ģ}}", @@ -1747,6 +1894,7 @@ "show_slideshow_transition": "ã‚šãƒŠã‚¤ãƒ‰ã‚ˇãƒ§ãƒŧãŽãƒˆãƒŠãƒŗã‚¸ã‚ˇãƒ§ãƒŗã‚’čĄ¨į¤ē", "show_supporter_badge": "ã‚ĩポãƒŧã‚ŋãƒŧバッジ", "show_supporter_badge_description": "ã‚ĩポãƒŧã‚ŋãƒŧãƒãƒƒã‚¸ã‚’čĄ¨į¤ē", + "show_text_search_menu": "テキ゚ト検į´ĸãƒĄãƒ‹ãƒĨãƒŧã‚’čĄ¨į¤ē", "shuffle": "ãƒŠãƒŗãƒ€ãƒ ", "sidebar": "ã‚ĩイドバãƒŧ", "sidebar_display_description": "ã‚ĩイドバãƒŧãĢビãƒĨãƒŧへぎãƒĒãƒŗã‚¯ã‚’čĄ¨į¤ē", @@ -1762,12 +1910,14 @@ "sort_created": "äŊœæˆæ—Ĩ", "sort_items": "é …į›ŽãŽæ•°", "sort_modified": "変更æ—Ĩ", + "sort_newest": "æœ€æ–°ãŽå†™įœŸ", "sort_oldest": "å¤ã„å†™įœŸ", "sort_people_by_similarity": "äŧŧãĻいる順ãĢäēēį‰Šã‚’ä¸Ļãŗæ›ŋえる", "sort_recent": "æœ€æ–°ãŽå†™įœŸ", "sort_title": "ã‚ŋイトãƒĢ", "source": "ã‚Ŋãƒŧ゚", "stack": "゚ã‚ŋック", + "stack_action_prompt": "{count}が重ねられぞした", "stack_duplicates": "゚ã‚ŋãƒƒã‚¯ãŽé‡č¤‡", "stack_select_one_photo": "゚ã‚ŋãƒƒã‚¯ãŽãƒĄã‚¤ãƒŗãŽå†™įœŸã‚’é¸æŠž", "stack_selected_photos": "é¸æŠžã—ãŸå†™įœŸã‚’ã‚šã‚ŋックする", @@ -1775,6 +1925,7 @@ "stacktrace": "゚ã‚ŋックトãƒŦãƒŧ゚", "start": "開始", "start_date": "開始æ—Ĩ", + "start_date_before_end_date": "開始æ—Ĩはįĩ‚ä熿—Ĩより前でãĒければãĒりぞせん", "state": "éƒŊ道åēœįœŒ", "status": "゚テãƒŧã‚ŋ゚", "stop_casting": "ã‚­ãƒŖã‚šãƒˆã‚’åœæ­ĸ", @@ -1787,6 +1938,7 @@ "storage_quota": "゚トãƒŦãƒŧジ厚量", "storage_usage": "{available} 中 {used} äŊŋᔍ䏭", "submit": "送äŋĄ", + "success": "成功", "suggestions": "ãƒĻãƒŧã‚ļãƒŧãƒĒ゚ト", "sunrise_on_the_beach": "æĩˇå˛¸ãŽæ—Ĩぎå‡ē", "support": "ã‚ĩポãƒŧト", @@ -1796,6 +1948,10 @@ "sync": "同期", "sync_albums": "ã‚ĸãƒĢバムを同期", "sync_albums_manual_subtitle": "ã‚ĸップロãƒŧド済ãŋぎ全ãĻãŽå†™įœŸã‚„å‹•į”ģを選択されたバックã‚ĸップã‚ĸãƒĢバムãĢ同期する", + "sync_local": "ロãƒŧã‚ĢãƒĢを同期", + "sync_remote": "ãƒĒãƒĸãƒŧトを同期", + "sync_status": "同期ぎįŠļ態", + "sync_status_subtitle": "åŒæœŸã‚ˇã‚šãƒ†ãƒ ã‚’įĸēčĒãƒģįŽĄį†", "sync_upload_album_setting_subtitle": "ã‚ĩãƒŧバãƒŧ上ぎã‚ĸãƒĢバムぎ内厚をį̝æœĢ上ぎã‚ĸãƒĢバムと同期しぞす (ã‚ĩãƒŧバãƒŧãĢã‚ĸãƒĢãƒãƒ ãŒį„Ąã„å ´åˆč‡Ē動でäŊœæˆã•れぞす。ぞた、ã‚ĸップロãƒŧドされãĻいãĒã„å†™įœŸã‚„å‹•į”ģは同期されぞせん)", "tag": "ã‚ŋグäģ˜ã‘する", "tag_assets": "ã‚ĸã‚ģットãĢã‚ŋグäģ˜ã‘する", @@ -1806,6 +1962,7 @@ "tag_updated": "ã‚ŋグ: {tag} を更新しぞした", "tagged_assets": "{count, plural, one {#個ぎã‚ĸã‚ģット} other {#個ぎã‚ĸã‚ģット}}をã‚ŋグäģ˜ã‘しぞした", "tags": "ã‚ŋグ", + "tap_to_run_job": "ã‚ŋップでジョブを開始", "template": "ãƒ†ãƒŗãƒ—ãƒŦãƒŧト", "theme": "テãƒŧマ", "theme_selection": "テãƒŧマ選択", @@ -1832,12 +1989,15 @@ "to_change_password": "パ゚ワãƒŧドを変更", "to_favorite": "お気ãĢå…Ĩり", "to_login": "ãƒ­ã‚°ã‚¤ãƒŗ", + "to_multi_select": "č¤‡æ•°é¸æŠž", "to_parent": "上äŊãŽéšŽåą¤ã¸", + "to_select": "選択", "to_trash": "ã‚´ãƒŸįŽą", "toggle_settings": "č¨­åŽšã‚’ãƒˆã‚°ãƒĢ", "total": "合荈", "total_usage": "įˇäŊŋį”¨é‡", "trash": "ã‚´ãƒŸįŽą", + "trash_action_prompt": "{count}é …į›Žã‚’ã‚´ãƒŸįŽąãĢį§ģ動しぞした", "trash_all": "全ãĻ削除", "trash_count": "{count, number}æžšã‚´ãƒŸįŽąã¸į§ģ動", "trash_delete_asset": "ã‚ĸã‚ģãƒƒãƒˆã‚’ã‚´ãƒŸįŽąã¸į§ģ動/削除", @@ -1851,13 +2011,16 @@ "trash_page_select_assets_btn": "é …į›Žã‚’é¸æŠž", "trash_page_title": "ã‚´ãƒŸįŽą ({count})", "trashed_items_will_be_permanently_deleted_after": "ã‚´ãƒŸįŽąãĢå…Ĩれられたã‚ĸイテムは{days, plural, one {#æ—Ĩ} other {#æ—Ĩ}}垌ãĢ厌全ãĢ削除されぞす。", + "troubleshoot": "トナブãƒĢã‚ˇãƒĨãƒŧãƒ†ã‚Ŗãƒŗã‚°", "type": "ã‚ŋイプ", "unable_to_change_pin_code": "PINã‚ŗãƒŧドを変更できぞせんでした", "unable_to_setup_pin_code": "PINã‚ŗãƒŧドをã‚ģットã‚ĸップできぞせんでした", "unarchive": "ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤", + "unarchive_action_prompt": "{count}é …į›Žã‚’ã‚ĸãƒŧã‚Ģイブから除きぞした", "unarchived_count": "{count, plural, other {#枚ã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‚’č§Ŗé™¤ã—ãžã—ãŸ}}", "undo": "元ãĢæˆģす", "unfavorite": "お気ãĢå…Ĩã‚Šč§Ŗé™¤", + "unfavorite_action_prompt": "{count}é …į›Žã‚’ãŠæ°—ãĢå…Ĩã‚Šã‹ã‚‰č§Ŗé™¤", "unhide_person": "äēēį‰ŠãŽéžčĄ¨į¤ēã‚’č§Ŗé™¤", "unknown": "不明", "unknown_country": "不明ãĒå›Ŋ", @@ -1875,15 +2038,21 @@ "unselect_all_duplicates": "全ãĻãŽé‡č¤‡ãŽé¸æŠžã‚’č§Ŗé™¤", "unselect_all_in": "{group}ぎすずãĻãŽé¸æŠžã‚’č§Ŗé™¤", "unstack": "゚ã‚ŋãƒƒã‚¯ã‚’č§Ŗé™¤", + "unstack_action_prompt": "{count}é …į›ŽãŽé‡ã­åˆã‚ã›ã‚’č§Ŗé™¤", "unstacked_assets_count": "{count, plural, one {#個ぎã‚ĸã‚ģット} other {#個ぎã‚ĸã‚ģット}}を゚ã‚ŋãƒƒã‚¯ã‹ã‚‰č§Ŗé™¤ã—ãžã—ãŸ", + "untagged": "ã‚ŋã‚°ã‚’č§Ŗé™¤", "up_next": "æŦĄã¸", + "update_location_action_prompt": "{count}é …į›Žã‚’åŗč¨˜ãŽäŊįŊŽæƒ…å ąãĢã‚ĸップデãƒŧトしぞす:", "updated_at": "更新", "updated_password": "パ゚ワãƒŧドを更新しぞした", "upload": "ã‚ĸップロãƒŧド", + "upload_action_prompt": "{count}é …į›ŽãŒã‚ĸップロãƒŧドぎ順į•Ēåž…ãĄä¸­", "upload_concurrency": "ã‚ĸップロãƒŧãƒ‰ãŽåŒæ™‚åŽŸčĄŒæ•°", + "upload_details": "ã‚ĸップロãƒŧãƒ‰ãŽčŠŗį´°", "upload_dialog_info": "é¸æŠžã—ãŸé …į›ŽãŽãƒãƒƒã‚¯ã‚ĸップをしぞすかīŧŸ", "upload_dialog_title": "ã‚ĸップロãƒŧド", "upload_errors": "ã‚ĸップロãƒŧドは{count, plural, one {#個} other {#個}}ぎエナãƒŧで厌äē†ã—ぞした、新しくã‚ĸップロãƒŧドされたã‚ĸã‚ģットをčĻ‹ã‚‹ãĢはペãƒŧジを更新しãĻください。", + "upload_finished": "ã‚ĸップロãƒŧド厌äē†", "upload_progress": "掋り {remaining, number} - {processed, number}/{total, number} å‡Ļį†æ¸ˆãŋ", "upload_skipped_duplicates": "{count, plural, one {#個} other {#個}}ãŽé‡č¤‡ã‚ĸã‚ģットを゚キップしぞした", "upload_status_duplicates": "重複", @@ -1892,6 +2061,7 @@ "upload_success": "ã‚ĸップロãƒŧド成功、新しくã‚ĸップロãƒŧドされたã‚ĸã‚ģットをčĻ‹ã‚‹ãĢはペãƒŧジを更新しãĻください。", "upload_to_immich": "ImmichãĢã‚ĸップロãƒŧド ({count})", "uploading": "ã‚ĸップロãƒŧド中", + "uploading_media": "ãƒĄãƒ‡ã‚Ŗã‚ĸをã‚ĸップロãƒŧド中", "url": "URL", "usage": "äŊŋį”¨åŽšé‡", "use_biometric": "į”ŸäŊ“čĒč¨ŧã‚’ã”åˆŠį”¨ãã ã•ã„", @@ -1912,6 +2082,7 @@ "user_usage_stats_description": "ã‚ĸã‚Ģã‚ĻãƒŗãƒˆåˆŠį”¨įŠļæŗįĩąč¨ˆã‚’襨į¤ē", "username": "ãƒĻãƒŧã‚ļãƒŧ名", "users": "ãƒĻãƒŧã‚ļãƒŧ", + "users_added_to_album_count": "{count, plural, one {#äēē} other {#äēē}}をã‚ĸãƒĢバムãĢčŋŊ加しぞした", "utilities": "ãƒĻãƒŧãƒ†ã‚ŖãƒĒãƒ†ã‚Ŗ", "validate": "čĒč¨ŧ", "validate_endpoint_error": "有劚ãĒURLをå…Ĩ力しãĻください", @@ -1930,6 +2101,7 @@ "view_album": "ã‚ĸãƒĢバムをčĻ‹ã‚‹", "view_all": "すずãĻčĻ‹ã‚‹", "view_all_users": "全ãĻぎãƒĻãƒŧã‚ļãƒŧをįĸēčĒã™ã‚‹", + "view_details": "čŠŗį´°ã‚’čĄ¨į¤ē", "view_in_timeline": "ã‚ŋã‚¤ãƒ ãƒŠã‚¤ãƒŗã§čĻ‹ã‚‹", "view_link": "ãƒĒãƒŗã‚¯ã‚’čĻ‹ã‚‹", "view_links": "ãƒĒãƒŗã‚¯ã‚’įĸēčĒã™ã‚‹", @@ -1937,6 +2109,7 @@ "view_next_asset": "æŦĄãŽã‚ĸã‚ģットをčĻ‹ã‚‹", "view_previous_asset": "前ぎã‚ĸã‚ģットをčĻ‹ã‚‹", "view_qr_code": "QRã‚ŗãƒŧドをčĻ‹ã‚‹", + "view_similar_photos": "類äŧŧã™ã‚‹å†™įœŸã‚’čĻ‹ã‚‹", "view_stack": "ビãƒĨãƒŧ゚ã‚ŋック", "view_user": "ãƒĻãƒŧã‚ļãƒŧをčĻ‹ã‚‹", "viewer_remove_from_stack": "゚ã‚ŋックから外す", @@ -1955,5 +2128,6 @@ "yes": "はい", "you_dont_have_any_shared_links": "å…ąæœ‰ãƒĒãƒŗã‚¯ã¯ã‚ã‚Šãžã›ã‚“", "your_wifi_name": "Wi-Fiぎ名前(SSID)", - "zoom_image": "į”ģåƒã‚’æ‹Ąå¤§" + "zoom_image": "į”ģåƒã‚’æ‹Ąå¤§", + "zoom_to_bounds": "į”ģéĸįĢ¯ãžã§ã‚ēãƒŧム" } diff --git a/i18n/kk.json b/i18n/kk.json index 5dabe59a13..7025ae0d22 100644 --- a/i18n/kk.json +++ b/i18n/kk.json @@ -1,11 +1,21 @@ { "about": "ĐĸŅƒŅ€Đ°ĐģŅ‹", "account": "ĐĸŅ–Ņ€ĐēĐĩĐģĐŗŅ–", + "add": "Ō›ĐžŅŅƒ", + "add_a_description": "ŅĐ¸ĐŋĐ°Ņ‚Ņ‚Đ°ĐŧаĐŊŅ‹ Ō›ĐžŅŅƒ", + "add_a_location": "ŅŅƒŅ€Đĩ҂҂Җ Ņ‚Ō¯ŅŅ–Ņ€ĐŗĐĩĐŊ ĐļĐĩŅ€Đ´Ņ– Ō›ĐžŅŅ‹", + "add_a_name": "ĐŅ‚Ņ‹ĐŊ Ō›ĐžŅŅƒ", + "add_birthday": "ĐĸŅƒŌ“Đ°ĐŊ ĐēŌ¯ĐŊŅ–ĐŊ Ō›ĐžŅŅƒ", + "add_location": "ĐļĐĩŅ€Đ´Ņ– Ō›ĐžŅŅƒ", + "add_more_users": "Ō›ĐžŅŅ‹ĐŧŅˆĐ° адаĐŧĐ´Đ°Ņ€Đ´Ņ‹ ҂ҖҀĐēĐĩ҃", + "add_partner": "ĐļĐžĐģĐ´Đ°ŅŅ‚Ņ‹ Ō›ĐžŅŅƒ", "add_photos": "ŅŅƒŅ€Đĩ҂҂ĐĩŅ€Đ´Ņ– Ō›ĐžŅŅƒ", + "add_tag": "Ņ‚ĐĩĐŗŅ‚Ņ– Ō›ĐžŅŅƒ", "add_to": "Ō›ĐžŅŅƒâ€Ļ", "add_to_album": "аĐģŅŒĐąĐžĐŧŌ“Đ° Ō›ĐžŅŅƒ", "add_to_album_bottom_sheet_added": "{album}'Ō“Đ° Ō›ĐžŅŅ‹ĐģŌ“Đ°ĐŊ", "add_to_album_bottom_sheet_already_exists": "ОĐŊŅŅ‹Đˇ да {album} йОĐģŌ“Đ°ĐŊ", + "add_to_albums": "аĐģŅŒĐąĐžĐŧĐ´Đ°Ņ€Ō“Đ° Ō›ĐžŅŅƒ", "add_to_shared_album": "ĐąĶŠĐģҖҁĐēĐĩĐŊ аĐģŅŒĐąĐžĐŧŌ“Đ° Ō›ĐžŅŅƒ", "add_url": "URL Ņ‚Đ°ŌŖĐ´Đ°Ņƒ", "added_to_archive": "ĐŅ€Ņ…Đ¸Đ˛ĐēĐĩ ĐļŅ–ĐąĐĩҀҖĐģĐŗĐĩĐŊ", diff --git a/i18n/kn.json b/i18n/kn.json index 388f06704c..111c802a1e 100644 --- a/i18n/kn.json +++ b/i18n/kn.json @@ -8,11 +8,13 @@ "actions": "ā˛•āŗā˛°ā˛ŋ➝⺆➗➺⺁", "active": "ā˛¸ā˛•āŗā˛°ā˛ŋ➝", "activity": "➚➟⺁ā˛ĩ➟ā˛ŋ➕⺆", + "activity_changed": "➚➟⺁ā˛ĩ➟ā˛ŋ➕⺆ {enabled, select, true{ā˛¸ā˛•āŗā˛°ā˛ŋā˛¯ā˛—āŗŠā˛ŗā˛ŋā˛¸ā˛˛ā˛žā˛—ā˛ŋā˛Ļāŗ†} other {➍ā˛ŋā˛ˇāŗā˛•āŗā˛°ā˛ŋā˛¯ā˛—āŗŠā˛ŗā˛ŋā˛¸ā˛˛ā˛žā˛—ā˛ŋā˛Ļāŗ†}}", "add": "➏⺇➰ā˛ŋ➏ā˛ŋ", "add_a_description": "ā˛ĩā˛ŋā˛ĩā˛°ā˛Ŗāŗ†ā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_a_location": "ā˛¸āŗā˛Ĩ➺ā˛ĩā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_a_name": "ā˛šāŗ†ā˛¸ā˛°ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_a_title": "ā˛ļāŗ€ā˛°āŗā˛ˇā˛ŋā˛•āŗ†ā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_birthday": "ā˛œā˛¨āŗā˛Žā˛Ļā˛ŋ➍ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_endpoint": "ā˛Žā˛‚ā˛Ąāŗâ€Œā˛Ēā˛žā˛¯ā˛ŋā˛‚ā˛Ÿāŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_exclusion_pattern": "ā˛šāŗŠā˛°ā˛—ā˛ŋā˛Ąāŗā˛ĩā˛ŋ➕⺆ ā˛Žā˛žā˛Ļ➰ā˛ŋā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_import_path": "ā˛†ā˛Žā˛Ļ⺁ ā˛Žā˛žā˛°āŗā˛—ā˛ĩā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", @@ -22,5 +24,6 @@ "add_path": "ā˛šā˛žā˛Ļā˛ŋā˛¯ā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_photos": "ā˛Ģāŗ‹ā˛Ÿāŗ‹ā˛—ā˛ŗā˛¨āŗā˛¨āŗ ➏⺇➰ā˛ŋ➏ā˛ŋ", "add_to": "➏⺇➰ā˛ŋ➏ā˛ŋâ€Ļ", - "add_to_album": "ā˛†ā˛˛āŗā˛Ŧā˛Žāŗâ€Œā˛—āŗ† ➏⺇➰ā˛ŋ➏ā˛ŋ" + "add_to_album": "ā˛†ā˛˛āŗā˛Ŧā˛Žāŗâ€Œā˛—āŗ† ➏⺇➰ā˛ŋ➏ā˛ŋ", + "add_to_album_bottom_sheet_added": "{album}➗⺆ ➏⺇➰ā˛ŋ➏ā˛ŋā˛Ļāŗ†" } diff --git a/i18n/ko.json b/i18n/ko.json index 01b29e6776..0c01c03263 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -8,12 +8,13 @@ "actions": "ėž‘ė—…", "active": "í™œė„ą", "activity": "활동", - "activity_changed": "ë°˜ė‘ė´ {enabled, select, true {í™œė„ąí™”} other {ëš„í™œė„ąí™”}}ë˜ė—ˆėŠĩ니다.", + "activity_changed": "í™œë™ė´ {enabled, select, true {í™œė„ąí™”} other {ëš„í™œė„ąí™”}}ë˜ė—ˆėŠĩ니다", "add": "ėļ”ę°€", "add_a_description": "네ëĒ… ėļ”ę°€", "add_a_location": "ėœ„ėš˜ ėļ”ę°€", "add_a_name": "ė´ëĻ„ ėļ”ę°€", "add_a_title": "렜ëĒŠ ėļ”ę°€", + "add_birthday": "ėƒėŧ ėļ”ę°€", "add_endpoint": "ė—”ë“œíŦė¸íŠ¸ ėļ”ę°€", "add_exclusion_pattern": "ė œė™¸ ęˇœėš™ ėļ”ę°€", "add_import_path": "氀렏ė˜Ŧ ę˛Ŋ로 ėļ”ę°€", @@ -22,137 +23,157 @@ "add_partner": "파트너 ėļ”ę°€", "add_path": "ę˛Ŋ로 ėļ”ę°€", "add_photos": "ė‚Ŧė§„ ėļ”ę°€", - "add_to": "ė•¨ë˛”ė— ėļ”ę°€â€Ļ", + "add_tag": "태그 ėļ”ę°€", + "add_to": "ė´ęŗŗė— ėļ”ę°€â€Ļ", "add_to_album": "ė•¨ë˛”ė— ėļ”ę°€", - "add_to_album_bottom_sheet_added": "{album}뗐 ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "add_to_album_bottom_sheet_already_exists": "{album}뗐 ė´ë¯¸ ėĄ´ėžŦ합니다.", + "add_to_album_bottom_sheet_added": "{album}뗐 ėļ”가됨", + "add_to_album_bottom_sheet_already_exists": "ė´ë¯¸ {album}뗐 ėžˆėŒ", + "add_to_album_bottom_sheet_some_local_assets": "ëLJ ę°œė˜ 로ėģŦ 항ëĒŠė´ ė•¨ë˛”ė— ėļ”ę°€ë˜ė§€ ė•Šė•˜ėŠĩ니다", + "add_to_album_toggle": "{album} ė„ íƒ/í•´ė œ", + "add_to_albums": "ė—ŦëŸŦ ė•¨ë˛”ė— ėļ”ę°€", + "add_to_albums_count": "ė—ŦëŸŦ ė•¨ë˛”ė— ėļ”ę°€ ({count})", "add_to_shared_album": "ęŗĩ뜠 ė•¨ë˛”ė— ėļ”ę°€", "add_url": "URL ėļ”ę°€", - "added_to_archive": "ëŗ´ę´€í•¨ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "added_to_favorites": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė— {count, number}氜 ėļ”가됨", + "added_to_archive": "ëŗ´ę´€í•¨ėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다", + "added_to_favorites": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€ë˜ė—ˆėŠĩ니다", + "added_to_favorites_count": "ėĻę˛¨ė°žę¸°ė— 항ëĒŠ {count, number}氜 ėļ”가됨", "admin": { - "add_exclusion_pattern_description": "ęˇœėš™ė— *, ** 및 ? ëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. ė´ëĻ„ė´ \"Raw\"ė¸ 디렉터ëĻŦė˜ ëĒ¨ë“  파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/Raw/**\"ëĨŧ, \".tif\"로 끝나는 ëĒ¨ë“  파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/*.tif\"ëĨŧ ė‚ŦėšŠí•˜ęŗ , ė ˆëŒ€ ę˛ŊëĄœė˜ ę˛Ŋ뚰 \"/path/to/ignore/**\"뙀 ę°™ė€ ë°Šė‹ėœŧ로 ė‚ŦėšŠí•Šë‹ˆë‹¤.", - "asset_offline_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ뗐 íŦ함된 ė´ 항ëĒŠė„ ë””ėŠ¤íŦė—ė„œ ë”ė´ėƒ ė°žė„ 눘 뗆떴 íœ´ė§€í†ĩėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다. 파ėŧė´ ëŧė´ë¸ŒëŸŦëĻŦ ë‚´ė—ė„œ ė´ë™ëœ ę˛Ŋ뚰 íƒ€ėž„ëŧė¸ė—ė„œ ėƒˆëĄœ ė—°ę˛°ëœ 항ëĒŠė„ í™•ė¸í•˜ė„¸ėš”. 항ëĒŠė„ ëŗĩė›í•˜ë ¤ëŠ´ ė•„ëž˜ė˜ 파ėŧ ę˛ŊëĄœė— Immich가 ė ‘ęˇŧ할 눘 ėžˆëŠ”ė§€ í™•ė¸í•˜ęŗ  ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė„ ė§„í–‰í•˜ė„¸ėš”.", + "add_exclusion_pattern_description": "*, **, ? ë“ąė˜ glob íŒ¨í„´ė„ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. 똈ëĨŧ ë“¤ė–´ \"Raw\" 폴더 내 ëĒ¨ë“  파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/Raw/**\"ëĨŧ, .tif 파ėŧė„ ė œė™¸í•˜ë ¤ëŠ´ \"**/*.tif\", íŠšė •í•œ ė ˆëŒ€ ę˛Ŋ로ëĨŧ ė œė™¸í•˜ë ¤ëŠ´ \"/path/to/ignore/**\" 래ëŸŧ ė‚ŦėšŠí•Šë‹ˆë‹¤.", + "admin_user": "관ëĻŦėž", + "asset_offline_description": "ė´ 항ëĒŠė€ 뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ뗐 ë“ąëĄë˜ė—ˆėœŧ나 ë””ėŠ¤íŦė—ė„œ ė°žė„ 눘 뗆떴 íœ´ė§€í†ĩėœŧ로 ė´ë™í–ˆėŠĩ니다. 파ėŧė´ ëŧė´ë¸ŒëŸŦëĻŦ ę˛Ŋ로 ë‚´ė—ė„œ ė´ë™ëœ ę˛Ŋ뚰 íƒ€ėž„ëŧė¸ė—ė„œ ėƒˆëĄœ ė¸ė‹ëœ 항ëĒŠė´ ėžˆëŠ”ė§€ í™•ė¸í•´ëŗ´ė„¸ėš”. ė´ 항ëĒŠė„ ëŗĩė›í•˜ë ¤ëŠ´ ė•„ëž˜ ę˛ŊëĄœė— Immich가 ė ‘ęˇŧ할 눘 ėžˆëŠ”ė§€ í™•ė¸í•˜ęŗ  ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ë‹¤ė‹œ 늤ėē”í•˜ė„¸ėš”.", "authentication_settings": "ė¸ėĻ 네렕", - "authentication_settings_description": "비밀번호, OAuth 및 기타 ė¸ėĻ 네렕 관ëĻŦ", - "authentication_settings_disable_all": "ëĄœęˇ¸ė¸ ėˆ˜ë‹¨ė„ ëĒ¨ë‘ ëš„í™œė„ąí™”í•˜ė‹œę˛ ėŠĩ니까? ëĄœęˇ¸ė¸ė´ ė™„ė „ížˆ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", + "authentication_settings_description": "비밀번호, OAuth 및 기타 ė¸ėĻ ė„¤ė •ė„ 관ëĻŦ합니다", + "authentication_settings_disable_all": "ëĒ¨ë“  ëĄœęˇ¸ė¸ ėˆ˜ë‹¨ė„ ëš„í™œė„ąí™”í•˜ė‹œę˛ ėŠĩ니까? ë”ė´ėƒ ëĄœęˇ¸ė¸í•  눘 ė—†ėŠĩ니다.", "authentication_settings_reenable": "ë‹¤ė‹œ í™œė„ąí™”í•˜ë ¤ëŠ´ ė„œë˛„ ëĒ…ë šė–´ëĨŧ ė‚ŦėšŠí•˜ė„¸ėš”.", "background_task_job": "밹꡸ëŧėš´ë“œ ėž‘ė—…", "backup_database": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ėƒė„ą", "backup_database_enable_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 í™œė„ąí™”", - "backup_keep_last_amount": "ëŗ´ę´€í•  ė´ė „ ë¤í”„ė˜ 눘", + "backup_keep_last_amount": "ëŗ´ę´€í•  ė´ė „ 덤프 눘", + "backup_onboarding_1_description": "개는 클ëŧėš°ë“œë‚˜ 다ëĨ¸ ëŦŧëĻŦ렁 ėœ„ėš˜ė— ëŗ´ę´€í•Šë‹ˆë‹¤.", + "backup_onboarding_2_description": "다ëĨ¸ ę¸°ę¸°ė˜ 로ėģŦ ė‚Ŧëŗ¸. ëŠ”ė¸ 파ėŧęŗŧ 로ėģŦ ë°ąė—…ė„ íŦ함합니다.", + "backup_onboarding_3_description": "ę°œė˜ ë°ė´í„° ė‚Ŧëŗ¸ė„ 만듭니다.", + "backup_onboarding_description": "ė†Œė¤‘í•œ ë°ė´í„°ëĨŧ ė•ˆė „í•˜ę˛Œ ëŗ´í˜¸í•˜ę¸° ėœ„í•´ 3-2-1 ë°ąė—… ė „ëžĩ ė‚ŦėšŠė„ ęļŒėžĨ합니다. ImmichëĨŧ ë°ąė—…í•  때 ė—…ëĄœë“œí•œ ė‚Ŧė§„ 및 ë™ė˜ėƒëŋ ė•„ë‹ˆëŧ ë°ė´í„°ë˛ ė´ėŠ¤ë„ 함ęģ˜ ëŗ´ę´€í•´ė•ŧ 한다는 ė ė„ ėžŠė§€ ë§ˆė„¸ėš”.", + "backup_onboarding_footer": "Immich ë°ąė—…ė— 대한 ėžė„¸í•œ ë‚´ėšŠė€ ęŗĩė‹ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", + "backup_onboarding_parts_title": "3-2-1 ë°ąė—…ė´ëž€:", + "backup_onboarding_title": "ë°ąė—…", "backup_settings": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 네렕", - "backup_settings_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ė„¤ė •ė„ 관ëĻŦ합니다. 및溠: ė´ ėž‘ė—…ė€ ė§„í–‰ 및 ė‹¤íŒ¨ ė—Ŧëļ€ëĨŧ í™•ė¸í•  눘 ė—†ėŠĩ니다.", + "backup_settings_description": "ë°ė´í„°ë˛ ė´ėŠ¤ 덤프 ėŖŧ揰뙀 ëŗ´ę´€ ę¸°ę°„ė„ ė„¤ė •í•Šë‹ˆë‹¤.", "cleared_jobs": "ėž‘ė—… ė¤‘ë‹¨: {job}", - "config_set_by_file": "현ėžŦ ęĩŦė„ąė€ 네렕 파ėŧė„ í†ĩ해 ė§€ė •ë˜ė–´ ėžˆėŠĩ니다.", + "config_set_by_file": "ė„¤ė •ė´ ęĩŦė„ą 파ėŧė„ í†ĩ해 관ëĻŦë˜ęŗ  ėžˆėŠĩ니다", "confirm_delete_library": "{library} ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "confirm_delete_library_assets": "ė´ ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? Immichė—ė„œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}가 ė‚­ė œë˜ëŠ° 되돌ëĻ´ 눘 ė—†ėŠĩ니다. ė›ëŗ¸ 파ėŧė€ ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", - "confirm_email_below": "ęŗ„ė† ė§„í–‰í•˜ë ¤ëŠ´ ė•„ëž˜ė— \"{email}\" ėž…ë Ĩ", - "confirm_reprocess_all_faces": "ëĒ¨ë“  ė–ŧęĩ´ė„ ë‹¤ė‹œ 래ëĻŦí•˜ė‹œę˛ ėŠĩ니까? ė´ëĻ„ė´ ė§€ė •ëœ ė¸ëŦŧė„ íŦ함한 ëĒ¨ë“  ė¸ëŦŧė´ ė‚­ė œëŠë‹ˆë‹¤.", - "confirm_user_password_reset": "{user}ë‹˜ė˜ 비밀번호ëĨŧ ėžŦė„¤ė •í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_delete_library_assets": "ė´ ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? Immichė—ė„œ {count, plural, one {항ëĒŠ #개가} other {항ëĒŠ #개가}} ė‚­ė œë˜ëŠ° 되돌ëĻ´ 눘 ė—†ėŠĩ니다. ė›ëŗ¸ 파ėŧė€ ë””ėŠ¤íŦ뗐 ë‚¨ė•„ ėžˆėŠĩ니다.", + "confirm_email_below": "ęŗ„ė†í•˜ë ¤ëŠ´ ė•„ëž˜ė— \"{email}\"ė„(ëĨŧ) ėž…ë Ĩí•˜ė„¸ėš”", + "confirm_reprocess_all_faces": "ëĒ¨ë“  ė–ŧęĩ´ė„ ë‹¤ė‹œ 래ëĻŦí•˜ė‹œę˛ ėŠĩ니까? ė´ëĻ„ė´ ė§€ė •ëœ ė¸ëŦŧ도 ė´ˆę¸°í™”ëŠë‹ˆë‹¤.", + "confirm_user_password_reset": "{user}ë‹˜ė˜ 비밀번호ëĨŧ ė´ˆę¸°í™”í•˜ė‹œę˛ ėŠĩ니까?", "confirm_user_pin_code_reset": "{user}ë‹˜ė˜ PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í•˜ė‹œę˛ ėŠĩ니까?", - "create_job": "ėž‘ė—… ėƒė„ą", + "create_job": "냈 ėž‘ė—…", "cron_expression": "Cron í‘œí˜„ė‹", - "cron_expression_description": "Cron í˜•ė‹ė„ ė‚ŦėšŠí•˜ė—Ŧ 늤ėē” ėŖŧ기ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ėžė„¸í•œ ë‚´ėšŠęŗŧ ė˜ˆė‹œëŠ” Crontab GuruëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", - "cron_expression_presets": "Cron í‘œí˜„ė‹ ė‚Ŧė „ 네렕", + "cron_expression_description": "Cron í‘œí˜„ė‹ėœŧ로 늤ėē” ėŖŧ기ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ėžė„¸í•œ ë‚´ėšŠė€ ë‹¤ėŒė„ ė°¸ėĄ°í•˜ė„¸ėš”, Crontab Guru", + "cron_expression_presets": "Cron í‘œí˜„ė‹ 프ëĻŦė…‹", "disable_login": "ëĄœęˇ¸ė¸ ëš„í™œė„ąí™”", - "duplicate_detection_job_description": "揰溄 학ėŠĩė„ í†ĩ해 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĨŧ ę°ė§€í•Šë‹ˆë‹¤. ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ í™œė„ąí™”ë˜ė–´ ėžˆė–´ė•ŧ 합니다.", - "exclusion_pattern_description": "ė œė™¸ ęˇœėš™ė„ ė‚ŦėšŠí•˜ė—Ŧ íŠšė • 파ėŧęŗŧ 폴더ëĨŧ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė—ė„œ ė œė™¸í•  눘 ėžˆėŠĩ니다. ę°€ė ¸ė˜¤ę¸° ė›í•˜ė§€ ė•ŠëŠ” 파ėŧ(RAW 파ėŧ 등)ė´ í´ë”ė— ėĄ´ėžŦ하는 ę˛Ŋ뚰 ėœ ėšŠí•Šë‹ˆë‹¤.", + "duplicate_detection_job_description": "揰溄 학ėŠĩėœŧ로 뜠ė‚Ŧ한 ė´ë¯¸ė§€ëĨŧ ę°ė§€í•Šë‹ˆë‹¤. ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ í™œė„ąí™”ë˜ė–´ ėžˆė–´ė•ŧ 합니다", + "exclusion_pattern_description": "ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė—ė„œ ė œė™¸í•  파ėŧė´ë‚˜ 폴더 ęˇœėš™ė„ ė„¤ė •í•Šë‹ˆë‹¤. í´ë”ė— ė›í•˜ė§€ ė•ŠëŠ” 파ėŧ(RAW 파ėŧ 등)ė´ 함ęģ˜ ėĄ´ėžŦ하는 ę˛Ŋ뚰 ėœ ėšŠí•Šë‹ˆë‹¤.", "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": "ę˛Ŋęŗ : ė´ ėž‘ė—…ė€ 해당 ė‚ŦėšŠėžė™€ ꡸ ė‚ŦėšŠėžę°€ ė†Œėœ í•œ ëĒ¨ë“  항ëĒŠė„ ėĻ‰ė‹œ ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėœŧ늰, ė‚­ė œëœ 파ėŧė€ ëŗĩęĩŦ할 눘 ė—†ėŠĩ니다.", + "force_delete_user_warning": "ę˛Ŋęŗ : ė´ ėž‘ė—…ė€ 해당 ė‚ŦėšŠėžė˜ ęŗ„ė •ęŗŧ ëĒ¨ë“  항ëĒŠė„ ėĻ‰ė‹œ ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėœŧ늰 ė‚­ė œëœ 파ėŧė€ ëŗĩęĩŦ할 눘 ė—†ėŠĩ니다.", "image_format": "í˜•ė‹", - "image_format_description": "WebP는 JPEGëŗ´ë‹¤ 파ėŧ íŦ기가 ėž‘ė§€ë§Œ, ė¸ėŊ”ë”Šė— 더 ë§Žė€ ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤.", - "image_fullsize_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 렄랴 íŦ기 ė´ë¯¸ė§€ëĄœ, 확대 ëŗ´ę¸° ė‹œ ė‚ŦėšŠëŠë‹ˆë‹¤.", + "image_format_description": "WebP는 JPEGëŗ´ë‹¤ 파ėŧ íŦ기가 ėž‘ė§€ë§Œ ė¸ėŊ”딊 ė†ë„ę°€ 느ëĻŊ니다.", + "image_fullsize_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ 렄랴 íŦ기 ė´ë¯¸ė§€. ė´ë¯¸ė§€ 확대 ė‹œ ė‚ŦėšŠëŠë‹ˆë‹¤.", "image_fullsize_enabled": "렄랴 íŦ기 ė´ë¯¸ė§€ ėƒė„ą í™œė„ąí™”", - "image_fullsize_enabled_description": "ė›šė— ė í•Ší•˜ė§€ ė•Šė€ í˜•ė‹ė¸ ę˛Ŋ뚰 렄랴 íŦ기 ė´ë¯¸ė§€ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤. \"내ėžĨ 미ëĻŦëŗ´ę¸° ėš°ė„  ė‚ŦėšŠ\"ė´ í™œė„ąí™”ë˜ė–´ ėžˆėœŧ늴, ëŗ€í™˜ ė—†ė´ 내ėžĨ된 미ëĻŦëŗ´ę¸°ëĨŧ 그대로 ė‚ŦėšŠí•Šë‹ˆë‹¤. JPEGęŗŧ ę°™ė€ ė›š ėšœí™”ė ė¸ í˜•ė‹ė—ëŠ” 똁í–Ĩė„ ėŖŧė§€ ė•ŠėŠĩ니다.", - "image_fullsize_quality_description": "렄랴 íŦ기 ė´ë¯¸ė§€ė˜ í’ˆė§ˆ (1~100). ėˆĢėžę°€ ë†’ė„ėˆ˜ëĄ í’ˆė§ˆė´ ėĸ‹ė§€ë§Œ 파ėŧ íŦ기도 ėģ¤ė§‘니다.", + "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_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_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_settings_description": "ė´ë¯¸ė§€ė˜ í’ˆė§ˆ 및 래ëĻŦ ë°Šė‹ė„ 관ëĻŦ합니다.", + "image_thumbnail_description": "ëŠ”íƒ€ë°ė´í„°ę°€ ė œęą°ëœ ėž‘ė€ ė„Ŧ네ėŧ. ëŠ”ė¸ íƒ€ėž„ëŧė¸ ë“ąė—ė„œ ė—ŦëŸŦ ė‚Ŧė§„ė„ í‘œė‹œí•  때 ė‚ŦėšŠëŠë‹ˆë‹¤.", + "image_thumbnail_quality_description": "ė„Ŧ네ėŧ í’ˆė§ˆė„ 1ė—ė„œ 100 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė„ ë†’ė´ëŠ´ í’ˆė§ˆė´ ėĸ‹ė•„ė§€ė§€ë§Œ 파ėŧ íŦ기가 ėģ¤ė§€ęŗ  ė•ą ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", "image_thumbnail_title": "ė„Ŧ네ėŧ 네렕", "job_concurrency": "{job} ë™ė‹œė„ą", "job_created": "ėž‘ė—…ė´ ėƒė„ąë˜ė—ˆėŠĩ니다.", "job_not_concurrency_safe": "ė´ ėž‘ė—…ė€ ë™ė‹œ ė‹¤í–‰ė— ė•ˆė „í•˜ė§€ ė•ŠėŠĩ니다.", "job_settings": "ėž‘ė—… 네렕", - "job_settings_description": "ėž‘ė—… ë™ė‹œė„ą 관ëĻŦ", + "job_settings_description": "각 ėž‘ė—…ė—ė„œ ë™ė‹œė— 래ëĻŦ할 항ëĒŠ 눘ëĨŧ ė§€ė •í•Šë‹ˆë‹¤.", "job_status": "ėž‘ė—… ėƒíƒœ", "jobs_delayed": "{jobCount, plural, other {#氜}} 맀뗰", "jobs_failed": "{jobCount, plural, other {#氜}} ė‹¤íŒ¨", "library_created": "{library} ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒė„ąí–ˆėŠĩ니다.", "library_deleted": "ëŧė´ë¸ŒëŸŦëĻŦ가 ė‚­ė œë˜ė—ˆėŠĩ니다.", "library_import_path_description": "氀렏ė˜Ŧ 폴더ëĨŧ ė§€ė •í•˜ė„¸ėš”. 해당 í´ë”ė™€ ëĒ¨ë“  í•˜ėœ„ í´ë”ė—ė„œ ė´ë¯¸ė§€ė™€ ë™ė˜ėƒė„ 늤ėē”핊니다.", - "library_scanning": "ėŖŧ揰렁 늤ėē”", - "library_scanning_description": "ëŧė´ë¸ŒëŸŦëĻŦ ėŖŧ揰렁 늤ėē” ęĩŦė„ą", - "library_scanning_enable_description": "ëŧė´ë¸ŒëŸŦëĻŦ ėŖŧ揰렁 늤ėē” í™œė„ąí™”", + "library_scanning": "ėŖŧę¸°ė ė¸ 늤ėē”", + "library_scanning_description": "ėŖŧę¸°ė ė¸ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”ė„ ęĩŦė„ąí•Šë‹ˆë‹¤.", + "library_scanning_enable_description": "ėŖŧę¸°ė ė¸ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” í™œė„ąí™”", "library_settings": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ", - "library_settings_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 네렕 관ëĻŦ", + "library_settings_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” ë° ę°ė‹œ 기ëŠĨė„ ė„¤ė •í•Šë‹ˆë‹¤.", "library_tasks_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦė—ė„œ 냈 항ëĒŠ 또는 ëŗ€ę˛Ŋ된 항ëĒŠė„ 늤ėē”", "library_watching_enable_description": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ 파ėŧ ëŗ€ę˛Ŋ ę°ė‹œ", "library_watching_settings": "ëŧė´ë¸ŒëŸŦëĻŦ ę°ė‹œ (ė‹¤í—˜ė )", - "library_watching_settings_description": "파ėŧ ëŗ€ę˛Ŋ ėžë™ 氐맀", - "logging_enable_description": "로깅 í™œė„ąí™”", + "library_watching_settings_description": "ëŗ€ę˛Ŋ된 파ėŧė„ ėžë™ėœŧ로 ę°ė‹œí•Šë‹ˆë‹¤.", + "logging_enable_description": "로그 기록 í™œė„ąí™”", "logging_level_description": "í™œė„ąí™” ė‹œ ė‚ŦėšŠí•  로그 ë ˆë˛¨ė„ ė„ íƒí•Šë‹ˆë‹¤.", "logging_settings": "로깅", + "machine_learning_availability_checks": "ę°€ėšŠė„ą í™•ė¸", + "machine_learning_availability_checks_description": "ė‚ŦėšŠ 가ëŠĨ한 ë¨¸ė‹  ëŸŦ닝 ė„œë˛„ëĨŧ ėžë™ėœŧ로 ę°ė§€í•˜ęŗ  ėš°ė„ ė ėœŧ로 ė„ íƒí•Šë‹ˆë‹¤", + "machine_learning_availability_checks_enabled": "ę°€ėšŠė„ą í™•ė¸ í™œė„ąí™”", + "machine_learning_availability_checks_interval": "í™•ė¸ ėŖŧ기", + "machine_learning_availability_checks_interval_description": "ę°€ėšŠė„ą í™•ė¸ ėŖŧ기 (밀ëĻŦ봈 ë‹¨ėœ„)", + "machine_learning_availability_checks_timeout": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ", + "machine_learning_availability_checks_timeout_description": "ę°€ėšŠė„ą í™•ė¸ ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ (밀ëĻŦ봈 ë‹¨ėœ„)", "machine_learning_clip_model": "CLIP ëĒ¨ë¸", - "machine_learning_clip_model_description": "CLIP ëĒ¨ë¸ė˜ ėĸ…ëĨ˜ëŠ” ė´ęŗŗė„ ė°¸ėĄ°í•˜ė„¸ėš”. 한ęĩ­ė–´ëĨŧ íŦ함한 다ęĩ­ė–´ ę˛€ėƒ‰ė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ 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_clip_model_description": "CLIP ëĒ¨ë¸ė˜ ėĸ…ëĨ˜ëŠ” ė´ęŗŗė„ ė°¸ėĄ°í•˜ė„¸ėš”. 한ęĩ­ė–´ 등 ė—ŦëŸŦ ė–¸ė–´ëĄœ ę˛€ėƒ‰í•˜ë ¤ëŠ´ 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": "揰溄 학ėŠĩ í™œė„ąí™”", - "machine_learning_enabled_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė•„ëž˜ 네렕 ė—Ŧëļ€ė™€ ę´€ęŗ„ė—†ė´ ëĒ¨ë“  揰溄 학ėŠĩ 기ëŠĨė´ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", + "machine_learning_enabled_description": "ëš„í™œė„ąí™”í•˜ëŠ´ ė•„ëž˜ 네렕ęŗŧ ę´€ęŗ„ė—†ė´ ëĒ¨ë“  揰溄 학ėŠĩ 기ëŠĨė´ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", "machine_learning_facial_recognition": "ė–ŧęĩ´ ė¸ė‹", - "machine_learning_facial_recognition_description": "ė´ë¯¸ė§€ė—ė„œ ė–ŧęĩ´ 氐맀, ė¸ė‹ 및 ęˇ¸ëŖší™”", + "machine_learning_facial_recognition_description": "ė´ë¯¸ė§€ė—ė„œ ė–ŧęĩ´ 氐맀, ė¸ė‹ 및 ęˇ¸ëŖší™”ëĨŧ ė§„í–‰í•Šë‹ˆë‹¤.", "machine_learning_facial_recognition_model": "ė–ŧęĩ´ ė¸ė‹ ëĒ¨ë¸", - "machine_learning_facial_recognition_model_description": "íŦ揰뗐 따ëŧ 내ëĻŧ밍눜ėœŧ로 ë‚˜ė—´ëŠë‹ˆë‹¤. íŦ기가 큰 ëĒ¨ë¸ė€ 느ëĻŦęŗ  메ëǍëĻŦëĨŧ ë§Žė´ ė‚ŦėšŠí•˜ė§€ë§Œ 더 ë‚˜ė€ 결ęŗŧëĨŧ ëŗ´ėž…ë‹ˆë‹¤. ëĒ¨ë¸ė„ ëŗ€ę˛Ŋ한 ė´í›„ ëĒ¨ë“  항ëĒŠė˜ ė–ŧęĩ´ 氐맀 ėž‘ė—…ė„ ë‹¤ė‹œ ė§„í–‰í•´ė•ŧ 합니다.", + "machine_learning_facial_recognition_model_description": "íŦ揰뗐 따ëŧ 내ëĻŧ밍눜ėœŧ로 ë‚˜ė—´ëŠë‹ˆë‹¤. 큰 ëĒ¨ë¸ė€ 느ëĻŦęŗ  메ëǍëĻŦëĨŧ ë§Žė´ ė‚ŦėšŠí•˜ė§€ë§Œ 더 ë‚˜ė€ 결ęŗŧëĨŧ ëŗ´ėž…ë‹ˆë‹¤. ëĒ¨ë¸ė„ ëŗ€ę˛Ŋ한 ę˛Ŋ뚰 ëĒ¨ë“  ė´ë¯¸ė§€ė˜ ė–ŧęĩ´ 氐맀 ėž‘ė—…ė„ ë‹¤ė‹œ ė‹¤í–‰í•´ė•ŧ 합니다.", "machine_learning_facial_recognition_setting": "ė–ŧęĩ´ ė¸ė‹ í™œė„ąí™”", - "machine_learning_facial_recognition_setting_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė´ë¯¸ė§€ė—ė„œ ė–ŧęĩ´ ė¸ė‹ė„ ė§„í–‰í•˜ė§€ ė•Šėœŧ늰, íƒėƒ‰ íŽ˜ė´ė§€ė— ė¸ëŦŧ ëĒŠëĄė´ í‘œė‹œë˜ė§€ ė•ŠėŠĩ니다.", + "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_max_recognition_distance_description": "두 ė–ŧęĩ´ė„ 동ėŧė¸ėœŧ로 간ėŖŧ하는 ėž„ęŗ„ę°’ė„ 0ė—ė„œ 2 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ė´ ę°’ė„ 낮ėļ”늴 두 ė‚ŦëžŒė„ 동ėŧė¸ėœŧ로 ė¸ė‹í•˜ëŠ” ę˛ƒė„ ë°Šė§€í•  눘 ėžˆęŗ , ę°’ė„ ë†’ė´ëŠ´ 한 ė‚ŦëžŒė„ 다ëĨ¸ 두 ė‚Ŧ람ėœŧ로 ė¸ė‹í•˜ëŠ” ę˛ƒė„ ë°Šė§€í•  눘 ėžˆėŠĩ니다. 두 ė‚ŦëžŒė„ í•Šėš˜ëŠ” ę˛ƒė´ 한 ė‚ŦëžŒė„ 두 ëĒ…ėœŧ로 ëļ„ëĻŦ하는 ę˛ƒëŗ´ë‹¤ ė‰Ŧėš°ë¯€ëĄœ 가ëŠĨ한 ë‚Žė€ ę°’ė„ ė‚ŦėšŠí•˜ė„¸ėš”.", "machine_learning_min_detection_score": "ėĩœė†Œ ė‹ ëĸ°ë„ 렐눘", - "machine_learning_min_detection_score_description": "ę°ė§€ëœ ė–ŧęĩ´ė˜ ėĩœė†Œ ė‹ ëĸ°ë„ 렐눘ëĨŧ 0ė—ė„œ 1 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 낮ėœŧ늴 ë§Žė€ ė–ŧęĩ´ė„ ę°ė§€í•˜ė§€ë§Œ ėž˜ëĒģ된 결ęŗŧëĨŧ ëŗ´ėŧ 눘 ėžˆėŠĩ니다.", + "machine_learning_min_detection_score_description": "ę°ė§€ëœ ė–ŧęĩ´ė˜ ėĩœė†Œ ė‹ ëĸ°ë„ 렐눘ëĨŧ 0ė—ė„œ 1 ė‚Ŧė´ëĄœ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė„ 낮ėļ”늴 더 ë§Žė€ ė–ŧęĩ´ė„ ę°ė§€í•˜ė§€ë§Œ ėž˜ëĒģ ę°ė§€ë  가ëŠĨė„ąë„ ë†’ė•„ė§‘ë‹ˆë‹¤.", "machine_learning_min_recognized_faces": "ėĩœė†Œ ė¸ė‹ ė–ŧęĩ´", "machine_learning_min_recognized_faces_description": "ė¸ëŦŧė„ ėƒė„ąí•˜ę¸° ėœ„í•´ ė¸ė‹í•  ė–ŧęĩ´ ėˆ˜ė˜ ėĩœė†Ÿę°’ė„ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 높ėœŧ늴 ė–ŧęĩ´ ė¸ė‹ė´ ė •í™•í•´ė§€ė§€ë§Œ ę°ė§€ëœ ė–ŧęĩ´ė´ ė¸ëŦŧ뗐 í• ë‹šë˜ė§€ ė•Šė„ 가ëŠĨė„ąė´ ėĻę°€í•Šë‹ˆë‹¤.", "machine_learning_settings": "揰溄 학ėŠĩ 네렕", - "machine_learning_settings_description": "揰溄 학ėŠĩ 기ëŠĨ 및 네렕 관ëĻŦ", + "machine_learning_settings_description": "揰溄 학ėŠĩ ė‹œ ė‚ŦėšŠí•  ëĒ¨ë¸ęŗŧ 넏ëļ€ ė„¤ė •ė„ 관ëĻŦ합니다.", "machine_learning_smart_search": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰", - "machine_learning_smart_search_description": "CLIP ėž„ë˛ ë”Šė„ ė‚ŦėšŠí•œ ë‚´ėšŠ 기반 ė´ë¯¸ė§€ ę˛€ėƒ‰", + "machine_learning_smart_search_description": "CLIP ėž„ë˛ ë”Šėœŧ로 ė˜ë¯¸ 기반 ę˛€ėƒ‰ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤.", "machine_learning_smart_search_enabled": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ í™œė„ąí™”", - "machine_learning_smart_search_enabled_description": "ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė„ ėœ„í•œ ė´ë¯¸ė§€ 래ëĻŦëĨŧ ė§„í–‰í•˜ė§€ ė•ŠėŠĩ니다.", - "machine_learning_url_description": "揰溄 학ėŠĩ ė„œë˛„ė˜ URLė„ ėž…ë Ĩ합니다. URLė´ ė—ŦëŸŦ ę°œė¸ ę˛Ŋ뚰 ė˛Ģ ë˛ˆė§¸ ė„œë˛„ëļ€í„° ë§ˆė§€ë§‰ęšŒė§€ ė„ąęŗĩ렁ėœŧ로 ė‘ë‹ĩ할 ë•ŒęšŒė§€ 한 ë˛ˆė— í•˜ë‚˜ė”Š ėˆœė„œëŒ€ëĄœ ėš”ė˛­ė„ ė‹œë„í•Šë‹ˆë‹¤. ė‘ë‹ĩí•˜ė§€ ė•ŠëŠ” ė„œë˛„ëŠ” ë‹¤ė‹œ ė‚ŦėšŠ 가ëŠĨ할 ë•ŒęšŒė§€ ėŧė‹œė ėœŧ로 ė œė™¸ëŠë‹ˆë‹¤.", + "machine_learning_smart_search_enabled_description": "ëš„í™œė„ąí™”í•˜ëŠ´ ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė„ ėœ„í•œ ė´ë¯¸ė§€ 래ëĻŦëĨŧ ė§„í–‰í•˜ė§€ ė•ŠėŠĩ니다.", + "machine_learning_url_description": "揰溄 학ėŠĩ ė„œë˛„ė˜ URLė„ ė„¤ė •í•Šë‹ˆë‹¤. ė—ŦëŸŦ 개가 ėž…ë Ĩ되늴 ė˛Ģ ë˛ˆė§¸ëļ€í„° 한 ë˛ˆė— í•˜ë‚˜ė”Š ėˆœė„œëŒ€ëĄœ ė‘ë‹ĩ하는 ė„œë˛„ëĨŧ ė°žė„ ë•ŒęšŒė§€ ėš”ė˛­ė„ ė‹œë„í•Šë‹ˆë‹¤. ė‘ë‹ĩí•˜ė§€ ė•ŠëŠ” ė„œë˛„ëŠ” ë‹¤ė‹œ ė‚ŦėšŠ 가ëŠĨ할 ë•ŒęšŒė§€ ėŧė‹œė ėœŧ로 ė œė™¸ëŠë‹ˆë‹¤.", "manage_concurrency": "ë™ė‹œė„ą 관ëĻŦ", - "manage_log_settings": "로그 네렕 관ëĻŦ", + "manage_log_settings": "로그 기록 ė„¤ė •ė„ 관ëĻŦ합니다.", "map_dark_style": "다íŦ ėŠ¤íƒ€ėŧ", "map_enable_description": "ė§€ë„ 기ëŠĨ í™œė„ąí™”", "map_gps_settings": "ė§€ë„ 및 GPS 네렕", - "map_gps_settings_description": "ė§€ë„ 및 GPS (ė—­ė§€ė˜¤ėŊ”딊) 네렕 관ëĻŦ", + "map_gps_settings_description": "ė§€ë„ ė‚ŦėšŠėž ė •ė˜ 및 ė—­ė§€ė˜¤ėŊ”딊 ė„¤ė •ė„ 관ëĻŦ합니다.", "map_implications": "ė§€ë„ 기ëŠĨė€ 뙏ëļ€ íƒ€ėŧ ė„œëš„ėŠ¤(tiles.immich.cloud)뗐 ė˜ėĄ´í•Šë‹ˆë‹¤.", "map_light_style": "ëŧė´íŠ¸ ėŠ¤íƒ€ėŧ", - "map_manage_reverse_geocoding_settings": "ė—­ė§€ė˜¤ėŊ”딊 네렕 관ëĻŦ", + "map_manage_reverse_geocoding_settings": "ė—­ė§€ė˜¤ėŊ”딊 ė„¤ė •ė„ 관ëĻŦ합니다.", "map_reverse_geocoding": "ė—­ė§€ė˜¤ėŊ”딊", "map_reverse_geocoding_enable_description": "ė—­ė§€ė˜¤ėŊ”딊 í™œė„ąí™”", "map_reverse_geocoding_settings": "ė—­ė§€ė˜¤ėŊ”딊 네렕", "map_settings": "ė§€ë„", - "map_settings_description": "ė§€ë„ 네렕 관ëĻŦ", + "map_settings_description": "ė§€ë„ ė„¤ė •ė„ 관ëĻŦ합니다.", "map_style_description": "ė§€ë„ 테마 style.json URL", "memory_cleanup_job": "ėļ”ė–ĩ ė •ëĻŦ", "memory_generate_job": "ėļ”ė–ĩ ėƒė„ą", @@ -161,15 +182,29 @@ "metadata_faces_import_setting": "ė–ŧęĩ´ ę°€ė ¸ė˜¤ę¸° í™œė„ąí™”", "metadata_faces_import_setting_description": "ė‚Ŧė´ë“œėš´ 파ėŧė˜ ė´ë¯¸ė§€ EXIF ë°ė´í„°ė—ė„œ ė–ŧęĩ´ ę°€ė ¸ė˜¤ę¸°", "metadata_settings": "ëŠ”íƒ€ë°ė´í„° 네렕", - "metadata_settings_description": "ëŠ”íƒ€ë°ė´í„° 네렕 관ëĻŦ", + "metadata_settings_description": "ëŠ”íƒ€ë°ė´í„° ė„¤ė •ė„ 관ëĻŦ합니다.", "migration_job": "ë§ˆė´ęˇ¸ë ˆė´ė…˜", - "migration_job_description": "각 항ëĒŠė˜ ė„Ŧ네ėŧ 및 ė¸ëŦŧė˜ ė–ŧęĩ´ė„ ėĩœė‹  폴더 ęĩŦėĄ°ëĄœ ë§ˆė´ęˇ¸ë ˆė´ė…˜", + "migration_job_description": "각 항ëĒŠė˜ ė„Ŧ네ėŧ 및 ė¸ëŦŧė˜ ė–ŧęĩ´ė„ ėĩœė‹  폴더 ęĩŦėĄ°ė— 맞게 ë§ˆė´ęˇ¸ë ˆė´ė…˜", + "nightly_tasks_cluster_faces_setting_description": "ėƒˆëĄœ ę°ė§€ëœ ė–ŧęĩ´ė˜ ė¸ė‹ ėž‘ė—…ė„ ė‹¤í–‰í•Šë‹ˆë‹¤.", + "nightly_tasks_cluster_new_faces_setting": "냈 ė–ŧęĩ´ ęˇ¸ëŖší™”", + "nightly_tasks_database_cleanup_setting": "ë°ė´í„°ë˛ ė´ėŠ¤ ė •ëĻŦ", + "nightly_tasks_database_cleanup_setting_description": "ë°ė´í„°ë˛ ė´ėŠ¤ė—ė„œ ė˜¤ëž˜ë˜ė—ˆęą°ë‚˜ ëļˆí•„ėš”í•œ ë°ė´í„°ëĨŧ ė •ëĻŦ합니다.", + "nightly_tasks_generate_memories_setting": "ėļ”ė–ĩ ėƒė„ą", + "nightly_tasks_generate_memories_setting_description": "ė‚Ŧė§„ 및 ë™ė˜ėƒė—ė„œ ėƒˆëĄœėš´ ėļ”ė–ĩ ëĒ¨ėŒė„ ėƒė„ąí•Šë‹ˆë‹¤.", + "nightly_tasks_missing_thumbnails_setting": "누ëŊ된 ė„Ŧ네ėŧ ėƒė„ą", + "nightly_tasks_missing_thumbnails_setting_description": "ė„Ŧ네ėŧ ėƒė„ą ëŒ€ę¸°ė—´ė— 누ëŊ된 항ëĒŠė„ ėļ”ę°€í•Šë‹ˆë‹¤.", + "nightly_tasks_settings": "렕揰 ėž‘ė—… 네렕", + "nightly_tasks_settings_description": "íŠšė • ė‹œę°„ė— ėˆ˜í–‰ë˜ëŠ” ėž‘ė—…ė„ 관ëĻŦ합니다.", + "nightly_tasks_start_time_setting": "ė‹œėž‘ ė‹œę°„", + "nightly_tasks_start_time_setting_description": "ė„œë˛„ę°€ ėž‘ė—…ė„ ė‹œėž‘í•˜ëŠ” ė‹œę°„", + "nightly_tasks_sync_quota_usage_setting": "ė‚ŦėšŠëŸ‰ 동기화", + "nightly_tasks_sync_quota_usage_setting_description": "ė‚ŦėšŠėžė˜ ė €ėžĨ ęŗĩ간 í• ë‹šëŸ‰ė„ 현ėžŦ ė‚ŦėšŠëŸ‰ 기반ėœŧ로 ę°ąė‹ í•Šë‹ˆë‹¤.", "no_paths_added": "ėļ”ę°€ëœ ę˛Ŋ로 ė—†ėŒ", "no_pattern_added": "ėļ”ę°€ëœ ęˇœėš™ ė—†ėŒ", "note_apply_storage_label_previous_assets": "및溠: ė´ė „ė— ė—…ëĄœë“œí•œ 항ëĒŠė—ë„ ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ė„ ė ėšŠí•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‹¤í–‰í•Šë‹ˆë‹¤,", "note_cannot_be_changed_later": "ėŖŧė˜: ėļ”후 ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다!", "notification_email_from_address": "ëŗ´ë‚¸ ė‚Ŧ람 ė´ëŠ”ėŧ", - "notification_email_from_address_description": "ëŗ´ë‚¸ ė‚ŦëžŒė˜ ė´ëŠ”ėŧ ėŖŧė†Œ, 똈: \"Immich Photo Server \"", + "notification_email_from_address_description": "ëŗ´ë‚¸ ė‚ŦëžŒė˜ ė´ëŠ”ėŧ ėŖŧė†Œ(똈: \"Immich Photo Server \"). ė´ëŠ”ėŧ ë°œė†Ąė´ í—ˆėšŠëœ ėŖŧė†ŒëĨŧ ė‚ŦėšŠí•´ė•ŧ 합니다.", "notification_email_host_description": "ė´ëŠ”ėŧ ė„œë˛„ė˜ í˜¸ėŠ¤íŠ¸ (똈: smtp.immich.app)", "notification_email_ignore_certificate_errors": "ė¸ėĻė„œ 똤ëĨ˜ ëŦ´ė‹œ", "notification_email_ignore_certificate_errors_description": "TLS ė¸ėĻė„œ ėœ íš¨ė„ą 검ė‚Ŧ 똤ëĨ˜ ëŦ´ė‹œ (ęļŒėžĨë˜ė§€ ė•ŠėŒ)", @@ -178,175 +213,181 @@ "notification_email_sent_test_email_button": "í…ŒėŠ¤íŠ¸ ė´ëŠ”ėŧ ė „ė†Ą 및 ė €ėžĨ", "notification_email_setting_description": "ė´ëŠ”ėŧ ė•ŒëĻŧ ė „ė†Ą 네렕", "notification_email_test_email": "í…ŒėŠ¤íŠ¸ ė´ëŠ”ėŧ ė „ė†Ą", - "notification_email_test_email_failed": "í…ŒėŠ¤íŠ¸ ė´ëŠ”ėŧė„ ė „ė†Ąí•˜ė§€ ëĒģ했ėŠĩ니다. ėž…ë Ĩ한 ę°’ė„ í™•ė¸í•˜ė„¸ėš”.", + "notification_email_test_email_failed": "í…ŒėŠ¤íŠ¸ ė´ëŠ”ėŧ ė „ė†Ąė— ė‹¤íŒ¨í–ˆėŠĩ니다. ėž…ë Ĩę°’ė„ í™•ė¸í•˜ė„¸ėš”", "notification_email_test_email_sent": "í…ŒėŠ¤íŠ¸ ė´ëŠ”ėŧė´ {email}(ėœŧ)로 ė „ė†Ąë˜ė—ˆėŠĩ니다. ë°›ė€íŽ¸ė§€í•¨ė„ í™•ė¸í•˜ė„¸ėš”.", - "notification_email_username_description": "ė´ëŠ”ėŧ ė„œë˛„ ė¸ėĻ ė‹œ ė‚ŦėšŠí•  ė‚ŦėšŠėž ė´ëĻ„", + "notification_email_username_description": "ė´ëŠ”ėŧ ė„œë˛„ ė¸ėĻ ė‹œ ė‚ŦėšŠí•  ė‚ŦėšŠėžëĒ…", "notification_enable_email_notifications": "ė´ëŠ”ėŧ ė•ŒëĻŧ í™œė„ąí™”", "notification_settings": "ė•ŒëĻŧ 네렕", - "notification_settings_description": "ė´ëŠ”ėŧė„ íŦ함한 ė•ŒëĻŧ 네렕 관ëĻŦ", + "notification_settings_description": "ė´ëŠ”ėŧė„ íŦ함한 ė•ŒëĻŧ ė„¤ė •ė„ 관ëĻŦ합니다.", "oauth_auto_launch": "ėžë™ ė‹¤í–‰", - "oauth_auto_launch_description": "ëĄœęˇ¸ė¸ íŽ˜ė´ė§€ė—ė„œ ėžë™ėœŧ로 OAuth ëĄœęˇ¸ė¸ ęŗŧė • ė§„í–‰", - "oauth_auto_register": "ėžë™ ę°€ėž…", - "oauth_auto_register_description": "OAuth로 냈 ė‚ŦėšŠėžę°€ ëĄœęˇ¸ė¸í•˜ëŠ” ę˛Ŋ뚰 ėžë™ėœŧ로 ę°€ėž…", + "oauth_auto_launch_description": "ëĄœęˇ¸ė¸ íŽ˜ė´ė§€ė— ė ‘ęˇŧ ė‹œ OAuth ëĄœęˇ¸ė¸ ęŗŧė •ė„ ėžë™ėœŧ로 ė§„í–‰", + "oauth_auto_register": "ėžë™ 등록", + "oauth_auto_register_description": "OAuth ëĄœęˇ¸ė¸ 후 냈 ė‚ŦėšŠėžëĨŧ ėžë™ėœŧ로 등록합니다.", "oauth_button_text": "버íŠŧ í…ėŠ¤íŠ¸", - "oauth_client_secret_description": "OAuth 렜ęŗĩėžę°€ PKCE(Proof Key for Code Exchange)ëĨŧ ė§€ė›í•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í•„ėš”í•Šë‹ˆë‹¤.", + "oauth_client_secret_description": "OAuth 렜ęŗĩėžę°€ PKCE(Proof Key for Code Exchange, ėŊ”드 ęĩí™˜ėšŠ 검ėĻ 키)ëĨŧ ė§€ė›í•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í•„ėš”í•Šë‹ˆë‹¤.", "oauth_enable_description": "OAuth ëĄœęˇ¸ė¸", "oauth_mobile_redirect_uri": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI", - "oauth_mobile_redirect_uri_override": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI ėžŦė •ė˜", - "oauth_mobile_redirect_uri_override_description": "OAuth ęŗĩę¸‰ėžę°€ ''{callback}''ęŗŧ ę°™ė€ ëĒ¨ë°”ėŧ URIëĨŧ 렜ęŗĩí•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", + "oauth_mobile_redirect_uri_override": "ëĒ¨ë°”ėŧ ëĻŦë‹¤ė´ë ‰íŠ¸ URI ė˜¤ë˛„ëŧė´ë“œ", + "oauth_mobile_redirect_uri_override_description": "OAuth ęŗĩę¸‰ėžę°€ ''{callback}'' ë“ąė˜ ëĒ¨ë°”ėŧ URIëĨŧ í—ˆėšŠí•˜ė§€ ė•ŠëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", + "oauth_role_claim": "ė—­í•  í´ë ˆėž„", + "oauth_role_claim_description": "ėš”ė˛­í•œ í´ë ˆėž„ė„ ė‚ŦėšŠėžė˜ ė—­í• ëĄœ ėžë™ ė„¤ė •í•Šë‹ˆë‹¤. 'user' 또는 'admin'ė„ ė„ íƒí•  눘 ėžˆėŠĩ니다.", "oauth_settings": "OAuth", - "oauth_settings_description": "OAuth ëĄœęˇ¸ė¸ 네렕 관ëĻŦ", + "oauth_settings_description": "OAuth ëĄœęˇ¸ė¸ ė„¤ė •ė„ 관ëĻŦ합니다.", "oauth_settings_more_details": "ė´ 기ëŠĨ뗐 대한 ėžė„¸í•œ ë‚´ėšŠė€ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", - "oauth_storage_label_claim": "ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸” ė„ íƒ", - "oauth_storage_label_claim_description": "ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ė„ ė‚ŦėšŠėžę°€ ėž…ë Ĩ한 값ėœŧ로 ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", - "oauth_storage_quota_claim": "ėŠ¤í† ëĻŦė§€ 할당량 ė„ íƒ", - "oauth_storage_quota_claim_description": "ėŠ¤í† ëĻŦė§€ í• ë‹šëŸ‰ė„ ė‚ŦėšŠėžę°€ ėž…ë Ĩ한 값ėœŧ로 ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", - "oauth_storage_quota_default": "ėŠ¤í† ëĻŦė§€ 할당량 ę¸°ëŗ¸ę°’ (GiB)", - "oauth_storage_quota_default_description": "ėž…ë Ĩí•˜ė§€ ė•Šė€ ę˛Ŋ뚰 ė‚ŦėšŠí•  GiB ë‹¨ėœ„ė˜ ę¸°ëŗ¸ 할당량 (ëŦ´ė œí•œ í• ë‹šëŸ‰ė˜ ę˛Ŋ뚰 0 ėž…ë Ĩ)", + "oauth_storage_label_claim": "ėŠ¤í† ëĻŦė§€ ëŧ벨 í´ë ˆėž„", + "oauth_storage_label_claim_description": "í´ë ˆėž„ė˜ ę°’ė„ ė‚ŦėšŠėž ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ëĄœ ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", + "oauth_storage_quota_claim": "ėŠ¤í† ëĻŦė§€ ėšŠëŸ‰ í´ë ˆėž„", + "oauth_storage_quota_claim_description": "ėš”ė˛­í•œ ę°’ė„ ė‚ŦėšŠėž ėŠ¤í† ëĻŦė§€ 할당량ėœŧ로 ėžë™ ė„¤ė •í•Šë‹ˆë‹¤.", + "oauth_storage_quota_default": "ę¸°ëŗ¸ ėŠ¤í† ëĻŦė§€ 할당량 (GiB)", + "oauth_storage_quota_default_description": "ėž…ë Ĩí•˜ė§€ ė•Šė€ ę˛Ŋ뚰 ė‚ŦėšŠí•  GiB ë‹¨ėœ„ė˜ 할당량", "oauth_timeout": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ", "oauth_timeout_description": "ėš”ė˛­ íƒ€ėž„ė•„ė›ƒ (밀ëĻŦ봈 ë‹¨ėœ„)", "password_enable_description": "ė´ëŠ”ėŧęŗŧ 비밀번호로 ëĄœęˇ¸ė¸", "password_settings": "비밀번호 ëĄœęˇ¸ė¸", - "password_settings_description": "비밀번호 ëĄœęˇ¸ė¸ 네렕 관ëĻŦ", - "paths_validated_successfully": "ëĒ¨ë“  ę˛Ŋ로ëĨŧ ė„ąęŗĩ렁ėœŧ로 검ėĻí–ˆėŠĩ니다.", + "password_settings_description": "비밀번호 ëĄœęˇ¸ė¸ ė„¤ė •ė„ 관ëĻŦ합니다.", + "paths_validated_successfully": "ëĒ¨ë“  ę˛Ŋ로가 검ėĻë˜ė—ˆėŠĩ니다.", "person_cleanup_job": "ė¸ëŦŧ ė •ëĻŦ", "quota_size_gib": "할당량 (GiB)", - "refreshing_all_libraries": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ ë‹¤ė‹œ 늤ėē” ė¤‘...", - "registration": "관ëĻŦėž ęŗ„ė • ėƒė„ą", - "registration_description": "ë‹šė‹ ė€ ė˛Ģ ë˛ˆė§¸ ė‚ŦėšŠėžė´ëŠ° 관ëĻŦėž ęļŒí•œė´ ėžë™ėœŧ로 ëļ€ė—Ŧ됩니다. 관ëĻŦ ėž‘ė—…ęŗŧ ė‚ŦėšŠėž ėƒė„ąė´ 가ëŠĨ합니다.", - "require_password_change_on_login": "ė˛Ģ ëĄœęˇ¸ė¸ ė‹œ 비밀번호 ëŗ€ę˛Ŋ ėš”ęĩŦ", + "refreshing_all_libraries": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒˆëĄœęŗ ėš¨í•Šë‹ˆë‹¤.", + "registration": "관ëĻŦėž 등록", + "registration_description": "ė˛Ģ ë˛ˆė§¸ ė‚ŦėšŠėžė´ë¯€ëĄœ 관ëĻŦėž ęļŒí•œė´ ëļ€ė—Ŧ됩니다. 관ëĻŦ ėž‘ė—… 및 ė‚ŦėšŠėž ėƒė„ąė´ 가ëŠĨ합니다.", + "require_password_change_on_login": "ėĩœė´ˆ ëĄœęˇ¸ė¸ ė‹œ 비밀번호 ëŗ€ę˛Ŋ ėš”ęĩŦ", "reset_settings_to_default": "ė„¤ė •ė„ ę¸°ëŗ¸ę°’ėœŧ로 ëŗĩ뛐", - "reset_settings_to_recent_saved": "ë§ˆė§€ë§‰ėœŧ로 ė €ėžĨ된 네렕 ëŗĩ뛐", + "reset_settings_to_recent_saved": "ë§ˆė§€ë§‰ė— ė €ėžĨ된 네렕ėœŧ로 ëŗĩ뛐", "scanning_library": "ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē” ė¤‘", "search_jobs": "ėž‘ė—… ę˛€ėƒ‰â€Ļ", "send_welcome_email": "í™˜ė˜ ė´ëŠ”ėŧ ė „ė†Ą", "server_external_domain_settings": "뙏ëļ€ ë„ëŠ”ė¸", "server_external_domain_settings_description": "ęŗĩ氜 ęŗĩ뜠 링íŦ뗐 ė‚ŦėšŠí•  ë„ëŠ”ė¸ (http(s):// íŦ함)", "server_public_users": "ëĒ¨ë“  ė‚ŦėšŠėž", - "server_public_users_description": "ęŗĩ뜠 ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”가할 때 ëĒ¨ë“  ė‚ŦėšŠėžė˜ ė´ëĻ„ęŗŧ ė´ëŠ”ėŧė„ ëĒŠëĄė— í‘œė‹œí•Šë‹ˆë‹¤. ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 관ëĻŦėžė¸ ę˛Ŋėš°ė—ë§Œ ė‚ŦėšŠėž ëĒŠëĄė´ í‘œė‹œëŠë‹ˆë‹¤.", + "server_public_users_description": "ė‚ŦėšŠėžëĨŧ ęŗĩ뜠 ė•¨ë˛”ė— ėļ”가할 때 ëĒ¨ë“  ė‚ŦėšŠėž(ė´ëĻ„ęŗŧ ė´ëŠ”ėŧ)가 í‘œė‹œëŠë‹ˆë‹¤. ëš„í™œė„ąí™”í•˜ëŠ´ 관ëĻŦėžë§Œ ëĒŠëĄė„ ëŗŧ 눘 ėžˆėŠĩ니다.", "server_settings": "ė„œë˛„ 네렕", - "server_settings_description": "ė„œë˛„ 네렕 관ëĻŦ", + "server_settings_description": "ė„œë˛„ ė„¤ė •ė„ 관ëĻŦ합니다.", "server_welcome_message": "í™˜ė˜ ëŠ”ė‹œė§€", "server_welcome_message_description": "ëĄœęˇ¸ė¸ íŽ˜ė´ė§€ė— í‘œė‹œë˜ëŠ” ëŠ”ė‹œė§€ėž…ë‹ˆë‹¤.", "sidecar_job": "ė‚Ŧė´ë“œėš´ ëŠ”íƒ€ë°ė´í„°", "sidecar_job_description": "파ėŧ ė‹œėŠ¤í…œė—ė„œ ė‚Ŧė´ë“œėš´ ëŠ”íƒ€ë°ė´í„° 파ėŧ íƒėƒ‰ 및 동기화", "slideshow_duration_description": "ę°œëŗ„ ė‚Ŧė§„ė´ í‘œė‹œë˜ëŠ” 봈 ë‹¨ėœ„ė˜ ė‹œę°„", "smart_search_job_description": "揰溄 학ėŠĩė„ ė§„í–‰í•˜ė—Ŧ ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ 기ëŠĨ 맀뛐", - "storage_template_date_time_description": "항ëĒŠė´ ėƒė„ąëœ ë‚ ė§œė˜ íƒ€ėž„ėŠ¤íƒŦ프ëĨŧ ë‚ ė§œ 및 ė‹œę°„ ė •ëŗ´ëĄœ ė‚ŦėšŠí•Šë‹ˆë‹¤.", - "storage_template_date_time_sample": "ė‹œę°„ í˜•ė‹ 똈: {date}", + "storage_template_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_hash_verification_enabled_description": "í•´ė‹œ 검ėĻė„ í™œė„ąí™”í•Šë‹ˆë‹¤. ė´ ė„¤ė •ė˜ 똁í–Ĩė„ í™•ė‹¤ížˆ ė´í•´í•˜ė§€ ė•ŠëŠ” 한 ëš„í™œė„ąí™”í•˜ė§€ ë§ˆė„¸ėš”.", "storage_template_migration": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋ ë§ˆė´ęˇ¸ë ˆė´ė…˜", "storage_template_migration_description": "ė´ė „ė— ė—…ëĄœë“œëœ 항ëĒŠė— 현ėžŦ {template} ė ėšŠ", "storage_template_migration_info": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋė€ ëĒ¨ë“  확ėžĨėžëĨŧ ė†ŒëŦ¸ėžëĄœ ëŗ€í™˜í•˜ëŠ°, ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ ėƒˆëĄœ ė—…ëĄœë“œí•œ 항ëĒŠė—ë§Œ ė ėšŠëŠë‹ˆë‹¤. ę¸°ėĄ´ė— ė—…ëĄœë“œëœ 항ëĒŠė— ė ėšŠí•˜ë ¤ëŠ´ {job}ė„ ė‹¤í–‰í•˜ė„¸ėš”.", "storage_template_migration_job": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋ ë§ˆė´ęˇ¸ë ˆė´ė…˜ ėž‘ė—…", "storage_template_more_details": "ė´ 기ëŠĨ뗐 대한 ėžė„¸í•œ ë‚´ėšŠė€ ėŠ¤í† ëĻŦė§€ 템플ëĻŋ 및 네ëĒ…ė„ ė°¸ėĄ°í•˜ė„¸ėš”.", + "storage_template_onboarding_description_v2": "í™œė„ąí™”í•˜ëŠ´ ė‚ŦėšŠėž ė •ė˜ 템플ëĻŋ뗐 따ëŧ 파ėŧė´ ėžë™ėœŧ로 ëļ„ëĨ˜ëŠë‹ˆë‹¤. ėžė„¸í•œ ë‚´ėšŠė€ ëŦ¸ė„œëĨŧ ė°¸ėĄ°í•˜ė„¸ėš”.", "storage_template_path_length": "대ëžĩė ė¸ ę˛Ŋ로 ę¸¸ė´ ė œí•œ: {length, number}/{limit, number}", "storage_template_settings": "ėŠ¤í† ëĻŦė§€ 템플ëĻŋ", - "storage_template_settings_description": "ė—…ëĄœë“œëœ 항ëĒŠė˜ 폴더 ęĩŦėĄ° 및 파ėŧ ė´ëĻ„ 관ëĻŦ", + "storage_template_settings_description": "ė—…ëĄœë“œëœ 항ëĒŠė˜ 파ėŧëĒ… 및 폴더 ęĩŦėĄ°ëĨŧ 관ëĻŦ합니다.", "storage_template_user_label": "ė‚ŦėšŠėžė˜ ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”: {label}", "system_settings": "ė‹œėŠ¤í…œ 네렕", "tag_cleanup_job": "태그 ė •ëĻŦ", "template_email_available_tags": "템플ëĻŋ뗐 ë‹¤ėŒ ëŗ€ėˆ˜ëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다: {tags}", "template_email_if_empty": "ëš„ė–´ ėžˆëŠ” ę˛Ŋ뚰 ę¸°ëŗ¸ 템플ëĻŋė´ ė‚ŦėšŠëŠë‹ˆë‹¤.", - "template_email_invite_album": "ė•¨ë˛” ė´ˆëŒ€ėžĨ 템플ëĻŋ", + "template_email_invite_album": "ė•¨ë˛” ė´ˆëŒ€ 템플ëĻŋ", "template_email_preview": "미ëĻŦëŗ´ę¸°", "template_email_settings": "ė´ëŠ”ėŧ 템플ëĻŋ", - "template_email_update_album": "ė•¨ë˛” ė—…ë°ė´íŠ¸ ė•ˆë‚´ 템플ëĻŋ", - "template_email_welcome": "í™˜ė˜ 메ėŧ 템플ëĻŋ", + "template_email_update_album": "ė•¨ë˛” ė—…ë°ė´íŠ¸ ė•ŒëĻŧ 템플ëĻŋ", + "template_email_welcome": "í™˜ė˜ ëŠ”ė‹œė§€ 템플ëĻŋ", "template_settings": "ė•ŒëĻŧ 템플ëĻŋ", - "template_settings_description": "ė•ŒëĻŧė„ ėœ„í•œ ė‚ŦėšŠėž 맀렕 템플ëĻŋė„ 관ëĻŦ합니다.", - "theme_custom_css_settings": "ė‚ŦėšŠėž ė •ė˜ CSS", - "theme_custom_css_settings_description": "Immich뗐 ė ėšŠí•  ė‚ŦėšŠėž ė •ė˜ CSS(Cascading Style Sheets) 네렕", + "template_settings_description": "ė•ŒëĻŧ뗐 ė‚ŦėšŠë˜ëŠ” ė‚ŦėšŠėž 맀렕 템플ëĻŋė„ 관ëĻŦ합니다.", + "theme_custom_css_settings": "ė‚ŦėšŠėž 맀렕 CSS", + "theme_custom_css_settings_description": "CSS로 Immichė˜ ë””ėžė¸ė„ ė‚ŦėšŠėž ė •ė˜í•  눘 ėžˆėŠĩ니다.", "theme_settings": "테마 네렕", - "theme_settings_description": "Immich ė›š ė¸í„°íŽ˜ė´ėŠ¤ ė‚ŦėšŠėž ė •ė˜", + "theme_settings_description": "Immich ė›š ė¸í„°íŽ˜ė´ėŠ¤ëĨŧ ė‚ŦėšŠėž ė •ė˜í•Šë‹ˆë‹¤.", "thumbnail_generation_job": "ė„Ŧ네ėŧ ėƒė„ą", - "thumbnail_generation_job_description": "각 항ëĒŠė— 대한 큰 ė„Ŧ네ėŧ, ėž‘ė€ ė„Ŧ네ėŧ, 흐ëϰ ė„Ŧ네ėŧ 및 ė¸ëŦŧ ė„Ŧ네ėŧ ėƒė„ą", + "thumbnail_generation_job_description": "각 항ëĒŠ 및 ė¸ëŦŧ뗐 대해 íŦęŗ  ėž‘ė€ ė¸ë„¤ėŧ, 흐ëĻŋ한 ė¸ë„¤ėŧ ėƒė„ą", "transcoding_acceleration_api": "ę°€ė† API", - "transcoding_acceleration_api_description": "íŠ¸ëžœėŠ¤ėŊ”딊 ę°€ė†ė„ ėœ„í•´ 揰揰뙀 ėƒí˜¸ ėž‘ėšŠí•  APIėž…ë‹ˆë‹¤. ė´ ė„¤ė •ė€ 'ėĩœė„ ė˜ 노ë Ĩ'ėœŧ로, ė‹¤íŒ¨ ė‹œ ė†Œí”„íŠ¸ė›¨ė–´ íŠ¸ëžœėŠ¤ėŊ”ë”Šė„ ė‚ŦėšŠí•Šë‹ˆë‹¤. VP9ė˜ ėž‘ë™ ė—Ŧëļ€ëŠ” í•˜ë“œė›¨ė–´ė— 따ëŧ ë‹Ŧëŧ마 눘 ėžˆėŠĩ니다.", + "transcoding_acceleration_api_description": "íŠ¸ëžœėŠ¤ėŊ”딊 ę°€ė†ė— ė‚ŦėšŠí•  APIëĨŧ ė§€ė •í•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ 'best effort' ë°Šė‹ėœŧ로 ë™ėž‘í•˜ëŠ°, ė‹¤íŒ¨ ė‹œ ė†Œí”„íŠ¸ė›¨ė–´ íŠ¸ëžœėŠ¤ėŊ”딊ėœŧ로 ė „í™˜ëŠë‹ˆë‹¤. í•˜ë“œė›¨ė–´ė— 따ëŧ VP9ė€ ė§€ė›ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", "transcoding_acceleration_nvenc": "NVENC (NVIDIA GPU í•„ėš”)", - "transcoding_acceleration_qsv": "í€ĩ ė‹ąíŦ (7ė„¸ëŒ€ ė´ėƒ Intel CPU í•„ėš”)", - "transcoding_acceleration_rkmpp": "RKMPP (Rockchip SOCs만 해당)", + "transcoding_acceleration_qsv": "Quick Sync (ė¸í…” 7ė„¸ëŒ€ ė´ėƒ CPU í•„ėš”)", + "transcoding_acceleration_rkmpp": "RKMPP (Rockchip SoC í•„ėš”)", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "í—ˆėšŠëœ ė˜¤ë””ė˜¤ ėŊ”덱", - "transcoding_accepted_audio_codecs_description": "íŠ¸ëžœėŠ¤ėŊ”ë”Ší•˜ė§€ ė•Šė„ ė˜¤ë””ė˜¤ ėŊ”ëąė„ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ė •ėą…ė—ë§Œ ė ėšŠëŠë‹ˆë‹¤.", - "transcoding_accepted_containers": "í—ˆėšŠëœ ėģ¨í…Œė´ë„ˆ", - "transcoding_accepted_containers_description": "MP4로 ëŗ€ę˛Ŋí•˜ė§€ ė•Šė„ ë™ė˜ėƒ ėģ¨í…Œė´ë„ˆ(확ėžĨėž)ëĨŧ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ė •ėą…ė—ë§Œ ė ėšŠëŠë‹ˆë‹¤.", - "transcoding_accepted_video_codecs": "í—ˆėšŠëœ ë™ė˜ėƒ ėŊ”덱", - "transcoding_accepted_video_codecs_description": "íŠ¸ëžœėŠ¤ėŊ”ë”Ší•˜ė§€ ė•Šė„ ë™ė˜ėƒ ėŊ”ëąė„ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ė •ėą…ė—ë§Œ ė ėšŠëŠë‹ˆë‹¤.", - "transcoding_advanced_options_description": "대ëļ€ëļ„ė˜ ė‚ŦėšŠėžę°€ ëŗ€ę˛Ŋ할 í•„ėš”ę°€ ė—†ëŠ” ė˜ĩė…˜", + "transcoding_accepted_audio_codecs": "í—ˆėšŠ ė˜¤ë””ė˜¤ ėŊ”덱", + "transcoding_accepted_audio_codecs_description": "íŠ¸ëžœėŠ¤ėŊ”ë”Šė—ė„œ ė œė™¸í•  ė˜¤ë””ė˜¤ ėŊ”ëąė„ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ę¸°ė¤€ė—ė„œë§Œ ė‚ŦėšŠëŠë‹ˆë‹¤.", + "transcoding_accepted_containers": "í—ˆėšŠ ėģ¨í…Œė´ë„ˆ", + "transcoding_accepted_containers_description": "MP4로 ėžŦë‹¤ė¤‘í™”(remux)í•˜ė§€ ė•Šė„ ėģ¨í…Œė´ë„ˆ íŦë§ˇė„ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ę¸°ė¤€ė—ė„œë§Œ ė‚ŦėšŠëŠë‹ˆë‹¤.", + "transcoding_accepted_video_codecs": "í—ˆėšŠ ë™ė˜ėƒ ėŊ”덱", + "transcoding_accepted_video_codecs_description": "íŠ¸ëžœėŠ¤ėŊ”ë”Šė—ė„œ ė œė™¸í•  ë™ė˜ėƒ ėŊ”ëąė„ ė„ íƒí•Šë‹ˆë‹¤. ė´ ė„¤ė •ė€ íŠšė • íŠ¸ëžœėŠ¤ėŊ”딊 ę¸°ė¤€ė—ė„œë§Œ ė‚ŦėšŠëŠë‹ˆë‹¤.", + "transcoding_advanced_options_description": "대ëļ€ëļ„ė˜ ė‚ŦėšŠėžę°€ ëŗ€ę˛Ŋ할 í•„ėš”ę°€ ė—†ëŠ” 네렕", "transcoding_audio_codec": "ė˜¤ë””ė˜¤ ėŊ”덱", - "transcoding_audio_codec_description": "Opus는 가ėžĨ ėĸ‹ė€ í’ˆė§ˆė˜ ė˜ĩė…˜ė´ė§€ë§Œ 기기 및 ė†Œí”„íŠ¸ė›¨ė–´ę°€ ė˜¤ëž˜ëœ ę˛Ŋ뚰 í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", - "transcoding_bitrate_description": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ 봈ęŗŧ하는 ë™ė˜ėƒ 또는 í—ˆėšŠë˜ė§€ ė•ŠëŠ” í˜•ė‹ė˜ ë™ė˜ėƒ", - "transcoding_codecs_learn_more": "ė—Ŧę¸°ė—ė„œ ė‚ŦėšŠë˜ëŠ” ėšŠė–´ė— 대한 ėžė„¸í•œ ë‚´ėšŠė€ FFmpeg ëŦ¸ė„œė˜ H.264 ėŊ”덱, HEVC ėŊ”덱 및 VP9 ėŊ”덱 항ëĒŠė„ ė°¸ėĄ°í•˜ė„¸ėš”.", + "transcoding_audio_codec_description": "Opus는 í’ˆė§ˆė´ 가ėžĨ ėĸ‹ė§€ë§Œ, ęĩŦ형 기기나 ė†Œí”„íŠ¸ė›¨ė–´ė—ė„œ í˜¸í™˜ė„ąė´ ë‚Žė•„ė§ˆ 눘 ėžˆėŠĩ니다.", + "transcoding_bitrate_description": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ 봈ęŗŧ하거나 í—ˆėšŠë˜ė§€ ė•Šė€ í˜•ė‹ė˜ ë™ė˜ėƒ", + "transcoding_codecs_learn_more": "ė´ęŗŗė—ė„œ ė‚ŦėšŠë˜ëŠ” ėšŠė–´ė— 대한 ėžė„¸í•œ ë‚´ėšŠė€ FFmpeg ëŦ¸ė„œė˜ H.264 ėŊ”덱, HEVC ėŊ”덱 및 VP9 ėŊ”덱 항ëĒŠė„ ė°¸ėĄ°í•˜ė„¸ėš”.", "transcoding_constant_quality_mode": "Constant quality mode", - "transcoding_constant_quality_mode_description": "ICQ는 CQPëŗ´ë‹¤ ë‚˜ė€ ė„ąëŠĨė„ ëŗ´ė´ë‚˜ ėŧëļ€ ę¸°ę¸°ė˜ í•˜ë“œė›¨ė–´ ę°€ė†ė—ė„œ ė§€ė›ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. ė´ ė˜ĩė…˜ė„ ė„¤ė •í•˜ëŠ´ í’ˆė§ˆ 기반 ė¸ėŊ”딊 ė‹œ ė§€ė •ëœ ëĒ¨ë“œëĨŧ ėš°ė„ ė ėœŧ로 ė‚ŦėšŠí•Šë‹ˆë‹¤. NVENCė—ė„œëŠ” ICQëĨŧ ė§€ė›í•˜ė§€ ė•Šė•„ ė´ ė„¤ė •ė´ ė ėšŠë˜ė§€ ė•ŠėŠĩ니다.", + "transcoding_constant_quality_mode_description": "ICQ는 CQPëŗ´ë‹¤ ėš°ėˆ˜í•˜ë‚˜ ėŧëļ€ í•˜ë“œė›¨ė–´ ę°€ė† ėžĨėš˜ė—ė„œëŠ” ė§€ė›ë˜ė§€ ė•ŠėŠĩ니다. ė´ ė˜ĩė…˜ė„ ė„¤ė •í•˜ëŠ´ í’ˆė§ˆ 기반 ė¸ėŊ”딊 ė§€ė •ëœ ëĒ¨ë“œëĨŧ ėš°ė„  ė‚ŦėšŠí•Šë‹ˆë‹¤. NVENC는 ICQëĨŧ ė§€ė›í•˜ė§€ ė•Šė•„ ė´ ė„¤ė •ė´ ëŦ´ė‹œëŠë‹ˆë‹¤.", "transcoding_constant_rate_factor": "Constant rate factor (-crf)", - "transcoding_constant_rate_factor_description": "ėŧë°˜ė ėœŧ로 H.264는 23, HEVC는 28, VP9는 31, AV1는 35ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. ę°’ė´ 낮ėœŧ늴 í’ˆė§ˆė´ í–Ĩėƒë˜ė§€ë§Œ 파ėŧ íŦ기가 ėĻę°€í•Šë‹ˆë‹¤.", + "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_acceleration_description": "(ė‹¤í—˜ė ) íŠ¸ëžœėŠ¤ėŊ”딊 ė†ë„ę°€ 뚨ëŧė§€ė§€ë§Œ 동ėŧ ëš„íŠ¸ë ˆė´íŠ¸ė—ė„œ í’ˆė§ˆė´ ėƒëŒ€ė ėœŧ로 ė €í•˜ë  눘 ėžˆėŠĩ니다.", "transcoding_hardware_decoding": "í•˜ë“œė›¨ė–´ 디ėŊ”딊", - "transcoding_hardware_decoding_setting_description": "ė¸ėŊ”딊 ę°€ė†ė„ ėœ„í•´ ė—”ë“œ íˆŦ ė—”ë“œ ę°€ė†ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤. ëĒ¨ë“  ë™ė˜ėƒė—ė„œ ėž‘ë™í•˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", - "transcoding_max_b_frames": "ėĩœëŒ€ B í”„ë ˆėž„", - "transcoding_max_b_frames_description": "ę°’ė´ 높ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ė§€ë§Œ ė¸ėŊ”딊 ė†ë„ę°€ ė €í•˜ëŠë‹ˆë‹¤. ė˜¤ëž˜ëœ ę¸°ę¸°ė˜ í•˜ë“œė›¨ė–´ ę°€ė†ęŗŧ í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 B í”„ë ˆėž„ė„ ëš„í™œė„ąí™”í•˜ëŠ°, -1ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", + "transcoding_hardware_decoding_setting_description": "ėĸ…단간 ę°€ė†ėœŧ로 디ėŊ”딊ëļ€í„° ė¸ėŊ”ë”ŠęšŒė§€ 렄랴 ęŗŧė •ė„ ę°€ė†í•Šë‹ˆë‹¤. ėŧëļ€ ë™ė˜ėƒė—ė„œëŠ” ėž‘ë™í•˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", + "transcoding_max_b_frames": "ėĩœëŒ€ B-í”„ë ˆėž„", + "transcoding_max_b_frames_description": "ę°’ė„ ë†’ė´ëŠ´ ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ė§€ë§Œ ė¸ėŊ”딊 ė†ë„ę°€ ëŠë ¤ė§‘ë‹ˆë‹¤. ė˜¤ëž˜ëœ ėžĨėš˜ė˜ í•˜ë“œė›¨ė–´ ę°€ė†ęŗŧ í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. 0ė„ ėž…ë Ĩ하면 B-í”„ë ˆėž„ė„ ëš„í™œė„ąí™”í•˜ęŗ , -1ė„ ėž…ë Ĩ하면 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", "transcoding_max_bitrate": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸", - "transcoding_max_bitrate_description": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ ė§€ė •í•˜ëŠ´ í’ˆė§ˆė´ ėŧëļ€ ė €í•˜ë˜ė§€ë§Œ 파ėŧ íŦ기가 ė˜ˆė¸Ą 가ëŠĨ한 ėˆ˜ė¤€ėœŧ로 ėŧė •í•˜ę˛Œ ėœ ė§€ëŠë‹ˆë‹¤. ėŧë°˜ė ėœŧ로 720p 揰뤀 VP9 및 HEVC는 2600 kbit/s, H.264는 4500 kbit/sëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", + "transcoding_max_bitrate_description": "ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ ė§€ė •í•˜ëŠ´ 파ėŧ íŦ기ëĨŧ ėŧė •í•˜ę˛Œ ėĄ°ė ˆí•  눘 ėžˆė§€ë§Œ í’ˆė§ˆė´ ë‹¤ė†Œ ė €í•˜ë  눘 ėžˆėŠĩ니다. ėŧë°˜ė ėœŧ로 720p 揰뤀 VP9뙀 HEVC는 2600kbit/sëĨŧ, H.264는 4500kbit/sëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. 0ė„ ėž…ë Ĩ하면 ëš„í™œė„ąí™”ëŠë‹ˆë‹¤.", "transcoding_max_keyframe_interval": "ėĩœëŒ€ í‚¤í”„ë ˆėž„ 간격", - "transcoding_max_keyframe_interval_description": "í‚¤í”„ë ˆėž„ ė‚Ŧė´ ėĩœëŒ€ í”„ë ˆėž„ ęą°ëĻŦëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 낮ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ ė €í•˜ë˜ė§€ë§Œ ę˛€ėƒ‰ ė‹œę°„ė´ ę°œė„ ë˜ęŗ  ëš ëĨ¸ ė›€ė§ėž„ė´ ėžˆëŠ” ėžĨëŠ´ė—ė„œ í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", - "transcoding_optimal_description": "ëĒŠí‘œ í•´ėƒë„ëŗ´ë‹¤ ë†’ė€ ë™ė˜ėƒ 또는 í—ˆėšŠë˜ė§€ ė•ŠëŠ” í˜•ė‹ė˜ ë™ė˜ėƒ", - "transcoding_policy": "íŠ¸ëžœėŠ¤ėŊ”드 ė •ėą…", + "transcoding_max_keyframe_interval_description": "í‚¤í”„ë ˆėž„ 간 ėĩœëŒ€ í”„ë ˆėž„ ę°„ę˛Šė„ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė„ 낮ėļ”늴 ė••ėļ• íš¨ėœ¨ė€ ë–¨ė–´ė§€ė§€ë§Œ íƒėƒ‰ ė†ë„ę°€ 뚨ëŧ맀溠 ė›€ė§ėž„ė´ ë§Žė€ ėžĨëŠ´ė—ė„œ í’ˆė§ˆė´ í–Ĩėƒë  눘 ėžˆėŠĩ니다. 0ė„ ėž…ë Ĩ하면 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", + "transcoding_optimal_description": "ëĒŠí‘œ í•´ėƒë„ëĨŧ 봈ęŗŧ하거나 í—ˆėšŠë˜ė§€ ė•Šė€ íŦë§ˇė˜ ë™ė˜ėƒ", + "transcoding_policy": "íŠ¸ëžœėŠ¤ėŊ”드 揰뤀", "transcoding_policy_description": "íŠ¸ëžœėŠ¤ėŊ”딊 ëŒ€ėƒ ë™ė˜ėƒ 네렕", - "transcoding_preferred_hardware_device": "ė„ í˜¸í•˜ëŠ” í•˜ë“œė›¨ė–´ 기기", - "transcoding_preferred_hardware_device_description": "í•˜ë“œė›¨ė–´ íŠ¸ëžœėŠ¤ėŊ”ë”Šė— ė‚ŦėšŠí•  dri 노드ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. (VAAPI뙀 QSV만 해당)", + "transcoding_preferred_hardware_device": "ę¸°ëŗ¸ í•˜ë“œė›¨ė–´ ėžĨėš˜", + "transcoding_preferred_hardware_device_description": "(VAAPI 및 QSVė¸ ę˛Ŋ뚰) í•˜ë“œė›¨ė–´ íŠ¸ëžœėŠ¤ėŊ”ë”Šė— ė‚ŦėšŠí•  dri 노드ëĨŧ ė§€ė •í•Šë‹ˆë‹¤.", "transcoding_preset_preset": "프ëĻŦė…‹ (-preset)", - "transcoding_preset_preset_description": "ė••ėļ• ė†ë„ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. 동ėŧ ëš„íŠ¸ë ˆė´íŠ¸ ę¸°ė¤€ė—ė„œ 느ëϰ ė†ë„ëĨŧ ė„ íƒí•˜ëŠ´ 파ėŧ íŦ기가 ę°ė†Œí•˜ęŗ  í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. VP9는 'faster' ė´ėƒė˜ ė†ë„ę°€ ė ėšŠë˜ė§€ ė•ŠėŠĩ니다.", - "transcoding_reference_frames": "ė°¸ėĄ° í”„ë ˆėž„", - "transcoding_reference_frames_description": "íŠšė • í”„ë ˆėž„ė„ ė••ėļ•í•  때 ė°¸ėĄ°í•˜ëŠ” í”„ë ˆėž„ 눘ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė´ 높ėœŧ늴 ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ë‚˜ ė¸ėŊ”딊 ė†ë„ę°€ ė €í•˜ëŠë‹ˆë‹¤. 0ė„ ėž…ë Ĩ한 ę˛Ŋ뚰 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", - "transcoding_required_description": "í—ˆėšŠëœ í˜•ė‹ė´ ė•„ë‹Œ ë™ė˜ėƒë§Œ", - "transcoding_settings": "ë™ė˜ėƒ íŠ¸ëžœėŠ¤ėŊ”딊 네렕", - "transcoding_settings_description": "íŠ¸ëžœėŠ¤ėŊ”딊할 ë™ė˜ėƒ 및 래ëĻŦ 방법 관ëĻŦ", + "transcoding_preset_preset_description": "ė••ėļ• ė†ë„ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. 동ėŧ ëš„íŠ¸ë ˆė´íŠ¸ė—ė„œ 느ëϰ ė†ë„ëĨŧ ė„ íƒí•˜ëŠ´ ėƒëŒ€ė ėœŧ로 파ėŧ íŦ기가 ę°ė†Œí•˜ëŠ° í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. VP9는 'faster' ė´ėƒė˜ ė†ë„ëĨŧ ëŦ´ė‹œí•Šë‹ˆë‹¤.", + "transcoding_reference_frames": "ė°¸ėĄ° í”„ë ˆėž„ 눘", + "transcoding_reference_frames_description": "íŠšė • í”„ë ˆėž„ė„ ė••ėļ•í•  때 ė°¸ėĄ°í•  í”„ë ˆėž„ 눘ëĨŧ ė„¤ė •í•Šë‹ˆë‹¤. ę°’ė„ ë†’ė´ëŠ´ ė••ėļ• íš¨ėœ¨ė´ í–Ĩėƒë˜ė§€ë§Œ ė¸ėŊ”딊 ė†ë„ę°€ ëŠë ¤ė§‘ë‹ˆë‹¤. 0ė„ ėž…ë Ĩ하면 ėžë™ėœŧ로 ė„¤ė •í•Šë‹ˆë‹¤.", + "transcoding_required_description": "í—ˆėšŠ íŦë§ˇė´ ė•„ë‹Œ ë™ė˜ėƒë§Œ íŠ¸ëžœėŠ¤ėŊ”딊", + "transcoding_settings": "íŠ¸ëžœėŠ¤ėŊ”딊 네렕", + "transcoding_settings_description": "íŠ¸ëžœėŠ¤ėŊ”딊할 ë™ė˜ėƒ 및 래ëĻŦ ë°Šë˛•ė„ 관ëĻŦ합니다.", "transcoding_target_resolution": "ëĒŠí‘œ í•´ėƒë„", - "transcoding_target_resolution_description": "ë†’ė€ í•´ėƒë„ëĨŧ ė„ íƒí•œ ę˛Ŋ뚰 넏ëļ€ ëŦ˜ė‚Ŧė˜ ė†ė‹¤ė„ ėĩœė†Œí™”í•  눘 ėžˆė§€ë§Œ, ė¸ėŊ”딊 ė‹œę°„ęŗŧ 파ėŧ íŦ기가 ėĻę°€í•˜ė—Ŧ ė•ąė˜ ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", + "transcoding_target_resolution_description": "í•´ėƒë„ëĨŧ ë†’ė´ëŠ´ 넏ëļ€ ė •ëŗ´ę°€ 더 ë§Žė´ ëŗ´ėĄ´ë˜ė§€ë§Œ, ė¸ėŊ”딊 ė‹œę°„ė´ ëŠ˜ė–´ë‚˜ęŗ  파ėŧ íŦ기가 ėģ¤ė ¸ ė•ą ë°˜ė‘ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", "transcoding_temporal_aq": "Temporal AQ", - "transcoding_temporal_aq_description": "넏ëļ€ ëŦ˜ė‚Ŧ가 ë§Žęŗ  ė›€ė§ėž„ė´ ė ė€ ėžĨëŠ´ė˜ í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. ė˜¤ëž˜ëœ 揰揰뙀 í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다. (NVENC만 해당)", - "transcoding_threads": "ėŠ¤ë ˆë“œ", - "transcoding_threads_description": "ę°’ė´ 높ėœŧ늴 ė¸ėŊ”딊 ė†ë„ę°€ í–Ĩėƒë˜ė§€ë§Œ ëĻŦė†ŒėŠ¤ ė‚ŦėšŠëŸ‰ė´ ėĻę°€í•Šë‹ˆë‹¤. ę°’ė€ CPU ėŊ”ė–´ ėˆ˜ëŗ´ë‹¤ ėž‘ė•„ė•ŧ 하며, ė„¤ė •í•˜ė§€ ė•Šėœŧ려늴 0ė„ ėž…ë Ĩ합니다.", + "transcoding_temporal_aq_description": "(NVENCė¸ ę˛Ŋ뚰) 디테ėŧė´ ë§Žęŗ  ė •ė ė¸ ėžĨëŠ´ė˜ í’ˆė§ˆė´ í–ĨėƒëŠë‹ˆë‹¤. ė˜¤ëž˜ëœ ę¸°ę¸°ė—ė„œ í˜¸í™˜ë˜ė§€ ė•Šė„ 눘 ėžˆėŠĩ니다.", + "transcoding_threads": "ėŠ¤ë ˆë“œ 눘", + "transcoding_threads_description": "ę°’ė„ ë†’ė´ëŠ´ ė¸ėŊ”딊 ė†ë„ę°€ 뚨ëŧė§€ė§€ë§Œ, ė„œë˛„ę°€ 다ëĨ¸ ėž‘ė—…ė„ 래ëĻŦ할 ė—Ŧėœ ę°€ ė¤„ė–´ë“­ë‹ˆë‹¤. ėž…ë Ĩ한 ę°’ė€ CPU ėŊ”ė–´ 눘ëĨŧ 봈ęŗŧí•˜ė§€ ė•Šė•„ė•ŧ 하며, 0ėœŧ로 ė„¤ė •í•˜ëŠ´ CPUëĨŧ ėĩœëŒ€í•œ í™œėšŠí•Šë‹ˆë‹¤.", "transcoding_tone_mapping": "톤 매핑", - "transcoding_tone_mapping_description": "HDR ë™ė˜ėƒė„ SDR로 ëŗ€í™˜í•  때 ė‚ŦėšŠí•  톤 매핑 ė•Œęŗ ëĻŦėĻ˜ė„ ė„¤ė •í•Šë‹ˆë‹¤. ė•Œęŗ ëĻŦėĻ˜ë§ˆë‹¤ ė¤‘ė ė„ 두는 ëļ€ëļ„뗐 ė°¨ė´ę°€ ėžˆėŠĩ니다. Hable ė•Œęŗ ëĻŦėĻ˜ė€ 넏ëļ€ ëŦ˜ė‚ŦëĨŧ ëŗ´ėĄ´í•˜ęŗ , Mobius ė•Œęŗ ëĻŦėĻ˜ė€ ėƒ‰ėƒė„ ëŗ´ėĄ´í•˜ëŠ°, Reinhard ė•Œęŗ ëĻŦėĻ˜ė€ 밝기ëĨŧ ëŗ´ėĄ´í•Šë‹ˆë‹¤.", - "transcoding_transcode_policy": "íŠ¸ëžœėŠ¤ėŊ”드 ė •ėą…", - "transcoding_transcode_policy_description": "íŠ¸ëžœėŠ¤ėŊ”딊할 ë™ė˜ėƒė„ ė„¤ė •í•Šë‹ˆë‹¤. HDR ė˜ėƒė€ í•­ėƒ íŠ¸ëžœėŠ¤ėŊ”ë”Šė„ ė§„í–‰í•Šë‹ˆë‹¤. (íŠ¸ëžœėŠ¤ėŊ”ë”Šė´ ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė œė™¸)", - "transcoding_two_pass_encoding": "íˆŦ íŒ¨ėŠ¤ ė¸ėŊ”딊", - "transcoding_two_pass_encoding_setting_description": "í’ˆė§ˆ í–Ĩėƒė„ ėœ„í•´ íˆŦ íŒ¨ėŠ¤ ė¸ėŊ”ë”Šė„ ė‚ŦėšŠí•Šë‹ˆë‹¤. ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸(H.264 및 HEVC뗐 í•„ėš”)가 í™œė„ąí™”ëœ ę˛Ŋ뚰 CRFëĨŧ ė‚ŦėšŠí•˜ė§€ ė•Šęŗ  ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ ę¸°ë°˜ė˜ ë˛”ėœ„ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ę°€ ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 VP9ė—ė„œ CRFëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다.", + "transcoding_tone_mapping_description": "HDR ė˜ėƒė„ SDR로 ëŗ€í™˜í•  때 ė‚ŦėšŠí•  톤 매핑 ė•Œęŗ ëĻŦėĻ˜ė„ ė„¤ė •í•Šë‹ˆë‹¤. Hableė€ 디테ėŧ, Mobius는 ėƒ‰ėƒ, Reinhard는 ë°ę¸°ė— ė¤‘ė ė„ 두며 'ëš„í™œė„ąí™”'는 톤 ë§¤í•‘ė„ ė‚ŦėšŠí•˜ė§€ ė•ŠėŠĩ니다.", + "transcoding_transcode_policy": "íŠ¸ëžœėŠ¤ėŊ”드 揰뤀", + "transcoding_transcode_policy_description": "ë™ė˜ėƒė„ íŠ¸ëžœėŠ¤ėŊ”딊할 ę¸°ė¤€ė„ ė„¤ė •í•Šë‹ˆë‹¤. HDR ë™ė˜ėƒė€ í•­ėƒ íŠ¸ëžœėŠ¤ėŊ”딊됊니다. (íŠ¸ëžœėŠ¤ėŊ”ë”Šė´ ëš„í™œė„ąí™”ëœ ę˛Ŋ뚰 ė œė™¸)", + "transcoding_two_pass_encoding": "2íŒ¨ėŠ¤ ė¸ėŊ”딊", + "transcoding_two_pass_encoding_setting_description": "2íŒ¨ėŠ¤ ė¸ėŊ”ë”Šė„ ė‚ŦėšŠí•´ ė¸ėŊ”딊 í’ˆė§ˆė„ ë†’ėž…ë‹ˆë‹¤. H.264 및 HEVCė˜ ę˛Ŋ뚰 CRFëĨŧ ëŦ´ė‹œí•˜ęŗ  ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ ę¸°ë°˜ė˜ ëš„íŠ¸ë ˆė´íŠ¸ ë˛”ėœ„ëĨŧ ė‚ŦėšŠí•Šë‹ˆë‹¤. VP9ė˜ ę˛Ŋ뚰 ėĩœëŒ€ ëš„íŠ¸ë ˆė´íŠ¸ëĨŧ ëš„í™œė„ąí™”í•˜ëŠ´ CRFëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다.", "transcoding_video_codec": "ë™ė˜ėƒ ėŊ”덱", - "transcoding_video_codec_description": "VP9는 íš¨ėœ¨ė ė´ęŗ  ė›š í˜¸í™˜ė„ąė´ ë†’ė§€ë§Œ íŠ¸ëžœėŠ¤ėŊ”ë”Šė— ë‹¤ė†Œ 긴 ė‹œę°„ė´ ė†Œėš”ëŠë‹ˆë‹¤. HEVC는 ė„ąëŠĨė€ ëš„ėŠˇí•˜ë‚˜ ė›š í˜¸í™˜ė„ąė´ 낮ėŠĩ니다. H.264는 í˜¸í™˜ė„ąė´ 가ėžĨ ë†’ė§€ë§Œ ė¸ėŊ”딊된 파ėŧ íŦ기가 íŦęŗ , AV1ė€ 가ėžĨ íš¨ėœ¨ė ė´ë‚˜ ė˜¤ëž˜ëœ ę¸°ę¸°ė™€ė˜ í˜¸í™˜ė„ąė´ 낮ėŠĩ니다.", - "trash_enabled_description": "íœ´ė§€í†ĩ í™œė„ąí™”", - "trash_number_of_days": "ė‚­ė œ 뜠똈 기간", - "trash_number_of_days_description": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė˜ ė‚­ė œ 뜠똈 기간", + "transcoding_video_codec_description": "VP9는 íš¨ėœ¨ė ė´ęŗ  ė›š í˜¸í™˜ė„ąė´ 높ėœŧ나 íŠ¸ëžœėŠ¤ėŊ”ë”Šė´ ė˜¤ëž˜ 깸ëĻŊ니다. HEVC는 VP9뙀 ëš„ėŠˇí•˜ė§€ë§Œ ė›š í˜¸í™˜ė„ąė´ 낮ėŠĩ니다. H.264는 í˜¸í™˜ė„ąė´ 가ėžĨ 높ėœŧ나 래ëĻŦ된 파ėŧė˜ íŦ기가 íŦęŗ , AV1ė€ 가ėžĨ íš¨ėœ¨ė ė´ė§€ë§Œ ė˜¤ëž˜ëœ ę¸°ę¸°ė™€ė˜ í˜¸í™˜ė„ąė´ 낮ėŠĩ니다.", + "trash_enabled_description": "íœ´ė§€í†ĩ 기ëŠĨ í™œė„ąí™”", + "trash_number_of_days": "íœ´ė§€í†ĩ ëŗ´ę´€ 기간", + "trash_number_of_days_description": "항ëĒŠė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ę¸° ė „ęšŒė§€ íœ´ė§€í†ĩ뗐 ëŗ´ę´€í•  기간(ėŧ)", "trash_settings": "íœ´ė§€í†ĩ 네렕", - "trash_settings_description": "íœ´ė§€í†ĩ 네렕 관ëĻŦ", + "trash_settings_description": "íœ´ė§€í†ĩ 기ëŠĨė„ ė„¤ė •í•Šë‹ˆë‹¤.", + "unlink_all_oauth_accounts": "ëĒ¨ë“  OAuth ęŗ„ė • 뗰枰 í•´ė œ", + "unlink_all_oauth_accounts_description": "냈 ęŗĩę¸‰ėžëĄœ ė´ė „í•˜ë ¤ëŠ´ ë¨ŧė € ëĒ¨ë“  OAuth ęŗ„ė •ė˜ ė—°ę˛°ė„ í•´ė œí•´ė•ŧ 합니다.", + "unlink_all_oauth_accounts_prompt": "ëĒ¨ë“  OAuth ęŗ„ė • ė—°ę˛°ė„ í•´ė œí•˜ė‹œę˛ ėŠĩ니까? 각 ė‚ŦėšŠėžė˜ OAuth IDëĨŧ ė´ˆę¸°í™”í•˜ëŠ° 되돌ëĻ´ 눘 ė—†ėŠĩ니다.", "user_cleanup_job": "ė‚ŦėšŠėž ė •ëĻŦ", - "user_delete_delay": "{user}ë‹˜ė´ ė—…ëĄœë“œí•œ 항ëĒŠė´ {delay, plural, one {#ėŧ} other {#ėŧ}} 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "user_delete_delay_settings": "ė‚­ė œ 뜠똈 기간", - "user_delete_delay_settings_description": "ė‚ŦėšŠėž ęŗ„ė •ęŗŧ 항ëĒŠė´ ė™„ė „ížˆ ė‚­ė œë˜ę¸°ęšŒė§€ė˜ 뜠똈 기간(ėŧ)ė„ ė„¤ė •í•Šë‹ˆë‹¤. ė‚ŦėšŠėž ė‚­ė œ ėž‘ė—…ė€ 매ėŧ ėžė •ė— ė‹¤í–‰ë˜ė–´ ė‚­ė œ ëŒ€ėƒ ė—Ŧëļ€ëĨŧ í™•ė¸í•Šë‹ˆë‹¤. ė´ ė„¤ė •ė˜ ëŗ€ę˛Ŋ ė‚Ŧí•­ė€ ë‹¤ėŒ ėž‘ė—… ė‹¤í–‰ ė‹œ ë°˜ė˜ëŠë‹ˆë‹¤.", - "user_delete_immediately": "{user}ë‹˜ė´ ė—…ëĄœë“œí•œ 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "user_delete_immediately_checkbox": "뜠똈 기간 ė—†ė´ ėĻ‰ė‹œ ė‚­ė œ", - "user_details": "ė‚ŦėšŠėž ėƒė„¸", + "user_delete_delay": "{user}ë‹˜ė˜ ęŗ„ė •ęŗŧ 항ëĒŠė´ {delay, plural, one {#ėŧ} other {#ėŧ}} 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "user_delete_delay_settings": "ė‚­ė œ 대기 기간", + "user_delete_delay_settings_description": "ė‚ŦėšŠėž ė œęą° 후 ęŗ„ė •ęŗŧ 항ëĒŠė´ 똁ęĩŦ ė‚­ė œë˜ę¸°ęšŒė§€ė˜ 기간(ėŧ). ė´ ė„¤ė •ė€ ėžė •ë§ˆë‹¤ ė‹¤í–‰ë˜ëŠ” ė‚­ė œ ėž‘ė—… ė‹œ ë°˜ė˜ëŠë‹ˆë‹¤.", + "user_delete_immediately": "{user}ë‹˜ė˜ ęŗ„ė •ęŗŧ 항ëĒŠė´ ėĻ‰ė‹œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "user_delete_immediately_checkbox": "ė‚ŦėšŠėž 및 항ëĒŠė„ ėĻ‰ė‹œ ė‚­ė œ ëŒ€ę¸°ė—´ė— ėļ”ę°€", + "user_details": "ė‚ŦėšŠėž ė •ëŗ´", "user_management": "ė‚ŦėšŠėž 관ëĻŦ", "user_password_has_been_reset": "ė‚ŦėšŠėžė˜ 비밀번호가 ė´ˆę¸°í™”ë˜ė—ˆėŠĩ니다:", - "user_password_reset_description": "ė´ 비밀번호ëĨŧ 해당 ė‚ŦėšŠėžė—ę˛Œ ė•Œë ¤ėŖŧė„¸ėš”. ėž„ė‹œ 비밀번호로 ëĄœęˇ¸ė¸í•œ 뒤 비밀번호ëĨŧ ë°˜ë“œė‹œ ëŗ€ę˛Ŋ해ė•ŧ 합니다.", + "user_password_reset_description": "ė´ ėž„ė‹œ 비밀번호ëĨŧ ė‚ŦėšŠėžė—ę˛Œ ė „ë‹Ŧí•˜ęŗ , ë‹¤ėŒ ëĄœęˇ¸ė¸ ė‹œ ë°˜ë“œė‹œ ëŗ€ę˛Ŋ해ė•ŧ í•œë‹¤ęŗ  ė•ˆë‚´í•˜ė„¸ėš”.", "user_restore_description": "똈ė•Ŋ된 {user}ë‹˜ė˜ ė‚­ė œëĨŧ ėˇ¨ė†Œí•Šë‹ˆë‹¤.", "user_restore_scheduled_removal": "{date, date, long}뗐 똈ė•Ŋ된 ė‚ŦėšŠėž ė‚­ė œ ėˇ¨ė†Œ", "user_settings": "ė‚ŦėšŠėž 네렕", - "user_settings_description": "ė‚ŦėšŠėž 네렕 관ëĻŦ", - "user_successfully_removed": "{email}ė´(가) ė„ąęŗĩ렁ėœŧ로 ė œęą°ë˜ė—ˆėŠĩ니다.", + "user_settings_description": "ė‚ŦėšŠėž ė„¤ė •ė„ 관ëĻŦ합니다.", + "user_successfully_removed": "ė‚ŦėšŠėž({email})가 ė œęą°ë˜ė—ˆėŠĩ니다.", "version_check_enabled_description": "ë˛„ė „ í™•ė¸ í™œė„ąí™”", - "version_check_implications": "ėŖŧ揰렁ėœŧ로 github.com뗐 ėš”ė˛­ė„ ëŗ´ë‚´ ėĩœė‹  ë˛„ė „ė„ í™•ė¸í•Šë‹ˆë‹¤.", + "version_check_implications": "ėŖŧ揰렁ėœŧ로 Github뗐 ėš”ė˛­ė„ ëŗ´ë‚´ 냈 ë˛„ė „ė„ í™•ė¸í•Šë‹ˆë‹¤.", "version_check_settings": "ë˛„ė „ í™•ė¸", - "version_check_settings_description": "ėĩœė‹  ë˛„ė „ ė•ŒëĻŧ 네렕 관ëĻŦ", + "version_check_settings_description": "냈 ë˛„ė „ í™•ė¸ 및 ė•ŒëĻŧ 기ëŠĨė„ 관ëĻŦ합니다.", "video_conversion_job": "ë™ė˜ėƒ íŠ¸ëžœėŠ¤ėŊ”드", "video_conversion_job_description": "ë‹¤ė–‘í•œ 브ëŧėš°ė € 및 ę¸°ę¸°ė™€ė˜ í˜¸í™˜ė„ąė„ ėœ„í•œ ë™ė˜ėƒ íŠ¸ëžœėŠ¤ėŊ”드" }, @@ -357,11 +398,13 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "ė´ ė˜ĩė…˜ė„ ė‚ŦėšŠí•˜ëŠ´ 동기화 뤑 ë¯¸ë””ė–´ëĨŧ ëŒ€ė˛´ 揰뤀ėœŧ로 필터링할 눘 ėžˆėŠĩ니다. ė•ąė´ ëĒ¨ë“  ė•¨ë˛”ė„ ė œëŒ€ëĄœ ę°ė§€í•˜ė§€ ëĒģ할 때만 ė‚ŦėšŠí•˜ė„¸ėš”.", "advanced_settings_enable_alternate_media_filter_title": "ëŒ€ė˛´ 기기 ė•¨ë˛” 동기화 필터 ė‚ŦėšŠ (ė‹¤í—˜ė )", "advanced_settings_log_level_title": "로그 레벨: {level}", - "advanced_settings_prefer_remote_subtitle": "ėŧëļ€ ę¸°ę¸°ė˜ ę˛Ŋ뚰 기기 ë‚´ė˜ ė„Ŧ네ėŧė„ 로드하는 ė†ë„ę°€ ë§¤ėš° 느ëĻŊ니다. ė„œë˛„ ė´ë¯¸ė§€ëĨŧ ëŒ€ė‹  로드하려면 ė´ ė„¤ė •ė„ í™œė„ąí™”í•˜ė„¸ėš”.", + "advanced_settings_prefer_remote_subtitle": "ėŧëļ€ ę¸°ę¸°ė˜ ę˛Ŋ뚰 로ėģŦ 항ëĒŠė—ė„œ ė„Ŧ네ėŧė„ 로드하는 ė†ë„ę°€ ë§¤ėš° 느ëĻŊ니다. ė„œë˛„ ė´ë¯¸ė§€ëĨŧ ëŒ€ė‹  로드하려면 ė´ ė„¤ė •ė„ í™œė„ąí™”í•˜ė„¸ėš”.", "advanced_settings_prefer_remote_title": "ė„œë˛„ ė´ë¯¸ė§€ ė„ í˜¸", - "advanced_settings_proxy_headers_subtitle": "ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė„ ëŗ´ë‚ŧ 때 Immich가 ė‚ŦėšŠí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", + "advanced_settings_proxy_headers_subtitle": "Immich가 ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ ė‹œ ė‚ŦėšŠí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", "advanced_settings_proxy_headers_title": "í”„ëĄė‹œ 헤더", - "advanced_settings_self_signed_ssl_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ė— 대한 SSL ė¸ėĻė„œ í™•ė¸ė„ 건너뜁니다. ėžė˛´ ė„œëĒ…ëœ ė¸ėĻė„œëĨŧ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", + "advanced_settings_readonly_mode_subtitle": "ėŊ기 ė „ėšŠ ëĒ¨ë“œëĨŧ í™œė„ąí™”í•˜ëŠ´ ė—ŦëŸŦ ė´ë¯¸ė§€ ė„ íƒ, ęŗĩ뜠, ėēėŠ¤íŠ¸, ė‚­ė œ ë™ėž‘ė´ ëĒ¨ë‘ ëš„í™œė„ąí™”ëŠë‹ˆë‹¤. ëŠ”ė¸ í™”ëŠ´ė—ė„œ ė‚ŦėšŠėž í”„ëĄœí•„ė„ í†ĩ해 ėŊ기 ė „ėšŠ ëĒ¨ë“œė˜ í™œė„ą ėƒíƒœëĨŧ ė „í™˜í•˜ė„¸ėš”", + "advanced_settings_readonly_mode_title": "ėŊ기 ė „ėšŠ ëĒ¨ë“œ", + "advanced_settings_self_signed_ssl_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ė˜ SSL ė¸ėĻė„œ 검ėĻė„ 건너뜁니다. ėžė˛´ ė„œëĒ… ė¸ėĻė„œëĨŧ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 í™œė„ąí™”í•˜ė„¸ėš”.", "advanced_settings_self_signed_ssl_title": "ėžė˛´ ė„œëĒ…ëœ SSL ė¸ėĻė„œ í—ˆėšŠ", "advanced_settings_sync_remote_deletions_subtitle": "ė›šė—ė„œ ė‚­ė œí•˜ęą°ë‚˜ ëŗĩė›í•œ 항ëĒŠė„ ė´ ę¸°ę¸°ė—ė„œë„ ėžë™ėœŧ로 래ëĻŦ하도록 네렕", "advanced_settings_sync_remote_deletions_title": "ė›ę˛Š ė‚­ė œ 동기화 (ė‹¤í—˜ė )", @@ -372,10 +415,11 @@ "age_year_months": "ėƒí›„ 1년 {months, plural, one {#ę°œė›”} other {#ę°œė›”}}", "age_years": "{years, plural, other {#넏}}", "album_added": "ęŗĩ뜠 ė•¨ë˛” ė´ˆëŒ€", - "album_added_notification_setting_description": "ęŗĩ뜠 ė•¨ë˛”ėœŧ로 ė´ˆëŒ€ëĨŧ ë°›ė€ ę˛Ŋ뚰 ė´ëŠ”ėŧ ė•ŒëĻŧ 받기", + "album_added_notification_setting_description": "ęŗĩ뜠 ė•¨ë˛”ė— ėļ”ę°€ëœ ę˛Ŋ뚰 ė´ëŠ”ėŧ ė•ŒëĻŧ 받기", "album_cover_updated": "ė•¨ë˛” ėģ¤ë˛„ ė—…ë°ė´íŠ¸ë¨", "album_delete_confirmation": "{album} ė•¨ë˛”ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "album_delete_confirmation_description": "ė´ ė•¨ë˛”ė„ ęŗĩėœ í•œ ę˛Ŋ뚰 다ëĨ¸ ė‚ŦėšŠėžę°€ 더 ė´ėƒ ė•¨ë˛”ė— ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", + "album_delete_confirmation_description": "ė´ ė•¨ë˛”ė´ ęŗĩėœ ëœ ę˛Ŋ뚰, 다ëĨ¸ ė‚ŦėšŠėžę°€ ë”ė´ėƒ ė•¨ë˛”ė— ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", + "album_deleted": "ė•¨ë˛” ė‚­ė œë¨", "album_info_card_backup_album_excluded": "ė œė™¸ë¨", "album_info_card_backup_album_included": "ė„ íƒë¨", "album_info_updated": "ė•¨ë˛” ė •ëŗ´ ė—…ë°ė´íŠ¸ë¨", @@ -385,22 +429,28 @@ "album_options": "ė•¨ë˛” ė˜ĩė…˜", "album_remove_user": "ė‚ŦėšŠėžëĨŧ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", "album_remove_user_confirmation": "{user}ë‹˜ė„ ė•¨ë˛”ė—ė„œ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", - "album_share_no_users": "ė´ë¯¸ ëĒ¨ë“  ė‚ŦėšŠėžė™€ ė•¨ë˛”ė„ ęŗĩ뜠 ė¤‘ė´ęą°ë‚˜ 다ëĨ¸ ė‚ŦėšŠėžę°€ ė—†ëŠ” 것 같ėŠĩ니다.", + "album_search_not_found": "ę˛€ėƒ‰ 결ęŗŧ뗐 해당하는 ė•¨ë˛”ė´ ė—†ėŠĩ니다.", + "album_share_no_users": "ė´ë¯¸ ëĒ¨ë“  ė‚ŦėšŠėžė™€ ė•¨ë˛”ė„ ęŗĩėœ í–ˆęą°ë‚˜ ęŗĩėœ í•  ė‚ŦėšŠėžę°€ ė—†ėŠĩ니다.", + "album_summary": "ė•¨ë˛” ėš”ė•Ŋ", "album_updated": "항ëĒŠ ėļ”ę°€ ė•ŒëĻŧ", "album_updated_setting_description": "ęŗĩ뜠 ė•¨ë˛”ė— 항ëĒŠė´ ėļ”ę°€ëœ ę˛Ŋ뚰 ė´ëŠ”ėŧ ė•ŒëĻŧ 받기", "album_user_left": "{album} ė•¨ë˛”ė—ė„œ ë‚˜ė˜´", "album_user_removed": "{user}ë‹˜ė„ ė•¨ë˛”ė—ė„œ ė œęą°í•¨", "album_viewer_appbar_delete_confirm": "ė´ ė•¨ë˛”ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "album_viewer_appbar_share_err_delete": "ė•¨ë˛” ė‚­ė œ ė‹¤íŒ¨", - "album_viewer_appbar_share_err_leave": "ė•¨ë˛”ė—ė„œ ë‚˜ę°€ė§€ ëĒģ했ėŠĩ니다.", - "album_viewer_appbar_share_err_remove": "ė•¨ë˛”ė—ė„œ 항ëĒŠė„ ė œęą°í•˜ëŠ” 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", - "album_viewer_appbar_share_err_title": "ė•¨ë˛” 렜ëĒŠė„ ëŗ€ę˛Ŋí•˜ė§€ ëĒģ했ėŠĩ니다.", + "album_viewer_appbar_share_err_leave": "ė•¨ë˛” 나가기 ė‹¤íŒ¨", + "album_viewer_appbar_share_err_remove": "ė•¨ë˛”ė—ė„œ 항ëĒŠ ė œęą° 뤑 ëŦ¸ė œ ë°œėƒ", + "album_viewer_appbar_share_err_title": "ė•¨ë˛”ëĒ… ëŗ€ę˛Ŋ ė‹¤íŒ¨", "album_viewer_appbar_share_leave": "ė•¨ë˛” 나가기", "album_viewer_appbar_share_to": "ęŗĩ뜠 ëŒ€ėƒ", "album_viewer_page_share_add_users": "ė‚ŦėšŠėž ėļ”ę°€", "album_with_link_access": "링íŦ가 ėžˆëŠ” ę˛Ŋ뚰 누ęĩŦ나 ė´ ė•¨ë˛”ė˜ ė‚Ŧė§„ęŗŧ ė¸ëŦŧė„ ëŗŧ 눘 ėžˆėŠĩ니다.", "albums": "ė•¨ë˛”", "albums_count": "ė•¨ë˛” {count, plural, one {{count, number}氜} other {{count, number}氜}}", + "albums_default_sort_order": "ę¸°ëŗ¸ ė•¨ë˛” ė •ë Ŧ ėˆœė„œ", + "albums_default_sort_order_description": "냈 ė•¨ë˛” ėƒė„ą ė‹œ ė ėšŠë˜ëŠ” ę¸°ëŗ¸ ė •ë Ŧė„ ė„¤ė •í•Šë‹ˆë‹¤.", + "albums_feature_description": "ė—ŦëŸŦ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ í•œęŗŗė— ëǍ땄 둘 눘 ėžˆėŠĩ니다.", + "albums_on_device_count": "ę¸°ę¸°ė˜ ė•¨ë˛” ({count}氜)", "all": "ëĒ¨ë‘", "all_albums": "ëĒ¨ë“  ė•¨ë˛”", "all_people": "ëĒ¨ë“  ė¸ëŦŧ", @@ -413,28 +463,30 @@ "anti_clockwise": "ë°˜ė‹œęŗ„ ë°Ší–Ĩ", "api_key": "API 키", "api_key_description": "ė´ ę°’ė€ 한 번만 í‘œė‹œëŠë‹ˆë‹¤. ė°Ŋė„ ë‹Ģ기 ė „ ë°˜ë“œė‹œ ëŗĩė‚Ŧ해ėŖŧė„¸ėš”.", - "api_key_empty": "키 ė´ëĻ„ė€ ëš„ė–´ ėžˆė„ 눘 ė—†ėŠĩ니다.", + "api_key_empty": "API 키 ė´ëĻ„ė€ ëš„ė›Œë‘˜ 눘 ė—†ėŠĩ니다.", "api_keys": "API 키", "app_bar_signout_dialog_content": "ė •ë§ ëĄœęˇ¸ė•„ė›ƒí•˜ė‹œę˛ ėŠĩ니까?", "app_bar_signout_dialog_ok": "네", "app_bar_signout_dialog_title": "ëĄœęˇ¸ė•„ė›ƒ", "app_settings": "ė•ą 네렕", "appears_in": "ë‹¤ėŒ ė•¨ë˛”ė— íŦ함됨", + "apply_count": "ė ėšŠ ({count, number})", "archive": "ëŗ´ę´€í•¨", + "archive_action_prompt": "ëŗ´ę´€í•¨ėœŧ로 항ëĒŠ {count}氜 ė´ë™ë¨", "archive_or_unarchive_photo": "ëŗ´ę´€ 래ëĻŦ 또는 í•´ė œ", "archive_page_no_archived_assets": "ëŗ´ę´€ëœ 항ëĒŠ ė—†ėŒ", "archive_page_title": "ëŗ´ę´€í•¨ ({count})", "archive_size": "ė••ėļ• íŒŒėŧ íŦ기", - "archive_size_description": "ë‹¤ėš´ëĄœë“œí•  ė••ėļ• íŒŒėŧė˜ íŦ기 ęĩŦė„ą (GiB ë‹¨ėœ„)", + "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": "항ëĒŠė˜ 네ëĒ…ė´ ė—…ë°ė´íŠ¸ë˜ė—ˆėŠĩ니다.", + "asset_description_updated": "항ëĒŠ 네ëĒ…ė´ ė—…ë°ė´íŠ¸ë˜ė—ˆėŠĩ니다.", "asset_filename_is_offline": "{filename} 항ëĒŠ 누ëŊ됨", "asset_has_unassigned_faces": "항ëĒŠė— í• ë‹šë˜ė§€ ė•Šė€ ė–ŧęĩ´ė´ ėžˆėŒ", "asset_hashing": "í•´ė‹ą 뤑â€Ļ", @@ -447,48 +499,60 @@ "asset_list_settings_subtitle": "ė‚Ŧė§„ ë°°ė—´ ë ˆė´ė•„ė›ƒ 네렕", "asset_list_settings_title": "ė‚Ŧė§„ ë°°ė—´", "asset_offline": "누ëŊ된 항ëĒŠ", - "asset_offline_description": "ë””ėŠ¤íŦė—ė„œ 항ëĒŠė„ ë”ė´ėƒ ė°žė„ 눘 ė—†ėŠĩ니다. ė„œë˛„ 관ëĻŦėžė—ę˛Œ ė—°ëŊ하ė—Ŧ ë„ė›€ė„ 받ėœŧė„¸ėš”.", - "asset_restored_successfully": "항ëĒŠė´ ė„ąęŗĩ렁ėœŧ로 ëŗĩė›ë˜ė—ˆėŠĩ니다.", + "asset_offline_description": "ë””ėŠ¤íŦė—ė„œ 항ëĒŠė„ ë”ė´ėƒ ė°žė„ 눘 ė—†ėŠĩ니다. ė„œë˛„ 관ëĻŦėžė—ę˛Œ ė—°ëŊí•˜ė„¸ėš”.", + "asset_restored_successfully": "항ëĒŠė´ ëŗĩė›ë˜ė—ˆėŠĩ니다.", "asset_skipped": "건너뜀", "asset_skipped_in_trash": "íœ´ė§€í†ĩė˜ 항ëĒŠ", + "asset_trashed": "항ëĒŠ ė‚­ė œë¨", + "asset_troubleshoot": "항ëĒŠ 트ëŸŦë¸”ėŠˆíŒ…", "asset_uploaded": "ė—…ëĄœë“œ ė™„ëŖŒ", "asset_uploading": "ė—…ëĄœë“œ 뤑â€Ļ", - "asset_viewer_settings_subtitle": "ę°¤ëŸŦëĻŦ ëˇ°ė–´ 네렕 관ëĻŦ", + "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 {{name}} other {냈 ė•¨ë˛”}}뗐 항ëĒŠ {count, plural, one {#氜} other {#氜}} ėļ”가됨", + "assets_added_to_albums_count": "{albumTotal, plural, one {ė•¨ë˛”ė—} other {ė•¨ë˛” #ę°œė—}} {assetTotal, plural, one {항ëĒŠė´} other {항ëĒŠ #개가}} ėļ”가됨", + "assets_cannot_be_added_to_album_count": "{count, plural, one {ė•¨ë˛”ė— 항ëĒŠė„} other {ėŧëļ€ í•­ëĒŠė„ ė•¨ë˛”ė—}} ėļ”가할 눘 ė—†ėŠĩ니다.", + "assets_cannot_be_added_to_albums": "{count, plural, one {항ëĒŠė„} other {항ëĒŠë“¤ė„}} ė–´ëŠ ė•¨ë˛”ė—ë„ ėļ”가할 눘 ė—†ėŠĩ니다.", "assets_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠ", "assets_deleted_permanently": "{count}氜 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", - "assets_deleted_permanently_from_server": "ė„œë˛„ė—ė„œ 항ëĒŠ {count}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_deleted_permanently_from_server": "{count}氜 항ëĒŠė´ ė„œë˛„ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_downloaded_failed": "{count, plural, one {파ėŧ #氜 ë‹¤ėš´ëĄœë“œ ė™„ëŖŒ - {error}氜 ė‹¤íŒ¨} other {파ėŧ #氜 ë‹¤ėš´ëĄœë“œ ė™„ëŖŒ - {error}氜 ė‹¤íŒ¨}}", + "assets_downloaded_successfully": "{count, plural, one {파ėŧ #氜 ë‹¤ėš´ëĄœë“œ ė™„ëŖŒ} other {파ėŧ #氜 ë‹¤ėš´ëĄœë“œ ė™„ëŖŒ}}", "assets_moved_to_trash_count": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", - "assets_permanently_deleted_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", - "assets_removed_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė œęą°í–ˆėŠĩ니다.", - "assets_removed_permanently_from_device": "ę¸°ę¸°ė—ė„œ 항ëĒŠ {count}개가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", - "assets_restore_confirmation": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė„ ëĒ¨ë‘ ëŗĩė›í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다! 누ëŊ된 항ëĒŠė˜ ę˛Ŋ뚰 ëŗĩė›ë˜ė§€ ė•ŠėŠĩ니다.", - "assets_restored_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ëŗĩė›í–ˆėŠĩ니다.", - "assets_restored_successfully": "항ëĒŠ {count}氜ëĨŧ ëŗĩė›í–ˆėŠĩ니다.", + "assets_permanently_deleted_count": "{count, plural, one {#氜 항ëĒŠ} other {#氜 항ëĒŠ}}ė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_removed_count": "{count, plural, one {항ëĒŠ #氜} other {항ëĒŠ #氜}} ė œęą°ë¨", + "assets_removed_permanently_from_device": "{count}氜 항ëĒŠė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "assets_restore_confirmation": "íœ´ė§€í†ĩė˜ ëĒ¨ë“  항ëĒŠė„ ëŗĩė›í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다! 누ëŊ된 항ëĒŠė€ ëŗĩė›ë˜ė§€ ė•ŠėŠĩ니다.", + "assets_restored_count": "{count, plural, one {항ëĒŠ #氜} other {항ëĒŠ #氜}} ëŗĩė›ë¨", + "assets_restored_successfully": "항ëĒŠ {count}氜 ëŗĩė›ë¨", "assets_trashed": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count}氜 ė´ë™ë¨", "assets_trashed_count": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", - "assets_trashed_from_server": "ė„œë˛„ė—ė„œ 항ëĒŠ {count}개가 íœ´ė§€í†ĩėœŧ로 ė´ë™ë¨", - "assets_were_part_of_album_count": "ė•¨ë˛”ė— ė´ë¯¸ ėĄ´ėžŦ하는 {count, plural, one {항ëĒŠ} other {항ëĒŠ}}ėž…ë‹ˆë‹¤.", - "authorized_devices": "ė¸ėĻëœ 기기", - "automatic_endpoint_switching_subtitle": "ė§€ė •ëœ Wi-Fi가 ė‚ŦėšŠ 가ëŠĨ한 ę˛Ŋ뚰 내ëļ€ë§ė„ í†ĩ해 ė—°ę˛°í•˜ęŗ , ęˇ¸ë ‡ė§€ ė•Šėœŧ늴 다ëĨ¸ 뗰枰 ë°Šė‹ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤.", + "assets_trashed_from_server": "ė„œë˛„ 항ëĒŠ {count}氜 íœ´ė§€í†ĩėœŧ로 ė´ë™ë¨", + "assets_were_part_of_album_count": "ė•¨ë˛”ė— ė´ë¯¸ íŦ함된 {count, plural, one {항ëĒŠ} other {항ëĒŠ}}ėž…ë‹ˆë‹¤.", + "assets_were_part_of_albums_count": "ė´ë¯¸ ė•¨ë˛”ė— íŦ함된 {count, plural, one {항ëĒŠ} other {항ëĒŠ}}ėž…ë‹ˆë‹¤.", + "authorized_devices": "내 기기", + "automatic_endpoint_switching_subtitle": "ė§€ė •ëœ Wi-Fi가 ė‚ŦėšŠ 가ëŠĨ한 ę˛Ŋ뚰 내ëļ€ë§ėœŧ로 ė—°ę˛°í•˜ęŗ , ꡸ ė™¸ė˜ ę˛Ŋ뚰 다ëĨ¸ 뗰枰 ë°Šė‹ė„ ė‚ŦėšŠí•Šë‹ˆë‹¤.", "automatic_endpoint_switching_title": "ėžë™ URL ė „í™˜", + "autoplay_slideshow": "ėŠŦëŧė´ë“œ ė‡ŧ ėžë™ ėžŦėƒ", "back": "뒤로", - "back_close_deselect": "뒤로, ë‹Ģ기, ė„ íƒ ėˇ¨ė†Œ", + "back_close_deselect": "뒤로, ë‹Ģ기 또는 ė„ íƒ í•´ė œ", + "background_backup_running_error": "밹꡸ëŧėš´ë“œ ë°ąė—…ė´ 현ėžŦ ė§„í–‰ ė¤‘ė´ë¯€ëĄœ ėˆ˜ë™ ë°ąė—…ė„ ė‹œėž‘í•  눘 ė—†ėŠĩ니다", "background_location_permission": "밹꡸ëŧėš´ë“œ ėœ„ėš˜ ęļŒí•œ", - "background_location_permission_content": "밹꡸ëŧėš´ë“œė—ė„œ ë„¤íŠ¸ė›ŒíŦëĨŧ ė „í™˜í•˜ë ¤ëŠ´, Immich가 Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•  눘 ėžˆë„ëĄ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė„ í•­ėƒ í—ˆėšŠí•´ė•ŧ 합니다.", + "background_location_permission_content": "Immich가 밹꡸ëŧėš´ë“œė—ė„œ ė‹¤í–‰ 뤑ėŧ 때 ë„¤íŠ¸ė›ŒíŦëĨŧ ė „í™˜í•˜ë ¤ëŠ´ Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•´ė•ŧ 하며, ė´ëĨŧ ėœ„í•´ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė„ í•­ėƒ í—ˆėšŠí•´ė•ŧ 합니다.", + "background_options": "밹꡸ëŧėš´ë“œ ė˜ĩė…˜", + "backup": "ë°ąė—…", "backup_album_selection_page_albums_device": "ę¸°ę¸°ė˜ ė•¨ë˛” ({count})", - "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_albums_sync": "ė•¨ë˛” 동기화 ë°ąė—…", "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": "{filename} ė—…ëĄœë“œ 뤑", "backup_background_service_default_notification": "ėƒˆëĄœėš´ 항ëĒŠė„ í™•ė¸í•˜ëŠ” 뤑â€Ļ", "backup_background_service_error_title": "ë°ąė—… 똤ëĨ˜", @@ -499,15 +563,15 @@ "backup_controller_page_background_app_refresh_disabled_title": "밹꡸ëŧėš´ë“œ ėƒˆëĄœ ęŗ ėš¨ ëš„í™œė„ąí™”ë¨", "backup_controller_page_background_app_refresh_enable_button_text": "네렕ėœŧ로 ė´ë™", "backup_controller_page_background_battery_info_link": "네렕 방법", - "backup_controller_page_background_battery_info_message": "ėĩœėƒė˜ 밹꡸ëŧėš´ë“œ ë°ąė—… 환ę˛Ŋė„ ėœ„í•´ Immich 밹꡸ëŧėš´ë“œ í™œë™ė„ ė œí•œí•˜ëŠ” 배터ëĻŦ ėĩœė í™” 기ëŠĨė„ ëš„í™œė„ąí™”í•˜ė„¸ėš”.\n\n기기마다 네렕 ë°Šë˛•ė— ė°¨ė´ę°€ ėžˆė–´ ė œėĄ° ė—…ė˛´ė—ė„œ 관련 ė •ëŗ´ëĨŧ ė°žė•„ëŗ´ė„¸ėš”.", + "backup_controller_page_background_battery_info_message": "ėĩœėƒė˜ 밹꡸ëŧėš´ë“œ ë°ąė—… 환ę˛Ŋė„ ėœ„í•´ Immichė˜ 밹꡸ëŧėš´ë“œ í™œë™ė„ ė œí•œí•˜ëŠ” 배터ëĻŦ ėĩœė í™” 기ëŠĨė„ ëš„í™œė„ąí™”í•˜ė„¸ėš”.\n\n기기마다 네렕 ë°Šë˛•ė— ė°¨ė´ę°€ ėžˆėœŧë¯€ëĄœ ė œėĄ°ė‚Ŧė—ė„œ 관련 ė •ëŗ´ëĨŧ ė°žė•„ëŗ´ė„¸ėš”.", "backup_controller_page_background_battery_info_ok": "í™•ė¸", "backup_controller_page_background_battery_info_title": "배터ëĻŦ ėĩœė í™”", "backup_controller_page_background_charging": "ėļŠė „ ė¤‘ė—ë§Œ", "backup_controller_page_background_configure_error": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ ęĩŦė„ą ė‹¤íŒ¨", - "backup_controller_page_background_delay": "냈 ë¯¸ë””ė–´ ë°ąė—… ë”œë ˆė´: {duration}", - "backup_controller_page_background_description": "ė•ąė„ ė—´ė§€ ė•Šė•„ë„ ėƒˆëĄœ ėļ”ę°€ëœ 항ëĒŠė´ ėžë™ėœŧ로 ë°ąė—…ë˜ë„ëĄ 하려면 밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ëĨŧ í™œė„ąí™”í•˜ė„¸ėš”.", - "backup_controller_page_background_is_off": "밹꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", - "backup_controller_page_background_is_on": "밹꡸ëŧėš´ë“œ ėžë™ ë°ąė—…ė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", + "backup_controller_page_background_delay": "냈 항ëĒŠ ë°ąė—… 맀뗰: {duration}", + "backup_controller_page_background_description": "ė•ąė„ ė—´ė§€ ė•Šęŗ ë„ 냈 항ëĒŠė„ ėžë™ėœŧ로 ë°ąė—…í•˜ë ¤ëŠ´ 밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ëĨŧ í™œė„ąí™”í•˜ė„¸ėš”.", + "backup_controller_page_background_is_off": "ėžë™ 밹꡸ëŧėš´ë“œ ë°ąė—…ė„ ė‚ŦėšŠí•˜ė§€ ė•ŠėŠĩ니다.", + "backup_controller_page_background_is_on": "ėžë™ 밹꡸ëŧėš´ë“œ ë°ąė—…ė„ ė‚ŦėšŠ ė¤‘ėž…ë‹ˆë‹¤.", "backup_controller_page_background_turn_off": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ ëš„í™œė„ąí™”", "backup_controller_page_background_turn_on": "밹꡸ëŧėš´ë“œ ė„œëš„ėŠ¤ í™œė„ąí™”", "backup_controller_page_background_wifi": "Wi-Fiė—ė„œë§Œ", @@ -526,42 +590,45 @@ "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": "{total} 뤑 {used} ė‚ŦėšŠ", "backup_controller_page_to_backup": "ë°ąė—…í•  ė•¨ë˛” ëĒŠëĄ", "backup_controller_page_total_sub": "ė„ íƒí•œ ė•¨ë˛”ė˜ ęŗ ėœ í•œ ė‚Ŧė§„ 및 ë™ė˜ėƒ", "backup_controller_page_turn_off": "ëš„í™œė„ąí™”", "backup_controller_page_turn_on": "í™œė„ąí™”", "backup_controller_page_uploading_file_info": "파ėŧ ė •ëŗ´ ė—…ëĄœë“œ 뤑", - "backup_err_only_album": "뜠ėŧ한 ė•¨ë˛”ė€ ė œęą°í•  눘 ė—†ėŠĩ니다.", + "backup_err_only_album": "뜠ėŧ한 ė•¨ë˛”ė€ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", + "backup_error_sync_failed": "ë™ę¸°í™”ė— ė‹¤íŒ¨í–ˆėŠĩ니다. ë°ąė—…ė„ ė§„í–‰í•  눘 ė—†ėŠĩ니다.", "backup_info_card_assets": "항ëĒŠ", "backup_manual_cancelled": "ėˇ¨ė†Œë¨", - "backup_manual_in_progress": "ė—…ëĄœë“œę°€ ė´ë¯¸ ė§„í–‰ ė¤‘ėž…ë‹ˆë‹¤. ėž ė‹œ 후 ë‹¤ė‹œ ė‹œë„í•˜ė„¸ėš”.", + "backup_manual_in_progress": "ė—…ëĄœë“œę°€ ė´ë¯¸ ė§„í–‰ ė¤‘ėž…ë‹ˆë‹¤. ėž ė‹œ 후 ë‹¤ė‹œ ė‹œë„í•˜ė„¸ėš”", "backup_manual_success": "ė„ąęŗĩ", "backup_manual_title": "ė—…ëĄœë“œ ėƒíƒœ", + "backup_options": "ë°ąė—… ė˜ĩė…˜", "backup_options_page_title": "ë°ąė—… ė˜ĩė…˜", - "backup_setting_subtitle": "밹꡸ëŧėš´ë“œ 및 íŦ꡸ëŧėš´ë“œ ė—…ëĄœë“œ 네렕 관ëĻŦ", + "backup_setting_subtitle": "밹꡸ëŧėš´ë“œ 및 íŦ꡸ëŧėš´ë“œ ë°ąė—… ė„¤ė •ė„ 관ëĻŦ합니다.", + "backup_settings_subtitle": "ė—…ëĄœë“œ ė„¤ė •ė„ 관ëĻŦ합니다.", "backward": "뒤로", "biometric_auth_enabled": "ėƒė˛´ ė¸ėĻė´ í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "biometric_locked_out": "ėƒė˛´ ė¸ėĻė´ ėŧė‹œė ėœŧ로 ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "biometric_no_options": "ė‚ŦėšŠ 가ëŠĨ한 ėƒė˛´ ė¸ėĻ ė˜ĩė…˜ ė—†ėŒ", - "biometric_not_available": "ė´ 기기는 ėƒė˛´ ė¸ėĻė„ ė§€ė›í•˜ė§€ ė•ŠėŠĩ니다.", - "birthdate_saved": "ėƒë…„ė›”ėŧė´ ė„ąęŗĩ렁ėœŧ로 ė €ėžĨë˜ė—ˆėŠĩ니다.", + "biometric_not_available": "ė´ ę¸°ę¸°ė—ė„œëŠ” ėƒė˛´ ė¸ėĻė„ ė‚ŦėšŠí•  눘 ė—†ėŠĩ니다.", + "birthdate_saved": "ėƒë…„ė›”ėŧė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", "birthdate_set_description": "ėƒë…„ė›”ėŧė€ ė‚Ŧė§„ ė´Ŧ똁 ë‹šė‹œ ė¸ëŦŧė˜ ë‚˜ė´ëĨŧ ęŗ„ė‚°í•˜ëŠ” 데 ė‚ŦėšŠëŠë‹ˆë‹¤.", "blurred_background": "흐ëϰ ë°°ę˛Ŋ", "bugs_and_feature_requests": "버그 ė œëŗ´ & 기ëŠĨ ėš”ė˛­", "build": "빌드", "build_image": "빌드 ė´ë¯¸ė§€", - "bulk_delete_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėŧ괄 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? 각 ęˇ¸ëŖšė—ė„œ 가ėžĨ 큰 항ëĒŠë§Œ ë‚¨ę¸°ęŗ  ë‚˜ë¨¸ė§€ 뤑ëŗĩ 항ëĒŠė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", - "bulk_keep_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} 그대로 ėœ ė§€í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ ė–´ë–¤ 항ëĒŠë„ ė‚­ė œí•˜ė§€ ė•Šęŗ , ëĒ¨ë“  뤑ëŗĩ ęˇ¸ëŖšė„ í™•ė¸í•œ 것ėœŧ로 래ëĻŦ합니다.", - "bulk_trash_duplicates_confirmation": "뤑ëŗĩ된 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėŧ괄 íœ´ė§€í†ĩėœŧ로 ė´ë™í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 각 ęˇ¸ëŖšė—ė„œ 가ėžĨ 큰 항ëĒŠë§Œ ë‚¨ę¸°ęŗ  ë‚˜ë¨¸ė§€ 뤑ëŗĩ 항ëĒŠė„ íœ´ė§€í†ĩėœŧ로 ė´ë™í•Šë‹ˆë‹¤.", + "bulk_delete_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? 각 ęˇ¸ëŖšė—ė„œ íŦ기가 가ėžĨ 큰 항ëĒŠė„ ė œė™¸í•œ ë‚˜ë¨¸ė§€ëĨŧ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•Šë‹ˆë‹¤. ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", + "bulk_keep_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} ėœ ė§€í•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ ëĒ¨ë“  ëš„ėŠˇí•œ 항ëĒŠė˜ ęˇ¸ëŖšė„ ė‚­ė œ ė—†ė´ ė •ëĻŦ합니다.", + "bulk_trash_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠ {count, plural, one {#氜ëĨŧ} other {#氜ëĨŧ}} íœ´ė§€í†ĩėœŧ로 ė´ë™í•˜ė‹œę˛ ėŠĩ니까? 각 ęˇ¸ëŖšė—ė„œ íŦ기가 가ėžĨ 큰 항ëĒŠė„ ė œė™¸í•œ ë‚˜ë¨¸ė§€ëĨŧ íœ´ė§€í†ĩėœŧ로 ė´ë™í•Šë‹ˆë‹¤.", "buy": "Immich ęĩŦ매", "cache_settings_clear_cache_button": "ėēė‹œ ė§€ėš°ę¸°", "cache_settings_clear_cache_button_title": "ė•ą ėēė‹œëĨŧ ė§€ė›ë‹ˆë‹¤. ė´ ėž‘ė—…ė€ ėēė‹œę°€ ë‹¤ė‹œ ėƒė„ąë  ë•ŒęšŒė§€ ė•ą ė„ąëŠĨ뗐 ėƒë‹ší•œ 똁í–Ĩė„ ë¯¸ėš  눘 ėžˆėŠĩ니다.", "cache_settings_duplicated_assets_clear_button": "ė§€ėš°ę¸°", - "cache_settings_duplicated_assets_subtitle": "ė—…ëĄœë“œë˜ė§€ ė•ŠëŠ” ė‚Ŧė§„ 및 ë™ė˜ėƒ", - "cache_settings_duplicated_assets_title": "뤑ëŗĩ 항ëĒŠ ({count})", + "cache_settings_duplicated_assets_subtitle": "ė œė™¸ëœ ė‚Ŧė§„ 및 ë™ė˜ėƒ", + "cache_settings_duplicated_assets_title": "ëš„ėŠˇí•œ 항ëĒŠ ({count})", "cache_settings_statistics_album": "ëŧė´ë¸ŒëŸŦëĻŦ ė„Ŧ네ėŧ", "cache_settings_statistics_full": "렄랴 ė´ë¯¸ė§€", "cache_settings_statistics_shared": "ęŗĩ뜠 ė•¨ë˛” ė„Ŧ네ėŧ", @@ -577,27 +644,31 @@ "cancel": "ë‹Ģ기", "cancel_search": "ę˛€ėƒ‰ ë‹Ģ기", "canceled": "ė¤‘ë‹¨ë¨", + "canceling": "ėˇ¨ė†Œ 뤑...", "cannot_merge_people": "ė¸ëŦŧė„ ëŗ‘í•Ší•  눘 ė—†ėŠĩ니다.", "cannot_undo_this_action": "ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", "cannot_update_the_description": "네ëĒ…ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "cast": "ėēėŠ¤íŠ¸", + "cast_description": "ė‚ŦėšŠ 가ëŠĨ한 ėēėŠ¤íŠ¸ 기기 ęĩŦė„ą", "change_date": "ë‚ ė§œ ëŗ€ę˛Ŋ", "change_description": "네ëĒ… ëŗ€ę˛Ŋ", "change_display_order": "í‘œė‹œ ėˆœė„œ ëŗ€ę˛Ŋ", "change_expiration_time": "ë§ŒëŖŒėŧ ëŗ€ę˛Ŋ", "change_location": "ėœ„ėš˜ ëŗ€ę˛Ŋ", "change_name": "ė´ëĻ„ ëŗ€ę˛Ŋ", - "change_name_successfully": "ė´ëĻ„ė„ ė„ąęŗĩ렁ėœŧ로 ëŗ€ę˛Ŋ했ėŠĩ니다.", + "change_name_successfully": "ė´ëĻ„ė´ ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "change_password": "비밀번호 ëŗ€ę˛Ŋ", - "change_password_description": "ė˛Ģ ëĄœęˇ¸ė¸ė´ęą°ë‚˜ 비밀번호가 ė´ˆę¸°í™”ë˜ė–´ 비밀번호ëĨŧ ė„¤ė •í•´ė•ŧ 합니다. ė•„ëž˜ė— 냈 비밀번호ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", + "change_password_description": "ė˛˜ėŒ ëĄœęˇ¸ė¸í•˜ęą°ë‚˜ 비밀번호 ė´ˆę¸°í™” ėš”ė˛­ė´ ėžˆėŠĩ니다. 냈 비밀번호ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "change_password_form_confirm_password": "현ėžŦ 비밀번호 ėž…ë Ĩ", - "change_password_form_description": "ė•ˆë…•í•˜ė„¸ėš” {name}님,\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": "냈 비밀번호 í™•ė¸", "change_pin_code": "PIN ėŊ”드 ëŗ€ę˛Ŋ", - "change_your_password": "비밀번호 ëŗ€ę˛Ŋ", - "changed_visibility_successfully": "í‘œė‹œ ė—Ŧëļ€ę°€ ė„ąęŗĩ렁ėœŧ로 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", + "change_your_password": "ė‚ŦėšŠėž ęŗ„ė •ė˜ 비밀번호ëĨŧ ëŗ€ę˛Ŋ합니다.", + "changed_visibility_successfully": "ėˆ¨ęš€ ė—Ŧëļ€ę°€ ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", + "charging": "ėļŠė „ 뤑", + "charging_requirement_mobile_backup": "밹꡸ëŧėš´ë“œ ë°ąė—…ė€ 기기 ėļŠė „ ėƒíƒœė—ė„œ 가ëŠĨ합니다", "check_corrupt_asset_backup": "ë°ąė—…ëœ 항ëĒŠė˜ ė†ėƒ ė—Ŧëļ€ í™•ė¸", "check_corrupt_asset_backup_button": "í™•ė¸ ėˆ˜í–‰", "check_corrupt_asset_backup_description": "ė´ 검ė‚Ŧ는 ëĒ¨ë“  항ëĒŠė´ ë°ąė—…ëœ 후 Wi-Fi가 ė—°ę˛°ëœ ėƒíƒœė—ė„œë§Œ ė‹¤í–‰í•˜ė„¸ėš”. ė´ ėž‘ė—…ė€ ëLJ ëļ„ ė •ë„ ė†Œėš”ë  눘 ėžˆėŠĩ니다.", @@ -607,6 +678,7 @@ "clear": "ė§€ėš°ę¸°", "clear_all": "ëĒ¨ë‘ ė§€ėš°ę¸°", "clear_all_recent_searches": "ę˛€ėƒ‰ 기록 렄랴 ė‚­ė œ", + "clear_file_cache": "파ėŧ ėēė‹œ ė§€ėš°ę¸°", "clear_message": "ëŠ”ė‹œė§€ ė§€ėš°ę¸°", "clear_value": "값 ė§€ėš°ę¸°", "client_cert_dialog_msg_confirm": "í™•ė¸", @@ -615,7 +687,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": "ë‹Ģ기", @@ -634,13 +706,15 @@ "confirm_admin_password": "관ëĻŦėž 비밀번호 í™•ė¸", "confirm_delete_face": "항ëĒŠė—ė„œ {name}ė˜ ė–ŧęĩ´ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "confirm_delete_shared_link": "ė´ ęŗĩ뜠 링íŦëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "confirm_keep_this_delete_others": "ė´ 항ëĒŠė„ ė œė™¸í•œ ėŠ¤íƒė˜ ëĒ¨ë“  항ëĒŠė´ ė‚­ė œëŠë‹ˆë‹¤. ęŗ„ė†í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_keep_this_delete_others": "ė´ 항ëĒŠė„ ė œė™¸í•œ ėŠ¤íƒė˜ ë‚˜ë¨¸ė§€ 항ëĒŠė´ ëĒ¨ë‘ ė‚­ė œëŠë‹ˆë‹¤. ęŗ„ė†í•˜ė‹œę˛ ėŠĩ니까?", "confirm_new_pin_code": "냈 PIN ėŊ”드 í™•ė¸", "confirm_password": "비밀번호 í™•ė¸", "confirm_tag_face": "ė´ ė–ŧęĩ´ė„ {name}로 íƒœęˇ¸í•˜ė‹œę˛ ėŠĩ니까?", + "confirm_tag_face_unnamed": "ė´ ė–ŧęĩ´ė— íƒœęˇ¸í•˜ė‹œę˛ ėŠĩ니까?", + "connected_device": "ė—°ę˛°ëœ 기기", "connected_to": "ė—°ę˛°ë¨:", "contain": "맞ėļ¤", - "context": "ë‚´ėšŠ", + "context": "ëŦ¸ë§Ĩ", "continue": "ęŗ„ė†", "control_bottom_app_bar_create_new_album": "ė•¨ë˛” ėƒė„ą", "control_bottom_app_bar_delete_from_immich": "Immichė—ė„œ ė‚­ė œ", @@ -665,21 +739,23 @@ "create": "ėƒė„ą", "create_album": "ė•¨ë˛” ėƒė„ą", "create_album_page_untitled": "렜ëĒŠ ė—†ėŒ", - "create_library": "ëŧė´ë¸ŒëŸŦëĻŦ ėƒė„ą", + "create_library": "냈 ëŧė´ë¸ŒëŸŦëĻŦ", "create_link": "링íŦ ėƒė„ą", "create_link_to_share": "ęŗĩ뜠 링íŦ ėƒė„ą", "create_link_to_share_description": "링íŦ가 ėžˆëŠ” ę˛Ŋ뚰 누ęĩŦ나 ė„ íƒí•œ ė‚Ŧė§„ė„ ëŗŧ 눘 ėžˆėŠĩ니다.", "create_new": "ėƒˆëĄœ 만들기", "create_new_person": "ė¸ëŦŧ ėƒė„ą", "create_new_person_hint": "ė„ íƒí•œ 항ëĒŠė˜ ė¸ëŦŧė„ 냈 ė¸ëŦŧ로 ëŗ€ę˛Ŋ", - "create_new_user": "ė‚ŦėšŠėž ėƒė„ą", + "create_new_user": "냈 ė‚ŦėšŠėž ėƒė„ą", "create_shared_album_page_share_add_assets": "항ëĒŠ ėļ”ę°€", "create_shared_album_page_share_select_photos": "ė‚Ŧė§„ ė„ íƒ", + "create_shared_link": "ęŗĩ뜠 링íŦ ėƒė„ą", "create_tag": "태그 ėƒė„ą", "create_tag_description": "냈 태그ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤. í•˜ėœ„ íƒœęˇ¸ė˜ ę˛Ŋ뚰 /ëĨŧ íŦ함한 렄랴 태그ëĒ…ė„ ėž…ë Ĩí•˜ė„¸ėš”.", - "create_user": "ė‚ŦėšŠėž ėƒė„ą", + "create_user": "ė‚ŦėšŠėž ęŗ„ė • ėƒė„ą", "created": "ėƒė„ąë¨", "created_at": "ėƒė„ąë¨", + "creating_linked_albums": "링íŦ ė—°ę˛°ëœ ė•¨ë˛” ėƒė„ą 뤑...", "crop": "ėžëĨ´ę¸°", "curated_object_page_title": "ė‚ŦëŦŧ", "current_device": "현ėžŦ 기기", @@ -687,40 +763,48 @@ "current_server_address": "현ėžŦ ė„œë˛„ ėŖŧė†Œ", "custom_locale": "ė‚ŦėšŠėž 맀렕 로ėŧ€ėŧ", "custom_locale_description": "떏떴 및 맀뗭뗐 따ëĨ¸ ë‚ ė§œ 및 ėˆĢėž í˜•ė‹ 맀렕", + "custom_url": "ė‚ŦėšŠėž 맀렕 URL", "daily_title_text_date": "Mė›” dėŧ EEEE", "daily_title_text_date_year": "yyyy년 Mė›” dėŧ EEEE", "dark": "다íŦ", + "dark_theme": "다íŦ 테마 토글", "date_after": "ë‹¤ėŒ ë‚ ė§œ ė´í›„", "date_and_time": "ë‚ ė§œ 및 ė‹œę°„", "date_before": "ë‹¤ėŒ ë‚ ė§œ ė „", "date_format": "yyyy년 Mė›” dėŧ EEEE â€ĸ a h:mm", - "date_of_birth_saved": "ėƒë…„ė›”ėŧė´ ė„ąęŗĩ렁ėœŧ로 ė €ėžĨë˜ė—ˆėŠĩ니다.", + "date_of_birth_saved": "ėƒë…„ė›”ėŧė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", "date_range": "ë‚ ė§œ ë˛”ėœ„", "day": "ėŧ", + "days": "ėŧ", "deduplicate_all": "ëĒ¨ë‘ ė‚­ė œ", "deduplication_criteria_1": "ė´ë¯¸ė§€ íŦ기 (ë°”ė´íŠ¸)", "deduplication_criteria_2": "EXIF ė •ëŗ´ 항ëĒŠ 눘", - "deduplication_info": "뤑ëŗĩ ė œęą° ė •ëŗ´", - "deduplication_info_description": "항ëĒŠė„ ėžë™ėœŧ로 미ëĻŦ ė„ íƒí•˜ęŗ  뤑ëŗĩ 항ëĒŠė„ ėŧ괄 ė œęą°í•˜ë ¤ëŠ´ ë‹¤ėŒė„ í™•ė¸í•˜ė„¸ėš”:", + "deduplication_info": "ëš„ėŠˇí•œ 항ëĒŠ ė •ëŗ´", + "deduplication_info_description": "항ëĒŠė„ ėžë™ėœŧ로 미ëĻŦ ė„ íƒí•˜ęŗ , ëš„ėŠˇí•œ 항ëĒŠė„ ęĩŦëļ„í•  때 ë‹¤ėŒ ė •ëŗ´ëĨŧ ė°¸ęŗ í•Šë‹ˆë‹¤:", "default_locale": "ę¸°ëŗ¸ 로ėŧ€ėŧ", "default_locale_description": "브ëŧėš°ė € 로ėŧ€ėŧ뗐 따ëĨ¸ ë‚ ė§œ 및 ėˆĢėž í˜•ė‹ 맀렕", "delete": "ė‚­ė œ", + "delete_action_confirmation_message": "ė´ 항ëĒŠė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? ė„œë˛„ė—ė„œëŠ” 항ëĒŠė„ íœ´ė§€í†ĩėœŧ로 ė´ë™ė‹œí‚¤ëŠ°, 로ėģŦė—ė„œë„ ė‚­ė œí•  ę˛ƒė¸ė§€ í™•ė¸ ëŠ”ė‹œė§€ę°€ í‘œė‹œëŠë‹ˆë‹¤.", + "delete_action_prompt": "{count}氜 항ëĒŠ ė‚­ė œë¨", "delete_album": "ė•¨ë˛” ė‚­ė œ", "delete_api_key_prompt": "API 키ëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "delete_dialog_alert": "ė´ 항ëĒŠë“¤ė´ Immich뙀 ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "delete_dialog_alert_local": "ė´ 항ëĒŠë“¤ė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤. Immich ė„œë˛„ė—ė„œëŠ” ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", - "delete_dialog_alert_local_non_backed_up": "ėŧëļ€ í•­ëĒŠė´ Immich뗐 ë°ąė—…ë˜ė§€ ė•Šė•˜ėœŧ늰, ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", - "delete_dialog_alert_remote": "ė´ 항ëĒŠë“¤ė´ Immich ė„œë˛„ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "delete_dialog_alert_local": "ė´ 항ëĒŠë“¤ė´ ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤. ė„œë˛„ė—ė„œëŠ” ė‚­ė œë˜ė§€ ė•ŠėŠĩ니다.", + "delete_dialog_alert_local_non_backed_up": "ėŧëļ€ í•­ëĒŠė´ ė„œë˛„ė— ë°ąė—…ë˜ė§€ ė•Šė•˜ėœŧ늰, ę¸°ę¸°ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "delete_dialog_alert_remote": "ė´ 항ëĒŠë“¤ė´ ė„œë˛„ė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "delete_dialog_ok_force": "ëŦ´ė‹œí•˜ęŗ  ė‚­ė œ", "delete_dialog_title": "똁ęĩŦ렁ėœŧ로 ė‚­ė œ", - "delete_duplicates_confirmation": "ė´ 뤑ëŗĩ 항ëĒŠë“¤ė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", + "delete_duplicates_confirmation": "ëš„ėŠˇí•œ 항ëĒŠë“¤ė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "delete_face": "ė–ŧęĩ´ ė‚­ė œ", "delete_key": "키 ė‚­ė œ", "delete_library": "ëŧė´ë¸ŒëŸŦëĻŦ ė‚­ė œ", "delete_link": "링íŦ ė‚­ė œ", + "delete_local_action_prompt": "ę¸°ę¸°ė—ė„œ {count}氜 항ëĒŠ ė‚­ė œë¨", "delete_local_dialog_ok_backed_up_only": "ë°ąė—…ëœ 항ëĒŠë§Œ ė‚­ė œ", "delete_local_dialog_ok_force": "ëŦ´ė‹œí•˜ęŗ  ė‚­ė œ", "delete_others": "다ëĨ¸ ė¸ëŦŧ ė‚­ė œ", + "delete_permanently": "똁ęĩŦ ė‚­ė œ", + "delete_permanently_action_prompt": "{count}氜 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", "delete_shared_link": "ęŗĩ뜠 링íŦ ė‚­ė œ", "delete_shared_link_dialog_title": "ęŗĩ뜠 링íŦ ė‚­ė œ", "delete_tag": "태그 ė‚­ė œ", @@ -730,51 +814,58 @@ "deletes_missing_assets": "ë””ėŠ¤íŦ뗐 ėĄ´ėžŦí•˜ė§€ ė•ŠëŠ” 항ëĒŠ ė œęą°", "description": "네ëĒ…", "description_input_hint_text": "네ëĒ… ėļ”ę°€...", - "description_input_submit_error": "네ëĒ… ė—…ë°ė´íŠ¸ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ėžė„¸í•œ ë‚´ėšŠė€ 로그ëĨŧ í™•ė¸í•˜ė„¸ėš”.", + "description_input_submit_error": "네ëĒ… ëŗ€ę˛Ŋ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ëĄœęˇ¸ė—ė„œ ėžė„¸í•œ ë‚´ėšŠė„ í™•ė¸í•˜ė„¸ėš”.", + "deselect_all": "ëĒ¨ë‘ ė„ íƒ í•´ė œ", "details": "ėƒė„¸ ė •ëŗ´", "direction": "ë°Ší–Ĩ", - "disabled": "ëš„í™œė„ąí™”ë¨", + "disabled": "ëš„í™œė„ąí™”", "disallow_edits": "ëˇ°ė–´ëĄœ 네렕", - "discord": "ë””ėŠ¤ėŊ”드", + "discord": "Discord", "discover": "íƒėƒ‰", + "discovered_devices": "ėŖŧëŗ€ 기기", "dismiss_all_errors": "ëĒ¨ë“  똤ëĨ˜ ëŦ´ė‹œ", "dismiss_error": "똤ëĨ˜ ëŦ´ė‹œ", "display_options": "í‘œė‹œ ė˜ĩė…˜", "display_order": "í‘œė‹œ ėˆœė„œ", - "display_original_photos": "ė›ëŗ¸ ė´ë¯¸ė§€ í‘œė‹œ", - "display_original_photos_setting_description": "ė›ëŗ¸ ė‚Ŧė§„ė´ ė›šęŗŧ 호환되는 ę˛Ŋ뚰 ė„Ŧ네ėŧ ëŒ€ė‹  ė›ëŗ¸ė„ í‘œė‹œí•Šë‹ˆë‹¤. ė‚Ŧė§„ė´ í‘œė‹œë˜ëŠ” ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", + "display_original_photos": "ė›ëŗ¸ ė‚Ŧė§„ í‘œė‹œ", + "display_original_photos_setting_description": "항ëĒŠė„ í‘œė‹œí•  때 ė›šęŗŧ 호환되는 ę˛Ŋ뚰 ė›ëŗ¸ė„ í‘œė‹œí•Šë‹ˆë‹¤. ė‚Ŧė§„ í‘œė‹œ ė†ë„ę°€ ëŠë ¤ė§ˆ 눘 ėžˆėŠĩ니다.", "do_not_show_again": "ė´ ëŠ”ė‹œė§€ëĨŧ ë‹¤ė‹œ í‘œė‹œí•˜ė§€ ė•ŠėŒ", "documentation": "ëŦ¸ė„œ", "done": "ė™„ëŖŒ", "download": "ë‹¤ėš´ëĄœë“œ", + "download_action_prompt": "항ëĒŠ {count}氜 ë‹¤ėš´ëĄœë“œ 뤑...", "download_canceled": "ë‹¤ėš´ëĄœë“œę°€ ėˇ¨ė†Œë˜ė—ˆėŠĩ니다.", "download_complete": "ë‹¤ė€ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "download_enqueue": "ëŒ€ę¸°ė—´ė— ë‹¤ėš´ëĄœë“œ", "download_error": "ë‹¤ėš´ëĄœë“œ 똤ëĨ˜", "download_failed": "ë‹¤ėš´ëĄœë“œ ė‹¤íŒ¨", "download_finished": "ë‹¤ėš´ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", - "download_include_embedded_motion_videos": "내ėžĨ된 ë™ė˜ėƒ", - "download_include_embedded_motion_videos_description": "ëĒ¨ė…˜ íŦí† ė— 내ėžĨ된 ë™ė˜ėƒė„ ę°œëŗ„ 파ėŧ로 íŦ함", + "download_include_embedded_motion_videos": "ëĒ¨ė…˜ íŦ토 똁냁", + "download_include_embedded_motion_videos_description": "ëĒ¨ė…˜ íŦí† ė— íŦ함된 ë™ė˜ėƒė„ ëŗ„ë„ė˜ 파ėŧ로 ëļ„ëĻŦ해 ė €ėžĨ합니다.", "download_notfound": "ë‹¤ėš´ëĄœë“œí•  눘 ė—†ėŒ", "download_paused": "ë‹¤ėš´ëĄœë“œ ėŧė‹œ ė¤‘ė§€ë¨", "download_settings": "ë‹¤ėš´ëĄœë“œ", - "download_settings_description": "ë‹¤ėš´ëĄœë“œ 네렕 관ëĻŦ", + "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} ë‹¤ėš´ëĄœë“œ 뤑...", "downloading_media": "ë¯¸ë””ė–´ ë‹¤ėš´ëĄœë“œ 뤑", "drop_files_to_upload": "ė•„ëŦ´ ęŗŗė—ë‚˜ 파ėŧė„ 드롭하ė—Ŧ ė—…ëĄœë“œ", - "duplicates": "뤑ëŗĩ 항ëĒŠ", - "duplicates_description": "각 ęˇ¸ëŖšė—ė„œ 뤑ëŗĩ된 항ëĒŠė„ í™•ė¸í•˜ęŗ  ė‚­ė œí•  항ëĒŠė„ ė„ íƒí•˜ė„¸ėš”.", + "duplicates": "ëš„ėŠˇí•œ 항ëĒŠ", + "duplicates_description": "각 ęˇ¸ëŖšė„ í™•ė¸í•˜ęŗ , ëš„ėŠˇí•œ 항ëĒŠė„ ė„ íƒí•´ ė‚­ė œ ė—Ŧëļ€ëĨŧ ę˛°ė •í•˜ė„¸ėš”.", "duration": "기간", "edit": "íŽ¸ė§‘", "edit_album": "ė•¨ë˛” ėˆ˜ė •", "edit_avatar": "프로필 ėˆ˜ė •", + "edit_birthday": "ėƒë…„ė›”ėŧ ėˆ˜ė •", "edit_date": "ë‚ ė§œ ëŗ€ę˛Ŋ", "edit_date_and_time": "ë‚ ė§œ 및 ė‹œę°„ ëŗ€ę˛Ŋ", + "edit_date_and_time_action_prompt": "항ëĒŠ {count}氜 ë‚ ė§œę°€ ëŗ€ę˛Ŋ됨", + "edit_date_and_time_by_offset": "ë‚ ė§œëĨŧ ė˜¤í”„ė…‹ėœŧ로 ëŗ€ę˛Ŋ", + "edit_date_and_time_by_offset_interval": "냈 ë‚ ė§œ ë˛”ėœ„: {from} - {to}", "edit_description": "네ëĒ… íŽ¸ė§‘", "edit_description_prompt": "냈 네ëĒ…ė„ ėž…ë Ĩí•˜ė„¸ėš”:", "edit_exclusion_pattern": "ė œė™¸ ęˇœėš™ ėˆ˜ė •", @@ -784,13 +875,14 @@ "edit_key": "키 ėˆ˜ė •", "edit_link": "링íŦ ėˆ˜ė •", "edit_location": "ėœ„ėš˜ ëŗ€ę˛Ŋ", + "edit_location_action_prompt": "항ëĒŠ {count}ę°œė˜ ėœ„ėš˜ę°€ ëŗ€ę˛Ŋ됨", "edit_location_dialog_title": "ėœ„ėš˜", "edit_name": "ė´ëĻ„ ëŗ€ę˛Ŋ", "edit_people": "ė¸ëŦŧ ėˆ˜ė •", "edit_tag": "태그 ėˆ˜ė •", "edit_title": "렜ëĒŠ ëŗ€ę˛Ŋ", "edit_user": "ė‚ŦėšŠėž ėˆ˜ė •", - "edited": "ęŗĩ뜠 링íŦ가 ėˆ˜ė •ë˜ė—ˆėŠĩ니다.", + "edited": "ėˆ˜ė •ë˜ė—ˆėŠĩ니다.", "editor": "íŽ¸ė§‘ėž", "editor_close_without_save_prompt": "ëŗ€ę˛Ŋ ė‚Ŧí•­ė´ ė €ėžĨë˜ė§€ ė•ŠėŠĩ니다.", "editor_close_without_save_title": "íŽ¸ė§‘ė„ ėĸ…ëŖŒí•˜ė‹œę˛ ėŠĩ니까?", @@ -802,92 +894,98 @@ "empty_trash": "íœ´ė§€í†ĩ ëš„ėš°ę¸°", "empty_trash_confirmation": "íœ´ė§€í†ĩė„ ëš„ėš°ė‹œę˛ ėŠĩ니까? íœ´ė§€í†ĩ뗐 ėžˆëŠ” ëĒ¨ë“  항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.\nė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다!", "enable": "í™œė„ąí™”", - "enable_biometric_auth_description": "ėƒė˛´ ė¸ėĻė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ PIN ėŊ”드ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", + "enable_backup": "ë°ąė—… í™œė„ąí™”", + "enable_biometric_auth_description": "PIN ėŊ”드ëĨŧ ėž…ë Ĩ해 ėƒė˛´ ė¸ėĻė„ í™œė„ąí™”í•˜ė„¸ėš”", "enabled": "í™œė„ąí™”ë¨", "end_date": "ėĸ…ëŖŒėŧ", "enqueued": "ëŒ€ę¸°ė—´ė— ėļ”가됨", "enter_wifi_name": "Wi-Fi ė´ëĻ„ ėž…ë Ĩ", "enter_your_pin_code": "PIN ėŊ”드 ėž…ë Ĩ", - "enter_your_pin_code_subtitle": "ėž ę¸´ í´ë”ė— ė ‘ęˇŧ하려면 PIN ėŊ”드ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", + "enter_your_pin_code_subtitle": "ėž ę¸ˆ í´ë”ė— ė ‘ęˇŧ하려면 PIN ėŊ”드ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "error": "똤ëĨ˜", "error_change_sort_album": "ė•¨ë˛” í‘œė‹œ ėˆœė„œ ëŗ€ę˛Ŋ ė‹¤íŒ¨", - "error_delete_face": "ė–ŧęĩ´ ė‚­ė œ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", - "error_loading_image": "ė´ë¯¸ė§€ 로드 똤ëĨ˜", + "error_delete_face": "항ëĒŠė—ė„œ ė–ŧęĩ´ ė‚­ė œ 뤑 똤ëĨ˜ ë°œėƒ", + "error_getting_places": "ėžĨė†Œ ė •ëŗ´ ėž…ë Ĩ ė‹¤íŒ¨", + "error_loading_image": "ė´ë¯¸ė§€ëĨŧ ëļˆëŸŦė˜¤ëŠ” 뤑 똤ëĨ˜ ë°œėƒ", + "error_loading_partners": "파트너 ëļˆëŸŦ똤揰 ė‹¤íŒ¨: {error}", "error_saving_image": "똤ëĨ˜: {error}", + "error_tag_face_bounding_box": "ė–ŧęĩ´ 태그 ė‹¤íŒ¨ - ė–ŧęĩ´ė˜ ėœ„ėš˜ëĨŧ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다.", "error_title": "똤ëĨ˜ - ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다", "errors": { "cannot_navigate_next_asset": "ë‹¤ėŒ 항ëĒŠėœŧ로 ė´ë™í•  눘 ė—†ėŠĩ니다.", "cannot_navigate_previous_asset": "ė´ė „ 항ëĒŠėœŧ로 ė´ë™í•  눘 ė—†ėŠĩ니다.", "cant_apply_changes": "ëŗ€ę˛Ŋ ė‚Ŧí•­ė„ ė ėšŠí•  눘 ė—†ėŠĩ니다.", "cant_change_activity": "í™œë™ė„ {enabled, select, true {ëš„í™œė„ąí™”} other {í™œė„ąí™”}}할 눘 ė—†ėŠĩ니다.", - "cant_change_asset_favorite": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€/ė œęą°í•  눘 ė—†ėŠĩ니다.", + "cant_change_asset_favorite": "ėĻę˛¨ė°žę¸°ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "cant_change_metadata_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė˜ ëŠ”íƒ€ë°ė´í„°ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "cant_get_faces": "ė–ŧęĩ´ė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŒ", "cant_get_number_of_comments": "댓글 눘ëĨŧ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŒ", "cant_search_people": "ė¸ëŦŧė„ ę˛€ėƒ‰í•  눘 ė—†ėŒ", "cant_search_places": "ėžĨė†ŒëĨŧ ę˛€ėƒ‰í•  눘 ė—†ėŒ", - "error_adding_assets_to_album": "ė•¨ë˛”ė— 항ëĒŠė„ ėļ”ę°€í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", - "error_adding_users_to_album": "ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”ę°€í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", - "error_deleting_shared_user": "ęŗĩėœ ëœ ė‚ŦėšŠėžëĨŧ ė œęą°í•˜ë˜ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", + "error_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_hiding_buy_button": "ęĩŦ매 버íŠŧ ėˆ¨ęš€ 뤑 똤ëĨ˜ ë°œėƒ", + "error_removing_assets_from_album": "ė•¨ë˛”ė—ė„œ 항ëĒŠ ė œęą° 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다. ėŊ˜ė†”ė—ė„œ 넏ëļ€ ė •ëŗ´ëĨŧ í™•ė¸í•˜ė„¸ėš”.", + "error_selecting_all_assets": "ëĒ¨ë“  항ëĒŠ ė„ íƒ 뤑 똤ëĨ˜ ë°œėƒ", "exclusion_pattern_already_exists": "ė´ ė œė™¸ ęˇœėš™ė€ ė´ë¯¸ ėĄ´ėžŦ합니다.", "failed_to_create_album": "ė•¨ë˛”ė„ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_create_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_edit_shared_link": "ęŗĩ뜠 링íŦëĨŧ ėˆ˜ė •í•˜ė§€ ëĒģ했ėŠĩ니다.", "failed_to_get_people": "ė¸ëŦŧ 로드 ė‹¤íŒ¨", - "failed_to_keep_this_delete_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  다ëĨ¸ 항ëĒŠė„ ė‚­ė œí•˜ė§€ ëĒģ했ėŠĩ니다.", + "failed_to_keep_this_delete_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  다ëĨ¸ 항ëĒŠ ė‚­ė œė— ė‹¤íŒ¨í–ˆėŠĩ니다.", "failed_to_load_asset": "항ëĒŠ 로드 ė‹¤íŒ¨", "failed_to_load_assets": "항ëĒŠ 로드 ė‹¤íŒ¨", "failed_to_load_notifications": "ė•ŒëĻŧ 로드 ė‹¤íŒ¨", "failed_to_load_people": "ė¸ëŦŧ 로드 ė‹¤íŒ¨", - "failed_to_remove_product_key": "ė œí’ˆ 키ëĨŧ ė œęą°í•˜ė§€ ëĒģ했ėŠĩ니다.", - "failed_to_stack_assets": "ėŠ¤íƒė„ ë§Œë“¤ė§€ ëĒģ했ėŠĩ니다.", - "failed_to_unstack_assets": "ėŠ¤íƒė„ í•´ė œí•˜ė§€ ëĒģ했ėŠĩ니다.", + "failed_to_remove_product_key": "ė œí’ˆ 키 ė œęą°ė— ė‹¤íŒ¨í–ˆėŠĩ니다.", + "failed_to_reset_pin_code": "PIN ėŊ”드 ė´ˆę¸°í™” ė‹¤íŒ¨", + "failed_to_stack_assets": "항ëĒŠ ėŠ¤íƒė— ė‹¤íŒ¨í–ˆėŠĩ니다.", + "failed_to_unstack_assets": "항ëĒŠ ėŠ¤íƒ í’€ę¸°ė— ė‹¤íŒ¨í–ˆėŠĩ니다.", "failed_to_update_notification_status": "ė•ŒëĻŧ ėƒíƒœ ė—…ë°ė´íŠ¸ ė‹¤íŒ¨", "import_path_already_exists": "ė´ 氀렏ė˜Ŧ ę˛Ŋ로는 ė´ë¯¸ ėĄ´ėžŦ합니다.", "incorrect_email_or_password": "ėž˜ëĒģ된 ė´ëŠ”ėŧ 또는 비밀번호", - "paths_validation_failed": "ę˛Ŋ로 {paths, plural, one {#氜} other {#氜}}ëĨŧ 검ėĻí•˜ė§€ ëĒģ했ėŠĩ니다.", + "paths_validation_failed": "{paths, plural, one {ę˛Ŋ로 #氜} other {ę˛Ŋ로 #氜}}가 ėœ íš¨ė„ą 검ė‚Ŧ뗐 ė‹¤íŒ¨í–ˆėŠĩ니다.", "profile_picture_transparent_pixels": "프로필 ė‚Ŧ맄뗐 íˆŦëĒ… í”Ŋė…€ė„ ė‚ŦėšŠí•  눘 ė—†ėŠĩ니다. ė‚Ŧė§„ė„ 확대하거나 ė´ë™í•˜ė„¸ėš”.", "quota_higher_than_disk_size": "í• ë‹šëŸ‰ė€ ë””ėŠ¤íŦ íŦę¸°ëŗ´ë‹¤ ėž‘ė•„ė•ŧ 합니다.", - "unable_to_add_album_users": "ė‚ŦėšŠėžëĨŧ ė•¨ë˛”ė— ėļ”가할 눘 ė—†ėŠĩ니다.", - "unable_to_add_assets_to_shared_link": "ęŗĩ뜠 링íŦ뗐 항ëĒŠė„ ėļ”가할 눘 ė—†ėŠĩ니다.", + "something_went_wrong": "ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", + "unable_to_add_album_users": "ė•¨ë˛”ė— ė‚ŦėšŠėžëĨŧ ėļ”가할 눘 ė—†ėŠĩ니다.", + "unable_to_add_assets_to_shared_link": "항ëĒŠė„ ęŗĩ뜠 링íŦ뗐 ėļ”가할 눘 ė—†ėŠĩ니다.", "unable_to_add_comment": "ëŒ“ę¸€ė„ ėļ”가할 눘 ė—†ėŠĩ니다.", "unable_to_add_exclusion_pattern": "ė œė™¸ ęˇœėš™ė„ ėļ”가할 눘 ė—†ėŠĩ니다.", "unable_to_add_import_path": "氀렏ė˜Ŧ ę˛Ŋ로ëĨŧ ėļ”가할 눘 ė—†ėŠĩ니다.", "unable_to_add_partners": "파트너ëĨŧ ėļ”가할 눘 ė—†ėŠĩ니다.", "unable_to_add_remove_archive": "{archived, select, true {ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠė„ ė œęą°í• } other {ëŗ´ę´€í•¨ėœŧ로 항ëĒŠė„ ė´ë™í• }} 눘 ė—†ėŠĩ니다.", - "unable_to_add_remove_favorites": "{favorite, select, true {ėĻę˛¨ė°žę¸°ė— 항ëĒŠė„ ėļ”가할} other {ėĻę˛¨ė°žę¸°ė—ė„œ 항ëĒŠė„ ė œęą°í• }} 눘 ė—†ėŠĩ니다.", - "unable_to_archive_unarchive": "{archived, select, true {ëŗ´ę´€í•¨ėœŧ로 항ëĒŠė„ ė´ë™í• } other {ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠė„ ė œęą°í• }} 눘 ė—†ėŠĩ니다.", - "unable_to_change_album_user_role": "ė‚ŦėšŠėžė˜ ė—­í• ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", + "unable_to_add_remove_favorites": "ėĻę˛¨ė°žę¸°ė— 항ëĒŠė„ {favorite, select, true {ėļ”ę°€} other {ė œęą°}}할 눘 ė—†ėŠĩ니다", + "unable_to_archive_unarchive": "항ëĒŠė„ {archived, select, true {ëŗ´ę´€} other {ëŗ´ę´€ í•´ė œ}}할 눘 ė—†ėŠĩ니다", + "unable_to_change_album_user_role": "ė•¨ë˛” ė‚ŦėšŠėžė˜ ė—­í• ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_date": "ë‚ ė§œëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_description": "네ëĒ…ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", - "unable_to_change_favorite": "ėĻę˛¨ė°žę¸°ė— ėļ”ę°€/ė œęą°í•  눘 ė—†ėŠĩ니다.", + "unable_to_change_favorite": "항ëĒŠė˜ ėĻę˛¨ė°žę¸°ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_location": "ėœ„ėš˜ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_password": "비밀번호ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_change_visibility": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė˜ í‘œė‹œ ė—Ŧëļ€ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŒ", "unable_to_complete_oauth_login": "OAuth ëĄœęˇ¸ė¸ė„ ė™„ëŖŒí•  눘 ė—†ėŠĩ니다.", "unable_to_connect": "ė—°ę˛°í•  눘 ė—†ėŒ", - "unable_to_copy_to_clipboard": "클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧ할 눘 ė—†ėŠĩ니다. httpsëĨŧ í†ĩ해 ė ‘ė† ė¤‘ė¸ė§€ í™•ė¸í•˜ė„¸ėš”.", + "unable_to_copy_to_clipboard": "클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧ할 눘 ė—†ėŠĩ니다. HTTPS로 ė ‘ė† ė¤‘ė¸ė§€ í™•ė¸í•˜ė„¸ėš”.", "unable_to_create_admin_account": "관ëĻŦėž ęŗ„ė •ė„ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", - "unable_to_create_api_key": "API 키ëĨŧ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", - "unable_to_create_library": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėļ”가할 눘 ė—†ėŠĩ니다.", - "unable_to_create_user": "ė‚ŦėšŠėžëĨŧ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", + "unable_to_create_api_key": "냈 API 키ëĨŧ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", + "unable_to_create_library": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒė„ąí•  눘 ė—†ėŠĩ니다.", + "unable_to_create_user": "ė‚ŦėšŠėž ęŗ„ė •ė„ ėƒė„ąí•  눘 ė—†ėŒ", "unable_to_delete_album": "ė•¨ë˛”ė„ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "unable_to_delete_asset": "항ëĒŠė„ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "unable_to_delete_assets": "항ëĒŠ ė‚­ė œ 뤑 똤ëĨ˜ ë°œėƒ", "unable_to_delete_exclusion_pattern": "ė œė™¸ ęˇœėš™ė„ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", - "unable_to_delete_import_path": "ę°€ė ¸ė˜¤ę¸° ę˛Ŋ로ëĨŧ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", + "unable_to_delete_import_path": "氀렏ė˜Ŧ ę˛Ŋ로ëĨŧ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "unable_to_delete_shared_link": "ęŗĩ뜠 링íŦëĨŧ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "unable_to_delete_user": "ė‚ŦėšŠėžëĨŧ ė‚­ė œí•  눘 ė—†ėŠĩ니다.", "unable_to_download_files": "파ėŧė„ ë‹¤ėš´ëĄœë“œí•  눘 ė—†ėŠĩ니다.", "unable_to_edit_exclusion_pattern": "ė œė™¸ ęˇœėš™ė„ ėˆ˜ė •í•  눘 ė—†ėŠĩ니다.", - "unable_to_edit_import_path": "ę°€ė ¸ė˜¤ę¸° ę˛Ŋ로ëĨŧ ėˆ˜ė •í•  눘 ė—†ėŠĩ니다.", + "unable_to_edit_import_path": "氀렏ė˜Ŧ ę˛Ŋ로ëĨŧ ėˆ˜ė •í•  눘 ė—†ėŠĩ니다.", "unable_to_empty_trash": "íœ´ė§€í†ĩė„ ëš„ėš¸ 눘 ė—†ėŠĩ니다.", "unable_to_enter_fullscreen": "렄랴 화면ėœŧ로 ė „í™˜í•  눘 ė—†ėŠĩ니다.", - "unable_to_exit_fullscreen": "렄랴 í™”ëŠ´ė—ė„œ 나갈 눘 ė—†ėŠĩ니다.", + "unable_to_exit_fullscreen": "렄랴 í™”ëŠ´ė„ ėĸ…ëŖŒí•  눘 ė—†ėŠĩ니다.", "unable_to_get_comments_number": "댓글 눘ëĨŧ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다.", "unable_to_get_shared_link": "ęŗĩ뜠 링íŦëĨŧ ëļˆëŸŦė˜¤ė§€ ëĒģ했ėŠĩ니다.", "unable_to_hide_person": "ė¸ëŦŧė„ 눍描 눘 ė—†ėŠĩ니다.", @@ -897,33 +995,33 @@ "unable_to_log_out_device": "ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•  눘 ė—†ėŠĩ니다.", "unable_to_login_with_oauth": "OAuth로 ëĄœęˇ¸ė¸í•  눘 ė—†ėŠĩ니다.", "unable_to_play_video": "ë™ė˜ėƒė„ ėžŦėƒí•  눘 ė—†ėŠĩ니다.", - "unable_to_reassign_assets_existing_person": "항ëĒŠė„ {name, select, null {다ëĨ¸ ė¸ëŦŧė—ę˛Œ} other {{name}ė—ę˛Œ}} 할당할 눘 ė—†ėŠĩ니다.", - "unable_to_reassign_assets_new_person": "항ëĒŠė„ 냈 ė¸ëŦŧ뗐 할당할 눘 ė—†ėŠĩ니다.", - "unable_to_refresh_user": "ė‚ŦėšŠėžëĨŧ ėƒˆëĄœ ęŗ ėš  눘 ė—†ėŠĩ니다.", + "unable_to_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_api_key": "API 키ëĨŧ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_remove_assets_from_shared_link": "ęŗĩ뜠 링íŦė—ė„œ 항ëĒŠė„ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_remove_library": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_remove_partner": "파트너ëĨŧ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_remove_reaction": "ë°˜ė‘ė„ ė œęą°í•  눘 ė—†ėŠĩ니다.", "unable_to_reset_password": "비밀번호ëĨŧ ė´ˆę¸°í™”í•  눘 ė—†ėŠĩ니다.", "unable_to_reset_pin_code": "PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í•  눘 ė—†ėŒ", - "unable_to_resolve_duplicate": "뤑ëŗĩ된 항ëĒŠė„ 래ëĻŦ할 눘 ė—†ėŠĩ니다.", + "unable_to_resolve_duplicate": "ëš„ėŠˇí•œ 항ëĒŠė„ 래ëĻŦ할 눘 ė—†ėŒ", "unable_to_restore_assets": "항ëĒŠė„ ëŗĩė›í•  눘 ė—†ėŠĩ니다.", - "unable_to_restore_trash": "íœ´ė§€í†ĩė—ė„œ 항ëĒŠė„ ëŗĩė›í•  눘 ė—†ėŒ", - "unable_to_restore_user": "ė‚ŦėšŠėž ė‚­ė œëĨŧ ėˇ¨ė†Œí•  눘 ė—†ėŠĩ니다.", + "unable_to_restore_trash": "íœ´ė§€í†ĩė„ ëŗĩė›í•  눘 ė—†ėŠĩ니다.", + "unable_to_restore_user": "ė‚ŦėšŠėžëĨŧ ëŗĩė›í•  눘 ė—†ėŠĩ니다.", "unable_to_save_album": "ė•¨ë˛”ė„ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", - "unable_to_save_api_key": "API 키ëĨŧ ėˆ˜ė •í•  눘 ė—†ėŠĩ니다.", + "unable_to_save_api_key": "API 키ëĨŧ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", "unable_to_save_date_of_birth": "ėƒë…„ė›”ėŧė„ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", - "unable_to_save_name": "ė´ëĻ„ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", - "unable_to_save_profile": "í”„ëĄœí•„ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", + "unable_to_save_name": "ė´ëĻ„ė„ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", + "unable_to_save_profile": "í”„ëĄœí•„ė„ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", "unable_to_save_settings": "ė„¤ė •ė„ ė €ėžĨ할 눘 ė—†ėŠĩ니다.", "unable_to_scan_libraries": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ 늤ėē”í•  눘 ė—†ėŠĩ니다.", "unable_to_scan_library": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ 늤ėē”í•  눘 ė—†ėŠĩ니다.", - "unable_to_set_feature_photo": "대표 ė‚Ŧė§„ė„ ė§€ė •í•  눘 ė—†ėŠĩ니다.", + "unable_to_set_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": "ė•¨ë˛” ėģ¤ë˛„ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", @@ -931,19 +1029,17 @@ "unable_to_update_library": "ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ė—…ë°ė´íŠ¸í•  눘 ė—†ėŠĩ니다.", "unable_to_update_location": "ėœ„ėš˜ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_update_settings": "ė„¤ė •ė„ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", - "unable_to_update_timeline_display_status": "íƒ€ėž„ëŧė¸ í‘œė‹œ ė—Ŧëļ€ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", + "unable_to_update_timeline_display_status": "íƒ€ėž„ëŧė¸ í‘œė‹œ ėƒíƒœëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다.", "unable_to_update_user": "ė‚ŦėšŠėžëĨŧ ė—…ë°ė´íŠ¸í•  눘 ė—†ėŠĩ니다.", "unable_to_upload_file": "파ėŧė„ ė—…ëĄœë“œí•  눘 ė—†ėŠĩ니다." }, "exif": "EXIF", "exif_bottom_sheet_description": "네ëĒ… ėļ”ę°€...", + "exif_bottom_sheet_description_error": "네ëĒ… ëŗ€ę˛Ŋ 뤑 똤ëĨ˜ ë°œėƒ", "exif_bottom_sheet_details": "ėƒė„¸ ė •ëŗ´", "exif_bottom_sheet_location": "ėœ„ėš˜", "exif_bottom_sheet_people": "ė¸ëŦŧ", "exif_bottom_sheet_person_add_person": "ė´ëĻ„ ėļ”ę°€", - "exif_bottom_sheet_person_age_months": "ėƒí›„ {months}ę°œė›”", - "exif_bottom_sheet_person_age_year_months": "ėƒí›„ 1년 {months}ę°œė›”", - "exif_bottom_sheet_person_age_years": "{years}넏", "exit_slideshow": "ėŠŦëŧė´ë“œ ė‡ŧ ėĸ…ëŖŒ", "expand_all": "ëĒ¨ë‘ 확ėžĨ", "experimental_settings_new_asset_list_subtitle": "ė§„í–‰ 뤑", @@ -957,44 +1053,55 @@ "explorer": "íƒėƒ‰ę¸°", "export": "ë‚´ëŗ´ë‚´ę¸°", "export_as_json": "JSONėœŧ로 ë‚´ëŗ´ë‚´ę¸°", + "export_database": "ë°ė´í„°ë˛ ė´ėŠ¤ ë‚´ëŗ´ë‚´ę¸°", + "export_database_description": "SQLite ë°ė´í„°ë˛ ė´ėŠ¤ ë‚´ëŗ´ë‚´ę¸°", "extension": "확ėžĨėž", "external": "뙏ëļ€", "external_libraries": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ", "external_network": "뙏ëļ€ ë„¤íŠ¸ė›ŒíŦ", - "external_network_sheet_info": "ė„ í˜¸í•˜ëŠ” Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆė§€ ė•Šė€ ę˛Ŋ뚰, ė•ąė€ ė•„ëž˜ė— ë‚˜ė—´ëœ URL 뤑 뗰枰 가ëŠĨ한 ė˛Ģ ë˛ˆė§¸ ėŖŧė†ŒëĨŧ ėœ„ė—ė„œëļ€í„° ėˆœė„œëŒ€ëĄœ ė‚ŦėšŠí•Šë‹ˆë‹¤.", + "external_network_sheet_info": "ė„ í˜¸í•˜ëŠ” Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆė§€ ė•Šė€ ę˛Ŋ뚰, ė•ąė€ ė•„ëž˜ ë‚˜ė—´ëœ URL 뤑 ėœ„ė—ė„œëļ€í„° ėˆœė„œëŒ€ëĄœ ė‚ŦėšŠ 가ëŠĨ한 ė˛Ģ ë˛ˆė§¸ URLė„ ė‚ŦėšŠí•˜ė—Ŧ ė—°ę˛°í•Šë‹ˆë‹¤.", "face_unassigned": "ė•Œ 눘 ė—†ėŒ", "failed": "ė‹¤íŒ¨í•¨", "failed_to_authenticate": "ė¸ėĻė— ė‹¤íŒ¨í–ˆėŠĩ니다.", "failed_to_load_assets": "항ëĒŠ 로드 ė‹¤íŒ¨", "failed_to_load_folder": "폴더 로드 ė‹¤íŒ¨", "favorite": "ėĻę˛¨ė°žę¸°", + "favorite_action_prompt": "ėĻę˛¨ė°žę¸°ė— {count}氜 항ëĒŠ ėļ”가됨", "favorite_or_unfavorite_photo": "ėĻę˛¨ė°žę¸° ėļ”ę°€/ė œęą°", "favorites": "ėĻę˛¨ė°žę¸°", "favorites_page_no_favorites": "ėĻę˛¨ė°žę¸°ëœ 항ëĒŠ ė—†ėŒ", "feature_photo_updated": "대표 ė‚Ŧė§„ ė—…ë°ė´íŠ¸ë¨", "features": "기ëŠĨ", - "features_setting_description": "ė•ą 기ëŠĨ 관ëĻŦ", + "features_in_development": "개발 ė¤‘ė¸ 기ëŠĨ", + "features_setting_description": "ė‚Ŧė§„ 및 ë™ė˜ėƒ 관ëĻŦ 기ëŠĨė„ ė„¤ė •í•Šë‹ˆë‹¤.", "file_name": "파ėŧ ė´ëĻ„", "file_name_or_extension": "파ėŧëĒ… 또는 확ėžĨėž", "filename": "파ėŧëĒ…", "filetype": "파ėŧ í˜•ė‹", "filter": "필터", "filter_people": "ė¸ëŦŧ 필터", - "filter_places": "ėžĨė†Œ 필터링", + "filter_places": "ėžĨė†Œ 필터", "find_them_fast": "ė´ëĻ„ėœŧ로 ę˛€ėƒ‰í•˜ė—Ŧ ëš ëĨ´ę˛Œ ė°žę¸°", + "first": "ė˛Ģ ë˛ˆė§¸", "fix_incorrect_match": "ėž˜ëĒģ된 ëļ„ëĨ˜ ėˆ˜ė •", "folder": "폴더", "folder_not_found": "폴더ëĨŧ ė°žė„ 눘 ė—†ėŒ", "folders": "폴더", - "folders_feature_description": "파ėŧ ė‹œėŠ¤í…œė— ėžˆëŠ” ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ 폴더 ëŗ´ę¸°ëĄœ íƒėƒ‰", + "folders_feature_description": "파ėŧ ė‹œėŠ¤í…œė˜ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ 폴더 ëŗ´ę¸°ëĄœ íƒėƒ‰í•Šë‹ˆë‹¤.", + "forgot_pin_code_question": "PIN 번호ëĨŧ ėžŠė–´ë˛„ë ¸ë‚˜ėš”?", "forward": "ė•žėœŧ로", + "gcast_enabled": "ęĩŦ글 ėēėŠ¤íŠ¸", + "gcast_enabled_description": "ė´ 기ëŠĨė€ Googleė˜ 뙏ëļ€ ëĻŦė†ŒėŠ¤ëĨŧ ė‚ŦėšŠí•˜ė—Ŧ ė‹¤í–‰ëŠë‹ˆë‹¤.", "general": "ėŧ반", - "get_help": "ë„ė›€ ėš”ė˛­", - "get_wifiname_error": "Wi-Fi ė´ëĻ„ė„ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다. í•„ėš”í•œ ęļŒí•œė´ í—ˆėšŠë˜ė–´ ėžˆęŗ  Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆëŠ”ė§€ í™•ė¸í•˜ė„¸ėš”.", + "geolocation_instruction_location": "GPS ėĸŒí‘œę°€ íŦ함된 항ëĒŠė„ 클ëĻ­í•´ ėœ„ėš˜ëĨŧ ė‚ŦėšŠí•˜ęą°ë‚˜, ė§€ë„ė—ė„œ 링렑 ėœ„ėš˜ëĨŧ ė„ íƒí•˜ė„¸ėš”", + "get_help": "ë„ė›€ ė–ģ기", + "get_wifiname_error": "Wi-Fi ė´ëĻ„ė„ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다. í•„ėˆ˜ ęļŒí•œė´ ëļ€ė—Ŧë˜ė—ˆëŠ”ė§€, Wi-Fi ë„¤íŠ¸ė›ŒíŦ뗐 ė—°ę˛°ë˜ė–´ ėžˆëŠ”ė§€ í™•ė¸í•˜ė„¸ėš”.", "getting_started": "ė‹œėž‘í•˜ę¸°", "go_back": "뒤로", "go_to_folder": "폴더로 ė´ë™", "go_to_search": "ę˛€ėƒ‰ėœŧ로 ė´ë™", + "gps": "GPS", + "gps_missing": "GPS ė—†ėŒ", "grant_permission": "ęļŒí•œ ëļ€ė—Ŧ", "group_albums_by": "ë‹¤ėŒėœŧ로 ė•¨ë˛” ęˇ¸ëŖší™”...", "group_country": "ęĩ­ę°€ëŗ„ëĄœ ęˇ¸ëŖší™”", @@ -1005,12 +1112,15 @@ "haptic_feedback_switch": "햅틱 í”ŧ드백 í™œė„ąí™”", "haptic_feedback_title": "햅틱 í”ŧ드백", "has_quota": "할당량", + "hash_asset": "항ëĒŠ í•´ė‹ą", + "hashed_assets": "í•´ė‹œëœ 항ëĒŠ", + "hashing": "í•´ė‹ą 뤑...", "header_settings_add_header_tip": "헤더 ėļ”ę°€", "header_settings_field_validator_msg": "ę°’ė€ ëš„ė›Œë‘˜ 눘 ė—†ėŠĩ니다.", "header_settings_header_name_input": "헤더 ė´ëĻ„", "header_settings_header_value_input": "헤더 값", - "headers_settings_tile_subtitle": "ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ė— 함ęģ˜ ė „ė†Ąí•  í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", - "headers_settings_tile_title": "ė‚ŦėšŠėž ė •ė˜ í”„ëĄė‹œ 헤더", + "headers_settings_tile_subtitle": "ë„¤íŠ¸ė›ŒíŦ ėš”ė˛­ ė „ė†Ąė— íŦ함할 í”„ëĄė‹œ 헤더ëĨŧ ė •ė˜í•Šë‹ˆë‹¤.", + "headers_settings_tile_title": "ė‚ŦėšŠėž 맀렕 í”„ëĄė‹œ 헤더", "hi_user": "ė•ˆë…•í•˜ė„¸ėš” {name}님, ({email})", "hide_all_people": "ëĒ¨ë“  ė¸ëŦŧ 눍揰揰", "hide_gallery": "ę°¤ëŸŦëĻŦ 눍揰揰", @@ -1019,24 +1129,26 @@ "hide_person": "ė¸ëŦŧ 눍揰揰", "hide_unnamed_people": "ė´ëĻ„ ė—†ëŠ” ė¸ëŦŧ 눍揰揰", "home_page_add_to_album_conflicts": "{album} ė•¨ë˛”ė— 항ëĒŠ {added}개가 ėļ”ę°€ë˜ė—ˆėŠĩ니다. 항ëĒŠ {failed}개는 ė•¨ë˛”ė— ė´ë¯¸ ėĄ´ėžŦ합니다.", - "home_page_add_to_album_err_local": "ę¸°ę¸°ė˜ 항ëĒŠė€ ė•¨ë˛”ė— ėļ”가할 눘 ė—†ėŠĩ니다. 건너뜁니다.", + "home_page_add_to_album_err_local": "로ėģŦ 항ëĒŠė€ ė•¨ë˛”ė— ėļ”가할 눘 뗆떴 건너뜁니다.", "home_page_add_to_album_success": "{album} ė•¨ë˛”ė— 항ëĒŠ {added}개가 ėļ”ę°€ë˜ė—ˆėŠĩ니다.", - "home_page_album_err_partner": "íŒŒíŠ¸ë„ˆė˜ 항ëĒŠė€ ė•¨ë˛”ė— ėļ”가할 눘 ė—†ėŠĩ니다. 건너뜁니다.", - "home_page_archive_err_local": "ę¸°ę¸°ė˜ 항ëĒŠė€ ëŗ´ę´€í•  눘 ė—†ėŠĩ니다. 건너뜁니다.", - "home_page_archive_err_partner": "ëŗ´ę´€í•¨ėœŧ로 íŒŒíŠ¸ë„ˆė˜ 항ëĒŠė€ ė´ë™í•  눘 ė—†ėŠĩ니다. 건너뜁니다.", + "home_page_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_locked_error_local": "로ėģŦ 항ëĒŠė€ ėž ę¸´ 폴더로 ė´ë™í•  눘 ė—†ėŠĩ니다. ėƒëžĩ됨", - "home_page_locked_error_partner": "파트너 항ëĒŠė€ ėž ę¸´ 폴더로 ė´ë™í•  눘 ė—†ėŠĩ니다. ėƒëžĩ됨", - "home_page_share_err_local": "ę¸°ę¸°ė—ë§Œ ė €ėžĨ된 항ëĒŠė€ 링íŦ로 ęŗĩėœ í•  눘 뗆떴 건너뜁니다.", - "home_page_upload_err_limit": "한 ë˛ˆė— ėĩœëŒ€ 30ę°œė˜ 항ëĒŠë§Œ ė—…ëĄœë“œí•  눘 ėžˆėŠĩ니다.", + "home_page_locked_error_local": "로ėģŦ 항ëĒŠė€ ėž ę¸ˆ 폴더로 ė´ë™í•  눘 ė—†ėŠĩ니다. ėƒëžĩ됨", + "home_page_locked_error_partner": "파트너 항ëĒŠė€ ėž ę¸ˆ 폴더로 ė´ë™í•  눘 ė—†ėŠĩ니다. ėƒëžĩ됨", + "home_page_share_err_local": "로ėģŦ 항ëĒŠė€ 링íŦ로 ęŗĩėœ í•  눘 뗆떴 건너뜁니다.", + "home_page_upload_err_limit": "한 ë˛ˆė— ėĩœëŒ€ 30ę°œė˜ 항ëĒŠęšŒė§€ ė—…ëĄœë“œí•  눘 ėžˆėŠĩ니다.", "host": "í˜¸ėŠ¤íŠ¸", "hour": "ė‹œę°„", + "hours": "ė‹œę°„", "id": "ID", + "idle": "ėœ íœ´", "ignore_icloud_photos": "iCloud ė‚Ŧė§„ ė œė™¸", "ignore_icloud_photos_description": "iCloud뗐 ė €ėžĨ된 ė‚Ŧė§„ė´ Immich뗐 ė—…ëĄœë“œë˜ė§€ ė•ŠėŠĩ니다.", "image": "ė´ë¯¸ė§€", @@ -1076,19 +1188,31 @@ "invalid_date_format": "ėœ íš¨í•˜ė§€ ė•Šė€ ë‚ ė§œ í˜•ė‹", "invite_people": "ė‚ŦėšŠėž ė´ˆëŒ€", "invite_to_album": "ė•¨ë˛”ėœŧ로 ė´ˆëŒ€", + "ios_debug_info_fetch_ran_at": "ë°ė´í„° ëļˆëŸŦ똤揰 ėž‘ė—…ė´ {dateTime}뗐 ė‹¤í–‰ë¨", + "ios_debug_info_last_sync_at": "ë§ˆė§€ë§‰ 동기화: {dateTime}", + "ios_debug_info_no_processes_queued": "대기 ė¤‘ė¸ 밹꡸ëŧėš´ë“œ ėž‘ė—… ė—†ėŒ", + "ios_debug_info_no_sync_yet": "밹꡸ëŧėš´ë“œ 동기화 ėž‘ė—…ė´ 땄링 ė‹¤í–‰ë˜ė§€ ė•ŠėŒ", + "ios_debug_info_processes_queued": "{count, plural, one {밹꡸ëŧėš´ë“œ ėž‘ė—… {count}氜 대기 뤑} other {밹꡸ëŧėš´ë“œ ėž‘ė—… {count}氜 대기 뤑}}", + "ios_debug_info_processing_ran_at": "{dateTime}뗐 ė§„í–‰ë¨", "items_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠ", "jobs": "ėž‘ė—…", "keep": "ėœ ė§€", "keep_all": "ëĒ¨ë‘ ėœ ė§€", "keep_this_delete_others": "ė´ 항ëĒŠė€ ėœ ė§€í•˜ęŗ  ë‚˜ë¨¸ė§€ëŠ” ė‚­ė œ", - "kept_this_deleted_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  {count, plural, one {#ę°œė˜ 항ëĒŠ} other {#ę°œė˜ 항ëĒŠ}}ė„ ė‚­ė œí–ˆėŠĩ니다.", + "kept_this_deleted_others": "ė´ 항ëĒŠė„ ėœ ė§€í•˜ęŗ  {count, plural, one {#ę°œė˜ 항ëĒŠ} other {#ę°œė˜ 항ëĒŠ}}ė„ ė‚­ė œí•¨", "keyboard_shortcuts": "í‚¤ëŗ´ë“œ 단ėļ•키", "language": "떏떴", - "language_setting_description": "ė„ í˜¸í•˜ëŠ” 떏떴 ė„ íƒ", + "language_no_results_subtitle": "다ëĨ¸ ę˛€ėƒ‰ė–´ëĨŧ ė‚ŦėšŠí•´ ëŗ´ė„¸ėš”.", + "language_no_results_title": "결ęŗŧ ė—†ėŒ", + "language_search_hint": "떏떴 ę˛€ėƒ‰...", + "language_setting_description": "ė‚ŦėšŠí•  떏떴ëĨŧ ė„ íƒí•˜ė„¸ėš”.", + "large_files": "큰 파ėŧ", + "last": "ë§ˆė§€ë§‰", "last_seen": "ėĩœęˇŧ 활동", "latest_version": "ėĩœė‹  ë˛„ė „", "latitude": "ėœ„ë„", "leave": "나가기", + "leave_album": "ė•¨ë˛”ė—ė„œ 나가기", "lens_model": "ėš´ëŠ”ëŧ 렌ėψ ëĒ¨ë¸", "let_others_respond": "다ëĨ¸ ė‚ŦėšŠėžė˜ ë°˜ė‘ í—ˆėšŠ", "level": "레벨", @@ -1099,29 +1223,36 @@ "library_page_sort_asset_count": "항ëĒŠ 눘", "library_page_sort_created": "만든 ë‚ ė§œ", "library_page_sort_last_modified": "ë§ˆė§€ë§‰ ėˆ˜ė •", - "library_page_sort_title": "ė•¨ë˛” 렜ëĒŠ", + "library_page_sort_title": "ė•¨ë˛”ëĒ…", + "licenses": "ëŧė´ė„ ėŠ¤", "light": "ëŧė´íŠ¸", + "like": "ėĸ‹ė•„ėš”", "like_deleted": "ėĸ‹ė•„ėš”ę°€ ė‚­ė œë˜ė—ˆėŠĩ니다.", "link_motion_video": "ëĒ¨ė…˜ ëš„ë””ė˜¤ 링íŦ", - "link_options": "링íŦ ė˜ĩė…˜", "link_to_oauth": "OAuth뗐 뗰枰", "linked_oauth_account": "OAuth ęŗ„ė •ė´ ė—°ę˛°ë˜ė—ˆėŠĩ니다.", "list": "ëĒŠëĄ", "loading": "로드 뤑", "loading_search_results_failed": "ę˛€ėƒ‰ 결ęŗŧ 로드 ė‹¤íŒ¨", + "local": "로ėģŦ", + "local_asset_cast_failed": "ė„œë˛„ė— ė—…ëĄœë“œë˜ė§€ ė•Šė€ 항ëĒŠė„ ėēėŠ¤íŒ…í•  눘 ė—†ėŒ", + "local_assets": "로ėģŦ 항ëĒŠ", + "local_media_summary": "로ėģŦ ë¯¸ë””ė–´ ėš”ė•Ŋ", "local_network": "로ėģŦ ë„¤íŠ¸ė›ŒíŦ", - "local_network_sheet_info": "ė§€ė •í•œ Wi-Fi뗐 ė—°ę˛°ëœ ę˛Ŋ뚰 ė•ąė€ 해당 URLė„ í†ĩ해 ė„œë˛„ė— ė—°ę˛°í•Šë‹ˆë‹¤.", + "local_network_sheet_info": "ė§€ė •ëœ Wi-FiëĨŧ ė‚ŦėšŠí•  때 ė•ąė´ ė•„ëž˜ URL로 ė„œë˛„ė— ė—°ę˛°í•Šë‹ˆë‹¤.", "location_permission": "ėœ„ėš˜ ęļŒí•œ", - "location_permission_content": "ėžë™ ė „í™˜ 기ëŠĨė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Immich가 현ėžŦ Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•˜ę¸° ėœ„í•œ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", + "location_permission_content": "ėžë™ ė „í™˜ 기ëŠĨė„ ė‚ŦėšŠí•˜ë ¤ëŠ´ Immich가 현ėžŦ ė—°ę˛°ëœ Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„ė„ í™•ė¸í•´ė•ŧ 하며, ė´ëĨŧ ėœ„í•´ 'ė •í™•í•œ ėœ„ėš˜' ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", "location_picker_choose_on_map": "ė§€ë„ė—ė„œ ė„ íƒ", "location_picker_latitude_error": "ėœ íš¨í•œ ėœ„ë„ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", - "location_picker_latitude_hint": "ė´ęŗŗė— ėœ„ë„ ėž…ë Ĩ", + "location_picker_latitude_hint": "ė—Ŧ揰뗐 ėœ„ë„ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”", "location_picker_longitude_error": "ėœ íš¨í•œ ę˛Ŋ도ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", - "location_picker_longitude_hint": "ė´ęŗŗė— ę˛Ŋ도 ėž…ë Ĩ", + "location_picker_longitude_hint": "ė—Ŧ揰뗐 ę˛Ŋ도ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”", "lock": "ėž ę¸ˆ", - "locked_folder": "ėž ę¸´ 폴더", + "locked_folder": "ėž ę¸ˆ 폴더", + "log_detail_title": "ėƒė„¸ 로그", "log_out": "ëĄœęˇ¸ė•„ė›ƒ", "log_out_all_devices": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒ", + "logged_in_as": "{user}로 ëĄœęˇ¸ė¸ë¨", "logged_out_all_devices": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒë˜ė—ˆėŠĩ니다.", "logged_out_device": "ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒë˜ė—ˆėŠĩ니다.", "login": "ëĄœęˇ¸ė¸", @@ -1134,38 +1265,39 @@ "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_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": "ëĄœęˇ¸ė¸ ėœ ė§€", "login_form_server_empty": "ė„œë˛„ URLė„ ėž…ë Ĩí•˜ė„¸ėš”.", "login_form_server_error": "ė„œë˛„ė— ė—°ę˛°í•  눘 ė—†ėŠĩ니다.", "login_has_been_disabled": "ëĄœęˇ¸ė¸ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", - "login_password_changed_error": "비밀번호ëĨŧ ëŗ€ę˛Ŋ하던 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", - "login_password_changed_success": "비밀번호가 ė„ąęŗĩ렁ėœŧ로 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", + "login_password_changed_error": "비밀번호 ëŗ€ę˛Ŋ 뤑 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", + "login_password_changed_success": "비밀번호가 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "logout_all_device_confirmation": "ëĒ¨ë“  ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•˜ė‹œę˛ ėŠĩ니까?", "logout_this_device_confirmation": "ė´ ę¸°ę¸°ė—ė„œ ëĄœęˇ¸ė•„ė›ƒí•˜ė‹œę˛ ėŠĩ니까?", + "logs": "로그", "longitude": "ę˛Ŋ도", "look": "ëŗ´ę¸°", "loop_videos": "ë™ė˜ėƒ 반ëŗĩ", - "loop_videos_description": "ėƒė„¸ ëŗ´ę¸°ė—ė„œ ėžë™ėœŧ로 ë™ė˜ėƒė„ 반ëŗĩ ėžŦėƒí•Šë‹ˆë‹¤.", - "main_branch_warning": "현ėžŦ 개발 ë˛„ė „ė„ ė‚ŦėšŠ ė¤‘ėž…ë‹ˆë‹¤. ė •ė‹ ë˛„ė „ė„ ė‚ŦėšŠí•˜ëŠ” ę˛ƒė„ 강ë Ĩ히 ęļŒėžĨ합니다!", + "loop_videos_description": "ėƒė„¸ ëŗ´ę¸°ė—ė„œ ė˜ėƒė„ 반ëŗĩ ėžŦėƒí•Šë‹ˆë‹¤.", + "main_branch_warning": "개발 ë˛„ė „ė„ ė‚ŦėšŠ ė¤‘ėž…ë‹ˆë‹¤. ė •ė‹ ëĻ´ëĻŦ늤 ë˛„ė „ ė‚ŦėšŠė„ ęļŒėžĨ합니다!", "main_menu": "ëŠ”ė¸ 메뉴", "make": "ė œėĄ°ė‚Ŧ", + "manage_geolocation": "ėœ„ėš˜ ė •ëŗ´ 관ëĻŦ", "manage_shared_links": "ęŗĩ뜠 링íŦ 관ëĻŦ", - "manage_sharing_with_partners": "íŒŒíŠ¸ë„ˆė™€ ęŗĩ뜠 관ëĻŦ", - "manage_the_app_settings": "ė•ą 네렕 관ëĻŦ", - "manage_your_account": "ė‚ŦėšŠėž ęŗ„ė • 관ëĻŦ", - "manage_your_api_keys": "API 키 관ëĻŦ", - "manage_your_devices": "ëĄœęˇ¸ė¸ëœ 기기 관ëĻŦ", - "manage_your_oauth_connection": "OAuth 뗰枰 관ëĻŦ", + "manage_sharing_with_partners": "ęŗĩėœ í•  파트너ëĨŧ ė´ˆëŒ€í•˜ęą°ë‚˜ ė œęą°í•Šë‹ˆë‹¤.", + "manage_the_app_settings": "ė•ą ë™ėž‘ 및 í‘œė‹œ 환ę˛Ŋė„ ė‚ŦėšŠėž ė •ė˜í•Šë‹ˆë‹¤.", + "manage_your_account": "ė‚ŦėšŠėžė˜ ęŗ„ė • ė •ëŗ´ëĨŧ í™•ė¸í•˜ęŗ  ëŗ€ę˛Ŋ합니다.", + "manage_your_api_keys": "API 키ëĨŧ ėƒė„ą, ė‚­ė œí•˜ęą°ë‚˜ ęļŒí•œė„ 관ëĻŦ합니다.", + "manage_your_devices": "현ėžŦ ëĄœęˇ¸ė¸í•œ 기기ëĨŧ í™•ė¸í•˜ęŗ  ëĄœęˇ¸ė•„ė›ƒí•  눘 ėžˆėŠĩ니다.", + "manage_your_oauth_connection": "OAuth ė—°ę˛°ė„ 관ëĻŦ합니다.", "map": "ė§€ë„", - "map_assets_in_bound": "ė‚Ŧė§„ {count}氜", - "map_assets_in_bounds": "ė‚Ŧė§„ {count}氜", + "map_assets_in_bounds": "{count, plural, =0 {ė‚Ŧė§„ ė—†ėŒ} one {ė‚Ŧė§„ #氜} other {ė‚Ŧė§„ #氜}}", "map_cannot_get_user_location": "ė‚ŦėšŠėžė˜ ėœ„ėš˜ëĨŧ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다.", "map_location_dialog_yes": "똈", "map_location_picker_page_use_location": "ė´ ėœ„ėš˜ ė‚ŦėšŠ", @@ -1173,7 +1305,6 @@ "map_location_service_disabled_title": "ėœ„ėš˜ ė„œëš„ėŠ¤ ëš„í™œė„ąí™”ë¨", "map_marker_for_images": "{country}, {city}ė—ė„œ ė´Ŧė˜ëœ ė´ë¯¸ė§€ė˜ ė§€ë„ 마ėģ¤", "map_marker_with_image": "ė´ë¯¸ė§€ę°€ ėžˆëŠ” ė§€ë„ 마ėģ¤", - "map_no_assets_in_bounds": "ė´ ė˜ė—­ė— ė‚Ŧė§„ ė—†ėŒ", "map_no_location_permission_content": "현ėžŦ ėœ„ėš˜ė˜ 항ëĒŠė„ í‘œė‹œí•˜ë ¤ëŠ´ ėœ„ėš˜ ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤. ė§€ę¸ˆ í—ˆėšŠí•˜ė‹œę˛ ėŠĩ니까?", "map_no_location_permission_title": "ėœ„ėš˜ ęļŒí•œ ęą°ëļ€ë¨", "map_settings": "ė§€ë„ 네렕", @@ -1190,13 +1321,14 @@ "map_zoom_to_see_photos": "ėļ•ė†Œí•˜ė—Ŧ ė‚Ŧė§„ ëŗ´ę¸°", "mark_all_as_read": "ëĒ¨ë‘ ėŊėŒėœŧ로 í‘œė‹œ", "mark_as_read": "ėŊėŒėœŧ로 í‘œė‹œ", - "marked_all_as_read": "ëĒ¨ë‘ ėŊė€ 것ėœŧ로 í‘œė‹œí–ˆėŠĩ니다.", + "marked_all_as_read": "ëĒ¨ë‘ ėŊėŒėœŧ로 í‘œė‹œí–ˆėŠĩ니다.", "matches": "ėŧėš˜", + "matching_assets": "ėŧėš˜í•˜ëŠ” 항ëĒŠ", "media_type": "ë¯¸ë””ė–´ ėĸ…ëĨ˜", "memories": "ėļ”ė–ĩ", "memories_all_caught_up": "ëĒ¨ë‘ í™•ė¸í•¨", - "memories_check_back_tomorrow": "내ėŧ 더 ë§Žė€ ėļ”ė–ĩė„ í™•ė¸í•˜ė„¸ėš”.", - "memories_setting_description": "ėļ”ė–ĩ í‘œė‹œ 네렕 관ëĻŦ", + "memories_check_back_tomorrow": "내ėŧ 더 ë§Žė€ ėļ”ė–ĩė„ í™•ė¸í•´ëŗ´ė„¸ėš”.", + "memories_setting_description": "ėļ”ė–ĩ í‘œė‹œ ė„¤ė •ė„ 관ëĻŦ합니다.", "memories_start_over": "ë‹¤ė‹œ ëŗ´ę¸°", "memories_swipe_to_close": "ėœ„ëĄœ ë°€ė–´ė„œ ë‹Ģ기", "memory": "ėļ”ė–ĩ", @@ -1204,40 +1336,47 @@ "menu": "메뉴", "merge": "ëŗ‘í•Š", "merge_people": "ė¸ëŦŧ ëŗ‘í•Š", - "merge_people_limit": "한 ë˛ˆė— ėĩœëŒ€ 5ę°œė˜ ė–ŧęĩ´ë§Œ í•Šėš  눘 ėžˆėŠĩ니다.", + "merge_people_limit": "한 ë˛ˆė— ėĩœëŒ€ 5ëĒ…ęšŒė§€ í•Šėš  눘 ėžˆėŠĩ니다.", "merge_people_prompt": "ė¸ëŦŧë“¤ė„ ëŗ‘í•Ší•˜ė‹œę˛ ėŠĩ니까? ė´ ėž‘ė—…ė€ 되돌ëĻ´ 눘 ė—†ėŠĩ니다.", - "merge_people_successfully": "ė¸ëŦŧė„ ė„ąęŗĩ렁ėœŧ로 í•Šėŗ¤ėŠĩ니다.", + "merge_people_successfully": "ė„ íƒí•œ ė¸ëŦŧė„ í•Šėŗ¤ėŠĩ니다.", "merged_people_count": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė„ í•Šėŗ¤ėŠĩ니다.", "minimize": "ėĩœė†Œí™”", "minute": "ëļ„", + "minutes": "ëļ„", "missing": "누ëŊ", "model": "ëĒ¨ë¸", "month": "ė›”", "monthly_title_text_date_format": "yyyy년 Mė›”", "more": "ë”ëŗ´ę¸°", "move": "ė´ë™", - "move_off_locked_folder": "ėž ę¸´ í´ë”ė—ė„œ í•´ė œ", - "move_to_locked_folder": "ėž ę¸´ 폴더로 ė´ë™", - "move_to_locked_folder_confirmation": "ė´ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė´ ëĒ¨ë“  ė•¨ë˛”ė—ė„œ ė œęą°ë˜ëŠ°, ėž ę¸´ í´ë”ė—ė„œë§Œ ëŗŧ 눘 ėžˆėŠĩ니다.", + "move_off_locked_folder": "ėž ę¸ˆ í´ë”ė—ė„œ í•´ė œ", + "move_to_lock_folder_action_prompt": "ėž ę¸ˆ 폴더로 항ëĒŠ {count}氜 ė´ë™ë¨", + "move_to_locked_folder": "ėž ę¸ˆ 폴더로 ė´ë™", + "move_to_locked_folder_confirmation": "ė„ íƒí•œ ė‚Ŧė§„ 또는 ë™ė˜ėƒė´ ëĒ¨ë“  ė•¨ë˛”ė—ė„œ ė œęą°ë˜ëŠ°, ėž ę¸ˆ í´ë”ė—ė„œë§Œ ëŗŧ 눘 ėžˆėŠĩ니다.", "moved_to_archive": "ëŗ´ę´€í•¨ėœŧ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", "moved_to_library": "ëŧė´ë¸ŒëŸŦëĻŦ로 항ëĒŠ {count, plural, one {#氜} other {#氜}} ė´ë™ë¨", "moved_to_trash": "íœ´ė§€í†ĩėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다.", - "multiselect_grid_edit_date_time_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė˜ ë‚ ė§œëŠ” ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다. 건너뜁니다.", - "multiselect_grid_edit_gps_err_read_only": "ėŊ기 ė „ėšŠ 항ëĒŠė˜ ėœ„ėš˜ëŠ” ëŗ€ę˛Ŋ할 눘 ė—†ėŠĩ니다. 건너뜁니다.", + "multiselect_grid_edit_date_time_err_read_only": "ėŊ기 ė „ėšŠė¸ 항ëĒŠė˜ ë‚ ė§œëŠ” ëŗ€ę˛Ŋ할 눘 뗆떴 건너뜁니다.", + "multiselect_grid_edit_gps_err_read_only": "ėŊ기 ė „ėšŠė¸ 항ëĒŠė˜ ėœ„ėš˜ëŠ” ëŗ€ę˛Ŋ할 눘 뗆떴 건너뜁니다.", "mute_memories": "ėļ”ė–ĩ ėŒė†Œęą°", "my_albums": "내 ė•¨ë˛”", "name": "ė´ëĻ„", "name_or_nickname": "ė´ëĻ„ 또는 ë‹‰ë„¤ėž„", + "network_requirement_photos_upload": "ė‚Ŧė§„ ë°ąė—…ė— ëĒ¨ë°”ėŧ ë°ė´í„° ė‚ŦėšŠ", + "network_requirement_videos_upload": "ë™ė˜ėƒ ë°ąė—…ė— ëĒ¨ë°”ėŧ ë°ė´í„° ė‚ŦėšŠ", + "network_requirements": "ë„¤íŠ¸ė›ŒíŦ ėš”ęĩŦė‚Ŧ항", + "network_requirements_updated": "ë„¤íŠ¸ė›ŒíŦ ėƒíƒœę°€ ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다. ë°ąė—… ëŒ€ę¸°ė—´ė„ ė´ˆę¸°í™”í•Šë‹ˆë‹¤.", "networking_settings": "뗰枰", - "networking_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ 네렕 관ëĻŦ", + "networking_subtitle": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸ ė„¤ė •ė„ 관ëĻŦ합니다.", "never": "ė—†ėŒ", "new_album": "냈 ė•¨ë˛”", - "new_api_key": "API 키 ėƒė„ą", + "new_api_key": "냈 API 키", "new_password": "냈 비밀번호", "new_person": "냈 ė¸ëŦŧ ėƒė„ą", "new_pin_code": "냈 PIN ėŊ”드", - "new_pin_code_subtitle": "ėž ę¸´ 폴더 ė„¤ė •ė„ ė‹œėž‘í•Šë‹ˆë‹¤. ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ė•ˆė „í•˜ę˛Œ ëŗ´í˜¸í•˜ę¸° ėœ„í•œ PIN ėŊ”드ëĨŧ ė„¤ė •í•˜ė„¸ėš”.", - "new_user_created": "ė‚ŦėšŠėžę°€ ėƒė„ąë˜ė—ˆėŠĩ니다.", + "new_pin_code_subtitle": "ėž ę¸ˆ í´ë”ė— ė˛˜ėŒ ė ‘ęˇŧí•˜ė…¨ėŠĩ니다. ė´ęŗŗė— ė•ˆė „í•˜ę˛Œ ė ‘ęˇŧ하기 ėœ„í•œ PIN ėŊ”드ëĨŧ ė„¤ė •í•˜ė„¸ėš”.", + "new_timeline": "냈 íƒ€ėž„ëŧė¸", + "new_user_created": "ė‚ŦėšŠėž ęŗ„ė •ė´ ėƒė„ąë˜ė—ˆėŠĩ니다.", "new_version_available": "냈 ë˛„ė „ ė‚ŦėšŠ 가ëŠĨ", "newest_first": "ėĩœė‹ ėˆœ", "next": "ë‹¤ėŒ", @@ -1246,22 +1385,29 @@ "no_albums_message": "ė•¨ë˛”ė„ ėƒė„ąí•˜ė—Ŧ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ė •ëĻŦ하기", "no_albums_with_name_yet": "땄링 해당하는 ė´ëĻ„ė˜ ė•¨ë˛”ė´ ė—†ëŠ” 것 같ėŠĩ니다.", "no_albums_yet": "땄링 ė•¨ë˛”ė´ ė—†ëŠ” 것 같ėŠĩ니다.", - "no_archived_assets_message": "ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ëŗ´ę´€í•¨ėœŧ로 ė´ë™í•˜ė—Ŧ ëĒŠëĄė—ė„œ 눍揰揰", - "no_assets_message": "ė—Ŧ기ëĨŧ 클ëĻ­í•˜ė—Ŧ ė˛Ģ ė‚Ŧė§„ė„ ė—…ëĄœë“œí•˜ė„¸ėš”.", + "no_archived_assets_message": "ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ëŗ´ę´€í•¨ėœŧ로 ė˜Žę˛¨ íŦ토 ëˇ°ė—ė„œ 눍揰揰", + "no_assets_message": "ė—Ŧ기ëĨŧ 클ëĻ­í•´ ė˛Ģ ė‚Ŧė§„ė„ ė—…ëĄœë“œí•˜ė„¸ėš”.", "no_assets_to_show": "í‘œė‹œí•  항ëĒŠ ė—†ėŒ", - "no_duplicates_found": "뤑ëŗĩ된 항ëĒŠė´ ė—†ėŠĩ니다.", + "no_cast_devices_found": "ėēėŠ¤íŠ¸ 기기 ė—†ėŒ", + "no_checksum_local": "랴íŦė„Ŧė´ ė—†ėŠĩ니다. 로ėģŦ 항ëĒŠė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다", + "no_checksum_remote": "랴íŦė„Ŧė´ ė—†ėŠĩ니다. 뙏ëļ€ í•­ëĒŠė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다", + "no_duplicates_found": "ëš„ėŠˇí•œ 항ëĒŠė´ ė—†ėŠĩ니다.", "no_exif_info_available": "EXIF ė •ëŗ´ ė—†ėŒ", "no_explore_results_message": "더 ë§Žė€ ė‚Ŧė§„ė„ ė—…ëĄœë“œí•˜ė—Ŧ íƒėƒ‰ 기ëŠĨė„ ė‚ŦėšŠí•˜ė„¸ėš”.", - "no_favorites_message": "ėĻę˛¨ė°žę¸°ė— ėĸ‹ė•„하는 ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ėļ”ę°€í•˜ę¸°", - "no_libraries_message": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦëĨŧ ėƒė„ąí•˜ė—Ŧ ę¸°ėĄ´ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ í™•ė¸í•˜ė„¸ėš”.", - "no_locked_photos_message": "ėž ę¸´ í´ë”ė˜ ė‚Ŧė§„ 및 ë™ė˜ėƒė€ ėˆ¨ę˛¨ė§€ëŠ° ëŧė´ë¸ŒëŸŦëĻŦëĨŧ íƒėƒ‰í•  때 í‘œė‹œë˜ė§€ ė•ŠėŠĩ니다.", + "no_favorites_message": "ėĻę˛¨ė°žę¸°ė—ė„œ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ ëš ëĨ´ę˛Œ ė°žę¸°", + "no_libraries_message": "뙏ëļ€ ëŧė´ë¸ŒëŸŦëĻŦ로 다ëĨ¸ ę˛ŊëĄœė˜ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ í™•ė¸í•˜ė„¸ėš”.", + "no_local_assets_found": "랴íŦė„Ŧ뗐 맞는 로ėģŦ 항ëĒŠė„ ė°žė„ 눘 ė—†ėŠĩ니다", + "no_locked_photos_message": "ėž ę¸ˆ í´ë”ė˜ ė‚Ŧė§„ 및 ë™ė˜ėƒė€ ėˆ¨ę˛¨ė§€ëŠ° ëŧė´ë¸ŒëŸŦëĻŦëĨŧ íƒėƒ‰í•  때 í‘œė‹œë˜ė§€ ė•ŠėŠĩ니다.", "no_name": "ė´ëĻ„ ė—†ėŒ", "no_notifications": "ė•ŒëĻŧ ė—†ėŒ", "no_people_found": "ėŧėš˜í•˜ëŠ” ė¸ëŦŧ ė—†ėŒ", "no_places": "ėžĨė†Œ ė—†ėŒ", - "no_results": "결ęŗŧ가 ė—†ėŠĩ니다.", + "no_remote_assets_found": "랴íŦė„Ŧ뗐 맞는 뙏ëļ€ í•­ëĒŠė„ ė°žė„ 눘 ė—†ėŠĩ니다", + "no_results": "결ęŗŧ ė—†ėŒ", "no_results_description": "ë™ė˜ė–´ 또는 더 ėŧë°˜ė ė¸ ë‹¨ė–´ëĨŧ ė‚ŦėšŠí•´ ëŗ´ė„¸ėš”.", - "no_shared_albums_message": "ęŗĩ뜠 ė•¨ë˛”ė„ ë§Œë“¤ė–´ ėŖŧëŗ€ ė‚Ŧ람들ęŗŧ ė‚Ŧė§„ 및 ë™ė˜ėƒ ęŗĩ뜠", + "no_shared_albums_message": "ė•¨ë˛”ė„ ë§Œë“¤ė–´ ėŖŧëŗ€ ė‚Ŧ람들ęŗŧ ė‚Ŧė§„ 및 ë™ė˜ėƒė„ ęŗĩėœ í•˜ė„¸ėš”.", + "no_uploads_in_progress": "ė§„í–‰ ė¤‘ė¸ ė—…ëĄœë“œ ė—†ėŒ", + "not_available": "ė—†ėŒ", "not_in_any_album": "ė•¨ë˛”ė— ė—†ėŒ", "not_selected": "ė„ íƒë˜ė§€ ė•ŠėŒ", "note_apply_storage_label_to_previously_uploaded assets": "및溠: ė´ė „ė— ė—…ëĄœë“œí•œ 항ëĒŠė—ë„ ėŠ¤í† ëĻŦė§€ ë ˆė´ë¸”ė„ ė ėšŠí•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‹¤í–‰í•Šë‹ˆë‹¤,", @@ -1273,16 +1419,20 @@ "notification_permission_list_tile_title": "ė•ŒëĻŧ ęļŒí•œ", "notification_toggle_setting_description": "ė´ëŠ”ėŧ ė•ŒëĻŧ í™œė„ąí™”", "notifications": "ė•ŒëĻŧ", - "notifications_setting_description": "ė•ŒëĻŧ 네렕 관ëĻŦ", + "notifications_setting_description": "ė•ŒëĻŧ ė „ė†Ą ė„¤ė •ė„ 관ëĻŦ합니다.", "oauth": "OAuth", "official_immich_resources": "Immich ęŗĩė‹ ëĻŦė†ŒėŠ¤", "offline": "ė˜¤í”„ëŧė¸", + "offset": "ė˜¤í”„ė…‹", "ok": "í™•ė¸", "oldest_first": "ė˜¤ëž˜ëœ 눜", "on_this_device": "ė´ ėžĨėš˜ė—ė„œ", - "onboarding": "ė˜¨ëŗ´ë”Š", - "onboarding_privacy_description": "ė´ ė„ íƒė  기ëŠĨė€ 뙏ëļ€ ė„œëš„ėŠ¤ëĨŧ ė‚ŦėšŠí•˜ëŠ°, 관ëĻŦėž ė„¤ė •ė—ė„œ ė–¸ė œë“  ëš„í™œė„ąí™”í•  눘 ėžˆėŠĩ니다.", - "onboarding_theme_description": "ėƒ‰ėƒ 테마ëĨŧ ė„ íƒí•˜ė„¸ėš”. ë‚˜ė¤‘ė— ė„¤ė •ė—ė„œ ëŗ€ę˛Ŋ할 눘 ėžˆėŠĩ니다.", + "onboarding": "봈揰 네렕", + "onboarding_locale_description": "ė‚ŦėšŠí•  떏떴ëĨŧ ė„ íƒí•˜ė„¸ėš”. ė„¤ė •ė—ė„œ ė–¸ė œë“  ëŗ€ę˛Ŋ할 눘 ėžˆėŠĩ니다.", + "onboarding_privacy_description": "ë‹¤ėŒ ė„ íƒė  기ëŠĨė€ 뙏ëļ€ ė„œëš„ėŠ¤ëĨŧ ė‚ŦėšŠí•˜ëŠ° ė„¤ė •ė—ė„œ ė–¸ė œë“  ëš„í™œė„ąí™”í•  눘 ėžˆėŠĩ니다.", + "onboarding_server_welcome_description": "ëLJ 氀맀 ėŧë°˜ė ė¸ ė„¤ė •ė„ ė§„í–‰í•˜ę˛ ėŠĩ니다.", + "onboarding_theme_description": "ė‚ŦėšŠí•  테마ëĨŧ ė„ íƒí•˜ė„¸ėš”. ė„¤ė •ė—ė„œ ė–¸ė œë“  ëŗ€ę˛Ŋ할 눘 ėžˆėŠĩ니다.", + "onboarding_user_welcome_description": "ė‹œėž‘í•´ ëŗ´ę˛ ėŠĩ니다!", "onboarding_welcome_user": "{user}님, í™˜ė˜í•Šë‹ˆë‹¤", "online": "똍ëŧė¸", "only_favorites": "ėĻę˛¨ė°žę¸°ë§Œ", @@ -1292,10 +1442,13 @@ "open_the_search_filters": "ę˛€ėƒ‰ 필터 뗴揰", "options": "ė˜ĩė…˜", "or": "또는", + "organize_into_albums": "ė•¨ë˛”ėœŧ로 ė •ëĻŦ하기", + "organize_into_albums_description": "현ėžŦ 동기화 ė„¤ė •ė„ ė‚ŦėšŠí•˜ė—Ŧ ę¸°ėĄ´ ė‚Ŧė§„ė„ ė•¨ë˛”ėœŧ로 ė •ëĻŦ합니다", "organize_your_library": "ëŧė´ë¸ŒëŸŦëĻŦ ė •ëĻŦ", "original": "ė›ëŗ¸", "other": "기타", "other_devices": "다ëĨ¸ 기기", + "other_entities": "기타 ė—”í‹°í‹°", "other_variables": "기타 ëŗ€ėˆ˜", "owned": "ė†Œėœ í•¨", "owner": "ė†Œėœ ėž", @@ -1306,8 +1459,8 @@ "partner_list_user_photos": "{user}ë‹˜ė˜ ė‚Ŧė§„", "partner_list_view_all": "ëĒ¨ë‘ ëŗ´ę¸°", "partner_page_empty_message": "ė‚Ŧė§„ė´ 땄링 ė–´ë–¤ íŒŒíŠ¸ë„ˆė™€ë„ ęŗĩėœ ë˜ė§€ ė•Šė•˜ėŠĩ니다.", - "partner_page_no_more_users": "더 ė´ėƒ ėļ”가할 ė‚ŦėšŠėžę°€ ė—†ėŠĩ니다.", - "partner_page_partner_add_failed": "파트너ëĨŧ ėļ”ę°€í•˜ė§€ ëĒģ했ėŠĩ니다.", + "partner_page_no_more_users": "ėļ”가할 ė‚ŦėšŠėžę°€ ė—†ėŠĩ니다.", + "partner_page_partner_add_failed": "파트너 ėļ”ę°€ ė‹¤íŒ¨", "partner_page_select_partner": "파트너 ė„ íƒ", "partner_page_shared_to_title": "ęŗĩ뜠 ëŒ€ėƒ", "partner_page_stop_sharing_content": "더 ė´ėƒ {partner}ë‹˜ė´ ė‚Ŧ맄뗐 ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", @@ -1329,16 +1482,18 @@ "paused": "ėŧė‹œ ė •ė§€ë¨", "pending": "ė§„í–‰ 뤑", "people": "ė¸ëŦŧ", - "people_edits_count": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė„ ėˆ˜ė •í–ˆėŠĩ니다.", + "people_edits_count": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė´ ėˆ˜ė •ë˜ė—ˆėŠĩ니다.", "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 {항ëĒŠ #氜ëĨŧ}} 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? {count, plural, one {항ëĒŠė´} other {항ëĒŠė´}} ė•¨ë˛”ė— íŦ함된 ę˛Ŋ뚰 ė•¨ë˛”ė—ė„œë„ ė œęą°ëŠë‹ˆë‹¤.", + "permanently_delete_assets_prompt": "{count, plural, one {ė´ 항ëĒŠė„} other {항ëĒŠ #氜ëĨŧ}} 똁ęĩŦ렁ėœŧ로 ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까? {count, plural, one {항ëĒŠė´} other {항ëĒŠė´}} ė•¨ë˛”ė— íŦ함된 ę˛Ŋ뚰 ė•¨ë˛”ė—ė„œ ė œęą°ëŠë‹ˆë‹¤.", "permanently_deleted_asset": "항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë˜ė—ˆėŠĩ니다.", - "permanently_deleted_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 똁ęĩŦ렁ėœŧ로 ė‚­ė œë˜ė—ˆėŠĩ니다.", + "permanently_deleted_assets_count": "{count, plural, one {#氜} other {#氜}} 항ëĒŠė´ 똁ęĩŦ렁ėœŧ로 ė‚­ė œë¨", + "permission": "ęļŒí•œ", + "permission_empty": "하나 ė´ėƒė˜ ęļŒí•œė„ ė„ íƒí•´ė•ŧ 합니다.", "permission_onboarding_back": "뒤로", "permission_onboarding_continue_anyway": "ëŦ´ė‹œí•˜ęŗ  ė§„í–‰", "permission_onboarding_get_started": "ė‹œėž‘í•˜ę¸°", @@ -1346,8 +1501,11 @@ "permission_onboarding_permission_denied": "ęļŒí•œė´ ė—†ėŠĩ니다. ImmichëĨŧ ė‚ŦėšŠí•˜ë ¤ëŠ´ ė„¤ė •ė—ė„œ ė‚Ŧė§„ 및 ë™ė˜ėƒ ęļŒí•œė„ ëļ€ė—Ŧí•˜ė„¸ėš”.", "permission_onboarding_permission_granted": "ęļŒí•œė´ ëļ€ė—Ŧë˜ė—ˆėŠĩ니다! ė¤€ëš„ę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "permission_onboarding_permission_limited": "ęļŒí•œė´ ė—†ėŠĩ니다. Immich가 렄랴 ę°¤ëŸŦëĻŦ ėģŦë ‰ė…˜ė„ ë°ąė—…í•˜ęŗ  관ëĻŦ할 눘 ėžˆë„ëĄ 하려면 ė„¤ė •ė—ė„œ ė‚Ŧė§„ 및 ë™ė˜ėƒ ęļŒí•œė„ ëļ€ė—Ŧí•˜ė„¸ėš”.", - "permission_onboarding_request": "ė‚Ŧė§„ 및 ë™ė˜ėƒ ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", + "permission_onboarding_request": "Immich가 ė‚Ŧė§„ 및 ë™ė˜ėƒė— ė ‘ęˇŧ하기 ėœ„í•œ ęļŒí•œė´ í•„ėš”í•Šë‹ˆë‹¤.", "person": "ė¸ëŦŧ", + "person_age_months": "{months, plural, one {#ę°œė›”} other {#ę°œė›”}}", + "person_age_year_months": "1넏, {months, plural, one {#ę°œė›”} other {#ę°œė›”}}", + "person_age_years": "{years, plural, other {#넏}}", "person_birthdate": "{date} ėļœėƒ", "person_hidden": "{name}{hidden, select, true { (ėˆ¨ęš€)} other {}}", "photo_shared_all_users": "ė´ë¯¸ ëĒ¨ë“  ė‚ŦėšŠėžė™€ ė‚Ŧė§„ė„ ęŗĩ뜠 ė¤‘ė´ęą°ë‚˜ 다ëĨ¸ ė‚ŦėšŠėžę°€ ė—†ëŠ” 것 같ėŠĩ니다.", @@ -1356,7 +1514,7 @@ "photos_count": "ė‚Ŧė§„ {count, plural, one {{count, number}氜} other {{count, number}氜}}", "photos_from_previous_years": "ė§€ë‚œ ëLJ ë…„ę°„ė˜ ė‚Ŧė§„", "pick_a_location": "ėœ„ėš˜ ė„ íƒ", - "pin_code_changed_successfully": "PIN ėŊ”드ëĨŧ ëŗ€ę˛Ŋ했ėŠĩ니다.", + "pin_code_changed_successfully": "PIN ėŊ”ë“œę°€ ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "pin_code_reset_successfully": "PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í–ˆėŠĩ니다.", "pin_code_setup_successfully": "PIN ėŊ”드ëĨŧ ė„¤ė •í–ˆėŠĩ니다.", "pin_verification": "PIN ėŊ”드 ė¸ėĻ", @@ -1369,21 +1527,26 @@ "play_or_pause_video": "ë™ė˜ėƒ ėžŦėƒ/ėŧė‹œ ė •ė§€", "please_auth_to_access": "ęŗ„ė† ė§„í–‰í•˜ë ¤ëŠ´ ė¸ėĻí•˜ė„¸ėš”.", "port": "íŦ트", - "preferences_settings_subtitle": "ė•ą 네렕 관ëĻŦ", + "preferences_settings_subtitle": "ė•ą ę°œė¸ ė„¤ė •ė„ 관ëĻŦ합니다.", "preferences_settings_title": "ę°œė¸ 네렕", - "preset": "ė‚Ŧė „ 네렕", + "preparing": "ė¤€ëš„ 뤑", + "preset": "프ëĻŦė…‹", "preview": "미ëĻŦ ëŗ´ę¸°", "previous": "ė´ė „", "previous_memory": "ė´ė „ ėļ”ė–ĩ", + "previous_or_next_day": "ė´ė „/ë‹¤ėŒ 날로", + "previous_or_next_month": "ė´ė „/ë‹¤ėŒ ë‹Ŧ로", "previous_or_next_photo": "ė´ė „/ë‹¤ėŒ ė‚Ŧė§„ėœŧ로", - "primary": "ėŖŧėš”", - "privacy": "ę°œė¸ ė •ëŗ´", + "previous_or_next_year": "ė´ė „/ë‹¤ėŒ ė—°ë„ëĄœ", + "primary": "ę¸°ëŗ¸", + "privacy": "ę°œė¸ė •ëŗ´", "profile": "프로필", "profile_drawer_app_logs": "로그", "profile_drawer_client_out_of_date_major": "ëĒ¨ë°”ėŧ ė•ąė´ ėĩœė‹  ë˛„ė „ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_drawer_client_out_of_date_minor": "ëĒ¨ë°”ėŧ ė•ąė´ ėĩœė‹  ë˛„ė „ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_drawer_client_server_up_to_date": "클ëŧė´ė–¸íŠ¸ė™€ ė„œë˛„ę°€ ėĩœė‹  ėƒíƒœėž…ë‹ˆë‹¤.", "profile_drawer_github": "Github", + "profile_drawer_readonly_mode": "ėŊ기 ė „ėšŠ ëĒ¨ë“œ í™œė„ąí™”. ėœ ė € ė•„ë°”íƒ€ ė•„ė´ėŊ˜ė„ 描枌 눌ëŸŦ í•´ė œí•  눘 ėžˆėŠĩ니다.", "profile_drawer_server_out_of_date_major": "ė„œë˛„ ë˛„ė „ė´ ėĩœė‹ ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_drawer_server_out_of_date_minor": "ė„œë˛„ ë˛„ė „ė´ ėĩœė‹ ė´ ė•„ë‹™ë‹ˆë‹¤. ėĩœė‹  ë˛„ė „ėœŧ로 ė—…ë°ė´íŠ¸í•˜ė„¸ėš”.", "profile_image_of_user": "{user}ë‹˜ė˜ 프로필 ė´ë¯¸ė§€", @@ -1391,26 +1554,26 @@ "public_album": "ęŗĩ氜 ė•¨ë˛”", "public_share": "ëĒ¨ë“  ė‚ŦėšŠėžė™€ ęŗĩ뜠", "purchase_account_info": "ė„œíŦ터", - "purchase_activated_subtitle": "Immich뙀 ė˜¤í”ˆ ė†ŒėŠ¤ ė†Œí”„íŠ¸ė›¨ė–´ëĨŧ ė§€ė›í•´ėŖŧė…”ė„œ 감ė‚Ŧ합니다.", + "purchase_activated_subtitle": "Immich뙀 ė˜¤í”ˆ ė†ŒėŠ¤ ė†Œí”„íŠ¸ė›¨ė–´ëĨŧ ė§€ė›í•´ ėŖŧė…”ė„œ 감ė‚Ŧ합니다.", "purchase_activated_time": "{date} 등록됨", - "purchase_activated_title": "ė œí’ˆ 키가 ė„ąęŗĩ렁ėœŧ로 ë“ąëĄë˜ė—ˆėŠĩ니다.", + "purchase_activated_title": "ė œí’ˆ 키가 í™œė„ąí™”ë˜ė—ˆėŠĩ니다.", "purchase_button_activate": "등록", "purchase_button_buy": "ęĩŦ매", "purchase_button_buy_immich": "Immich ęĩŦ매", "purchase_button_never_show_again": "ë‹¤ė‹œ ëŗ´ė§€ ė•Šę¸°", "purchase_button_reminder": "30ėŧ í›„ė— ë‹¤ė‹œ ė•ŒëĻŧ", - "purchase_button_remove_key": "ė œí’ˆ 키 ė œęą°", + "purchase_button_remove_key": "키 ė œęą°", "purchase_button_select": "ė„ íƒ", "purchase_failed_activation": "ë“ąëĄí•˜ė§€ ëĒģ했ėŠĩ니다. ė´ëŠ”ėŧ로 ė „ė†Ąëœ 키ëĨŧ ė •í™•ížˆ ėž…ë Ĩí–ˆëŠ”ė§€ í™•ė¸í•˜ė„¸ėš”!", "purchase_individual_description_1": "ę°œė¸ ė‚ŦėšŠėžėšŠ", "purchase_individual_description_2": "ė„œíŦ터 ë°°ė§€", "purchase_individual_title": "ę°œė¸", - "purchase_input_suggestion": "ė œí’ˆ 키ëĨŧ ëŗ´ėœ í•˜ęŗ  ėžˆë‚˜ėš”? ė•„ëž˜ė— ė œí’ˆ 키ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", - "purchase_license_subtitle": "ImmichëĨŧ ęĩŦ매하ė—Ŧ ė§€ė†ė ė¸ ę°œë°œė— ë„ė›€ė„ ėŖŧė„¸ėš”.", + "purchase_input_suggestion": "ė œí’ˆ 키ëĨŧ ëŗ´ėœ  ė¤‘ė¸ę°€ėš”? ė•„ëž˜ė— 키ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", + "purchase_license_subtitle": "ImmichëĨŧ ęĩŦ매하ė—Ŧ ė§€ė†ė ė¸ ę°œë°œė„ ė§€ė›í•˜ė„¸ėš”.", "purchase_lifetime_description": "ėŧíšŒė„ą ęĩŦ매", "purchase_option_title": "ęĩŦ매 ė˜ĩė…˜", - "purchase_panel_info_1": "ImmichëĨŧ 개발하는 데는 ë§Žė€ ė‹œę°„ęŗŧ 노ë Ĩė´ í•„ėš”í•Šë‹ˆë‹¤. 뚰ëĻŦ는 ėĸ‹ė€ ė•ąė„ 만들기 ėœ„í•´ 풀 íƒ€ėž„ ę°œë°œėžė™€ 함ęģ˜í•˜ęŗ  ėžˆėœŧ늰, ėĩœėĸ…렁ėœŧ로 ė˜¤í”ˆ ė†ŒėŠ¤ ė†Œí”„íŠ¸ė›¨ė–´ė™€ 비ėĻˆë‹ˆėŠ¤ 행동 뜤ëĻŦ가 ę°œë°œėžė—ę˛Œ ė§€ė† 가ëŠĨ한 ėˆ˜ėž…ė›ė„ 렜ęŗĩí•˜ęŗ  ė°Šėˇ¨ė ė¸ 클ëŧėš°ë“œ ė„œëš„ėŠ¤ëĨŧ ëŒ€ė˛´í•  눘 ėžˆëŠ” ę°œė¸ ė •ëŗ´ ëŗ´í˜¸ ėƒíƒœęŗ„ëĨŧ ęĩŦėļ•하는 ę˛ƒė„ ė›í•Šë‹ˆë‹¤.", - "purchase_panel_info_2": "ėœ ëŖŒ 기ëŠĨė„ ėļ”ę°€í•˜ė§€ ė•Šę¸°ëĄœ ė•Ŋė†í–ˆę¸°ė— ė´ ęĩŦ매는 ė–´ë– í•œ ėļ”ę°€ 기ëŠĨ도 렜ęŗĩí•˜ė§€ ė•ŠėŠĩ니다. 뚰ëĻŦ는 Immichė˜ ė§€ė†ė ė¸ ę°œë°œė„ ė§€ė›í•˜ëŠ” ė‚ŦėšŠėž ė—ŦëŸŦëļ„ė—ę˛Œ ė˜ėĄ´í•˜ęŗ  ėžˆėŠĩ니다.", + "purchase_panel_info_1": "ImmichëĨŧ 개발하는 데는 ë§Žė€ ė‹œę°„ęŗŧ 노ë Ĩė´ í•„ėš”í•Šë‹ˆë‹¤. 뚰ëĻŦ는 ėĸ‹ė€ ė•ąė„ 만들기 ėœ„í•´ 풀 íƒ€ėž„ ę°œë°œėžė™€ 함ęģ˜í•˜ęŗ  ėžˆėœŧ늰, ėĩœėĸ…렁ėœŧ로 ė˜¤í”ˆ ė†ŒėŠ¤ ė†Œí”„íŠ¸ė›¨ė–´ė™€ 비ėĻˆë‹ˆėŠ¤ 행동 뜤ëĻŦ가 ę°œë°œėžė—ę˛Œ ė§€ė† 가ëŠĨ한 ėˆ˜ėž…ė›ė„ 렜ęŗĩí•˜ęŗ  ė°Šėˇ¨ė ė¸ 클ëŧėš°ë“œ ė„œëš„ėŠ¤ëĨŧ ëŒ€ė˛´í•  눘 ėžˆëŠ” ę°œė¸ ė •ëŗ´ ëŗ´í˜¸ ėƒíƒœęŗ„ëĨŧ ęĩŦėļ•하는 ę˛ƒė„ ëĒŠí‘œëĄœ 합니다.", + "purchase_panel_info_2": "ėœ ëŖŒí™” ė •ėą…ė„ ë„ėž…í•˜ė§€ ė•Šę¸°ëĄœ ė•Ŋė†í–ˆę¸°ė— ė´ ęĩŦ매는 ė–´ë– í•œ ėļ”ę°€ 기ëŠĨ도 렜ęŗĩí•˜ė§€ ė•ŠėŠĩ니다. Immichė˜ ė§€ė†ė ė¸ ę°œë°œė€ ë‹šė‹ ęŗŧ ę°™ė€ ė—ŦëŸŦ ė‚ŦėšŠėžė˜ ė§€ė›ė´ ėžˆę¸°ė— 가ëŠĨ합니다.", "purchase_panel_title": "í”„ëĄœė íŠ¸ 맀뛐", "purchase_per_server": "ė„œë˛„ë‹š", "purchase_per_user": "ė‚ŦėšŠėžë‹š", @@ -1421,16 +1584,21 @@ "purchase_server_description_1": "ė„œë˛„ 렄랴뗐 ė ėšŠ", "purchase_server_description_2": "ė„œíŦ터 ë°°ė§€", "purchase_server_title": "ė„œë˛„", - "purchase_settings_server_activated": "ė„œë˛„ ė œí’ˆ 키는 관ëĻŦėžę°€ 관ëĻŦ합니다.", + "purchase_settings_server_activated": "ė„œë˛„ ė œí’ˆ 키는 관ëĻŦėžę°€ ė œė–´í•Šë‹ˆë‹¤.", + "query_asset_id": "ėŋŧëĻŦ 항ëĒŠ ID", + "queue_status": "렄랴 {total}, {count} 대기 뤑", "rating": "등급", "rating_clear": "등급 ė´ˆę¸°í™”", "rating_count": "{count, plural, one {#렐} other {#렐}}", "rating_description": "ėƒė„¸ ė •ëŗ´ íŒ¨ë„ė— EXIF 등급 태그 í‘œė‹œ", "reaction_options": "ë°˜ė‘ ė˜ĩė…˜", - "read_changelog": "ëŗ€ę˛Ŋ ė‚Ŧ항 ëŗ´ę¸°", + "read_changelog": "ëŗ€ę˛Ŋ ë‚´ė—­ ëŗ´ę¸°", + "readonly_mode_disabled": "ėŊ기 ė „ėšŠ ëĒ¨ë“œ ëš„í™œė„ąí™”", + "readonly_mode_enabled": "ėŊ기 ė „ėšŠ ëĒ¨ë“œ í™œė„ąí™”", + "ready_for_upload": "ė—…ëĄœë“œ ė¤€ëš„ ė™„ëŖŒ", "reassign": "ë‹¤ė‹œ 할당", - "reassigned_assets_to_existing_person": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 {name, select, null {다ëĨ¸ ė¸ëŦŧ뗐} other {{name}뗐}} í• ë‹šë˜ė—ˆėŠĩ니다.", - "reassigned_assets_to_new_person": "항ëĒŠ {count, plural, one {#氜} other {#氜}}가 냈 ė¸ëŦŧ뗐 í• ë‹šë˜ė—ˆėŠĩ니다.", + "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": "ėĩœęˇŧ ė•¨ë˛”", @@ -1446,10 +1614,13 @@ "refresh_thumbnails": "ė„Ŧ네ėŧ ë‹¤ė‹œ ėƒė„ą", "refreshed": "ėƒˆëĄœęŗ ėš¨ė´ ė™„ëŖŒë˜ė—ˆėŠĩ니다.", "refreshes_every_file": "ę¸°ėĄ´ 파ėŧ 및 냈 파ėŧ 늤ėē”", - "refreshing_encoded_video": "ė¸ėŊ”ë”Šė„ ë‹¤ė‹œ ė§„í–‰í•˜ëŠ” 뤑...", - "refreshing_faces": "ė–ŧęĩ´ ėƒˆëĄœęŗ ėš¨ 뤑...", - "refreshing_metadata": "ëŠ”íƒ€ë°ė´í„°ëĨŧ ėƒˆëĄœ ęŗ ėš˜ëŠ” 뤑...", + "refreshing_encoded_video": "ë™ė˜ėƒė„ ë‹¤ė‹œ ė¸ėŊ”딊핊니다.", + "refreshing_faces": "ė–ŧęĩ´ė„ ėƒˆëĄœęŗ ėš¨í•Šë‹ˆë‹¤.", + "refreshing_metadata": "ëŠ”íƒ€ë°ė´í„°ëĨŧ ėƒˆëĄœęŗ ėš¨í•Šë‹ˆë‹¤.", "regenerating_thumbnails": "ė„Ŧ네ėŧė„ ë‹¤ė‹œ ėƒė„ąí•˜ëŠ” 뤑...", + "remote": "ė›ę˛Š", + "remote_assets": "ė›ę˛Š 항ëĒŠ", + "remote_media_summary": "ė›ę˛Š ë¯¸ë””ė–´ ėš”ė•Ŋ", "remove": "ė œęą°", "remove_assets_album_confirmation": "ė•¨ë˛”ė—ė„œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", "remove_assets_shared_link_confirmation": "ęŗĩ뜠 링íŦė—ė„œ 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė œęą°í•˜ė‹œę˛ ėŠĩ니까?", @@ -1457,12 +1628,15 @@ "remove_custom_date_range": "맞ėļ¤ ę¸°ę°„ ė œęą°", "remove_deleted_assets": "누ëŊ된 파ėŧ ė œęą°", "remove_from_album": "ė•¨ë˛”ė—ė„œ ė œęą°", + "remove_from_album_action_prompt": "ė•¨ë˛”ė—ė„œ 항ëĒŠ {count}氜 ė œęą°ë¨", "remove_from_favorites": "ėĻę˛¨ė°žę¸°ė—ė„œ ė œęą°", - "remove_from_locked_folder": "ėž ę¸´ í´ë”ė—ė„œ ë‚´ëŗ´ë‚´ę¸°", - "remove_from_locked_folder_confirmation": "ė´ ė‚Ŧė§„ęŗŧ ė˜ėƒė„ ėž ę¸´ í´ë”ė—ė„œ í•´ė œí•˜ė‹œę˛ ėŠĩ니까? ė´ė œ ëŧė´ë¸ŒëŸŦëĻŦė—ė„œ ëŗ´ė´ę˛Œ 됩니다.", + "remove_from_lock_folder_action_prompt": "ėž ę¸ˆ í´ë”ė—ė„œ 항ëĒŠ {count}氜 ë‚´ëŗ´ëƒ„", + "remove_from_locked_folder": "ėž ę¸ˆ í´ë”ė—ė„œ ë‚´ëŗ´ë‚´ę¸°", + "remove_from_locked_folder_confirmation": "ė´ ė‚Ŧė§„ęŗŧ ė˜ėƒė„ ėž ę¸ˆ í´ë”ė—ė„œ í•´ė œí•˜ė‹œę˛ ėŠĩ니까? ė´ė œ ëŧė´ë¸ŒëŸŦëĻŦė—ė„œ ëŗ´ė´ę˛Œ 됩니다.", "remove_from_shared_link": "ęŗĩ뜠 링íŦė—ė„œ ė œęą°", "remove_memory": "ėļ”ė–ĩ ė œęą°", "remove_photo_from_memory": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ ė œęą°", + "remove_tag": "태그 ė œęą°", "remove_url": "URL ė œęą°", "remove_user": "ė‚ŦėšŠėž ė‚­ė œ", "removed_api_key": "API 키 ė‚­ė œ: {name}", @@ -1470,7 +1644,7 @@ "removed_from_favorites": "ėĻę˛¨ė°žę¸°ė—ė„œ ė œęą°ë˜ė—ˆėŠĩ니다.", "removed_from_favorites_count": "ėĻę˛¨ė°žę¸°ė—ė„œ 항ëĒŠ {count, plural, other {#氜}} ė œęą°ë¨", "removed_memory": "ėļ”ė–ĩė´ ė œęą°ë˜ė—ˆėŠĩ니다.", - "removed_photo_from_memory": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ė„ ė œęą°í–ˆėŠĩ니다.", + "removed_photo_from_memory": "ėļ”ė–ĩė—ė„œ ė‚Ŧė§„ė´ ė œęą°ë˜ė—ˆėŠĩ니다.", "removed_tagged_assets": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė—ė„œ 태그ëĨŧ ė œęą°í•¨", "rename": "ė´ëĻ„ 바꾸기", "repair": "눘ëĻŦ", @@ -1484,37 +1658,47 @@ "reset_password": "비밀번호 ėžŦ네렕", "reset_people_visibility": "ė¸ëŦŧ í‘œė‹œ ė—Ŧëļ€ ė´ˆę¸°í™”", "reset_pin_code": "PIN ėŊ”드 ė´ˆę¸°í™”", + "reset_pin_code_description": "PIN ėŊ”드ëĨŧ ėžŠė–´ë˛„ëϰ ę˛Ŋ뚰 ė´ˆę¸°í™”í•˜ë ¤ëŠ´ ė„œë˛„ 관ëĻŦėžė—ę˛Œ ė—°ëŊí•˜ė„¸ėš”.", + "reset_pin_code_success": "PIN ėŊ”드ëĨŧ ė´ˆę¸°í™”í–ˆėŠĩ니다.", + "reset_pin_code_with_password": "íŒ¨ėŠ¤ė›Œë“œëĄœ PIN ėŊ”드ëĨŧ ėžŦė„¤ė •í•  눘 ėžˆėŠĩ니다.", + "reset_sqlite": "SQLite ë°ė´í„°ë˛ ė´ėŠ¤ ė´ˆę¸°í™”", + "reset_sqlite_confirmation": "SQLite ë°ė´í„°ë˛ ė´ėŠ¤ëĨŧ ė´ˆę¸°í™”í•˜ė‹œę˛ ėŠĩ니까? ë°ė´í„°ëĨŧ ėžŦ동기화하려면 ëĄœęˇ¸ė•„ė›ƒ 후 ë‹¤ė‹œ ëĄœęˇ¸ė¸í•´ė•ŧ 합니다.", + "reset_sqlite_success": "SQLite ë°ė´í„°ë˛ ė´ėŠ¤ëĨŧ ė´ˆę¸°í™”í–ˆėŠĩ니다.", "reset_to_default": "ę¸°ëŗ¸ę°’ėœŧ로 ëŗĩ뛐", - "resolve_duplicates": "뤑ëŗĩ된 항ëĒŠ í™•ė¸", - "resolved_all_duplicates": "뤑ëŗĩ된 항ëĒŠė„ ëĒ¨ë‘ 래ëĻŦ했ėŠĩ니다.", + "resolve_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ í™•ė¸", + "resolved_all_duplicates": "ëš„ėŠˇí•œ 항ëĒŠė„ ëĒ¨ë‘ 래ëĻŦ했ėŠĩ니다.", "restore": "ëŗĩ뛐", "restore_all": "ëĒ¨ë‘ ëŗĩ뛐", + "restore_trash_action_prompt": "íœ´ė§€í†ĩė—ė„œ {count}氜 항ëĒŠ ëŗĩė›ë¨", "restore_user": "ė‚ŦėšŠėž ëŗĩ뛐", "restored_asset": "항ëĒŠė´ ëŗĩė›ë˜ė—ˆėŠĩ니다.", "resume": "ėžŦ氜", + "resume_paused_jobs": "ėžŦ氜 {count, plural, one {# ėŧė‹œ ė¤‘ė§€ëœ ėž‘ė—…} other {# ėŧė‹œ ė¤‘ė§€ëœ ėž‘ė—…}}", "retry_upload": "ë‹¤ė‹œ ė‹œë„", - "review_duplicates": "뤑ëŗĩ된 항ëĒŠ í™•ė¸", + "review_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ í™•ė¸", + "review_large_files": "ėšŠëŸ‰ė´ 큰 파ėŧ í™•ė¸", "role": "ė—­í• ", "role_editor": "íŽ¸ė§‘ėž", "role_viewer": "ëˇ°ė–´", + "running": "ėž‘ë™ 뤑", "save": "ė €ėžĨ", "save_to_gallery": "ę°¤ëŸŦëĻŦ뗐 ė €ėžĨ", "saved_api_key": "API 키가 ėˆ˜ė •ë˜ė—ˆėŠĩ니다.", "saved_profile": "í”„ëĄœí•„ė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", "saved_settings": "ė„¤ė •ė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", "say_something": "ëŒ“ę¸€ė„ ėž…ë Ĩí•˜ė„¸ėš”", - "scaffold_body_error_occurred": "ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", + "scaffold_body_error_occurred": "똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다.", "scan_all_libraries": "ëĒ¨ë“  ëŧė´ë¸ŒëŸŦëĻŦ 늤ėē”", "scan_library": "늤ėē”", "scan_settings": "늤ėē” ė„¤ė •", "scanning_for_album": "ė•¨ë˛”ė„ 늤ėē”하는 뤑...", "search": "ę˛€ėƒ‰", "search_albums": "ė•¨ë˛” ę˛€ėƒ‰", - "search_by_context": "ë‚´ėšŠ ę˛€ėƒ‰", + "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_by_filename_example": "똈: IMG_1234.JPG 또는 PNG", "search_camera_make": "ėš´ëŠ”ëŧ ė œėĄ°ė‚Ŧ ę˛€ėƒ‰...", "search_camera_model": "ėš´ëŠ”ëŧ ëĒ¨ë¸ëĒ… ę˛€ėƒ‰...", "search_city": "ë„ė‹œ ę˛€ėƒ‰...", @@ -1535,41 +1719,42 @@ "search_for": "ę˛€ėƒ‰", "search_for_existing_person": "ėĄ´ėžŦ하는 ė¸ëŦŧ ę˛€ėƒ‰", "search_no_more_result": "ë”ė´ėƒ 결ęŗŧ ė—†ėŒ", - "search_no_people": "ė¸ëŦŧė´ ė—†ėŠĩ니다.", - "search_no_people_named": "\"{name}\" ė¸ëŦŧė„ ė°žė„ 눘 ė—†ėŒ", + "search_no_people": "ė¸ëŦŧ ė—†ėŒ", + "search_no_people_named": "\"{name}\"ė„(ëĨŧ) ė°žė„ 눘 ė—†ėŒ", "search_no_result": "ę˛€ėƒ‰ 결ęŗŧ가 ė—†ėŠĩ니다. 다ëĨ¸ ę˛€ėƒ‰ė–´ë‚˜ ėĄ°í•Šėœŧ로 ë‹¤ė‹œ ė‹œë„í•´ ëŗ´ė„¸ėš”.", "search_options": "ę˛€ėƒ‰ ė˜ĩė…˜", "search_page_categories": "ëļ„ëĨ˜", "search_page_motion_photos": "ëĒ¨ė…˜ íŦ토", - "search_page_no_objects": "ė‚ŦėšŠ 가ëŠĨ한 ė‚ŦëŦŧ ė •ëŗ´ ė—†ėŒ", - "search_page_no_places": "ė‚ŦėšŠ 가ëŠĨ한 ėœ„ėš˜ ė •ëŗ´ ė—†ėŒ", + "search_page_no_objects": "í‘œė‹œí•  ė‚ŦëŦŧ ė •ëŗ´ ė—†ėŒ", + "search_page_no_places": "í‘œė‹œí•  ėžĨė†Œ ė •ëŗ´ ė—†ėŒ", "search_page_screenshots": "늤íŦëϰ냎", "search_page_search_photos_videos": "ė‚Ŧė§„ 및 ë™ė˜ėƒ ę˛€ėƒ‰", "search_page_selfies": "ė…€í”ŧ", "search_page_things": "ė‚ŦëŦŧ", "search_page_view_all_button": "ëĒ¨ë‘ ëŗ´ę¸°", "search_page_your_activity": "활동", - "search_page_your_map": "내 ė§€ë„", + "search_page_your_map": "ë‚˜ė˜ ė§€ë„", "search_people": "ė¸ëŦŧ ę˛€ėƒ‰", "search_places": "ėžĨė†Œ ę˛€ėƒ‰", "search_rating": "등급ėœŧ로 ę˛€ėƒ‰...", "search_result_page_new_search_hint": "냈 ę˛€ėƒ‰", "search_settings": "네렕 ę˛€ėƒ‰", "search_state": "맀뗭 ę˛€ėƒ‰...", - "search_suggestion_list_smart_search_hint_1": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ ę¸°ëŗ¸ė ėœŧ로 í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ëŠ”íƒ€ë°ė´í„°ëĄœ ę˛€ėƒ‰í•˜ë ¤ëŠ´ ë‹¤ėŒė„ ė‚ŦėšŠí•˜ė„¸ėš”. ", + "search_suggestion_list_smart_search_hint_1": "ėŠ¤ë§ˆíŠ¸ ę˛€ėƒ‰ė´ ę¸°ëŗ¸ í™œė„ąí™”ë˜ė–´ ėžˆėŠĩ니다. ëŠ”íƒ€ë°ė´í„° ę˛€ėƒ‰ė€ ë‹¤ėŒęŗŧ ę°™ė´ ėž…ë Ĩí•˜ė„¸ėš”: ", "search_suggestion_list_smart_search_hint_2": "m:ę˛€ėƒ‰ė–´", - "search_tags": "태그로 ę˛€ėƒ‰...", + "search_tags": "태그 ę˛€ėƒ‰...", "search_timezone": "ė‹œę°„ëŒ€ ę˛€ėƒ‰...", - "search_type": "ę˛€ėƒ‰ ėĸ…ëĨ˜", + "search_type": "ę˛€ėƒ‰ ėœ í˜•", "search_your_photos": "ė‚Ŧė§„ ę˛€ėƒ‰", "searching_locales": "로ėŧ€ėŧ ę˛€ėƒ‰...", "second": "봈", "see_all_people": "ëĒ¨ë“  ė¸ëŦŧ ëŗ´ę¸°", "select": "ė„ íƒ", - "select_album_cover": "ė•¨ë˛” ėģ¤ë˛„ ëŗ€ę˛Ŋ", + "select_album_cover": "ė•¨ë˛” ėģ¤ë˛„ ė„ íƒ", "select_all": "ëĒ¨ë‘ ė„ íƒ", - "select_all_duplicates": "ëĒ¨ë‘ ė„ íƒ", - "select_avatar_color": "프로필 ėƒ‰ėƒ ëŗ€ę˛Ŋ", + "select_all_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ ëĒ¨ë‘ ė„ íƒ", + "select_all_in": "{group}ė˜ ëĒ¨ë“  항ëĒŠ ė„ íƒ", + "select_avatar_color": "ė•„ë°”íƒ€ ėƒ‰ėƒ ė„ íƒ", "select_face": "ė–ŧęĩ´ ė„ íƒ", "select_featured_photo": "대표 ė‚Ŧė§„ ė„ íƒ", "select_from_computer": "ėģ´í“¨í„°ė—ė„œ ė„ íƒ", @@ -1581,7 +1766,8 @@ "select_trash_all": "ëĒ¨ë‘ ė‚­ė œ", "select_user_for_sharing_page_err_album": "ė•¨ë˛”ė„ ėƒė„ąí•˜ė§€ ëĒģ했ėŠĩ니다.", "selected": "ė„ íƒë¨", - "selected_count": "{count, plural, other {#氜}} ė„ íƒë¨", + "selected_count": "{count, plural, other {#氜 ė„ íƒë¨}}", + "selected_gps_coordinates": "ė„ íƒí•œ GPS ėĸŒí‘œ", "send_message": "ëŠ”ė‹œė§€ ė „ė†Ą", "send_welcome_email": "í™˜ė˜ ė´ëŠ”ėŧ ė „ė†Ą", "server_endpoint": "ė„œë˛„ ė—”ë“œíŦė¸íŠ¸", @@ -1589,6 +1775,7 @@ "server_info_box_server_url": "ė„œë˛„ URL", "server_offline": "ė˜¤í”„ëŧė¸", "server_online": "똍ëŧė¸", + "server_privacy": "ę°œė¸ė •ëŗ´", "server_stats": "ė„œë˛„ í†ĩęŗ„", "server_version": "ė„œë˛„ ë˛„ė „", "set": "네렕", @@ -1598,15 +1785,16 @@ "set_date_of_birth": "ėƒë…„ė›”ėŧ 네렕", "set_profile_picture": "프로필 ė‚Ŧė§„ėœŧ로 네렕", "set_slideshow_to_fullscreen": "ėŠŦëŧė´ë“œ ė‡ŧëĨŧ 렄랴 화면ėœŧ로 네렕", - "setting_image_viewer_help": "ėƒė„¸ ëŗ´ę¸°ëŠ” ë¨ŧė € ėž‘ė€ íŦę¸°ė˜ ė„Ŧ네ėŧė„ ëļˆëŸŦė˜¤ëŠ°, í™œė„ąí™”ëœ ę˛Ŋ뚰 뤑氄 íŦę¸°ė˜ ė´ë¯¸ė§€ė™€ ė›ëŗ¸ė„ ëļˆëŸŦė˜ĩ니다.", - "setting_image_viewer_original_subtitle": "ęŗ í•´ėƒë„ ė›ëŗ¸ ė´ë¯¸ė§€ëĨŧ ëļˆëŸŦė˜ĩ니다. ë°ė´í„° ė‚ŦėšŠëŸ‰ė„ ė¤„ė´ë ¤ëŠ´ ëš„í™œė„ąí™”í•˜ė„¸ėš”.", - "setting_image_viewer_original_title": "ė›ëŗ¸ ė´ë¯¸ė§€ í‘œė‹œ", - "setting_image_viewer_preview_subtitle": "뤑氄 íŦę¸°ė˜ ė´ë¯¸ė§€ëĨŧ ëļˆëŸŦė˜¤ë ¤ëŠ´ í™œė„ąí™”í•˜ė„¸ėš”. í•­ėƒ ė›ëŗ¸ė„ ëļˆëŸŦė˜¤ęą°ë‚˜ ė„Ŧ네ėŧ만 ëļˆëŸŦė˜¤ë ¤ëŠ´ ëš„í™œė„ąí™”í•˜ė„¸ėš”.", - "setting_image_viewer_preview_title": "미ëĻŦ ëŗ´ę¸° ė´ë¯¸ė§€ ëļˆëŸŦ똤揰", + "set_stack_primary_asset": "대표 항ëĒŠėœŧ로 네렕", + "setting_image_viewer_help": "ėƒė„¸ ëŗ´ę¸°ė—ė„œëŠ” ėž‘ė€ ė„Ŧ네ėŧ, (í™œė„ąí™”ëœ ę˛Ŋ뚰) 뤑氄 ė„Ŧ네ėŧ, ė›ëŗ¸ 눜ėœŧ로 ëļˆëŸŦė˜ĩ니다.", + "setting_image_viewer_original_subtitle": "ė›ëŗ¸ ęŗ í•´ėƒë„ ė´ë¯¸ė§€ëĨŧ ëļˆëŸŦė˜ĩ니다. ë°ė´í„° ė‚ŦėšŠëŸ‰ 및 ėēė‹œ íŦ기ëĨŧ ė¤„ė´ë ¤ëŠ´ ëš„í™œė„ąí™”í•˜ė„¸ėš”.", + "setting_image_viewer_original_title": "ė›ëŗ¸ ė´ë¯¸ė§€ 로드", + "setting_image_viewer_preview_subtitle": "ė›ëŗ¸ ęŗ í•´ėƒë„ ė´ë¯¸ė§€ëĨŧ ëļˆëŸŦė˜ĩ니다. ëš„í™œė„ąí™”í•˜ëŠ” ę˛Ŋ뚰 ė›ëŗ¸ 또는 ė„Ŧ네ėŧ만 ëļˆëŸŦė˜ĩ니다.", + "setting_image_viewer_preview_title": "미ëĻŦëŗ´ę¸° ė´ë¯¸ė§€ 로드", "setting_image_viewer_title": "ė´ë¯¸ė§€", "setting_languages_apply": "ė ėšŠ", "setting_languages_subtitle": "ė•ą 떏떴 ëŗ€ę˛Ŋ", - "setting_notifications_notify_failures_grace_period": "밹꡸ëŧėš´ë“œ ë°ąė—… ė‹¤íŒ¨ ė•ŒëĻŧ: {duration}", + "setting_notifications_notify_failures_grace_period": "밹꡸ëŧėš´ë“œ ë°ąė—… ė‹¤íŒ¨ ė•ŒëĻŧ: {duration} 후", "setting_notifications_notify_hours": "{count}ė‹œę°„", "setting_notifications_notify_immediately": "ėĻ‰ė‹œ", "setting_notifications_notify_minutes": "{count}ëļ„", @@ -1615,24 +1803,25 @@ "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": "ė„œë˛„ė—ė„œ ë™ė˜ėƒė„ ėŠ¤íŠ¸ëĻŦ밍할 때, íŠ¸ëžœėŠ¤ėŊ”딊된 ë˛„ė „ė´ ėžˆë”ëŧ도 ė›ëŗ¸ė„ ėžŦėƒí•Šë‹ˆë‹¤. ė´ëĄœ ė¸í•´ 버íŧë§ė´ ë°œėƒí•  눘 ėžˆėŠĩ니다. 揰揰뗐 ėžˆëŠ” ë™ė˜ėƒė€ ė´ 네렕ęŗŧ ę´€ęŗ„ė—†ė´ í•­ėƒ ė›ëŗ¸ í™”ė§ˆëĄœ ėžŦėƒëŠë‹ˆë‹¤.", + "setting_video_viewer_original_video_subtitle": "ë™ė˜ėƒ ėŠ¤íŠ¸ëĻŦ밍 ė‹œ íŠ¸ëžœėŠ¤ėŊ”딊된 파ėŧ ëŒ€ė‹  ė›ëŗ¸ė„ ėžŦėƒí•Šë‹ˆë‹¤. ėžŦėƒ ė‹œ 버íŧë§ė´ ë°œėƒí•  눘 ėžˆėŠĩ니다. 로ėģŦ뗐 ėžˆëŠ” ė˜ėƒė€ í•­ėƒ ė›ëŗ¸ í™”ė§ˆëĄœ ėžŦėƒëŠë‹ˆë‹¤.", "setting_video_viewer_original_video_title": "ė›ëŗ¸ ë™ė˜ėƒ ę°•ė œ ė‚ŦėšŠ", "settings": "네렕", - "settings_require_restart": "ė„¤ė •ė„ ė ėšŠí•˜ë ¤ëŠ´ ImmichëĨŧ ë‹¤ė‹œ ė‹œėž‘í•˜ė„¸ėš”.", + "settings_require_restart": "ė„¤ė •ė„ ė ėšŠí•˜ë ¤ëŠ´ ImmichëĨŧ ë‹¤ė‹œ ė‹œėž‘í•´ė•ŧ 합니다.", "settings_saved": "ė„¤ė •ė´ ė €ėžĨë˜ė—ˆėŠĩ니다.", "setup_pin_code": "PIN ėŊ”드 네렕", "share": "ęŗĩ뜠", + "share_action_prompt": "항ëĒŠ {count}氜 ęŗĩėœ ë¨", "share_add_photos": "ė‚Ŧė§„ ėļ”ę°€", "share_assets_selected": "{count}氜 ė„ íƒë¨", "share_dialog_preparing": "ė¤€ëš„ 뤑...", "share_link": "ęŗĩ뜠 링íŦ", "shared": "ęŗĩėœ ë¨", "shared_album_activities_input_disable": "ëŒ“ę¸€ė´ ëš„í™œė„ąí™”ë˜ė—ˆėŠĩ니다", - "shared_album_activity_remove_content": "ė´ ë°˜ė‘ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", - "shared_album_activity_remove_title": "ë°˜ė‘ ė‚­ė œ", + "shared_album_activity_remove_content": "ė´ í™œë™ė„ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", + "shared_album_activity_remove_title": "활동 ė‚­ė œ", "shared_album_section_people_action_error": "ė•¨ë˛”ė—ė„œ 나가기/ė œęą° 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", "shared_album_section_people_action_leave": "ė•¨ë˛”ė—ė„œ ė‚ŦėšŠėž ė œęą°", "shared_album_section_people_action_remove_user": "ė•¨ë˛”ė—ė„œ ė‚ŦėšŠėž ė œęą°", @@ -1641,11 +1830,12 @@ "shared_by_user": "{user}ë‹˜ė´ ęŗĩėœ í•¨", "shared_by_you": "내가 ęŗĩėœ í•¨", "shared_from_partner": "{partner}ë‹˜ė˜ ė‚Ŧė§„", - "shared_intent_upload_button_progress_text": "렄랴 {total}氜 뤑 {current}氜 ė—…ëĄœë“œë¨", + "shared_intent_upload_button_progress_text": "{total}氜 뤑 {current}氜 ė—…ëĄœë“œë¨", "shared_link_app_bar_title": "ęŗĩ뜠 링íŦ", "shared_link_clipboard_copied_massage": "클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧë˜ė—ˆėŠĩ니다.", "shared_link_clipboard_text": "링íŦ: {link}\n비밀번호: {password}", - "shared_link_create_error": "ęŗĩ뜠 링íŦ ėƒė„ą 뤑 ëŦ¸ė œę°€ ë°œėƒí–ˆėŠĩ니다.", + "shared_link_create_error": "ęŗĩ뜠 링íŦ ėƒė„ą 뤑 똤ëĨ˜ ë°œėƒ", + "shared_link_custom_url_description": "ė‚ŦėšŠėž 맀렕 URLė„ ė‚ŦėšŠí•˜ė—Ŧ ęŗĩ뜠 링íŦ ė ‘ęˇŧ", "shared_link_edit_description_hint": "ęŗĩ뜠 링íŦ 네ëĒ… ėž…ë Ĩ", "shared_link_edit_expire_after_option_day": "1ėŧ", "shared_link_edit_expire_after_option_days": "{count}ėŧ", @@ -1657,7 +1847,7 @@ "shared_link_edit_expire_after_option_year": "{count}년", "shared_link_edit_password_hint": "ęŗĩ뜠 비밀번호 ėž…ë Ĩ", "shared_link_edit_submit_button": "링íŦ íŽ¸ė§‘", - "shared_link_error_server_url_fetch": "ė„œë˛„ URLė„ ëļˆëŸŦė˜Ŧ 눘 ė—†ėŠĩ니다.", + "shared_link_error_server_url_fetch": "ė„œë˛„ URLė„ 氀렏ė˜Ŧ 눘 ė—†ėŠĩ니다", "shared_link_expires_day": "{count}ėŧ 후 ë§ŒëŖŒ", "shared_link_expires_days": "{count}ėŧ 후 ë§ŒëŖŒ", "shared_link_expires_hour": "{count}ė‹œę°„ 후 ë§ŒëŖŒ", @@ -1669,8 +1859,9 @@ "shared_link_expires_seconds": "{count}봈 후 ë§ŒëŖŒ", "shared_link_individual_shared": "ę°œė¸ ęŗĩ뜠", "shared_link_info_chip_metadata": "EXIF", - "shared_link_manage_links": "ęŗĩ뜠 링íŦ 관ëĻŦ", + "shared_link_manage_links": "ęŗĩ뜠 링íŦëĨŧ 관ëĻŦ합니다.", "shared_link_options": "ęŗĩ뜠 링íŦ ė˜ĩė…˜", + "shared_link_password_description": "ė´ íŽ˜ė´ė§€ëĨŧ ëŗ´ë ¤ëŠ´ 비밀번호ëĨŧ ėž…ë Ĩí•˜ė„¸ėš”.", "shared_links": "ęŗĩ뜠 링íŦ", "shared_links_description": "링íŦëĨŧ í†ĩ해 ė‚Ŧė§„ 및 ë™ė˜ėƒ ęŗĩ뜠", "shared_photos_and_videos_count": "ė‚Ŧė§„ 및 ë™ė˜ėƒ {assetCount, plural, other {#氜ëĨŧ ęŗĩėœ í–ˆėŠĩ니다.}}", @@ -1679,12 +1870,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_create_shared_album": "냈 ęŗĩ뜠 ė•¨ë˛”", "sharing_silver_appbar_share_partner": "íŒŒíŠ¸ë„ˆė™€ ęŗĩ뜠", - "shift_to_permanent_delete": "⇧ëĨŧ 눌ëŸŦ 항ëĒŠė„ 똁ęĩŦ렁ėœŧ로 ė‚­ė œ", + "shift_to_permanent_delete": "⇧ëĨŧ 눌ëŸŦ 항ëĒŠ 똁ęĩŦ ė‚­ė œ", "show_album_options": "ė•¨ë˛” ė˜ĩė…˜ í‘œė‹œ", "show_albums": "ė•¨ë˛” í‘œė‹œ", "show_all_people": "ëĒ¨ë“  ė¸ëŦŧ ëŗ´ę¸°", @@ -1696,7 +1887,7 @@ "show_in_timeline_setting_description": "íƒ€ėž„ëŧė¸ė— ė´ ė‚ŦėšŠėžė˜ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė„ í‘œė‹œ", "show_keyboard_shortcuts": "í‚¤ëŗ´ë“œ 단ėļ•키 í‘œė‹œ", "show_metadata": "ëŠ”íƒ€ë°ė´í„° í‘œė‹œ", - "show_or_hide_info": "ė •ëŗ´ í‘œė‹œ/눍揰揰", + "show_or_hide_info": "ė •ëŗ´ í‘œė‹œ 또는 눍揰揰", "show_password": "비밀번호 í‘œė‹œ", "show_person_options": "ė¸ëŦŧ ė˜ĩė…˜ í‘œė‹œ", "show_progress_bar": "ė§„í–‰ í‘œė‹œė¤„ í‘œė‹œ", @@ -1705,6 +1896,7 @@ "show_slideshow_transition": "ėŠŦëŧė´ë“œ ė „í™˜ í‘œė‹œ", "show_supporter_badge": "ė„œíŦ터 ë°°ė§€", "show_supporter_badge_description": "ė„œíŦ터 ë°°ė§€ í‘œė‹œ", + "show_text_search_menu": "í…ėŠ¤íŠ¸ ę˛€ėƒ‰ 메뉴 í‘œė‹œ", "shuffle": "ė…”í”Œ", "sidebar": "ė‚Ŧė´ë“œë°”", "sidebar_display_description": "ëŗ´ę¸° 링íŦëĨŧ ė‚Ŧė´ë“œë°”ė— í‘œė‹œ", @@ -1720,21 +1912,25 @@ "sort_created": "ėƒė„ąëœ ë‚ ė§œ", "sort_items": "항ëĒŠ 눘", "sort_modified": "ėˆ˜ė •ëœ ë‚ ė§œ", + "sort_newest": "ėĩœęˇŧ ė‚Ŧė§„", "sort_oldest": "ė˜¤ëž˜ëœ ė‚Ŧė§„", "sort_people_by_similarity": "뜠ė‚Ŧė„ąė„ 揰뤀ėœŧ로 ė¸ëŦŧ ė •ë Ŧ", "sort_recent": "ėĩœęˇŧ ė‚Ŧė§„", "sort_title": "렜ëĒŠ", "source": "ė†ŒėŠ¤", "stack": "ėŠ¤íƒ", - "stack_duplicates": "뤑ëŗĩ된 항ëĒŠ ėŠ¤íƒ", + "stack_action_prompt": "항ëĒŠ {count}氜 ėŠ¤íƒë¨", + "stack_duplicates": "ëš„ėŠˇí•œ 항ëĒŠ ėŠ¤íƒ", "stack_select_one_photo": "ėŠ¤íƒė˜ 대표 ė‚Ŧė§„ ė„ íƒ", "stack_selected_photos": "ė„ íƒí•œ ė´ë¯¸ė§€ ėŠ¤íƒ", "stacked_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}} ėŠ¤íƒë¨", "stacktrace": "ėŠ¤íƒ ėļ”렁", "start": "ė‹œėž‘", "start_date": "ė‹œėž‘ėŧ", + "start_date_before_end_date": "ė‹œėž‘ėŧė€ ėĸ…ëŖŒėŧëŗ´ë‹¤ ė´ė „ė´ė–´ė•ŧ 합니다", "state": "맀뗭", "status": "ėƒíƒœ", + "stop_casting": "ėēėŠ¤íŒ… ė¤‘ë‹¨", "stop_motion_photo": "ëĒ¨ė…˜ íŦ토 ė •ė§€", "stop_photo_sharing": "ęŗĩ뜠ëĨŧ ė¤‘ë‹¨í•˜ė‹œę˛ ėŠĩ니까?", "stop_photo_sharing_description": "더 ė´ėƒ {partner}ë‹˜ė´ ė‚Ŧ맄뗐 ė ‘ęˇŧ할 눘 ė—†ėŠĩ니다.", @@ -1744,8 +1940,9 @@ "storage_quota": "ėŠ¤í† ëĻŦė§€ 할당량", "storage_usage": "{available} 뤑 {used} ė‚ŦėšŠ", "submit": "í™•ė¸", + "success": "ė„ąęŗĩ", "suggestions": "ėļ”ė˛œ", - "sunrise_on_the_beach": "ė´ë¯¸ė§€ė— ėĄ´ėžŦ하는 ė‚ŦëŦŧ ę˛€ėƒ‰", + "sunrise_on_the_beach": "눈 내ëĻŦ는 ęą°ëĻŦ, ėą… ėŊ는 ė‚Ŧ람", "support": "맀뛐", "support_and_feedback": "맀뛐 & ė œė•ˆ", "support_third_party_description": "ė„œë“œíŒŒí‹° íŒ¨í‚¤ė§€ëĨŧ ė´ėšŠí•˜ė—Ŧ Immich가 ė„¤ėš˜ëœ 것ėœŧ로 ëŗ´ėž…ë‹ˆë‹¤. 현ėžŦ ë°œėƒí•˜ëŠ” ëŦ¸ė œëŠ” 해당 íŒ¨í‚¤ė§€ę°€ ė›ė¸ėŧ 눘 ėžˆėœŧë¯€ëĄœ, ë¨ŧė € ė•„ëž˜ 링íŦëĨŧ í†ĩ해 íŒ¨í‚¤ė§€ ę°œë°œėžė—ę˛Œ ëŦ¸ė˜í•´ėŖŧė„¸ėš”.", @@ -1753,6 +1950,10 @@ "sync": "동기화", "sync_albums": "ė•¨ë˛” 동기화", "sync_albums_manual_subtitle": "ė—…ëĄœë“œí•œ ëĒ¨ë“  ë™ė˜ėƒęŗŧ ė‚Ŧė§„ė„ ė„ íƒí•œ ë°ąė—… ė•¨ë˛”ė— 동기화", + "sync_local": "로ėģŦ 동기화", + "sync_remote": "ė›ę˛Š 동기화", + "sync_status": "동기화 ėƒíƒœ", + "sync_status_subtitle": "동기화 ė‹œėŠ¤í…œ í™•ė¸ 및 관ëĻŦ", "sync_upload_album_setting_subtitle": "ė„ íƒí•œ ė•¨ë˛”ė„ Immich뗐 ėƒė„ąí•˜ęŗ  ė‚Ŧė§„ 및 ë™ė˜ėƒ ė—…ëĄœë“œ", "tag": "태그", "tag_assets": "항ëĒŠ 태그", @@ -1763,24 +1964,25 @@ "tag_updated": "태그 ė—…ë°ė´íŠ¸ë¨: {tag}", "tagged_assets": "항ëĒŠ {count, plural, one {#氜} other {#氜}}뗐 태그ëĨŧ ė ėšŠí•¨", "tags": "태그", + "tap_to_run_job": "탭하ė—Ŧ ėž‘ė—… ė‹¤í–‰", "template": "템플ëĻŋ", "theme": "테마", - "theme_selection": "테마 네렕", - "theme_selection_description": "브ëŧėš°ė € 및 ė‹œėŠ¤í…œ ę¸°ëŗ¸ 네렕뗐 따ëŧ ëŧė´íŠ¸ ëĒ¨ë“œė™€ 다íŦ ëĒ¨ë“œëĨŧ ėžë™ėœŧ로 네렕", + "theme_selection": "테마 ė„ íƒ", + "theme_selection_description": "ė‹œėŠ¤í…œė˜ 다íŦ ëĒ¨ë“œ 네렕뗐 따ëŧ 테마ëĨŧ ėžë™ėœŧ로 ė ėšŠí•Šë‹ˆë‹¤.", "theme_setting_asset_list_storage_indicator_title": "타ėŧ뗐 ė„œë˛„ 동기화 ėƒíƒœ í‘œė‹œ", "theme_setting_asset_list_tiles_per_row_title": "한 뤄뗐 í‘œė‹œí•  항ëĒŠ 눘 ({count})", - "theme_setting_colorful_interface_subtitle": "ë°°ę˛Ŋ뗐 대표 ėƒ‰ėƒė„ ė ėšŠí•Šë‹ˆë‹¤.", + "theme_setting_colorful_interface_subtitle": "ę¸°ëŗ¸ ėƒ‰ėƒė„ ë°°ę˛Ŋ뗐 ė ėšŠ", "theme_setting_colorful_interface_title": "미려한 ė¸í„°íŽ˜ė´ėŠ¤", "theme_setting_image_viewer_quality_subtitle": "ėƒė„¸ ëŗ´ę¸° ė´ë¯¸ė§€ í’ˆė§ˆ ėĄ°ė •", "theme_setting_image_viewer_quality_title": "ė´ë¯¸ė§€ ëŗ´ę¸° í’ˆė§ˆ", - "theme_setting_primary_color_subtitle": "ėŖŧėš” 기ëŠĨęŗŧ ę°•ėĄ° ėƒ‰ėƒė— ė ėšŠí•  테마 ėƒ‰ėƒė„ ė„ íƒí•˜ė„¸ėš”.", - "theme_setting_primary_color_title": "대표 ėƒ‰ėƒ", + "theme_setting_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": "3ë‹¨ęŗ„ ëĄœë”Šė€ 로드 ė„ąëŠĨė„ í–Ĩėƒė‹œí‚Ŧ 눘 ėžˆėœŧ나, ë„¤íŠ¸ė›ŒíŦ ëļ€í•˜ę°€ íŦ枌 ėĻę°€í•  눘 ėžˆėŠĩ니다.", + "theme_setting_three_stage_loading_subtitle": "3ë‹¨ęŗ„ ëĄœë”Šė„ ė‚ŦėšŠí•˜ëŠ´ 로드 ė„ąëŠĨė„ í–Ĩėƒė‹œí‚Ŧ 눘 ėžˆėœŧ나, ë„¤íŠ¸ė›ŒíŦ ëļ€í•˜ę°€ íŦ枌 ėĻę°€í•Šë‹ˆë‹¤.", "theme_setting_three_stage_loading_title": "3ë‹¨ęŗ„ 로드 í™œė„ąí™”", - "they_will_be_merged_together": "ė„ íƒí•œ ė¸ëŦŧë“¤ė´ ëŗ‘í•ŠëŠë‹ˆë‹¤.", + "they_will_be_merged_together": "ė„ íƒí•œ ė¸ëŦŧë“¤ė„ 한 ė¸ëŦŧ로 í•ŠėšŠë‹ˆë‹¤.", "third_party_resources": "ė„œë“œ 파티 ëĻŦė†ŒėŠ¤", "time_based_memories": "ė‹œę°„ 揰뤀 ėļ”ė–ĩ", "timeline": "íƒ€ėž„ëŧė¸", @@ -1789,31 +1991,38 @@ "to_change_password": "비밀번호 ëŗ€ę˛Ŋ", "to_favorite": "ėĻę˛¨ė°žę¸°", "to_login": "ëĄœęˇ¸ė¸", + "to_multi_select": "ë‹¤ė¤‘ ė„ íƒ", "to_parent": "ėƒėœ„ 항ëĒŠėœŧ로", + "to_select": "ė„ íƒ", "to_trash": "ė‚­ė œ", "toggle_settings": "네렕 ëŗ€ę˛Ŋ", "total": "렄랴", "total_usage": "ė´ ė‚ŦėšŠëŸ‰", "trash": "íœ´ė§€í†ĩ", + "trash_action_prompt": "íœ´ė§€í†ĩėœŧ로 항ëĒŠ {count}氜 ė´ë™ë¨", "trash_all": "ëĒ¨ë‘ ė‚­ė œ", "trash_count": "{count, number}氜 ė‚­ė œ", "trash_delete_asset": "íœ´ė§€í†ĩ ė´ë™/ė‚­ė œ", "trash_emptied": "íœ´ė§€í†ĩė„ ëš„ė› ėŠĩ니다.", "trash_no_results_message": "ė‚­ė œëœ ė‚Ŧė§„ęŗŧ ë™ė˜ėƒė´ ė—Ŧ揰뗐 í‘œė‹œëŠë‹ˆë‹¤.", "trash_page_delete_all": "ëĒ¨ë‘ ė‚­ė œ", - "trash_page_empty_trash_dialog_content": "íœ´ė§€í†ĩė„ ëš„ėš°ė‹œę˛ ėŠĩ니까? íœ´ė§€í†ĩ뗐 ėžˆëŠ” ëĒ¨ë“  항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė œęą°ëŠë‹ˆë‹¤.", + "trash_page_empty_trash_dialog_content": "íœ´ė§€í†ĩė„ ëš„ėš°ė‹œę˛ ėŠĩ니까? íœ´ė§€í†ĩė˜ ëĒ¨ë“  항ëĒŠė´ Immichė—ė„œ 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "trash_page_info": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė€ {days}ėŧ 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", "trash_page_no_assets": "íœ´ė§€í†ĩė´ ëš„ė–´ ėžˆėŒ", "trash_page_restore_all": "ëĒ¨ë‘ ëŗĩ뛐", "trash_page_select_assets_btn": "항ëĒŠ ė„ íƒ", "trash_page_title": "íœ´ė§€í†ĩ ({count})", "trashed_items_will_be_permanently_deleted_after": "íœ´ė§€í†ĩėœŧ로 ė´ë™ëœ 항ëĒŠė€ {days, plural, one {#ėŧ} other {#ėŧ}} 후 똁ęĩŦ렁ėœŧ로 ė‚­ė œëŠë‹ˆë‹¤.", + "troubleshoot": "트ëŸŦë¸”ėŠˆíŒ…", "type": "í˜•ė‹", "unable_to_change_pin_code": "PIN ėŊ”드ëĨŧ ëŗ€ę˛Ŋ할 눘 ė—†ėŒ", "unable_to_setup_pin_code": "PIN ėŊ”드ëĨŧ ė„¤ė •í•  눘 ė—†ėŒ", "unarchive": "ëŗ´ę´€í•¨ė—ė„œ ė œęą°", + "unarchive_action_prompt": "ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠ {count}氜 ė œęą°ë¨", "unarchived_count": "ëŗ´ę´€í•¨ė—ė„œ 항ëĒŠ {count, plural, other {#氜}} ė œęą°ë¨", + "undo": "ė‹¤í–‰ ėˇ¨ė†Œ", "unfavorite": "ėĻę˛¨ė°žę¸° í•´ė œ", + "unfavorite_action_prompt": "ėĻę˛¨ė°žę¸°ė—ė„œ 항ëĒŠ {count}氜 ė œęą°ë¨", "unhide_person": "ė¸ëŦŧ ėˆ¨ęš€ í•´ė œ", "unknown": "ė•Œ 눘 ė—†ėŒ", "unknown_country": "ė•Œ 눘 ė—†ëŠ” 맀뗭", @@ -1829,24 +2038,32 @@ "unsaved_change": "ė €ėžĨë˜ė§€ ė•Šė€ ëŗ€ę˛Ŋ ė‚Ŧ항", "unselect_all": "ëĒ¨ë‘ ė„ íƒ í•´ė œ", "unselect_all_duplicates": "ëĒ¨ë‘ ė„ íƒ í•´ė œ", - "unstack": "ėŠ¤íƒ í•´ė œ", - "unstacked_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė˜ ėŠ¤íƒė„ í•´ė œí–ˆėŠĩ니다.", + "unselect_all_in": "ëĒ¨ë“  {group} ė„ íƒ í•´ė œ", + "unstack": "ėŠ¤íƒ 풀기", + "unstack_action_prompt": "항ëĒŠ {count}氜 ėŠ¤íƒ 풀ëĻŧ", + "unstacked_assets_count": "항ëĒŠ {count, plural, one {#氜} other {#氜}}ė˜ ėŠ¤íƒė„ í’€ė—ˆėŠĩ니다.", + "untagged": "태그 í•´ė œë¨", "up_next": "ë‹¤ėŒ", + "update_location_action_prompt": "ė„ íƒí•œ {count}氜 항ëĒŠ ėœ„ėš˜ ė—…ë°ė´íŠ¸:", "updated_at": "ė—…ë°ė´íŠ¸ë¨", "updated_password": "비밀번호가 ëŗ€ę˛Ŋë˜ė—ˆėŠĩ니다.", "upload": "ė—…ëĄœë“œ", + "upload_action_prompt": "{count}氜 항ëĒŠ ė—…ëĄœë“œ 대기 뤑", "upload_concurrency": "ė—…ëĄœë“œ ë™ė‹œė„ą", + "upload_details": "ė—…ëĄœë“œ ėƒė„¸", "upload_dialog_info": "ė„ íƒí•œ 항ëĒŠė„ ė„œë˛„ė— ë°ąė—…í•˜ė‹œę˛ ėŠĩ니까?", "upload_dialog_title": "항ëĒŠ ė—…ëĄœë“œ", "upload_errors": "ė—…ëĄœë“œę°€ ė™„ëŖŒë˜ė—ˆėŠĩ니다. 항ëĒŠ {count, plural, one {#氜} other {#氜}}ëĨŧ ė—…ëĄœë“œí•˜ė§€ ëĒģ했ėŠĩ니다. ė—…ëĄœë“œëœ 항ëĒŠė„ ëŗ´ë ¤ëŠ´ íŽ˜ė´ė§€ëĨŧ ėƒˆëĄœęŗ ėš¨í•˜ė„¸ėš”.", + "upload_finished": "ė—…ëĄœë“œ ė™„ëŖŒ", "upload_progress": "렄랴 {total, number}氜 뤑 {processed, number}氜 ė™„ëŖŒ, {remaining, 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_to_immich": "Immich뗐 ė—…ëĄœë“œ ({count})", "uploading": "ė—…ëĄœë“œ 뤑", + "uploading_media": "ë¯¸ë””ė–´ ė—…ëĄœë“œ 뤑...", "url": "URL", "usage": "ė‚ŦėšŠëŸ‰", "use_biometric": "ėƒė˛´ ė¸ėĻ ė‚ŦėšŠ", @@ -1855,48 +2072,53 @@ "user": "ė‚ŦėšŠėž", "user_has_been_deleted": "ė´ ė‚ŦėšŠėžëŠ” ė‚­ė œë˜ė—ˆėŠĩ니다.", "user_id": "ė‚ŦėšŠėž ID", - "user_liked": "{user}ë‹˜ė´ {type, select, photo {ė´ ė‚Ŧė§„ė„} video {ė´ ë™ė˜ėƒė„} asset {ė´ 항ëĒŠė„} other {ė´ 항ëĒŠė„}} ėĸ‹ė•„핊니다.", + "user_liked": "{user}ë‹˜ė´ {type, select, photo {ė´ ė‚Ŧė§„} video {ė´ ë™ė˜ėƒ} asset {ė´ 항ëĒŠ} other {해당 항ëĒŠ}}ė„ ėĸ‹ė•„핊니다.", "user_pin_code_settings": "PIN ėŊ”드", - "user_pin_code_settings_description": "PIN ėŊ”드 관ëĻŦ", + "user_pin_code_settings_description": "PIN ėŊ”드ëĨŧ ëŗ€ę˛Ŋ하거나 ėžŠė–´ë˛„ëϰ ę˛Ŋ뚰 ė´ˆę¸°í™”í•Šë‹ˆë‹¤.", + "user_privacy": "ę°œė¸ė •ëŗ´", "user_purchase_settings": "ęĩŦ매", - "user_purchase_settings_description": "ęĩŦ매 및 ė œí’ˆ 키 관ëĻŦ", - "user_role_set": "{user}ë‹˜ė—ę˛Œ {role} ė—­í• ė„ ė„¤ė •í–ˆėŠĩ니다.", + "user_purchase_settings_description": "ęĩŦ매 ė„¤ė •ė„ 관ëĻŦí•˜ęŗ  키ëĨŧ 등록 및 ė œęą°í•Šë‹ˆë‹¤.", + "user_role_set": "{user}ė—ę˛Œ {role} ė—­í•  맀렕", "user_usage_detail": "ė‚ŦėšŠėž ė‚ŦėšŠëŸ‰ ėƒė„¸", - "user_usage_stats": "ęŗ„ė • ė‚ŦėšŠëŸ‰ í†ĩęŗ„", - "user_usage_stats_description": "ęŗ„ė • ė‚ŦėšŠëŸ‰ í†ĩęŗ„ ëŗ´ę¸°", + "user_usage_stats": "ė‚ŦėšŠëŸ‰ í†ĩęŗ„", + "user_usage_stats_description": "ęŗ„ė •ė˜ 렄랴 및 범ėŖŧëŗ„ ė‚ŦėšŠëŸ‰ė„ í™•ė¸í•Šë‹ˆë‹¤.", "username": "ęŗ„ė •ëĒ…", "users": "ė‚ŦėšŠėž", + "users_added_to_album_count": "ė‚ŦėšŠėž {count, plural, one {#ëĒ…} other {#ëĒ…}}ė„ ė•¨ë˛”ė— ėļ”ę°€í–ˆėŠĩ니다.", "utilities": "도ęĩŦ", "validate": "검ėĻ", "validate_endpoint_error": "ėœ íš¨í•œ URLė„ ėž…ë Ĩí•˜ė„¸ėš”.", "variables": "ëŗ€ėˆ˜", "version": "ë˛„ė „", "version_announcement_closing": "ë‹šė‹ ė˜ ėšœęĩŦ, Alex가", - "version_announcement_message": "ė•ˆë…•í•˜ė„¸ėš”! 냈 ë˛„ė „ė˜ ImmichëĨŧ ė‚ŦėšŠí•  눘 ėžˆėŠĩ니다. ėž˜ëĒģ된 ęĩŦė„ąė„ ë°Šė§€í•˜ęŗ  ImmichëĨŧ ėĩœė‹  ėƒíƒœëĄœ ėœ ė§€í•˜ę¸° ėœ„í•´ ėž ė‹œ ė‹œę°„ė„ ë‚´ė–´ ëĻ´ëĻŦ늤 노트ëĨŧ ėŊė–´ëŗ´ëŠ” ę˛ƒė„ ęļŒėžĨ합니다. 특히 WatchTower ë“ąė˜ ėžë™ ė—…ë°ė´íŠ¸ 기ëŠĨė„ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ė˜ë„í•˜ė§€ ė•Šė€ ë™ėž‘ė„ ë°Šė§€í•˜ę¸° ėœ„í•´ ë”ë”ėšą ęļŒėžĨ됩니다.", + "version_announcement_message": "ė•ˆë…•í•˜ė„¸ėš”! ėƒˆëĄœėš´ ë˛„ė „ė˜ Immich가 ėļœė‹œë˜ė—ˆėŠĩ니다. ė„¤ė •ė„ ėĩœė‹  ėƒíƒœëĄœ ėœ ė§€í•˜ė—Ŧ ėž˜ëĒģ된 ęĩŦė„ąėœŧ로 ė¸í•œ ëŦ¸ė œëĨŧ ë°Šė§€í•  눘 ėžˆë„ëĄ, 특히 WatchTower ë“ąė˜ ėžë™ ė—…ë°ė´íŠ¸ 기ëŠĨė„ ė‚ŦėšŠí•˜ëŠ” ę˛Ŋ뚰 ėž ė‹œ ė‹œę°„ė„ ë‚´ė–´ ëĻ´ëĻŦ늤 노트ëĨŧ í™•ė¸í•´ëŗ´ė„¸ėš”.", "version_history": "ë˛„ė „ 기록", - "version_history_item": "{date} ë˛„ė „ {version} ė„¤ėš˜", + "version_history_item": "{date} {version} ė„¤ėš˜", "video": "ë™ė˜ėƒ", - "video_hover_setting": "ë§ˆėš°ėŠ¤ ė˜¤ë˛„ ėžŦėƒ", - "video_hover_setting_description": "ë§ˆėš°ėŠ¤ëĨŧ ë™ė˜ėƒ ėœ„ė— ė˜ŦëĻŦ늴 ėžŦėƒė´ ė‹œėž‘ëŠë‹ˆë‹¤. ëš„í™œė„ąí™”ëœ ę˛Ŋėš°ė—ë„ ėžŦėƒ ė•„ė´ėŊ˜ė— ë§ˆėš°ėŠ¤ëĨŧ ė˜ŦëĻŦ늴 ėžŦėƒė´ ė‹œėž‘ëŠë‹ˆë‹¤.", + "video_hover_setting": "ė„Ŧ네ėŧ 똁냁 미ëĻŦëŗ´ę¸°", + "video_hover_setting_description": "ė„Ŧ네ėŧ ėœ„ė— ë§ˆėš°ėŠ¤ëĨŧ ė˜ŦëĻŦ늴 미ëĻŦëŗ´ę¸°ëĨŧ ėžŦėƒí•Šë‹ˆë‹¤. ëš„í™œė„ąí™”í•´ë„ ėžŦėƒ ė•„ė´ėŊ˜ė— ë§ˆėš°ėŠ¤ëĨŧ ė˜Ŧë ¤ 미ëĻŦëŗŧ 눘 ėžˆėŠĩ니다.", "videos": "ë™ė˜ėƒ", "videos_count": "ë™ė˜ėƒ {count, plural, one {#氜} other {#氜}}", "view": "ëŗ´ę¸°", "view_album": "ė•¨ë˛” ëŗ´ę¸°", "view_all": "ëĒ¨ë‘ ëŗ´ę¸°", "view_all_users": "ëĒ¨ë“  ė‚ŦėšŠėž ëŗ´ę¸°", + "view_details": "ėƒė„¸ ëŗ´ę¸°", "view_in_timeline": "íƒ€ėž„ëŧė¸ė—ė„œ ëŗ´ę¸°", "view_link": "링íŦ ëŗ´ę¸°", - "view_links": "링íŦ í™•ė¸", + "view_links": "링íŦ ëŗ´ę¸°", "view_name": "ëŗ´ę¸°", "view_next_asset": "ë‹¤ėŒ 항ëĒŠ ëŗ´ę¸°", "view_previous_asset": "ė´ė „ 항ëĒŠ ëŗ´ę¸°", - "view_qr_code": "QRėŊ”드 ëŗ´ę¸°", + "view_qr_code": "QR ėŊ”드 ëŗ´ę¸°", + "view_similar_photos": "ëš„ėŠˇí•œ ė‚Ŧė§„ ëŗ´ę¸°", "view_stack": "ėŠ¤íƒ ëŗ´ę¸°", + "view_user": "ė‚ŦėšŠėž ëŗ´ę¸°", "viewer_remove_from_stack": "ėŠ¤íƒė—ė„œ ė œęą°", - "viewer_stack_use_as_main_asset": "대표 ė‚Ŧė§„ėœŧ로 네렕", - "viewer_unstack": "ėŠ¤íƒ í•´ė œ", + "viewer_stack_use_as_main_asset": "대표 항ëĒŠėœŧ로 네렕", + "viewer_unstack": "ėŠ¤íƒ 풀기", "visibility_changed": "ė¸ëŦŧ {count, plural, one {#ëĒ…} other {#ëĒ…}}ė˜ í‘œė‹œ ė—Ŧëļ€ę°€ ëŗ€ę˛Ŋ됨", - "waiting": "대기", + "waiting": "대기 뤑", "warning": "ę˛Ŋęŗ ", "week": "ėŖŧ", "welcome": "í™˜ė˜í•Šë‹ˆë‹¤", @@ -1906,7 +2128,8 @@ "year": "년", "years_ago": "{years, plural, one {#년} other {#년}} ė „", "yes": "네", - "you_dont_have_any_shared_links": "ėƒė„ąí•œ ęŗĩ뜠 링íŦ가 ė—†ėŠĩ니다.", + "you_dont_have_any_shared_links": "ęŗĩ뜠 링íŦ가 ė—†ėŠĩ니다.", "your_wifi_name": "Wi-Fi ë„¤íŠ¸ė›ŒíŦ ė´ëĻ„", - "zoom_image": "ė´ë¯¸ė§€ 확대" + "zoom_image": "ė´ë¯¸ė§€ 확대", + "zoom_to_bounds": "í™”ëŠ´ė— 맞ėļ° í™•ëŒ€" } diff --git a/i18n/lt.json b/i18n/lt.json index d5e6ff20ed..b849d335a4 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -14,6 +14,8 @@ "add_a_location": "Pridėti vietovę", "add_a_name": "Pridėti vardą", "add_a_title": "Pridėti pavadinimą", + "add_birthday": "Pridėti gimimo diena", + "add_endpoint": "Pridėti galutinį taÅĄką", "add_exclusion_pattern": "Pridėti iÅĄimčiÅŗ ÅĄabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", @@ -26,12 +28,17 @@ "add_to_album": "Pridėti į albumą", "add_to_album_bottom_sheet_added": "Pridėta į {album}", "add_to_album_bottom_sheet_already_exists": "Jau yra albume {album}", + "add_to_album_toggle": "Perjungti paÅžymėjimus albumui {album}", + "add_to_albums": "Pridėti į albumus", + "add_to_albums_count": "Pridėti į albumus ({count})", "add_to_shared_album": "Pridėti į bendrinamą albumą", "add_url": "Pridėti URL", "added_to_archive": "Pridėta į archyvą", "added_to_favorites": "Pridėta prie mėgstamiausiÅŗ", "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausiÅŗ", "admin": { + "add_exclusion_pattern_description": "Pridėti iÅĄimčiÅŗ taisykles. Palaikomi simboliai *,**, ir ?. Ignoruoti bet kokius failus bet kuriame aplanke pavadintame \"Raw\", naudokite \"**/RAW/**\". Ignoravimui failÅŗ su plėtiniu \".tif\", naudokite \"**/*.tiff\". Aplanko kelio nustatymams, naudokite \"/aplanko/kelias/ignoruoti/**\".", + "admin_user": "Administratorius", "asset_offline_description": "Å is iÅĄorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į ÅĄiukÅĄliadėŞę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite ÅĄÄ¯ elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą Åžemiau nurodytu adresu, ir suvykdykite bibliotekos skenavimą.", "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptaÅžodÅžiÅŗ, OAuth ir kitus autentifikavimo nustatymus", @@ -41,9 +48,16 @@ "backup_database": "Sukurti duomenÅŗ bazės iÅĄklotinę", "backup_database_enable_description": "ÄŽgalinti duomenÅŗ bazės iÅĄklotinės", "backup_keep_last_amount": "IÅĄsaugomÅŗ ankstesniÅŗ duomenÅŗ bazės iÅĄklotiniÅŗ skaičius", + "backup_onboarding_1_description": "iÅĄorinė kopija debesyje arba kitoje fizinėje lokacijoje.", + "backup_onboarding_2_description": "vietinės kopijos kituose prietaisuose. Tai apima pagrindinius failus ir jÅŗ vietines kopijas.", + "backup_onboarding_3_description": "viso jÅĢsÅŗ duomenÅŗ kopijÅŗ, įskaitant originalius failus. Tai apima 1 iÅĄorinę ir 2 vietines kopijas.", + "backup_onboarding_description": "JÅĢsÅŗ duomenÅŗ apsaugojimui rekomenduojama 3-2-1 atsarginės kopijos strategija . JÅĢs turėtumėte saugoti įkeltÅŗ nuotraukÅŗ/video bei Immich duomenÅŗ bazės kopijas iÅĄsamiam atsarginiÅŗ kopijÅŗ sprendimui.", + "backup_onboarding_footer": "Daugiau informacijos apie „Immich“ atsarginiÅŗ kopijÅŗ kÅĢrimą rasite dokumentacijoje.", + "backup_onboarding_parts_title": "3-2-1 atsarginė kopija apima:", + "backup_onboarding_title": "Atsarginės kopijos", "backup_settings": "DuomenÅŗ bazės iÅĄklotiniÅŗ nustatymai", - "backup_settings_description": "Tvarkyti duomenÅŗ bazės iÅĄklotinės nustatymus. Pastaba: Å ie darbai nėra stebimi ir jums nebus praneÅĄta apie nesėkmę.", - "cleared_jobs": "IÅĄvalyti darbai: {job}", + "backup_settings_description": "Tvarkyti duomenÅŗ bazės iÅĄklotinės nustatymus. Pastaba: ÅĄie darbai nėra stebimi ir jums nebus praneÅĄta apie nesėkmę.", + "cleared_jobs": "IÅĄvalytos uÅžduotys uÅžduočiai: {job}", "config_set_by_file": "KonfigÅĢracija nustatyta pagal konfigÅĢracinį failą", "confirm_delete_library": "Ar tikrai norite iÅĄtrinti {library} biblioteką?", "confirm_delete_library_assets": "Ar tikrai norite iÅĄtrinti ÅĄią biblioteką? Å is veiksmas iÅĄtrins {count, plural, one {# contained asset} other {all # contained assets}} iÅĄ Immich ir negali bÅĢti grÄ…Åžintas. Failai liks diske.", @@ -51,7 +65,7 @@ "confirm_reprocess_all_faces": "Ar tikrai norite iÅĄ naujo apdoroti visus veidus? Tai taip pat iÅĄtrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iÅĄ naujo nustatyti {user} slaptaÅžodį?", "confirm_user_pin_code_reset": "Ar tikrai norite iÅĄ naujo nustatyti {user} PIN kodą?", - "create_job": "Sukurti darbą", + "create_job": "Sukurti uÅžduotį", "cron_expression": "Cron iÅĄraiÅĄka", "cron_expression_description": "Nustatyti skenavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos ÅžiÅĢrėkite Crontab Guru", "cron_expression_presets": "IÅĄankstiniai Cron nustatymai", @@ -62,15 +76,19 @@ "face_detection": "VeidÅŗ aptikimas", "face_detection_description": "VeidÅŗ aptikimas bibliotekos elementuose naudojant maÅĄininį mokymąsi. Vaizdo įraÅĄÅŗ atveju naudojama tik miniatiÅĢra. \"Atnaujinti\" iÅĄ naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir iÅĄvalo visus esamus veidÅŗ duomenis. \"TrÅĢkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. VeidÅŗ aptikimo darbui pasibaigus, aptikti veidai patenka į veidÅŗ atpaÅžinimo darbÅŗ eilę, kur jie priskiriami jau esamiems ar naujai atpaÅžintiems Åžmonėms.", "facial_recognition_job_description": "AptiktÅŗ veidÅŗ atpaÅžinimas ir priskyrimas Åžmonėms. Å is darbas vykdomas pasibaigus \"veidÅŗ aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"TrÅĢkstami\" apdoroja jokiam Åžmogui dar nepriskirtus aptiktus veidus.", - "failed_job_command": "Darbo {job} komanda {command} nepavyko", + "failed_job_command": "UÅžduoties {job} komanda {command} nepavyko", "force_delete_user_warning": "ÄŽSPĖJIMAS: Å is veiksmas iÅĄ karto paÅĄalins naudotoją ir visą jo informaciją. Å is Åžingsnis nesugrÄ…Åžinamas ir failÅŗ nebus galima atkurti.", "image_format": "Formatas", "image_format_description": "WebP sukuria maÅžesnius failus nei JPEG, tačiau lėčiau juos apdoroja.", + "image_fullsize_description": "Pilno dydÅžio nuotrauka be meta duomenÅŗ naudojama priartinus", "image_fullsize_enabled": "ÄŽgalinti pilno dydÅžio nuotraukÅŗ generavimą", + "image_fullsize_enabled_description": "Generuoti viso dydÅžio vaizdą narÅĄyklėms nepritaikytiems formatams. Kai įjungta parinktis „Pirmenybė įterptai perÅžiÅĢrai“, įterptosios perÅžiÅĢros naudojamos tiesiogiai be konvertavimo. Tai neturi įtakos internetui pritaikytiems formatams, pvz., JPEG.", "image_fullsize_quality_description": "Pilno dydÅžio nuotraukÅŗ kokybė 1-100. Didesnė yra geresnė, tačiau sukuria didesniu failus.", "image_fullsize_title": "Pilno dydÅžio nuotraukÅŗ Nustatymai", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą perÅžiÅĢrą", + "image_prefer_embedded_preview_setting_description": "Naudokite įterptąsias perÅžiÅĢras RAW nuotraukose kaip įvestį vaizdÅŗ apdorojimui ir, jei įmanoma, tai gali suteikti tikslesnes kai kuriÅŗ vaizdÅŗ spalvas, tačiau perÅžiÅĢros kokybė priklauso nuo fotoaparato, todėl vaizde gali bÅĢti daugiau glaudinimo artefaktÅŗ.", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", + "image_prefer_wide_gamut_setting_description": "MiniatiÅĢroms naudokite „Display P3“. Taip geriau iÅĄsaugomas vaizdÅŗ, turinčiÅŗ plačias spalvÅŗ erdves, ryÅĄkumas, tačiau senesniuose įrenginiuose su senesne narÅĄyklės versija vaizdai gali atrodyti kitaip. sRGB vaizdai iÅĄsaugomi kaip sRGB, kad bÅĢtÅŗ iÅĄvengta spalvÅŗ pasikeitimo.", "image_preview_description": "Vidutinio dydÅžio vaizdas su iÅĄvalytais metaduomenimis, naudojamas kai ÅžiÅĢrimas vienas objektas arba maÅĄininiam mokymuisi", "image_preview_quality_description": "PerÅžiÅĢros kokybė nuo 1-100. AukÅĄtesnės reikÅĄmės yra geriau, bet sukuriami didesni failai gali sumaÅžinti programos reagavimo laiką. MaÅžos vertės nustatymas gali paveikti maÅĄininio mokymo kokybę.", "image_preview_title": "PerÅžiÅĢros nustatymai", @@ -83,11 +101,13 @@ "image_thumbnail_quality_description": "MiniatiÅĢros kokybė nuo 1-100. AukÅĄtesnės reikÅĄmės yra geriau, bet pagaminami didesni failai ir gali bÅĢti sulėtintas programos reagavimo greitis.", "image_thumbnail_title": "MiniatiÅĢros nustatymai", "job_concurrency": "{job} lygiagretumas", - "job_created": "Darbas sukurtas", - "job_not_concurrency_safe": "Å is darbas nėra saugus apdoroti lygiagrečiai.", - "job_settings": "DarbÅŗ nustatymai", - "job_settings_description": "Keisti darbÅŗ lygiagretumą", - "job_status": "DarbÅŗ bÅĢsenos", + "job_created": "UÅžduotis sukurta", + "job_not_concurrency_safe": "Å i uÅžduotis nėra saugi apdoroti lygiagrečiai.", + "job_settings": "UÅžduočiÅŗ nustatymai", + "job_settings_description": "Keisti uÅžduočiÅŗ lygiagretumą", + "job_status": "UÅžduočiÅŗ bÅĢsenos", + "jobs_delayed": "{jobCount, plural, one {# atidėtas} few {# atidėti} other {# atidėtÅŗ}}", + "jobs_failed": "{jobCount, plural, other {# nepavyko}}", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka iÅĄtrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Å iame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įraÅĄai.", @@ -103,7 +123,15 @@ "logging_enable_description": "ÄŽjungti Åžurnalo vedimą", "logging_level_description": "ÄŽjungus, kokį Åžurnalo vedimo lygį naudot.", "logging_settings": "ÅŊurnalo vedimas", + "machine_learning_availability_checks": "Prieinamumo patikrinimai", + "machine_learning_availability_checks_description": "AutomatiÅĄkai aptikti ir teikti pirmenybę prieinamiems maÅĄininio mokymosi serveriams", + "machine_learning_availability_checks_enabled": "ÄŽjungti prieinamumo patikrinimus", + "machine_learning_availability_checks_interval": "Patikros intervalas", + "machine_learning_availability_checks_interval_description": "Intervalas milisekundėmis tarp prieinamumo patikrinimÅŗ", + "machine_learning_availability_checks_timeout": "UÅžklausos laiko limitas", + "machine_learning_availability_checks_timeout_description": "Laiko limitas milisekundėmis prieinamumo patikrinimams", "machine_learning_clip_model": "CLIP modelis", + "machine_learning_clip_model_description": "Pavadinimas CLIP modelio įvardintio here. Dėmesio, keičiant modelį jÅĢs privalote iÅĄ naujo paleisti 'IÅĄmaniosios PaieÅĄkos' uÅžduotį visiems vaizdams.", "machine_learning_duplicate_detection": "DublikatÅŗ aptikimas", "machine_learning_duplicate_detection_enabled": "ÄŽjungti dublikatÅŗ aptikimą", "machine_learning_duplicate_detection_enabled_description": "Jei iÅĄjungta, visiÅĄkai identiÅĄki elementai vis tiek bus deduplikuoti.", @@ -113,12 +141,15 @@ "machine_learning_facial_recognition": "VeidÅŗ atpaÅžinimas", "machine_learning_facial_recognition_description": "Aptikti, atpaÅžinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "VeidÅŗ atpaÅžinimo modelis", + "machine_learning_facial_recognition_model_description": "Modeliai iÅĄvardinti apimties maŞėjančia tvarka. Didieji modeliai yra lėti ir naudoja daugiau atminties, tačiau sukuria geresnius rezultatus. Pastebime kad keičiant modelį jÅĢs turite iÅĄ naujo paleisti VeidÅŗ AtpaÅžinimo uÅžduotį visiems vaizdams.", "machine_learning_facial_recognition_setting": "ÄŽgalinti veidÅŗ atpaÅžinimą", "machine_learning_facial_recognition_setting_description": "IÅĄjungus, vaizdai nebus uÅžÅĄifruoti veidÅŗ atpaÅžinimui ir nebus naudojami ÅŊmoniÅŗ sekcijoje NarÅĄymo puslapyje.", "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "DidÅžiausias atstumas tarp dviejÅŗ vaizdÅŗ, kad jie bÅĢtÅŗ laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatÅŗ, tačiau gali bÅĢti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpaÅžinimo atstumas", + "machine_learning_max_recognition_distance_description": "DidÅžiausias skirtumas tarp veidÅŗ, kad bÅĢtÅŗ uÅžskaityti kaip vienas ir tas pats asmuo, rÄ—Åžis nuo 0-2. MaÅžinant tai gali apsaugoti nuo dviejÅŗ ÅžmoniÅŗ Åžymėjimo tuo pačiu asmeniu, didinant tai gali apsaugoti nuo to pačio asmens Åžymėjimo kaip du skirtingus Åžmones. Pastebime kad yra paprasčiau apjungti keliÅŗ ÅžmoniÅŗ modelius į vieną nei vieną iÅĄdalinti į du, taigi kai įmanoma geriau naudoti maÅžensę ribą.", "machine_learning_min_detection_score": "Minimalus aptikimo balas", + "machine_learning_min_detection_score_description": "Minimalus uÅžtikrintumo balas veido aptikimui nuo 0-1. MaÅžesnė reikÅĄmė aptiks daugiau veidÅŗ tačiau bus ir daugiau klaidingÅŗ teigiamÅŗ reÅžultatÅŗ.", "machine_learning_min_recognized_faces": "MaÅžiausias atpaÅžintÅŗ veidÅŗ skaičius", "machine_learning_min_recognized_faces_description": "MaÅžiausias atpaÅžintÅŗ veidÅŗ skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpaÅžinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas Åžmogui nepriskirtas.", "machine_learning_settings": "MaÅĄininio mokymosi nustatymai", @@ -148,17 +179,32 @@ "metadata_extraction_job": "MetaduomenÅŗ nuskaitymas", "metadata_extraction_job_description": "Kiekvieno bibliotekos elemento metaduomenÅŗ nuskaitymas, tokiÅŗ kaip GPS koordinatės, veidai ar rezoliucija", "metadata_faces_import_setting": "ÄŽjungti veidÅŗ importą", - "metadata_faces_import_setting_description": "Importuoti veidus iÅĄ vaizdo EXIF duomenÅŗ ir papildomÅŗ failÅŗ", + "metadata_faces_import_setting_description": "Importuoti veidus iÅĄ vaizdo EXIF duomenÅŗ ir susietÅŗ failÅŗ", "metadata_settings": "MetaduomenÅŗ nustatymai", "metadata_settings_description": "Tvarkyti metaduomenÅŗ nustatymus", - "migration_job": "Migracija", + "migration_job": "Tvarkymas", + "migration_job_description": "Pertvarkytį turinio ir veidÅŗ miniatiÅĢras pagal naują struktÅĢrą", + "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpaÅžinimą naujai aptiktiems veidams", + "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", + "nightly_tasks_database_cleanup_setting": "DuomenÅŗ bazės valymo darbai", + "nightly_tasks_database_cleanup_setting_description": "IÅĄvalyti senus, nebgaliojančius duomenis iÅĄ duomenÅŗ bazės", + "nightly_tasks_generate_memories_setting": "Kurti prisiminimus", + "nightly_tasks_generate_memories_setting_description": "IÅĄ duomenÅŗ kurti naujus prisiminimus", + "nightly_tasks_missing_thumbnails_setting": "Kurti trÅĢkstamas miniatiÅĢras", + "nightly_tasks_missing_thumbnails_setting_description": "Sudaryti įraÅĄÅŗ be miniatiÅĢrÅŗ eilę miniatiÅĢrÅŗ generavimui", + "nightly_tasks_settings": "NaktiniÅŗ uÅžduočiÅŗ nustatymai", + "nightly_tasks_settings_description": "Valdyti naktines uÅžduotis", + "nightly_tasks_start_time_setting": "PradÅžios laikas", + "nightly_tasks_start_time_setting_description": "Laikas kada serveris pradės vykdyti naktines uÅžduotis", + "nightly_tasks_sync_quota_usage_setting": "Sinchronizuoti kvotos naudojimą", + "nightly_tasks_sync_quota_usage_setting_description": "Atnaujinti vartotojo vietos kvotą remiantis dabartiniu vartojimu", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Å ablonas nepridėtas", - "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti saugyklos etiketę seniau įkeltiems iÅĄtekliams, paleiskite", + "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos ÅŊymą seniau įkeltiems iÅĄtekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "IÅĄ adreso", - "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdÅžiui: \"Immich Photo Server \"", - "notification_email_host_description": "Elektroninio paÅĄto serverio savininkas (pvz. smtp.immich.app)", + "notification_email_from_address_description": "Siuntėjo el. paÅĄto adresas, pavyzdÅžiui: \"Immich Photo Server \". BÅĢtinai naudokite adresą iÅĄ kurio jums galima siÅŗsti laiÅĄkus.", + "notification_email_host_description": "Elektroninio paÅĄto serverio adresas (pvz. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Nepaisyti sertifikatÅŗ klaidÅŗ", "notification_email_ignore_certificate_errors_description": "Nepaisyti TLS sertifikato patvirtinimo klaidÅŗ (nerekomenduojama)", "notification_email_password_description": "SlaptaÅžodis, naudojant autentikacijai su elektroninio paÅĄto serveriu", @@ -177,20 +223,29 @@ "oauth_auto_register": "Automatinis registravimas", "oauth_auto_register_description": "AutomatiÅĄkai uÅžregistruoti naujus naudotojus po prisijungimo per OAuth", "oauth_button_text": "Mygtuko tekstas", + "oauth_client_secret_description": "Privalomas jei PKCE (Proof Key for Code Exchange) nepalaikomas pagal OAuth tiekėją", "oauth_enable_description": "Prisijungti su OAuth", "oauth_mobile_redirect_uri": "Mobiliojo peradresavimo URI", "oauth_mobile_redirect_uri_override": "Mobiliojo peradresavimo URI pakeitimas", "oauth_mobile_redirect_uri_override_description": "ÄŽjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip ''{callback}''", + "oauth_role_claim": "Rolės Tvirtinimas", + "oauth_role_claim_description": "Suteikti admin teises automatiÅĄkai pagal ÅĄios rolės tvirtinimo buvimą. Tvirtinimas gali turėti priskirtus arba 'vartotoją' arba 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie ÅĄią funkciją galite paskaityti dokumentacijoje.", + "oauth_storage_label_claim": "Saugyklos ÅŊyma pagal tvirtinimą", + "oauth_storage_label_claim_description": "Priskirti Saugyklos ÅŊymą automatiÅĄkai pagal reikÅĄmę vartotojo tvirtinime.", + "oauth_storage_quota_claim": "Saugyklos apimties tvirtinimas", + "oauth_storage_quota_claim_description": "Priskirti vartotojo saugyklos apimties kvotą automatiÅĄkai pagal ÅĄio tvirtinimo reikÅĄmę.", "oauth_storage_quota_default": "Numatyta atminties kvota (GiB)", + "oauth_storage_quota_default_description": "Nustatoma appimties kvota GiB kai nėra nurodyta tvirtinime.", "oauth_timeout": "UÅžklausa virÅĄijo laiko limitą", "oauth_timeout_description": "Laiko limitas uÅžklausoms milisekundėmis", "password_enable_description": "Prisijungti su el. paÅĄtu ir slaptaÅžodÅžiu", "password_settings": "Prisijungimas slaptaÅžodÅžiu", "password_settings_description": "Tvarkyti prisijungimo slaptaÅžodÅžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", + "person_cleanup_job": "IÅĄvalyti asmenis", "quota_size_gib": "Kvotos dydis (GiB)", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", "registration": "Administratoriaus registracija", @@ -199,7 +254,7 @@ "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "reset_settings_to_recent_saved": "NustatymÅŗ atstatymas į neseniai iÅĄsaugotus nustatymus", "scanning_library": "Biblioteka skenuojama", - "search_jobs": "IeÅĄkoma darbÅŗâ€Ļ", + "search_jobs": "IeÅĄkoma uÅžduočiÅŗâ€Ļ", "send_welcome_email": "SiÅŗsti sveikinimo el. laiÅĄką", "server_external_domain_settings": "IÅĄorinis domenas", "server_external_domain_settings_description": "Bendrinimo nuorodÅŗ domenas, įskaitant http(s)://", @@ -209,46 +264,127 @@ "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo praneÅĄimas", "server_welcome_message_description": "ÅŊinutė, rodoma prisijungimo puslapyje.", + "sidecar_job": "Sidecar metaduomenys", + "sidecar_job_description": "Aptikti ar sinchronizuoti sidecar metaduomenis iÅĄ failÅŗ sistemos", "slideshow_duration_description": "SekundÅžiÅŗ skaičius, kiek viena nuotrauka rodoma", "smart_search_job_description": "Vykdykite maÅĄininį mokymąsi bibliotekos elementÅŗ iÅĄmaniajai paieÅĄkai", "storage_template_date_time_description": "Elemento sukÅĢrimo laiko Åžymė yra naudojama laiko informacijai", "storage_template_date_time_sample": "Pavyzdinis laikas {date}", + "storage_template_enable_description": "Aktyvuoti saugyklos ÅĄabloną", + "storage_template_hash_verification_enabled": "Aktyvuoti failo paraÅĄo tikrinimą", + "storage_template_hash_verification_enabled_description": "Aktyvuojamas failo paraÅĄo tikrinimas, neiÅĄjungti nebent gerai suprantate galimas pasekmes", + "storage_template_migration": "Saugyklos tvarkymas pagal ÅĄabloną", + "storage_template_migration_description": "Taikyti dabartinį {template} anksčiau įkeltiems duomenims", + "storage_template_migration_info": "Saugyklos tvarkyklė konvertuos visus plėtinius maÅžosiomis raidėmis. Å ablonas bus taikomas tik naujiems duomenims. Taikyti ÅĄabloną retroaktyviai anksčiau įkeltiems duomenims, paleiskite ÅĄią {job}.", + "storage_template_migration_job": "Saugyklos Tvarkymo Pagal Å abloną UÅžduotis", + "storage_template_more_details": "Daugiau detaliÅŗ apie ÅĄią funkciją, atsiÅžvelkite į Storage Template ir jo galimus implications", + "storage_template_onboarding_description_v2": "Kai aktyvuota, ÅĄi funkcija automatiÅĄkai sukurs failus pagal vartotojo-nustatytą ÅĄabloną. Daugiau informacijos, praÅĄome skaityti documentation.", + "storage_template_path_length": "Preliminarus struktÅĢros kelio ilgis/limitas:{length, number}/{limit, number}", + "storage_template_settings": "Saugyklos Å ablonas", + "storage_template_settings_description": "Tvarkyti aplankÅŗ struktÅĢrą bei failÅŗ pavadinimus įkeliamiems duomenims", + "storage_template_user_label": "{label} yra vartotojo Saugyklos ÅŊymą", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "ÅŊymÅŗ iÅĄvalymas", + "template_email_available_tags": "Savo ÅĄablone galite naudoti nurodytas kintamas reikÅĄmes:{tags}", + "template_email_if_empty": "Jei ÅĄablone tuÅĄÄia reikÅĄmė, bus naudojamas numatytas pagal nutylėjimą El. paÅĄto adresas.", + "template_email_invite_album": "KvietimÅŗ albumo ÅĄablonas", "template_email_preview": "PerÅžiÅĢra", "template_email_settings": "El. paÅĄto Å ablonai", + "template_email_update_album": "Atnaujinti albumo ÅĄabloną", + "template_email_welcome": "Sveikinimo el. laiÅĄko ÅĄablonas", "template_settings": "PraneÅĄimÅŗ ÅĄablonai", "template_settings_description": "Tvarkyti pasirinktinius praneÅĄimÅŗ ÅĄablonus", "theme_custom_css_settings": "Individualizuotas CSS", + "theme_custom_css_settings_description": "CSS leidÅžiantis keisti Immich dizainą.", "theme_settings": "Temos nustatymai", - "thumbnail_generation_job": "Generuoti miniatiÅĢras", + "theme_settings_description": "Valdyti Immich web sąsajos pritaikymus", + "thumbnail_generation_job": "Generuoti MiniatiÅĢras", "thumbnail_generation_job_description": "DideliÅŗ, maÅžÅŗ ir neryÅĄkiÅŗ miniatiÅĢrÅŗ generavimas kiekvienam bibliotekos elementui, taip pat miniatiÅĢrÅŗ generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", + "transcoding_acceleration_api_description": "API kurį naudos paspartintam perkodavimui. Tai veiks pagal \"geriausią bandymą\": nepavykus bus naudojamas programinis perkodavimas. VP9 gali veikti arba ne priklausomai nuo jÅĢsÅŗ techninės įrangos.", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (reikalingas 7-os arba vėlesnės generacijos Intel procesorius)", + "transcoding_acceleration_rkmpp": "RKMPP (tik Rockchip SOCs)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "Priimtini garso kodekai", + "transcoding_accepted_audio_codecs_description": "Pasirinkti kuriÅŗ garso kodekÅŗ nereikia perkoduoti. Naudojma tik kai kurioms perkodavimo taisyklėms.", "transcoding_accepted_containers": "Priimami konteineriai", + "transcoding_accepted_containers_description": "Pasirinkti kuriÅŗ konteineriÅŗ formatÅŗ nereikia performuoti į MP4. Naudojama tik kai kurioms perkodavimo taisyklėms.", + "transcoding_accepted_video_codecs": "Priimami vaizdo kodekai", + "transcoding_accepted_video_codecs_description": "Pasirinkti vaizdo kodekus kuriÅŗ nereikia perkoduoti. Naudojama tik kai kurioms perkodavimo taisyklėms.", "transcoding_advanced_options_description": "Parinktys, kuriÅŗ daugelis naudotojÅŗ keisti neturėtÅŗ", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukÅĄÄiausios kokybės variantas, tačiau turi maÅžesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įraÅĄai virÅĄija maksimalią leistiną bitÅŗ spartą arba nėra priimtino formato", + "transcoding_codecs_learn_more": "SuÅžinoti daugiau apie naudojamą terminologiją, naudokite FFmpeg dokumentaciją H.264 codec, HEVC codec and VP9 codec.", "transcoding_constant_quality_mode": "Pastovios kokybės reÅžimas", + "transcoding_constant_quality_mode_description": "ICQ yra geriau nei CPQ, tačiau ne visi įrenginiai palaiko ÅĄÄ¯ spartinimo bÅĢdą. Å is pasirinkimas bÅĢtÅŗ naudojamas kai nustatytas Kodavimas Pagal Kokybę. NVENC nepalaiko ÅĄio pasirinkimo todėl bus ignoruojamas.", + "transcoding_constant_rate_factor": "Pastovaus greičio faktorius (-crf)", + "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikÅĄmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo maÅžesnis tuo kokybiÅĄkesnis tačiau didesni failai.", + "transcoding_disabled_description": "Nedaryti perkodavimo, įraÅĄÅŗ perÅžiÅĢra gali neveikti ant kai kÅĢriÅŗ sąsajÅŗ", + "transcoding_encoding_options": "UÅžkodavimo parinktys", + "transcoding_encoding_options_description": "Nustatyti kodekus, rezoliuciją, kokybę ir kitas parinktis uÅžkoduojamiems vaizdo įraÅĄams", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", + "transcoding_hardware_acceleration_description": "Eksperimentinis: greitesnis perkodavimas, bet galimai prastesne kokybe prie tos pačios bitÅŗ spartos", "transcoding_hardware_decoding": "Aparatinis dekodavimas", + "transcoding_hardware_decoding_setting_description": "ÄŽgalina visapusiÅĄką paspartinimą vietoje tik uÅžkodavimo paspartinimo. Gali neveikti su kai kuriais vaizdo įraÅĄais.", + "transcoding_max_b_frames": "Maksimaliai B-kadrÅŗ", + "transcoding_max_b_frames_description": "Didesnės reikÅĄmės pagerina suspaudimo efektyvumą, bet sulėtina uÅžkodavimą. Senesniuose prietaisuose gali bÅĢti nepalaikomas aparatinis spartinimas. 0 iÅĄjungia B-kadrus, o -1 nustato reikÅĄmę automatiÅĄkai.", "transcoding_max_bitrate": "Maksimalus bitÅŗ srautas", + "transcoding_max_bitrate_description": "Pasirenkant max bitrate galima pasiekti labiau nuspėjamą failÅŗ dydį su minimaliais kokybės praradimais. Prie 720p, tipinės reikÅĄmės yra 2600 kbits/s jei BP9 ar HVEC, arba 4500 kbits/s jei H.264. Neveiksnus jei pasirenkamas 0.", + "transcoding_max_keyframe_interval": "Maksimalus raktinio kadro intervalas", + "transcoding_max_keyframe_interval_description": "Nustato maksimalÅŗ kadro atstumą tarp raktiniÅŗ kadrÅŗ. ÅŊemesnės reikÅĄmės pablogina suspaudimo efektyvumą, bet pagerina prasukimo laiką ir gali pagerinti greito veiksmo scenÅŗ kokybę. 0 - nustato ÅĄią reikÅĄmę automatiÅĄkai.", + "transcoding_optimal_description": "Vaizdo įraÅĄai aukÅĄtesne nei tikslinė rezoliucija arba nepalaikomu formatu", + "transcoding_policy": "Transkodavimo politika", + "transcoding_policy_description": "Nustatyti kada vaizdo įraÅĄas bus perkoduotas", + "transcoding_preferred_hardware_device": "Pageidaujamas aparatinės įrangos įrenginys", + "transcoding_preferred_hardware_device_description": "Galioja tik VAAPI ir QSV. Nustato dri mazgą aparatiniam perkodavimui.", + "transcoding_preset_preset": "IÅĄ anksto nustatytas (-preset)", + "transcoding_preset_preset_description": "Kompresijos greitis. Siekiant tam tikro bitrate lėtesnis apdorojimas lems maÅžesnius failÅŗ dydÅžius ir padidins kokybę. VP9 ignoruos greičius virÅĄ \"gretesnis\" lygio.", + "transcoding_reference_frames": "Nuorodiniai kadrai", + "transcoding_reference_frames_description": "KadrÅŗ, į kuriuos reikia remtis suspaudÅžiant duotą kadrą, skaičius. AukÅĄtesnė reikÅĄmė pagerina suspaudimo efektyvumą, bet sulėtina uÅžkodavimą. 0 - nustato reikÅĄmę automatiÅĄkai.", + "transcoding_required_description": "Tik nepalaikomo formato vaizdo įraÅĄai", + "transcoding_settings": "Vaizdo įraÅĄÅŗ perkodavimo nustatymai", + "transcoding_settings_description": "Valdyti kuriuos vaizdo įraÅĄus perkoduoti ir kaip juos apdoroti", + "transcoding_target_resolution": "Skiriamoji geba", "transcoding_target_resolution_description": "Didesnės skiriamosios gebos gali iÅĄsaugoti daugiau detaliÅŗ, tačiau jas koduoti uÅžtrunka ilgiau, failÅŗ dydÅžiai yra didesni ir gali sumaŞėti programos jautrumas.", + "transcoding_temporal_aq": "Laikinas adaptyvus kvantavimas", + "transcoding_temporal_aq_description": "Galioja tik NVENC. Pagerina detaliÅŗ, maÅžo judesio scenÅŗ kokybę. Gali bÅĢti nepalaikoma senesniÅŗ įrenginiÅŗ.", + "transcoding_threads": "Gijos", + "transcoding_threads_description": "Didesnės reikÅĄmės pagreitina kodavimą, bet kol aktyvus palieka maÅžiau serverio resursÅŗ kitoms uÅžduotims. Å i reikÅĄmė negali bÅĢti didesnė uÅž procesoriaus branduoliÅŗ kiekį. Jei reikÅĄmė 0, tai iÅĄnaudoja maksimaliai.", + "transcoding_tone_mapping": "TonÅŗ atvaizdavimas", + "transcoding_tone_mapping_description": "Bandoma iÅĄsaugoti HDR vaizdo įraÅĄÅŗ iÅĄvaizdą konvertuojant į SDR. Kiekvienas algoritmas taiko skirtingus kompromisus dėl spalvÅŗ, detaliÅŗ ir ÅĄviesumo. Hable iÅĄsaugo detales, Mobius iÅĄsaugo spalvas, o Reinhard iÅĄsaugo ÅĄviesumą.", + "transcoding_transcode_policy": "Perkodavimo strategija", + "transcoding_transcode_policy_description": "Strategija, kada vaizdo įraÅĄas turi bÅĢti perkoduotas. HDR vaizdo įraÅĄai visada bus perkoduoti (iÅĄskyrus jei perkodavimas iÅĄjungtas).", + "transcoding_two_pass_encoding": "DviejÅŗ perėjimÅŗ uÅžkodavimas", + "transcoding_two_pass_encoding_setting_description": "Perkoduoti su dviem perėjimais, kad gauti geriau uÅžkoduotą vaizdo įraÅĄÄ…. Kai maksimalus bitÅŗ srautas įjungtas (veikimui reikalaujamas H.264 ir HVEC), tada naudojamas bitÅŗ intervalas remiantis maksimaliu bitÅŗ srautu ir ignoruojamas CRF. Su VP9 gali bÅĢti naudojamas CRF, jei maksimalus bitÅŗ srautas yra iÅĄjungtas.", "transcoding_video_codec": "Video kodekas", + "transcoding_video_codec_description": "VP9 turi didelį efektyvumą ir tinklo suderinamumą, bet uÅžtrunka ilgiau perkoduojant. HVEC veikia panaÅĄiai, bet turi maÅžesnį tinklo suderinamumą. H.264 yra plačiai palaikomas ir greitai perkoduojamas, bet kuria didelius failus. AV1 yra efektyviausias kodekas, bet nepalaikomas senesniÅŗ prietaisÅŗ.", "trash_enabled_description": "ÄŽgalinti ÅĄiukÅĄliadėŞės funkcijas", "trash_number_of_days": "DienÅŗ skaičius", + "trash_number_of_days_description": "Kiek dienÅŗ bus laikomi elementai ÅĄiukÅĄliadėŞėje prieÅĄ galutinai juos iÅĄtrinant", "trash_settings": "Å iukÅĄliadėŞės nustatymai", "trash_settings_description": "Tvarkyti ÅĄiukÅĄliadėŞės nustatymus", + "unlink_all_oauth_accounts": "Atsieti visas OAuth paskyras", + "unlink_all_oauth_accounts_description": "NepamirÅĄkite atsieti visas OAuth paskyras prieÅĄ migruojant pas naują tiekėją.", + "unlink_all_oauth_accounts_prompt": "Ar tikrai norite atsieti visas OAuth paskyras? Tai negrįŞtama operacija kuri atstatys OAuth ID kiekvienam vartotojui.", + "user_cleanup_job": "VartotojÅŗ iÅĄvalymas", + "user_delete_delay": "{user} paskyra ir elementai bus nustatyti galutiniam iÅĄtrynimui uÅž {delay, plural, one {# dienos} other {# dienÅŗ}}.", "user_delete_delay_settings": "IÅĄtrynimo delsa", + "user_delete_delay_settings_description": "Skaičius dienÅŗ po iÅĄtrynimo kuomet naudotojo paskyra ir susiję duomenys bus negraÅžinamai iÅĄtrinti. Naudotojo trynimo uÅžduotis paleidÅžiama vidurnaktį ir tikrina kurie naudotojai gali bÅĢti trinami. Å io nustatymo pakeitimai bus naudojami sekančio uÅžduoties paleidimo metu.", + "user_delete_immediately": "{user} paskyra ir elementai bus nedelsiant įtraukti galutiniam paÅĄalinimui.", + "user_delete_immediately_checkbox": "IÅĄtrinti naudotoją ir elementus nedelsiant", + "user_details": "Naudotojo duomenys", "user_management": "NaudotojÅŗ valdymas", "user_password_has_been_reset": "Naudotojo slaptaÅžodis buvo iÅĄ naujo nustatytas:", + "user_password_reset_description": "Perduokite laikiną slaptaÅžodį naudotojui ir informuokite, kad pasikeistÅŗ slaptaÅžodį pirmo prisijungimo metu.", "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", + "user_restore_scheduled_removal": "Atkurti naudotoją - suplanuotas paÅĄalinimas {date, date, long}", "user_settings": "Naudotojo nustatymai", "user_settings_description": "Valdyti naudotojo nustatymus", "user_successfully_removed": "Naudotojas {email} sėkmingai paÅĄalintas.", + "version_check_enabled_description": "ÄŽgalinti versijÅŗ tikrinimą", + "version_check_implications": "VersijÅŗ tikrinimas reikalauja periodiÅĄkos komunikacijos su github.com", "version_check_settings": "Versijos tikrinimas", "version_check_settings_description": "ÄŽjungti/iÅĄjungti naujos versijos praneÅĄimus", "video_conversion_job": "Vaizdo įraÅĄÅŗ konvertavimas", @@ -257,10 +393,34 @@ "admin_email": "Administratoriaus el. paÅĄtas", "admin_password": "Administratoriaus slaptaÅžodis", "administration": "Administravimas", + "advanced": "Sudėtingesnis", + "advanced_settings_enable_alternate_media_filter_subtitle": "Naudokite ÅĄÄ¯ nustatymą medijos filtravimui sinchronizuojant remiantis alternatyviais kriterijais. Naudokite tik jei programa turi problemÅŗ su visÅŗ albumÅŗ aptikimu.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTINIS] Naudokite alternatyvÅŗ įrenginio albumÅŗ sinchronizavimo filtrą", + "advanced_settings_log_level_title": "ÅŊurnalo įraÅĄÅŗ lygis: {level}", + "advanced_settings_prefer_remote_subtitle": "Kai kurie įrenginiai labai lėtai įkelia miniatiÅĢras iÅĄ vietiniÅŗ elementÅŗ. Aktyvuokite ÅĄÄ¯ nustatymą, kad vietoje to uÅžkrautumėte nuotolines nuotraukas.", + "advanced_settings_prefer_remote_title": "Teikti pirmenybę nuotolinėms nuotraukoms", + "advanced_settings_proxy_headers_subtitle": "Nustatykite tarpinio serverio antraÅĄtes kurias Immich siÅŗs su kiekvienu uÅžklausimu", + "advanced_settings_proxy_headers_title": "Tarpinio serverio antraÅĄtės", + "advanced_settings_readonly_mode_subtitle": "ÄŽgalina tik skaitymo reÅžimą kai nuotraukas galima tik ÅžiÅĢrėti, draudÅžiama paÅžymėti kelias, dalintis, transliuoti ar iÅĄtrinti. ÄŽgalinkit/uÅždrauskit tik skaitymą per naudotojo avatar'ą iÅĄ pagrindinio lango", + "advanced_settings_readonly_mode_title": "Tik skaitymo reÅžimas", + "advanced_settings_self_signed_ssl_subtitle": "PraleidÅžia SSL sertifikato tikrinimą serverio galutiniam taÅĄkui. Privaloma pačiÅŗ pasiraÅĄytiems sertifikatams.", + "advanced_settings_self_signed_ssl_title": "Leisti pačiÅŗ pasiraÅĄytus SSL sertifikatus", + "advanced_settings_sync_remote_deletions_subtitle": "AutomatiÅĄkai iÅĄtrinti ar atkurti elementus įrenginyje, kai tie veiksmai atliekami narÅĄyklėje", + "advanced_settings_sync_remote_deletions_title": "Sinchronizuoti nuotolinius iÅĄtrynimus [EKSPERIMENTINIS]", + "advanced_settings_tile_subtitle": "PaÅžangesni naudotojÅŗ nustatymai", + "advanced_settings_troubleshooting_subtitle": "ÄŽgalinti papildomas galimybes trikčiÅŗ ÅĄalinimui", + "advanced_settings_troubleshooting_title": "TrikčiÅŗ ÅĄalinimas", + "age_months": "AmÅžius {months, plural, one {# mėnesis} few {# mėnesiai} other {# mėnesiÅŗ}}", + "age_year_months": "AmÅžius 1 metai, {months, plural, one {# mėnesis} few {# mėnesiai} other {# mėnesiÅŗ}}", + "age_years": "{years, plural, other {AmÅžius #}}", "album_added": "Albumas pridėtas", "album_added_notification_setting_description": "Gauti el. paÅĄto praneÅĄimą, kai bÅĢsite pridėtas prie bendrinamo albumo", "album_cover_updated": "Albumo virÅĄelis atnaujintas", "album_delete_confirmation": "Ar tikrai norite iÅĄtrinti albumą {album}?", + "album_delete_confirmation_description": "Jei ÅĄiuo albumu dalijamasi, tai kiti naudotojai jo nebegalės pasiekti.", + "album_deleted": "Albumas iÅĄtrintas", + "album_info_card_backup_album_excluded": "neįtrauktas", + "album_info_card_backup_album_included": "įtrauktas", "album_info_updated": "Albumo informacija atnaujinta", "album_leave": "Palikti albumą?", "album_leave_confirmation": "Ar tikrai norite palikti albumą {album}?", @@ -268,13 +428,28 @@ "album_options": "Albumo parinktys", "album_remove_user": "PaÅĄalinti naudotoją?", "album_remove_user_confirmation": "Ar tikrai norite paÅĄalinti naudotoją {user}?", + "album_search_not_found": "Pagal jÅĢsÅŗ paieÅĄką albumÅŗ nerasta", "album_share_no_users": "Atrodo, kad bendrinate ÅĄÄ¯ albumą su visais naudotojais, arba neturite naudotojÅŗ, su kuriais galėtumėte bendrinti.", + "album_summary": "Albumo santrauka", "album_updated": "Albumas atnaujintas", "album_updated_setting_description": "Gauti praneÅĄimą el. paÅĄtu, kai bendrinamas albumas turi naujÅŗ elementÅŗ", + "album_user_left": "Paliko {album}", "album_user_removed": "PaÅĄalintas {user}", + "album_viewer_appbar_delete_confirm": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ albumą iÅĄ savo paskyros?", + "album_viewer_appbar_share_err_delete": "Nepavyko iÅĄtrinti albumo", + "album_viewer_appbar_share_err_leave": "Nepavyko iÅĄeiti iÅĄ albumo", + "album_viewer_appbar_share_err_remove": "Kilo problemÅŗ paÅĄalinant elementus iÅĄ albumo", + "album_viewer_appbar_share_err_title": "Nepavyko pakeisti albumo pavadinimą", + "album_viewer_appbar_share_leave": "Palikti albumą", + "album_viewer_appbar_share_to": "Dalintis su", + "album_viewer_page_share_add_users": "Pridėti naudotojÅŗ", "album_with_link_access": "Tegul visi, turintys nuorodą, mato ÅĄio albumo nuotraukas ir Åžmones.", "albums": "Albumai", "albums_count": "{count, plural, one {# albumas} few {# albumai} other {# albumÅŗ}}", + "albums_default_sort_order": "Pradinė albumo rÅĢÅĄiavimo tvarka", + "albums_default_sort_order_description": "Pradinė elementÅŗ rÅĢÅĄiavimo tvarka kai kuriamas naujas albumas.", + "albums_feature_description": "ElementÅŗ rinkinys kuriuo galima dalintis su kitais naudotojais.", + "albums_on_device_count": "AlbumÅŗ įrenginyje ({count})", "all": "Visi", "all_albums": "Visi albumai", "all_people": "Visi Åžmonės", @@ -283,82 +458,267 @@ "allow_edits": "Leisti redagavimus", "allow_public_user_to_download": "Leisti vieÅĄam naudotojui atsisiÅŗsti", "allow_public_user_to_upload": "Leisti vieÅĄam naudotojui įkelti", + "alt_text_qr_code": "QR kodo paveiksliukas", + "anti_clockwise": "PrieÅĄ laikrodÅžio rodykles", "api_key": "API raktas", + "api_key_description": "Å i reikÅĄmė bus parodyta tik vieną kartą. PraÅĄome nusikopijuoti prieÅĄ uÅždarant ÅĄÄ¯ langą.", "api_key_empty": "JÅĢsÅŗ API rakto pavadinimas netÅĢrėtÅŗ bÅĢti tuÅĄÄias", "api_keys": "API raktai", + "app_bar_signout_dialog_content": "Ar tikrai norite atsijungti?", + "app_bar_signout_dialog_ok": "Taip", + "app_bar_signout_dialog_title": "Atsijungti", "app_settings": "Programos nustatymai", + "appears_in": "Susiję", + "apply_count": "Taikyti ({count, number})", "archive": "Archyvas", + "archive_action_prompt": "{count} pridėta į archyvą", "archive_or_unarchive_photo": "Archyvuoti arba iÅĄarchyvuoti nuotrauką", + "archive_page_no_archived_assets": "Nerasta jokiÅŗ archyvuotÅŗ elementÅŗ", + "archive_page_title": "Archyve ({count})", "archive_size": "Archyvo dydis", "archive_size_description": "KonfigÅĢruoti archyvo dydį atsisiuntimams (GiB)", + "archived": "Archyvuota", "archived_count": "{count, plural, other {# suarchyvuota}}", "are_these_the_same_person": "Ar tai tas pats asmuo?", "are_you_sure_to_do_this": "Ar tikrai norite tai daryti?", + "asset_action_delete_err_read_only": "Negalima iÅĄtrinti tik skaitom(o, Åŗ) element(o, Åŗ), praleidÅžiama", + "asset_action_share_err_offline": "Negalima uÅžkrauti neprisijungusiÅŗ elementÅŗ, praleidÅžiama", "asset_added_to_album": "Pridėta į albumą", - "asset_adding_to_album": "Pridedama į albumą...", + "asset_adding_to_album": "Pridedama į albumąâ€Ļ", "asset_description_updated": "Elemento apraÅĄymas buvo atnaujintas", "asset_filename_is_offline": "Elementas {filename} nepasiekiamas", + "asset_has_unassigned_faces": "Elementas turi nepriskirtÅŗ veidÅŗ", + "asset_hashing": "Kuriami bylÅŗ paraÅĄaiâ€Ļ", + "asset_list_group_by_sub_title": "Grupuoti pagal", + "asset_list_layout_settings_dynamic_layout_title": "Dinaminis iÅĄdėstymas", + "asset_list_layout_settings_group_automatically": "AutomatiÅĄkai", + "asset_list_layout_settings_group_by": "Grupuoti elementus pagal", + "asset_list_layout_settings_group_by_month_day": "Mėnesis + diena", + "asset_list_layout_sub_title": "IÅĄdėstymas", + "asset_list_settings_subtitle": "NuotraukÅŗ tinklelio iÅĄdėstymo nustatymai", + "asset_list_settings_title": "NuotraukÅŗ tinklelis", "asset_offline": "Elementas nepasiekiamas", "asset_offline_description": "Å is iÅĄorinis elementas neberandamas diske. Dėl pagalbos susisiekite su savo Immich administratoriumi.", + "asset_restored_successfully": "Elementas atkurtas sėkmingai", + "asset_skipped": "Praleista", + "asset_skipped_in_trash": "Å iukÅĄliadėŞėje", + "asset_trashed": "Elementai iÅĄtrinti", + "asset_troubleshoot": "ElementÅŗ trikčiÅŗ ÅĄalinimas", "asset_uploaded": "ÄŽkelta", - "asset_uploading": "ÄŽkeliama...", + "asset_uploading": "ÄŽkeliamaâ€Ļ", + "asset_viewer_settings_subtitle": "Tvarkykite savo galerijos perÅžiÅĢros nustatymus", + "asset_viewer_settings_title": "ElementÅŗ perÅžiÅĢra", "assets": "Elementai", "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementÅŗ}}", "assets_added_to_album_count": "ÄŽ albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementÅŗ}}", - "assets_added_to_name_count": "ÄŽ {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementÅŗ}}", + "assets_added_to_albums_count": "Pridėta {assetTotal, plural, one {# elementas} few {# elementai} other {# elementÅŗ}} į {albumTotal, plural, one {# albumą} few {# albumus} other {# albumÅŗ}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Elementas negali bÅĢti pridėtas} few {Elementai negali bÅĢti pridėti} other {ElementÅŗ negali bÅĢti pridėta}} į albumą", + "assets_cannot_be_added_to_albums": "{count, plural, one {Elementas negali bÅĢti pridėtas} few {Elementai negali bÅĢti pridėti} other {ElementÅŗ negali bÅĢti pridėta}} į nei vieną albumą", "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}}", + "assets_deleted_permanently": "{count} elementÅŗ iÅĄtrinta galutinai", + "assets_deleted_permanently_from_server": "{count} elementÅŗ iÅĄtrinta galutinai iÅĄ Immich serverio", + "assets_downloaded_failed": "{count, plural, one {AtsisiÅŗstas # failas - {error} failas nepavyko} few {AtsisiÅŗsti # failai - {error} failai nepavyko} other {AtsisiÅŗsta # failÅŗ - {error} failÅŗ nepavyko}}", + "assets_downloaded_successfully": "{count, plural, one {AtsisiÅŗstas # failas sėkmingai} few {AtsisiÅŗsti # failai sėkmingai} other {AtsisiÅŗsta # failÅŗ sėkmingai}}", "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementÅŗ perkelta}} į ÅĄiukÅĄliadėŞę", "assets_permanently_deleted_count": "{count, plural, one {# elementas iÅĄtrintas} few {# elementai iÅĄtrinti} other {# elementÅŗ iÅĄtrinta}} visam laikui", "assets_removed_count": "{count, plural, one {PaÅĄalintas # elementas} few {PaÅĄalinti # elementai} other {PaÅĄalinta # elementÅŗ}}", + "assets_removed_permanently_from_device": "{count} elementÅŗ paÅĄalinta galutinai iÅĄ jÅĢsÅŗ įrenginio", "assets_restore_confirmation": "Ar tikrai norite atkurti visus ÅĄiukÅĄliadėŞėje esančius perkeltus elementus? Å io veiksmo atÅĄaukti negalėsite! Pastaba: nepasiekiami elementai tokiu bÅĢdu atkurti nebus.", "assets_restored_count": "{count, plural, one {Atkurtas # elementas} few {Atkurti # elementai} other {Atkurta # elementÅŗ}}", + "assets_restored_successfully": "{count} element(as, ai, Åŗ) atkurta sėkmingai", + "assets_trashed": "{count} element(ai,Åŗ,as) perkelta į ÅĄiukÅĄliadėŞę", + "assets_trashed_count": "Perkelta į ÅĄiukÅĄliadėŞę {count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}}", + "assets_trashed_from_server": "{count} element(as, ai, Åŗ) perkelta į ÅĄiukÅĄliadėŞę iÅĄ Immich serverio", "assets_were_part_of_album_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}} jau prieÅĄ tai buvo albume", + "assets_were_part_of_albums_count": "{count, plural, one {Elementas } few {Elementai} other {ElementÅŗ}} jau buvo albumuose", "authorized_devices": "Autorizuoti įrenginiai", + "automatic_endpoint_switching_subtitle": "Prisijungti vietoje per priskirtą Wi-Fi kai įmanoma ir naudoti alternatyvÅŗ prisijungimą visur kitur", + "automatic_endpoint_switching_title": "Automatinis URL perjungimas", + "autoplay_slideshow": "AutomatiÅĄkai rodyti skaidriÅŗ demonstraciją", "back": "Atgal", "back_close_deselect": "Atgal, uÅždaryti arba atÅžymėti", - "backup_controller_page_background_wifi": "Only on WiFi", + "background_backup_running_error": "Vyksta foninis atsarginis kopijavimas, negalima pradėti rankinio kopijavimo", + "background_location_permission": "Foninis vietovės leidimas", + "background_location_permission_content": "Veikiant fone tinklo perjungimui Immich privalo *visada* turėti prieigą prie tikslios vietovės, kad programa galėtÅŗ perskaityti Wi-Fi tinklo pavadinimą", + "background_options": "Fono nuostatos", + "backup": "Atsarginė kopija", + "backup_album_selection_page_albums_device": "AlbumÅŗ įrenginyje ({count})", + "backup_album_selection_page_albums_tap": "Palieskite įtraukti, du kart palieskite neįtraukti", + "backup_album_selection_page_assets_scatter": "Elementai gali iÅĄsibarstyti per kelis albumus. Todėl albumai gali bÅĢti įtraukti arba neįtraukti per atsarginio kopijavimo procesą.", + "backup_album_selection_page_select_albums": "PaÅžymėti albumai", + "backup_album_selection_page_selection_info": "PaÅžymėjimo informacija", + "backup_album_selection_page_total_assets": "Viso unikaliÅŗ elementÅŗ", + "backup_albums_sync": "Atsarginio kopijavimo albumÅŗ sinchronizacija", + "backup_all": "Visi", + "backup_background_service_backup_failed_message": "Nepavyko sukurti atsarginiÅŗ kopijÅŗ. Bandoma dar kartąâ€Ļ", + "backup_background_service_connection_failed_message": "Nepavyko prisijungti prie serverio. Bandoma dar kartąâ€Ļ", + "backup_background_service_current_upload_notification": "ÄŽkeliamas {filename}", + "backup_background_service_default_notification": "IeÅĄkoma naujÅŗ elementÅŗâ€Ļ", + "backup_background_service_error_title": "Atsarginio kopijavimo klaida", + "backup_background_service_in_progress_notification": "Kuriama elementÅŗ atsarginė kopijaâ€Ļ", + "backup_background_service_upload_failure_notification": "Nepavyko įkelti {filename}", + "backup_controller_page_albums": "Atsarginės kopijos albumai", + "backup_controller_page_background_app_refresh_disabled_title": "Foninis programos atnaujinimas iÅĄjungtas", + "backup_controller_page_background_app_refresh_enable_button_text": "Eiti į nustatymus", + "backup_controller_page_background_battery_info_link": "Parodyk man kaip", + "backup_controller_page_background_battery_info_message": "Norint geriausiÅŗ foninio atsarginio kopijavimo rezultatÅŗ, praÅĄome iÅĄjungti akumuliatoriaus optimizavimą ribojantį foninį Immich veikimą.\n\nKadangi tai priklauso nuo įrenginio, praÅĄome susirasti reikiamą informaciją pas įrenginio gamintoją.", + "backup_controller_page_background_battery_info_ok": "OK", + "backup_controller_page_background_battery_info_title": "Akumuliatoriaus optimizavimai", + "backup_controller_page_background_charging": "Tik kol kraunasi", + "backup_controller_page_background_configure_error": "Nepavyko sukonfigÅĢruoti foniniÅŗ paslaugÅŗ", + "backup_controller_page_background_delay": "Atidėti naujÅŗ elementÅŗ atsarginį kopijavimą: {duration}", + "backup_controller_page_background_description": "ÄŽjunkite fonines paslaugas, kad galėtumėte automatiÅĄkai kurti atsargines kopijas neatidarant programos", + "backup_controller_page_background_is_off": "Automatinis atsarginis kopijavimas yra iÅĄjungtas", + "backup_controller_page_background_is_on": "Automatinis atsarginis kopijavimas yra įjungtas", + "backup_controller_page_background_turn_off": "IÅĄjungti fonines paslaugas", + "backup_controller_page_background_turn_on": "ÄŽjungti fonines paslaugas", + "backup_controller_page_background_wifi": "Tik su Wi-Fi", + "backup_controller_page_backup": "Atsarginis kopijavimas", + "backup_controller_page_backup_selected": "Pasirinkta: ", + "backup_controller_page_backup_sub": "Perkeltos nuotraukos ir vaizdo įraÅĄai", + "backup_controller_page_created": "Sukurta: {date}", + "backup_controller_page_desc_backup": "ÄŽjunkite foninį atsarginį kopijavimą, kad bÅĢtÅŗ automatiÅĄkai perkeliami nauji elementai į serverį kai atidaroma programa.", + "backup_controller_page_excluded": "Neįtraukta: ", + "backup_controller_page_failed": "Nepavyko ({count})", + "backup_controller_page_filename": "Failo pavadinimas: {filename}[{size}]", + "backup_controller_page_id": "ID: {id}", + "backup_controller_page_info": "Atsarginio kopijavimo informacija", + "backup_controller_page_none_selected": "Niekas nepasirinkta", + "backup_controller_page_remainder": "Dar liko", + "backup_controller_page_remainder_sub": "Likusios pasirinktos atsarginio kopijavimo nuotraukos ir vaizdo įraÅĄai", + "backup_controller_page_server_storage": "Serverio saugykla", + "backup_controller_page_start_backup": "Pradėti atsarginį kopijavimą", + "backup_controller_page_status_off": "Automatinis foninis atsarginis kopijavimas yra iÅĄjungtas", + "backup_controller_page_status_on": "Automatinis foninis atsarginis kopijavimas yra įjungtas", + "backup_controller_page_storage_format": "{used} iÅĄ {total} panaudota", + "backup_controller_page_to_backup": "Albumai kuriÅŗ atsarginis kopijavimas bus atliktas", + "backup_controller_page_total_sub": "Visos unikalios nuotraukos ir video įraÅĄai iÅĄ paÅžymėtÅŗ albumÅŗ", + "backup_controller_page_turn_off": "IÅĄjungti foninį atsarginį kopijavimą", + "backup_controller_page_turn_on": "ÄŽjungti foninį atsarginį kopijavimą", + "backup_controller_page_uploading_file_info": "ÄŽkeliama failo info", + "backup_err_only_album": "Negalima paÅĄalinti vienintelio albumo", + "backup_info_card_assets": "elementai", + "backup_manual_cancelled": "AtÅĄaukta", + "backup_manual_in_progress": "Jau įkeliama, bandykite dar kartą vėliau", + "backup_manual_success": "Pavyko", + "backup_manual_title": "ÄŽkėlimo bÅĢklė", + "backup_options": "Atsarginio kopijavimo nustatymai", + "backup_options_page_title": "Atsarginio kopijavimo nustatymai", + "backup_setting_subtitle": "Tvarkyti foninio ir priekinio plano įkėlimo nustatymus", + "backup_settings_subtitle": "Tvarkyti įkėlimo nustatymus", + "backward": "Atgalinis", + "biometric_auth_enabled": "Biometrinis autentifikavimas įgalintas", + "biometric_locked_out": "JÅĢs esate uÅžblokuotas biometrinio autentifikavimo funkcijai", + "biometric_no_options": "Nėra galimÅŗ biometriniÅŗ nustatymÅŗ", + "biometric_not_available": "Biometrinis autentifikavimas ÅĄiame įrenginyje negalimas", "birthdate_saved": "Sėkmingai iÅĄsaugota gimimo data", + "birthdate_set_description": "Gimimo data naudojama apskaičiuoti asmens amÅžiÅŗ nuotraukos darymo metu.", "blurred_background": "NeryÅĄkus fonas", "bugs_and_feature_requests": "KlaidÅŗ ir funkcijÅŗ uÅžklausos", "bulk_delete_duplicates_confirmation": "Ar tikrai norite iÅĄtrinti visus {count, plural, one {# besidubliuojantį elementą} few {# besidubliuojančius elementus} other {# besidubliuojančiÅŗ elementÅŗ}}? Bus paliktas didÅžiausias kiekvienos grupės elementas ir negrįŞtamai iÅĄtrinti kiti besidubliuojantys elementai. Å io veiksmo atÅĄaukti negalėsite!", "bulk_keep_duplicates_confirmation": "Ar tikrai norite palikti visus {count, plural, one {# besidubliuojantį elementą} few {# besidubliuojančius elementus} other {# besidubliuojančiÅŗ elementÅŗ}}? Tokiu bÅĢdu nieko netrinant bus sutvarkytos visos dublikatÅŗ grupės.", "bulk_trash_duplicates_confirmation": "Ar tikrai norite perkelti į ÅĄiukÅĄliadėŞę visus {count, plural, one {# besidubliuojantį elementą} few {# besidubliuojančius elementus} other {# besidubliuojančiÅŗ elementÅŗ}}? Bus paliktas didÅžiausias kiekvienos grupės elementas ir į ÅĄiukÅĄliadėŞę perkelti kiti besidubliuojantys elementai.", "buy": "ÄŽsigyti Immich", + "cache_settings_clear_cache_button": "IÅĄvalyti laikiną talpyklą", + "cache_settings_clear_cache_button_title": "IÅĄvalo programos laikiną talpyklą. Tai gali smarkiai paveikti programos greitį, kol bus sukurta nauja laikinoji talpykla.", + "cache_settings_duplicated_assets_clear_button": "IÅ VALYTI", + "cache_settings_duplicated_assets_subtitle": "Nuotraukos ir video įraÅĄai kurie yra programos ignoruojamÅŗ sąraÅĄe", + "cache_settings_duplicated_assets_title": "Sudubliuoti elementai ({count})", + "cache_settings_statistics_album": "Bibliotekos miniatiÅĢros", + "cache_settings_statistics_full": "Pilno dydÅžio nuotraukos", + "cache_settings_statistics_shared": "BendrinamÅŗ albumÅŗ miniatiÅĢros", + "cache_settings_statistics_thumbnail": "MiniatiÅĢros", + "cache_settings_statistics_title": "Laikinos talpyklos naudojimas", + "cache_settings_subtitle": "Valdykite Immich mobiliosios programos laikinosios talpyklos elgesį", + "cache_settings_tile_subtitle": "Valdykite vietinės talpyklos elgesį", + "cache_settings_tile_title": "Vietinė talpykla", + "cache_settings_title": "Laikinosios talpyklos nustatymai", "camera": "Fotoaparatas", "camera_brand": "Fotoaparato prekės Åženklas", "camera_model": "Fotoaparato modelis", "cancel": "AtÅĄaukti", "cancel_search": "AtÅĄaukti paieÅĄką", + "canceled": "AtÅĄaukta", + "canceling": "AtÅĄaukiama", "cannot_merge_people": "Negalima sujungti asmenÅŗ", + "cannot_undo_this_action": "JÅĢs negalėsite atkurti po ÅĄio veiksmo!", "cannot_update_the_description": "Negalima atnaujinti apraÅĄymo", + "cast": "Transliuoti", + "cast_description": "Valdyti galimas transliavimo kryptis", "change_date": "Pakeisti datą", + "change_description": "Pakeisti apraÅĄymus", + "change_display_order": "Pakeisti atvaizdavimo tvarką", "change_expiration_time": "Pakeisti galiojimo trukmę", "change_location": "Pakeisti vietovę", "change_name": "Pakeisti vardą", + "change_name_successfully": "Vardas pakeistas sėkmingai", "change_password": "Pakeisti slaptaÅžodį", "change_password_description": "Tai arba pirmas kartas, kai jungiatės prie sistemos, arba buvo pateikta uÅžklausa pakeisti jÅĢsÅŗ slaptaÅžodį. PraÅĄome įvesti naują slaptaÅžodį Åžemiau.", + "change_password_form_confirm_password": "Patvirtinti slaptaÅžodį", + "change_password_form_description": "Labas {name},\n\nTai yra pirmas kartas kai tu prisijungei prie sistemos arba buvo praÅĄymas pakeisti slaptaÅžodį. PraÅĄome įvesti naują slaptaÅžodį Åžemiau.", + "change_password_form_new_password": "Naujas slaptaÅžodis", + "change_password_form_password_mismatch": "SlaptaÅžodÅžiai nesutampa", + "change_password_form_reenter_new_password": "Pakartotinai įveskite naują slaptaÅžodį", + "change_pin_code": "Pakeisti PIN kodą", "change_your_password": "Pakeisti slaptaÅžodį", "changed_visibility_successfully": "Matomumas pakeistas sėkmingai", + "charging": "Kraunasi", + "charging_requirement_mobile_backup": "Foninis kopijavimas reikalauja, kad įrenginys bÅĢtÅŗ prijungtas pakrovimui", + "check_corrupt_asset_backup": "Patikrinti sugadintÅŗ elementÅŗ atsarginę kopiją", + "check_corrupt_asset_backup_button": "Atlikti patikrinimą", + "check_corrupt_asset_backup_description": "Paleiskite ÅĄÄ¯ patikrinimą tik per Wi-Fi ir tik kai visi elementai buvo perkopijuoti. Å i procedÅĢra uÅžtruks kelias minutes.", "check_logs": "Tikrinti Åžurnalus", + "choose_matching_people_to_merge": "Pasirinkite atitinkančius Åžmones sujungimui", "city": "Miestas", "clear": "IÅĄvalyti", "clear_all": "IÅĄvalyti viską", + "clear_all_recent_searches": "IÅĄvalyti visas naujausias paieÅĄkas", + "clear_file_cache": "IÅĄvalyti failÅŗ laikiną talpyklą", "clear_message": "IÅĄvalyti praneÅĄimą", "clear_value": "IÅĄvalyti reikÅĄmę", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "ÄŽveskite slaptaÅžodį", + "client_cert_import": "Importuoti", + "client_cert_import_success_msg": "Kliento sertifikatas yra importuotas", + "client_cert_invalid_msg": "Netinkamas sertifikato failas arba neteisingas slaptaÅžodis", + "client_cert_remove_msg": "Kliento sertifikatas yra paÅĄalintas", + "client_cert_subtitle": "Palaikomi tik PKCS12 (.p12, .pfx) formatai. Sertifikato importavimas/paÅĄalinimas galimas tik prieÅĄ prisijungimą", + "client_cert_title": "SSL kliento sertifikatas", + "clockwise": "Pagal laikrodÅžio rodykles", "close": "UÅždaryti", "collapse": "Suskleisti", "collapse_all": "Suskleisti viską", + "color": "Spalva", "color_theme": "Temos spalva", "comment_deleted": "Komentaras iÅĄtrintas", "comment_options": "KomentarÅŗ parinktys", "comments_and_likes": "Komentarai ir patiktukai", "comments_are_disabled": "Komentarai yra iÅĄjungti", + "common_create_new_album": "Sukurti naują albumą", + "common_server_error": "PraÅĄome patikrinti tinklo prisijungimą ir įsitikinti, kad serveris pasiekiamas ir programos/serverio versija sutampa.", + "completed": "UÅžbaigta", "confirm": "Patvirtinti", "confirm_admin_password": "Patvirtinti administratoriaus slaptaÅžodį", + "confirm_delete_face": "Ar tikrai norite iÅĄtrinti {name} veidą iÅĄ elementÅŗ?", "confirm_delete_shared_link": "Ar tikrai norite iÅĄtrinti ÅĄią bendrinimo nuorodą?", + "confirm_keep_this_delete_others": "Visi kiti elementai iÅĄ krÅĢvos bus iÅĄtrinti iÅĄskyrus ÅĄÄ¯ elementą. Ar tikrai norite tęsti?", + "confirm_new_pin_code": "Patvirtinkite naują PIN kodą", "confirm_password": "Patvirtinti slaptaÅžodį", + "confirm_tag_face": "Ar norite priskirti ÅĄÄ¯ veidą kaip {name}?", + "confirm_tag_face_unnamed": "Ar norite priskirti ÅĄÄ¯ veidą?", + "connected_device": "Prijungtas įrenginys", + "connected_to": "Prisijungta prie", + "contain": "Tilpti", "context": "Kontekstas", "continue": "Tęsti", + "control_bottom_app_bar_create_new_album": "Sukurti naują albumą", + "control_bottom_app_bar_delete_from_immich": "IÅĄtrinti iÅĄ Immich", + "control_bottom_app_bar_delete_from_local": "IÅĄtrinti iÅĄ įrenginio", + "control_bottom_app_bar_edit_location": "Redaguoti vietovę", + "control_bottom_app_bar_edit_time": "Redaguoti datą ir laiką", + "control_bottom_app_bar_share_link": "Dalintis nuoroda", + "control_bottom_app_bar_share_to": "Dalintis su", + "control_bottom_app_bar_trash_from_immich": "Perkelti į ÅĄiukÅĄliadėŞę", "copied_image_to_clipboard": "Nuotrauka nukopijuota į iÅĄkarpinę.", "copied_to_clipboard": "Nukopijuota į iÅĄkapinę!", "copy_error": "Kopijavimo klaida", @@ -369,67 +729,140 @@ "copy_password": "Kopijuoti slaptaÅžodį", "copy_to_clipboard": "Kopijuoti į iÅĄkarpinę", "country": "Å alis", + "cover": "UÅžpildyti", + "covers": "VirÅĄeliai", "create": "Sukurti", "create_album": "Sukurti albumą", + "create_album_page_untitled": "Be pavadinimo", "create_library": "Sukurti biblioteką", "create_link": "Sukurti nuorodą", "create_link_to_share": "Sukurti bendrinimo nuorodą", "create_link_to_share_description": "Leisti bet kam su nuoroda matyti paÅžymėtą(-as) nuotrauką(-as)", + "create_new": "SUKURTI NAUJĄ", "create_new_person": "Sukurti naują ÅžmogÅŗ", "create_new_person_hint": "Priskirti pasirinktus elementus naujam Åžmogui", "create_new_user": "Sukurti naują varotoją", + "create_shared_album_page_share_add_assets": "PRIDĖTI ELEMENTŞ", + "create_shared_album_page_share_select_photos": "PaÅžymėti nuotraukas", + "create_shared_link": "Sukurti dalijimosi nuorodą", "create_tag": "Sukurti Åžymą", "create_tag_description": "Sukurti naują Åžymą. ÄŽdėtinėms Åžymoms įveskite pilną kelią, įskaitant pasviruosius brÅĢkÅĄnius.", "create_user": "Sukurti naudotoją", "created": "Sukurta", + "created_at": "Sukurta", + "creating_linked_albums": "Kuriami susieti albumai...", + "crop": "Apkirpti", + "curated_object_page_title": "Daiktai", "current_device": "Dabartinis įrenginys", + "current_pin_code": "Dabartinis PIN kodas", + "current_server_address": "Dabartinis serverio adresas", + "custom_locale": "Pasirinktinė vietovė", "custom_locale_description": "Formatuoti datas ir skaičius pagal kalbą ir regioną", + "custom_url": "Pasirinktinis URL", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", + "dark": "Tamsi", + "dark_theme": "Perjungti tamsią temą", "date_after": "Data po", "date_and_time": "Data ir laikas", "date_before": "Data prieÅĄ", + "date_format": "E, LLL d, y â€ĸ h:mm", "date_of_birth_saved": "Gimimo data sėkmingai iÅĄsaugota", + "date_range": "DatÅŗ intervalas", "day": "Diena", + "days": "DienÅŗ", "deduplicate_all": "Å alinti visus dublikatus", "deduplication_criteria_1": "Failo dydis baitais", "deduplication_criteria_2": "EXIF metaduomenÅŗ įraÅĄÅŗ skaičius", "deduplication_info": "DublikatÅŗ ÅĄalinimo informacija", "deduplication_info_description": "Automatinis elementÅŗ parinkimas ir masinis dublikatÅŗ ÅĄalinimas atliekamas atsiÅžvelgiant į:", + "default_locale": "Pradinė vietovė", "default_locale_description": "Formatuoti datas ir skaičius pagal jÅĢsÅŗ narÅĄyklės lokalę", "delete": "IÅĄtrinti", + "delete_action_confirmation_message": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ elementą? Å is veiksmas perkels elementą į serverio ÅĄiukÅĄliadėŞę ir paklaus ar norite iÅĄtrinti vietiniame įrenginyje", + "delete_action_prompt": "{count} iÅĄtrinta", "delete_album": "IÅĄtrinti albumą", "delete_api_key_prompt": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ API raktą?", + "delete_dialog_alert": "Å ie elementai bus galutinai iÅĄtrinti iÅĄ Immich ir iÅĄ jÅĢsÅŗ įrenginio", + "delete_dialog_alert_local": "Å ie elementai bus galutinai paÅĄalinti iÅĄ jÅĢsÅŗ įrenginio, bet bus prieinami Immich serveryje", + "delete_dialog_alert_local_non_backed_up": "Kai kurie elementai be Immich atsarginės kopijos ir bus galutinai paÅĄalinti iÅĄ jÅĢsÅŗ įrenginio", + "delete_dialog_alert_remote": "Å ie elementai bus galutinai iÅĄtrinti iÅĄ Immich serverio", + "delete_dialog_ok_force": "Vis tiek iÅĄtrinti", + "delete_dialog_title": "IÅĄtrinti galutinai", "delete_duplicates_confirmation": "Ar tikrai norite visam laikui iÅĄtrinti ÅĄiuos dublikatus?", + "delete_face": "IÅĄtrinti veidą", "delete_key": "IÅĄtrinti raktą", "delete_library": "IÅĄtrinti biblioteką", "delete_link": "IÅĄtrinti nuorodą", + "delete_local_action_prompt": "{count} iÅĄtrinti vietiniame įrenginyje", + "delete_local_dialog_ok_backed_up_only": "IÅĄtrinti tik turinčius atsarginę kopiją", + "delete_local_dialog_ok_force": "Vis tiek iÅĄtrinti", + "delete_others": "IÅĄtrinti kitus", + "delete_permanently": "IÅĄtrinti galutinai", + "delete_permanently_action_prompt": "{count} iÅĄtrinta galutinai", "delete_shared_link": "IÅĄtrinti bendrinimo nuorodą", + "delete_shared_link_dialog_title": "IÅĄtrinti dalijimosi nuorodą", "delete_tag": "IÅĄtrinti Åžymą", "delete_tag_confirmation_prompt": "Ar tikrai norite iÅĄtrinti Åžymą {tagName}?", "delete_user": "IÅĄtrinti naudotoją", "deleted_shared_link": "Bendrinimo nuoroda iÅĄtrinta", + "deletes_missing_assets": "IÅĄtrinti diske trÅĢkstamus elementus", "description": "ApraÅĄymas", + "description_input_hint_text": "Pridėti apraÅĄymą...", + "description_input_submit_error": "Klaida atnaujinant apraÅĄymą, pasitikrinkite Åžurnalą norint detalesnės informacijos", + "deselect_all": "AtÅžymėti visus", "details": "Detalės", "direction": "Kryptis", "disabled": "IÅĄjungta", "disallow_edits": "Neleisti redaguoti", + "discord": "Discord", "discover": "Atrasti", + "discovered_devices": "Aptikti įrenginiai", "dismiss_all_errors": "Nepaisyti visÅŗ klaidÅŗ", "dismiss_error": "Nepaisyti klaidos", + "display_options": "Atvaizdavimo parinktys", "display_order": "Atvaizdavimo tvarka", "display_original_photos": "Rodyti originalias nuotraukas", + "display_original_photos_setting_description": "Pirmenybė rodyti originalią nuotrauką vietoje miniatiÅĢros kai originalo elementas yra palaikomas narÅĄyklės. Tai gali lemti lėtesnį nuotraukos rodymo greitį.", "do_not_show_again": "Daugiau nerodyti ÅĄio praneÅĄimo", "documentation": "Dokumentacija", + "done": "Atlikta", "download": "AtsisiÅŗsti", + "download_action_prompt": "Atsisiunčiami {count} elementai", + "download_canceled": "Atsisiuntimas atÅĄauktas", + "download_complete": "Atsisiuntimas pabaigtas", + "download_enqueue": "Atsisiuntimai įtraukti į eilę", + "download_error": "Atsisiuntimo klaida", + "download_failed": "Nepavyko parsisiÅŗsti", + "download_finished": "Atsisiuntimas pabaigtas", + "download_include_embedded_motion_videos": "ÄŽterpti vaizdo įraÅĄai", + "download_include_embedded_motion_videos_description": "Pridėti prie judesio nuotraukÅŗ įterptus video kaip atskirą failą", + "download_notfound": "Atsisiuntimas nerastas", + "download_paused": "Atsisiuntimas pristabdytas", "download_settings": "AtsisiÅŗsti", + "download_settings_description": "Tvarkyti elementÅŗ atsisiuntimo nustatymus", + "download_started": "Atsisiuntimas pradėtas", + "download_sucess": "Atsisiuntimas pavyko", + "download_sucess_android": "Medija buvo atsiÅŗsta į DCIM/Immich", + "download_waiting_to_retry": "Laukiama bandymo iÅĄ naujo", "downloading": "Siunčiama", + "downloading_asset_filename": "Parsisiunčiamas resursas {filename}", + "downloading_media": "Atsisiunčiama medija", + "drop_files_to_upload": "UÅžkelkite failus bet kurioje vietoje kad įkeltumėte", "duplicates": "Dublikatai", "duplicates_description": "Sutvarkykite kiekvieną elementÅŗ grupę nurodydami elementus, kurie yra dublikatai (jei tokiÅŗ yra)", "duration": "Trukmė", "edit": "Redaguoti", "edit_album": "Redaguoti albumą", "edit_avatar": "Redaguoti avatarą", + "edit_birthday": "Redaguoti gimtadienį", "edit_date": "Redaguoti datą", "edit_date_and_time": "Redaguoti datą ir laiką", + "edit_date_and_time_action_prompt": "{count} data ir laikas redaguotas", + "edit_date_and_time_by_offset": "Keisti datą pagal poslinkį", + "edit_date_and_time_by_offset_interval": "Naujas datos intervalas: {from} - {to}", + "edit_description": "Redaguoti apraÅĄymą", + "edit_description_prompt": "PraÅĄome pasirinkti naują apraÅĄymą:", "edit_exclusion_pattern": "Redaguoti iÅĄimčiÅŗ ÅĄabloną", "edit_faces": "Redaguoti veidus", "edit_import_path": "Redaguoti importavimo kelią", @@ -437,50 +870,97 @@ "edit_key": "Redaguoti raktą", "edit_link": "Redaguoti nuorodą", "edit_location": "Redaguoti vietovę", + "edit_location_action_prompt": "{count} vietovės pakeistos", + "edit_location_dialog_title": "Vietovė", "edit_name": "Redaguoti vardą", "edit_people": "Redaguoti Åžmones", "edit_tag": "Redaguoti Åžymą", "edit_title": "Redaguoti antraÅĄtę", "edit_user": "Redaguoti naudotoją", "edited": "Redaguota", + "editor": "Redaktorius", + "editor_close_without_save_prompt": "Pakeitimai nebus iÅĄsaugoti", + "editor_close_without_save_title": "UÅždaryti redaktoriÅŗ?", + "editor_crop_tool_h2_aspect_ratios": "Vaizdo santykis", + "editor_crop_tool_h2_rotation": "Pasukimas", "email": "El. paÅĄtas", + "email_notifications": "El. paÅĄto praneÅĄimai", + "empty_folder": "Å is katalogas yra tuÅĄÄias", "empty_trash": "IÅĄtuÅĄtinti ÅĄiukÅĄliadėŞę", + "empty_trash_confirmation": "Ar tikrai norite iÅĄtuÅĄtinti ÅĄiukÅĄliadėŞę? Tai galutinai paÅĄalins elementus iÅĄ Immich.\nJÅĢs negalėsite atkurti ÅĄio veiksmo!", "enable": "ÄŽgalinti", + "enable_backup": "ÄŽgalinti atsargines kopijas", + "enable_biometric_auth_description": "ÄŽveskite savo PIN kodą biometrinės autentifikacijos įjungimui", "enabled": "ÄŽgalintas", "end_date": "Pabaigos data", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "ÄŽtraukta į eilę", + "enter_wifi_name": "ÄŽveskite Wi-Fi pavadinimą", + "enter_your_pin_code": "ÄŽveskite savo PIN kodą", + "enter_your_pin_code_subtitle": "ÄŽveskite savo PIN kodą, kad pasiektumėte uÅžrakintą aplanką", "error": "Klaida", + "error_change_sort_album": "Nepavyko pakeisti albumo rÅĢÅĄiavimo tvarkos", + "error_delete_face": "Klaida trinant veidą iÅĄ elementÅŗ", + "error_getting_places": "Klaida gaunant vietoves", "error_loading_image": "Klaida įkeliant vaizdą", + "error_loading_partners": "Klaida uÅžkraunant partnerius: {error}", + "error_saving_image": "Klaida: {error}", + "error_tag_face_bounding_box": "Klaida apraÅĄant veidą - nepavyko gauti veido vietos koordinačiÅŗ", "error_title": "Klaida - KaÅžkas nutiko ne taip", "errors": { + "cannot_navigate_next_asset": "Negalima pereiti prie sekančio elemento", + "cannot_navigate_previous_asset": "Negalima pereiti prie buvusio elemento", "cant_apply_changes": "Negalima taikyti pakeitimÅŗ", + "cant_change_activity": "Negalima {enabled, select, true {iÅĄjungti} other {įjungti}} veiklos", + "cant_change_asset_favorite": "Elementui negalima pakeisti mėgstamiausio", + "cant_change_metadata_assets_count": "Negalima pakeisti {count, plural, one {# elemento} other {# elementÅŗ}} metadata", + "cant_get_faces": "Nepavyko gauti veidus", + "cant_get_number_of_comments": "Nepavyko gauti komentarÅŗ skaičiaus", + "cant_search_people": "Negalima ieÅĄkoti ÅžmoniÅŗ", + "cant_search_places": "Negalima ieÅĄkoti vietoviÅŗ", "error_adding_assets_to_album": "Klaida pridedant elementus į albumą", "error_adding_users_to_album": "Klaida pridedant naudotojus prie albumo", + "error_deleting_shared_user": "Klaida trinant pasidalintą naudotoją", "error_downloading": "Klaida atsisiunčiant {filename}", "error_hiding_buy_button": "Klaida slepiant pirkimo mygtuką", "error_removing_assets_from_album": "Klaida ÅĄalinant elementus iÅĄ albumo, patikrinkite konsolę dėl iÅĄsamesnės informacijos", + "error_selecting_all_assets": "Klaida pasirenkant visus elementus", "exclusion_pattern_already_exists": "Å is iÅĄimčiÅŗ ÅĄablonas jau egzistuoja.", "failed_to_create_album": "Nepavyko sukurti albumo", "failed_to_create_shared_link": "Nepavyko sukurti bendrinimo nuorodos", "failed_to_edit_shared_link": "Nepavyko redaguoti bendrinimo nuorodos", + "failed_to_get_people": "Nepavyko gauti ÅžmoniÅŗ", + "failed_to_keep_this_delete_others": "Nepavyko palikti ÅĄÄ¯ elementą ir iÅĄtrinti kitus elementus", + "failed_to_load_asset": "Nepavyko uÅžkrauti elemento", + "failed_to_load_assets": "Nepavyko uÅžrauti elementÅŗ", + "failed_to_load_notifications": "Nepavyko uÅžkrauti praneÅĄimÅŗ", "failed_to_load_people": "Nepavyko uÅžkrauti ÅžmoniÅŗ", "failed_to_remove_product_key": "Nepavyko paÅĄalinti produkto rakto", + "failed_to_reset_pin_code": "Nepavyko atkurti PIN kodo", "failed_to_stack_assets": "Nepavyko sugrupuoti elementÅŗ", "failed_to_unstack_assets": "Nepavyko iÅĄgrupuoti elementÅŗ", + "failed_to_update_notification_status": "Nepavyko atnaujinti praneÅĄimo statuso", "import_path_already_exists": "Å is importavimo kelias jau egzistuoja.", "incorrect_email_or_password": "Neteisingas el. paÅĄto adresas arba slaptaÅžodis", + "paths_validation_failed": "Nepavyko {paths, plural, one {# kelio} other {# keliÅŗ}} patvirtinimas", "profile_picture_transparent_pixels": "Profilio nuotrauka negali turėti permatomÅŗ pikseliÅŗ. PraÅĄome priartinti ir/arba perkelkite nuotrauką.", "quota_higher_than_disk_size": "Nustatyta kvota, virÅĄija disko dydį", + "something_went_wrong": "KaÅžkas nepavyko", "unable_to_add_album_users": "Nepavyksta pridėti naudotojÅŗ prie albumo", "unable_to_add_assets_to_shared_link": "Nepavyko į bendrinimo nuorodą pridėti elementÅŗ", "unable_to_add_comment": "Nepavyksta pridėti komentaro", "unable_to_add_exclusion_pattern": "Nepavyksta pridėti iÅĄimčiÅŗ ÅĄablono", "unable_to_add_import_path": "Nepavyksta pridėti importavimo kelio", "unable_to_add_partners": "Nepavyksta pridėti partneriÅŗ", + "unable_to_add_remove_archive": "Nepavyko {archived, select, true {iÅĄtraukti iÅĄ} other {pridėti prie}} arcyhvo", + "unable_to_add_remove_favorites": "Nepavyko {favorite, select, true {įtraukti elemento į mėgstamiausius} other {paÅĄalinti elemento iÅĄ mėgstamiausiÅŗ}}", + "unable_to_archive_unarchive": "Nepavyko {archived, select, true {archyvuoti} other {iÅĄarchyvuoti}}", "unable_to_change_album_user_role": "Nepavyksta pakeisti albumo naudotojo rolės", "unable_to_change_date": "Negalima pakeisti datos", + "unable_to_change_description": "Nepavyko pakeisti apraÅĄymo", + "unable_to_change_favorite": "Nepavyko pakeisti elementui mėgstamiausio", "unable_to_change_location": "Negalima pakeisti vietos", "unable_to_change_password": "Negalima pakeisti slaptaÅžodÅžio", + "unable_to_change_visibility": "Nepavyko pakeisti matomumo {count, plural, one {# asmeniui} few {#asmenims} other {# asmenÅŗ}}", "unable_to_complete_oauth_login": "Nepavyko prisijungti su OAuth", "unable_to_connect": "Nepavyko prisijungti", "unable_to_copy_to_clipboard": "Negalima kopijuoti į iÅĄkarpinę, įsitikinkite, kad prie puslapio prieinate per https", @@ -489,66 +969,153 @@ "unable_to_create_library": "Nepavyko sukurti bibliotekos", "unable_to_create_user": "Nepavyko sukurti naudotojo", "unable_to_delete_album": "Nepavyksta iÅĄtrinti albumo", + "unable_to_delete_asset": "Nepavyko iÅĄtrinti elemento", + "unable_to_delete_assets": "Klaida trinant elementus", "unable_to_delete_exclusion_pattern": "Nepavyksta iÅĄtrinti iÅĄimčiÅŗ ÅĄablono", "unable_to_delete_import_path": "Nepavyksta iÅĄtrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyko iÅĄtrinti bendrinimo nuorodos", "unable_to_delete_user": "Nepavyksta iÅĄtrinti naudotojo", + "unable_to_download_files": "Nepavyksta atsisiÅŗsti failÅŗ", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti iÅĄimčiÅŗ ÅĄablono", "unable_to_edit_import_path": "Nepavyksta redaguoti iÅĄimčiÅŗ kelio", + "unable_to_empty_trash": "Nepavyko iÅĄtrinti ÅĄiukÅĄliadėŞės", "unable_to_enter_fullscreen": "Nepavyksta pereiti į viso ekrano reÅžimą", "unable_to_exit_fullscreen": "Nepavyksta iÅĄeiti iÅĄ viso ekrano reÅžimo", + "unable_to_get_comments_number": "Nepavyko gauti komentarÅŗ skaičiaus", "unable_to_get_shared_link": "Nepavyko gauti bendrinimo nuorodos", "unable_to_hide_person": "Nepavyksta paslėpti Åžmogaus", + "unable_to_link_motion_video": "Nepavyko susieti judesio video", "unable_to_link_oauth_account": "Nepavyko susieti su OAuth paskyra", "unable_to_log_out_all_devices": "Nepavyksta atjungti visÅŗ įrenginiÅŗ", "unable_to_log_out_device": "Nepavyksta atjungti įrenginio", "unable_to_login_with_oauth": "Nepavyko prisijungti su OAuth", "unable_to_play_video": "Nepavyksta paleisti vaizdo įraÅĄo", + "unable_to_reassign_assets_existing_person": "Nepavyko priskirti elementÅŗ {name, select, null {egzistuojančiam asmeniui} other {{name}}}", + "unable_to_reassign_assets_new_person": "Nepavyko priskirti elementÅŗ naujam asmeniui", "unable_to_refresh_user": "Nepavyksta atnaujinti naudotojo", + "unable_to_remove_album_users": "Nepavyko paÅĄalinti naudotojÅŗ iÅĄ albumo", "unable_to_remove_api_key": "Nepavyko paÅĄalinti API rakto", "unable_to_remove_assets_from_shared_link": "Nepavyko iÅĄ bendrinimo nuorodos paÅĄalinti elementÅŗ", "unable_to_remove_library": "Nepavyksta paÅĄalinti bibliotekos", "unable_to_remove_partner": "Nepavyksta paÅĄalinti partnerio", "unable_to_remove_reaction": "Nepavyksta paÅĄalinti reakcijos", + "unable_to_reset_password": "Nepavyko atnaujinti slaptaÅžodÅžio", + "unable_to_reset_pin_code": "Nepavyko atnaujinti PIN kodo", "unable_to_resolve_duplicate": "Nepavyko sutvarkyti dublikatÅŗ", + "unable_to_restore_assets": "Nepavyko atstatyti elementÅŗ", + "unable_to_restore_trash": "Nepavyko atstatyti iÅĄ ÅĄiukÅĄliadėŞės", + "unable_to_restore_user": "Nepavyko atstatyti naudotojo", + "unable_to_save_album": "Nepavyko iÅĄsaugoti albumo", + "unable_to_save_api_key": "Nepavyko iÅĄsaugoti API rakto", + "unable_to_save_date_of_birth": "Nepavyko iÅĄsaugoti gimimo datos", + "unable_to_save_name": "Nepavyko iÅĄsaugoti vardo", "unable_to_save_profile": "Nepavyko iÅĄsaugoti profilio", "unable_to_save_settings": "Nepavyksta iÅĄsaugoti nustatymÅŗ", "unable_to_scan_libraries": "Nepavyksta nuskaityti bibliotekÅŗ", "unable_to_scan_library": "Nepavyksta nuskaityti bibliotekos", "unable_to_set_feature_photo": "Nepavyksta nustatyti mėgstamiausios nuotraukos", "unable_to_set_profile_picture": "Nepavyksta nustatyti profilio nuotraukos", + "unable_to_submit_job": "Napvyko sukurti uÅžduoties", "unable_to_trash_asset": "Nepavyko perkelti į ÅĄiukÅĄliadėŞę", + "unable_to_unlink_account": "Nepavyko atsieti paskyrÅŗ", + "unable_to_unlink_motion_video": "Nepavyko atsieti judesio video", + "unable_to_update_album_cover": "Nepavyko atnaujinti albumo virÅĄelio", + "unable_to_update_album_info": "Nepavyko atnaujinti albumo informacijos", + "unable_to_update_library": "Nepavyko atnaujinti bibliotekos", + "unable_to_update_location": "Nepavyko atnaujinti vietovės", + "unable_to_update_settings": "Nepavyko atnaujinti nustatymÅŗ", + "unable_to_update_timeline_display_status": "Nepavyko atnaujinti laiko juostos rodymo statuso", + "unable_to_update_user": "Nepavyko atnaujinti naudotoją", "unable_to_upload_file": "Nepavyksta įkelti failo" }, + "exif": "Exif", + "exif_bottom_sheet_description": "Pridėti apraÅĄymą...", + "exif_bottom_sheet_description_error": "Klaida atnaujinant apraÅĄymą", + "exif_bottom_sheet_details": "DETALĖS", + "exif_bottom_sheet_location": "VIETOVĖ", + "exif_bottom_sheet_people": "ÅŊMONĖS", + "exif_bottom_sheet_person_add_person": "Pridėti vardą", "exit_slideshow": "IÅĄeiti iÅĄ skaidriÅŗ perÅžiÅĢros", "expand_all": "IÅĄskleisti viską", + "experimental_settings_new_asset_list_subtitle": "Dirbama", + "experimental_settings_new_asset_list_title": "ÄŽgalinti eksperimentinį nuotraukÅŗ tinklelį", + "experimental_settings_subtitle": "Naudokite savo pačiÅŗ rizika!", + "experimental_settings_title": "Eksperimentinis", + "expire_after": "Galiojimas baigiasi", "expired": "Nebegalioja", "expires_date": "Nebegalios uÅž {date}", "explore": "NarÅĄyti", + "explorer": "NarÅĄyklė", "export": "Eksportuoti", "export_as_json": "Eksportuoti kaip JSON", + "export_database": "Eksportuoti duomenÅŗ bazę", + "export_database_description": "Eksportuoti SQLite duomenÅŗ bazę", "extension": "Plėtinys", "external": "IÅĄorinis", "external_libraries": "IÅĄorinės bibliotekos", - "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": "IÅĄorinis tinklas", + "external_network_sheet_info": "Kai neprisijungta prie pageidaujamo Wi-Fi tinklo, programa jungsis prie serverio per pirmą URL nuorodą, kurią galės pasiekti, pradedant nuo virÅĄaus į apačią", "face_unassigned": "Nepriskirta", + "failed": "ÄŽvyko klaida", + "failed_to_authenticate": "Nepavyko autentifikuoti", + "failed_to_load_assets": "Nepavyko įkelti elementÅŗ", + "failed_to_load_folder": "Nepavyko įkelti katalogą", "favorite": "Mėgstamiausias", + "favorite_action_prompt": "{count} pridėta prie mėgstamiausiÅŗ", "favorite_or_unfavorite_photo": "ÄŽtraukti prie arba paÅĄalinti iÅĄ mėgstamiausiÅŗ", "favorites": "Mėgstamiausi", + "favorites_page_no_favorites": "Nerasta mėgstamiausiÅŗ elementÅŗ", + "feature_photo_updated": "Pageidaujama nuotrauka atnaujinta", "features": "Funkcijos", + "features_in_development": "KÅĢrimo funkcijos", "features_setting_description": "Valdyti aplikacijos funkcijas", "file_name": "Failo pavadinimas", "file_name_or_extension": "Failo pavadinimas arba plėtinys", + "filename": "Failopavadinimas", "filetype": "Failo tipas", + "filter": "Filtras", "filter_people": "Filtruoti Åžmones", + "filter_places": "Filtruoti vietoves", + "find_them_fast": "Raskite greitai paieÅĄkoje pagal vardą", + "first": "Pirmas", + "fix_incorrect_match": "Pataisyti neteisingą porą", + "folder": "Katalogas", + "folder_not_found": "Katalogas nerastas", "folders": "Aplankai", "folders_feature_description": "PerÅžiÅĢrėkite failÅŗ sistemoje esančias nuotraukas ir vaizdo įraÅĄus aplankÅŗ rodinyje", + "forgot_pin_code_question": "PamirÅĄote savo PIN?", + "forward": "Pirmyn", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Kad veiktÅŗ, ÅĄi funkcija įkelia iÅĄorinius „Google“ iÅĄteklius.", + "general": "Bendri", + "geolocation_instruction_location": "Paspauskite ant elemento su GPS koordinatėmis norint naudoti tą vietovę arba pasirinkite vietovę tiesiogiai Åžemėlapyje", "get_help": "Gauti pagalbos", + "get_wifiname_error": "Nepavyko gauti Wi-Fi pavadinimo. ÄŽsitikinkite, kad suteikti bÅĢtini leidimai ir esate prisijungę prie Wi-Fi tinklo", + "getting_started": "Pradedama", + "go_back": "Eiti atgal", + "go_to_folder": "Eiti į katalogą", + "go_to_search": "Eiti į paieÅĄką", + "gps": "GPS", + "gps_missing": "Be GPS", + "grant_permission": "Suteikti leidimą", "group_albums_by": "Grupuoti albumus pagal...", + "group_country": "Grupuoti pagal ÅĄalis", "group_no": "Negrupuoti", "group_owner": "Grupuoti pagal savininką", + "group_places_by": "Grupuoti vietoves pagal...", "group_year": "Grupuoti pagal metus", + "haptic_feedback_switch": "ÄŽjungti haptinį grįŞtamąjį ryÅĄÄ¯", + "haptic_feedback_title": "Haptinis grįŞtamasis ryÅĄys", "has_quota": "Turi kvotą", + "hash_asset": "Kurti bylos paraÅĄÄ… elementui", + "hashed_assets": "Elementai su bylÅŗ paraÅĄais", + "hashing": "BylÅŗ paraÅĄo kÅĢrimas", + "header_settings_add_header_tip": "Pridėti antraÅĄtę", + "header_settings_field_validator_msg": "ReikÅĄmė negali bÅĢti tuÅĄÄia", + "header_settings_header_name_input": "AntraÅĄtės pavadinimas", + "header_settings_header_value_input": "AntraÅĄtės reikÅĄmė", + "headers_settings_tile_subtitle": "ApibrÄ—Åžkite tarpinio serverio antraÅĄtes, kurias programa turėtÅŗ siÅŗsti su kiekviena tinklo uÅžklausa", + "headers_settings_tile_title": "Pasirinktinės tarpinio serverio antraÅĄtės", "hi_user": "Labas {name} ({email})", "hide_all_people": "Slėpti visus asmenis", "hide_gallery": "Slėpti galeriją", @@ -556,56 +1123,167 @@ "hide_password": "Slėpti slaptaÅžodį", "hide_person": "Slėpti asmenį", "hide_unnamed_people": "Slėpti neįvardintus asmenis", - "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_add_to_album_conflicts": "Pridėta {added} elementÅŗ į albumą {album}. {failed} elementai jau yra albume.", + "home_page_add_to_album_err_local": "Kol kas negalima pridėti vietiniÅŗ elementÅŗ į albumus, praleidÅžiama", + "home_page_add_to_album_success": "Pridėta {added} elementÅŗ į albumą {album}.", + "home_page_album_err_partner": "Kol kas negalima pridėti partnerio elementÅŗ į albumą, praleidÅžiama", + "home_page_archive_err_local": "Kol kas negalima archyvuoti vietiniÅŗ elementÅŗ, praleidÅžiama", + "home_page_archive_err_partner": "Negalima archyvuoti partnerio elementÅŗ, praleidÅžiama", + "home_page_building_timeline": "Kuriama laiko juosta", + "home_page_delete_err_partner": "Negalima iÅĄtrinti partnerio elementÅŗ, praleidÅžiama", + "home_page_delete_remote_err_local": "Vietiniai elementai iÅĄtrinant nuotolinį pasirinkimą, praleidÅžiami", + "home_page_favorite_err_local": "Kol kas negalima priskirti mėgstamiausiÅŗ vietiniÅŗ elementÅŗ, praleidÅžiama", + "home_page_favorite_err_partner": "Kol kas negalima priskirti mėgstamiausiÅŗ partnerio elementÅŗ, praleidÅžiama", + "home_page_first_time_notice": "Jei jÅĢs naudojate programą pirmą kartą, tai praÅĄome pasirinkti atsarginės kopijos albumą, kad laiko juosta galėtÅŗ tvarkyti albumo nuotraukas ir vaizdo įraÅĄus", + "home_page_locked_error_local": "Nepavyko perkelti lokaliÅŗ failÅŗ į uÅžrakintą aplanką, praleidÅžiama", + "home_page_locked_error_partner": "Nepavyko perkelti partnerio failÅŗ į uÅžrakintą aplanką, praleidÅžiama", + "home_page_share_err_local": "Negalima dalinti vietiniÅŗ elementÅŗ per nuorodą, praleidÅžiama", + "home_page_upload_err_limit": "Galima įkelti tik iki 30 elementÅŗ vienu metu, praleidÅžiama", + "host": "Å eimininkas", "hour": "Valanda", + "hours": "Valandos", + "id": "ID", + "idle": "Laisva", + "ignore_icloud_photos": "Ignoruoti iCloud nuotraukas", + "ignore_icloud_photos_description": "Nuotraukos laikomos iCloud nebus įkeltos į Immich serverį", "image": "Nuotrauka", + "image_alt_text_date": "{isVideo, select, true {Filmuota} other {Fotografuota}} {date}", + "image_alt_text_date_1_person": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1} {date}", + "image_alt_text_date_2_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1} ir {person2} {date}", + "image_alt_text_date_3_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} {date} su {person1}, {person2} ir{person3}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} {date} su {person1}, {person2} ir {additionalCount, number} kitais", + "image_alt_text_date_place": "{isVideo, select, true {Filmuota} other {Fotografuota}} {city}, {country} {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1} - {city}, {country} {date}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1} ir {person2} - {city}, {country} {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1}, {person2}, ir {person3} - {city}, {country} {date}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Filmuota} other {Fotografuota}} su {person1}, {person2}, ir {additionalCount, number} kitais - {city}, {country} {date}", + "image_saved_successfully": "Nuotrauka iÅĄsaugota", + "image_viewer_page_state_provider_download_started": "Atsisiuntimas pradėtas", + "image_viewer_page_state_provider_download_success": "Atsisiuntimas pavyko", + "image_viewer_page_state_provider_share_error": "Dalinimosi klaida", "immich_logo": "Immich logotipas", + "immich_web_interface": "Immich Web sąsaja", "import_from_json": "Importuoti iÅĄ JSON", "import_path": "Importavimo kelias", + "in_albums": "{count, plural, one {# Albume} few {#Albumuose} other {# AlbumÅŗ}}", "in_archive": "Archyve", "include_archived": "ÄŽtraukti archyvuotus", "include_shared_albums": "ÄŽtraukti bendrinamus albumus", "include_shared_partner_assets": "ÄŽtraukti partnerio pasidalintus elementus", + "individual_share": "Pavienis pasidalinimas", + "individual_shares": "Pavieniai pasidalinimai", "info": "Informacija", "interval": { "day_at_onepm": "Kiekvieną dieną 13:00", + "hours": "Kas{hours, plural, one {valandą} few {#valandas} other {{hours, number} valandÅŗ}}", "night_at_midnight": "Kiekvieną vidurnaktį", "night_at_twoam": "Kiekvieną naktį 02:00" }, + "invalid_date": "Netinkama data", + "invalid_date_format": "Netinkamas datos formatas", "invite_people": "Kviesti Åžmones", "invite_to_album": "Pakviesti į albumą", + "ios_debug_info_fetch_ran_at": "UÅžkrovimas vyko {dateTime}", + "ios_debug_info_last_sync_at": "Paskutinė sinchronizacija {dateTime}", + "ios_debug_info_no_processes_queued": "Eilėje nėra foniniÅŗ procesÅŗ", + "ios_debug_info_no_sync_yet": "Jokia background sync uÅžduotis dar nebuvo paleista", + "ios_debug_info_processes_queued": "{count, plural, one {Eilėje {count} foninis procesas} few {Eilėje {count} foniniai procesai} other {Eilėje {count} foniniÅŗ procesÅŗ}}", + "ios_debug_info_processing_ran_at": "Apdorojimas vyko {dateTime}", "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementÅŗ}}", - "jobs": "Darbai", + "jobs": "UÅžduotys", "keep": "Palikti", "keep_all": "Palikti visus", + "keep_this_delete_others": "IÅĄsaugoti ÅĄÄ¯, kitus iÅĄtrinti", + "kept_this_deleted_others": "IÅĄsaugotas ÅĄis elementas ir {count, plural, one {iÅĄtrintas # elementas} few {iÅĄtrinti # elementai} other {iÅĄtrinta # elementÅŗ}}", "keyboard_shortcuts": "Spartieji klaviatÅĢros klaviÅĄai", "language": "Kalba", + "language_no_results_subtitle": "Bandykite pakoreguoti paieÅĄkos terminą", + "language_no_results_title": "Kalbos nerastos", + "language_search_hint": "IeÅĄkoti kalbÅŗ...", "language_setting_description": "Pasirinkti pageidaujamą kalbą", + "large_files": "Dideli failai", + "last": "Paskutinis", "last_seen": "Paskutinį kartą matytas", "latest_version": "Naujausia versija", "latitude": "Platuma", "leave": "IÅĄeiti", + "leave_album": "Palikti albumą", + "lens_model": "LÄ™ÅĄiÅŗ modelis", "let_others_respond": "Leisti kitiems reaguoti", "level": "Lygis", "library": "Biblioteka", "library_options": "Bibliotekos pasirinktys", - "link_options": "NuorodÅŗ parinktys", + "library_page_device_albums": "Albumai įrenginyje", + "library_page_new_album": "Naujas albumas", + "library_page_sort_asset_count": "ElementÅŗ skaičius", + "library_page_sort_created": "KÅĢrimo data", + "library_page_sort_last_modified": "Paskutinį kartą modifikuota", + "library_page_sort_title": "Albumo pavadinimas", + "licenses": "Licencijos", + "light": "Å viesi", + "like": "Kaip", + "like_deleted": "Kaip iÅĄtrintas", + "link_motion_video": "Susieti judesio vaizdo įraÅĄÄ…", "link_to_oauth": "Susieti su OAuth", "linked_oauth_account": "Susieta OAuth paskyra", "list": "SąraÅĄas", "loading": "Kraunama", "loading_search_results_failed": "Nepavyko uÅžkrauti paieÅĄkos rezultatÅŗ", - "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": "Vietinis", + "local_asset_cast_failed": "Negalima transliuoti elemento kuris neįkeltas į serverį", + "local_assets": "Vietiniai elementai", + "local_media_summary": "Vietinės medijos santrauka", + "local_network": "Vietinis tinklas", + "local_network_sheet_info": "Programa jungsis prie serverio per ÅĄÄ¯ URL kai naudos pasirinktą Wi-Fi tinklą", + "location_permission": "Vietovės leidimai", + "location_permission_content": "Norint naudoti automatinio persijungimo opciją, Immich reikia tikslios vietovės leidimo, kad galėtÅŗ nuskaityti Wi-Fi tinklo pavadinimą", + "location_picker_choose_on_map": "Pasirinkite Åžemėlapyje", + "location_picker_latitude_error": "ÄŽveskite tinkamą platumą", + "location_picker_latitude_hint": "ÄŽveskite platumą čia", + "location_picker_longitude_error": "ÄŽveskite tinkamą ilgumą", + "location_picker_longitude_hint": "ÄŽveskite ilgumą čia", + "lock": "UÅžrakinti", + "locked_folder": "UÅžrakintas aplankas", + "log_detail_title": "ÅŊurnalo detalės", "log_out": "Atsijungti", "log_out_all_devices": "Atsijungti iÅĄ visÅŗ įrenginiÅŗ", + "logged_in_as": "Prisijungta kaip {user}", "logged_out_all_devices": "Atsijungta iÅĄ visÅŗ įrenginiÅŗ", + "logged_out_device": "Atsijungta nuo įrenginio", "login": "Prisijungti", + "login_disabled": "Prisijungimas neįgalintas", + "login_form_api_exception": "API iÅĄimtis. Patikrinkite serverio URL ir bandykite dar kartą.", + "login_form_back_button_text": "Atgal", + "login_form_email_hint": "jusupastas@email.com", + "login_form_endpoint_hint": "http://jusu-serverio-ip:port", + "login_form_endpoint_url": "Serverio galutinio taÅĄko URL", + "login_form_err_http": "PraÅĄome nurodyti http:// arba https://", + "login_form_err_invalid_email": "Neteisingas el. paÅĄtas", + "login_form_err_invalid_url": "Neteisingas URL", + "login_form_err_leading_whitespace": "Pradinis tarpas", + "login_form_err_trailing_whitespace": "Galinis tarpas", + "login_form_failed_get_oauth_server_config": "Klaida prisijungiant su OAuth, patikrinkite serverio URL", + "login_form_failed_get_oauth_server_disable": "Serveryje OAuth funkcija negalima", + "login_form_failed_login": "Klaida prisijungiant, patikrinkite serverio URL, el. paÅĄtą ir slaptaÅžodį", + "login_form_handshake_exception": "ÄŽvyko serverio patvirtinimo iÅĄimtis. Jei naudojate savarankiÅĄkai pasiraÅĄytą sertifikatą, nustatymuose įjunkite savarankiÅĄkai pasiraÅĄyto sertifikato palaikymą.", + "login_form_password_hint": "slaptaÅžodis", + "login_form_save_login": "Likti prisijungus", + "login_form_server_empty": "ÄŽveskite serverio URL.", + "login_form_server_error": "Nepavyko prisijungti prie serverio.", "login_has_been_disabled": "Prisijungimas iÅĄjungtas.", + "login_password_changed_error": "ÄŽvyko klaida atnaujinant jÅĢsÅŗ slaptaÅžodį", + "login_password_changed_success": "SlaptaÅžodis sėkmingai atnaujintas", "logout_all_device_confirmation": "Ar tikrai norite atsijungti iÅĄ visÅŗ įrenginiÅŗ?", "logout_this_device_confirmation": "Ar tikrai norite atsijungti iÅĄ ÅĄio prietaiso?", + "logs": "ÅŊurnalas", "longitude": "Ilguma", + "look": "IÅĄvaizda", "loop_videos": "Kartoti vaizdo įraÅĄus", + "loop_videos_description": "ÄŽgalinti automatinį vaizdo įraÅĄo rodymą iÅĄ naujo detaliÅŗ perÅžiÅĢroje.", + "main_branch_warning": "JÅĢs naudojate kÅĢrėjo versiją, mes stipriai rekomenduojame naudoti galutinę versiją!", + "main_menu": "Pagrindinis meniu", "make": "Gamintojas", + "manage_geolocation": "Tvarkyti vietovę", "manage_shared_links": "Bendrinimo nuorodÅŗ tvarkymas", "manage_sharing_with_partners": "Valdyti dalijimąsi su partneriais", "manage_the_app_settings": "Valdyti programos nustatymus", @@ -614,12 +1292,42 @@ "manage_your_devices": "Valdyti prijungtus įrenginius", "manage_your_oauth_connection": "Tvarkyti OAuth prisijungimą", "map": "ÅŊemėlapis", + "map_assets_in_bounds": "{count, plural, =0 {NuotraukÅŗ nėra} one {# nuotrauka} other {# nuotraukos}}", + "map_cannot_get_user_location": "Negalime gauti naudotojo vietovės", + "map_location_dialog_yes": "Taip", + "map_location_picker_page_use_location": "Naudoti ÅĄią vietovę", + "map_location_service_disabled_content": "Vietovės servisas turi bÅĢti įjungtas, kad rodytÅŗ elementus iÅĄ dabartinės vietovės. ÄŽjungti vietovės servisą?", + "map_location_service_disabled_title": "Vietovės servisas iÅĄjungtas", + "map_marker_for_images": "ÅŊemėlapio Åžymeklis nuotraukoms yra {city}, {country}", + "map_marker_with_image": "ÅŊemėlapio Åžymeklis su nuotrauka", + "map_no_location_permission_content": "Reikalingas vietovės leidimas, kad rodytÅŗ elementus iÅĄ dabartinės vietovės. Ar norite suteikti leidimą?", + "map_no_location_permission_title": "Vietovės leidimas atmestas", "map_settings": "ÅŊemėlapio nustatymai", + "map_settings_dark_mode": "Tamsi tema", + "map_settings_date_range_option_day": "Pastarosios 24 valandos", + "map_settings_date_range_option_days": "Pastarąsias {days} dienas", + "map_settings_date_range_option_year": "Pastarieji metai", + "map_settings_date_range_option_years": "Pastaruosius {years} metus", + "map_settings_dialog_title": "ÅŊemėlapio nustatymai", + "map_settings_include_show_archived": "ÄŽtraukti archyvuotus", + "map_settings_include_show_partners": "Pridėti partneriai", + "map_settings_only_show_favorites": "Rodyti tik mėgstamiausius", + "map_settings_theme_settings": "ÅŊemėlapio tema", + "map_zoom_to_see_photos": "Atitolinkite, kad matytumėte nuotraukas", + "mark_all_as_read": "PaÅžymėti viską kaip perskaitytą", + "mark_as_read": "PaÅžymėti kaip perskaitytą", + "marked_all_as_read": "Viskas paÅžymėta kaip perskaityta", "matches": "Atitikmenys", + "matching_assets": "Atitinkantys elementai", "media_type": "Laikmenos tipas", "memories": "Atsiminimai", + "memories_all_caught_up": "Jau viskas perÅžiÅĢrėta", + "memories_check_back_tomorrow": "UÅžsukite rytoj, kad pamatytumėte daugiau prisiminimÅŗ", "memories_setting_description": "Valdyti tai, ką matote savo prisiminimuose", - "memory": "Atmintis", + "memories_start_over": "Pradėti iÅĄ naujo", + "memories_swipe_to_close": "Perbraukite į virÅĄÅŗ norėdami uÅždaryti", + "memory": "Prisiminimai", + "memory_lane_title": "PrisiminimÅŗ juosta {title}", "menu": "Meniu", "merge": "Sujungti", "merge_people": "Sujungti asmenis", @@ -629,19 +1337,40 @@ "merged_people_count": "{count, plural, one {Sujungtas # asmuo} few {Sujungti # asmenys} other {Sujungta # asmenÅŗ}}", "minimize": "SumaÅžinti", "minute": "Minutė", + "minutes": "Minutės", "missing": "TrÅĢkstami", "model": "Modelis", "month": "Mėnesis", + "monthly_title_text_date_format": "MMMM y", "more": "Daugiau", + "move": "Perkelti", + "move_off_locked_folder": "IÅĄtraukti iÅĄ uÅžrakinto aplanko", + "move_to_lock_folder_action_prompt": "{count} įkelta į uÅžrakintą aplanką", + "move_to_locked_folder": "ÄŽtraukti į uÅžrakintą aplanką", + "move_to_locked_folder_confirmation": "Å ios nuotraukos ir vaizdo įraÅĄai bus paÅĄalinti iÅĄ visÅŗ albumÅŗ ir bus matomi tik uÅžrakintame aplanke", + "moved_to_archive": "{count, plural, one {# Elementas perkeltas} few {# Elementai perkelti} other {# ElementÅŗ perkelta}} į archyvą", + "moved_to_library": "{count, plural, one {# Elementas perkeltas} few {# Elementai perkelti} other {# ElementÅŗ perkelta}} į biblioteką", "moved_to_trash": "Perkelta į ÅĄiukÅĄliadėŞę", + "multiselect_grid_edit_date_time_err_read_only": "Negalima redaguoti tik skaitomo elemento datos, praleidÅžiama", + "multiselect_grid_edit_gps_err_read_only": "Negalima redaguoti tik skaitomo elemento vietovės, praleidÅžiama", + "mute_memories": "UÅžtildyti prisiminimus", "my_albums": "Mano albumai", "name": "Vardas", "name_or_nickname": "Vardas arba slapyvardis", + "network_requirement_photos_upload": "Naudoti mobilÅŗ internetą atsarginėms nuotraukÅŗ kopijoms", + "network_requirement_videos_upload": "Naudoti mobilÅŗ internetą atsarginėms vaizdo įraÅĄÅŗ kopijoms", + "network_requirements": "Tinklo reikalavimai", + "network_requirements_updated": "Tinklo reikalavimai pakeisti, atstatoma atsarginio kopijavimo eilė", + "networking_settings": "Tinklai", + "networking_subtitle": "Tvarkyti serverio galutinio taÅĄko nustatymus", "never": "Niekada", "new_album": "Naujas albumas", "new_api_key": "Naujas API raktas", "new_password": "Naujas slaptaÅžodis", "new_person": "Naujas asmuo", + "new_pin_code": "Naujas PIN kodas", + "new_pin_code_subtitle": "Tai pirmas kartas, kai naudojate uÅžrakinto aplanko funkciją. Nustatykite PIN kodą savo uÅžrakintam aplankui", + "new_timeline": "Nauja laiko juosta", "new_user_created": "Naujas naudotojas sukurtas", "new_version_available": "PRIEINAMA NAUJA VERSIJA", "newest_first": "Pirmiausia naujausi", @@ -653,29 +1382,68 @@ "no_albums_yet": "Atrodo, kad dar neturite albumÅŗ.", "no_archived_assets_message": "Suarchyvuokite nuotraukas ir vaizdo įraÅĄus, kad jie nebÅĢtÅŗ rodomi nuotraukÅŗ rodinyje", "no_assets_message": "SPUSTELĖKITE NORĖDAMI ÄŽKELTI PIRMĄJĄ NUOTRAUKĄ", + "no_assets_to_show": "Nėra rodomÅŗ elementÅŗ", + "no_cast_devices_found": "Nerasta transliavimo įrenginiÅŗ", + "no_checksum_local": "Kontrolinė suma nepasiekiama – negalima gauti vietiniÅŗ elementÅŗ", + "no_checksum_remote": "Kontrolinė suma nepasiekiama – negalima gauti nuotoliniÅŗ elementÅŗ", "no_duplicates_found": "DublikatÅŗ nerasta.", + "no_exif_info_available": "Nėra Exif informacijos", "no_explore_results_message": "ÄŽkelkite daugiau nuotraukÅŗ ir tyrinėkite savo kolekciją.", + "no_favorites_message": "Pridėti į mėgstamiausius, kad greitai rastum geriausias nuotraukas ir vaizdo įraÅĄus", "no_libraries_message": "Sukurkite iÅĄorinę biblioteką nuotraukoms ir vaizdo įraÅĄams perÅžiÅĢrėti", + "no_local_assets_found": "Nerasta jokiÅŗ vietiniÅŗ elementÅŗ su ÅĄia kontroline suma", + "no_locked_photos_message": "UÅžrakintame aplanke esančios nuotraukos ir vaizdo įraÅĄai yra paslėpti ir nematomi narÅĄant ir ieÅĄkant.", "no_name": "Be vardo", - "no_results": "Nerasta", + "no_notifications": "PraneÅĄimÅŗ nėra", + "no_people_found": "IeÅĄkomÅŗ ÅžmoniÅŗ nerasta", + "no_places": "VietoviÅŗ nėra", + "no_remote_assets_found": "Nerasta jokiÅŗ nuotoliniÅŗ elementÅŗ su ÅĄia kontroline suma", + "no_results": "RezultatÅŗ nerasta", "no_results_description": "Pabandykite sinonimą arba bendresnį raktaÅžodį", + "no_shared_albums_message": "Sukurkite nuotraukÅŗ ar vaizdo įraÅĄÅŗ albumą dalinimuisi su Åžmonėmis jÅĢsÅŗ tinkle", + "no_uploads_in_progress": "Nėra vykstančiÅŗ įkėlimÅŗ", + "not_available": "Nepasiekiamas", "not_in_any_album": "Nė viename albume", + "not_selected": "Nepasirinkta", + "note_apply_storage_label_to_previously_uploaded assets": "Pastaba: Priskirti Saugyklos ÅŊymą prie anksčiau įkeltÅŗ iÅĄtekliu, paleiskite ÅĄÄ¯", "notes": "Pastabos", + "nothing_here_yet": "Kol kas tuÅĄÄia", + "notification_permission_dialog_content": "PraneÅĄimÅŗ įgalinimui eikite į Nustatymus ir pasirinkite Leisti.", + "notification_permission_list_tile_content": "Suteikti leidimą praneÅĄimÅŗ įgalinimui.", + "notification_permission_list_tile_enable_button": "ÄŽgalinti praneÅĄimus", + "notification_permission_list_tile_title": "PraneÅĄimÅŗ leidimai", "notification_toggle_setting_description": "ÄŽjungti el. paÅĄto praneÅĄimus", "notifications": "PraneÅĄimai", "notifications_setting_description": "Tvarkyti praneÅĄimus", + "oauth": "OAuth", "official_immich_resources": "OficialÅĢs Immich iÅĄtekliai", "offline": "Neprisijungęs", + "offset": "Ofsetas", + "ok": "Ok", "oldest_first": "Seniausias pirmas", + "on_this_device": "Å iame įrenginyje", + "onboarding": "ÄŽdarbinimas", + "onboarding_locale_description": "Pasirinkite pageidaujamą kalbą. Vėliau ją galėsite pakeisti nustatymuose.", + "onboarding_privacy_description": "Sekančios (neprivalomos) funkcijos remiasi iÅĄorinėmis paslaugomis ir gali bÅĢti bet kada iÅĄjungtos nustatymuose.", + "onboarding_server_welcome_description": "Nustatykime jÅĢsÅŗ programą su daÅžniausiai naudojamais nustatymais.", + "onboarding_theme_description": "Pasirinkite temos spalvą. Vėliau galite pasikeisti ją nustatymuose.", + "onboarding_user_welcome_description": "Pradėkime!", "onboarding_welcome_user": "Sveiki atvykę, {user}", "online": "Prisijungęs", "only_favorites": "Tik mėgstamiausi", + "open": "Atverti", + "open_in_map_view": "Atverti Åžemėlapio perÅžiÅĢroje", + "open_in_openstreetmap": "Atverti per OpenStreetMap", "open_the_search_filters": "Atidaryti paieÅĄkos filtrus", "options": "Pasirinktys", "or": "arba", + "organize_into_albums": "Sutvarkyti į albumus", + "organize_into_albums_description": "Sukelti egzistuojančias nuotraukas į albumus naudojant dabartinius sinchronizavimo nustatymus", "organize_your_library": "Tvarkykite savo biblioteką", "original": "Originalas", + "other": "Kiti", "other_devices": "Kiti įrenginiai", + "other_entities": "Kiti subjektai", "other_variables": "Kiti kintamieji", "owned": "Nuosavi", "owner": "Savininkas", @@ -683,12 +1451,27 @@ "partner_can_access": "{partner} gali naudotis", "partner_can_access_assets": "Visos jÅĢsÅŗ nuotraukos ir vaizdo įraÅĄai, iÅĄskyrus archyvuotus ir iÅĄtrintus", "partner_can_access_location": "Vieta, kurioje darytos nuotraukos", + "partner_list_user_photos": "{user} nuotraukos", + "partner_list_view_all": "ÅŊiÅĢrėti viską", + "partner_page_empty_message": "JÅĢsÅŗ nuotraukomis dar nesidalinama su jokiu partneriu.", + "partner_page_no_more_users": "Nėra daugiau naudotojÅŗ pridėjimui", + "partner_page_partner_add_failed": "Nepavyko pridėti partnerio", + "partner_page_select_partner": "Pasirinkite partnerį", + "partner_page_shared_to_title": "Dalinamasi su", + "partner_page_stop_sharing_content": "{partner} daugiau nebegalės pasiekti jÅĢsÅŗ nuotraukÅŗ.", + "partner_sharing": "Dalinimasis su partneriu", "partners": "Partneriai", "password": "SlaptaÅžodis", "password_does_not_match": "SlaptaÅžodis nesutampa", "password_required": "Reikalingas slaptaÅžodis", "password_reset_success": "SlaptaÅžodis sėkmingai atkurtas", + "past_durations": { + "days": "Per {days, plural, one {pastarąją dieną} few {# pastarąsias dienas} other {# pastarÅŗjÅŗ dienÅŗ}}", + "hours": "Per {hours, plural, one {pastarąją valandą} few{# pastarąsias valandas} other {# pastarÅŗjÅŗ valandÅŗ}}", + "years": "Per {years, plural, one {pastaruosius metus} few{# pastaruosius metus} other {# pastarÅŗjÅŗ metÅŗ}}" + }, "path": "Kelias", + "pattern": "RaÅĄtas", "pause": "Sustabdyti", "pause_memories": "Pristabdyti atsiminimus", "paused": "Sustabdyta", @@ -697,19 +1480,73 @@ "people_edits_count": "{count, plural, one {Redaguotas # asmuo} few {Redaguoti # asmenys} other {Redaguota # asmenÅŗ}}", "people_feature_description": "PerÅžiÅĢrėkite nuotraukas ir vaizdo įraÅĄus sugrupuotus pagal asmenis", "people_sidebar_description": "Rodyti asmenÅŗ rodinio nuorodą ÅĄoninėje juostoje", + "permanent_deletion_warning": "IÅĄtrynimo visam laikui perspėjimas", + "permanent_deletion_warning_setting_description": "Rodyti perspėjimą kai elementas iÅĄtrinamas visam laikui", "permanently_delete": "IÅĄtrinti visam laikui", "permanently_delete_assets_count": "Visam laikui iÅĄtrinti {count, plural, one {# elementą} few {# elementus} other {# elementÅŗ}}", + "permanently_delete_assets_prompt": "Ar tikrai norite visam laikui iÅĄtrinti {count, plural, one {ÅĄitą elementą?} few {ÅĄituos # elementus?} other {ÅĄitÅŗ # elementÅŗ?}} Tuo pačiu {count, plural, one {jis bus paÅĄalintas} other {jie bus paÅĄalinti}} iÅĄ albumo.", + "permanently_deleted_asset": "VisiÅĄkai iÅĄtrinti elementai", "permanently_deleted_assets_count": "Visam laikui {count, plural, one {iÅĄtrintas # elementas} few {iÅĄtrinti # elementai} other {iÅĄtrinta # elementÅŗ}}", + "permission": "Leidimas", + "permission_empty": "JÅĢsÅŗ leidimas neturėtÅŗ bÅĢti tuÅĄÄias", + "permission_onboarding_back": "Atgal", + "permission_onboarding_continue_anyway": "Vis tiek tęsti", + "permission_onboarding_get_started": "Pradėkite", + "permission_onboarding_go_to_settings": "Eiti į nustatymus", + "permission_onboarding_permission_denied": "Leidimas nesuteiktas. Norėdami naudoti Immich, suteikite nuotraukÅŗ ir vaizdo įraÅĄÅŗ leidimus nustatymuose.", + "permission_onboarding_permission_granted": "Leidimas suteiktas! jÅĢs pasiruoÅĄÄ™.", + "permission_onboarding_permission_limited": "Leidimai apriboti. Norėdami leisti Immich kurti atsargines kopijas ir tvarkyti visą jÅĢsÅŗ galerijos kolekciją, suteikite nuotraukÅŗ ir vaizdo įraÅĄÅŗ leidimus nustatymuose.", + "permission_onboarding_request": "Immich reikalingas leidimas perÅžiÅĢrėti jÅĢsÅŗ nuotraukas ir vaizdo įraÅĄus.", + "person": "Asmuo", + "person_age_months": "{months, plural, one {# mėnesio} other {# mėnesiÅŗ}} amÅžiaus", + "person_age_year_months": "1 metÅŗ ir {months, plural, one {# mėnesio} other {# mėnesiÅŗ}} amÅžiaus", + "person_age_years": "{years, plural, other {# metÅŗ}} amÅžiaus", + "person_birthdate": "Gimė {date}", + "person_hidden": "{name}{hidden, select, true { (paslėptas)} other {}}", + "photo_shared_all_users": "PanaÅĄu, kad savo nuotraukomis pasidalijote su visais naudotojais arba neturite naudotojÅŗ, su kuriais galėtumėte jomis pasidalyti.", "photos": "Nuotraukos", "photos_and_videos": "Nuotraukos ir vaizdo įraÅĄai", "photos_count": "{count, plural, one {{count, number} nuotrauka} few {{count, number} nuotraukos} other {{count, number} nuotraukÅŗ}}", "photos_from_previous_years": "AnkstesniÅŗ metÅŗ nuotraukos", + "pick_a_location": "IÅĄsirinkite vietovę", + "pin_code_changed_successfully": "PIN kodas pakeistas sėkmingai", + "pin_code_reset_successfully": "PIN kodas sėkmingai atstatytas", + "pin_code_setup_successfully": "PIN kodas sėkmingai nustatytas", + "pin_verification": "PIN kodo patvirtinimas", "place": "Vieta", "places": "Vietos", + "places_count": "{count, plural, one {{count, number} Vieta} few{{count, number} Vietos} other {{count, number} VietÅŗ}}", + "play": "Paleisti", "play_memories": "Leisti atsiminimus", + "play_motion_photo": "Rodyti judančias nuotraukas", + "play_or_pause_video": "Rodyti arba sustabdyti vaizdo įraÅĄÄ…", + "please_auth_to_access": "PraÅĄome patvirtinti prisijungimą", + "port": "Portas", + "preferences_settings_subtitle": "Tvarkyti programos nuostatas", + "preferences_settings_title": "Nuostatos", + "preset": "Å ablonas", + "preview": "PerÅžiÅĢra", + "previous": "Buvęs", + "previous_memory": "Buvęs prisiminimas", + "previous_or_next_day": "Dieną pirmyn/atgal", + "previous_or_next_month": "Mėnesį pirmyn/atgal", + "previous_or_next_photo": "Nuotrauką pirmyn/atgal", + "previous_or_next_year": "Metus pirmyn/atgal", + "primary": "Pirminis", + "privacy": "Privatumas", + "profile": "Profilis", + "profile_drawer_app_logs": "Logai", + "profile_drawer_client_out_of_date_major": "Mobili aplikacija jau pasenusios versijos. PraÅĄome atsinaujinti į paskutinę didÅžiąją versiją.", + "profile_drawer_client_out_of_date_minor": "Mobili aplikacija jau pasenusios versijos. PraÅĄome atsinaujinti į paskutinę maŞąją versiją.", + "profile_drawer_client_server_up_to_date": "Klientas ir Serveris yra atnaujinti", + "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Tik skaitymo rÄ—Åžimas įgalintas. Ilgai paspauskite vartotojo ikoną iÅĄÄ—jimui.", + "profile_drawer_server_out_of_date_major": "Serveris jau yra pasenusios versijos. PraÅĄome atsinaujinti į paskutinę didÅžiąją versiją.", + "profile_drawer_server_out_of_date_minor": "Serveris jau yra pasenusios versijos. PraÅĄome atsinaujinti į paskutinę maŞąją versiją.", "profile_image_of_user": "{user} profilio nuotrauka", "profile_picture_set": "Profilio nuotrauka nustatyta.", "public_album": "VieÅĄas albumas", + "public_share": "VieÅĄas dilinimasis", "purchase_account_info": "Rėmėjas", "purchase_activated_subtitle": "Dėkojame, kad remiate Immich ir atviro kodo programinę įrangą", "purchase_activated_time": "Suaktyvinta {date}", @@ -724,12 +1561,13 @@ "purchase_failed_activation": "Nepavyko suaktyvinti! Patikrinkite el. paÅĄtą, ar turite teisingo produkto koda!", "purchase_individual_description_1": "Asmeniui", "purchase_individual_description_2": "Rėmėjo statusas", + "purchase_individual_title": "Asmeninis", "purchase_input_suggestion": "Turite produkto raktą? ÄŽveskite jį Åžemiau", "purchase_license_subtitle": "ÄŽsigykite „Immich“, kad palaikytumėte tolesnį paslaugos vystymą", "purchase_lifetime_description": "Pirkimas visam gyvenimui", "purchase_option_title": "PIRKIMO PASIRINKIMAS", "purchase_panel_info_1": "„Immich“ kÅĢrimas uÅžima daug laiko ir pastangÅŗ, o visą darbo dieną dirba inÅžinieriai, kad jis bÅĢtÅŗ kuo geresnis. MÅĢsÅŗ misija yra, kad atvirojo kodo programinė įranga ir etiÅĄka verslo praktika taptÅŗ tvariu kÅĢrėjÅŗ pajamÅŗ ÅĄaltiniu ir sukurtÅŗ privatumą gerbiančią ekosistemą su realiomis alternatyvomis iÅĄnaudojamoms debesijos paslaugoms.", - "purchase_panel_info_2": "Kadangi esame įsipareigoję nepridėti mokamÅŗ sienÅŗ, ÅĄis pirkinys nesuteiks jums jokiÅŗ papildomÅŗ „Immich“ funkcijÅŗ. Mes tikime, kad tokie naudotojai kaip jÅĢs palaikys nuolatinį „Immich“ vystymąsi.", + "purchase_panel_info_2": "Kadangi esame įsipareigoję nepridėti mokamÅŗ sienÅŗ, ÅĄis pirkinys nesuteiks jums jokiÅŗ papildomÅŗ Immich funkcijÅŗ. Mes tikime, kad tokie naudotojai kaip jÅĢs palaikys nuolatinį Immich vystymąsi.", "purchase_panel_title": "Palaikykite projektą", "purchase_per_server": "Vienam serveriui", "purchase_per_user": "Vienam naudotojui", @@ -744,6 +1582,8 @@ "rating": "ÄŽvertinimas ÅžvaigÅždutėmis", "rating_count": "{count, plural, one {# įvertinimas} few {# įvertinimai} other {# įvertinimÅŗ}}", "rating_description": "Rodyti EXIF įvertinimus informacijos skydelyje", + "recently_taken": "Neseniai sukurti", + "recently_taken_page_title": "Neseniai sukurti", "refresh": "Atnaujinti", "refresh_encoded_videos": "Perkrauti apdorotus vaizdo įraÅĄus", "refresh_faces": "Perkrauti veidus", @@ -760,6 +1600,9 @@ "remove_deleted_assets": "PaÅĄalinti IÅĄtrintus Elemenuts", "remove_from_album": "PaÅĄalinti iÅĄ albumo", "remove_from_favorites": "PaÅĄalinti iÅĄ mėgstamiausiÅŗ", + "remove_from_lock_folder_action_prompt": "{count} iÅĄtraukta iÅĄ uÅžrakinto aplanko", + "remove_from_locked_folder": "IÅĄimti iÅĄ uÅžrakinto aplanko", + "remove_from_locked_folder_confirmation": "Ar tikrai norite perkelti ÅĄias nuotraukas ir vaizdo įraÅĄus iÅĄ uÅžrakinto aplanko? Jie taps matomi jÅĢsÅŗ galerijoje.", "remove_from_shared_link": "PaÅĄalinti iÅĄ bendrinimo nuorodos", "remove_user": "PaÅĄalinti naudotoją", "removed_api_key": "PaÅĄalintas API Raktas: {name}", @@ -819,6 +1662,7 @@ "search_no_more_result": "Nėra daugiau rezultatÅŗ", "search_no_people_named": "Nėra ÅžmoniÅŗ vardu „{name}“", "search_page_screenshots": "Ekrano nuotraukos", + "search_page_search_photos_videos": "IeÅĄkokite nuotraukÅŗ ir vaizdo įraÅĄÅŗ", "search_page_selfies": "Asmenukės", "search_page_things": "Dalykai", "search_page_view_all_button": "PerÅžiÅĢrėti visus", @@ -862,7 +1706,11 @@ "setting_image_viewer_preview_title": "UÅžkrauti perÅžiÅĢros nuotrauką", "setting_image_viewer_title": "Nuotraukos", "setting_languages_apply": "Pritaikyti", + "setting_notifications_notify_failures_grace_period": "Informuoti apie foninio atsarginio kopijavimo nesėkmes: {duration}", + "setting_notifications_notify_hours": "{count} valandÅŗ", + "setting_notifications_notify_minutes": "{count} minučiÅŗ", "setting_notifications_notify_never": "niekada", + "setting_notifications_notify_seconds": "{count} sekundÅžiÅŗ", "setting_notifications_single_progress_subtitle": "Detali įkėlimo progreso informacija kiekvienam elementui", "settings": "Nustatymai", "settings_require_restart": "PraÅĄome perkrauti Immich, siekiant pritaikyti ÅĄÄ¯ nustatymą", @@ -870,13 +1718,29 @@ "setup_pin_code": "Nustatyti PIN kodą", "share": "Dalintis", "share_add_photos": "ÄŽtraukti nuotraukÅŗ", + "share_assets_selected": "{count} paÅžymėta", "share_dialog_preparing": "RuoÅĄiama...", "share_link": "Bendrinti nuorodą", "shared": "Bendrinami", "shared_by_user": "Bendrina {user}", "shared_by_you": "Bendrinama jÅĢsÅŗ", "shared_from_partner": "Nuotraukos iÅĄ {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} ÄŽkelta", "shared_link_clipboard_copied_massage": "Nukopijuota į iÅĄkarpinę", + "shared_link_clipboard_text": "Nuoroda: {link}\nSlaptaÅžodis: {password}", + "shared_link_edit_expire_after_option_days": "{count} dienÅŗ", + "shared_link_edit_expire_after_option_hours": "{count} valandÅŗ", + "shared_link_edit_expire_after_option_minutes": "{count} minučiÅŗ", + "shared_link_edit_expire_after_option_months": "{count} mėnesiÅŗ", + "shared_link_edit_expire_after_option_year": "{count} metÅŗ", + "shared_link_expires_day": "Galiojimas baigsis uÅž {count} dienos", + "shared_link_expires_days": "Galiojimas baigsis uÅž {count} dienÅŗ", + "shared_link_expires_hour": "Galiojimas baigsis uÅž {count} valandos", + "shared_link_expires_hours": "Galiojimas baigsis uÅž {count} valandÅŗ", + "shared_link_expires_minute": "Galiojimas baigsis uÅž {count} minutės", + "shared_link_expires_minutes": "Galiojimas baigsis uÅž {count} minučiÅŗ", + "shared_link_expires_second": "Galiojimas baigsis uÅž {count} sekundės", + "shared_link_expires_seconds": "Galiojimas baigsis uÅž {count} sekundÅžiÅŗ", "shared_link_options": "Bendrinimo nuorodos parametrai", "shared_links": "Bendrinimo nuorodos", "shared_photos_and_videos_count": "{assetCount, plural, one {# bendrinama nuotrauka ir vaizdo įraÅĄas} few {# bendrinamos nuotraukos ir vaizdo įraÅĄai} other {# bendrinamÅŗ nuotraukÅŗ ir vaizdo įraÅĄÅŗ}}", @@ -893,7 +1757,7 @@ "show_albums": "Rodyti albumus", "show_all_people": "Rodyti visus asmenis", "show_and_hide_people": "Rodyti ir paslėpti Åžmones", - "show_file_location": "Rodyti rinkmenos vietą", + "show_file_location": "Rodyti failo vietą", "show_gallery": "Rodyti galeriją", "show_hidden_people": "Rodyti paslėptus asmenis", "show_in_timeline": "Rodyti laiko skalėje", @@ -936,6 +1800,7 @@ "status": "Statusas", "stop_casting": "Nutraukti transliavimą", "storage": "Saugykla", + "storage_label": "Saugyklos ÅŊyma", "storage_usage": "Naudojama {used} iÅĄ {available}", "submit": "Pateikti", "suggestions": "PasiÅĢlymai", @@ -955,6 +1820,7 @@ "template": "Å ablonas", "theme": "Tema", "theme_selection": "Temos pasirinkimas", + "theme_setting_asset_list_tiles_per_row_title": "ElementÅŗ per eilutę ({count})", "theme_setting_primary_color_title": "Pagrindinė spalva", "theme_setting_system_primary_color_title": "Naudoti sistemos spalvą", "theme_setting_system_theme_switch": "Automatinė (Naudoti sistemos nustatymus)", @@ -973,8 +1839,10 @@ "trash_no_results_message": "ÄŽ ÅĄiukÅĄliadėŞę perkeltos nuotraukos ir vaizdo įraÅĄai bus rodomi čia.", "trash_page_delete_all": "IÅĄtrinti Visus", "trash_page_empty_trash_dialog_content": "Ar norite iÅĄtrinti iÅĄmestus elementus? Å ie elementai bus visam laikui paÅĄalinti iÅĄ Immich", + "trash_page_info": "Å iukÅĄliadėŞės elementai bus galutinai iÅĄtrinti uÅž {days} dienÅŗ", "trash_page_no_assets": "Nėra iÅĄmestÅŗ elementÅŗ", "trash_page_restore_all": "Atkurti Visus", + "trash_page_title": "Å iukÅĄliÅŗ ({count})", "trashed_items_will_be_permanently_deleted_after": "ÄŽ ÅĄiukÅĄliadėŞę perkelti elementai bus visam laikui iÅĄtrinti po {days, plural, one {# dienos} other {# dienÅŗ}}.", "type": "Tipas", "unarchive": "IÅĄarchyvuoti", @@ -1009,7 +1877,8 @@ "upload_success": "ÄŽkėlimas pavyko, norėdami pamatyti naujai įkeltus elementus perkraukite puslapį.", "upload_to_immich": "ÄŽkelti į Immich ({count})", "uploading": "ÄŽkeliama", - "usage": "Naudojymas", + "url": "URL", + "usage": "Naudojimas", "use_biometric": "Naudoti biometriją", "use_current_connection": "naudoti dabartinį ryÅĄÄ¯", "user": "Naudotojas", diff --git a/i18n/lv.json b/i18n/lv.json index 3fcf228612..db02cea147 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -14,6 +14,7 @@ "add_a_location": "Pievienot atraÅĄanās vietu", "add_a_name": "Pievienot vārdu", "add_a_title": "Pievienot virsrakstu", + "add_birthday": "Pievienot dzimÅĄanas dienu", "add_endpoint": "Pievienot galapunktu", "add_exclusion_pattern": "Pievienot izslēgÅĄanas ÅĄablonu", "add_import_path": "Pievienot importa ceÄŧu", @@ -22,10 +23,14 @@ "add_partner": "Pievienot partneri", "add_path": "Pievienot ceÄŧu", "add_photos": "Pievienot fotoattēlus", + "add_tag": "Pievienot atzÄĢmi", "add_to": "Pievienotâ€Ļ", "add_to_album": "Pievienot albumam", "add_to_album_bottom_sheet_added": "Pievienots {album}", "add_to_album_bottom_sheet_already_exists": "Jau pievienots {album}", + "add_to_album_toggle": "Pārslēgt izvēli {album}", + "add_to_albums": "Pievienot albumiem", + "add_to_albums_count": "Pievienot albumiem ({count})", "add_to_shared_album": "Pievienot koplietotam albumam", "add_url": "Pievienot URL", "added_to_archive": "Pievienots arhÄĢvam", @@ -34,69 +39,116 @@ "admin": { "add_exclusion_pattern_description": "Pievienojiet izlaiÅĄanas shēmas. AizstājējzÄĢmju izmantoÅĄa *, **, un ? tiek atbalstÄĢta. Lai ignorētu visus failus jebkurā direktorijā ar nosaukumu “RAW”, izmantojiet “**/RAW/**”. Lai ignorētu visus failus, kas beidzas ar “. tif”, izmantojiet “**/*. tif”. Lai ignorētu absolÅĢto ceÄŧu, izmantojiet “/path/to/ignore/**”.", "admin_user": "Administrators", - "asset_offline_description": "Å is ārējās bibliotēkas resurss vairs nav atrodams diskā un ir pārvietots uz atkritumu grozu. Ja fails tika pārvietots bibliotēkas ietvaros, pārbaudiet, vai jÅĢsu hronoloÄŖijā ir jauns atbilstoÅĄais resurss. Lai atjaunotu ÅĄo resursu, pārliecinieties, vai Immich var piekÄŧÅĢt tālāk norādÄĢtajam faila ceÄŧam un skenēt bibliotēku.", + "asset_offline_description": "Å is ārējās bibliotēkas resurss vairs nav atrodams diskā un ir pārvietots uz atkritni. Ja fails tika pārvietots bibliotēkas ietvaros, pārbaudi, vai jÅĢsu laika skalā ir jauns atbilstoÅĄais resurss. Lai atjaunotu ÅĄo resursu, pārliecinies, vai Immich var piekÄŧÅĢt tālāk norādÄĢtajam faila ceÄŧam un uzsāc bibliotēkas skenÄ“ÅĄanu.", "authentication_settings": "Autentifikācijas iestatÄĢjumi", "authentication_settings_description": "ParoÄŧu, OAuth un citu autentifikācijas iestatÄĢjumu pārvaldÄĢba", "authentication_settings_disable_all": "Vai tieÅĄÄm vēlaties atspējot visas pieteikÅĄanās metodes? PieteikÅĄanās tiks pilnÄĢbā atspējota.", "authentication_settings_reenable": "Lai atkārtoti iespējotu, izmantojiet Servera Komandu.", - "background_task_job": "Fona Uzdevumi", - "backup_settings_description": "Datubāzes dublÄ“ÅĄanas iestatÄĢjumu pārvaldÄĢba", + "background_task_job": "Fona uzdevumi", + "backup_database": "Izveidot datu bāzes izrakstu", + "backup_database_enable_description": "Iespējot datu bāzes izrakstus", + "backup_keep_last_amount": "IepriekÅĄÄ“jo izrakstu daudzums, kas jāsaglabā", + "backup_onboarding_1_description": "ārēja kopija mākonÄĢ vai citā fiziskā atraÅĄanās vietā.", + "backup_onboarding_2_description": "vietējās kopijas citās ierÄĢcēs. Tas ietver galvenos failus un ÅĄo failu vietējo rezerves kopiju.", + "backup_onboarding_title": "Rezerves kopijas", + "backup_settings": "Datubāzes izrakstu iestatÄĢjumi", + "backup_settings_description": "Datubāzes izrakstu iestatÄĢjumu pārvaldÄĢba", "cleared_jobs": "NotÄĢrÄĢti uzdevumi priekÅĄ: {job}", "config_set_by_file": "Konfigurāciju paÅĄlaik iestata konfigurācijas fails", "confirm_delete_library": "Vai tieÅĄÄm vēlaties dzēst {library} bibliotēku?", "confirm_email_below": "Lai apstiprinātu, zemāk ierakstiet “{email}”", - "confirm_reprocess_all_faces": "Vai tieÅĄÄm vēlaties atkārtoti apstrādāt visas sejas? Tas arÄĢ atiestatÄĢs cilvēkus ar vārdiem.", + "confirm_reprocess_all_faces": "Vai tieÅĄÄm vēlies atkārtoti apstrādāt visas sejas? Tas arÄĢ atiestatÄĢs personas ar vārdiem.", "confirm_user_password_reset": "Vai tieÅĄÄm vēlaties atiestatÄĢt lietotāja {user} paroli?", "create_job": "Izveidot uzdevumu", "cron_expression": "Cron izteiksme", "disable_login": "Atspējot pieteikÅĄanos", - "duplicate_detection_job_description": "Palaidiet maÅĄÄĢnmācÄĢÅĄanos uz failiem, lai noteiktu lÄĢdzÄĢgus attēlus. PaÄŧaujas uz viedo meklÄ“ÅĄanu", + "duplicate_detection_job_description": "Analizēt failus ar maÅĄÄĢnmācÄĢÅĄanos, lai noteiktu lÄĢdzÄĢgus attēlus. Å ÄĢ funkcija izmanto viedo meklÄ“ÅĄanu", "external_library_management": "Ārējo bibliotēku pārvaldÄĢba", "face_detection": "Seju noteikÅĄana", + "face_detection_description": "AtpazÄĢt attēlos sejas, izmantojot maÅĄÄĢnmācÄĢÅĄanos. Video gadÄĢjumā tiek ņemta vērā tikai sÄĢktēls. \"Atsvaidzināt\" atkārtoti apstrādā visus attēlus. \"AtiestatÄĢt\" izdzÄ“ÅĄ visus paÅĄreizējos seju datus. \"TrÅĢkstoÅĄie\" ierindo attēlus, kas vēl nav apstrādāti. Pēc seju noteikÅĄanas pabeigÅĄanas atrastās sejas tiek ierindotas seju atpazÄĢÅĄanai, grupējot tās pēc esoÅĄas vai jauns personas.", + "facial_recognition_job_description": "Grupēt atpazÄĢtās sejas pēc cilvēkiem. Å is solis tiek veikts pēc seju noteikÅĄanas pabeigÅĄanas. \"AtiestatÄĢt\" atkārtoti sagrupē visas sejas. \"TrÅĢkstoÅĄie\" ierindo sejas, kurām nav pieÅĄÄˇirta persona.", "image_format": "Formāts", "image_format_description": "WebP veido mazākus failus nekā JPEG, taču to kodÄ“ÅĄana ir lēnāka.", - "image_fullsize_enabled_description": "Äĸenerēt pilna izmēra attēlu formātiem, kas nav piemēroti izmantoÅĄanai tÄĢmeklÄĢ. Ja ir iespējota opcija \"PriekÅĄroka iegultajam priekÅĄskatÄĢjumam\", tiks izmantoti iegultie priekÅĄskatÄĢjumi bez konvertÄ“ÅĄanas. Neietekmē tÄĢmeklim draudzÄĢgus formātus, piemēram, JPEG.", + "image_fullsize_description": "Pilnizmēra attēls ar noņemtiem metadatiem, ko izmanto, kad attēls ir tuvināts", + "image_fullsize_enabled": "Iespējot pilnizmēra attēlu ÄŖenerÄ“ÅĄanu", + "image_fullsize_enabled_description": "Äĸenerēt pilnizmēra attēlu formātiem, kas nav piemēroti izmantoÅĄanai tÄĢmeklÄĢ. Ja ir iespējota opcija \"PriekÅĄroka iegultajam priekÅĄskatÄĢjumam\", tiks izmantoti iegultie priekÅĄskatÄĢjumi bez konvertÄ“ÅĄanas. Neietekmē tÄĢmeklim draudzÄĢgus formātus, piemēram, JPEG.", "image_fullsize_quality_description": "Pilnizmēra attēlu kvalitāte no 1-100. Augstāka vērtÄĢba dos labāku kvalitāti, taču faili bÅĢs lielāka izmēra.", "image_fullsize_title": "Pilnizmēra attēlu iestatÄĢjumi", "image_prefer_embedded_preview": "PriekÅĄroka iegultajam priekÅĄskatÄĢjumam", - "image_prefer_embedded_preview_setting_description": "Izmantot RAW fotoattēlos iestrādātos priekÅĄskatÄĢjumus, ja tādi ir pieejami, kā ievades datus attēlu apstrādei. Tādējādi daÅžiem attēliem var iegÅĢt precÄĢzākas krāsas, taču priekÅĄskatÄĢjuma kvalitāte ir atkarÄĢga no fotokameras un attēlam var bÅĢt vairāk saspieÅĄanas artefaktu.", + "image_prefer_embedded_preview_setting_description": "Izmanto RAW fotoattēlos iestrādātos priekÅĄskatÄĢjumus, ja tādi ir pieejami, kā ievades datus attēlu apstrādei. Tādējādi daÅžiem attēliem var iegÅĢt precÄĢzākas krāsas, taču priekÅĄskatÄĢjuma kvalitāte ir atkarÄĢga no fotokameras un attēlam var bÅĢt vairāk saspieÅĄanas artefaktu.", + "image_prefer_wide_gamut_setting_description": "SÄĢktēliem izmanto Display P3. Tas labāk saglabā attēlu dzÄĢvÄĢgumu ar plaÅĄu krāsu gammu, bet attēli var izskatÄĢties atÅĄÄˇirÄĢgi vecās ierÄĢcēs ar vecu pārlÅĢka versiju. sRGB attēli tiek saglabāti kā sRGB, lai izvairÄĢtos no krāsu izmaiņām.", + "image_preview_title": "PriekÅĄskatÄĢjuma iestatÄĢjumi", "image_quality": "Kvalitāte", "image_resolution": "IzÅĄÄˇirtspēja", - "image_settings": "Attēla IestatÄĢjumi", + "image_settings": "Attēlu iestatÄĢjumi", "image_settings_description": "Äĸenerēto attēlu kvalitātes un izÅĄÄˇirtspējas pārvaldÄĢba", + "image_thumbnail_description": "Neliels sÄĢktēls bez metadatiem, ko izmanto, lai apskatÄĢtu vairākus fotoattēlus, piemēram, galvenajā laika skalā", "image_thumbnail_title": "SÄĢktēlu iestatÄĢjumi", "job_concurrency": "{job} vienlaicÄĢgi", "job_created": "Uzdevums izveidots", "job_settings": "Uzdevumu iestatÄĢjumi", "job_settings_description": "Uzdevumu izpildes vienlaicÄĢguma pārvaldÄĢba", "job_status": "Uzdevumu statuss", + "library_created": "Izveidoja bibliotēku: {library}", "library_deleted": "Bibliotēka dzēsta", "library_scanning": "Periodiska skenÄ“ÅĄana", "library_scanning_description": "Konfigurē periodisku bibliotēku skenÄ“ÅĄanu", "library_scanning_enable_description": "Iespējot periodisku bibliotēku skenÄ“ÅĄanu", "library_settings": "Ārējā bibliotēka", "library_settings_description": "Ārējo bibliotēku iestatÄĢjumu pārvaldÄĢba", + "library_tasks_description": "PārbaudÄĢt ārējās bibliotēkas, lai atrastu jaunus un/vai mainÄĢtus failus", "library_watching_settings": "Bibliotēku uzraudzÄĢÅĄana (EKSPERIMENTĀLA)", "library_watching_settings_description": "Automātiski uzraudzÄĢt, vai ir mainÄĢti faili", + "machine_learning_availability_checks_enabled": "Iespējot pieejamÄĢbas pārbaudes", "machine_learning_clip_model": "CLIP modelis", "machine_learning_duplicate_detection": "Dublikātu noteikÅĄana", + "machine_learning_duplicate_detection_enabled": "Iespējot dublikātu noteikÅĄanu", + "machine_learning_duplicate_detection_enabled_description": "Ja ÅĄÄĢ funkcija ir atspējota, joprojām tiks izlaisti identiski faili.", + "machine_learning_enabled": "Iespējot maÅĄÄĢnmācÄĢÅĄanos", + "machine_learning_enabled_description": "Ja funkcija ir atspējota, tiks atspējotas visas ML funkcijas neatkarÄĢgi no zemāk esoÅĄajiem iestatÄĢjumiem.", "machine_learning_facial_recognition": "Seju atpazÄĢÅĄana", "machine_learning_facial_recognition_model": "Seju atpazÄĢÅĄanas modelis", + "machine_learning_facial_recognition_setting": "Iespējot seju atpazÄĢÅĄanu", "machine_learning_settings": "MaÅĄÄĢnmācÄĢÅĄanās iestatÄĢjumi", "machine_learning_settings_description": "MaÅĄÄĢnmācÄĢÅĄanās funkciju un iestatÄĢjumu pārvaldÄĢba", "machine_learning_smart_search": "Viedā meklÄ“ÅĄana", - "machine_learning_url_description": "MaÅĄÄĢnmācÄĢÅĄanās servera URL", + "machine_learning_smart_search_enabled": "Iespējot viedo meklÄ“ÅĄanu", + "machine_learning_smart_search_enabled_description": "Ja funkcija ir atspējota, attēli netiks kodēti viedai meklÄ“ÅĄanai.", + "machine_learning_url_description": "MaÅĄÄĢnmācÄĢÅĄanās servera URL. Ja ir norādÄĢts vairāk nekā viens URL, katrs serveris, sākot no pirmā lÄĢdz pēdējam, tiks pārbaudÄĢts pa vienam, lÄĢdz kāds no tiem atbildēs veiksmÄĢgi. Serveri, kas neatbild, tiks ÄĢslaicÄĢgi ignorēti, lÄĢdz tie atkal bÅĢs pieejami tieÅĄsaistē.", "manage_concurrency": "VienlaicÄĢgas darbÄĢbas pārvaldÄĢba", "manage_log_settings": "ÅŊurnāla iestatÄĢjumu pārvaldÄĢba", + "map_dark_style": "TumÅĄais stils", "map_gps_settings": "Kartes un GPS iestatÄĢjumi", "map_gps_settings_description": "KarÅĄu un GPS (apgrieztās ÄŖeokodÄ“ÅĄanas) iestatÄĢjumu pārvaldÄĢba", + "map_light_style": "GaiÅĄais stils", "map_manage_reverse_geocoding_settings": "Reversās ÄŖeokodÄ“ÅĄanas iestatÄĢjumu pārvaldÄĢba", + "map_reverse_geocoding": "Reversā ÄŖeokodÄ“ÅĄana", + "map_reverse_geocoding_settings": "Reversās ÄŖeokodÄ“ÅĄanas iestatÄĢjumi", "map_settings": "Karte", "map_settings_description": "Kartes iestatÄĢjumu pārvaldÄĢba", + "map_style_description": "URL uz style.json kartes tēmu", + "memory_generate_job": "Atmiņu ÄŖenerÄ“ÅĄana", "metadata_extraction_job": "Metadatu iegÅĢÅĄana", + "metadata_extraction_job_description": "iegÅĢt metadatu informāciju no katra faila, piemēram, GPS, sejas un izÅĄÄˇirtspēju", + "metadata_faces_import_setting_description": "Importēt sejas no attēla EXIF datiem un blakusfailiem", "metadata_settings": "Metadatu iestatÄĢjumi", "metadata_settings_description": "Metadatu iestatÄĢjumu pārvaldÄĢba", "migration_job": "Migrācija", + "migration_job_description": "Pārvietot failu un seju sÄĢktēlus uz jaunāko mapju struktÅĢru", + "nightly_tasks_cluster_faces_setting_description": "Veikt sejas atpazÄĢÅĄanu jaunatklātajām sejām", + "nightly_tasks_cluster_new_faces_setting": "Sagrupēt jaunās sejas", + "nightly_tasks_database_cleanup_setting": "Datubāzes apkopes uzdevumi", + "nightly_tasks_database_cleanup_setting_description": "Dzēst vecus, neaktuālus datus no datubāzes", + "nightly_tasks_generate_memories_setting": "Veidot atmiņas", + "nightly_tasks_generate_memories_setting_description": "Veidot jaunas atmiņas no failiem", + "nightly_tasks_missing_thumbnails_setting": "Äĸenerēt trÅĢkstoÅĄos sÄĢktēlus", + "nightly_tasks_missing_thumbnails_setting_description": "Ierindot failus bez sÄĢktēliem sÄĢktēlu ÄŖenerÄ“ÅĄanai", + "nightly_tasks_settings": "Iknakts uzdevumu iestatÄĢjumi", + "nightly_tasks_settings_description": "PārvaldÄĢt iknakts uzdevumus", + "nightly_tasks_start_time_setting": "Sākuma laiks", + "nightly_tasks_start_time_setting_description": "Laiks, kad serveris sāk izpildÄĢt iknakts uzdevumus", + "nightly_tasks_sync_quota_usage_setting": "Sinhronizēt kvotas izmantoÅĄanu", + "nightly_tasks_sync_quota_usage_setting_description": "Pārrēķināt lietotāja uzglabÄÅĄanas kvotu, pamatojoties uz paÅĄreizējo izmantoÅĄanu", "no_paths_added": "Nav pievienots neviens ceÄŧÅĄ", "no_pattern_added": "Nav pievienots neviens izslēgÅĄanas ÅĄablons", "note_cannot_be_changed_later": "PIEZÄĒME: Vēlāk to vairs nevar mainÄĢt!", @@ -110,6 +162,10 @@ "notification_email_test_email_sent": "Uz {email} ir nosÅĢtÄĢts testa e-pasts. LÅĢdzu, pārbaudi savu iesÅĢtni.", "notification_settings": "Paziņojumu iestatÄĢjumi", "notification_settings_description": "Paziņojumu iestatÄĢjumu, tostarp e-pasta, pārvaldÄĢba", + "oauth_auto_launch": "Palaist automātiski", + "oauth_auto_launch_description": "Pie navigācijas uz pieslēgÅĄanās lapu automātiski uzsākt OAuth pieslēgÅĄanās plÅĢsmu", + "oauth_auto_register": "Automātiska reÄŖistrācija", + "oauth_auto_register_description": "Pēc pieslēgÅĄanās ar OAuth automātiski reÄŖistrēt jaunus lietotājus", "oauth_button_text": "Pogas teksts", "oauth_enable_description": "Pieslēgties ar OAuth", "oauth_settings": "OAuth", @@ -118,28 +174,49 @@ "password_enable_description": "PieteikÅĄanās ar e-pasta adresi un paroli", "password_settings": "PieteikÅĄanās ar paroli", "password_settings_description": "PieteikÅĄanās ar paroli iestatÄĢjumu pārvaldÄĢba", + "paths_validated_successfully": "Visi ceÄŧi veiksmÄĢgi pārbaudÄĢti", "person_cleanup_job": "Personu tÄĢrÄĢÅĄana", "quota_size_gib": "Kvotas izmērs (GiB)", + "refreshing_all_libraries": "Atsvaidzina visas bibliotēkas", "registration": "Administratora reÄŖistrācija", + "registration_description": "Tā kā tu esi pirmais sistēmas lietotājs, tev tiks pieÅĄÄˇirts administratora statuss un tu bÅĢsi atbildÄĢgs par administrÄ“ÅĄanas uzdevumiem, kā arÄĢ par citu lietotāju izveidi.", "require_password_change_on_login": "PieprasÄĢt lietotājam mainÄĢt paroli pēc pirmās pieteikÅĄanās", + "reset_settings_to_default": "Atjaunot iestatÄĢjumus uz noklusējuma vērtÄĢbām", + "reset_settings_to_recent_saved": "Atjaunot iestatÄĢjumus uz pēdējiem saglabātajiem iestatÄĢjumiem", "scanning_library": "Skenē bibliotēku", "search_jobs": "Meklēt uzdevumusâ€Ļ", + "send_welcome_email": "NosÅĢtÄĢt sveiciena e-pastu", + "server_external_domain_settings": "Ārējais domēns", + "server_external_domain_settings_description": "Domēns publiski kopÄĢgotajām saitēm, iekÄŧaujot http(s)://", + "server_public_users": "Publiski lietotāji", "server_settings": "Servera iestatÄĢjumi", "server_settings_description": "Servera iestatÄĢjumu pārvaldÄĢba", "server_welcome_message": "Sveiciena ziņa", "server_welcome_message_description": "Ziņojums, kas tiek parādÄĢts pieslēgÅĄanās lapā.", + "sidecar_job": "Blakusfailu metadati", + "sidecar_job_description": "Atklāt vai sinhronizēt blakusfailu metadatus no failu sistēmas", + "slideshow_duration_description": "Katra attēla rādÄĢÅĄanas ilgums sekundēs", + "smart_search_job_description": "Analizēt failus ar maÅĄÄĢnmācÄĢÅĄanos lai sagatavotu datus viedajai meklÄ“ÅĄanai", "storage_template_date_time_sample": "Laika paraugs {date}", "storage_template_migration": "Krātuves veidņu migrācija", - "storage_template_migration_job": "Krātuves veidņu migrācijas uzdevums", + "storage_template_migration_description": "Piemēro paÅĄreizējo {template} iepriekÅĄ augÅĄupielādētajiem failiem", + "storage_template_migration_info": "Krātuves veidne pārveidos visus failu paplaÅĄinājumus uz mazajiem burtiem. Veidnes izmaiņas attieksies tikai uz jauniem failiem. Lai veidni piemērotu ar atpakaÄŧejoÅĄu efektu iepriekÅĄ augÅĄupielādētiem failiem, palaidiet {job}.", + "storage_template_migration_job": "Krātuves veidņu migrācijas uzdevumu", "storage_template_path_length": "Aptuvenais ceÄŧa garuma ierobeÅžojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", "system_settings": "Sistēmas iestatÄĢjumi", + "template_email_available_tags": "Sagatavē var izmantot ÅĄos mainÄĢgos: {tags}", + "template_email_if_empty": "Ja sagatave ir tukÅĄa, tiks izmantots noklusējuma e-pasts.", + "template_email_invite_album": "Albuma ielÅĢguma sagatave", "template_email_preview": "PriekÅĄskatÄĢjums", + "template_email_settings": "E-pasta sagataves", + "template_email_update_album": "Atjaunināt albuma sagatavi", "template_settings_description": "Pielāgotu paziņojumu veidņu pārvaldÄĢba", "theme_custom_css_settings": "Pielāgots CSS", "theme_custom_css_settings_description": "Cascading Style Sheets Äŧauj pielāgot Immich izskatu.", "theme_settings_description": "Immich tÄĢmekÄŧa saskarnes pielāgojumu pārvaldÄĢba", "thumbnail_generation_job": "SÄĢktēlu ÄŖenerÄ“ÅĄana", + "thumbnail_generation_job_description": "Izveidot lielu, mazu un izplÅĢduÅĄu sÄĢktēlu katram failam, kā arÄĢ sÄĢktēlu katrai personai", "transcoding_acceleration_api": "PaātrinÄÅĄanas API", "transcoding_acceleration_nvenc": "NVENC (nepiecieÅĄams NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (nepiecieÅĄams 7. paaudzes vai jaunāks Intel procesors)", @@ -148,24 +225,38 @@ "transcoding_advanced_options_description": "Lielākajai daÄŧai lietotāju nevajadzētu mainÄĢt ÅĄÄĢs opcijas", "transcoding_audio_codec": "Audio kodeks", "transcoding_codecs_learn_more": "Lai uzzinātu vairāk par ÅĄeit lietoto terminoloÄŖiju, skatiet FFmpeg dokumentāciju par H.264 kodeku, HEVC kodeku un VP9 kodeku.", + "transcoding_constant_quality_mode_description": "ICQ ir labāks nekā CQP, bet daÅžas aparatÅĢras paātrinājuma ierÄĢces neatbalsta ÅĄo reÅžÄĢmu. Iestatot ÅĄo opciju, tiks izmantots norādÄĢtais reÅžÄĢms, ja tiek izmantota kvalitātē balstÄĢta kodÄ“ÅĄana. NVENC to ignorē, jo neatbalsta ICQ.", + "transcoding_constant_rate_factor_description": "Video kvalitātes lÄĢmenis. Tipiskās vērtÄĢbas ir 23 priekÅĄ H.264, 28 priekÅĄ HEVC, 31 priekÅĄ VP9 un 35 priekÅĄ AV1. Zemāka vērtÄĢba ir labāka, bet rada lielākus failus.", + "transcoding_hardware_acceleration": "AparatÅĢras paātrinājums", + "transcoding_required_description": "Tikai video, kas nav atbalstÄĢtā formātā", + "transcoding_settings": "Video transkodÄ“ÅĄanas iestatÄĢjumi", "transcoding_threads": "Pavedieni", "transcoding_video_codec": "Video kodeks", "trash_number_of_days": "Dienu skaits", + "trash_settings": "Atkritnes iestatÄĢjumi", "trash_settings_description": "Atkritnes iestatÄĢjumu pārvaldÄĢba", + "user_delete_delay_settings": "DzÄ“ÅĄanas aizture", + "user_delete_delay_settings_description": "Dienu skaits pēc izdzÄ“ÅĄanas, kad neatgriezeniski tiks dzēsti lietotāja konti un faili. Lietotāju dzÄ“ÅĄanas uzdevums tiek izpildÄĢts pusnaktÄĢ un pārbauda, kuri lietotāji ir gatavi dzÄ“ÅĄanai. Izmaiņas ÅĄajā iestatÄĢjumā tiks ņemtas vērā nākamajā izpildes reizē.", + "user_delete_immediately_checkbox": "Ierindot lietotāju un failus tÅĢlÄĢtējai dzÄ“ÅĄanai", + "user_details": "Lietotāja informācija", "user_management": "Lietotāju pārvaldÄĢba", "user_password_has_been_reset": "Lietotāja parole ir atiestatÄĢta:", + "user_password_reset_description": "LÅĢdzu, norādi lietotājam pagaidu paroli un informē viņu, ka nākamajā pieslēgÅĄanās reizē viņam bÅĢs jāmaina parole.", "user_restore_description": "{user} konts tiks atjaunots.", + "user_restore_scheduled_removal": "Atjaunot lietotāju - plānotā dzÄ“ÅĄana {date, date, long}", + "user_settings": "Lietotāja iestatÄĢjumi", "user_settings_description": "Lietotāju iestatÄĢjumu pārvaldÄĢba", "version_check_enabled_description": "Ieslēgt versijas pārbaudi", "version_check_implications": "Versiju pārbaudes funkcija ir atkarÄĢga no periodiskas saziņas ar github.com", - "version_check_settings": "Versijas pārbaude" + "version_check_settings": "Versijas pārbaude", + "version_check_settings_description": "Ieslēgt/izslēgt paziņojumus par jaunu versiju" }, "admin_email": "Administratora e-pasts", "admin_password": "Administratora parole", "administration": "AdministrÄ“ÅĄana", "advanced": "Papildu", "advanced_settings_log_level_title": "ÅŊurnalÄ“ÅĄanas lÄĢmenis: {level}", - "advanced_settings_prefer_remote_subtitle": "DaŞās ierÄĢcēs sÄĢktēli no ierÄĢcē esoÅĄajiem resursiem tiek ielādēti Äŧoti lēni. Aktivizējiet ÅĄo iestatÄĢjumu, lai tā vietā ielādētu attālus attēlus.", + "advanced_settings_prefer_remote_subtitle": "DaŞās ierÄĢcēs sÄĢktēli no ierÄĢces atmiņas ielādējas Äŧoti lēni. Aktivizējiet ÅĄo iestatÄĢjumu, lai tā vietā ielādētu attālus attēlus.", "advanced_settings_prefer_remote_title": "Dot priekÅĄroku attāliem attēliem", "advanced_settings_proxy_headers_title": "Starpniekservera galvenes", "advanced_settings_self_signed_ssl_subtitle": "IzlaiÅž servera galapunkta SSL sertifikātu verifikāciju. NepiecieÅĄams paÅĄparakstÄĢtajiem sertifikātiem.", @@ -177,7 +268,10 @@ "age_year_months": "Vecums 1 gads, {months, plural, zero {# mēneÅĄu} one {# mēnesis} other {# mēneÅĄi}}", "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", - "album_cover_updated": "Albuma attēls atjaunināts", + "album_added_notification_setting_description": "Saņemt e-pasta paziņojumu, kad tevi pievieno kopÄĢgam albumam", + "album_cover_updated": "Albuma vāciÅ†ÅĄ atjaunināts", + "album_delete_confirmation_description": "Ja ÅĄis albums tiek kopÄĢgots, citi lietotāji vairs nevarēs tam piekÄŧÅĢt.", + "album_deleted": "Albums dzēsts", "album_info_card_backup_album_excluded": "NEIEKÄģAUTS", "album_info_card_backup_album_included": "IEKÄģAUTS", "album_info_updated": "Albuma informācija atjaunināta", @@ -196,14 +290,19 @@ "album_viewer_appbar_share_to": "KopÄĢgot Uz", "album_viewer_page_share_add_users": "Pievienot lietotājus", "albums": "Albumi", - "all": "Viss", + "albums_default_sort_order": "Albuma noklusējuma kārtoÅĄanas secÄĢba", + "albums_default_sort_order_description": "Sākotnējā failu kārtoÅĄanas secÄĢba, veidojot jaunus albumus.", + "albums_feature_description": "Failu kolekcijas, kuras var koplietot ar citiem lietotājiem.", + "albums_on_device_count": "Albumi ierÄĢcē ({count})", + "all": "Visi", "all_albums": "Visi albumi", - "all_people": "Visi cilvēki", + "all_people": "Visas personas", "all_videos": "Visi video", "allow_dark_mode": "AtÄŧaut tumÅĄo reÅžÄĢmu", "allow_edits": "AtÄŧaut laboÅĄanu", "allow_public_user_to_download": "AtÄŧaut lejupielādēt publiskiem lietotājiem", "allow_public_user_to_upload": "AtÄŧaut augÅĄupielādēt publiskiem lietotājiem", + "alt_text_qr_code": "QR koda attēls", "anti_clockwise": "Pretēji pulksteņrādÄĢtāja virzienam", "api_key": "API atslēga", "api_key_description": "Å ÄĢ vērtÄĢba tiks parādÄĢta tikai vienu reizi. Nokopējiet to pirms loga aizvērÅĄanas.", @@ -211,6 +310,9 @@ "app_bar_signout_dialog_content": "Vai tieÅĄÄm vēlaties izrakstÄĢties?", "app_bar_signout_dialog_ok": "Jā", "app_bar_signout_dialog_title": "IzrakstÄĢties", + "app_settings": "Lietotnes iestatÄĢjumi", + "appears_in": "Parādās iekÅĄ", + "apply_count": "Pielietot ({count, number})", "archive": "ArhÄĢvs", "archive_page_no_archived_assets": "Nav atrasts neviens arhivēts aktÄĢvs", "archive_page_title": "ArhÄĢvs ({count})", @@ -219,27 +321,45 @@ "are_these_the_same_person": "Vai ÅĄÄĢ ir tā pati persona?", "asset_action_delete_err_read_only": "Nevar dzēst read only aktÄĢvu(-s), notiek izlaiÅĄana", "asset_action_share_err_offline": "Nevar iegÅĢt bezsaistes aktÄĢvu(-s), notiek izlaiÅĄana", + "asset_added_to_album": "Pievienots albumam", "asset_adding_to_album": "Pievieno albumamâ€Ļ", + "asset_description_updated": "Faila apraksts ir atjaunināts", "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", - "asset_list_layout_settings_group_by": "Grupēt aktÄĢvus pēc", + "asset_list_layout_settings_group_by": "Grupēt failus pēc", "asset_list_layout_settings_group_by_month_day": "Mēnesis + diena", "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "FotoreÅžÄŖa izkārtojuma iestatÄĢjumi", "asset_list_settings_title": "FotoreÅžÄŖis", + "asset_skipped": "Izlaists", + "asset_skipped_in_trash": "Atkritnē", + "asset_uploaded": "AugÅĄupielādēts", "asset_uploading": "AugÅĄupielādēâ€Ļ", - "asset_viewer_settings_title": "AktÄĢvu SkatÄĢtājs", - "assets": "aktÄĢvi", + "asset_viewer_settings_title": "Failu skatÄĢtājs", + "assets": "Faili", + "assets_added_count": "Pievienoja {count, plural, one {# failu} other {# failus}}", + "assets_added_to_album_count": "Pievienoja albumam {count, plural, one {# failu} other {# failus}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Failu} other {Failus}} nevar pievienot albumam", + "assets_deleted_permanently_from_server": "{count} faili dzēsti no Immich servera", + "assets_moved_to_trash_count": "Pārvietoja {count, plural, one {# failu} other {# failus}} uz atkritni", + "assets_removed_count": "Noņēma {count, plural, one {# failu} other {# failus}}", + "assets_trashed": "{count} faili pārvietoti uz atkritni", + "assets_trashed_from_server": "{count} faili pārvietoti uz Immich servera atkritni", "authorized_devices": "Autorizētās ierÄĢces", "automatic_endpoint_switching_title": "Automātiska URL pārslēgÅĄana", + "autoplay_slideshow": "Automātiska slaidrādes atskaņoÅĄana", "back": "AtpakaÄŧ", + "background_backup_running_error": "PaÅĄlaik darbojas dublÄ“ÅĄana fonā, nevar uzsākt manuālu dublÄ“ÅĄanu", + "background_options": "Fona opcijas", + "backup": "DublÄ“ÅĄana", "backup_album_selection_page_albums_device": "Albumi ierÄĢcē ({count})", "backup_album_selection_page_albums_tap": "Pieskarieties, lai iekÄŧautu, veiciet dubultskārienu, lai izslēgtu", - "backup_album_selection_page_assets_scatter": "AktÄĢvi var bÅĢt izmētāti pa vairākiem albumiem. Tādējādi dublÄ“ÅĄanas procesā albumus var iekÄŧaut vai neiekÄŧaut.", + "backup_album_selection_page_assets_scatter": "Faili var bÅĢt izmētāti pa vairākiem albumiem. Tādējādi dublÄ“ÅĄanas procesā albumus var iekÄŧaut vai neiekÄŧaut.", "backup_album_selection_page_select_albums": "AtlasÄĢt albumus", "backup_album_selection_page_selection_info": "Atlases informācija", - "backup_album_selection_page_total_assets": "Kopā unikālie aktÄĢvi", + "backup_album_selection_page_total_assets": "Unikālo failu kopsumma", + "backup_albums_sync": "DublÄ“ÅĄanas albumu sinhronizācija", "backup_all": "Viss", "backup_background_service_backup_failed_message": "Neizdevās dublēt lÄĢdzekÄŧus. Notiek atkārtota mēĪinÄÅĄanaâ€Ļ", "backup_background_service_connection_failed_message": "Neizdevās izveidot savienojumu ar serveri. Notiek atkārtota mēĪinÄÅĄanaâ€Ļ", @@ -254,6 +374,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Doties uz iestatÄĢjumiem", "backup_controller_page_background_battery_info_link": "ParādÄĢt, kā", "backup_controller_page_background_battery_info_message": "Lai iegÅĢtu vislabāko fona dublÄ“ÅĄanas pieredzi, lÅĢdzu, atspējojiet visas akumulatora optimizācijas, kas ierobeÅžo Immich fona aktivitāti.\n\nTā kā katrai ierÄĢcei iestatÄĢjumi ir citādāki, lÅĢdzu, meklējiet nepiecieÅĄamo informāciju pie ierÄĢces raÅžotāja.", + "backup_controller_page_background_battery_info_ok": "Labi", "backup_controller_page_background_battery_info_title": "Akumulatora optimizācija", "backup_controller_page_background_charging": "Tikai uzlādes laikā", "backup_controller_page_background_configure_error": "Neizdevās konfigurēt fona pakalpojumu", @@ -278,7 +399,7 @@ "backup_controller_page_remainder": "Atlikums", "backup_controller_page_remainder_sub": "AtlikuÅĄie fotoattēli un videoklipi, kurus dublēt no atlases", "backup_controller_page_server_storage": "Servera krātuve", - "backup_controller_page_start_backup": "Sākt DublÄ“ÅĄanu", + "backup_controller_page_start_backup": "Sākt dublÄ“ÅĄanu", "backup_controller_page_status_off": "Automātiskā priekÅĄplāna dublÄ“ÅĄana ir izslēgta", "backup_controller_page_status_on": "Automātiskā priekÅĄplāna dublÄ“ÅĄana ir ieslēgta", "backup_controller_page_storage_format": "{used} no {total} tiek izmantots", @@ -288,25 +409,29 @@ "backup_controller_page_turn_on": "Ieslēgt priekÅĄplāna dublÄ“ÅĄanu", "backup_controller_page_uploading_file_info": "Faila informācijas augÅĄupielāde", "backup_err_only_album": "Nevar noņemt vienÄĢgo albumu", - "backup_info_card_assets": "aktÄĢvi", + "backup_info_card_assets": "faili", "backup_manual_cancelled": "Atcelts", "backup_manual_in_progress": "AugÅĄupielāde jau notiek. MēĪiniet pēc kāda laika atkārtoti", "backup_manual_success": "VeiksmÄĢgi", "backup_manual_title": "AugÅĄupielādes statuss", "backup_options_page_title": "DublÄ“ÅĄanas iestatÄĢjumi", + "backup_settings_subtitle": "PārvaldÄĢt augÅĄupielādes iestatÄĢjumus", + "backward": "AtpakaÄŧejoÅĄa", "biometric_auth_enabled": "Ieslēgta biometriskā autentifikācija", "biometric_locked_out": "Biometriskā autentifikācija tev ir bloķēta", "biometric_no_options": "Nav pieejamas biometriskās autentifikācijas iespējas", "biometric_not_available": "Biometriskā autentifikācija ÅĄajā ierÄĢcē nav pieejama", "birthdate_saved": "DzimÅĄanas datums veiksmÄĢgi saglabāts", "birthdate_set_description": "DzimÅĄanas datums tiek izmantots, lai aprēķinātu ÅĄÄĢs personas vecumu fotogrāfijas uzņemÅĄanas brÄĢdÄĢ.", + "blurred_background": "Izpludināts fons", "bugs_and_feature_requests": "KÄŧÅĢdas un funkciju pieprasÄĢjumi", "build": "BÅĢvējums", "build_image": "BÅĢvējuma attēls", + "buy": "Iegādāties Immich", "cache_settings_clear_cache_button": "IztÄĢrÄĢt keÅĄatmiņu", "cache_settings_clear_cache_button_title": "IztÄĢra aplikācijas keÅĄatmiņu. Tas bÅĢtiski ietekmēs lietotnes veiktspēju, lÄĢdz keÅĄatmiņa bÅĢs pārbÅĢvēta.", "cache_settings_duplicated_assets_clear_button": "NOTÄĒRÄĒT", - "cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekÄŧāvusi melnajā sarakstā", + "cache_settings_duplicated_assets_subtitle": "Fotoattēli un videoklipi, kurus lietotne ir iekÄŧāvusi ignorējamo sarakstā", "cache_settings_duplicated_assets_title": "Dublicētie faili ({count})", "cache_settings_statistics_album": "Bibliotēkas sÄĢktēli", "cache_settings_statistics_full": "Pilni attēli", @@ -318,8 +443,16 @@ "cache_settings_tile_title": "Lokālā Krātuve", "cache_settings_title": "KeÅĄdarbes iestatÄĢjumi", "camera": "Fotokamera", + "camera_brand": "Fotokameras zÄĢmols", + "camera_model": "Fotokameras modelis", "cancel": "Atcelt", - "cannot_merge_people": "Nevar apvienot cilvēkus", + "cancel_search": "Atcelt meklÄ“ÅĄanu", + "canceled": "Atcelts", + "canceling": "AtceÄŧ", + "cannot_merge_people": "Nevar apvienot personas", + "cannot_undo_this_action": "Å o darbÄĢbu nevar atcelt!", + "cast": "PārraidÄĢt", + "cast_description": "Konfigurēt pieejamos pārraides galamērġus", "change_date": "MainÄĢt datumu", "change_description": "MainÄĢt aprakstu", "change_display_order": "MainÄĢt attēloÅĄanas secÄĢbu", @@ -328,17 +461,31 @@ "change_name": "MainÄĢt nosaukumu", "change_name_successfully": "Vārds veiksmÄĢgi nomainÄĢts", "change_password": "NomainÄĢt paroli", + "change_password_description": "Vai nu ÅĄÄĢ ir pirmā reize, kad pieslēdzaties sistēmai, vai arÄĢ ir iesniegts pieprasÄĢjums mainÄĢt paroli. LÅĢdzu, ievadiet jauno paroli zemāk.", "change_password_form_confirm_password": "Apstiprināt Paroli", "change_password_form_description": "Sveiki {name},\n\nÅ ÄĢ ir pirmā reize, kad pierakstāties sistēmā, vai arÄĢ ir iesniegts pieprasÄĢjums mainÄĢt paroli. LÅĢdzu, zemāk ievadiet jauno paroli.", "change_password_form_new_password": "Jauna Parole", "change_password_form_password_mismatch": "Paroles nesakrÄĢt", "change_password_form_reenter_new_password": "Atkārtoti ievadÄĢt jaunu paroli", "change_pin_code": "NomainÄĢt PIN kodu", - "choose_matching_people_to_merge": "Izvēlies atbilstoÅĄus cilvēkus apvienoÅĄanai", + "charging": "Lādē", + "charging_requirement_mobile_backup": "Fona dublÄ“ÅĄanai nepiecieÅĄams, lai ierÄĢce tiktu lādēta", + "check_corrupt_asset_backup_button": "Veikt pārbaudi", + "choose_matching_people_to_merge": "Izvēlies atbilstoÅĄas personas apvienoÅĄanai", "city": "Pilsēta", "clear": "NotÄĢrÄĢt", "clear_all": "NotÄĢrÄĢt visu", + "clear_all_recent_searches": "NotÄĢrÄĢt visas pēdējās meklÄ“ÅĄanas", + "clear_file_cache": "NotÄĢrÄĢt failu keÅĄatmiņu", + "clear_message": "NotÄĢrÄĢt paziņojumu", "clear_value": "NotÄĢrÄĢt vērtÄĢbu", + "client_cert_dialog_msg_confirm": "Labi", + "client_cert_enter_password": "Ievadi paroli", + "client_cert_import": "Importēt", + "client_cert_import_success_msg": "Klienta sertifikāts ir importēts", + "client_cert_invalid_msg": "NederÄĢgs sertifikāta fails vai nepareiza parole", + "client_cert_remove_msg": "Klienta sertifikāts ir noņemts", + "client_cert_subtitle": "Atbalsta tikai PKCS12 (.p12, .pfx) formātu. Sertifikātu importÄ“ÅĄana/noņemÅĄana ir pieejama tikai pirms pieslēgÅĄanās", "client_cert_title": "SSL klienta sertifikāts", "clockwise": "PulksteņrādÄĢtāja virzienā", "close": "Aizvērt", @@ -347,9 +494,14 @@ "color": "Krāsa", "color_theme": "Krāsu tēma", "comment_deleted": "Komentārs dzēsts", + "comment_options": "Komentāru iespējas", + "comments_and_likes": "Komentāri un tÄĢkÅĄÄˇi", + "comments_are_disabled": "Komentāri ir atslēgti", "common_create_new_album": "Izveidot jaunu albumu", "common_server_error": "LÅĢdzu, pārbaudiet tÄĢkla savienojumu, pārliecinieties, vai serveris ir sasniedzams un aplikācijas/servera versijas ir saderÄĢgas.", + "completed": "Pabeigts", "confirm": "Apstiprināt", + "confirm_admin_password": "Administratora paroles apstiprinājums", "confirm_new_pin_code": "Apstiprināt jauno PIN kodu", "confirm_password": "Apstiprināt paroli", "confirm_tag_face": "Vai vēlies atzÄĢmēt ÅĄo seju kā {name}?", @@ -359,19 +511,24 @@ "control_bottom_app_bar_create_new_album": "Izveidot jaunu albumu", "control_bottom_app_bar_delete_from_immich": "Dzēst no Immich", "control_bottom_app_bar_delete_from_local": "Dzēst no ierÄĢces", - "control_bottom_app_bar_edit_location": "RediÄŖÄ“t AtraÅĄanās Vietu", - "control_bottom_app_bar_edit_time": "RediÄŖÄ“t Datumu un Laiku", - "control_bottom_app_bar_share_to": "KopÄĢgot Uz", + "control_bottom_app_bar_edit_location": "RediÄŖÄ“t atraÅĄanās vietu", + "control_bottom_app_bar_edit_time": "RediÄŖÄ“t datumu un laiku", + "control_bottom_app_bar_share_to": "KopÄĢgot uz", "control_bottom_app_bar_trash_from_immich": "Pārvietot uz Atkritni", "copy_error": "KopÄ“ÅĄanas kÄŧÅĢda", + "copy_to_clipboard": "Kopēt starpliktuvē", "country": "Valsts", + "cover": "AizpildÄĢts ekrāns", + "covers": "Vāciņi", "create": "Izveidot", "create_album": "Izveidot albumu", "create_album_page_untitled": "Bez nosaukuma", "create_library": "Izveidot bibliotēku", "create_link": "Izveidot saiti", "create_link_to_share": "Izveidot kopÄĢgoÅĄanas saiti", + "create_new": "IZVEIDOT JAUNU", "create_new_person": "Izveidot jaunu personu", + "create_new_person_hint": "PiesaistÄĢt izvēlētos failus jaunai personai", "create_new_user": "Izveidot jaunu lietotāju", "create_shared_album_page_share_add_assets": "PIEVIENOT AKTÄĒVUS", "create_shared_album_page_share_select_photos": "Fotoattēlu Izvēle", @@ -379,7 +536,12 @@ "created_at": "Izveidots", "curated_object_page_title": "Lietas", "current_pin_code": "EsoÅĄais PIN kods", + "current_server_address": "PaÅĄreizējā servera adrese", + "custom_locale": "Pielāgota lokalizācija", + "custom_locale_description": "Formatēt datumus un skaitÄŧus atbilstoÅĄi valodai un reÄŖionam", + "custom_url": "Pielāgots URL", "daily_title_text_date_year": "E, MMM dd, gggg", + "dark_theme": "Pārslēgt tumÅĄo tēmu", "date_after": "Datums pēc", "date_and_time": "Datums un Laiks", "date_before": "Datums pirms", @@ -387,7 +549,14 @@ "date_of_birth_saved": "DzimÅĄanas datums veiksmÄĢgi saglabāts", "date_range": "Datumu diapazons", "day": "Diena", - "deduplication_criteria_1": "Attēla izmērs baitos", + "days": "Dienas", + "deduplicate_all": "Dedublicēt visus", + "deduplication_criteria_1": "Attēla izmēru baitos", + "deduplication_criteria_2": "EXIF datu skaitu", + "deduplication_info": "DeduplicÄ“ÅĄanas informācija", + "deduplication_info_description": "Lai automātiski atzÄĢmētu failus un masveidā noņemtu dublikātus, mēs skatāmies uz:", + "default_locale": "Noklusējuma lokalizācija", + "default_locale_description": "Formatēt datumus un skaitÄŧus atbilstoÅĄi pārlÅĢka lokalizācijai", "delete": "Dzēst", "delete_album": "Dzēst albumu", "delete_dialog_alert": "Å ie vienumi tiks neatgriezeniski dzēsti no Immich un jÅĢsu ierÄĢces", @@ -400,6 +569,7 @@ "delete_key": "Dzēst atslēgu", "delete_library": "Dzēst bibliotēku", "delete_link": "Dzēst saiti", + "delete_local_action_prompt": "{count} dzēsti lokāli", "delete_local_dialog_ok_backed_up_only": "Dzēst tikai Dublētos", "delete_local_dialog_ok_force": "Tā pat dzēst", "delete_others": "Dzēst citus", @@ -413,26 +583,40 @@ "details": "INFORMĀCIJA", "direction": "SecÄĢba", "discord": "Discord", + "discover": "Atklāt", + "discovered_devices": "Atrastās ierÄĢces", "display_order": "AttēloÅĄanas secÄĢba", + "display_original_photos": "RādÄĢt oriÄŖinālās fotogrāfijas", "documentation": "Dokumentācija", "done": "Gatavs", "download": "Lejupielādēt", + "download_action_prompt": "Lejupielādē {count} failus", "download_canceled": "Lejupielāde atcelta", "download_complete": "Lejupielāde pabeigta", + "download_enqueue": "Lejupielāde ierindota", "download_error": "Lejupielādes kÄŧÅĢda", "download_failed": "Lejupielāde neizdevās", + "download_finished": "Lejupielāde pabeigta", + "download_include_embedded_motion_videos": "Iegultie videoklipi", + "download_include_embedded_motion_videos_description": "IekÄŧaut video, kas iebÅĢvēti kustÄĢgos fotoattēlos, kā atseviÅĄÄˇu failu", "download_notfound": "Lejupielāde nav atrasta", "download_paused": "Lejupielāde nopauzēta", "download_settings": "Lejupielāde", "download_settings_description": "Ar failu lejupielādi saistÄĢto iestatÄĢjumu pārvaldÄĢba", "download_started": "Lejupielāde sākta", "download_sucess": "Lejupielāde izdevās", + "download_sucess_android": "Multivides fails ir lejupielādēts uz DCIM/Immich", + "download_waiting_to_retry": "Gaida, lai mēĪinātu atkārtoti", "downloading": "Lejupielādē", "downloading_asset_filename": "Lejupielādē failu {filename}", + "downloading_media": "Lejupielādē failu", "duplicates": "Dublikāti", + "duplicates_description": "Atrisini katru grupu, norādot, kuri no tiem ir dublikāti", "duration": "Ilgums", "edit": "Labot", "edit_album": "Labot albumu", + "edit_avatar": "Labot avatāru", + "edit_birthday": "Labot dzimÅĄanas dienu", "edit_date": "Labot datumu", "edit_date_and_time": "Labot datumu un laiku", "edit_description": "Labot aprakstu", @@ -457,34 +641,58 @@ "email_notifications": "E-pasta paziņojumi", "empty_folder": "Å ÄĢ mape ir tukÅĄa", "empty_trash": "IztukÅĄot atkritni", + "enable_backup": "Ieslēgt dublÄ“ÅĄanu", "enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu", "end_date": "Beigu datums", + "enqueued": "Ierindots", "enter_wifi_name": "Ievadi Wi-Fi nosaukumu", "enter_your_pin_code": "Ievadi savu PIN kodu", "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekÄŧÅĢtu slēgtajai mapei", "error": "KÄŧÅĢda", + "error_change_sort_album": "Neizdevās nomainÄĢt albuma kārtoÅĄanas secÄĢbu", + "error_loading_image": "KÄŧÅĢda, ielādējot attēlu", + "error_loading_partners": "KÄŧÅĢda, ielādējot partnerus: {error}", "error_saving_image": "KÄŧÅĢda: {error}", "errors": { "cant_get_faces": "Nevar iegÅĢt sejas", "cant_search_people": "Neizdevās veikt peronu meklÄ“ÅĄanu", + "exclusion_pattern_already_exists": "Šāds izslēgÅĄanas paraugs jau pastāv.", "failed_to_create_album": "Neizdevās izveidot albumu", + "failed_to_create_shared_link": "Neizdevās izvedot kopÄĢgoÅĄanas saiti", + "failed_to_edit_shared_link": "Neizdevās labot kopÄĢgoto saiti", + "failed_to_get_people": "Neizdevās iegÅĢt personas", + "failed_to_keep_this_delete_others": "Neizdevās paturēt ÅĄo failu un dzēst pārējos failus", + "failed_to_load_asset": "Neizdevās ielādēt failu", + "failed_to_load_assets": "Neizdevās ielādēt failus", + "failed_to_load_notifications": "Neizdevās ielādēt paziņojumus", + "failed_to_load_people": "Neizdevās ielādēt personas", + "failed_to_remove_product_key": "Neizdevās noņemt produkta atslēgu", + "failed_to_reset_pin_code": "Neizdevās atiestatÄĢt PIN kodu", + "failed_to_stack_assets": "Neizdevās apvienot failus kaudzē", + "failed_to_unstack_assets": "Neizdevās atcelt failu apvienoÅĄanu kaudzē", + "failed_to_update_notification_status": "Neizdevās mainÄĢt paziņojuma statusu", + "import_path_already_exists": "Å is importa ceÄŧÅĄ jau pastāv.", + "incorrect_email_or_password": "Nepareizs e-pasts vai parole", + "profile_picture_transparent_pixels": "Profila attēlos nevar bÅĢt caurspÄĢdÄĢgi pikseÄŧi. LÅĢdzu, palielini un/vai pārvieto attēlu.", + "something_went_wrong": "Kaut kas nogāja greizi", "unable_to_change_description": "Neizdevās nomainÄĢt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", "unable_to_delete_user": "Neizdevās dzēst lietotāju", + "unable_to_empty_trash": "Neizdevās iztukÅĄot atkritni", "unable_to_hide_person": "Neizdevās paslēpt personu", + "unable_to_restore_trash": "Neizdevās atjaunot failus no atkritnes", "unable_to_save_date_of_birth": "Neizdevās saglabāt dzimÅĄanas datumu", "unable_to_scan_libraries": "Bibliotēku skenÄ“ÅĄana neizdevās", - "unable_to_scan_library": "Bibliotēkas skenÄ“ÅĄana neizdevās" + "unable_to_scan_library": "Bibliotēkas skenÄ“ÅĄana neizdevās", + "unable_to_trash_asset": "Neizdevās pārvietot failu uz atkritni", + "unable_to_update_album_cover": "Nevar atjaunināt albuma vāciņu" }, "exif": "Exif", "exif_bottom_sheet_description": "Pievienot Aprakstu...", "exif_bottom_sheet_details": "INFORMĀCIJA", "exif_bottom_sheet_location": "ATRAÅ ANĀS VIETA", - "exif_bottom_sheet_people": "CILVĒKI", + "exif_bottom_sheet_people": "PERSONAS", "exif_bottom_sheet_person_add_person": "Pievienot vārdu", - "exif_bottom_sheet_person_age_months": "Vecums {months} mēneÅĄi", - "exif_bottom_sheet_person_age_year_months": "Vecums 1 gads, {months} mēneÅĄi", - "exif_bottom_sheet_person_age_years": "Vecums {years}", "exit_slideshow": "Iziet no slÄĢdrādes", "experimental_settings_new_asset_list_subtitle": "Izstrādes posmā", "experimental_settings_new_asset_list_title": "Iespējot eksperimentālo fotoreÅžÄŖi", @@ -494,23 +702,72 @@ "expired": "DerÄĢguma termiÅ†ÅĄ beidzās", "explore": "IzpētÄĢt", "export": "Eksportēt", + "export_as_json": "Eksportēt kā JSON", + "export_database": "Eksportēt datubāzi", + "export_database_description": "Eksportēt SQLite datubāzi", "extension": "PaplaÅĄinājums", - "external_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": "Ārējs", + "external_libraries": "Ārējas bibliotēkas", + "external_network": "Ārējs tÄĢkls", + "external_network_sheet_info": "Kad nav pieejams izvēlētais Wi-Fi tÄĢkls, aplikācija pieslēgsies serverim lietojot pirmo strādājoÅĄo URL no saraksta, sākot ar augÅĄÄ“jo", + "face_unassigned": "NepieÅĄÄˇirts", + "failed": "Neizdevās", "failed_to_authenticate": "Neizdevās autentificēties", "failed_to_load_assets": "Neizdevās ielādēt failus", + "failed_to_load_folder": "Neizdevās ielādēt mapi", "favorite": "Izlase", "favorites": "Izlase", "favorites_page_no_favorites": "Nav atrasti iecienÄĢtākie faili", + "features_in_development": "Izstrādes stadijā esoÅĄas funkcijas", "features_setting_description": "Lietotnes funkciju pārvaldÄĢba", + "file_name": "Faila nosaukums", + "file_name_or_extension": "Faila nosaukums vai paplaÅĄinājums", "filename": "Faila nosaukums", "filetype": "Faila tips", + "filter": "Filtrēt", + "filter_people": "Filtrēt personas", + "filter_places": "Filtrēt vietas", + "first": "Pirmais", + "folder": "Mape", + "folder_not_found": "Mape nav atrasta", "folders": "Mapes", - "haptic_feedback_switch": "IestatÄĢt haptisku reakciju", + "forgot_pin_code_question": "Aizmirsi savu PIN?", + "forward": "Uz priekÅĄu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Å ÄĢ funkcija darbojas, lejupielādējot ārējos resursus no Google.", + "get_help": "Saņemt palÄĢdzÄĢbu", + "get_wifiname_error": "Nevarēja iegÅĢt Wi-Fi nosaukumu. Pārliecinies, ka esi pieÅĄÄˇÄĢris nepiecieÅĄamās atÄŧaujas un esi savienots ar Wi-Fi tÄĢklu", + "getting_started": "Pirmie soÄŧi", + "go_back": "Doties atpakaÄŧ", + "go_to_folder": "Doties uz mapi", + "go_to_search": "Doties uz meklÄ“ÅĄanu", + "gps": "Ir koordinātas", + "gps_missing": "Nav koordinātu", + "grant_permission": "PieÅĄÄˇirt atÄŧauju", + "group_albums_by": "Grupēt albumus pēc...", + "group_country": "Grupēt pēc valsts", + "group_no": "Negrupēt", + "group_owner": "Grupēt pēc ÄĢpaÅĄnieka", + "group_places_by": "Grupēt vietas pēc...", + "group_year": "Grupēt pēc gada", + "haptic_feedback_switch": "Iespējot haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", - "has_quota": "Ir kvota", + "has_quota": "Kvota", + "hash_asset": "Veidot faila jaucējvērtÄĢbu", + "hashed_assets": "Faili ar jaucējvērtÄĢbām", + "hashing": "Veido jaucējvērtÄĢbas", + "header_settings_field_validator_msg": "VērtÄĢba nevar bÅĢt tukÅĄa", + "header_settings_header_name_input": "Galvenes lauks", + "header_settings_header_value_input": "Galvenes vērtÄĢba", + "headers_settings_tile_subtitle": "Norādiet starpniekservera galvenes, kuras lietotnei jānosÅĢta ar katru tÄĢkla pieprasÄĢjumu", + "headers_settings_tile_title": "Pielāgotas starpniekservera galvenes", + "hide_all_people": "Paslēpt visas personas", + "hide_gallery": "Paslēpt galeriju", "hide_named_person": "Paslēpt personu {name}", + "hide_password": "Paslēpt paroli", "hide_person": "Paslēpt personu", - "home_page_add_to_album_conflicts": "Pievienoja {added} aktÄĢvus albumam {album}. {failed} aktÄĢvi jau ir albumā.", + "hide_unnamed_people": "Paslēpt nenosauktas personas", + "home_page_add_to_album_conflicts": "Pievienoja {added} failus albumam {album}. {failed} faili jau ir albumā.", "home_page_add_to_album_err_local": "Albumiem vēl nevar pievienot lokālos aktÄĢvus, notiek izlaiÅĄana", "home_page_add_to_album_success": "Pievienoja {added} aktÄĢvus albumam {album}.", "home_page_album_err_partner": "Pagaidām nevar pievienot partnera aktÄĢvus albumam, notiek izlaiÅĄana", @@ -518,57 +775,86 @@ "home_page_archive_err_partner": "Nevarēja arhivēt partnera aktÄĢvus, notiek izlaiÅĄana", "home_page_building_timeline": "Tiek izveidota laika skala", "home_page_delete_err_partner": "Nevarēja dzēst partnera aktÄĢvus, notiek izlaiÅĄana", - "home_page_delete_remote_err_local": "Lokālie aktÄĢvi dzÄ“ÅĄanai attālinātajā izvēlē, tiek izlaists", + "home_page_delete_remote_err_local": "Lokālie faili dzÄ“ÅĄanai attālinātajā izvēlē, tiek izlaists", "home_page_favorite_err_local": "Vēl nevar pievienot izlasei vietējos failus, izlaiÅž", "home_page_favorite_err_partner": "Pagaidām nevar ievietot izlasē partnera failus, izlaiÅž", - "home_page_first_time_notice": "Ja ÅĄÄĢ ir pirmā reize, kad izmantojat aplikāciju, lÅĢdzu, izvēlieties dublējuma albumu(s), lai laika skala varētu aizpildÄĢt fotoattēlus un videoklipus albumā(os).", + "home_page_first_time_notice": "Ja ÅĄÄĢ ir pirmā reize, kad izmanto lietotni, lÅĢdzu, izvēlies dublējamo albumu, lai laika skalā varētu aizpildÄĢt fotoattēlus un videoklipus", "home_page_locked_error_local": "Nevar pārvietot vietējos failus uz slēgto mapi, izlaiÅž", "home_page_locked_error_partner": "Nevar pārvietot partneru failus uz slēgto mapi, izlaiÅž", "home_page_share_err_local": "Caur saiti nevarēja kopÄĢgot lokālos aktÄĢvus, notiek izlaiÅĄana", "home_page_upload_err_limit": "Vienlaikus var augÅĄupielādēt ne vairāk kā 30 aktÄĢvus, notiek izlaiÅĄana", "hour": "Stunda", + "hours": "Stundas", "id": "ID", + "idle": "DÄĢkstāvē", + "ignore_icloud_photos": "Ignorēt iCloud fotogrāfijas", + "ignore_icloud_photos_description": "iCloud uzglabātās fotogrāfijas netiks augÅĄupielādētas Immich serverÄĢ", "image": "Attēls", - "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", + "image_saved_successfully": "Attēls saglabāts", + "image_viewer_page_state_provider_download_started": "Lejupielāde uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "KopÄĢgoÅĄanas KÄŧÅĢda", "immich_logo": "Immich logo", + "immich_web_interface": "Immich tÄĢmekÄŧa saskarne", "import_from_json": "Importēt no JSON", "import_path": "Importa ceÄŧÅĄ", "in_albums": "{count, plural, one {# albumā} other {# albumos}}", "in_archive": "ArhÄĢvā", "include_archived": "IekÄŧaut arhivētos", "include_shared_albums": "IekÄŧaut koplietotos albumus", + "include_shared_partner_assets": "IekÄŧaut partneru koplietotos failus", "info": "Informācija", "interval": { "day_at_onepm": "Katru dienu 13.00", "night_at_midnight": "Katru dienu pusnaktÄĢ", "night_at_twoam": "Katru dienu 2.00 naktÄĢ" }, + "invalid_date": "NederÄĢgs datums", + "invalid_date_format": "NederÄĢgs datuma formāts", "invite_people": "IelÅĢgt cilvēkus", "invite_to_album": "Uzaicināt albumā", + "ios_debug_info_fetch_ran_at": "IelasÄĢÅĄana notika {dateTime}", + "ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}", + "ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu", + "ios_debug_info_processing_ran_at": "Apstrāde notika {dateTime}", + "items_count": "{count, plural, one {# vienums} other {# vienumi}}", "jobs": "Uzdevumi", "keep": "Paturēt", "keep_all": "Paturēt visus", + "keep_this_delete_others": "Paturēt ÅĄo, dzēst citus", "keyboard_shortcuts": "TastatÅĢras saÄĢsnes", "language": "Valoda", - "language_setting_description": "Izvēlieties vēlamo valodu", + "language_no_results_subtitle": "MēĪini pielāgot meklÄ“ÅĄanas terminu", + "language_no_results_title": "Nav atrasta neviena valoda", + "language_search_hint": "Meklēt valodas...", + "language_setting_description": "Izvēlies vēlamo valodu", + "large_files": "Lielie faili", + "last": "Pēdējais", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Äĸeogrāfiskais platums", "leave": "Paturēt", + "leave_album": "Pamest albumu", + "lens_model": "ObjektÄĢva modelis", "let_others_respond": "Äģaut citiem atbildēt", "level": "LÄĢmenis", "library": "Bibliotēka", + "library_options": "Bibliotēkas opcijas", "library_page_device_albums": "Albumi ierÄĢcē", "library_page_new_album": "Jauns albums", - "library_page_sort_asset_count": "Daudzums ar aktÄĢviem", + "library_page_sort_asset_count": "Failu skaits", "library_page_sort_created": "Jaunākais izveidotais", - "library_page_sort_last_modified": "Pēdējo reizi modificēts", + "library_page_sort_last_modified": "Pēdējās izmaiņas", "library_page_sort_title": "Albuma virsraksts", + "licenses": "Licences", + "link_to_oauth": "PiesaistÄĢt OAuth", + "linked_oauth_account": "PiesaistÄĢtais OAuth konts", "list": "Saraksts", "loading": "Ielādē", - "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": "Lokāli", + "local_network": "Lokālais tÄĢkls", + "location_permission": "AtraÅĄanās vietas atÄŧauja", + "location_permission_content": "Lai izmantotu automātiskās pārslēgÅĄanās funkciju, Immich ir nepiecieÅĄama precÄĢzas atraÅĄanās vietas atÄŧauja, lai varētu nolasÄĢt paÅĄreizējā Wi-Fi tÄĢkla nosaukumu", "location_picker_choose_on_map": "Izvēlēties uz kartes", "location_picker_latitude_error": "Ievadiet korektu ÄŖeogrāfisko platumu", "location_picker_latitude_hint": "Ievadiet savu ÄŖeogrāfisko platumu ÅĄeit", @@ -576,6 +862,7 @@ "location_picker_longitude_hint": "Ievadiet savu ÄŖeogrāfisko garumu ÅĄeit", "locked_folder": "Slēgtā mape", "log_out": "IzrakstÄĢties", + "login": "Pieslēgties", "login_disabled": "PieslēgÅĄanās ir atslēgta", "login_form_api_exception": "API izņēmums. LÅĢdzu, pārbaudiet servera URL un mēĪiniet vēlreiz.", "login_form_back_button_text": "AtpakaÄŧ", @@ -600,7 +887,8 @@ "longitude": "Äĸeogrāfiskais garums", "look": "Izskats", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaÄŧu skatÄĢtājā.", - "make": "Firma", + "make": "RaÅžotājs", + "manage_geolocation": "PārvaldÄĢt atraÅĄanās vietu", "manage_shared_links": "KopÄĢgoto saiÅĄu pārvaldÄĢba", "manage_sharing_with_partners": "KoplietoÅĄanas ar partneriem pārvaldÄĢba", "manage_the_app_settings": "Lietotnes iestatÄĢjumu pārvaldÄĢba", @@ -609,23 +897,21 @@ "manage_your_devices": "Pieslēgto ierÄĢču pārvaldÄĢba", "manage_your_oauth_connection": "OAuth savienojumu pārvaldÄĢba", "map": "Karte", - "map_assets_in_bound": "{count} fotoattēls", "map_assets_in_bounds": "{count} fotoattēli", "map_cannot_get_user_location": "Nevar iegÅĢt lietotāja atraÅĄanās vietu", "map_location_dialog_yes": "Jā", "map_location_picker_page_use_location": "Izvēlēties ÅĄo atraÅĄanās vietu", - "map_location_service_disabled_content": "Lai tiktu rādÄĢti jÅĢsu paÅĄreizējās atraÅĄanās vietas aktÄĢvi, ir jāaktivizē atraÅĄanās vietas pakalpojums. Vai vēlaties to iespējot tagad?", + "map_location_service_disabled_content": "Lai tiktu rādÄĢti jÅĢsu paÅĄreizējās atraÅĄanās vietas faili, ir jāaktivizē atraÅĄanās vietas pakalpojums. Vai vēlaties to iespējot tagad?", "map_location_service_disabled_title": "AtraÅĄanās vietas Pakalpojums atslēgts", "map_marker_for_images": "Kartes marġieris attēliem, kas uzņemti {city}, {country}", "map_marker_with_image": "Kartes marġieris ar attēlu", - "map_no_assets_in_bounds": "Å ajā lokācijā nav neviena fotoattēla", "map_no_location_permission_content": "AtraÅĄanās vietas atÄŧauja ir nepiecieÅĄama, lai parādÄĢtu jÅĢsu paÅĄreizējās atraÅĄanās vietas aktÄĢvus. Vai vēlaties to atÄŧaut tagad?", "map_no_location_permission_title": "AtraÅĄanās vietas AtÄŧaujas liegtas", - "map_settings": "Kartes IestatÄĢjumi", + "map_settings": "Kartes iestatÄĢjumi", "map_settings_dark_mode": "TumÅĄais reÅžÄĢms", "map_settings_date_range_option_day": "Pēdējās 24 stundas", "map_settings_date_range_option_days": "Pēdējās {days} dienas", - "map_settings_date_range_option_year": "Pēdējo gadu", + "map_settings_date_range_option_year": "Pēdējais gads", "map_settings_date_range_option_years": "Pēdējie {years} gadi", "map_settings_dialog_title": "Kartes IestatÄĢjumi", "map_settings_include_show_archived": "IekÄŧaut Arhivētos", @@ -634,7 +920,7 @@ "map_settings_theme_settings": "Kartes Dizains", "map_zoom_to_see_photos": "Attāliniet, lai redzētu fotoattēlus", "matches": "AtbilstÄĢbas", - "media_type": "Multivides veids", + "media_type": "Faila veids", "memories": "Atmiņas", "memories_all_caught_up": "Å obrÄĢd, tas arÄĢ viss", "memories_check_back_tomorrow": "Atgriezies rÄĢt, lai skatÄĢtu vairāk atmiņu", @@ -643,12 +929,13 @@ "memory": "Atmiņa", "menu": "Izvēlne", "merge": "Apvienot", - "merge_people": "Cilvēku apvienoÅĄana", + "merge_people": "Personu apvienoÅĄana", "merge_people_limit": "Vienlaikus var apvienot ne vairāk kā 5 sejas", - "merge_people_prompt": "Vai vēlies apvienot ÅĄos cilvēkus? Å ÄĢ darbÄĢba ir neatgriezeniska.", - "merge_people_successfully": "Cilvēki veiksmÄĢgi apvienoti", + "merge_people_prompt": "Vai vēlies apvienot ÅĄÄĢs personas? Å ÄĢ darbÄĢba ir neatceÄŧama.", + "merge_people_successfully": "Personas veiksmÄĢgi apvienotas", "minimize": "Minimizēt", "minute": "MinÅĢte", + "minutes": "MinÅĢtes", "missing": "TrÅĢkstoÅĄie", "model": "Modelis", "month": "Mēnesis", @@ -657,48 +944,72 @@ "move": "Pārvietot", "move_off_locked_folder": "Izņemt no slēgtās mapes", "move_to_locked_folder": "Pārvietot uz slēgto mapi", + "move_to_locked_folder_confirmation": "Å ÄĢs fotogrāfijas un video tiks izņemti no visiem albumiem un bÅĢs apskatāmi tikai no slēgtās mapes", + "moved_to_archive": "Pārvietoja {count, plural, one {# failu} other {# failus}} uz arhÄĢvu", "moved_to_library": "Pārvietoja {count, plural, one {# failu} other {# failus}} uz bibliotēku", "moved_to_trash": "Pārvietots uz atkritni", "multiselect_grid_edit_date_time_err_read_only": "Nevar rediÄŖÄ“t read only aktÄĢva(-u) datumu, notiek izlaiÅĄana", "multiselect_grid_edit_gps_err_read_only": "Nevar rediÄŖÄ“t atraÅĄanās vietu read only aktÄĢva(-u) datumu, notiek izlaiÅĄana", + "mute_memories": "Apklusināt atmiņas", "my_albums": "Mani albumi", "name": "Vārds", "name_or_nickname": "Vārds vai iesauka", + "network_requirement_photos_upload": "Izmantot mobilo datu pārraidi, lai dublētu fotoattēlus", + "network_requirement_videos_upload": "Izmantot mobilo datu pārraidi, lai dublētu video", + "network_requirements": "TÄĢkla prasÄĢbas", + "network_requirements_updated": "TÄĢkla prasÄĢbas ir mainÄĢjuÅĄÄs, atiestata dublÄ“ÅĄanas rindu", + "networking_settings": "TÄĢkla iestatÄĢjumi", + "networking_subtitle": "PārvaldÄĢt servera galapunktu iestatÄĢjumus", "never": "nekad", "new_album": "Jauns albums", "new_api_key": "Jauna API atslēga", "new_password": "Jaunā parole", "new_person": "Jauna persona", "new_pin_code": "Jaunais PIN kods", + "new_timeline": "Jaunā laikjosla", "new_user_created": "Izveidots jauns lietotājs", "new_version_available": "PIEEJAMA JAUNA VERSIJA", "next": "Nākamais", "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", + "no_albums_with_name_yet": "Izskatās, ka tev vēl nav albumu ar ÅĄÄdu nosaukumu.", + "no_albums_yet": "Izskatās, ka tev vēl nav neviena albuma.", + "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKÅ ÄļINIET, LAI AUGÅ UPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktÄĢvu", + "no_cast_devices_found": "Nav atrasta neviena pārraides ierÄĢce", + "no_checksum_local": "Nav pieejama kontrolsumma - nevar iegÅĢt vietējos failus", + "no_checksum_remote": "Nav pieejama kontrolsumma - nevar iegÅĢt attālo failu", "no_duplicates_found": "Dublikāti netika atrasti.", "no_exif_info_available": "Nav pieejama exif informācija", + "no_explore_results_message": "AugÅĄupielādē vairāk fotogrāfiju, lai iepazÄĢtu savu kolekciju.", "no_name": "Nav nosaukuma", "no_notifications": "Nav paziņojumu", "no_places": "Nav atraÅĄanās vietu", "no_results": "Nav rezultātu", "no_results_description": "IzmēĪiniet sinonÄĢmu vai vispārÄĢgāku atslēgvārdu", "not_in_any_album": "Nav nevienā albumā", + "not_selected": "Nav izvēlēts", "notes": "PiezÄĢmes", "nothing_here_yet": "Å eit vēl nekā nav", "notification_permission_dialog_content": "Lai iespējotu paziņojumus, atveriet IestatÄĢjumi un atlasiet AtÄŧaut.", "notification_permission_list_tile_content": "PieÅĄÄˇirt atÄŧauju, lai iespējotu paziņojumus.", - "notification_permission_list_tile_enable_button": "Iespējot Paziņojumus", - "notification_permission_list_tile_title": "Paziņojumu AtÄŧaujas", + "notification_permission_list_tile_enable_button": "Iespējot paziņojumus", + "notification_permission_list_tile_title": "Paziņojumu atÄŧaujas", "notification_toggle_setting_description": "Ieslēgt e-pasta paziņojumus", "notifications": "Paziņojumi", "notifications_setting_description": "Paziņojumu pārvaldÄĢba", "oauth": "OAuth", "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", + "offset": "NobÄĢde", "ok": "Labi", + "onboarding": "UzņemÅĄana", + "onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainÄĢt iestatÄĢjumos.", + "onboarding_server_welcome_description": "IestatÄĢsim ÅĄo instanci ar daÅžiem vispārÄĢgiem iestatÄĢjumiem.", + "onboarding_theme_description": "Izvēlies savas instances krāsu motÄĢvu. To vēlāk var mainÄĢt iestatÄĢjumos.", + "onboarding_user_welcome_description": "Sāksim darbu!", "online": "TieÅĄsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -706,13 +1017,18 @@ "open_the_search_filters": "Atvērt meklÄ“ÅĄanas filtrus", "options": "IestatÄĢjumi", "or": "vai", + "organize_into_albums": "Sakārtot albumos", + "organize_into_albums_description": "Ievietot esoÅĄÄs fotogrāfijas albumos, izmantojot paÅĄreizējos sinhronizācijas iestatÄĢjumus", + "organize_your_library": "Bibliotēkas organizÄ“ÅĄana", "original": "oriÄŖināls", "other": "Citi", "other_devices": "Citas ierÄĢces", "other_variables": "Citi mainÄĢgie", "owned": "ÄĒpaÅĄumā", "owner": "ÄĒpaÅĄnieks", + "partner": "Partneris", "partner_can_access": "{partner} var piekÄŧÅĢt", + "partner_can_access_location": "Fotogrāfiju uzņemÅĄanas vieta", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "ApskatÄĢt visu", "partner_page_empty_message": "JÅĢsu fotogrāfijas pagaidām nav kopÄĢgotas ar nevienu partneri.", @@ -721,13 +1037,18 @@ "partner_page_select_partner": "Izvēlēties partneri", "partner_page_shared_to_title": "KopÄĢgots uz", "partner_page_stop_sharing_content": "{partner} vairs nevarēs piekÄŧÅĢt jÅĢsu fotoattēliem.", + "partner_sharing": "KoplietoÅĄana ar partneriem", "partners": "Partneri", "password": "Parole", "password_does_not_match": "Parole nesakrÄĢt", "path": "CeÄŧÅĄ", "pause": "Pauzēt", + "pause_memories": "Pauzēt atmiņas", "paused": "Nopauzēts", - "people": "Cilvēki", + "people": "Personas", + "people_sidebar_description": "ParādÄĢt saiti uz personām sānu joslā", + "permission": "AtÄŧauja", + "permission_empty": "Tava atÄŧauja nedrÄĢkst bÅĢt tukÅĄa", "permission_onboarding_back": "AtpakaÄŧ", "permission_onboarding_continue_anyway": "Tomēr turpināt", "permission_onboarding_get_started": "Darba sākÅĄana", @@ -738,70 +1059,129 @@ "permission_onboarding_request": "Immich nepiecieÅĄama atÄŧauja skatÄĢt jÅĢsu fotoattēlus un videoklipus.", "person": "Persona", "photos": "Fotoattēli", + "photos_and_videos": "Fotogrāfijas un video", "photos_from_previous_years": "Fotogrāfijas no iepriekÅĄÄ“jiem gadiem", + "pick_a_location": "Izvēlies atraÅĄanās vietu", "pin_verification": "PIN koda pārbaude", "place": "AtraÅĄanās vieta", "places": "Vietas", + "play": "Atskaņot", + "play_memories": "Atskaņot atmiņas", "please_auth_to_access": "Lai piekÄŧÅĢtu, lÅĢdzu, autentificējieties", "port": "Ports", "preferences_settings_title": "IestatÄĢjumi", "preview": "PriekÅĄskatÄĢjums", "previous": "IepriekÅĄÄ“jais", + "previous_memory": "IepriekÅĄÄ“jā atmiņa", + "previous_or_next_day": "Dienu uz priekÅĄu/atpakaÄŧ", + "previous_or_next_month": "Mēnesi uz priekÅĄu/atpakaÄŧ", + "previous_or_next_year": "Gadu uz priekÅĄu/atpakaÄŧ", "privacy": "Privātums", "profile": "Profils", "profile_drawer_app_logs": "ÅŊurnāli", "profile_drawer_client_out_of_date_major": "Mobilā lietotne ir novecojusi. LÅĢdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_client_out_of_date_minor": "Mobilā lietotne ir novecojusi. LÅĢdzu, atjaunini to uz jaunāko papildversiju.", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serveris ir novecojis. LÅĢdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. LÅĢdzu, atjaunini to uz jaunāko papildversiju.", + "profile_image_of_user": "{user} profila attēls", + "profile_picture_set": "Profila attēls iestatÄĢts.", + "public_album": "Publisks albums", "purchase_account_info": "AtbalstÄĢtājs", + "purchase_activated_subtitle": "Paldies, ka atbalstāt Immich un atvērtā koda programmatÅĢru", + "purchase_activated_time": "Aktivizēts {date}", + "purchase_activated_title": "Tava atslēga ir sekmÄĢgi aktivizēta", + "purchase_button_activate": "Aktivizēt", "purchase_button_buy": "Pirkt", + "purchase_button_buy_immich": "Iegādāties Immich", "purchase_button_never_show_again": "Nekad vairs nerādÄĢt", "purchase_button_reminder": "Atgādināt man pēc 30 dienām", "purchase_button_remove_key": "Noņemt atslēgu", "purchase_button_select": "Izvēlēties", + "purchase_failed_activation": "Neizdevās aktivizēt! LÅĢdzu, pārbaudi savu e-pastu, lai iegÅĢtu pareizo produkta atslēgu!", + "purchase_individual_description_1": "Individuālam lietotājam", "purchase_individual_description_2": "AtbalstÄĢtāja statuss", + "purchase_individual_title": "Individuāla", "purchase_input_suggestion": "Vai tev ir produkta atslēga? Ievadi atslēgu zemāk", "purchase_license_subtitle": "Nopērc Immich licenci, lai atbalstÄĢtu turpmāku pakalpojuma attÄĢstÄĢbu", "purchase_lifetime_description": "Pirkums uz mÅĢÅžu", "purchase_option_title": "IEGĀDES IESPĒJAS", + "purchase_panel_info_1": "Immich veidoÅĄanai ir nepiecieÅĄams daudz laika un pÅĢÄŧu, un pie tā strādā pilna laika inÅženieri, lai padarÄĢtu to pēc iespējas labāku. MÅĢsu misija ir panākt, lai atvērtā koda programmatÅĢra un ētiska uzņēmējdarbÄĢbas prakse kÄŧÅĢtu par ilgtspējÄĢgu ienākumu avotu izstrādātājiem un izveidotu privātumu respektējoÅĄu ekosistēmu ar reālām alternatÄĢvām ekspluatējoÅĄiem mākoņpakalpojumiem.", + "purchase_panel_info_2": "Tā kā mēs esam apņēmuÅĄies nepievienot maksas funkcionalitāti, ÅĄis pirkums nepieÅĄÄˇirs jums nekādas papildu Immich funkcijas. Mēs paÄŧaujamies uz tādiem lietotājiem kā jÅĢs, lai atbalstÄĢtu nepārtrauktu Immich attÄĢstÄĢbu.", "purchase_panel_title": "Atbalsti projektu", + "purchase_per_server": "Par serveri", + "purchase_per_user": "Par lietotāju", "purchase_remove_product_key": "Noņemt produkta atslēgu", + "purchase_remove_product_key_prompt": "Vai tieÅĄÄm vēlaties noņemt produkta atslēgu?", "purchase_remove_server_product_key": "Noņemt servera produkta atslēgu", + "purchase_remove_server_product_key_prompt": "Vai tieÅĄÄm vēlaties noņemt Servera produkta atslēgu?", "purchase_server_description_1": "Visam serverim", "purchase_server_description_2": "AtbalstÄĢtāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", + "queue_status": "Ierindo {count}/{total}", "rating_clear": "Noņemt vērtējumu", + "rating_description": "RādÄĢt EXIF vērtējumu informācijas panelÄĢ", + "reaction_options": "Reakcijas iespējas", "read_changelog": "LasÄĢt izmaiņu sarakstu", + "ready_for_upload": "Gatavs augÅĄupielādei", "recently_added_page_title": "Nesen Pievienotais", + "refresh": "Atsvaidzināt", + "refresh_faces": "Atsvaidzināt sejas", + "refresh_metadata": "Atsvaidzināt metadatus", + "refresh_thumbnails": "Atsvaidzināt sÄĢktēlus", + "refreshed": "Atsvaidzināts", + "refreshes_every_file": "Vēlreiz nolasa esoÅĄos un jaunos failus", + "refreshing_faces": "Atsvaidzina sejas", + "refreshing_metadata": "Atsvaidzina metadatus", + "remote": "Attāli", "remove": "Noņemt", + "remove_assets_title": "Izņemt failus?", + "remove_custom_date_range": "Novākt pielāgoto datuma intervālu", + "remove_deleted_assets": "Izņemt dzēstos failus", "remove_from_album": "Noņemt no albuma", + "remove_from_album_action_prompt": "No albuma izņemti {count} faili", "remove_from_favorites": "Noņemt no izlases", + "remove_from_lock_folder_action_prompt": "No slēgtās mapes izņemti {count} faili", "remove_from_locked_folder": "Izņemt no slēgtās mapes", + "remove_memory": "Noņemt atmiņu", + "remove_photo_from_memory": "Noņemt fotogrāfiju no ÅĄÄĢs atmiņas", + "remove_url": "Noņemt URL", "remove_user": "Noņemt lietotāju", "removed_api_key": "Noņēma API atslēgu: {name}", "removed_from_archive": "Noņēma no arhÄĢva", "removed_from_favorites": "Noņēma no izlases", "removed_from_favorites_count": "{count, plural, other {Izņēma #}} no izlases", + "removed_memory": "Noņēma atmiņu", + "removed_photo_from_memory": "Noņēma fotogrāfiju no atmiņas", "rename": "Pārsaukt", "repair": "Remonts", "replace_with_upload": "Aizstāt ar augÅĄupielādi", + "repository": "Repozitorijs", "require_user_to_change_password_on_first_login": "PieprasÄĢt lietotājam mainÄĢt paroli pēc pirmās pieteikÅĄanās", "rescan": "Pārskenēt atkārtoti", + "reset": "AtiestatÄĢt", + "reset_password": "AtiestatÄĢt paroli", + "reset_people_visibility": "AtiestatÄĢt personu redzamÄĢbu", + "reset_pin_code": "AtiestatÄĢt PIN kodu", + "reset_sqlite": "AtiestatÄĢt SQLite datubāzi", + "reset_to_default": "AtiestatÄĢt noklusējuma iestatÄĢjumus", "resolve_duplicates": "Atrisināt dublÄ“ÅĄanās gadÄĢjumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", "restore_all": "Atjaunot visu", + "restore_trash_action_prompt": "{count} atjaunoti no atkritnes", "restore_user": "Atjaunot lietotāju", "resume": "Turpināt", "retry_upload": "Atkārtot augÅĄupielādi", "review_duplicates": "PārskatÄĢt dublikātus", + "review_large_files": "PārskatÄĢt lielos failus", "role": "Loma", "role_editor": "Redaktors", "role_viewer": "SkatÄĢtājs", "save": "Saglabāt", + "save_to_gallery": "Saglabāt galerijā", "saved_api_key": "API atslēga saglabāta", "saved_profile": "Profils saglabāts", "saved_settings": "IestatÄĢjumi saglabāti", @@ -813,11 +1193,28 @@ "scanning_for_album": "Skenē albumu...", "search": "Meklēt", "search_albums": "Meklēt albumus", + "search_by_context": "Meklēt pēc konteksta", + "search_by_description": "Meklēt pēc apraksta", + "search_by_description_example": "Pārgājiens LÄĢgatnē", + "search_by_filename": "Meklēt pēc faila nosaukuma vai paplaÅĄinājuma", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", + "search_camera_make": "Meklēt pēc fotokameras raÅžotāja...", + "search_camera_model": "Meklēt pēc fotokameras modeÄŧa...", + "search_city": "Meklēt pēc pilsētas...", + "search_country": "Meklēt pēc valsts...", "search_filter_apply": "Lietot filtru", + "search_filter_camera_title": "Izvēlies fotokameras veidu", + "search_filter_date": "Datums", "search_filter_display_option_not_in_album": "Nav albumā", - "search_no_people": "Nav cilvēku", - "search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"", + "search_filter_filename": "Meklēt pēc faila nosaukuma", + "search_filter_location": "AtraÅĄanās vieta", + "search_filter_location_title": "Izvēlies atraÅĄanās vietu", + "search_filter_media_type": "Multivides veids", + "search_filter_media_type_title": "Izvēlies multivides veidu", + "search_for_existing_person": "Meklēt esoÅĄu personu", + "search_no_people": "Nav personu", + "search_no_people_named": "Nav personas ar vārdu \"{name}\"", + "search_options": "MeklÄ“ÅĄanas iespējas", "search_page_categories": "Kategorijas", "search_page_motion_photos": "KustÄĢbu Fotoattēli", "search_page_no_objects": "Informācija par Objektiem nav pieejama", @@ -828,21 +1225,38 @@ "search_page_view_all_button": "ApskatÄĢt visu", "search_page_your_activity": "JÅĢsu aktivitāte", "search_page_your_map": "JÅĢsu Karte", - "search_people": "Meklēt cilvēkus", + "search_people": "Meklēt personas", + "search_rating": "Meklēt pēc vērtējuma...", "search_result_page_new_search_hint": "Jauns Meklējums", + "search_settings": "Meklēt iestatÄĢjumos", + "search_state": "Meklēt pēc ÅĄtata...", "search_suggestion_list_smart_search_hint_1": "Viedā meklÄ“ÅĄana pēc noklusējuma ir iespējota, lai meklētu metadatos, izmanto sintaksi ", "search_suggestion_list_smart_search_hint_2": "m:jÅĢsu-meklÄ“ÅĄanas-frāze", - "search_your_photos": "Meklēt JÅĢsu fotoattēlus", + "search_type": "MeklÄ“ÅĄanas veids", + "search_your_photos": "Meklēt fotoattēlos", "second": "Sekunde", + "see_all_people": "SkatÄĢt visas personas", "select_album_cover": "Izvēlieties albuma vāciņu", "select_all_duplicates": "AtlasÄĢt visus dublikātus", + "select_avatar_color": "Izvēlies avatāra krāsu", + "select_face": "Izvēlies seju", + "select_from_computer": "Izvēlēties no datora", + "select_keep_all": "AtzÄĢmēt visus paturÄ“ÅĄanai", + "select_library_owner": "Izvēlies bibliotēkas ÄĢpaÅĄnieku", + "select_new_face": "Izvēlies jaunu seju", "select_photos": "Fotoattēlu Izvēle", + "select_trash_all": "AtzÄĢmēt visus dzÄ“ÅĄanai", "select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu", + "selected": "Izvēlētie", + "selected_gps_coordinates": "Izvēlētās ÄŖeogrāfiskās koordinātas", "server_info_box_app_version": "Aplikācijas Versija", "server_info_box_server_url": "Servera URL", "server_online": "Serveris tieÅĄsaistē", + "server_privacy": "Servera privātums", "server_stats": "Servera statistika", "server_version": "Servera versija", + "set_as_album_cover": "IestatÄĢt kā albuma vāciņu", + "set_as_profile_picture": "IestatÄĢt kā profila attēlu", "set_date_of_birth": "IestatÄĢt dzimÅĄanas datumu", "setting_image_viewer_help": "DetaÄŧu skatÄĢtājs vispirms ielādē mazo sÄĢktēlu, pēc tam ielādē vidēja lieluma priekÅĄskatÄĢjumu (ja iespējots), visbeidzot ielādē oriÄŖinālu (ja iespējots).", "setting_image_viewer_original_subtitle": "Iespējot sākotnējā pilnas izÅĄÄˇirtspējas attēla (liels!) ielādi. Atspējot, lai samazinātu datu lietojumu (gan tÄĢklā, gan ierÄĢces keÅĄatmiņā).", @@ -857,18 +1271,20 @@ "setting_notifications_notify_minutes": "{count} minÅĢtes", "setting_notifications_notify_never": "nekad", "setting_notifications_notify_seconds": "{count} sekundes", - "setting_notifications_single_progress_subtitle": "Detalizēta augÅĄupielādes progresa informācija par katru aktÄĢvu", + "setting_notifications_single_progress_subtitle": "Detalizēta augÅĄupielādes progresa informācija par katru failu", "setting_notifications_single_progress_title": "RādÄĢt fona dublējuma detalizēto progresu", "setting_notifications_subtitle": "Paziņojumu preferenču pielāgoÅĄana", - "setting_notifications_total_progress_subtitle": "Kopējais augÅĄupielādes progress (pabeigti/kopējie aktÄĢvi)", + "setting_notifications_total_progress_subtitle": "Kopējais augÅĄupielādes progress (pabeigti/kopējie faili)", "setting_notifications_total_progress_title": "RādÄĢt fona dublējuma kopējo progresu", "setting_video_viewer_looping_title": "Cikliski", + "setting_video_viewer_original_video_subtitle": "Straumējot video no servera, izmantot oriÄŖinālu, pat ja ir pieejama pārkodÄ“ÅĄana. Tas var izraisÄĢt buferÄ“ÅĄanu. Lokāli pieejamie video tiek atskaņoti oriÄŖinālajā kvalitātē, neatkarÄĢgi no ÅĄÄĢs iestatÄĢjuma.", + "setting_video_viewer_original_video_title": "Vienmēr izmantot oriÄŖinālo video", "settings": "IestatÄĢjumi", "settings_require_restart": "LÅĢdzu, restartējiet Immich, lai lietotu ÅĄo iestatÄĢjumu", "setup_pin_code": "UzstādÄĢt PIN kodu", "share": "KopÄĢgot", "share_add_photos": "Pievienot fotoattēlus", - "share_assets_selected": "{count} izvēlēti", + "share_assets_selected": "{count} atlasÄĢti", "share_dialog_preparing": "Notiek sagatavoÅĄana...", "shared": "KopÄĢgots", "shared_album_activities_input_disable": "Komentāri atslēgti", @@ -877,7 +1293,7 @@ "shared_album_section_people_action_error": "KÄŧÅĢme pametot/noņemot no albuma", "shared_album_section_people_action_leave": "Noņemt lietotāju no albuma", "shared_album_section_people_action_remove_user": "Noņemt lietotāju no albuma", - "shared_album_section_people_title": "CILVĒKI", + "shared_album_section_people_title": "PERSONAS", "shared_intent_upload_button_progress_text": "AugÅĄupielādēti {current} / {total}", "shared_link_app_bar_title": "KopÄĢgotas Saites", "shared_link_clipboard_copied_massage": "Ievietots starpliktuvē", @@ -913,34 +1329,54 @@ "sharing_page_album": "KopÄĢgotie albumi", "sharing_page_description": "Izveidojiet koplietojamus albumus, lai kopÄĢgotu fotoattēlus un videoklipus ar JÅĢsu tÄĢkla lietotājiem.", "sharing_page_empty_list": "TUKÅ S SARAKSTS", + "sharing_sidebar_description": "ParādÄĢt saiti uz kopÄĢgoÅĄanu sānu joslā", "sharing_silver_appbar_create_shared_album": "Izveidot kopÄĢgotu albumu", "sharing_silver_appbar_share_partner": "DalÄĢties ar partneri", "show_album_options": "RādÄĢt albuma iespējas", "show_albums": "RādÄĢt albumus", - "show_all_people": "RādÄĢt visus cilvēkus", - "show_and_hide_people": "RādÄĢt un slēpt cilvēkus", + "show_all_people": "RādÄĢt visas personas", + "show_and_hide_people": "RādÄĢt un slēpt personas", "show_file_location": "RādÄĢt faila atraÅĄanās vietu", "show_gallery": "RādÄĢt galeriju", - "show_hidden_people": "RādÄĢt paslēptos cilvēkus", + "show_hidden_people": "RādÄĢt paslēptās personas", + "show_in_timeline": "ParādÄĢt laika skalā", + "show_in_timeline_setting_description": "RādÄĢt ÅĄÄĢ lietotāja fotogrāfijas un video tavā laika skalā", + "show_keyboard_shortcuts": "RādÄĢt tastatÅĢras saÄĢsnes", "show_metadata": "RādÄĢt metadatus", + "show_or_hide_info": "RādÄĢt vai slēpt informāciju", + "show_password": "ParādÄĢt paroli", + "show_person_options": "RādÄĢt personas opcijas", "show_progress_bar": "RādÄĢt progresa joslu", + "show_search_options": "RādÄĢt meklÄ“ÅĄanas opcijas", + "show_shared_links": "RādÄĢt kopÄĢgotās saites", + "show_slideshow_transition": "RādÄĢt slÄĢdrādes pāreju", "show_supporter_badge": "AtbalstÄĢtāja nozÄĢmÄĢte", "show_supporter_badge_description": "RādÄĢt atbalstÄĢtāja nozÄĢmÄĢti", + "show_text_search_menu": "RādÄĢt teksta meklÄ“ÅĄanas izvēlni", "shuffle": "Jaukta", + "sidebar": "Sānu josla", + "sidebar_display_description": "ParādÄĢt saiti uz skatu sānu joslā", + "sign_out": "Iziet", + "sign_up": "ReÄŖistrēties", "size": "Izmērs", + "skip_to_content": "Pāriet uz saturu", + "skip_to_folders": "Pāriet uz mapēm", "slideshow": "SlÄĢdrāde", "slideshow_settings": "SlÄĢdrādes iestatÄĢjumi", "sort_albums_by": "Kārtot albumus pēc...", "sort_created": "Izveides datums", - "sort_items": "VienÄĢbu skaits", + "sort_items": "Vienumu skaits", "sort_modified": "Izmaiņu datums", + "sort_newest": "Jaunākā fotogrāfija", "sort_oldest": "Vecākā fotogrāfija", - "sort_people_by_similarity": "Sakārtot cilvēkus pēc lÄĢdzÄĢbas", + "sort_people_by_similarity": "Sakārtot personas pēc lÄĢdzÄĢbas", "sort_recent": "Nesenākā fotogrāfija", "sort_title": "Nosaukums", "source": "Pirmkods", "stack": "Apvienot kaudzē", + "start": "Sākt", "start_date": "Sākuma datums", + "start_date_before_end_date": "Sākuma datumam jābÅĢt pirms beigu datuma", "state": "Å tats", "status": "Statuss", "stop_photo_sharing": "Beigt kopÄĢgot jÅĢsu fotogrāfijas?", @@ -954,33 +1390,49 @@ "support": "Atbalsts", "support_and_feedback": "Atbalsts un atsauksmes", "sync": "Sinhronizēt", + "sync_status": "Sinhronizācijas statuss", + "sync_status_subtitle": "SkatÄĢt un pārvaldÄĢt sinhronizācijas sistēmu", "theme": "Dizains", - "theme_setting_asset_list_storage_indicator_title": "RādÄĢt krātuves indikatoru uz aktÄĢvu elementiem", + "theme_setting_asset_list_storage_indicator_title": "RādÄĢt krātuves indikatoru uz attēliem reÅžga skatā", "theme_setting_asset_list_tiles_per_row_title": "Failu skaits rindā ({count})", + "theme_setting_colorful_interface_subtitle": "Piemērot pamatkrāsu fona virsmām.", + "theme_setting_colorful_interface_title": "Krāsaina saskarne", "theme_setting_image_viewer_quality_subtitle": "Attēlu skatÄĢtāja detaÄŧu kvalitātes pielāgoÅĄana", "theme_setting_image_viewer_quality_title": "Attēlu skatÄĢtāja kvalitāte", + "theme_setting_primary_color_subtitle": "Izvēlies krāsu galvenajām darbÄĢbām un akcentiem.", + "theme_setting_primary_color_title": "Pamatkrāsa", + "theme_setting_system_primary_color_title": "Izmantot sistēmas krāsu", "theme_setting_system_theme_switch": "Automātisks (sekot sistēmas iestatÄĢjumiem)", "theme_setting_theme_subtitle": "Izvēlieties programmas dizaina iestatÄĢjumu", "theme_setting_three_stage_loading_subtitle": "TrÄĢspakāpju ielāde var palielināt ielādÄ“ÅĄanas veiktspēju, bet izraisa ievērojami lielāku tÄĢkla noslodzi", "theme_setting_three_stage_loading_title": "Iespējot trÄĢspakāpju ielādi", "they_will_be_merged_together": "Tās tiks apvienotas", "third_party_resources": "TreÅĄo puÅĄu resursi", + "timeline": "Laika skala", "timezone": "Laika zona", "to_archive": "Arhivēt", "to_change_password": "MainÄĢt paroli", "to_favorite": "Pievienot izlasei", + "to_trash": "Pārvietot uz atkritni", "toggle_settings": "Pārslēgt iestatÄĢjumus", "total": "Kopā", "total_usage": "Kopējais lietojums", "trash": "Atkritne", - "trash_all": "Dzēst Visu", + "trash_action_prompt": "{count} pārvietoja uz atkritni", + "trash_all": "Dzēst visu", + "trash_count": "Pārvietot uz atkritni {count, number}", + "trash_delete_asset": "Pārvietot uz atkritni/dzēst failu", + "trash_emptied": "Atkritne iztukÅĄota", + "trash_no_results_message": "Å eit parādÄĢsies uz atkritni pārvietotās fotogrāfijas un video.", "trash_page_delete_all": "Dzēst Visu", - "trash_page_empty_trash_dialog_content": "Vai vēlaties iztukÅĄot savus izmestos aktÄĢvus? Tie tiks neatgriezeniski izņemti no Immich", + "trash_page_empty_trash_dialog_content": "Vai vēlaties iztukÅĄot savus izmestos failus? Tie tiks neatgriezeniski izņemti no Immich", "trash_page_info": "Atkritnes vienumi tiks neatgriezeniski dzēsti pēc {days} dienām", "trash_page_no_assets": "Atkritnē nav aktÄĢvu", "trash_page_restore_all": "Atjaunot Visu", "trash_page_select_assets_btn": "AtlasÄĢt aktÄĢvus", "trash_page_title": "Atkritne ({count})", + "trashed_items_will_be_permanently_deleted_after": "Faili no atkritnes tiks neatgriezeniski dzēsti pēc {days, plural, one {# dienas} other {# dienām}}.", + "troubleshoot": "Problēmu novērÅĄana", "type": "Veids", "unable_to_change_pin_code": "Neizdevās nomainÄĢt PIN kodu", "unable_to_setup_pin_code": "Neizdevās uzstādÄĢt PIN kodu", @@ -993,29 +1445,40 @@ "unlimited": "NeierobeÅžots", "unnamed_album": "Albums bez nosaukuma", "unsaved_change": "Nesaglabāta izmaiņa", + "unselect_all": "Atcelt visu atlasi", "unstack": "At-Stekot", "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", "upload": "AugÅĄupielādēt", + "upload_action_prompt": "{count} ierindoti augÅĄupielādei", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktÄĢva(-u) dublējumu uz servera?", "upload_dialog_title": "AugÅĄupielādēt AktÄĢvu", + "upload_finished": "AugÅĄupielāde pabeigta", "upload_status_duplicates": "Dublikāti", "upload_status_errors": "KÄŧÅĢdas", "upload_status_uploaded": "AugÅĄupielādēts", "upload_to_immich": "AugÅĄupielādēt Immich ({count})", + "uploading": "AugÅĄupielādē", + "uploading_media": "AugÅĄupielādē failus", "url": "URL", "usage": "Lietojums", "use_biometric": "Izmantot biometrisko autentifikāciju", + "use_current_connection": "izmantot paÅĄreizējo savienojumu", + "use_custom_date_range": "Izmantot pielāgotu datuma intervālu", "user": "Lietotājs", "user_has_been_deleted": "Å is lietotājs ir dzēsts.", "user_id": "Lietotāja ID", "user_pin_code_settings": "PIN kods", + "user_privacy": "Lietotāju privātums", "user_purchase_settings": "Iegādāties", "user_purchase_settings_description": "Pirkuma pārvaldÄĢba", "user_usage_detail": "Informācija par lietotāju lietojumu", + "user_usage_stats": "Konta izmantoÅĄanas statistika", + "user_usage_stats_description": "SkatÄĢt konta lietojuma statistiku", "username": "Lietotājvārds", "users": "Lietotāji", "utilities": "RÄĢki", + "validate": "PārbaudÄĢt", "variables": "MainÄĢgie", "version": "Versija", "version_announcement_closing": "Tavs draugs, Alekss", @@ -1023,14 +1486,28 @@ "version_history": "Versiju vēsture", "version_history_item": "{version} uzstādÄĢta {date}", "video": "Videoklips", + "video_hover_setting_description": "Atskaņot video sÄĢktēlu, kad peles kursors atrodas virs objekta. Pat ja funkcija ir atspējota, atskaņoÅĄanu var sākt, uzvirzot kursoru uz atskaņoÅĄanas ikonas.", "videos": "Videoklipi", + "view": "ApskatÄĢt", "view_album": "SkatÄĢt Albumu", "view_all": "ApskatÄĢt visu", "view_all_users": "SkatÄĢt visus lietotājus", + "view_details": "ApskatÄĢt informāciju", + "view_in_timeline": "SkatÄĢt laika skalā", + "view_link": "SkatÄĢt saiti", + "view_links": "SkatÄĢt saites", + "view_name": "ApskatÄĢt", + "view_next_asset": "SkatÄĢt nākamo failu", + "view_previous_asset": "SkatÄĢt iepriekÅĄÄ“jo failu", + "view_qr_code": "SkatÄĢt QR kodu", + "view_similar_photos": "SkatÄĢt lÄĢdzÄĢgas fotogrāfijas", + "view_stack": "ApskatÄĢt kaudzi", + "view_user": "ApskatÄĢt lietotāju", "viewer_remove_from_stack": "Noņemt no Steka", "viewer_stack_use_as_main_asset": "Izmantot kā Galveno AktÄĢvu", "viewer_unstack": "At-Stekot", "waiting": "Gaida", + "warning": "BrÄĢdinājums", "week": "NedēÄŧa", "welcome": "Laipni lÅĢgti", "welcome_to_immich": "Laipni lÅĢgti Immich", diff --git a/i18n/mk.json b/i18n/mk.json index 31d223cbd6..8430ae117e 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -4,6 +4,7 @@ "account_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са ĐŋŅ€ĐžŅ„Đ¸ĐģĐžŅ‚", "acknowledge": "ĐŸŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐž", "action": "АĐēŅ†Đ¸Ņ˜Đ°", + "action_common_update": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜", "actions": "АĐēŅ†Đ¸Đ¸", "active": "АĐēŅ‚Đ¸Đ˛ĐŊи", "activity": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚", @@ -13,6 +14,8 @@ "add_a_location": "Додади ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ°", "add_a_name": "Додади иĐŧĐĩ", "add_a_title": "Додади ĐŊĐ°ŅĐģОв", + "add_birthday": "Додади Ņ€ĐžĐ´ĐĩĐŊĐ´ĐĩĐŊ", + "add_endpoint": "Додади ĐēŅ€Đ°Ņ˜ĐŊа Ņ‚ĐžŅ‡Đēа", "add_exclusion_pattern": "Додади ŅˆĐ°ĐąĐģĐžĐŊ Са Đ¸ŅĐēĐģŅƒŅ‡ŅƒĐ˛Đ°ŅšĐĩ", "add_import_path": "Додади ĐŋĐ°Ņ‚ĐĩĐēа Са иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ŅšĐĩ", "add_location": "Додади ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ°", @@ -20,8 +23,15 @@ "add_partner": "Додади ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€", "add_path": "Додади ĐŋĐ°Ņ‚ĐĩĐēа", "add_photos": "Додади ҁĐģиĐēи", + "add_tag": "Додади ОСĐŊаĐēа", "add_to": "Додади вОâ€Ļ", "add_to_album": "Додади вО аĐģĐąŅƒĐŧ", + "add_to_album_bottom_sheet_added": "ДодадĐĩĐŊĐž вО {album}", + "add_to_album_bottom_sheet_already_exists": "ВĐĩҜĐĩ вО {album}", + "add_to_album_bottom_sheet_some_local_assets": "НĐĩĐēОи ĐģĐžĐēаĐģĐŊи Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐŊĐĩ ĐŧĐžĐļĐĩа да ҁĐĩ Đ´ĐžĐ´Đ°Đ´Đ°Ņ‚ вО аĐģĐąŅƒĐŧĐžŅ‚", + "add_to_album_toggle": "ĐŸŅ€ĐžĐŧĐĩĐŊи Ņ˜Đ° ҁĐĩĐģĐĩĐēŅ†Đ¸Ņ˜Đ°Ņ‚Đ° Са {album}", + "add_to_albums": "Додади вО аĐģĐąŅƒĐŧи", + "add_to_albums_count": "Додади вО аĐģĐąŅƒĐŧи ({count})", "add_to_shared_album": "Додади вО ҁĐŋОдĐĩĐģĐĩĐŊ аĐģĐąŅƒĐŧ", "add_url": "Додади URL", "added_to_archive": "ДодадĐĩĐŊĐž вО Đ°Ņ€Ņ…Đ¸Đ˛Đ°", @@ -29,17 +39,25 @@ "added_to_favorites_count": "ДодадĐĩĐŊи {count, number} вО ĐžĐŧиĐģĐĩĐŊи", "admin": { "add_exclusion_pattern_description": "Додади ŅˆĐ°ĐąĐģĐžĐŊи Са Đ¸ŅĐēĐģŅƒŅ‡ŅƒĐ˛Đ°ŅšĐĩ. ĐŸĐžĐ´Đ´Ņ€ĐļаĐŊĐž Đĩ ĐēĐžŅ€Đ¸ŅŅ‚ĐĩҚĐĩ ĐŊа glob ŅĐž *, **, и ?. За да ҁĐĩ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Đ°Ņ‚ ŅĐ¸Ņ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēи вО ĐēĐžŅ˜ йиĐģĐž Đ´Đ¸Ņ€ĐĩĐēŅ‚ĐžŅ€Đ¸ŅƒĐŧ иĐŧĐĩĐŊŅƒĐ˛Đ°ĐŊ \"Raw\", ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ \"**/Raw/**\". За да ҁĐĩ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ°Đ°Ņ‚ ŅĐ¸Ņ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēи ŅˆŅ‚Đž ĐˇĐ°Đ˛Ņ€ŅˆŅƒĐ˛Đ°Đ°Ņ‚ ŅĐž \".tif\", ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ \"**/*.tif\". За да ҁĐĩ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€Đ° аĐŋŅĐžĐģŅƒŅ‚ĐŊа ĐŋĐ°Ņ‚ĐĩĐēа, ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ \"/path/to/ignore/**\".", + "admin_user": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐĩĐŊ ĐšĐžŅ€Đ¸ŅĐŊиĐē", "asset_offline_description": "Ова ҁҀĐĩĐ´ŅŅ‚Đ˛Đž Од ĐĩĐēҁ҂ĐĩŅ€ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа вĐĩҜĐĩ ĐŊĐĩ Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ˜Đ´ĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐēĐžŅ‚ и Đĩ ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊĐž вО Ņ“ŅƒĐąŅ€Đĩ. АĐēĐž Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ° йиĐģа ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊа вО Ņ€Đ°ĐŧĐēĐ¸Ņ‚Đĩ ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ Ņ˜Đ° Đ˛Đ°ŅˆĐ°Ņ‚Đ° Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēа ĐģиĐŊĐ¸Ņ˜Đ° Са ĐŊĐžĐ˛ĐžŅ‚Đž ŅĐžĐžĐ´Đ˛ĐĩŅ‚ĐŊĐž ҁҀĐĩĐ´ŅŅ‚Đ˛Đž. За да ĐŗĐž Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ Ова ҁҀĐĩĐ´ŅŅ‚Đ˛Đž, ĐžŅĐ¸ĐŗŅƒŅ€Đ°Ņ˜Ņ‚Đĩ ҁĐĩ Đ´ĐĩĐēа Đ´ĐžĐģ҃ĐŊавĐĩĐ´ĐĩĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ‚ĐĩĐēа ĐŧĐžĐļĐĩ да йидĐĩ ĐŋŅ€Đ¸ŅŅ‚Đ°ĐŋĐĩĐŊа Од Immich и ҁĐēĐĩĐŊĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ Ņ˜Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°.", "authentication_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đ°", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐģОСиĐŊĐēи, OAuth, и Đ´Ņ€ŅƒĐŗĐ¸ ĐŋĐžŅŅ‚Đ°Đ˛Đēи Са Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸ĐēĐ°Ņ†Đ¸Ņ˜Đ°", "authentication_settings_disable_all": "ДаĐģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи Đ´ĐĩĐēа ŅĐ°ĐēĐ°Ņ‚Đĩ да ĐŗĐ¸ Đ¸ŅĐēĐģŅƒŅ‡Đ¸Ņ‚Đĩ ŅĐ¸Ņ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´Đ¸ Са ĐŊĐ°Ņ˜Đ°Đ˛Đ°? ĐĻĐĩĐģĐžŅĐŊĐž ҜĐĩ йидĐĩ ĐžĐŊĐĩвОСĐŧĐžĐļĐĩĐŊĐž ĐŊĐ°Ņ˜Đ°Đ˛ŅƒĐ˛Đ°ŅšĐĩ.", "authentication_settings_reenable": "За ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž да ОвОСĐŧĐžĐļĐ¸Ņ‚Đĩ, Đ¸ŅĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ‚Đĩ ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐēĐžĐŧаĐŊда.", "background_task_job": "ПозадиĐŊҁĐēи ĐˇĐ°Đ´Đ°Ņ‡Đ¸", - "backup_database": "Đ ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° Од ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", + "backup_database": "ĐšŅ€ĐĩĐ¸Ņ€Đ°Ņ˜ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° Од ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", "backup_database_enable_description": "ОвозĐŧĐžĐļи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии Од ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", "backup_keep_last_amount": "КоĐģĐ¸Ņ‡Đ¸ĐŊа ĐŊа ĐŋŅ€ĐĩŅ‚Ņ…ĐžĐ´ĐŊи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии Са Ņ‡ŅƒĐ˛Đ°ŅšĐĩ", - "backup_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии", - "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐŋĐžŅŅ‚Đ°Đ˛Đēи Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", + "backup_onboarding_1_description": "ĐŊĐ°Đ´Đ˛ĐžŅ€Đĩ҈ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° вО ОйĐģаĐēĐžŅ‚ иĐģи ĐŊа Đ´Ņ€ŅƒĐŗĐ° Ņ„Đ¸ĐˇĐ¸Ņ‡Đēа ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ°.", + "backup_onboarding_2_description": "ĐģĐžĐēаĐģĐŊи ĐēĐžĐŋии ĐŊа Ņ€Đ°ĐˇĐģĐ¸Ņ‡ĐŊи ŅƒŅ€Đĩди. Ова ĐŗĐ¸ вĐēĐģŅƒŅ‡ŅƒĐ˛Đ° и ĐžŅĐŊОвĐŊĐ¸Ņ‚Đĩ Ņ„Ņ˜Đ°ĐģОви и Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° Од Đ¸ŅŅ‚Đ¸Ņ‚Đĩ Ņ„Đ°Ņ˜ĐģОви ĐģĐžĐēаĐģĐŊĐž.", + "backup_onboarding_3_description": "ŅĐ¸Ņ‚Đĩ ĐēĐžĐŋии Од Ņ‚Đ˛ĐžĐ¸Ņ‚Đĩ ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸, вĐēĐģŅƒŅ‡ŅƒĐ˛Đ°Ņ˜ŅœĐ¸ и ĐžŅ€ĐŗĐ¸ĐŊаĐģĐŊĐ¸Ņ‚Đĩ Ņ„Đ°Ņ˜ĐģОви. Ова вĐēĐģŅƒŅ‡ŅƒĐ˛Đ° и 1 ĐŊĐ°Đ´Đ˛ĐžŅ€Đĩ҈ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° и 2 ĐģĐžĐēаĐģĐŊи ĐēĐžĐŋии.", + "backup_onboarding_description": "3-2-1 ŅŅ‚Ņ€Đ°Ņ‚ĐĩĐŗĐ¸Ņ˜Đ° Са Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° Đĩ ĐŋŅ€ĐĩĐŋĐžŅ€Đ°Ņ‡Đ°ĐŊĐž Са да ĐŗĐ¸ ĐˇĐ°ŅˆŅ‚Đ¸Ņ‚Đ¸ Ņ‚Đ˛ĐžĐ¸Ņ‚Đĩ ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸. ĐŸĐžŅ‚Ņ€ĐĩĐąĐŊĐž Đĩ да Ņ‡ŅƒĐ˛Đ°Ņˆ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии Од Ņ‚Đ˛ĐžĐ¸Ņ‚Đĩ ĐŋŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸/видĐĩа ĐēаĐēĐž и ĐąĐ°ĐˇĐ°Ņ‚Đ° Са ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸ ĐŊа Immich Са ҆ĐĩĐģĐžŅĐŊĐž Ņ€Đĩ҈ĐĩĐŊиĐĩ Са ĐˇĐ°Ņ‡ŅƒĐ˛ŅƒĐ˛Đ°ŅšĐĩ ĐŊа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ°", + "backup_onboarding_footer": "ПовĐĩҜĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ ĐžĐēĐžĐģ҃ ĐŋŅ€Đ°Đ˛ĐĩҚĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии Са Immich, вĐĩ ĐŧĐžĐģаĐŧ да ҁĐĩ Ņ€ĐĩŅ„ĐĩŅ€ĐĩĐŊŅ†Đ¸Ņ€Đ°Ņ‚Đĩ ĐŊа Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ˜Đ°Ņ‚Đ°", + "backup_onboarding_parts_title": "3-2-1 Ņ€ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ° вĐēĐģŅƒŅ‡ŅƒĐ˛Đ°:", + "backup_onboarding_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊи ĐēĐžĐŋии", + "backup_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи иСвĐĩĐˇŅƒĐ˛Đ°ŅšĐĩ йаСа ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", + "backup_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐŋĐžŅŅ‚Đ°Đ˛Đēи Са иСвĐĩĐˇŅƒĐ˛Đ°ŅšĐĩ ĐŊа ĐąĐ°ĐˇĐ°Ņ‚Đ° ĐŊа ĐŋĐžĐ´Đ°Ņ‚ĐžŅ†Đ¸", "cleared_jobs": "Đ˜ŅŅ‡Đ¸ŅŅ‚ĐĩĐŊи ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Са: {job}", "config_set_by_file": "КоĐŊĐŗĐ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸Ņ˜Đ°Ņ‚Đ° Đĩ ĐŧĐžĐŧĐĩĐŊŅ‚Đ°ĐģĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛ĐĩĐŊа Од ĐēĐžĐŊŅ„Đ¸ĐŗŅƒŅ€Đ°Ņ†Đ¸ŅĐēа Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа", "confirm_delete_library": "ДаĐģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи Đ´ĐĩĐēа ŅĐ°ĐēĐ°Ņ‚Đĩ да Ņ˜Đ° Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩŅ‚Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ° {library}?", @@ -47,19 +65,40 @@ "confirm_email_below": "За да ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ, вĐŊĐĩҁĐĩŅ‚Đĩ \"{email}\" Đ´ĐžĐģĐĩ", "confirm_reprocess_all_faces": "ДаĐģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи Đ´ĐĩĐēа ŅĐ°ĐēĐ°Ņ‚Đĩ да ҁĐĩ ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ°Ņ‚ ОдĐŊОвО ŅĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†Đ°? Ова ҜĐĩ ĐŗĐ¸ Đ¸ĐˇĐąŅ€Đ¸ŅˆĐĩ и ŅĐ¸Ņ‚Đĩ иĐŧĐĩĐŊŅƒĐ˛Đ°ĐŊи ĐģŅƒŅ“Đĩ.", "confirm_user_password_reset": "ДаĐģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи Đ´ĐĩĐēа ŅĐ°ĐēĐ°Ņ‚Đĩ да ҁĐĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸ ĐģОСиĐŊĐēĐ°Ņ‚Đ° ĐŊа {user}?", + "confirm_user_pin_code_reset": "ДаĐģи ŅĐ¸ĐŗŅƒŅ€ĐŊĐž ŅĐ°ĐēĐ°Ņˆ да ĐŗĐž ҁĐŧĐĩĐŊĐ¸Ņ‚Đĩ ПИН ĐēĐžĐ´ĐžŅ‚ Са {user}", "create_job": "ХОСдади ĐˇĐ°Đ´Đ°Ņ‡Đ°", "cron_expression": "Cron Đ¸ĐˇŅ€Đ°Đˇ", "cron_expression_description": "ПодĐĩŅĐ¸ ĐŗĐž иĐŊŅ‚ĐĩŅ€Đ˛Đ°ĐģĐžŅ‚ ĐŊа ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ ĐēĐžŅ€Đ¸ŅŅ‚ĐĩŅ˜ŅœĐ¸ ĐŗĐž cron Ņ„ĐžŅ€ĐŧĐ°Ņ‚ĐžŅ‚. За ĐŋОвĐĩҜĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ ĐŋĐžĐŗĐģĐĩĐ´ĐŊĐĩŅ‚Đĩ ĐŊа ĐŋŅ€. Crontab Guru", "cron_expression_presets": "ĐŸŅ€ĐĩĐ´ĐĩŅ„Đ¸ĐŊĐ¸Ņ€Đ°ĐŊи Cron Đ¸ĐˇŅ€Đ°ĐˇĐ¸", "disable_login": "ОĐŊĐĩвОСĐŧĐžĐļи ĐŊĐ°Ņ˜Đ°Đ˛Đ°", "duplicate_detection_job_description": "ĐŸŅƒŅˆŅ‚Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊҁĐēĐž ŅƒŅ‡ĐĩҚĐĩ ĐŊа ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°Ņ‚Đ° Са да ҁĐĩ ĐžŅ‚ĐēŅ€Đ¸Ņ˜Đ°Ņ‚ ҁĐģĐ¸Ņ‡ĐŊи ҁĐģиĐēи. ĐĄĐĩ ĐŋĐžŅ‚ĐŋĐ¸Ņ€Đ° ĐŊа Smart Search", + "external_library_management": "МĐĩĐŊĐ°ŅŸĐŧĐĩĐŊŅ‚ ĐŊа ĐĐ°Đ´Đ˛ĐžŅ€Đĩ҈ĐŊа БибĐģĐ¸ĐžŅ‚ĐĩĐēа", + "face_detection": "ДĐĩŅ‚ĐĩĐēŅ†Đ¸Ņ˜Đ° ĐŊа ĐģĐ¸Ņ†Đĩ", "force_delete_user_warning": "ĐŸĐ Đ•Đ”ĐŖĐŸĐ Đ•Đ”ĐŖĐ’ĐĐŠĐ•: Ова вĐĩĐ´ĐŊĐ°Ņˆ ҜĐĩ ĐŗĐž ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊи ĐēĐžŅ€Đ¸ŅĐŊиĐēĐžŅ‚ и ŅĐ¸Ņ‚Đĩ ҁҀĐĩĐ´ŅŅ‚Đ˛Đ°. Оваа аĐēŅ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŧĐžĐļĐĩ да ҁĐĩ ĐŋĐžĐŊĐ¸ŅˆŅ‚Đ¸ и Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐ¸Ņ‚Đĩ ĐŊĐĩĐŧа да ĐŧĐžĐļĐĩ да ҁĐĩ Đ˛Ņ€Đ°Ņ‚Đ°Ņ‚ ĐŊаСад.", "image_format": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚", + "image_format_description": "WebP ŅĐžĐˇĐ´Đ°Đ˛Đ° ĐŋĐžĐŧаĐģи Ņ„Đ°Ņ˜Đģви ĐžŅ‚ĐēĐžĐģĐē҃ JPEG, ĐŊĐž Đĩ ĐŋĐž ҁĐŋĐžŅ€ ĐŋŅ€Đ¸ ĐĩĐŊĐēĐžĐ´Đ¸Ņ€Đ°ŅšĐĩ.", + "image_fullsize_enabled": "ОвозĐŧĐžĐļи ҆ĐĩĐģĐžŅĐŊа-ĐŗĐžĐģĐĩĐŧиĐŊа ĐŊа ĐŗĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ŅšĐĩ ĐŊа ҁĐģиĐēа", + "image_fullsize_quality_description": "ĐĻĐĩĐģĐžŅĐŊа-ĐŗĐžĐģĐĩĐŧиĐŊа ĐŊа ҁĐģиĐēа ŅĐž ĐēваĐģĐ¸Ņ‚ĐĩŅ‚ Од 1-100. ĐŸĐžĐ˛Đ¸ŅĐžĐēŅ‚Đž Đĩ ĐŋĐžĐ´ĐžĐąŅ€Đž, ĐŊĐž ŅĐžĐˇĐ´Đ°Đ˛Đ° ĐŋĐžĐŗĐžĐģĐĩĐŧи Ņ„Đ°Ņ˜ĐģОви.", + "image_fullsize_title": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са ĐĻĐĩĐģĐžŅĐŊа-ĐŗĐžĐģĐĩĐŧиĐŊа ĐŊа ĐĄĐģиĐēа", + "image_prefer_embedded_preview": "ĐŸŅ€ĐĩŅ‚ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐŊ Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´", + "image_preview_title": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са ĐŸŅ€ĐĩĐŗĐģĐĩĐ´", "image_quality": "КваĐģĐ¸Ņ‚ĐĩŅ‚", "image_resolution": "Đ ĐĩСОĐģŅƒŅ†Đ¸Ņ˜Đ°", "image_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са ҁĐģиĐēи", + "job_concurrency": "{job} ĐēĐžĐŊĐēŅƒŅ€ĐĩĐŊŅ‚ĐŊĐžŅŅ‚", + "job_created": "ĐšŅ€ĐĩĐ¸Ņ€Đ°ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ°", + "job_not_concurrency_safe": "Оваа ĐˇĐ°Đ´Đ°Ņ‡Đ° ĐŊĐĩ Đĩ ĐēĐžĐŊĐēŅƒŅ€ĐĩŅ‚ĐŊĐž-ĐąĐĩСйĐĩĐ´ĐŊа.", + "job_settings": "ĐŸĐžŅŅ‚Đ°Đ˛Đēи Са ĐˇĐ°Đ´Đ°Ņ‡Đ°", + "job_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐēĐžĐŊĐēŅƒŅ€ĐĩĐŊŅ‚ĐŊĐžŅŅ‚ ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ¸", + "job_status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŊа ĐˇĐ°Đ´Đ°Ņ‡Đ¸", + "library_created": "ĐšŅ€ĐĩĐ¸Ņ€Đ°ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа: {library}", + "library_deleted": "БибĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ° Đĩ Đ¸ĐˇĐąŅ€Đ¸ŅˆĐ°ĐŊа", + "library_import_path_description": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļи ĐŋаĐŋĐēа Са вĐŊĐĩҁ. Оваа ĐŋаĐŋĐēа, вĐēĐģŅƒŅ‡ŅƒĐ˛Đ° и ĐŋОд ĐŋаĐŋĐēи, ҜĐĩ йидĐĩ ҁĐēĐĩĐŊĐ¸Ņ€Đ°ĐŊа Са ҁĐģиĐēи и видĐĩа.", "library_scanning": "ПĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ŅšĐĩ", + "library_scanning_description": "ПодĐĩŅĐ¸ ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ĐŊҘĐĩ ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°", + "library_scanning_enable_description": "ОвозĐŧĐžĐļи ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐŊĐž ҁĐēĐĩĐŊĐ¸Ņ€Đ°ĐŊҘĐĩ ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēĐ°Ņ‚Đ°", "library_settings": "ЕĐēҁ҂ĐĩŅ€ĐŊа йийĐģĐ¸ĐžŅ‚ĐĩĐēа", + "library_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ŅƒĐ˛Đ°Ņ˜ ŅĐž ĐŋОдĐĩŅŅƒĐ˛Đ°ŅšĐ°Ņ‚Đ° Са ĐŊĐ°Đ´Đ˛ĐžŅ€Đĩ҈ĐŊĐ°Ņ‚Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēа", "logging_enable_description": "ВĐēĐģŅƒŅ‡Đ¸ ĐĩвидĐĩĐŊŅ‚Đ¸Ņ€Đ°ŅšĐĩ", "logging_settings": "ЕвидĐĩĐŊŅ‚Đ¸Ņ€Đ°ŅšĐĩ", "map_dark_style": "ĐĸĐĩĐŧĐĩĐŊ ŅŅ‚Đ¸Đģ", @@ -77,7 +116,8 @@ "system_settings": "ĐĄĐ¸ŅŅ‚ĐĩĐŧҁĐēи ĐŋĐžŅŅ‚Đ°Đ˛Đēи", "thumbnail_generation_job": "ГĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°Ņ˜ ҁĐģиĐēĐ¸Ņ‡Đēи", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_threads": "ĐĐ¸ŅˆĐēи" + "transcoding_threads": "ĐĐ¸ŅˆĐēи", + "transcoding_tone_mapping": "ĐĸĐžĐŊҁĐēĐž ĐŧаĐŋĐ¸Ņ€Đ°ŅšĐĩ" }, "admin_email": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅĐēа Е-ĐŋĐžŅˆŅ‚Đ°", "admin_password": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅĐēа ĐģОСиĐŊĐēа", @@ -93,11 +133,15 @@ "asset_hashing": "ĐĨĐĩŅˆĐ¸Ņ€Đ°ŅšĐĩâ€Ļ", "asset_offline": "ĐĄŅ€ĐĩĐ´ŅŅ‚Đ˛ĐžŅ‚Đž Đĩ ĐžŅ„ĐģĐ°Ņ˜ĐŊ", "asset_skipped": "ĐŸŅ€ĐžĐŋŅƒŅˆŅ‚ĐĩĐŊĐž", + "asset_uploaded": "ĐŸŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊĐž", + "asset_uploading": "ĐŸŅ€Đ¸ĐēĐ°Ņ‡ŅƒĐ˛Đ°ŅšĐĩâ€Ļ", "assets": "ĐĄŅ€ĐĩĐ´ŅŅ‚Đ˛Đ°", "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ¸Ņ€Đ°ĐŊи ŅƒŅ€Đĩди", "back": "Назад", + "backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊа ĐēĐžĐŋĐ¸Ņ˜Đ°", "backward": "НаĐŊаСад", "blurred_background": "ЗаĐŧĐ°Ņ‚ĐĩĐŊа ĐŋОСадиĐŊа", + "build": "ВĐĩŅ€ĐˇĐ¸Ņ˜Đ°", "camera": "КаĐŧĐĩŅ€Đ°", "camera_brand": "ĐœĐ°Ņ€Đēа ĐŊа ĐēаĐŧĐĩŅ€Đ°", "camera_model": "МодĐĩĐģ ĐŊа ĐēаĐŧĐĩŅ€Đ°", @@ -162,15 +206,18 @@ "enabled": "ОвозĐŧĐžĐļĐĩĐŊĐž", "end_date": "ĐšŅ€Đ°ĐĩĐŊ Đ´Đ°Ņ‚ŅƒĐŧ", "error": "Đ“Ņ€Đĩ҈Đēа", + "exif": "Exif", "expand_all": "ĐŸŅ€ĐžŅˆĐ¸Ņ€Đ¸ ĐŗĐ¸ ŅĐ¸Ņ‚Đĩ", "expire_after": "Да Đ¸ŅŅ‚Đĩ҇Đĩ ĐŋĐžŅĐģĐĩ", "expired": "Đ˜ŅŅ‚Đĩ҇ĐĩĐŊĐž", "explore": "Đ˜ŅŅ‚Ņ€Đ°Đļи", + "explorer": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ŅƒĐ˛Đ°Ņ‡", "export": "ИСвĐĩСи", "extension": "ЕĐēҁ҂ĐĩĐŊĐˇĐ¸Ņ˜Đ°", "external": "ЕĐēҁ҂ĐĩŅ€ĐŊĐž", "external_libraries": "ЕĐēҁ҂ĐĩŅ€ĐŊи йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "face_unassigned": "НĐĩдОдĐĩĐģĐĩĐŊĐž", + "failed": "НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž", "favorite": "ОĐŧиĐģĐĩĐŊĐž", "favorites": "ОĐŧиĐģĐĩĐŊи", "features": "Đ¤ŅƒĐŊĐēии", @@ -199,7 +246,6 @@ "level": "Ниво", "library": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа", "light": "ХвĐĩŅ‚ĐģĐž", - "link_options": "ОĐŋŅ†Đ¸Đ¸ Са ĐģиĐŊĐē", "list": "Đ›Đ¸ŅŅ‚Đ°", "loading": "Đ’Ņ‡Đ¸Ņ‚ŅƒĐ˛Đ°ŅšĐĩ", "log_out": "ĐžĐ´Ņ˜Đ°Đ˛Đ¸ ҁĐĩ", @@ -230,8 +276,10 @@ "no_results": "НĐĩĐŧа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸", "notes": "БĐĩĐģĐĩ҈Đēи", "notifications": "ĐĐžŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", + "oauth": "OAuth", "offline": "ĐžŅ„ĐģĐ°Ņ˜ĐŊ", "ok": "ОĐē", + "onboarding": "ВовĐĩĐ´ŅƒĐ˛Đ°ŅšĐĩ", "online": "ОĐŊĐģĐ°Ņ˜ĐŊ", "options": "ОĐŋŅ†Đ¸Đ¸", "or": "иĐģи", diff --git a/i18n/ml.json b/i18n/ml.json index 8d25cb5604..0c85d53bd3 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -1,12 +1,2135 @@ { - "about": "ā´ĩā´ŋⴎⴝⴤāĩā´¤āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩ", + "about": "ā´•āĩā´ąā´ŋⴚāĩā´šāĩ", "account": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ", - "account_settings": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´¸āĩ†ā´ąāĩā´ąā´ŋā´‚ā´—āĩā´¸āĩ", + "account_settings": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", "acknowledge": "ā´…ā´‚ā´—āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "action": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚", + "action_common_update": "ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "actions": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩž", + "active": "ⴏⴜāĩ€ā´ĩā´‚", + "activity": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚", + "activity_changed": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ {enabled, select, true {ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ} other {ā´¨ā´ŋāĩŧⴜāĩā´œāĩ€ā´ĩā´Žā´žā´•āĩā´•ā´ŋ}}", "add": "ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", - "add_a_description": "ā´’ā´°āĩ ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", - "add_a_location": "ā´’ā´°āĩ ā´¸āĩā´Ĩⴞⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", - "add_a_name": "ā´’ā´°āĩ ā´Ēāĩ‡ā´°āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", - "add_a_title": "ā´’ā´°āĩ ā´ļāĩ€āĩŧⴎⴕⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", - "add_endpoint": "ā´Žāĩģā´Ąāĩā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•" + "add_a_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_name": "ā´Ēāĩ‡ā´°āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_a_title": "ā´ļāĩ€āĩŧⴎⴕⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_birthday": "ⴜⴍāĩā´Žā´Ļā´ŋⴍⴂ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_endpoint": "ā´Žāĩģā´Ąāĩâ€Œā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_exclusion_pattern": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_more_users": "ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_path": "ā´Ēā´žā´¤āĩā´¤āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_tag": "ā´Ÿā´žā´—āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_to": "...ā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_to_album_bottom_sheet_added": "{album} ā´Žā´¨āĩā´¨ā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "add_to_album_bottom_sheet_already_exists": "{album}-āĩŊ ⴇⴤā´ŋⴍⴕⴂ ⴤⴍāĩā´¨āĩ†ā´¯āĩā´Ŗāĩā´Ÿāĩ", + "add_to_album_bottom_sheet_some_local_assets": "ⴚā´ŋā´˛ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´žāĩā´žā´ŋā´˛āĩā´˛", + "add_to_album_toggle": "{album}-ā´¨āĩā´ŗāĩā´ŗ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´Ēāĩā´Ēāĩ ⴟāĩ‹ā´—ā´ŋāĩž ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "add_to_albums": "ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_to_albums_count": "ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´• ({count})", + "add_to_shared_album": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "add_url": "URL ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "added_to_archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "added_to_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "added_to_favorites_count": "{count, number} ā´Žā´Ŗāĩā´Ŗā´‚ ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "admin": { + "add_exclusion_pattern_description": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡ā´Ŗāĩā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•. *, **, ? ā´Žā´¨āĩā´¨ā´ŋā´ĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩā´ŗāĩā´ŗ ā´—āĩā´˛āĩ‹ā´Ŧā´ŋā´‚ā´—āĩ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ. \"Raw\" ā´Žā´¨āĩā´¨āĩ ā´Ēāĩ‡ā´°āĩā´ŗāĩā´ŗ ā´ā´¤āĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ā´Ąā´¯ā´ąā´•āĩā´Ÿā´ąā´ŋā´¯ā´ŋā´˛āĩ† ā´Žā´˛āĩā´˛ā´ž ā´Ģⴝⴞāĩā´•ā´ŗāĩā´‚ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ, \"**/Raw/**\" ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•. \".tif\"-āĩŊ ā´…ā´ĩā´¸ā´žā´¨ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Žā´˛āĩā´˛ā´ž ā´Ģⴝⴞāĩā´•ā´ŗāĩā´‚ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ, \"**/*.tif\" ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•. ā´’ā´°āĩ ā´•āĩ‡ā´ĩā´˛ ā´Ēā´žā´¤āĩā´¤āĩ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ, \"/path/to/ignore/**\" ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•.", + "admin_user": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩāĩ", + "asset_offline_description": "ⴈ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´…ā´¸ā´ąāĩā´ąāĩ ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ąā´ŋā´¸āĩā´•ā´ŋāĩŊ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛, ā´…ā´¤āĩ ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´Ģā´¯āĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´¨āĩ€ā´•āĩā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¨āĩā´Ŧā´¨āĩā´§ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨ā´žā´¯ā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ⴟāĩˆā´‚ā´˛āĩˆāĩģ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•. ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´žāĩģ, ā´¤ā´žā´´āĩ†ā´¯āĩā´ŗāĩā´ŗ ā´Ģā´¯āĩŊ ā´Ēā´žā´¤ Immich-ā´¨āĩ ⴆⴕāĩā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•ā´¯āĩā´‚ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "authentication_settings": "ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "authentication_settings_description": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ, OAuth, ā´Žā´ąāĩā´ąāĩ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´Žā´¨āĩā´¨ā´ŋā´ĩ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "authentication_settings_disable_all": "ā´Žā´˛āĩā´˛ā´ž ā´˛āĩ‹ā´—ā´ŋāĩģ ā´°āĩ€ā´¤ā´ŋā´•ā´ŗāĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗā´Žā´žā´¯āĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´‚.", + "authentication_settings_reenable": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´žāĩģ, ā´’ā´°āĩ ā´¸āĩ†āĩŧā´ĩāĩŧ ā´•ā´Žā´žāĩģā´Ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•.", + "background_task_job": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ⴜāĩ‹ā´˛ā´ŋā´•āĩž", + "backup_database": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´Ąā´‚ā´Ēāĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "backup_database_enable_description": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´Ąā´‚ā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "backup_keep_last_amount": "ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´Ēā´´ā´¯ ā´Ąā´‚ā´Ēāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "backup_onboarding_1_description": "ā´•āĩā´˛āĩ—ā´Ąā´ŋā´˛āĩ‹ ā´Žā´ąāĩā´ąāĩŠā´°āĩ ā´­āĩ—ā´¤ā´ŋā´• ā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤āĩ‹ ⴉⴺāĩā´ŗ ā´“ā´Ģāĩâ€Œā´¸āĩˆā´ąāĩā´ąāĩ ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩ.", + "backup_onboarding_2_description": "ā´ĩā´ŋā´ĩā´ŋā´§ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ† ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩā´•āĩž. ⴇⴤā´ŋāĩŊ ā´Ēāĩā´°ā´§ā´žā´¨ ā´Ģⴝⴞāĩā´•ā´ŗāĩā´‚ ā´† ā´Ģⴝⴞāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩā´‚ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨āĩ.", + "backup_onboarding_3_description": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´Ģⴝⴞāĩā´•āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩ† ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ąā´žā´ąāĩā´ąā´¯āĩā´Ÿāĩ† ⴆⴕāĩ† ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩā´•āĩž. ⴇⴤā´ŋāĩŊ 1 ā´“ā´Ģāĩâ€Œā´¸āĩˆā´ąāĩā´ąāĩ ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩā´‚ 2 ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩā´•ā´ŗāĩā´‚ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨āĩ.", + "backup_onboarding_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ąā´žā´ąāĩā´ą ā´Ēā´°ā´ŋā´°ā´•āĩā´ˇā´ŋā´•āĩā´•ā´žāĩģ 3-2-1 ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴤⴍāĩā´¤āĩā´°ā´‚ ā´ļāĩā´Ēā´žāĩŧā´ļ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. ā´¸ā´Žā´—āĩā´°ā´Žā´žā´¯ ā´’ā´°āĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēā´°ā´ŋā´šā´žā´°ā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ, ā´¨ā´ŋā´™āĩā´™āĩž ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´Ÿāĩ†/ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´Ÿāĩ† ā´Ēā´•āĩŧā´Ēāĩā´Ēāĩā´•ā´ŗāĩā´‚ Immich ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩā´‚ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿā´¤ā´žā´Ŗāĩ.", + "backup_onboarding_footer": "Immich ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩā´ŗāĩā´ŗ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ, ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´Ąāĩ‹ā´•āĩā´¯āĩā´Žāĩ†ā´¨āĩā´ąāĩ‡ā´ˇāĩģ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•.", + "backup_onboarding_parts_title": "ā´’ā´°āĩ 3-2-1 ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēā´ŋāĩŊ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ā´ĩ:", + "backup_onboarding_title": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩā´•āĩž", + "backup_settings": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´Ąā´‚ā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "backup_settings_description": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´Ąā´‚ā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "cleared_jobs": "{job}-ā´¨āĩā´ŗāĩā´ŗ ⴜāĩ‹ā´˛ā´ŋā´•āĩž ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¤āĩ", + "config_set_by_file": "ā´•āĩ‹āĩēā´Ģā´ŋā´—ā´ąāĩ‡ā´ˇāĩģ ā´¨ā´ŋā´˛ā´ĩā´ŋāĩŊ ā´’ā´°āĩ ā´•āĩ‹āĩēā´Ģā´ŋā´—ā´ąāĩ‡ā´ˇāĩģ ā´Ģā´¯āĩŊ ā´ĩā´´ā´ŋā´¯ā´žā´Ŗāĩ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ", + "confirm_delete_library": "{library} ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "confirm_delete_library_assets": "ⴈ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ Immich-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ {count, plural, one {ā´…ā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´…ā´¸ā´ąāĩā´ąāĩ} other {ā´…ā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ā´Žā´˛āĩā´˛ā´ž # ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚}} ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´‚, ⴇⴤāĩ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛. ā´Ģⴝⴞāĩā´•āĩž ā´Ąā´ŋā´¸āĩā´•ā´ŋāĩŊ ⴤⴍāĩā´¨āĩ† ā´¤āĩā´Ÿā´°āĩā´‚.", + "confirm_email_below": "ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, ā´¤ā´žā´´āĩ† \"{email}\" ā´Žā´¨āĩā´¨āĩ ⴟāĩˆā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "confirm_reprocess_all_faces": "ā´Žā´˛āĩā´˛ā´ž ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ā´Ēāĩ‡ā´°āĩā´ŗāĩā´ŗ ⴆⴺāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "confirm_user_password_reset": "{user}-ā´¨āĩā´ąāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "confirm_user_pin_code_reset": "{user}-ā´¨āĩā´ąāĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "create_job": "ⴜāĩ‹ā´˛ā´ŋ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "cron_expression": "ā´•āĩā´°āĩ‹āĩē ā´Žā´•āĩā´¸āĩā´Ēāĩā´°ā´ˇāĩģ", + "cron_expression_description": "ā´•āĩā´°āĩ‹āĩē ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¸āĩā´•ā´žā´¨ā´ŋā´‚ā´—āĩ ā´‡ā´Ÿā´ĩāĩ‡ā´ŗ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•. ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´•āĩā´°āĩ‹āĩēā´Ÿā´žā´Ŧāĩ ā´—āĩā´°āĩ ā´Ēāĩ‹ā´˛āĩā´ŗāĩā´ŗā´ĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "cron_expression_presets": "ā´•āĩā´°āĩ‹āĩē ā´Žā´•āĩā´¸āĩā´Ēāĩā´°ā´ˇāĩģ ā´Ēāĩā´°āĩ€ā´¸āĩ†ā´ąāĩā´ąāĩā´•āĩž", + "disable_login": "ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•", + "duplicate_detection_job_description": "ā´¸ā´Žā´žā´¨ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´žāĩģ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•. ⴇⴤāĩ ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šā´ŋā´¨āĩ† ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "exclusion_pattern_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ģⴝⴞāĩā´•ā´ŗāĩā´‚ ā´Ģāĩ‹āĩžā´Ąā´ąāĩā´•ā´ŗāĩā´‚ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ ā´Žā´•āĩā´¸āĩā´•āĩā´˛āĩ‚ā´ˇāĩģ ā´Ēā´žā´ąāĩā´ąāĩ‡ā´Ŗāĩā´•āĩž ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ† ā´¸ā´šā´žā´¯ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´¨ā´ŋā´™āĩā´™āĩž ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ⴆⴗāĩā´°ā´šā´ŋā´•āĩā´•ā´žā´¤āĩā´¤ ā´Ģⴝⴞāĩā´•āĩž ā´…ā´Ÿā´™āĩā´™ā´ŋā´¯ ā´Ģāĩ‹āĩžā´Ąā´ąāĩā´•āĩž (ā´‰ā´Ļā´žā´šā´°ā´Ŗā´¤āĩā´¤ā´ŋā´¨āĩ RAW ā´Ģⴝⴞāĩā´•āĩž) ⴉ⴪āĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ ⴇⴤāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´Ēāĩā´°ā´Ļā´Žā´žā´Ŗāĩ.", + "external_library_management": "ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´Žā´žā´¨āĩ‡ā´œāĩā´Žāĩ†ā´¨āĩā´ąāĩ", + "face_detection": "ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ", + "face_detection_description": "ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋā´˛āĩ† ā´Žāĩā´–ā´™āĩā´™āĩž ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•. ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ, ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´Ēā´°ā´ŋā´—ā´Ŗā´ŋā´•āĩā´•āĩ‚. \"ā´ąā´ŋā´Ģāĩā´°ā´ˇāĩ\" ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. \"ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ\" ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´Žā´˛āĩā´˛ā´ž ā´Žāĩā´– ā´Ąā´žā´ąāĩā´ąā´¯āĩā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. \"ā´Žā´ŋā´¸āĩā´¸ā´ŋā´‚ā´—āĩ\" ⴇⴤāĩā´ĩā´°āĩ† ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩ† ā´•āĩā´¯āĩ‚ā´ĩā´ŋā´˛ā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ ā´ļāĩ‡ā´ˇā´‚, ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ ā´Žāĩā´–ā´™āĩā´™āĩž ā´Ģāĩ‡ā´ˇāĩā´¯āĩŊ ā´ąāĩ†ā´•āĩā´•ā´—āĩā´¨ā´ŋā´ˇā´¨ā´žā´¯ā´ŋ ā´•āĩā´¯āĩ‚ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚, ā´…ā´ĩā´¯āĩ† ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗā´¤āĩ‹ ā´Ēāĩā´¤ā´ŋⴝⴤāĩ‹ ⴆⴝ ⴆⴺāĩā´•ā´ŗā´žā´¯ā´ŋ ⴤⴰⴂⴤā´ŋā´°ā´ŋā´•āĩā´•āĩā´‚.", + "facial_recognition_job_description": "ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ† ⴆⴺāĩā´•ā´ŗā´žā´¯ā´ŋ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•. ā´Žāĩā´–ā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ā´¤ā´ŋā´¨āĩ ā´ļāĩ‡ā´ˇā´Žā´žā´Ŗāĩ ⴈ ⴘⴟāĩā´Ÿā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ. \"ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ\" ā´Žā´˛āĩā´˛ā´ž ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ†ā´¯āĩā´‚ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´•āĩā´˛ā´¸āĩā´ąāĩā´ąāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. \"ā´Žā´ŋā´¸āĩā´¸ā´ŋā´‚ā´—āĩ\" ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ†ā´¯āĩā´‚ ā´…ā´¸āĩˆāĩģ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ† ā´•āĩā´¯āĩ‚ā´ĩā´ŋā´˛ā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "failed_job_command": "{job} ā´Žā´¨āĩā´¨ ⴜāĩ‹ā´˛ā´ŋā´•āĩā´•āĩā´ŗāĩā´ŗ ā´•ā´Žā´žāĩģā´Ąāĩ {command} ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "force_delete_user_warning": "ā´Žāĩā´¨āĩā´¨ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ: ⴇⴤāĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ†ā´¯āĩā´‚ ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´‰ā´Ÿā´¨ā´Ÿā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚. ⴇⴤāĩ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´Ģⴝⴞāĩā´•āĩž ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩ†ā´Ÿāĩā´•āĩā´•ā´žā´¨āĩā´‚ ā´¸ā´žā´§ā´ŋā´•āĩā´•ā´ŋā´˛āĩā´˛.", + "image_format": "ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ", + "image_format_description": "WebP, JPEG-ā´¨āĩ†ā´•āĩā´•ā´žāĩž ⴚāĩ†ā´ąā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´ĩā´žā´Ŗāĩ.", + "image_fullsize_description": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¤ ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´‚, ā´¸āĩ‚ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "image_fullsize_enabled": "ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´° ā´¨ā´ŋāĩŧā´Žāĩā´Žā´žā´Ŗā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "image_fullsize_enabled_description": "ā´ĩāĩ†ā´Ŧāĩ-ā´Ģāĩā´°ā´Ŗāĩā´Ÿāĩā´˛ā´ŋ ā´…ā´˛āĩā´˛ā´žā´¤āĩā´¤ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´‚ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•. \"ā´Žā´‚ā´Ŧā´Ąā´Ąāĩ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ā´ĩā´ŋā´¨āĩ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•\" ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ā´Žā´‚ā´Ŧā´Ąā´Ąāĩ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ā´•āĩž ā´Ēā´°ā´ŋā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´¨āĩ‡ā´°ā´ŋⴟāĩā´Ÿāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. JPEG ā´Ēāĩ‹ā´˛āĩā´ŗāĩā´ŗ ā´ĩāĩ†ā´Ŧāĩ-ā´Ģāĩā´°ā´Ŗāĩā´Ÿāĩā´˛ā´ŋ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩā´•ā´ŗāĩ† ⴇⴤāĩ ā´Ŧā´žā´§ā´ŋā´•āĩā´•ā´ŋā´˛āĩā´˛.", + "image_fullsize_quality_description": "ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž 1-100 ā´ĩā´°āĩ†. ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "image_fullsize_title": "ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´ĩā´˛āĩā´Ēāĩā´Ē ⴚā´ŋā´¤āĩā´° ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "image_prefer_embedded_preview": "ā´Žā´‚ā´Ŧā´Ąā´Ąāĩ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ā´ĩā´ŋā´¨āĩ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•", + "image_prefer_embedded_preview_setting_description": "ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ā´ąāĩ‹ (RAW) ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗā´ŋā´˛āĩ† ā´Žā´‚ā´Ŧā´Ąā´Ąāĩ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ā´•āĩž ā´‡ā´Žāĩ‡ā´œāĩ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸ā´ŋā´‚ā´—ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´‡āĩģā´Ēāĩā´Ÿāĩā´Ÿā´žā´¯ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•. ⴇⴤāĩ ⴚā´ŋā´˛ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´•āĩƒā´¤āĩā´¯ā´Žā´žā´¯ ā´¨ā´ŋā´ąā´™āĩā´™āĩž ā´¨āĩŊā´•ā´ŋā´¯āĩ‡ā´•āĩā´•ā´žā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ā´ĩā´ŋāĩģāĩā´ąāĩ† ā´—āĩā´Ŗā´¨ā´ŋā´˛ā´ĩā´žā´°ā´‚ ā´•āĩā´¯ā´žā´Žā´ąā´¯āĩ† ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´‚, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ⴚā´ŋā´¤āĩā´°ā´¤āĩā´¤ā´ŋāĩŊ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´•ā´‚ā´Ēāĩā´°ā´ˇāĩģ ā´†āĩŧⴟāĩā´Ÿā´ŋā´Ģā´žā´•āĩā´ąāĩā´ąāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•ā´žā´‚.", + "image_prefer_wide_gamut": "ā´ĩāĩˆā´Ąāĩ ā´—ā´žā´Žā´ąāĩā´ąā´ŋā´¨āĩ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•", + "image_prefer_wide_gamut_setting_description": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Ąā´ŋā´¸āĩā´Ēāĩā´˛āĩ‡ P3 ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•. ⴇⴤāĩ ā´ĩāĩˆā´Ąāĩ ā´•ā´ŗāĩŧā´¸āĩā´Ēāĩ‡ā´¸āĩā´•ā´ŗāĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´ŋā´´ā´ŋā´ĩāĩ ⴍⴍāĩā´¨ā´žā´¯ā´ŋ ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Ēā´´ā´¯ ā´Ŧāĩā´°āĩ—ā´¸āĩŧ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩā´ŗāĩā´ŗ ā´Ēā´´ā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´ĩāĩā´¯ā´¤āĩā´¯ā´¸āĩā´¤ā´Žā´žā´¯ā´ŋ ā´•ā´žā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ‡ā´•āĩā´•ā´žā´‚. ā´•ā´ŗāĩŧ ā´ˇā´ŋā´Ģāĩā´ąāĩā´ąāĩā´•āĩž ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ sRGB ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž sRGB ⴆⴝā´ŋ ⴤⴍāĩā´¨āĩ† ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤āĩā´¨āĩā´¨āĩ.", + "image_preview_description": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¤ ā´‡ā´Ÿā´¤āĩā´¤ā´°ā´‚ ā´ĩā´˛āĩā´Ēāĩā´Ēā´Žāĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´‚, ā´’ā´°āĩŠā´ąāĩā´ą ā´…ā´¸ā´ąāĩā´ąāĩ ā´•ā´žā´Ŗāĩā´Žāĩā´Ēāĩ‹ā´´āĩā´‚ ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—ā´ŋā´¨āĩā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "image_preview_quality_description": "ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž 1-100 ā´ĩā´°āĩ†. ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•ā´¯āĩā´‚ ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗā´ļāĩ‡ā´ˇā´ŋ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚. ā´•āĩā´ąā´žāĩā´ž ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Žā´¯āĩ† ā´Ŧā´žā´§ā´ŋⴚāĩā´šāĩ‡ā´•āĩā´•ā´žā´‚.", + "image_preview_title": "ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "image_quality": "ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž", + "image_resolution": "ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ā´ˇāĩģ", + "image_resolution_description": "ⴉⴝāĩŧā´¨āĩā´¨ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ⴎⴍāĩā´•āĩžā´•āĩā´•āĩ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´¸ā´Žā´¯ā´Žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´‚, ā´ĩā´˛ā´ŋā´¯ ā´Ģā´¯āĩŊ ā´ĩā´˛āĩā´Ēāĩā´Ēā´Žāĩā´Ŗāĩā´Ÿā´žā´•āĩā´‚, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗā´ļāĩ‡ā´ˇā´ŋ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "image_settings": "ⴚā´ŋā´¤āĩā´° ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "image_settings_description": "ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋⴚāĩā´š ⴚā´ŋā´¤āĩā´°ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Žā´¯āĩā´‚ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ⴎⴍāĩā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "image_thumbnail_description": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¤ ⴚāĩ†ā´ąā´ŋā´¯ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ, ā´Ēāĩā´°ā´§ā´žā´¨ ⴟāĩˆā´‚ā´˛āĩˆāĩģ ā´Ēāĩ‹ā´˛āĩā´ŗāĩā´ŗ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´Ÿāĩ† ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩā´•āĩž ā´•ā´žā´Ŗāĩā´Žāĩā´Ēāĩ‹āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "image_thumbnail_quality_description": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛ā´ŋā´¨āĩā´ąāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž 1-100 ā´ĩā´°āĩ†. ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•ā´¯āĩā´‚ ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗā´ļāĩ‡ā´ˇā´ŋ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "image_thumbnail_title": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "job_concurrency": "{job} ā´•āĩ‹āĩēā´•ā´ąāĩģā´¸ā´ŋ", + "job_created": "ⴜāĩ‹ā´˛ā´ŋ ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´šāĩ", + "job_not_concurrency_safe": "ⴈ ⴜāĩ‹ā´˛ā´ŋ ā´•āĩ‹āĩēā´•ā´ąāĩģā´¸ā´ŋ-ā´¸āĩ‡ā´Ģāĩ ā´…ā´˛āĩā´˛.", + "job_settings": "ⴜāĩ‹ā´˛ā´ŋ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "job_settings_description": "ⴜāĩ‹ā´˛ā´ŋ ā´•āĩ‹āĩēā´•ā´ąāĩģā´¸ā´ŋ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "job_status": "ⴜāĩ‹ā´˛ā´ŋā´¯āĩā´Ÿāĩ† ā´¨ā´ŋā´˛", + "jobs_delayed": "{jobCount, plural, one {# ⴜāĩ‹ā´˛ā´ŋ ā´ĩāĩˆā´•ā´ŋ} other {# ⴜāĩ‹ā´˛ā´ŋā´•āĩž ā´ĩāĩˆā´•ā´ŋ}}", + "jobs_failed": "{jobCount, plural, one {# ⴜāĩ‹ā´˛ā´ŋ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ} other {# ⴜāĩ‹ā´˛ā´ŋā´•āĩž ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ}}", + "library_created": "{library} ā´Žā´¨āĩā´¨ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´šāĩ", + "library_deleted": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "library_import_path_description": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´’ā´°āĩ ā´Ģāĩ‹āĩžā´Ąāĩŧ ā´ĩāĩā´¯ā´•āĩā´¤ā´Žā´žā´•āĩā´•āĩā´•. ā´¸ā´Ŧāĩā´Ģāĩ‹āĩžā´Ąā´ąāĩā´•āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩ† ⴈ ā´Ģāĩ‹āĩžā´Ąāĩŧ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "library_scanning": "ⴆⴍāĩā´•ā´žā´˛ā´ŋā´• ā´¸āĩā´•ā´žā´¨ā´ŋā´‚ā´—āĩ", + "library_scanning_description": "ⴆⴍāĩā´•ā´žā´˛ā´ŋā´• ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žā´¨ā´ŋā´‚ā´—āĩ ā´•āĩ‹āĩēā´Ģā´ŋā´—āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "library_scanning_enable_description": "ⴆⴍāĩā´•ā´žā´˛ā´ŋā´• ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žā´¨ā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "library_settings": "ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ", + "library_settings_description": "ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "library_tasks_description": "ā´Ēāĩā´¤ā´ŋⴝⴤāĩ‹ ā´Žā´žā´ąāĩā´ąā´‚ ā´ĩā´°āĩā´¤āĩā´¤ā´ŋⴝⴤāĩ‹ ⴆⴝ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•āĩž ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "library_watching_enable_description": "ā´Ģā´¯āĩŊ ā´Žā´žā´ąāĩā´ąā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•āĩž ā´¨ā´ŋā´°āĩ€ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•", + "library_watching_settings": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¨ā´ŋā´°āĩ€ā´•āĩā´ˇā´ŋā´•āĩā´•āĩŊ (ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋāĩŊ)", + "library_watching_settings_description": "ā´Žā´žā´ąāĩā´ąā´‚ ā´ĩā´¨āĩā´¨ ā´Ģⴝⴞāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋā´°āĩ€ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•", + "logging_enable_description": "ā´˛āĩ‹ā´—ā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "logging_level_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ā´ā´¤āĩ ā´˛āĩ‹ā´—āĩ ā´˛āĩ†ā´ĩāĩŊ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´Ŗā´‚.", + "logging_settings": "ā´˛āĩ‹ā´—ā´ŋā´‚ā´—āĩ", + "machine_learning_availability_checks": "ⴞⴭāĩā´¯ā´¤ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´•āĩž", + "machine_learning_availability_checks_description": "ⴞⴭāĩā´¯ā´Žā´žā´¯ ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´•āĩž ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•ā´¯āĩā´‚ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "machine_learning_availability_checks_enabled": "ⴞⴭāĩā´¯ā´¤ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "machine_learning_availability_checks_interval": "ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ā´‡ā´Ÿā´ĩāĩ‡ā´ŗ", + "machine_learning_availability_checks_interval_description": "ⴞⴭāĩā´¯ā´¤ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´•āĩžā´•āĩā´•ā´ŋⴟⴝā´ŋā´˛āĩā´ŗāĩā´ŗ ā´‡ā´Ÿā´ĩāĩ‡ā´ŗ (ā´Žā´ŋā´˛āĩā´˛ā´ŋā´¸āĩ†ā´•āĩā´•āĩģā´Ąā´ŋāĩŊ)", + "machine_learning_availability_checks_timeout": "ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴝāĩā´Ÿāĩ† ā´¸ā´Žā´¯ā´Ēā´°ā´ŋā´§ā´ŋ", + "machine_learning_availability_checks_timeout_description": "ⴞⴭāĩā´¯ā´¤ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´•āĩžā´•āĩā´•āĩā´ŗāĩā´ŗ ā´¸ā´Žā´¯ā´Ēā´°ā´ŋā´§ā´ŋ (ā´Žā´ŋā´˛āĩā´˛ā´ŋā´¸āĩ†ā´•āĩā´•āĩģā´Ąā´ŋāĩŊ)", + "machine_learning_clip_model": "CLIP ā´Žāĩ‹ā´ĄāĩŊ", + "machine_learning_clip_model_description": "ā´‡ā´ĩā´ŋⴟāĩ† ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋⴟāĩā´Ÿāĩā´ŗāĩā´ŗ ā´’ā´°āĩ CLIP ā´Žāĩ‹ā´Ąā´˛ā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ. ā´’ā´°āĩ ā´Žāĩ‹ā´ĄāĩŊ ā´Žā´žā´ąāĩā´ąāĩā´Žāĩā´Ēāĩ‹āĩž ā´Žā´˛āĩā´˛ā´ž ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ 'ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šāĩ' ⴜāĩ‹ā´˛ā´ŋ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿā´¤āĩā´Ŗāĩā´Ÿāĩ†ā´¨āĩā´¨āĩ ā´“āĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•.", + "machine_learning_duplicate_detection": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ", + "machine_learning_duplicate_detection_enabled": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "machine_learning_duplicate_detection_enabled_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´žā´˛āĩā´‚, ā´¤ā´ŋā´•ā´šāĩā´šāĩā´‚ ā´¸ā´Žā´žā´¨ā´Žā´žā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋā´˛āĩ† ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚.", + "machine_learning_duplicate_detection_setting_description": "ā´¸ā´Žā´žā´¨ā´Žā´žā´¯ ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´žāĩģ CLIP ā´Žā´‚ā´Ŧāĩ†ā´Ąāĩā´Ąā´ŋā´‚ā´—āĩâ€Œā´¸āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "machine_learning_enabled": "ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "machine_learning_enabled_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´žāĩŊ, ā´¤ā´žā´´āĩ†ā´¯āĩā´ŗāĩā´ŗ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´Ēā´°ā´ŋā´—ā´Ŗā´ŋā´•āĩā´•ā´žā´¤āĩ† ā´Žā´˛āĩā´˛ā´ž ML ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•ā´ŗāĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´‚.", + "machine_learning_facial_recognition": "ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ", + "machine_learning_facial_recognition_description": "ⴚā´ŋā´¤āĩā´°ā´™āĩā´™ā´ŗā´ŋā´˛āĩ† ā´Žāĩā´–ā´™āĩā´™āĩž ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•, ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩā´•, ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "machine_learning_facial_recognition_model": "ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ ā´Žāĩ‹ā´ĄāĩŊ", + "machine_learning_facial_recognition_model_description": "ā´Žāĩ‹ā´Ąā´˛āĩā´•āĩž ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´…ā´ĩā´°āĩ‹ā´šā´Ŗ ā´•āĩā´°ā´Žā´¤āĩā´¤ā´ŋā´˛ā´žā´Ŗāĩ ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ. ā´ĩā´˛ā´ŋā´¯ ā´Žāĩ‹ā´Ąā´˛āĩā´•āĩž ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´žāĩā´žā´¤āĩā´‚ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ĩā´¯āĩā´Žā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žā´ŋā´•ā´šāĩā´š ā´Ģⴞⴙāĩā´™āĩž ā´¨āĩŊā´•āĩā´¨āĩā´¨āĩ. ā´’ā´°āĩ ā´Žāĩ‹ā´ĄāĩŊ ā´Žā´žā´ąāĩā´ąāĩā´Žāĩā´Ēāĩ‹āĩž ā´Žā´˛āĩā´˛ā´ž ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ 'ā´Žāĩā´–ā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ' ⴜāĩ‹ā´˛ā´ŋ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿā´¤āĩā´Ŗāĩā´Ÿāĩ†ā´¨āĩā´¨āĩ ā´“āĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•.", + "machine_learning_facial_recognition_setting": "ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "machine_learning_facial_recognition_setting_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´žāĩŊ, ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋⴝⴞā´ŋā´¨ā´žā´¯ā´ŋ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿā´ŋā´˛āĩā´˛, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´Žā´•āĩā´¸āĩā´Ēāĩā´˛āĩ‹āĩŧ ā´Ēāĩ‡ā´œā´ŋā´˛āĩ† 'ⴆⴺāĩā´•āĩž' ā´ĩā´ŋā´­ā´žā´—ā´¤āĩā´¤ā´ŋāĩŊ ā´…ā´ĩ ā´Ļāĩƒā´ļāĩā´¯ā´Žā´žā´•āĩā´•ā´¯āĩā´Žā´ŋā´˛āĩā´˛.", + "machine_learning_max_detection_distance": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ ā´Ļāĩ‚ā´°ā´‚", + "machine_learning_max_detection_distance_description": "ā´°ā´Ŗāĩā´Ÿāĩ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™ā´ŗāĩ† ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗā´žā´¯ā´ŋ ā´•ā´Ŗā´•āĩā´•ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ļāĩ‚ā´°ā´‚, 0.001-0.1 ā´ĩā´°āĩ†. ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩ† ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´Ģⴞⴙāĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴍⴝā´ŋⴚāĩā´šāĩ‡ā´•āĩā´•ā´žā´‚.", + "machine_learning_max_recognition_distance": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ ā´Ļāĩ‚ā´°ā´‚", + "machine_learning_max_recognition_distance_description": "ā´°ā´Ŗāĩā´Ÿāĩ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ† ā´’ā´°āĩ‡ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ ā´•ā´Ŗā´•āĩā´•ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ļāĩ‚ā´°ā´‚, 0-2 ā´ĩā´°āĩ†. ⴇⴤāĩ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´°ā´Ŗāĩā´Ÿāĩ ā´Ēāĩ‡ā´°āĩ† ā´’ā´°āĩ‡ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ ā´˛āĩ‡ā´ŦāĩŊ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩ ā´¤ā´Ÿā´¯ā´žāĩģ ā´¸ā´šā´žā´¯ā´ŋā´•āĩā´•āĩā´‚, ā´…ā´¤āĩ‡ā´¸ā´Žā´¯ā´‚ ⴇⴤāĩ ā´•āĩ‚ā´Ÿāĩā´Ÿāĩā´¨āĩā´¨ā´¤āĩ ā´’ā´°āĩ‡ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´°ā´Ŗāĩā´Ÿāĩ ā´ĩāĩā´¯ā´¤āĩā´¯ā´¸āĩā´¤ ⴆⴺāĩā´•ā´ŗā´žā´¯ā´ŋ ā´˛āĩ‡ā´ŦāĩŊ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩ ā´¤ā´Ÿā´¯ā´žā´¨āĩā´‚ ā´¸ā´šā´žā´¯ā´ŋā´•āĩā´•āĩā´‚. ā´’ā´°ā´žā´ŗāĩ† ā´°ā´Ŗāĩā´Ÿā´žā´¯ā´ŋ ā´ĩā´ŋⴭⴜā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ‡ā´•āĩā´•ā´žāĩž ā´Žā´ŗāĩā´Ēāĩā´Ēā´Žā´žā´Ŗāĩ ā´°ā´Ŗāĩā´Ÿāĩā´Ēāĩ‡ā´°āĩ† ā´’ā´¨āĩā´¨ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´Žā´¨āĩā´¨ā´¤āĩ ā´ļāĩā´°ā´Ļāĩā´§ā´ŋā´•āĩā´•āĩā´•, ā´…ā´¤ā´ŋā´¨ā´žāĩŊ ā´¸ā´žā´§āĩā´¯ā´Žā´žā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´•āĩā´ąā´žāĩā´ž ā´Ēā´°ā´ŋā´§ā´ŋ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´žā´Ŗāĩ ā´‰ā´šā´ŋⴤⴂ.", + "machine_learning_min_detection_score": "ā´•āĩā´ąā´žāĩā´ž ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩŊ ā´¸āĩā´•āĩ‹āĩŧ", + "machine_learning_min_detection_score_description": "ā´’ā´°āĩ ā´Žāĩā´–ā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´•āĩā´ąā´žāĩā´ž ā´•āĩ‹āĩēā´Ģā´ŋā´Ąāĩģā´¸āĩ ā´¸āĩā´•āĩ‹āĩŧ 0-1 ā´ĩā´°āĩ†. ā´•āĩā´ąā´žāĩā´ž ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ† ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´Ģⴞⴙāĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴍⴝā´ŋⴚāĩā´šāĩ‡ā´•āĩā´•ā´žā´‚.", + "machine_learning_min_recognized_faces": "ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´žāĩā´ž ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´•āĩā´ąā´žāĩā´ž ā´Žā´Ŗāĩā´Ŗā´‚", + "machine_learning_min_recognized_faces_description": "ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´¯ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´žāĩā´ž ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´•āĩā´ąā´žāĩā´ž ā´Žā´Ŗāĩā´Ŗā´‚. ⴇⴤāĩ ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´ŋⴚāĩā´šā´ąā´ŋā´¯āĩŊ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´•āĩƒā´¤āĩā´¯ā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Žā´¨āĩā´¨ā´žāĩŊ ā´’ā´°āĩ ā´Žāĩā´–ā´‚ ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ ā´¨āĩŊā´•ā´žā´¤ā´ŋā´°ā´ŋā´•āĩā´•ā´žā´¨āĩā´ŗāĩā´ŗ ā´¸ā´žā´§āĩā´¯ā´¤ā´¯āĩā´‚ ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "machine_learning_settings": "ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "machine_learning_settings_description": "ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•ā´ŗāĩā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗāĩā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "machine_learning_smart_search": "ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šāĩ", + "machine_learning_smart_search_description": "CLIP ā´Žā´‚ā´Ŧāĩ†ā´Ąāĩā´Ąā´ŋā´‚ā´—āĩâ€Œā´¸āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´…āĩŧā´¤āĩā´Ĩā´Ēāĩ‚āĩŧā´ĩāĩā´ĩā´‚ ā´¤ā´ŋā´°ā´¯āĩā´•", + "machine_learning_smart_search_enabled": "ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "machine_learning_smart_search_enabled_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´žāĩŊ, ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šā´ŋā´¨ā´žā´¯ā´ŋ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿā´ŋā´˛āĩā´˛.", + "machine_learning_url_description": "ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´ąāĩ† URL. ā´’ā´¨āĩā´¨ā´ŋāĩŊ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ URL-ā´•āĩž ā´¨āĩŊā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´“ā´°āĩ‹ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´‚ ā´’ā´¨āĩā´¨ā´ŋā´¨āĩ ā´Ēāĩā´ąā´•āĩ† ā´’ā´¨āĩā´¨ā´žā´¯ā´ŋ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩā´ĩā´°āĩ† ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´‚. ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´ŋā´•āĩā´•ā´žā´¤āĩā´¤ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´•āĩž ā´“āĩēā´˛āĩˆā´¨ā´ŋāĩŊ ā´¤ā´ŋā´°ā´ŋā´•āĩ† ā´ĩā´°āĩā´¨āĩā´¨ā´¤āĩā´ĩā´°āĩ† ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚.", + "manage_concurrency": "ā´•āĩ‹āĩēā´•ā´ąāĩģā´¸ā´ŋ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_log_settings": "ā´˛āĩ‹ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "map_dark_style": "ā´Ąā´žāĩŧā´•āĩā´•āĩ ā´¸āĩā´ąāĩā´ąāĩˆāĩŊ", + "map_enable_description": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "map_gps_settings": "ā´Žā´žā´Ēāĩā´Ēāĩ & GPS ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "map_gps_settings_description": "ā´Žā´žā´Ēāĩā´Ēāĩ & GPS (ā´ąā´ŋā´ĩāĩ‡ā´´āĩā´¸āĩ ⴜā´ŋā´¯āĩ‹ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ) ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "map_implications": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´’ā´°āĩ ā´Ŧā´žā´šāĩā´¯ ⴟāĩˆāĩŊ ā´¸āĩ‡ā´ĩⴍⴤāĩā´¤āĩ† (tiles.immich.cloud) ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "map_light_style": "ā´˛āĩˆā´ąāĩā´ąāĩ ā´¸āĩā´ąāĩā´ąāĩˆāĩŊ", + "map_manage_reverse_geocoding_settings": "ā´ąā´ŋā´ĩāĩ‡ā´´āĩā´¸āĩ ⴜā´ŋā´¯āĩ‹ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "map_reverse_geocoding": "ā´ąā´ŋā´ĩāĩ‡ā´´āĩā´¸āĩ ⴜā´ŋā´¯āĩ‹ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ", + "map_reverse_geocoding_enable_description": "ā´ąā´ŋā´ĩāĩ‡ā´´āĩā´¸āĩ ⴜā´ŋā´¯āĩ‹ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "map_reverse_geocoding_settings": "ā´ąā´ŋā´ĩāĩ‡ā´´āĩā´¸āĩ ⴜā´ŋā´¯āĩ‹ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "map_settings": "ā´Žā´žā´Ēāĩā´Ēāĩ", + "map_settings_description": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "map_style_description": "ā´’ā´°āĩ style.json ā´Žā´žā´Ēāĩā´Ēāĩ ā´¤āĩ€ā´Žā´ŋā´˛āĩ‡ā´•āĩā´•āĩā´ŗāĩā´ŗ URL", + "memory_cleanup_job": "ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋ ā´•āĩā´˛āĩ€ā´¨ā´Ēāĩā´Ēāĩ", + "memory_generate_job": "ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋ ā´œā´¨ā´ąāĩ‡ā´ˇāĩģ", + "metadata_extraction_job": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´Žā´•āĩâ€Œā´¸āĩâ€Œā´Ÿāĩā´°ā´žā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "metadata_extraction_job_description": "ā´“ā´°āĩ‹ ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ GPS, ā´Žāĩā´–ā´™āĩā´™āĩž, ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ā´ˇāĩģ ā´¤āĩā´Ÿā´™āĩā´™ā´ŋā´¯ ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´Žā´•āĩâ€Œā´¸āĩâ€Œā´Ÿāĩā´°ā´žā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "metadata_faces_import_setting": "ā´Žāĩā´–ā´‚ ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "metadata_faces_import_setting_description": "ⴚā´ŋā´¤āĩā´°ā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† EXIF ā´Ąā´žā´ąāĩā´ąā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´¸āĩˆā´Ąāĩâ€Œā´•ā´žāĩŧ ā´Ģⴝⴞāĩā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´Žāĩā´–ā´™āĩā´™āĩž ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "metadata_settings": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "metadata_settings_description": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "migration_job": "ā´Žāĩˆā´—āĩā´°āĩ‡ā´ˇāĩģ", + "migration_job_description": "ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ†ā´¯āĩā´‚ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩā´Ÿāĩ†ā´¯āĩā´‚ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ģāĩ‹āĩžā´Ąāĩŧ ⴘⴟⴍⴝā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žāĩˆā´—āĩā´°āĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "nightly_tasks_cluster_faces_setting_description": "ā´Ēāĩā´¤āĩā´¤ā´žā´¯ā´ŋ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ ā´Žāĩā´–ā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ģāĩ‡ā´ˇāĩā´¯āĩŊ ā´ąāĩ†ā´•āĩā´•ā´—āĩā´¨ā´ŋā´ˇāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "nightly_tasks_cluster_new_faces_setting": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩ† ā´•āĩā´˛ā´¸āĩā´ąāĩā´ąāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "nightly_tasks_database_cleanup_setting": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´•āĩā´˛āĩ€ā´¨ā´Ēāĩā´Ēāĩ ā´Ÿā´žā´¸āĩā´•āĩā´•āĩā´•āĩž", + "nightly_tasks_database_cleanup_setting_description": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ēⴴⴝⴤāĩā´‚ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤āĩā´Žā´žā´¯ ā´Ąā´žā´ąāĩā´ą ā´ĩāĩƒā´¤āĩā´¤ā´ŋā´¯ā´žā´•āĩā´•āĩā´•", + "nightly_tasks_generate_memories_setting": "ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•", + "nightly_tasks_generate_memories_setting_description": "ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋā´•āĩž ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´•", + "nightly_tasks_missing_thumbnails_setting": "ⴍⴎāĩā´Ÿā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•", + "nightly_tasks_missing_thumbnails_setting_description": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ⴇⴞāĩā´˛ā´žā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩ† ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´žā´Ŗā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´•āĩā´¯āĩ‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "nightly_tasks_settings": "ā´°ā´žā´¤āĩā´°ā´ŋā´¯ā´ŋā´˛āĩ† ā´Ÿā´žā´¸āĩā´•āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "nightly_tasks_settings_description": "ā´°ā´žā´¤āĩā´°ā´ŋā´¯ā´ŋā´˛āĩ† ā´Ÿā´žā´¸āĩā´•āĩā´•āĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "nightly_tasks_start_time_setting": "ā´¤āĩā´Ÿā´™āĩā´™āĩā´¨āĩā´¨ ā´¸ā´Žā´¯ā´‚", + "nightly_tasks_start_time_setting_description": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´°ā´žā´¤āĩā´°ā´ŋā´¯ā´ŋā´˛āĩ† ā´Ÿā´žā´¸āĩā´•āĩā´•āĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´¤āĩā´Ÿā´™āĩā´™āĩā´¨āĩā´¨ ā´¸ā´Žā´¯ā´‚", + "nightly_tasks_sync_quota_usage_setting": "ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚ ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "nightly_tasks_sync_quota_usage_setting_description": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´‰ā´Ēā´¯āĩ‹ā´—ā´¤āĩā´¤āĩ† ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "no_paths_added": "ā´Ēā´žā´¤āĩā´¤āĩā´•āĩž ā´’ā´¨āĩā´¨āĩā´‚ ⴚāĩ‡āĩŧā´¤āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "no_pattern_added": "ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ā´’ā´¨āĩā´¨āĩā´‚ ⴚāĩ‡āĩŧā´¤āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "note_apply_storage_label_previous_assets": "ā´•āĩā´ąā´ŋā´Ēāĩā´Ēāĩ: ā´Žāĩā´Žāĩā´Ēāĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´ŦāĩŊ ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ, ⴇⴤāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "note_cannot_be_changed_later": "ā´•āĩā´ąā´ŋā´Ēāĩā´Ēāĩ: ⴇⴤāĩ ā´Ēā´ŋā´¨āĩā´¨āĩ€ā´Ÿāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛!", + "notification_email_from_address": "ⴅⴝⴝāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¯ā´žā´ŗāĩā´Ÿāĩ† ā´ĩā´ŋā´˛ā´žā´¸ā´‚", + "notification_email_from_address_description": "ⴅⴝⴝāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¯ā´žā´ŗāĩā´Ÿāĩ† ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´ĩā´ŋā´˛ā´žā´¸ā´‚, ā´‰ā´Ļā´žā´šā´°ā´Ŗā´¤āĩā´¤ā´ŋā´¨āĩ: \"Immich Photo Server \". ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‡ā´Žāĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ⴅⴝⴝāĩā´•āĩā´•ā´žāĩģ ā´…ā´¨āĩā´ĩā´žā´Ļā´Žāĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´ĩā´ŋā´˛ā´žā´¸ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩā´ĩāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•.", + "notification_email_host_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´šāĩ‹ā´¸āĩā´ąāĩā´ąāĩ (ā´‰ā´Ļā´ž. smtp.immich.app)", + "notification_email_ignore_certificate_errors": "ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´Ēā´ŋā´ļā´•āĩā´•āĩž ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´•", + "notification_email_ignore_certificate_errors_description": "TLS ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´¸ā´žā´§āĩā´¤ā´ž ā´Ēā´ŋā´ļā´•āĩā´•āĩž ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´• (ā´ļāĩā´Ēā´žāĩŧā´ļ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛)", + "notification_email_password_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "notification_email_port_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ (ā´‰ā´Ļā´ž. 25, 465, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ 587)", + "notification_email_sent_test_email_button": "ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´ž ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´¯ā´šāĩā´šāĩ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "notification_email_setting_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ⴅⴝⴕāĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "notification_email_test_email": "ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´ž ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴅⴝⴕāĩā´•āĩā´•", + "notification_email_test_email_failed": "ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´ž ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴅⴝⴕāĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ, ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "notification_email_test_email_sent": "{email} ā´Žā´¨āĩā´¨ ā´ĩā´ŋā´˛ā´žā´¸ā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´’ā´°āĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ā´ž ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´¯ā´šāĩā´šā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‡āĩģā´Ŧāĩ‹ā´•āĩā´¸āĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•.", + "notification_email_username_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒā´¨ā´žā´Žā´‚", + "notification_enable_email_notifications": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "notification_settings": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "notification_settings_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩ†ā´¯āĩā´ŗāĩā´ŗ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth_auto_launch": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¤āĩā´Ÿā´™āĩā´™āĩā´•", + "oauth_auto_launch_description": "ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩ‡ā´œā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´Žāĩā´Ēāĩ‹āĩž OAuth ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ģāĩā´˛āĩ‹ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴆⴰⴂⴭā´ŋā´•āĩā´•āĩā´•", + "oauth_auto_register": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴰⴜā´ŋā´¸āĩā´ąāĩā´ąāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth_auto_register_description": "OAuth ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¸āĩˆāĩģ ā´‡āĩģ ⴚāĩ†ā´¯āĩā´¤ ā´ļāĩ‡ā´ˇā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴰⴜā´ŋā´¸āĩā´ąāĩā´ąāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth_button_text": "ā´Ŧⴟāĩā´Ÿā´Ŗā´ŋā´˛āĩ† ā´ĩā´žā´šā´•ā´‚", + "oauth_client_secret_description": "OAuth ā´Ļā´žā´¤ā´žā´ĩāĩ PKCE (ā´Ēāĩā´°āĩ‚ā´Ģāĩ ā´•āĩ€ ā´Ģāĩ‹āĩŧ ā´•āĩ‹ā´Ąāĩ ā´Žā´•āĩā´¸āĩā´šāĩ‡ā´žāĩā´šāĩ) ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ", + "oauth_enable_description": "OAuth ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth_mobile_redirect_uri": "ā´ŽāĩŠā´ŦāĩˆāĩŊ ā´ąāĩ€ā´Ąā´¯ā´ąā´•āĩā´Ÿāĩ URI", + "oauth_mobile_redirect_uri_override": "ā´ŽāĩŠā´ŦāĩˆāĩŊ ā´ąāĩ€ā´Ąā´¯ā´ąā´•āĩā´Ÿāĩ URI ā´“ā´ĩāĩŧā´ąāĩˆā´Ąāĩ", + "oauth_mobile_redirect_uri_override_description": "OAuth ā´Ļā´žā´¤ā´žā´ĩāĩ \"{callback}\" ā´Ēāĩ‹ā´˛āĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´ŽāĩŠā´ŦāĩˆāĩŊ URI ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•ā´žā´¤āĩā´¤ā´Ēāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "oauth_role_claim": "ā´ąāĩ‹āĩž ā´•āĩā´˛āĩ†ā´¯ā´ŋā´‚", + "oauth_role_claim_description": "ⴈ ā´•āĩā´˛āĩ†ā´¯ā´ŋā´Žā´ŋā´¨āĩā´ąāĩ† ā´¸ā´žā´¨āĩā´¨ā´ŋā´§āĩā´¯ā´¤āĩā´¤āĩ† ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´…ā´Ąāĩā´Žā´ŋāĩģ ⴆⴕāĩā´¸ā´¸āĩ ā´¨āĩŊā´•āĩā´•. ā´•āĩā´˛āĩ†ā´¯ā´ŋā´Žā´ŋā´¨āĩ 'user' ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ 'admin' ā´Žā´¨āĩā´¨āĩ ⴉ⴪āĩā´Ÿā´žā´•ā´žā´‚.", + "oauth_settings": "OAuth", + "oauth_settings_description": "OAuth ā´˛āĩ‹ā´—ā´ŋāĩģ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth_settings_more_details": "ⴈ ā´Ģāĩ€ā´šāĩā´šā´ąā´ŋā´¨āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩā´ŗāĩā´ŗ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ, ā´Ąāĩ‹ā´•āĩā´¸āĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•.", + "oauth_storage_label_claim": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´ŦāĩŊ ā´•āĩā´˛āĩ†ā´¯ā´ŋā´‚", + "oauth_storage_label_claim_description": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´ŦāĩŊ ⴈ ā´•āĩā´˛āĩ†ā´¯ā´ŋā´Žā´ŋā´¨āĩā´ąāĩ† ā´Žāĩ‚ā´˛āĩā´¯ā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•.", + "oauth_storage_quota_claim": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ā´•āĩā´˛āĩ†ā´¯ā´ŋā´‚", + "oauth_storage_quota_claim_description": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ⴈ ā´•āĩā´˛āĩ†ā´¯ā´ŋā´Žā´ŋā´¨āĩā´ąāĩ† ā´Žāĩ‚ā´˛āĩā´¯ā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•.", + "oauth_storage_quota_default": "ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿāĩ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ (GiB)", + "oauth_storage_quota_default_description": "ā´•āĩā´˛āĩ†ā´¯ā´ŋā´‚ ā´’ā´¨āĩā´¨āĩā´‚ ā´¨āĩŊā´•ā´žā´¤āĩā´¤ā´Ēāĩā´Ēāĩ‹āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ (GiB-ā´¯ā´ŋāĩŊ).", + "oauth_timeout": "ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴝāĩā´Ÿāĩ† ā´¸ā´Žā´¯ā´Ēā´°ā´ŋā´§ā´ŋ", + "oauth_timeout_description": "ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴕāĩžā´•āĩā´•āĩā´ŗāĩā´ŗ ā´¸ā´Žā´¯ā´Ēā´°ā´ŋā´§ā´ŋ (ā´Žā´ŋā´˛āĩā´˛ā´ŋā´¸āĩ†ā´•āĩā´•āĩģā´Ąā´ŋāĩŊ)", + "password_enable_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋā´˛āĩā´‚ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "password_settings": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ", + "password_settings_description": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "paths_validated_successfully": "ā´Žā´˛āĩā´˛ā´ž ā´Ēā´žā´¤āĩā´¤āĩā´•ā´ŗāĩā´‚ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´¸ā´žā´§āĩ‚ā´•ā´°ā´ŋⴚāĩā´šāĩ", + "person_cleanup_job": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋ ā´•āĩā´˛āĩ€ā´¨ā´Ēāĩā´Ēāĩ", + "quota_size_gib": "ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ā´ĩā´˛āĩā´Ēāĩā´Ēā´‚ (GiB)", + "refreshing_all_libraries": "ā´Žā´˛āĩā´˛ā´ž ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•ā´ŗāĩā´‚ ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "registration": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ⴰⴜā´ŋā´¸āĩā´Ÿāĩā´°āĩ‡ā´ˇāĩģ", + "registration_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗā´žā´Ŗāĩ ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´¤āĩā´¤ā´ŋā´˛āĩ† ā´†ā´Ļāĩā´¯ā´¤āĩā´¤āĩ† ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩāĩ ā´Žā´¨āĩā´¨ā´¤ā´ŋā´¨ā´žāĩŊ, ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ† ā´…ā´Ąāĩā´Žā´ŋāĩģ ⴆⴝā´ŋ ā´¨ā´ŋā´¯ā´Žā´ŋā´•āĩā´•āĩā´‚. ā´­ā´°ā´Ŗā´Ēā´°ā´Žā´žā´¯ ⴜāĩ‹ā´˛ā´ŋā´•āĩžā´•āĩā´•āĩ ā´¨ā´ŋā´™āĩā´™āĩž ⴉⴤāĩā´¤ā´°ā´ĩā´žā´Ļā´ŋā´¯ā´žā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´‚, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´…ā´§ā´ŋā´• ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´¨ā´ŋā´™āĩā´™ā´ŗā´žā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´‚.", + "require_password_change_on_login": "ā´†ā´Ļāĩā´¯ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ‹ā´Ÿāĩ ā´†ā´ĩā´ļāĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´•", + "reset_settings_to_default": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "reset_settings_to_recent_saved": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "scanning_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "search_jobs": "ⴜāĩ‹ā´˛ā´ŋā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•â€Ļ", + "send_welcome_email": "ā´¸āĩā´ĩā´žā´—ā´¤ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴅⴝⴕāĩā´•āĩā´•", + "server_external_domain_settings": "ā´Ŧā´žā´šāĩā´¯ ā´ĄāĩŠā´Žāĩ†ā´¯āĩāĩģ", + "server_external_domain_settings_description": "ā´ĒāĩŠā´¤āĩā´ĩā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩžā´•āĩā´•āĩā´ŗāĩā´ŗ ā´ĄāĩŠā´Žāĩ†ā´¯āĩāĩģ, http(s):// ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩ†", + "server_public_users": "ā´ĒāĩŠā´¤āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•āĩž", + "server_public_users_description": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´’ā´°āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ†ā´¯āĩā´‚ (ā´Ēāĩ‡ā´°āĩā´‚ ā´‡ā´Žāĩ†ā´¯ā´ŋā´˛āĩā´‚) ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´‚. ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•āĩžā´•āĩā´•āĩ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ⴞⴭāĩā´¯ā´Žā´žā´•āĩ‚.", + "server_settings": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "server_settings_description": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "server_welcome_message": "ā´¸āĩā´ĩā´žā´—ā´¤ ⴏⴍāĩā´Ļāĩ‡ā´ļā´‚", + "server_welcome_message_description": "ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩ‡ā´œā´ŋāĩŊ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´’ā´°āĩ ⴏⴍāĩā´Ļāĩ‡ā´ļā´‚.", + "sidecar_job": "ā´¸āĩˆā´Ąāĩâ€Œā´•ā´žāĩŧ ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą", + "sidecar_job_description": "ā´Ģā´¯āĩŊā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¸āĩˆā´Ąāĩâ€Œā´•ā´žāĩŧ ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•ā´¯āĩ‹ ā´¸ā´ŋāĩģā´•āĩā´°āĩŠā´Ŗāĩˆā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•ā´¯āĩ‹ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "slideshow_duration_description": "ā´“ā´°āĩ‹ ⴚā´ŋā´¤āĩā´°ā´ĩāĩā´‚ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žā´¨āĩā´ŗāĩā´ŗ ā´¸āĩ†ā´•āĩā´•āĩģā´Ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "smart_search_job_description": "ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šā´ŋā´¨āĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´Žāĩ†ā´ˇāĩ€āĩģ ā´˛āĩ‡ā´Ŗā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "storage_template_date_time_description": "ā´Ąāĩ‡ā´ąāĩā´ąāĩā´Ÿāĩˆā´‚ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´•āĩā´°ā´ŋā´¯āĩ‡ā´ˇāĩģ ⴟāĩˆā´‚ā´¸āĩā´ąāĩā´ąā´žā´Žāĩā´Ēāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "storage_template_date_time_sample": "ā´¸ā´žā´Žāĩā´Ēā´ŋāĩž ā´¸ā´Žā´¯ā´‚ {date}", + "storage_template_enable_description": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´Žā´žāĩā´šā´ŋāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "storage_template_hash_verification_enabled": "ā´šā´žā´ˇāĩ ā´ĩāĩ†ā´°ā´ŋā´Ģā´ŋā´•āĩā´•āĩ‡ā´ˇāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ", + "storage_template_hash_verification_enabled_description": "ā´šā´žā´ˇāĩ ā´ĩāĩ†ā´°ā´ŋā´Ģā´ŋā´•āĩā´•āĩ‡ā´ˇāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēāĩā´°ā´¤āĩā´¯ā´žā´˜ā´žā´¤ā´™āĩā´™ā´ŗāĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩ ā´‰ā´ąā´Ēāĩā´Ēā´ŋā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ⴇⴤāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´°āĩā´¤āĩ", + "storage_template_migration": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´Žāĩˆā´—āĩā´°āĩ‡ā´ˇāĩģ", + "storage_template_migration_description": "ā´Žāĩā´Žāĩā´Ēāĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† {template} ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "storage_template_migration_info": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´Žā´˛āĩā´˛ā´ž ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩģⴎⴍāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ⴚāĩ†ā´ąā´ŋⴝⴕāĩā´ˇā´°ā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąāĩā´‚. ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąā´ŋā´˛āĩ† ā´Žā´žā´ąāĩā´ąā´™āĩā´™āĩž ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•āĩ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´Ŧā´žā´§ā´•ā´Žā´žā´•āĩ‚. ā´Žāĩā´Žāĩā´Ēāĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´Žāĩāĩģā´•ā´žā´˛ ā´Ēāĩā´°ā´žā´Ŧā´˛āĩā´¯ā´¤āĩā´¤āĩ‹ā´Ÿāĩ† ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ, {job} ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•.", + "storage_template_migration_job": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´Žāĩˆā´—āĩā´°āĩ‡ā´ˇāĩģ ⴜāĩ‹ā´˛ā´ŋ", + "storage_template_more_details": "ⴈ ā´Ģāĩ€ā´šāĩā´šā´ąā´ŋā´¨āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩā´ŗāĩā´ŗ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ, ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ-ⴉⴂ ā´…ā´¤ā´ŋāĩģāĩā´ąāĩ† ā´Ēāĩā´°ā´¤āĩā´¯ā´žā´˜ā´žā´¤ā´™āĩā´™ā´ŗāĩā´‚ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "storage_template_onboarding_description_v2": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩāĩ ā´¨ā´ŋāĩŧā´ĩⴚā´ŋⴚāĩā´š ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ⴈ ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´Ģⴝⴞāĩā´•āĩž ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´“āĩŧā´—ā´¨āĩˆā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´‚. ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ, ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´Ąāĩ‹ā´•āĩā´¯āĩā´Žāĩ†ā´¨āĩā´ąāĩ‡ā´ˇāĩģ ā´•ā´žā´Ŗāĩā´•.", + "storage_template_path_length": "ā´ā´•ā´Ļāĩ‡ā´ļ ā´Ēā´žā´¤āĩā´¤āĩ ā´Ļāĩˆāĩŧⴘāĩā´¯ ā´Ēā´°ā´ŋā´§ā´ŋ: {length, number}/{limit, number}", + "storage_template_settings": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ", + "storage_template_settings_description": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´Ģāĩ‹āĩžā´Ąāĩŧ ⴘⴟⴍⴝāĩā´‚ ā´Ģā´¯āĩŊ ā´¨ā´žā´Žā´ĩāĩā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "storage_template_user_label": "{label} ā´Žā´¨āĩā´¨ā´¤āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´Ŧā´˛ā´žā´Ŗāĩ", + "system_settings": "ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "tag_cleanup_job": "ā´Ÿā´žā´—āĩ ā´•āĩā´˛āĩ€ā´¨ā´Ēāĩā´Ēāĩ", + "template_email_available_tags": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąā´ŋāĩŊ ⴇⴍā´ŋā´Ēāĩā´Ēā´ąā´¯āĩā´¨āĩā´¨ ā´ĩāĩ‡ā´°ā´ŋā´¯ā´Ŧā´ŋā´ŗāĩā´•āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žā´‚: {tags}", + "template_email_if_empty": "ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´Ŗāĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿāĩ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´‚.", + "template_email_invite_album": "ā´†āĩŊā´Ŧā´‚ ā´•āĩā´ˇā´Ŗā´ŋā´•āĩā´•ā´žā´¨āĩā´ŗāĩā´ŗ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ", + "template_email_preview": "ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚", + "template_email_settings": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩā´•āĩž", + "template_email_update_album": "ā´†āĩŊā´Ŧā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ", + "template_email_welcome": "ā´¸āĩā´ĩā´žā´—ā´¤ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ", + "template_settings": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩā´•āĩž", + "template_settings_description": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ⴇⴎāĩā´Ÿā´žā´¨āĩā´¸āĩƒā´¤ ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "theme_custom_css_settings": "ā´•ā´¸āĩā´ąāĩā´ąā´‚ CSS", + "theme_custom_css_settings_description": "ā´•ā´žā´¸āĩā´•āĩ‡ā´Ąā´ŋā´‚ā´—āĩ ā´¸āĩā´ąāĩā´ąāĩˆāĩŊ ā´ˇāĩ€ā´ąāĩā´ąāĩā´•āĩž (CSS) Immich-ā´¨āĩā´ąāĩ† ā´Ąā´ŋā´¸āĩˆāĩģ ⴇⴎāĩā´Ÿā´žā´¨āĩā´¸āĩƒā´¤ā´Žā´žā´•āĩā´•ā´žāĩģ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "theme_settings": "ā´¤āĩ€ā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "theme_settings_description": "Immich ā´ĩāĩ†ā´Ŧāĩ ⴇⴍāĩā´ąāĩŧā´Ģāĩ‡ā´¸ā´ŋā´¨āĩā´ąāĩ† ā´•ā´¸āĩā´ąāĩā´ąā´Žāĩˆā´¸āĩ‡ā´ˇāĩģ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "thumbnail_generation_job": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•", + "thumbnail_generation_job_description": "ā´“ā´°āĩ‹ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´‚ ā´ĩā´˛āĩā´¤āĩā´‚ ⴚāĩ†ā´ąāĩā´¤āĩā´‚ ā´Žā´™āĩā´™ā´ŋⴝⴤāĩā´Žā´žā´¯ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•ā´ŗāĩā´‚ ā´“ā´°āĩ‹ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩā´‚ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•ā´ŗāĩā´‚ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•", + "transcoding_acceleration_api": "ⴆⴕāĩā´¸ā´ŋā´˛ā´ąāĩ‡ā´ˇāĩģ API", + "transcoding_acceleration_api_description": "ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´¤āĩā´ĩā´°ā´ŋā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´ĩāĩā´Žā´žā´¯ā´ŋ ⴏⴂā´ĩā´Ļā´ŋā´•āĩā´•āĩā´¨āĩā´¨ API. ⴈ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ 'ā´Ŧāĩ†ā´¸āĩā´ąāĩā´ąāĩ ā´Žā´Ģāĩ‡āĩŧⴟāĩā´Ÿāĩ' ⴆ⴪āĩ: ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´žāĩŊ ā´¸āĩ‹ā´Ģāĩā´ąāĩā´ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąā´ŋā´‚ā´—ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´‚. ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ VP9 ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´•ā´¯āĩ‹ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•ā´žā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´•ā´¯āĩ‹ ⴚāĩ†ā´¯āĩā´¯ā´žā´‚.", + "transcoding_acceleration_nvenc": "NVENC (NVIDIA GPU ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ)", + "transcoding_acceleration_qsv": "ā´•āĩā´ĩā´ŋā´•āĩā´•āĩ ā´¸ā´ŋā´™āĩā´•āĩ (7-ā´žā´‚ ā´¤ā´˛ā´Žāĩā´ą ⴇⴍāĩā´ąāĩŊ ā´¸ā´ŋā´Ēā´ŋā´¯āĩ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´…ā´¤ā´ŋā´¨āĩā´ļāĩ‡ā´ˇā´Žāĩā´ŗāĩā´ŗā´¤āĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ)", + "transcoding_acceleration_rkmpp": "RKMPP (Rockchip SOC-ā´•ā´ŗā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´‚)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´“ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•āĩā´•āĩž", + "transcoding_accepted_audio_codecs_description": "ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´žā´¤āĩā´¤ ā´“ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•āĩā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴚā´ŋā´˛ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴍⴝⴙāĩā´™āĩžā´•āĩā´•āĩ ā´ĩāĩ‡ā´Ŗāĩā´Ÿā´ŋ ā´Žā´žā´¤āĩā´°ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_accepted_containers": "ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¯āĩâ€Œā´¨ā´ąāĩā´•āĩž", + "transcoding_accepted_containers_description": "MP4-ā´˛āĩ‡ā´•āĩā´•āĩ ā´ąāĩ€ā´Žā´•āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´žā´¤āĩā´¤ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¯āĩâ€Œā´¨āĩŧ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴚā´ŋā´˛ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴍⴝⴙāĩā´™āĩžā´•āĩā´•āĩ ā´ĩāĩ‡ā´Ŗāĩā´Ÿā´ŋ ā´Žā´žā´¤āĩā´°ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_accepted_video_codecs": "ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•āĩā´•āĩž", + "transcoding_accepted_video_codecs_description": "ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´žā´¤āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•āĩā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴚā´ŋā´˛ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴍⴝⴙāĩā´™āĩžā´•āĩā´•āĩ ā´ĩāĩ‡ā´Ŗāĩā´Ÿā´ŋ ā´Žā´žā´¤āĩā´°ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_advanced_options_description": "ā´Žā´ŋā´•āĩā´• ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩā´‚ ā´Žā´žā´ąāĩā´ąā´‚ ā´ĩā´°āĩā´¤āĩā´¤āĩ‡ā´Ŗāĩā´Ÿā´¤ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "transcoding_audio_codec": "ā´“ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩ", + "transcoding_audio_codec_description": "Opus ā´ā´ąāĩā´ąā´ĩāĩā´‚ ⴉⴝāĩŧā´¨āĩā´¨ ā´¨ā´ŋā´˛ā´ĩā´žā´°ā´Žāĩā´ŗāĩā´ŗ ā´“ā´Ēāĩā´ˇā´¨ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Ēā´´ā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗāĩā´Žā´žā´¯āĩ‹ ā´¸āĩ‹ā´Ģāĩā´ąāĩā´ąāĩâ€Œā´ĩāĩ†ā´¯ā´ąāĩā´Žā´žā´¯āĩ‹ ⴇⴤā´ŋā´¨āĩ ā´•āĩā´ąā´žāĩā´ž ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´¤ā´¯āĩ‡ ⴉⴺāĩā´ŗāĩ‚.", + "transcoding_bitrate_description": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąā´ŋā´¨āĩ‡ā´•āĩā´•ā´žāĩž ⴉⴝāĩŧā´¨āĩā´¨ā´¤āĩ‹ ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąā´ŋāĩŊ ā´…ā´˛āĩā´˛ā´žā´¤āĩā´¤ā´¤āĩ‹ ⴆⴝ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž", + "transcoding_codecs_learn_more": "ā´‡ā´ĩā´ŋⴟāĩ† ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ēā´Ļā´™āĩā´™ā´ŗāĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩ ā´•āĩ‚ā´Ÿāĩā´¤ā´˛ā´ąā´ŋā´¯ā´žāĩģ, H.264 ā´•āĩ‹ā´Ąāĩ†ā´•āĩ, HEVC ā´•āĩ‹ā´Ąāĩ†ā´•āĩ, VP9 ā´•āĩ‹ā´Ąāĩ†ā´•āĩ ā´Žā´¨āĩā´¨ā´ŋā´ĩā´¯āĩā´Ÿāĩ† FFmpeg ā´Ąāĩ‹ā´•āĩā´¯āĩā´Žāĩ†ā´¨āĩā´ąāĩ‡ā´ˇāĩģ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•.", + "transcoding_constant_quality_mode": "ā´¸āĩā´Ĩā´ŋā´°ā´Žā´žā´¯ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´Žāĩ‹ā´Ąāĩ", + "transcoding_constant_quality_mode_description": "CQP-ā´¯āĩ†ā´•āĩā´•ā´žāĩž ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´Ŗāĩ ICQ, ā´Žā´¨āĩā´¨ā´žāĩŊ ⴚā´ŋā´˛ ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ⴆⴕāĩā´¸ā´ŋā´˛ā´ąāĩ‡ā´ˇāĩģ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™āĩž ⴈ ā´Žāĩ‹ā´Ąā´ŋā´¨āĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛. ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋā´¯āĩā´ŗāĩā´ŗ ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ⴈ ā´“ā´Ēāĩā´ˇāĩģ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´¨ā´ŋāĩŧā´Ļāĩā´Ļā´ŋā´ˇāĩā´Ÿ ā´Žāĩ‹ā´Ąā´ŋā´¨āĩ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´‚. ICQ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•ā´žā´¤āĩā´¤ā´¤ā´ŋā´¨ā´žāĩŊ NVENC ⴇⴤāĩ ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_constant_rate_factor": "ā´•āĩ‹āĩēā´¸āĩā´ąāĩā´ąā´¨āĩā´ąāĩ ā´ąāĩ‡ā´ąāĩā´ąāĩ ā´Ģā´žā´•āĩā´Ÿāĩŧ (-crf)", + "transcoding_constant_rate_factor_description": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´¯āĩā´Ÿāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´¨ā´ŋā´˛. ā´¸ā´žā´§ā´žā´°ā´Ŗ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž H.264-ā´¨āĩ 23, HEVC-ā´•āĩā´•āĩ 28, VP9-ā´¨āĩ 31, AV1-ā´¨āĩ 35 ā´Žā´¨āĩā´¨ā´ŋā´™āĩā´™ā´¨āĩ†ā´¯ā´žā´Ŗāĩ. ā´•āĩā´ąā´žāĩā´ž ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_disabled_description": "ā´’ā´°āĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´¯āĩā´‚ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´°āĩā´¤āĩ, ⴇⴤāĩ ⴚā´ŋā´˛ ā´•āĩā´˛ā´¯ā´ŋā´¨āĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´Ēāĩā´˛āĩ‡ā´Ŧā´žā´•āĩā´•āĩ ⴤⴟⴏāĩā´¸ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ŋā´¯āĩ‡ā´•āĩā´•ā´žā´‚", + "transcoding_encoding_options": "ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "transcoding_encoding_options_description": "ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•āĩā´•āĩž, ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ā´ˇāĩģ, ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž, ā´Žā´ąāĩā´ąāĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž ā´Žā´¨āĩā´¨ā´ŋā´ĩ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "transcoding_hardware_acceleration": "ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ⴆⴕāĩā´¸ā´ŋā´˛ā´ąāĩ‡ā´ˇāĩģ", + "transcoding_hardware_acceleration_description": "ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗā´¤āĩ: ā´ĩāĩ‡ā´—ⴤⴝāĩ‡ā´ąā´ŋā´¯ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ, ā´Žā´¨āĩā´¨ā´žāĩŊ ā´’ā´°āĩ‡ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąā´ŋāĩŊ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´•āĩā´ąā´šāĩā´šāĩ‡ā´•āĩā´•ā´žā´‚", + "transcoding_hardware_decoding": "ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ā´Ąāĩ€ā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ", + "transcoding_hardware_decoding_setting_description": "ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´Žā´žā´¤āĩā´°ā´‚ ā´¤āĩā´ĩā´°ā´ŋā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Ēā´•ā´°ā´‚ ā´Žāĩģā´Ąāĩ-ⴟāĩ-ā´Žāĩģā´Ąāĩ ⴆⴕāĩā´¸ā´ŋā´˛ā´ąāĩ‡ā´ˇāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´Žā´˛āĩā´˛ā´ž ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗā´ŋā´˛āĩā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋⴚāĩā´šāĩ‡ā´•āĩā´•ā´ŋā´˛āĩā´˛.", + "transcoding_max_b_frames": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋ-ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´Žāĩā´•āĩž", + "transcoding_max_b_frames_description": "ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´•ā´‚ā´Ēāĩā´°ā´ˇāĩģ ā´•ā´žā´°āĩā´¯ā´•āĩā´ˇā´Žā´¤ ā´Žāĩ†ā´šāĩā´šā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´Ēā´´ā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ† ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ⴆⴕāĩā´¸ā´ŋā´˛ā´ąāĩ‡ā´ˇā´¨āĩā´Žā´žā´¯ā´ŋ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿā´Ŗā´Žāĩ†ā´¨āĩā´¨ā´ŋā´˛āĩā´˛. 0 ā´Ŧā´ŋ-ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´Žāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´…ā´¤āĩ‡ā´¸ā´Žā´¯ā´‚ -1 ⴈ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_max_bitrate": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ", + "transcoding_max_bitrate_description": "ā´’ā´°āĩ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Žā´¯ā´ŋāĩŊ ⴚāĩ†ā´ąā´ŋā´¯āĩŠā´°āĩ ā´ĩā´ŋⴟāĩā´Ÿāĩā´ĩāĩ€ā´´āĩā´šā´¯āĩ‹ā´Ÿāĩ† ā´Ģā´¯āĩŊ ā´ĩā´˛āĩā´Ēāĩā´Ēā´™āĩā´™āĩž ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Ēāĩā´°ā´ĩā´šā´¨ā´žā´¤āĩ€ā´¤ā´Žā´žā´•āĩā´•āĩā´‚. 720p-āĩŊ, ā´¸ā´žā´§ā´žā´°ā´Ŗ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž VP9 ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ HEVC-ā´•āĩā´•āĩ 2600 kbit/s, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ H.264-ā´¨āĩ 4500 kbit/s ⴆ⴪āĩ. 0 ⴆⴝā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋⴚāĩā´šā´žāĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´‚.", + "transcoding_max_keyframe_interval": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´•āĩ€ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´‚ ā´‡ā´Ÿā´ĩāĩ‡ā´ŗ", + "transcoding_max_keyframe_interval_description": "ā´•āĩ€ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´Žāĩā´•āĩžā´•āĩā´•ā´ŋⴟⴝā´ŋā´˛āĩā´ŗāĩā´ŗ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´‚ ā´Ļāĩ‚ā´°ā´‚ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´•āĩā´ąā´žāĩā´ž ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´•ā´‚ā´Ēāĩā´°ā´ˇāĩģ ā´•ā´žā´°āĩā´¯ā´•āĩā´ˇā´Žā´¤ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´¸āĩ€ā´•āĩā´•āĩ ā´¸ā´Žā´¯ā´‚ ā´Žāĩ†ā´šāĩā´šā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•ā´¯āĩā´‚ ā´ĩāĩ‡ā´—ⴤⴝāĩ‡ā´ąā´ŋā´¯ ā´šā´˛ā´¨ā´Žāĩā´ŗāĩā´ŗ ā´°ā´‚ā´—ā´™āĩā´™ā´ŗā´ŋāĩŊ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´Žāĩ†ā´šāĩā´šā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ‡ā´•āĩā´•ā´žā´‚. 0 ⴈ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_optimal_description": "ⴞⴕāĩā´ˇāĩā´¯ā´Žā´ŋⴟāĩā´Ÿ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ⴎⴍāĩ‡ā´•āĩā´•ā´žāĩž ⴉⴝāĩŧā´¨āĩā´¨ā´¤āĩ‹ ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąā´ŋāĩŊ ā´…ā´˛āĩā´˛ā´žā´¤āĩā´¤ā´¤āĩ‹ ⴆⴝ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž", + "transcoding_policy": "ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴍⴝⴂ", + "transcoding_policy_description": "ā´’ā´°āĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Žā´Ēāĩā´Ēāĩ‹āĩž ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "transcoding_preferred_hardware_device": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´¨āĩā´¨ ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ā´‰ā´Ēā´•ā´°ā´Ŗā´‚", + "transcoding_preferred_hardware_device_description": "VAAPI, QSV ā´Žā´¨āĩā´¨ā´ŋā´ĩā´¯āĩā´•āĩā´•āĩ ā´Žā´žā´¤āĩā´°ā´‚ ā´Ŧā´žā´§ā´•ā´‚. ā´šā´žāĩŧā´Ąāĩâ€Œā´ĩāĩ†ā´¯āĩŧ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąā´ŋā´‚ā´—ā´ŋā´¨ā´žā´¯ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ dri ā´¨āĩ‹ā´Ąāĩ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_preset_preset": "ā´Ēāĩā´°āĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ (-preset)", + "transcoding_preset_preset_description": "ā´•ā´‚ā´Ēāĩā´°ā´ˇāĩģ ā´ĩāĩ‡ā´—ā´¤. ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´žāĩā´ž ā´Ēāĩā´°āĩ€ā´¸āĩ†ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ†ā´ąā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´’ā´°āĩ ā´¨ā´ŋā´ļāĩā´šā´ŋā´¤ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ ⴞⴕāĩā´ˇāĩā´¯ā´Žā´ŋⴟāĩā´Žāĩā´Ēāĩ‹āĩž ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. 'faster'-ā´¨āĩ‡ā´•āĩā´•ā´žāĩž ⴉⴝāĩŧā´¨āĩā´¨ ā´ĩāĩ‡ā´—ā´¤ VP9 ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_reference_frames": "ā´ąā´Ģā´ąāĩģā´¸āĩ ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´Žāĩā´•āĩž", + "transcoding_reference_frames_description": "ā´¨āĩŊā´•ā´ŋā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´’ā´°āĩ ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´‚ ā´•ā´‚ā´Ēāĩā´°ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´ąā´Ģā´ąāĩģā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿ ā´Ģāĩā´°āĩ†ā´¯ā´ŋā´Žāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚. ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´•ā´‚ā´Ēāĩā´°ā´ˇāĩģ ā´•ā´žā´°āĩā´¯ā´•āĩā´ˇā´Žā´¤ ā´Žāĩ†ā´šāĩā´šā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ. 0 ⴈ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_required_description": "ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąā´ŋāĩŊ ā´…ā´˛āĩā´˛ā´žā´¤āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´Žā´žā´¤āĩā´°ā´‚", + "transcoding_settings": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ⴟāĩā´°ā´žāĩģā´¸āĩā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "transcoding_settings_description": "ā´ā´¤āĩŠā´•āĩā´•āĩ† ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗā´žā´Ŗāĩ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´¤āĩ†ā´¨āĩā´¨āĩā´‚ ā´…ā´ĩ ā´Žā´™āĩā´™ā´¨āĩ† ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "transcoding_target_resolution": "ⴞⴕāĩā´ˇāĩā´¯ā´Žā´ŋⴟāĩā´¨āĩā´¨ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ā´ˇāĩģ", + "transcoding_target_resolution_description": "ⴉⴝāĩŧā´¨āĩā´¨ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ⴎⴍāĩā´•āĩžā´•āĩā´•āĩ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´¸ā´Žā´¯ā´Žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´‚, ā´ĩā´˛ā´ŋā´¯ ā´Ģā´¯āĩŊ ā´ĩā´˛āĩā´Ēāĩā´Ēā´Žāĩā´Ŗāĩā´Ÿā´žā´•āĩā´‚, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗā´ļāĩ‡ā´ˇā´ŋ ā´•āĩā´ąā´¯āĩā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "transcoding_temporal_aq": "ⴟāĩ†ā´Žāĩā´Ēā´ąāĩŊ AQ", + "transcoding_temporal_aq_description": "NVENC-ā´•āĩā´•āĩ ā´Žā´žā´¤āĩā´°ā´‚ ā´Ŧā´žā´§ā´•ā´‚. ⴉⴝāĩŧā´¨āĩā´¨ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™ā´ŗāĩā´‚ ā´•āĩā´ąā´žāĩā´ž ⴚⴞⴍā´ĩāĩā´Žāĩā´ŗāĩā´ŗ ā´°ā´‚ā´—ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´Ēā´´ā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗāĩā´Žā´žā´¯ā´ŋ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿā´Ŗā´Žāĩ†ā´¨āĩā´¨ā´ŋā´˛āĩā´˛.", + "transcoding_threads": "ā´¤āĩā´°āĩ†ā´Ąāĩā´•āĩž", + "transcoding_threads_description": "ⴉⴝāĩŧā´¨āĩā´¨ ā´Žāĩ‚ā´˛āĩā´¯ā´™āĩā´™āĩž ā´ĩāĩ‡ā´—ⴤⴝāĩ‡ā´ąā´ŋā´¯ ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴍⴝā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩ ā´Žā´ąāĩā´ąāĩ ⴜāĩ‹ā´˛ā´ŋā´•āĩž ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩā´ąā´šāĩā´šāĩ ā´‡ā´Ÿā´‚ ā´¨āĩŊā´•āĩā´¨āĩā´¨āĩ. ⴈ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´¸ā´ŋā´Ēā´ŋā´¯āĩ ā´•āĩ‹ā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´¤āĩā´¤āĩ‡ā´•āĩā´•ā´žāĩž ā´•āĩ‚ā´Ÿāĩā´¤ā´˛ā´žā´•ā´°āĩā´¤āĩ. 0 ⴆⴝā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋⴚāĩā´šā´žāĩŊ ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋā´¯ā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_tone_mapping": "ⴟāĩ‹āĩē-ā´Žā´žā´Ēāĩā´Ēā´ŋā´‚ā´—āĩ", + "transcoding_tone_mapping_description": "HDR ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž SDR-ā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´°ā´ŋā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´…ā´ĩā´¯āĩā´Ÿāĩ† ā´°āĩ‚ā´Ēā´‚ ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤ā´žāĩģ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´“ā´°āĩ‹ ā´…āĩŊā´—āĩ‹ā´°ā´ŋā´¤ā´ĩāĩā´‚ ā´¨ā´ŋā´ąā´‚, ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž, ā´¤āĩ†ā´ŗā´ŋⴚāĩā´šā´‚ ā´Žā´¨āĩā´¨ā´ŋā´ĩā´¯āĩā´•āĩā´•ā´žā´¯ā´ŋ ā´ĩāĩā´¯ā´¤āĩā´¯ā´¸āĩā´¤ ā´ĩā´ŋⴟāĩā´Ÿāĩā´ĩāĩ€ā´´āĩā´šā´•āĩž ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. Hable ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, Mobius ā´¨ā´ŋā´ąā´‚ ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, Reinhard ā´¤āĩ†ā´ŗā´ŋⴚāĩā´šā´‚ ⴏⴂⴰⴕāĩā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "transcoding_transcode_policy": "ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴍⴝⴂ", + "transcoding_transcode_policy_description": "ā´’ā´°āĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Žā´Ēāĩā´Ēāĩ‹āĩž ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´‚ ā´Žā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ†ā´•āĩā´•āĩā´ąā´ŋⴚāĩā´šāĩā´ŗāĩā´ŗ ⴍⴝⴂ. HDR ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´Žā´˛āĩā´˛ā´žā´¯āĩā´Ēāĩā´Ēāĩ‹ā´´āĩā´‚ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚ (ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ).", + "transcoding_two_pass_encoding": "ⴟāĩ-ā´Ēā´žā´¸āĩ ā´Žāĩģā´•āĩ‹ā´Ąā´ŋā´‚ā´—āĩ", + "transcoding_two_pass_encoding_setting_description": "ā´Žāĩ†ā´šāĩā´šā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´°ā´Ŗāĩā´Ÿāĩ ā´Ēā´žā´¸āĩā´•ā´ŗā´ŋā´˛ā´žā´¯ā´ŋ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•. ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž (H.264, HEVC ā´Žā´¨āĩā´¨ā´ŋā´ĩā´¯āĩâ€Œā´•āĩā´•āĩŠā´Ēāĩā´Ēā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•ā´žāĩģ ⴇⴤāĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ), ⴈ ā´Žāĩ‹ā´Ąāĩ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąā´ŋā´¨āĩ† ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´’ā´°āĩ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ ā´ļāĩā´°āĩ‡ā´Ŗā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•ā´¯āĩā´‚ CRF ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. VP9-ā´¨ā´žā´¯ā´ŋ, ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ŧā´ŋā´ąāĩā´ąāĩā´ąāĩ‡ā´ąāĩā´ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ CRF ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žā´‚.", + "transcoding_video_codec": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´•āĩ‹ā´Ąāĩ†ā´•āĩ", + "transcoding_video_codec_description": "VP9-ā´¨āĩ ⴉⴝāĩŧā´¨āĩā´¨ ā´•ā´žā´°āĩā´¯ā´•āĩā´ˇā´Žā´¤ā´¯āĩā´‚ ā´ĩāĩ†ā´Ŧāĩ ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´¤ā´¯āĩā´Žāĩā´Ŗāĩā´Ÿāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´¸ā´Žā´¯ā´Žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´‚. HEVC ā´¸ā´Žā´žā´¨ā´Žā´žā´¯ā´ŋ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩāĩ†ā´Ŧāĩ ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´¤ ā´•āĩā´ąā´ĩā´žā´Ŗāĩ. H.264 ā´ĩāĩā´¯ā´žā´Ēā´•ā´Žā´žā´¯ā´ŋ ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´ĩāĩā´‚ ā´ĩāĩ‡ā´—ā´¤āĩā´¤ā´ŋāĩŊ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´¨āĩā´¨ā´¤āĩā´Žā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´ĩā´ŗā´°āĩ† ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. AV1 ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´•ā´žā´°āĩā´¯ā´•āĩā´ˇā´Žā´Žā´žā´¯ ā´•āĩ‹ā´Ąāĩ†ā´•āĩā´•ā´žā´Ŗāĩ, ā´Ēā´•āĩā´ˇāĩ‡ ā´Ēā´´ā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ⴇⴤā´ŋā´¨āĩ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯ā´ŋā´˛āĩā´˛.", + "trash_enabled_description": "ⴟāĩā´°ā´žā´ˇāĩ ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "trash_number_of_days": "ā´Ļā´ŋā´ĩⴏⴙāĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "trash_number_of_days_description": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´Žāĩā´Žāĩā´Ēāĩ ⴟāĩā´°ā´žā´ˇā´ŋāĩŊ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´Ļā´ŋā´ĩⴏⴙāĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "trash_settings": "ⴟāĩā´°ā´žā´ˇāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "trash_settings_description": "ⴟāĩā´°ā´žā´ˇāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unlink_all_oauth_accounts": "ā´Žā´˛āĩā´˛ā´ž OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´•ā´ŗāĩā´‚ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unlink_all_oauth_accounts_description": "ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ļā´žā´¤ā´žā´ĩā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Žāĩā´Žāĩā´Ēāĩ ā´Žā´˛āĩā´˛ā´ž OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´•ā´ŗāĩā´‚ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´“āĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•.", + "unlink_all_oauth_accounts_prompt": "ā´Žā´˛āĩā´˛ā´ž OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´•ā´ŗāĩā´‚ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ā´“ā´°āĩ‹ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ†ā´¯āĩā´‚ OAuth ā´ā´Ąā´ŋ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´‚, ⴇⴤāĩ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "user_cleanup_job": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´•āĩā´˛āĩ€ā´¨ā´Ēāĩā´Ēāĩ", + "user_delete_delay": "{user}-ā´¨āĩā´ąāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´‚ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ {delay, plural, one {# ā´Ļā´ŋā´ĩⴏⴤāĩā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ} other {# ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩžā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ}} ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´ˇāĩ†ā´Ąāĩā´¯āĩ‚āĩž ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "user_delete_delay_settings": "ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žā´¨āĩā´ŗāĩā´ŗ ā´•ā´žā´˛ā´¤ā´žā´Žā´¸ā´‚", + "user_delete_delay_settings_description": "ā´’ā´°āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´‚ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤ā´¤ā´ŋā´¨āĩ ā´ļāĩ‡ā´ˇā´Žāĩā´ŗāĩā´ŗ ā´Ļā´ŋā´ĩⴏⴙāĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚. ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ⴤⴝāĩā´¯ā´žā´ąā´žā´¯ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•ā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žā´¨āĩā´ŗāĩā´ŗ ⴜāĩ‹ā´˛ā´ŋ ā´…āĩŧā´Ļāĩā´§ā´°ā´žā´¤āĩā´°ā´ŋā´¯ā´ŋāĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ⴈ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋā´˛āĩ† ā´Žā´žā´ąāĩā´ąā´™āĩā´™āĩž ā´…ā´Ÿāĩā´¤āĩā´¤ ā´Žā´•āĩā´¸ā´ŋā´•āĩā´¯āĩ‚ⴎⴍā´ŋāĩŊ ā´ĩā´ŋⴞⴝā´ŋā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚.", + "user_delete_immediately": "{user}-ā´¨āĩā´ąāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩā´‚ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´‰ā´Ÿā´¨ā´Ÿā´ŋ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´•āĩā´¯āĩ‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "user_delete_immediately_checkbox": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ†ā´¯āĩā´‚ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´‰ā´Ÿā´¨ā´Ÿā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•āĩā´¯āĩ‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "user_details": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž", + "user_management": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´Žā´žā´¨āĩ‡ā´œāĩā´Žāĩ†ā´¨āĩā´ąāĩ", + "user_password_has_been_reset": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ:", + "user_password_reset_description": "ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´• ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•, ā´…ā´Ÿāĩā´¤āĩā´¤ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩ‡ā´Ŗāĩā´Ÿā´ŋā´ĩā´°āĩā´Žāĩ†ā´¨āĩā´¨āĩ ā´…ā´ĩā´°āĩ† ā´…ā´ąā´ŋā´¯ā´ŋā´•āĩā´•āĩā´•.", + "user_restore_description": "{user}-ā´¨āĩā´ąāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´‚.", + "user_restore_scheduled_removal": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´• - {date, date, long}-ā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´ˇāĩ†ā´Ąāĩā´¯āĩ‚āĩž ⴚāĩ†ā´¯āĩâ€Œā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "user_settings": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "user_settings_description": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "user_successfully_removed": "{email} ā´Žā´¨āĩā´¨ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¤āĩ.", + "version_check_enabled_description": "ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "version_check_implications": "ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ā´Ģāĩ€ā´šāĩā´šāĩŧ github.com-ā´Žā´žā´¯ā´ŋ ⴆⴍāĩā´•ā´žā´˛ā´ŋā´• ā´†ā´ļā´¯ā´ĩā´ŋā´¨ā´ŋā´Žā´¯ā´¤āĩā´¤āĩ† ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "version_check_settings": "ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨", + "version_check_settings_description": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•/ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•", + "video_conversion_job": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "video_conversion_job_description": "ā´Ŧāĩā´°āĩ—ā´¸ā´ąāĩā´•ā´ŗāĩā´Žā´žā´¯āĩā´‚ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗāĩā´Žā´žā´¯āĩā´‚ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´¤ā´¯āĩā´•āĩā´•ā´žā´¯ā´ŋ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•" + }, + "admin_email": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ", + "admin_password": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "administration": "ā´…ā´Ąāĩā´Žā´ŋā´¨ā´ŋā´¸āĩā´Ÿāĩā´°āĩ‡ā´ˇāĩģ", + "advanced": "ā´ĩā´ŋā´Ēāĩā´˛ā´Žā´žā´¯ā´¤āĩ", + "advanced_settings_enable_alternate_media_filter_subtitle": "ⴇⴤⴰ ā´Žā´žā´¨ā´Ļā´Ŗāĩā´Ąā´™āĩā´™ā´ŗāĩ† ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´¸ā´ŋā´™āĩā´•āĩ ā´¸ā´Žā´¯ā´¤āĩā´¤āĩ ā´Žāĩ€ā´Ąā´ŋā´¯ ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ⴈ ā´“ā´Ēāĩā´ˇāĩģ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•. ā´Žā´˛āĩā´˛ā´ž ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩ ā´Ēāĩā´°ā´ļāĩā´¨ā´™āĩā´™ā´ŗāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´‚ ⴇⴤāĩ ā´Ēā´°āĩ€ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•.", + "advanced_settings_enable_alternate_media_filter_title": "[ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋāĩŊ] ⴇⴤⴰ ā´‰ā´Ēā´•ā´°ā´Ŗ ā´†āĩŊā´Ŧā´‚ ā´¸ā´ŋā´™āĩā´•āĩ ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "advanced_settings_log_level_title": "ā´˛āĩ‹ā´—āĩ ā´˛āĩ†ā´ĩāĩŊ: {level}", + "advanced_settings_prefer_remote_subtitle": "ⴚā´ŋā´˛ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´ĩā´ŗā´°āĩ† ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´ĩā´žā´Ŗāĩ. ā´Ēā´•ā´°ā´‚ ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ⴈ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ ⴏⴜāĩ€ā´ĩā´Žā´žā´•āĩā´•āĩā´•.", + "advanced_settings_prefer_remote_title": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•", + "advanced_settings_proxy_headers_subtitle": "ā´“ā´°āĩ‹ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴝāĩâ€Œā´•āĩā´•āĩŠā´Ēāĩā´Ēā´ĩāĩā´‚ Immich ⴅⴝⴝāĩâ€Œā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´Ēāĩā´°āĩ‹ā´•āĩā´¸ā´ŋ ā´šāĩ†ā´Ąā´ąāĩā´•āĩž ā´¨ā´ŋāĩŧā´ĩⴚā´ŋā´•āĩā´•āĩā´•", + "advanced_settings_proxy_headers_title": "ā´Ēāĩā´°āĩ‹ā´•āĩā´¸ā´ŋ ā´šāĩ†ā´Ąā´ąāĩā´•āĩž", + "advanced_settings_readonly_mode_subtitle": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Žā´žā´¤āĩā´°ā´‚ ā´•ā´žā´Ŗā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´¨āĩā´¨ ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Žāĩ‹ā´Ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´’ā´¨āĩā´¨ā´ŋⴞⴧā´ŋā´•ā´‚ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩŊ, ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ, ā´•ā´žā´¸āĩā´ąāĩā´ąā´ŋā´‚ā´—āĩ, ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩŊ ā´Žā´¨āĩā´¨ā´ŋā´ĩā´¯āĩ†ā´˛āĩā´˛ā´žā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´‚. ā´Ēāĩā´°ā´§ā´žā´¨ ā´¸āĩā´•āĩā´°āĩ€ā´¨ā´ŋā´˛āĩ† ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´…ā´ĩā´¤ā´žāĩŧ ā´ĩā´´ā´ŋ ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•/ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•.", + "advanced_settings_readonly_mode_title": "ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Žāĩ‹ā´Ąāĩ", + "advanced_settings_self_signed_ssl_subtitle": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Žāĩģā´Ąāĩâ€Œā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąā´ŋā´¨ā´žā´¯āĩā´ŗāĩā´ŗ SSL ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´¸āĩā´ĩⴝⴂ ā´’ā´Ēāĩā´Ēā´ŋⴟāĩā´Ÿ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•āĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ.", + "advanced_settings_self_signed_ssl_title": "ā´¸āĩā´ĩⴝⴂ ā´’ā´Ēāĩā´Ēā´ŋⴟāĩā´Ÿ SSL ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩā´•āĩž ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "advanced_settings_sync_remote_deletions_subtitle": "ā´ĩāĩ†ā´Ŧā´ŋāĩŊ ā´† ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ⴍⴟⴤāĩā´¤āĩā´Žāĩā´Ēāĩ‹āĩž ⴈ ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋā´˛āĩ† ā´’ā´°āĩ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•ā´¯āĩ‹ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•ā´¯āĩ‹ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "advanced_settings_sync_remote_deletions_title": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´˛āĩā´•āĩž ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´• [ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋāĩŊ]", + "advanced_settings_tile_subtitle": "ā´ĩā´ŋā´Ēāĩā´˛ā´Žā´žā´¯ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "advanced_settings_troubleshooting_subtitle": "ⴟāĩā´°ā´Ŧā´ŋāĩžā´ˇāĩ‚ā´Ÿāĩā´Ÿā´ŋā´‚ā´—ā´ŋā´¨ā´žā´¯ā´ŋ ā´…ā´§ā´ŋā´• ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "advanced_settings_troubleshooting_title": "ⴟāĩā´°ā´Ŧā´ŋāĩžā´ˇāĩ‚ā´Ÿāĩā´Ÿā´ŋā´‚ā´—āĩ", + "age_months": "ā´Ēāĩā´°ā´žā´¯ā´‚ {months, plural, one {# ā´Žā´žā´¸ā´‚} other {# ā´Žā´žā´¸ā´‚}}", + "age_year_months": "ā´Ēāĩā´°ā´žā´¯ā´‚ 1 ā´ĩāĩŧⴎⴂ, {months, plural, one {# ā´Žā´žā´¸ā´‚} other {# ā´Žā´žā´¸ā´‚}}", + "age_years": "{years, plural, other {ā´Ēāĩā´°ā´žā´¯ā´‚ #}}", + "album_added": "ā´†āĩŊā´Ŧā´‚ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "album_added_notification_setting_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ† ā´’ā´°āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´’ā´°āĩ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´¸āĩā´ĩāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "album_cover_updated": "ā´†āĩŊā´Ŧā´‚ ā´•ā´ĩāĩŧ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "album_delete_confirmation": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ {album} ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "album_delete_confirmation_description": "ⴈ ā´†āĩŊā´Ŧā´‚ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤ā´žā´Ŗāĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´Žā´ąāĩā´ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•āĩžā´•āĩā´•āĩ ⴇⴍā´ŋ ⴇⴤāĩ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "album_deleted": "ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "album_info_card_backup_album_excluded": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´ŋ", + "album_info_card_backup_album_included": "ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ŋ", + "album_info_updated": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "album_leave": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋⴟāĩā´•ā´¯ā´žā´Ŗāĩ‹?", + "album_leave_confirmation": "{album} ā´ĩā´ŋⴟāĩā´Ÿāĩā´Ēāĩ‹ā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "album_name": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ", + "album_options": "ā´†āĩŊā´Ŧā´‚ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "album_remove_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗāĩ‹?", + "album_remove_user_confirmation": "{user}-ā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "album_search_not_found": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¤ā´ŋⴰⴝⴞāĩā´Žā´žā´¯ā´ŋ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "album_share_no_users": "ā´¨ā´ŋā´™āĩā´™āĩž ⴈ ā´†āĩŊā´Ŧā´‚ ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩā´Žā´žā´¯āĩā´‚ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗā´žā´°āĩā´‚ ⴇⴞāĩā´˛āĩ†ā´¨āĩā´¨āĩ ā´¤āĩ‹ā´¨āĩā´¨āĩā´¨āĩā´¨āĩ.", + "album_summary": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ⴏⴂⴗāĩā´°ā´šā´‚", + "album_updated": "ā´†āĩŊā´Ŧā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "album_updated_setting_description": "ā´’ā´°āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´’ā´°āĩ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´¸āĩā´ĩāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "album_user_left": "{album} ā´ĩā´ŋⴟāĩā´Ÿāĩā´Ēāĩ‹ā´¯ā´ŋ", + "album_user_removed": "{user}-ā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "album_viewer_appbar_delete_confirm": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴈ ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "album_viewer_appbar_share_err_delete": "ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "album_viewer_appbar_share_err_leave": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋⴟāĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "album_viewer_appbar_share_err_remove": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēāĩā´°ā´ļāĩā´¨ā´™āĩā´™ā´ŗāĩā´Ŗāĩā´Ÿāĩ", + "album_viewer_appbar_share_err_title": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´ļāĩ€āĩŧⴎⴕⴂ ā´Žā´žā´ąāĩā´ąāĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "album_viewer_appbar_share_leave": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋⴟāĩā´•", + "album_viewer_appbar_share_to": "ⴇⴤā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "album_viewer_page_share_add_users": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "album_with_link_access": "ā´˛ā´ŋā´™āĩā´•āĩā´ŗāĩā´ŗ ā´†āĩŧā´•āĩā´•āĩā´‚ ⴈ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ⴆⴺāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´•ā´žā´Ŗā´žāĩģ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•.", + "albums": "ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "albums_count": "{count, plural, one {{count, number} ā´†āĩŊā´Ŧā´‚} other {{count, number} ā´†āĩŊā´Ŧā´™āĩā´™āĩž}}", + "albums_default_sort_order": "ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿāĩ ā´†āĩŊā´Ŧā´‚ ā´¸āĩ‹āĩŧⴟāĩā´Ÿāĩ ā´“āĩŧā´Ąāĩŧ", + "albums_default_sort_order_description": "ā´Ēāĩā´¤ā´ŋā´¯ ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´žā´°ā´‚ā´­ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¸āĩ‹āĩŧⴟāĩā´Ÿāĩ ā´“āĩŧā´Ąāĩŧ.", + "albums_feature_description": "ā´Žā´ąāĩā´ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩā´Žā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´¨āĩā´¨ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´ļāĩ‡ā´–ā´°ā´‚.", + "albums_on_device_count": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋā´˛āĩ† ā´†āĩŊā´Ŧā´™āĩā´™āĩž ({count})", + "all": "ā´Žā´˛āĩā´˛ā´žā´‚", + "all_albums": "ā´Žā´˛āĩā´˛ā´ž ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩā´‚", + "all_people": "ā´Žā´˛āĩā´˛ā´ž ⴆⴺāĩā´•ā´ŗāĩā´‚", + "all_videos": "ā´Žā´˛āĩā´˛ā´ž ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "allow_dark_mode": "ā´Ąā´žāĩŧā´•āĩā´•āĩ ā´Žāĩ‹ā´Ąāĩ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "allow_edits": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´•āĩž ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "allow_public_user_to_download": "ā´ĒāĩŠā´¤āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "allow_public_user_to_upload": "ā´ĒāĩŠā´¤āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "alt_text_qr_code": "QR ā´•āĩ‹ā´Ąāĩ ⴚā´ŋā´¤āĩā´°ā´‚", + "anti_clockwise": "ā´…ā´Ēāĩā´°ā´Ļā´•āĩā´ˇā´ŋā´Ŗā´Žā´žā´¯ā´ŋ", + "api_key": "API ā´•āĩ€", + "api_key_description": "ⴈ ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´’ā´°āĩ ā´¤ā´ĩā´Ŗ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩ‚. ā´ĩā´ŋāĩģā´Ąāĩ‹ ā´…ā´Ÿā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Žāĩā´Žāĩā´Ēāĩ ⴇⴤāĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•.", + "api_key_empty": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† API ā´•āĩ€ā´¯āĩā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•ā´°āĩā´¤āĩ", + "api_keys": "API ā´•āĩ€ā´•āĩž", + "app_bar_signout_dialog_content": "ā´¸āĩˆāĩģ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "app_bar_signout_dialog_ok": "ā´…ā´¤āĩ†", + "app_bar_signout_dialog_title": "ā´¸āĩˆāĩģ ā´”ā´Ÿāĩā´Ÿāĩ", + "app_settings": "ā´†ā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "appears_in": "ⴇⴤā´ŋāĩŊ ā´•ā´žā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨āĩ", + "apply_count": "ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´• ({count, number})", + "archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ", + "archive_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "archive_or_unarchive_photo": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´• ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´…āĩēā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "archive_page_no_archived_assets": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "archive_page_title": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ({count})", + "archive_size": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ā´ĩā´˛āĩā´Ēāĩā´Ēā´‚", + "archive_size_description": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ā´ĩā´˛āĩā´Ēāĩā´Ēā´‚ ā´•āĩ‹āĩēā´Ģā´ŋā´—āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´• (GiB-ā´¯ā´ŋāĩŊ)", + "archived": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤āĩ", + "archived_count": "{count, plural, other {ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤ā´ĩ #}}", + "are_these_the_same_person": "ā´‡ā´ĩāĩŧ ā´’ā´°āĩ‡ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯ā´žā´Ŗāĩ‹?", + "are_you_sure_to_do_this": "ⴇⴤāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "asset_action_delete_err_read_only": "ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´Žā´žā´¤āĩā´°ā´Žāĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "asset_action_share_err_offline": "ā´“ā´Ģāĩâ€Œā´˛āĩˆāĩģ ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "asset_added_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "asset_adding_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩā´•āĩŠā´Ŗāĩā´Ÿā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩâ€Ļ", + "asset_description_updated": "ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "asset_filename_is_offline": "{filename} ā´Žā´¨āĩā´¨ ā´…ā´¸ā´ąāĩā´ąāĩ ā´“ā´Ģāĩâ€Œā´˛āĩˆā´¨ā´žā´Ŗāĩ", + "asset_has_unassigned_faces": "ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´…ā´¸āĩˆāĩģ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ ā´Žāĩā´–ā´™āĩā´™ā´ŗāĩā´Ŗāĩā´Ÿāĩ", + "asset_hashing": "ā´šā´žā´ˇā´ŋā´‚ā´—āĩâ€Ļ", + "asset_list_group_by_sub_title": "ⴇⴤⴍāĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "asset_list_layout_settings_dynamic_layout_title": "ā´Ąāĩˆā´¨ā´žā´Žā´ŋā´•āĩ ā´˛āĩ‡ā´”ā´Ÿāĩā´Ÿāĩ", + "asset_list_layout_settings_group_automatically": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´‚", + "asset_list_layout_settings_group_by": "ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩ† ⴇⴤⴍāĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "asset_list_layout_settings_group_by_month_day": "ā´Žā´žā´¸ā´‚ + ā´Ļā´ŋā´ĩⴏⴂ", + "asset_list_layout_sub_title": "ā´˛āĩ‡ā´”ā´Ÿāĩā´Ÿāĩ", + "asset_list_settings_subtitle": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´—āĩā´°ā´ŋā´Ąāĩ ā´˛āĩ‡ā´”ā´Ÿāĩā´Ÿāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "asset_list_settings_title": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´—āĩā´°ā´ŋā´Ąāĩ", + "asset_offline": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´“ā´Ģāĩâ€Œā´˛āĩˆāĩģ", + "asset_offline_description": "ⴈ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩ‡ā´ŖāĩŊ ā´…ā´¸ā´ąāĩā´ąāĩ ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ąā´ŋā´¸āĩā´•ā´ŋāĩŊ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛. ā´¸ā´šā´žā´¯ā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† Immich ā´…ā´Ąāĩā´Žā´ŋā´¨ā´ŋā´¸āĩā´Ÿāĩā´°āĩ‡ā´ąāĩā´ąā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´•.", + "asset_restored_successfully": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ", + "asset_skipped": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´ŋ", + "asset_skipped_in_trash": "ⴟāĩā´°ā´žā´ˇā´ŋāĩŊ", + "asset_trashed": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "asset_troubleshoot": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´Ŧā´ŋāĩžā´ˇāĩ‚ā´Ÿāĩā´Ÿāĩ", + "asset_uploaded": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "asset_uploading": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩâ€Ļ", + "asset_viewer_settings_subtitle": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´—ā´žā´˛ā´ąā´ŋ ā´ĩāĩā´¯āĩ‚ā´ĩāĩŧ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "asset_viewer_settings_title": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´ĩāĩā´¯āĩ‚ā´ĩāĩŧ", + "assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "assets_added_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ}}", + "assets_added_to_album_count": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ {count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ}}", + "assets_added_to_albums_count": "{albumTotal, plural, one {# ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ} other {# ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ}} {assetTotal, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {ā´…ā´¸ā´ąāĩā´ąāĩ} other {ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "assets_cannot_be_added_to_albums": "{count, plural, one {ā´…ā´¸ā´ąāĩā´ąāĩ} other {ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´’ā´°āĩ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩā´‚ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "assets_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}}", + "assets_deleted_permanently": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "assets_deleted_permanently_from_server": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) Immich ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "assets_downloaded_failed": "{count, plural, one {# ā´Ģā´¯āĩŊ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ - {error} ā´Ģā´¯āĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ} other {# ā´Ģⴝⴞāĩā´•āĩž ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ - {error} ā´Ģⴝⴞāĩā´•āĩž ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ}}", + "assets_downloaded_successfully": "{count, plural, one {# ā´Ģā´¯āĩŊ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ} other {# ā´Ģⴝⴞāĩā´•āĩž ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ}}", + "assets_moved_to_trash_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ}}", + "assets_permanently_deleted_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ}}", + "assets_removed_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ}}", + "assets_removed_permanently_from_device": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "assets_restore_confirmation": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛! ā´“ā´Ģāĩâ€Œā´˛āĩˆāĩģ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ⴈ ā´°āĩ€ā´¤ā´ŋā´¯ā´ŋāĩŊ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛āĩ†ā´¨āĩā´¨ ā´•ā´žā´°āĩā´¯ā´‚ ā´ļāĩā´°ā´Ļāĩā´§ā´ŋā´•āĩā´•āĩā´•.", + "assets_restored_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ}}", + "assets_restored_successfully": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ", + "assets_trashed": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "assets_trashed_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤āĩ}}", + "assets_trashed_from_server": "{count} ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) Immich ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "assets_were_part_of_album_count": "{count, plural, one {ā´…ā´¸ā´ąāĩā´ąāĩ ⴇⴤā´ŋⴍⴕⴂ} other {ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴤā´ŋⴍⴕⴂ}} ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´­ā´žā´—ā´Žā´žā´¯ā´ŋā´°āĩā´¨āĩā´¨āĩ", + "assets_were_part_of_albums_count": "{count, plural, one {ā´…ā´¸ā´ąāĩā´ąāĩ ⴇⴤā´ŋⴍⴕⴂ} other {ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴤā´ŋⴍⴕⴂ}} ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´­ā´žā´—ā´Žā´žā´¯ā´ŋā´°āĩā´¨āĩā´¨āĩ", + "authorized_devices": "ā´…ā´‚ā´—āĩ€ā´•āĩƒā´¤ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "automatic_endpoint_switching_subtitle": "ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´¨ā´ŋā´ļāĩā´šā´ŋā´¤ ā´ĩāĩˆ-ā´Ģāĩˆ ā´ĩā´´ā´ŋ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´•ā´Ŗā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•, ā´Žā´ąāĩā´ąāĩ ā´¸āĩā´Ĩⴞⴙāĩā´™ā´ŗā´ŋāĩŊ ⴇⴤⴰ ā´•ā´Ŗā´•āĩā´ˇā´¨āĩā´•āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "automatic_endpoint_switching_title": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´• URL ā´¸āĩā´ĩā´ŋⴚāĩā´šā´ŋā´‚ā´—āĩ", + "autoplay_slideshow": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "back": "ā´¤ā´ŋā´°ā´ŋā´•āĩ†", + "back_close_deselect": "ā´Ēāĩā´ąā´•āĩ‹ā´Ÿāĩā´Ÿāĩ, ā´…ā´Ÿā´¯āĩā´•āĩā´•āĩā´•, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "background_backup_running_error": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Žā´žā´¨āĩā´ĩāĩŊ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴆⴰⴂⴭā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "background_location_permission": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ", + "background_location_permission_content": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ā´¤āĩā´¤ā´ŋāĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩā´•āĩž ā´Žā´žā´ąāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, Immich-ā´¨āĩ ā´Žā´Ēāĩā´Ēāĩ‹ā´´āĩā´‚ ā´•āĩƒā´¤āĩā´¯ā´Žā´žā´¯ ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴉ⴪āĩā´Ÿā´žā´¯ā´ŋā´°ā´ŋā´•āĩā´•ā´Ŗā´‚, ā´…ā´¤āĩā´ĩā´´ā´ŋ ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩ ā´ĩāĩˆ-ā´Ģāĩˆ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚", + "background_options": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "backup": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ", + "backup_album_selection_page_albums_device": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋā´˛āĩ† ā´†āĩŊā´Ŧā´™āĩā´™āĩž ({count})", + "backup_album_selection_page_albums_tap": "ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤ā´žāĩģ ā´Ÿā´žā´Ēāĩā´Ēāĩā´šāĩ†ā´¯āĩā´¯āĩā´•, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ ā´Ąā´Ŧā´ŋāĩž ā´Ÿā´žā´Ēāĩā´Ēāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "backup_album_selection_page_assets_scatter": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´’ā´¨āĩā´¨ā´ŋⴞⴧā´ŋā´•ā´‚ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛ā´žā´¯ā´ŋ ā´ĩāĩā´¯ā´žā´Ēā´ŋⴚāĩā´šāĩā´•ā´ŋā´Ÿā´•āĩā´•ā´žā´‚. ā´…ā´¤ā´ŋā´¨ā´žāĩŊ, ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēāĩā´°ā´•āĩā´°ā´ŋⴝⴝā´ŋāĩŊ ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•ā´¯āĩ‹ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´•ā´¯āĩ‹ ⴚāĩ†ā´¯āĩā´¯ā´žā´‚.", + "backup_album_selection_page_select_albums": "ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "backup_album_selection_page_selection_info": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩŊ ā´ĩā´ŋā´ĩā´°ā´‚", + "backup_album_selection_page_total_assets": "ⴆⴕāĩ† ā´¸ā´ĩā´ŋā´ļāĩ‡ā´ˇā´Žā´žā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "backup_albums_sync": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´†āĩŊā´Ŧā´‚ ā´¸ā´ŋāĩģā´•āĩā´°āĩŠā´Ŗāĩˆā´¸āĩ‡ā´ˇāĩģ", + "backup_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "backup_background_service_backup_failed_message": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩâ€Ļ", + "backup_background_service_connection_failed_message": "ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´•ā´Ŗā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩâ€Ļ", + "backup_background_service_current_upload_notification": "{filename} ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "backup_background_service_default_notification": "ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩâ€Ļ", + "backup_background_service_error_title": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēā´ŋā´ļā´•āĩ", + "backup_background_service_in_progress_notification": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩâ€Ļ", + "backup_background_service_upload_failure_notification": "{filename} ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "backup_controller_page_albums": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "backup_controller_page_background_app_refresh_disabled_content": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž > ā´ĒāĩŠā´¤āĩā´ĩā´žā´¯ā´¤āĩ > ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´†ā´Ēāĩā´Ēāĩ ā´ąā´ŋā´Ģāĩā´°ā´ˇāĩ ā´Žā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´†ā´Ēāĩā´Ēāĩ ā´ąā´ŋā´Ģāĩā´°ā´ˇāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•.", + "backup_controller_page_background_app_refresh_disabled_title": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´†ā´Ēāĩā´Ēāĩ ā´ąā´ŋā´Ģāĩā´°ā´ˇāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "backup_controller_page_background_app_refresh_enable_button_text": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "backup_controller_page_background_battery_info_link": "ā´Žā´™āĩā´™ā´¨āĩ†ā´¯āĩ†ā´¨āĩā´¨āĩ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "backup_controller_page_background_battery_info_message": "ā´Žā´ŋā´•ā´šāĩā´š ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´…ā´¨āĩā´­ā´ĩā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ, Immich-ā´¨āĩā´ąāĩ† ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´¨ā´ŋⴝⴍāĩā´¤āĩā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´ā´¤āĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ā´Ŧā´žā´ąāĩā´ąā´ąā´ŋ ā´’ā´Ēāĩā´ąāĩā´ąā´ŋā´Žāĩˆā´¸āĩ‡ā´ˇā´¨āĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•.\n\nⴇⴤāĩ ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤āĩ† ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨ā´žāĩŊ, ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´žā´¤ā´žā´ĩā´ŋā´¨āĩā´ŗāĩā´ŗ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´¯ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•.", + "backup_controller_page_background_battery_info_ok": "ā´ļā´°ā´ŋ", + "backup_controller_page_background_battery_info_title": "ā´Ŧā´žā´ąāĩā´ąā´ąā´ŋ ā´’ā´Ēāĩā´ąāĩā´ąā´ŋā´Žāĩˆā´¸āĩ‡ā´ˇā´¨āĩā´•āĩž", + "backup_controller_page_background_charging": "ā´šā´žāĩŧⴜāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´Žā´žā´¤āĩā´°ā´‚", + "backup_controller_page_background_configure_error": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´¸āĩ‡ā´ĩⴍⴂ ā´•āĩ‹āĩēā´Ģā´ŋā´—āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "backup_controller_page_background_delay": "ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´ĩāĩˆā´•ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•: {duration}", + "backup_controller_page_background_description": "ā´†ā´Ēāĩā´Ēāĩ ā´¤āĩā´ąā´•āĩā´•ā´žā´¤āĩ† ⴤⴍāĩā´¨āĩ† ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´¸āĩ‡ā´ĩⴍⴂ ā´“ā´Ŗā´žā´•āĩā´•āĩā´•", + "backup_controller_page_background_is_off": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´• ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ģā´žā´Ŗāĩ", + "backup_controller_page_background_is_on": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´• ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ŗā´žā´Ŗāĩ", + "backup_controller_page_background_turn_off": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´¸āĩ‡ā´ĩⴍⴂ ā´“ā´Ģā´žā´•āĩā´•āĩā´•", + "backup_controller_page_background_turn_on": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´¸āĩ‡ā´ĩⴍⴂ ā´“ā´Ŗā´žā´•āĩā´•āĩā´•", + "backup_controller_page_background_wifi": "ā´ĩāĩˆ-ā´Ģāĩˆā´¯ā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´‚", + "backup_controller_page_backup": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ", + "backup_controller_page_backup_selected": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ĩ: ", + "backup_controller_page_backup_sub": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "backup_controller_page_created": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´šā´¤āĩ: {date}", + "backup_controller_page_desc_backup": "ā´†ā´Ēāĩā´Ēāĩ ā´¤āĩā´ąā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ģāĩ‹āĩŧā´—āĩā´°āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ŗā´žā´•āĩā´•āĩā´•.", + "backup_controller_page_excluded": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´ŋā´¯ā´ĩ: ", + "backup_controller_page_failed": "ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ ({count})", + "backup_controller_page_filename": "ā´Ģⴝⴞā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ: {filename}[{size}]", + "backup_controller_page_id": "ā´ā´Ąā´ŋ: {id}", + "backup_controller_page_info": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž", + "backup_controller_page_none_selected": "ā´’ā´¨āĩā´¨āĩā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "backup_controller_page_remainder": "ā´ļāĩ‡ā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ", + "backup_controller_page_remainder_sub": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ĩā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¨āĩā´ŗāĩā´ŗ ā´ļāĩ‡ā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "backup_controller_page_server_storage": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ", + "backup_controller_page_start_backup": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴆⴰⴂⴭā´ŋā´•āĩā´•āĩā´•", + "backup_controller_page_status_off": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´• ā´Ģāĩ‹āĩŧā´—āĩā´°āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ģā´žā´Ŗāĩ", + "backup_controller_page_status_on": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´• ā´Ģāĩ‹āĩŧā´—āĩā´°āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ŗā´žā´Ŗāĩ", + "backup_controller_page_storage_format": "{total}-āĩŊ {used} ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ", + "backup_controller_page_to_backup": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿ ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "backup_controller_page_total_sub": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´ŗāĩā´ŗ ā´Žā´˛āĩā´˛ā´ž ā´¸ā´ĩā´ŋā´ļāĩ‡ā´ˇā´Žā´žā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "backup_controller_page_turn_off": "ā´Ģāĩ‹āĩŧā´—āĩā´°āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ģā´žā´•āĩā´•āĩā´•", + "backup_controller_page_turn_on": "ā´Ģāĩ‹āĩŧā´—āĩā´°āĩ—ā´Ŗāĩā´Ÿāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ŗā´žā´•āĩā´•āĩā´•", + "backup_controller_page_uploading_file_info": "ā´Ģā´¯āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "backup_err_only_album": "ā´’ā´°āĩ‡ā´¯āĩŠā´°āĩ ā´†āĩŊā´Ŧā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "backup_error_sync_failed": "ā´¸ā´ŋā´™āĩā´•āĩ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "backup_info_card_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "backup_manual_cancelled": "ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•ā´ŋ", + "backup_manual_in_progress": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴇⴤā´ŋⴍⴕⴂ ā´Ēāĩā´°āĩ‹ā´—ā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´•āĩā´ąā´šāĩā´šāĩ ā´¸ā´Žā´¯ā´¤āĩā´¤ā´ŋā´¨āĩ ā´ļāĩ‡ā´ˇā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•", + "backup_manual_success": "ā´ĩā´ŋā´œā´¯ā´‚", + "backup_manual_title": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´¨ā´ŋā´˛", + "backup_options": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "backup_options_page_title": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "backup_setting_subtitle": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛, ā´Žāĩāĩģā´¨ā´ŋā´° ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "backup_settings_subtitle": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "backward": "ā´Ēā´ŋā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ", + "biometric_auth_enabled": "ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ", + "biometric_locked_out": "ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ† ā´˛āĩ‹ā´•āĩā´•āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "biometric_no_options": "ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "biometric_not_available": "ⴈ ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗā´‚ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "birthdate_saved": "ⴜⴍⴍⴤāĩā´¤āĩ€ā´¯ā´¤ā´ŋ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "birthdate_set_description": "ā´’ā´°āĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Žā´Ÿāĩā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹ā´´āĩā´ŗāĩā´ŗ ⴈ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩā´Ÿāĩ† ā´Ēāĩā´°ā´žā´¯ā´‚ ā´•ā´Ŗā´•āĩā´•ā´žā´•āĩā´•ā´žāĩģ ⴜⴍⴍⴤāĩā´¤āĩ€ā´¯ā´¤ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ.", + "blurred_background": "ā´Žā´™āĩā´™ā´ŋā´¯ ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ā´‚", + "bugs_and_feature_requests": "ā´Ŧā´—āĩā´•ā´ŗāĩā´‚ ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴕⴺāĩā´‚", + "build": "ā´Ŧā´ŋāĩŊā´Ąāĩ", + "build_image": "ā´‡ā´Žāĩ‡ā´œāĩ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´•", + "bulk_delete_duplicates_confirmation": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ {count, plural, one {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´Ŧāĩžā´•āĩā´•ā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ā´“ā´°āĩ‹ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēā´ŋā´˛āĩ†ā´¯āĩā´‚ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´ĩā´˛ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤āĩā´•ā´¯āĩā´‚ ā´Žā´ąāĩā´ąāĩ ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚. ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛!", + "bulk_keep_duplicates_confirmation": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ {count, plural, one {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ā´’ā´¨āĩā´¨āĩā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žā´¤āĩ† ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´Ēā´°ā´ŋā´šā´°ā´ŋā´•āĩā´•āĩā´‚.", + "bulk_trash_duplicates_confirmation": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ {count, plural, one {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´Ŧāĩžā´•āĩā´•ā´žā´¯ā´ŋ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ā´“ā´°āĩ‹ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēā´ŋā´˛āĩ†ā´¯āĩā´‚ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´ĩā´˛ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤āĩā´•ā´¯āĩā´‚ ā´Žā´ąāĩā´ąāĩ ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "buy": "Immich ā´ĩā´žā´™āĩā´™āĩā´•", + "cache_settings_clear_cache_button": "ā´•ā´žā´ˇāĩ† ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "cache_settings_clear_cache_button_title": "ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´•ā´žā´ˇāĩ† ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ. ā´•ā´žā´ˇāĩ† ā´Ēāĩā´¨āĩŧā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩā´ĩā´°āĩ† ⴇⴤāĩ ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´•ā´Ÿā´¨ā´¤āĩā´¤āĩ† ā´¸ā´žā´°ā´Žā´žā´¯ā´ŋ ā´Ŧā´žā´§ā´ŋā´•āĩā´•āĩā´‚.", + "cache_settings_duplicated_assets_clear_button": "ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "cache_settings_duplicated_assets_subtitle": "ā´†ā´Ēāĩā´Ēāĩ ā´…ā´ĩā´—ā´Ŗā´ŋⴚāĩā´š ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "cache_settings_duplicated_assets_title": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ({count})", + "cache_settings_statistics_album": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž", + "cache_settings_statistics_full": "ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž", + "cache_settings_statistics_shared": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´‚ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž", + "cache_settings_statistics_thumbnail": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž", + "cache_settings_statistics_title": "ā´•ā´žā´ˇāĩ† ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚", + "cache_settings_subtitle": "Immich ā´ŽāĩŠā´ŦāĩˆāĩŊ ā´†ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ˇā´¨āĩā´ąāĩ† ā´•ā´žā´ˇā´ŋā´‚ā´—āĩ ā´¸āĩā´ĩā´­ā´žā´ĩā´‚ ā´¨ā´ŋⴝⴍāĩā´¤āĩā´°ā´ŋā´•āĩā´•āĩā´•", + "cache_settings_tile_subtitle": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´¸āĩā´ĩā´­ā´žā´ĩā´‚ ā´¨ā´ŋⴝⴍāĩā´¤āĩā´°ā´ŋā´•āĩā´•āĩā´•", + "cache_settings_tile_title": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ", + "cache_settings_title": "ā´•ā´žā´ˇā´ŋā´‚ā´—āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "camera": "ā´•āĩā´¯ā´žā´Žā´ą", + "camera_brand": "ā´•āĩā´¯ā´žā´Žā´ą ā´Ŧāĩā´°ā´žāĩģā´Ąāĩ", + "camera_model": "ā´•āĩā´¯ā´žā´Žā´ą ā´Žāĩ‹ā´ĄāĩŊ", + "cancel": "ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•āĩā´•", + "cancel_search": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•āĩā´•", + "canceled": "ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•ā´ŋ", + "canceling": "ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "cannot_merge_people": "ⴆⴺāĩā´•ā´ŗāĩ† ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cannot_undo_this_action": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛!", + "cannot_update_the_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cast": "ā´•ā´žā´¸āĩā´ąāĩā´ąāĩ", + "cast_description": "ⴞⴭāĩā´¯ā´Žā´žā´¯ ā´•ā´žā´¸āĩā´ąāĩā´ąāĩ ā´Ąāĩ†ā´¸āĩā´ąāĩā´ąā´ŋā´¨āĩ‡ā´ˇā´¨āĩā´•āĩž ā´•āĩ‹āĩēā´Ģā´ŋā´—āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "change_date": "ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_display_order": "ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´•āĩā´°ā´Žā´‚ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_expiration_time": "ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ ā´¸ā´Žā´¯ā´‚ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_name": "ā´Ēāĩ‡ā´°āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_name_successfully": "ā´Ēāĩ‡ā´°āĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Žā´žā´ąāĩā´ąā´ŋ", + "change_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_password_description": "ⴇⴤāĩ ā´¨ā´ŋā´™āĩā´™āĩž ā´†ā´Ļāĩā´¯ā´Žā´žā´¯ā´ŋ ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´¤āĩā´¤ā´ŋāĩŊ ā´¸āĩˆāĩģ ā´‡āĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩā´•āĩŠā´Ŗāĩā´Ÿāĩ‹ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´’ā´°āĩ ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩā´¨ ⴍⴟⴤāĩā´¤ā´ŋⴝⴤāĩā´•āĩŠā´Ŗāĩā´Ÿāĩ‹ ā´†ā´•ā´žā´‚. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¤ā´žā´´āĩ† ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•.", + "change_password_form_confirm_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "change_password_form_description": "ā´¨ā´Žā´¸āĩā´•ā´žā´°ā´‚ {name},\n\nⴇⴤāĩ ā´¨ā´ŋā´™āĩā´™āĩž ā´†ā´Ļāĩā´¯ā´Žā´žā´¯ā´ŋ ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´¤āĩā´¤ā´ŋāĩŊ ā´¸āĩˆāĩģ ā´‡āĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩā´•āĩŠā´Ŗāĩā´Ÿāĩ‹ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´’ā´°āĩ ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩā´¨ ⴍⴟⴤāĩā´¤ā´ŋⴝⴤāĩā´•āĩŠā´Ŗāĩā´Ÿāĩ‹ ā´†ā´•ā´žā´‚. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¤ā´žā´´āĩ† ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•.", + "change_password_form_new_password": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "change_password_form_password_mismatch": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩā´•āĩž ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "change_password_form_reenter_new_password": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•āĩā´•", + "change_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "change_your_password": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "changed_visibility_successfully": "ā´Ļāĩƒā´ļāĩā´¯ā´¤ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Žā´žā´ąāĩā´ąā´ŋ", + "charging": "ā´šā´žāĩŧⴜāĩā´œāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "charging_requirement_mobile_backup": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēā´ŋā´¨āĩ ā´‰ā´Ēā´•ā´°ā´Ŗā´‚ ā´šā´žāĩŧⴜāĩ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´¤āĩā´Ŗāĩā´Ÿāĩ", + "check_corrupt_asset_backup": "ā´•āĩ‡ā´Ÿā´žā´¯ ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩā´•āĩž ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "check_corrupt_asset_backup_button": "ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ⴍⴟⴤāĩā´¤āĩā´•", + "check_corrupt_asset_backup_description": "ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ ā´ļāĩ‡ā´ˇā´‚, ā´ĩāĩˆ-ā´Ģāĩˆā´¯ā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´‚ ⴈ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨ ⴍⴟⴤāĩā´¤āĩā´•. ⴍⴟā´Ēⴟā´ŋā´•āĩā´°ā´Žā´¤āĩā´¤ā´ŋā´¨āĩ ā´•āĩā´ąā´šāĩā´šāĩ ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩā´•āĩž ā´Žā´Ÿāĩā´¤āĩā´¤āĩ‡ā´•āĩā´•ā´žā´‚.", + "check_logs": "ā´˛āĩ‹ā´—āĩā´•āĩž ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "choose_matching_people_to_merge": "ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ ⴆⴺāĩā´•ā´ŗāĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "city": "ⴍⴗⴰⴂ", + "clear": "ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "clear_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "clear_all_recent_searches": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ⴍⴟⴤāĩā´¤ā´ŋā´¯ ā´Žā´˛āĩā´˛ā´ž ā´¤ā´ŋⴰⴝⴞāĩā´•ā´ŗāĩā´‚ ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "clear_file_cache": "ā´Ģā´¯āĩŊ ā´•ā´žā´ˇāĩ† ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "clear_message": "ⴏⴍāĩā´Ļāĩ‡ā´ļā´‚ ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "clear_value": "ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "client_cert_dialog_msg_confirm": "ā´ļā´°ā´ŋ", + "client_cert_enter_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•", + "client_cert_import": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "client_cert_import_success_msg": "ā´•āĩā´˛ā´¯ā´ŋā´¨āĩā´ąāĩ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩā´šāĩ†ā´¯āĩâ€Œā´¤āĩ", + "client_cert_invalid_msg": "ā´…ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´Ģā´¯āĩŊ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "client_cert_remove_msg": "ā´•āĩā´˛ā´¯ā´ŋā´¨āĩā´ąāĩ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩâ€Œā´¤āĩ", + "client_cert_subtitle": "PKCS12 (.p12, .pfx) ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ ā´Žā´žā´¤āĩā´°ā´‚ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Žāĩā´Žāĩā´Ēāĩ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ/ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯āĩŊ ⴞⴭāĩā´¯ā´Žā´žā´•āĩ‚", + "client_cert_title": "SSL ā´•āĩā´˛ā´¯ā´ŋā´¨āĩā´ąāĩ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ", + "clockwise": "ā´Ēāĩā´°ā´Ļā´•āĩā´ˇā´ŋā´Ŗā´Žā´žā´¯ā´ŋ", + "close": "ā´…ā´Ÿā´¯āĩā´•āĩā´•āĩā´•", + "collapse": "ⴚāĩā´°āĩā´•āĩā´•āĩā´•", + "collapse_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ⴚāĩā´°āĩā´•āĩā´•āĩā´•", + "color": "ā´¨ā´ŋā´ąā´‚", + "color_theme": "ā´•ā´ŗāĩŧ ā´¤āĩ€ā´‚", + "comment_deleted": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "comment_options": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´¤āĩā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "comments_and_likes": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´™āĩā´™ā´ŗāĩā´‚ ā´˛āĩˆā´•āĩā´•āĩā´•ā´ŗāĩā´‚", + "comments_are_disabled": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´™āĩā´™āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "common_create_new_album": "ā´Ēāĩā´¤ā´ŋā´¯ ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "common_server_error": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´•ā´Ŗā´•āĩā´ˇāĩģ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•, ā´¸āĩ†āĩŧā´ĩāĩŧ ⴞⴭāĩā´¯ā´Žā´žā´Ŗāĩ†ā´¨āĩā´¨āĩā´‚ ā´†ā´Ēāĩā´Ēāĩ/ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´Žā´žā´Ŗāĩ†ā´¨āĩā´¨āĩā´‚ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•.", + "completed": "ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ", + "confirm": "ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "confirm_admin_password": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "confirm_delete_face": "{name} ā´Žā´¨āĩā´¨ ā´Žāĩā´–ā´‚ ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "confirm_delete_shared_link": "ⴈ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "confirm_keep_this_delete_others": "ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ā´’ā´´ā´ŋā´•āĩ† ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•ā´ŋā´˛āĩ† ā´Žā´ąāĩā´ąāĩ†ā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚. ā´¤āĩā´Ÿā´°ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "confirm_new_pin_code": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "confirm_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¸āĩā´Ĩā´ŋā´°āĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "confirm_tag_face": "ⴈ ā´Žāĩā´–ā´‚ {name} ā´Žā´¨āĩā´¨āĩ ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗāĩ‹?", + "confirm_tag_face_unnamed": "ⴈ ā´Žāĩā´–ā´‚ ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗāĩ‹?", + "connected_device": "ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´š ā´‰ā´Ēā´•ā´°ā´Ŗā´‚", + "connected_to": "ⴇⴤāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ", + "contain": "ā´‰āĩžā´•āĩā´•āĩŠā´ŗāĩā´ŗāĩā´•", + "context": "ⴏⴍāĩā´Ļāĩŧā´­ā´‚", + "continue": "ā´¤āĩā´Ÿā´°āĩā´•", + "control_bottom_app_bar_create_new_album": "ā´Ēāĩā´¤ā´ŋā´¯ ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "control_bottom_app_bar_delete_from_immich": "Immich-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "control_bottom_app_bar_delete_from_local": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "control_bottom_app_bar_edit_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "control_bottom_app_bar_edit_time": "ā´¤āĩ€ā´¯ā´¤ā´ŋā´¯āĩā´‚ ā´¸ā´Žā´¯ā´ĩāĩā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "control_bottom_app_bar_share_link": "ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "control_bottom_app_bar_share_to": "ⴇⴤā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "control_bottom_app_bar_trash_from_immich": "ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "copied_image_to_clipboard": "ⴚā´ŋā´¤āĩā´°ā´‚ ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤ā´ŋ.", + "copied_to_clipboard": "ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤ā´ŋ!", + "copy_error": "ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "copy_file_path": "ā´Ģā´¯āĩŊ ā´Ēā´žā´¤āĩā´¤āĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "copy_image": "ⴚā´ŋā´¤āĩā´°ā´‚ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "copy_link": "ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "copy_link_to_clipboard": "ā´˛ā´ŋā´™āĩā´•āĩ ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "copy_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "copy_to_clipboard": "ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤āĩā´•", + "country": "ā´°ā´žā´œāĩā´¯ā´‚", + "cover": "ā´•ā´ĩāĩŧ", + "covers": "ā´•ā´ĩā´ąāĩā´•āĩž", + "create": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´•", + "create_album": "ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_album_page_untitled": "ā´Ēāĩ‡ā´°ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ā´¤āĩ", + "create_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_link": "ā´˛ā´ŋā´™āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_link_to_share": "ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´˛ā´ŋā´™āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_link_to_share_description": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹(ā´•āĩž) ā´•ā´žā´Ŗā´žāĩģ ā´˛ā´ŋā´™āĩā´•āĩā´ŗāĩā´ŗ ā´†āĩŧā´•āĩā´•āĩā´‚ ā´…ā´¨āĩā´ĩā´žā´Ļā´‚ ā´¨āĩŊā´•āĩā´•", + "create_new": "ā´Ēāĩā´¤ā´ŋⴝⴤāĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_new_person": "ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_new_person_hint": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ ā´¨āĩŊā´•āĩā´•", + "create_new_user": "ā´Ēāĩā´¤ā´ŋā´¯ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_shared_album_page_share_add_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "create_shared_album_page_share_select_photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "create_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_tag": "ā´Ÿā´žā´—āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "create_tag_description": "ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ÿā´žā´—āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•. ā´¨āĩ†ā´¸āĩā´ąāĩā´ąā´Ąāĩ ā´Ÿā´žā´—āĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ, ā´Ģāĩ‹āĩŧā´ĩāĩ‡ā´Ąāĩ ā´¸āĩā´˛ā´žā´ˇāĩā´•āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩ† ā´Ÿā´žā´—ā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ ā´Ēā´žā´¤ ā´¨āĩŊā´•āĩā´•.", + "create_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "created": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´šā´¤āĩ", + "created_at": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´šā´¤āĩ", + "creating_linked_albums": "ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´š ā´†āĩŊā´Ŧā´™āĩā´™āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ...", + "crop": "ā´•āĩā´°āĩ‹ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "curated_object_page_title": "ā´ĩā´¸āĩā´¤āĩā´•āĩā´•āĩž", + "current_device": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´‚", + "current_pin_code": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ", + "current_server_address": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´¸āĩ†āĩŧā´ĩāĩŧ ā´ĩā´ŋā´˛ā´žā´¸ā´‚", + "custom_locale": "ā´•ā´¸āĩā´ąāĩā´ąā´‚ ā´˛āĩŠā´•āĩā´•āĩ‡āĩŊ", + "custom_locale_description": "ā´­ā´žā´ˇā´¯āĩ†ā´¯āĩā´‚ ā´Ēāĩā´°ā´Ļāĩ‡ā´ļā´¤āĩā´¤āĩ†ā´¯āĩā´‚ ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´¤āĩ€ā´¯ā´¤ā´ŋā´•ā´ŗāĩā´‚ ā´…ā´•āĩā´•ā´™āĩā´™ā´ŗāĩā´‚ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "custom_url": "ā´•ā´¸āĩā´ąāĩā´ąā´‚ URL", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", + "dark": "ⴇⴰāĩā´Ŗāĩā´Ÿā´¤āĩ", + "dark_theme": "ā´Ąā´žāĩŧā´•āĩā´•āĩ ā´¤āĩ€ā´‚ ⴟāĩ‹ā´—ā´ŋāĩž ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "date_after": "ⴇⴤā´ŋā´¨āĩ ā´ļāĩ‡ā´ˇā´Žāĩā´ŗāĩā´ŗ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "date_and_time": "ā´¤āĩ€ā´¯ā´¤ā´ŋā´¯āĩā´‚ ā´¸ā´Žā´¯ā´ĩāĩā´‚", + "date_before": "ⴇⴤā´ŋā´¨āĩ ā´Žāĩā´Žāĩā´Ēāĩā´ŗāĩā´ŗ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "date_format": "E, LLL d, y â€ĸ h:mm a", + "date_of_birth_saved": "ⴜⴍⴍⴤāĩā´¤āĩ€ā´¯ā´¤ā´ŋ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "date_range": "ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ēā´°ā´ŋā´§ā´ŋ", + "day": "ā´Ļā´ŋā´ĩⴏⴂ", + "days": "ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩž", + "deduplicate_all": "ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´•", + "deduplication_criteria_1": "ⴚā´ŋā´¤āĩā´°ā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´ĩā´˛āĩā´Ēāĩā´Ēā´‚ (ā´Ŧāĩˆā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ)", + "deduplication_criteria_2": "EXIF ā´Ąā´žā´ąāĩā´ąā´¯āĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "deduplication_info": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ˇāĩģ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´ĩā´ŋā´ĩā´°ā´‚", + "deduplication_info_description": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´Žāĩāĩģā´•āĩ‚ā´Ÿāĩā´Ÿā´ŋ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´‚ ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´Ŧāĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩā´‚, ā´žā´™āĩā´™āĩž ā´‡ā´ĩ ā´Ēā´°ā´ŋā´—ā´Ŗā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ:", + "default_locale": "ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿāĩ ā´˛āĩŠā´•āĩā´•āĩ‡āĩŊ", + "default_locale_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ŧāĩā´°āĩ—ā´¸āĩŧ ā´˛āĩŠā´•āĩā´•āĩ‡ā´˛ā´ŋā´¨āĩ† ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋ ā´¤āĩ€ā´¯ā´¤ā´ŋā´•ā´ŗāĩā´‚ ā´…ā´•āĩā´•ā´™āĩā´™ā´ŗāĩā´‚ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "delete": "ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_action_confirmation_message": "ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩ† ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´ąāĩ† ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąāĩā´‚, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ⴇⴤāĩ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗāĩ‹ ā´Žā´¨āĩā´¨āĩ ⴚāĩ‹ā´Ļā´ŋā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚", + "delete_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "delete_album": "ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_api_key_prompt": "ⴈ API ā´•āĩ€ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "delete_dialog_alert": "ⴈ ⴇⴍⴙāĩā´™āĩž Immich-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "delete_dialog_alert_local": "ⴈ ⴇⴍⴙāĩā´™āĩž ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚, ā´Ēā´•āĩā´ˇāĩ‡ Immich ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ ā´¤āĩā´Ÿāĩŧā´¨āĩā´¨āĩā´‚ ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´‚", + "delete_dialog_alert_local_non_backed_up": "ⴚā´ŋā´˛ ⴇⴍⴙāĩā´™āĩž Immich-ā´˛āĩ‡ā´•āĩā´•āĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛, ā´…ā´ĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "delete_dialog_alert_remote": "ⴈ ⴇⴍⴙāĩā´™āĩž Immich ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "delete_dialog_ok_force": "ā´Žā´¨āĩā´¤ā´žā´¯ā´žā´˛āĩā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_dialog_title": "ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_duplicates_confirmation": "ⴈ ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "delete_face": "ā´Žāĩā´–ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_key": "ā´•āĩ€ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_link": "ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_local_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "delete_local_dialog_ok_backed_up_only": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ā´ĩ ā´Žā´žā´¤āĩā´°ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_local_dialog_ok_force": "ā´Žā´¨āĩā´¤ā´žā´¯ā´žā´˛āĩā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_others": "ā´Žā´ąāĩā´ąāĩā´ŗāĩā´ŗā´ĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_permanently": "ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_permanently_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "delete_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_shared_link_dialog_title": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_tag": "ā´Ÿā´žā´—āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "delete_tag_confirmation_prompt": "{tagName} ā´Žā´¨āĩā´¨ ā´Ÿā´žā´—āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "delete_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "deleted_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "deletes_missing_assets": "ā´Ąā´ŋā´¸āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´•ā´žā´Ŗā´žā´¤ā´žā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚", + "description_input_hint_text": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•...", + "description_input_submit_error": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ, ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ ā´˛āĩ‹ā´—āĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "deselect_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "details": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž", + "direction": "ā´Ļā´ŋā´ļ", + "disabled": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋⴤⴂ", + "disallow_edits": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´•āĩž ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•ā´°āĩā´¤āĩ", + "discord": "ā´Ąā´ŋā´¸āĩā´•āĩ‹āĩŧā´Ąāĩ", + "discover": "ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•", + "discovered_devices": "ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "dismiss_all_errors": "ā´Žā´˛āĩā´˛ā´ž ā´Ēā´ŋā´ļā´•āĩā´•ā´ŗāĩā´‚ ⴤⴺāĩā´ŗā´ŋā´•āĩā´•ā´ŗā´¯āĩā´•", + "dismiss_error": "ā´Ēā´ŋā´ļā´•āĩ ⴤⴺāĩā´ŗā´ŋā´•āĩā´•ā´ŗā´¯āĩā´•", + "display_options": "ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "display_order": "ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´•āĩā´°ā´Žā´‚", + "display_original_photos": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "display_original_photos_setting_description": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´…ā´¸ā´ąāĩā´ąāĩ ā´ĩāĩ†ā´Ŧāĩ-ā´…ā´¨āĩā´¯āĩ‹ā´œāĩā´¯ā´Žā´žā´•āĩā´Žāĩā´Ēāĩ‹āĩž, ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩžā´•āĩā´•āĩ ā´Ēā´•ā´°ā´‚ ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´¨āĩŊā´•āĩā´•. ⴇⴤāĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´ĩāĩ‡ā´—ā´¤ ā´•āĩā´ąā´¯āĩā´•āĩā´•ā´žāĩģ ā´‡ā´Ÿā´¯ā´žā´•āĩā´•āĩā´‚.", + "do_not_show_again": "ⴈ ⴏⴍāĩā´Ļāĩ‡ā´ļā´‚ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´•ā´žā´Ŗā´ŋā´•āĩā´•ā´°āĩā´¤āĩ", + "documentation": "ā´Ąāĩ‹ā´•āĩā´¯āĩā´Žāĩ†ā´¨āĩā´ąāĩ‡ā´ˇāĩģ", + "done": "ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ", + "download": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ", + "download_action_prompt": "{count} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "download_canceled": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´ąā´Ļāĩā´Ļā´žā´•āĩā´•ā´ŋ", + "download_complete": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ", + "download_enqueue": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´•āĩā´¯āĩ‚ā´ĩā´ŋāĩŊ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "download_error": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´Ēā´ŋā´ļā´•āĩ", + "download_failed": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "download_finished": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´•ā´´ā´ŋā´žāĩā´žāĩ", + "download_include_embedded_motion_videos": "ā´‰āĩžā´šāĩā´šāĩ‡āĩŧā´¤āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž", + "download_include_embedded_motion_videos_description": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗā´ŋāĩŊ ā´‰āĩžā´šāĩā´šāĩ‡āĩŧā´¤āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´’ā´°āĩ ā´Ēāĩā´°ā´¤āĩā´¯āĩ‡ā´• ā´Ģā´¯ā´˛ā´žā´¯ā´ŋ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "download_notfound": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "download_paused": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤ā´ŋ", + "download_settings": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "download_settings_description": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "download_started": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴆⴰⴂⴭā´ŋⴚāĩā´šāĩ", + "download_sucess": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´ĩā´ŋⴜⴝā´ŋⴚāĩā´šāĩ", + "download_sucess_android": "ā´Žāĩ€ā´Ąā´ŋā´¯ DCIM/Immich ā´Žā´¨āĩā´¨ā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "download_waiting_to_retry": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´žā´¤āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "downloading": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "downloading_asset_filename": "{filename} ā´Žā´¨āĩā´¨ ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "downloading_media": "ā´Žāĩ€ā´Ąā´ŋā´¯ ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "drop_files_to_upload": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ģⴝⴞāĩā´•āĩž ā´Žā´ĩā´ŋⴟāĩ†ā´¯āĩā´‚ ā´Ąāĩā´°āĩ‹ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "duplicates": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž", + "duplicates_description": "ā´ā´¤ā´žā´Ŗāĩ ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´Žā´¨āĩā´¨āĩ ā´¸āĩ‚ā´šā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ ā´“ā´°āĩ‹ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩā´‚ ā´Ēā´°ā´ŋā´šā´°ā´ŋā´•āĩā´•āĩā´•", + "duration": "ā´Ļāĩˆāĩŧⴘāĩā´¯ā´‚", + "edit": "ā´¤ā´ŋā´°āĩā´¤āĩā´¤āĩā´•", + "edit_album": "ā´†āĩŊā´Ŧā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_avatar": "ā´…ā´ĩā´¤ā´žāĩŧ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_birthday": "ⴜⴍāĩā´Žā´Ļā´ŋⴍⴂ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_date": "ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_date_and_time": "ā´¤āĩ€ā´¯ā´¤ā´ŋā´¯āĩā´‚ ā´¸ā´Žā´¯ā´ĩāĩā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_date_and_time_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´¤āĩ€ā´¯ā´¤ā´ŋā´¯āĩā´‚ ā´¸ā´Žā´¯ā´ĩāĩā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¤āĩ", + "edit_date_and_time_by_offset": "ā´“ā´Ģāĩā´¸āĩ†ā´ąāĩā´ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Žā´žā´ąāĩā´ąāĩā´•", + "edit_date_and_time_by_offset_interval": "ā´Ēāĩā´¤ā´ŋā´¯ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ēā´°ā´ŋā´§ā´ŋ: {from}-{to}", + "edit_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_description_prompt": "ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•:", + "edit_exclusion_pattern": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_faces": "ā´Žāĩā´–ā´™āĩā´™āĩž ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_import_paths": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩā´•āĩž ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_key": "ā´•āĩ€ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_link": "ā´˛ā´ŋā´™āĩā´•āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_location_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¤āĩ", + "edit_location_dialog_title": "ā´¸āĩā´Ĩā´žā´¨ā´‚", + "edit_name": "ā´Ēāĩ‡ā´°āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_tag": "ā´Ÿā´žā´—āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_title": "ā´ļāĩ€āĩŧⴎⴕⴂ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edit_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "edited": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¤āĩ", + "editor": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩŧ", + "editor_close_without_save_prompt": "ā´Žā´žā´ąāĩā´ąā´™āĩā´™āĩž ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´ŋā´˛āĩā´˛", + "editor_close_without_save_title": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩŧ ā´…ā´Ÿā´¯āĩā´•āĩā´•ā´Ŗāĩ‹?", + "editor_crop_tool_h2_aspect_ratios": "ā´ĩāĩ€ā´•āĩā´ˇā´Ŗā´žā´¨āĩā´Ēā´žā´¤ā´‚", + "editor_crop_tool_h2_rotation": "ā´ąāĩŠā´Ÿāĩā´Ÿāĩ‡ā´ˇāĩģ", + "email": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ", + "email_notifications": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž", + "empty_folder": "ⴈ ā´Ģāĩ‹āĩžā´Ąāĩŧ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´Ŗāĩ", + "empty_trash": "ⴟāĩā´°ā´žā´ˇāĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•āĩā´•āĩā´•", + "empty_trash_confirmation": "ⴟāĩā´°ā´žā´ˇāĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ⴇⴤāĩ ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ† ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ Immich-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯āĩā´‚.\nā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛!", + "enable": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "enable_backup": "ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "enable_biometric_auth_description": "ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´Ŗā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´žāĩģ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´¨āĩŊā´•āĩā´•", + "enabled": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ", + "end_date": "ā´…ā´ĩā´¸ā´žā´¨ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "enqueued": "ā´•āĩā´¯āĩ‚ā´ĩā´ŋāĩŊ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "enter_wifi_name": "ā´ĩāĩˆ-ā´Ģāĩˆā´¯āĩā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ ā´¨āĩŊā´•āĩā´•", + "enter_your_pin_code": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´¨āĩŊā´•āĩā´•", + "enter_your_pin_code_subtitle": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąāĩŧ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´¨āĩŊā´•āĩā´•", + "error": "ā´Ēā´ŋā´ļā´•āĩ", + "error_change_sort_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´¸āĩ‹āĩŧⴟāĩā´Ÿāĩ ā´“āĩŧā´Ąāĩŧ ā´Žā´žā´ąāĩā´ąāĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "error_delete_face": "ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žāĩā´–ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_getting_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_loading_image": "ⴚā´ŋā´¤āĩā´°ā´‚ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_loading_partners": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´•ā´ŗāĩ† ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ: {error}", + "error_saving_image": "ā´Ēā´ŋā´ļā´•āĩ: {error}", + "error_tag_face_bounding_box": "ā´Žāĩā´–ā´‚ ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ - ā´Ŧāĩ—ā´Ŗāĩā´Ÿā´ŋā´‚ā´—āĩ ā´Ŧāĩ‹ā´•āĩā´¸āĩ ā´•āĩ‹āĩŧā´Ąā´ŋā´¨āĩ‡ā´ąāĩā´ąāĩā´•āĩž ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "error_title": "ā´Ēā´ŋā´ļā´•āĩ - ā´Žā´¨āĩā´¤āĩ‹ ā´•āĩā´´ā´Ēāĩā´Ēā´‚ ⴏⴂⴭā´ĩā´ŋⴚāĩā´šāĩ", + "errors": { + "cannot_navigate_next_asset": "ā´…ā´Ÿāĩā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cannot_navigate_previous_asset": "ā´Žāĩā´Žāĩā´Ēā´¤āĩā´¤āĩ† ā´…ā´¸ā´ąāĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_apply_changes": "ā´Žā´žā´ąāĩā´ąā´™āĩā´™āĩž ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_change_activity": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ {enabled, select, true {ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´žāĩģ} other {ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´žāĩģ}} ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_change_asset_favorite": "ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_change_metadata_assets_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ†} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ†}} ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_get_faces": "ā´Žāĩā´–ā´™āĩā´™āĩž ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "cant_get_number_of_comments": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚ ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "cant_search_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´¤ā´ŋā´°ā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "cant_search_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž ā´¤ā´ŋā´°ā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "error_adding_assets_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_adding_users_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_deleting_shared_user": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_downloading": "{filename} ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_hiding_buy_button": "ā´ĩā´žā´™āĩā´™ā´žā´¨āĩā´ŗāĩā´ŗ ā´Ŧⴟāĩā´Ÿāĩē ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "error_removing_assets_from_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ, ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ ā´•āĩēā´¸āĩ‹āĩž ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "error_selecting_all_assets": "ā´Žā´˛āĩā´˛ā´ž ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "exclusion_pattern_already_exists": "ⴈ ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ⴇⴤā´ŋⴍⴕⴂ ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´Ŗāĩā´Ÿāĩ.", + "failed_to_create_album": "ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_create_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_edit_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_get_people": "ⴆⴺāĩā´•ā´ŗāĩ† ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_keep_this_delete_others": "ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨ā´ŋⴞⴍā´ŋāĩŧā´¤āĩā´¤ā´ŋ ā´Žā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_notifications": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_remove_product_key": "ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_reset_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_stack_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_unstack_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´…āĩē-ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_update_notification_status": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´¨ā´ŋā´˛ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "import_path_already_exists": "ⴈ ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ⴇⴤā´ŋⴍⴕⴂ ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´Ŗāĩā´Ÿāĩ.", + "incorrect_email_or_password": "ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "paths_validation_failed": "{paths, plural, one {# ā´Ēā´žā´¤āĩā´¤āĩ} other {# ā´Ēā´žā´¤āĩā´¤āĩā´•āĩž}} ā´¸ā´žā´§āĩ‚ā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "profile_picture_transparent_pixels": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩ ā´¸āĩā´¤ā´žā´°āĩā´¯ā´Žā´žā´¯ ā´Ēā´ŋā´•āĩā´¸ā´˛āĩā´•āĩž ⴉ⴪āĩā´Ÿā´žā´•ā´°āĩā´¤āĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¸āĩ‚ā´‚ ā´‡āĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´• ā´•āĩ‚ā´Ÿā´žā´¤āĩ†/ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ⴚā´ŋā´¤āĩā´°ā´‚ ā´¨āĩ€ā´•āĩā´•āĩā´•.", + "quota_higher_than_disk_size": "ā´Ąā´ŋā´¸āĩā´•āĩ ā´ĩā´˛āĩā´Ēāĩā´Ēā´¤āĩā´¤āĩ‡ā´•āĩā´•ā´žāĩž ⴉⴝāĩŧā´¨āĩā´¨ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ ā´¨ā´ŋā´™āĩā´™āĩž ⴏⴜāĩā´œā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "something_went_wrong": "ā´Žā´¨āĩā´¤āĩ‹ ā´•āĩā´´ā´Ēāĩā´Ēā´‚ ⴏⴂⴭā´ĩā´ŋⴚāĩā´šāĩ", + "unable_to_add_album_users": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_assets_to_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_comment": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´‚ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_exclusion_pattern": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_partners": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´•ā´ŗāĩ† ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_remove_archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋāĩŊ {archived, select, true {ā´¨ā´ŋā´¨āĩā´¨āĩ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ} other {ā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´¸ā´ąāĩā´ąāĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ}} ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_add_remove_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ {favorite, select, true {ā´…ā´¸ā´ąāĩā´ąāĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ} other {ā´¨ā´ŋā´¨āĩā´¨āĩ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ}} ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_archive_unarchive": "{archived, select, true {ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ} other {ā´…āĩēā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ}} ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_album_user_role": "ā´†āĩŊā´Ŧā´‚ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´ąāĩ‹āĩž ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_date": "ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_favorite": "ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_change_visibility": "{count, plural, one {# ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩā´Ÿāĩ†} other {# ⴆⴺāĩā´•ā´ŗāĩā´Ÿāĩ†}} ā´Ļāĩƒā´ļāĩā´¯ā´¤ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_complete_oauth_login": "OAuth ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_connect": "ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_copy_to_clipboard": "ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´¨ā´ŋā´™āĩā´™āĩž https ā´ĩā´´ā´ŋā´¯ā´žā´Ŗāĩ ā´Ēāĩ‡ā´œāĩ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•", + "unable_to_create_admin_account": "ā´…ā´Ąāĩā´Žā´ŋāĩģ ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_create_api_key": "ā´Ēāĩā´¤ā´ŋā´¯ API ā´•āĩ€ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_create_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_create_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_album": "ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "unable_to_delete_exclusion_pattern": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_delete_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_download_files": "ā´Ģⴝⴞāĩā´•āĩž ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_edit_exclusion_pattern": "ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩŊ ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_edit_import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_empty_trash": "ⴟāĩā´°ā´žā´ˇāĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_enter_fullscreen": "ā´Ģāĩāĩžā´¸āĩā´•āĩā´°āĩ€ā´¨ā´ŋāĩŊ ā´Ēāĩā´°ā´ĩāĩ‡ā´ļā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_exit_fullscreen": "ā´Ģāĩāĩžā´¸āĩā´•āĩā´°āĩ€ā´¨ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ēāĩā´ąā´¤āĩā´¤āĩā´•ā´Ÿā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_get_comments_number": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚ ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "unable_to_get_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "unable_to_hide_person": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´Žā´ąā´¯āĩā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_link_motion_video": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_link_oauth_account": "OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_log_out_all_devices": "ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_log_out_device": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_login_with_oauth": "OAuth ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_play_video": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_reassign_assets_existing_person": "{name, select, null {ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ} other {{name}-ā´¨āĩ}} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_reassign_assets_new_person": "ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_refresh_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Ēāĩā´¤āĩā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_album_users": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_api_key": "API ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_assets_from_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_remove_reaction": "ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_reset_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_reset_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_resolve_duplicate": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´Ēā´°ā´ŋā´šā´°ā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_restore_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_restore_trash": "ⴟāĩā´°ā´žā´ˇāĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_restore_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_album": "ā´†āĩŊā´Ŧā´‚ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_api_key": "API ā´•āĩ€ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_date_of_birth": "ⴜⴍⴍⴤāĩā´¤āĩ€ā´¯ā´¤ā´ŋ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_name": "ā´Ēāĩ‡ā´°āĩ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_profile": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_save_settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_scan_libraries": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•āĩž ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_scan_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_set_feature_photo": "ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_set_profile_picture": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´‚ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_submit_job": "ⴜāĩ‹ā´˛ā´ŋ ā´¸ā´Žāĩŧā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_trash_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_unlink_account": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_unlink_motion_video": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_album_cover": "ā´†āĩŊā´Ŧā´‚ ā´•ā´ĩāĩŧ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_album_info": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_timeline_display_status": "ⴟāĩˆā´‚ā´˛āĩˆāĩģ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´¨ā´ŋā´˛ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_update_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_upload_file": "ā´Ģā´¯āĩŊ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛" + }, + "exif": "Exif", + "exif_bottom_sheet_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•...", + "exif_bottom_sheet_description_error": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "exif_bottom_sheet_details": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž", + "exif_bottom_sheet_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚", + "exif_bottom_sheet_people": "ⴆⴺāĩā´•āĩž", + "exif_bottom_sheet_person_add_person": "ā´Ēāĩ‡ā´°āĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "exit_slideshow": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹ā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ēāĩā´ąā´¤āĩā´¤āĩā´•ā´Ÿā´•āĩā´•āĩā´•", + "expand_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´ĩā´ŋā´•ā´¸ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "experimental_settings_new_asset_list_subtitle": "ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋā´¯ā´ŋā´˛ā´žā´Ŗāĩ", + "experimental_settings_new_asset_list_title": "ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´—āĩā´°ā´ŋā´Ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "experimental_settings_subtitle": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¸āĩā´ĩā´¨āĩā´¤ā´‚ ⴉⴤāĩā´¤ā´°ā´ĩā´žā´Ļā´ŋā´¤āĩā´ĩā´¤āĩā´¤ā´ŋāĩŊ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•!", + "experimental_settings_title": "ā´Ēā´°āĩ€ā´•āĩā´ˇā´Ŗā´žā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗā´¤āĩ", + "expire_after": "ⴇⴤā´ŋā´¨āĩ ā´ļāĩ‡ā´ˇā´‚ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "expired": "ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "expires_date": "{date}-ā´¨āĩ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "explore": "ā´Ēā´°āĩā´¯ā´ĩāĩ‡ā´•āĩā´ˇā´Ŗā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "explorer": "ā´Žā´•āĩā´¸āĩā´Ēāĩā´˛āĩ‹ā´ąāĩŧ", + "export": "ā´•ā´¯ā´ąāĩā´ąāĩā´Žā´¤ā´ŋ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "export_as_json": "JSON ⴆⴝā´ŋ ā´•ā´¯ā´ąāĩā´ąāĩā´Žā´¤ā´ŋ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "export_database": "ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´•ā´¯ā´ąāĩā´ąāĩā´Žā´¤ā´ŋ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "export_database_description": "SQLite ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´•ā´¯ā´ąāĩā´ąāĩā´Žā´¤ā´ŋ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "extension": "ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩģā´ˇāĩģ", + "external": "ā´Ŧā´žā´šāĩā´¯ā´‚", + "external_libraries": "ā´Ŧā´žā´šāĩā´¯ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•āĩž", + "external_network": "ā´Ŧā´žā´šāĩā´¯ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ", + "external_network_sheet_info": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´ĩāĩˆ-ā´Ģāĩˆ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋāĩŊ ā´…ā´˛āĩā´˛ā´žā´¤āĩā´¤ā´Ēāĩā´Ēāĩ‹āĩž, ā´¤ā´žā´´āĩ† ā´¨āĩŊā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´ŗāĩā´ŗ URL-ā´•ā´ŗā´ŋāĩŊ ā´†ā´Ļāĩā´¯ā´‚ ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´¨āĩā´¨ ā´’ā´¨āĩā´¨ā´ŋā´˛āĩ‚ā´Ÿāĩ†, ā´Žāĩā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¤ā´žā´´āĩ‡ā´•āĩā´•āĩā´ŗāĩā´ŗ ā´•āĩā´°ā´Žā´¤āĩā´¤ā´ŋāĩŊ, ā´†ā´Ēāĩā´Ēāĩ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´‚", + "face_unassigned": "ā´…ā´¸āĩˆāĩģ ⴚāĩ†ā´¯āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "failed": "ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_authenticate": "ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "failed_to_load_folder": "ā´Ģāĩ‹āĩžā´Ąāĩŧ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "favorite": "ā´Ēāĩā´°ā´ŋⴝⴂ", + "favorite_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "favorite_or_unfavorite_photo": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤ā´žā´•āĩā´•āĩā´• ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´žā´ąāĩā´ąāĩā´•", + "favorites": "ā´Ēāĩā´°ā´ŋⴝⴙāĩā´™āĩž", + "favorites_page_no_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "feature_photo_updated": "ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "features": "ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž", + "features_in_development": "ā´ĩā´ŋā´•ā´¸ā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩā´•āĩŠā´Ŗāĩā´Ÿā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž", + "features_setting_description": "ā´†ā´Ēāĩā´Ēāĩ ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "file_name": "ā´Ģⴝⴞā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ", + "file_name_or_extension": "ā´Ģⴝⴞā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩģā´ˇāĩģ", + "filename": "ā´Ģā´¯āĩŊā´¨ā´žā´Žā´‚", + "filetype": "ā´Ģā´¯āĩŊ ⴤⴰⴂ", + "filter": "ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "filter_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "filter_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "find_them_fast": "ā´¤ā´ŋⴰⴝⴞā´ŋā´˛āĩ‚ā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´…ā´ĩā´°āĩ† ā´ĩāĩ‡ā´—ā´¤āĩā´¤ā´ŋāĩŊ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤āĩā´•", + "first": "ā´†ā´Ļāĩā´¯ā´‚", + "fix_incorrect_match": "ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´‚ ā´ļā´°ā´ŋā´¯ā´žā´•āĩā´•āĩā´•", + "folder": "ā´Ģāĩ‹āĩžā´Ąāĩŧ", + "folder_not_found": "ā´Ģāĩ‹āĩžā´Ąāĩŧ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "folders": "ā´Ģāĩ‹āĩžā´Ąā´ąāĩā´•āĩž", + "folders_feature_description": "ā´Ģā´¯āĩŊ ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´¤āĩā´¤ā´ŋā´˛āĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩžā´•āĩā´•āĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ ā´Ģāĩ‹āĩžā´Ąāĩŧ ā´•ā´žā´´āĩâ€Œā´š ā´Ŧāĩā´°āĩ—ā´¸āĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "forgot_pin_code_question": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´ŋāĩģ ā´Žā´ąā´¨āĩā´¨āĩ‹?", + "forward": "ā´Žāĩā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ", + "gcast_enabled": "ā´—āĩ‚ā´—ā´ŋāĩž ā´•ā´žā´¸āĩā´ąāĩā´ąāĩ", + "gcast_enabled_description": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ⴈ ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´—āĩ‚ā´—ā´ŋā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ŧā´žā´šāĩā´¯ ā´‰ā´ąā´ĩā´ŋā´Ÿā´™āĩā´™āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ.", + "general": "ā´ĒāĩŠā´¤āĩā´ĩā´žā´¯ā´¤āĩ", + "geolocation_instruction_location": "ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ GPS ā´•āĩ‹āĩŧā´Ąā´ŋā´¨āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´•āĩā´˛ā´ŋā´•āĩā´•āĩā´šāĩ†ā´¯āĩā´¯āĩā´•, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´žā´Ēāĩā´Ēā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ‡ā´°ā´ŋⴟāĩā´Ÿāĩ ā´’ā´°āĩ ā´¸āĩā´Ĩⴞⴂ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "get_help": "ā´¸ā´šā´žā´¯ā´‚ ā´¨āĩ‡ā´Ÿāĩā´•", + "get_wifiname_error": "ā´ĩāĩˆ-ā´Ģāĩˆā´¯āĩā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ ⴞⴭā´ŋⴚāĩā´šā´ŋā´˛āĩā´˛. ā´¨ā´ŋā´™āĩā´™āĩž ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´¯ ā´…ā´¨āĩā´Žā´¤ā´ŋā´•āĩž ā´¨āĩŊā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´¨āĩā´¨āĩā´‚ ā´’ā´°āĩ ā´ĩāĩˆ-ā´Ģāĩˆ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´•ā´Ŗā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩâ€Œā´¤ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´¨āĩā´¨āĩā´‚ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•", + "getting_started": "ⴆⴰⴂⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "go_back": "ā´Ēā´ŋā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ ā´Ēāĩ‹ā´•āĩā´•", + "go_to_folder": "ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "go_to_search": "ā´¤ā´ŋⴰⴝⴞā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "gps": "ⴜā´ŋā´Ēā´ŋā´Žā´¸āĩ", + "gps_missing": "GPS ⴇⴞāĩā´˛", + "grant_permission": "ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´¨āĩŊā´•āĩā´•", + "group_albums_by": "ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩ† ⴇⴤⴍāĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•...", + "group_country": "ā´°ā´žā´œāĩā´¯ā´‚ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "group_no": "ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēā´ŋā´‚ā´—āĩ ⴇⴞāĩā´˛", + "group_owner": "ā´‰ā´Ÿā´Ž ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "group_places_by": "ā´¸āĩā´Ĩⴞⴙāĩā´™ā´ŗāĩ† ⴇⴤⴍāĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•...", + "group_year": "ā´ĩāĩŧⴎⴂ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "haptic_feedback_switch": "ā´šā´žā´Ēāĩā´ąāĩā´ąā´ŋā´•āĩ ā´Ģāĩ€ā´Ąāĩâ€Œā´Ŧā´žā´•āĩā´•āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "haptic_feedback_title": "ā´šā´žā´Ēāĩā´ąāĩā´ąā´ŋā´•āĩ ā´Ģāĩ€ā´Ąāĩâ€Œā´Ŧā´žā´•āĩā´•āĩ", + "has_quota": "ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿā´¯āĩā´Ŗāĩā´Ÿāĩ", + "hash_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´šā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "hashed_assets": "ā´šā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "hashing": "ā´šā´žā´ˇā´ŋā´‚ā´—āĩ", + "header_settings_add_header_tip": "ā´šāĩ†ā´Ąāĩŧ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "header_settings_field_validator_msg": "ā´Žāĩ‚ā´˛āĩā´¯ā´‚ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•ā´°āĩā´¤āĩ", + "header_settings_header_name_input": "ā´šāĩ†ā´Ąā´ąā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ", + "header_settings_header_value_input": "ā´šāĩ†ā´Ąā´ąā´ŋā´¨āĩā´ąāĩ† ā´Žāĩ‚ā´˛āĩā´¯ā´‚", + "headers_settings_tile_subtitle": "ā´“ā´°āĩ‹ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´…ā´­āĩā´¯āĩŧā´¤āĩā´Ĩⴍⴝāĩâ€Œā´•āĩā´•āĩŠā´Ēāĩā´Ēā´ĩāĩā´‚ ā´†ā´Ēāĩā´Ēāĩ ⴅⴝⴝāĩâ€Œā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿ ā´Ēāĩā´°āĩ‹ā´•āĩā´¸ā´ŋ ā´šāĩ†ā´Ąā´ąāĩā´•āĩž ā´¨ā´ŋāĩŧā´ĩⴚā´ŋā´•āĩā´•āĩā´•", + "headers_settings_tile_title": "ā´•ā´¸āĩā´ąāĩā´ąā´‚ ā´Ēāĩā´°āĩ‹ā´•āĩā´¸ā´ŋ ā´šāĩ†ā´Ąā´ąāĩā´•āĩž", + "hi_user": "ā´¨ā´Žā´¸āĩā´•ā´žā´°ā´‚ {name} ({email})", + "hide_all_people": "ā´Žā´˛āĩā´˛ā´ž ⴆⴺāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "hide_gallery": "ā´—ā´žā´˛ā´ąā´ŋ ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "hide_named_person": "{name} ā´Žā´¨āĩā´¨ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "hide_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "hide_person": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "hide_unnamed_people": "ā´Ēāĩ‡ā´°ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ ⴆⴺāĩā´•ā´ŗāĩ† ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "home_page_add_to_album_conflicts": "{album} ā´Žā´¨āĩā´¨ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ {added} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ. {failed} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴤā´ŋⴍⴕⴂ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩā´Ŗāĩā´Ÿāĩ.", + "home_page_add_to_album_err_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_add_to_album_success": "{album} ā´Žā´¨āĩā´¨ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ {added} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ.", + "home_page_album_err_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´’ā´°āĩ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_archive_err_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_archive_err_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_building_timeline": "ⴟāĩˆā´‚ā´˛āĩˆāĩģ ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_delete_err_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_delete_remote_err_local": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩŊ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´Ēāĩā´Ēā´ŋāĩŊ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴉ⴪āĩā´Ÿāĩ, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_favorite_err_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_favorite_err_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_first_time_notice": "ā´¨ā´ŋā´™āĩā´™āĩž ā´†ā´Ļāĩā´¯ā´Žā´žā´¯ā´ŋⴟāĩā´Ÿā´žā´Ŗāĩ ⴈ ā´†ā´Ēāĩā´Ēāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´’ā´°āĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´†āĩŊā´Ŧā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•āĩā´•, ā´…ā´¤āĩā´ĩā´´ā´ŋ ⴟāĩˆā´‚ā´˛āĩˆā´¨ā´ŋāĩŊ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚", + "home_page_locked_error_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_locked_error_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_share_err_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´˛ā´ŋā´™āĩā´•āĩ ā´ĩā´´ā´ŋ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "home_page_upload_err_limit": "ā´’ā´°āĩ ā´¸ā´Žā´¯ā´‚ ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ 30 ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩ‚, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "host": "ā´šāĩ‹ā´¸āĩā´ąāĩā´ąāĩ", + "hour": "ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚āĩŧ", + "hours": "ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąāĩā´•āĩž", + "id": "ā´ā´Ąā´ŋ", + "idle": "ā´¨ā´ŋā´ˇāĩâ€Œā´•āĩā´°ā´ŋⴝⴂ", + "ignore_icloud_photos": "iCloud ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´…ā´ĩā´—ā´Ŗā´ŋā´•āĩā´•āĩā´•", + "ignore_icloud_photos_description": "iCloud-āĩŊ ⴏⴂⴭⴰā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž Immich ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´ŋā´˛āĩā´˛", + "image": "ⴚā´ŋā´¤āĩā´°ā´‚", + "image_alt_text_date": "{date}-ā´¨āĩ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_1_person": "{date}-ā´¨āĩ {person1}-ā´¨āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_2_people": "{date}-ā´¨āĩ {person1}, {person2} ā´Žā´¨āĩā´¨ā´ŋā´ĩāĩŧā´•āĩā´•āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_3_people": "{date}-ā´¨āĩ {person1}, {person2}, {person3} ā´Žā´¨āĩā´¨ā´ŋā´ĩāĩŧā´•āĩā´•āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_4_or_more_people": "{date}-ā´¨āĩ {person1}, {person2}, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´Žā´ąāĩā´ąāĩ {additionalCount, number} ā´Ēāĩ‡āĩŧā´•āĩā´•āĩā´ŽāĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_place": "{date}-ā´¨āĩ {city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´ĩāĩ†ā´šāĩā´šāĩ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_place_1_person": "{date}-ā´¨āĩ {city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´ĩāĩ†ā´šāĩā´šāĩ {person1}-ā´¨āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_place_2_people": "{date}-ā´¨āĩ {city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´ĩāĩ†ā´šāĩā´šāĩ {person1}, {person2} ā´Žā´¨āĩā´¨ā´ŋā´ĩāĩŧā´•āĩā´•āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_place_3_people": "{date}-ā´¨āĩ {city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´ĩāĩ†ā´šāĩā´šāĩ {person1}, {person2}, {person3} ā´Žā´¨āĩā´¨ā´ŋā´ĩāĩŧā´•āĩā´•āĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_alt_text_date_place_4_or_more_people": "{date}-ā´¨āĩ {city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´ĩāĩ†ā´šāĩā´šāĩ {person1}, {person2}, ā´•āĩ‚ā´Ÿā´žā´¤āĩ† ā´Žā´ąāĩā´ąāĩ {additionalCount, number} ā´Ēāĩ‡āĩŧā´•āĩā´•āĩā´ŽāĩŠā´Ēāĩā´Ēā´‚ ā´Žā´Ÿāĩā´¤āĩā´¤ {isVideo, select, true {ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {ⴚā´ŋā´¤āĩā´°ā´‚}}", + "image_saved_successfully": "ⴚā´ŋā´¤āĩā´°ā´‚ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "image_viewer_page_state_provider_download_started": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ⴆⴰⴂⴭā´ŋⴚāĩā´šāĩ", + "image_viewer_page_state_provider_download_success": "ā´Ąāĩ—āĩēā´˛āĩ‹ā´Ąāĩ ā´ĩā´ŋⴜⴝā´ŋⴚāĩā´šāĩ", + "image_viewer_page_state_provider_share_error": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "immich_logo": "Immich ā´˛āĩ‹ā´—āĩ‹", + "immich_web_interface": "Immich ā´ĩāĩ†ā´Ŧāĩ ⴇⴍāĩā´ąāĩŧā´Ģāĩ‡ā´¸āĩ", + "import_from_json": "JSON-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "import_path": "ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ ā´Ēā´žā´¤āĩā´¤āĩ", + "in_albums": "{count, plural, one {# ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ} other {# ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋāĩŊ}}", + "in_archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋāĩŊ", + "include_archived": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤ā´ĩ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "include_shared_albums": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "include_shared_partner_assets": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Ÿāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "individual_share": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´—ā´¤ ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ", + "individual_shares": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´—ā´¤ ā´Ēā´™āĩā´•ā´ŋⴟⴞāĩā´•āĩž", + "info": "ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž", + "interval": { + "day_at_onepm": "ā´Žā´˛āĩā´˛ā´ž ā´Ļā´ŋā´ĩā´¸ā´ĩāĩā´‚ ā´‰ā´šāĩā´šā´¯āĩā´•āĩā´•āĩ 1 ā´Žā´Ŗā´ŋā´•āĩā´•āĩ", + "hours": "{hours, plural, one {ā´“ā´°āĩ‹ ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąā´ŋā´˛āĩā´‚} other {ā´“ā´°āĩ‹ {hours, number} ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąā´ŋā´˛āĩā´‚}}", + "night_at_midnight": "ā´Žā´˛āĩā´˛ā´ž ā´°ā´žā´¤āĩā´°ā´ŋā´¯āĩā´‚ ā´…āĩŧā´Ļāĩā´§ā´°ā´žā´¤āĩā´°ā´ŋā´•āĩā´•āĩ", + "night_at_twoam": "ā´Žā´˛āĩā´˛ā´ž ā´°ā´žā´¤āĩā´°ā´ŋā´¯āĩā´‚ ā´Ēāĩā´˛āĩŧⴚāĩā´šāĩ† 2 ā´Žā´Ŗā´ŋā´•āĩā´•āĩ" + }, + "invalid_date": "ā´…ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "invalid_date_format": "ā´…ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ", + "invite_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´•āĩā´ˇā´Ŗā´ŋā´•āĩā´•āĩā´•", + "invite_to_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´•āĩā´ˇā´Ŗā´ŋā´•āĩā´•āĩā´•", + "ios_debug_info_fetch_ran_at": "ā´Ģāĩ†ā´šāĩā´šāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋⴚāĩā´šā´¤āĩ {dateTime}", + "ios_debug_info_last_sync_at": "ā´…ā´ĩā´¸ā´žā´¨ ā´¸ā´ŋā´™āĩā´•āĩ {dateTime}", + "ios_debug_info_no_processes_queued": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•āĩā´¯āĩ‚ā´ĩā´ŋāĩŊ ⴇⴞāĩā´˛", + "ios_debug_info_no_sync_yet": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´¸ā´ŋā´™āĩā´•āĩ ⴜāĩ‹ā´˛ā´ŋ ⴇⴤāĩā´ĩā´°āĩ† ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋⴚāĩā´šā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "ios_debug_info_processes_queued": "{count, plural, one {{count} ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ā´•āĩā´¯āĩ‚ā´ĩā´ŋā´˛āĩā´Ŗāĩā´Ÿāĩ} other {{count} ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩā´•āĩž ā´•āĩā´¯āĩ‚ā´ĩā´ŋā´˛āĩā´Ŗāĩā´Ÿāĩ}}", + "ios_debug_info_processing_ran_at": "ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸ā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋⴚāĩā´šā´¤āĩ {dateTime}", + "items_count": "{count, plural, one {# ⴇⴍⴂ} other {# ⴇⴍⴙāĩā´™āĩž}}", + "jobs": "ⴜāĩ‹ā´˛ā´ŋā´•āĩž", + "keep": "ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•", + "keep_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•", + "keep_this_delete_others": "ⴇⴤāĩ ā´¸āĩ‚ā´•āĩā´ˇā´ŋⴚāĩā´šāĩ ā´Žā´ąāĩā´ąāĩā´ŗāĩā´ŗā´ĩ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "kept_this_deleted_others": "ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´•ā´¯āĩā´‚ {count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "keyboard_shortcuts": "ā´•āĩ€ā´Ŧāĩ‹āĩŧā´Ąāĩ ā´•āĩā´ąāĩā´•āĩā´•āĩā´ĩā´´ā´ŋā´•āĩž", + "language": "ā´­ā´žā´ˇ", + "language_no_results_subtitle": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¤ā´ŋā´°ā´¯āĩŊ ā´Ēā´Ļā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋⴚāĩā´šāĩ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•", + "language_no_results_title": "ā´­ā´žā´ˇā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "language_search_hint": "ā´­ā´žā´ˇā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "language_setting_description": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•ā´ŋā´ˇāĩā´Ÿā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ā´­ā´žā´ˇ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "large_files": "ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž", + "last": "ā´…ā´ĩā´¸ā´žā´¨ā´¤āĩā´¤āĩ‡ā´¤āĩ", + "last_seen": "ā´…ā´ĩā´¸ā´žā´¨ā´‚ ā´•ā´Ŗāĩā´Ÿā´¤āĩ", + "latest_version": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ", + "latitude": "ā´…ā´•āĩā´ˇā´žā´‚ā´ļā´‚", + "leave": "ā´ĩā´ŋⴟāĩā´•", + "leave_album": "ā´†āĩŊā´Ŧā´‚ ā´ĩā´ŋⴟāĩā´•", + "lens_model": "ā´˛āĩ†āĩģā´¸āĩ ā´Žāĩ‹ā´ĄāĩŊ", + "let_others_respond": "ā´Žā´ąāĩā´ąāĩā´ŗāĩā´ŗā´ĩā´°āĩ† ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´ŋā´•āĩā´•ā´žāĩģ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•", + "level": "ⴤⴞⴂ", + "library": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ", + "library_options": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "library_page_device_albums": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋā´˛āĩ† ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "library_page_new_album": "ā´Ēāĩā´¤ā´ŋā´¯ ā´†āĩŊā´Ŧā´‚", + "library_page_sort_asset_count": "ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "library_page_sort_created": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´š ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "library_page_sort_last_modified": "ā´…ā´ĩā´¸ā´žā´¨ā´‚ ā´Žā´žā´ąāĩā´ąā´‚ ā´ĩā´°āĩā´¤āĩā´¤ā´ŋⴝⴤāĩ", + "library_page_sort_title": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´ļāĩ€āĩŧⴎⴕⴂ", + "licenses": "ā´˛āĩˆā´¸āĩģā´¸āĩā´•āĩž", + "light": "ā´˛āĩˆā´ąāĩā´ąāĩ", + "like": "ⴇⴎāĩā´Ÿā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "like_deleted": "ⴇⴎāĩā´Ÿā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¤āĩ", + "link_motion_video": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "link_to_oauth": "OAuth-ā´˛āĩ‡ā´•āĩā´•āĩ ā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "linked_oauth_account": "ā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ", + "list": "ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ", + "loading": "ā´˛āĩ‹ā´Ąā´ŋā´‚ā´—āĩ", + "loading_search_results_failed": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´Ģⴞⴙāĩā´™āĩž ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´‚", + "local_asset_cast_failed": "ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ ā´’ā´°āĩ ā´…ā´¸ā´ąāĩā´ąāĩ ā´•ā´žā´¸āĩā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "local_assets": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "local_media_summary": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´Žāĩ€ā´Ąā´ŋā´¯ ⴏⴂⴗāĩā´°ā´šā´‚", + "local_network": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ", + "local_network_sheet_info": "ā´¨ā´ŋāĩŧā´Ļāĩā´Ļā´ŋā´ˇāĩâ€Œā´Ÿ ā´ĩāĩˆ-ā´Ģāĩˆ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ⴈ URL ā´ĩā´´ā´ŋ ā´†ā´Ēāĩā´Ēāĩ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´‚", + "location_permission": "ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ", + "location_permission_content": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•-ā´¸āĩā´ĩā´ŋⴚāĩā´šā´ŋā´‚ā´—āĩ ā´Ģāĩ€ā´šāĩā´šāĩŧ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, Immich-ā´¨āĩ ā´•āĩƒā´¤āĩā´¯ā´Žā´žā´¯ ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ, ā´…ā´¤āĩā´ĩā´´ā´ŋ ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´ĩāĩˆ-ā´Ģāĩˆ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´¨āĩā´ąāĩ† ā´Ēāĩ‡ā´°āĩ ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚", + "location_picker_choose_on_map": "ā´Žā´žā´Ēāĩā´Ēā´ŋāĩŊ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "location_picker_latitude_error": "ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´…ā´•āĩā´ˇā´žā´‚ā´ļā´‚ ā´¨āĩŊā´•āĩā´•", + "location_picker_latitude_hint": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´•āĩā´ˇā´žā´‚ā´ļā´‚ ā´‡ā´ĩā´ŋⴟāĩ† ā´¨āĩŊā´•āĩā´•", + "location_picker_longitude_error": "ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´°āĩ‡ā´–ā´žā´‚ā´ļā´‚ ā´¨āĩŊā´•āĩā´•", + "location_picker_longitude_hint": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´°āĩ‡ā´–ā´žā´‚ā´ļā´‚ ā´‡ā´ĩā´ŋⴟāĩ† ā´¨āĩŊā´•āĩā´•", + "lock": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "locked_folder": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąāĩŧ", + "log_detail_title": "ā´˛āĩ‹ā´—āĩ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´‚", + "log_out": "ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ", + "log_out_all_devices": "ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "logged_in_as": "{user} ⴆⴝā´ŋ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¤āĩ", + "logged_out_all_devices": "ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "logged_out_device": "ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "login": "ā´˛āĩ‹ā´—ā´ŋāĩģ", + "login_disabled": "ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "login_form_api_exception": "API ā´Žā´•āĩā´¸āĩ†ā´Ēāĩā´ˇāĩģ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¸āĩ†āĩŧā´ĩāĩŧ URL ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋⴚāĩā´šāĩ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•.", + "login_form_back_button_text": "ā´¤ā´ŋā´°ā´ŋā´•āĩ†", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_endpoint_url": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Žāĩģā´Ąāĩâ€Œā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ URL", + "login_form_err_http": "ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ http:// ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ https:// ā´Žā´¨āĩā´¨āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´Žā´žā´•āĩā´•āĩā´•", + "login_form_err_invalid_email": "ā´…ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ", + "login_form_err_invalid_url": "ā´…ā´¸ā´žā´§āĩā´ĩā´žā´¯ URL", + "login_form_err_leading_whitespace": "ā´¤āĩā´Ÿā´•āĩā´•ā´¤āĩā´¤ā´ŋā´˛āĩ† ā´ļāĩ‚ā´¨āĩā´¯ā´¸āĩā´Ĩⴞⴂ", + "login_form_err_trailing_whitespace": "ā´…ā´ĩā´¸ā´žā´¨ā´¤āĩā´¤āĩ† ā´ļāĩ‚ā´¨āĩā´¯ā´¸āĩā´Ĩⴞⴂ", + "login_form_failed_get_oauth_server_config": "OAuth ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ, ā´¸āĩ†āĩŧā´ĩāĩŧ URL ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "login_form_failed_get_oauth_server_disable": "ⴈ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ OAuth ā´Ģāĩ€ā´šāĩā´šāĩŧ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "login_form_failed_login": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ† ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ, ā´¸āĩ†āĩŧā´ĩāĩŧ URL, ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ, ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´¨āĩā´¨ā´ŋā´ĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "login_form_handshake_exception": "ā´¸āĩ†āĩŧā´ĩā´ąāĩā´Žā´žā´¯ā´ŋ ā´’ā´°āĩ ā´šā´žāĩģā´Ąāĩâ€Œā´ˇāĩ‡ā´•āĩā´•āĩ ā´Žā´•āĩā´¸āĩ†ā´Ēāĩā´ˇāĩģ ⴉ⴪āĩā´Ÿā´žā´¯ā´ŋ. ā´¨ā´ŋā´™āĩā´™āĩž ā´’ā´°āĩ ā´¸āĩā´ĩⴝⴂ ā´’ā´Ēāĩā´Ēā´ŋⴟāĩā´Ÿ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąā´žā´Ŗāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¸āĩā´ĩⴝⴂ ā´’ā´Ēāĩā´Ēā´ŋⴟāĩā´Ÿ ā´¸āĩŧⴟāĩā´Ÿā´ŋā´Ģā´ŋā´•āĩā´•ā´ąāĩā´ąāĩ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•.", + "login_form_password_hint": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "login_form_save_login": "ā´˛āĩ‹ā´—ā´ŋāĩģ ⴆⴝā´ŋ ā´¤āĩā´Ÿā´°āĩā´•", + "login_form_server_empty": "ā´’ā´°āĩ ā´¸āĩ†āĩŧā´ĩāĩŧ URL ā´¨āĩŊā´•āĩā´•.", + "login_form_server_error": "ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´•ā´Ŗā´•āĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´žāĩā´žā´ŋā´˛āĩā´˛.", + "login_has_been_disabled": "ā´˛āĩ‹ā´—ā´ŋāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ.", + "login_password_changed_error": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´’ā´°āĩ ā´Ēā´ŋā´ļā´•āĩā´Ŗāĩā´Ÿā´žā´¯ā´ŋ", + "login_password_changed_success": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "logout_all_device_confirmation": "ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "logout_this_device_confirmation": "ⴈ ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "logs": "ā´˛āĩ‹ā´—āĩā´•āĩž", + "longitude": "ā´°āĩ‡ā´–ā´žā´‚ā´ļā´‚", + "look": "ā´•ā´žā´´āĩā´š", + "loop_videos": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´˛āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "loop_videos_description": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļ ā´ĩāĩā´¯āĩ‚ā´ĩā´ąā´ŋāĩŊ ā´’ā´°āĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´˛āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•.", + "main_branch_warning": "ā´¨ā´ŋā´™āĩā´™āĩž ā´’ā´°āĩ ā´Ąāĩ†ā´ĩā´˛ā´Ēāĩâ€Œā´Žāĩ†ā´¨āĩā´ąāĩ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´žā´Ŗāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ; ā´’ā´°āĩ ā´ąā´ŋā´˛āĩ€ā´¸āĩ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ ā´žā´™āĩā´™āĩž ā´ļā´•āĩā´¤ā´Žā´žā´¯ā´ŋ ā´ļāĩā´Ēā´žāĩŧā´ļ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ!", + "main_menu": "ā´Ēāĩā´°ā´§ā´žā´¨ ā´Žāĩ†ā´¨āĩ", + "make": "ā´¨ā´ŋāĩŧā´Žāĩā´Žā´žā´¤ā´žā´ĩāĩ", + "manage_geolocation": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_shared_links": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_sharing_with_partners": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´•ā´ŗāĩā´Žā´žā´¯āĩā´ŗāĩā´ŗ ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_the_app_settings": "ā´†ā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_your_account": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_your_api_keys": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† API ā´•āĩ€ā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_your_devices": "ā´¨ā´ŋā´™āĩā´™āĩž ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¤ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "manage_your_oauth_connection": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† OAuth ā´•ā´Ŗā´•āĩā´ˇāĩģ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "map": "ā´Žā´žā´Ēāĩā´Ēāĩ", + "map_assets_in_bounds": "{count, plural, =0 {ⴈ ā´Ēāĩā´°ā´Ļāĩ‡ā´ļā´¤āĩā´¤āĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛} one {# ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹} other {# ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž}}", + "map_cannot_get_user_location": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´Ĩā´žā´¨ā´‚ ⴞⴭā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "map_location_dialog_yes": "ā´…ā´¤āĩ†", + "map_location_picker_page_use_location": "ⴈ ā´¸āĩā´Ĩⴞⴂ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "map_location_service_disabled_content": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤āĩ ā´¨ā´ŋā´¨āĩā´¨āĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´¸āĩ‡ā´ĩⴍⴂ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩ‡ā´Ŗāĩā´Ÿā´¤āĩā´Ŗāĩā´Ÿāĩ. ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´Ŗāĩ‹?", + "map_location_service_disabled_title": "ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´¸āĩ‡ā´ĩⴍⴂ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "map_marker_for_images": "{city}, {country} ā´Žā´¨āĩā´¨ā´ŋā´ĩā´ŋā´Ÿā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Žā´Ÿāĩā´¤āĩā´¤ ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩžā´•āĩā´•āĩā´ŗāĩā´ŗ ā´Žā´žā´Ēāĩā´Ēāĩ ā´Žā´žāĩŧā´•āĩā´•āĩŧ", + "map_marker_with_image": "ⴚā´ŋā´¤āĩā´°ā´¤āĩā´¤āĩ‹ā´Ÿāĩā´•āĩ‚ā´Ÿā´ŋā´¯ ā´Žā´žā´Ēāĩā´Ēāĩ ā´Žā´žāĩŧā´•āĩā´•āĩŧ", + "map_no_location_permission_content": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´¸āĩā´Ĩā´žā´¨ā´¤āĩā´¤āĩ ā´¨ā´ŋā´¨āĩā´¨āĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ. ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•ā´Ŗāĩ‹?", + "map_no_location_permission_title": "ā´˛āĩŠā´•āĩā´•āĩ‡ā´ˇāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´¨ā´ŋā´ˇāĩ‡ā´§ā´ŋⴚāĩā´šāĩ", + "map_settings": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "map_settings_dark_mode": "ā´Ąā´žāĩŧā´•āĩā´•āĩ ā´Žāĩ‹ā´Ąāĩ", + "map_settings_date_range_option_day": "ā´•ā´´ā´ŋā´žāĩā´ž 24 ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚āĩŧ", + "map_settings_date_range_option_days": "ā´•ā´´ā´ŋā´žāĩā´ž {days} ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩž", + "map_settings_date_range_option_year": "ā´•ā´´ā´ŋā´žāĩā´ž ā´ĩāĩŧⴎⴂ", + "map_settings_date_range_option_years": "ā´•ā´´ā´ŋā´žāĩā´ž {years} ā´ĩāĩŧⴎⴙāĩā´™āĩž", + "map_settings_dialog_title": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "map_settings_include_show_archived": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤ā´ĩ ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "map_settings_include_show_partners": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´•ā´ŗāĩ† ā´‰āĩžā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "map_settings_only_show_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩ ā´Žā´žā´¤āĩā´°ā´‚ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "map_settings_theme_settings": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´¤āĩ€ā´‚", + "map_zoom_to_see_photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´•ā´žā´Ŗā´žāĩģ ā´¸āĩ‚ā´‚ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "mark_all_as_read": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´ĩā´žā´¯ā´ŋⴚāĩā´šā´¤ā´žā´¯ā´ŋ ā´…ā´Ÿā´¯ā´žā´ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "mark_as_read": "ā´ĩā´žā´¯ā´ŋⴚāĩā´šā´¤ā´žā´¯ā´ŋ ā´…ā´Ÿā´¯ā´žā´ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤āĩā´•", + "marked_all_as_read": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´ĩā´žā´¯ā´ŋⴚāĩā´šā´¤ā´žā´¯ā´ŋ ā´…ā´Ÿā´¯ā´žā´ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ŋ", + "matches": "ⴚāĩ‡āĩŧⴚāĩā´šā´•āĩž", + "matching_assets": "ⴚāĩ‡āĩŧⴚāĩā´šā´¯āĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "media_type": "ā´Žāĩ€ā´Ąā´ŋā´¯ ⴤⴰⴂ", + "memories": "ā´“āĩŧā´Žāĩā´Žā´•āĩž", + "memories_all_caught_up": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´•ā´Ŗāĩā´Ÿāĩā´¤āĩ€āĩŧā´¤āĩā´¤āĩ", + "memories_check_back_tomorrow": "ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´“āĩŧā´Žāĩā´Žā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¨ā´žā´ŗāĩ† ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•", + "memories_setting_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´“āĩŧā´Žāĩā´Žā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™āĩž ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤āĩ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "memories_start_over": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¤āĩā´Ÿā´™āĩā´™āĩā´•", + "memories_swipe_to_close": "ā´…ā´Ÿā´¯āĩā´•āĩā´•ā´žāĩģ ā´Žāĩā´•ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¸āĩā´ĩāĩˆā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "memory": "ā´Žāĩ†ā´Žāĩā´Žā´ąā´ŋ", + "memory_lane_title": "ā´“āĩŧā´Žāĩā´Žā´•ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¤ {title}", + "menu": "ā´Žāĩ†ā´¨āĩ", + "merge": "ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "merge_people": "ⴆⴺāĩā´•ā´ŗāĩ† ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "merge_people_limit": "ā´’ā´°āĩ ā´¸ā´Žā´¯ā´‚ 5 ā´Žāĩā´–ā´™āĩā´™āĩž ā´ĩā´°āĩ† ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩ‚", + "merge_people_prompt": "ⴈ ⴆⴺāĩā´•ā´ŗāĩ† ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´Ŗāĩ‹? ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ā´Ēā´´ā´¯ā´Ēⴟā´ŋā´¯ā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "merge_people_successfully": "ⴆⴺāĩā´•ā´ŗāĩ† ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ", + "merged_people_count": "{count, plural, one {# ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ} other {# ⴆⴺāĩā´•ā´ŗāĩ† ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ}}", + "minimize": "ⴚāĩ†ā´ąāĩā´¤ā´žā´•āĩā´•āĩā´•", + "minute": "ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩ", + "minutes": "ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩā´•āĩž", + "missing": "ā´•ā´žā´Ŗā´žā´¨ā´ŋā´˛āĩā´˛", + "model": "ā´Žāĩ‹ā´ĄāĩŊ", + "month": "ā´Žā´žā´¸ā´‚", + "monthly_title_text_date_format": "MMMM y", + "more": "ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ", + "move": "ā´¨āĩ€ā´•āĩā´•āĩā´•", + "move_off_locked_folder": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "move_to_lock_folder_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "move_to_locked_folder": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "move_to_locked_folder_confirmation": "ⴈ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´¯āĩā´‚ ā´Žā´˛āĩā´˛ā´ž ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚, ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žā´žā´¤āĩā´°ā´Žāĩ‡ ā´•ā´žā´Ŗā´žāĩģ ā´•ā´´ā´ŋā´¯āĩ‚", + "moved_to_archive": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ", + "moved_to_library": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ", + "moved_to_trash": "ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ", + "multiselect_grid_edit_date_time_err_read_only": "ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´Žā´žā´¤āĩā´°ā´Žāĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąā´ŋ(ā´•ā´ŗāĩ)ⴟāĩ† ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "multiselect_grid_edit_gps_err_read_only": "ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´Žā´žā´¤āĩā´°ā´Žāĩā´ŗāĩā´ŗ ā´…ā´¸ā´ąāĩā´ąā´ŋ(ā´•ā´ŗāĩ)ⴟāĩ† ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "mute_memories": "ā´“āĩŧā´Žāĩā´Žā´•āĩž ā´¨ā´ŋā´ļāĩā´ļā´Ŧāĩā´Ļā´Žā´žā´•āĩā´•āĩā´•", + "my_albums": "ā´Žā´¨āĩā´ąāĩ† ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "name": "ā´Ēāĩ‡ā´°āĩ", + "name_or_nickname": "ā´Ēāĩ‡ā´°āĩ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´ĩā´ŋā´ŗā´ŋā´Ēāĩā´Ēāĩ‡ā´°āĩ", + "network_requirement_photos_upload": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´¸āĩ†ā´˛āĩā´˛āĩā´˛ā´žāĩŧ ā´Ąā´žā´ąāĩā´ą ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "network_requirement_videos_upload": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´¸āĩ†ā´˛āĩā´˛āĩā´˛ā´žāĩŧ ā´Ąā´žā´ąāĩā´ą ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "network_requirements": "ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´†ā´ĩā´ļāĩā´¯ā´•ⴤⴕāĩž", + "network_requirements_updated": "ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´†ā´ĩā´ļāĩā´¯ā´•ⴤⴕāĩž ā´Žā´žā´ąā´ŋ, ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´•āĩā´¯āĩ‚ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "networking_settings": "ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´‚ā´—āĩ", + "networking_subtitle": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Žāĩģā´Ąāĩâ€Œā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "never": "ā´’ā´°ā´ŋā´•āĩā´•ā´˛āĩā´‚ ⴇⴞāĩā´˛", + "new_album": "ā´Ēāĩā´¤ā´ŋā´¯ ā´†āĩŊā´Ŧā´‚", + "new_api_key": "ā´Ēāĩā´¤ā´ŋā´¯ API ā´•āĩ€", + "new_password": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ", + "new_person": "ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋ", + "new_pin_code": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ", + "new_pin_code_subtitle": "ā´¨ā´ŋā´™āĩā´™āĩž ā´†ā´Ļāĩā´¯ā´Žā´žā´¯ā´ŋⴟāĩā´Ÿā´žā´Ŗāĩ ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąāĩŧ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩ. ⴈ ā´Ēāĩ‡ā´œāĩ ā´¸āĩā´°ā´•āĩā´ˇā´ŋā´¤ā´Žā´žā´¯ā´ŋ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´’ā´°āĩ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "new_timeline": "ā´Ēāĩā´¤ā´ŋā´¯ ⴟāĩˆā´‚ā´˛āĩˆāĩģ", + "new_user_created": "ā´Ēāĩā´¤ā´ŋā´¯ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´ŋ", + "new_version_available": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ⴞⴭāĩā´¯ā´Žā´žā´Ŗāĩ", + "newest_first": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋⴝⴤāĩ ā´†ā´Ļāĩā´¯ā´‚", + "next": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ", + "next_memory": "ā´…ā´Ÿāĩā´¤āĩā´¤ ā´“āĩŧā´Žāĩā´Ž", + "no": "ⴇⴞāĩā´˛", + "no_albums_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•ā´žāĩģ ā´’ā´°āĩ ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "no_albums_with_name_yet": "ⴈ ā´Ēāĩ‡ā´°ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ⴇⴤāĩā´ĩā´°āĩ† ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ⴇⴞāĩā´˛āĩ†ā´¨āĩā´¨āĩ ā´¤āĩ‹ā´¨āĩā´¨āĩā´¨āĩā´¨āĩ.", + "no_albums_yet": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ⴇⴤāĩā´ĩā´°āĩ† ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ⴇⴞāĩā´˛āĩ†ā´¨āĩā´¨āĩ ā´¤āĩ‹ā´¨āĩā´¨āĩā´¨āĩā´¨āĩ.", + "no_archived_assets_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´•ā´žā´´āĩâ€Œā´šā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žā´ąā´¯āĩā´•āĩā´•ā´žāĩģ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "no_assets_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´†ā´Ļāĩā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩā´˛ā´ŋā´•āĩā´•āĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "no_assets_to_show": "ā´•ā´žā´Ŗā´ŋā´•āĩā´•ā´žāĩģ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "no_cast_devices_found": "ā´•ā´žā´¸āĩā´ąāĩā´ąāĩ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_checksum_local": "ⴚāĩ†ā´•āĩā´•āĩā´¸ā´‚ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛ - ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_checksum_remote": "ⴚāĩ†ā´•āĩā´•āĩā´¸ā´‚ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛ - ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ā´…ā´¸ā´ąāĩā´ąāĩ ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_duplicates_found": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "no_exif_info_available": "Exif ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "no_explore_results_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´ļāĩ‡ā´–ā´°ā´‚ ā´Ēā´°āĩā´¯ā´ĩāĩ‡ā´•āĩā´ˇā´Ŗā´‚ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "no_favorites_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´ŋā´•ā´šāĩā´š ⴚā´ŋā´¤āĩā´°ā´™āĩā´™ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ‡ā´—ā´¤āĩā´¤ā´ŋāĩŊ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´žāĩģ ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩ ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "no_libraries_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´’ā´°āĩ ā´Ŧā´žā´šāĩā´¯ ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "no_local_assets_found": "ⴈ ⴚāĩ†ā´•āĩā´•āĩā´¸ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´• ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_locked_photos_message": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩâ€Œā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋā´˛āĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Žā´ąā´šāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´¨ā´ŋā´™āĩā´™āĩž ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´Ŧāĩā´°āĩ—ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹ā´´āĩ‹ ā´¤ā´ŋā´°ā´¯āĩā´Žāĩā´Ēāĩ‹ā´´āĩ‹ ā´…ā´ĩ ā´Ļāĩƒā´ļāĩā´¯ā´Žā´žā´•ā´ŋā´˛āĩā´˛.", + "no_name": "ā´Ēāĩ‡ā´°ā´ŋā´˛āĩā´˛", + "no_notifications": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "no_people_found": "ⴚāĩ‡āĩŧⴚāĩā´šā´¯āĩā´ŗāĩā´ŗ ⴆⴺāĩā´•ā´ŗāĩ† ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™ā´ŗā´ŋā´˛āĩā´˛", + "no_remote_assets_found": "ⴈ ⴚāĩ†ā´•āĩā´•āĩā´¸ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "no_results": "ā´Ģⴞⴙāĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "no_results_description": "ā´’ā´°āĩ ā´Ēā´°āĩā´¯ā´žā´¯ā´Ēā´Ļā´Žāĩ‹ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´ĒāĩŠā´¤āĩā´ĩā´žā´¯ ā´•āĩ€ā´ĩāĩ‡ā´Ąāĩ‹ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•", + "no_shared_albums_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´˛āĩ† ⴆⴺāĩā´•ā´ŗāĩā´Žā´žā´¯ā´ŋ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´’ā´°āĩ ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•", + "no_uploads_in_progress": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´Ēāĩā´°āĩ‹ā´—ā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "not_available": "ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "not_in_any_album": "ā´’ā´°āĩ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩā´Žā´ŋā´˛āĩā´˛", + "not_selected": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "note_apply_storage_label_to_previously_uploaded assets": "ā´•āĩā´ąā´ŋā´Ēāĩā´Ēāĩ: ā´Žāĩā´Žāĩā´Ēāĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´ŦāĩŊ ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ, ⴇⴤāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "notes": "ā´•āĩā´ąā´ŋā´Ēāĩā´Ēāĩā´•āĩž", + "nothing_here_yet": "ā´‡ā´ĩā´ŋⴟāĩ† ⴇⴤāĩā´ĩā´°āĩ† ā´’ā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "notification_permission_dialog_content": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´žāĩģ, ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´¯ā´ŋ 'ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´•' ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•.", + "notification_permission_list_tile_content": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´žāĩģ ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´¨āĩŊā´•āĩā´•.", + "notification_permission_list_tile_enable_button": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "notification_permission_list_tile_title": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´…ā´¨āĩā´Žā´¤ā´ŋ", + "notification_toggle_setting_description": "ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "notifications": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž", + "notifications_setting_description": "ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "oauth": "OAuth", + "official_immich_resources": "Immich-ā´¨āĩā´ąāĩ† ā´”ā´Ļāĩā´¯āĩ‹ā´—ā´ŋā´• ā´‰ā´ąā´ĩā´ŋā´Ÿā´™āĩā´™āĩž", + "offline": "ā´“ā´Ģāĩâ€Œā´˛āĩˆāĩģ", + "offset": "ā´“ā´Ģāĩā´¸āĩ†ā´ąāĩā´ąāĩ", + "ok": "ā´ļā´°ā´ŋ", + "oldest_first": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēⴴⴝⴤāĩ ā´†ā´Ļāĩā´¯ā´‚", + "on_this_device": "ⴈ ā´‰ā´Ēā´•ā´°ā´Ŗā´¤āĩā´¤ā´ŋāĩŊ", + "onboarding": "ā´“āĩēā´Ŧāĩ‹āĩŧā´Ąā´ŋā´‚ā´—āĩ", + "onboarding_locale_description": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•ā´ŋā´ˇāĩā´Ÿā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿ ā´­ā´žā´ˇ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴇⴤāĩ ā´Ēā´ŋā´¨āĩā´¨āĩ€ā´Ÿāĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Žā´žā´ąāĩā´ąā´žā´ĩāĩā´¨āĩā´¨ā´¤ā´žā´Ŗāĩ.", + "onboarding_privacy_description": "ⴇⴍā´ŋā´Ēāĩā´Ēā´ąā´¯āĩā´¨āĩā´¨ (ā´“ā´Ēāĩā´ˇā´ŖāĩŊ) ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•āĩž ā´Ŧā´žā´šāĩā´¯ ā´¸āĩ‡ā´ĩⴍⴙāĩā´™ā´ŗāĩ† ā´†ā´ļāĩā´°ā´¯ā´ŋⴚāĩā´šā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´…ā´ĩ ā´Žā´Ēāĩā´Ēāĩ‹āĩž ā´ĩāĩ‡ā´Ŗā´Žāĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´žā´‚.", + "onboarding_server_welcome_description": "ⴚā´ŋā´˛ ā´¸ā´žā´§ā´žā´°ā´Ŗ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‡āĩģā´¸āĩā´ąāĩā´ąāĩģā´¸āĩ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•ā´žā´‚.", + "onboarding_theme_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‡āĩģā´¸āĩā´ąāĩā´ąāĩģā´¸ā´ŋā´¨ā´žā´¯ā´ŋ ā´’ā´°āĩ ā´•ā´ŗāĩŧ ā´¤āĩ€ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴇⴤāĩ ā´Ēā´ŋā´¨āĩā´¨āĩ€ā´Ÿāĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Žā´žā´ąāĩā´ąā´žā´ĩāĩā´¨āĩā´¨ā´¤ā´žā´Ŗāĩ.", + "onboarding_user_welcome_description": "ā´¨ā´Žāĩā´•āĩā´•āĩ ⴆⴰⴂⴭā´ŋā´•āĩā´•ā´žā´‚!", + "onboarding_welcome_user": "ā´¸āĩā´ĩā´žā´—ā´¤ā´‚, {user}", + "online": "ā´“āĩēā´˛āĩˆāĩģ", + "only_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩ ā´Žā´žā´¤āĩā´°ā´‚", + "open": "ā´¤āĩā´ąā´•āĩā´•āĩā´•", + "open_in_map_view": "ā´Žā´žā´Ēāĩā´Ēāĩ ā´•ā´žā´´āĩâ€Œā´šā´¯ā´ŋāĩŊ ā´¤āĩā´ąā´•āĩā´•āĩā´•", + "open_in_openstreetmap": "OpenStreetMap-āĩŊ ā´¤āĩā´ąā´•āĩā´•āĩā´•", + "open_the_search_filters": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´Ģā´ŋāĩŊⴟāĩā´Ÿā´ąāĩā´•āĩž ā´¤āĩā´ąā´•āĩā´•āĩā´•", + "options": "ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "or": "ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ", + "organize_into_albums": "ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´žā´¯ā´ŋ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "organize_into_albums_description": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´¸ā´ŋā´™āĩā´•āĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "organize_your_library": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "original": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩā´‚", + "other": "ā´Žā´ąāĩā´ąāĩā´ŗāĩā´ŗā´ĩ", + "other_devices": "ā´Žā´ąāĩā´ąāĩ ā´‰ā´Ēā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "other_entities": "ā´Žā´ąāĩā´ąāĩ ā´Žā´¨āĩā´ąā´ŋā´ąāĩā´ąā´ŋā´•āĩž", + "other_variables": "ā´Žā´ąāĩā´ąāĩ ā´ĩāĩ‡ā´°ā´ŋā´¯ā´Ŧā´ŋā´ŗāĩā´•āĩž", + "owned": "ā´¸āĩā´ĩā´¨āĩā´¤ā´Žā´žā´¯āĩā´ŗāĩā´ŗā´¤āĩ", + "owner": "ā´‰ā´Ÿā´Ž", + "partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋ", + "partner_can_access": "{partner}-ā´•āĩā´•āĩ ⴆⴕāĩā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚", + "partner_can_access_assets": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩā´šāĩ†ā´¯āĩâ€Œā´¤ā´¤āĩā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋⴝⴤāĩā´Žā´žā´¯ā´ĩ ā´’ā´´ā´ŋā´•āĩ† ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´˛āĩā´˛ā´ž ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "partner_can_access_location": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Žā´Ÿāĩā´¤āĩā´¤ ā´¸āĩā´Ĩⴞⴂ", + "partner_list_user_photos": "{user}-ā´¨āĩā´ąāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž", + "partner_list_view_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´•ā´žā´Ŗāĩā´•", + "partner_page_empty_message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ⴇⴤāĩā´ĩā´°āĩ† ā´’ā´°āĩ ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Žā´žā´¯āĩā´‚ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛.", + "partner_page_no_more_users": "ⴚāĩ‡āĩŧā´•āĩā´•ā´žāĩģ ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗā´ŋā´˛āĩā´˛", + "partner_page_partner_add_failed": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩ† ⴚāĩ‡āĩŧā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "partner_page_select_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "partner_page_shared_to_title": "ⴇⴤā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ", + "partner_page_stop_sharing_content": "{partner}-ā´•āĩā´•āĩ ⴇⴍā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "partner_sharing": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Žā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ", + "partners": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´•āĩž", + "password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡āĩŧā´Ąāĩ", + "password_does_not_match": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ĒāĩŠā´°āĩā´¤āĩā´¤ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛", + "password_required": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ", + "password_reset_success": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ā´ĩā´ŋⴜⴝā´ŋⴚāĩā´šāĩ", + "past_durations": { + "days": "ā´•ā´´ā´ŋā´žāĩā´ž {days, plural, one {ā´Ļā´ŋā´ĩⴏⴂ} other {# ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩž}}", + "hours": "ā´•ā´´ā´ŋā´žāĩā´ž {hours, plural, one {ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚āĩŧ} other {# ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąāĩā´•āĩž}}", + "years": "ā´•ā´´ā´ŋā´žāĩā´ž {years, plural, one {ā´ĩāĩŧⴎⴂ} other {# ā´ĩāĩŧⴎⴙāĩā´™āĩž}}" + }, + "path": "ā´Ēā´žā´¤āĩā´¤āĩ", + "pattern": "ā´Ēā´žā´ąāĩā´ąāĩ‡āĩē", + "pause": "ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "pause_memories": "ā´“āĩŧā´Žāĩā´Žā´•āĩž ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "paused": "ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤ā´ŋ", + "pending": "ā´•ā´žā´¤āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "people": "ⴆⴺāĩā´•āĩž", + "people_edits_count": "{count, plural, one {# ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¤āĩ} other {# ⴆⴺāĩā´•ā´ŗāĩ† ā´Žā´Ąā´ŋā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩā´¤āĩ}}", + "people_feature_description": "ⴆⴺāĩā´•ā´ŗā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ŧāĩā´°āĩ—ā´¸āĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "people_sidebar_description": "ā´¸āĩˆā´Ąāĩâ€Œā´Ŧā´žā´ąā´ŋāĩŊ 'ⴆⴺāĩā´•āĩž' ā´Žā´¨āĩā´¨ā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´’ā´°āĩ ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "permanent_deletion_warning": "ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩŊ ā´Žāĩā´¨āĩā´¨ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ", + "permanent_deletion_warning_setting_description": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´Žāĩā´Ēāĩ‹āĩž ā´’ā´°āĩ ā´Žāĩā´¨āĩā´¨ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "permanently_delete": "ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "permanently_delete_assets_count": "{count, plural, one {ā´…ā´¸ā´ąāĩā´ąāĩ} other {ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "permanently_delete_assets_prompt": "{count, plural, one {ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?} other {ⴈ # ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?}} ⴇⴤāĩ {count, plural, one {ā´…ā´¤ā´ŋā´¨āĩ† ā´…ā´¤ā´ŋā´¨āĩā´ąāĩ†} other {ā´…ā´ĩā´¯āĩ† ā´…ā´ĩā´¯āĩā´Ÿāĩ†}} ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ(ā´™āĩā´™ā´ŗā´ŋāĩŊ) ā´¨ā´ŋā´¨āĩā´¨āĩā´‚ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "permanently_deleted_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "permanently_deleted_assets_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ", + "permission": "ā´…ā´¨āĩā´Žā´¤ā´ŋ", + "permission_empty": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•ā´°āĩā´¤āĩ", + "permission_onboarding_back": "ā´¤ā´ŋā´°ā´ŋā´•āĩ†", + "permission_onboarding_continue_anyway": "ā´Žā´¨āĩā´¤ā´žā´¯ā´žā´˛āĩā´‚ ā´¤āĩā´Ÿā´°āĩā´•", + "permission_onboarding_get_started": "ā´¤āĩā´Ÿā´™āĩā´™ā´žā´‚", + "permission_onboarding_go_to_settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "permission_onboarding_permission_denied": "ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´¨ā´ŋā´ˇāĩ‡ā´§ā´ŋⴚāĩā´šāĩ. Immich ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹, ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´…ā´¨āĩā´Žā´¤ā´ŋā´•āĩž ā´¨āĩŊā´•āĩā´•.", + "permission_onboarding_permission_granted": "ā´…ā´¨āĩā´Žā´¤ā´ŋ ⴞⴭā´ŋⴚāĩā´šāĩ! ā´¨ā´ŋā´™āĩā´™āĩž ⴤⴝāĩā´¯ā´žā´ąā´žā´Ŗāĩ.", + "permission_onboarding_permission_limited": "ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´Ēā´°ā´ŋā´Žā´ŋā´¤ā´Žā´žā´Ŗāĩ. Immich-ā´¨āĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žāĩā´´āĩā´ĩāĩģ ā´—ā´žā´˛ā´ąā´ŋ ā´ļāĩ‡ā´–ā´°ā´ĩāĩā´‚ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¨āĩā´‚ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´žā´¨āĩā´‚ ā´…ā´¨āĩā´ĩā´Ļā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ, ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹, ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´…ā´¨āĩā´Žā´¤ā´ŋā´•āĩž ā´¨āĩŊā´•āĩā´•.", + "permission_onboarding_request": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ Immich-ā´¨āĩ ā´…ā´¨āĩā´Žā´¤ā´ŋ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ.", + "person": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋ", + "person_age_months": "{months, plural, one {# ā´Žā´žā´¸ā´‚} other {# ā´Žā´žā´¸ā´‚}} ā´Ēāĩā´°ā´žā´¯ā´‚", + "person_age_year_months": "1 ā´ĩāĩŧā´ˇā´ĩāĩā´‚ {months, plural, one {# ā´Žā´žā´¸ā´ĩāĩā´‚} other {# ā´Žā´žā´¸ā´ĩāĩā´‚}} ā´Ēāĩā´°ā´žā´¯ā´‚", + "person_age_years": "{years, plural, other {# ā´ĩⴝⴏāĩā´¸āĩ}}", + "person_birthdate": "{date}-ā´¨āĩ ⴜⴍā´ŋⴚāĩā´šāĩ", + "person_hidden": "{name}{hidden, select, true { (ā´Žā´ąā´šāĩā´šā´¤āĩ)} other {}}", + "photo_shared_all_users": "ā´¨ā´ŋā´™āĩā´™āĩž ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩā´Žā´žā´¯āĩā´‚ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ, ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗā´žā´°āĩā´‚ ⴇⴞāĩā´˛āĩ†ā´¨āĩā´¨āĩ ā´¤āĩ‹ā´¨āĩā´¨āĩā´¨āĩā´¨āĩ.", + "photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž", + "photos_and_videos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚", + "photos_count": "{count, plural, one {{count, number} ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹} other {{count, number} ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž}}", + "photos_from_previous_years": "ā´Žāĩāĩģ ā´ĩāĩŧⴎⴙāĩā´™ā´ŗā´ŋā´˛āĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž", + "pick_a_location": "ā´’ā´°āĩ ā´¸āĩā´Ĩⴞⴂ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "pin_code_changed_successfully": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´Žā´žā´ąāĩā´ąā´ŋ", + "pin_code_reset_successfully": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "pin_code_setup_successfully": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋⴚāĩā´šāĩ", + "pin_verification": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´¨", + "place": "ā´¸āĩā´Ĩⴞⴂ", + "places": "ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž", + "places_count": "{count, plural, one {{count, number} ā´¸āĩā´Ĩⴞⴂ} other {{count, number} ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž}}", + "play": "ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "play_memories": "ā´“āĩŧā´Žāĩā´Žā´•āĩž ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "play_motion_photo": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "play_or_pause_video": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´• ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "please_auth_to_access": "ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´Ēāĩā´°ā´žā´Žā´žā´Ŗāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "port": "ā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩ", + "preferences_settings_subtitle": "ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´Žāĩāĩģⴗ⴪ⴍⴕāĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "preferences_settings_title": "ā´Žāĩāĩģⴗ⴪ⴍⴕāĩž", + "preparing": "ⴤⴝāĩā´¯ā´žā´ąā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "preset": "ā´Ēāĩā´°āĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ", + "preview": "ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚", + "previous": "ā´Žāĩā´Žāĩā´Ēā´¤āĩā´¤āĩ†", + "previous_memory": "ā´Žāĩā´Žāĩā´Ēā´¤āĩā´¤āĩ† ā´“āĩŧā´Žāĩā´Ž", + "previous_or_next_day": "ā´Ļā´ŋā´ĩⴏⴂ ā´Žāĩā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ/ā´Ēāĩā´ąā´•āĩ‹ā´Ÿāĩā´Ÿāĩ", + "previous_or_next_month": "ā´Žā´žā´¸ā´‚ ā´Žāĩā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ/ā´Ēāĩā´ąā´•āĩ‹ā´Ÿāĩā´Ÿāĩ", + "previous_or_next_photo": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´Žāĩā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ/ā´Ēāĩā´ąā´•āĩ‹ā´Ÿāĩā´Ÿāĩ", + "previous_or_next_year": "ā´ĩāĩŧⴎⴂ ā´Žāĩā´¨āĩā´¨āĩ‹ā´Ÿāĩā´Ÿāĩ/ā´Ēāĩā´ąā´•āĩ‹ā´Ÿāĩā´Ÿāĩ", + "primary": "ā´Ēāĩā´°ā´§ā´žā´¨ā´Žā´žā´¯ā´¤āĩ", + "privacy": "ā´¸āĩā´ĩā´•ā´žā´°āĩā´¯ā´¤", + "profile": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ", + "profile_drawer_app_logs": "ā´˛āĩ‹ā´—āĩā´•āĩž", + "profile_drawer_client_out_of_date_major": "ā´ŽāĩŠā´ŦāĩˆāĩŊ ā´†ā´Ēāĩā´Ēāĩ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēāĩā´°ā´§ā´žā´¨ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "profile_drawer_client_out_of_date_minor": "ā´ŽāĩŠā´ŦāĩˆāĩŊ ā´†ā´Ēāĩā´Ēāĩ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Žāĩˆā´¨āĩŧ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "profile_drawer_client_server_up_to_date": "ā´•āĩā´˛ā´¯ā´ŋā´¨āĩā´ąāĩā´‚ ā´¸āĩ†āĩŧā´ĩā´ąāĩā´‚ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ā´¤ā´žā´Ŗāĩ", + "profile_drawer_github": "ā´—ā´ŋā´ąāĩā´ąāĩā´šā´Ŧāĩ", + "profile_drawer_readonly_mode": "ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Žāĩ‹ā´Ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ. ā´Ēāĩā´ąā´¤āĩā´¤āĩā´•ā´Ÿā´•āĩā´•ā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´…ā´ĩā´¤ā´žāĩŧ ⴐⴕāĩā´•ā´Ŗā´ŋāĩŊ ā´Ļāĩ€āĩŧⴘⴍāĩ‡ā´°ā´‚ ā´…ā´Žāĩŧā´¤āĩā´¤āĩā´•.", + "profile_drawer_server_out_of_date_major": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēāĩā´°ā´§ā´žā´¨ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "profile_drawer_server_out_of_date_minor": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ. ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Žāĩˆā´¨āĩŧ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•.", + "profile_image_of_user": "{user}-ā´¨āĩā´ąāĩ† ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´‚", + "profile_picture_set": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´‚ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋⴚāĩā´šāĩ.", + "public_album": "ā´ĒāĩŠā´¤āĩ ā´†āĩŊā´Ŧā´‚", + "public_share": "ā´ĒāĩŠā´¤āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ", + "purchase_account_info": "ā´¸ā´šā´žā´¯ā´ŋ", + "purchase_activated_subtitle": "Immich-ā´¨āĩ†ā´¯āĩā´‚ ā´“ā´Ēāĩā´Ēāĩē ā´¸āĩ‹ā´´āĩâ€Œā´¸āĩ ā´¸āĩ‹ā´Ģāĩā´ąāĩā´ąāĩâ€Œā´ĩāĩ†ā´¯ā´ąā´ŋā´¨āĩ†ā´¯āĩā´‚ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´šāĩā´šā´¤ā´ŋā´¨āĩ ⴍⴍāĩā´Ļā´ŋ", + "purchase_activated_time": "{date}-ā´¨āĩ ⴏⴜāĩ€ā´ĩā´Žā´žā´•āĩā´•ā´ŋ", + "purchase_activated_title": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´•āĩ€ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ⴏⴜāĩ€ā´ĩā´Žā´žā´•āĩā´•ā´ŋ", + "purchase_button_activate": "ⴏⴜāĩ€ā´ĩā´Žā´žā´•āĩā´•āĩā´•", + "purchase_button_buy": "ā´ĩā´žā´™āĩā´™āĩā´•", + "purchase_button_buy_immich": "Immich ā´ĩā´žā´™āĩā´™āĩā´•", + "purchase_button_never_show_again": "ⴇⴍā´ŋ ā´•ā´žā´Ŗā´ŋā´•āĩā´•ā´°āĩā´¤āĩ", + "purchase_button_reminder": "30 ā´Ļā´ŋā´ĩⴏⴤāĩā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´“āĩŧā´Žāĩā´Žā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "purchase_button_remove_key": "ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "purchase_button_select": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "purchase_failed_activation": "ⴏⴜāĩ€ā´ĩā´Žā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ! ā´ļā´°ā´ŋā´¯ā´žā´¯ ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ā´•āĩā´•ā´žā´¯ā´ŋ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ā´Ēā´°ā´ŋā´ļāĩ‹ā´§ā´ŋā´•āĩā´•āĩā´•!", + "purchase_individual_description_1": "ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ", + "purchase_individual_description_2": "ā´¸ā´šā´žā´¯ā´ŋā´¯āĩā´Ÿāĩ† ā´¨ā´ŋā´˛", + "purchase_individual_title": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋⴗⴤⴂ", + "purchase_input_suggestion": "ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ⴉ⴪āĩā´Ÿāĩ‹? ā´¤ā´žā´´āĩ† ā´•āĩ€ ā´¨āĩŊā´•āĩā´•", + "purchase_license_subtitle": "ā´¸āĩ‡ā´ĩⴍⴤāĩā´¤ā´ŋā´¨āĩā´ąāĩ† ā´¤āĩā´Ÿāĩŧ ā´ĩā´ŋⴕⴏⴍⴤāĩā´¤āĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•ā´žāĩģ Immich ā´ĩā´žā´™āĩā´™āĩā´•", + "purchase_lifetime_description": "ā´†ā´œāĩ€ā´ĩā´¨ā´žā´¨āĩā´¤ ā´ĩā´žā´™āĩā´™āĩŊ", + "purchase_option_title": "ā´ĩā´žā´™āĩā´™ā´žā´¨āĩā´ŗāĩā´ŗ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "purchase_panel_info_1": "Immich ā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•ā´žāĩģ ā´§ā´žā´°ā´žā´ŗā´‚ ā´¸ā´Žā´¯ā´ĩāĩā´‚ ā´Ēāĩā´°ā´¯ā´¤āĩā´¨ā´ĩāĩā´‚ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ, ā´…ā´¤āĩ ā´•ā´´ā´ŋā´¯āĩā´¨āĩā´¨ā´¤āĩā´° ā´Žā´ŋā´•ā´šāĩā´šā´¤ā´žā´•āĩā´•ā´žāĩģ ā´Žāĩā´´āĩā´ĩāĩģ ā´¸ā´Žā´¯ ā´Žā´žāĩā´šā´ŋā´¨āĩ€ā´¯āĩŧā´Žā´žāĩŧ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ. ā´“ā´Ēāĩā´Ēāĩē ā´¸āĩ‹ā´´āĩâ€Œā´¸āĩ ā´¸āĩ‹ā´Ģāĩā´ąāĩā´ąāĩâ€Œā´ĩāĩ†ā´¯ā´ąāĩā´‚ ā´§ā´žāĩŧā´Žāĩā´Žā´ŋā´•ā´Žā´žā´¯ ā´Ŧā´ŋā´¸ā´ŋⴍⴏāĩā´¸āĩ ā´°āĩ€ā´¤ā´ŋā´•ā´ŗāĩā´‚ ā´Ąāĩ†ā´ĩā´˛ā´Ēāĩā´Ēāĩŧā´Žā´žāĩŧā´•āĩā´•āĩ ā´¸āĩā´¸āĩā´Ĩā´ŋā´°ā´Žā´žā´¯ ā´ĩā´°āĩā´Žā´žā´¨ ā´Žā´žāĩŧā´—āĩā´—ā´Žā´žā´•āĩā´•āĩā´•, ⴚāĩ‚ⴎ⴪ā´Ēā´°ā´Žā´žā´¯ ā´•āĩā´˛āĩ—ā´Ąāĩ ā´¸āĩ‡ā´ĩⴍⴙāĩā´™āĩžā´•āĩā´•āĩ ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´Ŧā´Ļā´˛āĩā´•ā´ŗāĩā´ŗāĩā´ŗ ā´¸āĩā´ĩā´•ā´žā´°āĩā´¯ā´¤ā´¯āĩ† ā´Žā´žā´¨ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´’ā´°āĩ ā´†ā´ĩā´žā´¸ā´ĩāĩā´¯ā´ĩā´¸āĩā´Ĩ ā´¸āĩƒā´ˇāĩā´Ÿā´ŋā´•āĩā´•āĩā´• ā´Žā´¨āĩā´¨ā´¤ā´žā´Ŗāĩ ā´žā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ļāĩ—ā´¤āĩā´¯ā´‚.", + "purchase_panel_info_2": "ā´Ēāĩ‡ā´ĩā´žā´ŗāĩā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•ā´ŋā´˛āĩā´˛āĩ†ā´¨āĩā´¨āĩ ā´žā´™āĩā´™āĩž ā´Ēāĩā´°ā´¤ā´ŋⴜāĩā´žā´žā´Ŧā´Ļāĩā´§ā´°ā´žā´¯ā´¤ā´ŋā´¨ā´žāĩŊ, ⴈ ā´ĩā´žā´™āĩā´™āĩŊ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ Immich-āĩŊ ā´…ā´§ā´ŋā´• ā´Ģāĩ€ā´šāĩā´šā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´¨āĩŊā´•ā´ŋā´˛āĩā´˛. Immich-ā´¨āĩā´ąāĩ† ā´¤āĩā´Ÿāĩŧ ā´ĩā´ŋⴕⴏⴍⴤāĩā´¤āĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•ā´žāĩģ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩ†ā´Ēāĩā´Ēāĩ‹ā´˛āĩā´ŗāĩā´ŗ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ†ā´¯ā´žā´Ŗāĩ ā´žā´™āĩā´™āĩž ā´†ā´ļāĩā´°ā´¯ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ.", + "purchase_panel_title": "ā´Ēā´Ļāĩā´§ā´¤ā´ŋā´¯āĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´•āĩā´•āĩā´•", + "purchase_per_server": "ā´“ā´°āĩ‹ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´‚", + "purchase_per_user": "ā´“ā´°āĩ‹ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩā´‚", + "purchase_remove_product_key": "ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "purchase_remove_product_key_prompt": "ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "purchase_remove_server_product_key": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "purchase_remove_server_product_key_prompt": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "purchase_server_description_1": "ā´Žāĩā´´āĩā´ĩāĩģ ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´¨āĩā´‚", + "purchase_server_description_2": "ā´¸ā´šā´žā´¯ā´ŋā´¯āĩā´Ÿāĩ† ā´¨ā´ŋā´˛", + "purchase_server_title": "ā´¸āĩ†āĩŧā´ĩāĩŧ", + "purchase_settings_server_activated": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Ēāĩā´°āĩŠā´Ąā´•āĩā´ąāĩā´ąāĩ ā´•āĩ€ ā´…ā´Ąāĩā´Žā´ŋāĩģ ⴆ⴪āĩ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤āĩ", + "query_asset_id": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´ā´Ąā´ŋ ā´…ā´¨āĩā´ĩāĩ‡ā´ˇā´ŋā´•āĩā´•āĩā´•", + "queue_status": "ā´•āĩā´¯āĩ‚ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ {count}/{total}", + "rating": "ā´¸āĩā´ąāĩā´ąā´žāĩŧ ā´ąāĩ‡ā´ąāĩā´ąā´ŋā´‚ā´—āĩ", + "rating_clear": "ā´ąāĩ‡ā´ąāĩā´ąā´ŋā´‚ā´—āĩ ā´•āĩā´˛ā´ŋā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "rating_count": "{count, plural, one {# ā´¸āĩā´ąāĩā´ąā´žāĩŧ} other {# ā´¸āĩā´ąāĩā´ąā´žā´ąāĩā´•āĩž}}", + "rating_description": "ā´ĩā´ŋā´ĩā´° ā´Ēā´žā´¨ā´˛ā´ŋāĩŊ EXIF ā´ąāĩ‡ā´ąāĩā´ąā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "reaction_options": "ā´Ēāĩā´°ā´¤ā´ŋā´•ā´°ā´Ŗ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "read_changelog": "ā´Žā´žā´ąāĩā´ąā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´˛āĩ‹ā´—āĩ ā´ĩā´žā´¯ā´ŋā´•āĩā´•āĩā´•", + "readonly_mode_disabled": "ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Žāĩ‹ā´Ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "readonly_mode_enabled": "ā´ąāĩ€ā´Ąāĩ-ā´“āĩēā´˛ā´ŋ ā´Žāĩ‹ā´Ąāĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋ", + "ready_for_upload": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąā´ŋā´¨ā´žā´¯ā´ŋ ⴤⴝāĩā´¯ā´žā´ąā´žā´Ŗāĩ", + "reassign": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•āĩā´•", + "reassigned_assets_to_existing_person": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} {name, select, null {ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ} other {{name}-ā´¨āĩ}} ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•ā´ŋ", + "reassigned_assets_to_new_person": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¨āĩŊā´•ā´ŋ", + "reassing_hint": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•āĩ ā´¨āĩŊā´•āĩā´•", + "recent": "ā´¸ā´Žāĩ€ā´Ēā´•ā´žā´˛ā´‚", + "recent-albums": "ā´¸ā´Žāĩ€ā´Ēā´•ā´žā´˛ ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "recent_searches": "ā´¸ā´Žāĩ€ā´Ēā´•ā´žā´˛ ā´¤ā´ŋⴰⴝⴞāĩā´•āĩž", + "recently_added": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ⴚāĩ‡āĩŧā´¤āĩā´¤ā´¤āĩ", + "recently_added_page_title": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ⴚāĩ‡āĩŧā´¤āĩā´¤ā´¤āĩ", + "recently_taken": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ā´Žā´Ÿāĩā´¤āĩā´¤ā´¤āĩ", + "recently_taken_page_title": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´ŋⴟāĩ† ā´Žā´Ÿāĩā´¤āĩā´¤ā´¤āĩ", + "refresh": "ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•", + "refresh_encoded_videos": "ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•", + "refresh_faces": "ā´Žāĩā´–ā´™āĩā´™āĩž ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•", + "refresh_metadata": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•", + "refresh_thumbnails": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•", + "refreshed": "ā´Ēāĩā´¤āĩā´•āĩā´•ā´ŋ", + "refreshes_every_file": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗā´¤āĩā´‚ ā´Ēāĩā´¤ā´ŋⴝⴤāĩā´Žā´žā´¯ ā´Žā´˛āĩā´˛ā´ž ā´Ģⴝⴞāĩā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ĩā´žā´¯ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "refreshing_encoded_video": "ā´Žāĩģā´•āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "refreshing_faces": "ā´Žāĩā´–ā´™āĩā´™āĩž ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "refreshing_metadata": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "regenerating_thumbnails": "ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋā´˛āĩā´•āĩž ā´Ēāĩā´¨āĩŧā´¨ā´ŋāĩŧā´Žāĩā´Žā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "remote": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ", + "remote_assets": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž", + "remote_media_summary": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿāĩ ā´Žāĩ€ā´Ąā´ŋā´¯ ⴏⴂⴗāĩā´°ā´šā´‚", + "remove": "ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_assets_album_confirmation": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ {count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "remove_assets_shared_link_confirmation": "ⴈ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ {count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "remove_assets_title": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ŗāĩ‹?", + "remove_custom_date_range": "ā´•ā´¸āĩā´ąāĩā´ąā´‚ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ēā´°ā´ŋā´§ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_deleted_assets": "ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋā´¯ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_from_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_from_album_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "remove_from_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_from_lock_folder_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "remove_from_locked_folder": "ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_from_locked_folder_confirmation": "ⴈ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´˛āĩ‹ā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹āĩžā´Ąā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žā´žā´ąāĩā´ąā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ā´…ā´ĩ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´¯ā´ŋāĩŊ ā´Ļāĩƒā´ļāĩā´¯ā´Žā´žā´•āĩā´‚.", + "remove_from_shared_link": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_memory": "ā´“āĩŧā´Žāĩā´Ž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_photo_from_memory": "ⴈ ā´“āĩŧā´Žāĩā´Žā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_tag": "ā´Ÿā´žā´—āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_url": "URL ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "remove_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "removed_api_key": "API ā´•āĩ€ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ: {name}", + "removed_from_archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "removed_from_favorites": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "removed_from_favorites_count": "{count, plural, other {# ā´Žā´Ŗāĩā´Ŗā´‚}} ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "removed_memory": "ā´“āĩŧā´Žāĩā´Ž ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "removed_photo_from_memory": "ā´“āĩŧā´Žāĩā´Žā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "removed_tagged_assets": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ}} ā´Ÿā´žā´—āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "rename": "ā´Ēāĩā´¨āĩŧā´¨ā´žā´Žā´•ā´°ā´Ŗā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "repair": "ā´ąā´ŋā´Ēāĩā´Ēā´¯āĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "repair_no_results_message": "ⴟāĩā´°ā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ā´¤āĩā´‚ ā´•ā´žā´Ŗā´žā´¤ā´žā´¯ā´¤āĩā´Žā´žā´¯ ā´Ģⴝⴞāĩā´•āĩž ā´‡ā´ĩā´ŋⴟāĩ† ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´‚", + "replace_with_upload": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´Žā´žā´ąāĩā´ąā´ŋā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•", + "repository": "ā´ąāĩ†ā´Ēāĩā´Ēāĩ‹ā´¸ā´ŋā´ąāĩā´ąā´ąā´ŋ", + "require_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ", + "require_user_to_change_password_on_first_login": "ā´†ā´Ļāĩā´¯ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ‹ā´Ÿāĩ ā´†ā´ĩā´ļāĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´•", + "rescan": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "reset": "ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "reset_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "reset_people_visibility": "ⴆⴺāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Ļāĩƒā´ļāĩā´¯ā´¤ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "reset_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "reset_pin_code_description": "ā´¨ā´ŋā´™āĩā´™āĩž ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´Žā´ąā´¨āĩā´¨āĩ†ā´™āĩā´•ā´ŋāĩŊ, ā´…ā´¤āĩ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•ā´žāĩģ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´¸āĩ†āĩŧā´ĩāĩŧ ā´…ā´Ąāĩā´Žā´ŋā´¨ā´ŋā´¸āĩā´Ÿāĩā´°āĩ‡ā´ąāĩā´ąā´ąāĩā´Žā´žā´¯ā´ŋ ā´Ŧā´¨āĩā´§ā´Ēāĩā´Ēāĩ†ā´Ÿā´žā´‚", + "reset_pin_code_success": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "reset_pin_code_with_password": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´Žā´Ēāĩā´Ēāĩ‹ā´´āĩā´‚ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´‚", + "reset_sqlite": "SQLite ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "reset_sqlite_confirmation": "SQLite ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹? ā´Ąā´žā´ąāĩā´ą ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩž ā´˛āĩ‹ā´—āĩ ā´”ā´Ÿāĩā´Ÿāĩ ⴚāĩ†ā´¯āĩā´¤āĩ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´˛āĩ‹ā´—ā´ŋāĩģ ⴚāĩ†ā´¯āĩā´¯āĩ‡ā´Ŗāĩā´Ÿā´ŋā´ĩā´°āĩā´‚", + "reset_sqlite_success": "SQLite ā´Ąā´žā´ąāĩā´ąā´žā´Ŧāĩ‡ā´¸āĩ ā´ĩā´ŋā´œā´¯ā´•ā´°ā´Žā´žā´¯ā´ŋ ā´ąāĩ€ā´¸āĩ†ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "reset_to_default": "ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩā´¨ā´ƒā´¸ā´œāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "resolve_duplicates": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´Ēā´°ā´ŋā´šā´°ā´ŋā´•āĩā´•āĩā´•", + "resolved_all_duplicates": "ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´Ēā´°ā´ŋā´šā´°ā´ŋⴚāĩā´šāĩ", + "restore": "ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•", + "restore_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•", + "restore_trash_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ⴟāĩā´°ā´žā´ˇā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ", + "restore_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•", + "restored_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋⴚāĩā´šāĩ", + "resume": "ā´Ēāĩā´¨ā´°ā´žā´°ā´‚ā´­ā´ŋā´•āĩā´•āĩā´•", + "resume_paused_jobs": "{count, plural, one {ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤ā´ŋā´¯ # ⴜāĩ‹ā´˛ā´ŋ} other {ā´¤ā´žāĩŊā´•āĩā´•ā´žā´˛ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¨ā´ŋāĩŧā´¤āĩā´¤ā´ŋā´¯ # ⴜāĩ‹ā´˛ā´ŋā´•āĩž}} ā´Ēāĩā´¨ā´°ā´žā´°ā´‚ā´­ā´ŋā´•āĩā´•āĩā´•", + "retry_upload": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩā´‚ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•", + "review_duplicates": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´…ā´ĩā´˛āĩ‹ā´•ⴍⴂ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "review_large_files": "ā´ĩā´˛ā´ŋā´¯ ā´Ģⴝⴞāĩā´•āĩž ā´…ā´ĩā´˛āĩ‹ā´•ⴍⴂ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "role": "ā´ąāĩ‹āĩž", + "role_editor": "ā´Žā´Ąā´ŋā´ąāĩā´ąāĩŧ", + "role_viewer": "ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¯ā´žāĩž", + "running": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "save": "ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "save_to_gallery": "ā´—ā´žā´˛ā´ąā´ŋā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "saved_api_key": "API ā´•āĩ€ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "saved_profile": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "saved_settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "say_something": "ā´Žā´¨āĩā´¤āĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ā´Ēā´ąā´¯āĩ‚", + "scaffold_body_error_occurred": "ā´Ēā´ŋā´ļā´•āĩ ⴏⴂⴭā´ĩā´ŋⴚāĩā´šāĩ", + "scan_all_libraries": "ā´Žā´˛āĩā´˛ā´ž ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋā´•ā´ŗāĩā´‚ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "scan_library": "ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "scan_settings": "ā´¸āĩā´•ā´žāĩģ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "scanning_for_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´¸āĩā´•ā´žāĩģ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ...", + "search": "ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_albums": "ā´†āĩŊā´Ŧā´™āĩā´™āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_by_context": "ⴏⴍāĩā´Ļāĩŧā´­ā´‚ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_by_description": "ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_by_description_example": "ā´¸ā´Ēāĩā´Ēā´¯ā´ŋā´˛āĩ† ā´šāĩˆā´•āĩā´•ā´ŋā´‚ā´—āĩ ā´Ļā´ŋⴍⴂ", + "search_by_filename": "ā´Ģā´¯āĩŊ ā´¨ā´žā´Žā´‚ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´•āĩā´¸āĩā´ąāĩā´ąāĩģā´ˇāĩģ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_by_filename_example": "ā´‰ā´Ļā´ž. IMG_1234.JPG ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ PNG", + "search_camera_make": "ā´•āĩā´¯ā´žā´Žā´ą ā´¨ā´ŋāĩŧā´Žāĩā´Žā´žā´¤ā´žā´ĩā´ŋā´¨ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_camera_model": "ā´•āĩā´¯ā´žā´Žā´ą ā´Žāĩ‹ā´Ąā´˛ā´ŋā´¨ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_city": "ⴍⴗⴰⴤāĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_country": "ā´°ā´žā´œāĩā´¯ā´¤āĩā´¤ā´ŋā´¨ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_filter_apply": "ā´Ģā´ŋāĩŊⴟāĩā´Ÿāĩŧ ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "search_filter_camera_title": "ā´•āĩā´¯ā´žā´Žā´ą ⴤⴰⴂ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "search_filter_date": "ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "search_filter_date_interval": "{start} ā´Žāĩā´¤āĩŊ {end} ā´ĩā´°āĩ†", + "search_filter_date_title": "ā´’ā´°āĩ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ēā´°ā´ŋā´§ā´ŋ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "search_filter_display_option_not_in_album": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ⴇⴞāĩā´˛", + "search_filter_display_options": "ā´Ēāĩā´°ā´Ļāĩŧā´ļā´¨ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "search_filter_filename": "ā´Ģā´¯āĩŊ ā´¨ā´žā´Žā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_filter_location": "ā´¸āĩā´Ĩā´žā´¨ā´‚", + "search_filter_location_title": "ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "search_filter_media_type": "ā´Žāĩ€ā´Ąā´ŋā´¯ ⴤⴰⴂ", + "search_filter_media_type_title": "ā´Žāĩ€ā´Ąā´ŋā´¯ ⴤⴰⴂ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "search_filter_people_title": "ⴆⴺāĩā´•ā´ŗāĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "search_for": "ⴇⴤā´ŋā´¨ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_for_existing_person": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩā´ŗāĩā´ŗ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_no_more_result": "ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´Ģⴞⴙāĩā´™ā´ŗā´ŋā´˛āĩā´˛", + "search_no_people": "ⴆⴺāĩā´•ā´ŗā´ŋā´˛āĩā´˛", + "search_no_people_named": "\"{name}\" ā´Žā´¨āĩā´¨āĩ ā´Ēāĩ‡ā´°āĩā´ŗāĩā´ŗ ⴆⴺāĩā´•ā´ŗā´ŋā´˛āĩā´˛", + "search_no_result": "ā´Ģⴞⴙāĩā´™ā´ŗāĩŠā´¨āĩā´¨āĩā´‚ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´ŋā´¯ā´ŋā´˛āĩā´˛, ā´Žā´ąāĩā´ąāĩŠā´°āĩ ā´¤ā´ŋā´°ā´¯āĩŊ ā´Ēā´Ļā´Žāĩ‹ ⴏⴂⴝāĩ‹ā´œā´¨ā´Žāĩ‹ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´ļāĩā´°ā´Žā´ŋā´•āĩā´•āĩā´•", + "search_options": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "search_page_categories": "ā´ĩā´ŋā´­ā´žā´—ā´™āĩā´™āĩž", + "search_page_motion_photos": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž", + "search_page_no_objects": "ā´ĩā´¸āĩā´¤āĩā´•āĩā´•ā´ŗāĩā´Ÿāĩ† ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "search_page_no_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™ā´ŗāĩā´Ÿāĩ† ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛", + "search_page_screenshots": "ā´¸āĩā´•āĩā´°āĩ€āĩģā´ˇāĩ‹ā´Ÿāĩā´Ÿāĩā´•āĩž", + "search_page_search_photos_videos": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩžā´•āĩā´•āĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_page_selfies": "ā´¸āĩ†āĩŊā´Ģā´ŋā´•āĩž", + "search_page_things": "ā´ĩā´¸āĩā´¤āĩā´•āĩā´•āĩž", + "search_page_view_all_button": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´•ā´žā´Ŗāĩā´•", + "search_page_your_activity": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚", + "search_page_your_map": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´žā´Ēāĩā´Ēāĩ", + "search_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_places": "ā´¸āĩā´Ĩⴞⴙāĩā´™āĩž ā´¤ā´ŋā´°ā´¯āĩā´•", + "search_rating": "ā´ąāĩ‡ā´ąāĩā´ąā´ŋā´‚ā´—āĩ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_result_page_new_search_hint": "ā´Ēāĩā´¤ā´ŋā´¯ ā´¤ā´ŋā´°ā´¯āĩŊ", + "search_settings": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "search_state": "ⴏⴂⴏāĩā´Ĩā´žā´¨ā´‚ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_suggestion_list_smart_search_hint_1": "ā´¸āĩā´Žā´žāĩŧⴟāĩā´Ÿāĩ ā´¸āĩ†āĩŧⴚāĩā´šāĩ ā´Ąā´ŋā´Ģāĩ‹āĩžā´Ÿāĩā´Ÿā´žā´¯ā´ŋ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ, ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ąā´¯āĩā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯ā´žāĩģ ⴈ ā´ĩā´žā´•āĩā´¯ā´˜ā´Ÿā´¨ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´• ", + "search_suggestion_list_smart_search_hint_2": "m:ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ†-ā´¤ā´ŋā´°ā´¯āĩŊ-ā´Ēā´Ļā´‚", + "search_tags": "ā´Ÿā´žā´—āĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_timezone": "ā´¸ā´Žā´¯ā´Žāĩ‡ā´–ⴞⴝāĩā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´•...", + "search_type": "ā´¤ā´ŋā´°ā´¯āĩŊ ⴤⴰⴂ", + "search_your_photos": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´¤ā´ŋā´°ā´¯āĩā´•", + "searching_locales": "ā´˛āĩŠā´•āĩā´•āĩ‡ā´˛āĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ ā´¤ā´ŋā´°ā´¯āĩā´¨āĩā´¨āĩ...", + "second": "ā´¸āĩ†ā´•āĩā´•ā´¨āĩā´ąāĩ", + "see_all_people": "ā´Žā´˛āĩā´˛ā´ž ⴆⴺāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´•ā´žā´Ŗāĩā´•", + "select": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_album_cover": "ā´†āĩŊā´Ŧā´‚ ā´•ā´ĩāĩŧ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_all_duplicates": "ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_all_in": "{group}-ā´˛āĩ† ā´Žā´˛āĩā´˛ā´žā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_avatar_color": "ā´…ā´ĩā´¤ā´žāĩŧ ā´¨ā´ŋā´ąā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_face": "ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_featured_photo": "ā´Ģāĩ€ā´šāĩā´šāĩŧ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_from_computer": "ā´•ā´Žāĩā´Ēāĩā´¯āĩ‚ā´Ÿāĩā´Ÿā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_keep_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•ā´žāĩģ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_library_owner": "ā´˛āĩˆā´Ŧāĩā´°ā´ąā´ŋ ā´‰ā´Ÿā´Žā´¯āĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_new_face": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Žāĩā´–ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_person_to_tag": "ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´’ā´°āĩ ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_trash_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "select_user_for_sharing_page_err_album": "ā´†āĩŊā´Ŧā´‚ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´°ā´žā´œā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "selected": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤āĩ", + "selected_count": "{count, plural, other {# ā´Žā´Ŗāĩā´Ŗā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤āĩ}}", + "selected_gps_coordinates": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ GPS ā´•āĩ‹āĩŧā´Ąā´ŋā´¨āĩ‡ā´ąāĩā´ąāĩā´•āĩž", + "send_message": "ⴏⴍāĩā´Ļāĩ‡ā´ļā´‚ ⴅⴝⴕāĩā´•āĩā´•", + "send_welcome_email": "ā´¸āĩā´ĩā´žā´—ā´¤ ā´‡ā´Žāĩ†ā´¯ā´ŋāĩŊ ⴅⴝⴕāĩā´•āĩā´•", + "server_endpoint": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Žāĩģā´Ąāĩâ€Œā´Ēāĩ‹ā´¯ā´ŋā´¨āĩā´ąāĩ", + "server_info_box_app_version": "ā´†ā´Ēāĩā´Ēāĩ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ", + "server_info_box_server_url": "ā´¸āĩ†āĩŧā´ĩāĩŧ URL", + "server_offline": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´“ā´Ģāĩâ€Œā´˛āĩˆāĩģ", + "server_online": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´“āĩēā´˛āĩˆāĩģ", + "server_privacy": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´¸āĩā´ĩā´•ā´žā´°āĩā´¯ā´¤", + "server_stats": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´¸āĩā´Ĩā´ŋā´¤ā´ŋā´ĩā´ŋā´ĩā´°ā´•āĩā´•ā´Ŗā´•āĩā´•āĩā´•āĩž", + "server_version": "ā´¸āĩ†āĩŧā´ĩāĩŧ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ", + "set": "ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_as_album_cover": "ā´†āĩŊā´Ŧā´‚ ā´•ā´ĩā´ąā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_as_featured_photo": "ā´Ģāĩ€ā´šāĩā´šāĩŧ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´¯ā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_as_profile_picture": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_date_of_birth": "ⴜⴍⴍⴤāĩā´¤āĩ€ā´¯ā´¤ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_profile_picture": "ā´Ēāĩā´°āĩŠā´ĢāĩˆāĩŊ ⴚā´ŋā´¤āĩā´°ā´‚ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_slideshow_to_fullscreen": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹ ā´Ģāĩāĩžā´¸āĩā´•āĩā´°āĩ€ā´¨ā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "set_stack_primary_asset": "ā´Ēāĩā´°ā´§ā´žā´¨ ā´…ā´¸ā´ąāĩā´ąā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "setting_image_viewer_help": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļ ā´ĩāĩā´¯āĩ‚ā´ĩāĩŧ ā´†ā´Ļāĩā´¯ā´‚ ⴚāĩ†ā´ąā´ŋā´¯ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ, ā´¤āĩā´Ÿāĩŧā´¨āĩā´¨āĩ ā´‡ā´Ÿā´¤āĩā´¤ā´°ā´‚ ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ (ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ), ā´’ā´Ÿāĩā´ĩā´ŋāĩŊ ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ⴚā´ŋā´¤āĩā´°ā´‚ (ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•ā´ŋā´¯ā´ŋⴟāĩā´Ÿāĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ) ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ.", + "setting_image_viewer_original_subtitle": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´Ēāĩ‚āĩŧā´Ŗāĩā´Ŗ-ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ā´ˇāĩģ ⴚā´ŋā´¤āĩā´°ā´‚ (ā´ĩā´˛āĩā´¤āĩ!) ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•. ā´Ąā´žā´ąāĩā´ą ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚ (ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩā´‚ ā´‰ā´Ēā´•ā´°ā´Ŗ ā´•ā´žā´ˇāĩ†ā´¯āĩā´‚) ā´•āĩā´ąā´¯āĩā´•āĩā´•ā´žāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•.", + "setting_image_viewer_original_title": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ⴚā´ŋā´¤āĩā´°ā´‚ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "setting_image_viewer_preview_subtitle": "ā´‡ā´Ÿā´¤āĩā´¤ā´°ā´‚ ā´ąāĩ†ā´¸ā´˛āĩā´¯āĩ‚ⴎⴍāĩā´ŗāĩā´ŗ ⴚā´ŋā´¤āĩā´°ā´‚ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯ā´žāĩģ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•. ā´’ā´¨āĩā´¨āĩā´•ā´ŋāĩŊ ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ⴚā´ŋā´¤āĩā´°ā´‚ ā´¨āĩ‡ā´°ā´ŋⴟāĩā´Ÿāĩ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ‹ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´Žā´žā´¤āĩā´°ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ‹ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•āĩā´•.", + "setting_image_viewer_preview_title": "ā´Ēāĩā´°ā´ŋā´ĩāĩā´¯āĩ‚ ⴚā´ŋā´¤āĩā´°ā´‚ ā´˛āĩ‹ā´Ąāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "setting_image_viewer_title": "ⴚā´ŋā´¤āĩā´°ā´™āĩā´™āĩž", + "setting_languages_apply": "ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "setting_languages_subtitle": "ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´­ā´žā´ˇ ā´Žā´žā´ąāĩā´ąāĩā´•", + "setting_notifications_notify_failures_grace_period": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´Ēā´°ā´žā´œā´¯ā´™āĩā´™āĩž ā´…ā´ąā´ŋā´¯ā´ŋā´•āĩā´•āĩā´•: {duration}", + "setting_notifications_notify_hours": "{count} ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚āĩŧ", + "setting_notifications_notify_immediately": "ā´‰ā´Ÿā´¨ā´Ÿā´ŋ", + "setting_notifications_notify_minutes": "{count} ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩ", + "setting_notifications_notify_never": "ā´’ā´°ā´ŋā´•āĩā´•ā´˛āĩā´‚", + "setting_notifications_notify_seconds": "{count} ā´¸āĩ†ā´•āĩā´•āĩģā´Ąāĩ", + "setting_notifications_single_progress_subtitle": "ā´“ā´°āĩ‹ ā´…ā´¸ā´ąāĩā´ąā´ŋā´¨āĩā´ąāĩ†ā´¯āĩā´‚ ā´ĩā´ŋā´ļā´Ļā´Žā´žā´¯ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋ ā´ĩā´ŋā´ĩā´°ā´‚", + "setting_notifications_single_progress_title": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´ĩā´ŋā´ļā´Ļā´Žā´žā´¯ ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "setting_notifications_subtitle": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´…ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ ā´Žāĩāĩģⴗ⴪ⴍⴕāĩž ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "setting_notifications_total_progress_subtitle": "ā´ŽāĩŠā´¤āĩā´¤ā´¤āĩā´¤ā´ŋā´˛āĩā´ŗāĩā´ŗ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋ (ⴚāĩ†ā´¯āĩā´¤ā´¤āĩ/ⴆⴕāĩ† ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž)", + "setting_notifications_total_progress_title": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´ŽāĩŠā´¤āĩā´¤ā´‚ ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "setting_video_viewer_looping_title": "ā´˛āĩ‚ā´Ēāĩā´Ēā´ŋā´‚ā´—āĩ", + "setting_video_viewer_original_video_subtitle": "ā´¸āĩ†āĩŧā´ĩā´ąā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´’ā´°āĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´¸āĩā´Ÿāĩā´°āĩ€ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž, ā´’ā´°āĩ ⴟāĩā´°ā´žāĩģā´¸āĩâ€Œā´•āĩ‹ā´Ąāĩ ⴞⴭāĩā´¯ā´Žā´žā´Ŗāĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•. ⴇⴤāĩ ā´Ŧā´Ģā´ąā´ŋā´‚ā´—ā´ŋā´¨āĩ ā´•ā´žā´°ā´Ŗā´Žā´žā´¯āĩ‡ā´•āĩā´•ā´žā´‚. ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴞⴭāĩā´¯ā´Žā´žā´¯ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž ⴈ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ ā´Ēā´°ā´ŋā´—ā´Ŗā´ŋā´•āĩā´•ā´žā´¤āĩ† ⴤⴍāĩā´¨āĩ† ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Žā´¯ā´ŋāĩŊ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´‚.", + "setting_video_viewer_original_video_title": "ā´¯ā´Ĩā´žāĩŧā´¤āĩā´Ĩ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´¨ā´ŋāĩŧā´Ŧā´¨āĩā´§ā´Žā´žā´•āĩā´•āĩā´•", + "settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "settings_require_restart": "ⴈ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•ā´žāĩģ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ Immich ā´Ēāĩā´¨ā´°ā´žā´°ā´‚ā´­ā´ŋā´•āĩā´•āĩā´•", + "settings_saved": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "setup_pin_code": "ā´’ā´°āĩ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "share": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "share_action_prompt": "{count} ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ", + "share_add_photos": "ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ⴚāĩ‡āĩŧā´•āĩā´•āĩā´•", + "share_assets_selected": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤āĩ", + "share_dialog_preparing": "ⴤⴝāĩā´¯ā´žā´ąā´žā´•āĩā´•āĩā´¨āĩā´¨āĩ...", + "share_link": "ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "shared": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤āĩ", + "shared_album_activities_input_disable": "ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´‚ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋ", + "shared_album_activity_remove_content": "ⴈ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗāĩ‹?", + "shared_album_activity_remove_title": "ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "shared_album_section_people_action_error": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ĩā´ŋⴟāĩā´Ÿāĩā´Ēāĩ‹ā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ‹/ā´¨āĩ€ā´•āĩā´•ā´‚ā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ‹ ā´Ēā´ŋā´ļā´•āĩ", + "shared_album_section_people_action_leave": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "shared_album_section_people_action_remove_user": "ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "shared_album_section_people_title": "ⴆⴺāĩā´•āĩž", + "shared_by": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤āĩ", + "shared_by_user": "{user} ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ", + "shared_by_you": "ā´¨ā´ŋā´™āĩā´™āĩž ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤āĩ", + "shared_from_partner": "{partner}-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´ŗāĩā´ŗ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž", + "shared_intent_upload_button_progress_text": "{current}/{total} ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "shared_link_app_bar_title": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž", + "shared_link_clipboard_copied_massage": "ā´•āĩā´˛ā´ŋā´Ēāĩā´Ēāĩā´Ŧāĩ‹āĩŧā´Ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēā´•āĩŧā´¤āĩā´¤ā´ŋ", + "shared_link_clipboard_text": "ā´˛ā´ŋā´™āĩā´•āĩ: {link}\nā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ: {password}", + "shared_link_create_error": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋāĩŊ ā´Ēā´ŋā´ļā´•āĩ", + "shared_link_custom_url_description": "ā´’ā´°āĩ ā´•ā´¸āĩā´ąāĩā´ąā´‚ URL ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ⴈ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "shared_link_edit_description_hint": "ā´Ēā´™āĩā´•ā´ŋⴟⴞā´ŋā´¨āĩā´ąāĩ† ā´ĩā´ŋā´ĩā´°ā´Ŗā´‚ ā´¨āĩŊā´•āĩā´•", + "shared_link_edit_expire_after_option_day": "1 ā´Ļā´ŋā´ĩⴏⴂ", + "shared_link_edit_expire_after_option_days": "{count} ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩž", + "shared_link_edit_expire_after_option_hour": "1 ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚āĩŧ", + "shared_link_edit_expire_after_option_hours": "{count} ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąāĩā´•āĩž", + "shared_link_edit_expire_after_option_minute": "1 ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩ", + "shared_link_edit_expire_after_option_minutes": "{count} ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩā´•āĩž", + "shared_link_edit_expire_after_option_months": "{count} ā´Žā´žā´¸ā´™āĩā´™āĩž", + "shared_link_edit_expire_after_option_year": "{count} ā´ĩāĩŧⴎⴂ", + "shared_link_edit_password_hint": "ā´Ēā´™āĩā´•ā´ŋⴟⴞā´ŋā´¨āĩā´ąāĩ† ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•", + "shared_link_edit_submit_button": "ā´˛ā´ŋā´™āĩā´•āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "shared_link_error_server_url_fetch": "ā´¸āĩ†āĩŧā´ĩāĩŧ url ⴞⴭāĩā´¯ā´Žā´žā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "shared_link_expires_day": "{count} ā´Ļā´ŋā´ĩⴏⴤāĩā´¤ā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_days": "{count} ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩžā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_hour": "{count} ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_hours": "{count} ā´Žā´Ŗā´ŋā´•āĩā´•āĩ‚ā´ąāĩā´•āĩžā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_minute": "{count} ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_minutes": "{count} ā´Žā´ŋā´¨ā´ŋā´ąāĩā´ąāĩā´•āĩžā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_never": "ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿā´ŋā´˛āĩā´˛ ∞", + "shared_link_expires_second": "{count} ā´¸āĩ†ā´•āĩā´•āĩģā´Ąā´ŋā´¨āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_expires_seconds": "{count} ā´¸āĩ†ā´•āĩā´•āĩģā´Ąāĩā´•āĩžā´•āĩā´•āĩā´ŗāĩā´ŗā´ŋāĩŊ ā´•ā´žā´˛ā´šā´°ā´Ŗā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "shared_link_individual_shared": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´—ā´¤ā´Žā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤āĩ", + "shared_link_info_chip_metadata": "EXIF", + "shared_link_manage_links": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "shared_link_options": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž", + "shared_link_password_description": "ⴈ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩ ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´’ā´°āĩ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´†ā´ĩā´ļāĩā´¯ā´Žā´žā´Ŗāĩ", + "shared_links": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž", + "shared_links_description": "ā´’ā´°āĩ ā´˛ā´ŋā´™āĩā´•āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "shared_photos_and_videos_count": "{assetCount, plural, other {ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ # ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚.}}", + "shared_with_me": "ā´Žā´¨ā´ŋā´•āĩā´•ā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿā´¤āĩ", + "shared_with_partner": "{partner}-ā´Žā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿāĩ", + "sharing": "ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ", + "sharing_enter_password": "ⴈ ā´Ēāĩ‡ā´œāĩ ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´¨āĩŊā´•āĩā´•.", + "sharing_page_album": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´™āĩā´™āĩž", + "sharing_page_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•ā´ŋā´˛āĩ† ⴆⴺāĩā´•ā´ŗāĩā´Žā´žā´¯ā´ŋ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ēā´™āĩā´•ā´ŋā´Ÿā´žāĩģ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´™āĩā´™āĩž ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•.", + "sharing_page_empty_list": "ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´¯ ā´˛ā´ŋā´¸āĩā´ąāĩā´ąāĩ", + "sharing_sidebar_description": "ā´¸āĩˆā´Ąāĩâ€Œā´Ŧā´žā´ąā´ŋāĩŊ 'ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ' ā´Žā´¨āĩā´¨ā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´’ā´°āĩ ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "sharing_silver_appbar_create_shared_album": "ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´†āĩŊā´Ŧā´‚", + "sharing_silver_appbar_share_partner": "ā´Ēā´™āĩā´•ā´žā´ŗā´ŋā´¯āĩā´Žā´žā´¯ā´ŋ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´•", + "shift_to_permanent_delete": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´žāĩģ ⇧ ā´…ā´Žāĩŧā´¤āĩā´¤āĩā´•", + "show_album_options": "ā´†āĩŊā´Ŧā´‚ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_albums": "ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_all_people": "ā´Žā´˛āĩā´˛ā´ž ⴆⴺāĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_and_hide_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•ā´¯āĩā´‚ ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•ā´¯āĩā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "show_file_location": "ā´Ģⴝⴞā´ŋā´¨āĩā´ąāĩ† ā´¸āĩā´Ĩā´žā´¨ā´‚ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_gallery": "ā´—ā´žā´˛ā´ąā´ŋ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_hidden_people": "ā´Žā´ąā´šāĩā´š ⴆⴺāĩā´•ā´ŗāĩ† ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_in_timeline": "ⴟāĩˆā´‚ā´˛āĩˆā´¨ā´ŋāĩŊ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_in_timeline_setting_description": "ⴈ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩā´ŗāĩā´ŗ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ⴟāĩˆā´‚ā´˛āĩˆā´¨ā´ŋāĩŊ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_keyboard_shortcuts": "ā´•āĩ€ā´Ŧāĩ‹āĩŧā´Ąāĩ ā´•āĩā´ąāĩā´•āĩā´•āĩā´ĩā´´ā´ŋā´•āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_metadata": "ā´Žāĩ†ā´ąāĩā´ąā´žā´Ąā´žā´ąāĩā´ą ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_or_hide_info": "ā´ĩā´ŋā´ĩā´°ā´™āĩā´™āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´• ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Žā´ąā´¯āĩā´•āĩā´•āĩā´•", + "show_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_person_options": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩā´Ÿāĩ† ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_progress_bar": "ā´Ēāĩā´°āĩ‹ā´—ā´¤ā´ŋ ā´Ŧā´žāĩŧ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_search_options": "ā´¤ā´ŋā´°ā´¯āĩŊ ā´“ā´Ēāĩā´ˇā´¨āĩā´•āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_shared_links": "ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_slideshow_transition": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹ ⴏⴂⴕāĩā´°ā´Žā´Ŗā´‚ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_supporter_badge": "ā´¸ā´šā´žā´¯ā´ŋā´¯āĩā´Ÿāĩ† ā´Ŧā´žā´Ąāĩā´œāĩ", + "show_supporter_badge_description": "ā´’ā´°āĩ ā´¸ā´šā´žā´¯ā´ŋā´¯āĩā´Ÿāĩ† ā´Ŧā´žā´Ąāĩā´œāĩ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "show_text_search_menu": "ⴟāĩ†ā´•āĩā´¸āĩā´ąāĩā´ąāĩ ā´¤ā´ŋā´°ā´¯āĩŊ ā´Žāĩ†ā´¨āĩ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "shuffle": "ā´ˇā´Ģā´ŋāĩž ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sidebar": "ā´¸āĩˆā´Ąāĩâ€Œā´Ŧā´žāĩŧ", + "sidebar_display_description": "ā´¸āĩˆā´Ąāĩâ€Œā´Ŧā´žā´ąā´ŋā´˛āĩ† ā´•ā´žā´´āĩâ€Œā´šā´¯ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´’ā´°āĩ ā´˛ā´ŋā´™āĩā´•āĩ ā´Ēāĩā´°ā´Ļāĩŧā´ļā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "sign_out": "ā´¸āĩˆāĩģ ā´”ā´Ÿāĩā´Ÿāĩ", + "sign_up": "ā´¸āĩˆāĩģ ā´…ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "size": "ā´ĩā´˛ā´ŋā´Ēāĩā´Ēā´‚", + "skip_to_content": "ⴉⴺāĩā´ŗā´Ÿā´•āĩā´•ā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "skip_to_folders": "ā´Ģāĩ‹āĩžā´Ąā´ąāĩā´•ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "skip_to_tags": "ā´Ÿā´žā´—āĩā´•ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "slideshow": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹", + "slideshow_settings": "ā´¸āĩā´˛āĩˆā´Ąāĩâ€Œā´ˇāĩ‹ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž", + "sort_albums_by": "ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗāĩ† ⴇⴤⴍāĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•...", + "sort_created": "ā´¸āĩƒā´ˇāĩā´Ÿā´ŋⴚāĩā´š ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "sort_items": "ⴇⴍⴙāĩā´™ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚", + "sort_modified": "ā´Žā´žā´ąāĩā´ąā´‚ ā´ĩā´°āĩā´¤āĩā´¤ā´ŋā´¯ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "sort_newest": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹", + "sort_oldest": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēā´´ā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹", + "sort_people_by_similarity": "ā´¸ā´žā´Žāĩā´¯ā´‚ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ⴆⴺāĩā´•ā´ŗāĩ† ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "sort_recent": "ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹", + "sort_title": "ā´ļāĩ€āĩŧⴎⴕⴂ", + "source": "ā´‰ā´ąā´ĩā´ŋā´Ÿā´‚", + "stack": "ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ", + "stack_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "stack_duplicates": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "stack_select_one_photo": "ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•ā´ŋā´¨ā´žā´¯ā´ŋ ā´’ā´°āĩ ā´Ēāĩā´°ā´§ā´žā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "stack_selected_photos": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "stacked_assets_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "stacktrace": "ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩā´Ÿāĩā´°āĩ‡ā´¸āĩ", + "start": "ⴆⴰⴂⴭā´ŋā´•āĩā´•āĩā´•", + "start_date": "ⴆⴰⴂⴭ ā´¤āĩ€ā´¯ā´¤ā´ŋ", + "start_date_before_end_date": "ⴆⴰⴂⴭ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´…ā´ĩā´¸ā´žā´¨ ā´¤āĩ€ā´¯ā´¤ā´ŋā´•āĩā´•āĩ ā´Žāĩā´Žāĩā´Ēā´žā´¯ā´ŋā´°ā´ŋā´•āĩā´•ā´Ŗā´‚", + "state": "ⴏⴂⴏāĩā´Ĩā´žā´¨ā´‚", + "status": "ā´¨ā´ŋā´˛", + "stop_casting": "ā´•ā´žā´¸āĩā´ąāĩā´ąā´ŋā´‚ā´—āĩ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "stop_motion_photo": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "stop_photo_sharing": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Ēā´™āĩā´•ā´ŋⴟāĩā´¨āĩā´¨ā´¤āĩ ā´¨ā´ŋāĩŧā´¤āĩā´¤ā´Ŗāĩ‹?", + "stop_photo_sharing_description": "{partner}-ā´•āĩā´•āĩ ⴇⴍā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ⴆⴕāĩâ€Œā´¸ā´¸āĩ ⴚāĩ†ā´¯āĩā´¯ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛.", + "stop_sharing_photos_with_user": "ⴈ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩāĩā´Žā´žā´¯ā´ŋ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´Ēā´™āĩā´•ā´ŋⴟāĩā´¨āĩā´¨ā´¤āĩ ā´¨ā´ŋāĩŧā´¤āĩā´¤āĩā´•", + "storage": "ⴏⴂⴭⴰ⴪ ā´¸āĩā´Ĩⴞⴂ", + "storage_label": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´˛āĩ‡ā´ŦāĩŊ", + "storage_quota": "ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´•āĩā´ĩā´žā´Ÿāĩā´Ÿ", + "storage_usage": "{available}-āĩŊ {used} ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ", + "submit": "ā´¸ā´Žāĩŧā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•", + "success": "ā´ĩā´ŋā´œā´¯ā´‚", + "suggestions": "ā´¨ā´ŋāĩŧā´Ļāĩā´Ļāĩ‡ā´ļā´™āĩā´™āĩž", + "sunrise_on_the_beach": "ā´Ŧāĩ€ā´šāĩā´šā´ŋā´˛āĩ† ā´¸āĩ‚ā´°āĩā´¯āĩ‹ā´Ļⴝⴂ", + "support": "ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗ", + "support_and_feedback": "ā´Ēā´ŋā´¨āĩā´¤āĩā´Ŗā´¯āĩā´‚ ā´…ā´­ā´ŋā´Ēāĩā´°ā´žā´¯ā´ĩāĩā´‚", + "support_third_party_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† Immich ā´‡āĩģā´¸āĩā´ąāĩā´ąā´žā´ŗāĩ‡ā´ˇāĩģ ā´’ā´°āĩ ā´Žāĩ‚ā´¨āĩā´¨ā´žā´‚ ā´•ā´•āĩā´ˇā´ŋ ā´Ēā´žā´•āĩā´•āĩ‡ā´œāĩ ⴚāĩ†ā´¯āĩā´¤ā´¤ā´žā´Ŗāĩ. ā´¨ā´ŋā´™āĩā´™āĩž ā´…ā´¨āĩā´­ā´ĩā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´Ēāĩā´°ā´ļāĩā´¨ā´™āĩā´™āĩž ā´† ā´Ēā´žā´•āĩā´•āĩ‡ā´œāĩ ā´•ā´žā´°ā´Ŗā´Žā´žā´•ā´žā´‚, ā´…ā´¤ā´ŋā´¨ā´žāĩŊ ā´¤ā´žā´´āĩ†ā´¯āĩā´ŗāĩā´ŗ ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´†ā´Ļāĩā´¯ā´‚ ā´…ā´ĩā´°āĩā´Žā´žā´¯ā´ŋ ā´Ēāĩā´°ā´ļāĩā´¨ā´™āĩā´™āĩž ⴉⴍāĩā´¨ā´¯ā´ŋā´•āĩā´•āĩā´•.", + "swap_merge_direction": "ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩŊ ā´Ļā´ŋā´ļ ā´Žā´žā´ąāĩā´ąāĩā´•", + "sync": "ā´¸ā´ŋā´™āĩā´•āĩ", + "sync_albums": "ā´†āĩŊā´Ŧā´™āĩā´™āĩž ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sync_albums_manual_subtitle": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Žā´˛āĩā´˛ā´ž ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sync_local": "ā´Ēāĩā´°ā´žā´Ļāĩ‡ā´ļā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sync_remote": "ā´ąā´ŋā´Žāĩ‹ā´Ÿāĩā´Ÿā´žā´¯ā´ŋ ā´¸ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sync_status": "ā´¸ā´ŋā´™āĩā´•āĩ ā´¨ā´ŋā´˛", + "sync_status_subtitle": "ā´¸ā´ŋā´™āĩā´•āĩ ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´‚ ā´•ā´žā´Ŗāĩā´•, ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "sync_upload_album_setting_subtitle": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ Immich-ā´˛āĩ† ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´†āĩŊā´Ŧā´™āĩā´™ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´ŋ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "tag": "ā´Ÿā´žā´—āĩ", + "tag_assets": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "tag_created": "{tag} ā´Žā´¨āĩā´¨ ā´Ÿā´žā´—āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•ā´ŋ", + "tag_feature_description": "ā´¯āĩā´•āĩā´¤ā´ŋā´¸ā´šā´Žā´žā´¯ ā´Ÿā´žā´—āĩ ā´ĩā´ŋⴎⴝⴙāĩā´™āĩž ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´—āĩā´°āĩ‚ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´Ŧāĩā´°āĩ—ā´¸āĩā´šāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "tag_not_found_question": "ā´’ā´°āĩ ā´Ÿā´žā´—āĩ ā´•ā´Ŗāĩā´Ÿāĩ†ā´¤āĩā´¤ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´¨āĩā´¨ā´ŋā´˛āĩā´˛āĩ‡? ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ÿā´žā´—āĩ ⴉ⴪āĩā´Ÿā´žā´•āĩā´•āĩā´•.", + "tag_people": "ⴆⴺāĩā´•ā´ŗāĩ† ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "tag_updated": "{tag} ā´Žā´¨āĩā´¨ ā´Ÿā´žā´—āĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "tagged_assets": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "tags": "ā´Ÿā´žā´—āĩā´•āĩž", + "tap_to_run_job": "ⴜāĩ‹ā´˛ā´ŋ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•ā´žāĩģ ā´Ÿā´žā´Ēāĩā´Ēāĩā´šāĩ†ā´¯āĩā´¯āĩā´•", + "template": "ⴟāĩ†ā´‚ā´Ēāĩā´˛āĩ‡ā´ąāĩā´ąāĩ", + "theme": "ā´¤āĩ€ā´‚", + "theme_selection": "ā´¤āĩ€ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩŊ", + "theme_selection_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ŧāĩā´°āĩ—ā´¸ā´ąā´ŋā´¨āĩā´ąāĩ† ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´‚ ā´Žāĩāĩģā´—ā´Ŗā´¨ ā´…ā´¨āĩā´¸ā´°ā´ŋⴚāĩā´šāĩ ā´¤āĩ€ā´‚ ā´˛āĩˆā´ąāĩā´ąāĩ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´Ąā´žāĩŧā´•āĩā´•āĩ ⴆⴝā´ŋ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•āĩā´•", + "theme_setting_asset_list_storage_indicator_title": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩˆā´˛āĩā´•ā´ŗā´ŋāĩŊ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ ā´‡āĩģā´Ąā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩŧ ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´•", + "theme_setting_asset_list_tiles_per_row_title": "ā´“ā´°āĩ‹ ā´ĩā´°ā´ŋā´¯ā´ŋā´˛āĩā´‚ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´Žā´Ŗāĩā´Ŗā´‚ ({count})", + "theme_setting_colorful_interface_subtitle": "ā´Ēā´ļāĩā´šā´žā´¤āĩā´¤ā´˛ ā´Ēāĩā´°ā´¤ā´˛ā´™āĩā´™ā´ŗā´ŋāĩŊ ā´Ēāĩā´°ā´§ā´žā´¨ ā´¨ā´ŋā´ąā´‚ ā´Ēāĩā´°ā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•.", + "theme_setting_colorful_interface_title": "ā´ĩāĩŧā´Ŗāĩā´Ŗā´žā´­ā´Žā´žā´¯ ⴇⴍāĩā´ąāĩŧā´Ģāĩ‡ā´¸āĩ", + "theme_setting_image_viewer_quality_subtitle": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļ ā´‡ā´Žāĩ‡ā´œāĩ ā´ĩāĩā´¯āĩ‚ā´ĩā´ąā´ŋā´¨āĩā´ąāĩ† ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "theme_setting_image_viewer_quality_title": "ā´‡ā´Žāĩ‡ā´œāĩ ā´ĩāĩā´¯āĩ‚ā´ĩāĩŧ ā´—āĩā´Ŗā´Žāĩ‡ā´¨āĩā´Ž", + "theme_setting_primary_color_subtitle": "ā´Ēāĩā´°ā´§ā´žā´¨ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´™āĩā´™āĩžā´•āĩā´•āĩā´‚ ⴆⴕāĩâ€Œā´¸ā´¨āĩā´ąāĩā´•āĩžā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ ā´’ā´°āĩ ā´¨ā´ŋā´ąā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•.", + "theme_setting_primary_color_title": "ā´Ēāĩā´°ā´§ā´žā´¨ ā´¨ā´ŋā´ąā´‚", + "theme_setting_system_primary_color_title": "ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´‚ ā´¨ā´ŋā´ąā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "theme_setting_system_theme_switch": "ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´‚ (ā´¸ā´ŋā´¸āĩā´ąāĩā´ąā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ ā´Ēā´ŋā´¨āĩā´¤āĩā´Ÿā´°āĩā´•)", + "theme_setting_theme_subtitle": "ā´†ā´Ēāĩā´Ēā´ŋā´¨āĩā´ąāĩ† ā´¤āĩ€ā´‚ ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "theme_setting_three_stage_loading_subtitle": "ā´Žāĩ‚ā´¨āĩā´¨āĩ-ⴘⴟāĩā´Ÿ ā´˛āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´•ā´Ÿā´¨ā´‚ ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋⴚāĩā´šāĩ‡ā´•āĩā´•ā´žā´‚, ā´Ēā´•āĩā´ˇāĩ‡ ā´¨āĩ†ā´ąāĩā´ąāĩâ€Œā´ĩāĩŧā´•āĩā´•āĩ ā´˛āĩ‹ā´Ąāĩ ā´—ā´Ŗāĩā´¯ā´Žā´žā´¯ā´ŋ ā´ĩāĩŧā´Ļāĩā´§ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "theme_setting_three_stage_loading_title": "ā´Žāĩ‚ā´¨āĩā´¨āĩ-ⴘⴟāĩā´Ÿ ā´˛āĩ‹ā´Ąā´ŋā´‚ā´—āĩ ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´•āĩā´ˇā´Žā´Žā´žā´•āĩā´•āĩā´•", + "they_will_be_merged_together": "ā´…ā´ĩā´¯āĩ† ā´’ā´°āĩā´Žā´ŋⴚāĩā´šāĩ ⴞⴝā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´‚", + "third_party_resources": "ā´Žāĩ‚ā´¨āĩā´¨ā´žā´‚ ā´•ā´•āĩā´ˇā´ŋ ā´‰ā´ąā´ĩā´ŋā´Ÿā´™āĩā´™āĩž", + "time_based_memories": "ā´¸ā´Žā´¯ā´‚ ā´…ā´Ÿā´ŋā´¸āĩā´Ĩā´žā´¨ā´Žā´žā´•āĩā´•ā´ŋā´¯āĩā´ŗāĩā´ŗ ā´“āĩŧā´Žāĩā´Žā´•āĩž", + "timeline": "ⴟāĩˆā´‚ā´˛āĩˆāĩģ", + "timezone": "ā´¸ā´Žā´¯ā´Žāĩ‡ā´–ā´˛", + "to_archive": "ā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "to_change_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "to_favorite": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤ā´žā´•āĩā´•āĩā´•", + "to_login": "ā´˛āĩ‹ā´—ā´ŋāĩģ", + "to_multi_select": "ā´’ā´¨āĩā´¨ā´ŋⴞⴧā´ŋā´•ā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•ā´žāĩģ", + "to_parent": "ā´Ēā´žā´°ā´¨āĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ēāĩ‹ā´•āĩā´•", + "to_select": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•ā´žāĩģ", + "to_trash": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "toggle_settings": "ā´•āĩā´°ā´Žāĩ€ā´•ā´°ā´Ŗā´™āĩā´™āĩž ⴟāĩ‹ā´—ā´ŋāĩž ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "total": "ⴆⴕāĩ†", + "total_usage": "ⴆⴕāĩ† ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚", + "trash": "ⴟāĩā´°ā´žā´ˇāĩ", + "trash_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ⴟāĩā´°ā´žā´ˇā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Žā´žā´ąāĩā´ąā´ŋ", + "trash_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "trash_count": "ⴟāĩā´°ā´žā´ˇāĩ ({count, number})", + "trash_delete_asset": "ā´…ā´¸ā´ąāĩā´ąāĩ ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•/ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "trash_emptied": "ⴟāĩā´°ā´žā´ˇāĩ ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•āĩā´•ā´ŋ", + "trash_no_results_message": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•ā´ŗāĩā´‚ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•ā´ŗāĩā´‚ ā´‡ā´ĩā´ŋⴟāĩ† ā´•ā´žā´Ŗā´ŋā´•āĩā´•āĩā´‚.", + "trash_page_delete_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "trash_page_empty_trash_dialog_content": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´ļāĩ‚ā´¨āĩā´¯ā´Žā´žā´•āĩā´•ā´Ŗāĩ‹? ⴈ ⴇⴍⴙāĩā´™āĩž Immich-āĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "trash_page_info": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ⴇⴍⴙāĩā´™āĩž {days} ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩžā´•āĩā´•āĩ ā´ļāĩ‡ā´ˇā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚", + "trash_page_no_assets": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "trash_page_restore_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´Ēāĩā´¨ā´ƒā´¸āĩā´Ĩā´žā´Ēā´ŋā´•āĩā´•āĩā´•", + "trash_page_select_assets_btn": "ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•", + "trash_page_title": "ⴟāĩā´°ā´žā´ˇāĩ ({count})", + "trashed_items_will_be_permanently_deleted_after": "ⴟāĩā´°ā´žā´ˇāĩ ⴚāĩ†ā´¯āĩā´¤ ⴇⴍⴙāĩā´™āĩž {days, plural, one {# ā´Ļā´ŋā´ĩⴏⴤāĩā´¤ā´ŋā´¨āĩ} other {# ā´Ļā´ŋā´ĩⴏⴙāĩā´™āĩžā´•āĩā´•āĩ}} ā´ļāĩ‡ā´ˇā´‚ ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´‚.", + "troubleshoot": "ⴟāĩā´°ā´Ŧā´ŋāĩžā´ˇāĩ‚ā´Ÿāĩā´Ÿāĩ", + "type": "ⴤⴰⴂ", + "unable_to_change_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´Žā´žā´ąāĩā´ąā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unable_to_setup_pin_code": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ⴏⴜāĩā´œāĩ€ā´•ā´°ā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯ā´ŋā´˛āĩā´˛", + "unarchive": "ā´…āĩēā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unarchive_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´†āĩŧā´•āĩā´•āĩˆā´ĩā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "unarchived_count": "{count, plural, other {ā´…āĩēā´†āĩŧā´•āĩā´•āĩˆā´ĩāĩ ⴚāĩ†ā´¯āĩā´¤ā´ĩ #}}", + "undo": "ā´¤ā´ŋā´°ā´ŋā´•āĩ†ā´šāĩ†ā´¯āĩā´¯āĩā´•", + "unfavorite": "ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´¤ā´˛āĩā´˛ā´žā´¤ā´žā´•āĩā´•āĩā´•", + "unfavorite_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´Ēāĩā´°ā´ŋā´¯ā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿā´ĩā´¯ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤āĩ", + "unhide_person": "ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩ† ā´Žā´ąā´ĩā´ŋāĩŊā´¨ā´ŋā´¨āĩā´¨āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "unknown": "ā´…ā´œāĩā´žā´žā´¤ā´‚", + "unknown_country": "ā´…ā´œāĩā´žā´žā´¤ ā´°ā´žā´œāĩā´¯ā´‚", + "unknown_year": "ā´…ā´œāĩā´žā´žā´¤ ā´ĩāĩŧⴎⴂ", + "unlimited": "ā´Ēā´°ā´ŋā´§ā´ŋā´¯ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ā´¤āĩ", + "unlink_motion_video": "ⴚⴞā´ŋā´•āĩā´•āĩā´¨āĩā´¨ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unlink_oauth": "OAuth ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unlinked_oauth_account": "ā´…āĩēā´˛ā´ŋā´™āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤ OAuth ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ", + "unmute_memories": "ā´“āĩŧā´Žāĩā´Žā´•āĩž ā´ļā´Ŧāĩā´Ļā´Žāĩā´ŗāĩā´ŗā´¤ā´žā´•āĩā´•āĩā´•", + "unnamed_album": "ā´Ēāĩ‡ā´°ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ ā´†āĩŊā´Ŧā´‚", + "unnamed_album_delete_confirmation": "ⴈ ā´†āĩŊā´Ŧā´‚ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´Ŗā´Žāĩ†ā´¨āĩā´¨āĩ ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´Ŗāĩ‹?", + "unnamed_share": "ā´Ēāĩ‡ā´°ā´ŋā´˛āĩā´˛ā´žā´¤āĩā´¤ ā´Ēā´™āĩā´•ā´ŋⴟāĩŊ", + "unsaved_change": "ā´¸āĩ‡ā´ĩāĩ ⴚāĩ†ā´¯āĩā´¯ā´žā´¤āĩā´¤ ā´Žā´žā´ąāĩā´ąā´‚", + "unselect_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "unselect_all_duplicates": "ā´Žā´˛āĩā´˛ā´ž ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•ā´ŗāĩā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "unselect_all_in": "{group}-ā´˛āĩ† ā´Žā´˛āĩā´˛ā´žā´‚ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ ā´Žā´žā´ąāĩā´ąāĩā´•", + "unstack": "ā´…āĩē-ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "unstack_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´…āĩē-ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "unstacked_assets_count": "{count, plural, one {# ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´…āĩē-ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "untagged": "ā´Ÿā´žā´—āĩ ⴚāĩ†ā´¯āĩā´¤ā´ŋⴟāĩā´Ÿā´ŋā´˛āĩā´˛", + "up_next": "ā´…ā´Ÿāĩā´¤āĩā´¤ā´¤āĩ", + "update_location_action_prompt": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ {count} ā´…ā´¸ā´ąāĩā´ąāĩā´•ā´ŗāĩā´Ÿāĩ† ā´¸āĩā´Ĩā´žā´¨ā´‚ ⴇⴤāĩā´Ēā´¯āĩ‹ā´—ā´ŋⴚāĩā´šāĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•:", + "updated_at": "ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤ā´¤āĩ", + "updated_password": "ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "upload": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ", + "upload_action_prompt": "{count} ā´Žā´Ŗāĩā´Ŗā´‚ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąā´ŋā´¨ā´žā´¯ā´ŋ ā´•āĩā´¯āĩ‚ā´ĩā´ŋāĩŊ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "upload_concurrency": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´•āĩ‹āĩēā´•ā´ąāĩģā´¸ā´ŋ", + "upload_details": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž", + "upload_dialog_info": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩ(ā´•āĩž) ā´¸āĩ†āĩŧā´ĩā´ąā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´Ŧā´žā´•āĩā´•ā´Ēāĩā´Ēāĩ ⴚāĩ†ā´¯āĩā´¯ā´Ŗāĩ‹?", + "upload_dialog_title": "ā´…ā´¸ā´ąāĩā´ąāĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "upload_errors": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ {count, plural, one {# ā´Ēā´ŋā´ļā´•āĩ‹ā´Ÿāĩ†} other {# ā´Ēā´ŋā´ļā´•āĩā´•ā´ŗāĩ‹ā´Ÿāĩ†}} ā´Ēāĩ‚āĩŧā´¤āĩā´¤ā´ŋā´¯ā´žā´¯ā´ŋ, ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Ēāĩ‡ā´œāĩ ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•.", + "upload_finished": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´•ā´´ā´ŋā´žāĩā´žāĩ", + "upload_progress": "ā´ļāĩ‡ā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤āĩ {remaining, number} - ā´Ēāĩā´°āĩ‹ā´¸ā´¸āĩā´¸āĩ ⴚāĩ†ā´¯āĩā´¤ā´¤āĩ {processed, number}/{total, number}", + "upload_skipped_duplicates": "{count, plural, one {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩ} other {# ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž}} ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´ŋ", + "upload_status_duplicates": "ā´Ąāĩā´¯āĩ‚ā´Ēāĩā´˛ā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩā´•āĩž", + "upload_status_errors": "ā´Ēā´ŋā´ļā´•āĩā´•āĩž", + "upload_status_uploaded": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¤āĩ", + "upload_success": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´ĩā´ŋⴜⴝā´ŋⴚāĩā´šāĩ, ā´Ēāĩā´¤ā´ŋā´¯ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ā´…ā´¸ā´ąāĩā´ąāĩā´•āĩž ā´•ā´žā´Ŗāĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ ā´Ēāĩ‡ā´œāĩ ā´Ēāĩā´¤āĩā´•āĩā´•āĩā´•.", + "upload_to_immich": "Immich-ā´˛āĩ‡ā´•āĩā´•āĩ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´• ({count})", + "uploading": "ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "uploading_media": "ā´Žāĩ€ā´Ąā´ŋā´¯ ā´…ā´Ēāĩâ€Œā´˛āĩ‹ā´Ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨āĩ", + "url": "ā´¯āĩā´†āĩŧā´ŽāĩŊ", + "usage": "ā´‰ā´Ēā´¯āĩ‹ā´—ā´‚", + "use_biometric": "ā´Ŧā´¯āĩ‹ā´Žāĩ†ā´Ÿāĩā´°ā´ŋā´•āĩ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "use_current_connection": "ā´¨ā´ŋā´˛ā´ĩā´ŋā´˛āĩ† ā´•ā´Ŗā´•āĩā´ˇāĩģ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "use_custom_date_range": "ā´Ēā´•ā´°ā´‚ ā´•ā´¸āĩā´ąāĩā´ąā´‚ ā´¤āĩ€ā´¯ā´¤ā´ŋ ā´Ēā´°ā´ŋā´§ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩāĩ", + "user_has_been_deleted": "ⴈ ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋ.", + "user_id": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´ā´Ąā´ŋ", + "user_liked": "{user} {type, select, photo {ⴈ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹} video {ⴈ ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} asset {ⴈ ā´…ā´¸ā´ąāĩā´ąāĩ} other {ⴇⴤāĩ}} ⴇⴎāĩā´Ÿā´Ēāĩā´Ēāĩ†ā´Ÿāĩā´Ÿāĩ", + "user_pin_code_settings": "ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ", + "user_pin_code_settings_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "user_privacy": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´¸āĩā´ĩā´•ā´žā´°āĩā´¯ā´¤", + "user_purchase_settings": "ā´ĩā´žā´™āĩā´™āĩŊ", + "user_purchase_settings_description": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´ĩā´žā´™āĩā´™āĩŊ ā´•āĩˆā´•ā´žā´°āĩā´¯ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "user_role_set": "{user}-ā´¨āĩ† {role} ⴆⴝā´ŋ ⴏⴜāĩā´œā´Žā´žā´•āĩā´•ā´ŋ", + "user_usage_detail": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒ ā´‰ā´Ēā´¯āĩ‹ā´— ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´‚", + "user_usage_stats": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´‰ā´Ēā´¯āĩ‹ā´— ā´¸āĩā´Ĩā´ŋā´¤ā´ŋā´ĩā´ŋā´ĩā´°ā´•āĩā´•ā´Ŗā´•āĩā´•āĩā´•āĩž", + "user_usage_stats_description": "ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ā´‰ā´Ēā´¯āĩ‹ā´— ā´¸āĩā´Ĩā´ŋā´¤ā´ŋā´ĩā´ŋā´ĩā´°ā´•āĩā´•ā´Ŗā´•āĩā´•āĩā´•āĩž ā´•ā´žā´Ŗāĩā´•", + "username": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤āĩƒā´¨ā´žā´Žā´‚", + "users": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•āĩž", + "users_added_to_album_count": "{count, plural, one {# ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ†} other {# ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ†}} ā´†āĩŊā´Ŧā´¤āĩā´¤ā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ⴚāĩ‡āĩŧā´¤āĩā´¤āĩ", + "utilities": "ā´¯āĩ‚ā´Ÿāĩā´Ÿā´ŋā´˛ā´ŋā´ąāĩā´ąā´ŋā´•āĩž", + "validate": "ā´¸ā´žā´§āĩ‚ā´•ā´°ā´ŋā´•āĩā´•āĩā´•", + "validate_endpoint_error": "ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´¸ā´žā´§āĩā´ĩā´žā´¯ ā´’ā´°āĩ URL ā´¨āĩŊā´•āĩā´•", + "variables": "ā´ĩāĩ‡ā´°ā´ŋā´¯ā´Ŧā´ŋā´ŗāĩā´•āĩž", + "version": "ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ", + "version_announcement_closing": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´¸āĩā´šāĩƒā´¤āĩā´¤āĩ, ⴅⴞⴕāĩā´¸āĩ", + "version_announcement_message": "ā´¨ā´Žā´¸āĩā´•ā´žā´°ā´‚! Immich-ā´¨āĩā´ąāĩ† ā´’ā´°āĩ ā´Ēāĩā´¤ā´ŋā´¯ ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ⴞⴭāĩā´¯ā´Žā´žā´Ŗāĩ. ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ⴏⴜāĩā´œāĩ€ā´•ā´°ā´Ŗā´‚ ā´ā´ąāĩā´ąā´ĩāĩā´‚ ā´Ēāĩā´¤ā´ŋā´¯ā´¤ā´žā´Ŗāĩ†ā´¨āĩā´¨āĩ ā´‰ā´ąā´Ēāĩā´Ēā´žā´•āĩā´•ā´žāĩģ ā´Ļā´¯ā´ĩā´žā´¯ā´ŋ ā´ąā´ŋā´˛āĩ€ā´¸āĩ ā´¨āĩ‹ā´Ÿāĩā´Ÿāĩā´•āĩž ā´ĩā´žā´¯ā´ŋā´•āĩā´•ā´žāĩģ ā´•āĩā´ąā´šāĩā´šāĩ ā´¸ā´Žā´¯ā´Žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•. ⴇⴤāĩ ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´•āĩ‹āĩēā´Ģā´ŋā´—ā´ąāĩ‡ā´ˇā´¨āĩā´•āĩž ā´’ā´´ā´ŋā´ĩā´žā´•āĩā´•ā´žāĩģ ā´¸ā´šā´žā´¯ā´ŋā´•āĩā´•āĩā´‚, ā´Ēāĩā´°ā´¤āĩā´¯āĩ‡ā´•ā´ŋⴚāĩā´šāĩā´‚ ā´¨ā´ŋā´™āĩā´™āĩž ā´ĩā´žā´šāĩā´šāĩā´Ÿā´ĩāĩŧ ā´…ā´˛āĩā´˛āĩ†ā´™āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† Immich ā´‡āĩģā´¸āĩā´ąāĩā´ąāĩģā´¸āĩ ā´¯ā´žā´¨āĩā´¤āĩā´°ā´ŋā´•ā´Žā´žā´¯ā´ŋ ā´…ā´Ēāĩā´Ąāĩ‡ā´ąāĩā´ąāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´¨āĩā´¨ ā´ā´¤āĩ†ā´™āĩā´•ā´ŋā´˛āĩā´‚ ⴏⴂā´ĩā´ŋā´§ā´žā´¨ā´‚ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩā´Ŗāĩā´Ÿāĩ†ā´™āĩā´•ā´ŋāĩŊ.", + "version_history": "ā´Ēā´¤ā´ŋā´Ēāĩā´Ēāĩ ⴚⴰā´ŋā´¤āĩā´°ā´‚", + "version_history_item": "{date}-ā´¨āĩ {version} ā´‡āĩģā´¸āĩā´ąāĩā´ąā´žāĩž ⴚāĩ†ā´¯āĩā´¤āĩ", + "video": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹", + "video_hover_setting": "ā´šāĩ‹ā´ĩāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "video_hover_setting_description": "ā´Žāĩ—ā´¸āĩ ā´’ā´°āĩ ⴇⴍⴤāĩā´¤ā´ŋā´¨āĩ ā´Žāĩā´•ā´ŗā´ŋā´˛āĩ‚ā´Ÿāĩ† ā´šāĩ‹ā´ĩāĩŧ ⴚāĩ†ā´¯āĩā´¯āĩā´Žāĩā´Ēāĩ‹āĩž ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ ⴤⴂā´Ŧāĩā´¨āĩ†ā´¯ā´ŋāĩŊ ā´Ēāĩā´˛āĩ‡ ⴚāĩ†ā´¯āĩā´¯āĩā´•. ā´Ēāĩā´°ā´ĩāĩŧā´¤āĩā´¤ā´¨ā´°ā´šā´ŋā´¤ā´Žā´žā´•āĩā´•ā´ŋā´¯ā´žā´˛āĩā´‚, ā´Ēāĩā´˛āĩ‡ ⴐⴕāĩā´•ā´Ŗā´ŋā´¨āĩ ā´Žāĩā´•ā´ŗā´ŋā´˛āĩ‚ā´Ÿāĩ† ā´šāĩ‹ā´ĩāĩŧ ⴚāĩ†ā´¯āĩā´¤āĩ ā´Ēāĩā´˛āĩ‡ā´Ŧā´žā´•āĩā´•āĩ ⴆⴰⴂⴭā´ŋā´•āĩā´•ā´žāĩģ ā´•ā´´ā´ŋā´¯āĩā´‚.", + "videos": "ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž", + "videos_count": "{count, plural, one {# ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹} other {# ā´ĩāĩ€ā´Ąā´ŋā´¯āĩ‹ā´•āĩž}}", + "view": "ā´•ā´žā´Ŗāĩā´•", + "view_album": "ā´†āĩŊā´Ŧā´‚ ā´•ā´žā´Ŗāĩā´•", + "view_all": "ā´Žā´˛āĩā´˛ā´žā´‚ ā´•ā´žā´Ŗāĩā´•", + "view_all_users": "ā´Žā´˛āĩā´˛ā´ž ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´•āĩā´•ā´ŗāĩ†ā´¯āĩā´‚ ā´•ā´žā´Ŗāĩā´•", + "view_details": "ā´ĩā´ŋā´ļā´Ļā´žā´‚ā´ļā´™āĩā´™āĩž ā´•ā´žā´Ŗāĩā´•", + "view_in_timeline": "ⴟāĩˆā´‚ā´˛āĩˆā´¨ā´ŋāĩŊ ā´•ā´žā´Ŗāĩā´•", + "view_link": "ā´˛ā´ŋā´™āĩā´•āĩ ā´•ā´žā´Ŗāĩā´•", + "view_links": "ā´˛ā´ŋā´™āĩā´•āĩā´•āĩž ā´•ā´žā´Ŗāĩā´•", + "view_name": "ā´•ā´žā´Ŗāĩā´•", + "view_next_asset": "ā´…ā´Ÿāĩā´¤āĩā´¤ ā´…ā´¸ā´ąāĩā´ąāĩ ā´•ā´žā´Ŗāĩā´•", + "view_previous_asset": "ā´Žāĩā´Žāĩā´Ēā´¤āĩā´¤āĩ† ā´…ā´¸ā´ąāĩā´ąāĩ ā´•ā´žā´Ŗāĩā´•", + "view_qr_code": "QR ā´•āĩ‹ā´Ąāĩ ā´•ā´žā´Ŗāĩā´•", + "view_similar_photos": "ā´¸ā´Žā´žā´¨ā´Žā´žā´¯ ā´Ģāĩ‹ā´Ÿāĩā´Ÿāĩ‹ā´•āĩž ā´•ā´žā´Ŗāĩā´•", + "view_stack": "ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ā´•ā´žā´Ŗāĩā´•", + "view_user": "ā´‰ā´Ēā´¯āĩ‹ā´•āĩā´¤ā´žā´ĩā´ŋā´¨āĩ† ā´•ā´žā´Ŗāĩā´•", + "viewer_remove_from_stack": "ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•ā´ŋāĩŊ ā´¨ā´ŋā´¨āĩā´¨āĩ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "viewer_stack_use_as_main_asset": "ā´Ēāĩā´°ā´§ā´žā´¨ ā´…ā´¸ā´ąāĩā´ąā´žā´¯ā´ŋ ā´‰ā´Ēā´¯āĩ‹ā´—ā´ŋā´•āĩā´•āĩā´•", + "viewer_unstack": "ā´…āĩē-ā´¸āĩā´ąāĩā´ąā´žā´•āĩā´•āĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•", + "visibility_changed": "{count, plural, one {# ā´ĩāĩā´¯ā´•āĩā´¤ā´ŋā´¯āĩā´Ÿāĩ†} other {# ⴆⴺāĩā´•ā´ŗāĩā´Ÿāĩ†}} ā´Ļāĩƒā´ļāĩā´¯ā´¤ ā´Žā´žā´ąāĩā´ąā´ŋ", + "waiting": "ā´•ā´žā´¤āĩā´¤ā´ŋā´°ā´ŋā´•āĩā´•āĩā´¨āĩā´¨āĩ", + "warning": "ā´Žāĩā´¨āĩā´¨ā´ąā´ŋā´¯ā´ŋā´Ēāĩā´Ēāĩ", + "week": "ⴆⴴāĩā´š", + "welcome": "ā´¸āĩā´ĩā´žā´—ā´¤ā´‚", + "welcome_to_immich": "Immich-ā´˛āĩ‡ā´•āĩā´•āĩ ā´¸āĩā´ĩā´žā´—ā´¤ā´‚", + "wifi_name": "ā´ĩāĩˆ-ā´Ģāĩˆā´¯āĩā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ", + "wrong_pin_code": "ā´¤āĩ†ā´ąāĩā´ąā´žā´¯ ā´Ēā´ŋāĩģ ā´•āĩ‹ā´Ąāĩ", + "year": "ā´ĩāĩŧⴎⴂ", + "years_ago": "{years, plural, one {# ā´ĩāĩŧⴎⴂ} other {# ā´ĩāĩŧⴎⴙāĩā´™āĩž}} ā´Žāĩā´Žāĩā´Ēāĩ", + "yes": "ā´…ā´¤āĩ†", + "you_dont_have_any_shared_links": "ā´¨ā´ŋā´™āĩā´™āĩžā´•āĩā´•āĩ ā´Ēā´™āĩā´•ā´ŋⴟāĩā´Ÿ ā´˛ā´ŋā´™āĩā´•āĩā´•ā´ŗāĩŠā´¨āĩā´¨āĩā´Žā´ŋā´˛āĩā´˛", + "your_wifi_name": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´ĩāĩˆ-ā´Ģāĩˆā´¯āĩā´Ÿāĩ† ā´Ēāĩ‡ā´°āĩ", + "zoom_image": "ⴚā´ŋā´¤āĩā´°ā´‚ ā´ĩā´˛āĩā´¤ā´žā´•āĩā´•āĩā´•", + "zoom_to_bounds": "ā´Ēā´°ā´ŋā´§ā´ŋā´•ā´ŗā´ŋā´˛āĩ‡ā´•āĩā´•āĩ ā´¸āĩ‚ā´‚ ⴚāĩ†ā´¯āĩā´¯āĩā´•" } diff --git a/i18n/mn.json b/i18n/mn.json index 8a18a1d5e5..85092fef0d 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -15,10 +15,13 @@ "add_a_name": "ĐŅŅ€ ĶŠĐŗĶŠŅ…", "add_a_title": "Đ“Đ°Ņ€Ņ‡Đ¸Đŗ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "add_endpoint": "Endpoint ĐŊŅĐŧŅŅ…", + "add_import_path": "ИĐŧĐŋĐžŅ€Ņ‚ĐģĐžŅ… СаĐŧ ĐŊŅĐŧŅŅ…", "add_location": "Đ‘Đ°ĐšŅ€ŅˆĐ¸Đģ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "add_more_users": "Ķ¨ĶŠŅ€ Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸Đ´ ĐŊŅĐŧŅŅ…", "add_partner": "ĐĨаĐŧŅ‚Ņ€Đ°ĐŗŅ‡ ĐŊŅĐŧŅŅ…", + "add_path": "ЗаĐŧ ĐŊŅĐŧŅŅ…", "add_photos": "Đ—ŅƒŅ€Đ°Đŗ ĐŊŅĐŧŅŅ…", + "add_tag": "Đ¨ĐžŅˆĐŗĐž ĐŊŅĐŧŅŅ…", "add_to_album": "ĐĻĐžĐŧĐžĐŗŅ‚ ĐžŅ€ŅƒŅƒĐģĐ°Ņ…", "add_to_album_bottom_sheet_added": "{album}-Đ´ ĐŊŅĐŧĐģŅŅ", "add_to_album_bottom_sheet_already_exists": "{album}-Đ´ аĐģҌ Ņ…ŅĐ´Đ¸ĐšĐŊ ĐžŅ€ŅĐžĐŊ йаКĐŊа", @@ -28,6 +31,7 @@ "added_to_favorites": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€ĐŗĐ°ĐŊĐ´ ĐŊŅĐŧŅŅ…", "added_to_favorites_count": "Đ”ŅƒŅ€Ņ‚Đ°Đš ĐˇŅƒŅ€Đ°ĐŗĐŊŅƒŅƒĐ´Đ°Đ´ {count, number} ĐŊŅĐŧŅĐŗĐ´ĐģŅŅ", "admin": { + "admin_user": "АдĐŧиĐŊ Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡", "authentication_settings": "ĐĸаĐŊиĐŊ ĐŊŅĐ˛Ņ‚Ņ€ŅĐģŅ‚ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "authentication_settings_description": "ĐŅƒŅƒŅ† Ō¯ĐŗĐ¸ĐšĐŊ ŅƒĐ´Đ¸Ņ€Đ´ĐģĐ°ĐŗĐ°, OAuth йОĐģĐžĐŊ ĐąŅƒŅĐ°Đ´ Ņ‚Đ°ĐŊиĐŊ ĐŊŅĐ˛Ņ‚Ņ€ŅĐģŅ‚Đ¸ĐšĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", "authentication_settings_disable_all": "Đ‘Ō¯Ņ… ĐŊŅĐ˛Ņ‚Ņ€ŅŅ… Đ°Ņ€ĐŗŅƒŅƒĐ´Ņ‹Đŗ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŗĐžŅ…Đ´ĐžĐž Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃? ĐŅĐ˛Ņ‚Ņ€ŅŅ… Ō¯ĐšĐģĐ´ŅĐģ ĐąŌ¯Ņ€ŅĐŊ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐŗŌ¯Đš йОĐģĐŊĐž.", @@ -35,11 +39,15 @@ "backup_database": "Ķ¨ĐŗĶŠĐŗĐ´ĐģиКĐŊ ŅĐ°ĐŊĐŗĐ¸ĐšĐŊ даĐŧĐŋ Ō¯Ō¯ŅĐŗŅŅ…", "backup_database_enable_description": "Ķ¨ĐŗĶŠĐŗĐ´ĐģиКĐŊ ŅĐ°ĐŊĐŗĐ¸ĐšĐŊ даĐŧĐŋ Đ¸Đ´ŅĐ˛Ņ…Đ¸ĐļŌ¯Ō¯ĐģŅŅ…", "backup_keep_last_amount": "͍ĐŧĐŊĶŠŅ… Ņ…ŅĐ´ŅĐŊ даĐŧĐŋŅ‹Đŗ Ņ…Đ°Đ´ĐŗĐ°ĐģĐ°Ņ… Đ˛Ņ", + "backup_settings": "Đ”Đ°Ņ‚Đ°ĐąĐ°Đˇ даĐŧĐŋ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", + "backup_settings_description": "Đ”Đ°Ņ‚Đ°ĐąĐ°ĐˇĐ°Đ°Ņ даĐŧĐŋ Ņ…Đ¸ĐšŅ… Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐžĐŊŅƒŅƒĐ´.", "config_set_by_file": "ĐĸĐžŅ…Đ¸Ņ€ĐŗĐžĐžĐŗ ĐžĐ´ĐžĐžĐŗĐžĐžŅ€ Ņ„Đ°ĐšĐģĐ°Đ°Ņ Đ°Đ˛Ņ‡ йаКĐŊа", "confirm_delete_library": "Đĸа {library} ĐŗŅŅŅĐŊ ŅĐ°ĐŊĐŗ ŅƒŅŅ‚ĐŗĐ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃?", "confirm_delete_library_assets": "Đĸа ŅĐŊŅ ŅĐ°ĐŊĐŗ ŅƒŅŅ‚ĐŗĐ°Ņ…Đ´Đ°Đ° Đ¸Ņ‚ĐŗŅĐģŅ‚ŅĐš йаКĐŊа ҃҃? Đ­ĐŊŅ Ō¯ĐšĐģĐ´ĐģŅŅŅ€ Ņ‚Đ°ĐŊŅ‹ {count, plural, one {# contained asset} other {all # contained assets}} ҁĐĩŅ€Đ˛ĐĩŅ€ŅŅŅ ŅƒŅŅ‚Đ°Ņ… ĐąĶŠĐŗĶŠĶŠĐ´ ĐąŅƒŅ†Đ°Đ°Ņ… йОĐģĐžĐŧĐļĐŗŌ¯Đš. Đ“ŅŅ…Đ´ŅŅ Ņ„Đ°ĐšĐģŅƒŅƒĐ´ Đ´Đ¸ŅĐē Đ´ŅŅŅ€ŅŅ Ō¯ĐģĐ´ŅĐŊŅ.", "confirm_email_below": "Đ‘Đ°Ņ‚Đ°ĐģĐŗĐ°Đ°Đļ҃҃ĐģĐ°Ņ…Ņ‹ĐŊ Ņ‚ŅƒĐģĐ´ Ņ‚Đ° \"{email}\" ĐŗŅĐļ ĐąĐ¸Ņ‡ĐŊŅ Ō¯Ō¯", "confirm_reprocess_all_faces": "Đ‘Ō¯Ņ… Ņ†Đ°Ņ€Đ°ĐšĐŗ Đ´Đ°Ņ…Đ¸ĐŊ ĐŋŅ€ĐžŅ†Đĩҁҁ Ņ…Đ¸ĐšŅ… Ō¯Ō¯? ĐĸŅĐŗĐ˛ŅĐģ ĐąŌ¯Ņ… ĐŊŅŅ€Ņ Đ°Ņ€Đ¸ĐģĐ°Ņ… йОĐģĐŊĐž.", + "confirm_user_password_reset": "{user}-иКĐŊ ĐŊŅƒŅƒŅ† Ō¯ĐŗĐ¸ĐšĐŗ Đ´Đ°Ņ…Đ¸ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ŅƒŅƒĐģĐ°Ņ… ҃҃?", + "confirm_user_pin_code_reset": "{user} Ņ…ŅŅ€ŅĐŗĐģŅĐŗŅ‡Đ¸ĐšĐŊ PIN code Đ´Đ°Ņ…Đ¸ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ŅƒŅƒĐģĐ°Ņ… ҃҃?", "face_detection": "ĐŌ¯Ō¯Ņ€ иĐģŅ€Ō¯Ō¯ĐģŅŅ…", "image_quality": "ЧаĐŊĐ°Ņ€", "job_settings": "АĐļĐģŅ‹ĐŊ Ņ‚ĐžŅ…Đ¸Ņ€ĐŗĐžĐž", diff --git a/i18n/mr.json b/i18n/mr.json index 6c2ab7406c..dbc2e3d114 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -4,6 +4,7 @@ "account_settings": "ā¤–ā¤žā¤¤āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", "acknowledge": "ā¤Žā¤žā¤¨āĨā¤¯ā¤¤ā¤ž", "action": "⤕āĨƒā¤¤āĨ€", + "action_common_update": "⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤", "actions": "⤕āĨƒā¤¤āĨā¤¯āĨ‡", "active": "⤏⤕āĨā¤°ā¤ŋ⤝", "activity": "⤗⤤ā¤ŋā¤ĩā¤ŋ⤧ā¤ŋ", @@ -13,6 +14,8 @@ "add_a_location": "ā¤ā¤• ⤏āĨā¤Ĩ⤺ ā¤Ÿā¤žā¤•ā¤ž", "add_a_name": "ā¤¨ā¤žā¤ĩ ā¤Ÿā¤žā¤•ā¤ž", "add_a_title": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ā¤Ÿā¤žā¤•ā¤ž", + "add_birthday": "⤜⤍āĨā¤Žā¤Ļā¤ŋā¤ĩ⤏ ⤍āĨ‹ā¤‚ā¤Ļā¤ĩā¤ž", + "add_endpoint": "ā¤ā¤‚ā¤Ąā¤ĒāĨ‰ā¤‡ā¤‚ā¤Ÿ ⤜āĨ‹ā¤Ąā¤ž", "add_exclusion_pattern": "⤅ā¤Ēā¤ĩā¤žā¤Ļ ā¤¨ā¤ŽāĨā¤¨ā¤ž ⤜āĨ‹ā¤Ąā¤ž", "add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ā¤Ÿā¤žā¤•ā¤ž", "add_location": "⤏āĨā¤Ĩ⤺ ā¤Ÿā¤žā¤•ā¤ž", @@ -20,24 +23,37 @@ "add_partner": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤° ⤜āĨ‹ā¤Ąā¤ž", "add_path": "ā¤Žā¤žā¤°āĨā¤— ā¤Ÿā¤žā¤•ā¤ž", "add_photos": "ā¤›ā¤žā¤¯ā¤žā¤šā¤ŋ⤤āĨā¤°āĨ‡ ⤜āĨ‹ā¤Ąā¤ž", + "add_tag": "⤟āĨ…⤗ ⤜āĨ‹ā¤Ąā¤ž", "add_to": "⤤āĨā¤¯ā¤ž ā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤žâ€Ļ", "add_to_album": "⤏⤂⤗āĨā¤°ā¤šā¤žā¤¤ ā¤Ÿā¤žā¤•ā¤ž", + "add_to_album_bottom_sheet_added": "{album} ā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ‡", + "add_to_album_bottom_sheet_already_exists": "⤆⤧āĨ€ā¤š {album} ā¤Žā¤§āĨā¤¯āĨ‡ ā¤†ā¤šāĨ‡", + "add_to_album_toggle": "⤅⤞āĨā¤Ŧā¤Žā¤¸ā¤žā¤ āĨ€ ⤍ā¤ŋā¤ĩā¤Ą ⤟āĨ‰ā¤—⤞ ā¤•ā¤°ā¤ž", + "add_to_albums": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤ž", "add_to_shared_album": "ā¤¸ā¤žā¤Žā¤žā¤¯ā¤ŋ⤕ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤¤ ā¤Ÿā¤žā¤•ā¤ž", - "add_url": "URL ⤜āĨ‹ā¤Ąā¤ž", - "added_to_archive": "⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ā¤žā¤¤ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", - "added_to_favorites": "⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤žā¤¤ ā¤Ÿā¤žā¤•ā¤˛āĨ‡", + "add_url": "URL ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "added_to_archive": "⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡", + "added_to_favorites": "⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤ž ⤏⤂⤗āĨā¤°ā¤šā¤žā¤¤ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", "added_to_favorites_count": "⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤žā¤¤ {count, number} ā¤Ÿā¤žā¤•ā¤˛āĨ‡", "admin": { "add_exclusion_pattern_description": "⤅ā¤Ēā¤ĩā¤žā¤Ļ ⤅⤍āĨā¤•āĨ‚⤞⤍ ⤜āĨ‹ā¤Ąā¤ž. ** ⤆⤪ā¤ŋ ? ā¤¯ā¤ž ⤉ā¤Ē⤝āĨ‹ā¤—ā¤žā¤¤ ⤗āĨā¤˛āĨ‹ā¤Ŧā¤ŋ⤂⤗ ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ā¤†ā¤šāĨ‡. ⤕āĨ‹ā¤Ŗā¤¤āĨā¤¯ā¤žā¤šāĨ€ \"Raw\" ā¤¨ā¤žā¤ĩā¤žā¤šāĨā¤¯ā¤ž ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋ⤕āĨ‡ā¤Žā¤§āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇāĨ€ā¤¤ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ \"/Raw/\" ā¤ĩā¤žā¤Ēā¤°ā¤ž. \".tif\" ā¤¯ā¤ž ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ ā¤Ēā¤Ĩā¤žā¤ĩ⤰ ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤ ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤏⤰āĨā¤ĩ ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇāĨ€ā¤¤ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ \"**/.tif\" ā¤ĩā¤žā¤Ēā¤°ā¤ž. ā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿ ā¤Ēā¤Ĩ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ \"/path/to/ignore/**\" ā¤ĩā¤žā¤Ēā¤°ā¤ž.", + "admin_user": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤¨ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž", "asset_offline_description": "ā¤šāĨ€ ā¤Ŧā¤žā¤šāĨā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨āĨ‡ ā¤Ąā¤ŋ⤏āĨā¤•ā¤ĩ⤰ ā¤¨ā¤žā¤šāĨ€ā¤¤ ⤆⤪ā¤ŋ ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤ŋ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ€ ⤗āĨ‡ā¤˛āĨ€ ā¤†ā¤šāĨ‡ā¤¤. ⤜⤰ ā¤Ģā¤žā¤‡ā¤˛ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ā¤žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤ŋ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ€ ⤗āĨ‡ā¤˛āĨ€ ā¤†ā¤šāĨ‡, ⤤⤰ ⤍ā¤ĩāĨ€ā¤¨ ⤏⤂⤗⤤ ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨ ⤕ā¤ŋ⤂ā¤ĩāĨā¤šā¤ž ⤰āĨ‹ā¤œāĨ€ā¤¨ā¤ŋā¤ļāĨ€ ā¤Žā¤§āĨā¤¯āĨ‡ ⤤ā¤Ēā¤žā¤¸ā¤ž. ā¤šā¤ž ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨ ā¤ĩā¤žā¤Ē⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍ā¤ŋā¤ŽāĨā¤¨ā¤˛ā¤ŋ⤖ā¤ŋ⤤ ā¤–ā¤¤ā¤žā¤ĩ⤪āĨ€ ā¤Ēā¤Ĩā¤žā¤˛ā¤ž ā¤‡ā¤ŽāĨā¤ŽāĨ€ā¤š ā¤ĻāĨā¤ĩā¤žā¤°ā¤ž ā¤ĩā¤žā¤Ē⤰āĨ‚ ā¤ļ⤕⤤āĨ‹ ā¤¯ā¤žā¤šāĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€ ā¤•ā¤°ā¤ž ⤆⤪ā¤ŋ ⤤āĨ‹ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤šā¤žā¤ŗā¤ž.", "authentication_settings": "ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ā¤¸ā¤žā¤§ā¤•", "authentication_settings_description": "ā¤Ē⤰ā¤ĩ⤞āĨ€ā¤šā¤ž ā¤ļā¤ŦāĨā¤Ļ, OAuth ⤆⤪ā¤ŋ ⤅⤍āĨā¤¯ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ā¤ĒāĨā¤°ā¤Ŧ⤂⤧⤍ ā¤•ā¤°ā¤ž", "authentication_settings_disable_all": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤–ā¤žā¤¤āĨā¤°āĨ€ ā¤†ā¤šāĨ‡ ⤕āĨ€ ⤤āĨā¤ŽāĨā¤šāĨ€ ⤏⤰āĨā¤ĩ ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļ ā¤Ēā¤ĻāĨā¤§ā¤¤āĨ€ ā¤Ŧ⤂ā¤Ļ ⤕⤰āĨ‚ ā¤‡ā¤šāĨā¤›ā¤ŋā¤¤ā¤ž? ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļ ā¤ĒāĨ‚⤰āĨā¤Ŗā¤Ē⤪āĨ‡ ā¤Ŧ⤂ā¤Ļ ā¤šāĨ‹ā¤‡ā¤˛!.", "authentication_settings_reenable": "ā¤Ē⤰⤤ ā¤šā¤žā¤˛āĨ‚ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ Server Command ā¤ĩā¤žā¤Ēā¤°ā¤ž.", "background_task_job": "ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­āĨ‚ā¤Žā¤ŋ ā¤•ā¤žā¤°āĨā¤¯", - "backup_database": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤¸ā¤‚ā¤šā¤¯ā¤žā¤šāĨ€ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ā¤ĒāĨā¤°ā¤¤ ā¤•ā¤°ā¤ž", - "backup_database_enable_description": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤¸ā¤‚ā¤šā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "backup_database": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "backup_database_enable_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ā¤Ąā¤‚ā¤Ē ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", "backup_keep_last_amount": "ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ā¤šāĨā¤¯ā¤ž ⤕ā¤ŋ⤤āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ⤠āĨ‡ā¤ĩā¤žā¤¯ā¤šāĨā¤¯ā¤ž", + "backup_onboarding_1_description": "⤕āĨā¤˛ā¤žā¤Šā¤Ąā¤Žā¤§āĨā¤¯āĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤇⤤⤰ ⤕āĨ‹ā¤Ŗā¤¤āĨā¤¯ā¤žā¤šāĨ€ ⤭āĨŒā¤¤ā¤ŋ⤕ ⤠ā¤ŋā¤•ā¤žā¤ŖāĨ€ ⤠āĨ‡ā¤ĩ⤞āĨ‡ā¤˛āĨ€ ⤑ā¤Ģā¤¸ā¤žā¤‡ā¤Ÿ ā¤ĒāĨā¤°ā¤¤.", + "backup_onboarding_2_description": "ā¤ĩā¤ŋā¤ĩā¤ŋ⤧ ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤ĩ⤰ ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤¤āĨ€ ⤠āĨ‡ā¤ĩ⤞āĨ€ ā¤œā¤žā¤¤ā¤žā¤¤. ā¤¯ā¤žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ŽāĨā¤–āĨā¤¯ ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ ⤆⤪ā¤ŋ ⤤āĨā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤ž ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ā¤ŦāĨ…⤕⤅ā¤Ēā¤šā¤ž ā¤¸ā¤Žā¤žā¤ĩāĨ‡ā¤ļ ā¤†ā¤šāĨ‡.", + "backup_onboarding_3_description": "ā¤ŽāĨā¤ŗ ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ā¤¸ā¤šā¤ŋ⤤ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤ĄāĨ‡ā¤Ÿā¤žā¤šāĨā¤¯ā¤ž ā¤ā¤•āĨ‚⤪ ā¤ĒāĨā¤°ā¤¤āĨā¤¯ā¤ž. ā¤¯ā¤žā¤Žā¤§āĨā¤¯āĨ‡ 1 ⤑ā¤Ģā¤¸ā¤žā¤‡ā¤Ÿ ā¤ĒāĨā¤°ā¤¤ ⤆⤪ā¤ŋ 2 ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤¤ā¤žā¤‚ā¤šā¤ž ā¤¸ā¤Žā¤žā¤ĩāĨ‡ā¤ļ ā¤†ā¤šāĨ‡.", + "backup_onboarding_description": "ā¤ĄāĨ‡ā¤Ÿā¤ž ⤏⤂⤰⤕āĨā¤ˇā¤Ŗā¤žā¤¸ā¤žā¤ āĨ€ 3-2-1 ā¤ŦāĨ…⤕⤅ā¤Ē ⤧āĨ‹ā¤°ā¤Ŗ ā¤ļā¤ŋā¤Ģā¤žā¤°ā¤¸ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤¤āĨ‡. ⤏⤰āĨā¤ĩā¤¸ā¤Žā¤žā¤ĩāĨ‡ā¤ļ⤕ ā¤ŦāĨ…⤕⤅ā¤Ē ⤉ā¤Ēā¤žā¤¯ā¤žā¤¸ā¤žā¤ āĨ€, ⤆ā¤Ē⤞āĨā¤¯ā¤ž ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹/ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤šāĨā¤¯ā¤ž ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤˛ā¤ž ⤤⤏āĨ‡ā¤š Immich ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ā¤šāĨ€ ā¤ĒāĨā¤°ā¤¤ ⤜⤤⤍ ā¤•ā¤°ā¤ž.", + "backup_onboarding_footer": "Immich ā¤šā¤ž ā¤ŦāĨ…⤕⤅ā¤Ē ā¤•ā¤¸ā¤ž ⤘āĨā¤¯ā¤žā¤ĩā¤ž ā¤¯ā¤žā¤Ŧā¤ĻāĨā¤Ļ⤞ ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€, ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤Ļ⤏āĨā¤¤ā¤ā¤ĩ⤜āĨ€ā¤•⤰⤪ ā¤Ēā¤žā¤šā¤ž.", + "backup_onboarding_parts_title": "3-2-1 ā¤ŦāĨ…⤕⤅ā¤Ēā¤Žā¤§āĨā¤¯āĨ‡ ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤†ā¤šāĨ‡:", + "backup_onboarding_title": "ā¤ŦāĨ…⤕⤅ā¤Ē", "backup_settings": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž", "backup_settings_description": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤¸ā¤‚ā¤šā¤¯ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍", "cleared_jobs": "{job}: ⤚āĨā¤¯ā¤ž ā¤•ā¤žā¤°āĨā¤¯ā¤ĩā¤žā¤šāĨā¤¯ā¤ž ā¤•ā¤žā¤ĸ⤞āĨā¤¯ā¤ž", @@ -47,6 +63,7 @@ "confirm_email_below": "ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ⤕⤰⤪āĨā¤¯ā¤ž ā¤¸ā¤žā¤ āĨ€, ā¤–ā¤žā¤˛āĨ€ \"{email}\" ā¤Ÿā¤‚ā¤•ā¤˛ā¤ŋ⤖ā¤ŋ⤤ ā¤•ā¤°ā¤ž", "confirm_reprocess_all_faces": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤–ā¤žā¤¤āĨā¤°āĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž ⤕āĨ€ ⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤏⤰āĨā¤ĩ ⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤ĩ⤰ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤•ā¤°ā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡? ā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ā¤¨ā¤žā¤ĩ ā¤Ļā¤ŋ⤞āĨ‡ā¤˛āĨ‡ ⤞āĨ‹ā¤•ā¤šāĨ€ ā¤¸ā¤žā¤Ģ ā¤šāĨ‹ā¤¤āĨ€ā¤˛.", "confirm_user_password_reset": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ {user} ā¤šā¤ž ā¤Ē⤰ā¤ĩ⤞āĨ€ā¤šā¤ž ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļā¤˛ā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "confirm_user_pin_code_reset": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ {user} ā¤šā¤ž ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", "create_job": "ā¤•ā¤žā¤°āĨā¤¯ ā¤Ŧ⤍ā¤ĩā¤ž", "cron_expression": "ā¤ĩāĨ‡ā¤ŗā¤žā¤Ē⤤āĨā¤°ā¤• ⤏āĨ‚⤤āĨā¤°", "cron_expression_description": "ā¤šā¤žā¤ŗā¤¨āĨā¤¯ā¤žā¤šāĨ‡ ā¤ĩāĨ‡ā¤ŗā¤žā¤Ē⤤āĨā¤°ā¤• ⤕āĨā¤°āĨ‰ā¤¨ ā¤Ēā¤ĻāĨā¤§ā¤¤āĨ€ ⤍āĨ‡ ā¤•ā¤°ā¤ž. ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤¸ā¤žā¤ āĨ€ ā¤Ēā¤šā¤ž: ⤕āĨā¤°āĨ‰ā¤¨ ⤗āĨā¤°āĨ", @@ -55,6 +72,1697 @@ "duplicate_detection_job_description": "ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤›ā¤žā¤¯ā¤žā¤šā¤ŋ⤤āĨā¤°ā¤žā¤‚ā¤šā¤ž ā¤ļāĨ‹ā¤§ ⤘āĨ‡ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤¯ā¤žā¤‚ā¤¤āĨā¤°ā¤ŋ⤕āĨ€ ā¤ĒāĨā¤°ā¤ļā¤ŋ⤕āĨā¤ˇā¤Ŗ ā¤ĻāĨā¤¯ā¤ž. ā¤šāĨ€ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤ā¤ž ⤚⤤āĨā¤° ā¤ļāĨ‹ā¤§ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‡", "exclusion_pattern_description": "⤆ā¤Ē⤞āĨ‡ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤šā¤žā¤ŗā¤¤ā¤žā¤¨ā¤ž ⤅ā¤Ēā¤ĩā¤žā¤Ļ ā¤¨ā¤ŽāĨā¤¨āĨ‡ ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤˛ā¤ž ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ⤆⤪ā¤ŋ ⤰āĨā¤¨ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋ⤕āĨ‡ā¤˛ā¤ž ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇāĨ€ā¤¤ ⤕⤰āĨ‚ ā¤ĻāĨ‡ā¤¤ā¤žā¤¤. ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤•ā¤ĄāĨ‡ ā¤•ā¤šāĨā¤šāĨā¤¯ā¤ž ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤†ā¤¯ā¤žā¤¤ ⤕⤰āĨ‚ ā¤‡ā¤šāĨā¤›ā¤ŋ⤤ ⤍⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ (RAW) ā¤–ā¤¤ā¤žā¤ĩ⤪āĨā¤¯ā¤ž ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤ŋā¤•ā¤ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸ ā¤šāĨ‡ ⤉ā¤Ē⤝āĨā¤•āĨā¤¤ ā¤†ā¤šāĨ‡.", "external_library_management": "ā¤Ŧā¤žā¤šāĨā¤¯ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤˛ā¤¯ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍", - "face_detection": "ā¤ŽāĨā¤– ⤏⤂ā¤ļāĨ‹ā¤§ā¤¨" - } + "face_detection": "ā¤ŽāĨā¤– ⤏⤂ā¤ļāĨ‹ā¤§ā¤¨", + "face_detection_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ā¤ĩā¤žā¤Ē⤰āĨ‚⤍ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤Žā¤§āĨ€ā¤˛ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤ļāĨ‹ā¤§ā¤ž. ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤¸ā¤žā¤ āĨ€, ā¤Ģ⤕āĨā¤¤ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ā¤šā¤ž ā¤ĩā¤ŋā¤šā¤žā¤° ⤕āĨ‡ā¤˛ā¤ž ā¤œā¤žā¤¤āĨ‹. \"⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ\" (ā¤ĒāĨā¤¨āĨā¤šā¤ž) ⤏⤰āĨā¤ĩ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ⤕⤰⤤āĨ‡. \"⤰āĨ€ā¤¸āĨ‡ā¤Ÿ\" ā¤¯ā¤žā¤ĩāĨā¤¯ā¤¤ā¤ŋ⤰ā¤ŋ⤕āĨā¤¤ ⤏⤰āĨā¤ĩ ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤚āĨ‡ā¤šā¤°ā¤ž ā¤ĄāĨ‡ā¤Ÿā¤ž ā¤¸ā¤žā¤Ģ ⤕⤰⤤āĨ‡. \"ā¤—ā¤šā¤žā¤ŗ\" ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ⤅ā¤ĻāĨā¤¯ā¤žā¤Ē ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ⤍ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ⤠āĨ‡ā¤ĩ⤤āĨ‡. ā¤ļāĨ‹ā¤§ā¤˛āĨ‡ā¤˛āĨ‡ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤ĢāĨ‡ā¤¸ ā¤Ąā¤ŋ⤟āĨ‡ā¤•āĨā¤ļ⤍ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨā¤¯ā¤žā¤¨ā¤‚⤤⤰ ā¤ĢāĨ‡ā¤ļā¤ŋ⤝⤞ ⤰āĨ‡ā¤•⤗āĨā¤¨ā¤ŋā¤ļā¤¨ā¤¸ā¤žā¤ āĨ€ ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ⤠āĨ‡ā¤ĩ⤞āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛, ⤤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤍ā¤ĩāĨ€ā¤¨ ⤞āĨ‹ā¤•ā¤žā¤‚ā¤Žā¤§āĨā¤¯āĨ‡ ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ˆā¤˛.", + "facial_recognition_job_description": "ā¤ļāĨ‹ā¤§ā¤˛āĨ‡ā¤˛āĨ‡ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤞āĨ‹ā¤•ā¤žā¤‚ā¤Žā¤§āĨā¤¯āĨ‡ ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž. ā¤šāĨ‡ ⤚⤰⤪ ⤚āĨ‡ā¤šā¤°ā¤ž ā¤ļāĨ‹ā¤§ā¤ŖāĨ‡ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨā¤¯ā¤žā¤¨ā¤‚⤤⤰ ā¤šā¤žā¤˛ā¤¤āĨ‡. \"⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž\" (ā¤ĒāĨā¤¨āĨā¤šā¤ž) ⤏⤰āĨā¤ĩ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤕āĨā¤˛ā¤¸āĨā¤Ÿā¤° ⤕⤰. \"ā¤—ā¤šā¤žā¤ŗ\" ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ⤕⤰⤤āĨ‡ ⤜āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¨ā¤žā¤šāĨ€.", + "failed_job_command": "{command} ā¤•ā¤Žā¤žā¤‚ā¤Ą ⤜āĨ‰ā¤Ŧā¤¸ā¤žā¤ āĨ€ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€ ā¤ā¤žā¤˛ā¤ž: {job}", + "force_delete_user_warning": "ā¤¸ā¤žā¤ĩā¤§ā¤žā¤¨: ā¤šāĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤆⤪ā¤ŋ ⤏⤰āĨā¤ĩ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤¤ā¤žā¤Ŧā¤Ąā¤¤āĨ‹ā¤Ŧ ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•āĨ‡ā¤˛. ā¤šāĨ‡ ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ ⤆⤪ā¤ŋ ā¤Ģā¤žā¤¯ā¤˛āĨ€ ā¤ĒāĨā¤¨ā¤°āĨā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤.", + "image_format": "ā¤ĢāĨ‰ā¤°ā¤ŽāĨ…ā¤Ÿ", + "image_format_description": "WebP JPEG ā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤˛ā¤šā¤žā¤¨ ā¤Ģā¤žā¤¯ā¤˛āĨ€ ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤤āĨ‡, ā¤Ē⤰⤂⤤āĨ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¸ ā¤šā¤ŗāĨ‚ ⤅⤏⤤āĨ‡.", + "image_fullsize_description": "ā¤āĨ‚ā¤Ž ⤇⤍ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤ĩā¤žā¤Ē⤰⤞āĨā¤¯ā¤ž ā¤œā¤žā¤Ŗā¤žā¤ąāĨā¤¯ā¤ž ⤏āĨā¤ŸāĨā¤°ā¤ŋā¤Ē ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤žā¤¸ā¤š ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤†ā¤•ā¤žā¤°ā¤žā¤šāĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "image_fullsize_enabled": "ā¤ĒāĨ‚⤰āĨā¤Ŗ-ā¤†ā¤•ā¤žā¤°ā¤žā¤¤āĨ€ā¤˛ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤āĨ€", + "image_fullsize_enabled_description": "ā¤ĩāĨ‡ā¤Ŧ-ā¤ĢāĨā¤°āĨ‡ā¤‚ā¤Ąā¤˛āĨ€ ⤍⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĢāĨ‰ā¤°ā¤ŽāĨ…ā¤Ÿā¤¸ā¤žā¤ āĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ-ā¤†ā¤•ā¤žā¤°ā¤žā¤šāĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž. ⤜āĨ‡ā¤ĩāĨā¤šā¤ž \"embedded preview\" ā¤šā¤žā¤˛āĨā¤†ā¤¸āĨ‡ā¤˛ ⤤āĨ‡ā¤ĩāĨā¤šā¤ž, \"embedded preview\" ā¤ĨāĨ‡ā¤Ÿ ⤰āĨ‚ā¤Ēā¤žā¤‚ā¤¤ā¤°ā¤Ŗā¤žā¤ļā¤ŋā¤ĩā¤žā¤¯ ā¤ĩā¤žā¤Ē⤰⤞āĨ‡ ā¤œā¤žā¤¤ā¤žā¤¤. JPEG ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤ĩāĨ‡ā¤Ŧ-ā¤ĢāĨā¤°āĨ‡ā¤‚ā¤Ąā¤˛āĨ€ ā¤ĢāĨ‰ā¤°ā¤ŽāĨ…ā¤Ÿā¤ĩ⤰ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤šāĨ‹ā¤¤ ā¤¨ā¤žā¤šāĨ€.", + "image_fullsize_quality_description": "āĨ§-āĨ§āĨĻāĨĻ ā¤Ē⤰āĨā¤¯ā¤‚⤤ ā¤ĒāĨ‚⤰āĨā¤Ŗ-ā¤†ā¤•ā¤žā¤°ā¤žā¤¤āĨ€ā¤˛ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž. ā¤œā¤žā¤¸āĨā¤¤ ⤤āĨ‡ā¤ĩāĨā¤šā¤ĄāĨ‡ ā¤šā¤žā¤‚ā¤—ā¤˛āĨ‡, ā¤Ē⤰⤂⤤āĨ ā¤ŽāĨ‹ā¤ āĨā¤¯ā¤ž ā¤Ģā¤žā¤¯ā¤˛āĨ€ ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤤āĨ‡.", + "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 ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž sRGB ā¤Žā¤§āĨā¤¯āĨ‡ā¤š ⤠āĨ‡ā¤ĩ⤞āĨ€ ā¤œā¤žā¤¤ā¤žā¤¤.", + "image_preview_description": "ā¤ā¤•ā¤š ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤Ēā¤žā¤šā¤¤ā¤žā¤¨ā¤ž ⤆⤪ā¤ŋ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋā¤‚ā¤—ā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤞āĨ€ ā¤œā¤žā¤Ŗā¤žā¤°āĨ€, ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ļā¤ŋ⤞āĨ‡ā¤˛āĨ€ ā¤Žā¤§āĨā¤¯ā¤Ž ā¤†ā¤•ā¤žā¤°ā¤žā¤šāĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "image_preview_quality_description": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤ĩ⤞āĨ‹ā¤•⤍ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž 1–100: ⤜ā¤ŋ⤤⤕āĨ€ ā¤‰ā¤šāĨā¤š, ⤤ā¤ŋ⤤⤕āĨ€ ā¤šā¤žā¤‚ā¤—ā¤˛āĨ€; ā¤Ģā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ā¤ĩā¤žā¤ĸ⤤āĨ‹ ⤆⤪ā¤ŋ āĨ˛ā¤Ē⤚āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ⤕āĨā¤ˇā¤Žā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡. ā¤•ā¤ŽāĨ€ ā¤ŽāĨ‚⤞āĨā¤¯ ⤏āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤ĒāĨā¤°ā¤­ā¤žā¤ĩā¤ŋ⤤ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡.", + "image_preview_title": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤ĩ⤞āĨ‹ā¤•⤍ ā¤ĩā¤ŋ⤍āĨā¤¯ā¤žā¤¸", + "image_quality": "⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž", + "image_resolution": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤ŽāĨ‡ā¤šāĨ€ ⤏āĨā¤Ē⤎āĨā¤Ÿā¤¤ā¤ž", + "image_resolution_description": "ā¤‰ā¤šāĨā¤š ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤅⤧ā¤ŋ⤕ ⤤ā¤Ēā¤ļāĨ€ā¤˛ ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ⤤āĨā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤œā¤žā¤¸āĨā¤¤ ā¤ĩāĨ‡ā¤ŗ ⤘āĨ‡ā¤¤ā¤‚, ā¤Ģā¤žā¤‡ā¤˛ ā¤¸ā¤žā¤ˆā¤œ ā¤ŽāĨ‹ā¤ āĨ€ ā¤šāĨ‹ā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤…â€āĨ…ā¤Ē⤚āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ⤕āĨā¤ˇā¤Žā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡.", + "image_settings": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "image_settings_description": "⤉⤤āĨā¤Ē⤍āĨā¤¨ ā¤ā¤žā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤žā¤‚ā¤šāĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤆⤪ā¤ŋ ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "image_thumbnail_description": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤¸ā¤ŽāĨ‚ā¤š ā¤Ēā¤žā¤šā¤¤ā¤žā¤¨ā¤ž ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ļā¤žā¤–ā¤ĩ⤞āĨ‡ā¤˛āĨ‡ ā¤˛ā¤šā¤žā¤¨ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "image_thumbnail_quality_description": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž (1–100): ⤜ā¤ŋ⤤⤕āĨ€ ā¤œā¤žā¤¸āĨā¤¤, ⤤ā¤ŋ⤤⤕āĨ€ ā¤šā¤žā¤‚ā¤—ā¤˛āĨ€; ā¤Ē⤰⤂⤤āĨ ā¤Ģā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° ā¤ĩā¤žā¤ĸ⤤āĨ‹ ⤆⤪ā¤ŋ āĨ˛ā¤Ē⤚āĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ⤕āĨā¤ˇā¤Žā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡.", + "image_thumbnail_title": "⤞⤘āĨā¤°āĨ‚ā¤Ē ā¤ĩā¤ŋ⤍āĨā¤¯ā¤žā¤¸", + "job_concurrency": "{job} ā¤ā¤•ā¤°āĨ‚ā¤Ēā¤¤ā¤ž", + "job_created": "ā¤•ā¤žā¤°āĨā¤¯ ā¤¤ā¤¯ā¤žā¤° ā¤ā¤žā¤˛āĨ‡", + "job_not_concurrency_safe": "ā¤šāĨ‡ ā¤•ā¤žā¤°āĨā¤¯ ā¤¸ā¤Žā¤žā¤‚ā¤¤ā¤°ā¤Ē⤪āĨ‡ ā¤šā¤žā¤˛ā¤ĩ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ā¤¨ā¤žā¤šāĨ€.", + "job_settings": "ā¤•ā¤žā¤°āĨā¤¯ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "job_settings_description": "ā¤•ā¤žā¤°āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ā¤¸ā¤Žā¤žā¤‚ā¤¤ā¤°ā¤¤ā¤ž ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "job_status": "ā¤•ā¤žā¤°āĨā¤¯ ⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "jobs_delayed": "{jobCount, plural, other {# ā¤ĩā¤ŋ⤞⤂ā¤Ŧā¤ŋ⤤}}", + "jobs_failed": "{jobCount, plural, other {# ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€}}", + "library_created": "⤏⤂⤗āĨā¤°ā¤š ā¤¤ā¤¯ā¤žā¤° ⤕āĨ‡ā¤˛ā¤ž: {library}", + "library_deleted": "⤏⤂⤗āĨā¤°ā¤š ā¤šā¤Ÿā¤ĩā¤˛ā¤ž", + "library_import_path_description": "ā¤†ā¤¯ā¤žā¤¤ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ā¤šā¤ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ⤆⤪ā¤ŋ ⤤āĨā¤¯ā¤žā¤Žā¤§āĨ€ā¤˛ ⤉ā¤Ēā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°āĨā¤¸ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤¸ā¤žā¤ āĨ€ ⤏āĨā¤•āĨ…⤍ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛.", + "library_scanning": "⤍ā¤ŋā¤¯ā¤Žā¤ŋ⤤ ⤏āĨā¤•āĨ…⤍ā¤ŋ⤂⤗", + "library_scanning_description": "⤍ā¤ŋā¤¯ā¤Žā¤ŋ⤤ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ā¤ŋ⤂⤗ ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŋ⤗⤰ ā¤•ā¤°ā¤ž", + "library_scanning_enable_description": "⤍ā¤ŋā¤¯ā¤Žā¤ŋ⤤ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ā¤ŋ⤂⤗ ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "library_settings": "ā¤Ŧā¤žā¤šāĨā¤¯ ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ⤏⤂⤗āĨā¤°ā¤š", + "library_settings_description": "ā¤Ŧā¤žā¤šāĨā¤¯ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "library_tasks_description": "ā¤Ŧā¤žā¤šāĨā¤¯ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ā¤¤āĨ€ā¤˛ ⤍ā¤ĩāĨ€ā¤¨ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤Ŧā¤Ļ⤞⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤¸ā¤žā¤ āĨ€ ⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤ž", + "library_watching_enable_description": "ā¤Ģā¤žā¤‡ā¤˛ ā¤Ŧā¤Ļā¤˛ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤Ŧā¤žā¤šāĨā¤¯ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤍ā¤ŋ⤰āĨ€ā¤•āĨā¤ˇā¤Ŗ ā¤•ā¤°ā¤ž", + "library_watching_settings": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤍ā¤ŋ⤰āĨ€ā¤•āĨā¤ˇā¤Ŗ (ā¤ĒāĨā¤°ā¤žā¤¯āĨ‹ā¤—ā¤ŋ⤕)", + "library_watching_settings_description": "ā¤Ŧā¤Ļ⤞ ā¤ā¤žā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ā¤ĩ⤰ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤍ā¤ŋā¤—ā¤°ā¤žā¤ŖāĨ€ ā¤•ā¤°ā¤ž", + "logging_enable_description": "⤞āĨ‰ā¤—ā¤ŋ⤂⤗ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "logging_level_description": "⤏⤕āĨā¤ˇā¤Ž ā¤ā¤žā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤ĩā¤žā¤Ē⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨ‹ā¤Ŗā¤¤ā¤ž ⤞āĨ‰ā¤— ⤏āĨā¤¤ā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž.", + "logging_settings": "⤞āĨ‰ā¤—ā¤ŋ⤂⤗", + "machine_learning_clip_model": "CLIP ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛", + "machine_learning_clip_model_description": "⤏āĨ‚ā¤šāĨ€ā¤Ŧā¤ĻāĨā¤§ CLIP ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛ā¤šāĨ‡ ā¤¨ā¤žā¤ĩ ⤝āĨ‡ā¤ĨāĨ‡. ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛ ā¤Ŧā¤Ļ⤞⤞āĨā¤¯ā¤žā¤ĩ⤰ ⤏⤰āĨā¤ĩ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ â€˜ā¤¸āĨā¤Žā¤žā¤°āĨā¤Ÿ ā¤ļāĨ‹ā¤§â€™ ⤍āĨ‹ā¤•⤰āĨ€ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤šā¤žā¤˛ā¤ĩā¤ž.", + "machine_learning_duplicate_detection": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ļāĨ‹ā¤§", + "machine_learning_duplicate_detection_enabled": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤓⤺⤖ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "machine_learning_duplicate_detection_enabled_description": "⤅⤕āĨā¤ˇā¤Ž ā¤…ā¤¸ā¤¤ā¤žā¤¨ā¤žā¤šāĨ€ ⤅⤗ā¤ĻāĨ€ ā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ⤏⤰āĨā¤ĩ ⤆⤏āĨā¤Ĩā¤žā¤Ēā¤¨ā¤žā¤‚ā¤šāĨ€ ā¤ĄāĨ€-ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛āĨ€ ā¤œā¤žā¤¤āĨ€ā¤˛.", + "machine_learning_duplicate_detection_setting_description": "ā¤¸ā¤‚ā¤­ā¤žā¤ĩāĨā¤¯ ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤞ā¤ŋā¤ĒāĨ€ ā¤ļāĨ‹ā¤§ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ CLIP ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ąā¤ŋ⤂⤗ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "machine_learning_enabled": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "machine_learning_enabled_description": "⤅⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸, ā¤–ā¤žā¤˛āĨ€ā¤˛ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤•ā¤ĄāĨ‡ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇ ⤕⤰āĨ‚⤍ ⤏⤰āĨā¤ĩ ā¤ā¤Žā¤ā¤˛ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝ ā¤šāĨ‹ā¤¤āĨ€ā¤˛.", + "machine_learning_facial_recognition": "⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤šāĨ€ ⤓⤺⤖", + "machine_learning_facial_recognition_description": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤žā¤‚ā¤Žā¤§āĨ€ā¤˛ ⤚āĨ‡ā¤šāĨ‡ā¤°āĨ‡ ā¤ļāĨ‹ā¤§ā¤ŖāĨ‡, ⤓⤺⤖⤪āĨ‡ ⤆⤪ā¤ŋ ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ⤕⤰⤪āĨ‡", + "machine_learning_facial_recognition_model": "⤚āĨ‡ā¤šā¤°ā¤ž ⤓⤺⤖ ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛", + "machine_learning_facial_recognition_model_description": "ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛ ā¤†ā¤•ā¤žā¤°ā¤žā¤šāĨā¤¯ā¤ž ⤅ā¤ĩ⤰āĨ‹ā¤šāĨ€ ⤕āĨā¤°ā¤Žā¤žā¤¤ ⤏āĨ‚ā¤šāĨ€ā¤Ŧā¤ĻāĨā¤§ ā¤†ā¤šāĨ‡ā¤¤. ā¤ŽāĨ‹ā¤ āĨ‡ ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛āĨā¤¸ ⤧āĨ€ā¤ŽāĨ‡ ā¤…ā¤¸ā¤¤ā¤žā¤¤ ⤆⤪ā¤ŋ ā¤œā¤žā¤¸āĨā¤¤ ⤏āĨā¤ŽāĨƒā¤¤āĨ€ ā¤ĩā¤žā¤Ēā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ⤉⤤āĨā¤¤ā¤Ž ⤍ā¤ŋā¤•ā¤žā¤˛ ā¤ĻāĨ‡ā¤¤ā¤žā¤¤. ⤞⤕āĨā¤ˇā¤žā¤¤ ⤠āĨ‡ā¤ĩā¤ž ⤕āĨ€ ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛ ā¤Ŧā¤Ļ⤞⤞āĨā¤¯ā¤žā¤¨ā¤‚⤤⤰ ⤏⤰āĨā¤ĩ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ‘ā¤ĢāĨ‡ā¤¸ ā¤Ąā¤ŋ⤟āĨ‡ā¤•āĨā¤ļā¤¨â€™ ā¤•ā¤žā¤°āĨā¤¯ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤šā¤žā¤˛ā¤ĩā¤žā¤ĩāĨ‡ ā¤˛ā¤žā¤—āĨ‡ā¤˛.", + "machine_learning_facial_recognition_setting": "⤚āĨ‡ā¤šā¤°ā¤ž ⤓⤺⤖ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "machine_learning_facial_recognition_setting_description": "⤅⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸, ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤚āĨ‡ā¤šā¤°ā¤ž ⤓⤺⤖⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ā¤šāĨ‹ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤ ⤆⤪ā¤ŋ ā¤ā¤•āĨā¤¸ā¤ĒāĨā¤˛āĨ‹ā¤° ā¤ĒāĨ‡ā¤œā¤Žā¤§āĨ€ā¤˛ â€˜ā¤˛āĨ‹ā¤•’ ā¤ĩā¤ŋā¤­ā¤žā¤— ā¤­ā¤°ā¤˛ā¤ž ā¤œā¤žā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€.", + "machine_learning_max_detection_distance": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤ļāĨ‹ā¤§ ⤅⤂⤤⤰", + "machine_learning_max_detection_distance_description": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤓⤺⤖⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĻāĨ‹ā¤¨ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤žā¤‚ā¤Žā¤§āĨ€ā¤˛ ā¤•ā¤Žā¤žā¤˛ ⤅⤂⤤⤰ 0.001 ⤤āĨ‡ 0.1 ā¤Ē⤰āĨā¤¯ā¤‚⤤ ā¤…ā¤¸ā¤žā¤ĩāĨ‡. ā¤œā¤žā¤¸āĨā¤¤ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤅⤧ā¤ŋ⤕ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤ļāĨ‹ā¤§ā¤¤āĨ€ā¤˛, ā¤Ē⤰⤂⤤āĨ ⤖āĨ‹ā¤ŸāĨ‡ ā¤¸ā¤•ā¤žā¤°ā¤žā¤¤āĨā¤Žā¤• ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤ĻāĨ‡ā¤–āĨ€ā¤˛ ā¤šāĨ‹ā¤Š ā¤ļā¤•ā¤¤ā¤žā¤¤.", + "machine_learning_max_recognition_distance": "ā¤œā¤žā¤¸āĨā¤¤āĨ€ā¤¤ ā¤œā¤žā¤¸āĨā¤¤ ⤓⤺⤖ ⤅⤂⤤⤰", + "machine_learning_max_recognition_distance_description": "ā¤¸ā¤Žā¤žā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¸ā¤Žā¤œā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĻāĨ‹ā¤¨ ⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤Žā¤§āĨ€ā¤˛ ā¤•ā¤Žā¤žā¤˛ ⤅⤂⤤⤰ 0 ⤤āĨ‡ 2 ā¤Ļā¤°ā¤ŽāĨā¤¯ā¤žā¤¨ ⤅⤏⤤āĨ‡. ā¤šāĨ‡ ā¤•ā¤ŽāĨ€ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ā¤ĻāĨ‹ā¤¨ ā¤ĩāĨ‡ā¤—⤺āĨā¤¯ā¤ž ⤞āĨ‹ā¤•ā¤žā¤‚ā¤¨ā¤ž ā¤ā¤•ā¤žā¤š ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤚ā¤ŋ⤍āĨā¤šā¤žā¤‚⤕ā¤ŋ⤤ ā¤šāĨ‹ā¤ŖāĨā¤¯ā¤žā¤Ēā¤žā¤¸āĨ‚⤍ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Ŧ⤂⤧ ā¤šāĨ‹ā¤¤āĨ‹, ⤤⤰ ā¤œā¤žā¤¸āĨā¤¤ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ā¤ā¤•ā¤žā¤š ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤˛ā¤ž ā¤ĻāĨ‹ā¤¨ ⤭ā¤ŋ⤍āĨā¤¨ ⤞āĨ‹ā¤•ā¤žā¤‚ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ‡ ⤞āĨ‡ā¤Ŧ⤞ ā¤šāĨ‹ā¤ŖāĨā¤¯ā¤žā¤Ēā¤žā¤¸āĨ‚⤍ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Ŧ⤂⤧ ā¤šāĨ‹ā¤¤āĨ‹. ā¤ā¤•ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤˛ā¤ž ā¤ĻāĨ‹ā¤¨ ā¤­ā¤žā¤—ā¤žā¤‚ā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤ŋā¤­ā¤žā¤—ā¤ŖāĨā¤¯ā¤žā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤ĻāĨ‹ā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤‚ā¤¨ā¤ž ā¤ā¤•ā¤¤āĨā¤° ⤕⤰⤪āĨ‡ ⤏āĨ‹ā¤ĒāĨ‡ ⤅⤏⤞āĨā¤¯ā¤žā¤¨āĨ‡, ā¤ļ⤕āĨā¤¯ ⤤āĨ‡ā¤ĩāĨā¤šā¤ž ā¤•ā¤ŽāĨ€ ā¤ĨāĨā¤°āĨ‡ā¤ļāĨ‹ā¤˛āĨā¤Ą ⤍ā¤ŋā¤ĩā¤Ąā¤ž.", + "machine_learning_min_detection_score": "⤕ā¤ŋā¤Žā¤žā¤¨ ā¤ļāĨ‹ā¤§ ⤗āĨā¤Ŗ", + "machine_learning_min_detection_score_description": "⤚āĨ‡ā¤šā¤°ā¤ž ā¤ļāĨ‹ā¤§ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕ā¤ŋā¤Žā¤žā¤¨ ⤆⤤āĨā¤Žā¤ĩā¤ŋā¤ļāĨā¤ĩā¤žā¤¸ ⤗āĨā¤Ŗā¤žā¤‚⤕ 0 ⤤āĨ‡ 1 ā¤Ļā¤°ā¤ŽāĨā¤¯ā¤žā¤¨ ā¤…ā¤¸ā¤žā¤ĩā¤ž. ā¤•ā¤ŽāĨ€ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤅⤧ā¤ŋ⤕ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤ļāĨ‹ā¤§ā¤¤āĨ€ā¤˛, ā¤Ē⤰⤂⤤āĨ ⤖āĨ‹ā¤ŸāĨ‡ ā¤¸ā¤•ā¤žā¤°ā¤žā¤¤āĨā¤Žā¤• ā¤ĻāĨ‡ā¤–āĨ€ā¤˛ ā¤šāĨ‹ā¤Š ā¤ļā¤•ā¤¤ā¤žā¤¤.", + "machine_learning_min_recognized_faces": "⤕ā¤ŋā¤Žā¤žā¤¨ ⤓⤺⤖⤞āĨ‡ā¤˛āĨ‡ ⤚āĨ‡ā¤šā¤°āĨ‡", + "machine_learning_min_recognized_faces_description": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤓⤺⤖⤞āĨā¤¯ā¤ž ⤗āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤚āĨ‡ā¤šā¤°āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤕ā¤ŋā¤Žā¤žā¤¨ ⤏⤂⤖āĨā¤¯ā¤ž. ā¤šāĨ‡ ā¤ĩā¤žā¤ĸā¤ĩ⤞āĨā¤¯ā¤žā¤¸ ⤚āĨ‡ā¤šā¤°ā¤ž ā¤ā¤•ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤˛ā¤ž ⤍ ⤜āĨ‹ā¤Ąā¤˛ā¤ž ā¤œā¤žā¤ŖāĨā¤¯ā¤žā¤šāĨ€ ā¤ļ⤕āĨā¤¯ā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤¤āĨ‡, ā¤Ē⤰⤂⤤āĨ ⤚āĨā¤•āĨ€ā¤šāĨā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤ā¤•ā¤¤āĨā¤° ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤šāĨ‡ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤ĩā¤žā¤ĸāĨ‚ ā¤ļā¤•ā¤¤ā¤žā¤¤.", + "machine_learning_settings": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "machine_learning_settings_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ⤆⤪ā¤ŋ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "machine_learning_smart_search": "⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ā¤ļāĨ‹ā¤§", + "machine_learning_smart_search_description": "CLIP ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ąā¤ŋ⤂⤗āĨā¤œ ā¤ĩā¤žā¤Ē⤰āĨ‚⤍ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤅⤰āĨā¤Ĩā¤ĒāĨ‚⤰āĨā¤Ŗā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤ļāĨ‹ā¤§ā¤ž", + "machine_learning_smart_search_enabled": "⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤏⤰āĨā¤š ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "machine_learning_smart_search_enabled_description": "⤅⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸, ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤏⤰āĨā¤šā¤¸ā¤žā¤ āĨ€ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤œā¤žā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤.", + "machine_learning_url_description": "ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨ€ URL. ā¤ā¤•ā¤žā¤šāĨ‚⤍ ⤅⤧ā¤ŋ⤕ URL ā¤Ļā¤ŋ⤞āĨā¤¯ā¤žā¤¸, ā¤ĒāĨā¤°ā¤Ĩā¤Ž ⤤āĨ‡ ā¤Ļā¤ŋ⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤕āĨā¤°ā¤Žā¤žā¤¨āĨ‡ ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ā¤ā¤•-ā¤ā¤• ⤕⤰āĨ‚⤍ ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛, ⤜āĨ‹ā¤Ē⤰āĨā¤¯ā¤‚⤤ ⤕āĨ‹ā¤ŖāĨ€ā¤¤ā¤°āĨ€ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ ā¤ĻāĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€ ⤤āĨ‹ā¤Ē⤰āĨā¤¯ā¤‚⤤. ⤜āĨ‡ ⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ ā¤ĻāĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤, ⤤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ⤤āĨ‡ ā¤Ē⤰⤤ ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨ ⤝āĨ‡ā¤ˆā¤Ē⤰āĨā¤¯ā¤‚⤤ ā¤¤ā¤žā¤¤āĨā¤ĒāĨā¤°ā¤¤āĨ‡ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ˆā¤˛.", + "manage_concurrency": "ā¤¸ā¤Žā¤žā¤‚ā¤¤ā¤°ā¤¤ā¤ž ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "manage_log_settings": "⤞āĨ‰ā¤— ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤Ŗ ā¤•ā¤°ā¤ž", + "map_dark_style": "ā¤—ā¤Ąā¤Ļ ā¤ļāĨˆā¤˛āĨ€", + "map_enable_description": "ā¤¨ā¤•ā¤žā¤ļāĨ€ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "map_gps_settings": "ā¤¨ā¤•ā¤žā¤ļā¤ž ⤆⤪ā¤ŋ ⤜āĨ€ā¤ĒāĨ€ā¤ā¤¸ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "map_gps_settings_description": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ā¤ĩ GPS (⤰ā¤ŋā¤ĩāĨā¤šā¤°āĨā¤¸ ⤜āĨā¤¯āĨ‹ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗) ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "map_implications": "ā¤Žā¤žā¤¨ā¤šā¤ŋ⤤āĨā¤° ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ ā¤Ŧā¤žā¤šāĨā¤¯ ā¤Ÿā¤žā¤‡ā¤˛ ⤏āĨ‡ā¤ĩāĨ‡ā¤ļāĨ€ (tiles.immich.cloud) ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‡", + "map_light_style": "ā¤‰ā¤œā¤ŗ ā¤ļāĨˆā¤˛āĨ€", + "map_manage_reverse_geocoding_settings": "⤰ā¤ŋā¤ĩāĨā¤šā¤°āĨā¤¸ ⤜ā¤ŋ⤓⤕āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "map_reverse_geocoding": "⤰ā¤ŋā¤ĩāĨā¤šā¤°āĨā¤¸ ⤜ā¤ŋ⤓⤕āĨ‹ā¤Ąā¤ŋ⤂⤗", + "map_reverse_geocoding_enable_description": "⤰ā¤ŋā¤ĩāĨā¤šā¤°āĨā¤¸ ⤜ā¤ŋ⤓⤕āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "map_reverse_geocoding_settings": "⤰ā¤ŋā¤ĩāĨā¤šā¤°āĨā¤¸ ⤜ā¤ŋ⤓⤕āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "map_settings": "ā¤¨ā¤•ā¤žā¤ļā¤ž", + "map_settings_description": "ā¤¨ā¤•ā¤žā¤ļā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "map_style_description": "style.json ā¤¨ā¤•ā¤žā¤ļā¤ž ā¤ĨāĨ€ā¤Žā¤¸ā¤žā¤ āĨ€ URL", + "memory_cleanup_job": "⤏āĨā¤ŽāĨƒā¤¤āĨ€ ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž", + "memory_generate_job": "⤏āĨā¤ŽāĨƒā¤¤āĨ€ ⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤āĨ€", + "metadata_extraction_job": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤•ā¤žā¤ĸā¤ž", + "metadata_extraction_job_description": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤏⤂ā¤Ē⤤āĨā¤¤āĨ€ā¤Žā¤§āĨ‚⤍ GPS, ⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤šāĨ€ ā¤ĩ ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ā¤¯ā¤žā¤‚ā¤¸ā¤žā¤°ā¤–āĨ€ ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤Žā¤ŋ⤺ā¤ĩā¤ž", + "metadata_faces_import_setting": "⤚āĨ‡ā¤šā¤°ā¤ž ā¤†ā¤¯ā¤žā¤¤ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "metadata_faces_import_setting_description": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž EXIF ā¤ĄāĨ‡ā¤Ÿā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ⤆⤪ā¤ŋ ā¤¸ā¤žā¤‡ā¤Ąā¤•ā¤žā¤° ā¤Ģā¤žā¤‡ā¤˛ā¤Žā¤§āĨ‚⤍ ⤚āĨ‡ā¤šā¤°āĨ‡ ā¤†ā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "metadata_settings": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "metadata_settings_description": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "migration_job": "⤏āĨā¤Ĩā¤žā¤¨ā¤žā¤‚ā¤¤ā¤°ā¤Ŗ", + "migration_job_description": "⤏⤂ā¤Ē⤤āĨā¤¤āĨ€ ⤆⤪ā¤ŋ ⤚āĨ‡ā¤šā¤°āĨâ€ā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤‚ā¤°ā¤šā¤¨āĨ‡ā¤¤ ⤏āĨā¤Ĩā¤˛ā¤žā¤‚ā¤¤ā¤°ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "nightly_tasks_cluster_faces_setting_description": "⤍ā¤ĩāĨ€ā¤¨ ⤓⤺⤖⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤚āĨ‡ā¤šā¤°āĨâ€ā¤¯ā¤žā¤‚ā¤ĩ⤰ ⤚āĨ‡ā¤šā¤°āĨ‡ ⤓⤺⤖⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤šā¤žā¤˛ā¤ĩā¤ž", + "nightly_tasks_cluster_new_faces_setting": "⤍ā¤ĩāĨ€ā¤¨ ⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž", + "nightly_tasks_database_cleanup_setting": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž ā¤•ā¤žā¤°āĨā¤¯āĨ‡", + "nightly_tasks_database_cleanup_setting_description": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ā¤Žā¤§āĨ‚⤍ ⤜āĨā¤¨āĨ€ ā¤ĩ ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "nightly_tasks_generate_memories_setting": "⤆⤠ā¤ĩ⤪āĨ€ ⤍ā¤ŋ⤰āĨā¤Žā¤žā¤Ŗ ā¤•ā¤°ā¤ž", + "nightly_tasks_generate_memories_setting_description": "⤏⤂ā¤Ē⤤āĨā¤¤āĨ€ā¤Žā¤§āĨ‚⤍ ⤍ā¤ĩāĨ€ā¤¨ ⤆⤠ā¤ĩ⤪āĨ€ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "nightly_tasks_missing_thumbnails_setting": "⤉ā¤Ē⤞ā¤ŦāĨā¤§ ⤍⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "nightly_tasks_missing_thumbnails_setting_description": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤍⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤏⤂ā¤Ē⤤āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€ ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ⤠āĨ‡ā¤ĩā¤ž", + "nightly_tasks_settings": "ā¤°ā¤žā¤¤āĨā¤°ā¤ŋā¤•ā¤žā¤ŗāĨ€ā¤¨ ā¤•ā¤žā¤°āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "nightly_tasks_settings_description": "ā¤°ā¤žā¤¤āĨā¤°āĨ€ā¤šāĨā¤¯ā¤ž ā¤•ā¤žā¤°āĨā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍ ā¤•ā¤°ā¤ž", + "nightly_tasks_start_time_setting": "⤏āĨā¤°āĨā¤ĩā¤žā¤¤ ā¤ĩāĨ‡ā¤ŗ", + "nightly_tasks_start_time_setting_description": "⤏⤰āĨā¤ĩ⤰⤚āĨ€ ā¤°ā¤žā¤¤āĨā¤°āĨ€ā¤šāĨ€ ā¤•ā¤žā¤°āĨā¤¯āĨ‡ ⤏āĨā¤°āĨ‚ ā¤šāĨ‹ā¤ŖāĨā¤¯ā¤žā¤šāĨ€ ā¤ĩāĨ‡ā¤ŗ", + "nightly_tasks_sync_quota_usage_setting": "⤕āĨ‹ā¤Ÿā¤ž ā¤ĩā¤žā¤Ē⤰ ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "nightly_tasks_sync_quota_usage_setting_description": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ā¤ĩā¤žā¤Ēā¤°ā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤¸ā¤‚ā¤šā¤¯ā¤¨ ⤕āĨ‹ā¤Ÿā¤ž ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "no_paths_added": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤Žā¤žā¤°āĨā¤— ⤜āĨ‹ā¤Ąā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "no_pattern_added": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ⤜āĨ‹ā¤Ąā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "note_apply_storage_label_previous_assets": "⤟āĨ€ā¤Ē: ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅āĨ…⤏āĨ‡ā¤ŸāĨā¤¸ā¤ĩ⤰ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€, ā¤šā¤žā¤˛ā¤ĩā¤ž", + "note_cannot_be_changed_later": "⤍āĨ‹ā¤Ÿ: ā¤šāĨ‡ ⤍⤂⤤⤰ ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€!", + "notification_email_from_address": "ā¤ĒāĨā¤°āĨ‡ā¤ˇā¤•ā¤žā¤šā¤ž ā¤Ē⤤āĨā¤¤ā¤ž", + "notification_email_from_address_description": "ā¤ĒāĨā¤°āĨ‡ā¤ˇā¤• ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ē⤤āĨā¤¤ā¤ž, ⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗā¤žā¤°āĨā¤Ĩ: \"Immich Photo Server noreply@example.com\". ā¤–ā¤žā¤¤āĨā¤°āĨ€ ā¤•ā¤°ā¤ž ⤕āĨ€ ⤆ā¤Ē⤪ ⤜āĨā¤¯ā¤žā¤Ēā¤žā¤¸āĨ‚⤍ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩ⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤†ā¤šāĨ‡ ⤤āĨ‹ā¤š ā¤Ē⤤āĨā¤¤ā¤ž ā¤ĩā¤žā¤Ēā¤°ā¤¤ā¤ž.", + "notification_email_host_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šā¤ž ā¤šāĨ‹ā¤¸āĨā¤Ÿ (⤉ā¤Ļā¤ž. smtp.immich.app)", + "notification_email_ignore_certificate_errors": "ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ⤤āĨā¤°āĨā¤ŸāĨ€ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "notification_email_ignore_certificate_errors_description": "TLS ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤Ēā¤Ąā¤¤ā¤žā¤ŗā¤ŖāĨ€ ⤤āĨā¤°āĨā¤ŸāĨ€ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇā¤ŋ⤤ ā¤•ā¤°ā¤ž (ā¤ļā¤ŋā¤Ģā¤žā¤°ā¤¸āĨ€ā¤¯ ā¤¨ā¤žā¤šāĨ€)", + "notification_email_password_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•ā¤°ā¤Ŗā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤪āĨā¤¯ā¤žā¤šā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "notification_email_port_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šā¤ž ā¤ĒāĨ‹ā¤°āĨā¤Ÿ (⤉ā¤Ļā¤ž. 25, 465 ⤕ā¤ŋ⤂ā¤ĩā¤ž 587)", + "notification_email_sent_test_email_button": "ā¤šā¤žā¤šā¤ŖāĨ€ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩā¤ž ⤆⤪ā¤ŋ ⤜⤤⤍ ā¤•ā¤°ā¤ž", + "notification_email_setting_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤Ēā¤žā¤ ā¤ĩ⤪āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "notification_email_test_email": "ā¤šā¤žā¤šā¤ŖāĨ€ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩā¤ž", + "notification_email_test_email_failed": "ā¤šā¤žā¤šā¤ŖāĨ€ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩ⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€ - ⤕āĨƒā¤Ēā¤¯ā¤ž ⤆ā¤Ē⤞āĨā¤¯ā¤ž ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤤ā¤Ēā¤žā¤¸ā¤ž", + "notification_email_test_email_sent": "ā¤ā¤• ā¤šā¤žā¤šā¤ŖāĨ€ ā¤ˆā¤ŽāĨ‡ā¤˛ {email} ā¤¯ā¤ž ā¤Ē⤤āĨā¤¤āĨā¤¯ā¤žā¤ĩ⤰ ā¤Ēā¤žā¤ ā¤ĩ⤪āĨā¤¯ā¤žā¤¤ ⤆⤞āĨ‡ ā¤†ā¤šāĨ‡. ⤕āĨƒā¤Ēā¤¯ā¤ž ⤤āĨā¤Žā¤šā¤ž ⤇⤍ā¤ŦāĨ‰ā¤•āĨā¤¸ ⤤ā¤Ēā¤žā¤¸ā¤ž.", + "notification_email_username_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•āĨƒā¤¤ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ā¤ĩā¤žā¤Ē⤰⤪āĨā¤¯ā¤žā¤šāĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤žā¤¨ā¤žā¤ĩ", + "notification_enable_email_notifications": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "notification_settings": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "notification_settings_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ā¤¸ā¤š ⤏āĨ‚ā¤šā¤¨ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "oauth_auto_launch": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "oauth_auto_launch_description": "⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤žā¤ĩ⤰ ā¤œā¤žā¤¤ā¤žā¤š OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨā¤°ā¤ĩā¤žā¤š ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "oauth_auto_register": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ⤍āĨ‹ā¤‚ā¤Ļ⤪āĨ€ ā¤•ā¤°ā¤ž", + "oauth_auto_register_description": "OAuth ā¤¸ā¤š ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ⤍ā¤ĩāĨ€ā¤¨ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ⤍āĨ‹ā¤‚ā¤Ļ⤪āĨ€ ā¤•ā¤°ā¤ž", + "oauth_button_text": "ā¤Ŧ⤟⤪ ā¤Žā¤œā¤•āĨ‚⤰", + "oauth_client_secret_description": "PKCE (ā¤ĒāĨā¤°āĨ‚ā¤Ģ ⤕āĨ€ ā¤ĢāĨ‰ā¤° ⤕āĨ‹ā¤Ą ā¤ā¤•āĨā¤¸ā¤šāĨ‡ā¤‚ā¤œ) OAuth ā¤ĒāĨā¤°ā¤Ļā¤žā¤¤āĨā¤¯ā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ⤍⤏⤞āĨā¤¯ā¤žā¤¸ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•", + "oauth_enable_description": "OAuth ā¤¸ā¤š ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤•ā¤°ā¤ž", + "oauth_mobile_redirect_uri": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ URI", + "oauth_mobile_redirect_uri_override": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤ˆā¤˛ ⤰āĨ€ā¤Ąā¤žā¤¯ā¤°āĨ‡ā¤•āĨā¤Ÿ URI ⤅⤧ā¤ŋ⤞āĨ‡ā¤–⤍", + "oauth_mobile_redirect_uri_override_description": "⤜āĨ‡ā¤ĩāĨā¤šā¤ž OAuth ā¤ĒāĨā¤°ā¤Ļā¤žā¤¤ā¤ž ā¤ŽāĨ‹ā¤Ŧā¤žā¤ˆā¤˛ URI (⤉ā¤Ļā¤žā¤šā¤°ā¤Ŗā¤žā¤°āĨā¤Ĩ “{callback}”) ā¤˛ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€, ⤤āĨ‡ā¤ĩāĨā¤šā¤ž ā¤šāĨ‡ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "oauth_role_claim": "⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž ā¤Ļā¤žā¤ĩā¤ž", + "oauth_role_claim_description": "ā¤¯ā¤ž ⤕āĨā¤˛āĨ‡ā¤Žā¤šāĨā¤¯ā¤ž ⤉ā¤Ē⤏āĨā¤Ĩā¤ŋ⤤āĨ€ā¤ĩ⤰āĨ‚⤍ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•āĨ€ā¤¯ ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļ ā¤ĻāĨā¤¯ā¤ž. ā¤¯ā¤ž ⤕āĨā¤˛āĨ‡ā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ‘user’ ⤕ā¤ŋ⤂ā¤ĩā¤ž ‘admin’ ā¤šāĨ€ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤅⤏āĨ‚ ā¤ļā¤•ā¤¤ā¤žā¤¤.", + "oauth_settings": "OAuth", + "oauth_settings_description": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "oauth_settings_more_details": "ā¤¯ā¤ž ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ā¤žā¤Ŧā¤ĻāĨā¤Ļ⤞ ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€, docs ā¤Ēā¤šā¤ž.", + "oauth_storage_label_claim": "⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤Ļā¤žā¤ĩā¤ž", + "oauth_storage_label_claim_description": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨ‡ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤞āĨ‡ā¤Ŧ⤞ ā¤¯ā¤ž ā¤Ļā¤žā¤ĩāĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ŽāĨ‚⤞āĨā¤¯ā¤žā¤ĩ⤰ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž.", + "oauth_storage_quota_claim": "⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž ā¤Ļā¤žā¤ĩā¤ž", + "oauth_storage_quota_claim_description": "ā¤¯ā¤ž ā¤Ļā¤žā¤ĩāĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ŽāĨ‚⤞āĨā¤¯ā¤žā¤ĩ⤰ ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šā¤ž ā¤¸ā¤‚ā¤šā¤¯ā¤¨ ⤕āĨ‹ā¤Ÿā¤ž ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤žāĨ¤", + "oauth_storage_quota_default": "ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž (GiB)", + "oauth_storage_quota_default_description": "⤕āĨā¤˛āĨ‡ā¤Ž ⤍ ā¤Ļā¤ŋ⤞āĨā¤¯ā¤žā¤¸ ā¤ĩā¤žā¤Ē⤰⤪āĨā¤¯ā¤žā¤¤ ⤝āĨ‡ā¤Ŗā¤žā¤°ā¤ž ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤕āĨ‹ā¤Ÿā¤ž (GiB)āĨ¤", + "oauth_timeout": "ā¤ĩā¤ŋ⤍⤂⤤āĨ€ ā¤ĩāĨ‡ā¤ŗ ā¤Žā¤°āĨā¤¯ā¤žā¤Ļā¤ž", + "oauth_timeout_description": "ā¤Žā¤ŋ⤞ā¤ŋ⤏āĨ‡ā¤•⤂ā¤Ļā¤žā¤‚ā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤ŋ⤍⤂⤤āĨā¤¯ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤ĩāĨ‡ā¤ŗā¤¸ā¤Žā¤žā¤ĒāĨā¤¤āĨ€", + "password_enable_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤆⤪ā¤ŋ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ąā¤¨āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤•ā¤°ā¤ž", + "password_settings": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤞āĨ‰ā¤—ā¤ŋ⤍", + "password_settings_description": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤞āĨ‰ā¤—ā¤ŋ⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "paths_validated_successfully": "⤏⤰āĨā¤ĩ ā¤Žā¤žā¤°āĨā¤— ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ⤏⤤āĨā¤¯ā¤žā¤Ēā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ€ ā¤†ā¤šāĨ‡ā¤¤", + "person_cleanup_job": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž ā¤•ā¤žā¤Ž", + "quota_size_gib": "ā¤¸ā¤‚ā¤šā¤¯ ⤕āĨ‹ā¤Ÿā¤ž ā¤†ā¤•ā¤žā¤° (GiB)", + "refreshing_all_libraries": "⤏⤰āĨā¤ĩ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ⤕⤰āĨ€ā¤¤ ā¤†ā¤šāĨ‡", + "registration": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤• ⤍āĨ‹ā¤‚ā¤Ļ⤪āĨ€", + "registration_description": "⤆ā¤Ē⤪ ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ā¤ĩ⤰āĨ€ā¤˛ ā¤Ēā¤šā¤ŋ⤞āĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤†ā¤šā¤žā¤¤, ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤˛ā¤ž ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤• ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ˆā¤˛ ⤆⤪ā¤ŋ ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•āĨ€ā¤¯ ā¤•ā¤žā¤°āĨā¤¯āĨ‡ ⤆ā¤Ē⤞āĨā¤¯ā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤šā¤žā¤¤ā¤žā¤ŗā¤˛āĨ€ ā¤œā¤žā¤¤āĨ€ā¤˛; ⤤⤏āĨ‡ā¤š ⤇⤤⤰ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ⤆ā¤Ē⤪ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤žā¤˛.", + "require_password_change_on_login": "ā¤Ēā¤šā¤ŋ⤞āĨā¤¯ā¤ž ⤞āĨ‰ā¤—ā¤ŋ⤍ā¤ĩ⤰ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤¸ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤪āĨā¤¯ā¤žā¤šāĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•ā¤¤ā¤ž ⤅⤏āĨ‡ā¤˛", + "reset_settings_to_default": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿā¤ĩ⤰ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_settings_to_recent_saved": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤅⤞āĨ€ā¤•ā¤ĄāĨ€ā¤˛ ⤜⤤⤍ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤ĩ⤰ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "scanning_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ ⤕⤰āĨ€ā¤¤ ā¤†ā¤šāĨ‡", + "search_jobs": "⤍āĨ‹ā¤•ā¤ąāĨā¤¯ā¤ž ā¤ļāĨ‹ā¤§ā¤¤ ā¤†ā¤šāĨ‡â€Ļ", + "send_welcome_email": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩā¤ž", + "server_external_domain_settings": "ā¤Ŧā¤žā¤šāĨā¤¯ ā¤ĄāĨ‹ā¤ŽāĨ‡ā¤¨", + "server_external_domain_settings_description": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤¸ā¤žā¤Žā¤žā¤¯ā¤ŋ⤕ ā¤ĻāĨā¤ĩāĨā¤¯ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤ĄāĨ‹ā¤ŽāĨ‡ā¤¨ (⤉ā¤Ļā¤ž. http(s)://)", + "server_public_users": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡", + "server_public_users_description": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤜āĨ‹ā¤Ąā¤¤ā¤žā¤¨ā¤ž ⤏⤰āĨā¤ĩ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤šāĨ€ (ā¤¨ā¤žā¤ĩ ā¤ĩ ā¤ˆā¤ŽāĨ‡ā¤˛) ā¤¯ā¤žā¤ĻāĨ€ ā¤Ļ⤰āĨā¤ļā¤ĩ⤞āĨ€ ā¤œā¤žā¤¤āĨ‡. ⤅⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸, ā¤šāĨ€ ā¤¯ā¤žā¤ĻāĨ€ ā¤Ģ⤕āĨā¤¤ ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ā¤š ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ⤅⤏āĨ‡ā¤˛.", + "server_settings": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "server_settings_description": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "server_welcome_message": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ⤏⤂ā¤ĻāĨ‡ā¤ļ", + "server_welcome_message_description": "⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨƒā¤ˇāĨā¤ ā¤žā¤ĩ⤰ ā¤Ļ⤰āĨā¤ļā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤¤ ⤝āĨ‡ā¤Ŗā¤žā¤°ā¤ž ⤏⤂ā¤ĻāĨ‡ā¤ļāĨ¤", + "sidecar_job": "ā¤¸ā¤žā¤‡ā¤Ąā¤•ā¤žā¤° ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž", + "sidecar_job_description": "ā¤Ģā¤žā¤ˆā¤˛ ⤏ā¤ŋ⤏āĨā¤Ÿā¤Žā¤Žā¤§āĨ‚⤍ ā¤¸ā¤žā¤‡ā¤Ąā¤•ā¤žā¤° ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤ļāĨ‹ā¤§ā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "slideshow_duration_description": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤕ā¤ŋ⤤āĨ€ ⤏āĨ‡ā¤•⤂ā¤Ļ ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļā¤ŋ⤤ ā¤•ā¤°ā¤žā¤¯ā¤šāĨ€", + "smart_search_job_description": "⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ā¤ļāĨ‹ā¤§ā¤žā¤¸ā¤žā¤ āĨ€ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ā¤Žā¤ļāĨ€ā¤¨ ⤞⤰āĨā¤¨ā¤ŋ⤂⤗ ā¤šā¤žā¤˛ā¤ĩā¤ž", + "storage_template_date_time_description": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤•-ā¤ĩāĨ‡ā¤ŗ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤āĨ‡ā¤šāĨā¤¯ā¤ž ⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤āĨ€ ā¤ĩāĨ‡ā¤ŗā¤žā¤šā¤ž ā¤ĩā¤žā¤Ē⤰", + "storage_template_date_time_sample": "ā¤¨ā¤ŽāĨā¤¨ā¤ž ā¤ĩāĨ‡ā¤ŗ {date}", + "storage_template_enable_description": "ā¤¸ā¤‚ā¤š ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ā¤‡ā¤‚ā¤œā¤ŋ⤍ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "storage_template_hash_verification_enabled": "ā¤šāĨ…ā¤ļ ⤏⤤āĨā¤¯ā¤žā¤Ē⤍ ⤏⤕āĨā¤ˇā¤Ž", + "storage_template_hash_verification_enabled_description": "ā¤šāĨ…ā¤ļ ⤏⤤āĨā¤¯ā¤žā¤Ē⤍ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤤āĨ‡; ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Žā¤žā¤‚ā¤šā¤ž ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤅⤰āĨā¤Ĩ ⤍ ⤕⤺⤞āĨā¤¯ā¤žā¤¸ ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝ ⤕⤰āĨ‚ ā¤¨ā¤•ā¤ž", + "storage_template_migration": "ā¤¸ā¤‚ā¤š ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤏āĨā¤Ĩā¤˛ā¤žā¤‚ā¤¤ā¤°", + "storage_template_migration_description": "ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ā¤šā¤žā¤˛āĨ‚ {template} ā¤˛ā¤žā¤—āĨ‚ ā¤•ā¤°ā¤ž", + "storage_template_migration_info": "ā¤¸ā¤‚ā¤š ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ⤏⤰āĨā¤ĩ ā¤ā¤•āĨā¤¸āĨā¤ŸāĨ‡ā¤‚ā¤ļ⤍āĨā¤¸ ⤞⤘āĨ‚ (⤞āĨ‹ā¤…⤰⤕āĨ‡ā¤¸)ā¤Žā¤§āĨā¤¯āĨ‡ ⤰āĨ‚ā¤Ēā¤žā¤‚ā¤¤ā¤°ā¤ŋ⤤ ⤕⤰āĨ‡ā¤˛. ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ ā¤Ŧā¤Ļ⤞ ā¤Ģ⤕āĨā¤¤ ⤍ā¤ĩāĨ€ā¤¨ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ā¤˛ā¤žā¤—āĨ‚ ā¤šāĨ‹ā¤¤āĨ€ā¤˛. ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤žā¤‚ā¤ĩ⤰ ⤰āĨ‡ā¤ŸāĨā¤°āĨ‹-ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ {job} ā¤šā¤žā¤˛ā¤ĩā¤žāĨ¤", + "storage_template_migration_job": "⤏⤂⤗āĨā¤°ā¤š ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤏āĨā¤Ĩā¤˛ā¤žā¤‚ā¤¤ā¤° ⤜āĨ‰ā¤Ŧ", + "storage_template_more_details": "ā¤¯ā¤ž ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ā¤žā¤Ŧā¤ĻāĨā¤Ļ⤞ ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€, ⤏⤂⤗āĨā¤°ā¤š ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤆⤪ā¤ŋ ⤤āĨā¤¯ā¤žā¤šāĨ‡ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤Ēā¤šā¤ž", + "storage_template_onboarding_description_v2": "⤏⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸, ā¤šāĨ€ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤Ē⤰ā¤ŋā¤­ā¤žā¤ˇā¤ŋ⤤ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿā¤šāĨā¤¯ā¤ž ā¤†ā¤§ā¤žā¤°āĨ‡ ā¤Ģā¤žā¤¯ā¤˛āĨ€ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤆⤝āĨ‹ā¤œā¤ŋ⤤ ⤕⤰āĨ‡ā¤˛. ⤅⤧ā¤ŋ⤕ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ā¤¸ā¤žā¤ āĨ€, ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤Ļ⤏āĨā¤¤ā¤ā¤ĩ⤜āĨ€ā¤•⤰⤪ ā¤Ēā¤šā¤ž.", + "storage_template_path_length": "ā¤Žā¤žā¤°āĨā¤—ā¤žā¤šāĨ€ ⤅⤂ā¤Ļā¤žā¤œāĨ‡ ā¤˛ā¤žā¤‚ā¤ŦāĨ€ ā¤Žā¤°āĨā¤¯ā¤žā¤Ļā¤ž: {length, number}/{limit, number}", + "storage_template_settings": "⤏⤂⤗āĨā¤°ā¤š ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ", + "storage_template_settings_description": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ģā¤žā¤¯ā¤˛āĨ€ā¤‚ā¤šāĨā¤¯ā¤ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ⤆⤪ā¤ŋ ā¤¨ā¤žā¤ĩ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "storage_template_user_label": "{label} ā¤šā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šā¤ž ⤏⤂⤗āĨā¤°ā¤š ⤞āĨ‡ā¤Ŧ⤞ ā¤†ā¤šāĨ‡", + "system_settings": "ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "tag_cleanup_job": "⤟āĨ…⤗ ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž", + "template_email_available_tags": "⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿā¤Žā¤§āĨā¤¯āĨ‡ ā¤–ā¤žā¤˛āĨ€ā¤˛ ⤚⤞ (variables) ā¤ĩā¤žā¤Ē⤰āĨ‚ ā¤ļā¤•ā¤¤ā¤ž: {tags}", + "template_email_if_empty": "⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤰ā¤ŋ⤕āĨā¤¤ ⤅⤏⤞āĨā¤¯ā¤žā¤¸, ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤ĩā¤žā¤Ēā¤°ā¤˛ā¤ž ā¤œā¤žā¤ˆā¤˛.", + "template_email_invite_album": "ā¤†ā¤Žā¤‚ā¤¤āĨā¤°ā¤Ŗ ⤅⤞āĨā¤Ŧā¤Ž ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ", + "template_email_preview": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤ĩ⤞āĨ‹ā¤•⤍", + "template_email_settings": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ", + "template_email_update_album": "⤅⤞āĨā¤Ŧā¤Ž ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "template_email_welcome": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤟āĨ‡ā¤ŽāĨā¤ĒāĨā¤˛āĨ‡ā¤Ÿ", + "template_settings": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤ŸāĨā¤¸", + "template_settings_description": "⤏āĨ‚ā¤šā¤¨ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ ⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤ŸāĨā¤¸ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "theme_custom_css_settings": "ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ CSS", + "theme_custom_css_settings_description": "Cascading Style Sheets (CSS) ā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ Immich ⤚āĨ‡ ā¤Ąā¤ŋā¤ā¤žā¤‡ā¤¨ ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤Žā¤ŋ⤺⤤āĨ‡.", + "theme_settings": "ā¤ĨāĨ€ā¤Ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "theme_settings_description": "Immich ⤚āĨā¤¯ā¤ž ā¤ĩāĨ‡ā¤Ŧ ā¤‡ā¤‚ā¤Ÿā¤°ā¤ĢāĨ‡ā¤¸ā¤šāĨ‡ ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞⤍ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "thumbnail_generation_job": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "thumbnail_generation_job_description": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤āĨ‡ā¤¸ā¤žā¤ āĨ€ ā¤ŽāĨ‹ā¤ āĨ‡, ā¤˛ā¤šā¤žā¤¨ ⤆⤪ā¤ŋ ā¤ŦāĨā¤˛ā¤° ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤤⤏āĨ‡ā¤š ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤¸ā¤žā¤ āĨ€ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "transcoding_acceleration_api": "ā¤ā¤•āĨā¤¸āĨ‡ā¤˛āĨ‡ā¤°āĨ‡ā¤ļ⤍ API", + "transcoding_acceleration_api_description": "⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤗⤤āĨ€ ā¤ĩā¤žā¤ĸā¤ĩ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ļāĨ€ ⤏⤂ā¤ĩā¤žā¤Ļ ā¤¸ā¤žā¤§ā¤Ŗā¤žā¤°āĨ€ API. ā¤šāĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ‘ā¤ŦāĨ‡ā¤¸āĨā¤Ÿ ⤇ā¤Ģ⤰āĨā¤Ÿâ€™ ā¤†ā¤šāĨ‡: ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€ ā¤ā¤žā¤˛āĨā¤¯ā¤žā¤¸ ⤏āĨ‰ā¤ĢāĨā¤Ÿā¤ĩāĨ‡ā¤…⤰ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋā¤‚ā¤—ā¤•ā¤ĄāĨ‡ ā¤Ē⤞⤟ā¤ĩ⤤āĨ‡. VP9 ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤•ā¤žā¤Ž ⤕⤰āĨ‡ā¤˛ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤¨ā¤žā¤šāĨ€āĨ¤", + "transcoding_acceleration_nvenc": "NVENC (NVIDIA GPU ⤆ā¤ĩā¤ļāĨā¤¯ā¤•)", + "transcoding_acceleration_qsv": "Quick Sync (7ā¤ĩāĨā¤¯ā¤ž ā¤Ēā¤ŋā¤ĸāĨ€ā¤šā¤ž Intel CPU ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤¨ā¤‚ā¤¤ā¤°ā¤šāĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•ā¤¤ā¤ž)", + "transcoding_acceleration_rkmpp": "RKMPP (⤕āĨ‡ā¤ĩ⤺ Rockchip SoC ā¤ĩ⤰)", + "transcoding_acceleration_vaapi": "ā¤ĩāĨ€ā¤ā¤ā¤ĒāĨ€ā¤†ā¤ˆ", + "transcoding_accepted_audio_codecs": "ā¤Žā¤žā¤¨āĨā¤¯ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤‘ā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤¸āĨ‡ā¤¸", + "transcoding_accepted_audio_codecs_description": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ ā¤‘ā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤¸āĨ‡ā¤¸ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ŖāĨā¤¯ā¤žā¤šāĨ€ ā¤—ā¤°ā¤œ ā¤¨ā¤žā¤šāĨ€ ⤤āĨ‡ ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ⤕āĨ‡ā¤ĩ⤺ ā¤‘ā¤Ąā¤ŋ⤓ ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤇⤍ā¤ĒāĨā¤Ÿā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤞āĨ‡ ā¤œā¤žā¤¤āĨ‡.", + "transcoding_accepted_containers": "ā¤Žā¤žā¤¨āĨā¤¯ ā¤•ā¤‚ā¤ŸāĨ‡ā¤¨ā¤° ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē", + "transcoding_accepted_containers_description": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ ā¤•ā¤‚ā¤ŸāĨ‡ā¤¨ā¤° ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ē MP4 ā¤Žā¤§āĨā¤¯āĨ‡ ⤰āĨ€ā¤Žā¤•āĨā¤¸ ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤—ā¤°ā¤œ ā¤¨ā¤žā¤šāĨ€ ⤤āĨ‡ ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ⤕āĨ‡ā¤ĩ⤺ ā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤧āĨ‹ā¤°ā¤Ŗā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤞āĨ‡ ā¤œā¤žā¤¤āĨ‡.", + "transcoding_accepted_video_codecs": "ā¤Žā¤žā¤¨āĨā¤¯ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤¸āĨ‡ā¤¸", + "transcoding_accepted_video_codecs_description": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤¸āĨ‡ā¤¸ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤—ā¤°ā¤œ ā¤¨ā¤žā¤šāĨ€ ⤤āĨ‡ ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ⤕āĨ‡ā¤ĩ⤺ ā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤧āĨ‹ā¤°ā¤Ŗā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤞āĨ‡ ā¤œā¤žā¤¤āĨ‡.", + "transcoding_advanced_options_description": "⤅ā¤ļāĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤜āĨā¤¯ā¤žā¤¤ ā¤Ŧā¤šāĨā¤¤āĨ‡ā¤• ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤Ŧā¤Ļ⤞ ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤—ā¤°ā¤œ ā¤¨ā¤žā¤šāĨ€", + "transcoding_audio_codec": "ā¤‘ā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤•", + "transcoding_audio_codec_description": "Opus ā¤šā¤ž ⤏⤰āĨā¤ĩā¤žā¤§ā¤ŋ⤕ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤†ā¤šāĨ‡, ā¤Ē⤰⤂⤤āĨ ⤜āĨā¤¨āĨā¤¯ā¤ž ⤉ā¤Ē⤕⤰⤪āĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤏āĨ‰ā¤ĢāĨā¤Ÿā¤ĩāĨ‡ā¤…⤰ā¤ļāĨ€ ā¤•ā¤ŽāĨ€ ⤏āĨā¤¸ā¤‚ā¤—ā¤¤ā¤¤ā¤ž ⤅⤏āĨ‚ ā¤ļ⤕⤤āĨ‡.", + "transcoding_bitrate_description": "⤜āĨā¤¯ā¤ž ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤šā¤ž ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ā¤œā¤žā¤¸āĨā¤¤ ā¤†ā¤šāĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤜āĨ‡ ā¤Žā¤žā¤¨āĨā¤¯ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤žā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "transcoding_codecs_learn_more": "⤝āĨ‡ā¤ĨāĨ‡ ā¤ĩā¤žā¤Ē⤰⤞āĨā¤¯ā¤ž ā¤œā¤žā¤Ŗā¤žā¤ąāĨā¤¯ā¤ž ā¤¸ā¤‚ā¤œāĨā¤žāĨ‡ā¤Ŧā¤ĻāĨā¤Ļ⤞ ⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤ŖāĨ‚⤍ ⤘āĨ‡ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€, H.264 ⤕āĨ‹ā¤ĄāĨ‡ā¤•, HEVC ⤕āĨ‹ā¤ĄāĨ‡ā¤• ⤆⤪ā¤ŋ VP9 ⤕āĨ‹ā¤ĄāĨ‡ā¤• ā¤¯ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ FFmpeg ā¤Ļ⤏āĨā¤¤ā¤ā¤ĩ⤜ ā¤Ēā¤šā¤ž.", + "transcoding_constant_quality_mode": "ā¤¸ā¤žā¤¤ā¤¤āĨā¤¯ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤ŽāĨ‹ā¤Ą", + "transcoding_constant_quality_mode_description": "ICQ ā¤šāĨ‡ CQP ā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤šā¤žā¤‚ā¤—ā¤˛āĨ‡ ā¤†ā¤šāĨ‡, ā¤Ē⤰⤂⤤āĨ ā¤•ā¤žā¤šāĨ€ ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ⤤āĨā¤ĩ⤰⤕ ⤉ā¤Ē⤕⤰⤪āĨ‡ ā¤šāĨ‡ ā¤ŽāĨ‹ā¤Ą ā¤¸ā¤Žā¤°āĨā¤Ĩ⤍ ⤕⤰⤤ ā¤¨ā¤žā¤šāĨ€ā¤¤. ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋā¤‚ā¤—ā¤¸ā¤žā¤ āĨ€ ICQ ā¤ŽāĨ‹ā¤Ą ⤍ā¤ŋā¤ĩā¤ĄāĨ‡ā¤˛. NVENC ICQ ā¤¸ā¤Žā¤°āĨā¤Ĩā¤ŋ⤤ ⤍⤏⤞āĨā¤¯ā¤žā¤¨āĨ‡ ⤤āĨā¤¯ā¤žā¤•ā¤ĄāĨ‡ ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ˆā¤˛.", + "transcoding_constant_rate_factor": "ā¤¸ā¤žā¤¤ā¤¤āĨā¤¯ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤰āĨ‡ā¤Ÿ ā¤ĢāĨ…⤕āĨā¤Ÿā¤° (-crf)", + "transcoding_constant_rate_factor_description": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤏āĨā¤¤ā¤°. H.264 ā¤¸ā¤žā¤ āĨ€ ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ā¤¤ā¤ƒ 23, HEVC ā¤¸ā¤žā¤ āĨ€ 28, VP9 ā¤¸ā¤žā¤ āĨ€ 31 ⤆⤪ā¤ŋ AV1 ā¤¸ā¤žā¤ āĨ€ 35. ā¤•ā¤ŽāĨ€ ā¤ŽāĨ‚⤞āĨā¤¯ ā¤ŽāĨā¤šā¤Ŗā¤œāĨ‡ ā¤‰ā¤šāĨā¤š ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž, ā¤Ē⤰⤂⤤āĨ ā¤Ģā¤žā¤¯ā¤˛āĨ€ ā¤ŽāĨ‹ā¤ āĨā¤¯ā¤ž ā¤šāĨ‹ā¤¤āĨ€ā¤˛.", + "transcoding_disabled_description": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤕⤰āĨ‚ ā¤¨ā¤•ā¤ž, ā¤•ā¤žā¤šāĨ€ ⤗āĨā¤°ā¤žā¤šā¤•ā¤žā¤‚ā¤šāĨā¤¯ā¤ž ā¤ĒāĨā¤˛āĨ‡ā¤ŦāĨ…ā¤•ā¤Žā¤§āĨā¤¯āĨ‡ ā¤…ā¤Ąā¤šā¤Ŗ ⤝āĨ‡ā¤Š ā¤ļ⤕⤤āĨ‡", + "transcoding_encoding_options": "ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "transcoding_encoding_options_description": "ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋā¤“ā¤‚ā¤¸ā¤žā¤ āĨ€ ⤕āĨ‹ā¤ĄāĨ‡ā¤¸āĨ‡ā¤¸, ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍, ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤆⤪ā¤ŋ ⤇⤤⤰ ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "transcoding_hardware_acceleration": "ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ⤤āĨā¤ĩ⤰⤪", + "transcoding_hardware_acceleration_description": "ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤—ā¤žā¤¤āĨā¤Žā¤•: ā¤ā¤•ā¤žā¤š ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿā¤ĩ⤰ ⤜⤞ā¤Ļ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗, ā¤Ē⤰⤂⤤āĨ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡", + "transcoding_hardware_decoding": "ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ā¤ĄāĨ€ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗", + "transcoding_hardware_decoding_setting_description": "ā¤Ģ⤕āĨā¤¤ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤤āĨā¤ĩ⤰⤪⤐ā¤ĩ⤜āĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤¨āĨā¤Ą-⤟āĨ‚-ā¤ā¤¨āĨā¤Ą ⤤āĨā¤ĩ⤰⤪ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž. ⤏⤰āĨā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓⤂ā¤ĩ⤰ ā¤•ā¤žā¤Ž ⤍⤏āĨ‡ā¤˛.", + "transcoding_max_b_frames": "ā¤•ā¤Žā¤žā¤˛ B-ā¤ĢāĨā¤°āĨ‡ā¤Ž", + "transcoding_max_b_frames_description": "ā¤œā¤žā¤¸āĨā¤¤ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤏⤂⤕āĨā¤šā¤¨ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤āĨ‡ā¤¤ ⤏āĨā¤§ā¤žā¤°ā¤Ŗā¤ž ā¤•ā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤Žā¤‚ā¤Ļ ā¤•ā¤°ā¤¤ā¤žā¤¤. ⤜āĨā¤¨āĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤ĩ⤰ ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ⤤āĨā¤ĩā¤°ā¤Ŗā¤žā¤ļāĨ€ ⤏āĨā¤¸ā¤‚⤗⤤ ā¤¨ā¤¸ā¤žā¤¯ā¤šāĨ‡ ā¤ļ⤕āĨā¤¯ā¤¤ā¤ž. 0 ⤅⤏āĨ‡ā¤˛ ⤤⤰ B-ā¤ĢāĨā¤°āĨ‡ā¤Ž ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝, -1 ⤠āĨ‡ā¤ĩ⤞āĨā¤¯ā¤žā¤¸ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤏āĨ‡ā¤Ÿ ā¤šāĨ‹ā¤ˆā¤˛.", + "transcoding_max_bitrate": "ā¤•ā¤Žā¤žā¤˛ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ", + "transcoding_max_bitrate_description": "ā¤•ā¤Žā¤žā¤˛ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ⤏āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¨āĨ‡ ā¤Ģā¤žā¤ˆā¤˛ ā¤¸ā¤žā¤‡ā¤œ ⤅⤧ā¤ŋ⤕ ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤¨āĨā¤ŽāĨ‡ā¤¯ ā¤šāĨ‹ā¤¤ā¤žā¤¤, ⤕ā¤ŋā¤‚ā¤šā¤ŋ⤤ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤖⤰āĨā¤šā¤žā¤¨āĨ‡. 720p ā¤¸ā¤žā¤ āĨ€ ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ā¤¤ā¤ƒ VP9/HEVC ā¤¸ā¤žā¤ āĨ€ 2600 kbit/s, H.264 ā¤¸ā¤žā¤ āĨ€ 4500 kbit/s. 0 ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝.", + "transcoding_max_keyframe_interval": "ā¤•ā¤Žā¤žā¤˛ ⤕āĨ€ā¤ĢāĨā¤°āĨ‡ā¤Ž ā¤…ā¤‚ā¤¤ā¤°ā¤žā¤˛", + "transcoding_max_keyframe_interval_description": "⤕āĨ€ā¤ĢāĨā¤°āĨ‡ā¤Ž ā¤Ļā¤°ā¤ŽāĨā¤¯ā¤žā¤¨ ā¤•ā¤Žā¤žā¤˛ ā¤ĢāĨā¤°āĨ‡ā¤Ž ā¤…ā¤‚ā¤¤ā¤°ā¤žā¤˛ ⤠⤰ā¤ĩ⤤āĨ‡. ā¤•ā¤ŽāĨ€ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤏⤂⤕āĨā¤šā¤¨ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤āĨ‡ā¤¤ ⤘⤟ ā¤•ā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ā¤ļāĨ‹ā¤§ ā¤ĩāĨ‡ā¤ŗ ⤏āĨā¤§ā¤žā¤°ā¤¤ā¤žā¤¤ ⤆⤪ā¤ŋ ā¤ĩāĨ‡ā¤—ā¤ĩā¤žā¤¨ ā¤šā¤žā¤˛ā¤šā¤žā¤˛āĨ€ā¤¤āĨ€ā¤˛ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ⤏āĨā¤§ā¤žā¤°āĨ‚ ā¤ļā¤•ā¤¤ā¤žā¤¤. 0 ⤠āĨ‡ā¤ĩ⤞āĨā¤¯ā¤žā¤¸ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤏āĨ‡ā¤Ÿ.", + "transcoding_optimal_description": "⤞⤕āĨā¤ˇāĨā¤¯ ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤œā¤žā¤¸āĨā¤¤ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤Žā¤žā¤¨āĨā¤¯ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤žā¤¤ ⤍⤏⤞āĨ‡ā¤˛āĨ‡ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "transcoding_policy": "⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤧āĨ‹ā¤°ā¤Ŗ", + "transcoding_policy_description": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤕āĨ‡ā¤ĩāĨā¤šā¤ž ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛ā¤ž ā¤œā¤žā¤ˆā¤˛ ⤤āĨ‡ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "transcoding_preferred_hardware_device": "ā¤ĒāĨā¤°ā¤žā¤§ā¤žā¤¨āĨā¤¯ā¤•āĨƒā¤¤ ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ⤉ā¤Ē⤕⤰⤪", + "transcoding_preferred_hardware_device_description": "⤕āĨ‡ā¤ĩ⤺ VAAPI ⤆⤪ā¤ŋ QSV ā¤¸ā¤žā¤ āĨ€ ā¤˛ā¤žā¤—āĨ‚. ā¤šā¤žā¤°āĨā¤Ąā¤ĩāĨ‡ā¤…⤰ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ēā¤°ā¤˛ā¤ž ā¤œā¤žā¤Ŗā¤žā¤°ā¤ž DRI ⤍āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž.", + "transcoding_preset_preset": "ā¤ĒāĨā¤°āĨ€ā¤¸āĨ‡ā¤Ÿ (–preset)", + "transcoding_preset_preset_description": "⤏⤂⤕āĨā¤šā¤¨ ⤗⤤āĨ€. ⤕ā¤ŋā¤‚ā¤šā¤ŋ⤤ ā¤Žā¤‚ā¤Ļ ā¤ĒāĨā¤°āĨ€ā¤¸āĨ‡ā¤ŸāĨā¤¸ ā¤˛ā¤šā¤žā¤¨ ā¤Ģā¤žā¤‡ā¤˛ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤žā¤¤ ⤆⤪ā¤ŋ ā¤ ā¤°ā¤žā¤ĩā¤ŋ⤕ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿā¤¸ā¤žā¤ āĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤ĩā¤žā¤ĸā¤ĩā¤¤ā¤žā¤¤. VP9 ‘faster’ ā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤œā¤žā¤¸āĨā¤¤ ⤗⤤āĨ€ ⤞⤕āĨā¤ˇā¤žā¤¤ ⤘āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€.", + "transcoding_reference_frames": "⤏⤂ā¤Ļ⤰āĨā¤­ ā¤ĢāĨā¤°āĨ‡ā¤ŽāĨā¤¸", + "transcoding_reference_frames_description": "ā¤Ļā¤ŋ⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĢāĨā¤°āĨ‡ā¤Žā¤šāĨ‡ ⤏⤂⤕āĨā¤šā¤¨ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤕ā¤ŋ⤤āĨ€ ā¤ĢāĨā¤°āĨ‡ā¤ŽāĨā¤¸ ⤏⤂ā¤Ļ⤰āĨā¤­ā¤ŋ⤤ ā¤•ā¤°ā¤žā¤ĩāĨā¤¯ā¤žā¤¤ ā¤šāĨ‡ ⤠⤰ā¤ĩ⤤āĨ‡. ā¤œā¤žā¤¸āĨā¤¤ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ⤏⤂⤕āĨā¤šā¤¨ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤āĨ‡ā¤¤ ⤏āĨā¤§ā¤žā¤°ā¤Ŗā¤ž ā¤•ā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤Žā¤‚ā¤Ļ ā¤•ā¤°ā¤¤ā¤žā¤¤. 0 ⤠āĨ‡ā¤ĩ⤞āĨā¤¯ā¤žā¤¸ ā¤šāĨ‡ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤏āĨ‡ā¤Ÿ ā¤šāĨ‹ā¤¤āĨ‡.", + "transcoding_required_description": "ā¤Ģ⤕āĨā¤¤ ā¤Žā¤žā¤¨āĨā¤¯ ā¤ĒāĨā¤°ā¤žā¤°āĨ‚ā¤Ēā¤žā¤¤ ⤍⤏⤞āĨ‡ā¤˛āĨ‡ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "transcoding_settings": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "transcoding_settings_description": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡ ⤆⤪ā¤ŋ ⤕⤏āĨ‡ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤•ā¤°ā¤žā¤¯ā¤šāĨ€ ⤤āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "transcoding_target_resolution": "⤞⤕āĨā¤ˇāĨā¤¯ ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍", + "transcoding_target_resolution_description": "ā¤‰ā¤šāĨā¤š ⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ⤅⤧ā¤ŋ⤕ ⤤ā¤Ēā¤ļāĨ€ā¤˛ ⤜ā¤Ē⤤āĨ‡, ā¤Ē⤰⤂⤤āĨ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ā¤œā¤žā¤¸āĨā¤¤ ā¤ĩāĨ‡ā¤ŗ ā¤˛ā¤žā¤—ā¤¤āĨ‹, ā¤Ģā¤žā¤‡ā¤˛ ā¤¸ā¤žā¤‡ā¤œ ā¤ŽāĨ‹ā¤ āĨ€ ā¤šāĨ‹ā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤ā¤Ē ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤¸ā¤žā¤Ļ⤕āĨā¤ˇā¤Žā¤¤ā¤ž ā¤•ā¤ŽāĨ€ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡.", + "transcoding_temporal_aq": "⤅⤏āĨā¤Ĩā¤žā¤¯āĨ€ AQ", + "transcoding_temporal_aq_description": "⤕āĨ‡ā¤ĩ⤺ NVENC ā¤¸ā¤žā¤ āĨ€ ā¤˛ā¤žā¤—āĨ‚. ā¤‰ā¤šāĨā¤š ⤤ā¤Ēā¤ļāĨ€ā¤˛ ⤆⤪ā¤ŋ ā¤•ā¤ŽāĨ€ ā¤šā¤žā¤˛ā¤šā¤žā¤˛āĨ€ ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĻāĨƒā¤ļāĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤ĩā¤žā¤ĸā¤ĩ⤤āĨ‡. ⤜āĨā¤¨āĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤ĩ⤰ ⤏āĨā¤¸ā¤‚⤗⤤ ⤍⤏āĨ‡ā¤˛.", + "transcoding_threads": "ā¤ĨāĨā¤°āĨ‡ā¤ĄāĨā¤¸", + "transcoding_threads_description": "ā¤œā¤žā¤¸āĨā¤¤ ā¤ŽāĨ‚⤞āĨā¤¯āĨ‡ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤜⤞ā¤Ļ ā¤•ā¤°ā¤¤ā¤žā¤¤, ā¤Ē⤰⤂⤤āĨ ⤏⤕āĨā¤°ā¤ŋ⤝ ā¤…ā¤¸ā¤¤ā¤žā¤¨ā¤ž ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤˛ā¤ž ⤇⤤⤰ ā¤•ā¤žā¤°āĨā¤¯āĨ‡ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤•ā¤ŽāĨ€ ā¤œā¤žā¤—ā¤ž ā¤°ā¤žā¤šā¤¤āĨ‡. ā¤šāĨ‡ ā¤ŽāĨ‚⤞āĨā¤¯ CPU ⤕āĨ‹ā¤…⤰āĨā¤¸ā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤œā¤žā¤¸āĨā¤¤ ā¤¨ā¤¸ā¤žā¤ĩāĨ‡. 0 ⤠āĨ‡ā¤ĩ⤞āĨā¤¯ā¤žā¤¸ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤕āĨā¤ˇā¤Žā¤¤āĨ‡ā¤¨āĨ‡ ā¤ĩā¤žā¤Ē⤰ ā¤šāĨ‹ā¤¤āĨ‡.", + "transcoding_tone_mapping": "⤟āĨ‹ā¤¨-ā¤ŽāĨ…ā¤Ēā¤ŋ⤂⤗", + "transcoding_tone_mapping_description": "HDR ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ SDR ā¤Žā¤§āĨā¤¯āĨ‡ ⤰āĨ‚ā¤Ēā¤žā¤‚ā¤¤ā¤°ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤ž ⤰āĨ‚ā¤Ēā¤žā¤šāĨ€ ā¤ļ⤕āĨā¤¯ ⤤ā¤ŋ⤤⤕āĨ€ ⤜⤤⤍ ⤕⤰⤪āĨā¤¯ā¤žā¤šā¤ž ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨. ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤅⤞āĨā¤—āĨ‹ā¤°ā¤ŋā¤Ĩā¤Ž ⤰⤂⤗, ⤤ā¤Ēā¤ļāĨ€ā¤˛ ⤆⤪ā¤ŋ ā¤‰ā¤œāĨ‡ā¤Ą ā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤žā¤¤ ⤭ā¤ŋ⤍āĨā¤¨ ā¤¸ā¤Žā¤¤āĨ‹ā¤˛ ā¤¸ā¤žā¤§ā¤¤āĨ‹. Hable ⤤ā¤Ēā¤ļāĨ€ā¤˛ ⤜⤤⤍ ⤕⤰⤤āĨ‡, Mobius ⤰⤂⤗ ⤜⤤⤍ ⤕⤰⤤āĨ‹, Reinhard ā¤‰ā¤œāĨ‡ā¤Ą ⤜⤤⤍ ⤕⤰⤤āĨ‹.", + "transcoding_transcode_policy": "⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤧āĨ‹ā¤°ā¤Ŗ", + "transcoding_transcode_policy_description": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤕⤧āĨ€ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤¯ā¤žā¤Ŧā¤žā¤Ŧ⤤ ⤧āĨ‹ā¤°ā¤Ŗ. HDR ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤍āĨ‡ā¤šā¤ŽāĨ€ā¤š ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤šāĨ‹ā¤¤āĨ€ā¤˛ (⤜āĨ‹ā¤Ē⤰āĨā¤¯ā¤‚⤤ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤏⤕āĨā¤ˇā¤Ž ⤍⤏āĨ‡ā¤˛ ⤤āĨ‡ā¤ĩāĨā¤šā¤ž ⤏āĨ‹ā¤Ą).", + "transcoding_two_pass_encoding": "ā¤ĻāĨ‹ā¤¨ ⤟ā¤ĒāĨā¤ĒāĨā¤¯ā¤žā¤¤ ā¤ā¤¨āĨā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗", + "transcoding_two_pass_encoding_setting_description": "ā¤ŦāĨ‡ā¤šā¤¤ā¤° ā¤ā¤¨āĨā¤•āĨ‹ā¤ĄāĨ‡ā¤Ą ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Žā¤ŋ⤺ā¤ĩ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĻāĨ‹ā¤¨ ⤟ā¤ĒāĨā¤ĒāĨā¤¯ā¤žā¤¤ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤•ā¤°ā¤ž. ā¤œā¤žā¤¸āĨā¤¤āĨ€ā¤¤ ā¤œā¤žā¤¸āĨā¤¤ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ⤏⤕āĨā¤ˇā¤Ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸ (H.264 ⤆⤪ā¤ŋ HEVC ā¤¸ā¤žā¤ āĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•), ā¤šā¤ž ā¤ŽāĨ‹ā¤Ą ā¤œā¤žā¤¸āĨā¤¤āĨ€ā¤¤ ā¤œā¤žā¤¸āĨā¤¤ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿā¤ĩ⤰ ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ⤰āĨ‡ā¤‚ā¤œ ā¤ĩā¤žā¤Ē⤰⤤āĨ‹ ⤆⤪ā¤ŋ CRF ā¤ĻāĨā¤°āĨā¤˛ā¤•āĨā¤ˇā¤ŋ⤤ ⤕⤰⤤āĨ‹. VP9 ā¤¸ā¤žā¤ āĨ€, ā¤œā¤žā¤¸āĨā¤¤āĨ€ā¤¤ ā¤œā¤žā¤¸āĨā¤¤ ā¤Ŧā¤ŋ⤟⤰āĨ‡ā¤Ÿ ⤅⤕āĨā¤ˇā¤Ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸ CRF ā¤ĩā¤žā¤Ēā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤¤āĨ‹.", + "transcoding_video_codec": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤕āĨ‹ā¤ĄāĨ‡ā¤•", + "transcoding_video_codec_description": "VP9 ā¤‰ā¤šāĨā¤š ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Žā¤¤ā¤ž ⤆⤪ā¤ŋ ā¤ĩāĨ‡ā¤Ŧ ⤏āĨā¤¸ā¤‚ā¤—ā¤¤ā¤¤ā¤ž ā¤ĻāĨ‡ā¤¤āĨ‹, ā¤Ē⤰⤂⤤āĨ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ā¤œā¤žā¤¸āĨā¤¤ ā¤ĩāĨ‡ā¤ŗ ⤘āĨ‡ā¤¤āĨ‹. HEVC ⤏āĨā¤ĻāĨā¤§ā¤ž ā¤šā¤žā¤‚ā¤—ā¤˛āĨ‡ ā¤•ā¤žā¤Ž ⤕⤰⤤āĨ‡, ā¤Ē⤰⤂⤤āĨ ⤏āĨā¤¸ā¤‚ā¤—ā¤¤ā¤¤ā¤ž ā¤•ā¤ŽāĨ€. H.264 ⤏⤰āĨā¤ĩ⤤āĨā¤° ⤏āĨā¤¸ā¤‚⤗⤤ ⤆⤪ā¤ŋ ⤜⤞ā¤Ļ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤕⤰⤤āĨ‹, ā¤Ē⤰⤂⤤āĨ ā¤Ģā¤žā¤‡ā¤˛ ā¤ŽāĨ‹ā¤ āĨā¤¯ā¤ž ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤤āĨ‹. AV1 ⤏⤰āĨā¤ĩā¤žā¤§ā¤ŋ⤕ ā¤•ā¤žā¤°āĨā¤¯ā¤•āĨā¤ˇā¤Ž ā¤Ē⤰⤂⤤āĨ ⤜āĨā¤¨āĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤ĩ⤰ ā¤•ā¤ŽāĨ€ ā¤¸ā¤Žā¤°āĨā¤Ĩ⤍.", + "trash_enabled_description": "⤟āĨā¤°āĨ…ā¤ļ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "trash_number_of_days": "ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤‚ā¤šāĨ€ ⤏⤂⤖āĨā¤¯ā¤ž", + "trash_number_of_days_description": "ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ€ā¤¤āĨā¤¯ā¤ž ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤ŖāĨā¤¯ā¤žā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤¸ā¤žā¤Žā¤—āĨā¤°āĨ€ ⤕ā¤ŋ⤤āĨ€ ā¤Ļā¤ŋā¤ĩ⤏ ⤠āĨ‡ā¤ĩā¤žā¤¯ā¤šāĨ€ ⤤āĨ‡ ⤕āĨā¤°ā¤Ž", + "trash_settings": "⤟āĨā¤°āĨ…ā¤ļ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "trash_settings_description": "⤟āĨā¤°āĨ…ā¤ļ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "user_cleanup_job": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž", + "user_delete_delay": "{user} ā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤–ā¤žā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤•ā¤žā¤¯ā¤Žā¤šāĨ€ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ {delay, plural, one {# ā¤Ļā¤ŋā¤ĩ⤏} other {# ā¤Ļā¤ŋā¤ĩ⤏}} ⤍⤂⤤⤰ ā¤ļāĨ‡ā¤ĄāĨā¤¯āĨ‚⤞ ⤕āĨ‡ā¤˛āĨ€ ā¤œā¤žā¤¤āĨ€ā¤˛.", + "user_delete_delay_settings": "ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤ĩā¤ŋ⤞⤂ā¤ŦāĨ€ā¤¤ ā¤•ā¤žā¤˛ā¤žā¤ĩ⤧āĨ€", + "user_delete_delay_settings_description": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨ‡ ā¤–ā¤žā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤•ā¤žā¤¯ā¤Žā¤šāĨ€ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤕ā¤ŋ⤤āĨ€ ā¤Ļā¤ŋā¤ĩ⤏ ā¤ĩā¤ŋ⤞⤂ā¤Ŧ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ⤤āĨ‡. ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤šāĨ‡ ⤜āĨ‰ā¤Ŧ ā¤Žā¤§āĨā¤¯ā¤°ā¤žā¤¤āĨā¤°āĨ€ ā¤šā¤žā¤˛ā¤ĩ⤞āĨ‡ ā¤œā¤žā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤¸ ā¤¤ā¤¯ā¤žā¤° ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€ ⤕⤰⤤āĨ‡. ā¤¯ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋā¤‚ā¤—ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ŧā¤Ļ⤞ ā¤ĒāĨā¤ĸāĨ€ā¤˛ ā¤šā¤žā¤˛āĨ‚ ā¤ĩāĨ‡ā¤ŗāĨ€ ā¤˛ā¤žā¤—āĨ‚ ā¤šāĨ‹ā¤¤āĨ€ā¤˛.", + "user_delete_immediately": "{user} ā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤–ā¤žā¤¤āĨ‡ ⤆⤪ā¤ŋ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤¤ā¤žā¤¤āĨā¤•ā¤žā¤ŗ ā¤•ā¤žā¤¯ā¤Žā¤šāĨ€ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ā¤Ÿā¤žā¤•ā¤˛āĨ€ ā¤œā¤žā¤ˆā¤˛.", + "user_delete_immediately_checkbox": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤆⤪ā¤ŋ ā¤Žā¤žā¤˛ā¤Žā¤¤āĨā¤¤ā¤ž ā¤¤ā¤žā¤¤āĨā¤•ā¤žā¤ŗ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ ⤠āĨ‡ā¤ĩā¤ž", + "user_details": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤤ā¤Ēā¤ļāĨ€ā¤˛", + "user_management": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍", + "user_password_has_been_reset": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨ‡ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ‡:", + "user_password_reset_description": "⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤˛ā¤ž ā¤¤ā¤žā¤¤āĨā¤ĒāĨā¤°ā¤¤ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤ĻāĨā¤¯ā¤ž ⤆⤪ā¤ŋ ⤤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ⤕⤺ā¤ĩā¤ž ⤕āĨ€ ā¤ĒāĨā¤ĸāĨ€ā¤˛ ⤞āĨ‰ā¤—ā¤ŋ⤍ā¤ĩ⤰ ⤤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļā¤˛ā¤žā¤ĩā¤ž ā¤˛ā¤žā¤—āĨ‡ā¤˛.", + "user_restore_description": "{user} ā¤¯ā¤žā¤‚ā¤šāĨ‡ ā¤–ā¤žā¤¤āĨ‡ ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤ˆā¤˛.", + "user_restore_scheduled_removal": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤ž – ⤍ā¤ŋ⤝āĨ‹ā¤œā¤ŋ⤤ ā¤šā¤Ÿā¤ĩā¤ŋ⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤¤ā¤žā¤°āĨ€ā¤– {date, date, long}", + "user_settings": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "user_settings_description": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "user_successfully_removed": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž {email} ā¤¯ā¤žā¤‚ā¤šāĨ€ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤šā¤Ÿā¤ĩ⤪āĨ€ ā¤ā¤žā¤˛āĨ€ ā¤†ā¤šāĨ‡.", + "version_check_enabled_description": "⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "version_check_implications": "⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ GitHub.com ⤏āĨ‹ā¤Ŧ⤤ ⤆ā¤ĩ⤰āĨā¤¤āĨ€ ⤏⤂ā¤ĩā¤žā¤Ļā¤žā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‡", + "version_check_settings": "⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€", + "version_check_settings_description": "⤍ā¤ĩāĨ€ā¤¨ ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž/⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "video_conversion_job": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "video_conversion_job_description": "ā¤ŦāĨā¤°ā¤žā¤‰ā¤ā¤° ⤆⤪ā¤ŋ ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤¸ā¤š ā¤œā¤žā¤¸āĨā¤¤ ⤏āĨā¤¸ā¤‚⤗⤤⤤āĨ‡ā¤¸ā¤žā¤ āĨ€ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ā¤•ā¤°ā¤ž" + }, + "admin_email": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤• ā¤ˆā¤ŽāĨ‡ā¤˛", + "admin_password": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤• ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "administration": "ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤¨", + "advanced": "ā¤ĒāĨā¤°ā¤—⤤", + "advanced_settings_enable_alternate_media_filter_subtitle": "⤏ā¤ŋ⤂⤕ ā¤Ļā¤°ā¤ŽāĨā¤¯ā¤žā¤¨ ā¤ĩāĨˆā¤•⤞āĨā¤Ēā¤ŋ⤕ ⤍ā¤ŋā¤•ā¤ˇā¤žā¤‚ā¤ĩ⤰ ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤šā¤ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤ĩā¤žā¤Ēā¤°ā¤ž. āĨ˛ā¤Ē ⤏⤰āĨā¤ĩ ⤅⤞āĨā¤Ŧā¤Ž ⤓⤺⤖⤪āĨā¤¯ā¤žā¤¤ ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤝āĨ‡ā¤¤ ⤅⤏⤞āĨā¤¯ā¤žā¤¸ā¤š ā¤ĩā¤žā¤Ēā¤°ā¤ž.", + "advanced_settings_enable_alternate_media_filter_title": "[ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤—ā¤žā¤¤āĨā¤Žā¤•] ⤉ā¤Ē⤕⤰⤪-ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ⤅⤞āĨā¤Ŧā¤Ž ⤏ā¤ŋ⤂⤕ ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "advanced_settings_log_level_title": "⤞āĨ‰ā¤— ā¤Ēā¤žā¤¤ā¤ŗāĨ€: {level}", + "advanced_settings_prefer_remote_subtitle": "ā¤•ā¤žā¤šāĨ€ ⤉ā¤Ē⤕⤰⤪āĨ‡ ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ⤅āĨ…⤏āĨ‡ā¤Ÿā¤Žā¤§āĨ‚⤍ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤖āĨ‚ā¤Ē ā¤Žā¤‚ā¤Ļ ā¤†ā¤šāĨ‡ā¤¤. ⤤āĨā¤¯ā¤žā¤ā¤ĩ⤜āĨ€ ⤰ā¤ŋā¤ŽāĨ‹ā¤Ÿ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤šā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž.", + "advanced_settings_prefer_remote_title": "⤰ā¤ŋā¤ŽāĨ‹ā¤Ÿ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤Ē⤏⤂⤤ ā¤•ā¤°ā¤ž", + "advanced_settings_proxy_headers_subtitle": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ā¤ĩā¤ŋ⤍⤂⤤āĨ€ā¤¸āĨ‹ā¤Ŧ⤤ Immich ā¤Ēā¤žā¤ ā¤ĩā¤žā¤ĩā¤¯ā¤žā¤šāĨ‡ ā¤ĒāĨā¤°āĨ‰ā¤•āĨā¤¸āĨ€ ā¤šāĨ‡ā¤Ąā¤° ⤝āĨ‡ā¤ĨāĨ‡ ā¤Ē⤰ā¤ŋā¤­ā¤žā¤ˇā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "advanced_settings_proxy_headers_title": "ā¤ĒāĨā¤°āĨ‰ā¤•āĨā¤¸āĨ€ ā¤šāĨ‡ā¤Ąā¤°", + "advanced_settings_self_signed_ssl_subtitle": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤ā¤‚ā¤Ąā¤ĒāĨ‰ā¤‡ā¤‚ā¤Ÿā¤¸ā¤žā¤ āĨ€ SSL ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ⤏⤤āĨā¤¯ā¤žā¤Ē⤍ ā¤ĩ⤗⤺⤤āĨ‡. ⤏āĨā¤ĩā¤žā¤•āĨā¤ˇā¤°āĨ€ā¤¤ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤°ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•.", + "advanced_settings_self_signed_ssl_title": "⤏āĨā¤ĩ⤤⤃ ⤏āĨā¤ĩā¤žā¤•āĨā¤ˇā¤°āĨ€ā¤¤ SSL ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤°ā¤žā¤‚ā¤¨ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "advanced_settings_sync_remote_deletions_subtitle": "ā¤ĩāĨ‡ā¤Ŧā¤ĩ⤰ ā¤šāĨ€ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤕āĨ‡ā¤˛āĨ€ ⤗āĨ‡ā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰ ⤅⤏⤞āĨ‡ā¤˛āĨ‡ ⤅āĨ…⤏āĨ‡ā¤Ÿ ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ā¤šā¤Ÿā¤ĩā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "advanced_settings_sync_remote_deletions_title": "⤰ā¤ŋā¤ŽāĨ‹ā¤Ÿ ā¤šā¤Ÿā¤ĩ⤪āĨā¤¯ā¤ž ⤏ā¤ŋ⤂⤕ ā¤•ā¤°ā¤ž [ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤—ā¤žā¤¤āĨā¤Žā¤•]", + "advanced_settings_tile_subtitle": "ā¤ĒāĨā¤°ā¤—⤤ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "advanced_settings_troubleshooting_subtitle": "ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤍ā¤ŋā¤ĩā¤žā¤°ā¤Ŗā¤žā¤¸ā¤žā¤ āĨ€ ⤅⤤ā¤ŋ⤰ā¤ŋ⤕āĨā¤¤ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "advanced_settings_troubleshooting_title": "ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤍ā¤ŋā¤ĩā¤žā¤°ā¤Ŗ", + "age_months": "ā¤ĩ⤝ {months, plural, one {ā¤ā¤• ā¤Žā¤šā¤ŋā¤¨ā¤ž} other {# ā¤Žā¤šā¤ŋ⤍āĨ‡}}", + "age_year_months": "ā¤ĩ⤝ āĨ§ ā¤ĩ⤰āĨā¤ˇ, {months, plural, one {ā¤ā¤• ā¤Žā¤šā¤ŋā¤¨ā¤ž} other {# ā¤Žā¤šā¤ŋ⤍āĨ‡}}", + "age_years": "{years, plural, other {ā¤ĩ⤝ #}}", + "album_added": "⤅⤞āĨā¤Ŧā¤Ž ⤜āĨ‹ā¤Ąā¤˛āĨ‡", + "album_added_notification_setting_description": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤Žā¤ŋ⤺ā¤ĩā¤ž", + "album_cover_updated": "⤅⤞āĨā¤Ŧā¤Ž ⤕ā¤ĩāĨā¤šā¤° ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤ā¤žā¤˛āĨ‡", + "album_delete_confirmation": "⤆ā¤Ē⤪ ⤍ā¤ŋā¤ļāĨā¤šā¤ŋ⤤⤚ ⤅⤞āĨā¤Ŧā¤Ž {album} ā¤šā¤Ÿā¤ĩā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž?", + "album_delete_confirmation_description": "ā¤šā¤ž ⤅⤞āĨā¤Ŧā¤Ž ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛ā¤ž ⤅⤏āĨ‡ā¤˛ ⤤⤰ ⤇⤤⤰ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ā¤†ā¤¤ā¤ž ⤤āĨ‹ ā¤Ēā¤žā¤šāĨ‚ ā¤ļā¤•ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤.", + "album_deleted": "⤅⤞āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤ĩ⤞āĨ‡", + "album_info_card_backup_album_excluded": "ā¤ĩ⤗⤺⤞āĨ‡ ⤗āĨ‡ā¤˛āĨ‡", + "album_info_card_backup_album_included": "ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ", + "album_info_updated": "⤅⤞āĨā¤Ŧā¤Ž ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤", + "album_leave": "⤅⤞āĨā¤Ŧā¤Ž ⤏āĨ‹ā¤Ąā¤Ŗā¤žā¤° ā¤•ā¤ž?", + "album_leave_confirmation": "⤆ā¤Ē⤪ ⤍ā¤ŋā¤ļāĨā¤šā¤ŋ⤤⤚ ⤅⤞āĨā¤Ŧā¤Ž {album} ⤏āĨ‹ā¤Ąā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž?", + "album_name": "⤅⤞āĨā¤Ŧā¤Žā¤šāĨ‡ ā¤¨ā¤žā¤ĩ", + "album_options": "⤅⤞āĨā¤Ŧā¤Ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "album_remove_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤žā¤¯ā¤šā¤ž ā¤•ā¤ž?", + "album_remove_user_confirmation": "⤆ā¤Ē⤪ ⤍ā¤ŋā¤ļāĨā¤šā¤ŋ⤤⤚ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž {user} ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž?", + "album_search_not_found": "⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤ļāĨ‹ā¤§ā¤žā¤ļāĨ€ ⤜āĨā¤ŗā¤Ŗā¤žā¤°āĨ‡ ⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ⤅⤞āĨā¤Ŧā¤Ž ⤆ā¤ĸ⤺⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "album_share_no_users": "ā¤…ā¤¸ā¤ž ā¤Ļā¤ŋ⤏⤤āĨ‡ ⤕āĨ€ ā¤šā¤ž ⤅⤞āĨā¤Ŧā¤Ž ⤤āĨā¤ŽāĨā¤šāĨ€ ⤏⤰āĨā¤ĩ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚⤏āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛ā¤ž ā¤†ā¤šāĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨā¤ ā¤˛ā¤žā¤šāĨ€ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤žā¤šāĨ€.", + "album_updated": "⤅⤞āĨā¤Ŧā¤Ž ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤", + "album_updated_setting_description": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤍ā¤ĩāĨ€ā¤¨ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤆⤞āĨā¤¯ā¤žā¤¸ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤žā¤°āĨā¤Ĩ ā¤ĒāĨā¤°ā¤žā¤ĒāĨā¤¤ ā¤•ā¤°ā¤ž", + "album_user_left": "⤏āĨ‹ā¤Ąā¤˛āĨ‡: {album}", + "album_user_removed": "ā¤•ā¤žā¤ĸ⤞āĨ‡: {user}", + "album_viewer_appbar_delete_confirm": "⤆ā¤Ē⤪ ⤍ā¤ŋā¤ļāĨā¤šā¤ŋ⤤⤚ ā¤šā¤ž ⤅⤞āĨā¤Ŧā¤Ž ⤆ā¤Ē⤞āĨā¤¯ā¤ž ā¤–ā¤žā¤¤āĨā¤¯ā¤žā¤¤āĨ‚⤍ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "album_viewer_appbar_share_err_delete": "⤅⤞āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤ĩ⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "album_viewer_appbar_share_err_leave": "⤅⤞āĨā¤Ŧā¤Ž ⤏āĨ‹ā¤Ąā¤ŖāĨā¤¯ā¤žā¤¸ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "album_viewer_appbar_share_err_remove": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤•ā¤žā¤ĸ⤪āĨā¤¯ā¤žā¤¤ ā¤…ā¤Ąā¤šā¤ŖāĨ€", + "album_viewer_appbar_share_err_title": "⤅⤞āĨā¤Ŧā¤Žā¤šā¤‚ ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ā¤Ŧā¤Ļ⤞⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "album_viewer_appbar_share_leave": "⤅⤞āĨā¤Ŧā¤Ž ⤏āĨ‹ā¤Ąā¤ž", + "album_viewer_appbar_share_to": "ā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "album_viewer_page_share_add_users": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ⤜āĨ‹ā¤Ąā¤ž", + "album_with_link_access": "⤞ā¤ŋ⤂⤕ ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤕āĨ‹ā¤Ŗā¤¤āĨā¤¯ā¤žā¤šāĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤¸ ā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ€ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ⤞āĨ‹ā¤• ā¤Ēā¤žā¤šā¤¤ā¤ž ⤝āĨ‡ā¤¤āĨ€ā¤˛.", + "albums": "⤅⤞āĨā¤Ŧā¤ŽāĨā¤¸", + "albums_count": "{count, plural, one {{count, number} ⤅⤞āĨā¤Ŧā¤Ž} other {{count, number} ⤅⤞āĨā¤Ŧā¤ŽāĨā¤¸}}", + "albums_default_sort_order": "ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿ ⤅⤞āĨā¤Ŧā¤Ž ⤕āĨā¤°ā¤Žā¤ĩā¤žā¤°āĨ€", + "albums_default_sort_order_description": "⤍ā¤ĩāĨ€ā¤¨ ⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ā¤šāĨ€ ā¤ĒāĨā¤°ā¤žā¤°ā¤‚⤭ā¤ŋ⤕ ⤕āĨā¤°ā¤Žā¤ĩā¤žā¤°āĨ€.", + "albums_feature_description": "⤇⤤⤰ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚⤏āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤ąāĨā¤¯ā¤ž ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ā¤šā¤ž ⤏⤂⤗āĨā¤°ā¤š.", + "albums_on_device_count": "ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ā¤ĩ⤰āĨ€ā¤˛ ⤅⤞āĨā¤Ŧā¤ŽāĨā¤¸ ({count})", + "all": "⤏⤰āĨā¤ĩ", + "all_albums": "⤏⤰āĨā¤ĩ ⤅⤞āĨā¤Ŧā¤ŽāĨā¤¸", + "all_people": "⤏⤰āĨā¤ĩ ⤞āĨ‹ā¤•", + "all_videos": "⤏⤰āĨā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "allow_dark_mode": "ā¤Ąā¤žā¤°āĨā¤• ā¤ŽāĨ‹ā¤Ąā¤˛ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "allow_edits": "⤏⤂ā¤Ēā¤žā¤Ļā¤¨ā¤žā¤‚ā¤¨ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "allow_public_user_to_download": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "allow_public_user_to_upload": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤¨ā¤ž ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "alt_text_qr_code": "QR ⤕āĨ‹ā¤Ą ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "anti_clockwise": "ā¤˜ā¤ĄāĨ€ā¤šāĨā¤¯ā¤ž ā¤‰ā¤˛ā¤Ÿ ā¤Ļā¤ŋā¤ļāĨ‡ā¤¨āĨ‡", + "api_key": "ā¤ā¤ĒāĨ€ā¤†ā¤ˆ ⤕āĨ€", + "api_key_description": "ā¤šā¤ž ā¤ŽāĨ‚⤞āĨā¤¯ ā¤ā¤•ā¤Ļā¤žā¤š ā¤Ļā¤žā¤–ā¤ĩā¤ŋā¤˛ā¤ž ā¤œā¤žā¤ˆā¤˛. ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ĩā¤ŋā¤‚ā¤ĄāĨ‹ ā¤Ŧ⤂ā¤Ļ ⤕⤰⤪āĨā¤¯ā¤žā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤤āĨ‡ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤žā¤¯ā¤˛ā¤ž ā¤ĩā¤ŋ⤏⤰āĨ‚ ā¤¨ā¤•ā¤ž.", + "api_key_empty": "⤆ā¤Ē⤞āĨ‡ API ⤕āĨ€ ā¤¨ā¤žā¤ĩ ⤰ā¤ŋ⤕āĨā¤¤ ⤅⤏āĨ‚ ⤍⤝āĨ‡", + "api_keys": "API ⤕āĨ€ā¤œ", + "app_bar_signout_dialog_content": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ ā¤¸ā¤žā¤‡ā¤¨ ā¤†ā¤‰ā¤Ÿ ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "app_bar_signout_dialog_ok": "ā¤šāĨ‹", + "app_bar_signout_dialog_title": "ā¤¸ā¤žā¤‡ā¤¨ ā¤†ā¤‰ā¤Ÿ", + "app_settings": "ā¤…â€āĨ…ā¤Ē ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "appears_in": "ā¤Ļā¤ŋ⤏⤤āĨ‡ (⤕āĨā¤ āĨ‡ ā¤Ļā¤ŋ⤏⤤āĨ‡)", + "archive": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š", + "archive_action_prompt": "{count} ⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤šā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", + "archive_or_unarchive_photo": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤅⤍⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤ž", + "archive_page_no_archived_assets": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤¸ā¤žā¤Ēā¤Ąā¤˛āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "archive_page_title": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ({count})", + "archive_size": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤†ā¤•ā¤žā¤°", + "archive_size_description": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ąā¤¸ā¤žā¤ āĨ€ ⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤†ā¤•ā¤žā¤° (GiB ā¤Žā¤§āĨā¤¯āĨ‡) ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "archived": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡", + "archived_count": "{count, plural, other {⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ #}}", + "are_these_the_same_person": "ā¤šāĨ‡ ā¤ĻāĨ‹ā¤¨āĨā¤šāĨ€ ā¤ā¤•ā¤žā¤š ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤†ā¤šāĨ‡ā¤¤ ā¤•ā¤ž?", + "are_you_sure_to_do_this": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šāĨ‡ ā¤–ā¤°ā¤‚ā¤š ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "asset_action_delete_err_read_only": "ā¤ĩā¤žā¤šā¤¨-⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ā¤¸ā¤žā¤§ā¤¨(āĨ‡) ā¤šā¤Ÿā¤ĩā¤ŋā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤, ā¤ĩ⤗⤺⤞āĨ‡ ā¤œā¤žā¤¤ ā¤†ā¤šāĨ‡", + "asset_action_share_err_offline": "⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨ ā¤¸ā¤žā¤§ā¤¨(āĨ‡) ā¤Žā¤ŋ⤺ā¤ĩā¤ŋā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤, ā¤ĩ⤗⤺⤞āĨ‡ ā¤œā¤žā¤¤ ā¤†ā¤šāĨ‡", + "asset_added_to_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ‡", + "asset_adding_to_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ ā¤†ā¤šāĨ‡â€Ļ", + "asset_description_updated": "ā¤¸ā¤žā¤§ā¤¨ā¤žā¤šāĨ‡ ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤅ā¤ĻāĨā¤¯ā¤žā¤ĩ⤤ ⤕āĨ‡ā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ‡ ā¤†ā¤šāĨ‡", + "asset_filename_is_offline": "{filename} ā¤¨ā¤žā¤ĩā¤žā¤šāĨ‡ ā¤¸ā¤žā¤§ā¤¨ ⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨ ā¤†ā¤šāĨ‡", + "asset_has_unassigned_faces": "ā¤¸ā¤žā¤§ā¤¨ā¤žā¤¤ ā¤…ā¤¸ā¤žā¤§āĨā¤¯ ⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤šāĨ€ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤†ā¤šāĨ‡", + "asset_hashing": "ā¤šāĨ…ā¤ļā¤ŋ⤂⤗â€Ļ", + "asset_list_group_by_sub_title": "ā¤—ā¤Ÿā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž", + "asset_list_layout_settings_dynamic_layout_title": "ā¤Ąā¤žā¤¯ā¤¨āĨ…ā¤Žā¤ŋ⤕ ⤞āĨ‡ā¤†ā¤‰ā¤Ÿ", + "asset_list_layout_settings_group_automatically": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤", + "asset_list_layout_settings_group_by": "ā¤¸ā¤žā¤§ā¤¨āĨ‡ ā¤—ā¤Ÿā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž", + "asset_list_layout_settings_group_by_month_day": "ā¤Žā¤šā¤ŋā¤¨ā¤ž + ā¤Ļā¤ŋā¤ĩ⤏", + "asset_list_layout_sub_title": "⤞āĨ‡ā¤†ā¤‰ā¤Ÿ", + "asset_list_settings_subtitle": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤗āĨā¤°ā¤ŋā¤Ą ⤞āĨ‡ā¤†ā¤‰ā¤Ÿ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "asset_list_settings_title": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤗āĨā¤°ā¤ŋā¤Ą", + "asset_offline": "ā¤¸ā¤žā¤§ā¤¨ ⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨ ā¤†ā¤šāĨ‡", + "asset_offline_description": "ā¤šāĨ‡ ā¤Ŧā¤žā¤šāĨā¤¯ ā¤¸ā¤žā¤§ā¤¨ ā¤†ā¤¤ā¤ž ā¤Ąā¤ŋ⤏āĨā¤•ā¤ĩ⤰ ā¤¸ā¤žā¤Ēā¤Ąā¤¤ ā¤¨ā¤žā¤šāĨ€. ā¤Žā¤Ļ⤤āĨ€ā¤¸ā¤žā¤ āĨ€ ⤆ā¤Ē⤞āĨā¤¯ā¤ž Immich ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•ā¤žā¤ļāĨ€ ⤏⤂ā¤Ē⤰āĨā¤• ā¤•ā¤°ā¤ž.", + "asset_restored_successfully": "ā¤¸ā¤žā¤§ā¤¨ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤Ē⤪āĨ‡ ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡ ⤗āĨ‡ā¤˛āĨ‡", + "asset_skipped": "ā¤ĩ⤗⤺⤞āĨ‡", + "asset_skipped_in_trash": "⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡", + "asset_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_cannot_be_added_to_album_count": "{count, plural, one {# ā¤¸ā¤žā¤§ā¤¨ ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€} other {# ā¤¸ā¤žā¤§ā¤¨āĨ‡ ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤}}", + "assets_count": "{count, plural, one {# ā¤¸ā¤žā¤§ā¤¨} other {# ā¤¸ā¤žā¤§ā¤¨āĨ‡}}", + "assets_deleted_permanently": "{count} ā¤¸ā¤žā¤§ā¤¨(āĨ‡) ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩā¤ŋ⤞āĨ‡", + "assets_deleted_permanently_from_server": "Immich ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰āĨ‚⤍ {count} ā¤¸ā¤žā¤§ā¤¨(āĨ‡) ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩā¤ŋ⤞āĨ‡", + "assets_downloaded_failed": "{count, plural, one {ā¤ā¤• ā¤Ģā¤žā¤ˆā¤˛ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€: {error}} other {# ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€: {error}}}", + "assets_downloaded_successfully": "{count, plural, one {ā¤ā¤• ā¤Ģā¤žā¤ˆā¤˛ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ā¤žā¤˛āĨ€} other {# ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ā¤žā¤˛āĨā¤¯ā¤ž}}", + "assets_moved_to_trash_count": "{count, plural, one {ā¤ā¤• ā¤Ģā¤žā¤ˆā¤˛ ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤šā¤˛ā¤ĩ⤞āĨ€} other {# ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤šā¤˛ā¤ĩ⤞āĨā¤¯ā¤ž}}", + "assets_permanently_deleted_count": "{count, plural, one {ā¤ā¤• ā¤Ģā¤žā¤ˆā¤˛ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨ€} other {# ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž}}", + "back": "ā¤Žā¤žā¤—āĨ‡", + "back_close_deselect": "ā¤Žā¤žā¤—āĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤ž / ⤍ā¤ŋā¤ĩā¤Ą ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "background_location_permission": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ąā¤Žā¤§āĨā¤¯āĨ‡ ⤏āĨā¤Ĩā¤žā¤¨ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "background_location_permission_content": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ąā¤Žā¤§āĨā¤¯āĨ‡ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ⤏āĨā¤ĩā¤ŋ⤚ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ Immich ā¤˛ā¤ž ⤍āĨ‡ā¤šā¤ŽāĨ€ ā¤…ā¤šāĨ‚⤕ ⤏āĨā¤Ĩā¤žā¤¨ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ (Wi-Fi ā¤¨ā¤žā¤ĩ) ā¤Ēā¤žā¤šā¤ŋ⤜āĨ‡", + "backup": "ā¤ŦāĨ…⤕⤅ā¤Ē", + "backup_album_selection_page_albums_device": "⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰āĨ€ā¤˛ ⤅⤞āĨā¤Ŧā¤Ž ({count})", + "backup_album_selection_page_albums_tap": "ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ā¤•ā¤Ļā¤žā¤š ⤟āĨ…ā¤Ē ā¤•ā¤°ā¤ž; ā¤ĩ⤗⤺⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤Ąā¤Ŧ⤞ ⤟āĨ…ā¤Ē ā¤•ā¤°ā¤ž", + "backup_album_selection_page_assets_scatter": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤅⤍āĨ‡ā¤• ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤ŋā¤­ā¤žā¤—ā¤˛āĨā¤¯ā¤ž ā¤œā¤žā¤Š ā¤ļā¤•ā¤¤ā¤žā¤¤; ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ļā¤°ā¤ŽāĨā¤¯ā¤žā¤¨ ⤅⤞āĨā¤Ŧā¤Ž ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ĩā¤—ā¤ŗā¤žāĨ¤", + "backup_album_selection_page_select_albums": "⤅⤞āĨā¤Ŧā¤Ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "backup_album_selection_page_selection_info": "⤍ā¤ŋā¤ĩā¤Ą ā¤Žā¤žā¤šā¤ŋ⤤āĨ€", + "backup_album_selection_page_total_assets": "ā¤ā¤•āĨ‚⤪ ⤏āĨā¤ĩ⤤⤂⤤āĨā¤° ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸", + "backup_all": "⤏⤰āĨā¤ĩ", + "backup_background_service_backup_failed_message": "ā¤ŦāĨ…⤕⤅ā¤Ē ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€. ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨ ⤕⤰⤤ ā¤†ā¤šāĨ‡â€Ļ", + "backup_background_service_connection_failed_message": "⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ļāĨ€ ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€. ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨ ⤕⤰⤤ ā¤†ā¤šāĨ‡â€Ļ", + "backup_background_service_current_upload_notification": "{filename} ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "backup_background_service_default_notification": "⤍ā¤ĩāĨ€ā¤¨ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ļāĨ‹ā¤§ā¤¤ ā¤†ā¤šāĨ‡â€Ļ", + "backup_background_service_error_title": "ā¤ŦāĨ…⤕⤅ā¤Ē ⤤āĨā¤°āĨā¤ŸāĨ€", + "backup_background_service_in_progress_notification": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ŦāĨ…⤕⤅ā¤Ē ⤕⤰⤤ ā¤†ā¤šāĨ‡â€Ļ", + "backup_background_service_upload_failure_notification": "{filename} ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "backup_controller_page_albums": "ā¤ŦāĨ…⤕⤅ā¤Ē ⤅⤞āĨā¤Ŧā¤Ž", + "backup_controller_page_background_app_refresh_disabled_content": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ēā¤¸ā¤žā¤ āĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ > ⤜⤍⤰⤞ > ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤅āĨ…ā¤Ē ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤Žā¤§āĨā¤¯āĨ‡ ‘ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤅āĨ…ā¤Ē ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ’ ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž.", + "backup_controller_page_background_app_refresh_disabled_title": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤅āĨ…ā¤Ē ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ ⤅⤕āĨā¤ˇā¤Ž", + "backup_controller_page_background_app_refresh_enable_button_text": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤œā¤ž", + "backup_controller_page_background_battery_info_link": "⤕⤏āĨ‡ ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡ ⤤āĨ‡ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "backup_controller_page_background_battery_info_message": "⤉⤤āĨā¤¤ā¤Ž ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ēā¤¸ā¤žā¤ āĨ€ Immich ā¤¸ā¤žā¤ āĨ€ ⤏⤰āĨā¤ĩ ā¤ŦāĨ…ā¤Ÿā¤°āĨ€ ⤑ā¤ĒāĨā¤Ÿā¤ŋā¤Žā¤žā¤¯ā¤āĨ‡ā¤ļ⤍ ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž. \n\nā¤šāĨ‡ ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ā¤¨āĨā¤¸ā¤žā¤° ā¤ĩāĨ‡ā¤—⤺āĨ‡ ⤅⤏āĨ‚ ā¤ļ⤕⤤āĨ‡, ⤤āĨā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ ⤍ā¤ŋ⤰āĨā¤¯ā¤žā¤¤ā¤•⤰āĨā¤¤āĨā¤¯ā¤žā¤Ēā¤žā¤¸āĨ‚⤍ ā¤Žā¤žā¤°āĨā¤—ā¤Ļ⤰āĨā¤ļ⤍ ⤤ā¤Ēā¤žā¤¸ā¤ž.", + "backup_controller_page_background_battery_info_ok": "⤠āĨ€ā¤• ā¤†ā¤šāĨ‡", + "backup_controller_page_background_battery_info_title": "ā¤ŦāĨ…ā¤Ÿā¤°āĨ€ ⤑ā¤ĒāĨā¤Ÿā¤ŋā¤Žā¤žā¤¯ā¤āĨ‡ā¤ļ⤍āĨā¤¸", + "backup_controller_page_background_charging": "ā¤šā¤žā¤°āĨā¤œ ā¤šāĨ‹ā¤¤ā¤žā¤¨ā¤žā¤š", + "backup_controller_page_background_configure_error": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤏āĨ‡ā¤ĩā¤ž ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŋ⤗⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "backup_controller_page_background_delay": "⤍ā¤ĩāĨ€ā¤¨ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ŦāĨ…⤕⤅ā¤Ē ⤉ā¤ļā¤ŋā¤°ā¤ž: {duration}", + "backup_controller_page_background_description": "⤅āĨ…ā¤Ē ā¤‰ā¤˜ā¤Ąā¤˛āĨā¤¯ā¤žā¤ļā¤ŋā¤ĩā¤žā¤¯ ⤍ā¤ĩāĨ€ā¤¨ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ā¤ŦāĨ…⤕⤅ā¤Ē ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤏āĨ‡ā¤ĩā¤ž ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "backup_controller_page_background_is_off": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ā¤†ā¤šāĨ‡", + "backup_controller_page_background_is_on": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤†ā¤šāĨ‡", + "backup_controller_page_background_turn_off": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤏āĨ‡ā¤ĩā¤ž ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤ž", + "backup_controller_page_background_turn_on": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤏āĨ‡ā¤ĩā¤ž ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "backup_controller_page_background_wifi": "ā¤Ģ⤕āĨā¤¤ Wi-Fi", + "backup_controller_page_backup": "ā¤ŦāĨ…⤕⤅ā¤Ē", + "backup_controller_page_backup_selected": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡: ", + "backup_controller_page_backup_sub": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ŦāĨ…⤕⤅ā¤Ē ā¤ā¤žā¤˛āĨ‡", + "backup_controller_page_created": "⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤: {date}", + "backup_controller_page_desc_backup": "⤅āĨ…ā¤Ē ā¤‰ā¤˜ā¤Ąā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ⤍ā¤ĩāĨ€ā¤¨ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤žāĨ¤", + "backup_controller_page_excluded": "ā¤ĩ⤗⤺⤞āĨ‡: ", + "backup_controller_page_failed": "{count} ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "backup_controller_page_filename": "ā¤Ģā¤žā¤ˆā¤˛ ā¤¨ā¤žā¤ĩ: {filename} ({size})", + "backup_controller_page_id": "ā¤†ā¤¯ā¤ĄāĨ€: {id}", + "backup_controller_page_info": "ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Žā¤žā¤šā¤ŋ⤤āĨ€", + "backup_controller_page_none_selected": "ā¤•ā¤žā¤šāĨ€ā¤šāĨ€ ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "backup_controller_page_remainder": "ā¤ļā¤ŋ⤞āĨā¤˛ā¤•", + "backup_controller_page_remainder_sub": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ā¤Žā¤§āĨ€ā¤˛ ⤉⤰āĨā¤ĩ⤰ā¤ŋ⤤ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ŦāĨ…⤕⤅ā¤Ē ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡", + "backup_controller_page_server_storage": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ", + "backup_controller_page_start_backup": "ā¤ŦāĨ…⤕⤅ā¤Ē ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "backup_controller_page_status_off": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ā¤†ā¤šāĨ‡", + "backup_controller_page_status_on": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤†ā¤šāĨ‡", + "backup_controller_page_storage_format": "{used} ā¤ĒāĨˆā¤•āĨ€ {total} ā¤ĩā¤žā¤Ē⤰⤞āĨ‡", + "backup_controller_page_to_backup": "ā¤ŦāĨ…⤕⤅ā¤Ēā¤¸ā¤žā¤ āĨ€ ⤅⤞āĨā¤Ŧā¤Ž", + "backup_controller_page_total_sub": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ⤏āĨā¤ĩ⤤⤂⤤āĨā¤° ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "backup_controller_page_turn_off": "ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤ž", + "backup_controller_page_turn_on": "ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ā¤ŦāĨ…⤕⤅ā¤Ē ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "backup_controller_page_uploading_file_info": "ā¤Ģā¤žā¤ˆā¤˛ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "backup_err_only_album": "⤅⤂⤤ā¤ŋā¤Ž ⤅⤞āĨā¤Ŧā¤Ž ā¤•ā¤žā¤ĸā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€", + "backup_info_card_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸", + "backup_manual_cancelled": "⤰ā¤ĻāĨā¤Ļ ⤕āĨ‡ā¤˛āĨ‡", + "backup_manual_in_progress": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤆⤧āĨ€ā¤š ā¤šā¤žā¤˛āĨ‚ ā¤†ā¤šāĨ‡. ā¤ĨāĨ‹ā¤ĄāĨā¤¯ā¤žā¤ĩāĨ‡ā¤ŗāĨ‡ā¤¨ā¤‚⤤⤰ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨ ā¤•ā¤°ā¤ž", + "backup_manual_success": "⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "backup_manual_title": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "backup_options": "ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "backup_options_page_title": "ā¤ŦāĨ…⤕⤅ā¤Ē ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "backup_setting_subtitle": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤆⤪ā¤ŋ ā¤ĢāĨ‹ā¤°ā¤—āĨā¤°ā¤žā¤‰ā¤‚ā¤Ą ⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "backup_settings_subtitle": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "backward": "ā¤Žā¤žā¤—ā¤žā¤¸ā¤˛āĨ‡ā¤˛āĨ‡", + "biometric_auth_enabled": "ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ā¤šā¤žā¤˛āĨ‚ ā¤†ā¤šāĨ‡", + "biometric_locked_out": "⤆ā¤Ē⤪ ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•ā¤°ā¤Ŗā¤žā¤Ēā¤žā¤¸āĨ‚⤍ ⤞āĨ‰ā¤• ā¤†ā¤šā¤žā¤¤", + "biometric_no_options": "⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "biometric_not_available": "ā¤¯ā¤ž ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ā¤ĩ⤰ ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤žā¤šāĨ€", + "birthdate_saved": "⤜⤍āĨā¤Žā¤¤ā¤žā¤°āĨ€ā¤– ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ⤜⤤⤍ ā¤ā¤žā¤˛āĨ€", + "birthdate_set_description": "ā¤ĢāĨ‹ā¤ŸāĨ‹ā¤šāĨā¤¯ā¤ž ā¤ĩāĨ‡ā¤ŗāĨ€ ā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤šāĨ‡ ā¤ĩ⤝ ā¤ŽāĨ‹ā¤œā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤜⤍āĨā¤Žā¤¤ā¤žā¤°āĨ€ā¤– ā¤ĩā¤žā¤Ē⤰⤞āĨ€ ā¤œā¤žā¤¤āĨ‡.", + "blurred_background": "ā¤Ēā¤žā¤°āĨā¤ļāĨā¤ĩ⤭āĨ‚ā¤ŽāĨ€ ⤧āĨā¤¸ā¤ŗā¤˛āĨ‡ā¤˛āĨ€", + "bugs_and_feature_requests": "ā¤Ŧ⤗āĨā¤œ & ā¤Ģā¤ŋ⤚⤰ ā¤ĩā¤ŋ⤍⤂⤤āĨā¤¯ā¤ž", + "build": "ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "build_image": "ā¤‡ā¤ŽāĨ‡ā¤œ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "bulk_delete_duplicates_confirmation": "⤆ā¤Ē⤪ {count, plural, one {1 ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛ ā¤šā¤Ÿā¤ĩā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž?} other {# ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤¸ā¤žā¤ŽāĨ‚ā¤šā¤ŋ⤕⤰ā¤ŋ⤤āĨā¤¯ā¤ž ā¤šā¤Ÿā¤ĩā¤Ŗā¤žā¤° ā¤†ā¤šāĨ‹ā¤¤ ā¤•ā¤ž?}} ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤—ā¤Ÿā¤žā¤¤āĨ€ā¤˛ ⤏⤰āĨā¤ĩā¤žā¤¤ ā¤ŽāĨ‹ā¤ āĨ€ ā¤Ģā¤žā¤ˆā¤˛ ⤠āĨ‡ā¤ĩ⤞āĨ€ ā¤œā¤žā¤ˆā¤˛; ⤇⤤⤰ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛. ā¤šāĨ€ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€!", + "bulk_keep_duplicates_confirmation": "⤆ā¤Ē⤪ {count, plural, one {1 ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛} other {# ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸}} ā¤°ā¤žā¤–ā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž? ⤕āĨ‹ā¤Ŗā¤¤āĨ€ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛ ā¤šā¤Ÿā¤ĩ⤞āĨ€ ā¤œā¤žā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€.", + "bulk_trash_duplicates_confirmation": "⤆ā¤Ē⤪ {count, plural, one {1 ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛ ⤟āĨā¤°āĨ…ā¤ļ ā¤•ā¤°ā¤Ŗā¤žā¤° ā¤†ā¤šā¤žā¤¤ ā¤•ā¤ž?} other {# ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤟āĨā¤°āĨ…ā¤ļ ā¤•ā¤°ā¤Ŗā¤žā¤° ā¤†ā¤šāĨ‹ā¤¤ ā¤•ā¤ž?}} ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤—ā¤Ÿā¤žā¤¤āĨ€ā¤˛ ⤏⤰āĨā¤ĩā¤žā¤¤ ā¤ŽāĨ‹ā¤ āĨ€ ā¤Ģā¤žā¤ˆā¤˛ ⤠āĨ‡ā¤ĩ⤞āĨ€ ā¤œā¤žā¤ˆā¤˛; ⤇⤤⤰ ⤟āĨā¤°āĨ…ā¤ļ ⤕āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛.", + "buy": "Immich ⤖⤰āĨ‡ā¤ĻāĨ€ ā¤•ā¤°ā¤ž", + "cache_settings_clear_cache_button": "⤕āĨ…ā¤ļ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "cache_settings_clear_cache_button_title": "⤅āĨ…ā¤Ē⤚āĨ‡ ⤕āĨ…ā¤ļ ā¤Žā¤ŋ⤟ā¤ĩ⤤āĨ‡. ⤕āĨ…ā¤ļ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤šāĨ‹ā¤ˆā¤Ē⤰āĨā¤¯ā¤‚⤤ ⤅āĨ…ā¤Ē⤚āĨ€ ā¤•ā¤žā¤Žā¤—ā¤ŋ⤰āĨ€ ā¤ĒāĨā¤°ā¤­ā¤žā¤ĩā¤ŋ⤤ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡.", + "cache_settings_duplicated_assets_clear_button": "ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "cache_settings_duplicated_assets_subtitle": "⤅āĨ…ā¤Ē⤍āĨ‡ ā¤ĩ⤗⤺⤞āĨ‡ā¤˛āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "cache_settings_duplicated_assets_title": "{count} ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸", + "cache_settings_statistics_album": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_full": "ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "cache_settings_statistics_shared": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤šāĨ‡ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_thumbnail": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛", + "cache_settings_statistics_title": "⤕āĨ…ā¤ļ ā¤ĩā¤žā¤Ē⤰", + "cache_settings_subtitle": "Immich ⤅āĨ…ā¤Ē⤚āĨ‡ ⤕āĨ…ā¤ļā¤ŋ⤂⤗ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤Ŗ ā¤•ā¤°ā¤ž", + "cache_settings_tile_subtitle": "⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ ⤍ā¤ŋ⤝⤂⤤āĨā¤°ā¤Ŗ ā¤•ā¤°ā¤ž", + "cache_settings_tile_title": "⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ⤏⤂⤗āĨā¤°ā¤šā¤Ŗ", + "cache_settings_title": "⤕āĨ…ā¤ļ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "camera": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž", + "camera_brand": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž ā¤ŦāĨā¤°ā¤ā¤Ą", + "camera_model": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛", + "cancel": "⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "cancel_search": "ā¤ļāĨ‹ā¤§ ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "canceled": "⤰ā¤ĻāĨā¤Ļ ā¤ā¤žā¤˛āĨ‡", + "canceling": "⤰ā¤ĻāĨā¤Ļ ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "cannot_merge_people": "⤞āĨ‹ā¤• ā¤ā¤•ā¤¤āĨā¤° ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤", + "cannot_undo_this_action": "ā¤šāĨ€ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€!", + "cannot_update_the_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€", + "cast": "ā¤•ā¤žā¤¸āĨā¤Ÿ", + "cast_description": "⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤•ā¤žā¤¸āĨā¤Ÿ ⤗⤂⤤ā¤ĩāĨā¤¯āĨ‡ ⤕āĨ‰ā¤¨āĨā¤Ģā¤ŋ⤗⤰ ā¤•ā¤°ā¤ž", + "change_date": "ā¤¤ā¤žā¤°āĨ€ā¤– ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_display_order": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ⤕āĨā¤°ā¤Ž ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_expiration_time": "ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤āĨ€ ā¤ĩāĨ‡ā¤ŗ ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_name": "ā¤¨ā¤žā¤ĩ ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_name_successfully": "ā¤¨ā¤žā¤ĩ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤Ŧā¤Ļ⤞⤞āĨ‡", + "change_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_password_description": "ā¤ĒāĨā¤°ā¤Ĩā¤Žā¤š ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļ⤞⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤ĩā¤ŋ⤍⤂⤤āĨ€, ā¤–ā¤žā¤˛āĨ€ ⤍ā¤ĩāĨ€ā¤¨ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ÿā¤žā¤•ā¤žāĨ¤", + "change_password_form_confirm_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ā¤•ā¤°ā¤ž", + "change_password_form_description": "ā¤šā¤žā¤¯ {name}, \n\nā¤ĒāĨā¤°ā¤Ĩā¤Žā¤š ā¤¸ā¤žā¤‡ā¤¨ ⤇⤍ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļ⤞⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤ĩā¤ŋ⤍⤂⤤āĨ€ ā¤ā¤žā¤˛āĨ€ ā¤†ā¤šāĨ‡. ā¤–ā¤žā¤˛āĨ€ ⤍ā¤ĩāĨ€ā¤¨ ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž.", + "change_password_form_new_password": "⤍ā¤ĩāĨ€ā¤¨ ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ", + "change_password_form_password_mismatch": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤜āĨā¤ŗā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "change_password_form_reenter_new_password": "⤍ā¤ĩāĨ€ā¤¨ ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "change_pin_code": "PIN ⤕āĨ‹ā¤Ą ā¤Ŧā¤Ļā¤˛ā¤ž", + "change_your_password": "⤆ā¤Ēā¤˛ā¤ž ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļā¤˛ā¤ž", + "changed_visibility_successfully": "ā¤ĻāĨƒā¤ļāĨā¤¯ā¤Žā¤žā¤¨ā¤¤ā¤ž ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤Ŧā¤Ļ⤞⤞āĨ€", + "check_corrupt_asset_backup": "⤭āĨā¤°ā¤ˇāĨā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛ ā¤ŦāĨ…⤕⤅ā¤Ē ⤤ā¤Ēā¤žā¤¸ā¤ž", + "check_corrupt_asset_backup_button": "⤤ā¤Ēā¤žā¤¸ā¤ŖāĨ€ ā¤•ā¤°ā¤ž", + "check_corrupt_asset_backup_description": "ā¤Ģ⤕āĨā¤¤ Wi-Fi ā¤ĩ⤰ ā¤šā¤ž ⤤ā¤Ēā¤žā¤¸ ā¤šā¤žā¤˛ā¤ĩā¤ž ⤆⤪ā¤ŋ ⤏⤰āĨā¤ĩ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ŦāĨ…⤕⤅ā¤Ē ā¤ā¤žā¤˛āĨā¤¯ā¤žā¤ĩ⤰⤚. ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤•ā¤žā¤šāĨ€ ā¤Žā¤ŋ⤍ā¤ŋ⤟āĨ‡ ā¤˛ā¤žā¤—āĨ‚ ā¤ļ⤕⤤āĨ‡.", + "check_logs": "⤞āĨ‰ā¤—āĨā¤œ ⤤ā¤Ēā¤žā¤¸ā¤ž", + "choose_matching_people_to_merge": "ā¤ĩā¤ŋ⤞āĨ€ā¤¨ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤜āĨā¤ŗā¤Ŗā¤žā¤°āĨ‡ ⤞āĨ‹ā¤• ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "city": "ā¤ļā¤šā¤°", + "clear": "ā¤¸ā¤žā¤Ģ ā¤•ā¤°ā¤ž", + "clear_all": "⤏⤰āĨā¤ĩ ā¤¸ā¤žā¤Ģ ā¤•ā¤°ā¤ž", + "clear_all_recent_searches": "⤏⤰āĨā¤ĩ ā¤ļāĨ‹ā¤§ ⤇⤤ā¤ŋā¤šā¤žā¤¸ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "clear_file_cache": "ā¤Ģā¤žā¤ˆā¤˛ ⤕āĨ…ā¤ļ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "clear_message": "⤏⤂ā¤ĻāĨ‡ā¤ļ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "clear_value": "ā¤ŽāĨ‚⤞āĨā¤¯ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "client_cert_dialog_msg_confirm": "⤠āĨ€ā¤• ā¤†ā¤šāĨ‡", + "client_cert_enter_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ÿā¤žā¤•ā¤ž", + "client_cert_import": "ā¤†ā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "client_cert_import_success_msg": "⤕āĨā¤˛ā¤žā¤¯ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤†ā¤¯ā¤žā¤¤ ā¤ā¤žā¤˛āĨ‡", + "client_cert_invalid_msg": "⤅ā¤ĩāĨˆā¤§ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤Ģā¤žā¤ˆā¤˛ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤚āĨā¤•āĨ€ā¤šā¤ž ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ", + "client_cert_remove_msg": "⤕āĨā¤˛ā¤žā¤¯ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤° ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤˛āĨ‡", + "client_cert_subtitle": "ā¤Ģ⤕āĨā¤¤ PKCS12 (.p12, .pfx) ā¤ĢāĨ‰ā¤°ā¤ŽāĨ…ā¤Ÿā¤˛ā¤ž ā¤¸ā¤Žā¤°āĨā¤Ĩ⤍. ā¤†ā¤¯ā¤žā¤¤/ā¤•ā¤žā¤ĸ⤪āĨ‡ ⤕āĨ‡ā¤ĩ⤺ ⤞āĨ‰ā¤—ā¤ŋ⤍ā¤ĒāĨ‚⤰āĨā¤ĩāĨ€ ⤉ā¤Ē⤞ā¤ŦāĨā¤§", + "client_cert_title": "SSL ⤕āĨā¤˛ā¤žā¤¯ā¤‚ā¤Ÿ ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗā¤Ē⤤āĨā¤°", + "clockwise": "ā¤¤ā¤žā¤¸ā¤žā¤šāĨā¤¯ā¤ž ā¤Ļā¤ŋā¤ļāĨ‡ā¤¨āĨ‡", + "close": "ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤ž", + "collapse": "⤏⤂⤕āĨā¤šā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "collapse_all": "⤏⤰āĨā¤ĩ ⤏⤂⤕āĨā¤šā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "color": "⤰⤂⤗", + "color_theme": "⤰⤂⤗ ā¤ĨāĨ€ā¤Ž", + "comment_deleted": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨ€", + "comment_options": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "comments_and_likes": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨā¤¯ā¤ž & ā¤˛ā¤žā¤ˆā¤•āĨā¤¸", + "comments_are_disabled": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨā¤¯ā¤ž ⤅⤕āĨā¤ˇā¤Ž ā¤†ā¤šāĨ‡ā¤¤", + "common_create_new_album": "⤍ā¤ĩāĨ€ā¤¨ ⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "common_server_error": "⤤āĨā¤Žā¤šāĨ‡ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ⤕⤍āĨ‡ā¤•āĨā¤ļ⤍ ⤤ā¤Ēā¤žā¤¸ā¤ž. ⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤ĒāĨ‹ā¤šāĨ‹ā¤šā¤ŖāĨā¤¯ā¤žā¤¯āĨ‹ā¤—āĨā¤¯ ā¤†ā¤šāĨ‡ ā¤•ā¤ž ā¤ĩ ⤅āĨ…ā¤Ē/⤏⤰āĨā¤ĩāĨā¤šā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤜āĨā¤ŗā¤¤ ā¤†ā¤šāĨ‡ ā¤•ā¤ž ⤤āĨ‡ ā¤Ēā¤žā¤šā¤ž.", + "completed": "ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨ‡", + "confirm": "ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ā¤•ā¤°ā¤ž", + "confirm_admin_password": "ā¤ā¤Ąā¤Žā¤ŋ⤍ ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ā¤•ā¤°ā¤ž", + "confirm_delete_face": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž {name} ⤚āĨ‡ ⤚āĨ‡ā¤šā¤°ā¤ž ā¤¯ā¤ž ā¤Ģā¤žā¤ˆā¤˛ā¤Žā¤§āĨ‚⤍ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "confirm_delete_shared_link": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šā¤ž ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "confirm_keep_this_delete_others": "ā¤¯ā¤ž ā¤Ģā¤žā¤ˆā¤˛ ā¤ĩāĨā¤¯ā¤¤ā¤ŋ⤰ā¤ŋ⤕āĨā¤¤ ⤇⤤⤰ ⤏⤰āĨā¤ĩ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛. ā¤ĒāĨā¤ĸāĨ‡ ⤏āĨā¤°āĨ‚ ⤠āĨ‡ā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "confirm_new_pin_code": "⤍ā¤ĩāĨ€ā¤¨ PIN ⤕āĨ‹ā¤Ą ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ā¤•ā¤°ā¤ž", + "confirm_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤ĒāĨā¤ˇāĨā¤ŸāĨ€ ā¤•ā¤°ā¤ž", + "confirm_tag_face": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šā¤ž ⤚āĨ‡ā¤šā¤°ā¤ž {name} ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤟āĨ…⤗ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "confirm_tag_face_unnamed": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šā¤ž ⤚āĨ‡ā¤šā¤°ā¤ž ⤟āĨ…⤗ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "connected_device": "⤕⤍āĨ‡ā¤•āĨā¤Ÿ ā¤ā¤žā¤˛āĨ‡ā¤˛āĨ‡ ⤉ā¤Ē⤕⤰⤪", + "connected_to": "ā¤ļāĨ€ ⤕⤍āĨ‡ā¤•āĨā¤Ÿ ⤕āĨ‡ā¤˛āĨ‡", + "contain": "ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "context": "⤏⤂ā¤Ļ⤰āĨā¤­", + "continue": "ā¤ĒāĨā¤ĸāĨ‡", + "control_bottom_app_bar_create_new_album": "⤍ā¤ĩāĨ€ā¤¨ ⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "control_bottom_app_bar_delete_from_immich": "Immich ā¤Žā¤§āĨ‚⤍ ā¤šā¤Ÿā¤ĩā¤ž", + "control_bottom_app_bar_delete_from_local": "⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤¤āĨ‚⤍ ā¤šā¤Ÿā¤ĩā¤ž", + "control_bottom_app_bar_edit_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "control_bottom_app_bar_edit_time": "ā¤¤ā¤žā¤°āĨ€ā¤– ā¤ĩ ā¤ĩāĨ‡ā¤ŗ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "control_bottom_app_bar_share_link": "⤞ā¤ŋ⤂⤕ ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "control_bottom_app_bar_share_to": "⤝āĨ‡ā¤ĨāĨ‡ ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "control_bottom_app_bar_trash_from_immich": "⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤šā¤˛ā¤ĩā¤ž", + "copied_image_to_clipboard": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ⤕āĨ‡ā¤˛āĨ€āĨ¤", + "copied_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤ā¤žā¤˛āĨ‡!", + "copy_error": "⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "copy_file_path": "ā¤Ģā¤žā¤ˆā¤˛ā¤šā¤ž ā¤Žā¤žā¤°āĨā¤— ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "copy_image": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "copy_link": "⤞ā¤ŋ⤂⤕ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "copy_link_to_clipboard": "⤞ā¤ŋ⤂⤕ ⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "copy_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "copy_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤ž", + "country": "ā¤ĻāĨ‡ā¤ļ", + "cover": "⤆ā¤ĩ⤰⤪", + "covers": "⤆ā¤ĩ⤰⤪āĨ‡", + "create": "ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_album_page_untitled": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤•āĨ‡ā¤¤ā¤°", + "create_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_link": "⤞ā¤ŋ⤂⤕ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_link_to_share": "ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤞ā¤ŋ⤂⤕ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_link_to_share_description": "⤞ā¤ŋ⤂⤕ ⤅⤏⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ⤕āĨ‹ā¤Ŗā¤žā¤˛ā¤žā¤šāĨ€ ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ēā¤žā¤šāĨ‚ ā¤ĻāĨā¤¯ā¤ž", + "create_new": "⤍ā¤ĩāĨ€ā¤¨ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_new_person": "⤍ā¤ĩāĨ€ā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_new_person_hint": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤍ā¤ĩāĨ€ā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤ļāĨ€ ⤜āĨ‹ā¤Ąā¤ž", + "create_new_user": "⤍ā¤ĩāĨ€ā¤¨ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_shared_album_page_share_add_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤜āĨ‹ā¤Ąā¤ž", + "create_shared_album_page_share_select_photos": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "create_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_tag": "⤟āĨ…⤗ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "create_tag_description": "⤍ā¤ĩāĨ€ā¤¨ ⤟āĨ…⤗ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž. ⤏ā¤Ŧ⤟āĨ…ā¤—ā¤¸ā¤žā¤ āĨ€ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤Ēā¤žā¤Ĩā¤¸ā¤šā¤ŋ⤤ ā¤¨ā¤žā¤ĩ ā¤Ÿā¤žā¤•ā¤žāĨ¤", + "create_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "created": "ā¤¤ā¤¯ā¤žā¤° ⤕āĨ‡ā¤˛āĨ‡", + "created_at": "⤍ā¤ŋ⤰āĨā¤Žā¤ŋ⤤āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "crop": "ā¤›ā¤žā¤Ÿā¤ŖāĨ€ ā¤•ā¤°ā¤ž", + "curated_object_page_title": "⤗āĨ‹ā¤ˇāĨā¤ŸāĨ€", + "current_device": "ā¤ĩ⤰āĨā¤¤ā¤Žā¤žā¤¨ ⤉ā¤Ē⤕⤰⤪", + "current_pin_code": "ā¤šā¤žā¤˛āĨ‚ PIN ⤕āĨ‹ā¤Ą", + "current_server_address": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤Ē⤤āĨā¤¤ā¤ž", + "custom_locale": "ā¤­ā¤žā¤ˇā¤ž ā¤ĩ ⤕āĨā¤ˇāĨ‡ā¤¤āĨā¤°", + "custom_locale_description": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ĩ ⤏⤂⤖āĨā¤¯ā¤ž ā¤­ā¤žā¤ˇāĨ‡ā¤¨āĨā¤¸ā¤žā¤° ā¤ĩ ⤕āĨā¤ˇāĨ‡ā¤¤āĨā¤°ā¤žā¤¨āĨā¤¸ā¤žā¤° format ā¤•ā¤°ā¤ž", + "custom_url": "ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ URL", + "daily_title_text_date": "⤈, ā¤ā¤Žā¤ā¤Žā¤ā¤Ž ā¤ĄāĨ€ā¤ĄāĨ€", + "daily_title_text_date_year": "⤈, ā¤ā¤Žā¤ā¤Žā¤ā¤Ž ā¤Ļā¤ŋā¤ĩ⤏, ā¤ĩ⤰āĨā¤ˇ", + "dark": "ā¤Ąā¤žā¤°āĨā¤•", + "dark_theme": "ā¤Ąā¤žā¤°āĨā¤• ā¤ĨāĨ€ā¤Ž ā¤Ŧā¤Ļ⤞", + "date_after": "ā¤¨ā¤‚ā¤¤ā¤°ā¤šāĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "date_and_time": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ĩ ā¤ĩāĨ‡ā¤ŗ", + "date_before": "ā¤ĒāĨ‚⤰āĨā¤ĩ⤚āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "date_format": "⤈, ā¤ā¤˛ā¤ā¤˛ā¤ā¤˛ ā¤ĄāĨ€, ā¤ĩā¤žā¤ˆ â€ĸ ā¤ā¤š:ā¤ā¤Žā¤ā¤Ž ā¤", + "date_of_birth_saved": "⤜⤍āĨā¤Žā¤¤ā¤žā¤°āĨ€ā¤– ⤜⤤⤍ ā¤ā¤žā¤˛āĨ€", + "date_range": "ā¤¤ā¤žā¤°āĨ€ā¤– ā¤ļāĨā¤°āĨ‡ā¤ŖāĨ€", + "day": "ā¤Ļā¤ŋā¤ĩ⤏", + "deduplicate_all": "⤏⤰āĨā¤ĩ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤•ā¤žā¤ĸā¤ž", + "deduplication_criteria_1": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤ŽāĨ‡ā¤šā¤ž ā¤†ā¤•ā¤žā¤° (ā¤Ŧā¤žā¤‡ā¤ŸāĨā¤¸)", + "deduplication_criteria_2": "EXIF ā¤ĄāĨ‡ā¤Ÿā¤ž ā¤ĒāĨā¤°ā¤Žā¤žā¤Ŗ", + "deduplication_info": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤍ā¤ŋā¤ĩā¤žā¤°ā¤Ŗ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€", + "deduplication_info_description": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ⤍ā¤ŋā¤ĩā¤ĄāĨ‚⤍ ā¤•ā¤žā¤ĸ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤–ā¤žā¤˛āĨ€ā¤˛ ⤍ā¤ŋ⤕⤎ ā¤ĩā¤žā¤Ē⤰⤞āĨ‡ ā¤œā¤žā¤¤ā¤žā¤¤:", + "default_locale": "ā¤ĒāĨ‚⤰āĨā¤ĩ⤍ā¤ŋ⤰āĨā¤§ā¤žā¤°ā¤ŋ⤤ ā¤­ā¤žā¤ˇā¤ž", + "default_locale_description": "⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤ŦāĨā¤°ā¤žā¤‰ā¤ā¤°ā¤šāĨā¤¯ā¤ž ā¤­ā¤žā¤ˇā¤ž-ā¤Ē⤰ā¤ŋā¤¸ā¤°ā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ĩ ⤏⤂⤖āĨā¤¯ā¤ž ⤏āĨā¤ĩ⤰āĨ‚ā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "delete": "ā¤šā¤Ÿā¤ĩā¤ž", + "delete_action_confirmation_message": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž? ā¤šāĨ€ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨā¤¯ā¤ž ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨā¤¯āĨ‡ ā¤šā¤˛ā¤ĩāĨ‡ā¤˛ ⤆⤪ā¤ŋ ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ā¤Ē⤪āĨ‡ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤•ā¤ž ⤤āĨ‡ ā¤ĩā¤ŋā¤šā¤žā¤°āĨ‡ā¤˛", + "delete_action_prompt": "{count} ā¤šā¤Ÿā¤ĩ⤞āĨ‡", + "delete_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤ĩā¤ž", + "delete_api_key_prompt": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šā¤ž API ⤕āĨ€ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "delete_dialog_alert": "ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ Immich ⤆⤪ā¤ŋ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛", + "delete_dialog_alert_local": "ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿā¤Ē⤪āĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛, ā¤Ē⤰⤂⤤āĨ Immich ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤°ā¤žā¤šā¤¤āĨ€ā¤˛", + "delete_dialog_alert_local_non_backed_up": "ā¤•ā¤žā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ Immich ā¤ĩ⤰ ā¤ŦāĨ…⤕⤅ā¤Ē ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤ ⤆⤪ā¤ŋ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤ĩā¤ŋā¤ļā¤ŋ⤎āĨā¤Ÿā¤Ē⤪āĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛", + "delete_dialog_alert_remote": "ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ Immich ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨā¤¯ā¤ž ā¤œā¤žā¤¤āĨ€ā¤˛", + "delete_dialog_ok_force": "⤤⤰āĨ€ā¤šāĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_dialog_title": "ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_duplicates_confirmation": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šāĨ‡ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ā¤¤ ā¤•ā¤ž?", + "delete_face": "⤚āĨ‡ā¤šā¤°ā¤ž ā¤šā¤Ÿā¤ĩā¤ž", + "delete_key": "⤕āĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_link": "⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_local_action_prompt": "{count} ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ā¤Ē⤪āĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨ‡", + "delete_local_dialog_ok_backed_up_only": "ā¤Ģ⤕āĨā¤¤ ā¤ŦāĨ…⤕⤅ā¤Ē ā¤ā¤žā¤˛āĨ‡ā¤˛āĨ‡ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_local_dialog_ok_force": "⤤⤰āĨ€ā¤šāĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_others": "⤇⤤⤰ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_permanently": "ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_permanently_action_prompt": "{count} ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨ‡", + "delete_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_shared_link_dialog_title": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_tag": "⤟āĨ…⤗ ā¤šā¤Ÿā¤ĩā¤ž", + "delete_tag_confirmation_prompt": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž {tagName} ⤟āĨ…⤗ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "delete_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤ĩā¤ž", + "deleted_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩ⤞āĨ‡", + "deletes_missing_assets": "ā¤Ąā¤ŋ⤏āĨā¤•ā¤ĩ⤰ ⤍⤏⤞āĨ‡ā¤˛āĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩā¤ž", + "description": "ā¤ĩ⤰āĨā¤Ŗā¤¨", + "description_input_hint_text": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤜āĨ‹ā¤Ąā¤žâ€Ļ", + "description_input_submit_error": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€ - ⤤ā¤Ēā¤ļāĨ€ā¤˛ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ⤞āĨ‰ā¤— ⤤ā¤Ēā¤žā¤¸ā¤ž", + "deselect_all": "⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤Ą ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "details": "⤤ā¤Ēā¤ļāĨ€ā¤˛", + "direction": "ā¤Ļā¤ŋā¤ļā¤ž", + "disabled": "⤅⤕āĨā¤ˇā¤Ž", + "disallow_edits": "⤏⤂ā¤Ēā¤žā¤Ļ⤍ ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "discord": "ā¤Ąā¤ŋ⤏āĨā¤•āĨ‰ā¤°āĨā¤Ą", + "discover": "ā¤ļāĨ‹ā¤§ā¤ž", + "discovered_devices": "ā¤ļāĨ‹ā¤§ā¤ŋ⤞āĨ‡ā¤˛āĨ‡ ⤉ā¤Ē⤕⤰⤪āĨ‡", + "dismiss_all_errors": "⤏⤰āĨā¤ĩ ⤤āĨā¤°āĨā¤ŸāĨ€ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "dismiss_error": "⤤āĨā¤°āĨā¤ŸāĨ€ ā¤Žā¤ŋ⤟ā¤ĩā¤ž", + "display_options": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "display_order": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ⤕āĨā¤°ā¤Ž", + "display_original_photos": "ā¤ŽāĨ‚⤺ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "display_original_photos_setting_description": "ā¤ŽāĨ‚⤺ ā¤Ģā¤žā¤ˆā¤˛ ā¤ĩāĨ‡ā¤Ŧ-⤏āĨā¤¸ā¤‚⤗⤤ ⤅⤏⤞āĨā¤¯ā¤žā¤¸ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ā¤ā¤ĩ⤜āĨ€ ā¤ŽāĨ‚⤺ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ļā¤žā¤–ā¤ĩā¤ž. ā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ā¤‰ā¤˜ā¤Ąā¤ŖāĨā¤¯ā¤žā¤¸ ā¤ĨāĨ‹ā¤Ąā¤ž ā¤ĩāĨ‡ā¤ŗ ā¤˛ā¤žā¤—āĨ‚ ā¤ļ⤕⤤āĨ‹.", + "do_not_show_again": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤Ļā¤žā¤–ā¤ĩāĨ‚ ā¤¨ā¤•ā¤ž", + "documentation": "ā¤Ļ⤏āĨā¤¤ā¤ā¤ĩ⤜āĨ€ā¤•⤰⤪", + "done": "ā¤ĒāĨ‚⤰āĨā¤Ŗ", + "download": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "download_action_prompt": "{count} ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "download_canceled": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤰ā¤ĻāĨā¤Ļ ā¤ā¤žā¤˛āĨ‡", + "download_complete": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨ‡", + "download_enqueue": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤°ā¤žā¤‚ā¤•āĨ‡ā¤¤ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", + "download_error": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤤āĨā¤°āĨā¤ŸāĨ€", + "download_failed": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "download_finished": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨ‡", + "download_include_embedded_motion_videos": "ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "download_include_embedded_motion_videos_description": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹ā¤Žā¤§āĨ€ā¤˛ ā¤ā¤ŽāĨā¤ŦāĨ‡ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤏āĨā¤ĩ⤤⤂⤤āĨā¤° ā¤Ģā¤žā¤ˆā¤˛ ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ā¤¸ā¤Žā¤žā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "download_notfound": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤆ā¤ĸā¤ŗā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "download_paused": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤˛ā¤ž", + "download_settings": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "download_settings_description": "ā¤Ģā¤žā¤ˆā¤˛ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏⤂ā¤Ŧ⤂⤧ā¤ŋ⤤ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "download_started": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤏āĨā¤°āĨ‚ ā¤ā¤žā¤˛ā¤ž", + "download_sucess": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "download_sucess_android": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž DCIM/Immich ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤ā¤žā¤˛ā¤ž ā¤†ā¤šāĨ‡", + "download_waiting_to_retry": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĒāĨā¤°ā¤¯ā¤¤āĨā¤¨ ⤕⤰⤪āĨā¤¯ā¤žā¤šāĨ€ ā¤ĒāĨā¤°ā¤¤āĨ€ā¤•āĨā¤ˇā¤ž", + "downloading": "ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "downloading_asset_filename": "{filename} ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "downloading_media": "ā¤ŽāĨ€ā¤Ąā¤ŋā¤¯ā¤ž ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "drop_files_to_upload": "⤅ā¤Ē⤞āĨ‹ā¤Ąā¤¸ā¤žā¤ āĨ€ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤇ā¤ĨāĨ‡ ā¤ĄāĨā¤°āĨ‰ā¤Ē ā¤•ā¤°ā¤ž", + "duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸", + "duplicates_description": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤—ā¤Ÿā¤žā¤¤ā¤˛āĨ‡ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "duration": "ā¤•ā¤žā¤˛ā¤žā¤ĩ⤧āĨ€", + "edit": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_album": "⤅⤞āĨā¤Ŧā¤Ž ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_avatar": "⤅ā¤ĩā¤¤ā¤žā¤° ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_birthday": "ā¤ĩā¤žā¤ĸā¤Ļā¤ŋā¤ĩ⤏ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_date": "ā¤¤ā¤žā¤°āĨ€ā¤– ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_date_and_time": "ā¤¤ā¤žā¤°āĨ€ā¤– ā¤ĩ ā¤ĩāĨ‡ā¤ŗ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_date_and_time_action_prompt": "{count} ā¤¤ā¤žā¤°āĨ€ā¤– ⤆⤪ā¤ŋ ā¤ĩāĨ‡ā¤ŗ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤ā¤žā¤˛āĨ‡", + "edit_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_description_prompt": "⤍ā¤ĩāĨ€ā¤¨ ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤍ā¤ŋā¤ĩā¤Ąā¤ž:", + "edit_exclusion_pattern": "ā¤ĩā¤—ā¤ŗā¤ž ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_faces": "⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤ĩ⤰ ⤏⤂ā¤Ēā¤žā¤Ļ⤍ ā¤•ā¤°ā¤ž", + "edit_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_import_paths": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤—āĨ‡ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_key": "⤕āĨ€ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_link": "⤞ā¤ŋ⤂⤕ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_location_action_prompt": "{count} ⤏āĨā¤Ĩā¤žā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤ā¤žā¤˛āĨ‡", + "edit_location_dialog_title": "⤏āĨā¤Ĩā¤žā¤¨", + "edit_name": "ā¤¨ā¤žā¤ĩ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_people": "⤞āĨ‹ā¤• ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_tag": "⤟āĨ…⤗ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_title": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤• ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edit_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "edited": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤ā¤žā¤˛āĨ‡", + "editor": "ā¤ā¤Ąā¤ŋ⤟⤰", + "editor_close_without_save_prompt": "ā¤Ŧā¤Ļ⤞ ⤜⤤⤍ ā¤šāĨ‹ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€", + "editor_close_without_save_title": "ā¤ā¤Ąā¤ŋ⤟⤰ ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤•ā¤ž?", + "editor_crop_tool_h2_aspect_ratios": "⤅⤍āĨā¤Ēā¤žā¤¤ ā¤•ā¤°ā¤ž", + "editor_crop_tool_h2_rotation": "ā¤Ģā¤ŋ⤰ā¤ĩā¤ž", + "email": "ā¤ˆā¤ŽāĨ‡ā¤˛", + "email_notifications": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž", + "empty_folder": "ā¤šā¤ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ⤰ā¤ŋā¤•ā¤žā¤Žā¤ž ā¤†ā¤šāĨ‡", + "empty_trash": "⤟āĨā¤°āĨ…ā¤ļ ⤰ā¤ŋā¤•ā¤žā¤ŽāĨ€ ā¤•ā¤°ā¤ž", + "empty_trash_confirmation": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤟āĨā¤°āĨ…ā¤ļ ⤰ā¤ŋā¤•ā¤žā¤ŽāĨ€ ā¤•ā¤°ā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž? ā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ⤟āĨā¤°āĨ…ā¤ļā¤Žā¤§āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ Immich ā¤ĩ⤰āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤šā¤Ÿā¤ĩ⤞āĨ€ ā¤œā¤žā¤¤āĨ€ā¤˛. \nā¤šāĨ€ ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€!", + "enable": "⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "enable_backup": "ā¤ŦāĨ…⤕⤅ā¤Ē ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "enable_biometric_auth_description": "ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ PIN ⤕āĨ‹ā¤Ą ā¤Ÿā¤žā¤•ā¤ž", + "enabled": "⤏⤕āĨā¤ˇā¤Ž ā¤†ā¤šāĨ‡", + "end_date": "ā¤¸ā¤Žā¤žā¤ĒāĨā¤¤āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "enqueued": "ā¤°ā¤žā¤‚ā¤•āĨ‡ā¤¤ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", + "enter_wifi_name": "Wi-Fi ā¤¨ā¤žā¤ĩ ā¤Ÿā¤žā¤•ā¤ž", + "enter_your_pin_code": "PIN ⤕āĨ‹ā¤Ą ā¤Ÿā¤žā¤•ā¤ž", + "enter_your_pin_code_subtitle": "⤞āĨ‰ā¤• ⤕āĨ‡ā¤˛āĨ‡ā¤˛ā¤ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ā¤‰ā¤˜ā¤Ąā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ PIN ⤕āĨ‹ā¤Ą ā¤Ÿā¤žā¤•ā¤ž", + "error": "⤤āĨā¤°āĨā¤ŸāĨ€", + "error_change_sort_album": "⤅⤞āĨā¤Ŧā¤Ž ⤕āĨā¤°ā¤Ž ā¤Ŧā¤Ļ⤞⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "error_delete_face": "ā¤Ģā¤žā¤ˆā¤˛ā¤Žā¤§āĨ‚⤍ ⤚āĨ‡ā¤šā¤°ā¤ž ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_saving_image": "⤤āĨā¤°āĨā¤ŸāĨ€: {error}", + "error_tag_face_bounding_box": "⤚āĨ‡ā¤šā¤°ā¤ž ⤟āĨ…⤗ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€ – ā¤Ŧā¤žā¤‰ā¤‚ā¤Ąā¤ŋ⤂⤗ ā¤ŦāĨ‰ā¤•āĨā¤¸ ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤žā¤‚ā¤• ā¤Žā¤ŋ⤺ā¤ĩā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "error_title": "⤤āĨā¤°āĨā¤ŸāĨ€ – ā¤•ā¤žā¤šāĨ€ā¤¤ā¤°āĨ€ ⤚āĨā¤•⤞āĨ‡", + "errors": { + "cannot_navigate_next_asset": "ā¤ĒāĨā¤ĸāĨ€ā¤˛ ā¤Ģā¤žā¤ˆā¤˛ā¤ĩ⤰ ā¤œā¤žā¤Š ā¤ļ⤕⤤ ā¤¨ā¤žā¤šāĨ€", + "cannot_navigate_previous_asset": "ā¤Žā¤žā¤—āĨ€ā¤˛ ā¤Ģā¤žā¤ˆā¤˛ā¤ĩ⤰ ā¤œā¤žā¤Š ā¤ļ⤕⤤ ā¤¨ā¤žā¤šāĨ€", + "cant_apply_changes": "ā¤Ŧā¤Ļ⤞ ā¤˛ā¤žā¤—āĨ‚ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "cant_change_activity": "{enabled, select, true{⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€} other{⤕āĨā¤°ā¤ŋā¤¯ā¤ž ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€}}", + "cant_change_asset_favorite": "ā¤Ģā¤žā¤ˆā¤˛ā¤¸ā¤žā¤ āĨ€ ⤆ā¤ĩā¤Ą ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "cant_change_metadata_assets_count": "{count, plural, one{ā¤ā¤•ā¤ž ā¤Ģā¤žā¤ˆā¤˛ā¤šāĨ‡ ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€} other{# ā¤Ģā¤žā¤ˆā¤˛ā¤šāĨ‡ ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€}}", + "cant_get_faces": "⤚āĨ‡ā¤šā¤ąāĨā¤¯ā¤žā¤‚ā¤šāĨ€ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤Žā¤ŋ⤺ā¤ĩā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "cant_get_number_of_comments": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ā¤Žā¤ŋ⤺ā¤ĩā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "cant_search_people": "⤞āĨ‹ā¤• ā¤ļāĨ‹ā¤§ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "cant_search_places": "⤠ā¤ŋā¤•ā¤žā¤ŖāĨ‡ ā¤ļāĨ‹ā¤§ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "error_adding_assets_to_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤜āĨ‹ā¤Ąā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_adding_users_to_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_deleting_shared_user": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_downloading": "{filename} ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_hiding_buy_button": "⤖⤰āĨ‡ā¤ĻāĨ€ ā¤Ŧ⤟⤪ ⤞ā¤Ēā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "error_removing_assets_from_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€ – ⤅⤧ā¤ŋ⤕ ⤤ā¤Ēā¤ļāĨ€ā¤˛ā¤žā¤‚ā¤¸ā¤žā¤ āĨ€ ⤕⤍āĨā¤¸āĨ‹ā¤˛ ā¤Ēā¤šā¤ž", + "error_selecting_all_assets": "⤏⤰āĨā¤ĩ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤍ā¤ŋā¤ĩā¤Ąā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "exclusion_pattern_already_exists": "ā¤šāĨ‡ ā¤ĩ⤗⤺⤪āĨā¤¯ā¤žā¤šāĨ‡ ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ⤆⤧āĨ€ā¤š ⤅⤏āĨā¤¤ā¤ŋ⤤āĨā¤ĩā¤žā¤¤ ā¤†ā¤šāĨ‡āĨ¤", + "failed_to_create_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_create_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_edit_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_get_people": "⤞āĨ‹ā¤• ā¤Žā¤ŋ⤺ā¤ĩ⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_keep_this_delete_others": "ā¤šāĨ€ ā¤Ģā¤žā¤ˆā¤˛ ⤠āĨ‡ā¤ĩāĨ‚⤍ ⤇⤤⤰ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_asset": "ā¤Ģā¤žā¤ˆā¤˛ ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_notifications": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_people": "⤞āĨ‹ā¤• ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_remove_product_key": "⤉⤤āĨā¤Ēā¤žā¤Ļ⤍ ⤕āĨ€ ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤ŖāĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_stack_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ā¤•ā¤¤āĨā¤° ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "failed_to_unstack_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ĩā¤ŋā¤­ā¤žā¤œā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "failed_to_update_notification_status": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤏āĨā¤Ĩā¤ŋ⤤āĨ€ ⤅ā¤Ēā¤ĄāĨ‡ā¤Ÿ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "import_path_already_exists": "ā¤šā¤ž ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ⤆⤧āĨ€ā¤š ⤅⤏āĨā¤¤ā¤ŋ⤤āĨā¤ĩā¤žā¤¤ ā¤†ā¤šāĨ‡āĨ¤", + "incorrect_email_or_password": "⤚āĨā¤•āĨ€ā¤šā¤ž ā¤ˆā¤ŽāĨ‡ā¤˛ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ", + "paths_validation_failed": "{paths, plural, one {ā¤ā¤• ā¤Žā¤žā¤°āĨā¤— ā¤ĩāĨˆā¤§ ā¤¨ā¤žā¤šāĨ€} other {# ā¤Žā¤žā¤°āĨā¤— ā¤ĩāĨˆā¤§ ā¤¨ā¤žā¤šāĨ€ā¤¤}}", + "profile_picture_transparent_pixels": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤°ā¤žā¤¤ ā¤Ēā¤žā¤°ā¤Ļ⤰āĨā¤ļ⤕ ā¤Ēā¤ŋ⤕āĨā¤¸āĨ‡ā¤˛ ⤅⤏āĨ‚ ā¤ļ⤕⤤ ā¤¨ā¤žā¤šāĨ€ā¤¤. ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤āĨ‚ā¤Ž ā¤•ā¤°ā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤šā¤˛ā¤ĩā¤ž.", + "quota_higher_than_disk_size": "⤤āĨā¤ŽāĨā¤šāĨ€ ā¤Ąā¤ŋ⤏āĨā¤• ā¤†ā¤•ā¤žā¤°ā¤žā¤ĒāĨ‡ā¤•āĨā¤ˇā¤ž ā¤ŽāĨ‹ā¤ ā¤ž ⤕āĨ‹ā¤Ÿā¤ž ⤏āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛ā¤ž ā¤†ā¤šāĨ‡", + "unable_to_add_album_users": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_add_assets_to_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋā¤‚ā¤•ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_add_comment": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_add_exclusion_pattern": "ā¤ĩ⤗⤺⤪āĨā¤¯ā¤žā¤šāĨ‡ ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_add_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_add_partners": "ā¤¸ā¤šā¤¯āĨ‹ā¤—āĨ€ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_add_remove_archive": "{archived, select, true{⤅⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤šā¤Žā¤§āĨ‚⤍ ā¤Ģā¤žā¤ˆā¤˛ ā¤•ā¤žā¤ĸā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€} other{⤅⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤šā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ģā¤žā¤ˆā¤˛ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€}}", + "unable_to_add_remove_favorites": "{favorite, select, true{ā¤Ģā¤žā¤ˆā¤˛ ⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤ž ā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€} other{ā¤Ģā¤žā¤ˆā¤˛ ⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤ž ā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€}}", + "unable_to_archive_unarchive": "{archived, select, true{⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€} other{⤅⤍⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€}}", + "unable_to_change_album_user_role": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ€ā¤˛ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨ€ ⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_date": "ā¤¤ā¤žā¤°āĨ€ā¤– ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_favorite": "ā¤Ģā¤žā¤ˆā¤˛ā¤¸ā¤žā¤ āĨ€ ⤆ā¤ĩā¤Ąā¤¤āĨā¤¯ā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_location": "⤏āĨā¤Ĩā¤žā¤¨ ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_change_visibility": "{count, plural, one{ā¤ā¤• ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤šāĨ€ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤Žā¤žā¤¨ā¤¤ā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€} other{# ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤‚ā¤šāĨ€ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤Žā¤žā¤¨ā¤¤ā¤ž ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€}}", + "unable_to_complete_oauth_login": "OAuth ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_connect": "⤕⤍āĨ‡ā¤•āĨā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_copy_to_clipboard": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€, https ā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤šāĨ€ ā¤–ā¤žā¤¤āĨā¤°āĨ€ ā¤•ā¤°ā¤ž", + "unable_to_create_admin_account": "ā¤ā¤Ąā¤Žā¤ŋ⤍ ā¤–ā¤žā¤¤āĨ‡ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_create_api_key": "⤍ā¤ĩāĨ€ā¤¨ API ⤕āĨ€ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_create_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_create_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_asset": "ā¤Ģā¤žā¤ˆā¤˛ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "unable_to_delete_exclusion_pattern": "ā¤ĩ⤗⤺⤪āĨ€ ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_delete_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_download_files": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤Ąā¤žā¤‰ā¤¨ā¤˛āĨ‹ā¤Ą ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_edit_exclusion_pattern": "ā¤ĩ⤗⤺⤪āĨ€ ā¤ĒāĨ…ā¤Ÿā¤°āĨā¤¨ ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_edit_import_path": "ā¤†ā¤¯ā¤žā¤¤ ā¤Žā¤žā¤°āĨā¤— ⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_empty_trash": "⤟āĨā¤°āĨ…ā¤ļ ⤰ā¤ŋā¤•ā¤žā¤Žā¤ž ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_enter_fullscreen": "ā¤ĢāĨā¤˛ā¤¸āĨā¤•āĨā¤°āĨ€ā¤¨ ā¤ŽāĨ‹ā¤Ąā¤Žā¤§āĨā¤¯āĨ‡ ā¤œā¤žā¤Š ā¤ļ⤕⤤ ā¤¨ā¤žā¤šāĨ€", + "unable_to_exit_fullscreen": "ā¤ĢāĨā¤˛ā¤¸āĨā¤•āĨā¤°āĨ€ā¤¨ ā¤ŽāĨ‹ā¤Ąā¤Žā¤§āĨ‚⤍ ā¤Ŧā¤žā¤šāĨ‡ā¤° ā¤Ēā¤Ąā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_get_comments_number": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨā¤¯ā¤žā¤‚ā¤šāĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ā¤Žā¤ŋ⤺ā¤ĩā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_get_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋ⤂⤕ ā¤Žā¤ŋ⤺ā¤ĩā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_hide_person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ⤞ā¤Ēā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_link_motion_video": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_link_oauth_account": "OAuth ā¤–ā¤žā¤¤āĨ‡ ⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_log_out_all_devices": "⤏⤰āĨā¤ĩ ⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤‚ā¤Žā¤§āĨ‚⤍ ⤞āĨ‰ā¤—ā¤†ā¤‰ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_log_out_device": "⤉ā¤Ēā¤•ā¤°ā¤Ŗā¤žā¤ĩ⤰āĨ‚⤍ ⤞āĨ‰ā¤—ā¤†ā¤‰ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_login_with_oauth": "OAuth ā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ⤞āĨ‰ā¤—ā¤ŋ⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_play_video": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_reassign_assets_existing_person": "{name} ā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤•ā¤ĄāĨ‡ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ĒāĨā¤¨ā¤ƒ ⤍āĨ‡ā¤Žā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_reassign_assets_new_person": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤍ā¤ĩāĨ€ā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤•ā¤ĄāĨ‡ ā¤ĒāĨā¤¨ā¤ƒ ⤍āĨ‡ā¤Žā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_refresh_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤰ā¤ŋā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_album_users": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_api_key": "API ⤕āĨ€ ā¤•ā¤žā¤ĸāĨ‚⤍ ā¤Ÿā¤žā¤•ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_assets_from_shared_link": "ā¤ļāĨ‡ā¤…⤰ ⤞ā¤ŋā¤‚ā¤•ā¤Žā¤§āĨ€ā¤˛ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_partner": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤° ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_remove_reaction": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ⤕āĨā¤°ā¤ŋā¤¯ā¤ž ā¤šā¤Ÿā¤ĩā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_reset_password": "⤏⤂⤕āĨ‡ā¤¤ā¤ļā¤ŦāĨā¤Ļ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_reset_pin_code": "PIN ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_resolve_duplicate": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ⤏āĨ‹ā¤Ąā¤ĩā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_restore_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_restore_trash": "⤟āĨā¤°āĨ…ā¤ļ ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_restore_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_album": "⤅⤞āĨā¤Ŧā¤Ž ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_api_key": "API ⤕āĨ€ ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_date_of_birth": "⤜⤍āĨā¤Žā¤¤ā¤žā¤°āĨ€ā¤– ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ€ ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_name": "ā¤¨ā¤žā¤ĩ ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_profile": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_save_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤜⤤⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_scan_libraries": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "unable_to_scan_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_set_feature_photo": "ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_set_profile_picture": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤¤ā¤ž ā¤†ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "unable_to_submit_job": "ā¤•ā¤žā¤Ž ⤏ā¤Ŧā¤Žā¤ŋ⤟ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_trash_asset": "ā¤Ģā¤žā¤ˆā¤˛ ⤟āĨā¤°āĨ…ā¤ļ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_unlink_account": "ā¤–ā¤žā¤¤āĨ‡ ⤅⤍⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_unlink_motion_video": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤅⤍⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_album_cover": "⤅⤞āĨā¤Ŧā¤Ž ⤕ā¤ĩāĨā¤šā¤° ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_album_info": "⤅⤞āĨā¤Ŧā¤Ž ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_library": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_timeline_display_status": "ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ ⤏āĨā¤Ĩā¤ŋ⤤āĨ€ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_update_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "unable_to_upload_file": "ā¤Ģā¤žā¤ˆā¤˛ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤¤ā¤ž ⤆⤞āĨ‡ ā¤¨ā¤žā¤šāĨ€" + }, + "exif": "ā¤ā¤•āĨā¤¸ā¤ŋā¤Ģ", + "exif_bottom_sheet_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤜āĨ‹ā¤Ąā¤žâ€Ļ", + "exif_bottom_sheet_description_error": "ā¤ĩ⤰āĨā¤Ŗā¤¨ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "exif_bottom_sheet_details": "⤤ā¤Ēā¤ļāĨ€ā¤˛", + "exif_bottom_sheet_location": "⤏āĨā¤Ĩā¤žā¤¨", + "exif_bottom_sheet_people": "⤞āĨ‹ā¤•", + "exif_bottom_sheet_person_add_person": "ā¤¨ā¤žā¤ĩ ⤜āĨ‹ā¤Ąā¤ž", + "exit_slideshow": "⤏āĨā¤˛ā¤žā¤‡ā¤Ąā¤ļāĨ‹ ā¤Ŧ⤂ā¤Ļ ā¤•ā¤°ā¤ž", + "expand_all": "⤏⤰āĨā¤ĩ ā¤ĩā¤ŋ⤏āĨā¤¤ā¤žā¤° ā¤•ā¤°ā¤ž", + "experimental_settings_new_asset_list_subtitle": "ā¤•ā¤žā¤Ž ⤏āĨā¤°āĨ‚ ā¤†ā¤šāĨ‡", + "experimental_settings_new_asset_list_title": "ā¤ĒāĨā¤°ā¤žā¤¯āĨ‹ā¤—ā¤ŋ⤕ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤗āĨā¤°ā¤ŋā¤Ą ā¤šā¤žā¤˛āĨ‚ ā¤•ā¤°ā¤ž", + "experimental_settings_subtitle": "⤏āĨā¤ĩ⤤⤃⤚āĨā¤¯ā¤ž ⤜ā¤Ŧā¤žā¤Ŧā¤Ļā¤žā¤°āĨ€ā¤ĩ⤰ ā¤ĩā¤žā¤Ēā¤°ā¤ž!", + "experimental_settings_title": "ā¤ĒāĨā¤°ā¤žā¤¯āĨ‹ā¤—ā¤ŋ⤕", + "expire_after": "⤍⤂⤤⤰ ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯", + "expired": "ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯", + "expires_date": "{date} ā¤˛ā¤ž ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯", + "explore": "⤅⤍āĨā¤ĩāĨ‡ā¤ˇā¤Ŗ ā¤•ā¤°ā¤ž", + "explorer": "ā¤ā¤•āĨā¤¸ā¤ĒāĨā¤˛āĨ‹ā¤°ā¤°", + "export": "⤍ā¤ŋ⤰āĨā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "export_as_json": "JSON ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤍ā¤ŋ⤰āĨā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "export_database": "ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤍ā¤ŋ⤰āĨā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "export_database_description": "SQLite ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤍ā¤ŋ⤰āĨā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž", + "extension": "ā¤ā¤•āĨā¤¸āĨā¤ŸāĨ‡ā¤‚ā¤ļ⤍", + "external": "ā¤Ŧā¤žā¤šāĨā¤¯", + "external_libraries": "ā¤Ŧā¤žā¤šāĨā¤¯ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€", + "external_network": "ā¤Ŧā¤žā¤šāĨā¤¯ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤•", + "external_network_sheet_info": "ā¤ĒāĨā¤°ā¤žā¤§ā¤žā¤¨āĨā¤¯ Wi-Fi ⤍⤏āĨ‡ā¤˛ ⤤⤰, ⤅āĨ…ā¤Ē ā¤–ā¤žā¤˛āĨ€ā¤˛ URL ā¤ĩ⤰āĨ‚⤍ ā¤ĩ⤰⤚āĨā¤¯ā¤ž ⤕āĨā¤°ā¤Žā¤žā¤¨āĨ‡ ⤤ā¤Ēā¤žā¤¸āĨ‚⤍ ā¤Ēā¤šā¤ŋ⤞āĨā¤¯ā¤ž ā¤ĒāĨ‹ā¤šāĨ‹ā¤šā¤Ŗā¤žā¤ąāĨā¤¯ā¤ž URL ⤍āĨ‡ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ļāĨ€ ⤜āĨ‹ā¤ĄāĨ‡ā¤˛", + "face_unassigned": "ā¤¨ā¤žā¤ĩ ā¤Ļā¤ŋ⤞āĨ‡ā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "failed": "⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_authenticate": "ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_assets": "ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "failed_to_load_folder": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "favorite": "⤆ā¤ĩā¤Ąā¤¤āĨ‡", + "favorite_action_prompt": "{count} ⤆ā¤ĩā¤ĄāĨ€ā¤‚ā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨ‡", + "favorite_or_unfavorite_photo": "ā¤ĢāĨ‹ā¤ŸāĨ‹ â€˜ā¤†ā¤ĩā¤Ąā¤¤āĨ‡â€™ ā¤•ā¤°ā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤•ā¤žā¤ĸā¤ž", + "favorites": "⤆ā¤ĩā¤ĄāĨ€", + "favorites_page_no_favorites": "⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ€ā¤˛ ā¤Ģā¤žā¤ˆā¤˛āĨā¤¸ ā¤¸ā¤žā¤Ēā¤Ąā¤˛āĨā¤¯ā¤ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "feature_photo_updated": "ā¤ĢāĨ€ā¤šā¤° ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ā¤ŋ⤤ ā¤ā¤žā¤˛ā¤ž", + "features": "ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡", + "features_setting_description": "⤅āĨ…ā¤Ē⤚āĨ€ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "file_name": "ā¤Ģā¤žā¤ˆā¤˛ ā¤¨ā¤žā¤ĩ", + "file_name_or_extension": "ā¤Ģā¤žā¤ˆā¤˛ ā¤¨ā¤žā¤ĩ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ā¤•āĨā¤¸āĨā¤ŸāĨ‡ā¤‚ā¤ļ⤍", + "filename": "ā¤Ģā¤žā¤‡ā¤˛ā¤¨ā¤žā¤ĩ", + "filetype": "ā¤Ģā¤žā¤ˆā¤˛ ā¤ĒāĨā¤°ā¤•ā¤žā¤°", + "filter": "ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤°", + "filter_people": "⤞āĨ‹ā¤• ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ā¤•ā¤°ā¤ž", + "filter_places": "⤠ā¤ŋā¤•ā¤žā¤ŖāĨ‡ ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ā¤•ā¤°ā¤ž", + "find_them_fast": "ā¤¨ā¤žā¤ĩā¤žā¤¨āĨ‡ ā¤Ēā¤Ÿā¤•ā¤¨ ā¤ļāĨ‹ā¤§ā¤ž", + "fix_incorrect_match": "⤚āĨā¤•āĨ€ā¤šāĨ€ ⤜āĨā¤ŗā¤ŖāĨ€ ā¤ĻāĨā¤°āĨā¤¸āĨā¤¤ ā¤•ā¤°ā¤ž", + "folder": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°", + "folder_not_found": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ā¤¸ā¤žā¤Ēā¤Ąā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "folders": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°āĨā¤¸", + "folders_feature_description": "ā¤Ģā¤žā¤ˆā¤˛ ⤏ā¤ŋ⤏āĨā¤ŸāĨ€ā¤Žā¤ĩ⤰āĨ€ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹-ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤° ā¤ĻāĨƒā¤ļāĨā¤¯ā¤žā¤¤ ā¤ŦāĨā¤°ā¤žā¤‰ā¤ ā¤•ā¤°ā¤ž", + "forgot_pin_code_question": "PIN ā¤ĩā¤ŋā¤¸ā¤°ā¤˛ā¤žā¤¤?", + "forward": "ā¤ĒāĨā¤ĸāĨ‡", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "ā¤šāĨ€ ⤏āĨā¤ĩā¤ŋā¤§ā¤ž ā¤šā¤žā¤˛ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ Google ā¤•ā¤ĄāĨ€ā¤˛ ā¤Ŧā¤žā¤šāĨā¤¯ ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨āĨ‡ ⤞āĨ‹ā¤Ą ⤕⤰⤤āĨ‡.", + "general": "ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯", + "get_help": "ā¤Žā¤Ļ⤤ ⤘āĨā¤¯ā¤ž", + "get_wifiname_error": "Wi-Fi ⤚āĨ‡ ā¤¨ā¤žā¤ĩ ā¤Žā¤ŋā¤ŗā¤žā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€. ⤆ā¤ĩā¤ļāĨā¤¯ā¤• ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨā¤¯ā¤ž ā¤Ļā¤ŋ⤞āĨā¤¯ā¤ž ā¤†ā¤šāĨ‡ā¤¤ ⤆⤪ā¤ŋ Wi-Fi ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤•ā¤ļāĨ€ ⤜āĨ‹ā¤Ąā¤˛āĨ‡ ā¤†ā¤šā¤žā¤¤ ā¤¯ā¤žā¤šāĨ€ ā¤–ā¤žā¤¤āĨā¤°āĨ€ ā¤•ā¤°ā¤ž", + "getting_started": "⤏āĨā¤°āĨā¤ĩā¤žā¤¤ ā¤•ā¤°ā¤ž", + "go_back": "ā¤Žā¤žā¤—āĨ‡ ā¤œā¤ž", + "go_to_folder": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°ā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "go_to_search": "ā¤ļāĨ‹ā¤§ā¤žā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "grant_permission": "ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž", + "group_albums_by": "⤅⤞āĨā¤Ŧā¤Ž ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž: â€Ļ", + "group_country": "ā¤ĻāĨ‡ā¤ļā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿ ā¤•ā¤°ā¤ž", + "group_no": "ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤¨ā¤žā¤šāĨ€", + "group_owner": "ā¤Žā¤žā¤˛ā¤•ā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿ ā¤•ā¤°ā¤ž", + "group_places_by": "⤏āĨā¤Ĩ⤺āĨ‡ ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤•ā¤°ā¤ž: â€Ļ", + "notification_permission_dialog_content": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤œā¤ž ⤆⤪ā¤ŋ ⤅⤍āĨā¤Žā¤¤āĨ€ ā¤ĻāĨā¤¯ā¤ž.", + "notification_permission_list_tile_content": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤ĻāĨā¤¯ā¤ž.", + "notification_permission_list_tile_enable_button": "⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "notification_permission_list_tile_title": "⤏āĨ‚ā¤šā¤¨ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€", + "notification_toggle_setting_description": "ā¤ˆā¤ŽāĨ‡ā¤˛ ⤏āĨ‚ā¤šā¤¨ā¤ž ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž", + "notifications": "⤏āĨ‚ā¤šā¤¨ā¤ž", + "notifications_setting_description": "⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "oauth": "OAuth", + "official_immich_resources": "⤅⤧ā¤ŋ⤕āĨƒā¤¤ Immich ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨āĨ‡", + "offline": "⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨", + "offset": "⤑ā¤Ģ⤏āĨ‡ā¤Ÿ", + "ok": "⤠āĨ€ā¤•", + "oldest_first": "⤏⤰āĨā¤ĩā¤žā¤¤ ⤜āĨā¤¨āĨ‡ ⤆⤧āĨ€", + "on_this_device": "ā¤¯ā¤ž ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ā¤ĩ⤰", + "onboarding": "⤑⤍ā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ŋ⤂⤗", + "onboarding_locale_description": "⤤āĨā¤Žā¤šāĨ€ ā¤Ē⤏⤂⤤āĨ€ā¤šāĨ€ ā¤­ā¤žā¤ˇā¤ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ā¤šāĨ‡ ⤍⤂⤤⤰ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ŧā¤Ļ⤞āĨ‚ ā¤ļā¤•ā¤¤ā¤ž.", + "onboarding_privacy_description": "ā¤–ā¤žā¤˛āĨ€ā¤˛ (ā¤Ē⤰āĨā¤¯ā¤žā¤¯āĨ€) ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ā¤Ŧā¤žā¤šāĨā¤¯ ⤏āĨ‡ā¤ĩā¤žā¤‚ā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‡ā¤¤ ⤆⤪ā¤ŋ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ⤕⤧āĨ€ā¤šāĨ€ ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤¤ā¤žā¤¤.", + "onboarding_server_welcome_description": "ā¤•ā¤žā¤šāĨ€ ā¤¸ā¤žā¤Žā¤žā¤¨āĨā¤¯ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤¸ā¤š ⤤āĨā¤Žā¤šāĨ€ ⤇⤍āĨā¤¸āĨā¤Ÿā¤¨āĨā¤¸ ⤏āĨ‡ā¤Ÿā¤…ā¤Ē ⤕⤰āĨ‚ā¤¯ā¤ž.", + "onboarding_theme_description": "⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤇⤍āĨā¤¸āĨā¤Ÿā¤¨āĨā¤¸ā¤¸ā¤žā¤ āĨ€ ⤰⤂⤗ ā¤ĨāĨ€ā¤Ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž. ā¤šāĨ‡ ⤍⤂⤤⤰ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ŧā¤Ļ⤞āĨ‚ ā¤ļā¤•ā¤¤ā¤ž.", + "onboarding_user_welcome_description": "ā¤šā¤˛ā¤ž, ⤏āĨā¤°āĨā¤ĩā¤žā¤¤ ⤕⤰āĨ‚ā¤¯ā¤ž!", + "onboarding_welcome_user": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤†ā¤šāĨ‡, {user}", + "online": "ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨", + "only_favorites": "ā¤Ģ⤕āĨā¤¤ ⤆ā¤ĩā¤Ąā¤¤āĨ‡", + "open": "ā¤‰ā¤˜ā¤Ąā¤ž", + "open_in_map_view": "ā¤¨ā¤•ā¤žā¤ļā¤ž ā¤ĻāĨƒā¤ļāĨā¤¯ā¤žā¤¤ ā¤‰ā¤˜ā¤Ąā¤ž", + "open_in_openstreetmap": "OpenStreetMap ā¤Žā¤§āĨā¤¯āĨ‡ ā¤‰ā¤˜ā¤Ąā¤ž", + "open_the_search_filters": "ā¤ļāĨ‹ā¤§ ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ā¤‰ā¤˜ā¤Ąā¤ž", + "options": "ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "or": "⤕ā¤ŋ⤂ā¤ĩā¤ž", + "organize_into_albums": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤆⤝āĨ‹ā¤œā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "organize_into_albums_description": "⤏⤧āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ā¤ĩā¤žā¤Ē⤰āĨ‚⤍ ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤠āĨ‡ā¤ĩā¤ž", + "organize_your_library": "⤤āĨā¤Žā¤šāĨ€ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "original": "ā¤ŽāĨ‚⤺", + "other": "⤇⤤⤰", + "other_devices": "⤇⤤⤰ ⤉ā¤Ē⤕⤰⤪āĨ‡", + "other_entities": "⤇⤤⤰ ā¤˜ā¤Ÿā¤•", + "other_variables": "⤇⤤⤰ ⤚⤞", + "owned": "ā¤Žā¤žā¤˛ā¤•āĨ€ā¤šāĨ‡", + "owner": "ā¤Žā¤žā¤˛ā¤•", + "partner": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤°", + "partner_can_access": "{partner} ā¤˛ā¤ž ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļ ā¤†ā¤šāĨ‡", + "partner_can_access_assets": "⤏⤂⤗āĨā¤°ā¤šā¤ŋ⤤ ā¤ĩ ā¤šā¤Ÿā¤ĩā¤ŋ⤞āĨ‡ā¤˛āĨ‡ ā¤ĩā¤—ā¤ŗā¤¤ā¤ž ⤤āĨā¤Žā¤šāĨ‡ ⤏⤰āĨā¤ĩ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "partner_can_access_location": "⤜āĨā¤¯ā¤ž ⤠ā¤ŋā¤•ā¤žā¤ŖāĨ€ ⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤•ā¤žā¤ĸ⤞āĨ‡ ⤗āĨ‡ā¤˛āĨ‡ ⤤āĨ‡ ⤏āĨā¤Ĩā¤žā¤¨", + "partner_list_user_photos": "{user} ⤚āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹", + "partner_list_view_all": "⤏⤰āĨā¤ĩ ā¤Ēā¤šā¤ž", + "partner_page_empty_message": "⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤…ā¤œāĨ‚⤍ ⤕āĨ‹ā¤Ŗā¤¤āĨā¤¯ā¤žā¤šāĨ€ ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤°ā¤žā¤¸āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤.", + "partner_page_no_more_users": "⤜āĨ‹ā¤Ąā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤆⤪⤖āĨ€ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "partner_page_partner_add_failed": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤° ⤜āĨ‹ā¤Ąā¤ŖāĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "partner_page_select_partner": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "partner_page_shared_to_title": "ā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "partner_page_stop_sharing_content": "{partner} ā¤†ā¤¤ā¤ž ⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ēā¤žā¤šāĨ‚ ā¤ļā¤•ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€.", + "partner_sharing": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤° ā¤ļāĨ‡ā¤…⤰ā¤ŋ⤂⤗", + "partners": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤°", + "password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą", + "password_does_not_match": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤜āĨā¤ŗā¤¤ ā¤¨ā¤žā¤šāĨ€", + "password_required": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤆ā¤ĩā¤ļāĨā¤¯ā¤•", + "password_reset_success": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "past_durations": { + "days": "ā¤Žā¤žā¤—āĨ€ā¤˛ {days, plural, one {# ā¤Ļā¤ŋā¤ĩ⤏} other {# ā¤Ļā¤ŋā¤ĩ⤏}}", + "hours": "ā¤Žā¤žā¤—āĨ€ā¤˛ {hours, plural, one {# ā¤¤ā¤žā¤¸} other {# ā¤¤ā¤žā¤¸}}", + "years": "ā¤Žā¤žā¤—āĨ€ā¤˛ {years, plural, one {# ā¤ĩ⤰āĨā¤ˇ} other {# ā¤ĩ⤰āĨā¤ˇāĨ‡}}" + }, + "path": "ā¤Žā¤žā¤°āĨā¤—", + "pattern": "ā¤¨ā¤ŽāĨā¤¨ā¤ž", + "pause": "ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤ž", + "pause_memories": "⤆⤠ā¤ĩ⤪āĨ€ ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤ž", + "paused": "ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩ⤞āĨ‡", + "pending": "ā¤ĒāĨā¤°ā¤˛ā¤‚ā¤Ŧā¤ŋ⤤", + "people": "⤞āĨ‹ā¤•", + "people_edits_count": "⤏⤂ā¤Ēā¤žā¤Ļā¤ŋ⤤ {count, plural, one {# ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€} other {# ⤞āĨ‹ā¤•}}", + "people_feature_description": "⤞āĨ‹ā¤•ā¤žā¤‚ā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ŦāĨā¤°ā¤žā¤‰ā¤ ā¤•ā¤°ā¤ž", + "people_sidebar_description": "ā¤¸ā¤žā¤‡ā¤Ąā¤Ŧā¤žā¤°ā¤Žā¤§āĨā¤¯āĨ‡ â€œā¤˛āĨ‹ā¤•” ā¤¸ā¤žā¤ āĨ€ ā¤ĻāĨā¤ĩā¤ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "permanent_deletion_warning": "ā¤•ā¤žā¤¯ā¤Žā¤¸āĨā¤ĩ⤰āĨ‚ā¤ĒāĨ€ ā¤ĩā¤ŋ⤞āĨ‹ā¤Ē⤍ ⤏āĨ‚ā¤šā¤¨ā¤ž", + "permanent_deletion_warning_setting_description": "ā¤…â€āĨ…⤏āĨ‡ā¤ŸāĨā¤¸ ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩā¤¤ā¤žā¤¨ā¤ž ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "permanently_delete": "ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩā¤ž", + "permanently_delete_assets_count": "{count, plural, one {ā¤…â€āĨ…⤏āĨ‡ā¤Ÿ} other {ā¤…â€āĨ…⤏āĨ‡ā¤ŸāĨā¤¸}} ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩā¤ž", + "permanently_delete_assets_prompt": "⤆ā¤Ē⤪ {count, plural, one {ā¤šā¤ž ā¤…â€āĨ…⤏āĨ‡ā¤Ÿ ā¤•ā¤žā¤¯ā¤Žā¤šā¤ž ā¤šā¤Ÿā¤ĩāĨ‚ ā¤‡ā¤šāĨā¤›ā¤ŋā¤¤ā¤ž?} other {ā¤šāĨ‡ ā¤…â€āĨ…⤏āĨ‡ā¤ŸāĨā¤¸ ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩāĨ‚ ā¤‡ā¤šāĨā¤›ā¤ŋā¤¤ā¤ž?}} ā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ {count, plural, one {⤤āĨ‹ ⤤āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž} other {⤤āĨ‡ ⤤āĨā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤ž}} ⤅⤞āĨā¤Ŧā¤Ž(ā¤Žā¤§āĨ‚⤍) ā¤ĻāĨ‡ā¤–āĨ€ā¤˛ ā¤•ā¤žā¤ĸ⤞āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛.", + "permanently_deleted_asset": "ā¤•ā¤žā¤¯ā¤Žā¤šā¤ž ā¤šā¤Ÿā¤ĩ⤞āĨ‡ā¤˛ā¤ž ā¤…â€āĨ…⤏āĨ‡ā¤Ÿ", + "permanently_deleted_assets_count": "ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨ‡ {count, plural, one {# ā¤…â€āĨ…⤏āĨ‡ā¤Ÿ} other {# ā¤…â€āĨ…⤏āĨ‡ā¤ŸāĨā¤¸}}", + "permission": "ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€", + "permission_empty": "⤤āĨā¤Žā¤šāĨ€ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ⤰ā¤ŋ⤕āĨā¤¤ ⤅⤏āĨ‚ ⤍⤝āĨ‡", + "permission_onboarding_back": "ā¤Žā¤žā¤—āĨ‡", + "permission_onboarding_continue_anyway": "⤤⤰āĨ€ā¤šāĨ€ ā¤ĒāĨā¤ĸāĨ‡ ā¤œā¤ž", + "permission_onboarding_get_started": "⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "permission_onboarding_go_to_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤œā¤ž", + "permission_onboarding_permission_denied": "ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤¨ā¤žā¤•ā¤žā¤°ā¤˛āĨ€. Immich ā¤ĩā¤žā¤Ē⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€, ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨā¤¯ā¤ž ā¤ĻāĨā¤¯ā¤ž.", + "permission_onboarding_permission_granted": "ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤Žā¤‚ā¤œāĨ‚⤰! ⤏⤰āĨā¤ĩ ā¤¤ā¤¯ā¤žā¤°.", + "permission_onboarding_permission_limited": "ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ā¤Žā¤°āĨā¤¯ā¤žā¤Ļā¤ŋ⤤. Immich ā¤˛ā¤ž ⤏⤂ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤗āĨ…⤞⤰āĨ€ ⤏⤂⤗āĨā¤°ā¤šā¤žā¤šā¤ž ā¤ŦāĨ…⤕⤅ā¤Ē ā¤ĩ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ē⤍ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€, ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨā¤¯ā¤ž ā¤ĻāĨā¤¯ā¤ž.", + "permission_onboarding_request": "⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Ēā¤žā¤šā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ Immich ā¤˛ā¤ž ā¤Ē⤰ā¤ĩā¤žā¤¨ā¤—āĨ€ ⤆ā¤ĩā¤ļāĨā¤¯ā¤• ā¤†ā¤šāĨ‡.", + "person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€", + "person_age_months": "{months, plural, one {# ā¤Žā¤šā¤ŋā¤¨ā¤ž} other {# ā¤Žā¤šā¤ŋ⤍āĨ‡}} ā¤ĩ⤝", + "person_age_year_months": "1 ā¤ĩ⤰āĨā¤ˇ, {months, plural, one {# ā¤Žā¤šā¤ŋā¤¨ā¤ž} other {# ā¤Žā¤šā¤ŋ⤍āĨ‡}} ā¤ĩ⤝", + "person_age_years": "{years, plural, other {# ā¤ĩ⤰āĨā¤ˇā¤žā¤‚ā¤šāĨ‡}}", + "person_birthdate": "⤜⤍āĨā¤Ž {date} ⤰āĨ‹ā¤œāĨ€", + "person_hidden": "{name}{hidden, select, true { {hidden}} other {}}", + "photo_shared_all_users": "⤤āĨā¤ŽāĨā¤šāĨ€ ⤏⤰āĨā¤ĩ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚⤏āĨ‹ā¤Ŧ⤤ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ ā¤†ā¤šāĨ‡ā¤¤ ⤅⤏āĨ‡ ā¤Ļā¤ŋ⤏⤤āĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨ‹ā¤Ŗā¤¤ā¤žā¤šāĨ€ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤¨ā¤žā¤šāĨ€.", + "photos": "ā¤ĢāĨ‹ā¤ŸāĨ‹", + "photos_and_videos": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "photos_count": "{count, plural, one {{count, number} ā¤ĢāĨ‹ā¤ŸāĨ‹} other {{count, number} ā¤ĢāĨ‹ā¤ŸāĨ‹}}", + "photos_from_previous_years": "ā¤Žā¤žā¤—āĨ€ā¤˛ ā¤ĩ⤰āĨā¤ˇā¤žā¤‚⤤āĨ€ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹", + "pick_a_location": "⤏āĨā¤Ĩā¤žā¤¨ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "pin_code_changed_successfully": "PIN ⤕āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ā¤Ŧā¤Ļā¤˛ā¤˛ā¤ž", + "pin_code_reset_successfully": "PIN ⤕āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛ā¤ž", + "pin_code_setup_successfully": "PIN ⤕āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°ā¤ŋ⤤āĨā¤¯ā¤ž ⤏āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛ā¤ž", + "pin_verification": "PIN ⤕āĨ‹ā¤Ą ā¤Ēā¤Ąā¤¤ā¤žā¤ŗā¤ŖāĨ€", + "place": "⤏āĨā¤Ĩā¤žā¤¨", + "places": "⤏āĨā¤Ĩā¤žā¤¨āĨ‡", + "places_count": "{count, plural, one {{count, number} ⤏āĨā¤Ĩā¤žā¤¨} other {{count, number} ⤏āĨā¤Ĩā¤žā¤¨āĨ‡}}", + "play": "ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž", + "play_memories": "⤆⤠ā¤ĩ⤪āĨ€ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž", + "play_motion_photo": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž", + "play_or_pause_video": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ĒāĨā¤˛āĨ‡ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤ĒāĨ‰ā¤œ ā¤•ā¤°ā¤ž", + "please_auth_to_access": "ā¤ĒāĨā¤°ā¤ĩāĨ‡ā¤ļā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ĒāĨā¤°ā¤Žā¤žā¤ŖāĨ€ā¤•⤰⤪ ā¤•ā¤°ā¤ž", + "port": "ā¤ĒāĨ‹ā¤°āĨā¤Ÿ", + "preferences_settings_subtitle": "ā¤…â€āĨ…ā¤Ē⤚āĨ€ ā¤ĒāĨā¤°ā¤žā¤§ā¤žā¤¨āĨā¤¯āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "preferences_settings_title": "ā¤ĒāĨā¤°ā¤žā¤§ā¤žā¤¨āĨā¤¯āĨ‡", + "preset": "ā¤ĒāĨā¤°ā¤ŋ⤏āĨ‡ā¤Ÿ", + "preview": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤žā¤ĩ⤞āĨ‹ā¤•⤍", + "previous": "ā¤Žā¤žā¤—āĨ€ā¤˛", + "previous_memory": "ā¤Žā¤žā¤—āĨ€ā¤˛ ⤆⤠ā¤ĩ⤪", + "previous_or_next_day": "ā¤Ļā¤ŋā¤ĩ⤏ ā¤ĒāĨā¤ĸāĨ‡/ā¤Žā¤žā¤—āĨ‡", + "previous_or_next_month": "ā¤Žā¤šā¤ŋā¤¨ā¤ž ā¤ĒāĨā¤ĸāĨ‡/ā¤Žā¤žā¤—āĨ‡", + "previous_or_next_photo": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĒāĨā¤ĸāĨ‡/ā¤Žā¤žā¤—āĨ‡", + "previous_or_next_year": "ā¤ĩ⤰āĨā¤ˇ ā¤ĒāĨā¤ĸāĨ‡/ā¤Žā¤žā¤—āĨ‡", + "primary": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕", + "privacy": "⤗āĨ‹ā¤Ē⤍āĨ€ā¤¯ā¤¤ā¤ž", + "profile": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛", + "profile_drawer_app_logs": "⤞āĨ‰ā¤—āĨā¤¸", + "profile_drawer_client_out_of_date_major": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ā¤…â€āĨ…ā¤Ē ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯ ā¤†ā¤šāĨ‡. ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤ŽāĨ‡ā¤œā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ā¤ĩ⤰ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤•ā¤°ā¤ž.", + "profile_drawer_client_out_of_date_minor": "ā¤ŽāĨ‹ā¤Ŧā¤žā¤‡ā¤˛ ā¤…â€āĨ…ā¤Ē ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯ ā¤†ā¤šāĨ‡. ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤Žā¤žā¤‡ā¤¨ā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ā¤ĩ⤰ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤•ā¤°ā¤ž.", + "profile_drawer_client_server_up_to_date": "⤕āĨā¤˛ā¤žā¤¯ā¤‚ā¤Ÿ ⤆⤪ā¤ŋ ⤏⤰āĨā¤ĩāĨā¤šā¤° ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ā¤†ā¤šāĨ‡ā¤¤", + "profile_drawer_github": "⤗ā¤ŋā¤Ÿā¤šā¤Ŧ", + "profile_drawer_readonly_mode": "ā¤Ģ⤕āĨā¤¤-ā¤ĩā¤žā¤šā¤¨ ā¤ŽāĨ‹ā¤Ą ⤏⤕āĨā¤ˇā¤Ž. ā¤Ŧā¤žā¤šāĨ‡ā¤° ā¤Ēā¤Ąā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ⤅ā¤ĩā¤¤ā¤žā¤° ⤆⤝⤕āĨ‰ā¤¨ā¤ĩ⤰ ā¤˛ā¤žā¤‚ā¤Ŧ-ā¤ĒāĨā¤°āĨ‡ā¤¸ ā¤•ā¤°ā¤ž.", + "profile_drawer_server_out_of_date_major": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯ ā¤†ā¤šāĨ‡. ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤ŽāĨ‡ā¤œā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ā¤ĩ⤰ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤•ā¤°ā¤ž.", + "profile_drawer_server_out_of_date_minor": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤•ā¤žā¤˛ā¤Ŧā¤žā¤šāĨā¤¯ ā¤†ā¤šāĨ‡. ⤕āĨƒā¤Ēā¤¯ā¤ž ⤍ā¤ĩāĨ€ā¤¨ā¤¤ā¤Ž ā¤Žā¤žā¤‡ā¤¨ā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ā¤ĩ⤰ ⤅ā¤ĻāĨā¤¯ā¤¤ā¤¨ ā¤•ā¤°ā¤ž.", + "profile_image_of_user": "{user} ⤚āĨ€ ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "profile_picture_set": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ⤚ā¤ŋ⤤āĨā¤° ⤏āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛āĨ‡.", + "public_album": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ⤅⤞āĨā¤Ŧā¤Ž", + "public_share": "ā¤¸ā¤žā¤°āĨā¤ĩ⤜⤍ā¤ŋ⤕ ā¤ļāĨ‡ā¤…⤰", + "purchase_account_info": "ā¤¸ā¤Žā¤°āĨā¤Ĩ⤕", + "purchase_activated_subtitle": "Immich ⤆⤪ā¤ŋ ā¤ŽāĨā¤•āĨā¤¤-⤏āĨā¤°āĨ‹ā¤¤ ⤏āĨ‰ā¤ĢāĨā¤Ÿā¤ĩāĨ‡ā¤…ā¤°ā¤˛ā¤ž ā¤Ēā¤žā¤ ā¤ŋ⤂ā¤Ŧā¤ž ā¤Ļā¤ŋ⤞āĨā¤¯ā¤žā¤Ŧā¤ĻāĨā¤Ļ⤞ ⤧⤍āĨā¤¯ā¤ĩā¤žā¤Ļ", + "purchase_activated_time": "{date} ⤰āĨ‹ā¤œāĨ€ ⤏⤕āĨā¤°ā¤ŋ⤝ ⤕āĨ‡ā¤˛āĨ‡", + "purchase_activated_title": "⤤āĨā¤Žā¤šāĨ€ ⤕āĨ€ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤Ē⤪āĨ‡ ⤏⤕āĨā¤°ā¤ŋ⤝ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤆⤞āĨ€ ā¤†ā¤šāĨ‡", + "purchase_button_activate": "⤏⤕āĨā¤°ā¤ŋ⤝ ā¤•ā¤°ā¤ž", + "purchase_button_buy": "⤖⤰āĨ‡ā¤ĻāĨ€ ā¤•ā¤°ā¤ž", + "purchase_button_buy_immich": "Immich ⤖⤰āĨ‡ā¤ĻāĨ€ ā¤•ā¤°ā¤ž", + "purchase_button_never_show_again": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤Ļā¤žā¤–ā¤ĩāĨ‚ ā¤¨ā¤•ā¤ž", + "purchase_button_reminder": "āĨŠāĨĻ ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤‚ā¤¨āĨ€ ā¤Žā¤˛ā¤ž ⤆⤠ā¤ĩ⤪ ⤕⤰āĨ‚⤍ ā¤ĻāĨā¤¯ā¤ž", + "purchase_button_remove_key": "⤕āĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "purchase_button_select": "⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "purchase_failed_activation": "⤏⤕āĨā¤°ā¤ŋ⤝ ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€! ⤝āĨ‹ā¤—āĨā¤¯ ā¤ĒāĨā¤°āĨ‹ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž ⤤āĨā¤Žā¤šāĨ‡ ā¤ˆā¤ŽāĨ‡ā¤˛ ⤤ā¤Ēā¤žā¤¸ā¤ž!", + "purchase_individual_description_1": "ā¤ĩāĨˆā¤¯ā¤•āĨā¤¤ā¤ŋ⤕ ā¤ĩā¤žā¤Ēā¤°ā¤žā¤¸ā¤žā¤ āĨ€", + "purchase_individual_description_2": "ā¤¸ā¤Žā¤°āĨā¤Ĩ⤕ ⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "purchase_individual_title": "ā¤ĩāĨˆā¤¯ā¤•āĨā¤¤ā¤ŋ⤕", + "purchase_input_suggestion": "ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤†ā¤šāĨ‡? ā¤–ā¤žā¤˛āĨ€ ⤕āĨ€ ā¤Ÿā¤žā¤•ā¤ž", + "purchase_license_subtitle": "⤏āĨ‡ā¤ĩāĨ‡ā¤šāĨā¤¯ā¤ž ā¤ĒāĨā¤ĸāĨ€ā¤˛ ā¤ĩā¤ŋā¤•ā¤žā¤¸ā¤žā¤¸ā¤žā¤ āĨ€ Immich ⤖⤰āĨ‡ā¤ĻāĨ€ ⤕⤰āĨ‚⤍ ā¤¸ā¤žā¤Ĩ ā¤ĻāĨā¤¯ā¤ž", + "purchase_lifetime_description": "⤆⤝āĨā¤ˇāĨā¤¯ā¤­ā¤°ā¤žā¤šāĨ€ ⤖⤰āĨ‡ā¤ĻāĨ€", + "purchase_option_title": "⤖⤰āĨ‡ā¤ĻāĨ€ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "purchase_panel_info_1": "Immich ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨ‡ ā¤ĩāĨ‡ā¤ŗā¤–ā¤žā¤Š ⤆⤪ā¤ŋ ⤕⤎āĨā¤Ÿā¤žā¤šāĨ‡ ā¤†ā¤šāĨ‡. ā¤†ā¤Žā¤šāĨ‡ ⤧āĨā¤¯āĨ‡ā¤¯ ā¤ŽāĨā¤•āĨā¤¤-⤏āĨā¤°āĨ‹ā¤¤ ⤏āĨ‰ā¤ĢāĨā¤Ÿā¤ĩāĨ‡ā¤…⤰ ā¤ĩ ⤍āĨˆā¤¤ā¤ŋ⤕ ā¤ĩāĨā¤¯ā¤žā¤ĩā¤¸ā¤žā¤¯ā¤ŋ⤕ ā¤Ēā¤ĻāĨā¤§ā¤¤āĨ€ā¤‚ā¤Žā¤§āĨ‚⤍ ⤟ā¤ŋā¤•ā¤žā¤Š ⤉⤤āĨā¤Ē⤍āĨā¤¨ ā¤Žā¤ŋ⤺ā¤ĩ⤪āĨ‡, ā¤ĩā¤ŋā¤•ā¤¸ā¤•ā¤žā¤‚ā¤¨ā¤ž ā¤†ā¤§ā¤žā¤° ā¤ĻāĨ‡ā¤ŖāĨ‡ ⤆⤪ā¤ŋ ā¤ļāĨ‹ā¤ˇā¤Ŗā¤•ā¤žā¤°āĨ€ ⤕āĨā¤˛ā¤žā¤‰ā¤Ą ⤏āĨ‡ā¤ĩā¤žā¤‚ā¤¨ā¤ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤ĻāĨ‡ā¤Ŗā¤žā¤°āĨ‡ ⤗āĨ‹ā¤Ē⤍āĨ€ā¤¯ā¤¤āĨ‡ā¤šā¤ž ā¤Žā¤žā¤¨ ā¤°ā¤žā¤–ā¤Ŗā¤žā¤°āĨ‡ ⤇⤕āĨ‹ā¤¸ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨ‡ ā¤šāĨ‡ ā¤†ā¤šāĨ‡.", + "purchase_panel_info_2": "ā¤†ā¤ŽāĨā¤šāĨ€ ā¤ĒāĨ‡ā¤ĩāĨ‰ā¤˛ ⤍ ā¤ĩā¤žā¤ĸā¤ĩ⤪āĨā¤¯ā¤žā¤¸ ā¤•ā¤Ÿā¤ŋā¤Ŧā¤ĻāĨā¤§ ā¤†ā¤šāĨ‹ā¤¤; ⤤āĨā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ā¤¯ā¤ž ⤖⤰āĨ‡ā¤ĻāĨ€ā¤ŽāĨā¤ŗāĨ‡ Immich ā¤Žā¤§āĨā¤¯āĨ‡ ⤕āĨ‹ā¤Ŗā¤¤āĨ€ā¤šāĨ€ ⤅⤤ā¤ŋ⤰ā¤ŋ⤕āĨā¤¤ ā¤ĩāĨˆā¤ļā¤ŋ⤎āĨā¤ŸāĨā¤¯āĨ‡ ā¤‰ā¤˜ā¤Ąā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤. ā¤šā¤žā¤˛āĨ‚ ā¤ĩā¤ŋā¤•ā¤žā¤¸ā¤žā¤¸ā¤žā¤ āĨ€ ā¤†ā¤ŽāĨā¤šāĨ€ ⤤āĨā¤Žā¤šāĨā¤¯ā¤žā¤¸ā¤žā¤°ā¤–āĨā¤¯ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤ž ā¤Ēā¤žā¤ ā¤Ŧā¤ŗā¤žā¤ĩ⤰ ⤅ā¤ĩ⤞⤂ā¤ŦāĨ‚⤍ ā¤†ā¤šāĨ‹ā¤¤.", + "purchase_panel_title": "ā¤ĒāĨā¤°ā¤•⤞āĨā¤Ēā¤žā¤˛ā¤ž ā¤¸ā¤žā¤Ĩ ā¤ĻāĨā¤¯ā¤ž", + "purchase_per_server": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ ⤏⤰āĨā¤ĩāĨā¤šā¤°", + "purchase_per_user": "ā¤ĒāĨā¤°ā¤¤ā¤ŋ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž", + "purchase_remove_product_key": "ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤•ā¤žā¤ĸā¤ž", + "purchase_remove_product_key_prompt": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤•ā¤žā¤ĸā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "purchase_remove_server_product_key": "⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨ€ ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤•ā¤žā¤ĸā¤ž", + "purchase_remove_server_product_key_prompt": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨ€ ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤•ā¤žā¤ĸā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "purchase_server_description_1": "⤏⤂ā¤ĒāĨ‚⤰āĨā¤Ŗ ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤¸ā¤žā¤ āĨ€", + "purchase_server_description_2": "ā¤¸ā¤Žā¤°āĨā¤Ĩ⤕ ⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "purchase_server_title": "⤏⤰āĨā¤ĩāĨā¤šā¤°", + "purchase_settings_server_activated": "⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤šāĨ€ ā¤ĒāĨā¤°āĨ‰ā¤Ąā¤•āĨā¤Ÿ ⤕āĨ€ ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•ā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ€ ā¤œā¤žā¤¤āĨ‡", + "query_asset_id": "⤅āĨ…⤏āĨ‡ā¤Ÿ ID ⤚āĨŒā¤•ā¤ļāĨ€", + "queue_status": "ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤ {count}/{total}", + "rating": "⤏āĨā¤Ÿā¤žā¤° ⤰āĨ‡ā¤Ÿā¤ŋ⤂⤗", + "rating_clear": "⤰āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤¸ā¤žā¤Ģ ā¤•ā¤°ā¤ž", + "rating_count": "{count, plural, one {# ā¤¤ā¤žā¤°ā¤ž} other {# ā¤¤ā¤žā¤°āĨ‡}}", + "rating_description": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤ĒāĨ…⤍āĨ‡ā¤˛ā¤Žā¤§āĨā¤¯āĨ‡ EXIF ⤰āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤Ļ⤰āĨā¤ļā¤ĩā¤ž", + "reaction_options": "⤰ā¤ŋā¤ā¤•āĨā¤ļ⤍ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "read_changelog": "⤚āĨ‡ā¤‚ā¤œā¤˛āĨ‰ā¤— ā¤ĩā¤žā¤šā¤ž", + "readonly_mode_disabled": "ā¤Ģ⤕āĨā¤¤-ā¤ĩā¤žā¤šā¤¨ ā¤ŽāĨ‹ā¤Ą ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝ ⤕āĨ‡ā¤˛ā¤ž", + "readonly_mode_enabled": "ā¤Ģ⤕āĨā¤¤-ā¤ĩā¤žā¤šā¤¨ ā¤ŽāĨ‹ā¤Ą ⤏⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛ā¤ž", + "reassign": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ā¤•ā¤°ā¤ž", + "reassigned_assets_to_existing_person": "{count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}} {name, select, null {ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤•ā¤ĄāĨ‡} other {{name} ā¤•ā¤ĄāĨ‡}} ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ⤕āĨ‡ā¤˛āĨ‡", + "reassigned_assets_to_new_person": "{count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}} ⤍ā¤ĩāĨā¤¯ā¤ž ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤•ā¤ĄāĨ‡ ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ⤕āĨ‡ā¤˛āĨ‡", + "reassing_hint": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨ‡ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤•ā¤ĄāĨ‡ ⤍ā¤ŋ⤝āĨā¤•āĨā¤¤ ā¤•ā¤°ā¤ž", + "recent": "⤅⤞āĨ€ā¤•ā¤ĄāĨ€ā¤˛", + "recent-albums": "⤅⤞āĨ€ā¤•ā¤ĄāĨ€ā¤˛ ⤅⤞āĨā¤Ŧā¤Ž", + "recent_searches": "⤅⤞āĨ€ā¤•ā¤ĄāĨ€ā¤˛ ā¤ļāĨ‹ā¤§", + "recently_added": "⤍āĨā¤•⤤āĨ‡ā¤š ⤜āĨ‹ā¤Ąā¤˛āĨ‡ā¤˛āĨ‡", + "recently_added_page_title": "⤍āĨā¤•⤤āĨ‡ā¤š ⤜āĨ‹ā¤Ąā¤˛āĨ‡ā¤˛āĨ‡", + "recently_taken": "⤅⤞āĨ€ā¤•ā¤ĄāĨ‡ ⤘āĨ‡ā¤¤ā¤˛āĨ‡ā¤˛āĨ‡", + "recently_taken_page_title": "⤅⤞āĨ€ā¤•ā¤ĄāĨ‡ ⤘āĨ‡ā¤¤ā¤˛āĨ‡ā¤˛āĨ‡", + "refresh": "⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž", + "refresh_encoded_videos": "ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž", + "refresh_faces": "⤚āĨ‡ā¤šā¤°āĨ‡ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž", + "refresh_metadata": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž", + "refresh_thumbnails": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž", + "refreshed": "⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤ā¤žā¤˛āĨ‡", + "refreshes_every_file": "ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ā¤ĩ ⤍ā¤ĩāĨ€ā¤¨ ⤏⤰āĨā¤ĩ ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤ĩā¤žā¤šā¤ž", + "refreshing_encoded_video": "ā¤ā¤¨āĨā¤•āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛ā¤ž ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "refreshing_faces": "⤚āĨ‡ā¤šā¤°āĨ‡ ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "refreshing_metadata": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "regenerating_thumbnails": "ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛āĨā¤¸ ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤤ ā¤†ā¤šāĨ‡", + "remote": "ā¤ĻāĨ‚⤰⤏āĨā¤Ĩ", + "remote_assets": "ā¤ĻāĨ‚⤰⤏āĨā¤Ĩ ā¤†ā¤¯ā¤Ÿā¤Ž", + "remove": "ā¤•ā¤žā¤ĸā¤ž", + "remove_assets_album_confirmation": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ {count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}} ā¤•ā¤žā¤ĸā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ā¤¤ ā¤•ā¤ž?", + "remove_assets_shared_link_confirmation": "ā¤¯ā¤ž ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩāĨā¤¯ā¤žā¤¤āĨ‚⤍ {count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}} ā¤•ā¤žā¤ĸā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ā¤¤ ā¤•ā¤ž?", + "remove_assets_title": "ā¤†ā¤¯ā¤Ÿā¤Ž ā¤•ā¤žā¤ĸā¤žā¤¯ā¤šāĨ‡?", + "remove_custom_date_range": "ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ļāĨā¤°āĨ‡ā¤ŖāĨ€ ā¤•ā¤žā¤ĸā¤ž", + "remove_deleted_assets": "ā¤šā¤Ÿā¤ĩ⤞āĨ‡ā¤˛āĨ‡ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤•ā¤žā¤ĸā¤ž", + "remove_from_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "remove_from_album_action_prompt": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ {count} ā¤•ā¤žā¤ĸ⤞āĨ‡", + "remove_from_favorites": "⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "remove_from_lock_folder_action_prompt": "⤞āĨ‰ā¤• ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°ā¤Žā¤§āĨ‚⤍ {count} ā¤•ā¤žā¤ĸ⤞āĨ‡", + "remove_from_locked_folder": "⤞āĨ‰ā¤• ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°ā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "remove_from_locked_folder_confirmation": "ā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤞āĨ‰ā¤• ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°ā¤Žā¤§āĨ‚⤍ ā¤Ŧā¤žā¤šāĨ‡ā¤° ā¤šā¤˛ā¤ĩā¤žā¤¯ā¤šāĨ‡ ā¤†ā¤šāĨ‡ā¤¤ ā¤•ā¤ž? ⤤āĨ‡ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ļā¤ŋ⤏⤤āĨ€ā¤˛.", + "remove_from_shared_link": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩāĨā¤¯ā¤žā¤¤āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "remove_memory": "ā¤ŽāĨ‡ā¤Žā¤°āĨ€ ā¤•ā¤žā¤ĸā¤ž", + "remove_photo_from_memory": "ā¤¯ā¤ž ā¤ŽāĨ‡ā¤Žā¤°āĨ€ā¤¤āĨ‚⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤•ā¤žā¤ĸā¤ž", + "remove_tag": "⤟āĨ…⤗ ā¤•ā¤žā¤ĸā¤ž", + "remove_url": "URL ā¤•ā¤žā¤ĸā¤ž", + "remove_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤•ā¤žā¤ĸā¤ž", + "removed_api_key": "ā¤•ā¤žā¤ĸ⤞āĨ‡ā¤˛āĨ€ API ⤕āĨ€: {name}", + "removed_from_archive": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤šā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸ⤞āĨ‡", + "removed_from_favorites": "⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ‚⤍ ā¤•ā¤žā¤ĸ⤞āĨ‡", + "removed_from_favorites_count": "{count, plural, other {⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ‚⤍ # ā¤•ā¤žā¤ĸ⤞āĨ‡}}", + "removed_memory": "ā¤ŽāĨ‡ā¤Žā¤°āĨ€ ā¤•ā¤žā¤ĸ⤞āĨ€", + "removed_photo_from_memory": "ā¤ŽāĨ‡ā¤Žā¤°āĨ€ā¤¤āĨ‚⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤•ā¤žā¤ĸā¤˛ā¤ž", + "removed_tagged_assets": "{count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Žā¤ĩ⤰āĨ‚⤍ ⤟āĨ…⤗ ā¤•ā¤žā¤ĸā¤˛ā¤ž} other {# ā¤†ā¤¯ā¤Ÿā¤Žā¤ĩ⤰āĨ‚⤍ ⤟āĨ…⤗ ā¤•ā¤žā¤ĸ⤞āĨ‡}}", + "rename": "ā¤¨ā¤žā¤ĩ ā¤Ŧā¤Ļā¤˛ā¤ž", + "repair": "ā¤ĻāĨā¤°āĨā¤¸āĨā¤¤āĨ€", + "repair_no_results_message": "ā¤…ā¤¨ā¤ŸāĨā¤°āĨ…⤕āĨā¤Ą ā¤ĩ ā¤šā¤°ā¤ĩ⤞āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ ⤝āĨ‡ā¤ĨāĨ‡ ā¤Ļā¤ŋ⤏⤤āĨ€ā¤˛", + "replace_with_upload": "⤅ā¤Ē⤞āĨ‹ā¤Ąā¤¨āĨ‡ ā¤Ŧā¤Ļā¤˛ā¤ž", + "repository": "⤰ā¤ŋā¤ĒāĨ‰ā¤ā¤ŋ⤟⤰āĨ€", + "require_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤆ā¤ĩā¤ļāĨā¤¯ā¤•", + "require_user_to_change_password_on_first_login": "ā¤Ēā¤šā¤ŋ⤞āĨā¤¯ā¤ž ⤞āĨ‰ā¤—ā¤ŋ⤍ā¤ĩāĨ‡ā¤ŗāĨ€ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤¨āĨ‡ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ŧā¤Ļ⤞⤪āĨ‡ ⤆ā¤ĩā¤ļāĨā¤¯ā¤•", + "rescan": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤ž", + "reset": "⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_people_visibility": "⤞āĨ‹ā¤•ā¤žā¤‚ā¤šāĨ€ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_pin_code": "PIN ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_pin_code_description": "⤤āĨā¤Žā¤šā¤ž PIN ā¤ĩā¤ŋā¤¸ā¤°ā¤˛ā¤ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸, ⤤āĨ‹ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤ĒāĨā¤°ā¤ļā¤žā¤¸ā¤•ā¤žā¤ļāĨ€ ⤏⤂ā¤Ē⤰āĨā¤• ā¤¸ā¤žā¤§ā¤ž", + "reset_pin_code_success": "PIN ⤕āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°āĨ€ā¤¤āĨā¤¯ā¤ž ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛ā¤ž", + "reset_pin_code_with_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ąā¤¨āĨ‡ ⤤āĨā¤ŽāĨā¤šāĨ€ ⤍āĨ‡ā¤šā¤ŽāĨ€ PIN ⤕āĨ‹ā¤Ą ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕⤰āĨ‚ ā¤ļā¤•ā¤¤ā¤ž", + "reset_sqlite": "SQLite ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "reset_sqlite_confirmation": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤍⤕āĨā¤•āĨ€ SQLite ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž? ā¤ĄāĨ‡ā¤Ÿā¤ž ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤ŋ⤤ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤞āĨ‰ā¤—ā¤†ā¤‰ā¤Ÿ ⤕⤰āĨ‚⤍ ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤞āĨ‰ā¤—⤇⤍ ā¤•ā¤°ā¤žā¤ĩāĨ‡ ā¤˛ā¤žā¤—āĨ‡ā¤˛", + "reset_sqlite_success": "SQLite ā¤ĄāĨ‡ā¤Ÿā¤žā¤ŦāĨ‡ā¤¸ ⤝ā¤ļ⤏āĨā¤ĩāĨ€ā¤°āĨ€ā¤¤āĨā¤¯ā¤ž ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ⤕āĨ‡ā¤˛ā¤ž", + "reset_to_default": "ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿā¤ĩ⤰ ⤰āĨ€ā¤¸āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "resolve_duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ ⤏āĨ‹ā¤Ąā¤ĩā¤ž", + "resolved_all_duplicates": "⤏⤰āĨā¤ĩ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ ⤏āĨ‹ā¤Ąā¤ĩ⤞āĨ‡", + "restore": "ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "restore_all": "⤏⤰āĨā¤ĩ ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "restore_trash_action_prompt": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤āĨ‚⤍ {count} ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ‡", + "restore_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "restored_asset": "ā¤ĒāĨā¤¨ā¤°āĨā¤¸ā¤‚ā¤šā¤¯ā¤ŋ⤤ ā¤†ā¤¯ā¤Ÿā¤Ž", + "resume": "ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "resume_paused_jobs": "{count, plural, one {# ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩ⤞āĨ‡ā¤˛āĨ‡ ā¤•ā¤žā¤Ž} other {# ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩ⤞āĨ‡ā¤˛āĨ€ ā¤•ā¤žā¤ŽāĨ‡}} ā¤ĒāĨā¤¨āĨā¤šā¤ž ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "retry_upload": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ĒāĨā¤¨āĨā¤šā¤ž ā¤•ā¤°ā¤ž", + "review_duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ā¤šāĨ‡ ā¤ĒāĨā¤¨ā¤°ā¤žā¤ĩ⤞āĨ‹ā¤•⤍ ā¤•ā¤°ā¤ž", + "review_large_files": "ā¤ŽāĨ‹ā¤ āĨā¤¯ā¤ž ā¤Ģā¤žā¤‡ā¤˛āĨā¤¸ā¤šāĨ‡ ā¤ĒāĨā¤¨ā¤°ā¤žā¤ĩ⤞āĨ‹ā¤•⤍ ā¤•ā¤°ā¤ž", + "role": "⤭āĨ‚ā¤Žā¤ŋā¤•ā¤ž", + "role_editor": "⤏⤂ā¤Ēā¤žā¤Ļ⤕", + "role_viewer": "ā¤Ļ⤰āĨā¤ļ⤕", + "running": "ā¤šā¤žā¤˛āĨ‚", + "save": "⤜⤤⤍ ā¤•ā¤°ā¤ž", + "save_to_gallery": "⤗āĨ…⤞⤰āĨ€ā¤Žā¤§āĨā¤¯āĨ‡ ⤜⤤⤍ ā¤•ā¤°ā¤ž", + "saved_api_key": "⤜⤤⤍ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ API ⤕āĨ€", + "saved_profile": "⤜⤤⤍ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛", + "saved_settings": "⤜⤤⤍ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "say_something": "ā¤•ā¤žā¤šāĨ€ā¤¤ā¤°āĨ€ ā¤ŦāĨ‹ā¤˛ā¤ž", + "scaffold_body_error_occurred": "⤤āĨā¤°āĨā¤ŸāĨ€ ⤆⤞āĨ€", + "scan_all_libraries": "⤏⤰āĨā¤ĩ ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤ž", + "scan_library": "⤏āĨā¤•āĨ…⤍ ā¤•ā¤°ā¤ž", + "scan_settings": "⤏āĨā¤•āĨ…⤍ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "scanning_for_album": "⤅⤞āĨā¤Ŧā¤Žā¤¸ā¤žā¤ āĨ€ ⤏āĨā¤•āĨ…⤍ ⤕⤰⤤ ā¤†ā¤šāĨ‡...", + "search": "ā¤ļāĨ‹ā¤§ā¤ž", + "search_albums": "⤅⤞āĨā¤Ŧā¤Ž ā¤ļāĨ‹ā¤§ā¤ž", + "search_by_context": "ā¤Ē⤰ā¤ŋ⤏āĨā¤Ĩā¤ŋ⤤āĨ€ā¤¨āĨā¤¸ā¤žā¤° ā¤ļāĨ‹ā¤§ā¤ž", + "search_by_description": "ā¤ĩ⤰āĨā¤Ŗā¤¨ā¤žā¤¨āĨā¤¸ā¤žā¤° ā¤ļāĨ‹ā¤§ā¤ž", + "search_by_description_example": "ā¤¸ā¤žā¤Ēā¤ž ā¤Žā¤§āĨ€ā¤˛ ā¤šā¤žā¤¯ā¤•ā¤ŋā¤‚ā¤—ā¤šā¤ž ā¤Ļā¤ŋā¤ĩ⤏", + "search_by_filename": "ā¤Ģā¤žā¤‡ā¤˛ ā¤¨ā¤žā¤ĩ/ā¤ā¤•āĨā¤¸āĨā¤ŸāĨ‡ā¤‚ā¤ļ⤍⤍āĨā¤¸ā¤žā¤° ā¤ļāĨ‹ā¤§ā¤ž", + "search_by_filename_example": "⤉ā¤Ļā¤ž. IMG_1234.JPG ⤕ā¤ŋ⤂ā¤ĩā¤ž PNG", + "search_camera_make": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž ⤍ā¤ŋ⤰āĨā¤Žā¤žā¤¤ā¤ž ā¤ļāĨ‹ā¤§ā¤ž...", + "search_camera_model": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž ā¤ŽāĨ‰ā¤ĄāĨ‡ā¤˛ ā¤ļāĨ‹ā¤§ā¤ž...", + "search_city": "ā¤ļā¤šā¤° ā¤ļāĨ‹ā¤§ā¤ž...", + "search_country": "ā¤ĻāĨ‡ā¤ļ ā¤ļāĨ‹ā¤§ā¤ž...", + "search_filter_apply": "ā¤Ģā¤ŋ⤞āĨā¤Ÿā¤° ā¤˛ā¤žā¤—āĨ‚ ā¤•ā¤°ā¤ž", + "search_filter_camera_title": "⤕āĨ…ā¤ŽāĨ‡ā¤°ā¤ž ā¤ĒāĨā¤°ā¤•ā¤žā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "search_filter_date": "ā¤¤ā¤žā¤°āĨ€ā¤–", + "search_filter_date_interval": "{start} ⤤āĨ‡ {end}", + "search_filter_date_title": "ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ļāĨā¤°āĨ‡ā¤ŖāĨ€ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "search_filter_display_option_not_in_album": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤¨ā¤žā¤šāĨ€", + "search_filter_display_options": "ā¤ĒāĨā¤°ā¤Ļ⤰āĨā¤ļ⤍ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "search_filter_filename": "ā¤Ģā¤žā¤‡ā¤˛ ā¤¨ā¤žā¤ĩā¤žā¤¨āĨ‡ ā¤ļāĨ‹ā¤§ā¤ž", + "search_filter_location": "⤏āĨā¤Ĩā¤žā¤¨", + "search_filter_location_title": "⤏āĨā¤Ĩā¤žā¤¨ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "search_filter_media_type": "ā¤Žā¤žā¤§āĨā¤¯ā¤Ž ā¤ĒāĨā¤°ā¤•ā¤žā¤°", + "search_filter_media_type_title": "ā¤Žā¤žā¤§āĨā¤¯ā¤Ž ā¤ĒāĨā¤°ā¤•ā¤žā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "search_filter_people_title": "⤞āĨ‹ā¤• ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "search_for": "ā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ļāĨ‹ā¤§ā¤ž", + "search_for_existing_person": "ā¤ĩā¤ŋā¤ĻāĨā¤¯ā¤Žā¤žā¤¨ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤ļāĨ‹ā¤§ā¤ž", + "search_no_more_result": "⤆⤪⤖āĨ€ ā¤Ē⤰ā¤ŋā¤Ŗā¤žā¤Ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "search_no_people": "⤕āĨ‹ā¤Ŗā¤¤āĨ€ā¤šāĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¨ā¤žā¤šāĨ€", + "search_no_people_named": "“{name}” ā¤¨ā¤žā¤ĩā¤žā¤šāĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤¸ā¤žā¤Ēā¤Ąā¤˛āĨ€ ā¤¨ā¤žā¤šāĨ€", + "search_no_result": "ā¤•ā¤žā¤šāĨ€ā¤šāĨ€ ā¤¸ā¤žā¤Ēā¤Ąā¤˛āĨ‡ ā¤¨ā¤žā¤šāĨ€; ā¤ĩāĨ‡ā¤—ā¤ŗā¤ž ā¤ļāĨ‹ā¤§ ā¤ļā¤ŦāĨā¤Ļ ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤏⤂⤝āĨ‹ā¤œā¤¨ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "search_options": "ā¤ļāĨ‹ā¤§ ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "search_page_categories": "ā¤ļāĨā¤°āĨ‡ā¤ŖāĨā¤¯ā¤ž", + "search_page_motion_photos": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹", + "search_page_no_objects": "ā¤ĩ⤏āĨā¤¤āĨ‚ā¤‚ā¤šāĨ€ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤žā¤šāĨ€", + "search_page_no_places": "⤠ā¤ŋā¤•ā¤žā¤Ŗā¤žā¤‚ā¤šāĨ€ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤¨ā¤žā¤šāĨ€", + "search_page_screenshots": "⤏āĨā¤•āĨā¤°āĨ€ā¤¨ā¤ļāĨ‰ā¤ŸāĨā¤¸", + "search_page_search_photos_videos": "⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ļāĨ‹ā¤§ā¤ž", + "search_page_selfies": "⤏āĨ‡ā¤˛āĨā¤ĢāĨ€ā¤œ", + "search_page_things": "ā¤ĩ⤏āĨā¤¤āĨ‚", + "search_page_view_all_button": "⤏⤰āĨā¤ĩ ā¤Ēā¤šā¤ž", + "search_page_your_activity": "⤤āĨā¤Žā¤šāĨ‡ ⤕āĨā¤°ā¤ŋā¤¯ā¤žā¤•ā¤˛ā¤žā¤Ē", + "search_page_your_map": "⤤āĨā¤Žā¤šā¤ž ā¤¨ā¤•ā¤žā¤ļā¤ž", + "search_people": "⤞āĨ‹ā¤• ā¤ļāĨ‹ā¤§ā¤ž", + "search_places": "⤠ā¤ŋā¤•ā¤žā¤ŖāĨ‡ ā¤ļāĨ‹ā¤§ā¤ž", + "search_rating": "⤰āĨ‡ā¤Ÿā¤ŋ⤂⤗⤍āĨā¤¸ā¤žā¤° ā¤ļāĨ‹ā¤§ā¤ž...", + "search_result_page_new_search_hint": "⤍ā¤ĩāĨ€ā¤¨ ā¤ļāĨ‹ā¤§", + "search_settings": "ā¤ļāĨ‹ā¤§ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "search_state": "ā¤°ā¤žā¤œāĨā¤¯/⤏āĨā¤ŸāĨ‡ā¤Ÿ ā¤ļāĨ‹ā¤§ā¤ž...", + "search_suggestion_list_smart_search_hint_1": "ā¤ĄāĨ€ā¤ĢāĨ‰ā¤˛āĨā¤Ÿā¤¨āĨ‡ ⤏āĨā¤Žā¤žā¤°āĨā¤Ÿ ⤏⤰āĨā¤š ⤏āĨā¤°āĨ‚ ā¤†ā¤šāĨ‡; ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤ļāĨ‹ā¤§ā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤šāĨ€ ā¤°ā¤šā¤¨ā¤ž ā¤ĩā¤žā¤Ēā¤°ā¤ž. ", + "search_suggestion_list_smart_search_hint_2": "m:⤤āĨā¤Žā¤šā¤ž-ā¤ļāĨ‹ā¤§-ā¤ļā¤ŦāĨā¤Ļ", + "search_tags": "⤟āĨ…⤗āĨā¤¸ ā¤ļāĨ‹ā¤§ā¤ž...", + "search_timezone": "ā¤ĩāĨ‡ā¤ŗā¤•āĨā¤ˇāĨ‡ā¤¤āĨā¤° ā¤ļāĨ‹ā¤§ā¤ž...", + "search_type": "ā¤ļāĨ‹ā¤§ ā¤ĒāĨā¤°ā¤•ā¤žā¤°", + "search_your_photos": "⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ļāĨ‹ā¤§ā¤ž", + "searching_locales": "⤞āĨ‹ā¤•⤞āĨā¤¸ ā¤ļāĨ‹ā¤§ā¤¤ ā¤†ā¤šāĨ‡...", + "second": "⤏āĨ‡ā¤•⤂ā¤Ļ", + "see_all_people": "⤏⤰āĨā¤ĩ ⤞āĨ‹ā¤• ā¤Ēā¤žā¤šā¤ž", + "select": "⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_album_cover": "⤅⤞āĨā¤Ŧā¤Ž ⤕ā¤ĩāĨā¤šā¤° ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_all": "⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_all_duplicates": "⤏⤰āĨā¤ĩ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_all_in": "{group} ā¤Žā¤§āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_avatar_color": "⤅ā¤ĩā¤¤ā¤žā¤°ā¤šā¤ž ⤰⤂⤗ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_face": "⤚āĨ‡ā¤šā¤°ā¤ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_featured_photo": "ā¤Ģā¤ŋ⤚⤰āĨā¤Ą ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_from_computer": "⤕āĨ‰ā¤ŽāĨā¤ĒāĨā¤¯āĨā¤Ÿā¤°ā¤Žā¤§āĨ‚⤍ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_keep_all": "⤏⤰āĨā¤ĩ ⤠āĨ‡ā¤ĩ⤪āĨ‡ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_library_owner": "ā¤˛ā¤žā¤¯ā¤ŦāĨā¤°ā¤°āĨ€ ā¤Žā¤žā¤˛ā¤• ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_new_face": "⤍ā¤ĩā¤ž ⤚āĨ‡ā¤šā¤°ā¤ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_person_to_tag": "⤟āĨ…⤗ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_photos": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_trash_all": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "select_user_for_sharing_page_err_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤪āĨā¤¯ā¤žā¤¤ ⤅⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "selected": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨ‡", + "selected_count": "{count, plural, other {# ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡}}", + "selected_gps_coordinates": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž GPS ⤏āĨā¤Ĩā¤žā¤¨ā¤¨ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļā¤žā¤‚ā¤•", + "send_message": "⤏⤂ā¤ĻāĨ‡ā¤ļ ā¤Ēā¤žā¤ ā¤ĩā¤ž", + "send_welcome_email": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤ˆā¤ŽāĨ‡ā¤˛ ā¤Ēā¤žā¤ ā¤ĩā¤ž", + "server_endpoint": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤ā¤‚ā¤Ąā¤ĒāĨ‰ā¤‡ā¤‚ā¤Ÿ", + "server_info_box_app_version": "⤅āĨ…ā¤Ē ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€", + "server_info_box_server_url": "⤏⤰āĨā¤ĩāĨā¤šā¤° URL", + "server_offline": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤑ā¤Ģā¤˛ā¤žā¤‡ā¤¨", + "server_online": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨", + "server_privacy": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤗āĨ‹ā¤Ē⤍āĨ€ā¤¯ā¤¤ā¤ž", + "server_stats": "⤏⤰āĨā¤ĩāĨā¤šā¤° ā¤†ā¤•ā¤ĄāĨ‡ā¤ĩā¤žā¤°āĨ€", + "server_version": "⤏⤰āĨā¤ĩāĨā¤šā¤° ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€", + "set": "⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_as_album_cover": "⤅⤞āĨā¤Ŧā¤Ž ⤕ā¤ĩāĨā¤šā¤° ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_as_featured_photo": "ā¤Ģā¤ŋ⤚⤰āĨā¤Ą ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_as_profile_picture": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_date_of_birth": "⤜⤍āĨā¤Žā¤¤ā¤žā¤°āĨ€ā¤– ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_profile_picture": "ā¤ĒāĨā¤°āĨ‹ā¤Ģā¤žā¤‡ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "set_slideshow_to_fullscreen": "⤏āĨā¤˛ā¤žā¤‡ā¤Ąā¤ļāĨ‹ ā¤ĢāĨā¤˛ā¤¸āĨā¤•āĨā¤°āĨ€ā¤¨ ā¤•ā¤°ā¤ž", + "set_stack_primary_asset": "ā¤ŽāĨā¤–āĨā¤¯ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "setting_image_viewer_help": "ā¤ĄāĨ€ā¤ŸāĨ‡ā¤˛ ā¤ĩāĨā¤šāĨā¤¯āĨ‚⤅⤰ ⤆⤧āĨ€ ā¤˛ā¤šā¤žā¤¨ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ⤞āĨ‹ā¤Ą ⤕⤰⤤āĨ‹, ⤍⤂⤤⤰ (⤏⤕āĨā¤ˇā¤Ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸) ā¤Žā¤§āĨā¤¯ā¤Ž ā¤†ā¤•ā¤žā¤°ā¤žā¤šāĨ‡ ā¤ĒāĨā¤°ā¤ŋā¤ĩāĨā¤šāĨā¤¯āĨ‚ ⤞āĨ‹ā¤Ą ⤕⤰⤤āĨ‹, ⤆⤪ā¤ŋ ā¤ļāĨ‡ā¤ĩ⤟āĨ€ (⤏⤕āĨā¤ˇā¤Ž ⤅⤏⤞āĨā¤¯ā¤žā¤¸) ā¤ŽāĨ‚⤺ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ⤕⤰⤤āĨ‹.", + "setting_image_viewer_original_subtitle": "ā¤ĒāĨ‚⤰āĨā¤Ŗ-⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍⤚āĨ€ ā¤ŽāĨ‚⤺ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž (ā¤ŽāĨ‹ā¤ āĨ€). ā¤ĄāĨ‡ā¤Ÿā¤ž ā¤ĩā¤žā¤Ē⤰ ā¤•ā¤ŽāĨ€ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ (⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ā¤ĩ ā¤Ąā¤ŋā¤ĩāĨā¤šā¤žā¤‡ā¤¸ ⤕āĨ…ā¤ļ ā¤ĻāĨ‹ā¤¨āĨā¤šāĨ€) ⤅⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž.", + "setting_image_viewer_original_title": "ā¤ŽāĨ‚⤺ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "setting_image_viewer_preview_subtitle": "ā¤Žā¤§āĨā¤¯ā¤Ž-⤰ā¤ŋā¤āĨ‹ā¤˛āĨā¤¯āĨ‚ā¤ļ⤍ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤏⤕āĨā¤ˇā¤Ž ā¤•ā¤°ā¤ž. ⤅⤕āĨā¤ˇā¤Ž ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ā¤ĨāĨ‡ā¤Ÿ ā¤ŽāĨ‚⤺ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ā¤šāĨ‹ā¤ˆā¤˛ ⤕ā¤ŋ⤂ā¤ĩā¤ž ā¤Ģ⤕āĨā¤¤ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤ĩā¤žā¤Ēā¤°ā¤˛ā¤ž ā¤œā¤žā¤ˆā¤˛.", + "setting_image_viewer_preview_title": "ā¤ĒāĨā¤°ā¤ŋā¤ĩāĨā¤šāĨā¤¯āĨ‚ ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "setting_image_viewer_title": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž", + "setting_languages_apply": "ā¤˛ā¤žā¤—āĨ‚ ā¤•ā¤°ā¤ž", + "setting_languages_subtitle": "⤅āĨ…ā¤Ē⤚āĨ€ ā¤­ā¤žā¤ˇā¤ž ā¤Ŧā¤Ļā¤˛ā¤ž", + "setting_notifications_notify_failures_grace_period": "ā¤Ēā¤žā¤°āĨā¤ļāĨā¤ĩ⤭āĨ‚ā¤ŽāĨ€ ā¤ŦāĨ…⤕⤅ā¤Ē ⤅ā¤Ē⤝ā¤ļā¤žā¤‚ā¤šāĨ€ ⤏āĨ‚ā¤šā¤¨ā¤ž: {duration}", + "setting_notifications_notify_hours": "{count} ā¤¤ā¤žā¤¸", + "setting_notifications_notify_immediately": "⤤⤤āĨā¤•ā¤žā¤ŗ", + "setting_notifications_notify_minutes": "{count} ā¤Žā¤ŋ⤍ā¤ŋ⤟āĨ‡", + "setting_notifications_notify_never": "⤕⤧āĨ€ā¤š ā¤¨ā¤žā¤šāĨ€", + "setting_notifications_notify_seconds": "{count} ⤏āĨ‡ā¤•⤂ā¤Ļ", + "setting_notifications_single_progress_subtitle": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤†ā¤¯ā¤Ÿā¤Žā¤¸ā¤žā¤ āĨ€ ⤤ā¤Ēā¤ļāĨ€ā¤˛ā¤ĩā¤žā¤° ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ĒāĨā¤°ā¤—⤤āĨ€ ā¤Žā¤žā¤šā¤ŋ⤤āĨ€", + "setting_notifications_single_progress_title": "ā¤Ēā¤žā¤°āĨā¤ļāĨā¤ĩ⤭āĨ‚ā¤ŽāĨ€ ā¤ŦāĨ…⤕⤅ā¤Ē⤚āĨ€ ⤤ā¤Ēā¤ļāĨ€ā¤˛ā¤ĩā¤žā¤° ā¤ĒāĨā¤°ā¤—⤤āĨ€ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "setting_notifications_subtitle": "⤤āĨā¤Žā¤šāĨ€ ⤏āĨ‚ā¤šā¤¨ā¤ž ā¤ĒāĨā¤°ā¤žā¤§ā¤žā¤¨āĨā¤¯āĨ‡ ā¤¸ā¤Žā¤žā¤¯āĨ‹ā¤œā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "setting_notifications_total_progress_subtitle": "ā¤ā¤•āĨ‚⤪ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ĒāĨā¤°ā¤—⤤āĨ€ (ā¤ĒāĨ‚⤰āĨā¤Ŗ/ā¤ā¤•āĨ‚⤪ ā¤†ā¤¯ā¤Ÿā¤Ž)", + "setting_notifications_total_progress_title": "ā¤Ēā¤žā¤°āĨā¤ļāĨā¤ĩ⤭āĨ‚ā¤ŽāĨ€ ā¤ŦāĨ…⤕⤅ā¤Ē⤚āĨ€ ā¤ā¤•āĨ‚⤪ ā¤ĒāĨā¤°ā¤—⤤āĨ€ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "setting_video_viewer_looping_title": "⤞āĨ‚ā¤Ēā¤ŋ⤂⤗", + "setting_video_viewer_original_video_subtitle": "⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰āĨ‚⤍ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤏āĨā¤ŸāĨā¤°ā¤ŋā¤Ž ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤟āĨā¤°ā¤žā¤¨āĨā¤¸ā¤•āĨ‹ā¤Ą ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤…ā¤¸ā¤˛ā¤ž ⤤⤰āĨ€ ā¤ŽāĨ‚⤺ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž. ā¤Ŧā¤Ģ⤰ā¤ŋ⤂⤗ ā¤šāĨ‹ā¤Š ā¤ļ⤕⤤āĨ‡. ⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕⤰āĨ€ā¤¤āĨā¤¯ā¤ž ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤¯ā¤ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ā¤Ēā¤žā¤¸āĨ‚⤍ ⤏āĨā¤ĩ⤤⤂⤤āĨā¤°ā¤Ē⤪āĨ‡ ā¤ŽāĨ‚⤺ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤āĨ‡ā¤¤ ā¤ĒāĨā¤˛āĨ‡ ā¤šāĨ‹ā¤¤ā¤žā¤¤.", + "setting_video_viewer_original_video_title": "ā¤ŽāĨ‚⤺ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤏⤕āĨā¤¤āĨ€ā¤¨āĨ‡ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž", + "settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "settings_require_restart": "ā¤šāĨ€ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ā¤˛ā¤žā¤—āĨ‚ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž Immich ⤰āĨ€ā¤¸āĨā¤Ÿā¤žā¤°āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "settings_saved": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤜⤤⤍ ⤕āĨ‡ā¤˛āĨā¤¯ā¤ž", + "setup_pin_code": "PIN ⤕āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "share": "ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "share_action_prompt": "{count} ā¤†ā¤¯ā¤Ÿā¤Ž ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "share_add_photos": "ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤜āĨ‹ā¤Ąā¤ž", + "share_assets_selected": "{count} ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡", + "share_dialog_preparing": "ā¤¤ā¤¯ā¤žā¤° ⤕⤰⤤ ā¤†ā¤šāĨ‡...", + "share_link": "ā¤ļāĨ‡ā¤…⤰ ā¤ĻāĨā¤ĩā¤ž", + "shared": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "shared_album_activities_input_disable": "⤟ā¤ŋā¤ĒāĨā¤Ē⤪āĨ€ ⤍ā¤ŋ⤎āĨā¤•āĨā¤°ā¤ŋ⤝ ā¤†ā¤šāĨ‡", + "shared_album_activity_remove_content": "ā¤šāĨ€ ⤕āĨƒā¤¤āĨ€ ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šāĨ€ ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "shared_album_activity_remove_title": "⤕āĨƒā¤¤āĨ€ ā¤šā¤Ÿā¤ĩā¤ž", + "shared_album_section_people_action_error": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤Ŧā¤žā¤šāĨ‡ā¤° ā¤Ēā¤Ąā¤¤ā¤žā¤¨ā¤ž/ā¤•ā¤žā¤ĸā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "shared_album_section_people_action_leave": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤•ā¤žā¤ĸā¤ž", + "shared_album_section_people_action_remove_user": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨ‚⤍ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤•ā¤žā¤ĸā¤ž", + "shared_album_section_people_title": "⤞āĨ‹ā¤•", + "shared_by": "ā¤¯ā¤žā¤‚ā¤¨āĨ€ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "shared_by_user": "{user} ā¤¯ā¤žā¤‚ā¤¨āĨ€ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "shared_by_you": "⤤āĨā¤Žā¤šāĨā¤¯ā¤žā¤•ā¤ĄāĨ‚⤍ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "shared_from_partner": "{partner} ā¤•ā¤ĄāĨ€ā¤˛ ā¤ĢāĨ‹ā¤ŸāĨ‹", + "shared_intent_upload_button_progress_text": "{current}/{total} ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ā¤žā¤˛āĨ‡", + "shared_link_app_bar_title": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩāĨ‡", + "shared_link_clipboard_copied_massage": "⤕āĨā¤˛ā¤ŋā¤Ēā¤ŦāĨ‹ā¤°āĨā¤Ąā¤ĩ⤰ ⤕āĨ‰ā¤ĒāĨ€ ⤕āĨ‡ā¤˛āĨ‡", + "shared_link_clipboard_text": "ā¤ĻāĨā¤ĩā¤ž: {link}\nā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą: {password}", + "shared_link_create_error": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩā¤ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤¤ā¤žā¤¨ā¤ž ⤤āĨā¤°āĨā¤ŸāĨ€", + "shared_link_custom_url_description": "ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ URL ā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤šā¤ž ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩā¤ž ā¤‰ā¤˜ā¤Ąā¤ž", + "shared_link_edit_description_hint": "ā¤ļāĨ‡ā¤…ā¤°ā¤šāĨ‡ ā¤ĩ⤰āĨā¤Ŗā¤¨ ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "shared_link_edit_expire_after_option_day": "1 ā¤Ļā¤ŋā¤ĩ⤏", + "shared_link_edit_expire_after_option_days": "{count} ā¤Ļā¤ŋā¤ĩ⤏", + "shared_link_edit_expire_after_option_hour": "1 ā¤¤ā¤žā¤¸", + "shared_link_edit_expire_after_option_hours": "{count} ā¤¤ā¤žā¤¸", + "shared_link_edit_expire_after_option_minute": "1 ā¤Žā¤ŋ⤍ā¤ŋ⤟", + "shared_link_edit_expire_after_option_minutes": "{count} ā¤Žā¤ŋ⤍ā¤ŋ⤟āĨ‡", + "shared_link_edit_expire_after_option_months": "{count} ā¤Žā¤šā¤ŋ⤍āĨ‡", + "shared_link_edit_expire_after_option_year": "{count} ā¤ĩ⤰āĨā¤ˇ", + "shared_link_edit_password_hint": "ā¤ļāĨ‡ā¤…ā¤°ā¤šā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "shared_link_edit_submit_button": "ā¤ĻāĨā¤ĩā¤ž ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ā¤•ā¤°ā¤ž", + "shared_link_error_server_url_fetch": "⤏⤰āĨā¤ĩāĨā¤šā¤° URL ā¤Žā¤ŋ⤺āĨ‚ ā¤ļā¤•ā¤˛ā¤ž ā¤¨ā¤žā¤šāĨ€", + "shared_link_expires_day": "{count} ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_days": "{count} ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_hour": "{count} ā¤¤ā¤žā¤¸ā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_hours": "{count} ā¤¤ā¤žā¤¸ā¤žā¤‚ā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_minute": "{count} ā¤Žā¤ŋ⤍ā¤ŋā¤Ÿā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_minutes": "{count} ā¤Žā¤ŋ⤍ā¤ŋā¤Ÿā¤žā¤‚ā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_never": "⤕⤧āĨ€ā¤š ⤏⤂ā¤Ē⤤ ā¤¨ā¤žā¤šāĨ€ ∞", + "shared_link_expires_second": "{count} ⤏āĨ‡ā¤•⤂ā¤Ļā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_expires_seconds": "{count} ⤏āĨ‡ā¤•⤂ā¤Ļā¤žā¤¤ ⤏⤂ā¤ĒāĨ‡ā¤˛", + "shared_link_individual_shared": "ā¤ĩāĨˆā¤¯ā¤•āĨā¤¤ā¤ŋ⤕ ā¤ļāĨ‡ā¤…⤰", + "shared_link_info_chip_metadata": "EXIF (ā¤ā¤•āĨā¤¸ā¤ŋā¤Ģ)", + "shared_link_manage_links": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩāĨ‡ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "shared_link_options": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩā¤ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯", + "shared_link_password_description": "ā¤šā¤ž ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩā¤ž ā¤Ēā¤žā¤šā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤆ā¤ĩā¤ļāĨā¤¯ā¤• ā¤†ā¤šāĨ‡", + "shared_links": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĻāĨā¤ĩāĨ‡", + "shared_links_description": "ā¤ĻāĨā¤ĩāĨā¤¯ā¤žā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "shared_photos_and_videos_count": "{assetCount, plural, other {# ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤆⤪ā¤ŋ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓}}", + "shared_with_me": "ā¤Žā¤žā¤āĨā¤¯ā¤žā¤¸āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡", + "shared_with_partner": "{partner} ⤏āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡", + "sharing": "ā¤ļāĨ‡ā¤…⤰ā¤ŋ⤂⤗", + "sharing_enter_password": "ā¤šāĨ‡ ā¤ĒāĨƒā¤ˇāĨā¤  ā¤Ēā¤žā¤šā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž.", + "sharing_page_album": "ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ⤅⤞āĨā¤Ŧā¤Ž", + "sharing_page_description": "⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤•ā¤Žā¤§āĨ€ā¤˛ ⤞āĨ‹ā¤•ā¤žā¤‚ā¤¸āĨ‹ā¤Ŧ⤤ ā¤ĢāĨ‹ā¤ŸāĨ‹-ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ⤅⤞āĨā¤Ŧā¤Ž ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž.", + "sharing_page_empty_list": "⤰ā¤ŋā¤•ā¤žā¤ŽāĨ€ ā¤¯ā¤žā¤ĻāĨ€", + "sharing_sidebar_description": "ā¤¸ā¤žā¤‡ā¤Ąā¤Ŧā¤žā¤°ā¤Žā¤§āĨā¤¯āĨ‡ ā¤ļāĨ‡ā¤…⤰ā¤ŋā¤‚ā¤—ā¤šā¤ž ā¤ĻāĨā¤ĩā¤ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "sharing_silver_appbar_create_shared_album": "⤍ā¤ĩāĨ€ā¤¨ ā¤ļāĨ‡ā¤…⤰āĨā¤Ą ⤅⤞āĨā¤Ŧā¤Ž", + "sharing_silver_appbar_share_partner": "ā¤­ā¤žā¤—āĨ€ā¤Ļā¤žā¤°ā¤žā¤¸āĨ‹ā¤Ŧ⤤ ā¤ļāĨ‡ā¤…⤰ ā¤•ā¤°ā¤ž", + "shift_to_permanent_delete": "⤅āĨ…⤏āĨ‡ā¤Ÿ ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⇧ ā¤Ļā¤žā¤Ŧā¤ž", + "show_album_options": "⤅⤞āĨā¤Ŧā¤Ž ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_albums": "⤅⤞āĨā¤Ŧā¤Ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_all_people": "⤏⤰āĨā¤ĩ ⤞āĨ‹ā¤• ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_and_hide_people": "⤞āĨ‹ā¤• ā¤Ļā¤žā¤–ā¤ĩā¤ž ⤆⤪ā¤ŋ ⤞ā¤Ēā¤ĩā¤ž", + "show_file_location": "ā¤Ģā¤žā¤‡ā¤˛ā¤šāĨ‡ ⤏āĨā¤Ĩā¤žā¤¨ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_gallery": "⤗āĨ…⤞⤰āĨ€ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_hidden_people": "⤞ā¤Ēā¤ĩ⤞āĨ‡ā¤˛āĨ‡ ⤞āĨ‹ā¤• ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_in_timeline": "ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_in_timeline_setting_description": "ā¤¯ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹-ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤤āĨā¤Žā¤šāĨā¤¯ā¤ž ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_keyboard_shortcuts": "⤕āĨ€ā¤ŦāĨ‹ā¤°āĨā¤Ą ā¤ļāĨ‰ā¤°āĨā¤Ÿā¤•ā¤Ÿ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_metadata": "ā¤ŽāĨ‡ā¤Ÿā¤žā¤ĄāĨ‡ā¤Ÿā¤ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_or_hide_info": "ā¤Žā¤žā¤šā¤ŋ⤤āĨ€ ā¤Ļā¤žā¤–ā¤ĩā¤ž ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤞ā¤Ēā¤ĩā¤ž", + "show_password": "ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_person_options": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤šāĨ‡ ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_progress_bar": "ā¤ĒāĨā¤°ā¤—⤤āĨ€ ā¤Ē⤟āĨā¤ŸāĨ€ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_search_options": "ā¤ļāĨ‹ā¤§ ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_shared_links": "ā¤ļāĨ‡ā¤…⤰ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ā¤ĻāĨā¤ĩāĨ‡ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_slideshow_transition": "⤏āĨā¤˛ā¤žā¤‡ā¤Ąā¤ļāĨ‹ ⤟āĨā¤°ā¤žā¤‚ā¤ā¤ŋā¤ļ⤍ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "show_supporter_badge": "ā¤¸ā¤Žā¤°āĨā¤Ĩ⤕ ā¤ŦāĨ…ā¤œ", + "show_supporter_badge_description": "ā¤¸ā¤Žā¤°āĨā¤Ĩ⤕ ā¤ŦāĨ…ā¤œ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "shuffle": "ā¤ļā¤Ģ⤞", + "sidebar": "ā¤¸ā¤žā¤‡ā¤Ąā¤Ŧā¤žā¤°", + "sidebar_display_description": "ā¤¸ā¤žā¤‡ā¤Ąā¤Ŧā¤žā¤°ā¤Žā¤§āĨā¤¯āĨ‡ ā¤ĻāĨƒā¤ļāĨā¤¯ā¤žā¤šā¤ž ā¤ĻāĨā¤ĩā¤ž ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "sign_out": "ā¤¸ā¤žā¤‡ā¤¨ ā¤†ā¤‰ā¤Ÿ", + "sign_up": "ā¤¸ā¤žā¤‡ā¤¨ ⤅ā¤Ē", + "size": "ā¤†ā¤•ā¤žā¤°", + "skip_to_content": "ā¤¸ā¤žā¤Žā¤—āĨā¤°āĨ€ā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "skip_to_folders": "ā¤ĢāĨ‹ā¤˛āĨā¤Ąā¤°āĨā¤¸ā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "skip_to_tags": "⤟āĨ…⤗āĨā¤¸ā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "slideshow": "⤏āĨā¤˛ā¤žā¤‡ā¤Ąā¤ļāĨ‹", + "slideshow_settings": "⤏āĨā¤˛ā¤žā¤‡ā¤Ąā¤ļāĨ‹ ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ", + "sort_albums_by": "⤅⤞āĨā¤Ŧā¤Ž ā¤¯ā¤žā¤¨āĨā¤¸ā¤žā¤° ⤕āĨā¤°ā¤Ž ā¤˛ā¤žā¤ĩā¤žâ€Ļ", + "sort_created": "ā¤¤ā¤¯ā¤žā¤° ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "sort_items": "ā¤†ā¤¯ā¤Ÿā¤Žā¤žā¤‚ā¤šāĨ€ ⤏⤂⤖āĨā¤¯ā¤ž", + "sort_modified": "ā¤Ŧā¤Ļ⤞ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "sort_newest": "⤅⤞āĨ€ā¤•ā¤Ąā¤šā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹", + "sort_oldest": "⤏⤰āĨā¤ĩā¤žā¤¤ ⤜āĨā¤¨ā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹", + "sort_people_by_similarity": "ā¤¸ā¤žā¤ŽāĨā¤¯ā¤¤āĨ‡ā¤¨āĨā¤¸ā¤žā¤° ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤‚ā¤šā¤ž ⤕āĨā¤°ā¤Ž ā¤˛ā¤žā¤ĩā¤ž", + "sort_recent": "⤍āĨā¤•ā¤¤ā¤žā¤š ⤘āĨ‡ā¤¤ā¤˛āĨ‡ā¤˛ā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹", + "sort_title": "ā¤ļāĨ€ā¤°āĨā¤ˇā¤•", + "source": "⤏āĨā¤¤āĨā¤°āĨ‹ā¤¤", + "stack": "⤏āĨā¤ŸāĨ…⤕", + "stack_action_prompt": "{count} ⤏āĨā¤ŸāĨ…⤕ ⤕āĨ‡ā¤˛āĨ‡", + "stack_duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ ⤏āĨā¤ŸāĨ…⤕ ā¤•ā¤°ā¤ž", + "stack_select_one_photo": "⤏āĨā¤ŸāĨ…ā¤•ā¤¸ā¤žā¤ āĨ€ ā¤ā¤• ā¤ŽāĨā¤–āĨā¤¯ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "stack_selected_photos": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ⤏āĨā¤ŸāĨ…⤕ ā¤•ā¤°ā¤ž", + "stacked_assets_count": "⤏āĨā¤ŸāĨ…⤕ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ {count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}}", + "stacktrace": "⤏āĨā¤ŸāĨ…ā¤•ā¤ŸāĨā¤°āĨ‡ā¤¸", + "start": "⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "start_date": "⤏āĨā¤°āĨā¤ĩā¤žā¤¤āĨ€ā¤šāĨ€ ā¤¤ā¤žā¤°āĨ€ā¤–", + "state": "⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "status": "⤏āĨā¤ŸāĨ‡ā¤Ÿā¤¸", + "stop_casting": "ā¤•ā¤žā¤¸āĨā¤Ÿā¤ŋ⤂⤗ ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤ž", + "stop_motion_photo": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤ž", + "stop_photo_sharing": "⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨ‡ ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤žā¤¯ā¤šāĨ‡?", + "stop_photo_sharing_description": "{partner} ā¤¯ā¤žā¤‚ā¤¨ā¤ž ā¤†ā¤¤ā¤ž ⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ēā¤žā¤šā¤¤ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤° ā¤¨ā¤žā¤šāĨ€ā¤¤.", + "stop_sharing_photos_with_user": "ā¤¯ā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤¸āĨ‹ā¤Ŧ⤤ ⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ļāĨ‡ā¤…⤰ ⤕⤰⤪āĨ‡ ā¤Ĩā¤žā¤‚ā¤Ŧā¤ĩā¤ž", + "storage": "ā¤¸ā¤‚ā¤šā¤¯ā¤¨ ā¤œā¤žā¤—ā¤ž", + "storage_label": "ā¤¸ā¤‚ā¤šā¤¯ā¤¨ ⤞āĨ‡ā¤Ŧ⤞", + "storage_quota": "ā¤¸ā¤‚ā¤šā¤¯ā¤¨ ⤕āĨ‹ā¤Ÿā¤ž", + "storage_usage": "{available} ā¤ĒāĨˆā¤•āĨ€ {used} ā¤ĩā¤žā¤Ē⤰⤞āĨ‡", + "submit": "ā¤¸ā¤žā¤Ļ⤰ ā¤•ā¤°ā¤ž", + "success": "⤝ā¤ļ⤏āĨā¤ĩāĨ€", + "suggestions": "⤏āĨ‚ā¤šā¤¨ā¤ž", + "sunrise_on_the_beach": "ā¤¸ā¤ŽāĨā¤ĻāĨā¤°ā¤•ā¤ŋā¤¨ā¤žā¤ąāĨā¤¯ā¤žā¤ĩ⤰ ⤏āĨ‚⤰āĨā¤¯āĨ‹ā¤Ļ⤝", + "support": "ā¤¸ā¤šā¤žā¤¯āĨā¤¯", + "support_and_feedback": "ā¤¸ā¤šā¤žā¤¯āĨā¤¯ ⤆⤪ā¤ŋ ⤅⤭ā¤ŋā¤ĒāĨā¤°ā¤žā¤¯", + "support_third_party_description": "⤤āĨā¤Žā¤šāĨ€ Immich ⤏āĨā¤Ĩā¤žā¤Ēā¤¨ā¤ž ⤤āĨƒā¤¤āĨ€ā¤¯-ā¤Ē⤕āĨā¤ˇ ā¤ĒāĨ…⤕āĨ‡ā¤œā¤ĻāĨā¤ĩā¤žā¤°āĨ‡ ā¤Ļā¤ŋ⤞āĨ€ ā¤†ā¤šāĨ‡. ⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ⤝āĨ‡ā¤Ŗā¤žā¤ąāĨā¤¯ā¤ž ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤤āĨā¤¯ā¤ž ā¤ĒāĨ…⤕āĨ‡ā¤œā¤ŽāĨā¤ŗāĨ‡ ⤅⤏āĨ‚ ā¤ļā¤•ā¤¤ā¤žā¤¤; ⤤āĨā¤¯ā¤žā¤ŽāĨā¤ŗāĨ‡ ā¤–ā¤žā¤˛āĨ€ā¤˛ ā¤ĻāĨā¤ĩāĨā¤¯ā¤žā¤‚ā¤šā¤ž ā¤ĩā¤žā¤Ē⤰ ⤕⤰āĨ‚⤍ ⤏⤰āĨā¤ĩā¤ĒāĨā¤°ā¤Ĩā¤Ž ⤤āĨā¤¯ā¤žā¤‚ā¤šāĨā¤¯ā¤žā¤•ā¤ĄāĨ‡ ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤍āĨ‹ā¤‚ā¤Ļā¤ĩā¤ž.", + "swap_merge_direction": "ā¤Žā¤°āĨā¤œ ā¤Ļā¤ŋā¤ļāĨ‡ā¤šāĨ€ ⤅ā¤Ļā¤˛ā¤žā¤Ŧā¤Ļ⤞ ā¤•ā¤°ā¤ž", + "sync": "ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ", + "sync_albums": "⤅⤞āĨā¤Ŧā¤Ž ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "sync_albums_manual_subtitle": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ ⤏⤰āĨā¤ĩ ā¤ĢāĨ‹ā¤ŸāĨ‹-ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ā¤ŦāĨ…⤕⤅ā¤Ē ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "sync_local": "⤏āĨā¤Ĩā¤žā¤¨ā¤ŋ⤕ ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ", + "sync_remote": "ā¤ĻāĨ‚⤰⤏āĨā¤Ĩ ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ", + "sync_status": "ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ ⤏āĨā¤Ĩā¤ŋ⤤āĨ€", + "sync_status_subtitle": "ā¤¸ā¤Žā¤•āĨā¤°ā¤Žā¤Ŗ ā¤ĒāĨā¤°ā¤Ŗā¤žā¤˛āĨ€ ā¤Ēā¤žā¤šā¤ž ⤆⤪ā¤ŋ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "sync_upload_album_setting_subtitle": "Immich ā¤ĩ⤰āĨ€ā¤˛ ⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž ⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ ⤤āĨā¤Žā¤šāĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž ⤆⤪ā¤ŋ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "tag": "⤟āĨ…⤗", + "tag_assets": "ā¤†ā¤¯ā¤Ÿā¤Žā¤¨ā¤ž ⤟āĨ…⤗ ā¤˛ā¤žā¤ĩā¤ž", + "tag_created": "ā¤¤ā¤¯ā¤žā¤° ⤕āĨ‡ā¤˛āĨ‡ā¤˛ā¤ž ⤟āĨ…⤗: {tag}", + "tag_feature_description": "ā¤¤ā¤žā¤°āĨā¤•ā¤ŋ⤕ ⤟āĨ…⤗ ā¤ĩā¤ŋā¤ˇā¤¯ā¤žā¤‚ā¤¨āĨā¤¸ā¤žā¤° ā¤—ā¤Ÿā¤Ŧā¤ĻāĨā¤§ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤ŦāĨā¤°ā¤žā¤‰ā¤ ā¤•ā¤°ā¤ž", + "tag_not_found_question": "⤟āĨ…⤗ ā¤¸ā¤žā¤Ēā¤Ąā¤¤ ā¤¨ā¤žā¤šāĨ€? ⤍ā¤ĩā¤ž ⤟āĨ…⤗ ā¤¤ā¤¯ā¤žā¤° ā¤•ā¤°ā¤ž", + "tag_people": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤‚ā¤¨ā¤ž ⤟āĨ…⤗ ā¤•ā¤°ā¤ž", + "tag_updated": "⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ⤟āĨ…⤗: {tag}", + "tagged_assets": "⤟āĨ…⤗ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ {count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}}", + "tags": "⤟āĨ…⤗āĨā¤¸", + "tap_to_run_job": "⤜āĨ‰ā¤Ŧ ā¤šā¤žā¤˛ā¤ĩ⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤟āĨ…ā¤Ē ā¤•ā¤°ā¤ž", + "template": "⤟āĨ‡ā¤ŽāĨā¤Ē⤞āĨ‡ā¤Ÿ", + "theme": "ā¤ĨāĨ€ā¤Ž", + "theme_selection": "ā¤ĨāĨ€ā¤Ž ⤍ā¤ŋā¤ĩā¤Ą", + "theme_selection_description": "ā¤ŦāĨā¤°ā¤žā¤‰ā¤ā¤°ā¤šāĨā¤¯ā¤ž ⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ā¤Ē⤏⤂⤤āĨ€ā¤¨āĨā¤¸ā¤žā¤° ā¤ĨāĨ€ā¤Ž ⤆ā¤ĒāĨ‹ā¤†ā¤Ē ā¤˛ā¤žā¤‡ā¤Ÿ/ā¤Ąā¤žā¤°āĨā¤• ā¤•ā¤°ā¤ž", + "theme_setting_asset_list_storage_indicator_title": "⤅āĨ…⤏āĨ‡ā¤Ÿ ā¤Ÿā¤žā¤‡ā¤˛āĨā¤¸ā¤ĩ⤰ ⤏āĨā¤ŸāĨ‹ā¤°āĨ‡ā¤œ ⤍ā¤ŋ⤰āĨā¤ĻāĨ‡ā¤ļ⤕ ā¤Ļā¤žā¤–ā¤ĩā¤ž", + "theme_setting_asset_list_tiles_per_row_title": "ā¤ĒāĨā¤°ā¤¤āĨā¤¯āĨ‡ā¤• ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤āĨ€ā¤˛ ⤅āĨ…⤏āĨ‡ā¤ŸāĨā¤¸ā¤šāĨ€ ⤏⤂⤖āĨā¤¯ā¤ž ({count})", + "theme_setting_colorful_interface_subtitle": "ā¤ŦāĨ…⤕⤗āĨā¤°ā¤žā¤Šā¤‚ā¤Ą ā¤ĒāĨƒā¤ˇāĨā¤ ā¤­ā¤žā¤—ā¤žā¤‚ā¤ĩ⤰ ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤰⤂⤗ ā¤˛ā¤žā¤—āĨ‚ ā¤•ā¤°ā¤ž.", + "theme_setting_colorful_interface_title": "⤰⤂⤗āĨ€ā¤ŦāĨ‡ā¤°ā¤‚⤗āĨ€ ā¤‡ā¤‚ā¤Ÿā¤°ā¤ĢāĨ‡ā¤¸", + "theme_setting_image_viewer_quality_subtitle": "ā¤ĄāĨ€ā¤ŸāĨ‡ā¤˛ ā¤‡ā¤ŽāĨ‡ā¤œ ā¤ĩāĨā¤šāĨā¤¯āĨ‚ā¤…ā¤°ā¤šāĨ€ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž ā¤¸ā¤Žā¤žā¤¯āĨ‹ā¤œā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "theme_setting_image_viewer_quality_title": "ā¤‡ā¤ŽāĨ‡ā¤œ ā¤ĩāĨā¤šāĨā¤¯āĨ‚⤅⤰ ⤗āĨā¤Ŗā¤ĩ⤤āĨā¤¤ā¤ž", + "theme_setting_primary_color_subtitle": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤕āĨƒā¤¤āĨ€ ā¤ĩ ⤅āĨ…⤕āĨā¤¸āĨ‡ā¤‚ā¤Ÿā¤¸ā¤žā¤ āĨ€ ⤰⤂⤗ ⤍ā¤ŋā¤ĩā¤Ąā¤ž.", + "theme_setting_primary_color_title": "ā¤ĒāĨā¤°ā¤žā¤Ĩā¤Žā¤ŋ⤕ ⤰⤂⤗", + "theme_setting_system_primary_color_title": "⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ⤰⤂⤗ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "theme_setting_system_theme_switch": "⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ (⤏ā¤ŋ⤏āĨā¤Ÿā¤Ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œā¤¨āĨā¤¸ā¤žā¤°)", + "theme_setting_theme_subtitle": "⤅āĨ…ā¤Ē⤚āĨ€ ā¤ĨāĨ€ā¤Ž ⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗ ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "theme_setting_three_stage_loading_subtitle": "ā¤ĨāĨā¤°āĨ€-⤏āĨā¤ŸāĨ‡ā¤œ ⤞āĨ‹ā¤Ąā¤ŋā¤‚ā¤—ā¤ŽāĨā¤ŗāĨ‡ ⤗⤤āĨ€ ā¤ĩā¤žā¤ĸāĨ‚ ā¤ļ⤕⤤āĨ‡; ā¤Ē⤰⤂⤤āĨ ⤍āĨ‡ā¤Ÿā¤ĩ⤰āĨā¤• ⤞āĨ‹ā¤Ą ⤞⤕āĨā¤ˇā¤ŖāĨ€ā¤¯ ā¤ĩā¤žā¤ĸ⤤āĨ‹", + "theme_setting_three_stage_loading_title": "ā¤ĨāĨā¤°āĨ€-⤏āĨā¤ŸāĨ‡ā¤œ ⤞āĨ‹ā¤Ąā¤ŋ⤂⤗ ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤ž", + "they_will_be_merged_together": "⤤āĨ‡ ā¤ā¤•ā¤¤āĨā¤° ā¤ĩā¤ŋ⤞āĨ€ā¤¨ ⤕āĨ‡ā¤˛āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛", + "third_party_resources": "⤤āĨƒā¤¤āĨ€ā¤¯-ā¤Ē⤕āĨā¤ˇ ā¤¸ā¤‚ā¤¸ā¤žā¤§ā¤¨āĨ‡", + "time_based_memories": "ā¤ĩāĨ‡ā¤ŗ-ā¤†ā¤§ā¤žā¤°ā¤ŋ⤤ ā¤ŽāĨ‡ā¤Žā¤°āĨ€ā¤œ", + "timeline": "ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨", + "timezone": "ā¤ĩāĨ‡ā¤ŗā¤•āĨā¤ˇāĨ‡ā¤¤āĨā¤°", + "to_archive": "⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤ž", + "to_change_password": "ā¤Ē⤰ā¤ĩ⤞āĨ€ā¤šā¤ž ā¤ļā¤ŦāĨā¤Ļ ā¤Ŧā¤Ļā¤˛ā¤ž", + "to_favorite": "⤆ā¤ĩā¤ĄāĨ€ā¤Žā¤§āĨā¤¯āĨ‡ ⤜āĨ‹ā¤Ąā¤ž", + "to_login": "⤞āĨ‰ā¤— ⤇⤍ ā¤•ā¤°ā¤ž", + "to_multi_select": "ā¤Ŧā¤šāĨ-⤍ā¤ŋā¤ĩā¤Ą ā¤•ā¤°ā¤ž", + "to_parent": "ā¤Ēā¤žā¤˛ā¤•ā¤žā¤•ā¤ĄāĨ‡ ā¤œā¤ž", + "to_select": "⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "to_trash": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ā¤Ÿā¤žā¤•ā¤ž", + "toggle_settings": "⤏āĨ‡ā¤Ÿā¤ŋ⤂⤗āĨā¤œ ⤟āĨ‰ā¤—⤞ ā¤•ā¤°ā¤ž", + "total": "ā¤ā¤•āĨ‚⤪", + "total_usage": "ā¤ā¤•āĨ‚⤪ ā¤ĩā¤žā¤Ē⤰", + "trash": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€", + "trash_action_prompt": "{count} ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ā¤šā¤˛ā¤ĩ⤞āĨ‡", + "trash_all": "⤏⤰āĨā¤ĩ ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ā¤Ÿā¤žā¤•ā¤ž", + "trash_count": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ {count, number}", + "trash_delete_asset": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ā¤Ÿā¤žā¤•ā¤ž/⤅āĨ…⤏āĨ‡ā¤Ÿ ā¤šā¤Ÿā¤ĩā¤ž", + "trash_emptied": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ ⤰ā¤ŋā¤•ā¤žā¤ŽāĨ€ ⤕āĨ‡ā¤˛āĨ€", + "trash_no_results_message": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ā¤Ÿā¤žā¤•ā¤˛āĨ‡ā¤˛āĨ‡ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤ĩ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤝āĨ‡ā¤ĨāĨ‡ ā¤Ļā¤ŋ⤏⤤āĨ€ā¤˛.", + "trash_page_delete_all": "⤏⤰āĨā¤ĩ ā¤šā¤Ÿā¤ĩā¤ž", + "trash_page_empty_trash_dialog_content": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ ⤰ā¤ŋā¤•ā¤žā¤ŽāĨ€ ā¤•ā¤°ā¤žā¤¯ā¤šāĨ€ ā¤•ā¤ž? ā¤šāĨ‡ ā¤†ā¤¯ā¤Ÿā¤Ž Immich ā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛", + "trash_page_info": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤āĨ€ā¤˛ ā¤†ā¤¯ā¤Ÿā¤Ž {days} ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤‚ā¤¨ā¤‚ā¤¤ā¤° ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛", + "trash_page_no_assets": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤ ⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤¨ā¤žā¤šāĨ€ā¤¤", + "trash_page_restore_all": "⤏⤰āĨā¤ĩ ā¤Ē⤰⤤ ā¤†ā¤Ŗā¤ž", + "trash_page_select_assets_btn": "ā¤†ā¤¯ā¤Ÿā¤Ž ⤍ā¤ŋā¤ĩā¤Ąā¤ž", + "trash_page_title": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ ({count})", + "trashed_items_will_be_permanently_deleted_after": "ā¤•ā¤šā¤°ā¤žā¤ĒāĨ‡ā¤ŸāĨ€ā¤¤āĨ€ā¤˛ ā¤†ā¤¯ā¤Ÿā¤Ž {days, plural, one {# ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤‚ā¤¨ā¤‚ā¤¤ā¤°} other {# ā¤Ļā¤ŋā¤ĩā¤¸ā¤žā¤‚ā¤¨ā¤‚ā¤¤ā¤°}} ā¤•ā¤žā¤¯ā¤Žā¤šāĨ‡ ā¤šā¤Ÿā¤ĩ⤞āĨ‡ ā¤œā¤žā¤¤āĨ€ā¤˛.", + "troubleshoot": "ā¤¸ā¤Žā¤¸āĨā¤¯ā¤ž ⤍ā¤ŋā¤ĩā¤žā¤°ā¤Ŗ", + "type": "ā¤ĒāĨā¤°ā¤•ā¤žā¤°", + "unable_to_change_pin_code": "PIN ⤕āĨ‹ā¤Ą ā¤Ŧā¤Ļā¤˛ā¤¤ā¤ž ⤝āĨ‡ā¤¤ ā¤¨ā¤žā¤šāĨ€", + "unable_to_setup_pin_code": "PIN ⤕āĨ‹ā¤Ą ⤏āĨ‡ā¤Ÿ ⤕⤰āĨ‚ ā¤ļ⤕⤤ ā¤¨ā¤žā¤šāĨ€", + "unarchive": "⤅⤍⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š ā¤•ā¤°ā¤ž", + "unarchive_action_prompt": "{count} ⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤šā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸ⤞āĨ‡", + "unarchived_count": "{count, plural, other {⤅⤍⤆⤰āĨā¤•ā¤žā¤‡ā¤ĩāĨā¤š #}}", + "undo": "ā¤ĒāĨ‚⤰āĨā¤ĩā¤ĩ⤤ ā¤•ā¤°ā¤ž", + "unfavorite": "⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "unfavorite_action_prompt": "{count} ⤆ā¤ĩā¤ĄāĨ€ā¤¤āĨ‚⤍ ā¤•ā¤žā¤ĸ⤞āĨ‡", + "unhide_person": "ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ ā¤Ļ⤰āĨā¤ļā¤ĩā¤ž", + "unknown": "ā¤…ā¤œāĨā¤žā¤žā¤¤", + "unknown_country": "ā¤…ā¤œāĨā¤žā¤žā¤¤ ā¤ĻāĨ‡ā¤ļ", + "unknown_year": "ā¤…ā¤œāĨā¤žā¤žā¤¤ ā¤ĩ⤰āĨā¤ˇ", + "unlimited": "ā¤…ā¤Žā¤°āĨā¤¯ā¤žā¤Ļā¤ŋ⤤", + "unlink_motion_video": "ā¤ŽāĨ‹ā¤ļ⤍ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ⤅⤍⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤ž", + "unlink_oauth": "OAuth ⤅⤍⤞ā¤ŋ⤂⤕ ā¤•ā¤°ā¤ž", + "unlinked_oauth_account": "OAuth ā¤–ā¤žā¤¤āĨ‡ ⤅⤍⤞ā¤ŋ⤂⤕ ⤕āĨ‡ā¤˛āĨ‡", + "unmute_memories": "ā¤ŽāĨ‡ā¤Žā¤°āĨ€ā¤œ ā¤…ā¤¨ā¤ŽāĨā¤¯āĨ‚ā¤Ÿ ā¤•ā¤°ā¤ž", + "unnamed_album": "ā¤¨ā¤žā¤ĩ ⤍⤏⤞āĨ‡ā¤˛ā¤ž ⤅⤞āĨā¤Ŧā¤Ž", + "unnamed_album_delete_confirmation": "⤤āĨā¤ŽāĨā¤šā¤žā¤˛ā¤ž ā¤šā¤ž ⤅⤞āĨā¤Ŧā¤Ž ā¤–ā¤°ā¤‚ā¤š ā¤šā¤Ÿā¤ĩā¤žā¤¯ā¤šā¤ž ā¤†ā¤šāĨ‡ ā¤•ā¤ž?", + "unnamed_share": "ā¤¨ā¤žā¤ĩ ⤍⤏⤞āĨ‡ā¤˛āĨ‡ ā¤ļāĨ‡ā¤…⤰", + "unsaved_change": "⤍ ā¤¸ā¤žā¤ ā¤ĩ⤞āĨ‡ā¤˛ā¤ž ā¤Ŧā¤Ļ⤞", + "unselect_all": "⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤ĄāĨ€ ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "unselect_all_duplicates": "⤏⤰āĨā¤ĩ ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤ŸāĨā¤¸ā¤šāĨ€ ⤍ā¤ŋā¤ĩā¤Ą ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "unselect_all_in": "{group} ā¤Žā¤§āĨ€ā¤˛ ⤏⤰āĨā¤ĩ ⤍ā¤ŋā¤ĩā¤ĄāĨ€ ⤰ā¤ĻāĨā¤Ļ ā¤•ā¤°ā¤ž", + "unstack": "⤏āĨā¤ŸāĨ…⤕ ā¤ĩāĨ‡ā¤—ā¤ŗā¤ž ā¤•ā¤°ā¤ž", + "unstack_action_prompt": "{count} ⤅⤍⤏āĨā¤ŸāĨ…⤕ ⤕āĨ‡ā¤˛āĨ‡", + "unstacked_assets_count": "⤅⤍⤏āĨā¤ŸāĨ…⤕ ⤕āĨ‡ā¤˛āĨ‡ā¤˛āĨ‡ {count, plural, one {# ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤†ā¤¯ā¤Ÿā¤Ž}}", + "untagged": "⤟āĨ…⤗ ⤍⤏⤞āĨ‡ā¤˛āĨ‡", + "up_next": "ā¤ĒāĨā¤ĸāĨ‡", + "update_location_action_prompt": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨā¤¯ā¤ž {count} ā¤†ā¤¯ā¤Ÿā¤Žā¤šāĨ‡ ⤏āĨā¤Ĩā¤žā¤¨ ā¤¯ā¤žā¤¨āĨ‡ ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ā¤•ā¤°ā¤ž:", + "updated_at": "⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ⤕āĨ‡ā¤˛āĨ‡", + "updated_password": "ā¤Ē⤰ā¤ĩ⤞āĨ€ā¤šā¤ž ā¤ļā¤ŦāĨā¤Ļ ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ⤕āĨ‡ā¤˛ā¤ž", + "upload": "⤅ā¤Ē⤞āĨ‹ā¤Ą", + "upload_action_prompt": "⤅ā¤Ē⤞āĨ‹ā¤Ąā¤¸ā¤žā¤ āĨ€ {count} ā¤°ā¤žā¤‚ā¤—āĨ‡ā¤¤", + "upload_concurrency": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤¸ā¤Žā¤žā¤‚ā¤¤ā¤°ā¤¤ā¤ž", + "upload_details": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤤ā¤Ēā¤ļāĨ€ā¤˛", + "upload_dialog_info": "⤍ā¤ŋā¤ĩā¤Ąā¤˛āĨ‡ā¤˛āĨ‡ ā¤†ā¤¯ā¤Ÿā¤Ž ⤏⤰āĨā¤ĩāĨā¤šā¤°ā¤ĩ⤰ ā¤ŦāĨ…⤕⤅ā¤Ē ā¤•ā¤°ā¤žā¤¯ā¤šāĨ‡ ā¤•ā¤ž?", + "upload_dialog_title": "⤅āĨ…⤏āĨ‡ā¤Ÿ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤ž", + "upload_errors": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ĒāĨ‚⤰āĨā¤Ŗ ā¤ā¤žā¤˛āĨ‡; {count, plural, one {# ⤤āĨā¤°āĨā¤ŸāĨ€} other {# ⤤āĨā¤°āĨā¤ŸāĨ€}} ⤆ā¤ĸ⤺⤞āĨā¤¯ā¤ž. ⤍ā¤ĩāĨ€ā¤¨ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤†ā¤¯ā¤Ÿā¤Ž ā¤Ēā¤žā¤šā¤ŖāĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĒāĨƒā¤ˇāĨā¤  ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž.", + "upload_finished": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ĒāĨ‚⤰āĨā¤Ŗ", + "upload_progress": "⤉⤰āĨā¤ĩ⤰ā¤ŋ⤤ {remaining, number} — ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ā¤ā¤žā¤˛āĨ‡ā¤˛āĨ‡ {processed, number}/{total, number}", + "upload_skipped_duplicates": "ā¤ĩ⤗⤺⤞āĨ‡ {count, plural, one {# ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤†ā¤¯ā¤Ÿā¤Ž} other {# ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ ā¤†ā¤¯ā¤Ÿā¤Ž}}", + "upload_status_duplicates": "ā¤ĄāĨā¤ĒāĨā¤˛ā¤ŋ⤕āĨ‡ā¤Ÿ", + "upload_status_errors": "⤤āĨā¤°āĨā¤ŸāĨ€", + "upload_status_uploaded": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤ā¤žā¤˛āĨ‡", + "upload_success": "⤅ā¤Ē⤞āĨ‹ā¤Ą ⤝ā¤ļ⤏āĨā¤ĩāĨ€. ⤍ā¤ĩāĨ€ā¤¨ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤†ā¤¯ā¤Ÿā¤Ž ā¤Ļā¤ŋ⤏⤪āĨā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ā¤ĒāĨƒā¤ˇāĨā¤  ⤰āĨ€ā¤ĢāĨā¤°āĨ‡ā¤ļ ā¤•ā¤°ā¤ž.", + "upload_to_immich": "Immich ā¤ĩ⤰ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤•ā¤°ā¤ž ({count})", + "uploading": "⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ā¤¤ ā¤†ā¤šāĨ‡", + "uploading_media": "ā¤Žā¤žā¤§āĨā¤¯ā¤ŽāĨ‡ ⤅ā¤Ē⤞āĨ‹ā¤Ą ā¤šāĨ‹ā¤¤ ā¤†ā¤šāĨ‡ā¤¤", + "url": "URL", + "usage": "ā¤ĩā¤žā¤Ē⤰", + "use_biometric": "ā¤Ŧā¤žā¤¯āĨ‹ā¤ŽāĨ‡ā¤ŸāĨā¤°ā¤ŋ⤕ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "use_current_connection": "⤏⤧āĨā¤¯ā¤žā¤šāĨ‡ ⤕⤍āĨ‡ā¤•āĨā¤ļ⤍ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "use_custom_date_range": "ā¤¯ā¤žā¤ā¤ĩ⤜āĨ€ ā¤¸ā¤žā¤¨āĨā¤•āĨ‚⤞ ā¤Ļā¤ŋā¤¨ā¤žā¤‚ā¤• ā¤ļāĨā¤°āĨ‡ā¤ŖāĨ€ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž", + "user_has_been_deleted": "ā¤šā¤ž ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤šā¤Ÿā¤ĩā¤ŋā¤˛ā¤ž ⤗āĨ‡ā¤˛ā¤ž ā¤†ā¤šāĨ‡.", + "user_id": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤†ā¤¯ā¤ĄāĨ€", + "user_liked": "{user} ā¤¯ā¤žā¤‚ā¤¨ā¤ž {type, select, photo {ā¤šā¤ž ā¤ĢāĨ‹ā¤ŸāĨ‹} video {ā¤šā¤ž ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓} asset {ā¤šā¤ž ā¤†ā¤¯ā¤Ÿā¤Ž} other {ā¤šāĨ‡}} ⤆ā¤ĩā¤Ąā¤˛āĨ‡", + "user_pin_code_settings": "PIN ⤕āĨ‹ā¤Ą", + "user_pin_code_settings_description": "⤤āĨā¤Žā¤šā¤ž PIN ⤕āĨ‹ā¤Ą ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "user_privacy": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤗āĨ‹ā¤Ē⤍āĨ€ā¤¯ā¤¤ā¤ž", + "user_purchase_settings": "⤖⤰āĨ‡ā¤ĻāĨ€", + "user_purchase_settings_description": "⤤āĨā¤Žā¤šāĨ€ ⤖⤰āĨ‡ā¤ĻāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ā¤•ā¤°ā¤ž", + "user_role_set": "{user} ā¤¯ā¤žā¤‚ā¤¨ā¤ž {role} ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ⤏āĨ‡ā¤Ÿ ā¤•ā¤°ā¤ž", + "user_usage_detail": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ĩā¤žā¤Ēā¤°ā¤žā¤šā¤ž ⤤ā¤Ēā¤ļāĨ€ā¤˛", + "user_usage_stats": "ā¤–ā¤žā¤¤āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ĩā¤žā¤Ēā¤°ā¤žā¤šāĨā¤¯ā¤ž ā¤¸ā¤žā¤‚ā¤–āĨā¤¯ā¤ŋ⤕āĨ€", + "user_usage_stats_description": "ā¤–ā¤žā¤¤āĨā¤¯ā¤žā¤šāĨā¤¯ā¤ž ā¤ĩā¤žā¤Ēā¤°ā¤žā¤šāĨā¤¯ā¤ž ā¤¸ā¤žā¤‚ā¤–āĨā¤¯ā¤ŋ⤕āĨ€ ā¤Ēā¤šā¤ž", + "username": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤žā¤¨ā¤žā¤ĩ", + "users": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡", + "users_added_to_album_count": "⤅⤞āĨā¤Ŧā¤Žā¤Žā¤§āĨā¤¯āĨ‡ {count, plural, one {# ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ⤜āĨ‹ā¤Ąā¤˛ā¤ž} other {# ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ⤜āĨ‹ā¤Ąā¤˛āĨ‡}}", + "utilities": "⤉ā¤Ē⤝āĨā¤•āĨā¤¤ā¤¤ā¤ž", + "validate": "⤤ā¤Ēā¤žā¤¸ā¤ž", + "validate_endpoint_error": "⤕āĨƒā¤Ēā¤¯ā¤ž ā¤ĩāĨˆā¤§ URL ā¤ĒāĨā¤°ā¤ĩā¤ŋ⤎āĨā¤Ÿ ā¤•ā¤°ā¤ž", + "variables": "⤚⤞", + "version": "⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€", + "version_announcement_closing": "⤤āĨā¤Žā¤šā¤ž ā¤Žā¤ŋ⤤āĨā¤°, ā¤…â€āĨ…⤞āĨ‡ā¤•āĨā¤¸", + "version_announcement_message": "ā¤¨ā¤Žā¤¸āĨā¤•ā¤žā¤°! Immich ⤚āĨ€ ⤍ā¤ĩāĨ€ ⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤉ā¤Ē⤞ā¤ŦāĨā¤§ ā¤†ā¤šāĨ‡. ⤤āĨā¤Žā¤šāĨ€ ā¤¸ā¤‚ā¤°ā¤šā¤¨ā¤ž ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ⤆⤪ā¤ŋ ā¤Ŧā¤ŋ⤍⤚āĨ‚⤕ ā¤°ā¤žā¤šā¤žā¤ĩāĨ€ ā¤¯ā¤žā¤¸ā¤žā¤ āĨ€ ⤕āĨƒā¤Ēā¤¯ā¤ž ā¤•ā¤žā¤šāĨ€ ā¤ĩāĨ‡ā¤ŗ ā¤•ā¤žā¤ĸāĨ‚⤍ ⤰ā¤ŋ⤞āĨ€ā¤œ ⤍āĨ‹ā¤ŸāĨā¤¸ ā¤ĩā¤žā¤šā¤ž, ā¤ĩā¤ŋā¤ļāĨ‡ā¤ˇā¤¤ā¤ƒ ⤤āĨā¤ŽāĨā¤šāĨ€ WatchTower ⤕ā¤ŋ⤂ā¤ĩā¤ž ⤅ā¤ĻāĨā¤¯ā¤¯ā¤žā¤ĩ⤤ ā¤ĒāĨā¤°ā¤•āĨā¤°ā¤ŋā¤¯ā¤ž ⤏āĨā¤ĩā¤¯ā¤‚ā¤šā¤˛ā¤ŋ⤤ā¤Ē⤪āĨ‡ ā¤šā¤žā¤¤ā¤žā¤ŗā¤Ŗā¤žā¤°āĨ€ ⤕āĨ‹ā¤Ŗā¤¤āĨ€ā¤šāĨ€ ā¤ĩāĨā¤¯ā¤ĩ⤏āĨā¤Ĩā¤ž ā¤ĩā¤žā¤Ē⤰⤤ ā¤…ā¤¸ā¤žā¤˛ ⤤⤰.", + "version_history": "⤆ā¤ĩāĨƒā¤¤āĨā¤¤āĨ€ ⤇⤤ā¤ŋā¤šā¤žā¤¸", + "version_history_item": "{date} ⤰āĨ‹ā¤œāĨ€ {version} ⤏āĨā¤Ĩā¤žā¤Ēā¤ŋ⤤ ⤕āĨ‡ā¤˛āĨ€", + "video": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "video_hover_setting": "ā¤šā¤žā¤ĩ⤰ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤ĒāĨā¤˛āĨ‡ ā¤•ā¤°ā¤ž", + "video_hover_setting_description": "ā¤†ā¤¯ā¤Ÿā¤Žā¤ĩ⤰ ā¤Žā¤žā¤Šā¤¸ ⤍āĨ‡ā¤˛āĨā¤¯ā¤žā¤ĩ⤰ ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓ ā¤Ĩ⤂ā¤Ŧ⤍āĨ‡ā¤˛ ā¤ĒāĨā¤˛āĨ‡ ā¤šāĨ‹ā¤ˆā¤˛. ā¤Ē⤰āĨā¤¯ā¤žā¤¯ ā¤Ŧ⤂ā¤Ļ ā¤…ā¤¸ā¤˛ā¤ž ⤤⤰āĨ€ ā¤ĒāĨā¤˛āĨ‡ ⤚ā¤ŋ⤍āĨā¤šā¤žā¤ĩ⤰ ā¤šā¤žā¤ĩ⤰ ⤕āĨ‡ā¤˛āĨā¤¯ā¤žā¤¸ ā¤ĒāĨā¤˛āĨ‡ā¤ŦāĨ…⤕ ⤏āĨā¤°āĨ‚ ā¤•ā¤°ā¤¤ā¤ž ⤝āĨ‡ā¤ˆā¤˛.", + "videos": "ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓", + "videos_count": "{count, plural, one {# ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓} other {# ā¤ĩāĨā¤šā¤ŋā¤Ąā¤ŋ⤓}}", + "view": "ā¤Ēā¤šā¤ž", + "view_album": "⤅⤞āĨā¤Ŧā¤Ž ā¤Ēā¤šā¤ž", + "view_all": "⤏⤰āĨā¤ĩ ā¤Ēā¤šā¤ž", + "view_all_users": "⤏⤰āĨā¤ĩ ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤āĨ‡ ā¤Ēā¤šā¤ž", + "view_details": "⤤ā¤Ēā¤ļāĨ€ā¤˛ ā¤Ēā¤šā¤ž", + "view_in_timeline": "ā¤Ÿā¤žā¤‡ā¤Žā¤˛ā¤žā¤‡ā¤¨ā¤Žā¤§āĨā¤¯āĨ‡ ā¤Ēā¤šā¤ž", + "view_link": "ā¤ĻāĨā¤ĩā¤ž ā¤Ēā¤šā¤ž", + "view_links": "ā¤ĻāĨā¤ĩāĨ‡ ā¤Ēā¤šā¤ž", + "view_name": "ā¤Ēā¤šā¤ž", + "view_next_asset": "ā¤ĒāĨā¤ĸāĨ€ā¤˛ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤Ēā¤šā¤ž", + "view_previous_asset": "ā¤Žā¤žā¤—āĨ€ā¤˛ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤Ēā¤šā¤ž", + "view_qr_code": "QR ⤕āĨ‹ā¤Ą ā¤Ēā¤šā¤ž", + "view_similar_photos": "ā¤¸ā¤Žā¤žā¤¨ ā¤ĢāĨ‹ā¤ŸāĨ‹ ā¤Ēā¤šā¤ž", + "view_stack": "⤏āĨā¤ŸāĨ…⤕ ā¤Ēā¤šā¤ž", + "view_user": "ā¤ĩā¤žā¤Ē⤰⤕⤰āĨā¤¤ā¤ž ā¤Ēā¤šā¤ž", + "viewer_remove_from_stack": "⤏āĨā¤ŸāĨ…ā¤•ā¤Žā¤§āĨ‚⤍ ā¤•ā¤žā¤ĸā¤ž", + "viewer_stack_use_as_main_asset": "ā¤ŽāĨā¤–āĨā¤¯ ā¤†ā¤¯ā¤Ÿā¤Ž ā¤ŽāĨā¤šā¤ŖāĨ‚⤍ ā¤ĩā¤žā¤Ēā¤°ā¤ž", + "viewer_unstack": "⤏āĨā¤ŸāĨ…⤕ ā¤ĩāĨ‡ā¤—ā¤ŗā¤ž ā¤•ā¤°ā¤ž", + "visibility_changed": "ā¤ĻāĨƒā¤ļāĨā¤¯ā¤¤ā¤ž {count, plural, one {# ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤¸ā¤žā¤ āĨ€ ā¤Ŧā¤Ļ⤞⤞āĨ€} other {# ā¤ĩāĨā¤¯ā¤•āĨā¤¤āĨ€ā¤‚ā¤¸ā¤žā¤ āĨ€ ā¤Ŧā¤Ļ⤞⤞āĨ€}}", + "waiting": "ā¤ĒāĨā¤°ā¤¤āĨ€ā¤•āĨā¤ˇāĨ‡ā¤¤", + "warning": "⤚āĨ‡ā¤¤ā¤žā¤ĩ⤪āĨ€", + "week": "⤆⤠ā¤ĩā¤Ąā¤ž", + "welcome": "⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤†ā¤šāĨ‡", + "welcome_to_immich": "Immich ā¤Žā¤§āĨā¤¯āĨ‡ ⤆ā¤Ē⤞āĨ‡ ⤏āĨā¤ĩā¤žā¤—ā¤¤ ā¤†ā¤šāĨ‡", + "wifi_name": "ā¤ĩā¤žā¤¯-ā¤Ģā¤žā¤¯ā¤šāĨ‡ ā¤¨ā¤žā¤ĩ", + "wrong_pin_code": "⤅ā¤ĩāĨˆā¤§ ā¤Ēā¤ŋ⤍ ⤕āĨ‹ā¤Ą", + "year": "ā¤ĩ⤰āĨā¤ˇ", + "yes": "ā¤šāĨ‹", + "you_dont_have_any_shared_links": "⤆ā¤Ē⤞āĨā¤¯ā¤žā¤•ā¤ĄāĨ‡ ⤕āĨ‹ā¤Ŗā¤¤āĨ‡ā¤šāĨ€ ā¤¸ā¤žā¤Žā¤žā¤¯ā¤ŋ⤕ ā¤ĻāĨā¤ĩāĨ‡ ā¤¨ā¤žā¤šāĨ€ā¤¤", + "zoom_image": "ā¤ĒāĨā¤°ā¤¤ā¤ŋā¤Žā¤ž ā¤āĨ‚ā¤Ž ā¤•ā¤°ā¤ž" } diff --git a/i18n/ms.json b/i18n/ms.json index cfe935102e..c72b1ff688 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -14,6 +14,8 @@ "add_a_location": "Tambah lokasi", "add_a_name": "Tambah nama", "add_a_title": "Tambah tajuk", + "add_birthday": "Tambah hari jadi", + "add_endpoint": "Tambah titik akhir", "add_exclusion_pattern": "Tambahkan corak pengecualian", "add_import_path": "Tambahkan laluan import", "add_location": "Tambah lokasi", @@ -21,10 +23,13 @@ "add_partner": "Tambah rakan", "add_path": "Tambah laluan", "add_photos": "Tambah gambar", + "add_tag": "Tambah tag", "add_to": "Tambah keâ€Ļ", "add_to_album": "Tambah ke album", "add_to_album_bottom_sheet_added": "Dimasukkan ke {album}", "add_to_album_bottom_sheet_already_exists": "Sudah ada di {album}", + "add_to_albums": "Tambah pada album", + "add_to_albums_count": "Tambah pada album ({count})", "add_to_shared_album": "Tambah ke album yang dikongsi", "add_url": "Tambah URL", "added_to_archive": "Tambah ke arkib", @@ -32,17 +37,21 @@ "added_to_favorites_count": "Menambahkan {count, number} ke kegemaran", "admin": { "add_exclusion_pattern_description": "Tambahkan corak pengecualian. Globbing menggunakan *, **, dan ? disokong. Untuk mengabaikan semua fail dalam mana-mana direktori bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua fail yang berakhir dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan laluan mutlak, gunakan \"/path/to/ignore/**\".", + "admin_user": "Pengguna Pentadbir", "asset_offline_description": "Aset pustaka luaran ini tidak lagi ditemui pada cakera dan telah dialihkan ke sampah. Jika fail telah dialihkan dalam pustaka, semak garis masa anda untuk aset baharu yang sepadan. Untuk memulihkan aset ini, sila pastikan bahawa laluan fail di bawah boleh diakses oleh Immich dan mengimbas pustaka.", "authentication_settings": "Tetapan Pengesahan", "authentication_settings_description": "Urus kata laluan, OAuth dan tetapan pengesahan lain", "authentication_settings_disable_all": "Adakah anda pasti mahu melumpuhkan semua kaedah log masuk? Log masuk akan dilumpuhkan sepenuhnya.", "authentication_settings_reenable": "Untuk menghidupkan semula, guna Arahan Pelayan.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Sandar pangkalan data", - "backup_database_enable_description": "Aktifkan sandaran pangkalan data", - "backup_keep_last_amount": "Jumlah sandaran sebelumnya yang hendak disimpan", - "backup_settings": "Tetapan Sandaran", - "backup_settings_description": "Urus tetapan sandaran pangkalan data", + "backup_database": "Buat Salinan Pangkalan Data", + "backup_database_enable_description": "Dayakan salinan pangkalan data", + "backup_keep_last_amount": "Jumlah salinan pangkalan data sebelumnya untuk disimpan", + "backup_onboarding_1_description": "salinan luar tapak di awan atau di lokasi fizikal lain", + "backup_onboarding_2_description": "salinan tempatan pada peranti yang berbeza. Ini termasuk fail utama dan sandaran fail tersebut secara setempat.", + "backup_onboarding_3_description": "jumlah salinan data anda, termasuk fail asal. Ini termasuk 1 salinan luar tapak dan 2 salinan tempatan.", + "backup_settings": "Tetapan Salinan Pangkalan Data", + "backup_settings_description": "Urus tetapan salinan pangkalan data.", "cleared_jobs": "Kerja telah dibersihkan untuk: {job}", "config_set_by_file": "Konfigurasi kini ditetapkan oleh fail konfigurasi", "confirm_delete_library": "Adakah anda pasti mahu memadamkan {library}?", @@ -72,7 +81,7 @@ "image_fullsize_quality_description": "Kualiti imej bersaiz penuh dari 1-100. Lebih tinggi adalah lebih baik, tetapi menghasilkan fail yang lebih besar.", "image_fullsize_title": "Tetapan Imej bersaiz penuh", "image_prefer_embedded_preview": "Cadangkan pratonton terbenam", - "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input kepada pemprosesan imej apabila tersedia. Cara ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung pada kamera dan imej mungkin mempunyai lebih banyak artifak mampatan.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input untuk pemprosesan imej apabila tersedia. Ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung kepada kamera dan imej mungkin mengandungi lebih banyak artifak pemampatan.", "image_prefer_wide_gamut": "Cadangkan warna gamut yang luas", "image_prefer_wide_gamut_setting_description": "Gunakan Paparan P3 untuk lakaran kenit. Ini lebih baik mengekalkan kerancakan imej dengan ruang warna yang luas, tetapi imej mungkin kelihatan berbeza pada peranti lama dengan versi penyemak imbas lama. Imej sRGB disimpan sebagai sRGB untuk mengelakkan peralihan warna.", "image_preview_description": "Imej bersaiz sederhana dengan metadata yang dilucutkan, digunakan semasa melihat aset tunggal dan untuk pembelajaran mesin", @@ -102,7 +111,7 @@ "library_scanning_enable_description": "Dayakan pengimbasan perpustakaan berkala", "library_settings": "Perpustakaan Luaran", "library_settings_description": "Urus tetapan perpustakaan luaran", - "library_tasks_description": "Laksanakan tugas perpustakaan", + "library_tasks_description": "Imbas pustaka luaran untuk aset yang baru dan/atau telah diubah", "library_watching_enable_description": "Perhatikan perpustakaan luaran untuk perubahan fail", "library_watching_settings": "Perhati perpustakaan (EKSPERIMEN)", "library_watching_settings_description": "Perhati fail yang diubah secara automatik", @@ -137,7 +146,7 @@ "machine_learning_smart_search_description": "Cari imej secara semantik menggunakan pembenaman CLIP", "machine_learning_smart_search_enabled": "Dayakan carian pintar", "machine_learning_smart_search_enabled_description": "Jika ditutup, gambar-gambar tidak akan dikodkan untuk carian pintar.", - "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu sehingga satu menjawab dengan jayanya, mengikut urutan dari pertama hingga terakhir.", + "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu mengikut turutan, dari yang pertama hingga yang terakhir, sehingga salah satu memberi maklum balas yang berjaya. Pelayan yang tidak memberi maklum balas akan diabaikan sementara sehingga ia kembali dalam talian.", "manage_concurrency": "Urus Concurrency", "manage_log_settings": "Urus tetapan log", "map_dark_style": "Tema gelap", @@ -146,13 +155,15 @@ "map_gps_settings_description": "Urus Tetapan Peta & GPS (Geokod Terbalik)", "map_implications": "Ciri peta bergantung pada perkhidmatan jubin luaran (tiles.immich.cloud)", "map_light_style": "Tema terang", - "map_manage_reverse_geocoding_settings": "Urus tetapan Geocoding Songsang", + "map_manage_reverse_geocoding_settings": "Urus tetapan Penentuan Alamat Songsang", "map_reverse_geocoding": "Geokoding Sonsang", "map_reverse_geocoding_enable_description": "Dayakan pengekodan geo terbalik", "map_reverse_geocoding_settings": "Tetapan Pengekodan Geo Terbalik", "map_settings": "Peta", "map_settings_description": "Urus tetapan peta", "map_style_description": "URL ke tema peta style.json", + "memory_cleanup_job": "Pembersihan memori", + "memory_generate_job": "Penjanaan memori", "metadata_extraction_job": "Sari metadata", "metadata_extraction_job_description": "Sari maklumat metadata dari setiap aset, seperti GPS, muka-muka, dan pelaraian", "metadata_faces_import_setting": "Dayakan import muka", @@ -161,12 +172,26 @@ "metadata_settings_description": "Urus tetapan metadata", "migration_job": "Migrasi", "migration_job_description": "Pindahkan imej kecil untuk aset-aset dan muka-muka kepada struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Jalankan pengecaman wajah kepada wajah baharu yang dijumpai", + "nightly_tasks_cluster_new_faces_setting": "Kumpulan wajah baharu", + "nightly_tasks_database_cleanup_setting": "Tugasan membersihkan pangkalan data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, luput dari pangkalan data", + "nightly_tasks_generate_memories_setting": "Menjana memori", + "nightly_tasks_generate_memories_setting_description": "Mencipta memori dari aset", + "nightly_tasks_missing_thumbnails_setting": "Menjana lakaran kecil yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Aturan aset tanpa lakaran kecil untuk janaan lakaran kecil", + "nightly_tasks_settings": "Tetapan tugasan malam", + "nightly_tasks_settings_description": "Mengurus tugasan malam", + "nightly_tasks_start_time_setting": "Masa mula", + "nightly_tasks_start_time_setting_description": "Masa di mana pelayan mula bekerja pada tugasan malam", + "nightly_tasks_sync_quota_usage_setting": "Penyelarasan penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Kemaskini kuota simpanan pengguna, berdasarkan kepada penggunaan terkini", "no_paths_added": "Tiada laluan yang ditambah", "no_pattern_added": "Tiada corak ditambah", "note_apply_storage_label_previous_assets": "Nota: Untuk menggunakan Label Storan pada aset yang dimuat naik sebelum ini, jalankan", "note_cannot_be_changed_later": "NOTA: Ini tidak boleh diubah kemudian!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Immich Photo Server \"", + "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Pelayan Gambar Immich \". Pastikan menggunakan alamat yang dibenarkan anda untuk menghantar e-mel.", "notification_email_host_description": "Hos e-mel pelayan (cth. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan ralat-ralat sijil", "notification_email_ignore_certificate_errors_description": "Abaikan ralat pengesahan sijil TLS (tidak disyorkan)", @@ -186,19 +211,24 @@ "oauth_auto_register": "Daftar secara automatik", "oauth_auto_register_description": "Daftar secara automatik pengguna-pengguna baharu selepas mendaftar masuk dengan OAuth", "oauth_button_text": "Teks butang", + "oauth_client_secret_description": "Diperlukan jika PKCE (Proof Key for Code Exchange) tidak disokong oleh penyedia OAuth", "oauth_enable_description": "Log masuk dengan OAuth", "oauth_mobile_redirect_uri": "URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override": "Penggantian URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti ''{callback}''", + "oauth_role_claim": "Tebus peranan", + "oauth_role_claim_description": "Automatik memberi kebenaran pentadbir berdasarkan tuntutan ini. Tuntutan ini mungkin mempunyai sama ada 'pengguna' atau 'pentadbir'.", "oauth_settings": "OAuth", - "oauth_settings_description": "Urus tetapan-tetapan log masuk OAuth", + "oauth_settings_description": "Urus tetapan log masuk OAuth", "oauth_settings_more_details": "Untuk maklumat lanjut tentang ciri ini, rujuk ke dokumen.", "oauth_storage_label_claim": "Tuntutan label storan", "oauth_storage_label_claim_description": "Tetapkan label storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_claim": "Tuntutan kuota storan", "oauth_storage_quota_claim_description": "Tetapkan kuota storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_default": "Kuota storan lalai (GiB)", - "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan apabila tiada tuntutan disediakan (Masukkan 0 untuk kuota tanpa had).", + "oauth_storage_quota_default_description": "Kuota dalam GiB yang akan digunakan jika tiada tuntutan disediakan.", + "oauth_timeout": "Had Masa Permintaan", + "oauth_timeout_description": "Had masa untuk permintaan dalam milisaat", "password_enable_description": "Log masuk dengan e-mel dan kata laluan", "password_settings": "Kata Laluan Log Masuk", "password_settings_description": "Urus tetapan-tetapan kata laluan log masuk", @@ -207,12 +237,12 @@ "quota_size_gib": "Saiz Kuota (GiB)", "refreshing_all_libraries": "Menyegarkan semua perpustakaan", "registration": "Pendaftaran Pentadbir", - "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Admin dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", + "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Pentadbir dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", "require_password_change_on_login": "Perlukan pengguna menukar kata laluan ketika log masuk pertama", "reset_settings_to_default": "Tetapkan semula tetapan kepada lalai", "reset_settings_to_recent_saved": "Tetapkan semula tetapan kepada tetapan yang disimpan baru-baru ini", "scanning_library": "Mengimbas perpustakaan", - "search_jobs": "Cari kerjaâ€Ļ", + "search_jobs": "Cari tugasanâ€Ļ", "send_welcome_email": "Hantar e-mel alu-aluan", "server_external_domain_settings": "Domain luaran", "server_external_domain_settings_description": "Domain untuk pautan kongsi awam, termasuk http(s)://", @@ -222,7 +252,7 @@ "server_settings_description": "Urus tetapan pelayan", "server_welcome_message": "Mesej alu-aluan", "server_welcome_message_description": "Mesej yang dipaparkan pada halaman log masuk.", - "sidecar_job": "Metadata kereta sisi", + "sidecar_job": "Metadata sampingan", "sidecar_job_description": "Temui atau segerakkan metadata sampingan daripada sistem fail", "slideshow_duration_description": "Bilangan saat untuk memaparkan setiap imej", "smart_search_job_description": "Jalankan pembelajaran mesin pada aset-aset untuk menyokong carian pintar", @@ -233,9 +263,10 @@ "storage_template_hash_verification_enabled_description": "Mendayakan pengesahan hac, jangan lumpuhkan melainkan anda pasti akan implikasinya", "storage_template_migration": "Penghijrahan templat storan", "storage_template_migration_description": "Gunakan {template} semasa pada aset-aset yang dimuat naik sebelum ini", - "storage_template_migration_info": "Perubahan templat hanya akan digunakan pada aset baharu. Untuk menggunakan templat secara retroaktif pada aset-aset yang dimuat naik sebelum ini, jalankan {job}.", + "storage_template_migration_info": "Templat storan akan menukar semua sambungan fail kepada huruf kecil. Perubahan templat hanya akan digunakan untuk aset baru. Untuk menggunakan templat ini secara retroaktif pada aset yang telah dimuat naik sebelum ini, jalankan {job}.", "storage_template_migration_job": "Kerja Migrasi Templat Storan", "storage_template_more_details": "Untuk butiran lanjut tentang ciri ini, rujuk kepada Templat Storan dan implikasi", + "storage_template_onboarding_description_v2": "Apabila diaktifkan, ciri ini akan mengatur fail secara automatik berdasarkan templat yang ditetapkan oleh pengguna. Untuk maklumat lanjut, sila rujuk dokumentasi.", "storage_template_path_length": "Anggaran kepanjangan laluan: {length, number}/{limit, number}", "storage_template_settings": "Templat Storan", "storage_template_settings_description": "Urus struktur folder dan nama fail aset dimuat naik", @@ -250,7 +281,7 @@ "template_email_update_album": "Templat Kemas kini Album", "template_email_welcome": "Templat e-mel alu-aluan", "template_settings": "Templat Pemberitahuan", - "template_settings_description": "Urus templat tersuai untuk pemberitahuan.", + "template_settings_description": "Urus templat tersuai untuk notifikasi", "theme_custom_css_settings": "CSS tersuai", "theme_custom_css_settings_description": "Lembaran Gaya Lata membolehkan reka bentuk Immich disuaikan.", "theme_settings": "Tetapan Tema", @@ -282,7 +313,7 @@ "transcoding_encoding_options": "Pilihan Pengekodan", "transcoding_encoding_options_description": "Tetapkan codec, resolusi, kualiti dan pilihan lain untuk video yang dikodkan", "transcoding_hardware_acceleration": "Pecutan Perkakasan", - "transcoding_hardware_acceleration_description": "Eksperimen; lebih pantas, tetapi akan mempunyai kualiti yang lebih rendah pada kadar bit yang sama", + "transcoding_hardware_acceleration_description": "Eksperimen: pengekodan semula yang lebih pantas tetapi mungkin mengurangkan kualiti pada kadar bit yang sama", "transcoding_hardware_decoding": "Penyahkodan perkakasan", "transcoding_hardware_decoding_setting_description": "Mendayakan pecutan hujung ke hujung dan bukannya hanya mempercepatkan pengekodan. Mungkin tidak berfungsi pada semua video.", "transcoding_max_b_frames": "Bingkai-B maksimum", @@ -311,8 +342,59 @@ "transcoding_threads_description": "Nilai yang lebih tinggi membawa kepada pengekodan yang lebih pantas, tetapi meninggalkan lebih sedikit ruang untuk pemproses tugas lain semasa aktif. Nilai ini tidak boleh lebih daripada bilangan teras CPU. Memaksimumkan penggunaan jika ditetapkan kepada 0.", "transcoding_tone_mapping": "Pemetaan nada", "transcoding_tone_mapping_description": "Percubaan untuk mengekalkan penampilan video HDR apabila ditukar kepada SDR. Setiap algoritma membuat pertukaran yang berbeza untuk warna, perincian dan kecerahan. Hable mengekalkan perincian, Mobius mengekalkan warna, dan Reinhard mengekalkan kecerahan.", - "transcoding_transcode_policy": "Dasar transkod" + "transcoding_transcode_policy": "Dasar transkod", + "transcoding_transcode_policy_description": "Dasar untuk bila video perlu ditranskod. Video HDR akan sentiasa ditranskod (kecuali jika pengekodan semula dinyahdayakan).", + "transcoding_two_pass_encoding": "Pengekodan dua lelaran", + "transcoding_two_pass_encoding_setting_description": "Transkod dalam dua lelaran untuk menghasilkan video yang ditranskod dengan kualiti lebih baik. Apabila kadar bit maksimum diaktifkan (diperlukan untuk berfungsi dengan H.264 dan HEVC), mod ini akan menggunakan julat kadar bit berdasarkan kadar bit maksimum dan mengabaikan CRF. Untuk VP9, CRF boleh digunakan jika kadar bit maksimum dinyahdayakan.", + "transcoding_video_codec": "Kodek video", + "transcoding_video_codec_description": "VP9 mempunyai kecekapan tinggi dan keserasian web yang baik, tetapi mengambil masa lebih lama untuk ditranskod. HEVC memberikan prestasi yang serupa, tetapi kurang serasi dengan web. H.264 sangat serasi dan pantas untuk ditranskod, tetapi menghasilkan fail yang jauh lebih besar. AV1 ialah kodek paling cekap tetapi tidak disokong pada peranti lama.", + "trash_enabled_description": "Dayakan ciri Tong Sampah", + "trash_number_of_days": "Bilangan hari", + "trash_number_of_days_description": "Bilangan hari untuk menyimpan aset dalam tong sampah sebelum dipadam secara kekal", + "trash_settings": "Tetapan Tong Sampah", + "trash_settings_description": "Urus tetapan tong sampah", + "user_cleanup_job": "Pembersihan pengguna", + "user_delete_delay": "Akaun dan aset {user} akan dijadualkan untuk dipadam secara kekal dalam {delay, plural, one {# hari} other {# hari}}.", + "user_delete_delay_settings": "Kelewatan pemadaman", + "user_delete_delay_settings_description": "Bilangan hari selepas penghapusan sebelum akaun dan aset pengguna dipadam secara kekal. Tugasan pemadaman pengguna dijalankan pada tengah malam untuk menyemak pengguna yang sedia untuk dipadam. Perubahan pada tetapan ini akan dinilai semasa pelaksanaan seterusnya.", + "user_delete_immediately": "Akaun dan aset {user} akan dimasukkan ke dalam baris gilir untuk dipadam secara kekal serta-merta.", + "user_delete_immediately_checkbox": "Masukkan pengguna dan aset ke dalam baris gilir untuk dipadam serta-merta", + "user_details": "Butiran Pengguna", + "user_management": "Pengurusan Pengguna", + "user_password_has_been_reset": "Katalaluan pengguna telah ditetapkan semula:", + "user_password_reset_description": "Sila berikan katalaluan sementara kepada pengguna dan maklumkan bahawa mereka perlu menukar katalaluan semasa log masuk yang seterusnya.", + "user_restore_description": "Akaun {user} akan dipulihkan.", + "user_restore_scheduled_removal": "Pulihkan pengguna – pemadaman dijadualkan pada {date, date, long}", + "user_settings": "Tetapan Pengguna", + "user_settings_description": "Urus tetapan pengguna", + "user_successfully_removed": "Pengguna {email} telah berjaya dipadam.", + "version_check_enabled_description": "Dayakan semakan versi", + "version_check_implications": "Ciri semakan versi bergantung kepada komunikasi berkala dengan github.com", + "version_check_settings": "Semakan Versi", + "version_check_settings_description": "Dayakan/nyahdayakan notifikasi versi baharu", + "video_conversion_job": "Transkod video", + "video_conversion_job_description": "Transkod video untuk keserasian yang lebih luas dengan pelayar dan peranti" }, + "admin_email": "Emel Pentadbir", + "admin_password": "Kata laluan Pentadbir", + "administration": "Pentadbiran", + "advanced": "Lanjutan", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan pilihan ini untuk menapis media semasa penyegerakan berdasarkan kriteria alternatif. Hanya cuba jika anda menghadapi masalah dengan aplikasi mengesan semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan penapis penyelarasan album peranti alternatif", + "advanced_settings_log_level_title": "Tahap log: {level}", + "advanced_settings_prefer_remote_subtitle": "Sesetengah peranti sangat perlahan untuk memuatkan imej kecil daripada aset lokal. Aktifkan tetapan ini untuk memuatkan imej dari jauh sebagai gantinya.", + "advanced_settings_prefer_remote_title": "Utamakan imej jauh", + "advanced_settings_proxy_headers_subtitle": "Tentukan pengepala proksi yang perlu dihantar oleh Immich dengan setiap permintaan rangkaian", + "advanced_settings_proxy_headers_title": "Pengepala Proksi", + "advanced_settings_self_signed_ssl_subtitle": "Langkau pengesahan sijil SSL untuk titik hujung pelayan. Diperlukan untuk sijil yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Benarkan sijil SSL yang ditandatangani sendiri", + "advanced_settings_sync_remote_deletions_subtitle": "Automatik memadam atau memulihkan satu asset di peranti ini apabila tindakan itu diambil di dalam laman sesawang", + "advanced_settings_sync_remote_deletions_title": "Selaraskan pemadaman kawalan jauh [UJI KAJI]", + "advanced_settings_tile_subtitle": "Tetapan lanjutan pengguna", + "advanced_settings_troubleshooting_subtitle": "Dayakan ciri tambahan untuk menyelesaikan masalah", + "advanced_settings_troubleshooting_title": "Menyelesaikan masalah", + "age_months": "Umur {bulan, plural, satu {# bulan} lain {# bulan}}", + "age_year_months": "Umur 1 tahun, {bulan, plural, satu {# bulan} lain {# bulan}}", "deduplication_criteria_1": "Saiz imej dalam bait", "deduplication_criteria_2": "Kiraan data EXIF", "deduplication_info": "Maklumat Pendeduplikasian", @@ -361,5 +443,6 @@ "year": "Tahun", "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak mempunyai apa-apa pautan yang dikongsi", + "your_wifi_name": "Nama Wi-Fi anda", "zoom_image": "Zum Gambar" } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 3478262019..621f67e1aa 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -14,6 +14,7 @@ "add_a_location": "Legg til sted", "add_a_name": "Legg til navn", "add_a_title": "Legg til tittel", + "add_birthday": "Legg til bursdag", "add_endpoint": "API endepunkt", "add_exclusion_pattern": "Legg til ekskluderingsmønster", "add_import_path": "Legg til importsti", @@ -22,20 +23,24 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", - "add_tag": "Legg til tag", - "add_to": "Legg tilâ€Ļ", + "add_tag": "Legg til merkelapp", + "add_to": "Legg til iâ€Ļ", "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Noen lokale elementer kunne ikke legges til i albumet", + "add_to_album_toggle": "Avhuking for {album}", + "add_to_albums": "Legg til i album", + "add_to_albums_count": "Legg til i albumer ({count})", "add_to_shared_album": "Legg til delt album", "add_url": "Legg til URL", - "added_to_archive": "Lagt til i arkiv", - "added_to_favorites": "Lagt til i favoritter", + "added_to_archive": "Lagt til i arkivet", + "added_to_favorites": "Lagt til favoritter", "added_to_favorites_count": "Lagt til {count, number} i favoritter", "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For ÃĨ ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For ÃĨ ignorere alle filer som slutter pÃĨ \".tif\", bruk \"**/*.tif\". For ÃĨ ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", "admin_user": "Administrasjonsbruker", - "asset_offline_description": "Denne eksterne bibliotekressursen finnes ikke lenger pÃĨ disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, sjekk tidslinjen din for den tilsvarende ressursen. For ÃĨ gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengelig for Immich og skan biblioteket.", + "asset_offline_description": "Dette eksterne bibliotekselementet finnes ikke lenger pÃĨ disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende elementet i tidslinjen din. For ÃĨ gjenopprette elementet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", "authentication_settings_disable_all": "Er du sikker pÃĨ at du ønsker ÃĨ deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", @@ -44,16 +49,23 @@ "backup_database": "Opprett database-dump", "backup_database_enable_description": "Aktiver database-dump", "backup_keep_last_amount": "Antall database-dumps ÃĨ beholde", + "backup_onboarding_1_description": "ekstern kopi i skyen eller pÃĨ et annet fysisk sted.", + "backup_onboarding_2_description": "lokale kopier pÃĨ forskjellige enheter. Dette inkluderer hovedfilene og en lokal sikkerhetskopi av disse filene.", + "backup_onboarding_3_description": "totale kopier av dataene dine, inkludert originalfilene. Dette inkluderer Ên ekstern kopi og to lokale kopier.", + "backup_onboarding_description": "En 3-2-1 sikkerhetskopieringsstrategi anbefales for ÃĨ beskytte dataene dine. Du bør beholde kopier av opplastede bilder/videoer samt Immich-databasen for en omfattende sikkerhetskopieringsløsning.", + "backup_onboarding_footer": "For mer informasjon om sikkerhetskopiering av Immich, se dokumentasjonen.", + "backup_onboarding_parts_title": "En 3-2-1 sikkerhetskopi inkluderer:", + "backup_onboarding_title": "Sikkerhetskopier", "backup_settings": "Database-dump instillinger", "backup_settings_description": "HÃĨndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", - "confirm_delete_library": "Er du sikker pÃĨ at du vil slette biblioteket {library}?", - "confirm_delete_library_assets": "Er du sikker pÃĨ at du vil slette dette biblioteket? Dette vil slette alle {count, plural, one {# contained asset} other {all # contained assets}} tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli pÃĨ disken.", + "confirm_delete_library": "Vil du virkelig slette biblioteket {library}?", + "confirm_delete_library_assets": "Vil du virkelig slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# element} other {# elementer}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli pÃĨ disken.", "confirm_email_below": "For ÃĨ bekrefte, skriv inn \"{email}\" nedenfor", - "confirm_reprocess_all_faces": "Er du sikker pÃĨ at du vil behandle alle ansikter pÃĨ nytt? Dette vil ogsÃĨ fjerne navngitte personer.", - "confirm_user_password_reset": "Er du sikker pÃĨ at du vil tilbakestille passordet til {user}?", - "confirm_user_pin_code_reset": "Er du sikker pÃĨ at du vil resette {user}'s PIN kode?", + "confirm_reprocess_all_faces": "Vil du virkelig behandle alle ansikter pÃĨ nytt? Dette vil ogsÃĨ fjerne navngitte personer.", + "confirm_user_password_reset": "Vil du virkelig tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Vil du virkelig tilbakestille PIN-koden til {user} ?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -111,10 +123,17 @@ "library_watching_settings_description": "Se automatisk etter endrede filer", "logging_enable_description": "Aktiver logging", "logging_level_description": "Hvis aktivert, hvilket loggnivÃĨ som skal brukes.", - "logging_settings": "Logger", + "logging_settings": "Loggføring", + "machine_learning_availability_checks": "Tilgjengelighetssjekk", + "machine_learning_availability_checks_description": "Automatisk oppdag og velg tilgjengelige maskinlÃĻring-servere", + "machine_learning_availability_checks_enabled": "Aktiver tilgjengelighetssjekk", + "machine_learning_availability_checks_interval": "Sjekkintervall", + "machine_learning_availability_checks_interval_description": "Interval i millisekunder mellom tilgjengelighetssjekk", + "machine_learning_availability_checks_timeout": "Forespørselstimeout", + "machine_learning_availability_checks_timeout_description": "Tidsavbrudd i millisekunder for tilgjengelighetssjekk", "machine_learning_clip_model": "Clip-modell", "machine_learning_clip_model_description": "Navnet pÃĨ en CLIP-modell finnes her. Merk at du mÃĨ kjøre 'Smart Søk'-jobben pÃĨ nytt for alle bilder etter at du har endret modell.", - "machine_learning_duplicate_detection": "Duplikat-deteksjon", + "machine_learning_duplicate_detection": "Duplikatsøk", "machine_learning_duplicate_detection_enabled": "Aktiver duplikatdeteksjon", "machine_learning_duplicate_detection_enabled_description": "Hvis deaktivert: helt identiske filer vil fremdeles de-duplisert.", "machine_learning_duplicate_detection_setting_description": "Bruk CLIP-embeddings for ÃĨ finne sannsynlige duplikater", @@ -166,6 +185,20 @@ "metadata_settings_description": "Administrer metadatainnstillinger", "migration_job": "Migrering", "migration_job_description": "Migrer miniatyrbilder for filer og ansikter til den nyeste mappestrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kjør ansiktsgjenkjenning pÃĨ nylige oppdagede ansikter", + "nightly_tasks_cluster_new_faces_setting": "Grupper nye ansikter", + "nightly_tasks_database_cleanup_setting": "Opprydningsjobber for databasen", + "nightly_tasks_database_cleanup_setting_description": "Rydder opp i gamle, utgÃĨtte data fra databasen", + "nightly_tasks_generate_memories_setting": "Genererer minner", + "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra elementer", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniatyrbilder", + "nightly_tasks_missing_thumbnails_setting_description": "Legg til elementer i kø som mangler miniatyrbilder for generering", + "nightly_tasks_settings": "Innstillinger for nattjobber", + "nightly_tasks_settings_description": "Endre pÃĨ nattjobber", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tiden som serveren starter med nattjobbene", + "nightly_tasks_sync_quota_usage_setting": "Synkroniser kvotebruk", + "nightly_tasks_sync_quota_usage_setting_description": "Oppdater brukerkvote basert pÃĨ nÃĨvÃĻrende bruk", "no_paths_added": "Ingen filstier lagt til", "no_pattern_added": "Ingen mønster lagt til", "note_apply_storage_label_previous_assets": "Merk: For ÃĨ bruke lagringsetiketten pÃĨ tidligere opplastede filer, kjør", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobil omdirigerings-URI", "oauth_mobile_redirect_uri_override": "Mobil omdirigerings-URI overstyring", "oauth_mobile_redirect_uri_override_description": "Aktiver nÃĨr OAuth-leverandøren ikke tillater en mobil URI, som ''{callback}''", + "oauth_role_claim": "Krev Rolle", + "oauth_role_claim_description": "Gi automatisk administratortilgang basert pÃĨ tilstedevÃĻrelsen av dette kravet. Kravet kan ha enten ÂĢbrukerÂģ eller ÂĢadministratorÂģ.", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", @@ -212,7 +247,7 @@ "password_settings_description": "Administrer innstillinger for passordinnlogging", "paths_validated_successfully": "Alle filstier validert uten problemer", "person_cleanup_job": "Person opprydding", - "quota_size_gib": "Kvotestørrelse (GiB)", + "quota_size_gib": "Kvote (GiB)", "refreshing_all_libraries": "Oppdaterer alle biblioteker", "registration": "Administrator registrering", "registration_description": "Siden du er den første brukeren pÃĨ systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgaver. Du vil ogsÃĨ opprette eventuelle nye brukere.", @@ -236,12 +271,12 @@ "smart_search_job_description": "Kjør maskinlÃĻring pÃĨ filer for ÃĨ støtte smart søk", "storage_template_date_time_description": "Elementets opprettelsestidspunkt brukes for datotid-informasjonen", "storage_template_date_time_sample": "Eksempeltid {date}", - "storage_template_enable_description": "Aktiver lagringstemplatmotoren", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", "storage_template_hash_verification_enabled": "Hash verifisering aktivert", "storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker pÃĨ konsekvensene", - "storage_template_migration": "Lagringsmal migrering", + "storage_template_migration": "Implementer lagringsmal", "storage_template_migration_description": "Bruk gjeldende {template} pÃĨ tidligere opplastede bilder", - "storage_template_migration_info": "Lagringsmalen vil endre filtypen til smÃĨ bokstaver. Malendringer vil kun gjelde nye ressurser. For ÃĨ anvende malen pÃĨ tidligere opplastede ressurser, kjør {job}.", + "storage_template_migration_info": "Lagringsmalen vil endre filtypen til smÃĨ bokstaver. Malendringer vil kun gjelde nye elementer. For ÃĨ anvende malen pÃĨ tidligere opplastede elementer, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", "storage_template_onboarding_description_v2": "NÃĨr aktivert vil denne funksjonen automatisk organisere filer basert pÃĨ en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", @@ -250,19 +285,19 @@ "storage_template_settings_description": "Administrer mappestrukturen og filnavnet til opplastede fil", "storage_template_user_label": "{label} er brukerens Lagringsetikett", "system_settings": "Systeminstillinger", - "tag_cleanup_job": "Tag opprydding", + "tag_cleanup_job": "Tagg-opprydding", "template_email_available_tags": "Du kan bruke følgende variabler i din mal: {tags}", "template_email_if_empty": "Hvis malen er tom, vil standard epost bli brut.", "template_email_invite_album": "Inviter Album Mal", "template_email_preview": "ForhÃĨndsvis", - "template_email_settings": "Epost mal", + "template_email_settings": "E-postmaler", "template_email_update_album": "Oppdater Album Mal", - "template_email_welcome": "Mal for velkomst epost", + "template_email_welcome": "Mal for velkomst-e-post", "template_settings": "Varslings Mal", "template_settings_description": "Administrer tilpassede maler for varsling", "theme_custom_css_settings": "Egendefinert CSS", "theme_custom_css_settings_description": "Cascading Style Sheets gjør det mulig ÃĨ tilpasse designet av Immich.", - "theme_settings": "Tema innstillinger", + "theme_settings": "Tema-innstillinger", "theme_settings_description": "Administrer tilpasning av Immich webgrensesnitt", "thumbnail_generation_job": "Generer miniatyrbilder", "thumbnail_generation_job_description": "Generer store, smÃĨ og uskarpe miniatyrbilder for hver fil, samt miniatyrbilder for hver person", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Antall dager ÃĨ beholde filer i papirkurven før de fjernes permanent", "trash_settings": "Innstillinger for papirkurv", "trash_settings_description": "Administrer papirkurv-innstillinger", + "unlink_all_oauth_accounts": "Koble fra alle OAuth-kontoer", + "unlink_all_oauth_accounts_description": "Husk ÃĨ koble fra alle OAuth-kontoer før du migrerer til ny leverandør.", + "unlink_all_oauth_accounts_prompt": "Vil du virkelig koble fra alle OAuth-kontoer? Dette vil nullstille OAuth ID for hver bruker, og kan ikke angres.", "user_cleanup_job": "Bruker opprydning", "user_delete_delay": "{user}s konto og elementer vil legges i kø for permanent sletting om {delay, plural, one {# dag} other {# dager}}.", "user_delete_delay_settings": "Sletteforsinkelse", @@ -353,17 +391,19 @@ "video_conversion_job": "Transkod videoer", "video_conversion_job_description": "Konverter videoer for bedre kompatibilitet med nettlesere og enheter" }, - "admin_email": "Administrator E-post", - "admin_password": "Administrator Passord", + "admin_email": "Administrator e-post", + "admin_password": "Administratorpassord", "administration": "Administrasjon", "advanced": "Avansert", "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for ÃĨ filtrere mediefiler under synkronisering basert pÃĨ alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", "advanced_settings_log_level_title": "LoggnivÃĨ: {level}", - "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_subtitle": "Noen enheter er veldige trege til ÃĨ hente miniatyrbilder fra enheten. Aktiver denne innstillingen for ÃĨ hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest", "advanced_settings_proxy_headers_title": "Proxy headere", + "advanced_settings_readonly_mode_subtitle": "Aktiverer skrivebeskyttet modus der bildene bare kan vises. Ting som ÃĨ velge flere bilder, dele, caste og slette er deaktivert. Aktiver/deaktiver skrivebeskyttet modus via brukerens avatar fra hovedskjermen", + "advanced_settings_readonly_mode_title": "Skrivebeskyttet modus", "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", @@ -374,30 +414,33 @@ "age_months": "Alder {months, plural, one {# mÃĨned} other {# mÃĨneder}}", "age_year_months": "Alder 1 ÃĨr, {months, plural, one {# mÃĨned} other {# mÃĨneder}}", "age_years": "{years, plural, other {Alder #}}", - "album_added": "Album lagt til", + "album_added": "Album opprettet", "album_added_notification_setting_description": "Motta en e-postvarsling nÃĨr du legges til i et delt album", "album_cover_updated": "Albumomslag oppdatert", - "album_delete_confirmation": "Er du sikker pÃĨ at du vil slette albumet {album}?", + "album_delete_confirmation": "Vil du virkelig slette albumet {album}?", "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", + "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERT", "album_info_card_backup_album_included": "INKLUDERT", "album_info_updated": "Albuminformasjon oppdatert", "album_leave": "Forlate album?", - "album_leave_confirmation": "Er du sikker pÃĨ at du vil forlate {album}?", - "album_name": "Album Navn", + "album_leave_confirmation": "Vil du virkelig forlate {album}?", + "album_name": "Albumnavn", "album_options": "Albumalternativer", "album_remove_user": "Fjerne bruker?", - "album_remove_user_confirmation": "Er du sikker pÃĨ at du vil fjerne {user}?", - "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere ÃĨ dele det med.", + "album_remove_user_confirmation": "Vil du virkelig fjerne {user}?", + "album_search_not_found": "Ingen album ble funnet som traff ditt søk", + "album_share_no_users": "Dette albumet er allerede delt med du har delt dette albumet med alle brukere, eller du ikke har noen brukere ÃĨ dele det med.", + "album_summary": "Oppsummering av album", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling nÃĨr et delt album fÃĨr nye filer", "album_user_left": "Forlot {album}", "album_user_removed": "Fjernet {user}", - "album_viewer_appbar_delete_confirm": "Er du sikker pÃĨ at du vil slette dette albumet fra kontoen din?", + "album_viewer_appbar_delete_confirm": "Vil du virkelig slette dette albumet fra kontoen din?", "album_viewer_appbar_share_err_delete": "Kunne ikke slette albumet", "album_viewer_appbar_share_err_leave": "Kunne ikke forlate albumet", - "album_viewer_appbar_share_err_remove": "Det oppstod et problem ved fjerning av objekter fra albumet", - "album_viewer_appbar_share_err_title": "Feilet ved endring av albumtittel", + "album_viewer_appbar_share_err_remove": "Det oppstod et problem ved fjerning av elementer fra albumet", + "album_viewer_appbar_share_err_title": "Mislyktes ved endring av albumtittel", "album_viewer_appbar_share_leave": "Forlat album", "album_viewer_appbar_share_to": "Del til", "album_viewer_page_share_add_users": "Legg til brukere", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Standard sorteringsrekkefølge for albumer", "albums_default_sort_order_description": "Standard sorteringsrekkefølge for bilder nÃĨr man lager et nytt album.", "albums_feature_description": "Samlinger av bilder som kan deles med andre brukere.", + "albums_on_device_count": "Albumer pÃĨ enheten {count}", "all": "Alle", "all_albums": "Alle album", "all_people": "Alle personer", @@ -417,27 +461,29 @@ "allow_public_user_to_upload": "Tillat uautentiserte brukere ÃĨ laste opp", "alt_text_qr_code": "QR-kodebilde", "anti_clockwise": "Mot klokken", - "api_key": "API Nøkkel", + "api_key": "API-nøkkel", "api_key_description": "Denne verdien vil vises kun Ên gang. Pass pÃĨ ÃĨ kopiere den før du lukker vinduet.", "api_key_empty": "API-nøkkelnavnet bør ikke vÃĻre tomt", "api_keys": "API-nøkler", - "app_bar_signout_dialog_content": "Er du sikker pÃĨ at du vil logge ut?", + "app_bar_signout_dialog_content": "Vil du virkelig logge ut?", "app_bar_signout_dialog_ok": "Ja", "app_bar_signout_dialog_title": "Logg ut", "app_settings": "Appinstillinger", "appears_in": "Vises i", - "archive": "Arkiver", + "apply_count": "Bruk ({count, number})", + "archive": "Arkiv", + "archive_action_prompt": "{count} lagt til i arkivet", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", - "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", + "archive_page_no_archived_assets": "Ingen arkiverte elementer funnet", "archive_page_title": "Arkiv ({count})", "archive_size": "Arkivstørrelse", "archive_size_description": "Konfigurer arkivstørrelsen for nedlastinger (i GiB)", "archived": "Arkivert", "archived_count": "{count, plural, other {Arkivert #}}", "are_these_the_same_person": "Er disse samme person?", - "are_you_sure_to_do_this": "Er du sikker pÃĨ at du vil gjøre dette?", - "asset_action_delete_err_read_only": "Kan ikke slette objekt(er) med kun lese-rettighet, hopper over", - "asset_action_share_err_offline": "Kan ikke hente offline objekt(er), hopper over", + "are_you_sure_to_do_this": "Vil du virkelig gjøre dette?", + "asset_action_delete_err_read_only": "Kunne ikke slette element(er) med kun lese-rettighet, hopper over", + "asset_action_share_err_offline": "Kunne ikke hente offline element(er), hopper over", "asset_added_to_album": "Lagt til i album", "asset_adding_to_album": "Legger til i albumâ€Ļ", "asset_description_updated": "Elementbeskrivelse har blitt oppdatert", @@ -453,56 +499,64 @@ "asset_list_settings_subtitle": "Innstillinger for layout av fotorutenett", "asset_list_settings_title": "Fotorutenett", "asset_offline": "Fil utilgjengelig", - "asset_offline_description": "Dette elementet er offline. Immich kan ikke aksessere dets lokasjon. Vennlist pÃĨse at elementet er tilgijengelig og skann sÃĨ biblioteket pÃĨ nytt.", + "asset_offline_description": "Dette elementet er offline. Immich kan ikke aksessere dets lokasjon. Vennligst pÃĨse at elementet er tilgjengelig og skann sÃĨ biblioteket pÃĨ nytt.", "asset_restored_successfully": "Objekt(er) gjenopprettet", "asset_skipped": "Hoppet over", - "asset_skipped_in_trash": "I søppelbøtten", + "asset_skipped_in_trash": "I papirkurven", + "asset_trashed": "Objekt slettet", + "asset_troubleshoot": "Feilsøk element", "asset_uploaded": "Lastet opp", "asset_uploading": "Laster oppâ€Ļ", "asset_viewer_settings_subtitle": "Endre dine visningsinnstillinger for galleriet", "asset_viewer_settings_title": "Objektviser", "assets": "Filer", "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", - "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_added_to_name_count": "Lagt til {count, plural, one {# asset} other {# assets}} i {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", + "assets_added_to_album_count": "Lagt til {count, plural, one {# elementer} other {# element}} i album", + "assets_added_to_albums_count": "Lagt til {assetTotal, plural, one {# asset} other {# assets}} til {albumTotal, plural, one {# album} other {# albums}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i noen av albumene", "assets_count": "{count, plural, one {# fil} other {# filer}}", - "assets_deleted_permanently": "{count} objekt(er) slettet permanent", - "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", + "assets_deleted_permanently": "{count} element(er) slettet permanent", + "assets_deleted_permanently_from_server": "{count} element(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", - "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}", - "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", - "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", - "assets_restore_confirmation": "Er du sikker pÃĨ at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! VÃĻr oppmerksom pÃĨ at frakoblede ressurser ikke kan gjenopprettes pÃĨ denne mÃĨten.", - "assets_restored_count": "Gjenopprettet {count, plural, one {# asset} other {# assets}}", - "assets_restored_successfully": "{count} objekt(er) gjenopprettet", - "assets_trashed": "{count} objekt(er) slettet", - "assets_trashed_count": "Kastet {count, plural, one {# asset} other {# assets}}", - "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", - "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} er allerede lagt til i albumet", + "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", + "assets_moved_to_trash_count": "Flyttet {count, plural, one {# element} other {# elementer}} til søppel", + "assets_permanently_deleted_count": "Slettet {count, plural, one {# element} other {# elementer}} permanent", + "assets_removed_count": "Slettet {count, plural, one {# element} other {# elementer}}", + "assets_removed_permanently_from_device": "{count} element(er) slettet permanent fra enheten din", + "assets_restore_confirmation": "Vil du virkelig gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! VÃĻr oppmerksom pÃĨ at frakoblede ressurser ikke kan gjenopprettes pÃĨ denne mÃĨten.", + "assets_restored_count": "Gjenopprettet {count, plural, one {# element} other {# elementer}}", + "assets_restored_successfully": "{count} element(er) gjenopprettet", + "assets_trashed": "{count} element(er) slettet", + "assets_trashed_count": "Kastet {count, plural, one {# element} other {# elementer}}", + "assets_trashed_from_server": "{count} element(er) slettet fra Immich serveren", + "assets_were_part_of_album_count": "{count, plural, one {Objektet} other {Objektene}} er allerede lagt til i albumet", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} allerede inkludert i albumet", "authorized_devices": "Autoriserte enheter", "automatic_endpoint_switching_subtitle": "Koble til lokalt over angitt Wi-Fi nÃĨr det er tilgjengelig, og bruk alternative tilkoblinger andre steder", "automatic_endpoint_switching_title": "Automatisk URL bytte", "autoplay_slideshow": "Autoavspilling av lysbildefremvisning", "back": "Tilbake", "back_close_deselect": "Tilbake, lukk eller fjern merking", + "background_backup_running_error": "Bakgrunnsbackup kjører, kan ikke starte manuell backup", "background_location_permission": "Bakgrunnstillatelse for plassering", "background_location_permission_content": "For ÃĨ bytte nettverk nÃĨr du kjører i bakgrunnen, mÃĨ Immich *alltid* ha presis posisjonstilgang slik at appen kan lese Wi-Fi-nettverkets navn", + "background_options": "Bakgrunnsinnstillinger", + "backup": "Sikkerhetskopiering", "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({count})", "backup_album_selection_page_albums_tap": "Trykk for ÃĨ inkludere, dobbelttrykk for ÃĨ ekskludere", "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", "backup_album_selection_page_select_albums": "Velg album", "backup_album_selection_page_selection_info": "Valginformasjon", - "backup_album_selection_page_total_assets": "Totalt antall unike objekter", + "backup_album_selection_page_total_assets": "Totalt antall unike elementer", + "backup_albums_sync": "Synkronisering av sikkerhetskopialbum", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Sikkerhetskopiering av objekter feilet. Prøver pÃĨ nyttâ€Ļ", + "backup_background_service_backup_failed_message": "Sikkerhetskopiering av elementer feilet. Prøver pÃĨ nyttâ€Ļ", "backup_background_service_connection_failed_message": "Tilkobling til server feilet. Prøver pÃĨ nyttâ€Ļ", "backup_background_service_current_upload_notification": "Laster opp {filename}", - "backup_background_service_default_notification": "Ser etter nye objekterâ€Ļ", - "backup_background_service_error_title": "Sikkerhetskopieringsfeil", - "backup_background_service_in_progress_notification": "Sikkerhetskopierer objekterâ€Ļ", + "backup_background_service_default_notification": "Ser etter nye elementerâ€Ļ", + "backup_background_service_error_title": "Feil under sikkerhetskopiering", + "backup_background_service_in_progress_notification": "Sikkerhetskopierer elementerâ€Ļ", "backup_background_service_upload_failure_notification": "Opplasting feilet {filename}", "backup_controller_page_albums": "Sikkerhetskopier albumer", "backup_controller_page_background_app_refresh_disabled_content": "Aktiver bakgrunnsoppdatering i Innstillinger > Generelt > Bakgrunnsoppdatering for ÃĨ bruke sikkerhetskopiering i bakgrunnen.", @@ -514,20 +568,20 @@ "backup_controller_page_background_battery_info_title": "Batterioptimalisering", "backup_controller_page_background_charging": "Kun ved lading", "backup_controller_page_background_configure_error": "Konfigurering av bakgrunnstjenesten feilet", - "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye objekter: {duration}", - "backup_controller_page_background_description": "Skru pÃĨ bakgrunnstjenesten for ÃĨ automatisk sikkerhetskopiere alle nye objekter uten ÃĨ mÃĨtte ÃĨpne appen", + "backup_controller_page_background_delay": "Forsink sikkerhetskopiering av nye elementer: {duration}", + "backup_controller_page_background_description": "Skru pÃĨ bakgrunnstjenesten for ÃĨ automatisk sikkerhetskopiere alle nye elementer uten ÃĨ mÃĨtte ÃĨpne appen", "backup_controller_page_background_is_off": "Automatisk sikkerhetskopiering i bakgrunnen er deaktivert", "backup_controller_page_background_is_on": "Automatisk sikkerhetskopiering i bakgrunnen er aktivert", "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", "backup_controller_page_background_turn_on": "Skru pÃĨ bakgrunnstjenesten", "backup_controller_page_background_wifi": "Kun pÃĨ Wi-Fi", - "backup_controller_page_backup": "Sikkerhetskopier", + "backup_controller_page_backup": "Sikkerhetskopiere", "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {date}", - "backup_controller_page_desc_backup": "SlÃĨ pÃĨ sikkerhetskopiering i forgrunnen for automatisk ÃĨ laste opp nye objekter til serveren nÃĨr du ÃĨpner appen.", + "backup_controller_page_desc_backup": "SlÃĨ pÃĨ sikkerhetskopiering i forgrunnen for automatisk ÃĨ laste opp nye elementer til serveren nÃĨr du ÃĨpner appen.", "backup_controller_page_excluded": "Ekskludert: ", - "backup_controller_page_failed": "Feilet ({count})", + "backup_controller_page_failed": "Mislyktes ({count})", "backup_controller_page_filename": "Filnavn: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informasjon om sikkerhetskopi", @@ -544,14 +598,17 @@ "backup_controller_page_turn_off": "SlÃĨ av sikkerhetskopiering i forgrunnen", "backup_controller_page_turn_on": "SlÃĨ pÃĨ sikkerhetskopiering i forgrunnen", "backup_controller_page_uploading_file_info": "Laster opp filinformasjon", - "backup_err_only_album": "Kan ikke fjerne det eneste albumet", - "backup_info_card_assets": "objekter", + "backup_err_only_album": "Kunne ikke fjerne det eneste albumet", + "backup_error_sync_failed": "Synkronisering feilet. Kunne ikke fortsette sikkerhetskopiering.", + "backup_info_card_assets": "elementer", "backup_manual_cancelled": "Avbrutt", "backup_manual_in_progress": "Opplasting er allerede i gang. Prøv igjen om litt", "backup_manual_success": "Vellykket", "backup_manual_title": "Opplastingsstatus", + "backup_options": "Backup innstillinger", "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", + "backup_settings_subtitle": "HÃĨndter opplastingsinnstillinger", "backward": "Bakover", "biometric_auth_enabled": "Biometrisk autentisering aktivert", "biometric_locked_out": "Du er lÃĨst ute av biometrisk verifisering", @@ -563,15 +620,15 @@ "bugs_and_feature_requests": "Feil og funksjonsforespørsler", "build": "Bygg", "build_image": "Lag Bilde", - "bulk_delete_duplicates_confirmation": "Er du sikker pÃĨ at du vil slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", - "bulk_keep_duplicates_confirmation": "Er du sikker pÃĨ at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil løse alle dupliserte grupper uten ÃĨ slette noe.", - "bulk_trash_duplicates_confirmation": "Er du sikker pÃĨ ønsker ÃĨ slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", + "bulk_delete_duplicates_confirmation": "Vil du virkelig slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", + "bulk_keep_duplicates_confirmation": "Vil du virkelig beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten ÃĨ slette noe.", + "bulk_trash_duplicates_confirmation": "Vil du virkelig ÃĨ slette {count, plural, one {# duplisert element} other {# dupliserte elementer}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning pÃĨ appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", - "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", - "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", + "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", + "cache_settings_duplicated_assets_title": "Dupliserte elementer ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", "cache_settings_statistics_shared": "Delte albumminiatyrbilder", @@ -587,9 +644,10 @@ "cancel": "Avbryt", "cancel_search": "Avbryt søk", "canceled": "Avbrutt", - "cannot_merge_people": "Kan ikke slÃĨ sammen personer", + "canceling": "Avbryter", + "cannot_merge_people": "Kunne ikke slÃĨ sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", - "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", + "cannot_update_the_description": "Kunne ikke oppdatere beskrivelsen", "cast": "Strøm", "cast_description": "Konfigurer tilgjengelige cast-destinasjoner", "change_date": "Endre dato", @@ -599,25 +657,28 @@ "change_location": "Endre sted", "change_name": "Endre navn", "change_name_successfully": "Navneendring vellykket", - "change_password": "Endre Passord", + "change_password": "Endre passord", "change_password_description": "Dette er enten første gang du logger inn i systemet, eller det har blitt gjort en forespørsel om ÃĨ endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.", "change_password_form_confirm_password": "Bekreft passord", "change_password_form_description": "Hei {name}!\n\nDette er enten første gang du logger pÃĨ systemet, eller det er sendt en forespørsel om ÃĨ endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.", "change_password_form_new_password": "Nytt passord", "change_password_form_password_mismatch": "Passordene stemmer ikke", "change_password_form_reenter_new_password": "Skriv nytt passord igjen", - "change_pin_code": "Endre PIN kode", + "change_pin_code": "Endre PIN-kode", "change_your_password": "Endre passordet ditt", "changed_visibility_successfully": "Endret synlighet vellykket", - "check_corrupt_asset_backup": "Sjekk etter korrupte backupobjekter", + "charging": "Lading", + "charging_requirement_mobile_backup": "Bakgrunnsbackup krever at enheten lader", + "check_corrupt_asset_backup": "Sjekk etter korrupte backupelementer", "check_corrupt_asset_backup_button": "Utfør sjekk", - "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og nÃĨr alle objekter har blitt lastet opp. Denne sjekken kan ta noen minutter.", + "check_corrupt_asset_backup_description": "Kjør denne sjekken kun over Wi-Fi og nÃĨr alle elementer har blitt lastet opp. Denne sjekken kan ta noen minutter.", "check_logs": "Sjekk Logger", "choose_matching_people_to_merge": "Velg personer som skal slÃĨs sammen", "city": "By", "clear": "Tøm", "clear_all": "Tøm alt", "clear_all_recent_searches": "Fjern alle nylige søk", + "clear_file_cache": "Tøm filcache", "clear_message": "Fjern melding", "clear_value": "Fjern verdi", "client_cert_dialog_msg_confirm": "OK", @@ -643,8 +704,8 @@ "completed": "Fullført", "confirm": "Bekreft", "confirm_admin_password": "Bekreft administratorpassord", - "confirm_delete_face": "Er du sikker pÃĨ at du vil slette {name} sitt ansikt fra ativia?", - "confirm_delete_shared_link": "Er du sikker pÃĨ at du vil slette denne delte lenken?", + "confirm_delete_face": "Vil du virkelig slette {name} sitt ansikt fra ativia?", + "confirm_delete_shared_link": "Vil du virkelig slette denne delte lenken?", "confirm_keep_this_delete_others": "Alle andre ressurser i denne stabelen vil bli slettet bortsett fra denne ressursen. Er du sikker pÃĨ at du vil fortsette?", "confirm_new_pin_code": "Bekreft ny PIN kode", "confirm_password": "Bekreft passord", @@ -662,7 +723,7 @@ "control_bottom_app_bar_edit_time": "Endre Dato og tid", "control_bottom_app_bar_share_link": "Del Lenke", "control_bottom_app_bar_share_to": "Del til", - "control_bottom_app_bar_trash_from_immich": "Flytt til søppelkasse", + "control_bottom_app_bar_trash_from_immich": "Flytt til papirkurv", "copied_image_to_clipboard": "Bildet er kopiert til utklippstavlen.", "copied_to_clipboard": "Kopiert til utklippstavlen!", "copy_error": "Kopi feil", @@ -677,9 +738,9 @@ "covers": "Omslag", "create": "Opprett", "create_album": "Opprett album", - "create_album_page_untitled": "Uten navn", + "create_album_page_untitled": "Navnløst", "create_library": "Opprett Bibliotek", - "create_link": "Opprett link", + "create_link": "Opprett lenke", "create_link_to_share": "Opprett delelink", "create_link_to_share_description": "La alle med lenken se de(t) valgte bildet/bildene", "create_new": "LAG NY", @@ -688,11 +749,13 @@ "create_new_user": "Opprett ny bruker", "create_shared_album_page_share_add_assets": "LEGG TIL OBJEKTER", "create_shared_album_page_share_select_photos": "Velg bilder", - "create_tag": "Lag tag", + "create_shared_link": "Opprett delt lenke", + "create_tag": "Lag merkelapp", "create_tag_description": "Lag en ny tag. For undertag, vennligst fullfør hele stien til taggen, inkludert forovervendt skrÃĨstrek.", "create_user": "Opprett Bruker", "created": "Opprettet", "created_at": "Laget", + "creating_linked_albums": "Oppretter sammenkoblede albumer...", "crop": "BeskjÃĻr", "curated_object_page_title": "Ting", "current_device": "NÃĨvÃĻrende enhet", @@ -700,10 +763,11 @@ "current_server_address": "NÃĨvÃĻrende serveradresse", "custom_locale": "Tilpasset lokalisering", "custom_locale_description": "Formater datoer og tall basert pÃĨ sprÃĨk og region", + "custom_url": "Tilpasset URL", "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", - "darkTheme": "Aktiver mørkt utsende", + "dark_theme": "Aktiver mørk-modus", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Fødselsdatoen ble lagret vellykket", "date_range": "DatoomrÃĨde", "day": "Dag", + "days": "Dager", "deduplicate_all": "De-dupliser alle", "deduplication_criteria_1": "Bilde størrelse i bytes", "deduplication_criteria_2": "Antall av EXIF data", @@ -719,32 +784,38 @@ "default_locale": "Standard sprÃĨkinnstilling", "default_locale_description": "Formater datoer og tall basert pÃĨ nettleserens sprÃĨkinnstilling", "delete": "Slett", + "delete_action_confirmation_message": "Vil du virkelig slette dette elementet? Dette vil flytte elementet til papirkurvn og vil gi deg beskjed om du vil slette det lokalt", + "delete_action_prompt": "{count} slettet", "delete_album": "Slett album", - "delete_api_key_prompt": "Er du sikker pÃĨ at du vil slette denne API-nøkkelen?", - "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", - "delete_dialog_alert_local": "Disse objektene vil bli permanent slettet fra enheten din, men vil fortsatt vÃĻre tilgjengelige fra Immich serveren", - "delete_dialog_alert_local_non_backed_up": "Noen av objektene er ikke sikkerhetskopiert til Immich og vil bli permanent fjernet fra enheten din", - "delete_dialog_alert_remote": "Disse objektene vil bli permanent slettet fra Immich serveren", + "delete_api_key_prompt": "Vil du virkelig slette denne API-nøkkelen?", + "delete_dialog_alert": "Disse elementene vil bli slettet permanent fra Immich og fra enheten din", + "delete_dialog_alert_local": "Disse elementene vil bli permanent slettet fra enheten din, men vil fortsatt vÃĻre tilgjengelige fra Immich serveren", + "delete_dialog_alert_local_non_backed_up": "Noen av elementene er ikke sikkerhetskopiert til Immich og vil bli permanent fjernet fra enheten din", + "delete_dialog_alert_remote": "Disse elementene vil bli permanent slettet fra Immich serveren", "delete_dialog_ok_force": "Slett uansett", "delete_dialog_title": "Slett permanent", - "delete_duplicates_confirmation": "Er du sikker pÃĨ at du vil slette disse duplikatene permanent?", + "delete_duplicates_confirmation": "Vil du virkelig slette disse duplikatene permanent?", "delete_face": "Slett ansik", "delete_key": "Slett nøkkel", "delete_library": "Slett bibliotek", "delete_link": "Slett lenke", - "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", + "delete_local_action_prompt": "{count} slettet lokalt", + "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte elementer", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", + "delete_permanently": "Slett permanent", + "delete_permanently_action_prompt": "{count} slettet permanent", "delete_shared_link": "Slett delt lenke", "delete_shared_link_dialog_title": "Slett delt link", "delete_tag": "Slett tag", - "delete_tag_confirmation_prompt": "Er du sikker pÃĨ at du vil slette {tagName} tag?", + "delete_tag_confirmation_prompt": "Vil du virkelig slette {tagName} tag?", "delete_user": "Slett bruker", "deleted_shared_link": "Slettet delt lenke", "deletes_missing_assets": "Slett eiendeler som mangler fra disk", "description": "Beskrivelse", "description_input_hint_text": "Legg til beskrivelse ...", "description_input_submit_error": "Feil ved oppdatering av beskrivelse, sjekk loggen for flere detaljer", + "deselect_all": "Avmerk alle", "details": "Detaljer", "direction": "Retning", "disabled": "Deaktivert", @@ -762,6 +833,7 @@ "documentation": "Dokumentasjon", "done": "Ferdig", "download": "Last ned", + "download_action_prompt": "Laster ned {count} elementer", "download_canceled": "Nedlasting avbrutt", "download_complete": "Nedlasting fullført", "download_enqueue": "Nedlasting satt i kø", @@ -788,22 +860,27 @@ "edit": "Rediger", "edit_album": "Rediger album", "edit_avatar": "Rediger avatar", + "edit_birthday": "Rediger fødselsdag", "edit_date": "Rediger dato", "edit_date_and_time": "Rediger dato og tid", + "edit_date_and_time_action_prompt": "{count} dato og tid endret", + "edit_date_and_time_by_offset": "Endre dato med forskyvning", + "edit_date_and_time_by_offset_interval": "Nytt datointervall: {from} - {to}", "edit_description": "Endre beskrivelse", "edit_description_prompt": "Vennligst velg en ny beskrivelse:", - "edit_exclusion_pattern": "Rediger eksklusjonsmønster", + "edit_exclusion_pattern": "Rediger utelukkelsesmønster", "edit_faces": "Rediger ansikter", - "edit_import_path": "Rediger import-sti", + "edit_import_path": "Rediger importsti", "edit_import_paths": "Rediger importstier", "edit_key": "Rediger nøkkel", "edit_link": "Endre lenke", - "edit_location": "Endre lokasjon", - "edit_location_dialog_title": "Lokasjon", - "edit_name": "Redigere navn", + "edit_location": "Rediger sted", + "edit_location_action_prompt": "{count} plassering endret", + "edit_location_dialog_title": "Plassering", + "edit_name": "Rediger navn", "edit_people": "Rediger personer", - "edit_tag": "Rediger tag", - "edit_title": "Rediger Tittel", + "edit_tag": "Rediger etikett", + "edit_title": "Rediger tittel", "edit_user": "Rediger bruker", "edited": "Redigert", "editor": "Redaktør", @@ -815,8 +892,9 @@ "email_notifications": "Epostvarsler", "empty_folder": "Denne mappen er tom", "empty_trash": "Tøm papirkurv", - "empty_trash_confirmation": "Er du sikker pÃĨ at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", + "empty_trash_confirmation": "Vil du virkelig Tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", + "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Skriv inn PINkoden for ÃĨ aktivere biometrisk autentisering", "enabled": "Aktivert", "end_date": "Slutt dato", @@ -825,23 +903,25 @@ "enter_your_pin_code": "Skriv inn din PIN kode", "enter_your_pin_code_subtitle": "Skriv inn din PIN kode for ÃĨ fÃĨ tilgang til lÃĨst mappe", "error": "Feil", - "error_change_sort_album": "Feilet ved endring av sorteringsrekkefølge pÃĨ albumer", + "error_change_sort_album": "Mislyktes ved endring av sorteringsrekkefølge pÃĨ albumer", "error_delete_face": "Feil ved sletting av ansikt fra aktivia", + "error_getting_places": "Feil ved henting av steder", "error_loading_image": "Feil ved lasting av bilde", + "error_loading_partners": "Feil ved lasting av partnere: {error}", "error_saving_image": "Feil: {error}", "error_tag_face_bounding_box": "Feil ved merking av ansikt - klarte ikke ÃĨ fÃĨ koordinatene pÃĨ omrisset", "error_title": "Feil - Noe gikk galt", "errors": { - "cannot_navigate_next_asset": "Kan ikke navigere til neste fil", - "cannot_navigate_previous_asset": "Kan ikke navigere til forrige fil", - "cant_apply_changes": "Kan ikke legge til endringene", - "cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet", - "cant_change_asset_favorite": "Kan ikke endre favoritt til filen", - "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# asset} other {# assets}}", - "cant_get_faces": "Kan ikke finne ansikter", - "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", - "cant_search_people": "Kan ikke søke etter mennesker", - "cant_search_places": "Kan ikke søke etter plasser", + "cannot_navigate_next_asset": "Kunne ikke navigere til neste fil", + "cannot_navigate_previous_asset": "Kunne ikke navigere til forrige fil", + "cant_apply_changes": "Kunne ikke legge til endringene", + "cant_change_activity": "Kunne ikke {enabled, select, true {disable} other {enable}} aktivitet", + "cant_change_asset_favorite": "Kunne ikke endre favoritt til filen", + "cant_change_metadata_assets_count": "Kunne ikke endre metadata for {count, plural, one {# element} other {# elementer}}", + "cant_get_faces": "Kunne ikke finne ansikter", + "cant_get_number_of_comments": "Kunne ikke hente antall kommentarer", + "cant_search_people": "Kunne ikke søke etter mennesker", + "cant_search_places": "Kunne ikke søke etter plasser", "error_adding_assets_to_album": "Feil med ÃĨ legge til bilder til album", "error_adding_users_to_album": "Feil, kan ikke legge til brukere til album", "error_deleting_shared_user": "Feil med ÃĨ slette delt bruker", @@ -852,114 +932,114 @@ "exclusion_pattern_already_exists": "Dette eksklusjonsmønsteret eksisterer allerede.", "failed_to_create_album": "Feil med ÃĨ lage album", "failed_to_create_shared_link": "Feil med ÃĨ lage delt lenke", - "failed_to_edit_shared_link": "Feilet med ÃĨ redigere delt lenke", - "failed_to_get_people": "Feilet med ÃĨ finne mennesker", - "failed_to_keep_this_delete_others": "Feilet med ÃĨ beholde dette bilde og slette de andre", - "failed_to_load_asset": "Feilet med ÃĨ laste bilder", - "failed_to_load_assets": "Feilet med ÃĨ laste bilde", + "failed_to_edit_shared_link": "Mislyktes med ÃĨ redigere delt lenke", + "failed_to_get_people": "Mislyktes med ÃĨ finne mennesker", + "failed_to_keep_this_delete_others": "Mislyktes med ÃĨ beholde dette bilde og slette de andre", + "failed_to_load_asset": "Mislyktes med ÃĨ laste bilder", + "failed_to_load_assets": "Mislyktes med ÃĨ laste bilde", "failed_to_load_notifications": "Kunne ikke laste inn varsler", "failed_to_load_people": "Feilen med ÃĨ laste mennesker", - "failed_to_remove_product_key": "Feilet med ÃĨ ta bort produkt nøkkel", - "failed_to_stack_assets": "Feilet med ÃĨ stable bilder", - "failed_to_unstack_assets": "Feilet med ÃĨ avstable bilder", + "failed_to_remove_product_key": "Mislyktes med ÃĨ ta bort produkt nøkkel", + "failed_to_reset_pin_code": "Kunne ikke tilbakestille PIN-koden", + "failed_to_stack_assets": "Mislyktes med ÃĨ stable bilder", + "failed_to_unstack_assets": "Mislyktes med ÃĨ avstable bilder", "failed_to_update_notification_status": "Kunne ikke oppdatere varslingsstatusen", "import_path_already_exists": "Denne importstien eksisterer allerede.", "incorrect_email_or_password": "Feil epost eller passord", "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", - "quota_higher_than_disk_size": "Du har satt en kvote høyere enn diskstørrelsen", - "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", - "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", - "unable_to_add_comment": "Kan ikke legge til kommentar", - "unable_to_add_exclusion_pattern": "Kan ikke legge til eksklusjonsmønster", - "unable_to_add_import_path": "Kan ikke legge til importsti", - "unable_to_add_partners": "Kan ikke legge til partnere", - "unable_to_add_remove_archive": "Kan ikke {archived, select, true {remove asset from} other {add asset to}} arkivet", - "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {add asset to} other {remove asset from}} favoritter", - "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", - "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", - "unable_to_change_date": "Kan ikke endre dato", + "quota_higher_than_disk_size": "Du har satt kvoten større enn diskstørrelsen", + "something_went_wrong": "Noe gikk galt", + "unable_to_add_album_users": "Kunne ikke legge til brukere i albumet", + "unable_to_add_assets_to_shared_link": "Kunne ikke legge til bilder til delt lenke", + "unable_to_add_comment": "Kunne ikke legge til kommentar", + "unable_to_add_exclusion_pattern": "Kunne ikke legge til eksklusjonsmønster", + "unable_to_add_import_path": "Kunne ikke legge til importsti", + "unable_to_add_partners": "Kunne ikke legge til partnere", + "unable_to_add_remove_archive": "Kunne ikke {archived, select, true {fjerne element fra} other {flytte element til}} arkivet", + "unable_to_add_remove_favorites": "Kunne ikke {favorite, select, true {legge til element til} other {fjerne element fra}} favoritter", + "unable_to_archive_unarchive": "Kunne ikke {archived, select, true {archive} other {unarchive}}", + "unable_to_change_album_user_role": "Kunne ikke endre brukerens rolle i albumet", + "unable_to_change_date": "Kunne ikke endre dato", "unable_to_change_description": "Klarte ikke ÃĨ oppdatere beskrivelse", - "unable_to_change_favorite": "Kan ikke endre favoritt for bildet", - "unable_to_change_location": "Kan ikke endre plassering", - "unable_to_change_password": "Kan ikke endre passord", - "unable_to_change_visibility": "Kan ikke endre synlighet for {count, plural, one {# person} other {# people}}", + "unable_to_change_favorite": "Kunne ikke endre favoritt for bildet", + "unable_to_change_location": "Kunne ikke endre plassering", + "unable_to_change_password": "Kunne ikke endre passord", + "unable_to_change_visibility": "Kunne ikke endre synlighet for {count, plural, one {# person} other {# people}}", "unable_to_complete_oauth_login": "Kunne ikke fullføre OAuth innlogging", - "unable_to_connect": "Kan ikke koble til", - "unable_to_copy_to_clipboard": "Kan ikke kopiere til utklippstavlen, sørg for at du fÃĨr tilgang til siden via HTTPS", - "unable_to_create_admin_account": "Kan ikke opprette administrator bruker", - "unable_to_create_api_key": "Kan ikke opprette en ny API-nøkkel", - "unable_to_create_library": "Kan ikke opprette bibliotek", - "unable_to_create_user": "Kan ikke opprette bruker", - "unable_to_delete_album": "Kan ikke slette album", - "unable_to_delete_asset": "Kan ikke slette filen", + "unable_to_connect": "Kunne ikke koble til", + "unable_to_copy_to_clipboard": "Kunne ikke kopiere til utklippstavlen, sørg for at du fÃĨr tilgang til siden via HTTPS", + "unable_to_create_admin_account": "Kunne ikke opprette administrator bruker", + "unable_to_create_api_key": "Kunne ikke opprette en ny API-nøkkel", + "unable_to_create_library": "Kunne ikke opprette bibliotek", + "unable_to_create_user": "Kunne ikke opprette bruker", + "unable_to_delete_album": "Kunne ikke slette album", + "unable_to_delete_asset": "Kunne ikke slette filen", "unable_to_delete_assets": "Feil med ÃĨ slette bilde", - "unable_to_delete_exclusion_pattern": "Kan ikke slette eksklusjonsmønster", - "unable_to_delete_import_path": "Kan ikke slette importsti", - "unable_to_delete_shared_link": "Kan ikke slette delt lenke", - "unable_to_delete_user": "Kan ikke slette bruker", - "unable_to_download_files": "Kan ikke laste ned filer", - "unable_to_edit_exclusion_pattern": "Kan ikke redigere eksklusjonsmønster", - "unable_to_edit_import_path": "Kan ikke redigere importsti", - "unable_to_empty_trash": "Kan ikke tømme papirkurven", - "unable_to_enter_fullscreen": "Kan ikke gÃĨ inn i fullskjerm", - "unable_to_exit_fullscreen": "Kan ikke gÃĨ ut fra fullskjerm", - "unable_to_get_comments_number": "Kan ikke hente antall kommentarer", - "unable_to_get_shared_link": "Kan ikke hente delt lenke", - "unable_to_hide_person": "Kan ikke skjule person", - "unable_to_link_motion_video": "Kan ikke lenke bevegelig video", - "unable_to_link_oauth_account": "Kan ikke lenke til OAuth-konto", - "unable_to_log_out_all_devices": "Kan ikke logge ut fra alle enheter", - "unable_to_log_out_device": "Kan ikke logge ut av enhet", - "unable_to_login_with_oauth": "Kan ikke logge inn med OAuth", - "unable_to_play_video": "Kan ikke spille av video", + "unable_to_delete_exclusion_pattern": "Kunne ikke slette eksklusjonsmønster", + "unable_to_delete_import_path": "Kunne ikke slette importsti", + "unable_to_delete_shared_link": "Kunne ikke slette delt lenke", + "unable_to_delete_user": "Kunne ikke slette bruker", + "unable_to_download_files": "Kunne ikke laste ned filer", + "unable_to_edit_exclusion_pattern": "Kunne ikke redigere eksklusjonsmønster", + "unable_to_edit_import_path": "Kunne ikke redigere importsti", + "unable_to_empty_trash": "Kunne ikke Tømme papirkurven", + "unable_to_enter_fullscreen": "Kunne ikke gÃĨ inn i fullskjerm", + "unable_to_exit_fullscreen": "Kunne ikke gÃĨ ut fra fullskjerm", + "unable_to_get_comments_number": "Kunne ikke hente antall kommentarer", + "unable_to_get_shared_link": "Kunne ikke hente delt lenke", + "unable_to_hide_person": "Kunne ikke skjule person", + "unable_to_link_motion_video": "Kunne ikke lenke bevegelig video", + "unable_to_link_oauth_account": "Kunne ikke lenke til OAuth-konto", + "unable_to_log_out_all_devices": "Kunne ikke logge ut fra alle enheter", + "unable_to_log_out_device": "Kunne ikke logge ut av enhet", + "unable_to_login_with_oauth": "Kunne ikke logge inn med OAuth", + "unable_to_play_video": "Kunne ikke spille av video", "unable_to_reassign_assets_existing_person": "Kunne ikke endre bruker pÃĨ bildene til {name, select, null {an existing person} other {{name}}}", "unable_to_reassign_assets_new_person": "Kunne ikke tildele bildene til en ny person", - "unable_to_refresh_user": "Kan ikke oppdatere bruker", - "unable_to_remove_album_users": "Kan ikke fjerne brukere fra album", - "unable_to_remove_api_key": "Kan ikke fjerne API-nøkkel", + "unable_to_refresh_user": "Kunne ikke oppdatere bruker", + "unable_to_remove_album_users": "Kunne ikke fjerne brukere fra album", + "unable_to_remove_api_key": "Kunne ikke fjerne API-nøkkel", "unable_to_remove_assets_from_shared_link": "Kunne ikke fjerne bilder fra delt lenke", - "unable_to_remove_library": "Kan ikke fjerne bibliotek", - "unable_to_remove_partner": "Kan ikke fjerne partner", - "unable_to_remove_reaction": "Kan ikke fjerne reaksjon", - "unable_to_reset_password": "Kan ikke tilbakestille passord", + "unable_to_remove_library": "Kunne ikke fjerne bibliotek", + "unable_to_remove_partner": "Kunne ikke fjerne partner", + "unable_to_remove_reaction": "Kunne ikke fjerne reaksjon", + "unable_to_reset_password": "Kunne ikke tilbakestille passord", "unable_to_reset_pin_code": "Klarte ikke ÃĨ resette PIN kode", - "unable_to_resolve_duplicate": "Kan ikke løse duplikat", - "unable_to_restore_assets": "Kan ikke gjenopprette filer", - "unable_to_restore_trash": "Kan ikke gjenopprette papirkurven", - "unable_to_restore_user": "Kan ikke gjenopprette bruker", - "unable_to_save_album": "Kan ikke lagre album", - "unable_to_save_api_key": "Kan ikke lagre API-nøkkel", + "unable_to_resolve_duplicate": "Kunne ikke løse duplikat", + "unable_to_restore_assets": "Kunne ikke gjenopprette filer", + "unable_to_restore_trash": "Kunne ikke gjenopprette papirkurven", + "unable_to_restore_user": "Kunne ikke gjenopprette bruker", + "unable_to_save_album": "Kunne ikke lagre album", + "unable_to_save_api_key": "Kunne ikke lagre API-nøkkel", "unable_to_save_date_of_birth": "Kunne ikke lagre bursdag", - "unable_to_save_name": "Kan ikke lagre navn", - "unable_to_save_profile": "Kan ikke lagre profil", - "unable_to_save_settings": "Kan ikke lagre instillinger", - "unable_to_scan_libraries": "Kan ikke skanne biblioteker", - "unable_to_scan_library": "Kan ikke skanne bibliotek", + "unable_to_save_name": "Kunne ikke lagre navn", + "unable_to_save_profile": "Kunne ikke lagre profil", + "unable_to_save_settings": "Kunne ikke lagre instillinger", + "unable_to_scan_libraries": "Kunne ikke skanne biblioteker", + "unable_to_scan_library": "Kunne ikke skanne bibliotek", "unable_to_set_feature_photo": "Kunne ikke sette funksjonsbilde", - "unable_to_set_profile_picture": "Kan ikke sette profilbilde", - "unable_to_submit_job": "Kan ikke sende inn jobb", - "unable_to_trash_asset": "Kan ikke flytte filen til papirkurven", - "unable_to_unlink_account": "Kan ikke fjerne kobling til konto", + "unable_to_set_profile_picture": "Kunne ikke sette profilbilde", + "unable_to_submit_job": "Kunne ikke sende inn jobb", + "unable_to_trash_asset": "Kunne ikke flytte filen til papirkurven", + "unable_to_unlink_account": "Kunne ikke fjerne kobling til konto", "unable_to_unlink_motion_video": "Kunne ikke ta pÃĨ kobling pÃĨ bevegelig video", "unable_to_update_album_cover": "Kunne ikke oppdatere album bilde", "unable_to_update_album_info": "Kunne ikke oppdatere informasjon i album", - "unable_to_update_library": "Kan ikke oppdatere bibliotek", - "unable_to_update_location": "Kan ikke oppdatere plassering", - "unable_to_update_settings": "Kan ikke oppdatere innstillinger", - "unable_to_update_timeline_display_status": "Kan ikke oppdatere visningsstatus for tidslinje", - "unable_to_update_user": "Kan ikke oppdatere bruker", + "unable_to_update_library": "Kunne ikke oppdatere bibliotek", + "unable_to_update_location": "Kunne ikke oppdatere plassering", + "unable_to_update_settings": "Kunne ikke oppdatere innstillinger", + "unable_to_update_timeline_display_status": "Kunne ikke oppdatere visningsstatus for tidslinje", + "unable_to_update_user": "Kunne ikke oppdatere bruker", "unable_to_upload_file": "Kunne ikke laste opp fil" }, "exif": "EXIF", "exif_bottom_sheet_description": "Legg til beskrivelse ...", + "exif_bottom_sheet_description_error": "Feil ved oppdatering av beskrivelsen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", "exif_bottom_sheet_person_add_person": "Legg til navn", - "exif_bottom_sheet_person_age_months": "Alder {months} mÃĨneder", - "exif_bottom_sheet_person_age_year_months": "Alder 1 ÃĨr, {months} mÃĨneder", - "exif_bottom_sheet_person_age_years": "Alder {years}", "exit_slideshow": "Avslutt lysbildefremvisning", "expand_all": "Utvid alle", "experimental_settings_new_asset_list_subtitle": "Under utvikling", @@ -973,22 +1053,26 @@ "explorer": "Utforsker", "export": "Eksporter", "export_as_json": "Eksporter som JSON", + "export_database": "Eksporter database", + "export_database_description": "Eksporter SQLite databasen", "extension": "Utvidelse", "external": "Ekstern", "external_libraries": "Eksterne Bibliotek", "external_network": "Eksternt nettverk", "external_network_sheet_info": "NÃĨr du ikke er pÃĨ det foretrukne Wi-Fi-nettverket, vil appen koble seg til serveren via den første av URL-ene nedenfor den kan nÃĨ, fra topp til bunn", "face_unassigned": "Ikke tilordnet", - "failed": "Feilet", + "failed": "Mislyktes", "failed_to_authenticate": "Kunne ikke autentisere", - "failed_to_load_assets": "Feilet med ÃĨ laste fil", + "failed_to_load_assets": "Mislyktes med ÃĨ laste fil", "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", + "favorite_action_prompt": "{count} lagt til i favoritter", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", - "favorites_page_no_favorites": "Ingen favorittobjekter funnet", + "favorites_page_no_favorites": "Ingen favorittelementer funnet", "feature_photo_updated": "Fremhevet bilde oppdatert", "features": "Funksjoner", + "features_in_development": "Funksjoner under utvikling", "features_setting_description": "Administrerer funksjoner for appen", "file_name": "Filnavn", "file_name_or_extension": "Filnavn eller filtype", @@ -998,21 +1082,26 @@ "filter_people": "Filtrer personer", "filter_places": "Filtrer steder", "find_them_fast": "Finn dem raskt ved søking av navn", + "first": "Første", "fix_incorrect_match": "Fiks feilaktig match", "folder": "Mappe", "folder_not_found": "Fant ikke mappe", "folders": "Mapper", "folders_feature_description": "Utforsker mappe visning for bilder og videoer pÃĨ fil systemet", + "forgot_pin_code_question": "Glemt PIN-koden?", "forward": "Fremover", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Denne funksjonen laster eksterne ressurser fra Google for ÃĨ fungere.", "general": "Generelt", + "geolocation_instruction_location": "Klikk pÃĨ et element med GPS-koordinater for ÃĨ bruke posisjonen, eller velg en posisjon direkte fra kartet", "get_help": "FÃĨ Hjelp", "get_wifiname_error": "Kunne ikke hente Wi-Fi-navnet. Sørg for at du har gitt de nødvendige tillatelsene og er koblet til et Wi-Fi-nettverk", "getting_started": "Kom i gang", "go_back": "GÃĨ tilbake", "go_to_folder": "GÃĨ til mappe", "go_to_search": "GÃĨ til søk", + "gps": "GPS", + "gps_missing": "Ingen GPS", "grant_permission": "Gi tillatelse", "group_albums_by": "Grupper album etter...", "group_country": "Grupper etter land", @@ -1022,7 +1111,10 @@ "group_year": "Grupper etter ÃĨr", "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", - "has_quota": "Har kvote", + "has_quota": "Kvote", + "hash_asset": "Hash elementer", + "hashed_assets": "Hashede elementer", + "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke vÃĻre null", "header_settings_header_name_input": "Header navn", @@ -1036,25 +1128,27 @@ "hide_password": "Skjul passord", "hide_person": "Skjul person", "hide_unnamed_people": "Skjul mennesker uten navn", - "home_page_add_to_album_conflicts": "Lagt til {added} objekter til album {album}. {failed} objekter er allerede i albumet.", - "home_page_add_to_album_err_local": "Kan ikke legge til lokale objekter til album enda, hopper over", - "home_page_add_to_album_success": "Lagt til {added} objekter til album {album}.", - "home_page_album_err_partner": "Kan ikke legge til partnerobjekter i album enda, hopper over", - "home_page_archive_err_local": "Kan ikke arkivere lokale objekter enda, hopper over", - "home_page_archive_err_partner": "Kan ikke arkivere partnerobjekter, hopper over", + "home_page_add_to_album_conflicts": "Lagt til {added} elementer til album {album}. {failed} elementer er allerede i albumet.", + "home_page_add_to_album_err_local": "Kunne ikke legge til lokale elementer til album enda, hopper over", + "home_page_add_to_album_success": "Lagt til {added} elementer til album {album}.", + "home_page_album_err_partner": "Kunne ikke legge til partnerelementer i album enda, hopper over", + "home_page_archive_err_local": "Kunne ikke arkivere lokale elementer enda, hopper over", + "home_page_archive_err_partner": "Kunne ikke arkivere partnerelementer, hopper over", "home_page_building_timeline": "Genererer tidslinjen", - "home_page_delete_err_partner": "Kan ikke slette partnerobjekter, hopper over", - "home_page_delete_remote_err_local": "Lokale objekter i fjernslettingsvalgene, hopper over", - "home_page_favorite_err_local": "Kan ikke sette favoritt pÃĨ lokale objekter enda, hopper over", - "home_page_favorite_err_partner": "Kan ikke merke partnerobjekter som favoritt enda, hopper over", + "home_page_delete_err_partner": "Kunne ikke slette partnerelementer, hopper over", + "home_page_delete_remote_err_local": "Lokale elementer i fjernslettingsvalgene, hopper over", + "home_page_favorite_err_local": "Kunne ikke sette favoritt pÃĨ lokale elementer enda, hopper over", + "home_page_favorite_err_partner": "Kunne ikke merke partnerelementer som favoritt enda, hopper over", "home_page_first_time_notice": "Hvis dette er første gangen du benytter appen, velg et album (eller flere) for sikkerhetskopiering, slik at tidslinjen kan fylles med dine bilder og videoer", - "home_page_locked_error_local": "Kunne ikke flytte lokale objekter til lÃĨst mappe, hopper over", - "home_page_locked_error_partner": "Kunne ikke flytte partner objekter til lÃĨst mappe, hopper over", - "home_page_share_err_local": "Kan ikke dele lokale objekter via link, hopper over", - "home_page_upload_err_limit": "Maksimalt 30 objekter kan lastes opp om gangen, hopper over", + "home_page_locked_error_local": "Kunne ikke flytte lokale elementer til lÃĨst mappe, hopper over", + "home_page_locked_error_partner": "Kunne ikke flytte partner elementer til lÃĨst mappe, hopper over", + "home_page_share_err_local": "Kunne ikke dele lokale elementer via link, hopper over", + "home_page_upload_err_limit": "Maksimalt 30 elementer kan lastes opp om gangen, hopper over", "host": "Vert", "hour": "Time", + "hours": "Timer", "id": "ID", + "idle": "Uvirksom", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret pÃĨ iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1105,17 +1199,20 @@ "keep": "Behold", "keep_all": "Behold alle", "keep_this_delete_others": "Behold denne, slett de andre", - "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# asset} other {# assets}}", + "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# element} other {# elementer}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "SprÃĨk", "language_no_results_subtitle": "Prøv ÃĨ endre søkeord", "language_no_results_title": "Ingen sprÃĨk funnet", "language_search_hint": "Søker etter sprÃĨk...", - "language_setting_description": "Velg ditt foretrukket sprÃĨk", + "language_setting_description": "Velg ditt foretrukne sprÃĨk", + "large_files": "Store Filer", + "last": "Siste", "last_seen": "Sist sett", "latest_version": "Siste versjon", "latitude": "Breddegrad", "leave": "Forlat", + "leave_album": "Forlat album", "lens_model": "Objektiv", "let_others_respond": "La andre respondere", "level": "NivÃĨ", @@ -1123,20 +1220,24 @@ "library_options": "Bibliotekalternativer", "library_page_device_albums": "Albumer pÃĨ enheten", "library_page_new_album": "Nytt album", - "library_page_sort_asset_count": "Antall objekter", + "library_page_sort_asset_count": "Antall elementer", "library_page_sort_created": "Nylig opplastet", "library_page_sort_last_modified": "Sist endret", "library_page_sort_title": "Albumtittel", + "licenses": "Lisenser", "light": "Lys", + "like": "Lik", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", - "link_options": "Lenkealternativer", "link_to_oauth": "Lenke til OAuth", "linked_oauth_account": "Lenket til OAuth-konto", "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke ÃĨ laste inn søkeresultater", - "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", + "local": "Lokal", + "local_asset_cast_failed": "Kunne ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale elementer", + "local_media_summary": "Oppsummering av lokale media", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en nÃĨr du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Skriv inn lengdegrad her", "lock": "LÃĨs", "locked_folder": "LÃĨst mappe", + "log_detail_title": "Loggdetaljer", "log_out": "Logg ut", "log_out_all_devices": "Logg ut fra alle enheter", "logged_in_as": "Logget inn som {user}", @@ -1172,12 +1274,13 @@ "login_form_password_hint": "passord", "login_form_save_login": "Forbli innlogget", "login_form_server_empty": "Skriv inn en server-URL.", - "login_form_server_error": "Kan ikke koble til server.", - "login_has_been_disabled": "Login har blitt deaktivert.", + "login_form_server_error": "Kunne ikke koble til server.", + "login_has_been_disabled": "Innlogging har blitt deaktivert.", "login_password_changed_error": "Det skjedde en feil ved oppdatering av passordet", "login_password_changed_success": "Passord oppdatert", - "logout_all_device_confirmation": "Er du sikker pÃĨ at du vil logge ut av alle enheter?", - "logout_this_device_confirmation": "Er du sikker pÃĨ at du vil logge ut av denne enheten?", + "logout_all_device_confirmation": "Vil du virkelig logge ut av alle enheter?", + "logout_this_device_confirmation": "Vil du virkelig logge ut av denne enheten?", + "logs": "Logger", "longitude": "Lengdegrad", "look": "Se", "loop_videos": "Gjenta Videoer", @@ -1185,6 +1288,7 @@ "main_branch_warning": "Du bruker en utviklingsversjon; vi anbefaler pÃĨ det sterkeste og bruke en utgitt versjon!", "main_menu": "Hovedmeny", "make": "Merke", + "manage_geolocation": "Administrer plassering", "manage_shared_links": "HÃĨndter delte linker", "manage_sharing_with_partners": "Administrer deling med partnere", "manage_the_app_settings": "Administrer appinnstillingene", @@ -1193,23 +1297,21 @@ "manage_your_devices": "Administrer dine innloggede enheter", "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", - "map_assets_in_bound": "{count} bilde", - "map_assets_in_bounds": "{count} bilder", - "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", + "map_assets_in_bounds": "{count, plural, =0 {Ingen bilder i dette omrÃĨdet} one {# photo} other {# photos}}", + "map_cannot_get_user_location": "Kunne ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", - "map_location_service_disabled_content": "Lokasjonstjeneste mÃĨ vÃĻre aktivert for ÃĨ vise objekter fra din nÃĨvÃĻrende lokasjon. Vil du aktivere det nÃĨ?", + "map_location_service_disabled_content": "Lokasjonstjeneste mÃĨ vÃĻre aktivert for ÃĨ vise elementer fra din nÃĨvÃĻrende lokasjon. Vil du aktivere det nÃĨ?", "map_location_service_disabled_title": "Lokasjonstjeneste deaktivert", "map_marker_for_images": "Kart makeringer for bilder tatt i {city}, {country}", "map_marker_with_image": "Kartmarkør med bilde", - "map_no_assets_in_bounds": "Ingen bilder i dette omrÃĨdet", - "map_no_location_permission_content": "Lokasjonstilgang er pÃĨkrevet for ÃĨ vise objekter fra din nÃĨvÃĻrende lokasjon. Vil du tillate det nÃĨ?", + "map_no_location_permission_content": "Lokasjonstilgang er pÃĨkrevet for ÃĨ vise elementer fra din nÃĨvÃĻrende lokasjon. Vil du tillate det nÃĨ?", "map_no_location_permission_title": "Lokasjonstilgang avvist", "map_settings": "Kartinnstillinger", "map_settings_dark_mode": "Mørk modus", "map_settings_date_range_option_day": "Siste 24 timer", "map_settings_date_range_option_days": "Siste {days} dager", - "map_settings_date_range_option_year": "Sist ÃĨr", + "map_settings_date_range_option_year": "Siste ÃĨr", "map_settings_date_range_option_years": "Siste {years} ÃĨr", "map_settings_dialog_title": "Kartinnstillinger", "map_settings_include_show_archived": "Inkluder arkiverte", @@ -1221,13 +1323,14 @@ "mark_as_read": "Merk som lest", "marked_all_as_read": "Merket alle som lest", "matches": "Samsvarende", + "matching_assets": "Matchende elementer", "media_type": "Mediatype", "memories": "Minner", "memories_all_caught_up": "Alt utført", "memories_check_back_tomorrow": "Sjekk igjen i morgen for flere minner", "memories_setting_description": "Administrer hva du ser i minnene dine", "memories_start_over": "Start pÃĨ nytt", - "memories_swipe_to_close": "Swipe opp for ÃĨ lukke", + "memories_swipe_to_close": "Sveip opp for ÃĨ lukke", "memory": "Minne", "memory_lane_title": "Minnefelt {title}", "menu": "Meny", @@ -1235,10 +1338,11 @@ "merge_people": "SlÃĨ sammen personer", "merge_people_limit": "Du kan bare slÃĨ sammen opp til 5 fjes om gangen", "merge_people_prompt": "Vil du slÃĨ sammen disse personene? Denne handlingen kan ikke reverseres.", - "merge_people_successfully": "Personene ble vellykket slÃĨtt sammen", + "merge_people_successfully": "SammenslÃĨing av personer var vellykket", "merged_people_count": "SammenslÃĨtt {count, plural, one {# person} other {# people}}", "minimize": "Minimer", "minute": "Minutt", + "minutes": "Minutter", "missing": "Mangler", "model": "Modell", "month": "MÃĨned", @@ -1246,26 +1350,32 @@ "more": "Mer", "move": "Flytt", "move_off_locked_folder": "Flytt ut av lÃĨst mappe", + "move_to_lock_folder_action_prompt": "{count} lagt til i lÃĨst mappe", "move_to_locked_folder": "Flytt til lÃĨst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den lÃĨste mappen", - "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", - "moved_to_library": "Flyttet {count, plural, one {# asset} other {# assets}} til biblioteket", + "moved_to_archive": "Flyttet {count, plural, one {# element} other {# elementer}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# element} other {# elementer}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", - "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato pÃĨ objekt(er) med kun lese-rettigheter, hopper over", - "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon pÃĨ objekt(er) med kun lese-rettigheter, hopper over", + "multiselect_grid_edit_date_time_err_read_only": "Kunne ikke endre dato pÃĨ element(er) med kun lese-rettigheter, hopper over", + "multiselect_grid_edit_gps_err_read_only": "Kunne ikke endre lokasjon pÃĨ element(er) med kun lese-rettigheter, hopper over", "mute_memories": "Demp minner", "my_albums": "Mine album", "name": "Navn", "name_or_nickname": "Navn eller kallenavn", + "network_requirement_photos_upload": "Bruk mobildata for backup av bilder", + "network_requirement_videos_upload": "Bruk mobildata for backup av videoer", + "network_requirements": "Nettverkskrav", + "network_requirements_updated": "Nettverkskrav endret, resetter backupkø", "networking_settings": "Nettverk", - "networking_subtitle": "Administrer serverendepunktinnstillingene", + "networking_subtitle": "Administrer serverendepunkt-innstillinger", "never": "aldri", "new_album": "Nytt Album", "new_api_key": "Ny API-nøkkel", "new_password": "Nytt passord", "new_person": "Ny person", - "new_pin_code": "Ny PIN kode", - "new_pin_code_subtitle": "Dette er første gang du ÃĨpner den lÃĨste mappen. Lag en PIN kode for ÃĨ sikre tilgangen til denne siden", + "new_pin_code": "Ny PIN-kode", + "new_pin_code_subtitle": "Dette er første gang du ÃĨpner den lÃĨste mappen. Lag en PIN-kode for ÃĨ sikre tilgangen til denne siden", + "new_timeline": "Ny tidslinje", "new_user_created": "Ny bruker opprettet", "new_version_available": "NY VERSJON TILGJENGELIG", "newest_first": "Nyeste først", @@ -1277,22 +1387,28 @@ "no_albums_yet": "Det ser ut som om du ikke har noen album enda.", "no_archived_assets_message": "Arkiver bilder og videoer for ÃĨ skjule dem fra visningen av bildene dine", "no_assets_message": "KLIKK FOR Å LASTE OPP DITT FØRSTE BILDE", - "no_assets_to_show": "Ingen objekter ÃĨ vise", + "no_assets_to_show": "Ingen elementer ÃĨ vise", "no_cast_devices_found": "Ingen caste-enheter oppdaget", + "no_checksum_local": "Ingen sjekksum tilgjengelig - Kunne ikke hente lokale elementer", + "no_checksum_remote": "Ingen sjekksum tilgjengelig - Kunne ikke hente eksterne elementer", "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for ÃĨ utforske samlingen din.", - "no_favorites_message": "Legg til favoritter for ÃĨ raskt finne dine beste bilder og videoer", + "no_favorites_message": "Legg til favoritter for ÃĨ finne dine beste bilder og videoer raskt", "no_libraries_message": "Opprett et eksternt bibliotek for ÃĨ se bildene og videoene dine", + "no_local_assets_found": "Ingen lokale elementer funnet med denne sjekksummen", "no_locked_photos_message": "Bilder og videoer i den lÃĨste mappen er skjult og vil ikke vises nÃĨr du blar i biblioteket.", "no_name": "Ingen navn", "no_notifications": "Ingen varsler", "no_people_found": "Ingen samsvarende personer funnet", "no_places": "Ingen steder", + "no_remote_assets_found": "Ingen eksterne elementer funnet med denne sjekksummen", "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for ÃĨ dele bilder og videoer med personer i nettverket ditt", - "not_in_any_album": "Ikke i noen album", + "no_uploads_in_progress": "Ingen opplasting pÃĨgÃĨr", + "not_available": "Ikke tilgjengelig", + "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For ÃĨ bruke lagringsetiketten pÃĨ tidligere opplastede filer, kjør", "notes": "Notater", @@ -1305,8 +1421,9 @@ "notifications": "Notifikasjoner", "notifications_setting_description": "Administrer varsler", "oauth": "OAuth", - "official_immich_resources": "Offisielle Immich Resurser", + "official_immich_resources": "Offisielle Immich-ressurser", "offline": "Frakoblet", + "offset": "Forskyving", "ok": "Ok", "oldest_first": "Eldste først", "on_this_device": "PÃĨ denne enheten", @@ -1315,7 +1432,7 @@ "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjenester, og kan bli deaktivert nÃĨr som helst under innstillinger.", "onboarding_server_welcome_description": "La oss sette opp din instans med noen standard innstillinger.", "onboarding_theme_description": "Velg et fargetema for din bruker. Du kan endre denne senere under dine instillinger.", - "onboarding_user_welcome_description": "La oss fÃĨ deg startet!", + "onboarding_user_welcome_description": "La oss fÃĨ deg i gang!", "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Åpne søkefiltrene", "options": "Valg", "or": "eller", + "organize_into_albums": "Organiser til albumer", + "organize_into_albums_description": "Plasser eksisterende bilder i albumer ved ÃĨ bruke synkroniseringsinnstillinger", "organize_your_library": "Organiser biblioteket ditt", "original": "original", "other": "Annet", "other_devices": "Andre enheter", + "other_entities": "Andre elementer", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1368,10 +1488,10 @@ "permanent_deletion_warning": "Advarsel om permanent sletting", "permanent_deletion_warning_setting_description": "Vis en advarsel ved permanent sletting av filer", "permanently_delete": "Slett permanent", - "permanently_delete_assets_count": "Permanent slett {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Er du sikker pÃĨ at du vil permanent slette {count, plural, one {this asset?} other {these # assets?}} Dette vil ogsÃĨ slette {count, plural, one {it from its} other {them from their}} album.", + "permanently_delete_assets_count": "Slett {count, plural, one {element} other {elementer}} permanent", + "permanently_delete_assets_prompt": "Vil du virkelig permanent slette {count, plural, one {dette elementet?} other {disse # elementene?}} Dette vil ogsÃĨ slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", "permanently_deleted_asset": "Filen har blitt permanent slettet", - "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# asset} other {# assets}}", + "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# element} other {# elementer}}", "permission": "Tillatelse", "permission_empty": "Dine tillatelser burde ikke vÃĻre tomme", "permission_onboarding_back": "Tilbake", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Begrenset tilgang. For ÃĨ la Immich sikkerhetskopiere og hÃĨndtere galleriet, tillatt bilde- og video-tilgang i Innstillinger.", "permission_onboarding_request": "Immich trenger tilgang til ÃĨ se dine bilder og videoer.", "person": "Person", + "person_age_months": "{months, plural, one {# month} other {# months}} gammel", + "person_age_year_months": "1 ÃĨr, {months, plural, one {# month} other {# months}} gammel", + "person_age_years": "{years, plural, other {# years}} gammel", "person_birthdate": "Født den {date}", "person_hidden": "{name}{hidden, select, true { (skjult)} other {}}", "photo_shared_all_users": "Det ser ut som om du deler bildene med alle brukere eller det er ingen brukere ÃĨ dele med.", @@ -1406,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Administrer appens preferanser", "preferences_settings_title": "Innstillinger", + "preparing": "Forbereder", "preset": "ForhÃĨndsinstilling", "preview": "ForhÃĨndsvis", "previous": "Forrige", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Mobilapp er utdatert. Vennligst oppdater til nyeste versjon.", "profile_drawer_client_server_up_to_date": "Klient og server er oppdatert", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Skrivebeskyttet modus er aktivert. Langttrykk pÃĨ brukerens avatarikon for ÃĨ avslutte.", "profile_drawer_server_out_of_date_major": "Server er utdatert. Vennligst oppdater til nyeste versjon.", "profile_drawer_server_out_of_date_minor": "Server er utdatert. Vennligst oppdater til nyeste versjon.", "profile_image_of_user": "Profil bilde av {user}", @@ -1439,7 +1564,7 @@ "purchase_button_reminder": "PÃĨminn meg om 30 dager", "purchase_button_remove_key": "Ta bort produktnøkkel", "purchase_button_select": "Velg", - "purchase_failed_activation": "Feilet med ÃĨ aktivere! Vennligst sjekk eposten for riktig produktnøkkel!", + "purchase_failed_activation": "Mislyktes med ÃĨ aktivere! Vennligst sjekk eposten for riktig produktnøkkel!", "purchase_individual_description_1": "For en person", "purchase_individual_description_2": "Støttespiller status", "purchase_individual_title": "Individuell", @@ -1453,28 +1578,33 @@ "purchase_per_server": "For hver server", "purchase_per_user": "For hver bruker", "purchase_remove_product_key": "Ta bor Produktnøkkel", - "purchase_remove_product_key_prompt": "Er du sikker pÃĨ at du vil ta bort produktnøkkelen?", + "purchase_remove_product_key_prompt": "Vil du virkelig ta bort produktnøkkelen?", "purchase_remove_server_product_key": "Ta bort Server Produktnøkkel", - "purchase_remove_server_product_key_prompt": "Er du sikker pÃĨ at du vil ta bort Server Produktnøkkelen?", + "purchase_remove_server_product_key_prompt": "Vil du virkelig ta bort Server Produktnøkkelen?", "purchase_server_description_1": "For hele serveren", "purchase_server_description_2": "Støttespiller status", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", + "query_asset_id": "Forespør elementID", + "queue_status": "Kø {count}/{total}", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", "rating_count": "{count, plural, one {# sjerne} other {# stjerner}}", "rating_description": "Hvis EXIF vurdering i informasjons panelet", "reaction_options": "Reaksjonsalternativer", "read_changelog": "Les endringslogg", + "readonly_mode_disabled": "Skrivebeskyttet modus deaktivert", + "readonly_mode_enabled": "Skrivebeskyttet modus aktivert", + "ready_for_upload": "Klar for opplasting", "reassign": "Tilordne pÃĨ nytt", - "reassigned_assets_to_existing_person": "Tildelt pÃĨ nytt {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", - "reassigned_assets_to_new_person": "Tildelt pÃĨ nytt {count, plural, one {# asset} other {# assets}} til en ny person", + "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# element} other {# elementer}} to {name, select, null {en eksisterende person} other {{name}}}", + "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# element} other {# elementer}} til en ny person", "reassing_hint": "Tilordne valgte eiendeler til en eksisterende person", "recent": "Nylig", "recent-albums": "Nylige album", "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", - "recently_added_page_title": "Nylig lagt til", + "recently_added_page_title": "Nylig oppført", "recently_taken": "Nylig tatt", "recently_taken_page_title": "Nylig Tatt", "refresh": "Oppdater", @@ -1488,16 +1618,21 @@ "refreshing_faces": "Oppdaterer ansikter", "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", + "remote": "Eksternt", + "remote_assets": "Eksterne elementer", + "remote_media_summary": "Oppsummering av eksterne media", "remove": "Fjern", - "remove_assets_album_confirmation": "Er du sikker pÃĨ at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", - "remove_assets_shared_link_confirmation": "Er du sikker pÃĨ at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", + "remove_assets_album_confirmation": "Er du sikker pÃĨ at du fil slette {count, plural, one {# element} other {# elementer}} fra albumet?", + "remove_assets_shared_link_confirmation": "Vil du virkelig slette {count, plural, one {# element} other {# elementer}} fra den delte lenken?", "remove_assets_title": "Vil du fjerne eiendeler?", "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", "remove_from_album": "Fjern fra album", + "remove_from_album_action_prompt": "{count} fjernet fra albumet", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_lock_folder_action_prompt": "{count} fjernet fra lÃĨst mappe", "remove_from_locked_folder": "Fjern fra lÃĨst mappe", - "remove_from_locked_folder_confirmation": "Er du sikker pÃĨ at du vil flytte disse bildene og videoene ut av den lÃĨste mappen? De vil bli synlige i biblioteket.", + "remove_from_locked_folder_confirmation": "Vil du virkelig flytte disse bildene og videoene ut av den lÃĨste mappen? De vil bli synlige i biblioteket.", "remove_from_shared_link": "Fjern fra delt lenke", "remove_memory": "Slett minne", "remove_photo_from_memory": "Slett bilde fra dette minne", @@ -1510,7 +1645,7 @@ "removed_from_favorites_count": "{count, plural, other {Removed #}} fra favoritter", "removed_memory": "Slettet minne", "removed_photo_from_memory": "Slettet bilde fra minne", - "removed_tagged_assets": "Fjern tag fra {count, plural, one {# asset} other {# assets}}", + "removed_tagged_assets": "Fjern tag fra {count, plural, one {# element} other {# elementer}}", "rename": "Gi nytt navn", "repair": "Reparer", "repair_no_results_message": "Usporrede og savnede filer vil vises her", @@ -1523,19 +1658,29 @@ "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", "reset_pin_code": "Resett PINkode", + "reset_pin_code_description": "Hvis du har glemt PIN-koden din, kan du kontakte serveradministratoren for ÃĨ fÃĨ den tilbakestilt", + "reset_pin_code_success": "PIN-koden er tilbakestilt", + "reset_pin_code_with_password": "Du kan alltid tilbakestiller PIN-koden med passordet ditt", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Vil du virkelig resette SQLite databasen? Du blir nødt til ÃĨ logge ut og inn igjen for ÃĨ resynkronisere data", + "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", + "restore_trash_action_prompt": "{count} gjenopprettet fra papirkurven", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", + "resume_paused_jobs": "Fortsett {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "Prøv opplasting pÃĨ nytt", "review_duplicates": "GjennomgÃĨ duplikater", + "review_large_files": "Se gjennom store filer", "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", + "running": "Kjører", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", "saved_api_key": "Lagret API-nøkkel", @@ -1580,7 +1725,7 @@ "search_options": "Søke alternativer", "search_page_categories": "Kategorier", "search_page_motion_photos": "Bevegelige bilder", - "search_page_no_objects": "Ingen objektinfo tilgjengelig", + "search_page_no_objects": "Ingen elementinfo tilgjengelig", "search_page_no_places": "Ingen stedsinformasjon er tilgjengelig", "search_page_screenshots": "Skjermbilder", "search_page_search_photos_videos": "Søk etter dine bilder og videoer", @@ -1619,9 +1764,10 @@ "select_person_to_tag": "Velg en person ÃĨ tagge", "select_photos": "Velg bilder", "select_trash_all": "Velg ÃĨ flytte alt til papirkurven", - "select_user_for_sharing_page_err_album": "Feilet ved oppretting av album", + "select_user_for_sharing_page_err_album": "Mislyktes ved oppretting av album", "selected": "Valgt", "selected_count": "{count, plural, other {# valgt}}", + "selected_gps_coordinates": "Valgte GPS-koordinater", "send_message": "Send melding", "send_welcome_email": "Send velkomstmelding", "server_endpoint": "Server endepunkt", @@ -1630,7 +1776,7 @@ "server_offline": "Server frakoblet", "server_online": "Server tilkoblet", "server_privacy": "Server personvern", - "server_stats": "Server Statistikk", + "server_stats": "Serverstatistikk", "server_version": "Server Versjon", "set": "Sett", "set_as_album_cover": "Sett som albumomslag", @@ -1654,10 +1800,10 @@ "setting_notifications_notify_minutes": "{count} minutter", "setting_notifications_notify_never": "aldri", "setting_notifications_notify_seconds": "{count} sekunder", - "setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon per objekt", + "setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon per element", "setting_notifications_single_progress_title": "Vis detaljert status pÃĨ sikkerhetskopiering i bakgrunnen", "setting_notifications_subtitle": "Juster notifikasjonsinnstillinger", - "setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt objekter)", + "setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt elementer)", "setting_notifications_total_progress_title": "Vis status pÃĨ sikkerhetskopiering i bakgrunnen", "setting_video_viewer_looping_title": "Looping", "setting_video_viewer_original_video_subtitle": "NÃĨr det streames en video fra serveren, spill originalkvaliteten selv om en omkodet versjon finnes. Dette kan medføre buffring. Videoer som er lagret lokalt pÃĨ enheten spilles i originalkvalitet uavhengig av denne innstillingen.", @@ -1667,6 +1813,7 @@ "settings_saved": "Innstillinger lagret", "setup_pin_code": "Sett opp en PINkode", "share": "Del", + "share_action_prompt": "Delte {count} elementer", "share_add_photos": "Legg til bilder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Kopiert til utklippslisten", "shared_link_clipboard_text": "Link: {link}\nPassord: {password}", "shared_link_create_error": "Feil ved oppretting av delbar link", + "shared_link_custom_url_description": "FÃĨ tilgang til denne delte lenken med en egendefinert URL", "shared_link_edit_description_hint": "Endre delebeskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dager", @@ -1699,7 +1847,7 @@ "shared_link_edit_expire_after_option_year": "{count} ÃĨr", "shared_link_edit_password_hint": "Skriv inn dele-passord", "shared_link_edit_submit_button": "Oppdater link", - "shared_link_error_server_url_fetch": "Kan ikke hente server-url", + "shared_link_error_server_url_fetch": "Kunne ikke hente server-url", "shared_link_expires_day": "UtgÃĨr om {count} dag", "shared_link_expires_days": "UtgÃĨr om {count} dager", "shared_link_expires_hour": "UtgÃĨr om {count} time", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "HÃĨndter delte linker", "shared_link_options": "Alternativer for delte lenke", + "shared_link_password_description": "Krev et passord for ÃĨ fÃĨ tilgang til denne delte lenken", "shared_links": "Delte linker", "shared_links_description": "Del bilder og videoer med lenke", "shared_photos_and_videos_count": "{assetCount, plural, other {# delte bilder og videoer.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Vis overgang til lysbildefremvisning", "show_supporter_badge": "Supportermerke", "show_supporter_badge_description": "Vis et supportermerke", + "show_text_search_menu": "Vis tekstsøk meny", "shuffle": "Bland", "sidebar": "Sidefelt", "sidebar_display_description": "Vis en lenke for visningen i sidefeltet", @@ -1762,19 +1912,22 @@ "sort_created": "Dato opprettet", "sort_items": "Antall enheter", "sort_modified": "Dato modifisert", + "sort_newest": "Nyeste bilde", "sort_oldest": "Eldste bilde", "sort_people_by_similarity": "Sorter folk etter likhet", "sort_recent": "Nyeste bilde", "sort_title": "Tittel", "source": "Kilde", "stack": "Stable", + "stack_action_prompt": "{count} stakket", "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", - "stacked_assets_count": "Stable {count, plural, one {# asset} other {# assets}}", + "stacked_assets_count": "Stable {count, plural, one {# element} other {# elementer}}", "stacktrace": "Stakkspor", "start": "Start", "start_date": "Startdato", + "start_date_before_end_date": "Startdato mÃĨ vÃĻre før sluttdato", "state": "Fylke", "status": "Status", "stop_casting": "Stopp casting", @@ -1787,6 +1940,7 @@ "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", + "success": "Vellykket", "suggestions": "Forslag", "sunrise_on_the_beach": "Soloppgang pÃĨ stranden", "support": "Støtte", @@ -1796,6 +1950,10 @@ "sync": "Synkroniser", "sync_albums": "Synkroniser albumer", "sync_albums_manual_subtitle": "Synkroniser alle opplastede videoer og bilder til det valgte backupalbumet", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", + "sync_status": "Synkroniseringsstatus", + "sync_status_subtitle": "Vis og hÃĨndter synkronisering", "sync_upload_album_setting_subtitle": "Opprett og last opp dine bilder og videoer til det valgte albumet pÃĨ Immich", "tag": "Tagg", "tag_assets": "Merk ressurser", @@ -1804,14 +1962,15 @@ "tag_not_found_question": "Finner du ikke en merke? Opprett en nytt merke.", "tag_people": "Tag Folk", "tag_updated": "Oppdater merke: {tag}", - "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", + "tagged_assets": "Merket {count, plural, one {# element} other {# elementer}}", "tags": "Merker", + "tap_to_run_job": "Trykk for ÃĨ kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", "theme_selection_description": "Automatisk sett tema til lys eller mørk basert pÃĨ nettleserens systeminnstilling", - "theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor pÃĨ objekter i fotorutenettet", - "theme_setting_asset_list_tiles_per_row_title": "Antall objekter per rad ({count})", + "theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor pÃĨ elementer i fotorutenettet", + "theme_setting_asset_list_tiles_per_row_title": "Antall elementer per rad ({count})", "theme_setting_colorful_interface_subtitle": "Angi primÃĻrfarge til bakgrunner.", "theme_setting_colorful_interface_title": "Fargefullt grensesnitt", "theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten pÃĨ bilder i detaljvisning", @@ -1832,66 +1991,79 @@ "to_change_password": "Endre passord", "to_favorite": "Favoritt", "to_login": "Logg inn", + "to_multi_select": "for multivalg", "to_parent": "GÃĨ til overodnet", + "to_select": "for valg", "to_trash": "Papirkurv", "toggle_settings": "Bytt innstillinger", "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", + "trash_action_prompt": "{count} flyttet til søppel", "trash_all": "Slett alt", "trash_count": "Slett {count, number}", - "trash_delete_asset": "Slett ressurs", - "trash_emptied": "Søppelbøtte tømt", + "trash_delete_asset": "Slett element", + "trash_emptied": "Søppelbøtte Tømt", "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", - "trash_page_empty_trash_dialog_content": "Vil du tømme søppelbøtten? Objektene vil bli permanent fjernet fra Immich", - "trash_page_info": "Objekter i søppelbøtten blir permanent fjernet etter {days} dager", - "trash_page_no_assets": "Ingen forkastede objekter", + "trash_page_empty_trash_dialog_content": "Vil du Tømme papirkurven? Objektene vil bli permanent fjernet fra Immich", + "trash_page_info": "Objekter i papirkurven blir permanent fjernet etter {days} dager", + "trash_page_no_assets": "Ingen forkastede elementer", "trash_page_restore_all": "Gjenopprett alt", - "trash_page_select_assets_btn": "Velg objekter", + "trash_page_select_assets_btn": "Velg elementer", "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", + "troubleshoot": "Feilsøk", "type": "Type", - "unable_to_change_pin_code": "Klarte ikke ÃĨ endre PINkode", + "unable_to_change_pin_code": "Klarte ikke ÃĨ endre PIN-kode", "unable_to_setup_pin_code": "Klarte ikke ÃĨ sette opp PINkode", "unarchive": "Fjern fra arkiv", + "unarchive_action_prompt": "{count} slettet fra Arkiv", "unarchived_count": "{count, plural, other {uarkivert #}}", "undo": "Angre", "unfavorite": "Fjern favoritt", + "unfavorite_action_prompt": "{count} slettet fra Favoritter", "unhide_person": "Vis person", "unknown": "Ukjent", "unknown_country": "Ukjent Land", - "unknown_year": "Ukjent År", + "unknown_year": "Ukjent ÃĨr", "unlimited": "Ubegrenset", "unlink_motion_video": "Koble fra bevegelsesvideo", "unlink_oauth": "Fjern kobling til OAuth", "unlinked_oauth_account": "Koblet fra OAuth-konto", "unmute_memories": "Opphev demping av minner", "unnamed_album": "Navnløst album", - "unnamed_album_delete_confirmation": "Er du sikker pÃĨ at du vil slette dette albumet?", + "unnamed_album_delete_confirmation": "Vil du virkelig slette dette albumet?", "unnamed_share": "Deling uten navn", "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", "unselect_all_duplicates": "Fjern markeringen av alle duplikater", - "unselect_all_in": "Fjern alle i {group}", + "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", - "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "unstack_action_prompt": "{count} ustakket", + "unstacked_assets_count": "Ikke stablet {count, plural, one {# element} other {# elementer}}", + "untagged": "Umerket", "up_next": "Neste", + "update_location_action_prompt": "Oppdater plasseringen til {count} valgte elementer med:", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", + "upload_action_prompt": "{count} i kø for opplasting", "upload_concurrency": "Samtidig opplastning", - "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", - "upload_dialog_title": "Last opp objekt", + "upload_details": "Opplastingsdetaljer", + "upload_dialog_info": "Vil du utføre backup av valgte element(er) til serveren?", + "upload_dialog_title": "Last opp element", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for ÃĨ se nye opplastingsressurser.", + "upload_finished": "Opplasting fullført", "upload_progress": "GjenstÃĨende {remaining, number} – behandlet {processed, number}/{total, number}", - "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplicate asset} other {# duplicate assets}}", + "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert element} other {# dupliserte elementer}}", "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", "upload_success": "Opplasting vellykket, oppdater siden for ÃĨ se nye opplastninger.", "upload_to_immich": "Last opp til Immich ({count})", "uploading": "Laster opp", + "uploading_media": "Laster opp media", "url": "URL", "usage": "Bruk", "use_biometric": "Bruk biometri", @@ -1900,7 +2072,7 @@ "user": "Bruker", "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", - "user_liked": "{user} likte {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette elementet} other {dette}}", "user_pin_code_settings": "PINkode", "user_pin_code_settings_description": "HÃĨndter din PINkode", "user_privacy": "Personverninnstillinger", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", + "users_added_to_album_count": "Lagt til {count, plural, one {# bruker} other {# brukere}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", @@ -1919,7 +2092,7 @@ "version": "Versjon", "version_announcement_closing": "Din venn, Alex", "version_announcement_message": "Hei! En ny versjon av Immich er tilgjengelig. Vennligst ta deg tid til ÃĨ lese utgivelsesnotatene for ÃĨ sikre at oppsettet ditt er oppdatert for ÃĨ forhindre feilkonfigurasjoner, spesielt hvis du bruker WatchTower eller en annen mekanisme som hÃĨndterer oppdatering av Immich-forekomsten din automatisk.", - "version_history": "Verson Historie", + "version_history": "Versjonshistorikk", "version_history_item": "Installert {version} den {date}", "video": "Video", "video_hover_setting": "Spill av forhÃĨndsvisining mens du holder over musepekeren", @@ -1927,9 +2100,10 @@ "videos": "Videoer", "videos_count": "{count, plural, one {# Video} other {# Videoer}}", "view": "Vis", - "view_album": "Vis Album", + "view_album": "Vis album", "view_all": "Vis alle", "view_all_users": "Vis alle brukere", + "view_details": "Vis detaljer", "view_in_timeline": "Vis i tidslinje", "view_link": "Vis lenke", "view_links": "Vis lenker", @@ -1937,10 +2111,11 @@ "view_next_asset": "Vis neste fil", "view_previous_asset": "Vis forrige fil", "view_qr_code": "Vis QR-kode", - "view_stack": "Vis Stabbel", + "view_similar_photos": "Vis lignende bilder", + "view_stack": "Vis stabel", "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", - "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", + "viewer_stack_use_as_main_asset": "Bruk som hovedelement", "viewer_unstack": "avstable", "visibility_changed": "Synlighet endret for {count, plural, one {# person} other {# people}}", "waiting": "Venter", @@ -1948,12 +2123,13 @@ "week": "Uke", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "Wi-Fi Navn", - "wrong_pin_code": "Feil PINkode", + "wifi_name": "Wi-Fi-navn", + "wrong_pin_code": "Feil PIN-kode", "year": "År", "years_ago": "{years, plural, one {# ÃĨr} other {# ÃĨr}} siden", "yes": "Ja", "you_dont_have_any_shared_links": "Du har ingen delte lenker", - "your_wifi_name": "Ditt Wi-Fi navn", - "zoom_image": "Zoom Bilde" + "your_wifi_name": "Ditt Wi-Fi-navn", + "zoom_image": "Zoom Bilde", + "zoom_to_bounds": "Zoom til grensene" } diff --git a/i18n/nl.json b/i18n/nl.json index 07699e0b31..55e934736c 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -14,6 +14,7 @@ "add_a_location": "Een locatie toevoegen", "add_a_name": "Naam toevoegen", "add_a_title": "Titel toevoegen", + "add_birthday": "Voeg een verjaardag toe", "add_endpoint": "Server toevoegen", "add_exclusion_pattern": "Uitsluitingspatroon toevoegen", "add_import_path": "Import-pad toevoegen", @@ -27,6 +28,10 @@ "add_to_album": "Aan album toevoegen", "add_to_album_bottom_sheet_added": "Toegevoegd aan {album}", "add_to_album_bottom_sheet_already_exists": "Staat al in {album}", + "add_to_album_bottom_sheet_some_local_assets": "Sommige lokale items konden niet aan album toegevoegd worden", + "add_to_album_toggle": "Selectie inschakelen voor {album}", + "add_to_albums": "Toevoegen aan albums", + "add_to_albums_count": "Toevoegen aan albums ({count})", "add_to_shared_album": "Aan gedeeld album toevoegen", "add_url": "URL toevoegen", "added_to_archive": "Toegevoegd aan archief", @@ -35,7 +40,7 @@ "admin": { "add_exclusion_pattern_description": "Uitsluitingspatronen toevoegen. Globbing met *, ** en ? wordt ondersteund. Om alle bestanden in een map met de naam \"Raw\" te negeren, gebruik \"**/Raw/**\". Om alle bestanden die eindigen op \".tif\" te negeren, gebruik \"**/*.tif\". Om een absoluut pad te negeren, gebruik \"/path/to/ignore/**\".", "admin_user": "Beheerder gebruiker", - "asset_offline_description": "Deze asset uit een externe bibliotheek is niet meer beschikbaar op de schijf en is naar de prullenbak verplaatst. Als het bestand binnen de bibliotheek is verplaatst, controleer dan je tijdlijn voor de nieuwe bijbehorende asset. Om dit bestand te herstellen, zorg ervoor dat het onderstaande bestandspad toegankelijk is voor Immich en scan de bibliotheek opnieuw.", + "asset_offline_description": "Dit item uit een externe bibliotheek is niet meer beschikbaar op de schijf en is naar de prullenbak verplaatst. Als het bestand binnen de bibliotheek is verplaatst, controleer dan je tijdlijn voor het nieuwe bijbehorende item. Om dit bestand te herstellen, zorg ervoor dat het onderstaande bestandspad toegankelijk is voor Immich en scan de bibliotheek opnieuw.", "authentication_settings": "Authenticatie-instellingen", "authentication_settings_description": "Wachtwoord, OAuth, en andere authenticatie-instellingen beheren", "authentication_settings_disable_all": "Weet je zeker dat je alle inlogmethoden wilt uitschakelen? Inloggen zal volledig worden uitgeschakeld.", @@ -44,12 +49,19 @@ "backup_database": "Maak database back-up", "backup_database_enable_description": "Database back-ups activeren", "backup_keep_last_amount": "Aantal back-ups om te bewaren", - "backup_settings": "Database back-up instellingen", - "backup_settings_description": "Beheer database back-up instellingen.", + "backup_onboarding_1_description": "externe kopie in de cloud of op een andere fysieke locatie.", + "backup_onboarding_2_description": "lokale kopieÃĢn op verschillende apparaten. Dit omvat de hoofdbestanden Ên een lokale back-up van deze bestanden.", + "backup_onboarding_3_description": "totaal aantal kopieÃĢn van de gegevens, inclusief originele bestanden. Dit omvat 1 externe kopie en 2 lokale kopieÃĢn.", + "backup_onboarding_description": "Een 3-2-1 back-up strategie wordt aanbevolen om de gegevens te beschermen. Bewaar kopieÃĢn van de geÃŧploade foto's/video's en de Immich database voor een complete back-up oplossing.", + "backup_onboarding_footer": "Raadpleeg de documentatie voor meer informatie over het maken van back-ups van Immich.", + "backup_onboarding_parts_title": "Een 3-2-1 back-up omvat:", + "backup_onboarding_title": "Back-ups", + "backup_settings": "Database dump instellingen", + "backup_settings_description": "Beheer database dump instellingen.", "cleared_jobs": "Taken gewist voor: {job}", "config_set_by_file": "Instellingen worden momenteel beheerd door een configuratiebestand", "confirm_delete_library": "Weet je zeker dat je de bibliotheek {library} wilt verwijderen?", - "confirm_delete_library_assets": "Weet je zeker dat je deze bibliotheek wilt verwijderen? Hiermee {count, plural, one {wordt # asset} other {worden alle # assets}} uit Immich verwijderd en dit kan niet ongedaan worden gemaakt. Bestanden blijven op de schijf staan.", + "confirm_delete_library_assets": "Weet je zeker dat je deze bibliotheek wilt verwijderen? Hiermee {count, plural, one {wordt # item} other {worden alle # items}} uit Immich verwijderd en dit kan niet ongedaan worden gemaakt. Bestanden blijven op de schijf staan.", "confirm_email_below": "Typ hieronder \"{email}\" ter bevestiging", "confirm_reprocess_all_faces": "Weet je zeker dat je alle gezichten opnieuw wilt verwerken? Hiermee worden ook alle mensen gewist.", "confirm_user_password_reset": "Weet je zeker dat je het wachtwoord van {user} wilt resetten?", @@ -59,14 +71,14 @@ "cron_expression_description": "Stel het scaninterval in met het cron-formaat. Voor meer informatie kun je bijvoorbeeld kijken naar Crontab Guru", "cron_expression_presets": "Cron-expressie presets", "disable_login": "Inloggen uitschakelen", - "duplicate_detection_job_description": "Machine learning uitvoeren op assets om vergelijkbare assets te vinden. Dit is gebaseerd op Slim Zoeken", + "duplicate_detection_job_description": "Machine learning uitvoeren op items om vergelijkbare items te vinden. Dit is gebaseerd op Slim Zoeken", "exclusion_pattern_description": "Met uitsluitingspatronen kun je bestanden en mappen negeren bij het scannen van je bibliotheek. Dit is handig als je mappen hebt met bestanden die je niet wilt importeren, zoals RAW bestanden.", "external_library_management": "Externe bibliotheek beheren", "face_detection": "Gezichtsdetectie", - "face_detection_description": "Detecteer gezichten in assets met behulp van machine learning. Voor video's wordt alleen de thumbnail gebruikt. \"Vernieuwen\" verwerkt alle assets (opnieuw). \"Reset\" verwijdert daarnaast alle huidige gezichtgegevens. \"Missend\" plaatst assets in de wachtrij die nog niet zijn verwerkt. Gedetecteerde gezichten worden in de wachtrij geplaatst voor gezichtsherkenning nadat gezichtsdetectie is voltooid, waarbij ze worden gegroepeerd in bestaande of nieuwe mensen.", + "face_detection_description": "Detecteer gezichten in items met behulp van machine learning. Voor video's wordt alleen de thumbnail gebruikt. \"Vernieuwen\" verwerkt alle items (opnieuw). \"Reset\" verwijdert daarnaast alle huidige gezichtgegevens. \"Missend\" plaatst items in de wachtrij die nog niet zijn verwerkt. Gedetecteerde gezichten worden in de wachtrij geplaatst voor gezichtsherkenning nadat gezichtsdetectie is voltooid, waarbij ze worden gegroepeerd in bestaande of nieuwe mensen.", "facial_recognition_job_description": "Groepeer gedetecteerde gezichten tot mensen. Deze stap wordt uitgevoerd nadat gezichtsdetectie is voltooid. \"Resetten\" (her-)clustert alle gezichten. \"Missend\" plaatst gezichten in de wachtrij waaraan geen persoon is toegewezen.", "failed_job_command": "Commando {command} mislukt voor taak: {job}", - "force_delete_user_warning": "WAARSCHUWING: Hiermee worden de gebruiker en alle assets onmiddellijk verwijderd. Dit kan niet ongedaan worden gemaakt en de bestanden kunnen niet worden hersteld.", + "force_delete_user_warning": "WAARSCHUWING: Hiermee worden de gebruiker en alle items onmiddellijk verwijderd. Dit kan niet ongedaan worden gemaakt en de bestanden kunnen niet worden hersteld.", "image_format": "Formaat", "image_format_description": "WebP produceert kleinere bestanden dan JPEG, maar is langzamer om te verwerken.", "image_fullsize_description": "Afbeelding op ware grootte met gestripte metadata, gebruikt bij inzoomen", @@ -78,7 +90,7 @@ "image_prefer_embedded_preview_setting_description": "Gebruik ingesloten voorbeeldafbeelding van RAW-bestanden als invoer voor beeldverwerking wanneer beschikbaar. Dit kan preciezere kleuren produceren voor sommige afbeeldingen, maar de kwaliteit van het voorbeeld is afhankelijk van de camera en de afbeelding kan mogelijk meer compressie-artefacten bevatten.", "image_prefer_wide_gamut": "Voorkeur geven aan wide gamut", "image_prefer_wide_gamut_setting_description": "Display P3 gebruiken voor voorbeeldafbeeldingen. Dit behoudt de levendigheid van afbeeldingen met brede kleurruimtes beter, maar afbeeldingen kunnen er anders uitzien op oude apparaten met een oude browserversie. sRGB-afbeeldingen blijven sRGB gebruiken om kleurverschuivingen te vermijden.", - "image_preview_description": "Middelgrote afbeelding met verwijderde metadata, gebruikt bij het bekijken van een enkele asset en voor machine learning", + "image_preview_description": "Middelgrote afbeelding met verwijderde metadata, gebruikt bij het bekijken van een enkele item en voor machine learning", "image_preview_quality_description": "Voorbeeldafbeelding kwaliteit van 1-100. Hoger is beter, maar produceert grotere bestanden en kan de app vertragen. Een lage waarde kan de kwaliteit van machine learning beïnvloeden.", "image_preview_title": "Voorbeeldafbeelding instellingen", "image_quality": "Kwaliteit", @@ -93,7 +105,7 @@ "job_created": "Taak aangemaakt", "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", "job_settings": "Achtergrondtaak-instellingen", - "job_settings_description": "Beheer gelijktijdige taken", + "job_settings_description": "Beheer aantal gelijktijdige taken", "job_status": "Taakstatus", "jobs_delayed": "{jobCount, plural, other {# vertraagd}}", "jobs_failed": "{jobCount, plural, other {# mislukt}}", @@ -112,11 +124,18 @@ "logging_enable_description": "Logboek inschakelen", "logging_level_description": "Indien ingeschakeld, welk logniveau er wordt gebruikt.", "logging_settings": "Logging", + "machine_learning_availability_checks": "Beschikbaarheid", + "machine_learning_availability_checks_description": "Automatisch detecteren en selecteren van beschikbare machine learning servers", + "machine_learning_availability_checks_enabled": "Activeer beschikbaarheid controles", + "machine_learning_availability_checks_interval": "Controleinterval", + "machine_learning_availability_checks_interval_description": "Interval in milliseconden tussen beschikbaarheid checks", + "machine_learning_availability_checks_timeout": "Verzoek time-out", + "machine_learning_availability_checks_timeout_description": "Time-out in milliseconden voor beschikbaarheidschecks", "machine_learning_clip_model": "CLIP model", "machine_learning_clip_model_description": "De naam van een CLIP-model dat hier is vermeld. Let op: je moet de 'Slim Zoeken -taak opnieuw uitvoeren voor alle afbeeldingen wanneer je een model wijzigt.", "machine_learning_duplicate_detection": "Duplicaat detectie", "machine_learning_duplicate_detection_enabled": "Duplicaatdetectie inschakelen", - "machine_learning_duplicate_detection_enabled_description": "Indien uitgeschakeld, worden identieke assets nog steeds gededupliceerd.", + "machine_learning_duplicate_detection_enabled_description": "Indien uitgeschakeld, worden identieke items nog steeds gededupliceerd.", "machine_learning_duplicate_detection_setting_description": "Gebruik CLIP om exactie kopieÃĢn te vinden", "machine_learning_enabled": "Machine learning inschakelen", "machine_learning_enabled_description": "Wanneer uitgeschakeld zullen alle ML instellingen uitgezet worden, ongeacht onderstaande instellingen.", @@ -159,16 +178,30 @@ "memory_cleanup_job": "Herinneringen opschonen", "memory_generate_job": "Herinneringen genereren", "metadata_extraction_job": "Metadata ophalen", - "metadata_extraction_job_description": "Metadata ophalen van iedere asset, zoals GPS, gezichten en resolutie", + "metadata_extraction_job_description": "Metadata ophalen van ieder item, zoals GPS, gezichten en resolutie", "metadata_faces_import_setting": "Gezichten importeren inschakelen", "metadata_faces_import_setting_description": "Gezichten importeren uit EXIF-gegevens van afbeeldingen en sidecar bestanden", "metadata_settings": "Metadata instellingen", "metadata_settings_description": "Beheer metadata instellingen", "migration_job": "Migratie", - "migration_job_description": "Migreer thumbnails voor assets en gezichten naar de nieuwste mapstructuur", + "migration_job_description": "Migreer thumbnails voor items en gezichten naar de nieuwste mapstructuur", + "nightly_tasks_cluster_faces_setting_description": "Gezichtsherkenning uitvoeren op nieuw gedetecteerde gezichten", + "nightly_tasks_cluster_new_faces_setting": "Cluster nieuwe gezichten", + "nightly_tasks_database_cleanup_setting": "Database opschoon taken", + "nightly_tasks_database_cleanup_setting_description": "Ruim oude data op van de database", + "nightly_tasks_generate_memories_setting": "Genereer herinneringen", + "nightly_tasks_generate_memories_setting_description": "Maak nieuwe herinneringen van items", + "nightly_tasks_missing_thumbnails_setting": "Genereer ontbrekende thumbnails", + "nightly_tasks_missing_thumbnails_setting_description": "Items zonder thumbnail in een wachtrij plaatsen voor het genereren van thumbnails", + "nightly_tasks_settings": "Instellingen voor nacht taken", + "nightly_tasks_settings_description": "Beheer nacht taken", + "nightly_tasks_start_time_setting": "Start tijd", + "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nacht taken", + "nightly_tasks_sync_quota_usage_setting": "Synchroniseer quota gebruik", + "nightly_tasks_sync_quota_usage_setting_description": "update gebruiker opslag quota, gebaseerd op huidig gebruik", "no_paths_added": "Geen paden toegevoegd", "no_pattern_added": "Geen patroon toegevoegd", - "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade assets, voer de volgende taak uit", + "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade items, voer de volgende taak uit", "note_cannot_be_changed_later": "LET OP: Dit kan later niet meer worden gewijzigd!", "notification_email_from_address": "Adres afzender", "notification_email_from_address_description": "E-mailadres van de afzender, bijvoorbeeld: \"Immich Foto Server \". Zorg ervoor dat je een adres gebruikt waar je e-mails van mag verzenden.", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Omleidings-URI voor mobiel", "oauth_mobile_redirect_uri_override": "Omleidings-URI voor mobiele app overschrijven", "oauth_mobile_redirect_uri_override_description": "Inschakelen wanneer de OAuth-provider geen mobiele URI toestaat, zoals ''{callback}''", + "oauth_role_claim": "Rol claim", + "oauth_role_claim_description": "Automatisch admin toegang geven als deze claim aanwezig is. De claim kan 'user' of 'admin' zijn.", "oauth_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", @@ -233,15 +268,15 @@ "sidecar_job": "Sidecar metagegevens", "sidecar_job_description": "Zoek of synchroniseer sidecar metadata van het bestandssysteem", "slideshow_duration_description": "Aantal seconden dat iedere afbeelding wordt getoond", - "smart_search_job_description": "Voer machine learning uit op assets om te gebruiken voor slim zoeken", - "storage_template_date_time_description": "De aanmaakdatum van een asset wordt gebruikt als datum", + "smart_search_job_description": "Voer machine learning uit op items om te gebruiken voor slim zoeken", + "storage_template_date_time_description": "De aanmaakdatum van een item wordt gebruikt als datum", "storage_template_date_time_sample": "Voorbeeldtijd {date}", "storage_template_enable_description": "Engine voor opslagtemplate inschakelen", "storage_template_hash_verification_enabled": "Hashverificatie ingeschakeld", "storage_template_hash_verification_enabled_description": "Zet hashverificatie aan, schakel dit niet uit tenzij je zeker bent van de implicaties", "storage_template_migration": "Opslagtemplate migratie", - "storage_template_migration_description": "Pas de huidige {template} toe op eerder geÃŧploade assets", - "storage_template_migration_info": "Wijzigingen in de opslagtemplate worden alleen toegepast op nieuwe assets. Om de template met terugwerkende kracht toe te passen op eerder geÃŧploade assets, voer je de {job} uit.", + "storage_template_migration_description": "Pas de huidige {template} toe op eerder geÃŧploade items", + "storage_template_migration_info": "Wijzigingen in de opslagtemplate worden alleen toegepast op nieuwe items. Om de template met terugwerkende kracht toe te passen op eerder geÃŧploade items, voer je de {job} uit.", "storage_template_migration_job": "Opslagtemplate migratietaak", "storage_template_more_details": "Voor meer details over deze functie, bekijk de Opslagstemplate en de implicaties daarvan", "storage_template_onboarding_description_v2": "Wanneer ingeschakeld, zal deze functie bestanden automatisch organiseren gebaseerd op een template gedefinieerd door de gebruiker. Voor meer informatie, bekijk de documentatie.", @@ -265,8 +300,8 @@ "theme_settings": "Thema-instellingen", "theme_settings_description": "Beheer het uiterlijk van de Immich webinterface", "thumbnail_generation_job": "Thumbnail genereren", - "thumbnail_generation_job_description": "Genereer grote, kleine en vervaagde thumbnails voor iedere asset, en genereer thumbnails voor iedere persoon", - "transcoding_acceleration_api": "Acceleration API", + "thumbnail_generation_job_description": "Genereer grote, kleine en vervaagde thumbnails voor ieder item, en genereer thumbnails voor iedere persoon", + "transcoding_acceleration_api": "Versnelling API", "transcoding_acceleration_api_description": "De API die met je apparaat zal communiceren om transcodering te versnellen. Deze instelling is 'best effort': wanneer fouten optreden wordt teruggevallen op softwaretranscodering. VP9 kan wel of niet werken, afhankelijk van je hardware.", "transcoding_acceleration_nvenc": "NVENC (vereist NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (vereist 7e generatie Intel CPU of nieuwer)", @@ -285,7 +320,7 @@ "transcoding_codecs_learn_more": "Om meer te leren over de terminologie die hier wordt gebruikt, bekijk de FFmpeg documentatie voor H.264 codec, HEVC codec en VP9 codec.", "transcoding_constant_quality_mode": "Constante kwaliteit modus", "transcoding_constant_quality_mode_description": "ICQ is beter dan CQP, maar sommige hardware versnellingsmethodes ondersteunen deze modus niet. Als u deze optie instelt, wordt de voorkeur gegeven aan de opgegeven modus bij gebruik van op kwaliteit gebaseerde encoding. Deze optie wordt genegeerd door NVENC omdat het ICQ niet ondersteunt.", - "transcoding_constant_rate_factor": "Constant rate factor (-crf)", + "transcoding_constant_rate_factor": "Constant tarief factor (-ctf)", "transcoding_constant_rate_factor_description": "Niveau voor videokwaliteit. Typische waarden zijn 23 voor H.264, 28 voor HEVC, 31 voor VP9 en 35 voor AV1. Lager is beter, maar produceert grotere bestanden.", "transcoding_disabled_description": "Transcodeer geen video's. Het afspelen kan op sommige clients niet meer werken", "transcoding_encoding_options": "Coderings Opties", @@ -298,14 +333,14 @@ "transcoding_max_b_frames_description": "Hogere waarden verbeteren de compressie efficiÃĢntie, maar vertragen de codering. Is mogelijk niet compatibel met hardwareversnelling op oudere apparaten. 0 schakelt B-frames uit, terwijl -1 deze waarde automatisch instelt.", "transcoding_max_bitrate": "Maximum bitrate", "transcoding_max_bitrate_description": "Het instellen van een maximale bitrate kan de bestandsgrootte voorspelbaarder maken, tegen geringe kosten voor de kwaliteit. Bij 720p zijn de typische waarden 2600 kbit/s voor VP9 of HEVC, of 4500 kbit/s voor H.264. Uitgeschakeld indien ingesteld op 0.", - "transcoding_max_keyframe_interval": "Maximum keyframe interval", + "transcoding_max_keyframe_interval": "Maximale keyframe interval", "transcoding_max_keyframe_interval_description": "Stelt de maximale frameafstand tussen keyframes in. Lagere waarden verslechteren de compressie efficiÃĢntie, maar verbeteren de zoektijden en kunnen de kwaliteit verbeteren in scènes met snelle bewegingen. 0 stelt deze waarde automatisch in.", "transcoding_optimal_description": "Video's met een hogere resolutie dan de doelresolutie of niet in een geaccepteerd formaat", "transcoding_policy": "Transcode beleid", "transcoding_policy_description": "Stel in wanneer een video wordt getranscodeerd", "transcoding_preferred_hardware_device": "Voorkeur hardwareapparaat", "transcoding_preferred_hardware_device_description": "Geldt alleen voor VAAPI en QSV. Stelt de dri node in die wordt gebruikt voor hardwaretranscodering.", - "transcoding_preset_preset": "Preset (-preset)", + "transcoding_preset_preset": "Voorkeuze (-preset)", "transcoding_preset_preset_description": "Compressiesnelheid. Langzamere presets produceren kleinere bestanden en verhogen de kwaliteit bij het targeten van een bepaalde bitrate. VP9 negeert snelheden boven 'faster'.", "transcoding_reference_frames": "Referentie frames", "transcoding_reference_frames_description": "Het aantal frames om naar te verwijzen bij het comprimeren van een bepaald frame. Hogere waarden verbeteren de compressie-efficiÃĢntie, maar vertragen de codering. Bij 0 wordt deze waarde automatisch ingesteld.", @@ -314,7 +349,7 @@ "transcoding_settings_description": "Beheer welke videos worden getranscodeerd en hoe ze worden verwerkt", "transcoding_target_resolution": "Target resolutie", "transcoding_target_resolution_description": "Hogere resoluties kunnen meer details behouden, maar het coderen ervan duurt langer, de bestandsgrootte is groter en de app reageert mogelijk minder snel.", - "transcoding_temporal_aq": "Temporal AQ", + "transcoding_temporal_aq": "Tijdelijke AQ", "transcoding_temporal_aq_description": "Alleen van toepassing op NVENC. Verhoogt de kwaliteit van scènes met veel details en weinig beweging. Is mogelijk niet compatibel met oudere apparaten.", "transcoding_threads": "Threads", "transcoding_threads_description": "Hogere waarden leiden tot snellere codering, maar laten minder ruimte over voor de server om andere taken te verwerken terwijl deze actief is. Deze waarde mag niet groter zijn dan het aantal CPU cores. Maximaliseert het gebruik als deze is ingesteld op 0.", @@ -328,15 +363,18 @@ "transcoding_video_codec_description": "VP9 heeft een hoge efficiÃĢntie en webcompatibiliteit, maar duurt langer om te transcoderen. HEVC presteert vergelijkbaar, maar heeft een lagere webcompatibiliteit. H.264 is breed compatibel en snel om te transcoderen, maar produceert veel grotere bestanden. AV1 is de meest efficiÃĢnte codec, maar mist ondersteuning op oudere apparaten.", "trash_enabled_description": "Prullenbakfuncties inschakelen", "trash_number_of_days": "Aantal dagen", - "trash_number_of_days_description": "Aantal dagen dat de assets in de prullenbak worden bewaard voordat ze definitief worden verwijderd", + "trash_number_of_days_description": "Aantal dagen dat de items in de prullenbak worden bewaard voordat ze definitief worden verwijderd", "trash_settings": "Prullenbak instellingen", "trash_settings_description": "Beheer prullenbak instellingen", + "unlink_all_oauth_accounts": "Ontkoppel alle OAuth accounts", + "unlink_all_oauth_accounts_description": "Vergeet niet alle OAuth accounts te ontkoppelen voor te migreren naar een nieuwe provider.", + "unlink_all_oauth_accounts_prompt": "Ben je zeker dat je alle OAuth accounts wilt ontkoppelen? Dit zal het OAuth ID voor elke gebruiker resetten en kan niet ongedaan gemaakt worden.", "user_cleanup_job": "Gebruiker opschoning", - "user_delete_delay": "Het account en de assets van {user} worden over {delay, plural, one {# dag} other {# dagen}} permanent verwijderd.", + "user_delete_delay": "Het account en de items van {user} worden over {delay, plural, one {# dag} other {# dagen}} permanent verwijderd.", "user_delete_delay_settings": "Verwijder vertraging", - "user_delete_delay_settings_description": "Aantal dagen na verwijdering om het account en de assets van een gebruiker permanent te verwijderen. De taak voor het verwijderen van gebruikers wordt om middernacht uitgevoerd om te controleren of gebruikers verwijderd kunnen worden. Wijzigingen in deze instelling worden bij de volgende uitvoering meegenomen.", - "user_delete_immediately": "Het account en de assets van {user} worden onmiddellijk in de wachtrij geplaatst voor permanente verwijdering.", - "user_delete_immediately_checkbox": "Gebruikers en assets in de wachtrij plaatsen voor onmiddellijke verwijdering", + "user_delete_delay_settings_description": "Aantal dagen na verwijdering om het account en de items van een gebruiker permanent te verwijderen. De taak voor het verwijderen van gebruikers wordt om middernacht uitgevoerd om te controleren of gebruikers verwijderd kunnen worden. Wijzigingen in deze instelling worden bij de volgende uitvoering meegenomen.", + "user_delete_immediately": "Het account en de items van {user} worden onmiddellijk in de wachtrij geplaatst voor permanente verwijdering.", + "user_delete_immediately_checkbox": "Gebruikers en items in de wachtrij plaatsen voor onmiddellijke verwijdering", "user_details": "Gebruiker details", "user_management": "Gebruikersbeheer", "user_password_has_been_reset": "Het wachtwoord van de gebruiker is gereset:", @@ -360,10 +398,12 @@ "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": "Logniveau: {level}", - "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_subtitle": "Sommige apparaten zijn traag met het laden van lokale afbeeldingen. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", "advanced_settings_proxy_headers_subtitle": "Definieer proxy headers die Immich bij elk netwerkverzoek moet verzenden", "advanced_settings_proxy_headers_title": "Proxy headers", + "advanced_settings_readonly_mode_subtitle": "Schakelt de alleen-lezenmodus in, waarbij de foto's alleen bekeken kunnen worden. Dingen zoals het selecteren van meerdere afbeeldingen, delen, casten en verwijderen zijn allemaal uitgeschakeld. Schakel alleen-lezen in of uit via de gebruikers avatar vanaf het hoofdscherm", + "advanced_settings_readonly_mode_title": "Alleen-lezen Mode", "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_subtitle": "Automatisch bestanden verwijderen of herstellen op dit apparaat als die actie op het web is ondernomen", @@ -376,9 +416,10 @@ "age_years": "{years, plural, other {Leeftijd #}}", "album_added": "Album toegevoegd", "album_added_notification_setting_description": "Ontvang een e-mailmelding wanneer je aan een gedeeld album wordt toegevoegd", - "album_cover_updated": "Album cover is bijgewerkt", + "album_cover_updated": "Albumomslag is bijgewerkt", "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_deleted": "Album verwijderd", "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INBEGREPEN", "album_info_updated": "Albumgegevens bijgewerkt", @@ -388,15 +429,17 @@ "album_options": "Albumopties", "album_remove_user": "Gebruiker verwijderen?", "album_remove_user_confirmation": "Weet je zeker dat je {user} wilt verwijderen?", + "album_search_not_found": "Geen albums gevonden die aan je zoekopdracht voldoen", "album_share_no_users": "Het lijkt erop dat je dit album met alle gebruikers hebt gedeeld, of dat je geen gebruikers hebt om mee te delen.", + "album_summary": "Album samenvatting", "album_updated": "Album bijgewerkt", - "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe assets heeft", + "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe items heeft", "album_user_left": "{album} verlaten", "album_user_removed": "{user} verwijderd", "album_viewer_appbar_delete_confirm": "Weet je zeker dat je dit album uit je account wilt verwijderen?", "album_viewer_appbar_share_err_delete": "Verwijderen album mislukt", "album_viewer_appbar_share_err_leave": "Verlaten album mislukt", - "album_viewer_appbar_share_err_remove": "Er gaat iets mis bij het verwijderen van assets uit het album", + "album_viewer_appbar_share_err_remove": "Er gaat iets mis bij het verwijderen van items uit het album", "album_viewer_appbar_share_err_title": "Albumtitel wijzigen mislukt", "album_viewer_appbar_share_leave": "Verlaat album", "album_viewer_appbar_share_to": "Delen via", @@ -406,7 +449,8 @@ "albums_count": "{count, plural, one {{count, number} album} other {{count, number} albums}}", "albums_default_sort_order": "Standaard sorteervolgorde album", "albums_default_sort_order_description": "InitiÃĢle sorteervolgorde bij het maken van nieuwe albums.", - "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", + "albums_feature_description": "Collectie van items die je kan delen met andere gebruikers.", + "albums_on_device_count": "Albums op apparaat ({count})", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -426,9 +470,11 @@ "app_bar_signout_dialog_title": "Log uit", "app_settings": "App instellingen", "appears_in": "Komt voor in", + "apply_count": "Toepassen ({count, number})", "archive": "Archief", + "archive_action_prompt": "{count} item(s) toegevoegd aan het archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", - "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", + "archive_page_no_archived_assets": "Geen gearchiveerde items gevonden", "archive_page_title": "Archief ({count})", "archive_size": "Archiefgrootte", "archive_size_description": "Configureer de archiefgrootte voor downloads (in GiB)", @@ -436,88 +482,96 @@ "archived_count": "{count, plural, other {# gearchiveerd}}", "are_these_the_same_person": "Zijn dit dezelfde personen?", "are_you_sure_to_do_this": "Weet je zeker dat je dit wilt doen?", - "asset_action_delete_err_read_only": "Kan alleen-lezen asset(s) niet verwijderen, overslaan", - "asset_action_share_err_offline": "Kan offline asset(s) niet ophalen, overslaan", + "asset_action_delete_err_read_only": "Kan alleen-lezen item(s) niet verwijderen, overslaan", + "asset_action_share_err_offline": "Kan offline item(s) niet ophalen, overslaan", "asset_added_to_album": "Toegevoegd aan album", "asset_adding_to_album": "Toevoegen aan albumâ€Ļ", - "asset_description_updated": "Asset beschrijving is bijgewerkt", - "asset_filename_is_offline": "Asset {filename} is offline", - "asset_has_unassigned_faces": "Asset heeft niet-toegewezen gezichten", + "asset_description_updated": "Item beschrijving is bijgewerkt", + "asset_filename_is_offline": "Item {filename} is offline", + "asset_has_unassigned_faces": "Item heeft niet-toegewezen gezichten", "asset_hashing": "Hashenâ€Ļ", "asset_list_group_by_sub_title": "Groepeer op", "asset_list_layout_settings_dynamic_layout_title": "Dynamische layout", "asset_list_layout_settings_group_automatically": "Automatisch", - "asset_list_layout_settings_group_by": "Groepeer assets per", + "asset_list_layout_settings_group_by": "Groepeer items per", "asset_list_layout_settings_group_by_month_day": "Maand + dag", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Fotorasterlayoutinstellingen", + "asset_list_settings_subtitle": "Fotoraster layout instellingen", "asset_list_settings_title": "Fotoraster", - "asset_offline": "Asset offline", - "asset_offline_description": "Deze externe asset is niet meer op de schijf te vinden. Neem contact op met de Immich beheerder voor hulp.", - "asset_restored_successfully": "Asset succesvol hersteld", + "asset_offline": "Item offline", + "asset_offline_description": "Dit externe item is niet meer op de schijf te vinden. Neem contact op met de Immich beheerder voor hulp.", + "asset_restored_successfully": "Item succesvol hersteld", "asset_skipped": "Overgeslagen", "asset_skipped_in_trash": "In prullenbak", + "asset_trashed": "Asset verwijderd", + "asset_troubleshoot": "Asset probleemoplossing", "asset_uploaded": "GeÃŧpload", "asset_uploading": "Uploadenâ€Ļ", - "asset_viewer_settings_subtitle": "Beheer je instellingen voor gallerijweergave", - "asset_viewer_settings_title": "Foto weergave", - "assets": "Assets", - "assets_added_count": "{count, plural, one {# asset} other {# assets}} toegevoegd", - "assets_added_to_album_count": "{count, plural, one {# asset} other {# assets}} aan het album toegevoegd", - "assets_added_to_name_count": "{count, plural, one {# asset} other {# assets}} toegevoegd aan {hasName, select, true {{name}} other {nieuw album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {# asset} other {# assets}} konden niet aan album toegevoegd worden", - "assets_count": "{count, plural, one {# asset} other {# assets}}", - "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", - "assets_deleted_permanently_from_server": "{count} asset(s) permanent verwijderd van de Immich server", + "asset_viewer_settings_subtitle": "Beheer je instellingen voor galerijweergave", + "asset_viewer_settings_title": "Fotoweergave", + "assets": "Items", + "assets_added_count": "{count, plural, one {# item} other {# items}} toegevoegd", + "assets_added_to_album_count": "{count, plural, one {# item} other {# items}} aan het album toegevoegd", + "assets_added_to_albums_count": "{assetTotal, plural, one {# asset} other {# assets}} toegevoegd aan {albumTotal, plural, one {# album} other {#albums}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {# item} other {# items}} konden niet aan album toegevoegd worden", + "assets_cannot_be_added_to_albums": "{count, plural, one {Middel kan} other {Middelen kunnen}} niet toegevoegd worden aan de albums", + "assets_count": "{count, plural, one {# item} other {# items}}", + "assets_deleted_permanently": "{count} item(s) permanent verwijderd", + "assets_deleted_permanently_from_server": "{count} item(s) permanent verwijderd van de Immich server", "assets_downloaded_failed": "{count, plural, one {# bestand gedownload - {error} bestand mislukt} other {# bestanden gedownload - {error} bestanden mislukt}}", "assets_downloaded_successfully": "{count, plural, one {# bestand succesvol gedownload} other {# bestanden succesvol gedownload}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} verplaatst naar prullenbak", - "assets_permanently_deleted_count": "{count, plural, one {# asset} other {# assets}} permanent verwijderd", - "assets_removed_count": "{count, plural, one {# asset} other {# assets}} verwijderd", - "assets_removed_permanently_from_device": "{count} asset(s) permanent verwijderd van je apparaat", - "assets_restore_confirmation": "Weet je zeker dat je alle verwijderde assets wilt herstellen? Je kunt deze actie niet ongedaan maken! Offline assets kunnen op deze manier niet worden hersteld.", - "assets_restored_count": "{count, plural, one {# asset} other {# assets}} hersteld", - "assets_restored_successfully": "{count} asset(s) succesvol hersteld", - "assets_trashed": "{count} asset(s) naar de prullenbak verplaatst", - "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} naar prullenbak verplaatst", - "assets_trashed_from_server": "{count} asset(s) naar de prullenbak verplaatst op de Immich server", - "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets waren}} al onderdeel van het album", + "assets_moved_to_trash_count": "{count, plural, one {# item} other {# items}} verplaatst naar prullenbak", + "assets_permanently_deleted_count": "{count, plural, one {# item} other {# items}} permanent verwijderd", + "assets_removed_count": "{count, plural, one {# item} other {# items}} verwijderd", + "assets_removed_permanently_from_device": "{count} item(s) permanent verwijderd van je apparaat", + "assets_restore_confirmation": "Weet je zeker dat je alle verwijderde items wilt herstellen? Je kunt deze actie niet ongedaan maken! Offline items kunnen op deze manier niet worden hersteld.", + "assets_restored_count": "{count, plural, one {# item} other {# items}} hersteld", + "assets_restored_successfully": "{count} item(s) succesvol hersteld", + "assets_trashed": "{count} item(s) naar de prullenbak verplaatst", + "assets_trashed_count": "{count, plural, one {# item} other {# items}} naar prullenbak verplaatst", + "assets_trashed_from_server": "{count} item(s) naar de prullenbak verplaatst op de Immich server", + "assets_were_part_of_album_count": "{count, plural, one {Item was} other {Items waren}} al onderdeel van het album", + "assets_were_part_of_albums_count": "{count, plural, one {Middel is} other {Middelen zijn}} al onderdeel van de albums", "authorized_devices": "Geautoriseerde apparaten", "automatic_endpoint_switching_subtitle": "Maak een lokale verbinding bij het opgegeven WiFi-netwerk en gebruik in andere gevallen de externe URL", "automatic_endpoint_switching_title": "Automatische serverwissel", "autoplay_slideshow": "Diavoorstelling automatisch afspelen", "back": "Terug", "back_close_deselect": "Terug, sluiten of deselecteren", + "background_backup_running_error": "Achtergrond backup draait, handmatige backup kan niet worden gestart", "background_location_permission": "Achtergrond locatie toestemming", "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het WiFi-netwerk te kunnen lezen", + "background_options": "Achtergrond opties", + "backup": "Back-up", "backup_album_selection_page_albums_device": "Albums op apparaat ({count})", - "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", - "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.", - "backup_album_selection_page_select_albums": "Albums selecteren", + "backup_album_selection_page_albums_tap": "Tik om op te nemen, dubbel tik om uit te sluiten", + "backup_album_selection_page_assets_scatter": "Items kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het back-up proces.", + "backup_album_selection_page_select_albums": "Selecteer albums", "backup_album_selection_page_selection_info": "Selectie info", - "backup_album_selection_page_total_assets": "Totaal unieke assets", + "backup_album_selection_page_total_assets": "Totaal unieke items", + "backup_albums_sync": "Backup albums synchronisatie", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Fout bij back-uppen assets. Opnieuw proberenâ€Ļ", - "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberenâ€Ļ", - "backup_background_service_current_upload_notification": "{filename} aan het uploaden...", - "backup_background_service_default_notification": "Controleren op nieuwe assetsâ€Ļ", - "backup_background_service_error_title": "Backupfout", - "backup_background_service_in_progress_notification": "Back-up van assets makenâ€Ļ", + "backup_background_service_backup_failed_message": "Fout bij het back-uppen van de items. Opnieuw proberenâ€Ļ", + "backup_background_service_connection_failed_message": "Fout bij het verbinden met de server. Opnieuw proberenâ€Ļ", + "backup_background_service_current_upload_notification": "{filename} wordt geÃŧpload", + "backup_background_service_default_notification": "Controleren op nieuwe itemsâ€Ļ", + "backup_background_service_error_title": "Back-up fout", + "backup_background_service_in_progress_notification": "Back-up van items makenâ€Ļ", "backup_background_service_upload_failure_notification": "Fout bij het uploaden van {filename}", "backup_controller_page_albums": "Back-up albums", - "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via Instellingen > Algemeen > Ververs op achtergrond, om back-ups op de achtergrond te maken.", - "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld", + "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via 'Instellingen > Algemeen > Ververs op achtergrond', om back-ups op de achtergrond te maken.", + "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond is uitgeschakeld", "backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen", "backup_controller_page_background_battery_info_link": "Laat zien hoe", - "backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", + "backup_controller_page_background_battery_info_message": "Voor de beste achtergrond back-up ervaring schakelt u alle batterij optimalisaties uit die de achtergrondactiviteit voor Immich kunnen beperken.\n\nAangezien dit apparaat specifiek is, raden we aan om de vereiste informatie op te zoeken bij de fabrikant van je apparaat.", "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Batterijoptimalisaties", + "backup_controller_page_background_battery_info_title": "Batterij optimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", - "backup_controller_page_background_configure_error": "Achtergrondserviceconfiguratie mislukt", - "backup_controller_page_background_delay": "Back-upvertraging voor nieuwe assets: {duration}", - "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe assets zonder de app te hoeven openen", - "backup_controller_page_background_is_off": "Automatische achtergrondback-up staat uit", - "backup_controller_page_background_is_on": "Automatische achtergrondback-up staat aan", + "backup_controller_page_background_configure_error": "Achtergrondservice configuratie mislukt", + "backup_controller_page_background_delay": "Back-up vertraging voor nieuwe items: {duration}", + "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe items zonder de app te hoeven openen", + "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", "backup_controller_page_background_turn_off": "Achtergrondservice uitzetten", "backup_controller_page_background_turn_on": "Achtergrondservice aanzetten", "backup_controller_page_background_wifi": "Alleen op WiFi", @@ -525,7 +579,7 @@ "backup_controller_page_backup_selected": "Geselecteerd: ", "backup_controller_page_backup_sub": "Geback-upte foto's en video's", "backup_controller_page_created": "Gemaakt op: {date}", - "backup_controller_page_desc_backup": "Schakel back-up op de voorgrond in om automatisch nieuwe assets naar de server te uploaden bij het openen van de app.", + "backup_controller_page_desc_backup": "Schakel back-up op de voorgrond in om automatisch nieuwe items naar de server te uploaden bij het openen van de app.", "backup_controller_page_excluded": "Uitgezonderd: ", "backup_controller_page_failed": "Mislukt ({count})", "backup_controller_page_filename": "Bestandsnaam: {filename} [{size}]", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Back-up op de voorgrond aanzetten", "backup_controller_page_uploading_file_info": "Bestandsgegevens uploaden", "backup_err_only_album": "Kan het enige album niet verwijderen", + "backup_error_sync_failed": "Synchronisatie mislukt. Kan back-up niet verwerken.", "backup_info_card_assets": "bestanden", "backup_manual_cancelled": "Geannuleerd", "backup_manual_in_progress": "Het uploaden is al bezig. Probeer het na een tijdje", "backup_manual_success": "Succes", "backup_manual_title": "Uploadstatus", + "backup_options": "Backup opties", "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", + "backup_settings_subtitle": "Beheer upload instellingen", "backward": "Achteruit", "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", "biometric_locked_out": "Biometrische authenticatie is vergrendeld", @@ -563,15 +620,15 @@ "bugs_and_feature_requests": "Bugs & functieverzoeken", "build": "Bouwen", "build_image": "Build image", - "bulk_delete_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} in bulk wilt verwijderen? Dit zal de grootste asset van elke groep behouden en alle andere duplicaten permanent verwijderen. Je kunt deze actie niet ongedaan maken!", - "bulk_keep_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} wilt behouden? Dit zal alle groepen met duplicaten oplossen zonder iets te verwijderen.", - "bulk_trash_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# duplicate asset} other {# duplicate assets}} in bulk naar de prullenbak wilt verplaatsen? Dit zal de grootste asset van elke groep behouden en alle andere duplicaten naar de prullenbak verplaatsen.", + "bulk_delete_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# dubbel item} other {# dubbele items}} in bulk wilt verwijderen? Dit zal de grootste item van elke groep behouden en alle andere duplicaten permanent verwijderen. Je kunt deze actie niet ongedaan maken!", + "bulk_keep_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# dubbel item} other {# dubbele items}} wilt behouden? Dit zal alle groepen met duplicaten oplossen zonder iets te verwijderen.", + "bulk_trash_duplicates_confirmation": "Weet je zeker dat je {count, plural, one {# dubbel item} other {# dubbele items}} in bulk naar de prullenbak wilt verplaatsen? Dit zal de grootste item van elke groep behouden en alle andere duplicaten naar de prullenbak verplaatsen.", "buy": "Immich kopen", "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", - "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", - "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", + "cache_settings_duplicated_assets_subtitle": "Foto’s en video's die de app negeert", + "cache_settings_duplicated_assets_title": "Gedupliceerde items ({count})", "cache_settings_statistics_album": "Bibliotheekthumbnails", "cache_settings_statistics_full": "Volledige afbeeldingen", "cache_settings_statistics_shared": "Gedeeld-albumthumbnails", @@ -587,6 +644,7 @@ "cancel": "Annuleren", "cancel_search": "Zoeken annuleren", "canceled": "Geannuleerd", + "canceling": "Annuleren", "cannot_merge_people": "Kan mensen niet samenvoegen", "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", @@ -609,15 +667,18 @@ "change_pin_code": "Wijzig PIN code", "change_your_password": "Wijzig je wachtwoord", "changed_visibility_successfully": "Zichtbaarheid succesvol gewijzigd", - "check_corrupt_asset_backup": "Controleer op corrupte back-ups van assets", + "charging": "Opladen", + "charging_requirement_mobile_backup": "Achtergrond backup vereist dat het apparaat wordt opgeladen", + "check_corrupt_asset_backup": "Controleer op corrupte back-ups van items", "check_corrupt_asset_backup_button": "Controle uitvoeren", - "check_corrupt_asset_backup_description": "Voer deze controle alleen uit via WiFi en nadat alle assets zijn geback-upt. De procedure kan een paar minuten duren.", + "check_corrupt_asset_backup_description": "Voer deze controle alleen uit via WiFi en nadat alle items zijn geback-upt. De procedure kan een paar minuten duren.", "check_logs": "Controleer logboek", "choose_matching_people_to_merge": "Kies overeenkomende mensen om samen te voegen", "city": "Stad", "clear": "Wissen", "clear_all": "Alles wissen", "clear_all_recent_searches": "Wis alle recente zoekopdrachten", + "clear_file_cache": "Bestandcache leegmaken", "clear_message": "Bericht wissen", "clear_value": "Waarde wissen", "client_cert_dialog_msg_confirm": "OK", @@ -643,9 +704,9 @@ "completed": "Voltooid", "confirm": "Bevestigen", "confirm_admin_password": "Bevestig beheerder wachtwoord", - "confirm_delete_face": "Weet je zeker dat je {name} gezicht wilt verwijderen uit de asset?", + "confirm_delete_face": "Weet je zeker dat je het gezicht van {name} wilt verwijderen uit het item?", "confirm_delete_shared_link": "Weet je zeker dat je deze gedeelde link wilt verwijderen?", - "confirm_keep_this_delete_others": "Alle andere assets in de stack worden verwijderd, behalve deze. Weet je zeker dat je wilt doorgaan?", + "confirm_keep_this_delete_others": "Alle andere items in de stack worden verwijderd, behalve deze. Weet je zeker dat je wilt doorgaan?", "confirm_new_pin_code": "Bevestig nieuwe PIN code", "confirm_password": "Bevestig wachtwoord", "confirm_tag_face": "Wil je dit gezicht taggen als {name}?", @@ -662,7 +723,7 @@ "control_bottom_app_bar_edit_time": "Datum & tijd bewerken", "control_bottom_app_bar_share_link": "Link delen", "control_bottom_app_bar_share_to": "Delen met", - "control_bottom_app_bar_trash_from_immich": "Naar prullenbak", + "control_bottom_app_bar_trash_from_immich": "Verwijderen van Immich", "copied_image_to_clipboard": "Afbeelding gekopieerd naar klembord.", "copied_to_clipboard": "Gekopieerd naar klembord!", "copy_error": "Fout bij kopiÃĢren", @@ -674,7 +735,7 @@ "copy_to_clipboard": "KopiÃĢren naar klembord", "country": "Land", "cover": "Bedekken", - "covers": "Covers", + "covers": "Omslagen", "create": "Aanmaken", "create_album": "Album aanmaken", "create_album_page_untitled": "Naamloos", @@ -684,26 +745,29 @@ "create_link_to_share_description": "Laat iedereen met de link de geselecteerde foto(s) zien", "create_new": "MAAK NIEUW", "create_new_person": "Nieuwe persoon aanmaken", - "create_new_person_hint": "Geselecteerde assets toewijzen aan een nieuwe persoon", + "create_new_person_hint": "Geselecteerde items toewijzen aan een nieuwe persoon", "create_new_user": "Nieuwe gebruiker aanmaken", - "create_shared_album_page_share_add_assets": "ASSETS TOEVOEGEN", + "create_shared_album_page_share_add_assets": "ITEMS TOEVOEGEN", "create_shared_album_page_share_select_photos": "Selecteer foto's", + "create_shared_link": "Gedeelde link maken", "create_tag": "Tag aanmaken", "create_tag_description": "Maak een nieuwe tag. Voor geneste tags, voer het volledige pad van de tag in, inclusief schuine strepen.", "create_user": "Gebruiker aanmaken", "created": "Aangemaakt", "created_at": "Aangemaakt", + "creating_linked_albums": "Gekoppelde albums worden aangemaakt...", "crop": "Bijsnijden", "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", "current_pin_code": "Huidige PIN code", - "current_server_address": "Huidige serveradres", + "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", + "custom_url": "Aangepaste URL", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", - "darkTheme": "Donker thema in-/uitschakelen", + "dark_theme": "Wissel naar donker thema", "date_after": "Datum na", "date_and_time": "Datum en tijd", "date_before": "Datum voor", @@ -711,14 +775,17 @@ "date_of_birth_saved": "Geboortedatum succesvol opgeslagen", "date_range": "Datumbereik", "day": "Dag", + "days": "Dagen", "deduplicate_all": "Alles dedupliceren", "deduplication_criteria_1": "Grootte van afbeelding in bytes", "deduplication_criteria_2": "Aantal EXIF data", "deduplication_info": "Deduplicatie-info", - "deduplication_info_description": "Om automatisch bezittingen te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", + "deduplication_info_description": "Om automatisch items te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", + "delete_action_confirmation_message": "Weet je zeker dat je dit item wilt verwijderen? Deze actie zorgt ervoor dat het item naar de prullenbak van de server wordt verplaatst en je wordt gevraagd of je deze ook lokaal wilt verwijderen", + "delete_action_prompt": "{count} item(s) verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -732,36 +799,41 @@ "delete_key": "Verwijder key", "delete_library": "Verwijder bibliotheek", "delete_link": "Verwijder link", + "delete_local_action_prompt": "{count} item(s) lokaal verwijderd", "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", + "delete_permanently": "Permanent verwijderen", + "delete_permanently_action_prompt": "{count} item(s) permanent verwijderd", "delete_shared_link": "Verwijder gedeelde link", "delete_shared_link_dialog_title": "Verwijder gedeelde link", "delete_tag": "Tag verwijderen", "delete_tag_confirmation_prompt": "Weet je zeker dat je de tag {tagName} wilt verwijderen?", "delete_user": "Verwijder gebruiker", "deleted_shared_link": "Gedeelde link verwijderd", - "deletes_missing_assets": "Verwijdert assets die ontbreken op de schijf", + "deletes_missing_assets": "Verwijdert items die ontbreken op de schijf", "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", + "deselect_all": "Alles deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", "disallow_edits": "Geen bewerkingen toestaan", "discord": "Discord", - "discover": "Zoeken", + "discover": "Zoek", "discovered_devices": "Gevonden apparaten", "dismiss_all_errors": "Negeer alle fouten", "dismiss_error": "Negeer fout", "display_options": "Weergaveopties", "display_order": "Weergavevolgorde", "display_original_photos": "Toon originele foto's", - "display_original_photos_setting_description": "Geef de voorkeur aan het weergeven van de originele foto bij het bekijken van een asset in plaats van thumbnails wanneer de originele asset webcompatibel is. Dit kan resulteren in lagere weergavesnelheid van foto's.", + "display_original_photos_setting_description": "Geef de voorkeur aan het weergeven van de originele foto bij het bekijken van een item in plaats van thumbnails wanneer het originele item compatibel is. Dit kan resulteren in een lagere weergavesnelheid van foto's.", "do_not_show_again": "Laat dit bericht niet meer zien", "documentation": "Documentatie", "done": "Klaar", "download": "Downloaden", + "download_action_prompt": "{count} item(s) aan het downloaden", "download_canceled": "Download geannuleerd", "download_complete": "Download voltooid", "download_enqueue": "Download in wachtrij", @@ -769,17 +841,17 @@ "download_failed": "Download mislukt", "download_finished": "Download voltooid", "download_include_embedded_motion_videos": "Ingesloten video's", - "download_include_embedded_motion_videos_description": "Voeg video's toe die ingesloten zijn in bewegende foto's als een apart bestand", + "download_include_embedded_motion_videos_description": "Voeg video's die in bewegingsfoto's zijn ingebed toe als een apart bestand", "download_notfound": "Download niet gevonden", "download_paused": "Download gepauseerd", "download_settings": "Downloaden", - "download_settings_description": "Beheer instellingen voor het downloaden van assets", + "download_settings_description": "Beheer instellingen voor het downloaden van items", "download_started": "Download gestart", "download_sucess": "Succesvol gedownload", "download_sucess_android": "Het bestand is gedownload naar DCIM/Immich", "download_waiting_to_retry": "Wachten om opnieuw te proberen", "downloading": "Downloaden", - "downloading_asset_filename": "Asset {filename} downloaden", + "downloading_asset_filename": "Downloaden asset {filename}", "downloading_media": "Media aan het downloaden", "drop_files_to_upload": "Zet bestanden ergens neer om ze te uploaden", "duplicates": "Duplicaten", @@ -788,8 +860,12 @@ "edit": "Bewerken", "edit_album": "Album bewerken", "edit_avatar": "Avatar bewerken", + "edit_birthday": "Wijzig verjaardag", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", + "edit_date_and_time_action_prompt": "Datum en tijd bijgewerkt van {count} item(s)", + "edit_date_and_time_by_offset": "Wijzigen datum door verschuiving", + "edit_date_and_time_by_offset_interval": "Nieuw datuminterval: {from}-{to}", "edit_description": "Beschrijving bewerken", "edit_description_prompt": "Selecteer een nieuwe beschrijving:", "edit_exclusion_pattern": "Uitsluitingspatroon bewerken", @@ -799,6 +875,7 @@ "edit_key": "Key bewerken", "edit_link": "Link bewerken", "edit_location": "Locatie bewerken", + "edit_location_action_prompt": "Locatie bijgewerkt van {count} item(s)", "edit_location_dialog_title": "Locatie", "edit_name": "Naam bewerken", "edit_people": "Mensen bewerken", @@ -815,8 +892,9 @@ "email_notifications": "E-mailmeldingen", "empty_folder": "Deze map is leeg", "empty_trash": "Prullenbak leegmaken", - "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle assets in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", + "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle items in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", "enable": "Inschakelen", + "enable_backup": "Back-up aanzetten", "enable_biometric_auth_description": "Voer uw pincode in om biometrische authenticatie in te schakelen", "enabled": "Ingeschakeld", "end_date": "Einddatum", @@ -826,61 +904,65 @@ "enter_your_pin_code_subtitle": "Voer uw pincode in om toegang te krijgen tot de vergrendelde map", "error": "Fout", "error_change_sort_album": "Sorteervolgorde van album wijzigen mislukt", - "error_delete_face": "Fout bij verwijderen gezicht uit asset", + "error_delete_face": "Fout bij verwijderen van gezicht uit het item", + "error_getting_places": "Fout bij ophalen plaatsen", "error_loading_image": "Fout bij laden afbeelding", + "error_loading_partners": "Fout bij ophalen partners: {error}", "error_saving_image": "Fout: {error}", "error_tag_face_bounding_box": "Fout bij taggen van gezicht - kan coÃļrdinaten van omvattend kader niet ophalen", "error_title": "Fout - Er is iets misgegaan", "errors": { - "cannot_navigate_next_asset": "Kan niet naar de volgende asset navigeren", - "cannot_navigate_previous_asset": "Kan niet naar vorige asset navigeren", + "cannot_navigate_next_asset": "Kan niet naar het volgende item navigeren", + "cannot_navigate_previous_asset": "Kan niet naar het vorige item navigeren", "cant_apply_changes": "Kan wijzigingen niet toepassen", "cant_change_activity": "Kan activiteit niet {enabled, select, true {uitschakelen} other {inschakelen}}", - "cant_change_asset_favorite": "Kan asset niet toevoegen aan of verwijderen uit favorieten", - "cant_change_metadata_assets_count": "Kan metadata van {count, plural, one {# asset} other {# assets}} niet wijzigen", + "cant_change_asset_favorite": "Kan item niet toevoegen aan of verwijderen uit favorieten", + "cant_change_metadata_assets_count": "Kan metadata van {count, plural, one {# item} other {# items}} niet wijzigen", "cant_get_faces": "Kan gezichten niet ophalen", "cant_get_number_of_comments": "Kan het aantal opmerkingen niet ophalen", "cant_search_people": "Kan mensen niet zoeken", "cant_search_places": "Kan plaatsen niet zoeken", - "error_adding_assets_to_album": "Fout bij toevoegen van assets aan album", + "error_adding_assets_to_album": "Fout bij toevoegen van items aan album", "error_adding_users_to_album": "Fout bij toevoegen van gebruikers aan album", "error_deleting_shared_user": "Fout bij verwijderen van gedeelde gebruiker", "error_downloading": "Fout bij downloaden {filename}", "error_hiding_buy_button": "Fout bij het verbergen van de koop knop", - "error_removing_assets_from_album": "Fout bij verwijderen van assets uit album, controleer de console voor meer details", - "error_selecting_all_assets": "Fout bij selecteren van alle assets", + "error_removing_assets_from_album": "Fout bij het verwijderen van items uit het album, controleer de console voor meer details", + "error_selecting_all_assets": "Fout bij selecteren van alle items", "exclusion_pattern_already_exists": "Dit uitsluitingspatroon bestaat al.", "failed_to_create_album": "Fout bij maken van album", "failed_to_create_shared_link": "Fout bij maken van gedeelde link", "failed_to_edit_shared_link": "Fout bij bewerken van gedeelde link", "failed_to_get_people": "Fout bij ophalen van mensen", - "failed_to_keep_this_delete_others": "Het is niet gelukt om dit asset te behouden en de andere assets te verwijderen", - "failed_to_load_asset": "Kan asset niet laden", - "failed_to_load_assets": "Kan assets niet laden", + "failed_to_keep_this_delete_others": "Het is niet gelukt om dit item te behouden en de andere items te verwijderen", + "failed_to_load_asset": "Kan item niet laden", + "failed_to_load_assets": "Kan items niet laden", "failed_to_load_notifications": "Kon meldingen niet laden", "failed_to_load_people": "Kan mensen niet laden", "failed_to_remove_product_key": "Fout bij het verwijderen van de licentiesleutel", - "failed_to_stack_assets": "Fout bij stapelen van assets", - "failed_to_unstack_assets": "Fout bij ontstapelen van assets", + "failed_to_reset_pin_code": "Resetten van PIN code mislukt", + "failed_to_stack_assets": "Fout bij stapelen van items", + "failed_to_unstack_assets": "Fout bij ontstapelen van items", "failed_to_update_notification_status": "Kon notificatiestatus niet updaten", "import_path_already_exists": "Dit import-pad bestaat al.", "incorrect_email_or_password": "Onjuist e-mailadres of wachtwoord", "paths_validation_failed": "validatie van {paths, plural, one {# pad} other {# paden}} mislukt", "profile_picture_transparent_pixels": "Profielfoto's kunnen geen transparante pixels bevatten. Zoom in en/of verplaats de afbeelding.", "quota_higher_than_disk_size": "Je hebt een opslaglimiet ingesteld die hoger is dan de schijfgrootte", + "something_went_wrong": "Er liep iets mis", "unable_to_add_album_users": "Kan gebruikers niet aan album toevoegen", - "unable_to_add_assets_to_shared_link": "Kan assets niet aan gedeelde link toevoegen", + "unable_to_add_assets_to_shared_link": "Kan items niet aan gedeelde link toevoegen", "unable_to_add_comment": "Kan geen opmerking toevoegen", "unable_to_add_exclusion_pattern": "Kan geen uitsluitingspatroon toevoegen", "unable_to_add_import_path": "Kan geen import-pad toevoegen", "unable_to_add_partners": "Kan geen partners toevoegen", - "unable_to_add_remove_archive": "Kan assets niet {archived, select, true {verwijderen uit} other {toevoegen aan}} archief", - "unable_to_add_remove_favorites": "Kan assets niet {favorite, select, true {toevoegen aan} other {verwijderen uit}} favorieten", + "unable_to_add_remove_archive": "Kan items niet {archived, select, true {verwijderen uit} other {toevoegen aan}} archief", + "unable_to_add_remove_favorites": "Kan items niet {favorite, select, true {toevoegen aan} other {verwijderen uit}} favorieten", "unable_to_archive_unarchive": "Kan niet {archived, select, true {toevoegen aan} other {verwijderen uit}} archief", "unable_to_change_album_user_role": "Kan rol van de albumgebruiker niet wijzigen", "unable_to_change_date": "Kan datum niet wijzigen", "unable_to_change_description": "Beschrijving kan niet worden gewijzigd", - "unable_to_change_favorite": "Kan asset niet toevoegen aan of verwijderen uit favorieten", + "unable_to_change_favorite": "Kan item niet toevoegen aan of verwijderen uit favorieten", "unable_to_change_location": "Kan locatie niet wijzigen", "unable_to_change_password": "Kan wachtwoord niet veranderen", "unable_to_change_visibility": "Kan de zichtbaarheid van {count, plural, one {# persoon} other {# mensen}} niet wijzigen", @@ -892,8 +974,8 @@ "unable_to_create_library": "Kan bibliotheek niet aanmaken", "unable_to_create_user": "Kan geen gebruiker aanmaken", "unable_to_delete_album": "Kan album niet verwijderen", - "unable_to_delete_asset": "Kan asset niet verwijderen", - "unable_to_delete_assets": "Fout bij verwijderen assets", + "unable_to_delete_asset": "Kan item niet verwijderen", + "unable_to_delete_assets": "Fout bij verwijderen items", "unable_to_delete_exclusion_pattern": "Kan uitsluitingspatroon niet verwijderen", "unable_to_delete_import_path": "Kan import-pad niet verwijderen", "unable_to_delete_shared_link": "Kan gedeelde link niet verwijderen", @@ -913,19 +995,19 @@ "unable_to_log_out_device": "Kan apparaat niet uitloggen", "unable_to_login_with_oauth": "Kan niet inloggen met OAuth", "unable_to_play_video": "Kan video niet afspelen", - "unable_to_reassign_assets_existing_person": "Kan assets niet opnieuw toewijzen aan {name, select, null {een bestaand persoon} other {{name}}}", - "unable_to_reassign_assets_new_person": "Kan assets niet opnieuw toewijzen aan een nieuw persoon", + "unable_to_reassign_assets_existing_person": "Kan items niet opnieuw toewijzen aan {name, select, null {een bestaand persoon} other {{name}}}", + "unable_to_reassign_assets_new_person": "Kan items niet opnieuw toewijzen aan een nieuw persoon", "unable_to_refresh_user": "Kan gebruiker niet vernieuwen", "unable_to_remove_album_users": "Kan gebruiker niet van album verwijderen", "unable_to_remove_api_key": "Kan API-sleutel niet verwijderen", - "unable_to_remove_assets_from_shared_link": "Kan assets niet verwijderen uit gedeelde link", + "unable_to_remove_assets_from_shared_link": "Kan items niet verwijderen uit gedeelde link", "unable_to_remove_library": "Kan bibliotheek niet verwijderen", "unable_to_remove_partner": "Kan partner niet verwijderen", "unable_to_remove_reaction": "Kan reactie niet verwijderen", "unable_to_reset_password": "Kan wachtwoord niet resetten", "unable_to_reset_pin_code": "Kan PIN code niet resetten", "unable_to_resolve_duplicate": "Kan duplicaat niet oplossen", - "unable_to_restore_assets": "Kan assets niet herstellen", + "unable_to_restore_assets": "Kan items niet herstellen", "unable_to_restore_trash": "Kan niet herstellen uit prullenbak", "unable_to_restore_user": "Kan gebruiker niet herstellen", "unable_to_save_album": "Kan album niet opslaan", @@ -939,10 +1021,10 @@ "unable_to_set_feature_photo": "Kan uitgelichte foto niet instellen", "unable_to_set_profile_picture": "Kan profielfoto niet instellen", "unable_to_submit_job": "Kan taak niet uitvoeren", - "unable_to_trash_asset": "Kan asset niet naar prullenbak verplaatsen", + "unable_to_trash_asset": "Kan item niet naar prullenbak verplaatsen", "unable_to_unlink_account": "Kan account niet ontkoppelen", "unable_to_unlink_motion_video": "Kan bewegende video niet ontkoppelen", - "unable_to_update_album_cover": "Kan album cover niet bijwerken", + "unable_to_update_album_cover": "Kan albumomslag niet bijwerken", "unable_to_update_album_info": "Kan albumgegevens niet bijwerken", "unable_to_update_library": "Kan bibliotheek niet bijwerken", "unable_to_update_location": "Kan locatie niet bijwerken", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Beschrijving toevoegen...", + "exif_bottom_sheet_description_error": "Fout bij het bijwerken van de beschrijving", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", "exif_bottom_sheet_people": "MENSEN", "exif_bottom_sheet_person_add_person": "Naam toevoegen", - "exif_bottom_sheet_person_age_months": "Leeftijd {months} maanden", - "exif_bottom_sheet_person_age_year_months": "Leeftijd 1 jaar, {months} maanden", - "exif_bottom_sheet_person_age_years": "Leeftijd {years}", "exit_slideshow": "Diavoorstelling sluiten", "expand_all": "Alles uitvouwen", "experimental_settings_new_asset_list_subtitle": "Werk in uitvoering", @@ -973,6 +1053,8 @@ "explorer": "Verkenner", "export": "Exporteren", "export_as_json": "Exporteren als JSON", + "export_database": "Exporteer database", + "export_database_description": "Exporteer de SQLite database", "extension": "Extensie", "external": "Extern", "external_libraries": "Externe bibliotheken", @@ -981,14 +1063,16 @@ "face_unassigned": "Niet toegewezen", "failed": "Mislukt", "failed_to_authenticate": "Authenticatie mislukt", - "failed_to_load_assets": "Kan assets niet laden", + "failed_to_load_assets": "Kan items niet laden", "failed_to_load_folder": "Laden van map mislukt", "favorite": "Favoriet", + "favorite_action_prompt": "{count} item(s) toegevoegd aan je favorieten", "favorite_or_unfavorite_photo": "Foto markeren als of verwijderen uit favorieten", "favorites": "Favorieten", - "favorites_page_no_favorites": "Geen favoriete assets gevonden", + "favorites_page_no_favorites": "Geen favoriete items gevonden", "feature_photo_updated": "Uitgelichte afbeelding bijgewerkt", "features": "Functies", + "features_in_development": "Functies in ontwikkeling", "features_setting_description": "Beheer de app functies", "file_name": "Bestandsnaam", "file_name_or_extension": "Bestandsnaam of extensie", @@ -998,21 +1082,26 @@ "filter_people": "Filter op mensen", "filter_places": "Filter locaties", "find_them_fast": "Vind ze snel op naam door te zoeken", + "first": "Eerste", "fix_incorrect_match": "Onjuiste overeenkomst corrigeren", "folder": "Map", "folder_not_found": "Map niet gevonden", "folders": "Mappen", "folders_feature_description": "Bladeren door de mapweergave van de foto's en video's op het bestandssysteem", + "forgot_pin_code_question": "PIN vergeten?", "forward": "Vooruit", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Deze functie gebruikt externe bronnen van Google om te kunnen werken.", "general": "Algemeen", + "geolocation_instruction_location": "Klik op een item met GPS coÃļrdinaten om de locatie te gebruiken, of selecteer een locatie direct vanaf de kaart", "get_help": "Krijg hulp", "get_wifiname_error": "Kon de WiFi-naam niet ophalen. Zorg ervoor dat je de benodigde machtigingen hebt verleend en verbonden bent met een WiFi-netwerk", "getting_started": "Aan de slag", "go_back": "Ga terug", "go_to_folder": "Ga naar map", "go_to_search": "Ga naar zoeken", + "gps": "GPS", + "gps_missing": "Geen GPS", "grant_permission": "Geef toestemming", "group_albums_by": "Groepeer albums op...", "group_country": "Groepeer op land", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", + "hash_asset": "Hash item", + "hashed_assets": "Gehashte items", + "hashing": "Hashen", "header_settings_add_header_tip": "Header toevoegen", "header_settings_field_validator_msg": "Waarde kan niet leeg zijn", "header_settings_header_name_input": "Header naam", @@ -1031,30 +1123,32 @@ "headers_settings_tile_title": "Aangepaste proxy headers", "hi_user": "Hallo {name} ({email})", "hide_all_people": "Verberg alle mensen", - "hide_gallery": "Gallerij verbergen", + "hide_gallery": "Galerij verbergen", "hide_named_person": "Verberg persoon {name}", "hide_password": "Verberg wachtwoord", "hide_person": "Verberg persoon", "hide_unnamed_people": "Verberg mensen zonder naam", - "home_page_add_to_album_conflicts": "{added} assets toegevoegd aan album {album}. {failed} assets staan al in het album.", - "home_page_add_to_album_err_local": "Lokale assets kunnen nog niet aan albums worden toegevoegd, overslaan", - "home_page_add_to_album_success": "{added} assets toegevoegd aan album {album}.", - "home_page_album_err_partner": "Partner assets kunnen nog niet toegevoegd worden aan een album, overslaan", - "home_page_archive_err_local": "Lokale assets kunnen nog niet gearchiveerd worden, overslaan", - "home_page_archive_err_partner": "Partner assets kunnen niet gearchiveerd worden, overslaan", + "home_page_add_to_album_conflicts": "{added} items toegevoegd aan album {album}. {failed} items staan al in het album.", + "home_page_add_to_album_err_local": "Lokale items kunnen nog niet aan albums worden toegevoegd, overslaan", + "home_page_add_to_album_success": "{added} items toegevoegd aan album {album}.", + "home_page_album_err_partner": "Partner items kunnen nog niet toegevoegd worden aan een album, overslaan", + "home_page_archive_err_local": "Lokale items kunnen nog niet gearchiveerd worden, overslaan", + "home_page_archive_err_partner": "Partner items kunnen niet gearchiveerd worden, overslaan", "home_page_building_timeline": "Tijdlijn opbouwen", - "home_page_delete_err_partner": "Partner assets kunnen niet verwijderd worden, overslaan", - "home_page_delete_remote_err_local": "Lokale assets staan in verwijder selectie externe assets, overslaan", - "home_page_favorite_err_local": "Lokale assets kunnen nog niet als favoriet worden aangemerkt, overslaan", - "home_page_favorite_err_partner": "Partner assets kunnen nog niet ge-favoriet worden, overslaan", + "home_page_delete_err_partner": "Partner items kunnen niet verwijderd worden, overslaan", + "home_page_delete_remote_err_local": "Lokale items staan in verwijder selectie externe items, overslaan", + "home_page_favorite_err_local": "Lokale items kunnen nog niet als favoriet worden aangemerkt, overslaan", + "home_page_favorite_err_partner": "Partner items kunnen nog niet ge-favoriet worden, overslaan", "home_page_first_time_notice": "Als dit de eerste keer is dat je de app gebruikt, zorg er dan voor dat je een back-up album kiest, zodat de tijdlijn gevuld kan worden met foto's en video's uit het album", "home_page_locked_error_local": "Kan lokale bestanden niet naar de vergrendelde map verplaatsen, sla over", "home_page_locked_error_partner": "Kan partnerbestanden niet naar de vergrendelde map verplaatsen, sla over", - "home_page_share_err_local": "Lokale assets kunnen niet via een link gedeeld worden, overslaan", - "home_page_upload_err_limit": "Kan maximaal 30 assets tegelijk uploaden, overslaan", + "home_page_share_err_local": "Lokale items kunnen niet via een link gedeeld worden, overslaan", + "home_page_upload_err_limit": "Kan maximaal 30 items tegelijk uploaden, overslaan", "host": "Host", "hour": "Uur", + "hours": "Uren", "id": "ID", + "idle": "Inactief", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geÃŧpload naar de Immich server", "image": "Afbeelding", @@ -1080,7 +1174,7 @@ "in_archive": "In archief", "include_archived": "Toon gearchiveerde", "include_shared_albums": "Toon gedeelde albums", - "include_shared_partner_assets": "Toon assets van gedeelde partner", + "include_shared_partner_assets": "Toon items van gedeelde partner", "individual_share": "Individuele deellink", "individual_shares": "Individuele deellinks", "info": "Info", @@ -1105,17 +1199,20 @@ "keep": "Behouden", "keep_all": "Behoud alle", "keep_this_delete_others": "Deze behouden, andere verwijderen", - "kept_this_deleted_others": "Deze asset behouden en {count, plural, one {# andere asset} other {# andere assets}} verwijderd", + "kept_this_deleted_others": "Dit item behouden en {count, plural, one {# ander item} other {# andere items}} verwijderd", "keyboard_shortcuts": "Sneltoetsen", "language": "Taal", "language_no_results_subtitle": "Probeer je zoekterm aan te passen", "language_no_results_title": "Geen talen gevonden", "language_search_hint": "Zoek talen...", "language_setting_description": "Selecteer je voorkeurstaal", + "large_files": "Grote bestanden", + "last": "Laatste", "last_seen": "Laatst gezien", "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", "leave": "Verlaten", + "leave_album": "Verlaat album", "lens_model": "Lensmodel", "let_others_respond": "Laat anderen reageren", "level": "Niveau", @@ -1123,20 +1220,24 @@ "library_options": "Bibliotheek opties", "library_page_device_albums": "Albums op apparaat", "library_page_new_album": "Nieuw album", - "library_page_sort_asset_count": "Aantal assets", + "library_page_sort_asset_count": "Aantal items", "library_page_sort_created": "Meest recent gemaakt", "library_page_sort_last_modified": "Laatst aangepast", "library_page_sort_title": "Albumtitel", + "licenses": "Licenties", "light": "Licht", + "like": "Vind ik leuk", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", - "link_options": "Opties voor koppeling", "link_to_oauth": "Koppel OAuth", "linked_oauth_account": "Gekoppeld OAuth account", "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", - "local_asset_cast_failed": "Kan geen asset casten die nog niet geÃŧpload is naar de server", + "local": "Lokaal", + "local_asset_cast_failed": "Kan geen item casten die nog niet geÃŧpload is naar de server", + "local_assets": "Lokale Items", + "local_media_summary": "Lokale media samenvatting", "local_network": "Lokaal netwerk", "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatietoestemming", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Voer hier je lengtegraad in", "lock": "Vergrendel", "locked_folder": "Vergrendelde map", + "log_detail_title": "Log details", "log_out": "Uitloggen", "log_out_all_devices": "Uitloggen op alle apparaten", "logged_in_as": "Ingelogd als {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Wachtwoord succesvol bijgewerkt", "logout_all_device_confirmation": "Weet je zeker dat je wilt uitloggen op alle apparaten?", "logout_this_device_confirmation": "Weet je zeker dat je wilt uitloggen op dit apparaat?", + "logs": "Logs", "longitude": "Lengtegraad", "look": "Uiterlijk", "loop_videos": "Video's herhalen", @@ -1185,6 +1288,7 @@ "main_branch_warning": "Je gebruikt een ontwikkelingsversie. We raden je ten zeerste aan een releaseversie te gebruiken!", "main_menu": "Hoofdmenu", "make": "Merk", + "manage_geolocation": "Beheer locatie", "manage_shared_links": "Beheer gedeelde links", "manage_sharing_with_partners": "Beheer delen met partners", "manage_the_app_settings": "Beheer de appinstellingen", @@ -1193,17 +1297,15 @@ "manage_your_devices": "Beheer je ingelogde apparaten", "manage_your_oauth_connection": "Beheer je OAuth-koppeling", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto's", + "map_assets_in_bounds": "{count, plural, =0 {Geen fotos in dit gebied}one {# foto} other {# foto's}}", "map_cannot_get_user_location": "Kan locatie van de gebruiker niet ophalen", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Gebruik deze locatie", - "map_location_service_disabled_content": "Locatie service moet ingeschakeld zijn om assets van je huidige locatie weer te geven. Wil je het nu inschakelen?", + "map_location_service_disabled_content": "Locatie service moet ingeschakeld zijn om items van je huidige locatie weer te geven. Wil je het nu inschakelen?", "map_location_service_disabled_title": "Locatie service uitgeschakeld", "map_marker_for_images": "Kaartmarkering voor afbeeldingen gemaakt in {city}, {country}", "map_marker_with_image": "Kaartmarkering met afbeelding", - "map_no_assets_in_bounds": "Geen foto's in dit gebied", - "map_no_location_permission_content": "Locatietoestemming is nodig om assets van je huidige locatie weer te geven. Wil je dit nu toestaan?", + "map_no_location_permission_content": "Locatietoestemming is nodig om items van je huidige locatie weer te geven. Wil je dit nu toestaan?", "map_no_location_permission_title": "Locatietoestemming geweigerd", "map_settings": "Kaartinstellingen", "map_settings_dark_mode": "Donkere modus", @@ -1221,6 +1323,7 @@ "mark_as_read": "Markeren als gelezen", "marked_all_as_read": "Allen gemarkeerd als gelezen", "matches": "Overeenkomsten", + "matching_assets": "Overeenkomende assets", "media_type": "Mediatype", "memories": "Herinneringen", "memories_all_caught_up": "Je bent helemaal bij", @@ -1239,6 +1342,7 @@ "merged_people_count": "{count, plural, one {# person} other {# mensen}} samengevoegd", "minimize": "Minimaliseren", "minute": "Minuut", + "minutes": "Minuten", "missing": "Missend", "model": "Model", "month": "Maand", @@ -1246,17 +1350,22 @@ "more": "Meer", "move": "Verplaats", "move_off_locked_folder": "Verplaats uit vergrendelde map", + "move_to_lock_folder_action_prompt": "{count} item(s) toegevoegd aan de vergrendelde map", "move_to_locked_folder": "Verplaats naar vergrendelde map", "move_to_locked_folder_confirmation": "Deze foto’s en video’s worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map", - "moved_to_archive": "{count, plural, one {# asset} other {# assets}} verplaatst naar archief", - "moved_to_library": "{count, plural, one {# asset} other {# assets}} verplaatst naar bibliotheek", + "moved_to_archive": "{count, plural, one {# item} other {# items}} verplaatst naar archief", + "moved_to_library": "{count, plural, one {# item} other {# items}} verplaatst naar bibliotheek", "moved_to_trash": "Naar de prullenbak verplaatst", - "multiselect_grid_edit_date_time_err_read_only": "Kan datum van alleen-lezen asset(s) niet wijzigen, overslaan", - "multiselect_grid_edit_gps_err_read_only": "Kan locatie van alleen-lezen asset(s) niet wijzigen, overslaan", + "multiselect_grid_edit_date_time_err_read_only": "Kan datum van alleen-lezen item(s) niet wijzigen, overslaan", + "multiselect_grid_edit_gps_err_read_only": "Kan locatie van alleen-lezen item(s) niet wijzigen, overslaan", "mute_memories": "Herrinneringen dempen", "my_albums": "Mijn albums", "name": "Naam", "name_or_nickname": "Naam of gebruikersnaam", + "network_requirement_photos_upload": "Gebruik mobiele data voor de backup van foto's", + "network_requirement_videos_upload": "Gebruik mobiele data voor de backups van video's", + "network_requirements": "Netwerk vereisten", + "network_requirements_updated": "Netwerkeisen zijn gewijzigd, back-upwachtrij wordt opnieuw ingesteld", "networking_settings": "Netwerk", "networking_subtitle": "Beheer de instellingen voor de server-URL", "never": "Nooit", @@ -1266,6 +1375,7 @@ "new_person": "Nieuw persoon", "new_pin_code": "Nieuwe PIN code", "new_pin_code_subtitle": "Dit is de eerste keer dat u de vergrendelde map opent. Stel een pincode in om deze pagina veilig te openen", + "new_timeline": "Nieuwe tijdlijn", "new_user_created": "Nieuwe gebruiker aangemaakt", "new_version_available": "NIEUWE VERSIE BESCHIKBAAR", "newest_first": "Nieuwste eerst", @@ -1279,22 +1389,28 @@ "no_assets_message": "KLIK HIER OM JE EERSTE FOTO TE UPLOADEN", "no_assets_to_show": "Geen foto's om te laten zien", "no_cast_devices_found": "Geen cast-apparaten gevonden", + "no_checksum_local": "Geen checksum beschikbaar - kan lokale assets niet ophalen", + "no_checksum_remote": "Geen checksum beschikbaar - kan online assets niet ophalen", "no_duplicates_found": "Er zijn geen duplicaten gevonden.", "no_exif_info_available": "Geen exif info beschikbaar", "no_explore_results_message": "Upload meer foto's om je verzameling te verkennen.", "no_favorites_message": "Voeg favorieten toe om snel je beste foto's en video's te vinden", "no_libraries_message": "Maak een externe bibliotheek om je foto's en video's te bekijken", + "no_local_assets_found": "Geen lokale assets gevonden met deze checksum", "no_locked_photos_message": "Foto’s en video’s in de vergrendelde map zijn verborgen en worden niet weergegeven wanneer je door je bibliotheek bladert of zoekt.", "no_name": "Geen naam", "no_notifications": "Geen meldingen", "no_people_found": "Geen mensen gevonden", "no_places": "Geen plaatsen", + "no_remote_assets_found": "Geen online assets gevonden met deze checksum", "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", + "no_uploads_in_progress": "Geen uploads bezig", + "not_available": "N.B.", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", - "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade assets, voer de volgende taak uit", + "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geÃŧploade items, voer de volgende taak uit", "notes": "Opmerkingen", "nothing_here_yet": "Hier staan nog geen items", "notification_permission_dialog_content": "Om meldingen in te schakelen, ga naar Instellingen en selecteer toestaan.", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "OfficiÃĢle Immich bronnen", "offline": "Offline", + "offset": "Verrekening", "ok": "Ok", "oldest_first": "Oudste eerst", "on_this_device": "Op dit apparaat", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Open de zoekfilters", "options": "Opties", "or": "of", + "organize_into_albums": "Organiseren in albums", + "organize_into_albums_description": "Bestaande foto's in albums plaatsen met de huidige synchronisatie-instellingen", "organize_your_library": "Organiseer je bibliotheek", "original": "origineel", "other": "Overige", "other_devices": "Andere apparaten", + "other_entities": "Andere entities", "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", @@ -1366,12 +1486,12 @@ "people_feature_description": "Bladeren door foto's en video's gegroepeerd op personen", "people_sidebar_description": "Toon een link naar Mensen in de zijbalk", "permanent_deletion_warning": "Waarschuwing voor permanent verwijderen", - "permanent_deletion_warning_setting_description": "Toon een waarschuwing bij het permanent verwijderen van assets", + "permanent_deletion_warning_setting_description": "Toon een waarschuwing bij het permanent verwijderen van items", "permanently_delete": "Permanent verwijderen", - "permanently_delete_assets_count": "{count, plural, one {Asset} other {Assets}} permanent verwijderen", - "permanently_delete_assets_prompt": "Weet je zeker dat je deze {count, plural, one {asset} other {# assets}} permanent wilt verwijderen? Hiermee {count, plural, one {wordt} other {worden}} deze ook uit de bijbehorende album(s) verwijderd.", - "permanently_deleted_asset": "Asset permanent verwijderd", - "permanently_deleted_assets_count": "{count, plural, one {# asset} other {# assets}} permanent verwijderd", + "permanently_delete_assets_count": "{count, plural, one {Item} other {Items}} permanent verwijderen", + "permanently_delete_assets_prompt": "Weet je zeker dat je {count, plural, one {dit item} other {deze # items}} permanent wilt verwijderen? Hiermee {count, plural, one {wordt} other {worden}} deze ook uit de bijbehorende album(s) verwijderd.", + "permanently_deleted_asset": "Item permanent verwijderd", + "permanently_deleted_assets_count": "{count, plural, one {# item} other {# items}} permanent verwijderd", "permission": "Rechten", "permission_empty": "Je rechten mogen niet leeg zijn", "permission_onboarding_back": "Terug", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Beperkte toestemming. Geef toestemming tot foto's en video's in Instellingen om Immich een back-up te laten maken van je galerij en deze te beheren.", "permission_onboarding_request": "Immich heeft toestemming nodig om je foto's en video's te bekijken.", "person": "Persoon", + "person_age_months": "{months, plural, one {# maand} other {# maanden}} oud", + "person_age_year_months": "1 year, {months, plural, one {# maand} other {# maanden}} oud", + "person_age_years": "{years, plural, other {# jaar}} oud", "person_birthdate": "Geboren op {date}", "person_hidden": "{name}{hidden, select, true { (verborgen)} other {}}", "photo_shared_all_users": "Het lijkt erop dat je foto's met alle gebruikers zijn gedeeld, of dat je geen gebruikers hebt om mee te delen.", @@ -1406,6 +1529,7 @@ "port": "Poort", "preferences_settings_subtitle": "Beheer de voorkeuren van de app", "preferences_settings_title": "Voorkeuren", + "preparing": "Voorbereiden", "preset": "Voorinstelling", "preview": "Voorbeeld", "previous": "Vorige", @@ -1417,18 +1541,19 @@ "primary": "Primair", "privacy": "Privacy", "profile": "Profiel", - "profile_drawer_app_logs": "Logboek", + "profile_drawer_app_logs": "Logs", "profile_drawer_client_out_of_date_major": "Mobiele app is verouderd. Werk bij naar de nieuwste hoofdversie.", "profile_drawer_client_out_of_date_minor": "Mobiele app is verouderd. Werk bij naar de nieuwste subversie.", "profile_drawer_client_server_up_to_date": "App en server zijn up-to-date", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Alleen-lezen-modus ingeschakeld. Druk lang op je profielfoto om te verlaten.", "profile_drawer_server_out_of_date_major": "Server is verouderd. Werk bij naar de nieuwste hoofdversie.", "profile_drawer_server_out_of_date_minor": "Server is verouderd. Werk bij naar de nieuwste subversie.", "profile_image_of_user": "Profielfoto van {user}", "profile_picture_set": "Profielfoto ingesteld.", "public_album": "Openbaar album", "public_share": "Openbare deellink", - "purchase_account_info": "Supporter", + "purchase_account_info": "Ondersteuner", "purchase_activated_subtitle": "Bedankt voor het ondersteunen van Immich en open-source software", "purchase_activated_time": "Geactiveerd op {date}", "purchase_activated_title": "Je licentiesleutel is succesvol geactiveerd", @@ -1460,16 +1585,21 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", + "query_asset_id": "Query Asset ID", + "queue_status": "Wachtrij {count}/{total}", "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", "rating_count": "{count, plural, one {# ster} other {# sterren}}", "rating_description": "De EXIF-waardering weergeven in het infopaneel", "reaction_options": "Reactie-opties", "read_changelog": "Lees wijzigingen", + "readonly_mode_disabled": "Alleen-lezen modus uitgeschakeld", + "readonly_mode_enabled": "Alleen-lezen modus ingeschakeld", + "ready_for_upload": "Klaar voor upload", "reassign": "Opnieuw toewijzen", - "reassigned_assets_to_existing_person": "{count, plural, one {# asset} other {# assets}} opnieuw toegewezen aan {name, select, null {een bestaand persoon} other {{name}}}", - "reassigned_assets_to_new_person": "{count, plural, one {# asset} other {# assets}} opnieuw toegewezen aan een nieuw persoon", - "reassing_hint": "Geselecteerde assets toewijzen aan een bestaand persoon", + "reassigned_assets_to_existing_person": "{count, plural, one {# item} other {# items}} opnieuw toegewezen aan {name, select, null {een bestaand persoon} other {{name}}}", + "reassigned_assets_to_new_person": "{count, plural, one {# item} other {# items}} opnieuw toegewezen aan een nieuw persoon", + "reassing_hint": "Geselecteerde items toewijzen aan een bestaand persoon", "recent": "Recent", "recent-albums": "Recente albums", "recent_searches": "Recente zoekopdrachten", @@ -1488,14 +1618,19 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", + "remote": "Externe", + "remote_assets": "Externe Items", + "remote_media_summary": "Online media samenvatting", "remove": "Verwijderen", - "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", - "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", - "remove_assets_title": "Assets verwijderen?", + "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# item} other {# items}} uit het album wilt verwijderen?", + "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# item} other {# items}} uit deze gedeelde link wilt verwijderen?", + "remove_assets_title": "Items verwijderen?", "remove_custom_date_range": "Aangepast datumbereik verwijderen", "remove_deleted_assets": "Verwijder offline bestanden", - "remove_from_album": "Verwijder uit album", + "remove_from_album": "Verwijderen uit album", + "remove_from_album_action_prompt": "{count} item(s) verwijderd uit het album", "remove_from_favorites": "Verwijderen uit favorieten", + "remove_from_lock_folder_action_prompt": "{count} item(s) verwijderd uit de vergrendelde map", "remove_from_locked_folder": "Verwijder uit de vergrendelde map", "remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.", "remove_from_shared_link": "Verwijderen uit gedeelde link", @@ -1510,7 +1645,7 @@ "removed_from_favorites_count": "{count, plural, other {# verwijderd}} uit favorieten", "removed_memory": "Herinnering verwijderd", "removed_photo_from_memory": "Foto verwijderd uit herinnering", - "removed_tagged_assets": "Tag verwijderd van {count, plural, one {# asset} other {# assets}}", + "removed_tagged_assets": "Tag verwijderd van {count, plural, one {# item} other {# items}}", "rename": "Hernoemen", "repair": "Repareren", "repair_no_results_message": "Niet bijgehouden en ontbrekende bestanden zullen hier verschijnen", @@ -1523,19 +1658,29 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", + "reset_pin_code_description": "Als je jouw PIN code bent vergeten, neem dan contact op met de administrator van de server om deze te resetten", + "reset_pin_code_success": "Resetten van PIN code gelukt", + "reset_pin_code_with_password": "Je kan altijd je PIN code resetten met je wachtwoord", + "reset_sqlite": "SQLite database resetten", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moeten uitloggen om de data opnieuw te synchroniseren", + "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", + "restore_trash_action_prompt": "{count} item(s) teruggehaald uit de prullenbak", "restore_user": "Gebruiker herstellen", - "restored_asset": "Asset hersteld", + "restored_asset": "Item hersteld", "resume": "Hervatten", + "resume_paused_jobs": "Hervat {count, plural, one {# gepauseerde taak} other {# gepauseerde taken}}", "retry_upload": "Opnieuw uploaden", "review_duplicates": "Controleer duplicaten", + "review_large_files": "Grote bestanden beoordelen", "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", + "running": "Actief", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", "saved_api_key": "API-sleutel opgeslagen", @@ -1582,7 +1727,7 @@ "search_page_motion_photos": "Bewegende foto's", "search_page_no_objects": "Geen objectgegevens beschikbaar", "search_page_no_places": "Geen locatiegegevens beschikbaar", - "search_page_screenshots": "Screenshots", + "search_page_screenshots": "Schermafbeelding", "search_page_search_photos_videos": "Zoek naar je foto's en video's", "search_page_selfies": "Selfies", "search_page_things": "Dingen", @@ -1605,7 +1750,7 @@ "second": "Seconde", "see_all_people": "Bekijk alle mensen", "select": "Selecteer", - "select_album_cover": "Selecteer album cover", + "select_album_cover": "Selecteer albumomslag", "select_all": "Alles selecteren", "select_all_duplicates": "Selecteer alle duplicaten", "select_all_in": "Selecteer alles in {group}", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Album aanmaken mislukt", "selected": "Geselecteerd", "selected_count": "{count, plural, other {# geselecteerd}}", + "selected_gps_coordinates": "Geselecteerde GPS CoÃļrdinaten", "send_message": "Bericht versturen", "send_welcome_email": "Stuur welkomstmail", "server_endpoint": "Server-URL", @@ -1633,13 +1779,13 @@ "server_stats": "Serverstatistieken", "server_version": "Serverversie", "set": "Instellen", - "set_as_album_cover": "Stel in als album cover", + "set_as_album_cover": "Stel in als albumomslag", "set_as_featured_photo": "Instellen als uitgelichte foto", "set_as_profile_picture": "Instellen als profielfoto", "set_date_of_birth": "Geboortedatum instellen", "set_profile_picture": "Profielfoto instellen", "set_slideshow_to_fullscreen": "Diavoorstelling op volledig scherm", - "set_stack_primary_asset": "Instellen als primaire asset", + "set_stack_primary_asset": "Instellen als primair item", "setting_image_viewer_help": "De gedetailleerde weergave laadt eerst de kleine thumbnail, vervolgens het middelgrote voorbeeld (indien ingeschakeld) en ten slotte het origineel (indien ingeschakeld).", "setting_image_viewer_original_subtitle": "Schakel in om de originele afbeelding met volledige resolutie (groot!) te laden. Schakel uit om datagebruik te verminderen (zowel netwerk als apparaatcache).", "setting_image_viewer_original_title": "Originele afbeelding laden", @@ -1654,10 +1800,10 @@ "setting_notifications_notify_minutes": "{count} minuten", "setting_notifications_notify_never": "nooit", "setting_notifications_notify_seconds": "{count} seconden", - "setting_notifications_single_progress_subtitle": "Gedetailleerde informatie over de uploadvoortgang per asset", + "setting_notifications_single_progress_subtitle": "Gedetailleerde informatie over de uploadvoortgang per item", "setting_notifications_single_progress_title": "Gedetailleerde informatie over achtergrond back-ups tonen", "setting_notifications_subtitle": "Voorkeuren voor meldingen beheren", - "setting_notifications_total_progress_subtitle": "Algehele uploadvoortgang (voltooid/totaal aantal assets)", + "setting_notifications_total_progress_subtitle": "Algehele uploadvoortgang (voltooid/totaal aantal items)", "setting_notifications_total_progress_title": "Totale voortgang van achtergrond back-up tonen", "setting_video_viewer_looping_title": "Herhalen", "setting_video_viewer_original_video_subtitle": "Speel video's altijd in originele kwaliteit af, zelfs als er een getranscodeerd bestand beschikbaar is op de server. Dit kan leiden tot buffering. Video's die lokaal beschikbaar zijn, worden altijd in originele kwaliteit afgespeeld, ongeacht deze instelling.", @@ -1667,8 +1813,9 @@ "settings_saved": "Instellingen opgeslagen", "setup_pin_code": "Stel een PIN code in", "share": "Delen", + "share_action_prompt": "{count} item(s) gedeeld", "share_add_photos": "Foto's toevoegen", - "share_assets_selected": "{count} geselecteerd", + "share_assets_selected": "{count} item(s) geselecteerd", "share_dialog_preparing": "Voorbereiden...", "share_link": "Link delen", "shared": "Gedeeld", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Gekopieerd naar klembord", "shared_link_clipboard_text": "Link: {link}\nWachtwoord: {password}", "shared_link_create_error": "Fout bij het maken van een gedeelde link", + "shared_link_custom_url_description": "Krijg toegang tot deze gedeelde link met een aangepaste URL", "shared_link_edit_description_hint": "Voer beschrijving voor de gedeelde link in", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dagen", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Beheer gedeelde links", "shared_link_options": "Opties voor gedeelde links", + "shared_link_password_description": "Vraag een wachtwoord om toegang te krijgen tot deze gedeelde link", "shared_links": "Gedeelde links", "shared_links_description": "Deel foto's en video's via een link", "shared_photos_and_videos_count": "{assetCount, plural, other {# gedeelde foto's & video's.}}", @@ -1726,7 +1875,7 @@ "sharing_sidebar_description": "Toon een link naar Delen in de zijbalk", "sharing_silver_appbar_create_shared_album": "Gedeeld album maken", "sharing_silver_appbar_share_partner": "Delen met partner", - "shift_to_permanent_delete": "druk op ⇧ om assets permanent te verwijderen", + "shift_to_permanent_delete": "druk op ⇧ om items permanent te verwijderen", "show_album_options": "Toon albumopties", "show_albums": "Toon albums", "show_all_people": "Toon alle mensen", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Diavoorstellingsovergang tonen", "show_supporter_badge": "Supportersbadge", "show_supporter_badge_description": "Toon een supportersbadge", + "show_text_search_menu": "Laat tekst zoek menu zien", "shuffle": "Willekeurig", "sidebar": "Zijbalk", "sidebar_display_description": "Toon een link naar deze pagina in de zijbalk", @@ -1762,19 +1912,22 @@ "sort_created": "Datum aangemaakt", "sort_items": "Aantal items", "sort_modified": "Datum aangepast", + "sort_newest": "Nieuwste foto", "sort_oldest": "Oudste foto", "sort_people_by_similarity": "Sorteer personen op gelijkenis", "sort_recent": "Meest recente foto", "sort_title": "Titel", "source": "Bron", "stack": "Stapel", + "stack_action_prompt": "{count} item(s) gestapeld", "stack_duplicates": "Stapel duplicaten", "stack_select_one_photo": "Selecteer ÊÊn primaire foto voor de stapel", "stack_selected_photos": "Geselecteerde foto's stapelen", - "stacked_assets_count": "{count, plural, one {# asset} other {# assets}} gestapeld", + "stacked_assets_count": "{count, plural, one {# item} other {# items}} gestapeld", "stacktrace": "Stacktrace", "start": "Start", "start_date": "Startdatum", + "start_date_before_end_date": "Startdatum moet voor einddatum liggen", "state": "Staat", "status": "Status", "stop_casting": "Stop met casten", @@ -1787,6 +1940,7 @@ "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", + "success": "Succes", "suggestions": "Suggesties", "sunrise_on_the_beach": "Zonsopkomst op het strand", "support": "Ondersteuning", @@ -1796,22 +1950,27 @@ "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geÃŧploade video’s en foto’s naar de geselecteerde back-up albums", + "sync_local": "Lokaal synchroniseren", + "sync_remote": "Op afstand synchroniseren", + "sync_status": "Sync Status", + "sync_status_subtitle": "Bekijk en beheer het synchronisatie systeem", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", "tag": "Tag", - "tag_assets": "Assets taggen", + "tag_assets": "Items taggen", "tag_created": "Tag aangemaakt: {tag}", "tag_feature_description": "Bladeren door foto's en video's gegroepeerd op tags", "tag_not_found_question": "Kun je een tag niet vinden? Maak een nieuwe tag.", "tag_people": "Mensen taggen", "tag_updated": "Tag bijgewerkt: {tag}", - "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", + "tagged_assets": "{count, plural, one {# item} other {# items}} getagd", "tags": "Tags", + "tap_to_run_job": "Klik om job te starten", "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", "theme_selection_description": "Stel het thema automatisch in op licht of donker op basis van de systeemvoorkeuren van je browser", - "theme_setting_asset_list_storage_indicator_title": "Toon opslag indicator bij de asset tegels", - "theme_setting_asset_list_tiles_per_row_title": "Aantal assets per rij ({count})", + "theme_setting_asset_list_storage_indicator_title": "Toon opslag indicator bij de item tegels", + "theme_setting_asset_list_tiles_per_row_title": "Aantal items per rij ({count})", "theme_setting_colorful_interface_subtitle": "Pas primaire kleuren toe op achtergronden.", "theme_setting_colorful_interface_title": "Kleurrijke interface", "theme_setting_image_viewer_quality_subtitle": "De kwaliteit van de gedetailleerde-fotoweergave aanpassen", @@ -1832,32 +1991,38 @@ "to_change_password": "Wijzig wachtwoord", "to_favorite": "Toevoegen aan favorieten", "to_login": "Inloggen", + "to_multi_select": "naar multi-select", "to_parent": "Ga naar hoofdmap", + "to_select": "naar selecteren", "to_trash": "Prullenbak", "toggle_settings": "Zichtbaarheid instellingen wisselen", "total": "Totaal", "total_usage": "Totaal gebruik", "trash": "Prullenbak", + "trash_action_prompt": "{count} item(s) verplaatst naar de prullenbak", "trash_all": "Verplaats alle naar prullenbak", "trash_count": "{count, number} naar prullenbak", - "trash_delete_asset": "Assets naar prullenbak verplaatsen of verwijderen", + "trash_delete_asset": "Items naar prullenbak verplaatsen of verwijderen", "trash_emptied": "Prullenbak geleegd", "trash_no_results_message": "Hier verschijnen foto's en video's die in de prullenbak zijn geplaatst.", "trash_page_delete_all": "Verwijder alle", "trash_page_empty_trash_dialog_content": "Wil je de prullenbak leegmaken? Deze items worden permanent verwijderd van Immich", "trash_page_info": "Verwijderde items worden permanent verwijderd na {days} dagen", - "trash_page_no_assets": "Geen verwijderde assets", + "trash_page_no_assets": "Geen verwijderde items", "trash_page_restore_all": "Herstel alle", - "trash_page_select_assets_btn": "Selecteer assets", + "trash_page_select_assets_btn": "Selecteer items", "trash_page_title": "Prullenbak ({count})", "trashed_items_will_be_permanently_deleted_after": "Items in de prullenbak worden na {days, plural, one {# dag} other {# dagen}} permanent verwijderd.", + "troubleshoot": "Problemen oplossen", "type": "Type", "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", "unarchive": "Herstellen uit archief", + "unarchive_action_prompt": "{count} verwijderd uit het archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", "undo": "Ongedaan maken", "unfavorite": "Verwijderen uit favorieten", + "unfavorite_action_prompt": "{count} verwijderd uit je favorieten", "unhide_person": "Persoon zichtbaar maken", "unknown": "Onbekend", "unknown_country": "Onbekend Land", @@ -1875,23 +2040,30 @@ "unselect_all_duplicates": "Deselecteer alle duplicaten", "unselect_all_in": "Deselecteer alles in {group}", "unstack": "Ontstapelen", - "unstacked_assets_count": "{count, plural, one {# asset} other {# assets}} ontstapeld", + "unstack_action_prompt": "{count} item(s) ontstapeld", + "unstacked_assets_count": "{count, plural, one {# item} other {# items}} ontstapeld", + "untagged": "Ongemarkeerd", "up_next": "Volgende", + "update_location_action_prompt": "Werk de locatie bij van {count} geselecteerde items met:", "updated_at": "GeÃŧpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", - "upload_concurrency": "Upload gelijktijdigheid", - "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", - "upload_dialog_title": "Asset uploaden", - "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", + "upload_action_prompt": "{count} item(s) staan in de wachtrij voor uploaden", + "upload_concurrency": "Aantal gelijktijdige uploads", + "upload_details": "Uploaddetails", + "upload_dialog_info": "Wil je een backup maken van de geselecteerde item(s) op de server?", + "upload_dialog_title": "Item uploaden", + "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe items te zien.", + "upload_finished": "Uploaden is voltooid", "upload_progress": "Resterend {remaining, number} - Verwerkt {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {# duplicate asset} other {# duplicate assets}} overgeslagen", + "upload_skipped_duplicates": "{count, plural, one {# duplicate item} other {# duplicate items}} overgeslagen", "upload_status_duplicates": "Duplicaten", "upload_status_errors": "Fouten", "upload_status_uploaded": "GeÃŧpload", - "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", + "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe items te zien.", "upload_to_immich": "Uploaden naar Immich ({count})", "uploading": "Aan het uploaden", + "uploading_media": "Media wordt geÃŧpload", "url": "URL", "usage": "Gebruik", "use_biometric": "Gebruik biometrische authenticatie", @@ -1900,7 +2072,7 @@ "user": "Gebruiker", "user_has_been_deleted": "Deze gebruiker is verwijderd.", "user_id": "Gebruikers ID", - "user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} asset {deze asset} other {dit}} geliket", + "user_liked": "{user} heeft {type, select, photo {deze foto} video {deze video} asset {} other {dit item}} geliket", "user_pin_code_settings": "PIN Code", "user_pin_code_settings_description": "Beheer je PIN code", "user_privacy": "Gebruikersprivacy", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Bekijk statistieken van accountgebruik", "username": "Gebruikersnaam", "users": "Gebruikers", + "users_added_to_album_count": "{count, plural, one {# Gebruiker} other {# Gebruikers}} toegevoegd aan album", "utilities": "Gereedschap", "validate": "Valideren", "validate_endpoint_error": "Vul een geldige URL in", @@ -1930,17 +2103,19 @@ "view_album": "Bekijk album", "view_all": "Bekijk alle", "view_all_users": "Bekijk alle gebruikers", + "view_details": "Bekijk details", "view_in_timeline": "Bekijk in tijdlijn", "view_link": "Bekijk link", "view_links": "Links bekijken", "view_name": "Bekijken", - "view_next_asset": "Bekijk volgende asset", - "view_previous_asset": "Bekijk vorige asset", + "view_next_asset": "Bekijk volgend item", + "view_previous_asset": "Bekijk vorig item", "view_qr_code": "QR-code bekijken", + "view_similar_photos": "Bekijk vergelijkbare foto's", "view_stack": "Bekijk stapel", "view_user": "Bekijk gebruiker", - "viewer_remove_from_stack": "Verwijder van Stapel", - "viewer_stack_use_as_main_asset": "Gebruik als Hoofd Asset", + "viewer_remove_from_stack": "Verwijder van stapel", + "viewer_stack_use_as_main_asset": "Zet bovenaan de stapel", "viewer_unstack": "Ontstapel", "visibility_changed": "Zichtbaarheid gewijzigd voor {count, plural, one {# persoon} other {# mensen}}", "waiting": "Wachtend", @@ -1955,5 +2130,6 @@ "yes": "Ja", "you_dont_have_any_shared_links": "Je hebt geen gedeelde links", "your_wifi_name": "Je WiFi-naam", - "zoom_image": "Inzoomen" + "zoom_image": "Inzoomen", + "zoom_to_bounds": "Zoom naar randen" } diff --git a/i18n/nn.json b/i18n/nn.json index 7fb5fdef02..7b2f256c94 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -14,6 +14,7 @@ "add_a_location": "Legg til ein stad", "add_a_name": "Legg til eit namn", "add_a_title": "Legg til ein tittel", + "add_birthday": "Legg til ein fødselsdag", "add_endpoint": "Legg til endepunkt", "add_exclusion_pattern": "Legg til unnlatingsmønster", "add_import_path": "Legg til sti for importering", @@ -22,18 +23,22 @@ "add_partner": "Legg til partnar", "add_path": "Legg til sti", "add_photos": "Legg til bilete", + "add_tag": "Legg til tagg", "add_to": "Legg tilâ€Ļ", - "add_to_album": "Legg til album", + "add_to_album": "Legg til i album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allereie i {album}", - "add_to_shared_album": "Legg til delt album", + "add_to_albums": "Legg til i album", + "add_to_albums_count": "Legg til i album ({count})", + "add_to_shared_album": "Legg til i delt album", "add_url": "Legg til URL", - "added_to_archive": "Lagt til arkiv", - "added_to_favorites": "Lagt til favorittar", - "added_to_favorites_count": "Lagt {count, number} til favorittar", + "added_to_archive": "Lagt til i arkiv", + "added_to_favorites": "Lagt til i favorittar", + "added_to_favorites_count": "La til {count, number} i favorittar", "admin": { "add_exclusion_pattern_description": "Legg til utelatingsmønstre. Du kan bruke jokerteikna *, **, og ? for ÃĨ finne filer som passar mønsteret. For ÃĨ ignorere alle filer i ei mappe kalla \"Raw\", bruk \"Raw\", bruk \"**/Raw/**\". For ÃĨ ignorere alle filer som sluttar pÃĨ \".tif\", bruk \"**/*.tif\". For ÃĨ ignorere ein absolutt sti, bruk \"/path/to/ignore/**\".", - "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger pÃĨ disk og har blitt flytta til papirkurven. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For ÃĨ gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", + "admin_user": "Admin-brukar", + "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger pÃĨ disk og har blitt flytta til papirkorga. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For ÃĨ gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillingar", "authentication_settings_description": "Handsam passord, OAuth, og godkjenningsinnstillingar", "authentication_settings_disable_all": "Er du sikker at du ynskjer ÃĨ gjera alle innloggingsmetodar uverksame? Innlogging vil bli heilt uverksam.", @@ -42,8 +47,15 @@ "backup_database": "Lag tryggingskopi av database", "backup_database_enable_description": "Aktiver tryggingskopiering av database", "backup_keep_last_amount": "Antal tryggingskopiar ÃĨ behalde", + "backup_onboarding_1_description": "sikkerheitskopi i skya eller pÃĨ eit anna fysisk sted.", + "backup_onboarding_2_description": "lokale kopiar pÃĨ andre einingar. Dette inkluderer hovudfilene og backup av desse filene lokalt.", + "backup_onboarding_3_description": "fullstendige kopiar av dine data, inkludert originalfilene. Dette inkluderer 1 utomhus kopi og 2 lokale kopiar.", + "backup_onboarding_description": "Ein 3-2-1 backup-strategi tilrÃĨdast for ÃĨ verne dataa dine. Du bør ha kopiar av dei opplasta bileta/videoane dine samt Immich-databasen, slik at du har ei fleirdelt backup-løysing.", + "backup_onboarding_footer": "Meir informasjon om ÃĨ ta backup av Immich finn du i documentation.", + "backup_onboarding_parts_title": "3-2-1-backup bestÃĨr av:", + "backup_onboarding_title": "Backupar", "backup_settings": "Tryggingskopi-innstillingar", - "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du fÃĨr inga varsling ved feil.", + "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du fÃĨr inga varsling ved feil.", "cleared_jobs": "Rydda jobbar for: {job}", "config_set_by_file": "Oppsettet blir sett av ei oppsettfil", "confirm_delete_library": "Er du sikker at du vil slette biblioteket {library}?", @@ -51,6 +63,7 @@ "confirm_email_below": "For ÃĨ bekrefte, skriv \"{email}\" under", "confirm_reprocess_all_faces": "Er du sikker pÃĨ at du vil behandle alle ansikt pÃĨ nytt? Det vil Ã˛g fjerne namngjevne personar.", "confirm_user_password_reset": "Er du sikker at du vil tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Er du sikker pÃĨ at du vil tilbakestille {user} sin PIN-kode?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Set inn skanningsintervall med cron-formatet. For meir informasjon sjÃĨ t.d. Crontab Guru", @@ -66,6 +79,11 @@ "force_delete_user_warning": "ÅTVARING: Handlinga fjernar brukaren og all data. Du kan ikkje angre, og filane kan ikkje gjenopprettast.", "image_format": "Format", "image_format_description": "WebP gjev mindre filstorleik enn JPEG, men er treigare ÃĨ lage.", + "image_fullsize_description": "Bilete i full storleik utan metadata, i bruk nÃĨr zooma inn", + "image_fullsize_enabled": "Skru pÃĨ generering av bilete i full storleik", + "image_fullsize_enabled_description": "Generer bilete i full storleik for ikkje web-tilpassa formatar. NÃĨr \"Foretrekk", + "image_fullsize_quality_description": "Kvalitet pÃĨ bilete i full storleik frÃĨ 1-100. Høgare er betre, men gjev større filer.", + "image_fullsize_title": "Innstillingar for bilete i full storleik", "image_prefer_embedded_preview": "Bruk helst innebygd førehandsvisning", "image_prefer_embedded_preview_setting_description": "NÃĨr mogleg bruk innebygd førehandsvisning av RAW bilete som inndata til biletehandsaming. For noko bilete kan det gje meir nøyaktige farger, men kvaliteten kjem an pÃĨ kamera og det kan oppstÃĨ komprimeringsartefakt i bilete.", "image_prefer_wide_gamut": "Bruk helst breitt fargespektrum", @@ -104,6 +122,9 @@ "logging_enable_description": "Aktiver loggføring", "logging_level_description": "NÃĨr aktivert, kva loggnivÃĨ ÃĨ bruke.", "logging_settings": "Logging", + "machine_learning_availability_checks_description": "Automatiser oppdaging og prioritet av tilgjengelege maskinlÃĻrings-serverar", + "machine_learning_availability_checks_interval": "Sjekk intervall", + "machine_learning_availability_checks_timeout_description": "Utløpstid i millisekund for tilgjengelegheitssjekk", "machine_learning_clip_model": "CLIP modell", "machine_learning_clip_model_description": "Namnet pÃĨ ein CLIP modell finst her. Merk at du mÃĨ køyre 'Smart Søk'-jobben pÃĨ nytt for alle bilete etter du har forandra modell.", "machine_learning_duplicate_detection": "Duplikatdeteksjon", @@ -121,9 +142,11 @@ "machine_learning_max_detection_distance": "Maksimal oppdagingsverdi", "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_max_recognition_distance_description": "Maksimal forskjell pÃĨ to ansikt for ÃĨ bli rekna som same person, pÃĨ ein skala frÃĨ 0-2. Eit lÃĨgare tal kan hindre to personar i ÃĨ bli rekna som den same, medan eit høgare tal kan hindre at same individ vert merka som to forskjellige personar. Merk at det er lettare ÃĨ slÃĨ saman to personar enn ÃĨ dele Êin person i to, sÃĨ sikt mot den lÃĨge sida av skalaen nÃĨr mogleg.", "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 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_min_recognized_faces_description": "Minste tal pÃĨ gjenkjende fjes for ÃĨ opprette ein person. Aukar ein dette, vert ansiktsgjenkjenninga meir presis, pÃĨ bekostning av auka sjanse for at ansikt ikkje vert tileigna ein person.", "machine_learning_settings": "Innstillingar for maskinlÃĻring", "machine_learning_settings_description": "Administrer maskinlÃĻringsfunksjonar og innstillingar", "machine_learning_smart_search": "Smart Søk", @@ -137,16 +160,50 @@ "map_gps_settings_description": "Administrer innstillingar for kart og GPS (Reversert geokoding)", "map_light_style": "Lys modus", "map_settings": "Kart", + "map_settings_description": "Endre kartinnstillingar", + "map_style_description": "URL til eit style.json-karttema", + "memory_generate_job": "Minne-generering", "metadata_extraction_job": "Hent ut metadata", + "metadata_extraction_job_description": "Hent ut metadata frÃĨ kvart bilete, slik som GPS, ansikt og oppløysing", + "metadata_faces_import_setting": "Skru pÃĨ import av ansikt", + "metadata_faces_import_setting_description": "Importer ansikt frÃĨ bilete sine EXIF-data og sidecar-filer", "metadata_settings": "Metadata Innstillinger", + "metadata_settings_description": "Endre metadata-innstillingar", "migration_job": "Migrasjon", + "migration_job_description": "Overfør miniatyrbilete for bilete og ansikt til den nyaste mappestrukturen", + "nightly_tasks_cluster_faces_setting_description": "Køyr ansiktsgjenkjenning pÃĨ nyleg identifiserte ansikt", + "nightly_tasks_database_cleanup_setting_description": "Fjern gamal, utgÃĨtt data frÃĨ databasen", + "nightly_tasks_generate_memories_setting": "Generer minner", + "nightly_tasks_generate_memories_setting_description": "Lag nye minner frÃĨ bilete", + "nightly_tasks_missing_thumbnails_setting": "Generer manglande miniatyrbilete", + "nightly_tasks_missing_thumbnails_setting_description": "Set bilete utan miniatyrbilete i kø for generering av miniatyrbilete", + "nightly_tasks_settings": "Innstillingar for nattlege jobbar", + "nightly_tasks_settings_description": "Handsam nattlege jobbar", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tidspunktet serveren køyrer nattlege jobbar", "notification_email_from_address": "FrÃĨ adresse", + "notification_email_test_email_failed": "Mislukka sending av test-e-post, sjekk konfigurasjonen din", + "notification_email_test_email_sent": "Det vart sendt ei test-melding til {email}. Sjekk e-posten din.", + "notification_email_username_description": "Brukarnamn for autentisering pÃĨ e-post-serveren", + "notification_enable_email_notifications": "Aktiver e-post-varslingar", "notification_settings": "Varselinnstillingar", + "notification_settings_description": "Endre varslingsinnstillingar, inkludert e-post", "oauth_auto_launch": "Autostart", + "oauth_auto_launch_description": "Start OAuth-innloggingsprosessen automatisk nÃĨr innloggingssida vert opna", + "oauth_auto_register_description": "Registrer nye brukarar automatisk etter innlogging med OAuth", "oauth_button_text": "Tekst pÃĨ knapp", + "oauth_client_secret_description": "Krevjast dersom PKCE (Proof Key for Code Exchange) ikkje støttast av OAuth-tilbydaren", + "oauth_enable_description": "Logg inn med OAuth", + "oauth_settings": "OAuth", + "oauth_settings_description": "Innstillingar for innlogging med OAuth", + "oauth_storage_quota_default": "Standard lagringskvote (GiB)", + "oauth_timeout": "Tidsavbrot pÃĨ førespurnad", + "oauth_timeout_description": "Tidsavbrot for førespurnadar i millisekund", "password_enable_description": "Logg inn med e-post og passord", "password_settings": "Passordinnlogging", + "password_settings_description": "Innstillingar for innlogging med passord", "person_cleanup_job": "Personopprydding", + "quota_size_gib": "Lagringskvote (GiB)", "refreshing_all_libraries": "Laster alle bibliotek opp att", "registration": "Administrator registrering", "registration_description": "Sidan du er den første brukaren pÃĨ systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgÃĨver. Du vil Ã˛g opprette eventuelle nye brukarar.", @@ -164,8 +221,17 @@ "server_settings_description": "Administrer serverinnstillingar", "server_welcome_message": "Velkomstmelding", "server_welcome_message_description": "Ei melding som synast pÃĨ innloggingssida.", + "sidecar_job": "Sidecar-metadata", + "sidecar_job_description": "Oppdag eller synkroniser sidecar-metadata frÃĨ filsystemet", + "slideshow_duration_description": "Antal sekund ÃĨ vise kvart bilete", + "storage_template_date_time_sample": "Døme pÃĨ tid {date}", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", + "storage_template_migration": "Overgang til ny lagringsmal", + "storage_template_migration_job": "Omorganisering etter ny lagringsmal", + "storage_template_settings": "Lagringsmal", "system_settings": "Systeminnstillingar", "template_email_preview": "Førehandsvisning", + "thumbnail_generation_job": "Generer miniatyrbilete", "transcoding_acceleration_nvenc": "NVENC (Krev NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (Krev 7. generasjons Intel CPU eller nyare)", "transcoding_acceleration_rkmpp": "RKMPP (Berre pÃĨ Rockchip SOCer)", @@ -180,7 +246,11 @@ "transcoding_audio_codec": "Lydkodek", "transcoding_audio_codec_description": "Opus er det valet med høgast lydkvalitet, men mindre kompabilitet med gamlare einingar og programvare.", "transcoding_bitrate_description": "Videoar med bitrate over høgste tillatte verdi, eller i eit format som ikkje er tillate", - "transcoding_codecs_learn_more": "For ÃĨ lÃĻre meir om nytta begrep, sjÃĨ FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec." + "transcoding_codecs_learn_more": "For ÃĨ lÃĻre meir om nytta begrep, sjÃĨ FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec.", + "transcoding_constant_rate_factor_description": "Videokvalitet. Vanlege verdiar er 23 for H.264, 28 for HEVC, 31 for VP9, og 35 for AV1. LÃĨgare er betre, men gjev større filer.", + "transcoding_hardware_acceleration": "Maskinvare-akselerasjon", + "transcoding_max_bitrate": "Maksimal bitrate", + "transcoding_optimal_description": "Videoar med for høg oppløysing, eller ikkje i eit godkjend format" }, "admin_email": "Adminisrator E-post", "admin_password": "Administratorpassord", diff --git a/i18n/pl.json b/i18n/pl.json index e84c6f53e6..a97ea701d0 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -5,7 +5,7 @@ "acknowledge": "Zrozumiałem/łam", "action": "Akcja", "action_common_update": "Aktualizuj", - "actions": "Akcje/i", + "actions": "Akcje", "active": "Aktywne", "activity": "Aktywność", "activity_changed": "Aktywność jest {enabled, select, true {włączona} other {wyłączona}}", @@ -14,6 +14,7 @@ "add_a_location": "Dodaj lokalizację", "add_a_name": "Dodaj nazwę", "add_a_title": "Dodaj tytuł", + "add_birthday": "Dodaj datę urodzin", "add_endpoint": "Dodaj punkt końcowy", "add_exclusion_pattern": "Dodaj wzÃŗr wykluczający", "add_import_path": "Dodaj ścieÅŧkę importu", @@ -27,6 +28,10 @@ "add_to_album": "Dodaj do albumu", "add_to_album_bottom_sheet_added": "Dodano do {album}", "add_to_album_bottom_sheet_already_exists": "JuÅŧ jest w {album}", + "add_to_album_bottom_sheet_some_local_assets": "NiektÃŗre lokalne zasoby nie mogły zostać dodane do albumu", + "add_to_album_toggle": "Przełącz wybieranie dla {album}", + "add_to_albums": "Dodaj do albumÃŗw", + "add_to_albums_count": "Dodaj do albumÃŗw ({count})", "add_to_shared_album": "Dodaj do udostępnionego albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano do archiwum", @@ -40,10 +45,17 @@ "authentication_settings_description": "Zarządzaj hasłem, OAuth i innymi ustawienia uwierzytelnienia", "authentication_settings_disable_all": "Czy jesteś pewny, Åŧe chcesz wyłączyć wszystkie metody logowania? Logowanie będzie całkowicie wyłączone.", "authentication_settings_reenable": "Aby ponownie włączyć, uÅŧyj Polecenia serwera.", - "background_task_job": "Zadania w Tle", + "background_task_job": "Zadania w tle", "backup_database": "UtwÃŗrz Zrzut Bazy Danych", "backup_database_enable_description": "Włącz zrzuty bazy danych", "backup_keep_last_amount": "Ile poprzednich zrzutÃŗw przechowywać", + "backup_onboarding_1_description": "kopia offsite w chmurze lub w innej fizycznej lokalizacji.", + "backup_onboarding_2_description": "kopie lokalne na rÃŗÅŧnych urządzeniach. Obejmuje to gÅ‚Ãŗwne pliki i lokalną kopię zapasową tych plikÃŗw.", + "backup_onboarding_3_description": "wszystkie kopie danych, w tym oryginalne pliki. Obejmuje to 1 kopię zewnętrzną i 2 kopie lokalne.", + "backup_onboarding_description": "W celu ochrony danych zalecana jest strategia tworzenia kopii zapasowych 3-2-1. Powinieneś zachować kopie przesłanych zdjęć/filmÃŗw, a wraz z nimi bazę danych Immich, aby uzyskać kompleksowe rozwiązanie przy tworzeniu kopii zapasowych.", + "backup_onboarding_footer": "Więcej informacji na temat tworzenia kopii zapasowych Immich moÅŧna znaleÅēć w dokumentacji.", + "backup_onboarding_parts_title": "Kopia zapasowa 3-2-1 obejmuje:", + "backup_onboarding_title": "Kopie zapasowe", "backup_settings": "Ustawienia zrzutu bazy danych", "backup_settings_description": "Zarządzanie ustawieniami zrzutu bazy danych.", "cleared_jobs": "Usunięto zadania dla: {job}", @@ -112,6 +124,13 @@ "logging_enable_description": "Uruchom zapisywanie logÃŗw", "logging_level_description": "Kiedy włączone, jakiego poziomu uÅŧyć.", "logging_settings": "Rejestrowanie logÃŗw", + "machine_learning_availability_checks": "Sprawdzanie dostępności", + "machine_learning_availability_checks_description": "Automatyczne wykrywaj i preferuj dostępne serwery uczenia maszynowego", + "machine_learning_availability_checks_enabled": "Włącz sprawdzanie dostępności", + "machine_learning_availability_checks_interval": "Częstotliwość sprawdzania", + "machine_learning_availability_checks_interval_description": "Odstęp czasu w milisekundach między sprawdzeniami dostępności", + "machine_learning_availability_checks_timeout": "Upłynął czas Åŧądania", + "machine_learning_availability_checks_timeout_description": "Limit czasu Åŧądania w milisekundach dla sprawdzania dostępności", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Nazwa modelu CLIP jest wymieniona tutaj. ZwrÃŗÄ‡ uwagę, Åŧe po zmianie modelu musisz ponownie uruchomić zadanie 'Smart Search' dla wszystkich obrazÃŗw.", "machine_learning_duplicate_detection": "Wykrywanie DuplikatÃŗw", @@ -166,6 +185,20 @@ "metadata_settings_description": "Zarządzaj ustawieniami metadanych", "migration_job": "Migracja", "migration_job_description": "Przenieś miniatury zasobÃŗw i twarzy do najnowszej struktury folderÃŗw", + "nightly_tasks_cluster_faces_setting_description": "Uruchom rozpoznawanie twarzy dla nowo wykrytych twarzy", + "nightly_tasks_cluster_new_faces_setting": "Zgrupuj nowe twarze", + "nightly_tasks_database_cleanup_setting": "Zadania związane z czyszczeniem bazy danych", + "nightly_tasks_database_cleanup_setting_description": "Wyczyść stare, nieaktualne dane z bazy danych", + "nightly_tasks_generate_memories_setting": "Generuj wspomnienia", + "nightly_tasks_generate_memories_setting_description": "StwÃŗrz nowe wspomnienia z zasobÃŗw", + "nightly_tasks_missing_thumbnails_setting": "Wygeneruj brakujące miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Dodaj zasoby bez miniatur do kolejki generowania miniatur", + "nightly_tasks_settings": "Ustawienia nocnych zadań", + "nightly_tasks_settings_description": "Zarządzaj zadaniami wykonywanymi w nocy", + "nightly_tasks_start_time_setting": "Czas rozpoczęcia", + "nightly_tasks_start_time_setting_description": "Czas, w ktÃŗrym serwer rozpoczyna wykonywanie nocnych zadań", + "nightly_tasks_sync_quota_usage_setting": "Zsynchronizuj wykorzystanie kontyngentu", + "nightly_tasks_sync_quota_usage_setting_description": "Zaktualizuj kontyngent przestrzeni dyskowej uÅŧytkownika na podstawie aktualnego zuÅŧycia", "no_paths_added": "Nie dodano ścieÅŧki", "no_pattern_added": "Nie dodano wzoru", "note_apply_storage_label_previous_assets": "Uwaga: aby zastosować etykietę magazynu do wcześniej przesłanych zasobÃŗw, uruchom", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobilny adres zwrotny", "oauth_mobile_redirect_uri_override": "Zapasowy URI przekierowania mobilnego", "oauth_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu ''{callback}''", + "oauth_role_claim": "Oświadczenie roli", + "oauth_role_claim_description": "Automatycznie przyznaj dostęp administratora na podstawie obecności tego oświadczenia. Oświadczenie moÅŧe mieć wartość „uÅŧytkownik” lub „administrator”.", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", @@ -206,7 +241,7 @@ "oauth_storage_quota_default": "Domyślna ilość miejsca w magazynie (GiB)", "oauth_storage_quota_default_description": "Limit w GiB do wykorzystania, gdy nie podano Åŧadnej wartości.", "oauth_timeout": "Upłynął czas Åŧądania", - "oauth_timeout_description": "Limit czasu Åŧądania (w milisekundach)", + "oauth_timeout_description": "Limit czasu Åŧądania w milisekundach", "password_enable_description": "Zaloguj uÅŧywając e-mail i hasła", "password_settings": "Logowanie Hasłem", "password_settings_description": "Zarządzaj ustawieniami logowania hasłem", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Liczba dni przechowywania zasobÃŗw w koszu przed ich trwałym usunięciem", "trash_settings": "Ustawienia Kosza", "trash_settings_description": "Zarządzaj ustawieniami kosza", + "unlink_all_oauth_accounts": "Odłącz wszystkie konta OAuth", + "unlink_all_oauth_accounts_description": "Pamiętaj, aby przed migracją do nowego dostawcy odłączyć wszystkie konta OAuth.", + "unlink_all_oauth_accounts_prompt": "Czy na pewno chcesz odłączyć wszystkie konta OAuth? Spowoduje to zresetowanie identyfikatora OAuth dla kaÅŧdego uÅŧytkownika i nie będzie moÅŧna tego cofnąć.", "user_cleanup_job": "Porządkowanie uÅŧytkownika", "user_delete_delay": "Konto {user} oraz jego zasoby zostaną zaplanowane do trwałego usunięcia za {delay, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}.", "user_delete_delay_settings": "Usuń opÃŗÅēnienie", @@ -360,10 +398,12 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "UÅŧyj tej opcji do filtrowania mediÃŗw podczas synchronizacji alternatywnych kryteriÃŗw. UÅŧywaj tylko wtedy gdy aplikacja ma problemy z wykrywaniem wszystkich albumÃŗw.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERYMENTALNE] UÅŧyj alternatywnego filtra synchronizacji albumu", "advanced_settings_log_level_title": "Poziom szczegÃŗÅ‚owości dziennika: {level}", - "advanced_settings_prefer_remote_subtitle": "NiektÃŗre urządzenia bardzo wolno ładują miniatury z zasobÃŗw na urządzeniu. Aktywuj to ustawienie, aby ładować zdalne obrazy.", + "advanced_settings_prefer_remote_subtitle": "NiektÃŗre urządzenia bardzo wolno ładują miniatury z lokalnych zasobÃŗw. Aktywuj to ustawienie, aby ładować zdalne obrazy.", "advanced_settings_prefer_remote_title": "Preferuj obrazy zdalne", "advanced_settings_proxy_headers_subtitle": "Zdefiniuj nagÅ‚Ãŗwki proxy, ktÃŗre Immich powinien wysyłać z kaÅŧdym Åŧądaniem sieciowym", "advanced_settings_proxy_headers_title": "NagÅ‚Ãŗwki proxy", + "advanced_settings_readonly_mode_subtitle": "Włącza tryb tylko do odczytu, w ktÃŗrym zdjęcia moÅŧna tylko przeglądać, a takie czynności jak wybieranie wielu obrazÃŗw, udostępnianie, przesyłanie i usuwanie są wyłączone. Włącz/wyłącz tryb tylko do odczytu za pomocą awatara uÅŧytkownika na ekranie gÅ‚Ãŗwnym", + "advanced_settings_readonly_mode_title": "Tryb tylko do odczytu", "advanced_settings_self_signed_ssl_subtitle": "Pomija weryfikację certyfikatu SSL dla punktu końcowego serwera. Wymagane w przypadku certyfikatÃŗw z podpisem własnym.", "advanced_settings_self_signed_ssl_title": "Zezwalaj na certyfikaty SSL z podpisem własnym", "advanced_settings_sync_remote_deletions_subtitle": "Automatycznie usuń lub przywrÃŗÄ‡ zasÃŗb na tym urządzeniu po wykonaniu tej czynności w interfejsie webowym", @@ -379,6 +419,7 @@ "album_cover_updated": "Okładka albumu została zaktualizowana", "album_delete_confirmation": "Czy na pewno chcesz usunąć album {album}?", "album_delete_confirmation_description": "JeÅŧeli album jest udostępniany, inny stracą do niego dostęp.", + "album_deleted": "Album usunięty", "album_info_card_backup_album_excluded": "WYKLUCZONE", "album_info_card_backup_album_included": "WŁĄCZONE", "album_info_updated": "SzczegÃŗÅ‚y albumu zostały zaktualizowane", @@ -388,7 +429,9 @@ "album_options": "Opcje albumu", "album_remove_user": "Usunąć uÅŧytkownika?", "album_remove_user_confirmation": "Na pewno chcesz usunąć {user}?", + "album_search_not_found": "Nie znaleziono albumÃŗw pasujących do Twojego wyszukiwania", "album_share_no_users": "Wygląda na to, Åŧe ten album albo udostępniono wszystkim uÅŧytkownikom, albo nie ma komu go udostępnić.", + "album_summary": "Podsumowanie albumu", "album_updated": "Album zaktualizowany", "album_updated_setting_description": "Otrzymaj powiadomienie e-mail, gdy do udostępnionego Ci albumu zostaną dodane nowe zasoby", "album_user_left": "Opuszczono {album}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Domyślna kolejność sortowania w albumach", "albums_default_sort_order_description": "Początkowa kolejność sortowania zasobÃŗw przy tworzeniu nowych albumÃŗw.", "albums_feature_description": "Kolekcje zasobÃŗw, ktÃŗre moÅŧna udostępniać innym uÅŧytkownikom.", + "albums_on_device_count": "Albumy na urządzeniu ({count})", "all": "Wszystkie", "all_albums": "Wszystkie albumy", "all_people": "Wszystkie osoby", @@ -424,9 +468,11 @@ "app_bar_signout_dialog_content": "Czy na pewno chcesz się wylogować?", "app_bar_signout_dialog_ok": "Tak", "app_bar_signout_dialog_title": "Wyloguj się", - "app_settings": "Ustawienia Aplikacji", + "app_settings": "Ustawienia aplikacji", "appears_in": "W albumach", + "apply_count": "Zastosuj ({count, number})", "archive": "Archiwum", + "archive_action_prompt": "{count} dodanych do Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasÃŗb z archiwum", "archive_page_no_archived_assets": "Nie znaleziono zarchiwizowanych zasobÃŗw", "archive_page_title": "Archiwum {count}", @@ -457,6 +503,8 @@ "asset_restored_successfully": "ZasÃŗb został pomyślnie przywrÃŗcony", "asset_skipped": "Pominięto", "asset_skipped_in_trash": "W koszu", + "asset_trashed": "ZasÃŗb wrzucono do kosza", + "asset_troubleshoot": "Rozwiązywanie problemÃŗw z zasobami", "asset_uploaded": "Przesłano", "asset_uploading": "Przesyłanieâ€Ļ", "asset_viewer_settings_subtitle": "Zarządzaj ustawieniami przeglądarki galerii", @@ -464,8 +512,9 @@ "assets": "Zasoby", "assets_added_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", "assets_added_to_album_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do albumu", - "assets_added_to_name_count": "Dodano {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. ElementÃŗw}} nie moÅŧe być dodana do albumu", + "assets_added_to_albums_count": "Dodano {assetTotal, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do {albumTotal, plural, one {# albumu} other {# albumÃŗw}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {ZasÃŗb nie moÅŧe zostać dodany} other {Zasoby nie mogą zostać dodane}} do albumu", + "assets_cannot_be_added_to_albums": "{count, plural, one {ZasÃŗb nie moÅŧe być dodany} other {Zasoby nie mogą być dodane}} do Åŧadnego z albumÃŗw", "assets_count": "{count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", "assets_deleted_permanently": "{count} zostało trwale usuniętych", "assets_deleted_permanently_from_server": "{count} zostało trwale usuniętych z serwera Immich", @@ -481,30 +530,35 @@ "assets_trashed": "{count} szt. zostało wrzucone do kosza", "assets_trashed_count": "Wrzucono do kosza {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", "assets_trashed_from_server": "{count} szt. usuniętych z serwera Immich", - "assets_were_part_of_album_count": "{count, plural, one {ZasÃŗb był} few {Zasoby były} many {ZasobÃŗw było} other {ZasobÃŗw było}} juÅŧ częścią albumu", - "authorized_devices": "UpowaÅŧnione Urządzenia", + "assets_were_part_of_album_count": "{count, plural, one {ZasÃŗb był} other {Zasoby były}} juÅŧ częścią albumu", + "assets_were_part_of_albums_count": "{count, plural, one {ZasÃŗb był} other {Zasoby były}} juÅŧ częścią albumÃŗw", + "authorized_devices": "Autoryzowane urządzenia", "automatic_endpoint_switching_subtitle": "Połącz się lokalnie przez wyznaczoną sieć Wi-Fi, jeśli jest dostępna, i korzystaj z alternatywnych połączeń gdzie indziej", "automatic_endpoint_switching_title": "Automatyczne przełączanie adresÃŗw URL", "autoplay_slideshow": "Automatyczne odtwarzanie pokazu slajdÃŗw", "back": "Wstecz", "back_close_deselect": "WrÃŗÄ‡, zamknij lub odznacz", + "background_backup_running_error": "Tworzenie kopii zapasowej w tle jest obecnie w toku, nie moÅŧna rozpocząć ręcznego tworzenia kopii zapasowej", "background_location_permission": "Uprawnienia do lokalizacji w tle", "background_location_permission_content": "Aby mÃŗc przełączać sieć podczas pracy w tle, Immich musi *zawsze* mieć dostęp do dokładnej lokalizacji, aby aplikacja mogła odczytać nazwę sieci Wi-Fi", + "background_options": "Opcje w tle", + "backup": "Kopia zapasowa", "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({count})", "backup_album_selection_page_albums_tap": "Stuknij, aby włączyć, stuknij dwukrotnie, aby wykluczyć", "backup_album_selection_page_assets_scatter": "Pliki mogą być rozproszone w wielu albumach. Dzięki temu albumy mogą być włączane lub wyłączane podczas procesu tworzenia kopii zapasowej.", - "backup_album_selection_page_select_albums": "Zaznacz albumy", + "backup_album_selection_page_select_albums": "Wybierz albumy", "backup_album_selection_page_selection_info": "Info o wyborze", "backup_album_selection_page_total_assets": "Łącznie unikalnych plikÃŗw", + "backup_albums_sync": "Synchronizacja kopii zapasowych albumÃŗw", "backup_all": "Wszystkie", "backup_background_service_backup_failed_message": "Nie udało się wykonać kopii zapasowej zasobÃŗw. Ponowna prÃŗbaâ€Ļ", "backup_background_service_connection_failed_message": "Nie udało się połączyć z serwerem. Ponowna prÃŗbaâ€Ļ", - "backup_background_service_current_upload_notification": "Wysyłanie {filename}", + "backup_background_service_current_upload_notification": "Przesyłanie {filename}", "backup_background_service_default_notification": "Sprawdzanie nowych zasobÃŗwâ€Ļ", "backup_background_service_error_title": "Błąd kopii zapasowej", "backup_background_service_in_progress_notification": "Tworzenie kopii zapasowej twoich zasobÃŗwâ€Ļ", "backup_background_service_upload_failure_notification": "Błąd przesyłania {filename}", - "backup_controller_page_albums": "Kopia Zapasowa albumÃŗw", + "backup_controller_page_albums": "Albumy z włączoną kopią zapasową", "backup_controller_page_background_app_refresh_disabled_content": "Włącz odświeÅŧanie aplikacji w tle w Ustawienia > OgÃŗlne > OdświeÅŧanie aplikacji w tle, aby mÃŗc korzystać z kopii zapasowej w tle.", "backup_controller_page_background_app_refresh_disabled_title": "OdświeÅŧanie aplikacji w tle wyłączone", "backup_controller_page_background_app_refresh_enable_button_text": "PrzejdÅē do ustawień", @@ -521,37 +575,40 @@ "backup_controller_page_background_turn_off": "Wyłącz usługę w tle", "backup_controller_page_background_turn_on": "Włącz usługę w tle", "backup_controller_page_background_wifi": "Tylko Wi-Fi", - "backup_controller_page_backup": "Kopia Zapasowa", - "backup_controller_page_backup_selected": "Zaznaczone: ", - "backup_controller_page_backup_sub": "Skopiowane zdjęcia oraz filmy", + "backup_controller_page_backup": "Kopia zapasowa", + "backup_controller_page_backup_selected": "Wybrane: ", + "backup_controller_page_backup_sub": "Zdjęcia i filmy z utworzoną kopią zapasową", "backup_controller_page_created": "Utworzono dnia: {date}", - "backup_controller_page_desc_backup": "Włącz kopię zapasową, aby automatycznie przesyłać nowe zasoby na serwer.", + "backup_controller_page_desc_backup": "Włącz kopię zapasową na pierwszym planie, aby automatycznie przesyłać nowe zasoby na serwer po otworzeniu aplikacji.", "backup_controller_page_excluded": "Wykluczone: ", "backup_controller_page_failed": "Nieudane ({count})", "backup_controller_page_filename": "Nazwa pliku: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informacje o kopii zapasowej", - "backup_controller_page_none_selected": "Brak wybranych", - "backup_controller_page_remainder": "Reszta", - "backup_controller_page_remainder_sub": "Pozostałe zdjęcia i albumy do wykonania kopii zapasowej z wyboru", + "backup_controller_page_none_selected": "Nic nie wybrano", + "backup_controller_page_remainder": "Pozostałe", + "backup_controller_page_remainder_sub": "Pozostałe zdjęcia i filmy wybrane do wykonania kopii zapasowej", "backup_controller_page_server_storage": "Pamięć Serwera", "backup_controller_page_start_backup": "Rozpocznij Kopię Zapasową", - "backup_controller_page_status_off": "Kopia Zapasowa jest wyłaczona", - "backup_controller_page_status_on": "Kopia Zapasowa jest włączona", - "backup_controller_page_storage_format": "{used} z {total} wykorzystanych", - "backup_controller_page_to_backup": "Albumy z Kopią Zapasową", + "backup_controller_page_status_off": "Automatyczne tworzenie kopii zapasowej na pierwszym planie jest wyłączone", + "backup_controller_page_status_on": "Automatyczne tworzenie kopii zapasowej na pierwszym planie jest włączone", + "backup_controller_page_storage_format": "Wykorzystano {used} z {total}", + "backup_controller_page_to_backup": "Albumy, dla ktÃŗrych ma być tworzona kopia zapasowa", "backup_controller_page_total_sub": "Wszystkie unikalne zdjęcia i filmy z wybranych albumÃŗw", - "backup_controller_page_turn_off": "Wyłącz Kopię Zapasową", - "backup_controller_page_turn_on": "Włącz Kopię Zapasową", - "backup_controller_page_uploading_file_info": "Przesyłanie informacji o pliku", + "backup_controller_page_turn_off": "Wyłącz kopię zapasową na pierwszym planie", + "backup_controller_page_turn_on": "Włącz kopię zapasową na pierwszym planie", + "backup_controller_page_uploading_file_info": "Informacje o przesyłanym pliku", "backup_err_only_album": "Nie moÅŧna usunąć jedynego albumu", + "backup_error_sync_failed": "Synchronizacja nie powiodła się. Nie moÅŧna wykonać kopii zapasowej.", "backup_info_card_assets": "zasoby", "backup_manual_cancelled": "Anulowano", "backup_manual_in_progress": "Przesyłanie juÅŧ trwa. SprÃŗbuj po pewnym czasie", "backup_manual_success": "Sukces", "backup_manual_title": "Stan przesyłania", - "backup_options_page_title": "Opcje kopi zapasowej", + "backup_options": "Opcje kopii zapasowej", + "backup_options_page_title": "Opcje kopii zapasowej", "backup_setting_subtitle": "Zarządzaj ustawieniami przesyłania w tle i na pierwszym planie", + "backup_settings_subtitle": "Zarządzanie ustawieniami przesyłania", "backward": "Do tyłu", "biometric_auth_enabled": "Włączono logowanie biometryczne", "biometric_locked_out": "Uwierzytelnianie biometryczne jest dla Ciebie zablokowane", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "Wyczyść Cache", "cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopÃŗki pamięć podręczna nie zostanie odbudowana.", "cache_settings_duplicated_assets_clear_button": "WYCZYŚĆ", - "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na czarnej liście aplikacji", + "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na liście ignorowanych w aplikacji", "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({count})", "cache_settings_statistics_album": "Biblioteka miniatur", "cache_settings_statistics_full": "Pełne Zdjęcia", @@ -587,13 +644,14 @@ "cancel": "Anuluj", "cancel_search": "Anuluj wyszukiwanie", "canceled": "Anulowano", + "canceling": "Anulowanie", "cannot_merge_people": "Złączenie osÃŗb nie powiodło się", "cannot_undo_this_action": "Nie da się tego cofnąć!", "cannot_update_the_description": "Nie moÅŧna zaktualizować opisu", "cast": "OdtwÃŗrz na telewizorze", "cast_description": "Skonfiguruj dostępne cele do przesyłania", "change_date": "Zmień datę", - "change_description": "Zmiana opisu", + "change_description": "Zmień opis", "change_display_order": "Zmień kolejność wyświetlania", "change_expiration_time": "Zmień czas waÅŧności", "change_location": "Zmień lokalizację", @@ -609,6 +667,8 @@ "change_pin_code": "Zmień kod PIN", "change_your_password": "Zmień swoje hasło", "changed_visibility_successfully": "Pomyślnie zmieniono widoczność", + "charging": "Ładowanie", + "charging_requirement_mobile_backup": "Tworzenie kopii zapasowej w tle wymaga by urządzenie było podłączone do ładowania", "check_corrupt_asset_backup": "SprawdÅē, czy kopie zapasowe zasobÃŗw nie są uszkodzone", "check_corrupt_asset_backup_button": "Wykonaj sprawdzenie", "check_corrupt_asset_backup_description": "Uruchom sprawdzenie tylko przez Wi-Fi i po utworzeniu kopii zapasowej wszystkich zasobÃŗw. Procedura moÅŧe potrwać kilka minut.", @@ -618,6 +678,7 @@ "clear": "Wyczyść", "clear_all": "Wyczyść", "clear_all_recent_searches": "Usuń ostatnio wyszukiwane", + "clear_file_cache": "Wyczyść pamięć podręczną plikÃŗw", "clear_message": "Zamknij wiadomość", "clear_value": "Wyczyść wartość", "client_cert_dialog_msg_confirm": "OK", @@ -645,7 +706,7 @@ "confirm_admin_password": "PotwierdÅē Hasło Administratora", "confirm_delete_face": "Czy na pewno chcesz usunąć twarz {name} z zasobÃŗw?", "confirm_delete_shared_link": "Czy na pewno chcesz usunąć ten udostępniony link?", - "confirm_keep_this_delete_others": "Wszystkie inne zasoby zostaną usunięte poza tym zasobem. Czy jesteś pewien, Åŧe chcesz kontynuować?", + "confirm_keep_this_delete_others": "Wszystkie inne zasoby w tym stosie, z wyjątkiem tego zasobu, zostaną usunięte. Czy jesteś pewien, Åŧe chcesz kontynuować?", "confirm_new_pin_code": "PotwierdÅē nowy kod PIN", "confirm_password": "PotwierdÅē hasło", "confirm_tag_face": "Chcesz dodać do tej twarzy etykietę {name}?", @@ -662,7 +723,7 @@ "control_bottom_app_bar_edit_time": "Edytuj datę i godzinę", "control_bottom_app_bar_share_link": "Udostępnij link", "control_bottom_app_bar_share_to": "Wyślij", - "control_bottom_app_bar_trash_from_immich": "Przenieść do kosza", + "control_bottom_app_bar_trash_from_immich": "Przenieś do kosza", "copied_image_to_clipboard": "Skopiowano obraz do schowka.", "copied_to_clipboard": "Skopiowano do schowka!", "copy_error": "Błąd kopiowania", @@ -688,11 +749,13 @@ "create_new_user": "StwÃŗrz nowego uÅŧytkownika", "create_shared_album_page_share_add_assets": "DODAJ ZASOBY", "create_shared_album_page_share_select_photos": "Zaznacz Zdjęcia", + "create_shared_link": "UtwÃŗrz link udostępniający", "create_tag": "StwÃŗrz etykietę", "create_tag_description": "StwÃŗrz nową etykietę. Dla etykiet zagnieÅŧdÅŧonych, wprowadÅē pełną ścieÅŧkę etykiety zawierającą ukośniki.", "create_user": "StwÃŗrz uÅŧytkownika", "created": "Utworzono", "created_at": "Utworzony", + "creating_linked_albums": "Tworzenie połączonych albumÃŗw...", "crop": "Przytnij", "curated_object_page_title": "Rzeczy", "current_device": "Obecne urządzenie", @@ -700,10 +763,11 @@ "current_server_address": "Aktualny adres serwera", "custom_locale": "Niestandardowy Region", "custom_locale_description": "Formatuj daty i liczby na podstawie języka i regionu", + "custom_url": "Niestandardowy URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", - "darkTheme": "Włącz ciemny motyw", + "dark_theme": "Przełącz ciemny motyw", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Data urodzenia zapisana pomyślnie", "date_range": "Zakres dat", "day": "Dzień", + "days": "Dni", "deduplicate_all": "Usuń duplikaty", "deduplication_criteria_1": "Rozmiar obrazu w bajtach", "deduplication_criteria_2": "Ilość plikÃŗw EXIF", @@ -719,11 +784,13 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", + "delete_action_confirmation_message": "Jesteś pewien, Åŧe chcesz usunąć ten zasÃŗb? Ta czynność przeniesie zasÃŗb do kosza na serwerze i wyświetli komunikat z pytaniem, czy chcesz go usunąć lokalnie", + "delete_action_prompt": "{count} usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", "delete_dialog_alert_local": "Elementy te zostaną trwale usunięte z Twojego urządzenia, ale nadal będą dostępne na serwerze Immich", - "delete_dialog_alert_local_non_backed_up": "Kopia zapasowa niektÃŗrych elementÃŗw nie jest tworzona w Immich i zostanie trwale usunięta z Twojego urządzenia", + "delete_dialog_alert_local_non_backed_up": "NiektÃŗre elementy nie mają kopii zapasowej w Immich i zostaną trwale usunięte z Twojego urządzenia", "delete_dialog_alert_remote": "Elementy te zostaną trwale usunięte z serwera Immich", "delete_dialog_ok_force": "Usuń mimo to", "delete_dialog_title": "Usuń trwale", @@ -732,9 +799,12 @@ "delete_key": "Usuń klucz", "delete_library": "Usuń bibliotekę", "delete_link": "Usuń link", + "delete_local_action_prompt": "{count} lokalnie usunięto", "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", - "delete_others": "Usuń inne", + "delete_others": "Usuń pozostałe", + "delete_permanently": "Usuń trwale", + "delete_permanently_action_prompt": "{count} trwale usuniętych", "delete_shared_link": "Usuń udostępniony link", "delete_shared_link_dialog_title": "Usuń udostępniony link", "delete_tag": "Usuń etykietę", @@ -745,6 +815,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Błąd aktualizacji opisu, sprawdÅē dziennik, aby uzyskać więcej szczegÃŗÅ‚Ãŗw", + "deselect_all": "Odznacz wszystkie", "details": "SzczegÃŗÅ‚y", "direction": "Kierunek", "disabled": "Wyłączone", @@ -762,6 +833,7 @@ "documentation": "Dokumentacja", "done": "Gotowe", "download": "Pobierz", + "download_action_prompt": "Pobieranie {count} zasobÃŗw", "download_canceled": "Pobieranie anulowane", "download_complete": "Pobieranie zakończone", "download_enqueue": "Pobieranie w kolejce", @@ -781,15 +853,19 @@ "downloading": "Pobieranie", "downloading_asset_filename": "Pobieranie zasobu {filename}", "downloading_media": "Pobieranie multimediÃŗw", - "drop_files_to_upload": "Upuść pliki gdziekolwiek, aby je załadować", + "drop_files_to_upload": "Upuść pliki w dowolnym miejscu, aby je przesłać", "duplicates": "Duplikaty", "duplicates_description": "Rozstrzygnij kaÅŧdą grupę, określając, ktÃŗre zasoby są duplikatami, jeÅŧeli są duplikatami", "duration": "Czas trwania", "edit": "Edytuj", "edit_album": "Edytuj album", "edit_avatar": "Edytuj awatar", + "edit_birthday": "Edytuj datę urodzin", "edit_date": "Edytuj datę", "edit_date_and_time": "Edytuj datę i czas", + "edit_date_and_time_action_prompt": "{count} daty i godziny zmodyfikowane", + "edit_date_and_time_by_offset": "Zmień datę o przesunięcie", + "edit_date_and_time_by_offset_interval": "Nowy zakres dat: {from} - {to}", "edit_description": "Edycja opisu", "edit_description_prompt": "Wybierz nowy opis:", "edit_exclusion_pattern": "Edytuj wzÃŗr wykluczający", @@ -799,6 +875,7 @@ "edit_key": "Edytuj klucz", "edit_link": "Edytuj link", "edit_location": "Edytuj lokalizację", + "edit_location_action_prompt": "{count} edytowana lokalizacja", "edit_location_dialog_title": "Lokalizacja", "edit_name": "Edytuj nazwę", "edit_people": "Edytuj osoby", @@ -817,6 +894,7 @@ "empty_trash": "OprÃŗÅŧnij kosz", "empty_trash_confirmation": "Czy na pewno chcesz oprÃŗÅŧnić kosz? Spowoduje to trwałe usunięcie wszystkich zasobÃŗw znajdujących się w koszu z Immich.\nNie moÅŧna cofnąć tej operacji!", "enable": "Włącz", + "enable_backup": "Włącz kopię zapasową", "enable_biometric_auth_description": "WprowadÅē kod PIN aby włączyć logowanie biometryczne", "enabled": "Włączone", "end_date": "Do dnia", @@ -826,8 +904,10 @@ "enter_your_pin_code_subtitle": "WprowadÅē twÃŗj kod PIN, aby uzyskać dostęp do folderu zablokowanego", "error": "Błąd", "error_change_sort_album": "Nie udało się zmienić kolejności sortowania albumÃŗw", - "error_delete_face": "Wystąpił błąd podczas usuwania twarzy z zasobÃŗw", + "error_delete_face": "Błąd podczas usuwania twarzy z zasobÃŗw", + "error_getting_places": "Błąd podczas pozyskiwania lokalizacji", "error_loading_image": "Błąd podczas ładowania zdjęcia", + "error_loading_partners": "Błąd podczas ładowania partnerÃŗw: {error}", "error_saving_image": "Błąd: {error}", "error_tag_face_bounding_box": "Błąd przy dodawaniu etykiety dla tej twarzy - nie moÅŧe uzyskać wspÃŗÅ‚rzędnych granicznych", "error_title": "Błąd - Coś poszło nie tak", @@ -836,7 +916,7 @@ "cannot_navigate_previous_asset": "Nie moÅŧna przejść do poprzedniego zasobu", "cant_apply_changes": "Nie moÅŧna zastosować zmian", "cant_change_activity": "Nie moÅŧna {enabled, select, true {wyłączyć} other {włączyć}} aktywności", - "cant_change_asset_favorite": "Nie moÅŧna zmienić ulubionego dla zasobu", + "cant_change_asset_favorite": "Nie moÅŧna zmienić statusu ulubionego dla zasobu", "cant_change_metadata_assets_count": "Nie moÅŧna zmienić metadanych {count, plural, one {# zasobu} other {# zasobÃŗw}}", "cant_get_faces": "Nie moÅŧna pozyskać twarzy", "cant_get_number_of_comments": "Nie moÅŧna uzyskać liczby komentarzy", @@ -860,7 +940,8 @@ "failed_to_load_notifications": "Nie udało się załadować powiadomień", "failed_to_load_people": "Nie udało się pobrać ludzi", "failed_to_remove_product_key": "Nie udało się usunąć klucza produktu", - "failed_to_stack_assets": "Nie udało się zestawić zasobÃŗw", + "failed_to_reset_pin_code": "Nie udało się zresetować kodu PIN", + "failed_to_stack_assets": "Nie udało się utworzyć stosu z zasobÃŗw", "failed_to_unstack_assets": "Nie udało się rozdzielić zasobÃŗw", "failed_to_update_notification_status": "Nie udało się zaktualizować stanu powiadomienia", "import_path_already_exists": "Ta ścieÅŧka importu juÅŧ istnieje.", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# ścieÅŧka} few {# ścieÅŧki} other {# ścieÅŧek}}", "profile_picture_transparent_pixels": "Zdjęcia profilowe nie mogą mieć przezroczystych pikseli. Powiększ i/lub przesuń obraz.", "quota_higher_than_disk_size": "Ustawiony przez ciebie limit większy niÅŧ rozmiar dysku", + "something_went_wrong": "Coś poszło nie tak", "unable_to_add_album_users": "Nie moÅŧna dodać uÅŧytkownikÃŗw do albumu", "unable_to_add_assets_to_shared_link": "Nie moÅŧna dodać zasobÃŗw do udostępnionego linku", "unable_to_add_comment": "Nie moÅŧna dodać komentarza", @@ -953,13 +1035,11 @@ }, "exif": "Metadane EXIF", "exif_bottom_sheet_description": "Dodaj Opis...", + "exif_bottom_sheet_description_error": "Wystąpił błąd podczas aktualizacji opisu", "exif_bottom_sheet_details": "SZCZEGÓŁY", "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", "exif_bottom_sheet_person_add_person": "Dodaj nazwę", - "exif_bottom_sheet_person_age_months": "Wiek {months} miesięcy", - "exif_bottom_sheet_person_age_year_months": "Wiek 1 rok, {months} miesięcy", - "exif_bottom_sheet_person_age_years": "Wiek: {years} lat", "exit_slideshow": "Zamknij Pokaz SlajdÃŗw", "expand_all": "Rozwiń wszystko", "experimental_settings_new_asset_list_subtitle": "Praca w toku", @@ -973,6 +1053,8 @@ "explorer": "Eksplorator", "export": "Eksportuj", "export_as_json": "Eksportuj jako JSON", + "export_database": "Exportuj bazę danych", + "export_database_description": "Exportuj bazę danych SQLite", "extension": "Rozszerzenie", "external": "Zewnętrzny", "external_libraries": "Biblioteki Zewnętrzne", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Nie udało się załadować zasobÃŗw", "failed_to_load_folder": "Nie udało się załadować folderu", "favorite": "Ulubione", + "favorite_action_prompt": "{count} dodane do ulubionych", "favorite_or_unfavorite_photo": "Dodaj lub usuń z ulubionych", "favorites": "Ulubione", "favorites_page_no_favorites": "Nie znaleziono ulubionych zasobÃŗw", "feature_photo_updated": "Zdjęcie gÅ‚Ãŗwne zaktualizowane pomyślnie", "features": "Funkcje", + "features_in_development": "Funkcje w fazie rozwoju", "features_setting_description": "Zarządzaj funkcjami aplikacji", "file_name": "Nazwa pliku", "file_name_or_extension": "Nazwie lub rozszerzeniu pliku", @@ -998,21 +1082,26 @@ "filter_people": "Szukaj osoby", "filter_places": "Filtruj miejsca", "find_them_fast": "Wyszukuj szybciej przypisując nazwę", + "first": "Pierwszy", "fix_incorrect_match": "Napraw nieprawidłowe dopasowanie", "folder": "Folder", "folder_not_found": "Nie znaleziono folderu", "folders": "Foldery", "folders_feature_description": "Przeglądanie zdjęć i filmÃŗw w widoku folderÃŗw", + "forgot_pin_code_question": "Nie pamiętasz kodu PIN?", "forward": "Do przodu", "gcast_enabled": "Google Cast", - "gcast_enabled_description": "Ta funkcja pobiera zewnętrzne zasoby z Google, aby działać.", + "gcast_enabled_description": "Ta funkcja , aby działać, ładuje zewnętrzne zasoby z Google.", "general": "OgÃŗlne", + "geolocation_instruction_location": "Kliknij na zasÃŗb z wspÃŗÅ‚rzędnymi GPS, aby uÅŧyć jego lokalizacji, lub wybierz lokalizację bezpośrednio z mapy", "get_help": "Pomoc", "get_wifiname_error": "Nie moÅŧna uzyskać nazwy Wi-Fi. Upewnij się, Åŧe udzieliłeś niezbędnych uprawnień i jesteś połączony z siecią Wi-Fi", "getting_started": "Pierwsze kroki", "go_back": "Wstecz", "go_to_folder": "IdÅē do folderu", "go_to_search": "PrzejdÅē do wyszukiwania", + "gps": "GPS", + "gps_missing": "Brak GPS", "grant_permission": "Udziel pozwolenia", "group_albums_by": "Grupuj albumy...", "group_country": "Grupuj według państwa", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Włącz technologię haptyczną", "haptic_feedback_title": "Technologia haptyczna", "has_quota": "Ma limit", + "hash_asset": "Hashuj zasÃŗb", + "hashed_assets": "Zahashowane zasoby", + "hashing": "Hashowanie", "header_settings_add_header_tip": "Dodaj nagÅ‚Ãŗwek", "header_settings_field_validator_msg": "Wartość nie moÅŧe być pusta", "header_settings_header_name_input": "Nazwa nagÅ‚Ãŗwka", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "MoÅŧna przesłać maksymalnie 30 zasobÃŗw jednocześnie, pomijanie", "host": "Host", "hour": "Godzina", + "hours": "Godziny", "id": "ID", + "idle": "Bezczynny", "ignore_icloud_photos": "Ignoruj zdjęcia w iCloud", "ignore_icloud_photos_description": "Zdjęcia przechowywane w usłudze iCloud nie zostaną przesłane na serwer Immich", "image": "Zdjęcie", @@ -1076,7 +1170,7 @@ "immich_web_interface": "Interfejs internetowy Immich", "import_from_json": "Wczytaj z JSON", "import_path": "ŚcieÅŧka importu", - "in_albums": "W {count, plural, one {# album} other {# albumy}}", + "in_albums": "W {count, plural, one {# albumie} other {# albumach}}", "in_archive": "W archiwum", "include_archived": "Uwzględnij zarchiwizowane", "include_shared_albums": "Uwzględnij udostępnione albumy", @@ -1100,11 +1194,11 @@ "ios_debug_info_no_sync_yet": "Nie uruchomiono jeszcze Åŧadnego zadania synchronizacji w tle", "ios_debug_info_processes_queued": "{count, plural, one {{count} proces w tle w kolejce} few {{count} procesy w tle w kolejce} other {{count} procesÃŗw w tle w kolejce}}", "ios_debug_info_processing_ran_at": "Przetwarzanie przebiegło {dateTime}", - "items_count": "{count, plural, one {# element} other {# elementy}}", + "items_count": "{count, plural, one {# element} few {# elementy} other {# elementÃŗw}}", "jobs": "Zadania", "keep": "Zachowaj", "keep_all": "Zachowaj wszystko", - "keep_this_delete_others": "Zachowaj to, usuń inne", + "keep_this_delete_others": "Zachowaj to, usuń pozostałe", "kept_this_deleted_others": "Zachowano ten zasÃŗb i usunięto {count, plural, one {#zasÃŗb} other {#zasoby}}", "keyboard_shortcuts": "SkrÃŗty klawiaturowe", "language": "Język", @@ -1112,10 +1206,13 @@ "language_no_results_title": "Nie znaleziono Åŧadnych językÃŗw", "language_search_hint": "Szukaj językÃŗw...", "language_setting_description": "Wybierz swÃŗj preferowany język", + "large_files": "DuÅŧe pliki", + "last": "Ostatni", "last_seen": "Ostatnio widziane", - "latest_version": "Najnowsza Wersja", + "latest_version": "Najnowsza wersja", "latitude": "Szerokość geograficzna", "leave": "Opuść", + "leave_album": "Opuść album", "lens_model": "Model obiektywu", "let_others_respond": "PozwÃŗl innym reagować", "level": "Poziom", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "Ostatnio utworzone", "library_page_sort_last_modified": "Ostatnio zmodyfikowany", "library_page_sort_title": "Tytuł albumu", + "licenses": "Licencje", "light": "Jasny", + "like": "Polub", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", - "link_options": "Opcje linku", "link_to_oauth": "Połącz z OAuth", "linked_oauth_account": "Połączone konto OAuth", "list": "Lista", "loading": "Ładowanie", "loading_search_results_failed": "Ładowanie wynikÃŗw wyszukiwania nie powiodło się", + "local": "Lokalny", "local_asset_cast_failed": "Nie moÅŧna strumieniować zasobu, ktÃŗry nie został przesłany na serwer", + "local_assets": "Zasoby lokalne", + "local_media_summary": "Podsumowanie lokalnych mediÃŗw", "local_network": "Sieć lokalna", "local_network_sheet_info": "Aplikacja połączy się z serwerem za pośrednictwem tego adresu URL podczas korzystania z określonej sieci Wi-Fi", "location_permission": "Zezwolenie na lokalizację", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Wpisz tutaj swoją długość geograficzną", "lock": "Zablokuj", "locked_folder": "Folder zablokowany", + "log_detail_title": "SzczegÃŗÅ‚y dziennika", "log_out": "Wyloguj", "log_out_all_devices": "Wyloguj ze Wszystkich Urządzeń", "logged_in_as": "Zalogowano jako {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Hasło zostało zmienione", "logout_all_device_confirmation": "Czy na pewno chcesz wylogować się ze wszystkich urządzeń?", "logout_this_device_confirmation": "Czy na pewno chcesz wylogować to urządzenie?", + "logs": "Logi", "longitude": "Długość geograficzna", "look": "Wygląd", "loop_videos": "Powtarzaj filmy", @@ -1185,6 +1288,7 @@ "main_branch_warning": "UÅŧywasz wersji deweloperskiej. Zdecydowanie zalecamy korzystanie z wydanej wersji aplikacji!", "main_menu": "Menu gÅ‚Ãŗwne", "make": "Marka", + "manage_geolocation": "Zarządzaj lokalizacją", "manage_shared_links": "Zarządzaj udostępnionymi linkami", "manage_sharing_with_partners": "Zarządzaj dzieleniem z partnerami", "manage_the_app_settings": "Zarządzaj ustawieniami aplikacji", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Zarządzaj swoimi zalogowanymi urządzeniami", "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} zdjęcie", - "map_assets_in_bounds": "{count} zdjęć", + "map_assets_in_bounds": "{count, plural, =0 {Brak zdjęć w tym obszarze} one {# zdjęcie} other {# zdjęć}}", "map_cannot_get_user_location": "Nie moÅŧna uzyskać lokalizacji uÅŧytkownika", "map_location_dialog_yes": "Tak", "map_location_picker_page_use_location": "UÅŧyj tej lokalizacji", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Usługa lokalizacji wyłączona", "map_marker_for_images": "WskaÅēnik mapy dla zdjęć zrobionych w {city}, {country}", "map_marker_with_image": "Znacznik na mapie ze zdjęciem", - "map_no_assets_in_bounds": "Brak zdjęć w tym obszarze", "map_no_location_permission_content": "Aby wyświetlić zasoby z Twojej bieÅŧącej lokalizacji, potrzebne jest pozwolenie na lokalizację. Czy chcesz teraz na to pozwolić?", "map_no_location_permission_title": "Odmowa dostępu do lokalizacji", "map_settings": "Ustawienia mapy", @@ -1221,6 +1323,7 @@ "mark_as_read": "Zaznacz jako odczytane", "marked_all_as_read": "Zaznaczono wszystkie jako przeczytane", "matches": "Powiązania", + "matching_assets": "Pasujące zasoby", "media_type": "Typ zasobu", "memories": "Wspomnienia", "memories_all_caught_up": "Wszystko złapane", @@ -1239,6 +1342,7 @@ "merged_people_count": "Połączono {count, plural, one {# osobę} few {# osoby} other {# osÃŗb}}", "minimize": "Zminimalizuj", "minute": "Minuta", + "minutes": "Minuty", "missing": "Brakujące", "model": "Model", "month": "Miesiąc", @@ -1246,6 +1350,7 @@ "more": "Więcej", "move": "Przenieś", "move_off_locked_folder": "Przenieś z folderu zablokowanego", + "move_to_lock_folder_action_prompt": "{count} dodanych do folderu zablokowanego", "move_to_locked_folder": "Przenieś do folderu zablokowanego", "move_to_locked_folder_confirmation": "Te zdjęcia i filmy zostaną usunięte ze wszystkich albumÃŗw i będą widzialne tylko w folderze zablokowanym", "moved_to_archive": "Przeniesiono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} do archiwum", @@ -1257,6 +1362,10 @@ "my_albums": "Moje albumy", "name": "Nazwa", "name_or_nickname": "Nazwa lub pseudonim", + "network_requirement_photos_upload": "UÅŧywaj danych komÃŗrkowych do tworzenia kopii zapasowych zdjęć", + "network_requirement_videos_upload": "UÅŧywaj danych komÃŗrkowych do tworzenia kopii zapasowych filmÃŗw", + "network_requirements": "Wymagania sieciowe", + "network_requirements_updated": "Zmieniono wymagania sieciowe, resetowanie kolejki kopii zapasowych", "networking_settings": "Sieć", "networking_subtitle": "Zarządzaj ustawieniami punktu końcowego serwera", "never": "nigdy", @@ -1266,6 +1375,7 @@ "new_person": "Nowa osoba", "new_pin_code": "Nowy kod PIN", "new_pin_code_subtitle": "Jest to pierwszy raz, kiedy wchodzisz do folderu zablokowanego. UtwÃŗrz kod PIN, aby bezpiecznie korzystać z tej strony", + "new_timeline": "Nowa oś czasu", "new_user_created": "Pomyślnie stworzono nowego uÅŧytkownika", "new_version_available": "NOWA WERSJA DOSTĘPNA", "newest_first": "Od najnowszych", @@ -1279,19 +1389,25 @@ "no_assets_message": "KLIKNIJ, ABY WYSŁAĆ PIERWSZE ZDJĘCIE", "no_assets_to_show": "Brak zasobÃŗw do pokazania", "no_cast_devices_found": "Nie znaleziono urządzeń do przesyłania strumieniowego", + "no_checksum_local": "Brak sumy kontrolnej - nie moÅŧna pobrać lokalnych zasobÃŗw", + "no_checksum_remote": "Brak sumy kontrolnej - nie moÅŧna pobrać zdalnego zasobu", "no_duplicates_found": "Nie znaleziono duplikatÃŗw.", "no_exif_info_available": "Nie znaleziono informacji exif", "no_explore_results_message": "Prześlij więcej zdjęć, aby przeglądać swÃŗj zbiÃŗr.", "no_favorites_message": "Dodaj ulubione aby szybko znaleÅēć swoje najlepsze zdjęcia i filmy", "no_libraries_message": "StwÃŗrz bibliotekę zewnętrzną, aby przeglądać swoje zdjęcia i filmy", + "no_local_assets_found": "Nie znaleziono Åŧadnych lokalnych zasobÃŗw o tej sumie kontrolnej", "no_locked_photos_message": "Zdjęcia i filmy w folderze zablokowanym są ukryte i nie będą wyświetlane podczas przeglądania biblioteki.", "no_name": "Brak Nazwy", "no_notifications": "Brak powiadomień", "no_people_found": "Brak pasujących osÃŗb", "no_places": "Brak miejsc", + "no_remote_assets_found": "Nie znaleziono Åŧadnych zdalnych zasobÃŗw o tej sumie kontrolnej", "no_results": "Brak wynikÃŗw", "no_results_description": "SprÃŗbuj uÅŧyć synonimu lub bardziej ogÃŗlnego słowa kluczowego", "no_shared_albums_message": "StwÃŗrz album aby udostępnić zdjęcia i filmy osobom w Twojej sieci", + "no_uploads_in_progress": "Brak przesyłań w toku", + "not_available": "Nie dotyczy", "not_in_any_album": "Bez albumu", "not_selected": "Nie wybrano", "note_apply_storage_label_to_previously_uploaded assets": "Uwaga: Aby przypisać etykietę magazynowania do wcześniej przesłanych zasobÃŗw, uruchom", @@ -1307,12 +1423,13 @@ "oauth": "OAuth", "official_immich_resources": "Oficjalne zasoby Immicha", "offline": "Offline", + "offset": "Przesunięcie", "ok": "Ok", "oldest_first": "Od najstarszych", "on_this_device": "Na tym urządzeniu", "onboarding": "WdroÅŧenie", "onboarding_locale_description": "Wybierz preferowany język. MoÅŧna to pÃŗÅēniej zmienić w ustawieniach.", - "onboarding_privacy_description": "Śledzenie (opcjonalne) funkcja opiera się na zewnętrznych usługach i moÅŧe zostać wyłączona w dowolnym momencie w ustawieniach.", + "onboarding_privacy_description": "Następujące (opcjonalne) funkcje opierają się na usługach zewnętrznych i moÅŧna je w dowolnym momencie wyłączyć w ustawieniach.", "onboarding_server_welcome_description": "Skonfigurujmy twoją instancję z kilkoma typowymi ustawieniami.", "onboarding_theme_description": "Wybierz motyw kolorystyczny dla twojej instancji. MoÅŧesz go pÃŗÅēniej zmienić w ustawieniach.", "onboarding_user_welcome_description": "Zaczynamy!", @@ -1325,26 +1442,29 @@ "open_the_search_filters": "OtwÃŗrz filtry wyszukiwania", "options": "Opcje", "or": "lub", + "organize_into_albums": "Uporządkuj w albumy", + "organize_into_albums_description": "Umieść istniejące zdjęcia w albumach przy uÅŧyciu bieÅŧących ustawień synchronizacji", "organize_your_library": "Organizuj swoją bibliotekę", "original": "oryginalny", "other": "Inne", "other_devices": "Inne urządzenia", + "other_entities": "Inne byty", "other_variables": "Inne zmienne", "owned": "Posiadany", "owner": "Właściciel", "partner": "Partner", "partner_can_access": "{partner} ma dostęp do", - "partner_can_access_assets": "Twoje wszystkie zdjęcia i filmy, oprÃŗcz tych w Archiwum i Koszu", + "partner_can_access_assets": "Wszystkie Twoje zdjęcia i filmy, oprÃŗcz tych w Archiwum i Koszu", "partner_can_access_location": "Informacji o tym, gdzie zostały zrobione Twoje zdjęcia", - "partner_list_user_photos": "{user} zdjęcia", + "partner_list_user_photos": "Zdjęcia naleÅŧące do {user}", "partner_list_view_all": "PokaÅŧ wszystkie", "partner_page_empty_message": "Twoje zdjęcia nie są udostępnione Åŧadnemu partnerowi.", "partner_page_no_more_users": "Brak uÅŧytkownikÃŗw do dodania", "partner_page_partner_add_failed": "Nie udało się dodać partnera", "partner_page_select_partner": "Wybierz partnera", "partner_page_shared_to_title": "Udostępniono", - "partner_page_stop_sharing_content": "{partner} nie będzie juÅŧ mieć dostępu do twoich zdjęć.", - "partner_sharing": "Dzielenie z Partnerami", + "partner_page_stop_sharing_content": "{partner} nie będzie juÅŧ mieć dostępu do Twoich zdjęć.", + "partner_sharing": "Dzielenie z partnerami", "partners": "Partnerzy", "password": "Hasło", "password_does_not_match": "Hasła nie są takie same", @@ -1369,7 +1489,7 @@ "permanent_deletion_warning_setting_description": "PokaÅŧ ostrzeÅŧenie przy trwałym usuwaniu zasobÃŗw", "permanently_delete": "Usuń trwale", "permanently_delete_assets_count": "Trwale usuń {count, plural, one {zasÃŗb} few {zasoby} many {zasobÃŗw} other {zasobÃŗw}}", - "permanently_delete_assets_prompt": "Czy na pewno chcesz trwale usunąć {count, plural, one {ten zasÃŗb?} other {te # zasoby?}} Spowoduje to rÃŗwnieÅŧ usunięcie {count, plural, one {go z jego} other {ich z ich}} album(Ãŗw).", + "permanently_delete_assets_prompt": "Czy na pewno chcesz trwale usunąć {count, plural, one {ten zasÃŗb?} few {te # zasoby?} other {te # zasobÃŗw?}} Spowoduje to rÃŗwnieÅŧ usunięcie {count, plural, one {go z jego} other {ich z ich}} album(Ãŗw).", "permanently_deleted_asset": "Pomyślnie trwale usunięto zasÃŗb", "permanently_deleted_assets_count": "Trwale usunięto {count, plural, one {# zasÃŗb} other {# zasobÃŗw}}", "permission": "Pozwolenie", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Pozwolenie ograniczone. Aby umoÅŧliwić Immichowi tworzenie kopii zapasowych całej kolekcji galerii i zarządzanie nią, przyznaj uprawnienia do zdjęć i filmÃŗw w Ustawieniach.", "permission_onboarding_request": "Immich potrzebuje pozwolenia na przeglądanie Twoich zdjęć i filmÃŗw.", "person": "Osoba", + "person_age_months": "{months, plural, one {# miesiąc} few {# miesiące} many {# miesięcy} other {# miesięcy}}", + "person_age_year_months": "1 rok, {months, plural, one {# miesiąc} few {# miesiące} many {# miesięcy} other {# miesięcy}}", + "person_age_years": "{years, plural, one {# rok} few {# lata} many {# lat} other {# lat}}", "person_birthdate": "Urodzony {date}", "person_hidden": "{name}{hidden, select, true { (ukryty)} other {}}", "photo_shared_all_users": "Wygląda na to, Åŧe udostępniłeś swoje zdjęcia wszystkim uÅŧytkownikom lub nie masz Åŧadnego uÅŧytkownika, z ktÃŗrym moÅŧna by było je udostępnić.", @@ -1406,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Zarządzaj preferencjami aplikacji", "preferences_settings_title": "Ustawienia", + "preparing": "Przygotowywanie", "preset": "Ustawienie", "preview": "Podgląd", "previous": "Poprzedni", @@ -1418,12 +1542,13 @@ "privacy": "Prywatność", "profile": "Profil", "profile_drawer_app_logs": "Logi", - "profile_drawer_client_out_of_date_major": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej wersji gÅ‚Ãŗwnej.", - "profile_drawer_client_out_of_date_minor": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej wersji dodatkowej.", + "profile_drawer_client_out_of_date_major": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej gÅ‚Ãŗwnej wersji.", + "profile_drawer_client_out_of_date_minor": "Aplikacja mobilna jest nieaktualna. Zaktualizuj do najnowszej pomniejszej wersji.", "profile_drawer_client_server_up_to_date": "Klient i serwer są aktualne", "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Serwer jest nieaktualny. Zaktualizuj do najnowszej wersji gÅ‚Ãŗwnej.", - "profile_drawer_server_out_of_date_minor": "Serwer jest nieaktualny. Zaktualizuj do najnowszej wersji dodatkowej.", + "profile_drawer_readonly_mode": "Włączono tryb tylko do odczytu. Aby wyjść, naciśnij i przytrzymaj ikonę awatara uÅŧytkownika.", + "profile_drawer_server_out_of_date_major": "Serwer jest nieaktualny. Zaktualizuj do najnowszej gÅ‚Ãŗwnej wersji.", + "profile_drawer_server_out_of_date_minor": "Serwer jest nieaktualny. Zaktualizuj do najnowszej pomniejszej wersji.", "profile_image_of_user": "Zdjęcie profilowe {user}", "profile_picture_set": "Zdjęcie profilowe ustawione.", "public_album": "Publiczny album", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Status wspierającego", "purchase_server_title": "Serwer", "purchase_settings_server_activated": "Klucz produktu serwera jest zarządzany przez administratora", + "query_asset_id": "Zapytanie o ID zasobu", + "queue_status": "Kolejkowanie {count}/{total}", "rating": "Ocena gwiazdkowa", "rating_clear": "Wyczyść ocenę", "rating_count": "{count, plural, one {# gwiazdka} other {# gwiazdek}}", "rating_description": "Wyświetl ocenę z EXIF w panelu informacji", "reaction_options": "Opcje reakcji", "read_changelog": "Zobacz Zmiany", + "readonly_mode_disabled": "Tryb tylko do odczytu wyłączony", + "readonly_mode_enabled": "Tryb tylko do odczytu włączony", + "ready_for_upload": "Gotowe do przesłania", "reassign": "Przypisz ponownie", "reassigned_assets_to_existing_person": "Przypisano ponownie {count, plural, one {# zasÃŗb} other {# zasobÃŗw}} do {name, select, null {istniejącej osoby} other {{name}}}", "reassigned_assets_to_new_person": "Przypisano ponownie {count, plural, one {# zasÃŗb} other {# zasobÃŗw}} do nowej osoby", @@ -1488,19 +1618,24 @@ "refreshing_faces": "OdświeÅŧanie twarzy", "refreshing_metadata": "OdświeÅŧanie metadanych", "regenerating_thumbnails": "Regenerowanie miniatur", + "remote": "Zdalny", + "remote_assets": "Zasoby zdalne", + "remote_media_summary": "Podsumowanie mediÃŗw zdalnych", "remove": "Usuń", - "remove_assets_album_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasÃŗb} other {# zasoby}} z albumu?", + "remove_assets_album_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}} z albumu?", "remove_assets_shared_link_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasÃŗb} other {# zasoby}} z tego udostępnionego linku?", "remove_assets_title": "Usunąć zasoby?", "remove_custom_date_range": "Usuń niestandardowy zakres dat", "remove_deleted_assets": "Usuń Niedostępne Pliki", "remove_from_album": "Usuń z albumu", + "remove_from_album_action_prompt": "{count} usunięto z albumu", "remove_from_favorites": "Usuń z ulubionych", + "remove_from_lock_folder_action_prompt": "{count} usunięte z folderu zablokowanego", "remove_from_locked_folder": "Usuń z folderu zablokowanego", "remove_from_locked_folder_confirmation": "Czy na pewno chcesz przenieść te zdjęcia i filmy z folderu zablokowanego? Będą one widoczne w bibliotece.", "remove_from_shared_link": "Usuń z udostępnionego linku", - "remove_memory": "Usuń pamięć", - "remove_photo_from_memory": "Usuń zdjęcia z tej pamięci", + "remove_memory": "Usuń wspomnienie", + "remove_photo_from_memory": "Usuń zdjęcia z tych wspomnień", "remove_tag": "Usuń tag", "remove_url": "Usuń URL", "remove_user": "Usuń uÅŧytkownika", @@ -1508,34 +1643,44 @@ "removed_from_archive": "Usunięto z archiwum", "removed_from_favorites": "Usunięto z ulubionych", "removed_from_favorites_count": "{count, plural, other {Usunięto #}} z ulubionych", - "removed_memory": "Pamięć została usunięta", - "removed_photo_from_memory": "Usunięto zdjęcie z pamięci", + "removed_memory": "Wspomnienie usunięte", + "removed_photo_from_memory": "Usunięto zdjęcie ze wspomnień", "removed_tagged_assets": "Usunięto etykietę z {count, plural, one {# zasobu} other {# zasobÃŗw}}", "rename": "Zmień nazwę", "repair": "Napraw", "repair_no_results_message": "Tutaj pojawią się nieśledzone i brakujące pliki", "replace_with_upload": "Prześlij nową wersję", "repository": "Repozytorium", - "require_password": "Wymagaj hasło", + "require_password": "Wymagaj hasła", "require_user_to_change_password_on_first_login": "Zmuś uÅŧytkownika do zmiany hasła podczas następnego logowania", "rescan": "Ponowne skanowanie", "reset": "Reset", "reset_password": "Resetuj hasło", "reset_people_visibility": "Zresetuj widoczność osÃŗb", "reset_pin_code": "Zresetuj kod PIN", + "reset_pin_code_description": "Jeśli zapomniałeś swojego kodu PIN, moÅŧesz skontaktować się z administratorem serwera, aby go zresetować", + "reset_pin_code_success": "Pomyślnie zresetowano kod PIN", + "reset_pin_code_with_password": "Zawsze moÅŧesz zresetować swÃŗj kod PIN za pomocą hasła", + "reset_sqlite": "Zresetuj bazę danych SQLite", + "reset_sqlite_confirmation": "Czy na pewno chcesz zresetować bazę danych SQLite? Wymagane będzie wylogowanie oraz ponowne zalogowanie, aby zsynchronizować dane", + "reset_sqlite_success": "Pomyślnie zresetowano bazę danych SQLite", "reset_to_default": "PrzywrÃŗÄ‡ ustawienia domyślne", "resolve_duplicates": "RozwiąÅŧ problemy z duplikatami", "resolved_all_duplicates": "Rozwiązano wszystkie duplikaty", "restore": "PrzywrÃŗcić", "restore_all": "PrzywrÃŗÄ‡ wszystko", + "restore_trash_action_prompt": "{count} przywrÃŗcono z kosza", "restore_user": "PrzywrÃŗÄ‡ uÅŧytkownika", "restored_asset": "PrzywrÃŗcony zasÃŗb", "resume": "WznÃŗw", + "resume_paused_jobs": "WznÃŗw {count, plural, one {# wstrzymane zadanie} few {# wstrzymane zadania} other {# wstrzymanych zadań}}", "retry_upload": "Prześlij ponownie", "review_duplicates": "Przejrzyj duplikaty", + "review_large_files": "Przejrzyj duÅŧe pliki", "role": "Rola", "role_editor": "Edytor", "role_viewer": "Widz", + "running": "W trakcie", "save": "Zapisz", "save_to_gallery": "Zapisz w galerii", "saved_api_key": "Zapisany klucz API", @@ -1551,7 +1696,7 @@ "search_albums": "Przeszukaj albumy", "search_by_context": "Wyszukaj według treści", "search_by_description": "Wyszukaj według opisu", - "search_by_description_example": "Jednodniowa wycieczka gÃŗrska w Bieszczady", + "search_by_description_example": "Całodniowa wycieczka w Bieszczady", "search_by_filename": "Szukaj według nazwy pliku lub rozszerzenia", "search_by_filename_example": "np. IMG_1234.JPG lub PNG", "search_camera_make": "Wyszukaj markę aparatu...", @@ -1600,7 +1745,7 @@ "search_tags": "Wyszukaj etykiety...", "search_timezone": "Wyszukaj strefę czasową...", "search_type": "Wyszukaj w", - "search_your_photos": "Szukaj swoich zdjęć", + "search_your_photos": "Przeszukaj swoje zdjęcia", "searching_locales": "Wyszukaj region...", "second": "Sekunda", "see_all_people": "Zobacz wszystkie osoby", @@ -1620,12 +1765,13 @@ "select_photos": "Wybierz zdjęcia", "select_trash_all": "Zaznacz wszystko do kosza", "select_user_for_sharing_page_err_album": "Nie udało się utworzyć albumu", - "selected": "Zaznaczone", + "selected": "Wybrane", "selected_count": "{count, plural, other {# wybrane}}", + "selected_gps_coordinates": "Wybrane WspÃŗÅ‚rzędne GPS", "send_message": "Wyślij wiadomość", "send_welcome_email": "Wyślij e-mail powitalny", "server_endpoint": "Punkt końcowy serwera", - "server_info_box_app_version": "Wersja Aplikacji", + "server_info_box_app_version": "Wersja aplikacji", "server_info_box_server_url": "Adres URL", "server_offline": "Serwer Offline", "server_online": "Serwer Online", @@ -1667,6 +1813,7 @@ "settings_saved": "Ustawienia zapisane", "setup_pin_code": "Ustaw kod PIN", "share": "Udostępnij", + "share_action_prompt": "Udostępniono {count} zasobÃŗw", "share_add_photos": "Dodaj zdjęcia", "share_assets_selected": "Wybrano {count}", "share_dialog_preparing": "Przygotowywanieâ€Ļ", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Skopiowane do schowka", "shared_link_clipboard_text": "Link: {link}\nHasło: {password}", "shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia", + "shared_link_custom_url_description": "OtwÃŗrz udostępniony link z niestandardowym adresem URL", "shared_link_edit_description_hint": "WprowadÅē opis udostępnienia", "shared_link_edit_expire_after_option_day": "1 dniu", "shared_link_edit_expire_after_option_days": "{count} dniach", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Zarządzaj udostępnionymi linkami", "shared_link_options": "Opcje udostępniania linku", + "shared_link_password_description": "Wymagaj hasła dostępu dla udostępnionego linku", "shared_links": "Udostępnione linki", "shared_links_description": "Udostępnij zdjęcia oraz filmy przez link", "shared_photos_and_videos_count": "{assetCount, plural, one {# udostępnione zdjęcie lub film.} other {# udostępnione zdjęcia i filmy.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "PokaÅŧ przejście pokazu slajdÃŗw", "show_supporter_badge": "Odznaka wspierającego", "show_supporter_badge_description": "PokaÅŧ odznakę wspierającego", + "show_text_search_menu": "PokaÅŧ menu wyszukiwania tekstowego", "shuffle": "Losuj", "sidebar": "Panel boczny", "sidebar_display_description": "Wyświetl link do widoku w pasku bocznym", @@ -1762,31 +1912,35 @@ "sort_created": "Data utworzenia", "sort_items": "Liczba rzeczy", "sort_modified": "Data modyfikacji", + "sort_newest": "Najnowsze zdjęcie", "sort_oldest": "Najstarsze zdjęcie", "sort_people_by_similarity": "Sortuj twarze według cech podobnych", "sort_recent": "Najnowsze zdjęcie", "sort_title": "Tytuł", "source": "ÅšrÃŗdło", "stack": "Stos", + "stack_action_prompt": "{count} zgrupowano", "stack_duplicates": "Stos duplikatÃŗw", - "stack_select_one_photo": "Wybierz jedno gÅ‚Ãŗwne zdjęcie do stosu", - "stack_selected_photos": "Układaj wybrane zdjęcia", - "stacked_assets_count": "UłoÅŧone {count, plural, one {# zasÃŗb} other{# zasoby}}", + "stack_select_one_photo": "Wybierz jedno gÅ‚Ãŗwne zdjęcie dla stosu", + "stack_selected_photos": "UtwÃŗrz stos z wybranych zdjęć", + "stacked_assets_count": "Utworzono stos z {count, plural, one {# zasobu} other {# zasobÃŗw}}", "stacktrace": "Ślad stosu", "start": "Start", "start_date": "Od dnia", + "start_date_before_end_date": "Data początkowa musi być wcześniejsza niÅŧ data końcowa", "state": "WojewÃŗdztwo", "status": "Status", "stop_casting": "Zatrzymaj strumieniowanie", "stop_motion_photo": "Zatrzymaj zdjęcie w ruchu", "stop_photo_sharing": "Przestać udostępniać swoje zdjęcia?", - "stop_photo_sharing_description": "Od teraz {partner} nie będzie widzieć Twoich zdjęć.", + "stop_photo_sharing_description": "{partner} nie będzie juÅŧ mieć dostępu do Twoich zdjęć.", "stop_sharing_photos_with_user": "Przestań udostępniać zdjęcia temu uÅŧytkownikowi", "storage": "Przestrzeń dyskowa", "storage_label": "Etykieta magazynu", "storage_quota": "Limit pamięci", - "storage_usage": "{used} z {available} uÅŧyte", + "storage_usage": "Wykorzystano {used} z {available}", "submit": "ZatwierdÅē", + "success": "Sukces", "suggestions": "Sugestie", "sunrise_on_the_beach": "WschÃŗd słońca na plaÅŧy", "support": "Wsparcie", @@ -1795,7 +1949,11 @@ "swap_merge_direction": "Zmień kierunek złączenia", "sync": "Synchronizuj", "sync_albums": "Synchronizuj albumy", - "sync_albums_manual_subtitle": "Zsynchronizuj wszystkie przesłane filmy i zdjęcia z wybranymi albumami kopii zapasowych", + "sync_albums_manual_subtitle": "Zsynchronizuj wszystkie przesłane filmy i zdjęcia z wybranymi albumami z włączoną kopią zapasową", + "sync_local": "Synchronizacja lokalna", + "sync_remote": "Synchronizacja zdalna", + "sync_status": "Stan synchronizacji", + "sync_status_subtitle": "Wyświetl i zarządzaj systemem synchronizacji", "sync_upload_album_setting_subtitle": "TwÃŗrz i przesyłaj swoje zdjęcia i filmy do wybranych albumÃŗw w Immich", "tag": "Etykieta", "tag_assets": "Ustaw etykiety zasobÃŗw", @@ -1806,6 +1964,7 @@ "tag_updated": "Uaktualniono etykietę: {tag}", "tagged_assets": "Przypisano etykietę {count, plural, one {# zasobowi} other {# zasobom}}", "tags": "Etykiety", + "tap_to_run_job": "Uruchom zadanie", "template": "Szablon", "theme": "Motyw", "theme_selection": "WybÃŗr motywu", @@ -1828,18 +1987,21 @@ "time_based_memories": "Wspomnienia oparte na czasie", "timeline": "Oś czasu", "timezone": "Strefa czasowa", - "to_archive": "Archiwum", + "to_archive": "Zarchiwizuj", "to_change_password": "Zmień hasło", "to_favorite": "Dodaj do ulubionych", "to_login": "Zaloguj się", + "to_multi_select": "aby wybrać wiele", "to_parent": "IdÅē do rodzica", + "to_select": "aby wybrać", "to_trash": "Kosz", "toggle_settings": "Przełącz ustawienia", - "total": "Całkowity", + "total": "Razem", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", + "trash_action_prompt": "{count} przeniesione do kosza", "trash_all": "Usuń wszystkie", - "trash_count": "Kosz {count, number}", + "trash_count": "Usuń {count, number}", "trash_delete_asset": "Kosz/Usuń zasÃŗb", "trash_emptied": "OprÃŗÅŧnione śmieci", "trash_no_results_message": "Tu znajdziesz wyrzucone zdjęcia i filmy.", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Wybierz zasoby", "trash_page_title": "Kosz ({count})", "trashed_items_will_be_permanently_deleted_after": "Wyrzucone zasoby zostaną trwale usunięte po {days, plural, one {jednym dniu} other {# dniach}}.", + "troubleshoot": "RozwiąÅŧ problemy", "type": "Typ", "unable_to_change_pin_code": "Nie moÅŧna zmienić kodu PIN", "unable_to_setup_pin_code": "Nie moÅŧna ustawić kodu PIN", - "unarchive": "Cofnij archiwizację", + "unarchive": "PrzywrÃŗÄ‡ z archiwum", + "unarchive_action_prompt": "{count} usunięto z archiwum", "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", "undo": "Cofnij", "unfavorite": "Usuń z ulubionych", + "unfavorite_action_prompt": "{count} usunięto z ulubionych", "unhide_person": "PrzywrÃŗÄ‡ osobę", "unknown": "Nieznany", "unknown_country": "Nieznane państwo", @@ -1874,16 +2039,22 @@ "unselect_all": "Odznacz wszystko", "unselect_all_duplicates": "Odznacz wszystkie duplikaty", "unselect_all_in": "Odznacz wszystkie w {group}", - "unstack": "RozÅ‚ÃŗÅŧ stos", - "unstacked_assets_count": "{count, plural, one {RozłoÅŧony # zasÃŗb} few {RozłoÅŧone # zasoby} other {RozłoÅŧonych # zasobÃŗw}}", + "unstack": "Rozdziel stos", + "unstack_action_prompt": "{count} rozdzielono", + "unstacked_assets_count": "Rozdzielono {count, plural, one {# zasÃŗb} few {# zasoby} other {# zasobÃŗw}}", + "untagged": "Nieoznaczone", "up_next": "Do następnego", + "update_location_action_prompt": "Zaktualizuj lokalizację {count} wybranych zasobÃŗw na:", "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", "upload": "Prześlij", + "upload_action_prompt": "{count} w kolejce do wysłania", "upload_concurrency": "WspÃŗÅ‚bieÅŧność wysyłania", + "upload_details": "SzczegÃŗÅ‚y przesyłania", "upload_dialog_info": "Czy chcesz wykonać kopię zapasową wybranych zasobÃŗw na serwerze?", "upload_dialog_title": "Prześlij ZasÃŗb", "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błędem} other {# błędami}}. OdświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", + "upload_finished": "Przesyłanie zakończone", "upload_progress": "Pozostałe {remaining, number} - Przetworzone {processed, number}/{total, number}", "upload_skipped_duplicates": "Pominięto {count, plural, one {# zduplikowany zasÃŗb} few {# zduplikowane zasoby} other {# zduplikowanych zasobÃŗw}}", "upload_status_duplicates": "Duplikaty", @@ -1892,6 +2063,7 @@ "upload_success": "Przesyłanie powiodło się, odświeÅŧ stronę, aby zobaczyć nowo przesłane zasoby.", "upload_to_immich": "Prześlij do Immich ({count})", "uploading": "Przesyłanie", + "uploading_media": "Przesyłanie multimediÃŗw", "url": "URL", "usage": "UÅŧycie", "use_biometric": "UÅŧyj biometrii", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Wyświetl statystyki uÅŧytkowania konta", "username": "Nazwa uÅŧytkownika", "users": "UÅŧytkownicy", + "users_added_to_album_count": "Dodano {count, plural, one {# uÅŧytkownika} other {# uÅŧytkownikÃŗw}} do albumu", "utilities": "Narzędzia", "validate": "Walidacja", "validate_endpoint_error": "Proszę wprowadzić prawidłowy adres URL", @@ -1930,6 +2103,7 @@ "view_album": "Wyświetl Album", "view_all": "PokaÅŧ wszystkie", "view_all_users": "PokaÅŧ wszystkich uÅŧytkownikÃŗw", + "view_details": "Zobacz szczegÃŗÅ‚y", "view_in_timeline": "PokaÅŧ na osi czasu", "view_link": "Zobacz link", "view_links": "PokaÅŧ łącza", @@ -1937,11 +2111,12 @@ "view_next_asset": "Wyświetl następny zasÃŗb", "view_previous_asset": "Wyświetl poprzedni zasÃŗb", "view_qr_code": "PokaÅŧ kod QR", - "view_stack": "Zobacz UłoÅŧenie", + "view_similar_photos": "Zobacz podobne zdjęcia", + "view_stack": "Zobacz stos", "view_user": "Wyświetl uÅŧytkownika", "viewer_remove_from_stack": "Usuń ze stosu", "viewer_stack_use_as_main_asset": "UÅŧyj jako gÅ‚Ãŗwnego zasobu", - "viewer_unstack": "RozÅ‚ÃŗÅŧ Stos", + "viewer_unstack": "Rozdziel stos", "visibility_changed": "Zmieniono widoczność dla {count, plural, one {# osoby} other {# osÃŗb}}", "waiting": "Oczekujące", "warning": "OstrzeÅŧenie", @@ -1955,5 +2130,6 @@ "yes": "Tak", "you_dont_have_any_shared_links": "Nie masz Åŧadnych udostępnionych linkÃŗw", "your_wifi_name": "Twoja nazwa Wi-Fi", - "zoom_image": "Powiększ obraz" + "zoom_image": "Powiększ obraz", + "zoom_to_bounds": "Powiększ do krawędzi" } diff --git a/i18n/pt.json b/i18n/pt.json index bb88cafbfa..6bd4a786dd 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -14,6 +14,7 @@ "add_a_location": "Adicionar localizaÃ§ÃŖo", "add_a_name": "Adicionar um nome", "add_a_title": "Adicionar um título", + "add_birthday": "Definir aniversÃĄrio", "add_endpoint": "Adicionar URL", "add_exclusion_pattern": "Adicionar um padrÃŖo de exclusÃŖo", "add_import_path": "Adicionar um caminho de importaÃ§ÃŖo", @@ -27,6 +28,10 @@ "add_to_album": "Adicionar ao ÃĄlbum", "add_to_album_bottom_sheet_added": "Adicionado a {album}", "add_to_album_bottom_sheet_already_exists": "JÃĄ existe em {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns conteÃēdos locais nÃŖo puderam ser adicionados no ÃĄlbum", + "add_to_album_toggle": "Alternar seleÃ§ÃŖo para {album}", + "add_to_albums": "Adicionar aos ÃĄlbuns", + "add_to_albums_count": "Adicionar aos ÃĄlbuns ({count})", "add_to_shared_album": "Adicionar ao ÃĄlbum partilhado", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", @@ -44,6 +49,13 @@ "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_onboarding_1_description": "Uma cÃŗpia remota na cloud ou outra localizaÃ§ÃŖo física.", + "backup_onboarding_2_description": "CÃŗpias locais em dispositivos diferentes, incluindo os ficheiros principais e uma cÃŗpia de segurança local dos mesmos.", + "backup_onboarding_3_description": "CÃŗpias completas dos seus dados, incluindo os ficheiros originais. Inclui uma cÃŗpia remota e duas cÃŗpias locais.", + "backup_onboarding_description": "É recomendada a estratÊgia de cÃŗpia de segurança 3-2-1 para proteger os seus dados. Para uma soluÃ§ÃŖo de cÃŗpia de segurança completa, deve manter cÃŗpias das suas fotos e vídeos tal como da base de dados do Immich.", + "backup_onboarding_footer": "Para mais informaçÃĩes sobre como criar uma cÃŗpia de segurança do Immich, por favor leia a documentaÃ§ÃŖo.", + "backup_onboarding_parts_title": "A cÃŗpia de segurança 3-2-1 Ê definida por:", + "backup_onboarding_title": "CÃŗpias de segurança", "backup_settings": "DefiniçÃĩes de CÃŗpia da Base de Dados", "backup_settings_description": "Gerir definiçÃĩes de cÃŗpia da base de dados.", "cleared_jobs": "Eliminadas as tarefas de: {job}", @@ -112,6 +124,13 @@ "logging_enable_description": "Ativar registo", "logging_level_description": "Quando ativado, qual o nível de log a usar.", "logging_settings": "Registo", + "machine_learning_availability_checks": "VerificaÃ§ÃŖo de disponibilidade", + "machine_learning_availability_checks_description": "Detectar automaticamente e dar preferÃĒncia aos servidores de aprendizagem automÃĄtica disponíveis", + "machine_learning_availability_checks_enabled": "Ativar confirmaçÃĩes de disponibilidade", + "machine_learning_availability_checks_interval": "ConfirmaÃ§ÃŖo de intervalo", + "machine_learning_availability_checks_interval_description": "Intervalo, em milisegundos, entre confirmaçÃĩes de disponibilidade", + "machine_learning_availability_checks_timeout": "Tempo limite para requisiÃ§ÃŖo", + "machine_learning_availability_checks_timeout_description": "Tempo limite em milissegundos para verificaçÃĩes de disponibilidade", "machine_learning_clip_model": "Modelo CLIP", "machine_learning_clip_model_description": "O nome do modelo CLIP definido aqui. Tome nota de que Ê necessÃĄrio voltar a executar a tarefa de \"Pesquisa Inteligente\" para todas as imagens depois de alterar o modelo.", "machine_learning_duplicate_detection": "DeteÃ§ÃŖo de Itens Duplicados", @@ -166,6 +185,20 @@ "metadata_settings_description": "Gerir definiçÃĩes de metadados", "migration_job": "MigraÃ§ÃŖo", "migration_job_description": "Migra miniaturas de ficheiros e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Executar reconhecimento facial em faces detetadas recentemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novas faces", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpar dados antigos e expirados da base de dados", + "nightly_tasks_generate_memories_setting": "Gerar memÃŗrias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memÃŗrias a partir de ficheiros", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Colocar em fila ficheiros sem miniaturas para a geraÃ§ÃŖo das mesmas", + "nightly_tasks_settings": "DefiniçÃĩes de Tarefas DiÃĄrias", + "nightly_tasks_settings_description": "Gerir tarefas diÃĄrias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora em qual o servidor começa a executar as tarefas diÃĄrias", + "nightly_tasks_sync_quota_usage_setting": "UtilizaÃ§ÃŖo da quota de sincronizaÃ§ÃŖo", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento de utilizadores, com base na utilizaÃ§ÃŖo atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrÃŖo adicionado", "note_apply_storage_label_previous_assets": "ObservaÃ§ÃŖo: Para aplicar o RÃŗtulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override": "SubstituiÃ§ÃŖo de URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo permite um URI mÃŗvel, como ''{callback}''", + "oauth_role_claim": "ReivindicaÃ§ÃŖo de FunçÃĩes", + "oauth_role_claim_description": "Automaticamente concede acesso de administrador, com base na presença desta reivindicaÃ§ÃŖo. A reivindicaÃ§ÃŖo tanto pode ter \"user\" como \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Gerir definiçÃĩes de inicio de sessÃŖo do OAuth", "oauth_settings_more_details": "Para mais informaçÃĩes sobre esta funcionalidade, veja a documentaÃ§ÃŖo.", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "NÃēmero de dias para manter os ficheiros na reciclagem antes de os eliminar permanentemente", "trash_settings": "DefiniçÃĩes da Reciclagem", "trash_settings_description": "Gerir definiçÃĩes da reciclagem", + "unlink_all_oauth_accounts": "Desvincular todas as contas OAuth", + "unlink_all_oauth_accounts_description": "Lembre-se de desvincular todas as contas OAuth antes de migrar para um novo provedor.", + "unlink_all_oauth_accounts_prompt": "Tem a certeza de que deseja desvincular todas as contas OAuth? Isto irÃĄ redefinir o ID OAuth de cada utilizador e nÃŖo poderÃĄ ser anulado.", "user_cleanup_job": "Limpeza de utilizadores", "user_delete_delay": "A conta e os ficheiros de {user} serÃŖo agendados para eliminaÃ§ÃŖo permanente dentro de {delay, plural, one {# dia} other {# dias}}.", "user_delete_delay_settings": "Atraso de eliminaÃ§ÃŖo", @@ -360,10 +398,12 @@ "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: {level}", - "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_subtitle": "Alguns dispositivos sÃŖo extremamente lentos a carregar miniaturas da memÃŗria interna. 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_readonly_mode_subtitle": "Ativa o modo sÃŗ de leitura, onde as fotos apenas podem ser visualizadas. FunçÃĩes como selecionar vÃĄrias imagens, partilhar, transmitir e eliminar ficam deactivadas. Pode ativar ou desativar o modo sÃŗ de leitura atravÊs da imagem de perfil do utilizador na janela principal", + "advanced_settings_readonly_mode_title": "Modo sÃŗ de leitura", "advanced_settings_self_signed_ssl_subtitle": "NÃŖo validar o certificado SSL com o endereço do servidor. Isto Ê necessÃĄrio para certificados auto-assinados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto-assinados", "advanced_settings_sync_remote_deletions_subtitle": "Automaticamente eliminar ou restaurar um ficheiro neste dispositivo quando essa mesma aÃ§ÃŖo for efetuada na web", @@ -379,6 +419,7 @@ "album_cover_updated": "Capa do ÃĄlbum atualizada", "album_delete_confirmation": "Tem a certeza de que quer eliminar o ÃĄlbum {album}?", "album_delete_confirmation_description": "Se este ÃĄlbum for partilhado, os outros utilizadores deixam de o poder aceder.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "InformaçÃĩes do ÃĄlbum atualizadas", @@ -388,7 +429,9 @@ "album_options": "OpçÃĩes de ÃĄlbum", "album_remove_user": "Remover utilizador?", "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", + "album_search_not_found": "Nenhum ÃĄlbum encontrado segundo a pesquisa", "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_summary": "Resumo do ÃĄlbum", "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}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Ordem padrÃŖo de organizaÃ§ÃŖo do ÃĄlbum", "albums_default_sort_order_description": "Ordem inicial dos ficheiros ao criar novos ÃĄlbuns.", "albums_feature_description": "ColeçÃĩes de ficheiros que podem ser partilhados com outros utilizadores.", + "albums_on_device_count": "Álbums no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os ÃĄlbuns", "all_people": "Todas as pessoas", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Sair", "app_settings": "DefiniçÃĩes da AplicaÃ§ÃŖo", "appears_in": "Aparece em", + "apply_count": "Aplicar ({count, number})", "archive": "Arquivo", + "archive_action_prompt": "{count} adicionados ao Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivo ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "Arquivo restaurado com sucesso", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na reciclagem", + "asset_trashed": "Ficheiro apagado", + "asset_troubleshoot": "ResoluÃ§ÃŖo de problemas com conteÃēdos", "asset_uploaded": "Enviado", "asset_uploading": "A enviarâ€Ļ", "asset_viewer_settings_subtitle": "Gerenciar as configuraçÃĩes do visualizador da galeria", @@ -464,8 +512,9 @@ "assets": "Ficheiros", "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao ÃĄlbum", - "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {{name}} other {novo ÃĄlbum}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {Foi adicionado # ficheiro} other {Foram adiciondos # ficheiros}} a {albumTotal, plural, one {# ÃĄlbum} other {# albuns}}", "assets_cannot_be_added_to_album_count": "NÃŖo foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao ÃĄlbum", + "assets_cannot_be_added_to_albums": "{count, plural, one {Ficheiro nÃŖo pode ser adicionado} other {Ficheiros nÃŖo podem ser adiciondos}} a nenhum dos ÃĄlbuns", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} ficheiro(s) eliminado(s) permanentemente do servidor Immich", @@ -482,20 +531,25 @@ "assets_trashed_count": "{count, plural, one {# ficheiro enviado} other {# ficheiros enviados}} para a reciclagem", "assets_trashed_from_server": "{count} ficheiro(s) do servidor Immich foi/foram enviados para a reciclagem", "assets_were_part_of_album_count": "{count, plural, one {O ficheiro jÃĄ fazia} other {Os ficheiros jÃĄ faziam}} parte do ÃĄlbum", + "assets_were_part_of_albums_count": "{count, plural, one {Ficheiro jÃĄ fazia} other {Ficheiros jÃĄ faziam}} parte dos ÃĄlbuns", "authorized_devices": "Dispositivos Autorizados", "automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexÃĩes alternativas em outras redes", "automatic_endpoint_switching_title": "Troca automÃĄtica de URL", "autoplay_slideshow": "ApresentaÃ§ÃŖo automÃĄtica de diapositivos", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", + "background_backup_running_error": "Com a cÃŗpia de segurança de fundo em execuÃ§ÃŖo, nÃŖo Ê possível inicar uma manual", "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissÃŖo de localizaÃ§ÃŖo precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_options": "OpçÃĩes de fundo", + "backup": "CÃŗpia de segurança", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", "backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vÃĄrios ÃĄlbuns. Assim, os ÃĄlbuns podem ser incluídos ou excluídos durante o processo de backup.", "backup_album_selection_page_select_albums": "Selecione Álbuns", "backup_album_selection_page_selection_info": "InformaçÃĩes da SeleÃ§ÃŖo", "backup_album_selection_page_total_assets": "Total de arquivos Ãēnicos", + "backup_albums_sync": "CÃŗpia de segurança de sincronizaÃ§ÃŖo de ÃĄlbuns", "backup_all": "Tudo", "backup_background_service_backup_failed_message": "Ocorreu um erro ao efetuar cÃŗpia de segurança dos ficheiros. A tentar de novoâ€Ļ", "backup_background_service_connection_failed_message": "Ocorreu um erro na ligaÃ§ÃŖo ao servidor. A tentar de novoâ€Ļ", @@ -535,7 +589,7 @@ "backup_controller_page_remainder": "Restante", "backup_controller_page_remainder_sub": "Fotos e vídeos selecionados restantes para fazer backup", "backup_controller_page_server_storage": "Armazenamento no servidor", - "backup_controller_page_start_backup": "Iniciar Backup", + "backup_controller_page_start_backup": "Iniciar CÃŗpia de Segurança", "backup_controller_page_status_off": "Backup automÃĄtico desativado", "backup_controller_page_status_on": "Backup automÃĄtico ativado", "backup_controller_page_storage_format": "{used} de {total} utilizado", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Ativar backup", "backup_controller_page_uploading_file_info": "Enviando arquivo", "backup_err_only_album": "NÃŖo Ê possível remover apenas o ÃĄlbum", + "backup_error_sync_failed": "A sincronizaÃ§ÃŖo falhou. NÃŖo Ê possível fazer cÃŗpia de segurança.", "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio jÃĄ estÃĄ em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", "backup_manual_title": "Estado do envio", + "backup_options": "DefiniçÃĩes de cÃŗpia de segurança", "backup_options_page_title": "OpçÃĩes de backup", "backup_setting_subtitle": "Gerenciar as configuraçÃĩes de envio em primeiro e segundo plano", + "backup_settings_subtitle": "Gerir definiçÃĩes de carregamento", "backward": "Para trÃĄs", "biometric_auth_enabled": "AutenticaÃ§ÃŖo biomÊtrica ativada", "biometric_locked_out": "EstÃĄ impedido de utilizar a autenticaÃ§ÃŖo biomÊtrica", @@ -570,7 +627,7 @@ "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_subtitle": "Fotos e vídeos que estÃŖo na lista de bloqueio da aplicaÃ§ÃŖo", "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -587,6 +644,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "A cancelar", "cannot_merge_people": "NÃŖo foi possível unir pessoas", "cannot_undo_this_action": "NÃŖo Ê possível anular esta aÃ§ÃŖo!", "cannot_update_the_description": "NÃŖo foi possível atualizar a descriÃ§ÃŖo", @@ -609,6 +667,8 @@ "change_pin_code": "Alterar cÃŗdigo PIN", "change_your_password": "Alterar a sua palavra-passe", "changed_visibility_successfully": "Visibilidade alterada com sucesso", + "charging": "A carregar", + "charging_requirement_mobile_backup": "CÃŗpia de segurança de fundo necessita que o dispositivo esteja a carregar", "check_corrupt_asset_backup": "Verificar por backups corrompidos", "check_corrupt_asset_backup_button": "Verificar", "check_corrupt_asset_backup_description": "Execute esta verificaÃ§ÃŖo somente em uma rede Wi-Fi e quando o backup de todos os arquivos jÃĄ estiver concluído. O processo demora alguns minutos.", @@ -618,6 +678,7 @@ "clear": "Limpar", "clear_all": "Limpar tudo", "clear_all_recent_searches": "Limpar todas as pesquisas recentes", + "clear_file_cache": "Limpar cache de ficheiros", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", "client_cert_dialog_msg_confirm": "OK", @@ -688,11 +749,13 @@ "create_new_user": "Criar novo utilizador", "create_shared_album_page_share_add_assets": "ADICIONAR ARQUIVOS", "create_shared_album_page_share_select_photos": "Selecionar Fotos", + "create_shared_link": "Criar link partilhado", "create_tag": "Criar etiqueta", "create_tag_description": "Criar uma nova etiqueta. Para etiquetas compostas, introduza o caminho completo, incluindo as barras.", "create_user": "Criar utilizador", "created": "Criado", "created_at": "Criado a", + "creating_linked_albums": "A criar albuns ligados...", "crop": "Cortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo atual", @@ -700,10 +763,11 @@ "current_server_address": "Endereço atual do servidor", "custom_locale": "LocalizaÃ§ÃŖo Personalizada", "custom_locale_description": "Formatar datas e nÃēmeros baseados na língua e na regiÃŖo", + "custom_url": "URL personalizado", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "Alternar tema escuro", + "dark_theme": "Alternar tema escuro", "date_after": "Data apÃŗs", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Data de nascimento guardada com sucesso", "date_range": "Intervalo de datas", "day": "Dia", + "days": "Dias", "deduplicate_all": "Remover todos os duplicados", "deduplication_criteria_1": "Tamanho da imagem em bytes", "deduplication_criteria_2": "Quantidade de dados EXIF", @@ -719,22 +784,27 @@ "default_locale": "LocalizaÃ§ÃŖo PadrÃŖo", "default_locale_description": "Formatar datas e nÃēmeros baseados na linguagem do seu navegador", "delete": "Eliminar", - "delete_album": "Eliminar ÃĄlbum", - "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", + "delete_action_confirmation_message": "Tem a certeza de que quer eliminar este ficheiro? EstÃĄ aÃ§ÃŖo irÃĄ mover o ficheiro para a reciclagem do servidor e perguntar se quer apagÃĄ-lo localmente", + "delete_action_prompt": "{count} eliminados", + "delete_album": "Apagar ÃĄlbum", + "delete_api_key_prompt": "Tem a certeza de que deseja remover esta chave de API?", "delete_dialog_alert": "Esses arquivos serÃŖo permanentemente apagados do Immich e de seu dispositivo", "delete_dialog_alert_local": "Estes arquivos serÃŖo permanentemente excluídos do seu dispositivo, mas continuarÃŖo disponíveis no servidor Immich", "delete_dialog_alert_local_non_backed_up": "NÃŖo hÃĄ backup de alguns dos arquivos no servidor e eles serÃŖo excluídos permanentemente do seu dispositivo", "delete_dialog_alert_remote": "Estes arquivos serÃŖo permanentemente excluídos do servidor Immich", - "delete_dialog_ok_force": "Excluir mesmo assim", + "delete_dialog_ok_force": "Confirmo que quero excluir", "delete_dialog_title": "Excluir Permanentemente", "delete_duplicates_confirmation": "Tem a certeza de que deseja eliminar permanentemente estes itens duplicados?", "delete_face": "Remover rosto", - "delete_key": "Eliminar chave", + "delete_key": "Apagar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar link de partilha", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Eliminar etiqueta", @@ -745,6 +815,7 @@ "description": "DescriÃ§ÃŖo", "description_input_hint_text": "Adicionar descriÃ§ÃŖo...", "description_input_submit_error": "Erro ao atualizar a descriÃ§ÃŖo, verifique o registo para obter mais detalhes", + "deselect_all": "Remover seleÃ§ÃŖo de tudo", "details": "Detalhes", "direction": "DireÃ§ÃŖo", "disabled": "Desativado", @@ -762,6 +833,7 @@ "documentation": "DocumentaÃ§ÃŖo", "done": "Feito", "download": "Transferir", + "download_action_prompt": "A descarregar {count} ficheiros", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -788,8 +860,12 @@ "edit": "Editar", "edit_album": "Editar ÃĄlbum", "edit_avatar": "Editar imagem de perfil", + "edit_birthday": "Editar aniversÃĄrio", "edit_date": "Editar data", "edit_date_and_time": "Editar data e hora", + "edit_date_and_time_action_prompt": "Alterada a data e hora de {count} ficheiros", + "edit_date_and_time_by_offset": "Alterar data com diferença", + "edit_date_and_time_by_offset_interval": "Novo período: {from} - {to}", "edit_description": "Editar descriÃ§ÃŖo", "edit_description_prompt": "Por favor selecione uma nova descriÃ§ÃŖo:", "edit_exclusion_pattern": "Editar o padrÃŖo de exclusÃŖo", @@ -799,6 +875,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar LocalizaÃ§ÃŖo", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "LocalizaÃ§ÃŖo", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -817,6 +894,7 @@ "empty_trash": "Esvaziar reciclagem", "empty_trash_confirmation": "Tem a certeza de que deseja esvaziar a reciclagem? Isto removerÃĄ todos os ficheiros da reciclagem do Immich permanentemente.\nNÃŖo Ê possível anular esta aÃ§ÃŖo!", "enable": "Ativar", + "enable_backup": "Ativar CÃŗpia de Segurança", "enable_biometric_auth_description": "Insira o cÃŗdigo PIN para ativar a autenticaÃ§ÃŖo biomÊtrica", "enabled": "Ativado", "end_date": "Data final", @@ -827,7 +905,9 @@ "error": "Erro", "error_change_sort_album": "Ocorreu um erro ao mudar a ordem de exibiÃ§ÃŖo", "error_delete_face": "Falha ao remover rosto do ficheiro", + "error_getting_places": "Erro ao obter locais", "error_loading_image": "Erro ao carregar a imagem", + "error_loading_partners": "Erro ao carregar parceiros: {error}", "error_saving_image": "Erro: {error}", "error_tag_face_bounding_box": "Erro ao marcar o rosto - nÃŖo foi possível localizar o rosto", "error_title": "Erro - Algo correu mal", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "Ocorreu um erro ao carregar notificaçÃĩes", "failed_to_load_people": "Ocorreu um erro ao carregar pessoas", "failed_to_remove_product_key": "Ocorreu um erro ao remover chave de produto", + "failed_to_reset_pin_code": "Ocorreu um erro ao repor o cÃŗdigo PIN", "failed_to_stack_assets": "Ocorreu um erro ao empilhar os ficheiros", "failed_to_unstack_assets": "Ocorreu um erro ao desempilhar ficheiros", "failed_to_update_notification_status": "Ocorreu um erro ao atualizar o estado das notificaçÃĩes", @@ -868,6 +949,7 @@ "paths_validation_failed": "Ocorreu um erro na validaÃ§ÃŖo de {paths, plural, one {# caminho} other {# caminhos}}", "profile_picture_transparent_pixels": "Imagem de perfil nÃŖo pode ter pixeis transparentes. Por favor amplie e/ou mova a imagem.", "quota_higher_than_disk_size": "Definiu uma quota maior do que o tamanho do disco", + "something_went_wrong": "Algo correu mal", "unable_to_add_album_users": "NÃŖo foi possível adicionar utilizadores ao ÃĄlbum", "unable_to_add_assets_to_shared_link": "NÃŖo foi possível adicionar os ficheiros ao link partilhado", "unable_to_add_comment": "NÃŖo foi possível adicionar o comentÃĄrio", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Adicionar DescriÃ§ÃŖo...", + "exif_bottom_sheet_description_error": "Ocorreu um erro ao alterar a descriÃ§ÃŖo", "exif_bottom_sheet_details": "DETALHES", "exif_bottom_sheet_location": "LOCALIZAÇÃO", "exif_bottom_sheet_people": "PESSOAS", "exif_bottom_sheet_person_add_person": "Adicionar nome", - "exif_bottom_sheet_person_age_months": "Idade {months} meses", - "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", - "exif_bottom_sheet_person_age_years": "Idade {years}", "exit_slideshow": "Sair da apresentaÃ§ÃŖo", "expand_all": "Expandir tudo", "experimental_settings_new_asset_list_subtitle": "Trabalho em andamento", @@ -973,6 +1053,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Base de Dados", + "export_database_description": "Exportar a Base de Dados SQLite", "extension": "ExtensÃŖo", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Ocorreu um erro ao carregar ficheiros", "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} adicionados aos favoritos", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhum favorito encontrado", "feature_photo_updated": "Foto principal atualizada", "features": "Funcionalidades", + "features_in_development": "Funcionalidades em Desenvolvimento", "features_setting_description": "Configurar as funcionalidades da aplicaÃ§ÃŖo", "file_name": "Nome do ficheiro", "file_name_or_extension": "Nome do ficheiro ou extensÃŖo", @@ -998,21 +1082,26 @@ "filter_people": "Filtrar pessoas", "filter_places": "Filtrar lugares", "find_them_fast": "Encontre-as mais rapidamente pelo nome numa pesquisa", + "first": "Primeiro", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", "folder": "Pasta", "folder_not_found": "Pasta nÃŖo encontrada", "folders": "Pastas", "folders_feature_description": "Navegar na vista de pastas por fotos e vídeos no sistema de ficheiros", + "forgot_pin_code_question": "Esqueceu-se do seu PIN?", "forward": "Para a frente", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Esta funcionalidade requer o carregamento de recursos externos da Google para poder funcionar.", "general": "Geral", + "geolocation_instruction_location": "Clique num ficheiro com coordenadas GPS para usar a sua localizaÃ§ÃŖo ou selecione um local diretamente do mapa", "get_help": "Obter Ajuda", "get_wifiname_error": "NÃŖo foi possível obter o nome do Wi-Fi. Verifique se concedeu as permissÃĩes necessÃĄrias e se estÃĄ conectado a uma rede Wi-Fi", "getting_started": "Primeiros Passos", "go_back": "Regressar", "go_to_folder": "Ir para a pasta", "go_to_search": "Ir para a pesquisa", + "gps": "GPS", + "gps_missing": "Sem GPS", "grant_permission": "Conceder permissÃŖo", "group_albums_by": "Agrupar ÃĄlbuns por...", "group_country": "Agrupar por país", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Habilitar vibraÃ§ÃŖo", "haptic_feedback_title": "VibraÃ§ÃŖo", "has_quota": "Tem quota", + "hash_asset": "Criptografar ficheiro", + "hashed_assets": "Ficheiros criptografados", + "hashing": "A criptografar", "header_settings_add_header_tip": "Adicionar cabeçalho", "header_settings_field_validator_msg": "Campo deve ser preenchido", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "SÃŗ Ê possível enviar 30 arquivos por vez, ignorando", "host": "Servidor", "hour": "Hora", + "hours": "Horas", "id": "ID", + "idle": "Em espera", "ignore_icloud_photos": "ignorar fotos no iCloud", "ignore_icloud_photos_description": "Fotos que estÃŖo armazenadas no iCloud nÃŖo serÃŖo carregadas para o servidor do Immich", "image": "Imagem", @@ -1112,10 +1206,13 @@ "language_no_results_title": "Nenhuma língua encontrada", "language_search_hint": "Procurar línguas...", "language_setting_description": "Selecione o seu Idioma preferido", + "large_files": "Ficheiros Grandes", + "last": "Último", "last_seen": "Visto pela ultima vez", "latest_version": "VersÃŖo mais recente", "latitude": "Latitude", "leave": "Sair", + "leave_album": "Sair do ÃĄlbum", "lens_model": "Modelo de lente", "let_others_respond": "Permitir respostas", "level": "Nível", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "Data de criaÃ§ÃŖo", "library_page_sort_last_modified": "Última modificaÃ§ÃŖo", "library_page_sort_title": "Título do ÃĄlbum", + "licenses": "Licenças", "light": "Claro", + "like": "Gosto", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", - "link_options": "OpçÃĩes do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Associada", "list": "Lista", "loading": "A Carregar", "loading_search_results_failed": "Ocorreu um erro ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "NÃŖo Ê possível transmitir um ficheiro que nÃŖo tenha sido enviado antes para o servidor", + "local_assets": "Ficheiros Locais", + "local_media_summary": "SumÃĄrio de conteÃēdo local", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irÃĄ se conectar ao servidor atravÊs desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "PermissÃŖo de localizaÃ§ÃŖo", @@ -1148,12 +1249,13 @@ "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", "locked_folder": "Pasta Trancada", + "log_detail_title": "Detalhes de registo", "log_out": "Sair", "log_out_all_devices": "Terminar a sessÃŖo de todos os dispositivos", "logged_in_as": "Utilizador atual: {user}", "logged_out_all_devices": "SessÃŖo terminada em todos os dispositivos", "logged_out_device": "SessÃŖo terminada no dispositivo", - "login": "Iniciar sessÃŖo", + "login": "Iniciar SessÃŖo", "login_disabled": "Login desativado", "login_form_api_exception": "Erro de API. Verifique a URL do servidor e tente novamente.", "login_form_back_button_text": "Voltar", @@ -1162,7 +1264,7 @@ "login_form_endpoint_url": "URL do servidor", "login_form_err_http": "Por favor especifique http:// ou https://", "login_form_err_invalid_email": "Email InvÃĄlido", - "login_form_err_invalid_url": "URL invÃĄlida", + "login_form_err_invalid_url": "URL invÃĄlido", "login_form_err_leading_whitespace": "Espaço em branco no início", "login_form_err_trailing_whitespace": "Espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Ocorreu um erro ao iniciar sessÃŖo com o OAuth, verifique o URL do servidor", @@ -1178,13 +1280,15 @@ "login_password_changed_success": "Palavra-passe atualizada com sucesso", "logout_all_device_confirmation": "Tem a certeza de que deseja terminar a sessÃŖo em todos os dispositivos?", "logout_this_device_confirmation": "Tem a certeza de que deseja terminar a sessÃŖo deste dispositivo?", + "logs": "Logs", "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", "loop_videos_description": "Ativar para repetir os vídeos automaticamente durante a exibiÃ§ÃŖo.", - "main_branch_warning": "EstÃĄ a utilizar uma versÃŖo de desenvolvimento, recomendamos vivamente que utilize uma versÃŖo estÃĄvel!", + "main_branch_warning": "EstÃĄ a usar uma versÃŖo de desenvolvimento; recomendamos vivamente que use uma versÃŖo de lançamento!", "main_menu": "Menu Principal", "make": "Marca", + "manage_geolocation": "Gerir localizaÃ§ÃŖo", "manage_shared_links": "Gerir links partilhados", "manage_sharing_with_partners": "Gerir partilha com parceiros", "manage_the_app_settings": "Gerir definiçÃĩes da aplicaÃ§ÃŖo", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Gerir os seus dispositivos com sessÃŖo iniciada", "manage_your_oauth_connection": "Gerir a sua ligaÃ§ÃŖo ao OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, =0 {Sem fotos nesta ÃĄrea} one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Impossível obter a sua localizaÃ§ÃŖo", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localizaÃ§ÃŖo", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Serviço de localizaÃ§ÃŖo desativado", "map_marker_for_images": "Marcador no mapa para fotos tiradas em {city}, {country}", "map_marker_with_image": "Marcador de mapa com imagem", - "map_no_assets_in_bounds": "NÃŖo hÃĄ fotos nesta ÃĄrea", "map_no_location_permission_content": "A permissÃŖo da localizaÃ§ÃŖo Ê necessÃĄria para mostrar recursos da localizaÃ§ÃŖo atual. Deseja conceder a permissÃŖo agora?", "map_no_location_permission_title": "PermissÃŖo de localizaÃ§ÃŖo foi negada", "map_settings": "DefiniçÃĩes do mapa", @@ -1221,6 +1323,7 @@ "mark_as_read": "Marcar como lido", "marked_all_as_read": "Tudo marcado como lido", "matches": "CorrespondÃĒncias", + "matching_assets": "ConteÃēdos coincidentes", "media_type": "Tipo de mÊdia", "memories": "MemÃŗrias", "memories_all_caught_up": "Finalizamos por hoje", @@ -1239,6 +1342,7 @@ "merged_people_count": "Unidas {count, plural, one {# pessoa} other {# pessoas}}", "minimize": "Minimizar", "minute": "Minuto", + "minutes": "Minutos", "missing": "Em falta", "model": "Modelo", "month": "MÃĒs", @@ -1246,6 +1350,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta trancada", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta trancada", "move_to_locked_folder": "Mover para a pasta trancada", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serÃŖo removidas de todos os ÃĄlbuns, e sÃŗ serÃŖo visíveis na pasta trancada", "moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo", @@ -1257,8 +1362,12 @@ "my_albums": "Os meus ÃĄlbuns", "name": "Nome", "name_or_nickname": "Nome ou alcunha", - "networking_settings": "ConexÃĩes", - "networking_subtitle": "Gerencie a conexÃŖo do servidor", + "network_requirement_photos_upload": "Usar dados mÃŗveis para fazer cÃŗpia de segurança de fotos", + "network_requirement_videos_upload": "Usar dados mÃŗveis para fazer cÃŗpia de segurança de vídeos", + "network_requirements": "Requisitos de rede", + "network_requirements_updated": "Requisitos de rede alterados, a redefinir fila de cÃŗpia de segurança", + "networking_settings": "LigaçÃĩes", + "networking_subtitle": "Gerir as ligaçÃĩes de rede do servidor", "never": "Nunca", "new_album": "Novo Álbum", "new_api_key": "Nova Chave de API", @@ -1266,6 +1375,7 @@ "new_person": "Nova Pessoa", "new_pin_code": "Novo cÃŗdigo PIN", "new_pin_code_subtitle": "Esta Ê a primeira vez que acede à pasta trancada. Crie um cÃŗdigo PIN para aceder a esta pÃĄgina de forma segura", + "new_timeline": "Nova Linha do Tempo", "new_user_created": "Novo utilizador criado", "new_version_available": "NOVA VERSÃO DISPONÍVEL", "newest_first": "Mais recente primeiro", @@ -1279,19 +1389,25 @@ "no_assets_message": "FAÇA CLIQUE PARA CARREGAR A SUA PRIMEIRA FOTO", "no_assets_to_show": "NÃŖo hÃĄ arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo de transmissÃŖo encontrado", + "no_checksum_local": "Sem cÃĄlculo de verificaÃ§ÃŖo disponível - nÃŖo pode capturar conteÃēdos locais", + "no_checksum_remote": "Soma de verificaÃ§ÃŖo (checksum) nÃŖo disponível - nÃŖo Ê possível obter o recurso remoto", "no_duplicates_found": "Nenhum item duplicado foi encontrado.", "no_exif_info_available": "Sem informaçÃĩes exif disponíveis", "no_explore_results_message": "Carregue mais fotos para explorar a sua coleÃ§ÃŖo.", "no_favorites_message": "Adicione aos favoritos para encontrar as suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver as suas fotos e vídeos", + "no_local_assets_found": "Sem cÃĄlculo de verificaÃ§ÃŖo disponível", "no_locked_photos_message": "Fotos e vídeos na pasta trancada estÃŖo ocultos e nÃŖo serÃŖo exibidos enquanto explora ou pesquisa na biblioteca.", "no_name": "Sem nome", "no_notifications": "Sem notificaçÃĩes", "no_people_found": "Nenhuma pessoa encontrada", "no_places": "Sem lugares", + "no_remote_assets_found": "Soma de verificaÃ§ÃŖo (checksum) nÃŖo disponível - nÃŖo Ê possível obter o recurso remoto", "no_results": "Sem resultados", "no_results_description": "Tente um sinÃŗnimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um ÃĄlbum para partilhar fotos e vídeos com pessoas na sua rede", + "no_uploads_in_progress": "Nenhum carregamento em curso", + "not_available": "N/A", "not_in_any_album": "NÃŖo estÃĄ em nenhum ÃĄlbum", "not_selected": "NÃŖo selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o RÃŗtulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Recursos oficiais do Immich", "offline": "Offline", + "offset": "Desvio", "ok": "Ok", "oldest_first": "Mais antigo primeiro", "on_this_device": "Neste dispositivo", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Abrir os filtros de pesquisa", "options": "OpçÃĩes", "or": "ou", + "organize_into_albums": "Organizar em ÃĄlbuns", + "organize_into_albums_description": "Colocar fotos existentes em ÃĄlbuns utilizando as definiçÃĩes atuais de sincronizaÃ§ÃŖo", "organize_your_library": "Organizar a sua biblioteca", "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variÃĄveis", "owned": "Seu", "owner": "Dono", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "PermissÃŖo limitada. Para permitir que o Immich faça backups e gerencie sua galeria, conceda permissÃĩes para fotos e vídeos nas configuraçÃĩes.", "permission_onboarding_request": "O Immich requer autorizaÃ§ÃŖo para ver as suas fotos e vídeos.", "person": "Pessoa", + "person_age_months": "{months, plural, one {# month} other {# months}} de idade", + "person_age_year_months": "1 ano, {months, plural, one {# month} other {# months}} de idade", + "person_age_years": "{years, plural, other {# anos}} de idade", "person_birthdate": "Nasceu a {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", "photo_shared_all_users": "Parece que partilhou as suas fotos com todos os utilizadores ou nÃŖo tem nenhum utilizador para partilhar.", @@ -1406,6 +1529,7 @@ "port": "Porta", "preferences_settings_subtitle": "Gerenciar preferÃĒncias do aplicativo", "preferences_settings_title": "PreferÃĒncias", + "preparing": "A Preparar", "preset": "PredefiniÃ§ÃŖo", "preview": "PrÊ-visualizar", "previous": "Anterior", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", "profile_drawer_client_server_up_to_date": "Cliente e Servidor atualizados", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Modo sÃŗ de leitura ativado. Faça um toque longo no ícone do perfil do utilizador para sair.", "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo principal mais recente.", "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo mais recente.", "profile_image_of_user": "Imagem de perfil de {user}", @@ -1448,7 +1573,7 @@ "purchase_lifetime_description": "Compra vitalícia", "purchase_option_title": "OPÇÕES DE COMPRA", "purchase_panel_info_1": "O desenvolvimento do Immich requer muito tempo e esforço, e temos engenheiros a tempo inteiro a trabalhar nele para melhorÃĄ-lo quanto possível. A nossa missÃŖo Ê para que o software de cÃŗdigo aberto e prÃĄticas de negÃŗcio Êticas se tornem numa fonte de rendimento sustentÃĄvel para os desenvolvedores e criar um ecossistema que respeite a privacidade dos utilizadores e que ofereça alternativas reais a serviços cloud explorativos.", - "purchase_panel_info_2": "Como estamos comprometidos a nÃŖo adicionar acesso pago, esta compra nÃŖo lhe darÃĄ acesso a nenhuma funcionalidade adicional do Immich. Contamos com utilizadores como vocÃĒ para dar suporte ao desenvolvimento contínuo do Immich.", + "purchase_panel_info_2": "Uma vez que estamos empenhados em nÃŖo adicionar barreiras de pagamento, esta compra nÃŖo lhe darÃĄ quaisquer funcionalidades adicionais no Immich. Contamos com utilizadores como vocÃĒ para apoiar o desenvolvimento contínuo do Immich.", "purchase_panel_title": "Apoie o projeto", "purchase_per_server": "Por servidor", "purchase_per_user": "Por utilizador", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Status de apoiante", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto do servidor Ê gerida pelo administrador", + "query_asset_id": "Consultar ID do ficheiro", + "queue_status": "Em fila {count}/{total}", "rating": "ClassificaÃ§ÃŖo por estrelas", "rating_clear": "Limpar classificaÃ§ÃŖo", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", "rating_description": "Mostrar a classificaÃ§ÃŖo EXIF no painel de informaçÃĩes", "reaction_options": "OpçÃĩes de reaÃ§ÃŖo", "read_changelog": "Ler Novidades", + "readonly_mode_disabled": "Modo sÃŗ de leitura desativado", + "readonly_mode_enabled": "Modo sÃŗ de leitura ativado", + "ready_for_upload": "Pronto para upload", "reassign": "Reatribuir", "reassigned_assets_to_existing_person": "Reatribuir {count, plural, one {# ficheiro} other {# ficheiros}} para {name, select, null {uma pessoa existente} other {{name}}}", "reassigned_assets_to_new_person": "Reatribuído {count, plural, one {# ficheiro} other {# ficheiros}} a uma nova pessoa", @@ -1488,6 +1618,9 @@ "refreshing_faces": "A atualizar rostos", "refreshing_metadata": "A atualizar metadados", "regenerating_thumbnails": "A atualizar miniaturas", + "remote": "Remoto", + "remote_assets": "Ficheiros Remotos", + "remote_media_summary": "SumÃĄrio de Ficheiros Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem a certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} do ÃĄlbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} deste link partilhado?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover ficheiros indisponíveis", "remove_from_album": "Remover do ÃĄlbum", + "remove_from_album_action_prompt": "{count} removido(s) do ÃĄlbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta trancada", "remove_from_locked_folder": "Remover da pasta trancada", "remove_from_locked_folder_confirmation": "Tem a certeza de que quer mover estas fotos e vídeos para fora da pasta trancada? PassarÃŖo a ser visíveis na biblioteca.", "remove_from_shared_link": "Remover do link partilhado", @@ -1523,19 +1658,29 @@ "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", "reset_pin_code": "Repor cÃŗdigo PIN", + "reset_pin_code_description": "Se esqueceu o seu cÃŗdigo PIN, pode entrar em contacto com o administrador do servidor para o repor", + "reset_pin_code_success": "CÃŗdigo PIN redefinido com sucesso", + "reset_pin_code_with_password": "Pode sempre repor o seu cÃŗdigo PIN com a sua palavra-passe", + "reset_sqlite": "Reiniciar Base de Dados SQLite", + "reset_sqlite_confirmation": "Tem a certeza de que quer reiniciar a base de dados SQLite? Vai ter de terminar a sessÃŖo e entrar outra vez para sincronizar os dados de novo", + "reset_sqlite_success": "Base de dados SQLite reiniciada com sucesso", "reset_to_default": "Repor predefiniçÃĩes", "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todos os itens duplicados resolvidos", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da reciclagem", "restore_user": "Restaurar utilizador", "restored_asset": "Ficheiro restaurado", "resume": "Continuar", + "resume_paused_jobs": "Continuar {count, plural, one {# trabalho em pausa} other {# trabalhos em pausa}}", "retry_upload": "Tentar carregar novamente", "review_duplicates": "Rever itens duplicados", + "review_large_files": "Rever ficheiros grandes", "role": "FunÃ§ÃŖo", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "A executar", "save": "Guardar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API guardada", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Ocorreu um erro ao criar o ÃĄlbum", "selected": "Selecionados", "selected_count": "{count, plural, other {# selecionados}}", + "selected_gps_coordinates": "Coordenadas GPS selecionadas", "send_message": "Enviar mensagem", "send_welcome_email": "Enviar E-mail de boas vindas", "server_endpoint": "URL do servidor", @@ -1667,6 +1813,7 @@ "settings_saved": "DefiniçÃĩes guardadas", "setup_pin_code": "Configurar um cÃŗdigo PIN", "share": "Partilhar", + "share_action_prompt": "Partilhados {count} ficheiros", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionados", "share_dialog_preparing": "Preparando...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Copiado para a ÃĄrea de transferÃĒncia", "shared_link_clipboard_text": "LigaÃ§ÃŖo: {link}\nPalavra-passe: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_custom_url_description": "Aceda a este link partilhado com um URL personalizado", "shared_link_edit_description_hint": "Digite a descriÃ§ÃŖo do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dias", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "OpçÃĩes de link partilhado", + "shared_link_password_description": "Exigir uma palavra-passe para aceder a este link partilhado", "shared_links": "Links partilhados", "shared_links_description": "Partilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos & videos partilhados.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Mostrar transiçÃĩes no Modo de ApresentaÃ§ÃŖo", "show_supporter_badge": "Emblema de apoiante", "show_supporter_badge_description": "Mostrar um emblema de apoiante", + "show_text_search_menu": "Mostrar menu de pesquisa de texto", "shuffle": "AleatÃŗrio", "sidebar": "Barra lateral", "sidebar_display_description": "Mostrar um link para a vista na barra lateral", @@ -1762,12 +1912,14 @@ "sort_created": "Data de criaÃ§ÃŖo", "sort_items": "NÃēmero de itens", "sort_modified": "Data de modificaÃ§ÃŖo", + "sort_newest": "A foto mais recente", "sort_oldest": "Foto mais antiga", "sort_people_by_similarity": "Ordenar pessoas por semelhança", "sort_recent": "Foto mais recente", "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_action_prompt": "{count} empilhados", "stack_duplicates": "Empilhar itens duplicados", "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", @@ -1775,6 +1927,7 @@ "stacktrace": "Stacktrace", "start": "Iniciar", "start_date": "Data de início", + "start_date_before_end_date": "A data de início deve ser anterior à data de fim", "state": "Estado/Distrito", "status": "Estado", "stop_casting": "Parar transmissÃŖo", @@ -1787,6 +1940,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "SugestÃĩes", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Apoio", @@ -1796,6 +1950,10 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar ÃĄlbuns", "sync_albums_manual_subtitle": "Sincronizar todas as fotos e vídeos enviados para o ÃĄlbum de backup selecionado", + "sync_local": "SincronizaÃ§ÃŖo Local", + "sync_remote": "SincronizaÃ§ÃŖo Remota", + "sync_status": "Estado da sincronizaÃ§ÃŖo", + "sync_status_subtitle": "Ver e gerir o sistema de sincronizaÃ§ÃŖo", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o ÃĄlbum selecionado no Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar ficheiros", @@ -1806,6 +1964,7 @@ "tag_updated": "Atualizada a etiqueta: {tag}", "tagged_assets": "Etiquetado {count, plural, one {# ficheiros} other {# ficheiros}}", "tags": "Etiquetas", + "tap_to_run_job": "Tocar para executar tarefa", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1832,12 +1991,15 @@ "to_change_password": "Alterar palavra-passe", "to_favorite": "Favorito", "to_login": "Iniciar SessÃŖo", + "to_multi_select": "multi-selecÃ§ÃŖo", "to_parent": "Subir um nível", + "to_select": "seleccionar", "to_trash": "Reciclagem", "toggle_settings": "Alternar configuraçÃĩes", "total": "Total", "total_usage": "Total utilizado", "trash": "Reciclagem", + "trash_action_prompt": "{count} movidos para a reciclagem", "trash_all": "Mover todos para a reciclagem", "trash_count": "Reciclar {count, number}", "trash_delete_asset": "Eliminar ficheiro", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Selecionar arquivos", "trash_page_title": "Reciclagem ({count})", "trashed_items_will_be_permanently_deleted_after": "Os itens da reciclagem sÃŖo eliminados permanentemente apÃŗs {days, plural, one {# dia} other {# dias}}.", + "troubleshoot": "Diagnosticar problemas", "type": "Tipo", "unable_to_change_pin_code": "NÃŖo foi possível alterar o cÃŗdigo PIN", "unable_to_setup_pin_code": "NÃŖo foi possível configurar o cÃŗdigo PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} removidos do Arquivo", "unarchived_count": "{count, plural, other {NÃŖo arquivado #}}", "undo": "Anular", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removidos dos Favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "Remover seleÃ§ÃŖo de todos os itens duplicados", "unselect_all_in": "Remover seleÃ§ÃŖo de {group}", "unstack": "Desempilhar", + "unstack_action_prompt": "{count} desempilhados", "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", + "untagged": "Marcador removido", "up_next": "A seguir", + "update_location_action_prompt": "Atualize a localizaÃ§ÃŖo de {count} ficheiros selecionados com:", "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", + "upload_action_prompt": "{count} à espera de carregar", "upload_concurrency": "Carregamentos em simultÃĸneo", + "upload_details": "Detalhes do Carregamento", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a pÃĄgina para ver os novos ficheiros enviados.", + "upload_finished": "Carregamento acabado", "upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# Ignorado ficheiro duplicado} other {# Ignorados ficheiros duplicados}}", "upload_status_duplicates": "Duplicados", @@ -1892,6 +2063,7 @@ "upload_success": "Carregamento realizado com sucesso, atualize a pÃĄgina para ver os novos ficheiros carregados.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "A carregar media", "url": "URL", "usage": "UtilizaÃ§ÃŖo", "use_biometric": "Utilizar dados biomÊtricos", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Ver estatísticas de utilizaÃ§ÃŖo de conta", "username": "Nome de utilizador", "users": "Utilizadores", + "users_added_to_album_count": "{count, plural, one {Foi adicionado # utilizador} other {Foram adicionados # utilizadores}} ao ÃĄlbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL vÃĄlida", @@ -1930,6 +2103,7 @@ "view_album": "Ver Álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os utilizadores", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", @@ -1937,6 +2111,7 @@ "view_next_asset": "Ver prÃŗximo ficheiro", "view_previous_asset": "Ver ficheiro anterior", "view_qr_code": "Ver cÃŗdigo QR", + "view_similar_photos": "Ver fotos similares", "view_stack": "Ver pilha", "view_user": "Ver utilizador", "viewer_remove_from_stack": "Remover da pilha", @@ -1951,9 +2126,10 @@ "wifi_name": "Nome da rede Wi-Fi", "wrong_pin_code": "CÃŗdigo PIN errado", "year": "Ano", - "years_ago": "HÃĄ {years, plural, one {# ano} other {# anos}} atrÃĄs", + "years_ago": "HÃĄ {years, plural, one {# ano} other {# anos}}", "yes": "Sim", "you_dont_have_any_shared_links": "NÃŖo tem links partilhados", "your_wifi_name": "Nome da sua rede Wi-Fi", - "zoom_image": "Ampliar/Reduzir imagem" + "zoom_image": "Ampliar/Reduzir imagem", + "zoom_to_bounds": "Aproximar aos limites" } diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 705180cafd..a4d590bc2c 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -14,6 +14,7 @@ "add_a_location": "Adicionar uma localizaÃ§ÃŖo", "add_a_name": "Adicionar um nome", "add_a_title": "Adicionar um título", + "add_birthday": "Definir aniversÃĄrio", "add_endpoint": "Adicionar URL", "add_exclusion_pattern": "Adicionar padrÃŖo de exclusÃŖo", "add_import_path": "Adicionar caminho de importaÃ§ÃŖo", @@ -27,6 +28,10 @@ "add_to_album": "Adicionar ao ÃĄlbum", "add_to_album_bottom_sheet_added": "Adicionado ao {album}", "add_to_album_bottom_sheet_already_exists": "JÃĄ existe em {album}", + "add_to_album_bottom_sheet_some_local_assets": "Alguns arquivos / mídias nÃŖo puderam ser adicionados ao ÃĄlbum", + "add_to_album_toggle": "Alternar a seleÃ§ÃŖo de {album}", + "add_to_albums": "Adicionar aos ÃĄlbuns", + "add_to_albums_count": "Adicionar aos ÃĄlbuns ({count})", "add_to_shared_album": "Adicionar ao ÃĄlbum compartilhado", "add_url": "Adicionar URL", "added_to_archive": "Adicionado ao arquivo", @@ -44,6 +49,13 @@ "backup_database": "Criar backup do banco de dados", "backup_database_enable_description": "Ativar backup do banco de dados", "backup_keep_last_amount": "Quantidade de backups anteriores para manter salvo", + "backup_onboarding_1_description": "Uma cÃŗpia na nuvem ou outro lugar físico.", + "backup_onboarding_2_description": "CÃŗpias em dispositivos diferentes. Incluindo os arquivos originais e o backup deles.", + "backup_onboarding_3_description": "CÃŗpias completas de seus dados, com os arquivos originais. Inclusive 1 cÃŗpia externa e 2 cÃŗpias locais.", + "backup_onboarding_description": "A estratÊgia de backup 3-2-1 Ê recomendada para proteger seus dados. Para uma soluÃ§ÃŖo completa de backup, vocÃĒ deve manter cÃŗpias de suas fotos, vídeos e backups do banco de dados do Immich.", + "backup_onboarding_footer": "Para mais informaçÃĩes sobre o backup do Immich, leia a documentaÃ§ÃŖo.", + "backup_onboarding_parts_title": "O backup 3-2-1 Ê definido por:", + "backup_onboarding_title": "Backups", "backup_settings": "ConfiguraçÃĩes de backup", "backup_settings_description": "Gerenciar configuraçÃĩes de backup do banco de dados.", "cleared_jobs": "Tarefas removidas de: {job}", @@ -105,13 +117,20 @@ "library_scanning_enable_description": "Habilitar verificaÃ§ÃŖo periÃŗdica da biblioteca", "library_settings": "Biblioteca Externa", "library_settings_description": "Gerenciar configuraçÃĩes de biblioteca externa", - "library_tasks_description": "Escanear bibliotecas externas para ativos novos ou modificados", + "library_tasks_description": "Verificar se hÃĄ arquivos novos ou modificados nas bibliotecas externas", "library_watching_enable_description": "Observe bibliotecas externas para alteraçÃĩes de arquivos", "library_watching_settings": "ObservaÃ§ÃŖo de biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Observe automaticamente os arquivos alterados", "logging_enable_description": "Habilitar logs", "logging_level_description": "Quando ativado, qual nível de log usar.", "logging_settings": "Logs", + "machine_learning_availability_checks": "VerficaçÃĩes de disponibilidade", + "machine_learning_availability_checks_description": "Automaticamente detectar e preferir servidores de machine learning disponíveis", + "machine_learning_availability_checks_enabled": "Habilitar verificaçÃĩes de disponibilidade", + "machine_learning_availability_checks_interval": "Intervalo de verificaÃ§ÃŖo", + "machine_learning_availability_checks_interval_description": "Intervalo em milisegundos entre verificaçÃĩes de disponibilidade", + "machine_learning_availability_checks_timeout": "Tempo limite da solicitaÃ§ÃŖo", + "machine_learning_availability_checks_timeout_description": "Tempo limite em milisegundos para verificaçÃĩes de disponibilidade", "machine_learning_clip_model": "Modelo CLIP", "machine_learning_clip_model_description": "O nome de um modelo CLIP listado aqui. Lembre-se de executar novamente a tarefa de 'Pesquisa Inteligente' para todas as imagens apÃŗs alterar o modelo.", "machine_learning_duplicate_detection": "DetecÃ§ÃŖo de duplicidade", @@ -166,9 +185,23 @@ "metadata_settings_description": "Gerenciar configuraçÃĩes de metadados", "migration_job": "MigraÃ§ÃŖo", "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Fazer o reconhecimento facial dos novos rostos detectados", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novos rostos", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza do banco de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpe dados velhos e expirados do banco de dados", + "nightly_tasks_generate_memories_setting": "Gerar memÃŗrias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memÃŗrias a partir dos arquivos", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Adiciona na fila de geraÃ§ÃŖo de miniaturas as fotos ainda sem miniaturas", + "nightly_tasks_settings": "ConfiguraçÃĩes de Tarefas DiÃĄrias", + "nightly_tasks_settings_description": "Gerenciar tarefas diÃĄrias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora que o servidor começa a executar as tarefas diÃĄrias", + "nightly_tasks_sync_quota_usage_setting": "UtilizaÃ§ÃŖo da quota de sincronizaÃ§ÃŖo", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento dos usuÃĄrios, com base na utilizaÃ§ÃŖo atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrÃŖo adicionado", - "note_apply_storage_label_previous_assets": "ObservaÃ§ÃŖo: Para aplicar o rÃŗtulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_previous_assets": "ObservaÃ§ÃŖo: Para aplicar o rÃŗtulo de armazenamento a arquivos enviados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto nÃŖo pode ser alterado posteriormente!", "notification_email_from_address": "E-mail de origem", "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \". Tenha certeza de ter permissÃŖo para enviar e-mails a partir do endereço selecionado.", @@ -196,15 +229,17 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override": "SubstituiÃ§ÃŖo de URI de redirecionamento mÃŗvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth nÃŖo suportar uma URI de aplicativo, por exemplo ''{callback}''", + "oauth_role_claim": "DeclaraÃ§ÃŖo de funÃ§ÃŖo", + "oauth_role_claim_description": "DÃĄ permissÃĩes de administrador baseado no valor desta declaraÃ§ÃŖo. A declaraÃ§ÃŖo pode conter os valores 'user' ou 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gerenciar configuraçÃĩes de login do OAuth", "oauth_settings_more_details": "Para mais detalhes sobre este recurso, consulte a documentaÃ§ÃŖo.", - "oauth_storage_label_claim": "ReivindicaÃ§ÃŖo de rÃŗtulo de armazenamento", + "oauth_storage_label_claim": "DeclaraÃ§ÃŖo do rÃŗtulo de armazenamento", "oauth_storage_label_claim_description": "Defina automaticamente o rÃŗtulo de armazenamento do usuÃĄrio para o valor desta declaraÃ§ÃŖo.", - "oauth_storage_quota_claim": "Cota de armazenamento", + "oauth_storage_quota_claim": "DeclaraÃ§ÃŖo de cota de armazenamento", "oauth_storage_quota_claim_description": "Defina automaticamente a cota de armazenamento do usuÃĄrio para o valor desta declaraÃ§ÃŖo.", "oauth_storage_quota_default": "Cota de armazenamento padrÃŖo (GiB)", - "oauth_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma outra reivindicaÃ§ÃŖo for fornecida.", + "oauth_storage_quota_default_description": "Cota em GiB que serÃĄ usada caso esta declaraÃ§ÃŖo nÃŖo seja fornecida.", "oauth_timeout": "Tempo Limite de RequisiÃ§ÃŖo", "oauth_timeout_description": "Tempo limite para requisiçÃĩes, em milissegundos", "password_enable_description": "Login com e-mail e senha", @@ -234,20 +269,20 @@ "sidecar_job_description": "Descubra ou sincronize metadados secundÃĄrios do sistema de arquivos", "slideshow_duration_description": "Tempo em segundos para exibir cada imagem", "smart_search_job_description": "Execute aprendizado de mÃĄquina em arquivos para oferecer suporte à pesquisa inteligente", - "storage_template_date_time_description": "A data e hora da criaÃ§ÃŖo do ativo Ê usado para a informaçÃĩes de data e hora", + "storage_template_date_time_description": "A data e hora da criaÃ§ÃŖo do arquivo Ê usado para a informaçÃĩes de data e hora", "storage_template_date_time_sample": "Exemplo {date}", "storage_template_enable_description": "Habilitar mecanismo de modelo de armazenamento", "storage_template_hash_verification_enabled": "VerificaÃ§ÃŖo de hash ativada", "storage_template_hash_verification_enabled_description": "Ativa a verificaÃ§ÃŖo de hash, nÃŖo desative a menos que vocÃĒ tenha certeza das implicaçÃĩes", "storage_template_migration": "MigraÃ§ÃŖo de modelo de armazenamento", - "storage_template_migration_description": "Aplique o {template} atual aos arquivos carregados anteriormente", - "storage_template_migration_info": "O modelo altera todas extensÃĩes para minÃēsculo. As mudanças no modelo serÃŖo aplicadas apenas em novos arquivos. Para aplicar retroativamente o modelo aos arquivos carregados anteriormente, execute o {job}.", + "storage_template_migration_description": "Aplicar o {template} atual aos arquivos enviados anteriormente", + "storage_template_migration_info": "O modelo altera todas extensÃĩes para minÃēsculo. As mudanças no modelo serÃŖo aplicadas apenas em novos arquivos; para aplicar o modelo aos arquivos enviados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de MigraÃ§ÃŖo de Modelo de Armazenamento", "storage_template_more_details": "Para mais detalhes sobre este recurso, consulte o Modelo de Armazenamento e suas implicaçÃĩes", "storage_template_onboarding_description_v2": "Ao ser ativado, este recurso irÃĄ organizar automaticamente os arquivos com base em um modelo definido pelo usuÃĄrio. Para mais informaçÃĩes, consulte a documentaÃ§ÃŖo.", "storage_template_path_length": "Limite aproximado de comprimento do caminho: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Armazenamento", - "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo carregado", + "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo enviado", "storage_template_user_label": "{label} Ê o RÃŗtulo de Armazenamento do usuÃĄrio", "system_settings": "ConfiguraçÃĩes do Sistema", "tag_cleanup_job": "Limpeza de marcadores", @@ -331,12 +366,15 @@ "trash_number_of_days_description": "NÃēmero de dias para manter os arquivos na lixeira antes de deletar permanentemente", "trash_settings": "ConfiguraçÃĩes da Lixeira", "trash_settings_description": "Gerenciar configuraçÃĩes da lixeira", + "unlink_all_oauth_accounts": "Desvincular todas as contas OAuth", + "unlink_all_oauth_accounts_description": "Lembre-se de desvincular todas as contas OAuth antes de migrar para um novo provedor.", + "unlink_all_oauth_accounts_prompt": "Tem certeza que deseja desvincular todas as contas OAuth? Isto vai redefinir o ID OAuth de todos os usuÃĄrio e nÃŖo pode ser desfeito.", "user_cleanup_job": "Limpeza de usuÃĄrios", "user_delete_delay": "A conta e os arquivos de {user} serÃŖo programados para exclusÃŖo permanente em {delay, plural, one {# dia} other {# dias}}.", "user_delete_delay_settings": "Remover atraso", "user_delete_delay_settings_description": "NÃēmero de dias apÃŗs a remoÃ§ÃŖo para excluir permanentemente a conta e os arquivos de um usuÃĄrio. A tarefa de exclusÃŖo de usuÃĄrio Ê executada à meia-noite para verificar usuÃĄrios que estÃŖo prontos para exclusÃŖo. As alteraçÃĩes nesta configuraÃ§ÃŖo serÃŖo avaliadas na prÃŗxima execuÃ§ÃŖo.", "user_delete_immediately": "A conta e os arquivos de {user} serÃŖo programados para exclusÃŖo permanente imediata.", - "user_delete_immediately_checkbox": "Adicionar o usuÃĄrio e seus ativos na fila para serem deletados imediatamente", + "user_delete_immediately_checkbox": "Adicionar o usuÃĄrio e seus arquivos na fila para serem deletados imediatamente", "user_details": "Detalhes do UsuÃĄrio", "user_management": "Gerenciamento de usuÃĄrios", "user_password_has_been_reset": "A senha do usuÃĄrio foi redefinida:", @@ -364,6 +402,8 @@ "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicaçÃĩes com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", + "advanced_settings_readonly_mode_subtitle": "Ativar o modo de apenas visualizaÃ§ÃŖo dos arquivos. As outras açÃĩes, como: selecionar vÃĄrias imagens, compartilhar, transmitir ou deletar serÃŖo desabilitadas. Ative ou Desative este modo clicando na foto do usuÃĄrio na tela principal", + "advanced_settings_readonly_mode_title": "Modo de apenas visualizaÃ§ÃŖo", "advanced_settings_self_signed_ssl_subtitle": "Ignora a verificaÃ§ÃŖo do certificado SSL do servidor. ObrigatÃŗrio para certificados auto assinados.", "advanced_settings_self_signed_ssl_title": "Permitir certificados SSL auto assinados", "advanced_settings_sync_remote_deletions_subtitle": "Excluir ou restaurar os arquivos automaticamente neste dispositivo quando essas açÃĩes forem realizada na interface web", @@ -379,6 +419,7 @@ "album_cover_updated": "Capa do ÃĄlbum atualizada", "album_delete_confirmation": "Tem certeza de que deseja excluir o ÃĄlbum {album}?", "album_delete_confirmation_description": "Se este ÃĄlbum Ê compartilhado, os outros usuÃĄrios nÃŖo conseguiram mais acessÃĄ-lo.", + "album_deleted": "Álbum deletado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "InformaçÃĩes do ÃĄlbum atualizadas", @@ -388,7 +429,9 @@ "album_options": "OpçÃĩes de ÃĄlbum", "album_remove_user": "Remover usuÃĄrio?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", + "album_search_not_found": "NÃŖo hÃĄ ÃĄlbum que corresponda à sua pesquisa", "album_share_no_users": "Parece que vocÃĒ jÃĄ compartilhou este ÃĄlbum com todos os usuÃĄrios ou nÃŖo hÃĄ nenhum usuÃĄrio para compartilhar.", + "album_summary": "Resumo do ÃĄlbum", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificaÃ§ÃŖo por e-mail quando um ÃĄlbum compartilhado tiver novos recursos", "album_user_left": "Saiu do ÃĄlbum {album}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Ordem padrÃŖo do ÃĄlbum", "albums_default_sort_order_description": "Ordem padrÃŖo dos arquivos ao criar novos ÃĄlbuns.", "albums_feature_description": "ColeçÃĩes de arquivos que podem ser compartilhados com outros usuÃĄrios.", + "albums_on_device_count": "Álbuns no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os ÃĄlbuns", "all_people": "Todas as pessoas", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Sair", "app_settings": "ConfiguraçÃĩes do Aplicativo", "appears_in": "Aparece em", - "archive": "Arquivados", + "apply_count": "Aplicar ({count, number})", + "archive": "Arquivar", + "archive_action_prompt": "{count} mídias arquivadas", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivados ({count})", @@ -440,7 +486,7 @@ "asset_action_share_err_offline": "NÃŖo foi possível obter os arquivos indisponíveis, ignorando", "asset_added_to_album": "Adicionado ao ÃĄlbum", "asset_adding_to_album": "Adicionando ao ÃĄlbumâ€Ļ", - "asset_description_updated": "A descriÃ§ÃŖo do ativo foi atualizada", + "asset_description_updated": "A descriÃ§ÃŖo do arquivo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} nÃŖo estÃĄ disponível", "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processandoâ€Ļ", @@ -457,15 +503,18 @@ "asset_restored_successfully": "Arquivo restaurado", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na lixeira", - "asset_uploaded": "Carregado", - "asset_uploading": "Carregandoâ€Ļ", + "asset_trashed": "Arquivo enviado para a lixeira", + "asset_troubleshoot": "DiagnÃŗstico do arquivo", + "asset_uploaded": "Enviado", + "asset_uploading": "Enviandoâ€Ļ", "asset_viewer_settings_subtitle": "Gerenciar as configuraçÃĩes do visualizador da galeria", "asset_viewer_settings_title": "Visualizador de Mídia", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao ÃĄlbum", - "assets_added_to_name_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} {hasName, select, true {ao ÃĄlbum {name}} other {em um novo ÃĄlbum}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {# Arquivo adicionado} other {# Arquivos adicionados}} {albumTotal, plural, one {# ao ÃĄlbum} other {# aos ÃĄlbuns}}", "assets_cannot_be_added_to_album_count": "NÃŖo foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao ÃĄlbum", + "assets_cannot_be_added_to_albums": "{count, plural, one {Arquivo nÃŖo pode ser adicionado} other {Arquivos nÃŖo podem ser adicionados}} a nenhum ÃĄlbum", "assets_count": "{count, plural, one {# arquivo} other {# arquivos}}", "assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} arquivo(s) deletado(s) permanentemente do servidor Immich", @@ -482,29 +531,34 @@ "assets_trashed_count": "{count, plural, one {# arquivo movido para a lixeira} other {# arquivos movidos para a lixeira}}", "assets_trashed_from_server": "{count} arquivos foram enviados para a lixeira", "assets_were_part_of_album_count": "{count, plural, one {O arquivo jÃĄ faz} other {Os arquivos jÃĄ fazem}} parte do ÃĄlbum", + "assets_were_part_of_albums_count": "{count, plural, one {Arquivo jÃĄ existe} other {Arquivos jÃĄ existem}} nos ÃĄlbuns", "authorized_devices": "Dispositivos Autorizados", "automatic_endpoint_switching_subtitle": "Conecte-se localmente quando estiver em uma rede uma Wi-Fi específica e use conexÃĩes alternativas em outras redes", "automatic_endpoint_switching_title": "Troca automÃĄtica de URL", "autoplay_slideshow": "ApresentaÃ§ÃŖo de slides automÃĄtica", "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", + "background_backup_running_error": "NÃŖo Ê possível iniciar o backup manual agora pois o backup em segundo plano jÃĄ estÃĄ sendo executado", "background_location_permission": "PermissÃŖo de localizaÃ§ÃŖo em segundo plano", - "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissÃŖo de localizaÃ§ÃŖo precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissÃŖo de localizaÃ§ÃŖo precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_options": "OpçÃĩes de Plano de Fundo", + "backup": "Backup", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vÃĄrios ÃĄlbuns. Assim, os ÃĄlbuns podem ser incluídos ou excluídos durante o processo de backup.", "backup_album_selection_page_select_albums": "Selecionar ÃĄlbuns", "backup_album_selection_page_selection_info": "InformaçÃĩes da SeleÃ§ÃŖo", "backup_album_selection_page_total_assets": "Total de recursos exclusivos", + "backup_albums_sync": "Backup de sincronizaÃ§ÃŖo de ÃĄlbuns", "backup_all": "Todos", "backup_background_service_backup_failed_message": "Falha ao fazer backup. Tentando novamenteâ€Ļ", "backup_background_service_connection_failed_message": "Falha na conexÃŖo com o servidor. Tentando novamenteâ€Ļ", "backup_background_service_current_upload_notification": "Enviando {filename}", "backup_background_service_default_notification": "Verificando se hÃĄ novos arquivosâ€Ļ", "backup_background_service_error_title": "Erro no backup", - "backup_background_service_in_progress_notification": "Fazendo backup de seus ativosâ€Ļ", + "backup_background_service_in_progress_notification": "Fazendo backup de seus arquivosâ€Ļ", "backup_background_service_upload_failure_notification": "Falha ao enviar {filename}", - "backup_controller_page_albums": "Álbuns de backup", + "backup_controller_page_albums": "Backup de ÃĄ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 em 2Âē plano.", "backup_controller_page_background_app_refresh_disabled_title": "AtualizaÃ§ÃŖo em 2Âē plano desativada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configuraçÃĩes", @@ -512,46 +566,49 @@ "backup_controller_page_background_battery_info_message": "Para uma melhor experiÃĒncia de backup em segundo plano, desative todas as otimizaçÃĩes de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso Ê específico por dispositivo, consulte as informaçÃĩes de como fazer isso com o fabricante do seu dispositivo.", "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "OtimizaçÃĩes de bateria", - "backup_controller_page_background_charging": "Apenas durante o carregamento", + "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": "Adiar backup de novos arquivos: {duration}", - "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automÃĄtico de novos ativos sem precisar abrir o aplicativo", + "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", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", "backup_controller_page_background_wifi": "Apenas no Wi-Fi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado: ", - "backup_controller_page_backup_sub": "Backup de fotos e vídeos", - "backup_controller_page_created": "Criado em: {date}", - "backup_controller_page_desc_backup": "Ative o backup para carregar automaticamente novos ativos no servidor.", - "backup_controller_page_excluded": "Excluído: ", + "backup_controller_page_backup_selected": "Selecionados: ", + "backup_controller_page_backup_sub": "Total de mídias com backup", + "backup_controller_page_created": "Data: {date}", + "backup_controller_page_desc_backup": "Ative para fazer backup automÃĄtico dos novos arquivos ao abrir este aplicativo.", + "backup_controller_page_excluded": "Ignorados: ", "backup_controller_page_failed": "Falhou ({count})", - "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", + "backup_controller_page_filename": "Arquivo: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", - "backup_controller_page_info": "InformaçÃĩes de backup", - "backup_controller_page_none_selected": "Nenhum selecionado", + "backup_controller_page_info": "InformaçÃĩes do backup", + "backup_controller_page_none_selected": "Nenhum ÃĄlbum selecionado", "backup_controller_page_remainder": "Restante", - "backup_controller_page_remainder_sub": "Fotos e vídeos restantes para fazer backup da seleÃ§ÃŖo", + "backup_controller_page_remainder_sub": "Mídias nos ÃĄlbuns selecionados que ainda nÃŖo tem backup", "backup_controller_page_server_storage": "Armazenamento do servidor", - "backup_controller_page_start_backup": "Iniciar backup", - "backup_controller_page_status_off": "O backup estÃĄ desativado", - "backup_controller_page_status_on": "O backup estÃĄ ativado", + "backup_controller_page_start_backup": "Iniciar backup manual", + "backup_controller_page_status_off": "O backup automÃĄtico estÃĄ desativado", + "backup_controller_page_status_on": "O backup automÃĄtico estÃĄ ativado", "backup_controller_page_storage_format": "{used} de {total} usados", - "backup_controller_page_to_backup": "Álbuns para backup", - "backup_controller_page_total_sub": "Todas as fotos e vídeos Ãēnicos dos ÃĄlbuns selecionados", - "backup_controller_page_turn_off": "Desativar o backup", - "backup_controller_page_turn_on": "Ativar Backup", - "backup_controller_page_uploading_file_info": "Carregando informaçÃĩes do arquivo", + "backup_controller_page_to_backup": "Escolha os ÃĄlbuns para fazer backup", + "backup_controller_page_total_sub": "Total de mídias nos ÃĄlbuns selecionados", + "backup_controller_page_turn_off": "Desativar backup automÃĄtico", + "backup_controller_page_turn_on": "Ativar backup automÃĄtico", + "backup_controller_page_uploading_file_info": "InformaçÃĩes do arquivo", "backup_err_only_album": "NÃŖo Ê possível remover o Ãēnico ÃĄlbum", - "backup_info_card_assets": "ativos", + "backup_error_sync_failed": "A sincronizaÃ§ÃŖo falhou. NÃŖo foi possível processar o backup.", + "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio jÃĄ estÃĄ em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", "backup_manual_title": "Estado do envio", + "backup_options": "OpçÃĩes de backup", "backup_options_page_title": "OpçÃĩes de backup", "backup_setting_subtitle": "Gerenciar as configuraçÃĩes de envio em primeiro e segundo plano", + "backup_settings_subtitle": "Gerenciar configuraçÃĩes de envio", "backward": "Para trÃĄs", "biometric_auth_enabled": "AutenticaÃ§ÃŖo por biometria ativada", "biometric_locked_out": "Sua autenticaÃ§ÃŖo por biometria estÃĄ bloqueada", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "Limpar o 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 sÃŖo bloqueados pelo app", + "cache_settings_duplicated_assets_subtitle": "Mídias ignoradas pelo app", "cache_settings_duplicated_assets_title": "Arquivos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -587,6 +644,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "NÃŖo Ê possível mesclar pessoas", "cannot_undo_this_action": "VocÃĒ nÃŖo pode desfazer esta aÃ§ÃŖo!", "cannot_update_the_description": "NÃŖo Ê possível atualizar a descriÃ§ÃŖo", @@ -609,6 +667,8 @@ "change_pin_code": "Alterar cÃŗdigo PIN", "change_your_password": "Alterar sua senha", "changed_visibility_successfully": "Visibilidade alterada com sucesso", + "charging": "Carregando", + "charging_requirement_mobile_backup": "Backups em plano de fundo requerem que o dispositivo esteja sendo carregado", "check_corrupt_asset_backup": "Verifique se hÃĄ backups corrompidos", "check_corrupt_asset_backup_button": "Verificar", "check_corrupt_asset_backup_description": "Execute esta verificaÃ§ÃŖo somente em uma rede Wi-Fi e quando o backup de todos os arquivos jÃĄ estiver concluído. O processo demora alguns minutos.", @@ -618,6 +678,7 @@ "clear": "Limpar", "clear_all": "Limpar tudo", "clear_all_recent_searches": "Limpar todas as buscas recentes", + "clear_file_cache": "Limpar cache arquivos", "clear_message": "Limpar mensagem", "clear_value": "Limpar valor", "client_cert_dialog_msg_confirm": "OK", @@ -640,7 +701,7 @@ "comments_are_disabled": "ComentÃĄrios estÃŖo desativados", "common_create_new_album": "Criar novo ÃĄlbum", "common_server_error": "Verifique a sua conexÃŖo de rede, certifique-se de que o servidor estÃĄ acessível e de que as versÃĩes do aplicativo e servidor sÃŖo compatíveis.", - "completed": "Sucesso", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", "confirm_delete_face": "Tem certeza que deseja remover a rosto de {name} deste arquivo?", @@ -658,9 +719,9 @@ "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", - "control_bottom_app_bar_edit_location": "Editar LocalizaÃ§ÃŖo", + "control_bottom_app_bar_edit_location": "Alterar Local", "control_bottom_app_bar_edit_time": "Editar data e hora", - "control_bottom_app_bar_share_link": "Compartilhar Link", + "control_bottom_app_bar_share_link": "Link", "control_bottom_app_bar_share_to": "Compartilhar", "control_bottom_app_bar_trash_from_immich": "Mover para a Lixeira", "copied_image_to_clipboard": "Imagem copiada para a ÃĄrea de transferÃĒncia.", @@ -680,7 +741,7 @@ "create_album_page_untitled": "Sem título", "create_library": "Criar biblioteca", "create_link": "Criar link", - "create_link_to_share": "Criar link para partilhar", + "create_link_to_share": "Criar link e compartilhar", "create_link_to_share_description": "Permitir que qualquer pessoa com o link veja a(s) foto(s) selecionada(s)", "create_new": "CRIAR NOVO", "create_new_person": "Criar nova pessoa", @@ -688,11 +749,13 @@ "create_new_user": "Criar novo usuÃĄrio", "create_shared_album_page_share_add_assets": "ADICIONAR FOTOS", "create_shared_album_page_share_select_photos": "Selecionar fotos", + "create_shared_link": "Criar link", "create_tag": "Criar marcador", "create_tag_description": "Cria um novo marcador. Para marcadores multi nível, digite o caminho completo do marcador, inclusive as barras.", "create_user": "Criar usuÃĄrio", "created": "Criado", "created_at": "Criado em", + "creating_linked_albums": "Criando ÃĄlbuns relacionados...", "crop": "Cortar", "curated_object_page_title": "Objetos", "current_device": "Dispositivo atual", @@ -700,10 +763,11 @@ "current_server_address": "Endereço atual do servidor", "custom_locale": "LocalizaÃ§ÃŖo Customizada", "custom_locale_description": "Formatar datas e nÃēmeros baseados na linguagem e regiÃŖo", + "custom_url": "URL personalizada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "trocar para tema escuro", + "dark_theme": "Usar tema escuro", "date_after": "Data apÃŗs", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Data de nascimento salvo com sucesso", "date_range": "Intervalo de datas", "day": "Dia", + "days": "Dias", "deduplicate_all": "Limpar todas Duplicidades", "deduplication_criteria_1": "Tamanho do arquivo em bytes", "deduplication_criteria_2": "Quantidade de dados EXIF", @@ -719,6 +784,8 @@ "default_locale": "LocalizaÃ§ÃŖo PadrÃŖo", "default_locale_description": "Formatar datas e nÃēmeros baseados na linguagem do seu navegador", "delete": "Excluir", + "delete_action_confirmation_message": "Confirma deletar este arquivo? O arquivo serÃĄ enviado para a lixeira do servidor e depois perguntarÃĄ se deseja deletar do seu dispositivo local", + "delete_action_prompt": "{count} deletados", "delete_album": "Excluir ÃĄlbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serÃŖo excluídos permanentemente do Immich e do seu dispositivo", @@ -732,9 +799,12 @@ "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", + "delete_local_action_prompt": "{count} deletados do dispositivo local", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup feito", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir restante", + "delete_permanently": "Deletar permanentemente", + "delete_permanently_action_prompt": "{count} Deletado permanentemente", "delete_shared_link": "Excluir link de compartilhamento", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Excluir marcador", @@ -745,6 +815,7 @@ "description": "DescriÃ§ÃŖo", "description_input_hint_text": "Adicionar descriÃ§ÃŖo...", "description_input_submit_error": "Erro ao atualizar a descriÃ§ÃŖo, verifique o log para mais detalhes", + "deselect_all": "Desselecionar tudo", "details": "Detalhes", "direction": "DireÃ§ÃŖo", "disabled": "Desativado", @@ -762,6 +833,7 @@ "documentation": "DocumentaÃ§ÃŖo", "done": "Feito", "download": "Baixar", + "download_action_prompt": "Baixando {count} arquivos", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -781,15 +853,19 @@ "downloading": "Baixando", "downloading_asset_filename": "Baixando arquivo {filename}", "downloading_media": "Baixando mídia", - "drop_files_to_upload": "Solte arquivos em qualquer lugar para carregar", + "drop_files_to_upload": "Solte os arquivos em qualquer lugar para enviar", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, sÃŖo duplicados", "duration": "DuraÃ§ÃŖo", "edit": "Editar", "edit_album": "Editar ÃĄlbum", "edit_avatar": "Editar foto de perfil", + "edit_birthday": "Alterar aniversÃĄrio", "edit_date": "Editar data", "edit_date_and_time": "Editar data e hora", + "edit_date_and_time_action_prompt": "Alterado data e hora de {count} arquivos", + "edit_date_and_time_by_offset": "Alterar data por deslocamento", + "edit_date_and_time_by_offset_interval": "Novas datas: De {from} a {to}", "edit_description": "Editar descriÃ§ÃŖo", "edit_description_prompt": "Por favor selecione uma nova descriÃ§ÃŖo:", "edit_exclusion_pattern": "Editar o padrÃŖo de exclusÃŖo", @@ -799,6 +875,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar LocalizaÃ§ÃŖo", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "LocalizaÃ§ÃŖo", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -814,9 +891,10 @@ "email": "E-mail", "email_notifications": "NotificaçÃĩes por e-mail", "empty_folder": "A pasta estÃĄ vazia", - "empty_trash": "Esvaziar lixo", + "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerÃĄ permanentemente do Immich todos os arquivos que estÃŖo na lixeira.\nVocÃĒ nÃŖo pode desfazer esta aÃ§ÃŖo!", "enable": "Habilitar", + "enable_backup": "Ativar Backup", "enable_biometric_auth_description": "Insira seu cÃŗdigo PIN para ativar a autenticaÃ§ÃŖo por biometria", "enabled": "Habilitado", "end_date": "Data final", @@ -827,7 +905,9 @@ "error": "Erro", "error_change_sort_album": "Falha ao alterar a ordem de exibiÃ§ÃŖo", "error_delete_face": "Erro ao remover face do arquivo", + "error_getting_places": "Erro ao buscar os locais", "error_loading_image": "Erro ao carregar a pÃĄgina", + "error_loading_partners": "Erro ao carregar parceiros: {error}", "error_saving_image": "Erro: {error}", "error_tag_face_bounding_box": "Erro ao marcar o rosto - nÃŖo foi possível localizar o rosto", "error_title": "Erro - Algo deu errado", @@ -855,11 +935,12 @@ "failed_to_edit_shared_link": "Falha ao editar o link compartilhado", "failed_to_get_people": "Falha na obtenÃ§ÃŖo de pessoas", "failed_to_keep_this_delete_others": "Falha ao manter este arquivo e excluir os outros", - "failed_to_load_asset": "NÃŖo foi possível carregar o ativo", - "failed_to_load_assets": "NÃŖo foi possível carregar os ativos", + "failed_to_load_asset": "NÃŖo foi possível carregar o arquivo", + "failed_to_load_assets": "NÃŖo foi possível carregar os arquivos", "failed_to_load_notifications": "Falha ao carregar notificaçÃĩes", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover a chave do produto", + "failed_to_reset_pin_code": "Falha ao redefinir o CÃŗdigo PIN", "failed_to_stack_assets": "Falha ao agrupar arquivos", "failed_to_unstack_assets": "Falha ao remover arquivos do grupo", "failed_to_update_notification_status": "Falha ao atualizar o status da notificaÃ§ÃŖo", @@ -868,6 +949,7 @@ "paths_validation_failed": "A validaÃ§ÃŖo de {paths, plural, one {# caminho falhou} other {# caminhos falharam}}", "profile_picture_transparent_pixels": "As imagens de perfil nÃŖo podem ter pixels transparentes. Aumente o zoom e/ou mova a imagem.", "quota_higher_than_disk_size": "VocÃĒ definiu uma cota maior do que o tamanho do disco", + "something_went_wrong": "Algo deu errado", "unable_to_add_album_users": "NÃŖo foi possível adicionar usuÃĄrios ao ÃĄlbum", "unable_to_add_assets_to_shared_link": "NÃŖo Ê possível adicionar arquivos ao link compartilhado", "unable_to_add_comment": "NÃŖo foi possível adicionar o comentÃĄrio", @@ -949,17 +1031,15 @@ "unable_to_update_settings": "NÃŖo foi possível atualizar as configuraçÃĩes", "unable_to_update_timeline_display_status": "NÃŖo foi possível atualizar o modo de visualizaÃ§ÃŖo da linha do tempo", "unable_to_update_user": "NÃŖo foi possível atualizar o usuÃĄrio", - "unable_to_upload_file": "NÃŖo foi possível carregar o arquivo" + "unable_to_upload_file": "NÃŖo foi possível enviar o arquivo" }, "exif": "Exif", "exif_bottom_sheet_description": "Adicionar descriÃ§ÃŖo...", + "exif_bottom_sheet_description_error": "Erro ao alterar a descriÃ§ÃŖo", "exif_bottom_sheet_details": "DETALHES", "exif_bottom_sheet_location": "LOCALIZAÇÃO", "exif_bottom_sheet_people": "PESSOAS", "exif_bottom_sheet_person_add_person": "Adicionar nome", - "exif_bottom_sheet_person_age_months": "Idade {months} meses", - "exif_bottom_sheet_person_age_year_months": "Idade 1 ano, {months} meses", - "exif_bottom_sheet_person_age_years": "Idade {years}", "exit_slideshow": "Sair da apresentaÃ§ÃŖo", "expand_all": "Expandir tudo", "experimental_settings_new_asset_list_subtitle": "Em andamento", @@ -973,22 +1053,26 @@ "explorer": "Explorar", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Banco de Dados", + "export_database_description": "Exportar o Banco de Dados SQLite", "extension": "ExtensÃŖo", "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 na rede Wi-Fi especificada, o aplicativo irÃĄ se conectar usando o primeiro endereço abaixo que obtiver sucesso, começando do topo da lista para baixo", "face_unassigned": "Sem nome", "failed": "Falhou", "failed_to_authenticate": "NÃŖo foi possível autenticar", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} marcados como favorito", "favorite_or_unfavorite_photo": "Marque ou desmarque a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhuma mídia favorita encontrada", "feature_photo_updated": "Foto principal atualizada", "features": "Funcionalidades", + "features_in_development": "FunçÃĩes em desenvolvimento", "features_setting_description": "Gerenciar as funcionalidades da aplicaÃ§ÃŖo", "file_name": "Nome do arquivo", "file_name_or_extension": "Nome do arquivo ou extensÃŖo", @@ -998,21 +1082,26 @@ "filter_people": "Filtrar pessoas", "filter_places": "Filtrar lugares", "find_them_fast": "Encontre pelo nome em uma pesquisa", + "first": "Primeiro", "fix_incorrect_match": "Corrigir correspondÃĒncia incorreta", "folder": "Pasta", "folder_not_found": "Pasta nÃŖo encontrada", "folders": "Pastas", "folders_feature_description": "Navegar pelas pastas das fotos e vídeos no sistema de arquivos", + "forgot_pin_code_question": "Esqueceu seu PIN?", "forward": "Para frente", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Esta funcionalidade carrega recursos externos do Google para funcionar.", "general": "Geral", + "geolocation_instruction_location": "Selecione um arquivo com as coordenadas de GPS desejada, ou selecione a localizaÃ§ÃŖo diretamente no mapa", "get_help": "Obter Ajuda", "get_wifiname_error": "NÃŖo foi possível obter o nome do Wi-Fi. Verifique se concedeu as permissÃĩes necessÃĄrias e se estÃĄ conectado a uma rede Wi-Fi", "getting_started": "Primeiros passos", "go_back": "Voltar", "go_to_folder": "Ir para a pasta", "go_to_search": "Ir para a pesquisa", + "gps": "GPS", + "gps_missing": "Sem GPS", "grant_permission": "Conceder permissÃŖo", "group_albums_by": "Agrupar ÃĄlbuns por...", "group_country": "Agrupar por país", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Ativar vibraÃ§ÃŖo", "haptic_feedback_title": "VibraÃ§ÃŖo", "has_quota": "Cota", + "hash_asset": "Calcular hash dos arquivos", + "hashed_assets": "Com hash", + "hashing": "Calculando", "header_settings_add_header_tip": "Adicionar Cabeçalho", "header_settings_field_validator_msg": "O valor nÃŖo pode estar vazio", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "SÃŗ Ê possível enviar 30 arquivos de cada vez, ignorando", "host": "Servidor", "hour": "Hora", + "hours": "Horas", "id": "ID", + "idle": "Inativo", "ignore_icloud_photos": "Ignorar fotos do iCloud", "ignore_icloud_photos_description": "Fotos que estÃŖo armazenadas no iCloud nÃŖo serÃŖo enviadas para o servidor do Immich", "image": "Imagem", @@ -1112,10 +1206,13 @@ "language_no_results_title": "nenhum idioma encontrado", "language_search_hint": "Procure idiomas...", "language_setting_description": "Selecione seu Idioma preferido", + "large_files": "Arquivos Grandes", + "last": "Último", "last_seen": "Visto pela ultima vez", "latest_version": "VersÃŖo mais recente", "latitude": "Latitude", "leave": "Sair", + "leave_album": "Sair do ÃĄlbum", "lens_model": "Modelo da lente", "let_others_respond": "Permitir respostas", "level": "Nível", @@ -1127,18 +1224,22 @@ "library_page_sort_created": "Data de criaÃ§ÃŖo", "library_page_sort_last_modified": "Última modificaÃ§ÃŖo", "library_page_sort_title": "Título do ÃĄlbum", + "licenses": "Licenças", "light": "Claro", + "like": "Curtir", "like_deleted": "Curtida excluída", "link_motion_video": "Relacionar video animado", - "link_options": "OpçÃĩes do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Vinculada", "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "NÃŖo Ê possível transmitir um arquivo que nÃŖo foi enviado ao servidor", + "local_assets": "Arquivos no dispositivo", + "local_media_summary": "Resumo das mídias locais", "local_network": "Rede local", - "local_network_sheet_info": "O aplicativo irÃĄ se conectar ao servidor atravÊs desta URL quando estiver na rede Wi-Fi especificada", + "local_network_sheet_info": "O aplicativo irÃĄ se conectar ao servidor atravÊs deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "PermissÃŖo de localizaÃ§ÃŖo", "location_permission_content": "Para utilizar a funÃ§ÃŖo de troca automÃĄtica de URL Ê necessÃĄrio a permissÃŖo de localizaÃ§ÃŖo precisa, para que seja possível ler o nome da rede Wi-Fi", "location_picker_choose_on_map": "Escolha no mapa", @@ -1147,7 +1248,8 @@ "location_picker_longitude_error": "Digite uma longitude vÃĄlida", "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", - "locked_folder": "Pasta Trancada", + "locked_folder": "Pasta com senha", + "log_detail_title": "Detalhes do Log", "log_out": "Sair", "log_out_all_devices": "Sair de todos dispositivos", "logged_in_as": "UsuÃĄrio atual: {user}", @@ -1167,7 +1269,7 @@ "login_form_err_trailing_whitespace": "HÃĄ um espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Erro de login com OAuth, verifique a URL do servidor", "login_form_failed_get_oauth_server_disable": "O recurso OAuth nÃŖo estÃĄ disponível neste servidor", - "login_form_failed_login": "Erro ao fazer login, verifique a url do servidor, e-mail e senha", + "login_form_failed_login": "Erro ao fazer login, verifique a URL do servidor, e-mail e senha", "login_form_handshake_exception": "Houve um erro de autorizaÃ§ÃŖo com o servidor. Se estiver utilizando um certificado auto assinado, ative o suporte a isso nas configuraçÃĩes.", "login_form_password_hint": "senha", "login_form_save_login": "Permaneçer conectado", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Senha atualizada com sucesso", "logout_all_device_confirmation": "Tem certeza de que deseja sair de todos os dispositivos?", "logout_this_device_confirmation": "Tem certeza de que deseja sair deste dispositivo?", + "logs": "Logs", "longitude": "Longitude", "look": "Estilo", "loop_videos": "Repetir vídeos", @@ -1185,6 +1288,7 @@ "main_branch_warning": "VocÃĒ estÃĄ utilizando uma versÃŖo de desenvolvimento. É fortemente recomendado que utilize uma versÃŖo estÃĄvel!", "main_menu": "Menu Principal", "make": "Marca", + "manage_geolocation": "Gerenciar localizaÃ§ÃŖo", "manage_shared_links": "Gerir links partilhados", "manage_sharing_with_partners": "Gerenciar compartilhamento com parceiros", "manage_the_app_settings": "Gerenciar configuraçÃĩes do app", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Gerenciar seus dispositivos logados", "manage_your_oauth_connection": "Gerenciar sua conexÃŖo OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, =0 {Sem fotos nesta ÃĄrea} one {# foto} other {# fotos}}", "map_cannot_get_user_location": "NÃŖo foi possível obter a sua localizaÃ§ÃŖo", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Use esta localizaÃ§ÃŖo", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Serviço de localizaÃ§ÃŖo desativado", "map_marker_for_images": "Marcador de mapa para imagens tiradas em {city}, {country}", "map_marker_with_image": "Marcador de mapa com imagem", - "map_no_assets_in_bounds": "NÃŖo hÃĄ fotos nesta ÃĄrea", "map_no_location_permission_content": "É necessÃĄria a permissÃŖo de localizaÃ§ÃŖo para exibir os arquivos da sua localizaÃ§ÃŖo atual. Deseja conceder a permissÃŖo agora?", "map_no_location_permission_title": "PermissÃŖo de localizaÃ§ÃŖo foi negada", "map_settings": "DefiniçÃĩes do mapa", @@ -1221,6 +1323,7 @@ "mark_as_read": "Marcar como lido", "marked_all_as_read": "Tudo marcado como lido", "matches": "CorrespondÃĒncias", + "matching_assets": "Arquivos encontrados", "media_type": "Tipo de mídia", "memories": "MemÃŗrias", "memories_all_caught_up": "Finalizamos por hoje", @@ -1239,6 +1342,7 @@ "merged_people_count": "{count, plural, one {# pessoa foi mesclada} other {# pessoas foram mescladas}}", "minimize": "Minimizar", "minute": "Minuto", + "minutes": "Minutos", "missing": "Faltando", "model": "Modelo", "month": "MÃĒs", @@ -1246,6 +1350,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta com senha", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta com senha", "move_to_locked_folder": "Mover para a pasta com senha", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serÃŖo removidos de todos os ÃĄlbuns e somente poderÃŖo ser visualizados de dentro da pasta com senha", "moved_to_archive": "{count, plural, one {# mídia foi arquivada} other {# mídias foram arquivadas}}", @@ -1257,6 +1362,10 @@ "my_albums": "Meus Álbuns", "name": "Nome", "name_or_nickname": "Nome ou apelido", + "network_requirement_photos_upload": "Use a rede mÃŗvel para enviar fotos", + "network_requirement_videos_upload": "Use a rede mÃŗvel para enviar vídeos", + "network_requirements": "Requerimentos de Rede", + "network_requirements_updated": "Requerimentos de rede alterados, reiniciando a fila de envio", "networking_settings": "ConexÃĩes", "networking_subtitle": "Gerencie as conexÃĩes ao servidor", "never": "Nunca", @@ -1266,6 +1375,7 @@ "new_person": "Nova Pessoa", "new_pin_code": "Novo cÃŗdigo PIN", "new_pin_code_subtitle": "Esta Ê a primeira vez que estÃĄ acessando a pasta com senha. Crie um cÃŗdigo PIN para acessar esta pÃĄgina de forma segura", + "new_timeline": "Nova Linha do Tempo", "new_user_created": "Novo usuÃĄrio criado", "new_version_available": "NOVA VERSÃO DISPONÍVEL", "newest_first": "Mais recente primeiro", @@ -1276,25 +1386,31 @@ "no_albums_with_name_yet": "Parece que vocÃĒ ainda nÃŖo tem nenhum ÃĄlbum com esse nome.", "no_albums_yet": "Parece que vocÃĒ ainda nÃŖo tem nenhum ÃĄlbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualizaÃ§ÃŖo de fotos", - "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", + "no_assets_message": "CLIQUE PARA ENVIAR SUA PRIMEIRA FOTO", "no_assets_to_show": "NÃŖo hÃĄ arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo encontrado", + "no_checksum_local": "Nenhum checksum disponível - nÃŖo foi possível carregar os arquivos locais", + "no_checksum_remote": "Nenhum checksum disponível - nÃŖo foi possível carregar os arquivos remotos", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", "no_exif_info_available": "Sem informaçÃĩes exif disponíveis", - "no_explore_results_message": "Carregue mais fotos para explorar sua coleÃ§ÃŖo.", + "no_explore_results_message": "Envie mais fotos para explorar sua coleÃ§ÃŖo.", "no_favorites_message": "Adicione aos favoritos para encontrar suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver suas fotos e vídeos", + "no_local_assets_found": "Nenhum arquivo local foi encontrado com este checksum", "no_locked_photos_message": "Fotos e vídeos na pasta com senha sÃŖo ocultos e nÃŖo serÃŖo exibidos enquanto explora ou pesquisa na biblioteca.", "no_name": "Sem Nome", "no_notifications": "Nenhuma notificaÃ§ÃŖo", "no_people_found": "Nenhuma pessoa encontrada", "no_places": "Sem lugares", + "no_remote_assets_found": "Nenhum arquivo remoto foi encontrado com este checksum", "no_results": "Sem resultados", "no_results_description": "Tente um sinônimo ou uma palavra-chave mais geral", "no_shared_albums_message": "Crie um ÃĄlbum para compartilhar fotos e vídeos com pessoas em sua rede", + "no_uploads_in_progress": "Nenhum envio em progresso", + "not_available": "N/A", "not_in_any_album": "Fora de ÃĄlbum", "not_selected": "NÃŖo selecionado", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rÃŗtulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rÃŗtulo de armazenamento a arquivos enviados anteriormente, execute o", "notes": "Notas", "nothing_here_yet": "Ainda nÃŖo existe nada aqui", "notification_permission_dialog_content": "Para ativar as notificaçÃĩes, vÃĄ em ConfiguraçÃĩes e selecione permitir.", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Recursos oficiais do Immich", "offline": "Desconectado", + "offset": "Deslocamento", "ok": "Ok", "oldest_first": "Mais antigo primeiro", "on_this_device": "Neste dispositivo", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Abre os filtros de pesquisa", "options": "OpçÃĩes", "or": "ou", + "organize_into_albums": "Organizar em ÃĄlbuns", + "organize_into_albums_description": "Colocar imagens existentes em ÃĄlbuns usando as configuraçÃĩes de sincronizaÃ§ÃŖo atuais", "organize_your_library": "Organize sua biblioteca", "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variÃĄveis", "owned": "Seu", "owner": "Dono", @@ -1369,7 +1489,7 @@ "permanent_deletion_warning_setting_description": "Exibe um aviso ao deletar arquivos de forma permanente", "permanently_delete": "Deletar permanentemente", "permanently_delete_assets_count": "Excluir permanentemente {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "VocÃĒ tem certeza de que deseja excluir permanentemente {count, plural, one {este ativo?} other {estes # ativos?}} Esta aÃ§ÃŖo tambÊm removerÃĄ {count, plural, one {o ativo} other {os ativos}} de um ou mais ÃĄlbuns.", + "permanently_delete_assets_prompt": "VocÃĒ tem certeza de que deseja excluir permanentemente {count, plural, one {este arquivo?} other {estes # arquivos?}} Esta aÃ§ÃŖo tambÊm removerÃĄ {count, plural, one {o arquivo} other {os arquivos}} de um ou mais ÃĄlbuns.", "permanently_deleted_asset": "Arquivo deletado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo permanentemente excluído} other {# arquivos permanentemente excluídos}}", "permission": "PermissÃŖo", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "PermissÃŖo limitada. Para permitir que o Immich faça backups e gerencie sua galeria, conceda permissÃĩes para fotos e vídeos nas configuraçÃĩes.", "permission_onboarding_request": "Immich requer permissÃŖo para visualizar suas fotos e vídeos.", "person": "Pessoa", + "person_age_months": "{months, plural, one {# mÃĒs} other {# meses}} de idade", + "person_age_year_months": "1 ano, {months, plural, one {# mÃĒs} other {# meses}} de idade", + "person_age_years": "{years, plural, other {# anos}}", "person_birthdate": "Nasceu em {date}", "person_hidden": "{name}{hidden, select, true { (oculto)} other {}}", "photo_shared_all_users": "Parece que vocÃĒ compartilhou suas fotos com todos os usuÃĄrios ou nÃŖo tem nenhum usuÃĄrio com quem compartilhar.", @@ -1406,6 +1529,7 @@ "port": "Porta", "preferences_settings_subtitle": "Gerenciar as preferÃĒncias do aplicativo", "preferences_settings_title": "PreferÃĒncias", + "preparing": "Preparando", "preset": "PredefiniÃ§ÃŖo", "preview": "PrÊ-visualizar", "previous": "Anterior", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "O aplicativo estÃĄ desatualizado. Por favor, atualize para a versÃŖo mais recente.", "profile_drawer_client_server_up_to_date": "Cliente e Servidor estÃŖo atualizados", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Modo apenas leitura habilidato. DÃĒ um toque prolongado na foto do usuÃĄrio para sair deste modo.", "profile_drawer_server_out_of_date_major": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo principal mais recente.", "profile_drawer_server_out_of_date_minor": "O servidor estÃĄ desatualizado. Atualize para a versÃŖo mais recente.", "profile_image_of_user": "Imagem do perfil de {user}", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Status de Contribuidor", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave do produto para servidor Ê gerenciada pelo administrador", + "query_asset_id": "Consultar ID do Ativo", + "queue_status": "Na fila {count} de {total}", "rating": "Estrelas", "rating_clear": "Limpar classificaÃ§ÃŖo", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", "rating_description": "Exibir o EXIF de classificaÃ§ÃŖo no painel de informaçÃĩes", "reaction_options": "OpçÃĩes de reaÃ§ÃŖo", "read_changelog": "Ler Novidades", + "readonly_mode_disabled": "Modo apenas visualizaÃ§ÃŖo desativado", + "readonly_mode_enabled": "Modo apenas visualizaÃ§ÃŖo ativado", + "ready_for_upload": "Pronto para upload", "reassign": "Reatribuir", "reassigned_assets_to_existing_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a {name, select, null {uma pessoa} other {{name}}}", "reassigned_assets_to_new_person": "{count, plural, one {# arquivo reatribuído} other {# arquivos reatribuídos}} a uma nova pessoa", @@ -1488,6 +1618,9 @@ "refreshing_faces": "Atualizando rostos", "refreshing_metadata": "Atualizando metadados", "regenerating_thumbnails": "Regenerando miniaturas", + "remote": "Remoto", + "remote_assets": "Arquivos Remotos", + "remote_media_summary": "Resumo das mídias remotas", "remove": "Remover", "remove_assets_album_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} do ÃĄlbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover arquivos excluídos", "remove_from_album": "Remover do ÃĄlbum", + "remove_from_album_action_prompt": "{count} removido do ÃĄlbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta com senha", "remove_from_locked_folder": "Remover da pasta com senha", "remove_from_locked_folder_confirmation": "Tem a certeza de que deseja mover estes arquivos para fora da pasta com senha? Eles ficarÃŖo visíveis na biblioteca principal.", "remove_from_shared_link": "Remover do link compartilhado", @@ -1523,19 +1658,29 @@ "reset_password": "Resetar senha", "reset_people_visibility": "Resetar pessoas ocultas", "reset_pin_code": "Redefinir cÃŗdigo PIN", + "reset_pin_code_description": "Se esqueceu seu cÃŗdigo PIN, entre em contato com o administrador do Immich e peça para redefinir", + "reset_pin_code_success": "cÃŗdigo PIN alterado com sucesso", + "reset_pin_code_with_password": "VocÃĒ sempre poderÃĄ redefinir seu cÃŗdigo PIN usando a sua senha", + "reset_sqlite": "Redefinir o Banco de Dados SQLite", + "reset_sqlite_confirmation": "Realmente deseja redefinir o banco de dados SQLite? SerÃĄ necessÃĄrio sair e entrar em sua conta novamente para ressincronizar os dados", + "reset_sqlite_success": "Banco de dados SQLite redefinido com sucesso", "reset_to_default": "Redefinir para a configuraÃ§ÃŖo padrÃŖo", "resolve_duplicates": "Resolver duplicatas", "resolved_all_duplicates": "Todas duplicidades resolvidas", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da lixeira", "restore_user": "Restaurar usuÃĄrio", "restored_asset": "Arquivo restaurado", "resume": "Continuar", - "retry_upload": "Tentar carregar novamente", + "resume_paused_jobs": "Retomar {count, plural, one {# paused job} other {# paused jobs}}", + "retry_upload": "Tentar enviar novamente", "review_duplicates": "Revisar duplicidade", + "review_large_files": "Ver arquivos grandes", "role": "FunÃ§ÃŖo", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "Executando", "save": "Salvar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API salva", @@ -1622,11 +1767,12 @@ "select_user_for_sharing_page_err_album": "Falha ao criar ÃĄlbum", "selected": "Selecionados", "selected_count": "{count, plural, one {# selecionado} other {# selecionados}}", + "selected_gps_coordinates": "Coordenadas de GPS Selecionada", "send_message": "Enviar mensagem", "send_welcome_email": "Enviar E-mail de boas vindas", "server_endpoint": "URL do servidor", "server_info_box_app_version": "VersÃŖo do aplicativo", - "server_info_box_server_url": "URL do servidor", + "server_info_box_server_url": "Endereço", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", "server_privacy": "Privacidade do servidor", @@ -1667,16 +1813,17 @@ "settings_saved": "ConfiguraçÃĩes salvas", "setup_pin_code": "Criar um cÃŗdigo PIN", "share": "Compartilhar", + "share_action_prompt": "{count} arquivos compartilhados", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", - "share_link": "Compartilhar Link", + "share_link": "Criar Link", "shared": "Compartilhado", "shared_album_activities_input_disable": "ComentÃĄrios desativados", "shared_album_activity_remove_content": "Deseja excluir esta atividade?", "shared_album_activity_remove_title": "Excluir atividade", "shared_album_section_people_action_error": "Erro ao sair/remover do ÃĄlbum", - "shared_album_section_people_action_leave": "Sair do ÃĄlbum", + "shared_album_section_people_action_leave": "Remover usuÃĄrio do ÃĄlbum", "shared_album_section_people_action_remove_user": "Remover usuÃĄrio do ÃĄlbum", "shared_album_section_people_title": "PESSOAS", "shared_by": "Compartilhado por", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Copiado para a ÃĄrea de transferÃĒncia", "shared_link_clipboard_text": "Link: {link}\nSenha: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_custom_url_description": "Acessar este link com uma URL personalizada", "shared_link_edit_description_hint": "Digite a descriÃ§ÃŖo do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dias", @@ -1713,7 +1861,8 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "OpçÃĩes de link compartilhado", - "shared_links": "Links compartilhados", + "shared_link_password_description": "Exija uma senha para acessar este link compartilhado", + "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", "shared_with_me": "Compartilhado comigo", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "Usar transiçÃĩes no modo de apresentaÃ§ÃŖo", "show_supporter_badge": "Insígnia de apoiador", "show_supporter_badge_description": "Mostrar uma insígnia de apoiador", + "show_text_search_menu": "Mostrar menu de pesquisa por texto", "shuffle": "AleatÃŗrio", "sidebar": "Barra lateral", "sidebar_display_description": "Exibir um link para a visualizaÃ§ÃŖo na barra lateral", @@ -1762,12 +1912,14 @@ "sort_created": "Data de criaÃ§ÃŖo", "sort_items": "NÃēmero de itens", "sort_modified": "Data de modificaÃ§ÃŖo", + "sort_newest": "Foto mais nova", "sort_oldest": "Foto mais antiga", "sort_people_by_similarity": "Ordenar pessoas por semelhança", "sort_recent": "Foto mais recente", "sort_title": "Título", "source": "Fonte", "stack": "Agrupar", + "stack_action_prompt": "{count} agrupados", "stack_duplicates": "Agrupar duplicados", "stack_select_one_photo": "Selecione uma foto principal para o grupo", "stack_selected_photos": "Agrupar fotos selecionadas", @@ -1775,6 +1927,7 @@ "stacktrace": "Stacktrace", "start": "Início", "start_date": "Data inicial", + "start_date_before_end_date": "A data de início deve ser antes da data final", "state": "Estado", "status": "Status", "stop_casting": "Parar transmissÃŖo", @@ -1787,6 +1940,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "SugestÃĩes", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Ajuda", @@ -1796,6 +1950,10 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar ÃĄlbuns", "sync_albums_manual_subtitle": "Sincronize todos as fotos e vídeos enviados para os ÃĄlbuns de backup selecionados", + "sync_local": "SincronizaÃ§ÃŖo Local", + "sync_remote": "SincronizaÃ§ÃŖo Remota", + "sync_status": "Status da SincronizaÃ§ÃŖo", + "sync_status_subtitle": "Ver e gerenciar o sistema de sincronizaÃ§ÃŖo", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o ÃĄlbum selecionado no Immich", "tag": "Marcador", "tag_assets": "Marcar arquivos", @@ -1806,6 +1964,7 @@ "tag_updated": "Marcador foi atualizado: {tag}", "tagged_assets": "{count, plural, one {# Arquivo marcado} other {# Arquivos marcados}}", "tags": "Marcadores", + "tap_to_run_job": "Toque para executar", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1832,12 +1991,15 @@ "to_change_password": "Alterar senha", "to_favorite": "Favorito", "to_login": "Iniciar sessÃŖo", + "to_multi_select": "selecionar vÃĄrios", "to_parent": "Voltar para nível acima", + "to_select": "selecionar", "to_trash": "Mover para a lixeira", "toggle_settings": "Alternar configuraçÃĩes", "total": "Total", "total_usage": "UtilizaÃ§ÃŖo total", "trash": "Lixeira", + "trash_action_prompt": "{count} enviados à lixeira", "trash_all": "Mover todos para o lixo", "trash_count": "Lixo {count, number}", "trash_delete_asset": "Jogar na lixeira/Excluir Arquivo", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Selecionar arquivos", "trash_page_title": "Lixeira ({count})", "trashed_items_will_be_permanently_deleted_after": "Os itens da lixeira serÃŖo deletados permanentemente apÃŗs {days, plural, one {# dia} other {# dias}}.", + "troubleshoot": "Diagnosticar", "type": "Tipo", "unable_to_change_pin_code": "NÃŖo foi possível alterar o cÃŗdigo PIN", "unable_to_setup_pin_code": "NÃŖo foi possível criar o cÃŗdigo PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} desarquivado", "unarchived_count": "{count, plural, one {# Desarquivado} other {# Desarquivados}}", "undo": "Desfazer", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removido dos favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,23 +2040,30 @@ "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unselect_all_in": "Remover seleÃ§ÃŖo de {group}", "unstack": "Retirar do grupo", + "unstack_action_prompt": "{count} desagrupados", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", + "untagged": "Marcador removido", "up_next": "A seguir", + "update_location_action_prompt": "Atualizar a localizaÃ§ÃŖo de {count} arquivos selecionados para:", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", - "upload": "Carregar", + "upload": "Enviar", + "upload_action_prompt": "{count} na fila de envio", "upload_concurrency": "Envios simultÃĸneos", + "upload_details": "Detalhes do envio", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", - "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a pÃĄgina para ver os novos arquivos carregados.", + "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a pÃĄgina para ver os novos arquivos.", + "upload_finished": "Envio finalizado", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} jÃĄ processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", - "upload_status_uploaded": "Carregado", - "upload_success": "Carregado com sucesso, atualize a pÃĄgina para ver os novos arquivos.", + "upload_status_uploaded": "Enviado", + "upload_success": "Enviado com sucesso, atualize a pÃĄgina para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "Enviando mídia", "url": "URL", "usage": "Uso", "use_biometric": "Usar biometria", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Ver estatísticas de utilizaÃ§ÃŖo da conta", "username": "Nome do usuÃĄrio", "users": "UsuÃĄrios", + "users_added_to_album_count": "{count, plural, one {# usuÃĄrio adicionado} other {# usuÃĄrios adicionados}} ao ÃĄlbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL vÃĄlida", @@ -1930,6 +2103,7 @@ "view_album": "Ver ÃĄlbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os usuÃĄrios", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", @@ -1937,6 +2111,7 @@ "view_next_asset": "Ver prÃŗximo arquivo", "view_previous_asset": "Ver arquivo anterior", "view_qr_code": "Ver QR Code", + "view_similar_photos": "Ver fotos similares", "view_stack": "Ver grupo", "view_user": "Visualizar usuÃĄrio", "viewer_remove_from_stack": "Remover do grupo", @@ -1955,5 +2130,6 @@ "yes": "Sim", "you_dont_have_any_shared_links": "NÃŖo hÃĄ links compartilhados", "your_wifi_name": "Nome do seu Wi-Fi", - "zoom_image": "Ampliar imagem" + "zoom_image": "Ampliar imagem", + "zoom_to_bounds": "Ampliar para preencher" } diff --git a/i18n/ro.json b/i18n/ro.json index 0b2ef61a36..71784cfcf9 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -1,12 +1,12 @@ { "about": "Despre", "account": "Cont", - "account_settings": "Setări Cont", + "account_settings": "Setări cont", "acknowledge": "Văzut", "action": "AcÅŖiune", "action_common_update": "Actualizează", "actions": "AcÅŖiuni", - "active": "Activ", + "active": "Active", "activity": "Activitate", "activity_changed": "Activitatea este {enabled, select, true {activată} other {dezactivată}}", "add": "Adaugă", @@ -14,6 +14,8 @@ "add_a_location": "Adaugă o locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", + "add_birthday": "Adaugă zi de naștere", + "add_endpoint": "Adaugă punct final", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", "add_location": "Adaugă locație", @@ -21,10 +23,15 @@ "add_partner": "Adaugă partener", "add_path": "Adaugă o cale", "add_photos": "Adaugă fotografii", + "add_tag": "Adaugă etichetă", "add_to": "Adaugă laâ€Ļ", "add_to_album": "Adaugă ÃŽn album", "add_to_album_bottom_sheet_added": "Adăugat ÃŽn {album}", "add_to_album_bottom_sheet_already_exists": "Deja ÃŽn {album}", + "add_to_album_bottom_sheet_some_local_assets": "Unele resurse locale nu au putut fi adăugate la album", + "add_to_album_toggle": "Selectează/deselectează {album}", + "add_to_albums": "Adaugă la albume", + "add_to_albums_count": "Adaugă la albume ({count})", "add_to_shared_album": "Adaugă la album partajat", "add_url": "Adăugați adresa URL", "added_to_archive": "Adăugat la arhivă", @@ -32,24 +39,33 @@ "added_to_favorites_count": "Adăugat {count, number} la favorite", "admin": { "add_exclusion_pattern_description": "Adăugați modele de excludere. Globing folosind *, ** și ? este suportat. Pentru a ignora toate fișierele din orice director numit „Raw”, utilizați „**/Raw/**”. Pentru a ignora toate fișierele care se termină ÃŽn „.tif”, utilizați „**/*.tif”. Pentru a ignora o cale absolută, utilizați „/path/to/ignore/**”.", + "admin_user": "Utilizator admin", "asset_offline_description": "Acest material din biblioteca externă nu se mai găsește pe disc și a fost mutat ÃŽn coșul de gunoi. Dacă fișierul a fost mutat ÃŽn bibliotecă, verificați cronologia pentru noul material corespunzător. Pentru a restabili acest material, asigurați-vă că calea fișierului de mai jos poate fi accesată de Immich și scanați biblioteca.", - "authentication_settings": "Setări de Autentificare", + "authentication_settings": "Setări de autentificare", "authentication_settings_description": "Gestionează parola, OAuth și alte setări de autentificare", "authentication_settings_disable_all": "Ești sigur că vrei sa dezactivezi toate metodele de autentificare? Autentificarea va fi complet dezactivată.", "authentication_settings_reenable": "Pentru a reactiva, folosește Comandă Server.", "background_task_job": "Activități de Fundal", - "backup_database": "Salvare Bază de Date", - "backup_database_enable_description": "Activare salvare bază de date", - "backup_keep_last_amount": "Cantitatea de copii de rezervă anterioare de păstrat", - "backup_settings": "Setări Copii de Rezervă", - "backup_settings_description": "Gestionați setările de salvare a bazei de date", - "cleared_jobs": "Activități eliminate pentru: {job}", + "backup_database": "Salvare bază de date", + "backup_database_enable_description": "Activare salvarea bazei de date", + "backup_keep_last_amount": "Număr de copii de rezervă anterioare de păstrat", + "backup_onboarding_1_description": "copie externă ÃŽn cloud sau ÃŽntr-o altă locație fizică.", + "backup_onboarding_2_description": "copii locale pe diferite dispozitive. Include fișierele principale și o copie de rezervă a acestor fișiere la nivel local.", + "backup_onboarding_3_description": "numărul total de copii ale datelor dvs., inclusiv fișierele originale. Aceasta include 1 copie externă și 2 copii locale.", + "backup_onboarding_description": "Pentru a vă proteja datele, vă recomandăm să utilizați strategia de backup 3-2-1. Pentru o soluție completă de backup, ar trebui să păstrați copii ale fotografiilor/videoclipurilor ÃŽncărcate, precum și ale bazei de date Immich.", + "backup_onboarding_footer": "Pentru mai multe informații despre copierea de rezervă a Immich, consultați documentația.", + "backup_onboarding_parts_title": "O copie de rezervă 3-2-1 include:", + "backup_onboarding_title": "Copii de rezervă", + "backup_settings": "Setări pentru descărcarea bazei de date", + "backup_settings_description": "Gestionați setările de descărcare a bazei de date.", + "cleared_jobs": "Sarcini șterse pentru: {job}", "config_set_by_file": "Configurația este setată ÃŽn prezent de un fișier de configurare", "confirm_delete_library": "Sigur doriți să ștergeți biblioteca {library}?", "confirm_delete_library_assets": "Sigur doriți să ștergeți această bibliotecă? Aceasta va șterge {count, plural, one {# contained asset} other {all # contained assets}} din Immich și nu poate fi anulată. Fișierele vor rămÃĸne pe disc.", "confirm_email_below": "Pentru a confirma, tastați „{email}” mai jos", "confirm_reprocess_all_faces": "Sigur doriți să reprocesați toate fețele? Acest lucru va șterge și persoanele cu nume.", "confirm_user_password_reset": "Sigur doriți să resetați parola utilizatorului {user}?", + "confirm_user_pin_code_reset": "Ești sigur că vrei să resetezi codul PIN al {user}?", "create_job": "Creează sarcină", "cron_expression": "Expresia cron", "cron_expression_description": "Setați intervalul de scanare folosind formatul cron. Pentru mai multe informații, consultați de ex. Crontab Guru", @@ -57,9 +73,9 @@ "disable_login": "Dezactivați autentificarea", "duplicate_detection_job_description": "Rulați ÃŽnvățarea automată pe materiale pentru a detecta imagini similare. Se bazează pe Căutare Inteligentă", "exclusion_pattern_description": "Modelele de excludere vă permit să ignorați fișierele și folderele atunci cÃĸnd vă scanați biblioteca. Acest lucru este util dacă aveți foldere care conțin fișiere pe care nu doriți să le importați, cum ar fi fișierele RAW.", - "external_library_management": "Managementul Bibliotecii Externe", + "external_library_management": "Gestionarea bibliotecilor externe", "face_detection": "Detecție facială", - "face_detection_description": "Detectează fețele din fișiere folosind ÃŽnvățare automată. Pentru videoclipuri, este luată ÃŽn considerare doar miniatura. „ReÃŽnprospătează” (re)procesează toate fișierele. „Resetează” adaugă ÃŽn coadă fișierele care nu au fost ÃŽncă procesate. Fețele detectate vor fi puse ÃŽn coadă pentru recunoașterea facială după finalizarea detectării feței, grupÃĸndu-le ÃŽn persoane existente sau noi.", + "face_detection_description": "Detectează fețele din fișiere folosind ÃŽnvățare automată. Pentru videoclipuri, este luată ÃŽn considerare doar miniatura. „ReÃŽmprospătează” (re)procesează toate fișierele. „Resetează” adaugă ÃŽn coadă fișierele care nu au fost ÃŽncă procesate. Fețele detectate vor fi puse ÃŽn coadă pentru recunoașterea facială după finalizarea detectării feței, grupÃĸndu-le ÃŽn persoane existente sau noi.", "facial_recognition_job_description": "Grupați fețele detectate ÃŽn persoane. Acest pas rulează după ce Detectarea Feței este finalizată. „Resetează” (re)grupează toate fețele. „Lipsă” adaugă ÃŽn coadă fețe care nu au o persoană desemnată.", "failed_job_command": "Comanda {command} a eșuat pentru jobul: {job}", "force_delete_user_warning": "AVERTISMENT: Acest lucru va elimina imediat utilizatorul și toate activele sale. Acest lucru nu poate fi anulat și fișierele nu pot fi recuperate.", @@ -76,30 +92,30 @@ "image_prefer_wide_gamut_setting_description": "Utilizați Display P3 pentru miniaturi. Acest lucru păstrează mai bine vibrația imaginilor cu spații de culoare largi, dar imaginile pot apărea diferit pe dispozitivele cu o versiune mai veche de browser. Imaginile sRGB sunt păstrate ca sRGB pentru a evita schimbările de culoare.", "image_preview_description": "Imagine de dimensiune medie cu metadate eliminate, utilizată la vizualizarea unui singur element și pentru ÃŽnvățarea automată", "image_preview_quality_description": "Calitatea previzualizării de la 1 la 100. O valoare mai mare oferă o calitate mai bună, dar produce fișiere mai mari și poate reduce receptivitatea aplicației. Setarea unei valori scăzute poate afecta calitatea ÃŽnvățării automate.", - "image_preview_title": "Previzualizați Setările", + "image_preview_title": "Previzualizați setările", "image_quality": "Calitate", "image_resolution": "Rezolutie", "image_resolution_description": "Rezoluțiile mai mari pot păstra mai multe detalii, dar necesită mai mult timp pentru a fi codificate, au dimensiuni mai mari ale fișierelor și pot reduce răspunsul aplicației.", - "image_settings": "Setări Imagine", + "image_settings": "Setări imagine", "image_settings_description": "Gestionează calitatea și rezoluția imaginilor generate", "image_thumbnail_description": "Miniatură mică cu metadate eliminate, utilizată la vizualizarea grupurilor de fotografii, cum ar fi ÃŽn cronologia principală", "image_thumbnail_quality_description": "Calitatea miniaturii de la 1 la 100. O valoare mai mare oferă o calitate mai bună, dar produce fișiere mai mari și poate reduce receptivitatea aplicației.", - "image_thumbnail_title": "Setari Miniaturi", + "image_thumbnail_title": "Setari miniaturi", "job_concurrency": "Concurență {job}", "job_created": "Sarcină creată", "job_not_concurrency_safe": "Această sarcină nu este sigură pentru a rula ÃŽn concurență.", - "job_settings": "Setări Sarcină", + "job_settings": "Setări sarcină", "job_settings_description": "Administrează concurența sarcinilor", - "job_status": "Starea Sarcinii", + "job_status": "Starea sarcinii", "jobs_delayed": "{jobCount, plural, other {# ÃŽntÃĸrziat}}", "jobs_failed": "{jobCount, plural, other {# eșuat}}", - "library_created": "Librărie creată:{library}", + "library_created": "Librărie creată: {library}", "library_deleted": "Bibliotecă ștearsă", "library_import_path_description": "Specificați un folder pentru a ÃŽl importa. Acest folder, inclusiv sub-folderele, vor fi scanate pentru imagini și videoclipuri.", - "library_scanning": "Scanare Periodică", + "library_scanning": "Scanare periodică", "library_scanning_description": "Configurează scanarea periodică pentru bibliotecă", "library_scanning_enable_description": "Activează scanarea periodică pentru bibliotecă", - "library_settings": "Bibliotecă Externă", + "library_settings": "Bibliotecă externă", "library_settings_description": "Administrează setările pentru biblioteci externe", "library_tasks_description": "Scanează bibliotecile externe de active noi sau modificate", "library_watching_enable_description": "Urmărește bibliotecile externe pentru schimbări ale fișierelor", @@ -108,9 +124,16 @@ "logging_enable_description": "Activează ÃŽnregistrarea log-urilor", "logging_level_description": "Dacă setarea este activată, ÃŽnregistrează evenimentele cu nivelul de utilizat.", "logging_settings": "Înregistrare", + "machine_learning_availability_checks": "Verificări disponibilitate", + "machine_learning_availability_checks_description": "Detectează automat si preferă serverele cu ÃŽnvațare automată", + "machine_learning_availability_checks_enabled": "Activează verificare disponibilitate", + "machine_learning_availability_checks_interval": "Interval verificare", + "machine_learning_availability_checks_interval_description": "Interval in milisecunde ÃŽntre verificările de disponibilitate", + "machine_learning_availability_checks_timeout": "Timp de expirare cerere", + "machine_learning_availability_checks_timeout_description": "Timp de așteptare ÃŽn milisecunde pentru verificările de disponibilitate", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Numele unui model CLIP listat aici. Rețineți că trebuie să rulați din nou funcția „Smart Search” pentru toate imaginile la schimbarea unui model.", - "machine_learning_duplicate_detection": "Detectare Duplicate", + "machine_learning_duplicate_detection": "Detectare duplicate", "machine_learning_duplicate_detection_enabled": "Activează detectarea duplicatelor", "machine_learning_duplicate_detection_enabled_description": "Dacă este dezactivată, elementele identice vor fi ÃŽn continuare de-duplicate.", "machine_learning_duplicate_detection_setting_description": "Utilizați ÃŽncorporările CLIP pentru a găsi dubluri probabile", @@ -137,11 +160,11 @@ "machine_learning_smart_search_enabled": "Activați căutarea inteligentă", "machine_learning_smart_search_enabled_description": "Dacă este dezactivată, imaginile nu vor fi codificate pentru căutarea inteligentă.", "machine_learning_url_description": "URL-ul serverului de ÃŽnvățare automată. Dacă sunt furnizate mai multe URL-uri, fiecare server va fi ÃŽncercat pe rÃĸnd, pÃĸnă cÃĸnd unul răspunde cu succes, ÃŽn ordine de la primul pÃĸnă la ultimul. Serverele care nu răspund vor fi ignorate temporar pÃĸnă revin online.", - "manage_concurrency": "Gestionarea Simultaneității", + "manage_concurrency": "Gestionarea simultaneității", "manage_log_settings": "Administrați setările jurnalului", "map_dark_style": "Mod ÃŽntunecat", "map_enable_description": "Activați funcțiile hărții", - "map_gps_settings": "Setări Hartă & GPS", + "map_gps_settings": "Setări hartă & GPS", "map_gps_settings_description": "Gestionare setări Hartă & GPS (localizare inversă)", "map_implications": "Caracteristica hărții se bazează pe un serviciu extern de planșe (tiles.immich.cloud)", "map_light_style": "Mod deschis", @@ -158,16 +181,30 @@ "metadata_extraction_job_description": "Extragere informații metadate din fiecare fișier cum ar fi localizare GPS, fețe și rezoluție,", "metadata_faces_import_setting": "Activare import fețe", "metadata_faces_import_setting_description": "Importă fețe din datele EXIF ale imaginii și din fișiere tip \"sidecar\"", - "metadata_settings": "Setări Metadate", + "metadata_settings": "Setări metadate", "metadata_settings_description": "Gestionează setările pentru metadate", "migration_job": "Migrare", "migration_job_description": "Migrați miniaturile pentru elemente și fețe la cea mai recentă structură de foldere", + "nightly_tasks_cluster_faces_setting_description": "Rulează recunoașterea facială pe fețele noi recunoscute", + "nightly_tasks_cluster_new_faces_setting": "Grupează fetele noi", + "nightly_tasks_database_cleanup_setting": "Sarcini curățare baze de date", + "nightly_tasks_database_cleanup_setting_description": "Curată date vechi, expirate din baza de date", + "nightly_tasks_generate_memories_setting": "Generare memorii", + "nightly_tasks_generate_memories_setting_description": "Creează amintiri noi din resurse", + "nightly_tasks_missing_thumbnails_setting": "Generează miniaturi lipsă", + "nightly_tasks_missing_thumbnails_setting_description": "Pune ÃŽn coadă elementele fără miniaturi pentru generarea miniaturilor", + "nightly_tasks_settings": "Setări pentru sarcinile nocturne", + "nightly_tasks_settings_description": "Gestionați sarcinile nocturne", + "nightly_tasks_start_time_setting": "Ora de ÃŽncepere", + "nightly_tasks_start_time_setting_description": "Ora la care serverul ÃŽncepe să execute sarcinile nocturne", + "nightly_tasks_sync_quota_usage_setting": "Utilizarea cotei de sincronizare", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizați cota de stocare a utilizatorului, ÃŽn funcție de utilizarea actuală", "no_paths_added": "Nicio cale adăugată", "no_pattern_added": "Niciun tipar adăugat", "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele ÃŽncărcate anterior, executați", "note_cannot_be_changed_later": "NOTĂ: Nu se va mai putea modifica ulterior!", "notification_email_from_address": "De la adresa", - "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”", + "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”. Asigură-te că folosești o adresă de la care ai permisiunea de a trimite e-mailuri.", "notification_email_host_description": "Adresa serverului de email (ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ingnoră erorile de certificat", "notification_email_ignore_certificate_errors_description": "Ignoră erorile de validare a certificatului TLS (nerecomandat)", @@ -187,10 +224,13 @@ "oauth_auto_register": "Auto ÃŽnregistrare", "oauth_auto_register_description": "Înregistrează automat utilizatori noi după autentificarea cu OAuth", "oauth_button_text": "Text buton", + "oauth_client_secret_description": "Necesar dacă PKCE (Proof Key for Code Exchange) nu este suportat de furnizorul OAuth", "oauth_enable_description": "Autentifică-te cu OAuth", "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override_description": "Activați atunci cÃĸnd furnizorul OAuth nu permite un URI mobil, precum ''{callback}''", + "oauth_role_claim": "Revendicare de rol", + "oauth_role_claim_description": "Acordă automat acces de administrator ÃŽn funcție de prezența acestei revendicări. Revendicarea poate avea fie 'utilizator', fie 'administrator'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestionați setările de conectare OAuth", "oauth_settings_more_details": "Pentru mai multe detalii despre aceastĮŽ funcționalitate, verificĮŽ documentația.", @@ -199,7 +239,9 @@ "oauth_storage_quota_claim": "Revendicare spațiu de stocare", "oauth_storage_quota_claim_description": "Setează automat spațiul de stocare al utilizatorului la valoarea acestei cereri.", "oauth_storage_quota_default": "Cota implicită a spațiului de stocare (GiB)", - "oauth_storage_quota_default_description": "Spațiul ÃŽn GiB ce urmează a fi utilizat atunci cÃĸnd nu este furnizată nicio solicitare (introduceți 0 pentru spațiu nelimitat).", + "oauth_storage_quota_default_description": "Spațiul ÃŽn GiB ce urmează a fi utilizat atunci cÃĸnd nu este furnizată nicio solicitare.", + "oauth_timeout": "Solicitarea a expirat", + "oauth_timeout_description": "Timp de expirare pentru solicitări ÃŽn milisecunde", "password_enable_description": "Autentificare cu email și parolĮŽ", "password_settings": "Autentificare cu ParolĮŽ", "password_settings_description": "GestioneazĮŽ setĮŽrile de autentificare cu parola", @@ -217,7 +259,7 @@ "send_welcome_email": "Trimite email de bun-venit", "server_external_domain_settings": "Domeniu extern", "server_external_domain_settings_description": "Domeniu pentru distribuire publicĮŽ a scurtĮŽturilor, incluzÃĸnd http(s)://", - "server_public_users": "Utilizatori Publici", + "server_public_users": "Utilizatori publici", "server_public_users_description": "Toți utilizatorii (nume și e-mail) sunt listați atunci cÃĸnd adăugați un utilizator la albumele partajate. CÃĸnd este dezactivată, lista de utilizatori va fi disponibilă numai pentru utilizatorii admin.", "server_settings": "SetĮŽri Server", "server_settings_description": "GestioneazĮŽ setĮŽrile serverului", @@ -237,8 +279,9 @@ "storage_template_migration_info": "Șablonul de stocare va converti extensiile in litere mici. Modificările șablonului se vor aplica doar materialelor noi. Pentru a aplica retroactiv șablonul la materialele ÃŽncărcate anterior, rulați {job}.", "storage_template_migration_job": "Sarcină Migrare Șablon Stocare", "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", + "storage_template_onboarding_description_v2": "CÃĸnd este activată, această funcție va organiza automat fișierele pe baza șablonului definit de către utilizator. Pentru mai multe informații, accesează documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", - "storage_template_settings": "Șablon Stocare", + "storage_template_settings": "Șablon stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele ÃŽncărcate", "storage_template_user_label": "{label} este eticheta de stocare a utilizatorului", "system_settings": "SetĮŽri de Sistem", @@ -251,12 +294,12 @@ "template_email_update_album": "Actualizați Șablonul de Album", "template_email_welcome": "Șablon de e-mail de bun venit", "template_settings": "Șabloane de Notificare", - "template_settings_description": "Gestionați șabloanele personalizate pentru notificări.", + "template_settings_description": "Gestionați șabloanele personalizate pentru notificări", "theme_custom_css_settings": "CSS personalizat", "theme_custom_css_settings_description": "Foile de stil ÃŽn cascadă (CSS) permit personalizarea designului Immich.", - "theme_settings": "Setări Temă", + "theme_settings": "Setări temă", "theme_settings_description": "Gestionează personalizarea interfeței web Immich", - "thumbnail_generation_job": "Generare Miniaturi", + "thumbnail_generation_job": "Generare miniaturi", "thumbnail_generation_job_description": "Generează miniaturi mari, mici și estompate pentru fiecare resursă, precum și miniaturi pentru fiecare persoană", "transcoding_acceleration_api": "API de accelerare", "transcoding_acceleration_api_description": "API-ul care va interacționa cu dispozitivul tău pentru a accelera transcodarea. Această setare este 'cel mai bun efort': va reveni la transcodarea software ÃŽn caz de eșec. VP9 poate funcționa sau nu, ÃŽn funcție de hardware-ul tău.", @@ -282,8 +325,8 @@ "transcoding_disabled_description": "Nu transcodifică niciun videoclip; acest lucru poate afecta redarea pe anumite dispozitive", "transcoding_encoding_options": "Opțiuni codificare", "transcoding_encoding_options_description": "Setează codecuri , calitatea, rezoluția și alte opțiuni pentru videoclipuri codificare", - "transcoding_hardware_acceleration": "Accelerare Hardware", - "transcoding_hardware_acceleration_description": "Experimental; mult mai rapid, dar va avea o calitate mai scăzută la același bitrate", + "transcoding_hardware_acceleration": "Accelerare hardware", + "transcoding_hardware_acceleration_description": "Experimental: transcodare mai rapidă, dar poate reduce calitatea la aceeași rată de biți", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă ÃŽn loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", "transcoding_max_b_frames": "Număr maxim de cadre B", @@ -302,7 +345,7 @@ "transcoding_reference_frames": "Cadre de referință", "transcoding_reference_frames_description": "Numărul de cadre de referință atunci cÃĸnd se comprimă un cadru dat. Valorile mai mari ÃŽmbunătățesc eficiența compresiei, dar ÃŽncetinesc codarea. 0 setează această valoare automat.", "transcoding_required_description": "Numai videoclipuri care nu sunt ÃŽntr-un format acceptat", - "transcoding_settings": "Setări de Transcodare Video", + "transcoding_settings": "Setări de transcodare video", "transcoding_settings_description": "Gestionează care videoclipuri să transcodam și cum să le procesam", "transcoding_target_resolution": "Rezoluția țintă", "transcoding_target_resolution_description": "Rezoluțiile mai mari pot păstra mai multe detalii, dar necesită mai mult timp pentru codare, au dimensiuni mai mari ale fișierelor și pot reduce răspunsul aplicației.", @@ -323,36 +366,44 @@ "trash_number_of_days_description": "NumĮŽr de zile pentru pĮŽstrarea fișierelor ÃŽn coșul de gunoi pÃĸnĮŽ la ștergerea permanentĮŽ", "trash_settings": "SetĮŽri Coș de Gunoi", "trash_settings_description": "GestioneazĮŽ setĮŽrile coșului de gunoi", + "unlink_all_oauth_accounts": "Deconectează toate conturile OAuth", + "unlink_all_oauth_accounts_description": "Nu uita să deconectezi toate conturile OAuth ÃŽnainte de a migra la un nou furnizor.", + "unlink_all_oauth_accounts_prompt": "Ești sigur că vrei să deconectezi toate conturile OAuth? Aceasta va reseta ID-ul OAuth pentru fiecare utilizator și nu poate fi anulată.", "user_cleanup_job": "Curățare utilizator", "user_delete_delay": "Contul și resursele utilizatorului {user} vor fi programate pentru ștergere permanentă ÃŽn {delay, plural, one {# zi} other {# zile}}.", "user_delete_delay_settings": "ÎntÃĸrziere la ștergere", "user_delete_delay_settings_description": "Numărul de zile după eliminare pÃĸnă la ștergerea permanentă a contului și a resurselor unui utilizator. Procesul de ștergere a utilizatorului rulează la miezul nopții pentru a verifica utilizatorii care sunt pregătiți pentru ștergere. Modificările aduse acestei setări vor fi evaluate la următoarea execuție.", "user_delete_immediately": "Contul și resursele utilizatorului {user} vor fi puse ÃŽn coadă pentru ștergere permanentă imediat.", "user_delete_immediately_checkbox": "Pune utilizatorul și resursele ÃŽn coadă pentru ștergere imediată", - "user_management": "Gestionarea Utilizatorilor", + "user_details": "Detalii utilizator", + "user_management": "Gestionarea utilizatorilor", "user_password_has_been_reset": "Parola utilizatorului a fost resetată:", "user_password_reset_description": "Vă rugăm să furnizați utilizatorului parola temporară și să ÃŽi informați că va trebui să o schimbe la următoarea autentificare.", "user_restore_description": "Contul utilizatorului {user} va fi restaurat.", "user_restore_scheduled_removal": "Restaurare utilizator - ștergere programată pe {date, date, long}", - "user_settings": "SetĮŽri Utilizator", + "user_settings": "SetĮŽri utilizator", "user_settings_description": "GestioneazĮŽ setĮŽrile utilizatorului", "user_successfully_removed": "Utilizatorul {email} a fost eliminat cu succes.", "version_check_enabled_description": "Activează verificarea versiunii", "version_check_implications": "Funcția de verificare a versiunii se bazează pe comunicarea periodică cu github.com", - "version_check_settings": "Verificare Versiune", + "version_check_settings": "Verificare versiune", "version_check_settings_description": "ActiveazĮŽ/dezactiveazĮŽ notificarea unei noi versiuni", "video_conversion_job": "Transcodați videoclipuri", "video_conversion_job_description": "Transcodați videoclipurile pentru o compatibilitate mai mare cu browserele și dispozitivele" }, - "admin_email": "E-mail Administrator", - "admin_password": "Parolă Administrator", + "admin_email": "E-mail administrator", + "admin_password": "Parolă administrator", "administration": "Administrare", "advanced": "Avansat", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media ÃŽn timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă ÃŽntÃĸmpinați probleme cu aplicația la detectarea tuturor albumelor.", "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: {level}", - "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_subtitle": "Unele dispozitive ÃŽncarcă extrem de lent miniaturile din resursele locale. Activați această setare pentru a ÃŽncărca imagini la distanță.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", + "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", + "advanced_settings_proxy_headers_title": "Antete Proxy", + "advanced_settings_readonly_mode_subtitle": "Activează modul doar-citire, ÃŽn care fotografiile pot fi doar vizualizate, iar acțiuni precum selectarea mai multor imagini, partajarea, redarea pe alt dispozitiv sau ștergerea sunt dezactivate. Activează/Dezactivează modul doar-citire din avatarul utilizatorului de pe ecranul principal", + "advanced_settings_readonly_mode_title": "Mod doar-citire", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", "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", @@ -368,6 +419,7 @@ "album_cover_updated": "Coperta albumului a fost actualizată", "album_delete_confirmation": "Ești sigur că vrei să ștergi albumul {album}?", "album_delete_confirmation_description": "Dacă acest album este partajat, alți utilizatori nu vor mai putea accesa.", + "album_deleted": "Album șters", "album_info_card_backup_album_excluded": "EXCLUSE", "album_info_card_backup_album_included": "INCLUSE", "album_info_updated": "Informații album actualizate", @@ -377,11 +429,14 @@ "album_options": "Opțiuni album", "album_remove_user": "Eliminare utilizator?", "album_remove_user_confirmation": "Ești sigur că dorești eliminarea {user}?", + "album_search_not_found": "Nu s-au găsit albume care să corespundă căutării dumneavoastră", "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_summary": "Rezumat album", "album_updated": "Album actualizat", "album_updated_setting_description": "Primiți o notificare prin e-mail cÃĸnd un album partajat are elemente noi", "album_user_left": "A părăsit {album}", "album_user_removed": "{user} eliminat", + "album_viewer_appbar_delete_confirm": "Ești sigur că vrei să ștergi acest album din contul tău?", "album_viewer_appbar_share_err_delete": "Ștergere album eșuată", "album_viewer_appbar_share_err_leave": "Părăsire album eșuată", "album_viewer_appbar_share_err_remove": "Probleme la ștergerea resurselor din album", @@ -392,6 +447,10 @@ "album_with_link_access": "Permite oricui cu link-ul să vadă fotografiile și persoanele din acest album.", "albums": "Albume", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albume}}", + "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", + "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", + "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", + "albums_on_device_count": "{count} albume pe dispozitiv", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -409,14 +468,17 @@ "app_bar_signout_dialog_content": "Ești sigur că vrei să te deconectezi?", "app_bar_signout_dialog_ok": "Da", "app_bar_signout_dialog_title": "Deconectare", - "app_settings": "Setări Aplicație", + "app_settings": "Setări aplicație", "appears_in": "Apare ÃŽn", + "apply_count": "Aplică ({count, number})", "archive": "Arhivă", + "archive_action_prompt": "{count} adăugate la Arhivă", "archive_or_unarchive_photo": "ArhiveazĮŽ sau dezarhiveazĮŽ fotografia", "archive_page_no_archived_assets": "Nu au fost găsite resurse favorite", "archive_page_title": "Arhivă ({count})", "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (ÃŽn GiB)", + "archived": "Arhivat", "archived_count": "{count, plural, other {Arhivat/e#}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", @@ -427,39 +489,67 @@ "asset_description_updated": "Descrierea resursei a fost actualizată", "asset_filename_is_offline": "Resursa {filename} este offline", "asset_has_unassigned_faces": "Resursa are fețe neatribuite", + "asset_hashing": "Calculare amprentă digitalăâ€Ļ", + "asset_list_group_by_sub_title": "Grupare după", "asset_list_layout_settings_dynamic_layout_title": "Aspect dinamic", "asset_list_layout_settings_group_automatically": "Automat", "asset_list_layout_settings_group_by": "Grupează resurse după", "asset_list_layout_settings_group_by_month_day": "Lună + zi", + "asset_list_layout_sub_title": "Aspect", "asset_list_settings_subtitle": "Setări format grilă fotografii", "asset_list_settings_title": "Grilă fotografii", "asset_offline": "Resursă Offline", "asset_offline_description": "Această resursă externă nu mai este găsită pe disc. Contactează te rog administratorul tău Immich pentru ajutor.", + "asset_restored_successfully": "Date restaurate cu succes", "asset_skipped": "Sărit", "asset_skipped_in_trash": "În coșul de gunoi", + "asset_trashed": "Resursă ștearsă", + "asset_troubleshoot": "Depanare resursă", "asset_uploaded": "Încărcat", "asset_uploading": "Se incarcăâ€Ļ", + "asset_viewer_settings_subtitle": "Gestionați setările de vizualizare a galeriei", + "asset_viewer_settings_title": "Vizualizator resurse", "assets": "Resurse", "assets_added_count": "Adăugat {count, plural, one {# resursă} other {# resurse}}", "assets_added_to_album_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} ÃŽn album", - "assets_added_to_name_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} ÃŽn {hasName, select, true {{name}} other {albumul nou}}", + "assets_added_to_albums_count": "Au fost adăugate {assetTotal, plural, one {# element} other {# elemente}} la {albumTotal, plural, one {# album} other {# albume}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} nu pot fi adăugate ÃŽn album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Elementul} other {Elementele}} nu poate fi adăugat la niciunul dintre albume", "assets_count": "{count, plural, one {# resursă} other {# resurse}}", + "assets_deleted_permanently": "{count} poză/poze ștearsă/șterse permanent", + "assets_deleted_permanently_from_server": "{count} poză/poze ștearsă/șterse permanent din serverul Immich", + "assets_downloaded_failed": "{count, plural, one {S-a descărcat # fișier – {error} fișier eșuat} other {S-au descărcat # fișiere – {error} fișiere eșuate}}", + "assets_downloaded_successfully": "{count, plural, one {S-a descărcat cu succes # fișier} other {S-au descărcat cu succes # fișiere}}", "assets_moved_to_trash_count": "Am mutat {count, plural, one {# resursă} other {# resurse}} ÃŽn coșul de gunoi", "assets_permanently_deleted_count": "Șters permanent {count, plural, one {# resursă} other {# resurse}}", "assets_removed_count": "Eliminat {count, plural, one {# resursă} other {# resurse}}", + "assets_removed_permanently_from_device": "{count} resursă(e) eliminate permanent din dispozitivul dvs.", "assets_restore_confirmation": "Ești sigur că vrei să restaurezi toate resursele tale din coșul de gunoi? Nu poți anula această acțiune! Ține minte că resursele offline nu se restaurează astfel.", "assets_restored_count": "Restaurat {count, plural, one {# resursă} other {# resurse}}", + "assets_restored_successfully": "{count} resursă(e) restaurate cu succes", + "assets_trashed": "{count} resursă(e) eliminate", "assets_trashed_count": "Mutat ÃŽn coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", + "assets_trashed_from_server": "{count} resursă(e) eliminate de pe serverul Immich", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} deja parte din albume", "authorized_devices": "Dispozitive Autorizate", + "automatic_endpoint_switching_subtitle": "Conectează-te local prin rețeaua Wi‐Fi configurată cÃĸnd este valabilă și prin rețele alternative ÃŽn caz contrar", + "automatic_endpoint_switching_title": "Alternare URL automată", + "autoplay_slideshow": "Derulare slideshow automat", "back": "Înapoi", "back_close_deselect": "Înapoi, ÃŽnchidere sau deselectare", + "background_backup_running_error": "Procesul de backup ÃŽn fundal este activ, nu se poate porni backup manual", + "background_location_permission": "Permisiune locație ÃŽn fundal", + "background_location_permission_content": "Pentru a putea schimba rețeaua activă ÃŽn fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", + "background_options": "Opțiuni de fundal", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albume ÃŽn dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi ÃŽmprăștiate ÃŽn mai multe albume. Prin urmare, albumele pot fi incluse sau excluse ÃŽn timpul procesului de backup.", "backup_album_selection_page_select_albums": "Selectează albume", "backup_album_selection_page_selection_info": "Informații selecție", "backup_album_selection_page_total_assets": "Total resurse unice", + "backup_albums_sync": "Sincronizarea albumelor de backup", "backup_all": "Toate", "backup_background_service_backup_failed_message": "Eșuare backup resurse. ReÃŽncercareâ€Ļ", "backup_background_service_connection_failed_message": "Conectare la server eșuată. ReÃŽncercareâ€Ļ", @@ -474,6 +564,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Mergi la setări", "backup_controller_page_background_battery_info_link": "Arată-mi cum", "backup_controller_page_background_battery_info_message": "Pentru cea mai bună experiență a backup-ului ÃŽn fundal, te rugăm să dezactivezi orice optimizare pentru baterie care restricționează activitatea ÃŽn fundal pentru Immich.\n\nDeoarece aceasta este specifică fiecărui dispozitiv, te rugăm verifică informațiile necesare tipului tău de dispozitiv.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizări baterie", "backup_controller_page_background_charging": "Doar ÃŽn timpul ÃŽncărcării", "backup_controller_page_background_configure_error": "Configurare serviciu ÃŽn fundal eșuată", @@ -483,7 +574,8 @@ "backup_controller_page_background_is_on": "Backup-ul automat ÃŽn fundal este activat", "backup_controller_page_background_turn_off": "Dezactivează serviciul ÃŽn fundal", "backup_controller_page_background_turn_on": "Activează serviciul ÃŽn fundal", - "backup_controller_page_background_wifi": "Doar conectat la WiFi", + "backup_controller_page_background_wifi": "Numai prin Wi-Fi", + "backup_controller_page_backup": "Backup", "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: {date}", @@ -491,12 +583,13 @@ "backup_controller_page_excluded": "Exclus(e): ", "backup_controller_page_failed": "Eșuate ({count})", "backup_controller_page_filename": "Nume fișier: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informații backup", "backup_controller_page_none_selected": "Nici o selecție", "backup_controller_page_remainder": "Rămas(e)", "backup_controller_page_remainder_sub": "Fotografii și videoclipuri din selecție rămase pentru backup", "backup_controller_page_server_storage": "Stocare server", - "backup_controller_page_start_backup": "Începe backup", + "backup_controller_page_start_backup": "Începe copia de rezervă", "backup_controller_page_status_off": "Backup-ul automat ÃŽn prim-plan este oprit", "backup_controller_page_status_on": "Backup-ul automat ÃŽn prim-plan este pornit", "backup_controller_page_storage_format": "{used} din {total} folosit", @@ -504,18 +597,27 @@ "backup_controller_page_total_sub": "Toate fotografiile și videoclipurile unice din albumele selectate", "backup_controller_page_turn_off": "Dezactivează backup-ul ÃŽn prim-plan", "backup_controller_page_turn_on": "Activează backup-ul ÃŽn prim-plan", - "backup_controller_page_uploading_file_info": "Încărcare informații fișier", + "backup_controller_page_uploading_file_info": "Informații ÃŽncărcare fișier", "backup_err_only_album": "Nu poți șterge singurul album", + "backup_error_sync_failed": "Sincronizarea a eșuat. Nu se poate procesa copia de rezervă.", "backup_info_card_assets": "resurse", "backup_manual_cancelled": "Anulat", "backup_manual_in_progress": "Încărcarea este deja ÃŽn curs. Încearcă din nou mai tÃĸrziu", "backup_manual_success": "Succes", "backup_manual_title": "Status ÃŽncărcare", + "backup_options": "Opțiuni copie de rezervă", + "backup_options_page_title": "Opțiuni copie de rezervă", + "backup_setting_subtitle": "Schimbă opțiuni pentru backup ÃŽn prim-plan și ÃŽn fundal", + "backup_settings_subtitle": "Gestionați setările de ÃŽncărcare", "backward": "În sens invers", + "biometric_auth_enabled": "Autentificare biometrică activată", + "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", + "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", + "biometric_not_available": "Autentificarea biometrică nu este disponibilă pe acest dispozitiv", "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vÃĸrsta acestei persoane la momentul realizării fotografiei.", "blurred_background": "Fundal neclar", - "bugs_and_feature_requests": "Erori și Solicitări de Caracteristici", + "bugs_and_feature_requests": "Erori și solicitări de caracteristici", "build": "Versiunea", "build_image": "Versiune Imagine", "bulk_delete_duplicates_confirmation": "Ești sigur că vrei să ștergi ÃŽn masă {count, plural, one {# resursă duplicată} other {# resurse duplicate}}? Aceasta va păstra cea mai mare resursă din fiecare grup și va șterge permanent toate celelalte duplicate. Nu poți anula această acțiune!", @@ -525,7 +627,7 @@ "cache_settings_clear_cache_button": "Șterge cache", "cache_settings_clear_cache_button_title": "Șterge memoria cache a aplicatiei. Performanța aplicației va fi semnificativ afectată pÃĸnă cÃĸnd va fi reconstruită.", "cache_settings_duplicated_assets_clear_button": "ȘTERGE", - "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri care sunt pe lista neagră a aplicației", + "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri ignorate ÃŽn lista aplicației", "cache_settings_duplicated_assets_title": "Resurse duplicate ({count})", "cache_settings_statistics_album": "Miniaturi pentru librării", "cache_settings_statistics_full": "Fotografii complete", @@ -541,14 +643,20 @@ "camera_model": "Model cameră", "cancel": "Anulați", "cancel_search": "Anulați căutarea", + "canceled": "Anulat", + "canceling": "În curs de anulare", "cannot_merge_people": "Nu se pot ÃŽmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", + "cast": "Partajare", + "cast_description": "Configurați destinațiile de difuzare disponibile", "change_date": "Schimbați data", + "change_description": "Schimbă descrierea", + "change_display_order": "Schimbați ordinea de afișare", "change_expiration_time": "Schimbați data expirare", "change_location": "Schimbați locația", "change_name": "Schimbați nume", - "change_name_successfully": "Schimbare nume cu succes", + "change_name_successfully": "Schimbare a numelui făcută cu succes", "change_password": "Schimbați parolă", "change_password_description": "Aceasta este fie prima dată cÃĸnd te conectezi ÃŽn sistem, fie s-a făcut o solicitare pentru a schimba parola ta. Te rog să introduci noua parolă mai jos.", "change_password_form_confirm_password": "Confirmă parola", @@ -556,16 +664,31 @@ "change_password_form_new_password": "Parolă nouă", "change_password_form_password_mismatch": "Parolele nu se potrivesc", "change_password_form_reenter_new_password": "Reintrodu noua parolă", + "change_pin_code": "Schimbă codul PIN", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", + "charging": "Încărcare", + "charging_requirement_mobile_backup": "Pentru copia de rezervă ÃŽn fundal, dispozitivul trebuie să fie ÃŽn curs de ÃŽncărcare", + "check_corrupt_asset_backup": "Verifică copii de rezervă a resurselor corupte", + "check_corrupt_asset_backup_button": "Efectuează verificarea", + "check_corrupt_asset_backup_description": "Rulează această verificare doar prin Wi-Fi și doar după ce toate resursele au fost salvate ÃŽn copia de rezerva. Procedura poate dura cÃĸteva minute.", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", "clear": "Curățați", "clear_all": "Curățați tot", "clear_all_recent_searches": "Curățați toate căutările recente", + "clear_file_cache": "Ștergeți memoria cache a fișierelor", "clear_message": "Ștergeți mesajul", "clear_value": "Ștergeți valoarea", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Introdu Parola", + "client_cert_import": "Importă", + "client_cert_import_success_msg": "Certificatul de client este importat", + "client_cert_invalid_msg": "Fisier cu certificat invalid sau parola este greșită", + "client_cert_remove_msg": "Certificatul de client este șters", + "client_cert_subtitle": "Acceptă doar formatul PKCS12 (.p12, .pfx). Importul/ștergerea certificatului este disponibil(ă) doar ÃŽnainte de autentificare", + "client_cert_title": "Certificat SSL pentru client", "clockwise": "În sensul acelor de ceas", "close": "Închideți", "collapse": "RestrÃĸngeți", @@ -578,19 +701,27 @@ "comments_are_disabled": "Comentariile sunt dezactivate", "common_create_new_album": "Creează album nou", "common_server_error": "Te rugăm să verifici conexiunea la rețea, asigura-te că server-ul este accesibil și că versiunile aplicației/server-ului sunt compatibile.", + "completed": "Finalizat", "confirm": "Confirmați", "confirm_admin_password": "Confirmați Parola de Administrator", "confirm_delete_face": "Ești sigur ca vrei sa ștergi {name} din activ?", "confirm_delete_shared_link": "Sunteți sigur că doriți să ștergeți acest link partajat?", "confirm_keep_this_delete_others": "Toate celelalte active din stivă vor fi șterse, cu excepția acestui material. Sunteți sigur că doriți să continuați?", + "confirm_new_pin_code": "Confirmă noul cod PIN", "confirm_password": "Confirmați parola", + "confirm_tag_face": "Vrei să etichetezi această față ca {name}?", + "confirm_tag_face_unnamed": "Vrei să etichetezi această față?", + "connected_device": "Dispozitiv conectat", + "connected_to": "Conectat la", "contain": "Încadrează", + "context": "Context", "continue": "Continuați", "control_bottom_app_bar_create_new_album": "Creează album nou", "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", - "control_bottom_app_bar_edit_time": "Editează Data și Ora", + "control_bottom_app_bar_edit_time": "Editează data și ora", + "control_bottom_app_bar_share_link": "Partajează linkul", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută ÃŽn coș", "copied_image_to_clipboard": "Imagine copiată ÃŽn clipboard.", @@ -612,26 +743,39 @@ "create_link": "Creează link", "create_link_to_share": "Creează link pentru a distribui", "create_link_to_share_description": "Permiteți oricui are link-ul să vadă fotografia (fotografiile) selectată(e)", + "create_new": "CREARE NOUĂ", "create_new_person": "Creați o persoană nouă", "create_new_person_hint": "Atribuiți resursele selectate unei persoane noi", "create_new_user": "Creează utilizator nou", "create_shared_album_page_share_add_assets": "ADAUGĂ RESURSE", "create_shared_album_page_share_select_photos": "Selectează fotografii", + "create_shared_link": "Creați un link partajat", "create_tag": "Creează etichetă", "create_tag_description": "Creează o etichetă nouă. Pentru etichete imbricate, te rog să introduci calea completă a etichetei, inclusiv bare oblice (/).", "create_user": "Creează utilizator", "created": "Creat", + "created_at": "Creat", + "creating_linked_albums": "Crearea albumelor cu link...", + "crop": "Decupează", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", + "current_pin_code": "Codul PIN actual", + "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele ÃŽn funcție de limbă și regiune", + "custom_url": "URL personalizat", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Întunecat", + "dark_theme": "Comută tema ÃŽntunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", + "date_format": "E, LLL d, y â€ĸ h:mm a", "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", + "days": "Zile", "deduplicate_all": "Deduplicați Toate", "deduplication_criteria_1": "Marimea imagini ÃŽn octeți", "deduplication_criteria_2": "Numărul de date EXIF", @@ -640,6 +784,8 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele ÃŽn funcție de regiunea browserului dvs", "delete": "Ștergere", + "delete_action_confirmation_message": "Sigur vrei să ștergi acest element? Această acțiune va muta elementul ÃŽn coșul de gunoi al serverului și te va ÃŽntreba dacă vrei să-l ștergi local", + "delete_action_prompt": "{count} șterse", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -653,9 +799,12 @@ "delete_key": "Ștergere cheie", "delete_library": "Ștergere biblioteca", "delete_link": "Ștergere link", + "delete_local_action_prompt": "{count} șterse local", "delete_local_dialog_ok_backed_up_only": "Șterge doar fișierele pentru care s-a făcut backup", "delete_local_dialog_ok_force": "Șterge oricum", "delete_others": "Ștergeți celelalte", + "delete_permanently": "Șterge permanent", + "delete_permanently_action_prompt": "{count} șterse permanent", "delete_shared_link": "Ștergere link partajat", "delete_shared_link_dialog_title": "Șterge link distribuire", "delete_tag": "Ștergere etichetă", @@ -666,12 +815,14 @@ "description": "Descriere", "description_input_hint_text": "Adaugă descriere...", "description_input_submit_error": "Eroare actualizare descriere, verifică log-urile pentru mai multe detalii", + "deselect_all": "Deselectează toate", "details": "Detalii", "direction": "Direcție", "disabled": "Dezactivat", "disallow_edits": "Interzice modificările", "discord": "Server Discord", "discover": "Descoperiți", + "discovered_devices": "Dispozititve descoperite", "dismiss_all_errors": "Ignorați toate erorile", "dismiss_error": "Ignorați eroarea", "display_options": "Opțiuni de afișare", @@ -682,12 +833,26 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_action_prompt": "Se descarcă {count} elemente", + "download_canceled": "Descărcare anulată", + "download_complete": "Descărcare completă", + "download_enqueue": "Descărcare ÃŽn coadă", + "download_error": "Eroare de descărcare", + "download_failed": "Descărcare eșuată", + "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri ÃŽncorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile ÃŽncorporate ÃŽn fotografiile ÃŽn mișcare ca fișier separat", + "download_notfound": "Descărcare negăsită", + "download_paused": "Descărcarea a fost ÃŽntreruptă", "download_settings": "Descărcați", "download_settings_description": "Gestionați setările legate de descărcarea resurselor", + "download_started": "Descărcarea a ÃŽnceput", + "download_sucess": "Descărcare reușită", + "download_sucess_android": "Fișierul media a fost descărcat ÃŽn DCIM/Immich", + "download_waiting_to_retry": "Se așteaptă o nouă ÃŽncercare", "downloading": "Se descarcă", "downloading_asset_filename": "Se descarcă resursa {filename}", + "downloading_media": "Se descarcă fișierele media", "drop_files_to_upload": "Trageți fișierele aici pentru a le ÃŽncărca", "duplicates": "Duplicate", "duplicates_description": "Rezolvați fiecare grup indicÃĸnd care sunt duplicate, dacă există", @@ -695,8 +860,14 @@ "edit": "Editare", "edit_album": "Editare album", "edit_avatar": "Editare avatar", + "edit_birthday": "Modifică ziua de naștere", "edit_date": "Editare dată", "edit_date_and_time": "Editare dată și oră", + "edit_date_and_time_action_prompt": "{count} data și ora modificării", + "edit_date_and_time_by_offset": "Schimbă data prin decalaj", + "edit_date_and_time_by_offset_interval": "Noul interval de date: {from} - {to}", + "edit_description": "Editează descrierea", + "edit_description_prompt": "Vă rugăm să selectați o descriere nouă:", "edit_exclusion_pattern": "Editarea modelului de excludere", "edit_faces": "Editare fețe", "edit_import_path": "Editare cale de import", @@ -704,6 +875,7 @@ "edit_key": "Tastă de editare", "edit_link": "Editare link", "edit_location": "Editare locație", + "edit_location_action_prompt": "{count} locație(i) modificată(e)", "edit_location_dialog_title": "Locație", "edit_name": "Editare nume", "edit_people": "Editare persoane", @@ -711,19 +883,33 @@ "edit_title": "Editare Titlu", "edit_user": "Editare utilizator", "edited": "Editat", + "editor": "Editor", "editor_close_without_save_prompt": "Schimbările nu vor fi salvate", "editor_close_without_save_title": "Închideți editorul?", "editor_crop_tool_h2_aspect_ratios": "Raporturi de aspect", "editor_crop_tool_h2_rotation": "Rotire", + "email": "Adresă de mail", + "email_notifications": "Notificări e-mail", + "empty_folder": "Acest dosar este gol", "empty_trash": "Goliți coșul de gunoi", "empty_trash_confirmation": "Sunteți sigur că doriți să goliți coșul de gunoi? Acest lucru va elimina definitiv din Immich toate resursele din coșul de gunoi.\nNu puteți anula această acțiune!", "enable": "Permite", + "enable_backup": "Activează backup", + "enable_biometric_auth_description": "Introduceți codul PIN pentru a activa autentificarea biometrică", "enabled": "Activat", "end_date": "Data de ÃŽncheiere", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Pus ÃŽn coadă", + "enter_wifi_name": "Introduceți numele rețelei Wi-Fi", + "enter_your_pin_code": "Introduceți codul PIN", + "enter_your_pin_code_subtitle": "Introduceți codul PIN pentru a accesa folderul blocat", "error": "Eroare", + "error_change_sort_album": "Nu s-a putut modifica ordinea de sortare a albumului", "error_delete_face": "Eroare la ștergerea feței din activ", + "error_getting_places": "Eroare la obținerea locațiilor", "error_loading_image": "Eroare la ÃŽncărcarea imaginii", + "error_loading_partners": "Eroare la ÃŽncărcarea partenerilor: {error}", + "error_saving_image": "Eroare: {error}", + "error_tag_face_bounding_box": "Eroare la etichetarea feței - nu se pot obține coordonatele casetei de delimitare", "error_title": "Eroare - ceva nu a mers", "errors": { "cannot_navigate_next_asset": "Nu se poate naviga către următoarea resursă", @@ -751,15 +937,19 @@ "failed_to_keep_this_delete_others": "Nu s-a putut păstra acest material respectiv nu s-au putut șterge celelalte materiale", "failed_to_load_asset": "Eșec la ÃŽncărcarea resursei", "failed_to_load_assets": "Eșec la ÃŽncărcarea resurselor", + "failed_to_load_notifications": "Nu s-au putut ÃŽncărca notificările", "failed_to_load_people": "Eșec la ÃŽncărcarea persoanelor", "failed_to_remove_product_key": "Eșec la eliminarea cheii de produs", + "failed_to_reset_pin_code": "Nu s-a reușit resetarea codului PIN", "failed_to_stack_assets": "Eșec la combinarea resurselor", "failed_to_unstack_assets": "Eșec la desfășurarea resurselor", + "failed_to_update_notification_status": "Nu s-a putut actualiza starea notificării", "import_path_already_exists": "Această cale de import există deja.", "incorrect_email_or_password": "E-mail sau parolă incorect/ă", "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", "profile_picture_transparent_pixels": "Pozele de profil nu pot avea pixeli transparenți. Te rugăm să mărești imaginea și/sau să o muți.", "quota_higher_than_disk_size": "Ați stabilit o valoare a spațiului de stocare mai mare decÃĸt dimensiunea discului", + "something_went_wrong": "Ceva nu a mers bine", "unable_to_add_album_users": "Imposibil de adăugat utilizatori ÃŽn album", "unable_to_add_assets_to_shared_link": "Imposibil de adăugat resurse la link-ul partajat", "unable_to_add_comment": "Imposibil de adăugat comentariu", @@ -771,6 +961,7 @@ "unable_to_archive_unarchive": "Nu se poate {archived, select, true {arhiva} other {dezarhiva}}", "unable_to_change_album_user_role": "Nu se poate schimba rolul utilizatorului de album", "unable_to_change_date": "Imposibil de schimbat data", + "unable_to_change_description": "Nu se poate schimba descrierea", "unable_to_change_favorite": "Nu se pot modifica favoritele pentru resursa", "unable_to_change_location": "Imposibil de schimbat locația", "unable_to_change_password": "Imposibil de schimbat parola", @@ -814,6 +1005,7 @@ "unable_to_remove_partner": "Imposibil de eliminat partenerul", "unable_to_remove_reaction": "Nu se poate elimina reacția", "unable_to_reset_password": "Imposibil de resetat parola", + "unable_to_reset_pin_code": "Nu se poate reseta codul PIN", "unable_to_resolve_duplicate": "Nu se poate rezolva duplicatul", "unable_to_restore_assets": "Nu se pot restaura resursele", "unable_to_restore_trash": "Nu se poate restaura coșul de gunoi", @@ -843,14 +1035,17 @@ }, "exif": "Format comutabil pentru fișiere imagine", "exif_bottom_sheet_description": "Adaugă Descriere...", + "exif_bottom_sheet_description_error": "Eroare la actualizarea descrierii", "exif_bottom_sheet_details": "DETALII", "exif_bottom_sheet_location": "LOCAȚIE", "exif_bottom_sheet_people": "PERSOANE", + "exif_bottom_sheet_person_add_person": "Adăugați nume", "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_subtitle": "Folosește pe propria răspundere!", + "experimental_settings_title": "Experimental", "expire_after": "Expiră după", "expired": "Expirat", "expires_date": "Expiră la {date}", @@ -858,42 +1053,74 @@ "explorer": "Explorator", "export": "Exportare", "export_as_json": "Exportare ca JSON", + "export_database": "Exportați baza de date", + "export_database_description": "Exportați baza de date SQLite", "extension": "Extensie", "external": "Extern", - "external_libraries": "Biblioteci Externe", - "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_libraries": "Biblioteci externe", + "external_network": "Rețea externă", + "external_network_sheet_info": "CÃĸnd nu se află ÃŽn rețeaua Wi-Fi preferată, aplicația se va conecta la server prin prima dintre adresele URL de mai jos pe care o poate accesa, ÃŽncepÃĸnd de sus ÃŽn jos", "face_unassigned": "Nealocat", + "failed": "Eșuat", + "failed_to_authenticate": "Autentificarea nu a reușit", "failed_to_load_assets": "Nu s-au ÃŽncărcat activele", + "failed_to_load_folder": "Nu s-a putut ÃŽncărca folderul", "favorite": "Favorit", + "favorite_action_prompt": "{count} adăugate la Favorite", "favorite_or_unfavorite_photo": "Fotografie preferată sau nepreferată", "favorites": "Favorite", "favorites_page_no_favorites": "Nu au fost găsite resurse favorite", "feature_photo_updated": "Fotografie caracteristică actualizată", "features": "Caracteristici", + "features_in_development": "Funcții ÃŽn dezvoltare", "features_setting_description": "Gestionați funcțiile aplicației", "file_name": "Nume de fișier", "file_name_or_extension": "Numele sau extensia fișierului", "filename": "Numele fișierului", "filetype": "Tipul fișierului", + "filter": "Filtre", "filter_people": "Filtrați persoanele", "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", + "first": "Primul", "fix_incorrect_match": "Remediați potrivirea incorectă", + "folder": "Dosar", + "folder_not_found": "Dosar negăsit", "folders": "Foldere", "folders_feature_description": "Răsfoire ÃŽn conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", + "forgot_pin_code_question": "Ai uitat codul PIN?", "forward": "Redirecționare", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Această funcție ÃŽncarcă resurse externe de la Google pentru a funcționa.", + "general": "General", + "geolocation_instruction_location": "Apasă pe o resursă cu coordonate GPS pentru a folosi locația sa, sau selectează direct o locație de pe hartă", "get_help": "Obțineți Ajutor", + "get_wifiname_error": "Nu s-a putut obține numele rețelei Wi-Fi. Asigurați-vă că ați acordat permisiunile necesare și că sunteți conectat la o rețea Wi-Fi", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", + "gps": "GPS", + "gps_missing": "Fără GPS", + "grant_permission": "Acordați permisiunea", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", "group_no": "Fără grupare", "group_owner": "Grupați după proprietar", "group_places_by": "Grupare locuri după...", "group_year": "Grupați după an", + "haptic_feedback_switch": "Activează feedback-ul haptic", + "haptic_feedback_title": "Feedback haptic", "has_quota": "Are spațiu de stocare", + "hash_asset": "Hash-ul resursei", + "hashed_assets": "Resurse hashed", + "hashing": "Generare hash", + "header_settings_add_header_tip": "Adăugați antet", + "header_settings_field_validator_msg": "Valoarea nu poate fi goală", + "header_settings_header_name_input": "Numele antetului", + "header_settings_header_value_input": "Valoarea antetului", + "headers_settings_tile_subtitle": "Definiți header-urile proxy pe care aplicația ar trebui să le trimită cu fiecare solicitare de rețea", + "headers_settings_tile_title": "Header-uri proxy personalizate", "hi_user": "Bună {name} ({email})", "hide_all_people": "Ascundeți toate persoanele", "hide_gallery": "Ascundeți galeria", @@ -913,10 +1140,17 @@ "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_locked_error_local": "Nu se pot muta resursele locale ÃŽn folderul blocat, se omit", + "home_page_locked_error_partner": "Nu se pot muta resursele partenerului ÃŽn folderul blocat, se omit.", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot ÃŽncărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", + "hours": "Ore", + "id": "ID", + "idle": "Inactiv", + "ignore_icloud_photos": "Ignoră fotografiile din iCloud", + "ignore_icloud_photos_description": "Fotografiile stocate pe iCloud nu vor fi ÃŽncărcate pe serverul Immich", "image": "Imagine", "image_alt_text_date": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {imagine}} preluată cu {person1} ÃŽn {date}", @@ -928,6 +1162,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1} și {person2} ÃŽn {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1}, {person2}, și {person3} ÃŽn {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {imagine}} preluată ÃŽn {city}, {country} cu {person1}, {person2}, și {additionalCount, number} alții ÃŽn {date}", + "image_saved_successfully": "Imaginea a fost salvată", + "image_viewer_page_state_provider_download_started": "Descărcare ÃŽncepută", "image_viewer_page_state_provider_download_success": "Descărcare cu succes", "image_viewer_page_state_provider_share_error": "Eroare distribuire", "immich_logo": "Logo Immich", @@ -948,8 +1184,15 @@ "night_at_midnight": "În fiecare noapte la miezul nopții", "night_at_twoam": "În fiecare noapte la 2 dimineața" }, + "invalid_date": "Dată invalidă", + "invalid_date_format": "Format de dată invalid", "invite_people": "Invitați Persoane", "invite_to_album": "Invitați ÃŽn album", + "ios_debug_info_fetch_ran_at": "Fetch a funcționat la {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizare {dateTime}", + "ios_debug_info_no_processes_queued": "Niciun proces ÃŽn fundal pus ÃŽn coadă", + "ios_debug_info_no_sync_yet": "Nicio sarcină de sincronizare ÃŽn fundal nu a fost ÃŽncă executată", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces ÃŽn fundal pus ÃŽn coadă} other {{count} procese ÃŽn fundal puse ÃŽn coadă}}", "ios_debug_info_processing_ran_at": "Procesarea a rulat {dateTime}", "items_count": "{count, plural, one {# element} other{# elemente}}", "jobs": "Sarcini", @@ -959,11 +1202,17 @@ "kept_this_deleted_others": "S-a păstrat acest material și s-au șters {count, plural, one {# material} other {# materiale}}", "keyboard_shortcuts": "Comenzi rapide de tastatură", "language": "Limbă", + "language_no_results_subtitle": "Încercați să ajustați termenul de căutare", + "language_no_results_title": "Nu au fost găsite limbi", + "language_search_hint": "Căutați limbi...", "language_setting_description": "Selectați limba preferată", + "large_files": "Fișiere mari", + "last": "Ultimul", "last_seen": "Văzut ultima dată", "latest_version": "Ultima Versiune", "latitude": "Latitudine", "leave": "Părăsiți", + "leave_album": "Părăsește albumul", "lens_model": "Model obiectiv", "let_others_respond": "Permite altora să răspundă", "level": "Nivel", @@ -975,23 +1224,35 @@ "library_page_sort_created": "Data creării", "library_page_sort_last_modified": "Ultima dată modificat", "library_page_sort_title": "Titlu album", + "licenses": "Licențe", "light": "Lumină", + "like": "Îmi place", "like_deleted": "Preferat șters", "link_motion_video": "Link video ÃŽn mișcare", - "link_options": "Opțiuni de link", "link_to_oauth": "Link către OAuth", "linked_oauth_account": "Cont OAuth conectat", "list": "Listă", "loading": "Încărcare", "loading_search_results_failed": "Încărcarea rezultatelor căutării nu a reușit", - "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": "Local", + "local_asset_cast_failed": "Nu se poate converti un element care nu este ÃŽncărcat pe server", + "local_assets": "Asset-uri locale", + "local_media_summary": "Rezumatul fișierelor media locale", + "local_network": "Rețea locală", + "local_network_sheet_info": "Aplicația se va conecta la server prin intermediul acestei adrese URL atunci cÃĸnd utilizează rețeaua Wi-Fi specificată", + "location_permission": "Permisiunea de locație", + "location_permission_content": "Pentru a utiliza funcția de comutare automată, Immich are nevoie de permisiune pentru locația precisă, astfel ÃŽncÃĸt să poată citi numele rețelei Wi-Fi curente", "location_picker_choose_on_map": "Alege pe hartă", "location_picker_latitude_error": "Introdu o latitudine validă", "location_picker_latitude_hint": "Introdu latitudinea aici", "location_picker_longitude_error": "Introdu o longitudine validă", "location_picker_longitude_hint": "Introdu longitudinea aici", + "lock": "Blocare", + "locked_folder": "Dosar blocat", + "log_detail_title": "Detalii jurnal", "log_out": "Deconectare", "log_out_all_devices": "Deconectați-vă de la toate dispozitivele", + "logged_in_as": "Conectat ca {user}", "logged_out_all_devices": "S-au deconectat toate dispozitivele", "logged_out_device": "Dispozitiv deconectat", "login": "Conectare", @@ -1019,6 +1280,7 @@ "login_password_changed_success": "Parola a fost actualizată cu succes", "logout_all_device_confirmation": "Sigur doriți să deconectați toate dispozitivele?", "logout_this_device_confirmation": "Sigur doriți să deconectați acest dispozitiv?", + "logs": "Jurnale", "longitude": "Longitudine", "look": "Examinare", "loop_videos": "Buclă videoclipuri", @@ -1026,6 +1288,7 @@ "main_branch_warning": "Utilizați o versiune de dezvoltare; vă recomandăm insistent să utilizați o versiune de lansare!", "main_menu": "Meniu principal", "make": "Face", + "manage_geolocation": "Gestionați locația", "manage_shared_links": "Administrați link-urile distribuite", "manage_sharing_with_partners": "Gestionați partajarea cu partenerii", "manage_the_app_settings": "Gestionați setările aplicației", @@ -1034,8 +1297,7 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bound": "{count} fotografie", - "map_assets_in_bounds": "{count} fotografii", + "map_assets_in_bounds": "{count, plural, =0 {Nu există fotografii ÃŽn această zonă} one {# fotografie} other {# fotografii}}", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1043,7 +1305,6 @@ "map_location_service_disabled_title": "Serviciul de localizare este dezactivat", "map_marker_for_images": "Marcator de hartă pentru imaginile realizate ÃŽn {city}, {country}", "map_marker_with_image": "Marcator de hartă cu imagine", - "map_no_assets_in_bounds": "Nici o fotografie ÃŽn acestă zonă", "map_no_location_permission_content": "Permisiunea de localizare este necesară pentru a afișa resursele din locația actuală. Dorești să o activezi acum?", "map_no_location_permission_title": "Permisiunea de localizare este dezactivată", "map_settings": "Setările hărții", @@ -1054,13 +1315,22 @@ "map_settings_date_range_option_years": "Ultimii {years} ani", "map_settings_dialog_title": "Setările hărții", "map_settings_include_show_archived": "Include resursele arhivate", + "map_settings_include_show_partners": "Includeți partenerii", "map_settings_only_show_favorites": "Arată doar favorite", "map_settings_theme_settings": "Stilul hărții", "map_zoom_to_see_photos": "Zoom out pentru a vedea fotografii", + "mark_all_as_read": "Marchează toate ca citite", + "mark_as_read": "Marchează ca citit", + "marked_all_as_read": "Marcate toate ca citite", "matches": "Corespunde", + "matching_assets": "Resurse similare", "media_type": "Tip media", "memories": "Amintiri", + "memories_all_caught_up": "Sunteți la zi", + "memories_check_back_tomorrow": "Reveniți mÃĸine pentru mai multe amintiri", "memories_setting_description": "Administrați ce vedeți ÃŽn amintiri", + "memories_start_over": "Începeți de la ÃŽnceput", + "memories_swipe_to_close": "Glisează ÃŽn sus pentru a ÃŽnchide", "memory": "Amintire", "memory_lane_title": "Banda Memoriei {title}", "menu": "Meniu", @@ -1071,9 +1341,20 @@ "merge_people_successfully": "Persoane ÃŽmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", + "minute": "Minut", + "minutes": "Minute", "missing": "Lipsă", + "model": "Model", "month": "Lună", + "monthly_title_text_date_format": "MMMM y", "more": "Mai mult", + "move": "Mută", + "move_off_locked_folder": "Mutați din folderul blocat", + "move_to_lock_folder_action_prompt": "{count} adăugate ÃŽn dosarul blocat", + "move_to_locked_folder": "Mută ÃŽn dosarul blocat", + "move_to_locked_folder_confirmation": "Aceste fotografii și videoclipuri vor fi eliminate din toate albumele și vor putea fi vizualizate doar din dosarul blocat", + "moved_to_archive": "Au fost mutate {count, plural, one {# element} other {# elemente}} ÃŽn arhivă", + "moved_to_library": "Au fost mutate {count, plural, one {# element} other {# elemente}} la bibliotecă", "moved_to_trash": "Mutat ÃŽn coșul de gunoi", "multiselect_grid_edit_date_time_err_read_only": "Nu se poate edita data fișierului(lor) cu permisiuni doar pentru citire, omitere", "multiselect_grid_edit_gps_err_read_only": "Nu se poate edita locația fișierului(lor) cu permisiuni doar pentru citire, omitere", @@ -1081,11 +1362,20 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclĮŽ", + "network_requirement_photos_upload": "Utilizați datele mobile pentru a face copii de rezervă ale fotografiilor", + "network_requirement_videos_upload": "Utilizați datele mobile pentru a face copii de rezervă ale videoclipurilor", + "network_requirements": "Cerințe privind rețeaua", + "network_requirements_updated": "Cerințele rețelei s-au modificat, resetarea cozii copiei de rezervă", + "networking_settings": "Rețele", + "networking_subtitle": "Gestionați setările endpoint-ului serverului", "never": "Niciodată", "new_album": "Album Nou", "new_api_key": "Cheie API nouĮŽ", "new_password": "Parolă nouă", "new_person": "PersoanĮŽ nouĮŽ", + "new_pin_code": "Cod PIN nou", + "new_pin_code_subtitle": "Aceasta este prima dată cÃĸnd accesați folderul blocat. Creați un cod PIN pentru a accesa ÃŽn siguranță această pagină", + "new_timeline": "Noua cronologie", "new_user_created": "Utilizator nou creat", "new_version_available": "VERSIUNE NOUĂ DISPONIBILĂ", "newest_first": "Cel mai nou primul", @@ -1097,19 +1387,32 @@ "no_albums_yet": "Se pare că nu aveți ÃŽncă niciun album.", "no_archived_assets_message": "Arhivați fotografii și videoclipuri pentru a le ascunde din vizualizarea fotografii", "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", + "no_assets_to_show": "Nicio resursă de afișat", + "no_cast_devices_found": "Nu s-au găsit dispozitive de difuzare", + "no_checksum_local": "Nu există checksum – nu se pot prelua resursele locale", + "no_checksum_remote": "Nu există checksum – nu se pot prelua resursele la distanță", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", "no_favorites_message": "Adăugați favorite pentru a găsi rapid cele mai bune fotografii și videoclipuri", "no_libraries_message": "Creați o bibliotecă externă pentru a vă vizualiza fotografiile și videoclipurile", + "no_local_assets_found": "Nicio resursă locală găsită cu acest checksum", + "no_locked_photos_message": "Fotografiile și videoclipurile din folderul blocat sunt ascunse și nu vor apărea atunci cÃĸnd răsfoiți sau căutați ÃŽn bibliotecă.", "no_name": "Fără Nume", + "no_notifications": "Nicio notificare", + "no_people_found": "Nu au fost găsite persoane potrivite căutării", "no_places": "Nu există locuri", + "no_remote_assets_found": "Nicio resursă de la distanță găsită cu acest checksum", "no_results": "Fără rezultate", "no_results_description": "Încercați un sinonim sau un cuvÃĸnt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", + "no_uploads_in_progress": "Nicio ÃŽncărcare ÃŽn curs", + "not_available": "N/A", "not_in_any_album": "Nu există ÃŽn niciun album", + "not_selected": "Neselectat", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele ÃŽncărcate anterior, rulați", "notes": "Note", + "nothing_here_yet": "Nimic aici ÃŽncă", "notification_permission_dialog_content": "Pentru a activa notificările, mergi ÃŽn Setări > Immich și selectează permite.", "notification_permission_list_tile_content": "Acordă permisiunea pentru a activa notificările.", "notification_permission_list_tile_enable_button": "Activează notificările", @@ -1117,13 +1420,21 @@ "notification_toggle_setting_description": "Activați notificările prin email", "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", + "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", + "offline": "Offline", + "offset": "Decalaj", "ok": "Bine", "oldest_first": "Cel mai vechi mai ÃŽntÃĸi", + "on_this_device": "Pe acest dispozitiv", "onboarding": "Integrare", - "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate ÃŽn orice moment din setările de administrare.", + "onboarding_locale_description": "Selectați limba preferată. Puteți schimba această opțiune ulterior ÃŽn setări.", + "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate ÃŽn orice moment din setări.", + "onboarding_server_welcome_description": "Hai să configurăm instanța cu cÃĸteva setări comune.", "onboarding_theme_description": "Alegeți o temă de culoare pentru exemplul dvs. Puteți modifica acest lucru mai tÃĸrziu ÃŽn setări.", + "onboarding_user_welcome_description": "Hai să ÃŽncepem!", "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", @@ -1131,9 +1442,13 @@ "open_the_search_filters": "Deschideți filtrele de căutare", "options": "Opțiuni", "or": "sau", + "organize_into_albums": "Organizați ÃŽn albume", + "organize_into_albums_description": "Pune fotografiile existente ÃŽn albume folosind setările curente de sincronizare", "organize_your_library": "Organizează-ți biblioteca", + "original": "original", "other": "Alte", "other_devices": "Alte dispozitive", + "other_entities": "Alte entități", "other_variables": "Alte variabile", "owned": "Deținut", "owner": "Proprietar", @@ -1141,6 +1456,8 @@ "partner_can_access": "{partner} poate accesa", "partner_can_access_assets": "Toate fotografiile și videoclipurile tale, cu excepția celor din arhivate și sterse", "partner_can_access_location": "Locația ÃŽn care au fost făcute fotografiile dvs", + "partner_list_user_photos": "Fotografiile lui {user}", + "partner_list_view_all": "Vezi toate", "partner_page_empty_message": "Fotografiile tale nu sunt ÃŽncă distribuite cu nici un partener.", "partner_page_no_more_users": "Nu mai sunt utilizatori de adăugat", "partner_page_partner_add_failed": "Eșuare adăugare partener", @@ -1175,6 +1492,8 @@ "permanently_delete_assets_prompt": "Sigur doriți să ștergeți definitiv {count, plural, one {această resursă?} other {aceste # resurse?}} Acest lucru va elimina și {count, plural, one {din ea} other {din ele}} album(e).", "permanently_deleted_asset": "Resursă ștearsă definitiv", "permanently_deleted_assets_count": "S-au șters definitiv {count, plural, one {# resursă} other {# resurse}}", + "permission": "Permisiune", + "permission_empty": "Permisiunea dvs. nu trebuie să fie goală", "permission_onboarding_back": "Înapoi", "permission_onboarding_continue_anyway": "Continuă oricum", "permission_onboarding_get_started": "Începe", @@ -1184,6 +1503,9 @@ "permission_onboarding_permission_limited": "Permisiune limitată. Pentru a permite Immich să facă copii de siguranță și să gestioneze ÃŽntreaga colecție de galerii, acordă permisiuni pentru fotografii și videoclipuri ÃŽn Setări.", "permission_onboarding_request": "Immich necesită permisiunea de a vizualiza fotografiile și videoclipurile tale.", "person": "PersoanĮŽ", + "person_age_months": "{months, plural, one {# month} other {# months}} vechime", + "person_age_year_months": "1 year, {months, plural, one {# month} other {# months}} vechime", + "person_age_years": "{years, plural, other {# years}} vechime", "person_birthdate": "Născut pe {date}", "person_hidden": "{name}{hidden, select, true { (ascuns)} other {}}", "photo_shared_all_users": "Se pare că ți-ai partajat fotografiile tuturor utilizatorilor sau că nu ai niciun utilizator căruia să le distribui.", @@ -1192,6 +1514,10 @@ "photos_count": "{count, plural, one {{count, number} imagine} other{{count, number} imagini}}", "photos_from_previous_years": "Fotografii din anii anteriori", "pick_a_location": "Alegeți o locație", + "pin_code_changed_successfully": "Codul PIN a fost modificat cu succes", + "pin_code_reset_successfully": "Codul PIN a fost resetat cu succes", + "pin_code_setup_successfully": "Configurarea cu succes a unui cod PIN", + "pin_verification": "Verificarea codului PIN", "place": "Loc", "places": "Locații", "places_count": "{count, plural, one {{count, number} Loc} other {{count, number} Locuri}}", @@ -1199,18 +1525,29 @@ "play_memories": "Redare amintiri", "play_motion_photo": "Redare Fotografie ÃŽn Mișcare", "play_or_pause_video": "Redați sau ÃŽntrerupeți videoclipul", + "please_auth_to_access": "Vă rugăm să vă autentificați pentru a accesa", + "port": "Port", + "preferences_settings_subtitle": "Gestionați preferințele aplicației", + "preferences_settings_title": "Preferințe", + "preparing": "Se prepară", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", "previous_memory": "Memoria anterioară", + "previous_or_next_day": "Zi ÃŽnainte/ÃŽnapoi", + "previous_or_next_month": "Lună ÃŽnainte/ÃŽnapoi", "previous_or_next_photo": "Fotografie ÃŽnainte/ÃŽnapoi", + "previous_or_next_year": "An ÃŽnainte/ÃŽnapoi", "primary": "Primar", "privacy": "Confidențialitate", + "profile": "Profil", "profile_drawer_app_logs": "Log-uri", - "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", - "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", + "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", + "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", - "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", + "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Mod doar citire activat. Ține apăsat pe pictograma avatarului utilizatorului pentru a ieși.", + "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", "profile_picture_set": "Poză de profil setată.", @@ -1230,44 +1567,60 @@ "purchase_failed_activation": "Activare eșuată! Vă rugăm să vă verificați e-mailul pentru cheia de produs corectă!", "purchase_individual_description_1": "Pentru un individ", "purchase_individual_description_2": "Statutul de suporter", + "purchase_individual_title": "Individual", "purchase_input_suggestion": "Aveți o cheie de produs? Introduceți cheia mai jos", "purchase_license_subtitle": "Cumpărați Immich pentru a sprijini dezvoltarea continuă a serviciului", "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", - "purchase_panel_info_1": "Dezvoltarea Immich necesită mult timp și efort și avem ingineri cu normă ÃŽntreagă care lucrează la ea pentru a o face cÃĸt se poate de bună. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea, cu alternative reale la serviciile cloud care exploatează.", - "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară ÃŽn Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", + "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă ÃŽntreagă care lucrează la el pentru a-l face cÃĸt se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", + "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară ÃŽn Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_server": "Per server", + "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", "purchase_remove_server_product_key": "Eliminați cheia de produs a Serverului", "purchase_remove_server_product_key_prompt": "Sigur doriți să eliminați cheia de produs a Serverului?", "purchase_server_description_1": "Pentru tot serverul", "purchase_server_description_2": "Statutul de suporter", + "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", + "query_asset_id": "Interoghează ID-ul resursei", + "queue_status": "Se pun ÃŽn coadă {count}/{total}", "rating": "Evaluare cu stele", - "rating_clear": "Anulați evaluare", + "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", "rating_description": "Afișați evaluarea EXIF ÃŽn panoul de informații", "reaction_options": "Opțiuni de reacție", "read_changelog": "Citiți Jurnalul de Modificări", + "readonly_mode_disabled": "Modul doar citire dezactivat", + "readonly_mode_enabled": "Modul doar citire activat", + "ready_for_upload": "Pregătit pentru ÃŽncărcare", "reassign": "Reatribuiți", "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", "reassing_hint": "Atribuiți resursele selectate unei persoane existente", + "recent": "Recent", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", + "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", + "recently_taken": "Recent realizate", + "recently_taken_page_title": "Recent realizate", "refresh": "ReÃŽmprospătare", - "refresh_encoded_videos": "Actualizează videoclipurile codificate", + "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "ReÃŽmprospătați fețele", "refresh_metadata": "Actualizați metadatele", "refresh_thumbnails": "ReÃŽmprospătați miniaturile", "refreshed": "ReÃŽmprospătat", "refreshes_every_file": "Recitește toate fișierele existente și noi", - "refreshing_encoded_video": "Se reÃŽmprospătează videoclipul codificat", + "refreshing_encoded_video": "Se reÃŽmprospătează videoclipul encodat", "refreshing_faces": "Se reÃŽmprospătează fețele", "refreshing_metadata": "Se reÃŽmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", + "remote": "De la distanță", + "remote_assets": "Elemente la distanță", + "remote_media_summary": "Rezumat media de la distanță", "remove": "Eliminați", "remove_assets_album_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din album?", "remove_assets_shared_link_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din acest link comun?", @@ -1275,10 +1628,15 @@ "remove_custom_date_range": "Eliminați intervalul de date personalizat", "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", + "remove_from_album_action_prompt": "{count} șters(e) din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_lock_folder_action_prompt": "{count} șters(e) din dosarul blocat", + "remove_from_locked_folder": "Eliminați din folderul securizat", + "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile ÃŽn biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", "remove_memory": "Șterge amintirea", "remove_photo_from_memory": "Șterge fotografia din această amintire", + "remove_tag": "Eliminați ticheta", "remove_url": "Eliminați adresa URL", "remove_user": "Eliminați utilizatorul", "removed_api_key": "Cheie API eliminată: {name}", @@ -1299,25 +1657,38 @@ "reset": "Resetare", "reset_password": "Resetare parolă", "reset_people_visibility": "Resetați vizibilitatea persoanelor", + "reset_pin_code": "Resetare cod PIN", + "reset_pin_code_description": "Dacă ți-ai uitat codul PIN, poți contacta administratorul serverului pentru a-l reseta", + "reset_pin_code_success": "Codul PIN a fost resetat cu succes", + "reset_pin_code_with_password": "Puteți reseta oricÃĸnd codul PIN cu ajutorul parolei", + "reset_sqlite": "Resetare bază de date SQLite", + "reset_sqlite_confirmation": "Sigur doriți să resetați baza de date SQLite? Va trebui să vă deconectați și să vă conectați din nou pentru a resincroniza datele", + "reset_sqlite_success": "Resetarea cu succes a bazei de date SQLite", "reset_to_default": "Resetați la valoarea implicită", "resolve_duplicates": "Rezolvați duplicatele", "resolved_all_duplicates": "Rezolvați toate duplicatele", "restore": "Restaurați", "restore_all": "Restaurați toate", + "restore_trash_action_prompt": "{count} restaurate din gunoi", "restore_user": "Restabiliți utilizatorul", "restored_asset": "Resursă restaurată", "resume": "Reluare", + "resume_paused_jobs": "Reluați {count, plural, one {# paused job} other {# paused jobs}}", "retry_upload": "ReÃŽncercați ÃŽncărcarea", "review_duplicates": "Examinați duplicatele", + "review_large_files": "Revizuirea fișierelor mari", "role": "Rol", + "role_editor": "Editor", "role_viewer": "Vizualizator", + "running": "Rulează", "save": "Salvați", + "save_to_gallery": "Salvați ÃŽn galerie", "saved_api_key": "Cheie API salvată", "saved_profile": "Profil salvat", "saved_settings": "Setări salvate", "say_something": "Spuneți ceva", "scaffold_body_error_occurred": "A apărut o eroare", - "scan_all_libraries": "Scanați Toate Bibliotecile", + "scan_all_libraries": "Scanați toate bibliotecile", "scan_library": "Scanare", "scan_settings": "Setări Scanare", "scanning_for_album": "Se scanează după album...", @@ -1332,16 +1703,32 @@ "search_camera_model": "Se caută modelul camerei...", "search_city": "Se caută orașul...", "search_country": "Se caută țara...", + "search_filter_apply": "Aplicați filtrul", + "search_filter_camera_title": "Selectați tipul de cameră", + "search_filter_date": "Dată", + "search_filter_date_interval": "{start} la {end}", + "search_filter_date_title": "Selectați un interval de dată", + "search_filter_display_option_not_in_album": "Nu este ÃŽn album", + "search_filter_display_options": "Opțiuni de afișare", + "search_filter_filename": "Căutare după numele fișierului", + "search_filter_location": "LocaÅŖie", + "search_filter_location_title": "Selectați locația", + "search_filter_media_type": "Tip media", + "search_filter_media_type_title": "Selectați tipul media", + "search_filter_people_title": "Selectați persoane", "search_for": "Căutare după", "search_for_existing_person": "Se caută o persoană existentă", + "search_no_more_result": "Nu mai există rezultate", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", + "search_no_result": "Nu s-au găsit rezultate, ÃŽncercați un alt termen sau o altă combinație de termeni de căutare", "search_options": "Opțiuni de căutare", "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_screenshots": "Capturi de ecran", + "search_page_search_photos_videos": "Caută fotografiile și videoclipurile tale", "search_page_selfies": "Selfie-uri", "search_page_things": "Obiecte", "search_page_view_all_button": "Vezi toate", @@ -1366,6 +1753,7 @@ "select_album_cover": "Selectați coperta albumului", "select_all": "Selectați tot", "select_all_duplicates": "Selectați toate duplicatele", + "select_all_in": "Selectați tot ÃŽn {group}", "select_avatar_color": "Selectați culoarea avatarului", "select_face": "Selectați fața", "select_featured_photo": "Selectați fotografia recomandată", @@ -1373,16 +1761,22 @@ "select_keep_all": "Selectați tot pentru păstrare", "select_library_owner": "Selectați proprietarul bibliotecii", "select_new_face": "Selectați o nouĮŽ fațĮŽ", + "select_person_to_tag": "Selectați o persoană pentru a o eticheta", "select_photos": "Selectați fotografii", "select_trash_all": "Selectați tot pentru ștergere", "select_user_for_sharing_page_err_album": "Creare album eșuată", "selected": "Selectat", "selected_count": "{count, plural, other {# selectat}}", + "selected_gps_coordinates": "Coordonate GPS selectate", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", + "server_endpoint": "Endpoint server", "server_info_box_app_version": "Versiune Aplicatie", "server_info_box_server_url": "URL-ul server-ului", - "server_stats": "Statistici Server", + "server_offline": "Serverul este offline", + "server_online": "Server online", + "server_privacy": "Confidențialitatea serverului", + "server_stats": "Statistici server", "server_version": "Versiune Server", "set": "Setați", "set_as_album_cover": "Setați ca și copertă a albumului", @@ -1391,11 +1785,15 @@ "set_date_of_birth": "Setați data nașterii", "set_profile_picture": "Setați poza de profil", "set_slideshow_to_fullscreen": "Setați Prezentare de Diapozitive la ecran complet", + "set_stack_primary_asset": "Setați ca element principal", "setting_image_viewer_help": "Vizualizatorul detaliilor ÃŽncarcă mai ÃŽntÃĸi miniatura mică, apoi ÃŽncarcă previzualizarea de dimensiune medie (dacă este activată), ÃŽn cele din urmă ÃŽncarcă originalul (dacă este activat).", "setting_image_viewer_original_subtitle": "Activează pentru a ÃŽncărca imaginea originală ÃŽn rezoluție completă (mare!). Dezactivează pentru a reduce consumul de date (atat pe rețea, cÃĸt și ÃŽn memoria cache a dispozitivului).", "setting_image_viewer_original_title": "Încarcă fotografia originală", "setting_image_viewer_preview_subtitle": "Activează pentru a ÃŽncărca o imagine ÃŽn rezoluție medie. Dezactivează pentru a ÃŽncărca direct imaginea originală sau doar a utiliza miniatura.", "setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare", + "setting_image_viewer_title": "Imagini", + "setting_languages_apply": "Aplică", + "setting_languages_subtitle": "Schimbați limba aplicației", "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup ÃŽn fundal: {duration}", "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "imediat", @@ -1407,12 +1805,19 @@ "setting_notifications_subtitle": "Ajustează preferințele pentru notificări", "setting_notifications_total_progress_subtitle": "Progresul general al ÃŽncărcării (resurse finalizate/total)", "setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță ÃŽn fundal", + "setting_video_viewer_looping_title": "Buclă", + "setting_video_viewer_original_video_subtitle": "CÃĸnd redați ÃŽn flux un videoclip de pe server, redați originalul chiar și atunci cÃĸnd este disponibilă o transcodare. Poate duce la ÃŽncărcare temporară. Videoclipurile disponibile local sunt redate la calitatea originală indiferent de această setare.", + "setting_video_viewer_original_video_title": "Forțează videoclipul original", "settings": "Setări", "settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare", "settings_saved": "Setările au fost salvate", + "setup_pin_code": "Configurați un cod PIN", "share": "Distribuiți", + "share_action_prompt": "{count} elemente partajate", "share_add_photos": "Adaugă fotografii", + "share_assets_selected": "{count} selectat(e)", "share_dialog_preparing": "Se pregătește...", + "share_link": "Partajați linkul", "shared": "Partajat", "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "Dorești să ștergi această activitate?", @@ -1425,10 +1830,12 @@ "shared_by_user": "Partajat de {user}", "shared_by_you": "Partajat de tine", "shared_from_partner": "Fotografii de la {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} ÃŽncărcate", "shared_link_app_bar_title": "Link-uri distribuite", "shared_link_clipboard_copied_massage": "Copiat ÃŽn clipboard", "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", "shared_link_create_error": "Eroare ÃŽn timpul creării linkului de distribuire", + "shared_link_custom_url_description": "Accesează acest link partajat cu un URL personalizat", "shared_link_edit_description_hint": "Introdu descrierea distribuirii", "shared_link_edit_expire_after_option_day": "1 zi", "shared_link_edit_expire_after_option_days": "{count} zile", @@ -1436,6 +1843,8 @@ "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minut", "shared_link_edit_expire_after_option_minutes": "{count} minute", + "shared_link_edit_expire_after_option_months": "{count} luni", + "shared_link_edit_expire_after_option_year": "{count} an", "shared_link_edit_password_hint": "Introdu parola de distribuire", "shared_link_edit_submit_button": "Actualizează link", "shared_link_error_server_url_fetch": "Nu se poate accesa URL-ul serverului", @@ -1448,11 +1857,15 @@ "shared_link_expires_never": "Expiră ∞", "shared_link_expires_second": "Expiră ÃŽn {count} secunde", "shared_link_expires_seconds": "Expiră ÃŽn {count} secunde", + "shared_link_individual_shared": "Partajat individual", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", + "shared_link_password_description": "Solicită o parolă pentru a accesa acest link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", + "shared_with_me": "Distribuit cu mine", "shared_with_partner": "Partajat cu {partner}", "sharing": "Distribuire", "sharing_enter_password": "Vă rugăm să introduceți parola pentru a vizualiza această pagină.", @@ -1483,6 +1896,7 @@ "show_slideshow_transition": "Afișați tranziția de prezentare", "show_supporter_badge": "Insigna suporterului", "show_supporter_badge_description": "Arată o insignă de suporter", + "show_text_search_menu": "Afișează meniul de căutare text", "shuffle": "Amestecați", "sidebar": "Bara laterală", "sidebar_display_description": "Afișați un link către vizualizare ÃŽn bara laterală", @@ -1498,12 +1912,14 @@ "sort_created": "Data creării", "sort_items": "Numărul de articole", "sort_modified": "Data modificării", + "sort_newest": "Cea mai nouă fotografie", "sort_oldest": "Cea mai veche fotografie", "sort_people_by_similarity": "Sortează oameni după asemanare", "sort_recent": "Cea mai recentă fotografie", "sort_title": "Titlu", "source": "Sursă", "stack": "Stivă", + "stack_action_prompt": "{count} suprapuse", "stack_duplicates": "Duplicate stive", "stack_select_one_photo": "Selectați o fotografie principală pentru stivă", "stack_selected_photos": "Fotografie stivă selectată", @@ -1511,16 +1927,20 @@ "stacktrace": "Urmă stivă", "start": "Început", "start_date": "Data de ÃŽncepere", + "start_date_before_end_date": "Data de ÃŽnceput trebuie să fie ÃŽnainte de data de sfÃĸrșit", "state": "SituaÅŖie", "status": "Stare", + "stop_casting": "Opriți difuzarea", "stop_motion_photo": "Opriți Fotografia in Mișcare", "stop_photo_sharing": "Încetați distribuirea fotografiilor?", "stop_photo_sharing_description": "{partner} nu va mai putea accesa fotografiile dvs.", "stop_sharing_photos_with_user": "Nu mai partajați fotografiile cu acest utilizator", "storage": "Spațiu de stocare", "storage_label": "Eticheta de depozitare", + "storage_quota": "Cotă de stocare", "storage_usage": "{used} din {available} utilizați", "submit": "Trimiteți", + "success": "Succes", "suggestions": "Sugestii", "sunrise_on_the_beach": "RĮŽsĮŽrit pe plajĮŽ", "support": "Suport tehnic", @@ -1528,6 +1948,13 @@ "support_third_party_description": "Instalarea dvs. Immich a fost pregătită de o terță parte. Problemele pe care le ÃŽntÃĸmpinați pot fi cauzate de acel pachet, așa că vă rugăm să ridicați probleme cu ei ÃŽn primă instanță utilizÃĸnd linkurile de mai jos.", "swap_merge_direction": "Schimbați direcția de ÃŽmbinare", "sync": "Sincronizare", + "sync_albums": "Sincronizează albumele", + "sync_albums_manual_subtitle": "Sincronizează toate videoclipurile și fotografiile ÃŽncărcate cu albumele de rezervă selectate", + "sync_local": "Sincronizare locală", + "sync_remote": "Sincronizare la distanță", + "sync_status": "Status-ul sincronizării", + "sync_status_subtitle": "Vizualizează și gestionează sistemul de sincronizare", + "sync_upload_album_setting_subtitle": "Creează și ÃŽncarcă fotografiile și videoclipurile tale ÃŽn albumele selectate de pe Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", "tag_created": "Etichetă creată: {tag}", @@ -1537,14 +1964,20 @@ "tag_updated": "Etichetă actualizată: {tag}", "tagged_assets": "Etichetat {count, plural, one {# resursă} other {# resurse}}", "tags": "Etichete", + "tap_to_run_job": "Atingeți pentru a rula job-ul", "template": "Șablon", "theme": "Temă", "theme_selection": "Selectarea temei", "theme_selection_description": "Setați automat tema la mod luminos sau ÃŽntunecată, ÃŽn funcție de preferințele de sistem ale browserului dvs", "theme_setting_asset_list_storage_indicator_title": "Arată indicator stocare", "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rÃĸnd ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicați culoarea primară pe suprafețele de fundal.", + "theme_setting_colorful_interface_title": "Interfață colorată", "theme_setting_image_viewer_quality_subtitle": "Ajustează calitatea detaliilor vizualizatorului de imagine", "theme_setting_image_viewer_quality_title": "Calitate vizualizator de imagine", + "theme_setting_primary_color_subtitle": "Alege o culoare pentru acțiunile și accentele principale.", + "theme_setting_primary_color_title": "Culoare primară", + "theme_setting_system_primary_color_title": "Folosește culoarea sistemului", "theme_setting_system_theme_switch": "Automat (La fel ca setarea sistemului)", "theme_setting_theme_subtitle": "Alege tema aplicației", "theme_setting_three_stage_loading_subtitle": "Încărcarea ÃŽn trei etape are putea crește performanța ÃŽncărcării dar generează un volum semnificativ mai mare de trafic pe rețea", @@ -1558,14 +1991,19 @@ "to_change_password": "SchimbaÅŖi parola", "to_favorite": "Favorit", "to_login": "Conectare", + "to_multi_select": "pentru selecție multiplă", "to_parent": "Du-te la părinte", + "to_select": "a selecta", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", + "total": "Total", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", + "trash_action_prompt": "{count} mutat(e) la coșul de gunoi", "trash_all": "Ștergeți Tot", "trash_count": "Ștergeți {count, number}", "trash_delete_asset": "Coș de gunoi/Ștergeți resursa", + "trash_emptied": "Coș de gunoi golit", "trash_no_results_message": "Fotografiile și videoclipurile mutate ÃŽn coșul de gunoi vor apărea aici.", "trash_page_delete_all": "Șterge tot", "trash_page_empty_trash_dialog_content": "Dorești să golești coșul? Aceste fișiere vor fi șterse permanent din Immich", @@ -1575,10 +2013,16 @@ "trash_page_select_assets_btn": "Selectează resurse", "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", + "troubleshoot": "Depanați", "type": "Tip", + "unable_to_change_pin_code": "Nu se poate schimba codul PIN", + "unable_to_setup_pin_code": "Nu se poate configura codul PIN", "unarchive": "Dezarhivați", + "unarchive_action_prompt": "{count} șters(e) din Arhivă", "unarchived_count": "{count, plural, other {dezarhivat #}}", + "undo": "Anulează", "unfavorite": "Ștergeți din favorite", + "unfavorite_action_prompt": "{count} șters(e) de la Favorite", "unhide_person": "Dezvăluie persoana", "unknown": "Necunoscut", "unknown_country": "Țară necunoscută", @@ -1594,26 +2038,44 @@ "unsaved_change": "Modificare nesalvată", "unselect_all": "Deselectați toate", "unselect_all_duplicates": "Deselectați toate duplicatele", + "unselect_all_in": "Deselectați toate din {group}", "unstack": "Dezasamblați", + "unstack_action_prompt": "{count} neÃŽmpachetate", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", + "untagged": "Neetichetat", "up_next": "Mai departe", + "update_location_action_prompt": "Actualizează locația pentru {count} resurse selectate cu:", + "updated_at": "Actualizat", "updated_password": "Parolă actualizată", "upload": "Încărcați", + "upload_action_prompt": "{count} ÃŽn coadă pentru ÃŽncărcare", "upload_concurrency": "Încărcați simultan", + "upload_details": "Detalii ÃŽncărcare", "upload_dialog_info": "Vrei să backup resursele selectate pe server?", "upload_dialog_title": "Încarcă resursă", "upload_errors": "Încărcare finalizată cu {count, plural, one {# eroare} other {# erori}}, reÃŽmprospătați pagina pentru a reÃŽncărca noile resurse.", + "upload_finished": "Încărcarea s-a finalizat", "upload_progress": "Rămas {remaining, number} - Procesat {processed, number}/{total, number}", "upload_skipped_duplicates": "Sărit {count, plural, one {# duplicat resursă} other {# duplicate resurse}}", "upload_status_duplicates": "Duplicate", "upload_status_errors": "Erori", "upload_status_uploaded": "Încărcat", "upload_success": "Încărcare reușită, reÃŽmprospătați pagina pentru a vedea resursele noi ÃŽncărcate.", + "upload_to_immich": "Încărcați pe Immich ({count})", + "uploading": "Se ÃŽncarcă", + "uploading_media": "Se ÃŽncarcă fișierele media", + "url": "URL", "usage": "Utilizare", + "use_biometric": "Folosește biometrice", + "use_current_connection": "folosește conexiunea curentă", "use_custom_date_range": "Utilizați ÃŽn schimb un interval de date personalizat", "user": "Utilizator", + "user_has_been_deleted": "Acest utilizator a fost șters.", "user_id": "ID utilizator", "user_liked": "{user} a apreciat {type, select, photo {această imagine} video {acest video} asset {această resursă} other {it}}", + "user_pin_code_settings": "Cod PIN", + "user_pin_code_settings_description": "Gestionați-vă codul PIN", + "user_privacy": "Confidențialitatea utilizatorilor", "user_purchase_settings": "Cumpărare", "user_purchase_settings_description": "Gestionați-vă achiziția", "user_role_set": "Setați {user} ca {role}", @@ -1622,8 +2084,10 @@ "user_usage_stats_description": "Vedeți statisticile de utilizare a contului", "username": "Nume de utilizator", "users": "Utilizatori", + "users_added_to_album_count": "{count, plural, one {# utilizator a fost adăugat} other {# utilizatori au fost adăugați}} ÃŽn album", "utilities": "UtilitĮŽČ›i", "validate": "Validați", + "validate_endpoint_error": "Vă rugăm să introduceți o adresă URL validă", "variables": "Variabile", "version": "Versiune", "version_announcement_closing": "Prietenul tĮŽu, Alex", @@ -1639,6 +2103,7 @@ "view_album": "Vizualizați Album", "view_all": "Vizualizați Tot", "view_all_users": "Vizulizați toți utilizatorii", + "view_details": "Vedeți detaliile", "view_in_timeline": "Vizualizați ÃŽn cronologie", "view_link": "Vezi link", "view_links": "Vizualizați scurtĮŽturi", @@ -1646,21 +2111,25 @@ "view_next_asset": "Vizualizați următoarea resursă", "view_previous_asset": "Vizualizați resursa anterioară", "view_qr_code": "Vezi cod QR", + "view_similar_photos": "Vizualizați poze similare", "view_stack": "Vizualizați Stiva", + "view_user": "Vizualizare utilizator", "viewer_remove_from_stack": "Șterge din grup", "viewer_stack_use_as_main_asset": "Folosește ca resursă principală", "viewer_unstack": "Anulează grup", "visibility_changed": "Vizibilitatea schimbată pentru {count, plural, one {# persoană} other {# persoane}}", - "waiting": "Așteptați", + "waiting": "În așteptare", "warning": "Avertisment", "week": "SĮŽptĮŽmÃĸnĮŽ", "welcome": "Bun venit", "welcome_to_immich": "Bun venit la Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nume Wi-Fi", + "wrong_pin_code": "Cod PIN greșit", "year": "An", "years_ago": "acum {years, plural, one {# an} other {# ani}} ÃŽn urmă", "yes": "Da", "you_dont_have_any_shared_links": "Nu aveți linkuri partajate", - "your_wifi_name": "Your WiFi name", - "zoom_image": "Măriți Imaginea" + "your_wifi_name": "Numele rețelei tale WiFi", + "zoom_image": "Măriți Imaginea", + "zoom_to_bounds": "Mărește la margini" } diff --git a/i18n/ru.json b/i18n/ru.json index 5810a31053..51d80614d3 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -7,13 +7,14 @@ "action_common_update": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "actions": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ", "active": "Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ", - "activity": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ", + "activity": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ", "activity_changed": "АĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ {enabled, select, true {вĐēĐģŅŽŅ‡ĐĩĐŊа} other {ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊа}}", "add": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ", "add_a_description": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "add_a_location": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "add_a_name": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ иĐŧŅ", "add_a_title": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŊаСваĐŊиĐĩ", + "add_birthday": "ĐŖĐēĐ°ĐˇĐ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", "add_endpoint": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Đ°Đ´Ņ€Đĩҁ", "add_exclusion_pattern": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", "add_import_path": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", @@ -25,17 +26,21 @@ "add_tag": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", "add_to": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ вâ€Ļ", "add_to_album": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧ", - "add_to_album_bottom_sheet_added": "ДобавĐģĐĩĐŊĐž в {album}", - "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐĩ в {album}", + "add_to_album_bottom_sheet_added": "ДобавĐģĐĩĐŊĐž в аĐģŅŒĐąĐžĐŧ {album}", + "add_to_album_bottom_sheet_already_exists": "ĐŖĐļĐĩ в аĐģŅŒĐąĐžĐŧĐĩ {album}", + "add_to_album_bottom_sheet_some_local_assets": "НĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐĩ дОйавĐģĐĩĐŊŅ‹ в аĐģŅŒĐąĐžĐŧ, ĐŋĐžŅĐēĐžĐģҌĐē҃ Đĩ҉Đĩ ĐŊĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊŅ‹ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", + "add_to_album_toggle": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ Đ´ĐģŅ аĐģŅŒĐąĐžĐŧа {album}", + "add_to_albums": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧŅ‹", + "add_to_albums_count": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧŅ‹ ({count})", "add_to_shared_album": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ URL", "added_to_archive": "ДобавĐģĐĩĐŊĐž в Đ°Ņ€Ņ…Đ¸Đ˛", "added_to_favorites": "ДобавĐģĐĩĐŊĐž в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", - "added_to_favorites_count": "ДобавĐģĐĩĐŊĐž{count, number} в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", + "added_to_favorites_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ дОйавĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ дОйавĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° дОйавĐģĐĩĐŊĐž}} в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "admin": { "add_exclusion_pattern_description": "Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊŅ‹ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊиК. ПоддĐĩŅ€ĐļĐ¸Đ˛Đ°ŅŽŅ‚ŅŅ ŅĐ¸ĐŧвОĐģŅ‹ ĐŋĐžĐ´ŅŅ‚Đ°ĐŊОвĐēи *, ** и ?. Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹ в ĐģŅŽĐąĐžĐŧ ĐēĐ°Ņ‚Đ°ĐģĐžĐŗĐĩ ҁ иĐŧĐĩĐŊĐĩĐŧ \"Raw\", ҃ĐēаĐļĐ¸Ņ‚Đĩ \"**/Raw/**\". Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Ņ„Đ°ĐšĐģŅ‹, СаĐēаĐŊŅ‡Đ¸Đ˛Đ°ŅŽŅ‰Đ¸ĐĩŅŅ ĐŊа \".tif\", Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ \"**/*.tif\". Đ§Ņ‚ĐžĐąŅ‹ Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ ҆ĐĩĐģиĐēĐžĐŧ, ҃ĐēаĐļĐ¸Ņ‚Đĩ \"/path/to/ignore/**\".", "admin_user": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", - "asset_offline_description": "Đ­Ņ‚ĐžŅ‚ Ņ„Đ°ĐšĐģ вĐŊĐĩ҈ĐŊĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи ĐŊĐĩ ĐąŅ‹Đģ ĐŊаКдĐĩĐŊ ĐŊа Đ´Đ¸ŅĐēĐĩ и ĐąŅ‹Đģ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃. Đ•ŅĐģи Ņ„Đ°ĐšĐģ ĐąŅ‹Đģ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ вĐŊŅƒŅ‚Ņ€Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēи, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅƒŅŽ ҈ĐēаĐģ҃, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ĐšŅ‚Đ¸ ĐŊĐžĐ˛Ņ‹Đš ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ. Đ§Ņ‚ĐžĐąŅ‹ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģ, ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ĐŋŅƒŅ‚ŅŒ ĐŊиĐļĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ Đ´ĐģŅ Immich и Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚Đĩ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", + "asset_offline_description": "Đ­Ņ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ иС вĐŊĐĩ҈ĐŊĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēи ĐŊĐĩ ĐąŅ‹Đģ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊ ĐŊа Đ´Đ¸ŅĐēĐĩ и ĐŋĐžŅŅ‚ĐžĐŧ҃ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃. Đ•ŅĐģи Ņ„Đ°ĐšĐģ ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐąŅ‹Đģ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ вĐŊŅƒŅ‚Ņ€Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēи, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊŅƒŅŽ ҈ĐēаĐģ҃, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ĐšŅ‚Đ¸ ĐŊĐžĐ˛Ņ‹Đš ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đš ĐžĐąŅŠĐĩĐēŅ‚. Đ§Ņ‚ĐžĐąŅ‹ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģ, ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đš ĐŋŅƒŅ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ Đ´ĐģŅ Immich, и Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚Đĩ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "authentication_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€ĐžĐģŅĐŧи, OAuth и Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "authentication_settings_disable_all": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐŧĐĩŅ‚ĐžĐ´Ņ‹ Đ˛Ņ…ĐžĐ´Đ°? Đ’Ņ…ĐžĐ´ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐģĐŊĐžŅŅ‚ŅŒŅŽ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ.", @@ -44,6 +49,13 @@ "backup_database": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅƒŅŽ ĐēĐžĐŋĐ¸ŅŽ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", "backup_database_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅĐžĐˇĐ´Đ°ĐŊиĐĩ даĐŧĐŋОв ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", "backup_keep_last_amount": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Ņ…Ņ€Đ°ĐŊиĐŧҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "backup_onboarding_1_description": "Ņ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК вĐŊĐĩ҈ĐŊĐĩĐš ĐēĐžĐŋии в ОйĐģаĐēĐĩ иĐģи Đ´Ņ€ŅƒĐŗĐžĐŧ Ņ„Đ¸ĐˇĐ¸Ņ‡ĐĩҁĐēĐžĐŧ ĐŧĐĩҁ҂Đĩ.", + "backup_onboarding_2_description": "Ņ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐžŅĐŊОвĐŊҋ҅ Ņ„Đ°ĐšĐģОв и Đ¸Ņ… ĐģĐžĐēаĐģҌĐŊОК ĐēĐžĐŋии ĐŊа Đ´Đ˛ŅƒŅ… Ņ€Đ°ĐˇĐŊҋ҅ Ņ‚Đ¸ĐŋĐ°Ņ… ĐŊĐžŅĐ¸Ņ‚ĐĩĐģĐĩĐš.", + "backup_onboarding_3_description": "ŅĐžĐˇĐ´Đ°ĐŊиĐĩ ҂Ҁґ҅ ĐēĐžĐŋиК даĐŊĐŊҋ҅, вĐēĐģŅŽŅ‡Đ°Ņ Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹. 2 ĐģĐžĐēаĐģҌĐŊҋ҅ ĐēĐžĐŋии и 1 вĐŊĐĩ҈ĐŊŅŽŅŽ.", + "backup_onboarding_description": "ДĐģŅ ĐŊĐ°Đ´Ņ‘ĐļĐŊОК ĐˇĐ°Ņ‰Đ¸Ņ‚Ņ‹ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ŅŅ‚Ņ€Đ°Ņ‚ĐĩĐŗĐ¸ŅŽ Ņ€ĐĩСĐĩŅ€Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ даĐŊĐŊҋ҅ 3-2-1. ДĐĩĐģĐ°ĐšŅ‚Đĩ ĐēĐžĐŋии ĐēаĐē ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊҋ҅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž, Ņ‚Đ°Đē и ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅ Immich.", + "backup_onboarding_footer": "ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ĐŋĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŧ҃ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸ŅŽ Immich Đ´ĐžŅŅ‚ŅƒĐŋĐŊа в Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", + "backup_onboarding_parts_title": "ĐĄŅ‚Ņ€Đ°Ņ‚ĐĩĐŗĐ¸Ņ 3-2-1 ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐĩŅ‚:", + "backup_onboarding_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", "backup_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅.", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ‹ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Đ´ĐģŅ: {job}", @@ -54,9 +66,9 @@ "confirm_reprocess_all_faces": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐģĐ¸Ņ†Đ°? Đ‘ŅƒĐ´ŅƒŅ‚ Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иĐŧĐĩĐŊа ŅĐž Đ˛ŅĐĩŅ… ĐģĐ¸Ņ†.", "confirm_user_password_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", "confirm_user_pin_code_reset": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", - "create_job": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ СадаĐŊиĐĩ", + "create_job": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐˇĐ°Đ´Đ°Ņ‡Ņƒ", "cron_expression": "Đ Đ°ŅĐŋĐ¸ŅĐ°ĐŊиĐĩ (Đ˛Ņ‹Ņ€Đ°ĐļĐĩĐŊиĐĩ ĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đēа cron)", - "cron_expression_description": "Đ§Đ°ŅŅ‚ĐžŅ‚Đ° и Đ˛Ņ€ĐĩĐŧŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ СадаĐŊĐ¸Ņ в Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đēа cron. Đ’ĐžŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚ĐĩҁҌ ĐŋŅ€Đ¸ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ Đ˛Đ¸ĐˇŅƒĐ°ĐģҌĐŊŅ‹Đŧ Ņ€ĐĩдаĐēŅ‚ĐžŅ€ĐžĐŧ Crontab Guru", + "cron_expression_description": "Đ§Đ°ŅŅ‚ĐžŅ‚Đ° и Đ˛Ņ€ĐĩĐŧŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ в Ņ„ĐžŅ€ĐŧĐ°Ņ‚Đĩ ĐŋĐģаĐŊĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đēа cron. Đ’ĐžŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚ĐĩҁҌ ĐŋŅ€Đ¸ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ Đ˛Đ¸ĐˇŅƒĐ°ĐģҌĐŊŅ‹Đŧ Ņ€ĐĩдаĐēŅ‚ĐžŅ€ĐžĐŧ Crontab Guru", "cron_expression_presets": "Đ Đ°ŅĐŋĐ¸ŅĐ°ĐŊиĐĩ (ĐŋŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ)", "disable_login": "ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ˛Ņ…ĐžĐ´", "duplicate_detection_job_description": "ЗаĐŋ҃ҁĐēаĐĩŅ‚ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŋĐžŅ…ĐžĐļĐ¸Ņ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ĐŋŅ€Đ¸ ĐŋĐžĐŧĐžŅ‰Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐˇŅ€ĐĩĐŊĐ¸Ņ (ĐˇĐ°Đ˛Đ¸ŅĐ¸Ņ‚ ĐžŅ‚ ҃ĐŧĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа)", @@ -66,7 +78,7 @@ "face_detection_description": "ОбĐŊĐ°Ņ€ŅƒĐļиваĐĩŅ‚ ĐģĐ¸Ņ†Đ° ĐŊа ĐžĐąŅŠĐĩĐēŅ‚Đ°Ņ… ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ. ДĐģŅ видĐĩĐž аĐŊаĐģĐ¸ĐˇĐ¸Ņ€ŅƒĐĩŅ‚ŅŅ Ņ‚ĐžĐģҌĐēĐž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ°. КĐŊĐžĐŋĐēа \"ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ\" СаĐŋ҃ҁĐēаĐĩŅ‚ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊŅƒŅŽ ĐžĐąŅ€Đ°ĐąĐžŅ‚Đē҃ Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. \"ĐĄĐąŅ€ĐžŅ\" — Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊĐž ŅƒĐ´Đ°ĐģŅĐĩŅ‚ Đ˛ŅĐĩ иĐŧĐĩŅŽŅ‰Đ¸ĐĩŅŅ даĐŊĐŊŅ‹Đĩ Đž ĐģĐ¸Ņ†Đ°Ņ…. \"ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ\" — ŅŅ‚Đ°Đ˛Đ¸Ņ‚ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Đĩ҉ґ ĐŊĐĩ ĐąŅ‹Đģи ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ°ĐŊŅ‹. ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ĐģĐ¸Ņ†Đ° ĐŋĐžĐŧĐĩŅ‰Đ°ŅŽŅ‚ŅŅ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ Đ Đ°ŅĐŋОСĐŊаваĐŊиĐĩ ĐģĐ¸Ņ† и ĐŋĐžŅĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐš Đ¸Ņ… ĐŋŅ€Đ¸Đ˛ŅĐˇĐēи Đē ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đŧ иĐģи ĐŊĐžĐ˛Ņ‹Đŧ ĐģŅŽĐ´ŅĐŧ.", "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ŅƒĐĩŅ‚ и ĐŊаСĐŊĐ°Ņ‡Đ°ĐĩŅ‚ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ĐģĐ¸Ņ†Đ° ĐģŅŽĐ´ŅĐŧ. Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ĐŋĐžŅĐģĐĩ СавĐĩŅ€ŅˆĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°Ņ‡Đ¸ ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ ĐģĐ¸Ņ†. КĐŊĐžĐŋĐēа \"ĐĄĐąŅ€ĐžŅ\" (ĐŋĐĩŅ€Đĩ)ĐŊаСĐŊĐ°Ņ‡Đ°ĐĩŅ‚ Đ˛ŅĐĩ ĐģĐ¸Ņ†Đ°. \"ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ\" — дОйавĐģŅĐĩŅ‚ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēи ĐģĐ¸Ņ†Đ°, ĐŊĐĩ ĐŋŅ€Đ¸Đ˛ŅĐˇĐ°ĐŊĐŊŅ‹Đĩ Đē ҇ĐĩĐģОвĐĩĐē҃.", "failed_job_command": "КоĐŧаĐŊда {command} ĐŊĐĩ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊа Đ´ĐģŅ ĐˇĐ°Đ´Đ°Ņ‡Đ¸: {job}", - "force_delete_user_warning": "ĐŸĐ Đ•Đ”ĐŖĐŸĐ Đ•Đ–Đ”Đ•ĐĐ˜Đ•: Đ­Ņ‚Đž ĐŋŅ€Đ¸Đ˛ĐĩĐ´ĐĩŅ‚ Đē ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐžĐŧ҃ ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и ĐĩĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛. Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩвОСĐŧĐžĐļĐŊĐž ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ, и Ņ„Đ°ĐšĐģŅ‹ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹.", + "force_delete_user_warning": "ĐŸĐ Đ•Đ”ĐŖĐŸĐ Đ•Đ–Đ”Đ•ĐĐ˜Đ•: Đ­Ņ‚Đž ĐŋŅ€Đ¸Đ˛ĐĩĐ´ĐĩŅ‚ Đē ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐžĐŧ҃ ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и Đ˛ŅĐĩŅ… ĐĩĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩвОСĐŧĐžĐļĐŊĐž ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ, Ņ„Đ°ĐšĐģŅ‹ ĐŊĐĩ ҁĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹.", "image_format": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚", "image_format_description": "WebP ŅĐžĐˇĐ´Đ°ĐĩŅ‚ Ņ„Đ°ĐšĐģŅ‹ ĐŧĐĩĐŊҌ҈ĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ°, ҇ĐĩĐŧ JPEG, ĐŊĐž ĐēĐžĐ´Đ¸Ņ€ŅƒĐĩŅ‚ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐĩĐĩ.", "image_fullsize_description": "ПоĐģĐŊĐžŅ€Đ°ĐˇĐŧĐĩŅ€ĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐąĐĩС ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ ĐŋŅ€Đ¸ ŅƒĐ˛ĐĩĐģĐ¸Ņ‡ĐĩĐŊии", @@ -89,11 +101,11 @@ "image_thumbnail_description": "МаĐģĐĩĐŊҌĐēĐ°Ņ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ° ҁ ŅƒĐ´Đ°ĐģĐĩĐŊĐŊŅ‹Đŧи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đŧи, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧĐ°Ņ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ ĐŗŅ€ŅƒĐŋĐŋ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš, Ņ‚Đ°ĐēĐ¸Ņ… ĐēаĐē ĐžŅĐŊОвĐŊĐ°Ņ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐ°Ņ ҈ĐēаĐģа", "image_thumbnail_quality_description": "ĐšĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ ĐžŅ‚ 1 Đ´Đž 100. ЧĐĩĐŧ Đ˛Ņ‹ŅˆĐĩ ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž, Ņ‚ĐĩĐŧ ĐģŅƒŅ‡ŅˆĐĩ, ĐŊĐž ĐŋŅ€Đ¸ ŅŅ‚ĐžĐŧ ŅĐžĐˇĐ´Đ°ŅŽŅ‚ŅŅ Ņ„Đ°ĐšĐģŅ‹ йОĐģҌ҈ĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ° и ĐŧĐžĐļĐĩŅ‚ ҁĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒŅŅ ҁĐēĐžŅ€ĐžŅŅ‚ŅŒ ĐžŅ‚ĐēĐģиĐēа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ.", "image_thumbnail_title": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€", - "job_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐ°Ņ ĐžĐąŅ€Đ°ĐąĐžŅ‚Đēа СадаĐŊĐ¸Ņ - {job}", - "job_created": "ЗадаĐŊиĐĩ ŅĐžĐˇĐ´Đ°ĐŊĐž", + "job_concurrency": "Đ§Đ¸ŅĐģĐž ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊҋ҅ ĐŋĐžŅ‚ĐžĐēОв ĐˇĐ°Đ´Đ°Ņ‡Đ¸ {job}", + "job_created": "Đ—Đ°Đ´Đ°Ņ‡Đ° ŅĐžĐˇĐ´Đ°ĐŊа", "job_not_concurrency_safe": "Đ­Ņ‚Đ° ĐˇĐ°Đ´Đ°Ņ‡Đ° ĐŊĐĩ ОйĐĩҁĐŋĐĩŅ‡Đ¸Đ˛Đ°ĐĩŅ‚ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚Đ¸ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ.", - "job_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи СадаĐŊиК", - "job_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊОК ĐžĐąŅ€Đ°ĐąĐžŅ‚ĐēОК СадаĐŊиК", + "job_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐˇĐ°Đ´Đ°Ņ‡", + "job_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒŅŽ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°Ņ‡", "job_status": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ ĐˇĐ°Đ´Đ°Ņ‡", "jobs_delayed": "{jobCount, plural, one {# ĐžŅ‚ĐģĐžĐļĐĩĐŊа} other {# ĐžŅ‚ĐģĐžĐļĐĩĐŊĐž}}", "jobs_failed": "{jobCount, plural, other {# ĐŊĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ}}", @@ -112,20 +124,27 @@ "logging_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ вĐĩĐ´ĐĩĐŊиĐĩ ĐļŅƒŅ€ĐŊаĐģа", "logging_level_description": "Đ•ŅĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐž, Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐļĐĩĐģаĐĩĐŧŅ‹Đš ŅƒŅ€ĐžĐ˛ĐĩĐŊҌ ĐļŅƒŅ€ĐŊаĐģĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ.", "logging_settings": "ВĐĩĐ´ĐĩĐŊиĐĩ ĐļŅƒŅ€ĐŊаĐģа", + "machine_learning_availability_checks": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Đ¸", + "machine_learning_availability_checks_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐžĐŋŅ€ĐĩĐ´ĐĩĐģŅŅ‚ŅŒ и Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ҁĐĩŅ€Đ˛ĐĩҀҋ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ", + "machine_learning_availability_checks_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Đ¸", + "machine_learning_availability_checks_interval": "ИĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēи", + "machine_learning_availability_checks_interval_description": "ИĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ в ĐŧиĐģĐģĐ¸ŅĐĩĐē҃ĐŊĐ´Đ°Ņ… ĐŧĐĩĐļĐ´Ņƒ ĐŋŅ€ĐžĐ˛ĐĩŅ€ĐēаĐŧи", + "machine_learning_availability_checks_timeout": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ СаĐŋŅ€ĐžŅĐ°", + "machine_learning_availability_checks_timeout_description": "Đ’Ņ€ĐĩĐŧŅ ĐžĐļидаĐŊĐ¸Ņ ĐžŅ‚Đ˛ĐĩŅ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ° в ĐŧиĐģĐģĐ¸ŅĐĩĐē҃ĐŊĐ´Đ°Ņ… Đ´ĐģŅ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊĐ¸Ņ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Đ¸", "machine_learning_clip_model": "CLIP ĐŧОдĐĩĐģҌ", - "machine_learning_clip_model_description": "НазваĐŊĐ¸Ņ ĐŧОдĐĩĐģĐĩĐš CLIP Ņ€Đ°ĐˇĐŧĐĩ҉ĐĩĐŊŅ‹ СдĐĩҁҌ. ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŊиĐŧаĐŊиĐĩ, Ņ‡Ņ‚Đž ĐŋŅ€Đ¸ иСĐŧĐĩĐŊĐĩĐŊии ĐŧОдĐĩĐģи ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž СаĐŊОвО СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ ĐˇĐ°Đ´Đ°Ņ‡Ņƒ ÂĢИĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐēÂģ Đ´ĐģŅ Đ˛ŅĐĩŅ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК.", + "machine_learning_clip_model_description": "НазваĐŊĐ¸Ņ Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅ CLIP ĐŧОдĐĩĐģĐĩĐš Ņ€Đ°ĐˇĐŧĐĩ҉ĐĩĐŊŅ‹ СдĐĩҁҌ.\nĐŸŅ€Đ¸ иСĐŧĐĩĐŊĐĩĐŊии ĐŧОдĐĩĐģи ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž СаĐŊОвО СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ ĐˇĐ°Đ´Đ°Ņ‡Ņƒ ÂĢИĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐēÂģ Đ´ĐģŅ Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛.", "machine_learning_duplicate_detection": "ĐŸĐžĐ¸ŅĐē Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", "machine_learning_duplicate_detection_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", - "machine_learning_duplicate_detection_enabled_description": "Đ•ŅĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ, Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊĐž идĐĩĐŊŅ‚Đ¸Ņ‡ĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹ Đ˛ŅŅ‘ Ņ€Đ°Đ˛ĐŊĐž ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛.", - "machine_learning_duplicate_detection_setting_description": "Đ˜ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ Đ˛ŅŅ‚Ņ€Đ°Đ¸Đ˛Đ°ĐŊĐ¸Ņ CLIP Đ´ĐģŅ ĐŋĐžĐ¸ŅĐēа вĐĩŅ€ĐžŅŅ‚ĐŊҋ҅ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", - "machine_learning_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐĩ ĐžĐąŅƒŅ‡ĐĩĐŊиĐĩ", - "machine_learning_enabled_description": "ĐŸŅ€Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊии, Đ˛ŅĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ML ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ ĐŊĐĩĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐž ĐžŅ‚ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Ņ… ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛.", + "machine_learning_duplicate_detection_enabled_description": "Đ•ŅĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡Ņ‘ĐŊ, Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊĐž идĐĩĐŊŅ‚Đ¸Ņ‡ĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹ Đ˛ŅŅ‘ Ņ€Đ°Đ˛ĐŊĐž ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒŅŅ.", + "machine_learning_duplicate_detection_setting_description": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ CLIP ĐŧОдĐĩĐģĐĩĐš Đ´ĐģŅ Đ˛Ņ‹ŅĐ˛ĐģĐĩĐŊĐ¸Ņ вОСĐŧĐžĐļĐŊҋ҅ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", + "machine_learning_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐĩ ĐžĐąŅƒŅ‡ĐĩĐŊиĐĩ", + "machine_learning_enabled_description": "ĐŸŅ€Đ¸ Đ˛Ņ‹ĐēĐģŅŽŅ‡ĐĩĐŊии ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ Đ˛ŅĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ML ĐŊĐĩĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐž ĐžŅ‚ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Ņ… ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛.", "machine_learning_facial_recognition": "Đ Đ°ŅĐŋОСĐŊаваĐŊиĐĩ ĐģĐ¸Ņ†", "machine_learning_facial_recognition_description": "ОбĐŊĐ°Ņ€ŅƒĐļĐ¸Đ˛Đ°Ņ‚ŅŒ, Ņ€Đ°ŅĐŋОСĐŊĐ°Đ˛Đ°Ņ‚ŅŒ и ĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐģĐ¸Ņ†Đ° ĐŊа Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅŅ…", "machine_learning_facial_recognition_model": "МодĐĩĐģҌ Đ´ĐģŅ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ†", - "machine_learning_facial_recognition_model_description": "МодĐĩĐģи ĐŋĐĩŅ€ĐĩŅ‡Đ¸ŅĐģĐĩĐŊŅ‹ в ĐŋĐžŅ€ŅĐ´ĐēĐĩ ŅƒĐąŅ‹Đ˛Đ°ĐŊĐ¸Ņ Ņ€Đ°ĐˇĐŧĐĩŅ€Đ°. БоĐģŅŒŅˆĐ¸Đĩ ĐŧОдĐĩĐģи Ņ€Đ°ĐąĐžŅ‚Đ°ŅŽŅ‚ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐĩĐĩ и Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒŅŽŅ‚ йОĐģҌ҈Đĩ ĐŋаĐŧŅŅ‚Đ¸, ĐŊĐž Đ´Đ°ŅŽŅ‚ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ‹. ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŊиĐŧаĐŊиĐĩ, Ņ‡Ņ‚Đž ĐŋŅ€Đ¸ ҁĐŧĐĩĐŊĐĩ ĐŧОдĐĩĐģи ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ СадаĐŊиĐĩ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ† Đ´ĐģŅ Đ˛ŅĐĩŅ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК.", + "machine_learning_facial_recognition_model_description": "МодĐĩĐģи ĐŋĐĩŅ€ĐĩŅ‡Đ¸ŅĐģĐĩĐŊŅ‹ в ĐŋĐžŅ€ŅĐ´ĐēĐĩ ŅƒĐąŅ‹Đ˛Đ°ĐŊĐ¸Ņ Đ¸Ņ… Ņ€Đ°ĐˇĐŧĐĩŅ€Đ°. БоĐģŅŒŅˆĐ¸Đĩ ĐŧОдĐĩĐģи Ņ€Đ°ĐąĐžŅ‚Đ°ŅŽŅ‚ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐĩĐĩ и Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒŅŽŅ‚ йОĐģҌ҈Đĩ ĐŋаĐŧŅŅ‚Đ¸, ĐŊĐž Đ´Đ°ŅŽŅ‚ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ‹. ĐŸŅ€Đ¸ ҁĐŧĐĩĐŊĐĩ ĐŧОдĐĩĐģи ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž СаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ ĐˇĐ°Đ´Đ°Ņ‡Ņƒ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ† Đ´ĐģŅ Đ˛ŅĐĩŅ… Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК.", "machine_learning_facial_recognition_setting": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŽ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ†", - "machine_learning_facial_recognition_setting_description": "Đ•ŅĐģи ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅŅ‚Ņƒ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŽ, Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´ĐģŅ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ† и ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ СаĐŋĐžĐģĐŊŅŅ‚ŅŒ Ņ€Đ°ĐˇĐ´ĐĩĐģ Đ›ŅŽĐ´Đ¸ ĐŊа ĐžĐąĐˇĐžŅ€ĐŊОК ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đĩ.", + "machine_learning_facial_recognition_setting_description": "ĐŸŅ€Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊии ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´ĐģŅ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ ĐģĐ¸Ņ†, и ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ СаĐŋĐžĐģĐŊŅŅ‚ŅŒŅŅ Ņ€Đ°ĐˇĐ´ĐĩĐģ Đ›ŅŽĐ´Đ¸.", "machine_learning_max_detection_distance": "МаĐēŅĐ¸ĐŧаĐģҌĐŊĐžĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК", "machine_learning_max_detection_distance_description": "МаĐēŅĐ¸ĐŧаĐģҌĐŊĐžĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Đĩ ĐŧĐĩĐļĐ´Ņƒ Đ´Đ˛ŅƒĐŧŅ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸ŅĐŧи, Ņ‡Ņ‚ĐžĐąŅ‹ ŅŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ Đ¸Ņ… Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ°Đŧи, в диаĐŋаСОĐŊĐĩ 0,001-0,1. БоĐģĐĩĐĩ Đ˛Ņ‹ŅĐžĐēиĐĩ СĐŊĐ°Ņ‡ĐĩĐŊĐ¸Ņ ĐŋОСвОĐģŅŅŽŅ‚ ОйĐŊĐ°Ņ€ŅƒĐļĐ¸Ņ‚ŅŒ йОĐģҌ҈Đĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛, ĐŊĐž ĐŧĐžĐŗŅƒŅ‚ ĐŋŅ€Đ¸Đ˛ĐĩŅŅ‚Đ¸ Đē ĐģĐžĐļĐŊŅ‹Đŧ ŅŅ€Đ°ĐąĐ°Ņ‚Ņ‹Đ˛Đ°ĐŊĐ¸ŅĐŧ.", "machine_learning_max_recognition_distance": "ĐŸĐžŅ€ĐžĐŗ Ņ€Đ°ŅĐŋОСĐŊаваĐŊĐ¸Ņ", @@ -135,13 +154,13 @@ "machine_learning_min_recognized_faces": "МиĐŊиĐŧ҃Đŧ Ņ€Đ°ŅĐŋОСĐŊаĐŊĐŊҋ҅ ĐģĐ¸Ņ†", "machine_learning_min_recognized_faces_description": "МиĐŊиĐŧаĐģҌĐŊĐžĐĩ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Ņ€Đ°ŅĐŋОСĐŊаĐŊĐŊҋ҅ ĐģĐ¸Ņ† Đ´ĐģŅ ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ ҇ĐĩĐģОвĐĩĐēа. ĐŖĐ˛ĐĩĐģĐ¸Ņ‡ĐĩĐŊиĐĩ ŅŅ‚ĐžĐŗĐž ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ° Đ´ĐĩĐģаĐĩŅ‚ Ņ€Đ°ŅĐŋОСĐŊаваĐŊиĐĩ ĐģĐ¸Ņ† йОĐģĐĩĐĩ Ņ‚ĐžŅ‡ĐŊŅ‹Đŧ, ĐŊĐž ĐŋŅ€Đ¸ ŅŅ‚ĐžĐŧ ŅƒĐ˛ĐĩĐģĐ¸Ņ‡Đ¸Đ˛Đ°ĐĩŅ‚ŅŅ вĐĩŅ€ĐžŅŅ‚ĐŊĐžŅŅ‚ŅŒ Ņ‚ĐžĐŗĐž, Ņ‡Ņ‚Đž ĐģĐ¸Ņ†Đž ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ€Đ¸ŅĐ˛ĐžĐĩĐŊĐž ҇ĐĩĐģОвĐĩĐē҃.", "machine_learning_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ", - "machine_learning_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅĐŧи и ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ", + "machine_learning_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅĐŧи и ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ (ML)", "machine_learning_smart_search": "ИĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐē", - "machine_learning_smart_search_description": "ĐĄĐĩĐŧаĐŊŅ‚Đ¸Ņ‡ĐĩҁĐēиК ĐŋĐžĐ¸ŅĐē Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ вĐģĐžĐļĐĩĐŊиК CLIP", + "machine_learning_smart_search_description": "ĐĄĐĩĐŧаĐŊŅ‚Đ¸Ņ‡ĐĩҁĐēиК (ĐēĐžĐŊŅ‚ĐĩĐēҁ҂ĐŊŅ‹Đš) ĐŋĐžĐ¸ŅĐē ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ҁ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩĐŧ CLIP ĐŧОдĐĩĐģĐĩĐš", "machine_learning_smart_search_enabled": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐŋĐžĐ¸ŅĐē", - "machine_learning_smart_search_enabled_description": "Đ•ŅĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊ, Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´ĐģŅ иĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа.", + "machine_learning_smart_search_enabled_description": "ĐŸŅ€Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊии ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐēĐžĐ´Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´ĐģŅ иĐŊŅ‚ĐĩĐģĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа.", "machine_learning_url_description": "URL-Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐžĐąŅƒŅ‡ĐĩĐŊĐ¸Ņ. Đ•ŅĐģи ҃ĐēаСаĐŊĐž ĐŊĐĩҁĐēĐžĐģҌĐēĐž, СаĐŋŅ€ĐžŅŅ‹ ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒŅŅ ĐŋĐž ĐžŅ‡ĐĩŅ€Đĩди ĐŊа ĐēаĐļĐ´Ņ‹Đš, ĐŋĐžĐēа ĐžŅ‚ ОдĐŊĐžĐŗĐž иС ĐŊĐ¸Ņ… ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊ ҃ҁĐŋĐĩ҈ĐŊŅ‹Đš ĐžŅ‚Đ˛ĐĩŅ‚. ĐĄĐĩŅ€Đ˛ĐĩҀҋ, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐĩ ĐžŅ‚Đ˛ĐĩŅ‡Đ°ŅŽŅ‚, ĐąŅƒĐ´ŅƒŅ‚ Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊĐž Đ¸ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ Đ´Đž Ņ‚ĐĩŅ… ĐŋĐžŅ€, ĐŋĐžĐēа ĐŊĐĩ ŅŅ‚Đ°ĐŊŅƒŅ‚ ҁĐŊОва Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹.", - "manage_concurrency": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒŅŽ СадаĐŊиК", + "manage_concurrency": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒŅŽ", "manage_log_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐļŅƒŅ€ĐŊаĐģа", "map_dark_style": "ĐĸŅ‘ĐŧĐŊŅ‹Đš ŅŅ‚Đ¸ĐģҌ", "map_enable_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐēĐ°Ņ€Ņ‚Ņ‹", @@ -159,13 +178,27 @@ "memory_cleanup_job": "ĐžŅ‡Đ¸ŅŅ‚Đēа Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК", "memory_generate_job": "ХОСдаĐŊиĐĩ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК", "metadata_extraction_job": "ИСвĐģĐĩ҇ĐĩĐŊиĐĩ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", - "metadata_extraction_job_description": "ИСвĐģĐĩĐēаĐĩŅ‚ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ иС ĐēаĐļĐ´ĐžĐŗĐž Ņ„Đ°ĐšĐģа, Ņ‚Đ°ĐēиĐĩ ĐēаĐē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ, ĐģĐ¸Ņ†Đ° и Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", + "metadata_extraction_job_description": "ИСвĐģĐĩ҇ĐĩĐŊиĐĩ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅ иС Ņ„Đ°ĐšĐģОв, Ņ‚Đ°ĐēĐ¸Ņ… ĐēаĐē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ, ĐģĐ¸Ņ†Đ° и Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", "metadata_faces_import_setting": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚ ĐģĐ¸Ņ†", - "metadata_faces_import_setting_description": "ИĐŧĐŋĐžŅ€Ņ‚ ĐģĐ¸Ņ† иС Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК EXIF-даĐŊĐŊҋ҅ и Ņ„Đ°ĐšĐģОв sidecar", + "metadata_faces_import_setting_description": "ИĐŧĐŋĐžŅ€Ņ‚ ĐģĐ¸Ņ† иС EXIF-даĐŊĐŊҋ҅ и Ņ„Đ°ĐšĐģОв sidecar", "metadata_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", "metadata_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", "migration_job": "ĐœĐ¸ĐŗŅ€Đ°Ņ†Đ¸Ņ", "migration_job_description": "ПĐĩŅ€ĐĩĐŊĐžŅ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ и ĐģĐ¸Ņ† в ĐŋĐžŅĐģĐĩĐ´ĐŊŅŽŅŽ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Ņƒ ĐŋаĐŋĐžĐē", + "nightly_tasks_cluster_faces_setting_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ Ņ€Đ°ŅĐŋОСĐŊаваĐŊиĐĩ ĐģŅŽĐ´ĐĩĐš ĐŋĐž ĐŊĐžĐ˛Ņ‹Đŧ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ ĐģĐ¸Ņ†Đ°Đŧ", + "nightly_tasks_cluster_new_faces_setting": "Đ Đ°ŅĐŋОСĐŊаваĐŊиĐĩ ĐŊĐžĐ˛Ņ‹Ņ… ĐģĐ¸Ņ†", + "nightly_tasks_database_cleanup_setting": "Đ—Đ°Đ´Đ°Ņ‡Đ¸ ĐžŅ‡Đ¸ŅŅ‚Đēи ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "nightly_tasks_database_cleanup_setting_description": "ĐŖĐ´Đ°ĐģĐĩĐŊиĐĩ ŅŅ‚Đ°Ņ€Ņ‹Ņ… и йОĐģĐĩĐĩ ĐŊĐĩĐŊ҃ĐļĐŊҋ҅ СаĐŋĐ¸ŅĐĩĐš иС ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "nightly_tasks_generate_memories_setting": "ХОСдаĐŊиĐĩ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК", + "nightly_tasks_generate_memories_setting_description": "ХОСдаĐŊиĐĩ ĐŊĐžĐ˛Ņ‹Ņ… Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиК иС ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "nightly_tasks_missing_thumbnails_setting": "ХОСдаĐŊиĐĩ ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Ņ… ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€", + "nightly_tasks_missing_thumbnails_setting_description": "ДобавĐģĐĩĐŊиĐĩ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐąĐĩС ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ Đ¸Ņ… ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ", + "nightly_tasks_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊĐžŅ‡ĐŊҋ҅ ĐˇĐ°Đ´Đ°Ņ‡", + "nightly_tasks_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐžŅ‡ĐŊŅ‹Đŧи Ņ€ĐĩĐŗĐģаĐŧĐĩĐŊŅ‚ĐŊŅ‹Đŧи ĐˇĐ°Đ´Đ°Ņ‡Đ°Đŧи", + "nightly_tasks_start_time_setting": "Đ’Ņ€ĐĩĐŧŅ ĐŊĐ°Ņ‡Đ°Đģа", + "nightly_tasks_start_time_setting_description": "Đ’Ņ€ĐĩĐŧŅ, ĐēĐžĐŗĐ´Đ° ҁĐĩŅ€Đ˛ĐĩŅ€ ĐŊĐ°Ņ‡Đ¸ĐŊаĐĩŅ‚ Đ˛Ņ‹ĐŋĐžĐģĐŊŅŅ‚ŅŒ ĐŊĐžŅ‡ĐŊŅ‹Đĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸", + "nightly_tasks_sync_quota_usage_setting": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐēĐ˛ĐžŅ‚ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", + "nightly_tasks_sync_quota_usage_setting_description": "ОбĐŊОвĐģĐĩĐŊиĐĩ ĐēĐ˛ĐžŅ‚Ņ‹ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐŊа ĐžŅĐŊОвĐĩ аĐēŅ‚ŅƒĐ°ĐģҌĐŊҋ҅ даĐŊĐŊҋ҅", "no_paths_added": "ĐŸŅƒŅ‚Đ¸ ĐŊĐĩ дОйавĐģĐĩĐŊŅ‹", "no_pattern_added": "ШайĐģĐžĐŊ ĐŊĐĩ дОйавĐģĐĩĐŊ", "note_apply_storage_label_previous_assets": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đē҃ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI Ņ€ĐĩĐ´Đ¸Ņ€ĐĩĐēŅ‚Đ° Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ URI Đ´ĐģŅ ĐŧОйиĐģҌĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", "oauth_mobile_redirect_uri_override_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩҁĐģи ĐŋĐžŅŅ‚Đ°Đ˛Ņ‰Đ¸Đē OAuth ĐŊĐĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐ°ĐĩŅ‚ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ ĐŧОйиĐģҌĐŊĐžĐŗĐž URI, ĐŊаĐŋŅ€Đ¸ĐŧĐĩŅ€, ''{callback}''", + "oauth_role_claim": "ĐŖŅ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊиĐĩ Ņ€ĐžĐģи", + "oauth_role_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊиĐĩ Đ´ĐžŅŅ‚ŅƒĐŋа адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ĐŊа ĐžŅĐŊОвĐĩ ĐŊаĐģĐ¸Ņ‡Đ¸Ņ ŅŅ‚ĐžĐŗĐž ŅƒŅ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ. ĐŖŅ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŧĐžĐļĐĩŅ‚ иĐŧĐĩŅ‚ŅŒ СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ 'user' иĐģи 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đ˛Ņ…ĐžĐ´Đ° ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đš Ой ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", @@ -212,20 +247,20 @@ "password_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи Đ˛Ņ…ĐžĐ´Đ° ĐŋĐž ĐŋĐ°Ņ€ĐžĐģŅŽ", "paths_validated_successfully": "Đ’ŅĐĩ ĐŋŅƒŅ‚Đ¸ ҃ҁĐŋĐĩ҈ĐŊĐž ĐŋŅ€ĐžŅˆĐģи ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃", "person_cleanup_job": "ĐžŅ‡Đ¸ŅŅ‚Đēа ĐŋĐĩŅ€ŅĐžĐŊŅ‹", - "quota_size_gib": "РаСĐŧĐĩŅ€ ĐēĐ˛ĐžŅ‚Ņ‹ (ГБ)", + "quota_size_gib": "РаСĐŧĐĩŅ€ ĐēĐ˛ĐžŅ‚Ņ‹ (GiB)", "refreshing_all_libraries": "ОбĐŊОвĐģĐĩĐŊиĐĩ Đ˛ŅĐĩŅ… йийĐģĐ¸ĐžŅ‚ĐĩĐē", "registration": "Đ ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Ņ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "registration_description": "ПĐĩŅ€Đ˛Ņ‹Đš ĐˇĐ°Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đš ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ĐąŅƒĐ´ĐĩŅ‚ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ. В даĐģҌĐŊĐĩĐšŅˆĐĩĐŧ ŅŅ‚ĐžĐš ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ ĐąŅƒĐ´ĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž ŅĐžĐˇĐ´Đ°ĐŊиĐĩ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊҋ҅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš и ҃ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ.", "require_password_change_on_login": "ĐĸŅ€ĐĩĐąĐžĐ˛Đ°Ņ‚ŅŒ ҁĐŧĐĩĐŊ҃ ĐŋĐ°Ņ€ĐžĐģŅ ĐŋŅ€Đ¸ ĐŋĐĩŅ€Đ˛ĐžĐŧ Đ˛Ņ…ĐžĐ´Đĩ", "reset_settings_to_default": "ĐĄĐąŅ€ĐžŅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ´Đž СĐŊĐ°Ņ‡ĐĩĐŊиК ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", - "reset_settings_to_recent_saved": "ĐĄĐąŅ€ĐžŅŅŒŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Đē ĐŋĐžŅĐģĐĩĐ´ĐŊиĐŧ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊŅ‹Đŧ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧ", + "reset_settings_to_recent_saved": "НĐĩ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊĐŊŅ‹Đĩ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ ŅĐąŅ€ĐžŅˆĐĩĐŊŅ‹ Đē ĐŋĐžŅĐģĐĩĐ´ĐŊиĐŧ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐŊŅ‹Đŧ СĐŊĐ°Ņ‡ĐĩĐŊĐ¸ŅĐŧ", "scanning_library": "ĐĄĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", - "search_jobs": "ĐŸĐžĐ¸ŅĐē СадаĐŊиКâ€Ļ", + "search_jobs": "ĐŸĐžĐ¸ŅĐē ĐˇĐ°Đ´Đ°Ņ‡â€Ļ", "send_welcome_email": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛ĐĩĐŊĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž", "server_external_domain_settings": "ВĐŊĐĩ҈ĐŊиК Đ´ĐžĐŧĐĩĐŊ", "server_external_domain_settings_description": "ДоĐŧĐĩĐŊ Đ´ĐģŅ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊҋ҅ ҁҁҋĐģĐžĐē, вĐēĐģŅŽŅ‡Đ°Ņ http(s)://", "server_public_users": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи", - "server_public_users_description": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đ˛ŅĐĩŅ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš (иĐŧĐĩĐŊа и email) Đ´ĐģŅ дОйавĐģĐĩĐŊĐ¸Ņ в ĐžĐąŅ‰Đ¸Đĩ аĐģŅŒĐąĐžĐŧŅ‹. ĐšĐžĐŗĐ´Đ° ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž, ҁĐŋĐ¸ŅĐžĐē ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐąŅƒĐ´ĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ Ņ‚ĐžĐģҌĐēĐž адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°Đŧ.", + "server_public_users_description": "Đ’Ņ‹Đ˛ĐžĐ´Đ¸Ņ‚ŅŒ ҁĐŋĐ¸ŅĐžĐē ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš (иĐŧĐĩĐŊа и email) в ĐžĐąŅ‰Đ¸Ņ… аĐģŅŒĐąĐžĐŧĐ°Ņ…. ĐšĐžĐŗĐ´Đ° ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž, ҁĐŋĐ¸ŅĐžĐē Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ Ņ‚ĐžĐģҌĐēĐž адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°Đŧ, ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи ҁĐŧĐžĐŗŅƒŅ‚ Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ Ņ‚ĐžĐģҌĐēĐž ҁҁҋĐģĐēОК.", "server_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_welcome_message": "ĐŸŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛ĐĩĐŊĐŊĐžĐĩ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ", @@ -241,8 +276,8 @@ "storage_template_hash_verification_enabled_description": "ВĐēĐģŅŽŅ‡Đ°ĐĩŅ‚ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Ņ…ĐĩŅˆĐ°, ĐŊĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ°ĐšŅ‚Đĩ ĐĩŅ‘, ĐĩҁĐģи ĐŊĐĩ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹ в ĐŋĐžŅĐģĐĩĐ´ŅŅ‚Đ˛Đ¸ŅŅ…", "storage_template_migration": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐĩĐŊиĐĩ ŅˆĐ°ĐąĐģĐžĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_template_migration_description": "ĐŸŅ€Đ¸ĐŧĐĩĐŊŅĐĩŅ‚ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš {template} Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ", - "storage_template_migration_info": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģОв Đ˛ŅĐĩĐŗĐ´Đ° ĐąŅƒĐ´ŅƒŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊŅŅ‚ŅŒŅŅ в ĐŊиĐļĐŊĐĩĐŧ Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đĩ. ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ в ŅˆĐ°ĐąĐģĐžĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐŋŅ€Đ¸ĐŧĐĩĐŊŅŅ‚ŅŒŅŅ Ņ‚ĐžĐģҌĐēĐž Đē ĐŊĐžĐ˛Ņ‹Đŧ Ņ€ĐĩŅŅƒŅ€ŅĐ°Đŧ. Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ Ņ€ĐĩŅŅƒŅ€ŅĐ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ {job}.", - "storage_template_migration_job": "ЗадаĐŊиĐĩ ĐŋĐž ĐŋŅ€Đ¸ĐŧĐĩĐŊĐĩĐŊĐ¸ŅŽ ŅˆĐ°ĐąĐģĐžĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", + "storage_template_migration_info": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģОв Đ˛ŅĐĩĐŗĐ´Đ° ĐąŅƒĐ´ŅƒŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊŅŅ‚ŅŒŅŅ в ĐŊиĐļĐŊĐĩĐŧ Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đĩ. ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ в ŅˆĐ°ĐąĐģĐžĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐŋŅ€Đ¸ĐŧĐĩĐŊŅŅ‚ŅŒŅŅ Ņ‚ĐžĐģҌĐēĐž Đē ĐŊĐžĐ˛Ņ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ. Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅˆĐ°ĐąĐģĐžĐŊ Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ {job}.", + "storage_template_migration_job": "Đ—Đ°Đ´Đ°Ņ‡Đ° ĐŋĐž ĐŋŅ€Đ¸ĐŧĐĩĐŊĐĩĐŊĐ¸ŅŽ ŅˆĐ°ĐąĐģĐžĐŊа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_template_more_details": "ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ Ой ŅŅ‚ĐžĐš Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Ņ€Đ°ĐˇĐ´ĐĩĐģаĐŧ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸ ШайĐģĐžĐŊ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° и ĐĄŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ° Ņ…Ņ€Đ°ĐŊĐĩĐŊĐ¸Ņ Ņ„Đ°ĐšĐģОв", "storage_template_onboarding_description_v2": "Đ•ŅĐģи ŅŅ‚Đ° Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ вĐēĐģŅŽŅ‡ĐĩĐŊа, ĐžĐŊа Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇŅƒĐĩŅ‚ Ņ„Đ°ĐšĐģŅ‹ ĐŊа ĐžŅĐŊОвĐĩ СадаĐŊĐŊĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ ŅˆĐ°ĐąĐģĐžĐŊа. ДĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Đ¸.", "storage_template_path_length": "ĐŸŅ€Đ¸ĐŧĐĩŅ€ĐŊŅ‹Đš ĐŋŅ€ĐĩĐ´ĐĩĐģ Đ´ĐģиĐŊŅ‹ ĐŋŅƒŅ‚Đ¸: {length, number}/{limit, number}", @@ -331,10 +366,13 @@ "trash_number_of_days_description": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž Đ´ĐŊĐĩĐš, в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… Ņ„Đ°ĐšĐģŅ‹ ĐąŅƒĐ´ŅƒŅ‚ Ņ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒŅŅ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ Đ´Đž ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ", "trash_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹", "trash_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹", + "unlink_all_oauth_accounts": "ĐžŅ‚Đ˛ŅĐˇĐ°Ņ‚ŅŒ Đ˛ŅĐĩ ŅƒŅ‡ĐĩŅ‚ĐŊŅ‹Đĩ СаĐŋĐ¸ŅĐ¸ OAuth", + "unlink_all_oauth_accounts_description": "НĐĩ ĐˇĐ°ĐąŅƒĐ´ŅŒŅ‚Đĩ ĐžŅ‚Đ˛ŅĐˇĐ°Ņ‚ŅŒ Đ˛ŅĐĩ ŅƒŅ‡ĐĩŅ‚ĐŊŅ‹Đĩ СаĐŋĐ¸ŅĐ¸ OAuth ĐŋĐĩŅ€ĐĩĐ´ ĐŧĐ¸ĐŗŅ€Đ°Ņ†Đ¸ĐĩĐš Đē ĐŊОвОĐŧ҃ ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€Ņƒ.", + "unlink_all_oauth_accounts_prompt": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚Đ˛ŅĐˇĐ°Ņ‚ŅŒ Đ˛ŅĐĩ ŅƒŅ‡ĐĩŅ‚ĐŊŅ‹Đĩ СаĐŋĐ¸ŅĐ¸ OAuth? Đ­Ņ‚Đž ĐŋŅ€Đ¸Đ˛ĐĩĐ´ĐĩŅ‚ Đē ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐžĐŧ҃ ŅĐąŅ€ĐžŅŅƒ OAuth ID Đ´ĐģŅ ĐēаĐļĐ´ĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ.", "user_cleanup_job": "ĐžŅ‡Đ¸ŅŅ‚Đēа ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "user_delete_delay": "АĐēĐēĐ°ŅƒĐŊŅ‚ и Ņ„Đ°ĐšĐģŅ‹ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user} ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ĐģĐžĐļĐĩĐŊŅ‹ Đ´Đž ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ ҇ĐĩŅ€ĐĩС {delay, plural, one {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}.", "user_delete_delay_settings": "ĐžŅ‚ĐģĐžĐļĐĩĐŊĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ", - "user_delete_delay_settings_description": "ĐĄŅ€ĐžĐē в Đ´ĐŊŅŅ…, ĐŋĐž Đ¸ŅŅ‚Đĩ҇ĐĩĐŊиĐĩ ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž ĐŋŅ€ĐžĐ¸ŅŅ…ĐžĐ´Đ¸Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и ĐĩĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛. Đ—Đ°Đ´Đ°Ņ‡Đ° ĐŋĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ в ĐŋĐžĐģĐŊĐžŅ‡ŅŒ. ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ ŅŅ‚ĐžĐš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐąŅƒĐ´ŅƒŅ‚ ŅƒŅ‡Ņ‚ĐĩĐŊŅ‹ ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ СаĐŋ҃ҁĐēĐĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸.", + "user_delete_delay_settings_description": "ĐĄŅ€ĐžĐē в Đ´ĐŊŅŅ…, ĐŋĐž Đ¸ŅŅ‚Đĩ҇ĐĩĐŊии ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž ĐŋŅ€ĐžĐ¸ŅŅ…ĐžĐ´Đ¸Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐĩ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ŅƒŅ‡Ņ‘Ņ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и Đ˛ŅĐĩŅ… ĐĩĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. Đ—Đ°Đ´Đ°Ņ‡Đ° ĐŋĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ в ĐŋĐžĐģĐŊĐžŅ‡ŅŒ. ИСĐŧĐĩĐŊĐĩĐŊиĐĩ ŅŅ‚ĐžĐš ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐąŅƒĐ´ĐĩŅ‚ ŅƒŅ‡Ņ‚ĐĩĐŊĐž ĐŋŅ€Đ¸ ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ СаĐŋ҃ҁĐēĐĩ ĐˇĐ°Đ´Đ°Ņ‡Đ¸.", "user_delete_immediately": "АĐēĐēĐ°ŅƒĐŊŅ‚ и Ņ„Đ°ĐšĐģŅ‹ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user} ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊŅ‹ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ.", "user_delete_immediately_checkbox": "ПоĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ и ĐĩĐŗĐž Ņ„Đ°ĐšĐģŅ‹ в ĐžŅ‡ĐĩŅ€ĐĩĐ´ŅŒ Đ´ĐģŅ ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ", "user_details": "ДаĐŊĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", @@ -357,28 +395,31 @@ "admin_password": "ĐŸĐ°Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "administration": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", "advanced": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ", - "advanced_settings_enable_alternate_media_filter_subtitle": "Đ˜ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ Đ´ĐģŅ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Đ°Ņ†Đ¸Đ¸ ĐŧĐĩĐ´Đ¸Đ°Ņ„Đ°ĐšĐģОв вО Đ˛Ņ€ĐĩĐŧŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐŊа ĐžŅĐŊОвĐĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊҋ҅ ĐēŅ€Đ¸Ņ‚ĐĩŅ€Đ¸Đĩв. ĐŸŅ€ĐžĐąŅƒĐšŅ‚Đĩ Ņ‚ĐžĐģҌĐēĐž в Ņ‚ĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ, ĐĩҁĐģи ҃ Đ˛Đ°Ņ ĐĩŅŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ҁ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩĐŧ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", - "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНĐĸАЛĐŦНО] Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Đ° ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ аĐģŅŒĐąĐžĐŧОв аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛", + "advanced_settings_enable_alternate_media_filter_subtitle": "ĐŸĐžĐ´ĐąĐžŅ€ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ´ĐģŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐŊа ĐžŅĐŊОвĐĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊҋ҅ ĐēŅ€Đ¸Ņ‚ĐĩŅ€Đ¸Đĩв. ĐŸŅ€ĐžĐąŅƒĐšŅ‚Đĩ вĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž в Ņ‚ĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ, ĐĩҁĐģи в ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊии ĐĩŅŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ҁ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩĐŧ Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", + "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНĐĸАЛĐŦНО] Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊĐžĐŗĐž ҁĐŋĐžŅĐžĐąĐ° ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ аĐģŅŒĐąĐžĐŧОв ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "advanced_settings_log_level_title": "ĐŖŅ€ĐžĐ˛ĐĩĐŊҌ ĐģĐžĐŗĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ: {level}", - "advanced_settings_prefer_remote_subtitle": "НĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ĐžŅ‡ĐĩĐŊҌ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°ŅŽŅ‚ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ŅƒĐšŅ‚Đĩ ŅŅ‚Ņƒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐē҃, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Đ˛ŅĐĩĐŗĐ´Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐģĐ¸ŅŅŒ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", + "advanced_settings_prefer_remote_subtitle": "НĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ĐžŅ‡ĐĩĐŊҌ ĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°ŅŽŅ‚ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹. АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ŅƒĐšŅ‚Đĩ ŅŅ‚Ņƒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐē҃, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Đ˛ŅĐĩĐŗĐ´Đ° ĐˇĐ°ĐŗŅ€ŅƒĐļаĐģĐ¸ŅŅŒ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°.", "advanced_settings_prefer_remote_title": "ĐŸŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚Đž ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", "advanced_settings_proxy_headers_subtitle": "ОĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēŅĐ¸-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Immich Đ´ĐžĐģĐļĐĩĐŊ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ҁ ĐēаĐļĐ´Ņ‹Đŧ ҁĐĩŅ‚ĐĩĐ˛Ņ‹Đŧ СаĐŋŅ€ĐžŅĐžĐŧ", "advanced_settings_proxy_headers_title": "Đ—Đ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēŅĐ¸", + "advanced_settings_readonly_mode_subtitle": "ВĐēĐģŅŽŅ‡Đ°ĐĩŅ‚ Ņ€ĐĩĐļиĐŧ, в ĐēĐžŅ‚ĐžŅ€ĐžĐŧ ĐŧĐžĐļĐŊĐž Ņ‚ĐžĐģҌĐēĐž ĐŋŅ€ĐžŅĐŧĐ°Ņ‚Ņ€Đ¸Đ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ. Đ¤ŅƒĐŊĐēŅ†Đ¸Đ¸ Đ˛Ņ‹ĐąĐžŅ€Đ° ĐŊĐĩҁĐēĐžĐģҌĐēĐ¸Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛, ĐŋŅƒĐąĐģиĐēĐ°Ņ†Đ¸Đ¸, Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸ и ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹. ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ/ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ Ņ€ĐĩĐļиĐŧ ĐŧĐžĐļĐŊĐž ŅƒĐ´ĐĩŅ€ĐļĐ¸Đ˛Đ°Ņ СĐŊĐ°Ņ‡ĐžĐē Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐŊа ĐŗĐģавĐŊĐžĐŧ ŅĐēŅ€Đ°ĐŊĐĩ.", + "advanced_settings_readonly_mode_title": "Đ ĐĩĐļиĐŧ ÂĢŅ‚ĐžĐģҌĐēĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Âģ", "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚ŅŒ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°. ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊҋ҅ ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžĐ˛.", "advanced_settings_self_signed_ssl_title": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ŅĐ°ĐŧĐžĐŋОдĐŋĐ¸ŅĐ°ĐŊĐŊŅ‹Đĩ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚Ņ‹", - "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģŅŅ‚ŅŒ иĐģи Đ˛ĐžŅŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚ ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ, ĐēĐžĐŗĐ´Đ° ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ҇ĐĩŅ€ĐĩС вĐĩĐą-иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ", - "advanced_settings_sync_remote_deletions_title": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒĐ´Đ°ĐģĐĩĐŊĐŊҋ҅ ŅƒĐ´Đ°ĐģĐĩĐŊиК [ЭКСПЕРИМЕНĐĸАЛĐŦНО]", + "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģŅŅ‚ŅŒ иĐģи Đ˛ĐžŅŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ, ĐēĐžĐŗĐ´Đ° ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ Đ˛Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ҇ĐĩŅ€ĐĩС вĐĩĐą-иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ", + "advanced_settings_sync_remote_deletions_title": "[ЭКСПЕРИМЕНĐĸАЛĐŦНО] ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "advanced_settings_tile_subtitle": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", - "advanced_settings_troubleshooting_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ€Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ вОСĐŧĐžĐļĐŊĐžŅŅ‚Đ¸ Đ´ĐģŅ Ņ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", - "advanced_settings_troubleshooting_title": "Đ Đĩ҈ĐĩĐŊиĐĩ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", + "advanced_settings_troubleshooting_subtitle": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ€Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ‹Đĩ вОСĐŧĐžĐļĐŊĐžŅŅ‚Đ¸ Đ´ĐģŅ Đ´Đ¸Đ°ĐŗĐŊĐžŅŅ‚Đ¸Đēи и Ņ€Đĩ҈ĐĩĐŊĐ¸Ņ ĐŋŅ€ĐžĐąĐģĐĩĐŧ", + "advanced_settings_troubleshooting_title": "Đ ĐĩĐļиĐŧ Đ´Đ¸Đ°ĐŗĐŊĐžŅŅ‚Đ¸Đēи", "age_months": "{months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", "age_year_months": "1 ĐŗĐžĐ´ {months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", "age_years": "{years, plural, one {# ĐŗĐžĐ´} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}}", "album_added": "АĐģŅŒĐąĐžĐŧ дОйавĐģĐĩĐŊ", - "album_added_notification_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ, ĐēĐžĐŗĐ´Đ° Đ˛Ņ‹ дОйавĐģĐĩĐŊŅ‹ Đē ĐžĐąŅ‰ĐĩĐŧ҃ аĐģŅŒĐąĐžĐŧ҃", + "album_added_notification_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ, ĐēĐžĐŗĐ´Đ° ваĐŧ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģи Đ´ĐžŅŅ‚ŅƒĐŋ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "album_cover_updated": "ОбĐģĐžĐļĐēа аĐģŅŒĐąĐžĐŧа ОйĐŊОвĐģĐĩĐŊа", "album_delete_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ {album}?", "album_delete_confirmation_description": "Đ•ŅĐģи аĐģŅŒĐąĐžĐŧ ĐąŅ‹Đģ ĐžĐąŅ‰Đ¸Đŧ, Đ´Ņ€ŅƒĐŗĐ¸Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐŗŅƒŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đē ĐŊĐĩĐŧ҃ Đ´ĐžŅŅ‚ŅƒĐŋ.", + "album_deleted": "АĐģŅŒĐąĐžĐŧ ŅƒĐ´Đ°ĐģŅ‘ĐŊ", "album_info_card_backup_album_excluded": "ИСКЛЮЧЕН", "album_info_card_backup_album_included": "ВКЛЮЧЕН", "album_info_updated": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой аĐģŅŒĐąĐžĐŧĐĩ ОйĐŊОвĐģĐĩĐŊа", @@ -388,9 +429,11 @@ "album_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ аĐģŅŒĐąĐžĐŧа", "album_remove_user": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ?", "album_remove_user_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}?", - "album_share_no_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅŅ‚Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", + "album_search_not_found": "НĐĩ ĐŊаКдĐĩĐŊĐž аĐģŅŒĐąĐžĐŧОв ĐŋĐž Đ˛Đ°ŅˆĐĩĐŧ҃ СаĐŋŅ€ĐžŅŅƒ", + "album_share_no_users": "НĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ аĐģŅŒĐąĐžĐŧĐžĐŧ.", + "album_summary": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой аĐģŅŒĐąĐžĐŧĐĩ", "album_updated": "АĐģŅŒĐąĐžĐŧ ОйĐŊОвĐģŅ‘ĐŊ", - "album_updated_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐŊĐžĐ˛Ņ‹Ņ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", + "album_updated_setting_description": "ПоĐģŅƒŅ‡Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "album_user_left": "Đ’Ņ‹ ĐŋĐžĐēиĐŊ҃Đģи {album}", "album_user_removed": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {user} ŅƒĐ´Đ°ĐģĐĩĐŊ", "album_viewer_appbar_delete_confirm": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ иС ŅĐ˛ĐžĐĩĐš ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸?", @@ -399,7 +442,7 @@ "album_viewer_appbar_share_err_remove": "ВозĐŊиĐēĐģи ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ҁ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩĐŧ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ иС аĐģŅŒĐąĐžĐŧа", "album_viewer_appbar_share_err_title": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩиĐŧĐĩĐŊĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "album_viewer_appbar_share_leave": "ПоĐēиĐŊŅƒŅ‚ŅŒ аĐģŅŒĐąĐžĐŧ", - "album_viewer_appbar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ...", + "album_viewer_appbar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ", "album_viewer_page_share_add_users": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš", "album_with_link_access": "ПодĐĩĐģĐ¸Ņ‚ĐĩҁҌ ҁҁҋĐģĐēОК ĐŊа аĐģŅŒĐąĐžĐŧ, Ņ‡Ņ‚ĐžĐąŅ‹ Đ˛Đ°ŅˆĐ¸ Đ´Ņ€ŅƒĐˇŅŒŅ ĐŧĐžĐŗĐģи ĐĩĐŗĐž ĐŋĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ.", "albums": "АĐģŅŒĐąĐžĐŧŅ‹", @@ -407,14 +450,15 @@ "albums_default_sort_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи в аĐģŅŒĐąĐžĐŧĐ°Ņ… ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "albums_default_sort_order_description": "ПĐĩŅ€Đ˛ĐžĐŊĐ°Ņ‡Đ°ĐģҌĐŊŅ‹Đš ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи, ŅƒŅŅ‚Đ°ĐŊавĐģиваĐĩĐŧŅ‹Đš в ĐŊĐžĐ˛Ņ‹Ņ… аĐģŅŒĐąĐžĐŧĐ°Ņ….", "albums_feature_description": "КоĐģĐģĐĩĐēŅ†Đ¸Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž, ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи.", + "albums_on_device_count": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ({count})", "all": "Đ’ŅĐĩ", "all_albums": "Đ’ŅĐĩ аĐģŅŒĐąĐžĐŧŅ‹", "all_people": "Đ’ŅĐĩ ĐģŅŽĐ´Đ¸", "all_videos": "Đ’ŅĐĩ видĐĩĐž", - "allow_dark_mode": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŧĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", + "allow_dark_mode": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ҂ґĐŧĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ", "allow_edits": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", - "allow_public_user_to_download": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ҁĐēĐ°Ņ‡Đ¸Đ˛Đ°ĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧ", - "allow_public_user_to_upload": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹", + "allow_public_user_to_download": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ҁĐēĐ°Ņ‡Đ¸Đ˛Đ°ĐŊиĐĩ", + "allow_public_user_to_upload": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ дОйавĐģĐĩĐŊиĐĩ Ņ„Đ°ĐšĐģОв", "alt_text_qr_code": "QR-ĐēОд", "anti_clockwise": "ĐŸŅ€ĐžŅ‚Đ¸Đ˛ Ņ‡Đ°ŅĐžĐ˛ĐžĐš", "api_key": "API ĐēĐģŅŽŅ‡", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Đ’Ņ‹ĐšŅ‚Đ¸", "app_settings": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "appears_in": "ДобавĐģĐĩĐŊĐž в", + "apply_count": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ({count, number})", "archive": "ĐŅ€Ņ…Đ¸Đ˛", + "archive_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ дОйавĐģĐĩĐŊŅ‹ в ĐŅ€Ņ…Đ¸Đ˛ ({count} ŅˆŅ‚.)", "archive_or_unarchive_photo": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ иĐģи Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚Đž", "archive_page_no_archived_assets": "В Đ°Ņ€Ņ…Đ¸Đ˛Đĩ ҁĐĩĐšŅ‡Đ°Ņ ĐŋŅƒŅŅ‚Đž", "archive_page_title": "ĐŅ€Ņ…Đ¸Đ˛ ({count})", @@ -457,54 +503,62 @@ "asset_restored_successfully": "ĐžĐąŅŠĐĩĐēŅ‚ ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ", "asset_skipped": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "asset_skipped_in_trash": "В ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ", + "asset_trashed": "ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ", + "asset_troubleshoot": "ДаĐŊĐŊŅ‹Đĩ Đ´ĐģŅ Đ´Đ¸Đ°ĐŗĐŊĐžŅŅ‚Đ¸Đēи", "asset_uploaded": "Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", "asset_uploading": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēаâ€Ļ", - "asset_viewer_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", - "asset_viewer_settings_title": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиК", + "asset_viewer_settings_subtitle": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", + "asset_viewer_settings_title": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "assets": "ĐžĐąŅŠĐĩĐē҂ҋ", "assets_added_count": "{count, plural, one {ДобавĐģĐĩĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {ДобавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ДобавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", "assets_added_to_album_count": "В аĐģŅŒĐąĐžĐŧ {count, plural, one {дОйавĐģĐĩĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {дОйавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {дОйавĐģĐĩĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", - "assets_added_to_name_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ дОйавĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ дОйавĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° дОйавĐģĐĩĐŊĐž}} в {hasName, select, true {аĐģŅŒĐąĐžĐŧ {name}} other {ĐŊĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ дОйавĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ дОйавĐģĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° дОйавĐģĐĩĐŊŅ‹}} в {albumTotal, plural, one {# аĐģŅŒĐąĐžĐŧ} many {# аĐģŅŒĐąĐžĐŧОв} other {# аĐģŅŒĐąĐžĐŧа}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {ĐžĐąŅŠĐĩĐēŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ дОйавĐģĐĩĐŊ} other {ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ дОйавĐģĐĩĐŊŅ‹}} в аĐģŅŒĐąĐžĐŧ", + "assets_cannot_be_added_to_albums": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ дОйавĐģĐĩĐŊŅ‹ ĐŊи в ОдиĐŊ аĐģŅŒĐąĐžĐŧ", "assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", - "assets_deleted_permanently": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ŅƒĐ´Đ°ĐģĐĩĐŊĐž ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "assets_deleted_permanently_from_server": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", + "assets_deleted_permanently": "ĐžĐąŅŠĐĩĐē҂ҋ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ({count} ŅˆŅ‚.)", + "assets_deleted_permanently_from_server": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich ({count} ŅˆŅ‚.)", "assets_downloaded_failed": "{count, plural, one {ĐĄĐēĐ°Ņ‡Đ°ĐŊ # Ņ„Đ°ĐšĐģ} many {ĐĄĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģОв} other {ĐĄĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģа}}, {error} - ŅĐąĐžĐš", "assets_downloaded_successfully": "ĐŖŅĐŋĐĩ҈ĐŊĐž {count, plural, one {ҁĐēĐ°Ņ‡Đ°ĐŊ # Ņ„Đ°ĐšĐģ} many {ҁĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģОв} other {ҁĐēĐ°Ņ‡Đ°ĐŊĐž # Ņ„Đ°ĐšĐģа}}", - "assets_moved_to_trash_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", + "assets_moved_to_trash_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "assets_permanently_deleted_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "assets_removed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", - "assets_removed_permanently_from_device": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", - "assets_restore_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ иС ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ! ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŊиĐŧаĐŊиĐĩ, Ņ‡Ņ‚Đž ĐģŅŽĐąŅ‹Đĩ ĐžŅ„Ņ„ĐģаКĐŊ-ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐĩ ĐŧĐžĐŗŅƒŅ‚ ĐąŅ‹Ņ‚ŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹ Ņ‚Đ°ĐēиĐŧ ҁĐŋĐžŅĐžĐąĐžĐŧ.", - "assets_restored_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž}}", - "assets_restored_successfully": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž", - "assets_trashed": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŋĐžĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "assets_trashed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "assets_trashed_from_server": "{count} ĐžĐąŅŠĐĩĐēŅ‚(Ов) ĐŋĐžĐŧĐĩ҉ĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Immich", + "assets_removed_permanently_from_device": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ({count} ŅˆŅ‚.)", + "assets_restore_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ иС ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ! ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ вĐŊиĐŧаĐŊиĐĩ, ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹ Ņ‚Đ°ĐēиĐŧ ҁĐŋĐžŅĐžĐąĐžĐŧ.", + "assets_restored_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹}}", + "assets_restored_successfully": "ĐžĐąŅŠĐĩĐē҂ҋ ҃ҁĐŋĐĩ҈ĐŊĐž Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹ ({count} ŅˆŅ‚.)", + "assets_trashed": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ({count} ŅˆŅ‚.)", + "assets_trashed_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹}} в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", + "assets_trashed_from_server": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Immich ({count} ŅˆŅ‚.)", "assets_were_part_of_album_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} ҃ĐļĐĩ в аĐģŅŒĐąĐžĐŧĐĩ", + "assets_were_part_of_albums_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} ҃ĐļĐĩ в аĐģŅŒĐąĐžĐŧĐ°Ņ…", "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "automatic_endpoint_switching_subtitle": "ПодĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ ĐģĐžĐēаĐģҌĐŊĐž ĐŋĐž Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК ҁĐĩŅ‚Đ¸ и Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ‹Đĩ Đ°Đ´Ņ€ĐĩŅĐ° в иĐŊĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ", "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐ°Ņ ҁĐŧĐĩĐŊа URL", "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒ", "back": "Назад", "back_close_deselect": "Назад, СаĐēŅ€Ņ‹Ņ‚ŅŒ иĐģи ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€", + "background_backup_running_error": "Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ Ņ„ĐžĐŊОвОĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ, СаĐŋ҃ҁĐē Đ˛Ņ€ŅƒŅ‡ĐŊŅƒŅŽ ĐŋĐžĐēа ĐŊĐĩвОСĐŧĐžĐļĐĩĐŊ", "background_location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ в Ņ„ĐžĐŊĐĩ", "background_location_permission_content": "Đ§Ņ‚ĐžĐąŅ‹ ŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸ в Ņ„ĐžĐŊĐĩ, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ *Đ˛ŅĐĩĐŗĐ´Đ°* ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸Đŧ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ‚ĐžŅ‡ĐŊĐžĐŧ҃ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "background_options": "Đ’Ņ‹ĐŋĐžĐģĐŊĐĩĐŊиĐĩ Ņ„ĐžĐŊĐžĐ˛Ņ‹Ņ… ĐˇĐ°Đ´Đ°Ņ‡", + "backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ({count})", "backup_album_selection_page_albums_tap": "НаĐļĐŧĐ¸Ņ‚Đĩ, Ņ‡Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, дваĐļĐ´Ņ‹, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", - "backup_album_selection_page_assets_scatter": "Đ’Đ°ŅˆĐ¸ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ и видĐĩĐž ĐŧĐžĐŗŅƒŅ‚ ĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚ŅŒŅŅ в Ņ€Đ°ĐˇĐŊҋ҅ аĐģŅŒĐąĐžĐŧĐ°Ņ…. Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ Đ˛Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ, ĐēаĐēиĐĩ аĐģŅŒĐąĐžĐŧŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, а ĐēаĐēиĐĩ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иС Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ.", + "backup_album_selection_page_assets_scatter": "Đ’Đ°ŅˆĐ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž ĐŧĐžĐŗŅƒŅ‚ ĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚ŅŒŅŅ в Ņ€Đ°ĐˇĐŊҋ҅ аĐģŅŒĐąĐžĐŧĐ°Ņ…/ĐŋаĐŋĐēĐ°Ņ… ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ. Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ Đ˛Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ, ĐēаĐēиĐĩ аĐģŅŒĐąĐžĐŧŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ, а ĐēаĐēиĐĩ Đ¸ŅĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ иС Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ.", "backup_album_selection_page_select_albums": "Đ’Ņ‹ĐąĐžŅ€ аĐģŅŒĐąĐžĐŧОв", - "backup_album_selection_page_selection_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Đ˛Ņ‹ĐąĐžŅ€Đĩ", + "backup_album_selection_page_selection_info": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ аĐģŅŒĐąĐžĐŧŅ‹", "backup_album_selection_page_total_assets": "Đ’ŅĐĩĐŗĐž ҃ĐŊиĐēаĐģҌĐŊҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "backup_albums_sync": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ аĐģŅŒĐąĐžĐŧОв", "backup_all": "Đ’ŅĐĩ", "backup_background_service_backup_failed_message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ ĐŋĐžĐŋҋ҂Đēаâ€Ļ", "backup_background_service_connection_failed_message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ ĐŋĐžĐŋҋ҂Đēаâ€Ļ", "backup_background_service_current_upload_notification": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ {filename}", "backup_background_service_default_notification": "ĐŸĐžĐ¸ŅĐē ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛â€Ļ", "backup_background_service_error_title": "ĐžŅˆĐ¸ĐąĐēа Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", - "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Đ˛Đ°ŅˆĐ¸Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛â€Ļ", + "backup_background_service_in_progress_notification": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛â€Ļ", "backup_background_service_upload_failure_notification": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи {filename}", - "backup_controller_page_albums": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ аĐģŅŒĐąĐžĐŧОв", + "backup_controller_page_albums": "АĐģŅŒĐąĐžĐŧŅ‹", "backup_controller_page_background_app_refresh_disabled_content": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ„ĐžĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ в ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи > ĐžĐąŅ‰Đ¸Đĩ > ФОĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžĐŊОвОĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ.", "backup_controller_page_background_app_refresh_disabled_title": "ФОĐŊОвОĐĩ ОйĐŊОвĐģĐĩĐŊиĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_background_app_refresh_enable_button_text": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи", @@ -514,15 +568,15 @@ "backup_controller_page_background_battery_info_title": "ОĐŋŅ‚Đ¸ĐŧĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐąĐ°Ņ‚Đ°Ņ€Đĩи", "backup_controller_page_background_charging": "ĐĸĐžĐģҌĐēĐž вО Đ˛Ņ€ĐĩĐŧŅ ĐˇĐ°Ņ€ŅĐ´Đēи", "backup_controller_page_background_configure_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", - "backup_controller_page_background_delay": "ĐžŅ‚ĐģĐžĐļĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛: {duration}", + "backup_controller_page_background_delay": "ЗадĐĩŅ€ĐļĐēа ĐŋĐĩŅ€ĐĩĐ´ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēОК ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛: {duration}", "backup_controller_page_background_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ Đ´ĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐģŅŽĐąŅ‹Ņ… ĐŊĐžĐ˛Ņ‹Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐąĐĩС ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ", "backup_controller_page_background_is_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_background_is_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž", "backup_controller_page_background_turn_off": "Đ’Ņ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", "backup_controller_page_background_turn_on": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ„ĐžĐŊĐžĐ˛ŅƒŅŽ ҁĐģ҃ĐļĐąŅƒ", "backup_controller_page_background_wifi": "ĐĸĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС Wi-Fi", - "backup_controller_page_backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", - "backup_controller_page_backup_selected": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž: ", + "backup_controller_page_backup": "Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", + "backup_controller_page_backup_selected": "Đ’Ņ‹ĐąŅ€Đ°ĐŊŅ‹: ", "backup_controller_page_backup_sub": "Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž", "backup_controller_page_created": "ХОСдаĐŊĐž: {date}", "backup_controller_page_desc_backup": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋŅ€Đ¸ ĐžŅ‚ĐēŅ€Ņ‹Ņ‚Đ¸Đ¸ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ.", @@ -531,7 +585,7 @@ "backup_controller_page_filename": "ИĐŧŅ Ņ„Đ°ĐšĐģа: {filename} [{size}]", "backup_controller_page_id": "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": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", "backup_controller_page_uploading_file_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž ĐˇĐ°ĐŗŅ€ŅƒĐļаĐĩĐŧĐžĐŧ Ņ„Đ°ĐšĐģĐĩ", "backup_err_only_album": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐĩдиĐŊŅŅ‚Đ˛ĐĩĐŊĐŊŅ‹Đš аĐģŅŒĐąĐžĐŧ", + "backup_error_sync_failed": "ХйОК ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. НĐĩвОСĐŧĐžĐļĐŊĐž Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ.", "backup_info_card_assets": "ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "backup_manual_cancelled": "ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", "backup_manual_in_progress": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в ĐŋŅ€ĐžŅ†ĐĩҁҁĐĩ. ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ĐŋОСĐļĐĩ", "backup_manual_success": "ĐŖŅĐŋĐĩ҈ĐŊĐž", "backup_manual_title": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", + "backup_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "backup_options_page_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "backup_setting_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŗĐž и Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", + "backup_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "backward": "Назад", "biometric_auth_enabled": "БиоĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēĐ°Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ вĐēĐģŅŽŅ‡ĐĩĐŊа", "biometric_locked_out": "ВаĐŧ СаĐēҀҋ҂ Đ´ĐžŅŅ‚ŅƒĐŋ Đē йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", @@ -560,7 +617,7 @@ "birthdate_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", "birthdate_set_description": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊĐ¸Ņ Đ˛ĐžĐˇŅ€Đ°ŅŅ‚Đ° ҇ĐĩĐģОвĐĩĐēа ĐŊа ĐŧĐžĐŧĐĩĐŊŅ‚ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸.", "blurred_background": "РаСĐŧŅ‹Ņ‚Ņ‹Đš Ņ„ĐžĐŊ", - "bugs_and_feature_requests": "ĐžŅˆĐ¸ĐąĐēи и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", + "bugs_and_feature_requests": "ĐžŅˆĐ¸ĐąĐēи и СаĐŋŅ€ĐžŅŅ‹", "build": "ĐĄĐąĐžŅ€Đēа", "build_image": "ВĐĩŅ€ŅĐ¸Ņ ŅĐąĐžŅ€Đēи", "bulk_delete_duplicates_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐšŅŅ ĐžĐąŅŠĐĩĐēŅ‚} many {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# Đ´ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸Ņ…ŅŅ ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ‘ŅƒĐ´ĐĩŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅĐ°ĐŧŅ‹Đš йОĐģŅŒŅˆĐžĐš Ņ„Đ°ĐšĐģ в ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋĐĩ, а ĐĩĐŗĐž Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹. Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", @@ -570,8 +627,8 @@ "cache_settings_clear_cache_button": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēŅŅˆ", "cache_settings_clear_cache_button_title": "ĐžŅ‡Đ¸Ņ‰Đ°ĐĩŅ‚ ĐēŅŅˆ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ. Đ­Ņ‚Đž ĐŊĐĩĐŗĐ°Ņ‚Đ¸Đ˛ĐŊĐž ĐŋОвĐģĐ¸ŅĐĩŅ‚ ĐŊа ĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ĐĩĐģҌĐŊĐžŅŅ‚ŅŒ, ĐŋĐžĐēа ĐēŅŅˆ ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ ŅĐžĐˇĐ´Đ°ĐŊ СаĐŊОвО.", "cache_settings_duplicated_assets_clear_button": "ОЧИСĐĸИĐĸĐŦ", - "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž и видĐĩĐž, СаĐŊĐĩҁĐĩĐŊĐŊŅ‹Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ в ҇ĐĩŅ€ĐŊŅ‹Đš ҁĐŋĐ¸ŅĐžĐē", - "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģĐ¸Ņ€ŅƒŅŽŅ‰Đ¸ĐĩŅŅ ĐžĐąŅŠĐĩĐē҂ҋ ({count})", + "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž и видĐĩĐž, ĐŋŅ€ĐžĐŋ҃ҁĐēаĐĩĐŧŅ‹Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ", + "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ({count})", "cache_settings_statistics_album": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "cache_settings_statistics_full": "ПоĐģĐŊŅ‹Đĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "cache_settings_statistics_shared": "МиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹ ĐžĐąŅ‰Đ¸Ņ… аĐģŅŒĐąĐžĐŧОв", @@ -587,15 +644,16 @@ "cancel": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", "cancel_search": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē", "canceled": "ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊĐž", + "canceling": "ĐžŅ‚ĐŧĐĩĐŊа", "cannot_merge_people": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "cannot_undo_this_action": "Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐģŅŒĐˇŅ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ!", "cannot_update_the_description": "НĐĩвОСĐŧĐžĐļĐŊĐž ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", - "cast": "ĐĸŅ€Đ°ĐŊҁĐģĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", - "cast_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅ ҆ĐĩĐģĐĩĐš Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸", + "cast": "ĐĸŅ€Đ°ĐŊҁĐģŅŅ†Đ¸Ņ", + "cast_description": "Đ’Ņ‹ĐąĐžŅ€ Đ´ĐžŅŅ‚ŅƒĐŋĐŊҋ҅ ҁĐŋĐžŅĐžĐąĐžĐ˛ Đ´ĐģŅ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸", "change_date": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", "change_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "change_display_order": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžŅ€ŅĐ´ĐžĐē ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", - "change_expiration_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ€ĐĩĐŧŅ ĐžĐēĐžĐŊŅ‡Đ°ĐŊĐ¸Ņ", + "change_expiration_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ", "change_location": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "change_name": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ иĐŧŅ", "change_name_successfully": "ИĐŧŅ ҃ҁĐŋĐĩ҈ĐŊĐž иСĐŧĐĩĐŊĐĩĐŊĐž", @@ -609,6 +667,8 @@ "change_pin_code": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ PIN-ĐēОд", "change_your_password": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŋĐ°Ņ€ĐžĐģҌ", "changed_visibility_successfully": "ВидиĐŧĐžŅŅ‚ŅŒ ҃ҁĐŋĐĩ҈ĐŊĐž иСĐŧĐĩĐŊĐĩĐŊа", + "charging": "ĐŸŅ€Đ¸ ĐˇĐ°Ņ€ŅĐ´ĐēĐĩ", + "charging_requirement_mobile_backup": "ЗаĐŋ҃ҁĐēĐ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐĩ ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Ņ‚ĐžĐģҌĐēĐž вО Đ˛Ņ€ĐĩĐŧŅ ĐˇĐ°Ņ€ŅĐ´Đēи", "check_corrupt_asset_backup": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа ĐŋĐžĐ˛Ņ€ĐĩĐļĐ´ĐĩĐŊĐŊҋ҅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊҋ҅ ĐēĐžĐŋиК", "check_corrupt_asset_backup_button": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ", "check_corrupt_asset_backup_description": "ЗаĐŋ҃ҁĐēĐ°ĐšŅ‚Đĩ ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃ Ņ‚ĐžĐģҌĐēĐž ҇ĐĩŅ€ĐĩС Wi-Fi и ĐŋĐžŅĐģĐĩ ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊОК ĐēĐžĐŋии Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛. ОĐŋĐĩŅ€Đ°Ņ†Đ¸Ņ ĐŧĐžĐļĐĩŅ‚ СаĐŊŅŅ‚ŅŒ ĐŊĐĩҁĐēĐžĐģҌĐēĐž ĐŧиĐŊŅƒŅ‚.", @@ -618,6 +678,7 @@ "clear": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ", "clear_all": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Đ˛ŅŅ‘", "clear_all_recent_searches": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐŊĐĩдавĐŊиĐĩ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ‹ ĐŋĐžĐ¸ŅĐēа", + "clear_file_cache": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Ņ„Đ°ĐšĐģĐžĐ˛Ņ‹Đš ĐēŅŅˆ", "clear_message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ", "clear_value": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ СĐŊĐ°Ņ‡ĐĩĐŊиĐĩ", "client_cert_dialog_msg_confirm": "OK", @@ -635,8 +696,8 @@ "color": "ĐĻвĐĩŅ‚", "color_theme": "ĐĻвĐĩŅ‚ĐžĐ˛Đ°Ņ Ņ‚ĐĩĐŧа", "comment_deleted": "КоĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đš ŅƒĐ´Đ°ĐģŅ‘ĐŊ", - "comment_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đĩв", - "comments_and_likes": "КоĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đ¸ и ĐģаКĐēи", + "comment_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ĐĩĐŧ", + "comments_and_likes": "КоĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đ¸ и ĐžŅ‚ĐŧĐĩŅ‚Đēи \"ĐŊŅ€Đ°Đ˛Đ¸Ņ‚ŅŅ\"", "comments_are_disabled": "КоĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊŅ‹", "common_create_new_album": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ", "common_server_error": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Đē ҁĐĩŅ‚Đ¸ и ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Đ°Ņˆ ҁĐĩŅ€Đ˛ĐĩŅ€ Đ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ, а вĐĩŅ€ŅĐ¸Đ¸ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ и ҁĐĩŅ€Đ˛ĐĩŅ€Đ° — ŅĐžĐ˛ĐŧĐĩŅŅ‚Đ¸ĐŧŅ‹.", @@ -644,7 +705,7 @@ "confirm": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", "confirm_admin_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "confirm_delete_face": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа {name} иС ŅŅ‚ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°?", - "confirm_delete_shared_link": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃?", + "confirm_delete_shared_link": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃?", "confirm_keep_this_delete_others": "Đ’ŅĐĩ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐŗŅ€ŅƒĐŋĐŋĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹, ĐēŅ€ĐžĐŧĐĩ ŅŅ‚ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ?", "confirm_new_pin_code": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš PIN-ĐēОд", "confirm_password": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ", @@ -660,12 +721,12 @@ "control_bottom_app_bar_delete_from_local": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ҁ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "control_bottom_app_bar_edit_location": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚Đž", "control_bottom_app_bar_edit_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", - "control_bottom_app_bar_share_link": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁҁҋĐģĐēОК", - "control_bottom_app_bar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ...", + "control_bottom_app_bar_share_link": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃", + "control_bottom_app_bar_share_to": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ", "control_bottom_app_bar_trash_from_immich": "В ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "copied_image_to_clipboard": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ҁĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа.", "copied_to_clipboard": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа!", - "copy_error": "ĐžŅˆĐ¸ĐąĐēа ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", + "copy_error": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐē҃", "copy_file_path": "КоĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ Đē Ņ„Đ°ĐšĐģ҃", "copy_image": "КоĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "copy_link": "КоĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃", @@ -681,18 +742,20 @@ "create_library": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "create_link": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃", "create_link_to_share": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐžĐąŅ‰ĐĩĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", - "create_link_to_share_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Đ˛ŅĐĩĐŧ, ҃ ĐēĐžĐŗĐž ĐĩŅŅ‚ŅŒ ҁҁҋĐģĐēа, ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", + "create_link_to_share_description": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Đ˛ŅĐĩĐŧ, ҃ ĐēĐžĐŗĐž ĐĩŅŅ‚ŅŒ ҁҁҋĐģĐēа, ĐŋŅ€ĐžŅĐŧĐ°Ņ‚Ņ€Đ¸Đ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", "create_new": "СОЗДАĐĸĐŦ НОВĐĢЙ", "create_new_person": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", "create_new_person_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", "create_new_user": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "create_shared_album_page_share_add_assets": "ДОБАВИĐĸĐŦ ОБĐĒЕКĐĸĐĢ", "create_shared_album_page_share_select_photos": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", + "create_shared_link": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐžĐąŅ‰ŅƒŅŽ ҁҁҋĐģĐē҃", "create_tag": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ Ņ‚ĐĩĐŗ", "create_tag_description": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ ĐŊĐžĐ˛Ņ‹Đš Ņ‚ĐĩĐŗ. ДĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ‚ĐĩĐŗĐžĐ˛ ввĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐžĐģĐŊŅ‹Đš ĐŋŅƒŅ‚ŅŒ Đē Ņ‚ĐĩĐŗŅƒ, вĐēĐģŅŽŅ‡Đ°Ņ ҁĐģŅŅˆĐ¸.", "create_user": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "created": "ХОСдаĐŊ", "created_at": "ХОСдаĐŊ", + "creating_linked_albums": "ХОСдаĐŊиĐĩ ŅĐ˛ŅĐˇĐ°ĐŊĐŊҋ҅ аĐģŅŒĐąĐžĐŧОв...", "crop": "ĐžĐąŅ€ĐĩĐˇĐ°Ņ‚ŅŒ", "curated_object_page_title": "ĐŸŅ€ĐĩĐ´ĐŧĐĩ҂ҋ", "current_device": "ĐĸĐĩĐēŅƒŅ‰ĐĩĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž", @@ -700,10 +763,11 @@ "current_server_address": "ĐĸĐĩĐēŅƒŅ‰Đ¸Đš Đ°Đ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК Ņ€ĐĩĐŗĐ¸ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ Đ´Đ°Ņ‚ и Ņ‡Đ¸ŅĐĩĐģ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ŅĐˇŅ‹Đēа и Ņ€ĐĩĐŗĐ¸ĐžĐŊа", + "custom_url": "ХвОК URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "ĐĸŅ‘ĐŧĐŊĐ°Ņ", - "darkTheme": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Ņ‚ĐĩĐŧĐŊОК Ņ‚ĐĩĐŧŅ‹", + "dark_theme": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ/Đ˛Ņ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ҂ґĐŧĐŊŅƒŅŽ Ņ‚ĐĩĐŧ҃", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅĐģĐĩ", "date_and_time": "Đ”Đ°Ņ‚Đ° и Đ’Ņ€ĐĩĐŧŅ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", @@ -711,14 +775,17 @@ "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊа", "date_range": "ДиаĐŋаСОĐŊ Đ´Đ°Ņ‚", "day": "ДĐĩĐŊҌ", + "days": "ДĐŊи", "deduplicate_all": "ĐŖĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "deduplication_criteria_1": "РаСĐŧĐĩŅ€ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ в ĐąĐ°ĐšŅ‚Đ°Ņ…", - "deduplication_criteria_2": "ĐŸĐžĐ´ŅŅ‡ĐĩŅ‚ даĐŊĐŊҋ҅ EXIF", + "deduplication_criteria_2": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž EXIF даĐŊĐŊҋ҅", "deduplication_info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Đ´ĐĩĐ´ŅƒĐŋĐģиĐēĐ°Ņ†Đ¸Đ¸", - "deduplication_info_description": "ДĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ĐŋŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊĐžĐŗĐž Đ˛Ņ‹ĐąĐžŅ€Đ° ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ и ĐŧĐ°ŅŅĐžĐ˛ĐžĐŗĐž ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛ ĐŧŅ‹ Ņ€Đ°ŅŅĐŧĐžŅ‚Ņ€Đ¸Đŧ:", + "deduplication_info_description": "ДĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž Đ˛Ņ‹ĐąĐžŅ€Đ° ĐģŅƒŅ‡ŅˆĐ¸Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ҁҀĐĩди Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛ аĐŊаĐģĐ¸ĐˇĐ¸Ņ€ŅƒĐĩŅ‚ŅŅ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ:", "default_locale": "Đ”Đ°Ņ‚Đ° и Đ˛Ņ€ĐĩĐŧŅ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "default_locale_description": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹ и Đ˛Ņ€ĐĩĐŧĐĩĐŊи в ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Đ¸ ҁ ŅĐˇŅ‹ĐēĐžĐ˛Ņ‹Đŧ ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐžĐŧ Đ˛Đ°ŅˆĐĩĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", "delete": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", + "delete_action_confirmation_message": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ ĐžĐąŅŠĐĩĐēŅ‚ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐĩŅ‚ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐĩĐŗĐž ĐģĐžĐēаĐģҌĐŊĐž.", + "delete_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ({count} ŅˆŅ‚.)", "delete_album": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "delete_api_key_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ API ĐēĐģŅŽŅ‡?", "delete_dialog_alert": "Đ­Ņ‚Đ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ ĐąŅƒĐ´ŅƒŅ‚ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊĐž ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, а Ņ‚Đ°ĐēĐļĐĩ ҁ Đ˛Đ°ŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", @@ -732,9 +799,12 @@ "delete_key": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐēĐģŅŽŅ‡", "delete_library": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "delete_link": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ҁҁҋĐģĐē҃", + "delete_local_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҁ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ° ({count} ŅˆŅ‚.)", "delete_local_dialog_ok_backed_up_only": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ‚ĐžĐģҌĐēĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ‹Đĩ ĐēĐžĐŋии", "delete_local_dialog_ok_force": "Đ’ŅĐĩ Ņ€Đ°Đ˛ĐŊĐž ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", "delete_others": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ", + "delete_permanently": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", + "delete_permanently_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ({count} ŅˆŅ‚.)", "delete_shared_link": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃", "delete_shared_link_dialog_title": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃", "delete_tag": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", @@ -745,6 +815,7 @@ "description": "ОĐŋĐ¸ŅĐ°ĐŊиĐĩ", "description_input_hint_text": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ...", "description_input_submit_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐģĐžĐŗĐ¸, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐˇĐŊĐ°Ņ‚ŅŒ ĐŋŅ€Đ¸Ņ‡Đ¸ĐŊ҃", + "deselect_all": "ĐĄĐŊŅŅ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ", "details": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸", "direction": "НаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ", "disabled": "ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž", @@ -754,7 +825,7 @@ "discovered_devices": "ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", "dismiss_all_errors": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ĐžŅˆĐ¸ĐąĐēи", "dismiss_error": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐžŅˆĐ¸ĐąĐē҃", - "display_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", + "display_options": "ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊĐž", "display_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "display_original_photos": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģҌĐŊҋ҅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš", "display_original_photos_setting_description": "ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ вĐŧĐĩŅŅ‚Đž ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Ņ‹, ĐĩҁĐģи Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đš Ņ„ĐžŅ€ĐŧĐ°Ņ‚ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€ĐžĐŧ. ВозĐŧĐžĐļĐŊĐž ҁĐŊиĐļĐĩĐŊиĐĩ ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš.", @@ -762,6 +833,7 @@ "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ", "done": "Đ“ĐžŅ‚ĐžĐ˛Đž", "download": "ĐĄĐēĐ°Ņ‡Đ°Ņ‚ŅŒ", + "download_action_prompt": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°ŅŽŅ‚ŅŅ {count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "download_canceled": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐžŅ‚ĐŧĐĩĐŊĐĩĐŊа", "download_complete": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐžĐēĐžĐŊ҇ĐĩĐŊа", "download_enqueue": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в ĐžŅ‡ĐĩŅ€Đĩди", @@ -769,7 +841,7 @@ "download_failed": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ", "download_finished": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐžĐēĐžĐŊ҇ĐĩĐŊа", "download_include_embedded_motion_videos": "Đ’ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ видĐĩĐž", - "download_include_embedded_motion_videos_description": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ видĐĩĐž, Đ˛ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ в ĐļĐ¸Đ˛Ņ‹Đĩ Ņ„ĐžŅ‚Đž, в видĐĩ ĐžŅ‚Đ´ĐĩĐģҌĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģа", + "download_include_embedded_motion_videos_description": "ĐĄĐžŅ…Ņ€Đ°ĐŊŅŅ‚ŅŒ видĐĩĐž, Đ˛ŅŅ‚Ņ€ĐžĐĩĐŊĐŊŅ‹Đĩ в ĐļĐ¸Đ˛Ņ‹Đĩ Ņ„ĐžŅ‚Đž, в видĐĩ ĐžŅ‚Đ´ĐĩĐģҌĐŊҋ҅ Ņ„Đ°ĐšĐģОв", "download_notfound": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŊĐĩ ĐŊаКдĐĩĐŊа", "download_paused": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŋŅ€Đ¸ĐžŅŅ‚Đ°ĐŊОвĐģĐĩĐŊа", "download_settings": "ĐĄĐēĐ°Ņ‡Đ¸Đ˛Đ°ĐŊиĐĩ", @@ -783,13 +855,17 @@ "downloading_media": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŧĐĩдиа", "drop_files_to_upload": "ПĐĩŅ€ĐĩĐŊĐĩŅĐ¸Ņ‚Đĩ Ņ„Đ°ĐšĐģŅ‹ в ĐģŅŽĐąĐžĐĩ ĐŧĐĩŅŅ‚Đž Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "duplicates": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", - "duplicates_description": "РаСйĐĩŅ€Đ¸Ņ‚ĐĩҁҌ ҁ ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋОК, ҃ĐēаСав, ĐēаĐēиĐĩ иС ĐŊĐ¸Ņ… ŅĐ˛ĐģŅŅŽŅ‚ŅŅ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ°Đŧи, ĐĩҁĐģи Ņ‚Đ°ĐēĐžĐ˛Ņ‹Đĩ иĐŧĐĩŅŽŅ‚ŅŅ", + "duplicates_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ¸Ņ‚Đĩ ĐŊаКдĐĩĐŊĐŊŅ‹Đĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ и в ĐēаĐļдОК ĐŗŅ€ŅƒĐŋĐŋĐĩ ҃ĐēаĐļĐ¸Ņ‚Đĩ, ĐēаĐēиĐĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ, а ĐēаĐēиĐĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", "duration": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ĐĩĐģҌĐŊĐžŅŅ‚ŅŒ", - "edit": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", - "edit_album": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", + "edit": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", + "edit_album": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "edit_avatar": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", - "edit_date": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", - "edit_date_and_time": "Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ и Đ˛Ņ€ĐĩĐŧŅ", + "edit_birthday": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", + "edit_date": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ", + "edit_date_and_time": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ и Đ˛Ņ€ĐĩĐŧŅ", + "edit_date_and_time_action_prompt": "Đ”Đ°Ņ‚Đ° и Đ˛Ņ€ĐĩĐŧŅ иСĐŧĐĩĐŊĐĩĐŊŅ‹ ҃ {count} ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "edit_date_and_time_by_offset": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ ĐŋĐž ҁĐŧĐĩ҉ĐĩĐŊĐ¸ŅŽ", + "edit_date_and_time_by_offset_interval": "ĐĐžĐ˛Ņ‹Đš диаĐŋаСОĐŊ Đ´Đ°Ņ‚: {from} - {to}", "edit_description": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "edit_description_prompt": "ĐŖĐēаĐļĐ¸Ņ‚Đĩ ĐŊОвОĐĩ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ:", "edit_exclusion_pattern": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ŅˆĐ°ĐąĐģĐžĐŊа Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", @@ -797,14 +873,15 @@ "edit_import_path": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", "edit_import_paths": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋŅƒŅ‚ŅŒ иĐŧĐŋĐžŅ€Ņ‚Đ°", "edit_key": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐēĐģŅŽŅ‡", - "edit_link": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃", - "edit_location": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "edit_link": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ҁҁҋĐģĐē҃", + "edit_location": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", + "edit_location_action_prompt": "МĐĩŅŅ‚Đ° иСĐŧĐĩĐŊĐĩĐŊŅ‹ ({count} ŅˆŅ‚.)", "edit_location_dialog_title": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", - "edit_name": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ иĐŧŅ", - "edit_people": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", + "edit_name": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ иĐŧŅ", + "edit_people": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "edit_tag": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗ", - "edit_title": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ—Đ°ĐŗĐžĐģОвОĐē", - "edit_user": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", + "edit_title": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐˇĐ°ĐŗĐžĐģОвОĐē", + "edit_user": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "edited": "ĐžŅ‚Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐž", "editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "editor_close_without_save_prompt": "ИСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", @@ -815,8 +892,9 @@ "email_notifications": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ ĐŋĐž ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ", "empty_folder": "ĐŸŅƒŅŅ‚Đ°Ņ ĐŋаĐŋĐēа", "empty_trash": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", - "empty_trash_confirmation": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃? Đ’ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Immich.\nĐ’Ņ‹ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚Đĩ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", + "empty_trash_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐēĐžŅ€ĐˇĐ¸ĐŊ҃? Đ’ŅĐĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐŊĐĩĐš ĐąŅƒĐ´ŅƒŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Immich.\nĐ’Ņ‹ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚Đĩ ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ!", "enable": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ", + "enable_backup": "АĐēŅ‚Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "enable_biometric_auth_description": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžĐš PIN-ĐēОд Đ´ĐģŅ вĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ йиОĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐĩҁĐēОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "enabled": "ВĐēĐģŅŽŅ‡ĐĩĐŊĐž", "end_date": "Đ”Đ°Ņ‚Đ° ĐžĐēĐžĐŊŅ‡Đ°ĐŊĐ¸Ņ", @@ -827,28 +905,30 @@ "error": "ĐžŅˆĐ¸ĐąĐēа", "error_change_sort_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đēи аĐģŅŒĐąĐžĐŧа", "error_delete_face": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐģĐ¸Ņ†Đ° иС ĐžĐąŅŠĐĩĐēŅ‚Đ°", + "error_getting_places": "ĐžŅˆĐ¸ĐąĐēа ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ ĐŧĐĩҁ҂", "error_loading_image": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", + "error_loading_partners": "ĐžŅˆĐ¸ĐąĐēа ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€ĐžĐ˛: {error}", "error_saving_image": "ĐžŅˆĐ¸ĐąĐēа: {error}", "error_tag_face_bounding_box": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐžŅ‚ĐŧĐĩŅ‚Đēи - ĐŊĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Ņ‹ Ņ€Đ°ĐŧĐēи ĐģĐ¸Ņ†Đ°", "error_title": "ĐžŅˆĐ¸ĐąĐēа - Đ§Ņ‚Đž-Ņ‚Đž ĐŋĐžŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "errors": { "cannot_navigate_next_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐŧ҃ ĐžĐąŅŠĐĩĐēŅ‚Ņƒ", - "cannot_navigate_previous_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ĐŋŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐŧ҃ Ņ€ĐĩŅŅƒŅ€ŅŅƒ", + "cannot_navigate_previous_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ĐŋŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐŧ҃ ĐžĐąŅŠĐĩĐēŅ‚Ņƒ", "cant_apply_changes": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ", "cant_change_activity": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ {enabled, select, true {ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ} other {вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ}} аĐēŅ‚Đ¸Đ˛ĐŊĐžŅŅ‚ŅŒ", - "cant_change_asset_favorite": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ \"Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ\" Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅĐ°", + "cant_change_asset_favorite": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ \"Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ\" Đ´ĐģŅ ĐžĐąŅŠĐĩĐēŅ‚Đ°", "cant_change_metadata_assets_count": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ ҃ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", "cant_get_faces": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐģĐ¸Ņ†Đ°", "cant_get_number_of_comments": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đĩв", "cant_search_people": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē ĐģŅŽĐ´ĐĩĐš", "cant_search_places": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐē ĐŧĐĩҁ҂", - "error_adding_assets_to_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ в аĐģŅŒĐąĐžĐŧ", + "error_adding_assets_to_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в аĐģŅŒĐąĐžĐŧ", "error_adding_users_to_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ дОйавĐģĐĩĐŊии ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš в аĐģŅŒĐąĐžĐŧ", "error_deleting_shared_user": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ҁ ĐžĐąŅ‰Đ¸Đŧ Đ´ĐžŅŅ‚ŅƒĐŋĐžĐŧ", "error_downloading": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ {filename}", "error_hiding_buy_button": "ĐžŅˆĐ¸ĐąĐēа ҁĐēŅ€Ņ‹Ņ‚Đ¸Ņ ĐēĐŊĐžĐŋĐēи", - "error_removing_assets_from_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ иС аĐģŅŒĐąĐžĐŧа, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐēĐžĐŊŅĐžĐģҌ Đ´ĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", - "error_selecting_all_assets": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ Đ˛Ņ‹ĐąĐžŅ€Đĩ Đ˛ŅĐĩŅ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛", + "error_removing_assets_from_album": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅƒĐ´Đ°ĐģĐĩĐŊии ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ иС аĐģŅŒĐąĐžĐŧа, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐēĐžĐŊŅĐžĐģҌ Đ´ĐģŅ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐ¸Ņ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", + "error_selecting_all_assets": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ Đ˛Ņ‹ĐąĐžŅ€Đĩ Đ˛ŅĐĩŅ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "exclusion_pattern_already_exists": "ĐĸаĐēĐ°Ņ ĐŧОдĐĩĐģҌ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ ҃ĐļĐĩ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒĐĩŅ‚.", "failed_to_create_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "failed_to_create_shared_link": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅƒŅŽ ҁҁҋĐģĐē҃", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "ХйОК ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "failed_to_load_people": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "failed_to_remove_product_key": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐēĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ°", + "failed_to_reset_pin_code": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд", "failed_to_stack_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "failed_to_unstack_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Ņ€Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "failed_to_update_notification_status": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# ĐŋŅƒŅ‚ŅŒ ĐŊĐĩ ĐŋŅ€ĐžŅˆŅ‘Đģ} many {# ĐŋŅƒŅ‚ĐĩĐš ĐŊĐĩ ĐŋŅ€ĐžŅˆĐģи} other {# ĐŋŅƒŅ‚Đ¸ ĐŊĐĩ ĐŋŅ€ĐžŅˆĐģи}} ĐŋŅ€ĐžĐ˛ĐĩŅ€Đē҃", "profile_picture_transparent_pixels": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ ĐŊĐĩ Đ´ĐžĐģĐļĐŊа ŅĐžĐ´ĐĩŅ€ĐļĐ°Ņ‚ŅŒ ĐŋŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊҋ҅ ĐŋиĐēҁĐĩĐģĐĩĐš. ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ŅƒĐ˛ĐĩĐģĐ¸Ņ‡Đ¸Ņ‚ŅŒ и/иĐģи ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ.", "quota_higher_than_disk_size": "Đ’Ņ‹ ŅƒŅŅ‚Đ°ĐŊОвиĐģи ĐēĐ˛ĐžŅ‚Ņƒ, ĐŋŅ€ĐĩĐ˛Ņ‹ŅˆĐ°ŅŽŅ‰ŅƒŅŽ Ņ€Đ°ĐˇĐŧĐĩŅ€ Đ´Đ¸ŅĐēа", + "something_went_wrong": "Đ§Ņ‚Đž-Ņ‚Đž ĐŋĐžŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "unable_to_add_album_users": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš в аĐģŅŒĐąĐžĐŧ", "unable_to_add_assets_to_shared_link": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ Đē ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēĐĩ", "unable_to_add_comment": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đš", @@ -923,7 +1005,7 @@ "unable_to_remove_partner": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "unable_to_remove_reaction": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ€ĐĩаĐēŅ†Đ¸ŅŽ", "unable_to_reset_password": "НĐĩ ŅƒĐ´Đ°ĐĩŅ‚ŅŅ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", - "unable_to_reset_pin_code": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд", + "unable_to_reset_pin_code": "НĐĩвОСĐŧĐžĐļĐŊĐž ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд", "unable_to_resolve_duplicate": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐ¸Ņ‚ŅŒ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊад Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ°Đŧи", "unable_to_restore_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "unable_to_restore_trash": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹", @@ -938,7 +1020,7 @@ "unable_to_scan_library": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋŅ€ĐžŅĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "unable_to_set_feature_photo": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ ĐŊа ОйĐģĐžĐļĐē҃", "unable_to_set_profile_picture": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", - "unable_to_submit_job": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ СадаĐŊиĐĩ", + "unable_to_submit_job": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐˇĐ°Đ´Đ°Ņ‡Ņƒ ĐŊа Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊиĐĩ", "unable_to_trash_asset": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "unable_to_unlink_account": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ ŅƒŅ‡Ņ‘Ņ‚ĐŊŅƒŅŽ СаĐŋĐ¸ŅŅŒ", "unable_to_unlink_motion_video": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžŅ‚ŅĐžĐĩдиĐŊĐ¸Ņ‚ŅŒ двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ...", + "exif_bottom_sheet_description_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "exif_bottom_sheet_details": "ПОДРОБНОСĐĸИ", "exif_bottom_sheet_location": "МЕСĐĸО", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ иĐŧŅ", - "exif_bottom_sheet_person_age_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {months} ĐŧĐĩŅŅŅ†Đĩв", - "exif_bottom_sheet_person_age_year_months": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ 1 ĐŗĐžĐ´, {months} ĐŧĐĩŅŅŅ†Đĩв", - "exif_bottom_sheet_person_age_years": "Đ’ĐžĐˇŅ€Đ°ŅŅ‚ {years}", "exit_slideshow": "Đ’Ņ‹ĐšŅ‚Đ¸ иС ҁĐģаКд-ŅˆĐžŅƒ", "expand_all": "РаСвĐĩŅ€ĐŊŅƒŅ‚ŅŒ Đ˛ŅŅ‘", "experimental_settings_new_asset_list_subtitle": "В Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚ĐēĐĩ", @@ -967,28 +1047,32 @@ "experimental_settings_subtitle": "Đ˜ŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ĐŊа ŅĐ˛ĐžĐš ŅŅ‚Ņ€Đ°Ņ… и Ņ€Đ¸ŅĐē!", "experimental_settings_title": "Đ­ĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģҌĐŊŅ‹Đĩ Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸", "expire_after": "Đ˜ŅŅ‚ĐĩĐēаĐĩŅ‚ ҇ĐĩŅ€ĐĩС", - "expired": "ĐĄŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ¸ŅŅ‚ĐĩĐē", + "expired": "ĐĄŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ¸ŅŅ‚Ņ‘Đē", "expires_date": "ĐĄŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ´Đž {date}", "explore": "ĐŸĐžĐ¸ŅĐē", "explorer": "ĐŸŅ€ĐžĐ˛ĐžĐ´ĐŊиĐē", "export": "Đ­ĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "export_as_json": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ в JSON", + "export_database": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅", + "export_database_description": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ ĐąĐ°ĐˇŅ‹ даĐŊĐŊҋ҅ SQLite", "extension": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", "external": "ВĐŊĐĩ҈ĐŊиК", "external_libraries": "ВĐŊĐĩ҈ĐŊиĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "external_network": "ВĐŊĐĩ҈ĐŊŅŅ ҁĐĩŅ‚ŅŒ", - "external_network_sheet_info": "ĐšĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŊĐĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ‹Ņ‚Đ°Ņ‚ŅŒŅŅ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž Đ°Đ´Ņ€ĐĩŅĐ°Đŧ ĐŊиĐļĐĩ, ŅĐ˛ĐĩŅ€Ņ…Ņƒ вĐŊиС, Đ´Đž ҃ҁĐŋĐĩ҈ĐŊĐžĐŗĐž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", + "external_network_sheet_info": "ĐšĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŊĐĩ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē ҃ĐēаСаĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸, ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋŅ‹Ņ‚Đ°Ņ‚ŅŒŅŅ ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž Đ°Đ´Ņ€ĐĩŅĐ°Đŧ ĐŊиĐļĐĩ, ŅĐ˛ĐĩŅ€Ņ…Ņƒ вĐŊиС Đ´Đž ҃ҁĐŋĐĩ҈ĐŊĐžĐŗĐž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ", "face_unassigned": "НĐĩ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊĐž", "failed": "ĐžŅˆĐ¸ĐąĐēа", "failed_to_authenticate": "ĐžŅˆĐ¸ĐąĐēа Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸", "failed_to_load_assets": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ", "failed_to_load_folder": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ ĐŋаĐŋĐēи", "favorite": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", + "favorite_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ дОйавĐģĐĩĐŊŅ‹ в Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ ({count} ŅˆŅ‚.)", "favorite_or_unfavorite_photo": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ иĐģи ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", "favorites": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "favorites_page_no_favorites": "В Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŧ ҁĐĩĐšŅ‡Đ°Ņ ĐŋŅƒŅŅ‚Đž", "feature_photo_updated": "Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ Ņ„ĐžŅ‚Đž ОйĐŊОвĐģĐĩĐŊĐž", "features": "ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đĩ вОСĐŧĐžĐļĐŊĐžŅŅ‚Đ¸", + "features_in_development": "Đ¤ŅƒĐŊĐēŅ†Đ¸Đ¸ в Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚ĐēĐĩ", "features_setting_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ Đ´ĐžĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đŧи вОСĐŧĐžĐļĐŊĐžŅŅ‚ŅĐŧи ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "file_name": "ИĐŧŅ Ņ„Đ°ĐšĐģа", "file_name_or_extension": "ИĐŧŅ Ņ„Đ°ĐšĐģа иĐģи Ņ€Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊиĐĩ", @@ -998,21 +1082,26 @@ "filter_people": "ФиĐģŅŒŅ‚Ņ€ ĐŋĐž ĐģŅŽĐ´ŅĐŧ", "filter_places": "ФиĐģŅŒŅ‚Ņ€ ĐŋĐž ĐŧĐĩŅŅ‚Đ°Đŧ", "find_them_fast": "Đ‘Ņ‹ŅŅ‚Ņ€Đž ĐŊĐ°ĐšĐ´Đ¸Ņ‚Đĩ Đ¸Ņ… ĐŋĐž иĐŧĐĩĐŊи ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ĐŋĐžĐ¸ŅĐēа", + "first": "ПĐĩŅ€Đ˛Ņ‹Đš", "fix_incorrect_match": "Đ˜ŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊĐžĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Đĩ", "folder": "ПаĐŋĐēа", "folder_not_found": "ПаĐŋĐēа ĐŊĐĩ ĐŊаКдĐĩĐŊа", "folders": "ПаĐŋĐēи", - "folders_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ ĐŋаĐŋĐžĐē ҁ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи и видĐĩĐž в Ņ„Đ°ĐšĐģОвОК ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", + "folders_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ ĐŋаĐŋĐžĐē ҁ Ņ„ĐžŅ‚Đž и видĐĩĐž в Ņ„Đ°ĐšĐģОвОК ŅĐ¸ŅŅ‚ĐĩĐŧĐĩ", + "forgot_pin_code_question": "Đ—Đ°ĐąŅ‹Đģи PIN-ĐēОд?", "forward": "ВĐŋĐĩŅ€Ņ‘Đ´", "gcast_enabled": "Google Cast", - "gcast_enabled_description": "Đ­Ņ‚ĐžŅ‚ Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģ ҂ҀĐĩĐąŅƒĐĩŅ‚ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи вĐŊĐĩ҈ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐ˛ Google.", + "gcast_enabled_description": "ДĐģŅ Ņ€Đ°ĐąĐžŅ‚Ņ‹ ҂ҀĐĩĐąŅƒĐĩŅ‚ŅŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēа вĐŊĐĩ҈ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅĐžĐ˛ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐ˛ Google.", "general": "ĐžĐąŅ‰Đ¸Đĩ", + "geolocation_instruction_location": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐžĐąŅŠĐĩĐēŅ‚ ҁ иĐŧĐĩŅŽŅ‰Đ¸ĐŧĐ¸ŅŅ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ°Đŧи, Ņ‡Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Đ¸Ņ…, ĐģийО Đ˛Ņ€ŅƒŅ‡ĐŊŅƒŅŽ ҃ĐēаĐļĐ¸Ņ‚Đĩ ĐŧĐĩŅŅ‚Đž ĐŊа ĐēĐ°Ņ€Ņ‚Đĩ", "get_help": "ПоĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐŋĐžĐŧĐžŅ‰ŅŒ", "get_wifiname_error": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ иĐŧŅ Wi-Fi ҁĐĩŅ‚Đ¸. ĐŖĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ Đē ҁĐĩŅ‚Đ¸ и ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģи ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧŅ‹Đĩ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", "getting_started": "ĐĄŅ‚Đ°Ņ€Ņ‚", "go_back": "Назад", "go_to_folder": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ в ĐŋаĐŋĐē҃", "go_to_search": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đē ĐŋĐžĐ¸ŅĐē҃", + "gps": "Đ•ŅŅ‚ŅŒ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Ņ‹", + "gps_missing": "НĐĩŅ‚ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚", "grant_permission": "ĐŸŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ", "group_albums_by": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧŅ‹ ĐŋĐž...", "group_country": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐž ŅŅ‚Ņ€Đ°ĐŊаĐŧ", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Ņ‚Đ°ĐēŅ‚Đ¸ĐģҌĐŊŅƒŅŽ ĐžŅ‚Đ´Đ°Ņ‡Ņƒ", "haptic_feedback_title": "ĐĸаĐēŅ‚Đ¸ĐģҌĐŊĐ°Ņ ĐžŅ‚Đ´Đ°Ņ‡Đ°", "has_quota": "ĐšĐ˛ĐžŅ‚Đ°", + "hash_asset": "ĐĨĐĩŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "hashed_assets": "ĐĨĐĩŅˆĐ¸", + "hashing": "ĐĨĐĩŅˆĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "header_settings_add_header_tip": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐˇĐ°ĐŗĐžĐģОвОĐē", "header_settings_field_validator_msg": "ЗĐŊĐ°Ņ‡ĐĩĐŊиĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐŋŅƒŅŅ‚Ņ‹Đŧ", "header_settings_header_name_input": "ИĐŧŅ ĐˇĐ°ĐŗĐžĐģОвĐēа", @@ -1039,14 +1131,14 @@ "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_album_err_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° в аĐģŅŒĐąĐžĐŧ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_archive_err_local": "ПоĐēа ĐŊĐĩĐģŅŒĐˇŅ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ Ņ„Đ°ĐšĐģŅ‹ в Đ°Ņ€Ņ…Đ¸Đ˛, ĐŋŅ€ĐžĐŋ҃ҁĐē", - "home_page_archive_err_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŧĐĩдиа ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°, ĐŋŅ€ĐžĐŋ҃ҁĐē", + "home_page_archive_err_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° в Đ°Ņ€Ņ…Đ¸Đ˛, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_building_timeline": "ĐŸĐžŅŅ‚Ņ€ĐžĐĩĐŊиĐĩ Ņ…Ņ€ĐžĐŊĐžĐģĐžĐŗĐ¸Đ¸", - "home_page_delete_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_favorite_err_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_first_time_notice": "ПĐĩŅ€ĐĩĐ´ ĐŊĐ°Ņ‡Đ°ĐģĐžĐŧ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ аĐģŅŒĐąĐžĐŧ ҁ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧи Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐžĐŊи ĐžŅ‚ĐžĐąŅ€Đ°ĐˇĐ¸ĐģĐ¸ŅŅŒ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊОК ҈ĐēаĐģĐĩ", "home_page_locked_error_local": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐģĐžĐēаĐģҌĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ĐŋŅ€ĐžĐŋ҃ҁĐē", "home_page_locked_error_partner": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ĐŋŅ€ĐžĐŋ҃ҁĐē", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐŧаĐēŅĐ¸Đŧ҃Đŧ 30 Ņ„Đ°ĐšĐģОв Са Ņ€Đ°Đˇ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "host": "ĐĨĐžŅŅ‚", "hour": "Đ§Đ°Ņ", + "hours": "Đ§Đ°ŅŅ‹", "id": "ID", + "idle": "В ĐžĐļидаĐŊии", "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ иС iCloud", "ignore_icloud_photos_description": "НĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģŅ‹ в Immich, ĐĩҁĐģи ĐžĐŊи Ņ…Ņ€Đ°ĐŊŅŅ‚ŅŅ в iCloud", "image": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", @@ -1079,10 +1173,10 @@ "in_albums": "В {count, plural, one {# аĐģŅŒĐąĐžĐŧĐĩ} other {# аĐģŅŒĐąĐžĐŧĐ°Ņ…}}", "in_archive": "В Đ°Ņ€Ņ…Đ¸Đ˛Đĩ", "include_archived": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đ°Ņ€Ņ…Đ¸Đ˛", - "include_shared_albums": "ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đĩ аĐģŅŒĐąĐžĐŧŅ‹", - "include_shared_partner_assets": "ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", - "individual_share": "ПĐĩŅ€ŅĐžĐŊаĐģҌĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ", - "individual_shares": "ИĐŊĐ´Đ¸Đ˛Đ¸Đ´ŅƒĐ°ĐģҌĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ", + "include_shared_albums": "ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐžĐąŅ‰Đ¸Ņ… аĐģŅŒĐąĐžĐŧОв", + "include_shared_partner_assets": "ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€ĐžĐ˛", + "individual_share": "ИĐŊĐ´Đ¸Đ˛Đ¸Đ´ŅƒĐ°ĐģҌĐŊĐ°Ņ ĐŋĐžĐ´ĐąĐžŅ€Đēа", + "individual_shares": "ĐŸĐžĐ´ĐąĐžŅ€Đēи", "info": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ", "interval": { "day_at_onepm": "КаĐļĐ´Ņ‹Đš Đ´ĐĩĐŊҌ в 13:00", @@ -1092,7 +1186,7 @@ }, "invalid_date": "НĐĩвĐĩŅ€ĐŊĐ°Ņ Đ´Đ°Ņ‚Đ°", "invalid_date_format": "НĐĩвĐĩŅ€ĐŊŅ‹Đš Ņ„ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹", - "invite_people": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ", + "invite_people": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ ŅƒŅ‡Đ°ŅŅ‚ĐŊиĐēа", "invite_to_album": "ĐŸŅ€Đ¸ĐŗĐģĐ°ŅĐ¸Ņ‚ŅŒ в аĐģŅŒĐąĐžĐŧ", "ios_debug_info_fetch_ran_at": "Đ’Ņ‹ĐąĐžŅ€Đēа СаĐŋŅƒŅ‰ĐĩĐŊа {dateTime}", "ios_debug_info_last_sync_at": "ĐŸĐžŅĐģĐĩĐ´ĐŊŅŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ {dateTime}", @@ -1103,42 +1197,49 @@ "items_count": "{count, plural, one {# ŅĐģĐĩĐŧĐĩĐŊŅ‚} many {# ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛} other {# ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°}}", "jobs": "Đ—Đ°Đ´Đ°Ņ‡Đ¸", "keep": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ", - "keep_all": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", + "keep_all": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "keep_this_delete_others": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ ŅŅ‚ĐžŅ‚, ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžŅŅ‚Đ°ĐģҌĐŊŅ‹Đĩ", "kept_this_deleted_others": "ĐĄĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚ и {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}}", "keyboard_shortcuts": "ĐĄĐžŅ‡ĐĩŅ‚Đ°ĐŊĐ¸Ņ ĐēĐģĐ°Đ˛Đ¸Ņˆ", "language": "Đ¯ĐˇŅ‹Đē", "language_no_results_subtitle": "ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ҁĐēĐžŅ€Ņ€ĐĩĐēŅ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đš СаĐŋŅ€ĐžŅ", "language_no_results_title": "Đ¯ĐˇŅ‹ĐēОв ĐŊĐĩ ĐŊаКдĐĩĐŊĐž", - "language_search_hint": "ĐŸĐžĐ¸ŅĐē ŅĐˇŅ‹ĐēОв...", + "language_search_hint": "ĐŸĐžĐ¸ŅĐē ŅĐˇŅ‹Đēа...", "language_setting_description": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Đ¸Ņ‚Đ°ĐĩĐŧŅ‹Đš ваĐŧи ŅĐˇŅ‹Đē", + "large_files": "ФаКĐģŅ‹ ĐŊаийОĐģҌ҈ĐĩĐŗĐž Ņ€Đ°ĐˇĐŧĐĩŅ€Đ°", + "last": "ĐŸĐžŅĐģĐĩĐ´ĐŊиК", "last_seen": "ĐŸĐžŅĐģĐĩĐ´ĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ", "latest_version": "ĐŸĐžŅĐģĐĩĐ´ĐŊŅŅ вĐĩŅ€ŅĐ¸Ņ", "latitude": "Đ¨Đ¸Ņ€ĐžŅ‚Đ°", "leave": "ПоĐēиĐŊŅƒŅ‚ŅŒ", + "leave_album": "ПоĐēиĐŊŅƒŅ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "lens_model": "МодĐĩĐģҌ ĐžĐąŅŠĐĩĐēŅ‚Đ¸Đ˛Đ°", - "let_others_respond": "ПозвоĐģŅŅ‚ŅŒ Đ´Ņ€ŅƒĐŗĐ¸Đŧ ĐžŅ‚ĐēĐģиĐēĐ°Ņ‚ŅŒŅŅ", + "let_others_respond": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐ¸Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧ дОйавĐģŅŅ‚ŅŒ ĐēĐžĐŧĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Đ¸ и ĐžŅ‚ĐŧĐĩŅ‚Đēи \"ĐŊŅ€Đ°Đ˛Đ¸Ņ‚ŅŅ\"", "level": "ĐŖŅ€ĐžĐ˛ĐĩĐŊҌ", "library": "БибĐģĐ¸ĐžŅ‚ĐĩĐēа", - "library_options": "ОĐŋŅ†Đ¸Đ¸ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", + "library_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ йийĐģĐ¸ĐžŅ‚ĐĩĐēОК", "library_page_device_albums": "АĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "library_page_new_album": "ĐĐžĐ˛Ņ‹Đš аĐģŅŒĐąĐžĐŧ", "library_page_sort_asset_count": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "library_page_sort_created": "НĐĩдавĐŊĐž ŅĐžĐˇĐ´Đ°ĐŊĐŊŅ‹Đĩ", "library_page_sort_last_modified": "ĐŸĐžŅĐģĐĩĐ´ĐŊĐĩĐĩ иСĐŧĐĩĐŊĐĩĐŊиĐĩ", "library_page_sort_title": "НазваĐŊиĐĩ аĐģŅŒĐąĐžĐŧа", + "licenses": "Đ›Đ¸Ņ†ĐĩĐŊСии", "light": "ХвĐĩŅ‚ĐģĐ°Ņ", + "like": "ĐŅ€Đ°Đ˛Đ¸Ņ‚ŅŅ", "like_deleted": "ЛайĐē ŅƒĐ´Đ°ĐģĐĩĐŊ", "link_motion_video": "ĐĄŅŅ‹ĐģĐēа ĐŊа двиĐļŅƒŅ‰ĐĩĐĩŅŅ видĐĩĐž", - "link_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ҁҁҋĐģĐēи", "link_to_oauth": "ĐŸŅ€Đ¸ŅĐžĐĩдиĐŊĐĩĐŊиĐĩ Đē OAuth", "linked_oauth_account": "ĐŸŅ€Đ¸ŅĐžĐĩдиĐŊŅ‘ĐŊĐŊŅ‹Đš аĐēĐēĐ°ŅƒĐŊŅ‚ OAuth", "list": "ĐĄĐŋĐ¸ŅĐžĐē", "loading": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа", "loading_search_results_failed": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ", - "local_asset_cast_failed": "НĐĩвОСĐŧĐžĐļĐŊĐž Ņ‚Ņ€Đ°ĐŊҁĐģĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚, ĐēĐžŅ‚ĐžŅ€Ņ‹Đš Đĩ҉ґ ĐŊĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", + "local": "На ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", + "local_asset_cast_failed": "НĐĩвОСĐŧĐžĐļĐŊа Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Ņ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ Đĩ҉ґ ĐŊĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊŅ‹ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", + "local_assets": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", + "local_media_summary": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой ĐžĐąŅŠĐĩĐēŅ‚Đĩ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", "local_network": "ЛоĐēаĐģҌĐŊĐ°Ņ ҁĐĩŅ‚ŅŒ", - "local_network_sheet_info": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž ŅŅ‚ĐžĐŧ҃ Đ°Đ´Ņ€Đĩҁ҃, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸", + "local_network_sheet_info": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ ĐŋĐž ŅŅ‚ĐžĐŧ҃ Đ°Đ´Ņ€Đĩҁ҃, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž Đē ҃ĐēаСаĐŊĐŊОК Wi-Fi ҁĐĩŅ‚Đ¸", "location_permission": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ", "location_permission_content": "Đ§Ņ‚ĐžĐąŅ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŽ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ, Immich ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Ņ‚ĐžŅ‡ĐŊĐžĐĩ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊиĐĩ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸Ņ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐžĐŊĐž ĐŧĐžĐŗĐģĐž ŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŊаСваĐŊиĐĩ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐš Wi-Fi ҁĐĩŅ‚Đ¸", "location_picker_choose_on_map": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŊа ĐēĐ°Ņ€Ņ‚Đĩ", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ Đ´ĐžĐģĐŗĐžŅ‚Ņƒ", "lock": "ЗабĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "locked_folder": "Đ›Đ¸Ņ‡ĐŊĐ°Ņ ĐŋаĐŋĐēа", + "log_detail_title": "ДĐĩŅ‚Đ°Đģи ŅĐžĐąŅ‹Ņ‚Đ¸Ņ", "log_out": "Đ’Ņ‹ĐšŅ‚Đ¸", "log_out_all_devices": "ЗавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ҁĐĩаĐŊҁҋ ĐŊа Đ˛ŅĐĩŅ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ…", "logged_in_as": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊ ĐēаĐē {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "ĐŸĐ°Ņ€ĐžĐģҌ ҃ҁĐŋĐĩ҈ĐŊĐž ОйĐŊОвĐģĐĩĐŊ", "logout_all_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ СавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ ҁĐĩаĐŊҁҋ, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐŗĐž?", "logout_this_device_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ СавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ҁĐĩаĐŊҁ ĐŊа ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ?", + "logs": "Đ–ŅƒŅ€ĐŊаĐģ ŅĐžĐąŅ‹Ņ‚Đ¸Đš", "longitude": "ДоĐģĐŗĐžŅ‚Đ°", "look": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€", "loop_videos": "ĐĻиĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ видĐĩĐž", @@ -1185,16 +1288,16 @@ "main_branch_warning": "Đ’Ņ‹ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚Đĩ вĐĩŅ€ŅĐ¸ŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ Đ´ĐģŅ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đēи. ĐĐ°ŅŅ‚ĐžŅŅ‚ĐĩĐģҌĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ ĐŊа Ņ€ĐĩĐģиСĐŊŅƒŅŽ вĐĩŅ€ŅĐ¸ŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ!", "main_menu": "ГĐģавĐŊĐžĐĩ ĐŧĐĩĐŊŅŽ", "make": "ĐŸŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ĐĩĐģҌ", + "manage_geolocation": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŧĐĩŅŅ‚Đ°Đŧи ŅŅŠŅ‘ĐŧĐēи", "manage_shared_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧи ҁҁҋĐģĐēаĐŧи", - "manage_sharing_with_partners": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ОйĐŧĐĩĐŊĐžĐŧ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ĐĩĐš ҁ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°Đŧи. Đ­Ņ‚Đ° Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ĐŋОСвОĐģŅĐĩŅ‚ Đ˛Đ°ŅˆĐĩĐŧ҃ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Ņƒ видĐĩŅ‚ŅŒ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩОСаĐŋĐ¸ŅĐ¸, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩŅ…, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐ°Ņ…ĐžĐ´ŅŅ‚ŅŅ в ĐŅ€Ņ…Đ¸Đ˛Đĩ и ĐšĐžŅ€ĐˇĐ¸ĐŊĐĩ", + "manage_sharing_with_partners": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ŅĐžĐ˛ĐŧĐĩҁ҂ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа Đē Ņ„ĐžŅ‚Đž и видĐĩĐž, ĐŋОСвОĐģŅŅŽŅ‰Đ°Ņ видĐĩŅ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€ĐžĐ˛, а Ņ‚Đ°ĐēĐļĐĩ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐģŅŅ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ˛ĐžĐ¸Đŧ", "manage_the_app_settings": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēаĐŧи ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "manage_your_account": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅƒŅ‡Ņ‘Ņ‚ĐŊОК СаĐŋĐ¸ŅŅŒŅŽ", "manage_your_api_keys": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ API ĐēĐģŅŽŅ‡Đ°Đŧи Đ´ĐģŅ вСаиĐŧОдĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ Đ´Ņ€ŅƒĐŗĐ¸Đŧи ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŧаĐŧи", "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Đŧи, ĐŊа ĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… Đ˛Ņ‹ Đ˛Ņ…ĐžĐ´Đ¸Đģи в ŅĐ˛ĐžĐš аĐēĐēĐ°ŅƒĐŊŅ‚", "manage_your_oauth_connection": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŋОдĐēĐģŅŽŅ‡Ņ‘ĐŊĐŊĐžĐŗĐž OAuth", "map": "ĐšĐ°Ņ€Ņ‚Đ°", - "map_assets_in_bound": "{count} Ņ„ĐžŅ‚Đž", - "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚Đž", + "map_assets_in_bounds": "{count, plural, =0 {НĐĩŅ‚ Ņ„ĐžŅ‚Đž} other {# Ņ„ĐžŅ‚Đž}}", "map_cannot_get_user_location": "НĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "Đ­Ņ‚Đž ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", @@ -1202,7 +1305,6 @@ "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_title": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐžŅ‚ĐēĐģĐžĐŊĐĩĐŊ", "map_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐēĐ°Ņ€Ņ‚Ņ‹", @@ -1221,6 +1323,7 @@ "mark_as_read": "ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ ĐēаĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐŊĐžĐĩ", "marked_all_as_read": "ĐžŅ‚ĐŧĐĩ҇ĐĩĐŊŅ‹ ĐēаĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ‹Đĩ", "matches": "ХОвĐŋадĐĩĐŊĐ¸Ņ", + "matching_assets": "ĐĄĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "media_type": "ĐĸиĐŋ ĐŧĐĩдиа", "memories": "Đ’ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", "memories_all_caught_up": "Đ­Ņ‚Đž Đ˛ŅŅ‘ ĐŊа ҁĐĩĐŗĐžĐ´ĐŊŅ", @@ -1232,24 +1335,26 @@ "memory_lane_title": "Đ’ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ {title}", "menu": "МĐĩĐŊŅŽ", "merge": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ", - "merge_people": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", + "merge_people": "ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ҁ Đ´Ņ€ŅƒĐŗĐ¸Đŧ", "merge_people_limit": "Đ’Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐžĐąŅŠĐĩдиĐŊŅŅ‚ŅŒ Đ´Đž 5 ĐģĐ¸Ņ† Са ОдиĐŊ Ņ€Đ°Đˇ", "merge_people_prompt": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžĐąŅŠĐĩдиĐŊĐ¸Ņ‚ŅŒ ŅŅ‚Đ¸Ņ… ĐģŅŽĐ´ĐĩĐš? Đ­Ņ‚Đž Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŊĐĩĐžĐąŅ€Đ°Ņ‚Đ¸ĐŧĐž.", "merge_people_successfully": "Đ›Đ¸Ņ†Đ° ĐģŅŽĐ´ĐĩĐš ҃ҁĐŋĐĩ҈ĐŊĐž ĐžĐąŅŠĐĩдиĐŊĐĩĐŊŅ‹", "merged_people_count": "ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž {count, plural, one {# ҇ĐĩĐģОвĐĩĐē} many {# ҇ĐĩĐģОвĐĩĐē} other {# ҇ĐĩĐģОвĐĩĐēа}}", "minimize": "МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", "minute": "МиĐŊŅƒŅ‚Đ°", + "minutes": "МиĐŊŅƒŅ‚Ņ‹", "missing": "ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ", "model": "МодĐĩĐģҌ", "month": "МĐĩŅŅŅ†", "monthly_title_text_date_format": "MMMM y", - "more": "БоĐģҌ҈Đĩ", + "more": "ДоĐŋĐžĐģĐŊĐ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đĩ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ", "move": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ", - "move_off_locked_folder": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", - "move_to_locked_folder": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", + "move_off_locked_folder": "ĐŖĐąŅ€Đ°Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", + "move_to_lock_folder_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ дОйавĐģĐĩĐŊŅ‹ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃ ({count} ŅˆŅ‚.)", + "move_to_locked_folder": "В ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃", "move_to_locked_folder_confirmation": "Đ­Ņ‚Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв и ĐąŅƒĐ´ŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ Ņ‚ĐžĐģҌĐēĐž в ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ", - "moved_to_archive": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в Đ°Ņ€Ņ…Đ¸Đ˛", - "moved_to_library": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐž}} в йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", + "moved_to_archive": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹}} в Đ°Ņ€Ņ…Đ¸Đ˛", + "moved_to_library": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ґĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹}} в йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "moved_to_trash": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "multiselect_grid_edit_date_time_err_read_only": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ„Đ°ĐšĐģОв Ņ‚ĐžĐģҌĐēĐž Đ´ĐģŅ ҇҂ĐĩĐŊĐ¸Ņ, ĐŋŅ€ĐžĐŋ҃ҁĐē", "multiselect_grid_edit_gps_err_read_only": "НĐĩвОСĐŧĐžĐļĐŊĐž иСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ Ņ„Đ°ĐšĐģОв Ņ‚ĐžĐģҌĐēĐž Đ´ĐģŅ ҇҂ĐĩĐŊĐ¸Ņ, ĐŋŅ€ĐžĐŋ҃ҁĐē", @@ -1257,6 +1362,10 @@ "my_albums": "Мои аĐģŅŒĐąĐžĐŧŅ‹", "name": "ИĐŧŅ", "name_or_nickname": "ИĐŧŅ иĐģи ĐŊиĐē", + "network_requirement_photos_upload": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐŧОйиĐģҌĐŊŅ‹Đš иĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Ņ„ĐžŅ‚Đž", + "network_requirement_videos_upload": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐŧОйиĐģҌĐŊŅ‹Đš иĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚ Đ´ĐģŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи видĐĩĐž", + "network_requirements": "ĐĸŅ€ĐĩйОваĐŊĐ¸Ņ Đē ҁĐĩŅ‚Đ¸", + "network_requirements_updated": "ĐĸŅ€ĐĩйОваĐŊĐ¸Ņ Đē ҁĐĩŅ‚Đ¸ иСĐŧĐĩĐŊиĐģĐ¸ŅŅŒ, ŅĐąŅ€ĐžŅ ĐžŅ‡ĐĩŅ€Đĩди ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "networking_settings": "ĐĄĐĩŅ‚ŅŒ", "networking_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ Đē ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", "never": "ĐŊиĐēĐžĐŗĐ´Đ°", @@ -1266,6 +1375,7 @@ "new_person": "ĐĐžĐ˛Ņ‹Đš ҇ĐĩĐģОвĐĩĐē", "new_pin_code": "ĐĐžĐ˛Ņ‹Đš PIN-ĐēОд", "new_pin_code_subtitle": "Đ­Ņ‚Đž Đ˛Đ°Ņˆ ĐŋĐĩŅ€Đ˛Ņ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ. ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ PIN-ĐēОд Đ´ĐģŅ ĐˇĐ°Ņ‰Đ¸Ņ‰ĐĩĐŊĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа Đē ŅŅ‚ĐžĐš ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đĩ.", + "new_timeline": "ĐĐžĐ˛Đ°Ņ ĐģĐĩĐŊŅ‚Đ°", "new_user_created": "ĐĐžĐ˛Ņ‹Đš ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ŅĐžĐˇĐ´Đ°ĐŊ", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ ĐĐžĐ’ĐĐ¯ Đ’Đ•Đ ĐĄĐ˜Đ¯", "newest_first": "ĐĄĐŊĐ°Ņ‡Đ°Đģа ĐŊĐžĐ˛Ņ‹Đĩ", @@ -1279,22 +1389,28 @@ "no_assets_message": "НАЖМИĐĸЕ Đ”Đ›Đ¯ Đ—ĐĐ“Đ ĐŖĐ—ĐšĐ˜ ВАШЕГО ПЕРВОГО ФОĐĸО", "no_assets_to_show": "МĐĩдиа ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‚", "no_cast_devices_found": "НĐĩ ĐŊаКдĐĩĐŊĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ Đ´ĐģŅ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸Đ¸", + "no_checksum_local": "КоĐŊŅ‚Ņ€ĐžĐģҌĐŊŅ‹Đĩ ҁ҃ĐŧĐŧŅ‹ ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‚ - ĐŊĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", + "no_checksum_remote": "КоĐŊŅ‚Ņ€ĐžĐģҌĐŊŅ‹Đĩ ҁ҃ĐŧĐŧŅ‹ ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒŅŽŅ‚ - ĐŊĐĩвОСĐŧĐžĐļĐŊĐž ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐē҂ҋ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "no_duplicates_found": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛ ĐŊĐĩ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐž.", "no_exif_info_available": "НĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ exif", "no_explore_results_message": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°ĐšŅ‚Đĩ йОĐģҌ҈Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŊĐ°ŅĐģаĐļĐ´Đ°Ņ‚ŅŒŅŅ Đ˛Đ°ŅˆĐĩĐš ĐēĐžĐģĐģĐĩĐēŅ†Đ¸ĐĩĐš.", - "no_favorites_message": "ДобавĐģŅĐšŅ‚Đĩ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐąŅ‹ŅŅ‚Ņ€Đž ĐŊĐ°ĐšŅ‚Đ¸ ŅĐ˛ĐžĐ¸ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž", + "no_favorites_message": "ДобавĐģŅĐšŅ‚Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐąŅ‹ŅŅ‚Ņ€ĐĩĐĩ ĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚ŅŒ ŅĐ˛ĐžĐ¸ ĐģŅƒŅ‡ŅˆĐ¸Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž", "no_libraries_message": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ вĐŊĐĩ҈ĐŊŅŽŅŽ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° в Immich ŅŅ‚ĐžŅ€ĐžĐŊĐŊĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž", + "no_local_assets_found": "На ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ ĐŊĐĩ ĐŊаКдĐĩĐŊĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ҁ Ņ‚Đ°ĐēОК ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊОК ҁ҃ĐŧĐŧОК", "no_locked_photos_message": "Đ¤ĐžŅ‚Đž и видĐĩĐž, ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊĐŊŅ‹Đĩ в ĐģĐ¸Ņ‡ĐŊŅƒŅŽ ĐŋаĐŋĐē҃, ҁĐēҀҋ҂ҋ и ĐŊĐĩ ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°ŅŽŅ‚ŅŅ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи.", "no_name": "НĐĩŅ‚ иĐŧĐĩĐŊи", "no_notifications": "НĐĩŅ‚ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "no_people_found": "НиĐēĐžĐŗĐž ĐŊĐĩ ĐŊаКдĐĩĐŊĐž", "no_places": "НĐĩŅ‚ ĐŧĐĩҁ҂", + "no_remote_assets_found": "На ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ ĐŊĐĩ ĐŊаКдĐĩĐŊĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ҁ Ņ‚Đ°ĐēОК ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊОК ҁ҃ĐŧĐŧОК", "no_results": "НĐĩŅ‚ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛", "no_results_description": "ПоĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ŅĐ¸ĐŊĐžĐŊиĐŧ иĐģи йОĐģĐĩĐĩ ĐžĐąŅ‰ĐĩĐĩ ĐēĐģŅŽŅ‡ĐĩвОĐĩ ҁĐģОвО", "no_shared_albums_message": "ĐĄĐžĐˇĐ´Đ°ĐšŅ‚Đĩ аĐģŅŒĐąĐžĐŧ Đ´ĐģŅ ОйĐŧĐĩĐŊа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи и видĐĩОСаĐŋĐ¸ŅŅĐŧи ҁ ĐģŅŽĐ´ŅŒĐŧи в Đ˛Đ°ŅˆĐĩĐš ҁĐĩŅ‚Đ¸", + "no_uploads_in_progress": "НĐĩŅ‚ аĐēŅ‚Đ¸Đ˛ĐŊҋ҅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐžĐē", + "not_available": "НĐĩŅ‚ даĐŊĐŊҋ҅", "not_in_any_album": "Ни в ОдĐŊĐžĐŧ аĐģŅŒĐąĐžĐŧĐĩ", "not_selected": "НĐĩ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", - "note_apply_storage_label_to_previously_uploaded assets": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đē҃ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ Ņ€ĐĩŅŅƒŅ€ŅĐ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ", + "note_apply_storage_label_to_previously_uploaded assets": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ: Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐĩŅ‚Đē҃ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đē Ņ€Đ°ĐŊĐĩĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đŧ ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ, СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ", "notes": "ĐŸŅ€Đ¸ĐŧĐĩŅ‡Đ°ĐŊиĐĩ", "nothing_here_yet": "ЗдĐĩҁҌ ĐŋĐžĐēа ĐŊĐ¸Ņ‡ĐĩĐŗĐž ĐŊĐĩŅ‚", "notification_permission_dialog_content": "Đ§Ņ‚ĐžĐąŅ‹ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ, ĐŋĐĩŅ€ĐĩĐšĐ´Đ¸Ņ‚Đĩ в ÂĢĐĐ°ŅŅ‚Ņ€ĐžĐšĐēиÂģ и Đ˛Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ÂĢĐ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒÂģ.", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģҌĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ Immich", "offline": "НĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐĩĐŊ", + "offset": "ĐĄĐŧĐĩ҉ĐĩĐŊиĐĩ", "ok": "ОК", "oldest_first": "ĐĄĐŊĐ°Ņ‡Đ°Đģа ŅŅ‚Đ°Ņ€Ņ‹Đĩ", "on_this_device": "На ŅŅ‚ĐžĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ", @@ -1323,27 +1440,30 @@ "open_in_map_view": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ в Ņ€ĐĩĐļиĐŧĐĩ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° ĐēĐ°Ņ€Ņ‚Ņ‹", "open_in_openstreetmap": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ в OpenStreetMap", "open_the_search_filters": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Ņ‹ ĐŋĐžĐ¸ŅĐēа", - "options": "ОĐŋŅ†Đ¸Đ¸", + "options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ", "or": "иĐģи", + "organize_into_albums": "Đ Đ°ŅĐŋŅ€ĐĩĐ´ĐĩĐģĐ¸Ņ‚ŅŒ ĐŋĐž аĐģŅŒĐąĐžĐŧаĐŧ", + "organize_into_albums_description": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ҃ĐļĐĩ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒŅŽŅ‰Đ¸Đĩ ĐžĐąŅŠĐĩĐē҂ҋ в аĐģŅŒĐąĐžĐŧŅ‹, Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒŅ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸", "organize_your_library": "ĐŸŅ€Đ¸Đ˛ĐĩĐ´Đ¸Ņ‚Đĩ в ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐ˛ĐžŅŽ йийĐģĐ¸ĐžŅ‚ĐĩĐē҃", "original": "ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģ", "other": "Đ”Ņ€ŅƒĐŗĐžĐĩ", "other_devices": "Đ”Ņ€ŅƒĐŗĐ¸Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°", + "other_entities": "Đ”Ņ€ŅƒĐŗĐ¸Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "other_variables": "Đ”Ņ€ŅƒĐŗĐ¸Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩĐŊĐŊŅ‹Đĩ", "owned": "Мои", "owner": "ВĐģадĐĩĐģĐĩ҆", - "partner": "ĐŸĐ°Ņ€Ņ‚ĐŊĐĩŅ€", - "partner_can_access": "{partner} иĐŧĐĩĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋ", - "partner_can_access_assets": "Đ’ŅĐĩ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩОСаĐŋĐ¸ŅĐ¸, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩŅ…, ĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ĐŊĐ°Ņ…ĐžĐ´ŅŅ‚ŅŅ в ĐŅ€Ņ…Đ¸Đ˛Đĩ и ĐšĐžŅ€ĐˇĐ¸ĐŊĐĩ", - "partner_can_access_location": "МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ, ĐŗĐ´Đĩ ĐąŅ‹Đģи ŅĐ´ĐĩĐģаĐŊŅ‹ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", - "partner_list_user_photos": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}", + "partner": "ĐŸĐ°Ņ€Ņ‚ĐŊґҀ", + "partner_can_access": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ {partner} Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹", + "partner_can_access_assets": "Đ’ŅĐĩ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž, ĐēŅ€ĐžĐŧĐĩ Ņ‚ĐĩŅ…, Ņ‡Ņ‚Đž ĐŊĐ°Ņ…ĐžĐ´ŅŅ‚ŅŅ в Đ°Ņ€Ņ…Đ¸Đ˛Đĩ и ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ", + "partner_can_access_location": "МĐĩŅŅ‚Đ°, ĐŗĐ´Đĩ ĐąŅ‹Đģи ŅĐ´ĐĩĐģаĐŊŅ‹ Đ˛Đ°ŅˆĐ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž", + "partner_list_user_photos": "Đ¤ĐžŅ‚Đž и видĐĩĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ {user}", "partner_list_view_all": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛ŅĐĩ", - "partner_page_empty_message": "ĐŖ Đ˛Đ°ŅˆĐĩĐŗĐž ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° Đĩ҉Đĩ ĐŊĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž.", + "partner_page_empty_message": "Đ’Ņ‹ ĐŋĐžĐēа ĐŊиĐēĐžĐŧ҃ иС ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€ĐžĐ˛ ĐŊĐĩ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģи Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ˛ĐžĐ¸Đŧ Ņ„ĐžŅ‚Đž и видĐĩĐž.", "partner_page_no_more_users": "Đ’Ņ‹ĐąŅ€Đ°ĐŊŅ‹ Đ˛ŅĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи", "partner_page_partner_add_failed": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ Đ´ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "partner_page_select_partner": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ°", "partner_page_shared_to_title": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ...", - "partner_page_stop_sharing_content": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {partner} йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž.", + "partner_page_stop_sharing_content": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {partner} йОĐģҌ҈Đĩ ĐŊĐĩ ĐąŅƒĐ´ĐĩŅ‚ иĐŧĐĩŅ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž и видĐĩĐž.", "partner_sharing": "ХОвĐŧĐĩҁ҂ĐŊĐžĐĩ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", "partners": "ĐŸĐ°Ņ€Ņ‚ĐŊґҀҋ", "password": "ĐŸĐ°Ņ€ĐžĐģҌ", @@ -1363,13 +1483,13 @@ "pending": "ОĐļидаĐĩŅ‚", "people": "Đ›ŅŽĐ´Đ¸", "people_edits_count": "{count, plural, one {ИСĐŧĐĩĐŊŅ‘ĐŊ # ҇ĐĩĐģОвĐĩĐē} many {ИСĐŧĐĩĐŊĐĩĐŊĐž # ҇ĐĩĐģОвĐĩĐē} other {ИСĐŧĐĩĐŊĐĩĐŊĐž # ҇ĐĩĐģОвĐĩĐēа}}", - "people_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đš и видĐĩĐž, ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊҋ҅ ĐŋĐž ĐģŅŽĐ´ŅĐŧ", + "people_feature_description": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ Ņ„ĐžŅ‚Đž и видĐĩĐž, ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊҋ҅ ĐŋĐž ĐģŅŽĐ´ŅĐŧ", "people_sidebar_description": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Đŋ҃ĐŊĐēŅ‚ ĐŧĐĩĐŊŅŽ \"Đ›ŅŽĐ´Đ¸\" в йОĐēОвОК ĐŋаĐŊĐĩĐģи", "permanent_deletion_warning": "ĐŸŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ Ой ŅƒĐ´Đ°ĐģĐĩĐŊии", "permanent_deletion_warning_setting_description": "ĐŸŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´Đ°Ņ‚ŅŒ ĐŋĐĩŅ€ĐĩĐ´ ĐąĐĩĐˇĐ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‚ĐŊŅ‹Đŧ ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩĐŧ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "permanently_delete": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "permanently_delete_assets_count": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {ĐžĐąŅŠĐĩĐēŅ‚} other {ĐžĐąŅŠĐĩĐē҂ҋ}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "permanently_delete_assets_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚} many {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? Đ­Ņ‚Đž Ņ‚Đ°ĐēĐļĐĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ {count, plural, one {ĐĩĐŗĐž} other {Đ¸Ņ…}} иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", + "permanently_delete_assets_prompt": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ° ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, =1 {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚} one {ŅŅ‚ĐžŅ‚ # ĐžĐąŅŠĐĩĐēŅ‚} many {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {ŅŅ‚Đ¸ # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}? {count, plural, =1 {ĐžĐąŅŠĐĩĐēŅ‚ Ņ‚Đ°ĐēĐļĐĩ ĐąŅƒĐ´ĐĩŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊ} other {ĐžĐąŅŠĐĩĐē҂ҋ Ņ‚Đ°ĐēĐļĐĩ ĐąŅƒĐ´ŅƒŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹}} иС Đ˛ŅĐĩŅ… аĐģŅŒĐąĐžĐŧОв.", "permanently_deleted_asset": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "permanently_deleted_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ŅƒĐ´Đ°ĐģŅ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ŅƒĐ´Đ°ĐģĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ŅƒĐ´Đ°ĐģĐĩĐŊĐž}} ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", "permission": "Đ Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐ¸Ņ", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Đ”ĐžŅŅ‚ŅƒĐŋ Đē Ņ„Đ°ĐšĐģаĐŧ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ. Đ§Ņ‚ĐžĐąŅ‹ Immich ĐŧĐžĐŗ ŅĐžĐˇĐ´Đ°Đ˛Đ°Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ‹Đĩ ĐēĐžĐŋии и ҃ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ Đ˛Đ°ŅˆĐĩĐš ĐŗĐ°ĐģĐĩŅ€ĐĩĐĩĐš, ĐŋĐžĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ŅŒŅ‚Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅ‚ŅƒĐŋ Đē \"Đ¤ĐžŅ‚Đž и видĐĩĐž\" в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ….", "permission_onboarding_request": "ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊиĐĩ ĐŊа Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž и видĐĩĐž.", "person": "ЧĐĩĐģОвĐĩĐē", + "person_age_months": "{months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", + "person_age_year_months": "1 ĐŗĐžĐ´, {months, plural, one {# ĐŧĐĩŅŅŅ†} many {# ĐŧĐĩŅŅŅ†Đĩв} other {# ĐŧĐĩŅŅŅ†Đ°}}", + "person_age_years": "{years, plural, one {# ĐŗĐžĐ´} many {# ĐģĐĩŅ‚} other {# ĐŗĐžĐ´Đ°}}", "person_birthdate": "Đ”Đ°Ņ‚Đ° Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ: {date}", "person_hidden": "{name}{hidden, select, true { (ҁĐēҀҋ҂)} other {}}", "photo_shared_all_users": "ĐŸĐžŅ…ĐžĐļĐĩ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ŅĐž Đ˛ŅĐĩĐŧи ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅĐŧи иĐģи ҃ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš, ҁ ĐēĐžŅ‚ĐžŅ€Ņ‹Đŧи ĐŧĐžĐļĐŊĐž ĐŋОдĐĩĐģĐ¸Ņ‚ŅŒŅŅ.", @@ -1392,7 +1515,7 @@ "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ ĐŋŅ€ĐžŅˆĐģҋ҅ ĐģĐĩŅ‚ в ŅŅ‚ĐžŅ‚ Đ´ĐĩĐŊҌ", "pick_a_location": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ", "pin_code_changed_successfully": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž иСĐŧĐĩĐŊŅ‘ĐŊ", - "pin_code_reset_successfully": "PIN-ĐēОд ŅĐąŅ€ĐžŅˆĐĩĐŊ", + "pin_code_reset_successfully": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐąŅ€ĐžŅˆĐĩĐŊ", "pin_code_setup_successfully": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ", "pin_verification": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēа PIN-ĐēОда", "place": "МĐĩŅŅ‚Đ°", @@ -1406,6 +1529,7 @@ "port": "ĐŸĐžŅ€Ņ‚", "preferences_settings_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа вĐŊĐĩ҈ĐŊĐĩĐŗĐž вида", "preferences_settings_title": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ", + "preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа", "preset": "ĐŸŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ", "preview": "ĐŸŅ€ĐĩĐ´Đ˛Đ°Ņ€Đ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đš ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€", "previous": "ĐŸŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰ĐĩĐĩ", @@ -1417,16 +1541,17 @@ "primary": "ГĐģавĐŊĐžĐĩ", "privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ", "profile": "ĐŸŅ€ĐžŅ„Đ¸ĐģҌ", - "profile_drawer_app_logs": "Đ–ŅƒŅ€ĐŊаĐģ", + "profile_drawer_app_logs": "Đ–ŅƒŅ€ĐŊаĐģ ŅĐžĐąŅ‹Ņ‚Đ¸Đš", "profile_drawer_client_out_of_date_major": "ВĐĩŅ€ŅĐ¸Ņ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_client_out_of_date_minor": "ВĐĩŅ€ŅĐ¸Ņ ĐŧОйиĐģҌĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_client_server_up_to_date": "КĐģиĐĩĐŊŅ‚ и ҁĐĩŅ€Đ˛ĐĩŅ€ ОйĐŊОвĐģĐĩĐŊŅ‹", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "ВĐēĐģŅŽŅ‡Ņ‘ĐŊ Ņ€ĐĩĐļиĐŧ ÂĢŅ‚ĐžĐģҌĐēĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Âģ. ĐŖĐ´ĐĩŅ€ĐļĐ¸Đ˛Đ°ĐšŅ‚Đĩ СĐŊĐ°Ņ‡ĐžĐē Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ Đ´ĐģŅ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐ¸Ņ.", "profile_drawer_server_out_of_date_major": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_drawer_server_out_of_date_minor": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ŅƒŅŅ‚Đ°Ņ€ĐĩĐģа. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐĩĐŗĐž.", "profile_image_of_user": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ ĐŋŅ€ĐžŅ„Đ¸ĐģŅ {user}", "profile_picture_set": "Đ¤ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž.", - "public_album": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đš аĐģŅŒĐąĐžĐŧ", + "public_album": "ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "public_share": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ", "purchase_account_info": "ПоддĐĩŅ€ĐļĐēа", "purchase_activated_subtitle": "БĐģĐ°ĐŗĐžĐ´Đ°Ņ€Đ¸Đŧ Đ˛Đ°Ņ Са ĐŋОддĐĩŅ€ĐļĐē҃ Immich и ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐŧĐŊĐžĐŗĐž ОйĐĩҁĐŋĐĩ҇ĐĩĐŊĐ¸Ņ ҁ ĐžŅ‚ĐēҀҋ҂ҋĐŧ Đ¸ŅŅ…ĐžĐ´ĐŊŅ‹Đŧ ĐēОдОĐŧ", @@ -1460,16 +1585,21 @@ "purchase_server_description_2": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ ĐŋОддĐĩŅ€ĐļĐēи", "purchase_server_title": "ĐĄĐĩŅ€Đ˛ĐĩŅ€", "purchase_settings_server_activated": "КĐģŅŽŅ‡ĐžĐŧ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ° ҃ĐŋŅ€Đ°Đ˛ĐģŅĐĩŅ‚ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "rating": "Đ ĐĩĐšŅ‚Đ¸ĐŊĐŗ ĐˇĐ˛Ņ‘ĐˇĐ´", + "query_asset_id": "ИдĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€ Đ¸ŅŅ…ĐžĐ´ĐŊĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", + "queue_status": "В ĐžŅ‡ĐĩŅ€Đĩди {count}/{total}", + "rating": "Đ ĐĩĐšŅ‚Đ¸ĐŊĐŗ", "rating_clear": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ", "rating_count": "{count, plural, one {# СвĐĩСда} many {# СвĐĩСд} other {# СвĐĩĐˇĐ´Ņ‹}}", - "rating_description": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ в ĐŋаĐŊĐĩĐģи иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", - "reaction_options": "ОĐŋŅ†Đ¸Đ¸ Ņ€ĐĩаĐēŅ†Đ¸Đš", - "read_changelog": "ĐŸŅ€ĐžŅ‡Đ¸Ņ‚Đ°Ņ‚ŅŒ ҁĐŋĐ¸ŅĐžĐē иСĐŧĐĩĐŊĐĩĐŊиК", + "rating_description": "ĐĄĐ¸ŅŅ‚ĐĩĐŧа ĐžŅ†ĐĩĐŊĐēи ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в ĐŋаĐŊĐĩĐģи иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", + "reaction_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ ĐžŅ‚ĐŧĐĩŅ‚ĐēОК", + "read_changelog": "Đ˜ŅŅ‚ĐžŅ€Đ¸Ņ Ņ€ĐĩĐģиСОв", + "readonly_mode_disabled": "Đ ĐĩĐļиĐŧ ÂĢŅ‚ĐžĐģҌĐēĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Âģ ĐžŅ‚ĐēĐģŅŽŅ‡Ņ‘ĐŊ", + "readonly_mode_enabled": "Đ ĐĩĐļиĐŧ ÂĢŅ‚ĐžĐģҌĐēĐž ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Âģ вĐēĐģŅŽŅ‡Ņ‘ĐŊ", + "ready_for_upload": "Đ“ĐžŅ‚ĐžĐ˛Đž Đē ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ", "reassign": "ПĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ", "reassigned_assets_to_existing_person": "Đ›Đ¸Ņ†Đ° ĐŊа {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đĩ} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°Ņ…}} ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊŅ‹ ĐŊа {name, select, null {Đ´Ņ€ŅƒĐŗĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа} other {҇ĐĩĐģОвĐĩĐēа ҁ иĐŧĐĩĐŊĐĩĐŧ {name}}}", "reassigned_assets_to_new_person": "Đ›Đ¸Ņ†Đ° ĐŊа {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đĩ} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°Ņ…}} ĐŋĐĩŅ€ĐĩĐŊаСĐŊĐ°Ņ‡ĐĩĐŊŅ‹ ĐŊа ĐŊĐžĐ˛ĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", - "reassing_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ Ņ€ĐĩŅŅƒŅ€ŅŅ‹ ҃ĐēаСаĐŊĐŊĐžĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", + "reassing_hint": "НазĐŊĐ°Ņ‡Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ҃ĐēаСаĐŊĐŊĐžĐŧ҃ ҇ĐĩĐģОвĐĩĐē҃", "recent": "НĐĩдавĐŊиĐĩ", "recent-albums": "НĐĩдавĐŊиĐĩ аĐģŅŒĐąĐžĐŧŅ‹", "recent_searches": "НĐĩдавĐŊиĐĩ ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đĩ СаĐŋŅ€ĐžŅŅ‹", @@ -1488,6 +1618,9 @@ "refreshing_faces": "ОбĐŊОвĐģĐĩĐŊиĐĩ ĐģĐ¸Ņ†", "refreshing_metadata": "ОбĐŊОвĐģĐĩĐŊиĐĩ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊҋ҅", "regenerating_thumbnails": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиĐĩ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€", + "remote": "На ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", + "remote_assets": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", + "remote_media_summary": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой ĐžĐąŅŠĐĩĐēŅ‚Đĩ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ", "remove": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ", "remove_assets_album_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС аĐģŅŒĐąĐžĐŧа?", "remove_assets_shared_link_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ°}} иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа ĐŋĐž ŅŅ‚ĐžĐš ҁҁҋĐģĐēĐĩ?", @@ -1495,9 +1628,11 @@ "remove_custom_date_range": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК диаĐŋаСОĐŊ Đ´Đ°Ņ‚", "remove_deleted_assets": "ĐŖĐ´Đ°ĐģĐĩĐŊиĐĩ Đ°Đ˛Ņ‚ĐžĐŊĐžĐŧĐŊҋ҅ Ņ„Đ°ĐšĐģОв", "remove_from_album": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС аĐģŅŒĐąĐžĐŧа", + "remove_from_album_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС аĐģŅŒĐąĐžĐŧа ({count} ŅˆŅ‚.)", "remove_from_favorites": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", + "remove_from_lock_folder_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи ({count} ŅˆŅ‚.)", "remove_from_locked_folder": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи", - "remove_from_locked_folder_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ ŅŅ‚Đ¸ Ņ„ĐžŅ‚Đž и видĐĩĐž иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи? ОĐŊи ŅŅ‚Đ°ĐŊŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ в Đ˛Đ°ŅˆĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", + "remove_from_locked_folder_confirmation": "Đ’Ņ‹ Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐąŅ€Đ°Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ иС ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēи? ОĐŊи ҁĐŊОва ŅŅ‚Đ°ĐŊŅƒŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋĐŊŅ‹ в Đ˛Đ°ŅˆĐĩĐš йийĐģĐ¸ĐžŅ‚ĐĩĐēĐĩ.", "remove_from_shared_link": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", "remove_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊиĐĩ", "remove_photo_from_memory": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž иС Đ˛ĐžŅĐŋĐžĐŧиĐŊаĐŊĐ¸Ņ", @@ -1520,28 +1655,38 @@ "require_user_to_change_password_on_first_login": "ĐĸŅ€ĐĩĐąĐžĐ˛Đ°Ņ‚ŅŒ ҃ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ҁĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€Đ¸ ĐŋĐĩŅ€Đ˛ĐžĐŧ Đ˛Ņ…ĐžĐ´Đĩ", "rescan": "ĐŸĐžĐ˛Ņ‚ĐžŅ€ĐŊĐžĐĩ ҁĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ", "reset": "ĐĄĐąŅ€ĐžŅ", - "reset_password": "ĐĄĐąŅ€ĐžŅ ĐŋĐ°Ņ€ĐžĐģŅ", + "reset_password": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", "reset_people_visibility": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ видиĐŧĐžŅŅ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "reset_pin_code": "ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд", + "reset_pin_code_description": "Đ•ŅĐģи Đ˛Ņ‹ ĐˇĐ°ĐąŅ‹Đģи ŅĐ˛ĐžĐš PIN-ĐēОд, Đ˛Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ŅŒŅŅ Đē адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, Ņ‡Ņ‚ĐžĐąŅ‹ ĐĩĐŗĐž ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ", + "reset_pin_code_success": "PIN-ĐēОд ҃ҁĐŋĐĩ҈ĐŊĐž ŅĐąŅ€ĐžŅˆĐĩĐŊ", + "reset_pin_code_with_password": "Đ’Ņ‹ Đ˛ŅĐĩĐŗĐ´Đ° ĐŧĐžĐļĐĩŅ‚Đĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ PIN-ĐēОд ҁ ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ĐŋĐ°Ņ€ĐžĐģŅ", + "reset_sqlite": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐąĐ°ĐˇŅƒ даĐŊĐŊҋ҅ SQLite", + "reset_sqlite_confirmation": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚ŅŒ ĐąĐ°ĐˇŅƒ даĐŊĐŊҋ҅ SQLite? ВаĐŧ ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Đ˛Ņ‹ĐšŅ‚Đ¸ иС ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ и ҁĐŊОва Đ˛ĐžĐšŅ‚Đ¸ Đ´ĐģŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊОК ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ даĐŊĐŊҋ҅.", + "reset_sqlite_success": "База даĐŊĐŊҋ҅ SQLite ҃ҁĐŋĐĩ҈ĐŊĐž ĐžŅ‡Đ¸Ņ‰ĐĩĐŊа", "reset_to_default": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиĐĩ СĐŊĐ°Ņ‡ĐĩĐŊиК ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ", "resolve_duplicates": "ĐŖŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "resolved_all_duplicates": "Đ’ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹ ŅƒŅŅ‚Ņ€Đ°ĐŊĐĩĐŊŅ‹", "restore": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", "restore_all": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", + "restore_trash_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ‹ иС ĐēĐžŅ€ĐˇĐ¸ĐŊŅ‹ ({count} ŅˆŅ‚.)", "restore_user": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "restored_asset": "Đ’ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ‹Đš ĐžĐąŅŠĐĩĐēŅ‚", "resume": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ", + "resume_paused_jobs": "ВозобĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊиĐĩ {count, plural, one {# ĐˇĐ°Đ´Đ°Ņ‡Đ¸} other {# ĐˇĐ°Đ´Đ°Ņ‡}}", "retry_upload": "ĐŸĐžĐ˛Ņ‚ĐžŅ€Đ¸Ņ‚ŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐē҃", - "review_duplicates": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", + "review_duplicates": "Đ Đ°ĐˇĐąĐžŅ€ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", + "review_large_files": "ĐžĐąĐˇĐžŅ€ йОĐģŅŒŅˆĐ¸Ņ… Ņ„Đ°ĐšĐģОв", "role": "Đ ĐžĐģҌ", "role_editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "role_viewer": "Đ—Ņ€Đ¸Ņ‚ĐĩĐģҌ", + "running": "Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ", "save": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ", "save_to_gallery": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ в ĐŗĐ°ĐģĐĩŅ€ĐĩŅŽ", "saved_api_key": "API ĐēĐģŅŽŅ‡ иСĐŧĐĩĐŊŅ‘ĐŊ", "saved_profile": "ĐŸŅ€ĐžŅ„Đ¸ĐģҌ ŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊ", "saved_settings": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", - "say_something": "ĐĄĐēаĐļĐ¸Ņ‚Đĩ Ņ‡Ņ‚Đž-ĐŊĐ¸ĐąŅƒĐ´ŅŒ", + "say_something": "НаĐŋĐ¸ŅˆĐ¸Ņ‚Đĩ Ņ‡Ņ‚Đž-ĐŊĐ¸ĐąŅƒĐ´ŅŒ", "scaffold_body_error_occurred": "ВозĐŊиĐēĐģа ĐžŅˆĐ¸ĐąĐēа", "scan_all_libraries": "ĐĄĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ йийĐģĐ¸ĐžŅ‚ĐĩĐēи", "scan_library": "ĐĄĐēаĐŊĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", @@ -1562,7 +1707,7 @@ "search_filter_camera_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ‚Đ¸Đŋ ĐēаĐŧĐĩҀҋ", "search_filter_date": "Đ”Đ°Ņ‚Đ°", "search_filter_date_interval": "{start} — {end}", - "search_filter_date_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋŅ€ĐžĐŧĐĩĐļŅƒŅ‚ĐžĐē", + "search_filter_date_title": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŋĐĩŅ€Đ¸ĐžĐ´", "search_filter_display_option_not_in_album": "НĐĩ в аĐģŅŒĐąĐžĐŧĐĩ", "search_filter_display_options": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "search_filter_filename": "ĐŸĐžĐ¸ŅĐē ĐŋĐž иĐŧĐĩĐŊи Ņ„Đ°ĐšĐģа", @@ -1607,33 +1752,34 @@ "select": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ", "select_album_cover": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ОйĐģĐžĐļĐē҃ аĐģŅŒĐąĐžĐŧа", "select_all": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ", - "select_all_duplicates": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", + "select_all_duplicates": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ĐģŅ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐ¸Ņ", "select_all_in": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ в {group}", "select_avatar_color": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ†Đ˛ĐĩŅ‚ Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ°", "select_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐģĐ¸Ņ†Đž", "select_featured_photo": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ Ņ„ĐžŅ‚Đž", - "select_from_computer": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ҁ ĐēĐžĐŧĐŋŅŒŅŽŅ‚ĐĩŅ€Đ°", - "select_keep_all": "ĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", + "select_from_computer": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ҁ ĐēĐžĐŧĐŋŅŒŅŽŅ‚ĐĩŅ€Đ°", + "select_keep_all": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ĐģŅ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐ¸Ņ", "select_library_owner": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ вĐģадĐĩĐģŅŒŅ†Đ° йийĐģĐ¸ĐžŅ‚ĐĩĐēи", - "select_new_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐžĐĩ ĐģĐ¸Ņ†Đž", + "select_new_face": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ´Ņ€ŅƒĐŗĐžĐŗĐž ҇ĐĩĐģОвĐĩĐēа", "select_person_to_tag": "Đ’Ņ‹Đ´ĐĩĐģĐ¸Ņ‚Đĩ ĐģĐ¸Ņ†Đž ҇ĐĩĐģОвĐĩĐēа, ĐēĐžŅ‚ĐžŅ€ĐžĐŗĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐŧĐĩŅ‚Đ¸Ņ‚ŅŒ", "select_photos": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", - "select_trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊĐžĐĩ", + "select_trash_all": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ĐģŅ ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ", "select_user_for_sharing_page_err_album": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžĐˇĐ´Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "selected": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž", "selected_count": "{count, plural, one {Đ’Ņ‹ĐąŅ€Đ°ĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {Đ’Ņ‹ĐąŅ€Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", + "selected_gps_coordinates": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Ņ‹", "send_message": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ", "send_welcome_email": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŋŅ€Đ¸Đ˛ĐĩŅ‚ŅŅ‚Đ˛ĐĩĐŊĐŊĐžĐĩ ĐŋĐ¸ŅŅŒĐŧĐž", "server_endpoint": "ĐĐ´Ņ€Đĩҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_info_box_app_version": "ВĐĩŅ€ŅĐ¸Ņ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "server_info_box_server_url": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "server_offline": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐŊĐĩ в ҁĐĩŅ‚Đ¸", + "server_offline": "ĐžŅ„Ņ„ĐģаКĐŊ", "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ в ҁĐĩŅ‚Đ¸", "server_privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_stats": "ĐĄŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_version": "ВĐĩŅ€ŅĐ¸Ņ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "set": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", - "set_as_album_cover": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ ОйĐģĐžĐļĐēи аĐģŅŒĐąĐžĐŧа", + "set_as_album_cover": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐēаĐē ОйĐģĐžĐļĐē҃ аĐģŅŒĐąĐžĐŧа", "set_as_featured_photo": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐēаĐē ĐžŅĐŊОвĐŊĐžĐĩ Ņ„ĐžŅ‚Đž", "set_as_profile_picture": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐēаĐē Ņ„ĐžŅ‚Đž ĐŋŅ€ĐžŅ„Đ¸ĐģŅ", "set_date_of_birth": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´Đ°Ņ‚Ņƒ Ņ€ĐžĐļĐ´ĐĩĐŊĐ¸Ņ", @@ -1647,7 +1793,7 @@ "setting_image_viewer_preview_title": "Đ—Đ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ ҃ĐŧĐĩĐŊҌ҈ĐĩĐŊĐŊĐžĐĩ Đ¸ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊиĐĩ", "setting_image_viewer_title": "Đ˜ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐ¸Ņ", "setting_languages_apply": "ĐŸŅ€Đ¸ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", - "setting_languages_subtitle": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅĐˇŅ‹Đē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", + "setting_languages_subtitle": "ИСĐŧĐĩĐŊĐĩĐŊиĐĩ ŅĐˇŅ‹Đēа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ", "setting_notifications_notify_failures_grace_period": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģŅŅ‚ŅŒ Ой ĐžŅˆĐ¸ĐąĐēĐ°Ņ… Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ: {duration}", "setting_notifications_notify_hours": "{count} ҇.", "setting_notifications_notify_immediately": "ĐŊĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž", @@ -1656,7 +1802,7 @@ "setting_notifications_notify_seconds": "{count} ҁĐĩĐē.", "setting_notifications_single_progress_subtitle": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž Ņ…ĐžĐ´Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи Đ´ĐģŅ ĐēаĐļĐ´ĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", "setting_notifications_single_progress_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ Ņ…ĐžĐ´ Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", - "setting_notifications_subtitle": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐ˛ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", + "setting_notifications_subtitle": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК", "setting_notifications_total_progress_subtitle": "ĐžĐąŅ‰Đ¸Đš ĐŋŅ€ĐžĐŗŅ€Đĩҁҁ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи (Đ˛Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐž/Đ˛ŅĐĩĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛)", "setting_notifications_total_progress_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đš ĐŋŅ€ĐžĐŗŅ€Đĩҁҁ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", "setting_video_viewer_looping_title": "ĐĻиĐēĐģĐ¸Ņ‡ĐĩҁĐēĐžĐĩ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиĐĩ", @@ -1667,6 +1813,7 @@ "settings_saved": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊŅ‹", "setup_pin_code": "ХОСдаĐŊиĐĩ PIN-ĐēОда", "share": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ", + "share_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ в ĐžĐąŅ‰ĐĩĐŧ Đ´ĐžŅŅ‚ŅƒĐŋĐĩ ({count} ŅˆŅ‚.)", "share_add_photos": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ„ĐžŅ‚Đž", "share_assets_selected": "{count} Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐž", "share_dialog_preparing": "ĐŸĐžĐ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", @@ -1682,22 +1829,23 @@ "shared_by": "ПодĐĩĐģиĐģŅŅ", "shared_by_user": "ВĐģадĐĩĐģĐĩ҆: {user}", "shared_by_you": "Đ’Ņ‹ ĐŋОдĐĩĐģиĐģĐ¸ŅŅŒ", - "shared_from_partner": "Đ¤ĐžŅ‚Đž ĐžŅ‚ {partner}", + "shared_from_partner": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {partner} ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģ ваĐŧ Đ´ĐžŅŅ‚ŅƒĐŋ", "shared_intent_upload_button_progress_text": "{current} / {total} Đ—Đ°ĐŗŅ€ŅƒĐļĐĩĐŊĐž", "shared_link_app_bar_title": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", "shared_link_clipboard_copied_massage": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа", "shared_link_clipboard_text": "ĐĄŅŅ‹ĐģĐēа: {link}\nĐŸĐ°Ņ€ĐžĐģҌ: {password}", "shared_link_create_error": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐžĐˇĐ´Đ°ĐŊии ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊОК ҁҁҋĐģĐēи", - "shared_link_edit_description_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", + "shared_link_custom_url_description": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēиК URL-Đ°Đ´Ņ€Đĩҁ ĐžĐąŅ‰ĐĩĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", + "shared_link_edit_description_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩ", "shared_link_edit_expire_after_option_day": "1 Đ´ĐĩĐŊҌ", "shared_link_edit_expire_after_option_days": "{count} Đ´ĐŊĐĩĐš", "shared_link_edit_expire_after_option_hour": "1 Ņ‡Đ°Ņ", "shared_link_edit_expire_after_option_hours": "{count} Ņ‡Đ°ŅĐžĐ˛", "shared_link_edit_expire_after_option_minute": "1 ĐŧиĐŊŅƒŅ‚Ņƒ", "shared_link_edit_expire_after_option_minutes": "{count} ĐŧиĐŊŅƒŅ‚", - "shared_link_edit_expire_after_option_months": "{count} ĐŧĐĩŅŅŅ†Đĩв", + "shared_link_edit_expire_after_option_months": "{count} ĐŧĐĩŅŅŅ†Đ°", "shared_link_edit_expire_after_option_year": "{count} ĐģĐĩŅ‚", - "shared_link_edit_password_hint": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋа", + "shared_link_edit_password_hint": "Đ—Đ°Ņ‰Đ¸Ņ‚Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ", "shared_link_edit_submit_button": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ҁҁҋĐģĐē҃", "shared_link_error_server_url_fetch": "НĐĩвОСĐŧĐžĐļĐŊĐž СаĐŋŅ€ĐžŅĐ¸Ņ‚ŅŒ URL ҁ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "shared_link_expires_day": "Đ˜ŅŅ‚Đĩ҇ґ҂ ҇ĐĩŅ€ĐĩС {count} Đ´ĐĩĐŊҌ", @@ -1712,12 +1860,13 @@ "shared_link_individual_shared": "ИĐŊĐ´Đ¸Đ˛Đ¸Đ´ŅƒĐ°ĐģҌĐŊŅ‹Đš ĐžĐąŅ‰Đ¸Đš Đ´ĐžŅŅ‚ŅƒĐŋ", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ĐŖĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đŧи ҁҁҋĐģĐēаĐŧи", - "shared_link_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊҋ҅ ҁҁҋĐģĐžĐē", + "shared_link_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ŅĐž ҁҁҋĐģĐēОК", + "shared_link_password_description": "ĐĸŅ€ĐĩĐąĐžĐ˛Đ°Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē ĐžĐąŅŠĐĩĐēŅ‚Đ°Đŧ", "shared_links": "ĐŸŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", "shared_links_description": "ДĐĩĐģĐ¸Ņ‚ĐĩҁҌ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи и видĐĩĐž ĐŋĐž ҁҁҋĐģĐēĐĩ", "shared_photos_and_videos_count": "{assetCount, plural, other {# Ņ„ĐžŅ‚Đž и видĐĩĐž.}}", "shared_with_me": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊŅ‹Đĩ ĐŧĐŊĐĩ", - "shared_with_partner": "ХОвĐŧĐĩҁ҂ĐŊĐž ҁ {partner}", + "shared_with_partner": "Đ’Ņ‹ ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛Đ¸Đģи Đ´ĐžŅŅ‚ŅƒĐŋ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ {partner}", "sharing": "ĐžĐąŅ‰Đ¸Đĩ", "sharing_enter_password": "ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ввĐĩĐ´Đ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° ŅŅ‚ĐžĐš ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņ‹.", "sharing_page_album": "ĐžĐąŅ‰Đ¸Đĩ аĐģŅŒĐąĐžĐŧŅ‹", @@ -1727,7 +1876,7 @@ "sharing_silver_appbar_create_shared_album": "ĐĄĐžĐˇĐ´Đ°Ņ‚ŅŒ ĐžĐąŅ‰Đ¸Đš аĐģŅŒĐąĐžĐŧ", "sharing_silver_appbar_share_partner": "ПодĐĩĐģĐ¸Ņ‚ŅŒŅŅ ҁ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€ĐžĐŧ", "shift_to_permanent_delete": "ĐŊаĐļĐŧĐ¸Ņ‚Đĩ ⇧ Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°", - "show_album_options": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ аĐģŅŒĐąĐžĐŧа", + "show_album_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ аĐģŅŒĐąĐžĐŧĐžĐŧ", "show_albums": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧŅ‹", "show_all_people": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ Đ˛ŅĐĩŅ… ĐģŅŽĐ´ĐĩĐš", "show_and_hide_people": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ и ҁĐēŅ€Ņ‹Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", @@ -1735,21 +1884,22 @@ "show_gallery": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŗĐ°ĐģĐĩŅ€ĐĩŅŽ", "show_hidden_people": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁĐēҀҋ҂ҋ҅ ĐģŅŽĐ´ĐĩĐš", "show_in_timeline": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊОК ҈ĐēаĐģĐĩ", - "show_in_timeline_setting_description": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°ĐšŅ‚Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž ŅŅ‚ĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ в ŅĐ˛ĐžĐĩĐš ĐģĐĩĐŊŅ‚Đĩ", + "show_in_timeline_setting_description": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Ņ„ĐžŅ‚Đž и видĐĩĐž ŅŅ‚ĐžĐŗĐž ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ ĐŊа ŅĐ˛ĐžĐĩĐš Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊОК ҈ĐēаĐģĐĩ", "show_keyboard_shortcuts": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ŅĐžŅ‡ĐĩŅ‚Đ°ĐŊĐ¸Ņ ĐēĐģĐ°Đ˛Đ¸Ņˆ", "show_metadata": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐŊŅ‹Đĩ", "show_or_hide_info": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ иĐģи ҁĐēŅ€Ņ‹Ņ‚ŅŒ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ", "show_password": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", - "show_person_options": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐžĐŋŅ†Đ¸Đ¸ ĐŋĐĩŅ€ŅĐžĐŊŅ‹", + "show_person_options": "ДĐĩĐšŅŅ‚Đ˛Đ¸Ņ ҁ ҇ĐĩĐģОвĐĩĐēĐžĐŧ", "show_progress_bar": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ИĐŊдиĐēĐ°Ņ‚ĐžŅ€ Đ’Ņ‹ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ", "show_search_options": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁҋ ĐŋĐžĐ¸ŅĐēа", "show_shared_links": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊŅ‹Đĩ ҁҁҋĐģĐēи", "show_slideshow_transition": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁĐģаКд-ŅˆĐžŅƒ ĐŋĐĩŅ€ĐĩŅ…ĐžĐ´", "show_supporter_badge": "ЗĐŊĐ°Ņ‡ĐžĐē ĐŋОддĐĩŅ€ĐļĐēи", "show_supporter_badge_description": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ СĐŊĐ°Ņ‡ĐžĐē ĐŋОддĐĩŅ€ĐļĐēи", + "show_text_search_menu": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŧĐĩĐŊŅŽ Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛ĐžĐŗĐž ĐŋĐžĐ¸ŅĐēа", "shuffle": "ПĐĩŅ€ĐĩĐŧĐĩŅˆĐ°Ņ‚ŅŒ", "sidebar": "БоĐēĐžĐ˛Đ°Ņ ĐŋаĐŊĐĩĐģҌ", - "sidebar_display_description": "ПоĐēĐ°ĐˇŅ‹Đ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐŊа ĐŋŅ€ĐĩĐ´ŅŅ‚Đ°Đ˛ĐģĐĩĐŊиĐĩ в йОĐēОвОК ĐŋаĐŊĐĩĐģи", + "sidebar_display_description": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ Ņ€Đ°ĐˇĐ´ĐĩĐģ ĐŊа йОĐēОвОК ĐŋаĐŊĐĩĐģи", "sign_out": "Đ’Ņ‹Ņ…ĐžĐ´", "sign_up": "Đ—Đ°Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒŅŅ", "size": "РаСĐŧĐĩŅ€", @@ -1762,31 +1912,35 @@ "sort_created": "Đ”Đ°Ņ‚Đ° ŅĐžĐˇĐ´Đ°ĐŊĐ¸Ņ", "sort_items": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛", "sort_modified": "Đ”Đ°Ņ‚Đ° иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ", - "sort_oldest": "ĐĄŅ‚Đ°Ņ€Ņ‹Đĩ Ņ„ĐžŅ‚Đž", + "sort_newest": "ĐŸĐžŅĐģĐĩĐ´ĐŊĐĩĐĩ Ņ„ĐžŅ‚Đž", + "sort_oldest": "ĐĄŅ‚Đ°Ņ€ĐĩĐšŅˆĐĩĐĩ Ņ„ĐžŅ‚Đž", "sort_people_by_similarity": "ĐĄĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš ĐŋĐž ŅŅ…ĐžĐ´ŅŅ‚Đ˛Ņƒ", "sort_recent": "НĐĩдавĐŊиĐĩ Ņ„ĐžŅ‚Đž", "sort_title": "Đ—Đ°ĐŗĐžĐģОвОĐē", "source": "Đ˜ŅŅ…ĐžĐ´ĐŊŅ‹Đš ĐēОд", - "stack": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", - "stack_duplicates": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", + "stack": "ĐĄĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "stack_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊŅ‹ ({count} ŅˆŅ‚.)", + "stack_duplicates": "ĐĄĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", "stack_select_one_photo": "Đ’Ņ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ĐŗĐģавĐŊŅƒŅŽ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅŽ Đ´ĐģŅ ĐŗŅ€ŅƒĐŋĐŋŅ‹", - "stack_selected_photos": "Đ“Ņ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", + "stack_selected_photos": "ĐĄĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "stacked_assets_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ ĐžĐąŅŠĐĩдиĐŊĐĩĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° ĐžĐąŅŠĐĩдиĐŊĐĩĐŊĐž}} в ĐŗŅ€ŅƒĐŋĐŋ҃", "stacktrace": "ĐĸŅ€Đ°ŅŅĐ¸Ņ€ĐžĐ˛Đēа ҁ҂ĐĩĐēа", "start": "ĐĄŅ‚Đ°Ņ€Ņ‚", "start_date": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ‡Đ°Đģа", + "start_date_before_end_date": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ‡Đ°Đģа Đ´ĐžĐģĐļĐŊа ĐąŅ‹Ņ‚ŅŒ ĐŧĐĩĐŊҌ҈Đĩ Đ´Đ°Ņ‚Ņ‹ ĐžĐēĐžĐŊŅ‡Đ°ĐŊĐ¸Ņ", "state": "Đ ĐĩĐŗĐ¸ĐžĐŊ", "status": "ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ", "stop_casting": "ĐžŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Đ¸ŅŽ", "stop_motion_photo": "ПоĐēĐ°Đ´Ņ€ĐžĐ˛Đ°Ņ аĐŊиĐŧĐ°Ņ†Đ¸Ņ", - "stop_photo_sharing": "ЗаĐēŅ€Ņ‹Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Đ° Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚Đž?", - "stop_photo_sharing_description": "{partner} йОĐģҌ҈Đĩ ĐŊĐĩ ҁĐŧĐžĐļĐĩŅ‚ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧ.", - "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧи ҁ ŅŅ‚Đ¸Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ", + "stop_photo_sharing": "ЗаĐēŅ€Ņ‹Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŋĐ°Ņ€Ņ‚ĐŊŅ‘Ņ€Ņƒ?", + "stop_photo_sharing_description": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ {partner} йОĐģҌ҈Đĩ ĐŊĐĩ иĐŧĐĩĐĩŅ‚ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Đ˛Đ°ŅˆĐ¸Đŧ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸ŅĐŧ.", + "stop_sharing_photos_with_user": "ĐŸŅ€ĐĩĐēŅ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐĩĐģĐ¸Ņ‚ŅŒŅŅ ŅĐ˛ĐžĐ¸Đŧи Ņ„ĐžŅ‚Đž и видĐĩĐž ҁ ŅŅ‚Đ¸Đŧ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐŧ", "storage": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", "storage_label": "МĐĩŅ‚Đēа Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_quota": "ĐšĐ˛ĐžŅ‚Đ° Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°", "storage_usage": "{used} иС {available}", "submit": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚ŅŒ", + "success": "ĐŖŅĐŋĐĩ҈ĐŊĐž", "suggestions": "ĐŸŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸Ņ", "sunrise_on_the_beach": "Đ’ĐžŅŅ…ĐžĐ´ ŅĐžĐģĐŊŅ†Đ° ĐŊа ĐŋĐģŅĐļĐĩ", "support": "ПоддĐĩŅ€ĐļĐēа", @@ -1796,7 +1950,11 @@ "sync": "ХиĐŊŅ…Ņ€.", "sync_albums": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧŅ‹", "sync_albums_manual_subtitle": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Đ˛ŅĐĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ Ņ„ĐžŅ‚Đž и видĐĩĐž в Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ аĐģŅŒĐąĐžĐŧŅ‹ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ", - "sync_upload_album_setting_subtitle": "ĐĄĐžĐˇĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ и ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°ĐšŅ‚Đĩ ŅĐ˛ĐžĐ¸ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸ и видĐĩĐž в Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ аĐģŅŒĐąĐžĐŧŅ‹ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€ Immich", + "sync_local": "ЛоĐēаĐģҌĐŊĐ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ", + "sync_remote": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ҁ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", + "sync_status": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸", + "sync_status_subtitle": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ и ҃ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧОК ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸", + "sync_upload_album_setting_subtitle": "ĐĄĐžĐˇĐ´Đ°Đ˛Đ°Ņ‚ŅŒ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€Đĩ Ņ‚Đ°ĐēиĐĩ ĐļĐĩ аĐģŅŒĐąĐžĐŧŅ‹, ĐēаĐē Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ, и ĐˇĐ°ĐŗŅ€ŅƒĐļĐ°Ņ‚ŅŒ в ĐŊĐ¸Ņ… Ņ„ĐžŅ‚Đž и видĐĩĐž", "tag": "ĐĸĐĩĐŗ", "tag_assets": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ Ņ‚ĐĩĐŗĐ¸", "tag_created": "ĐĸĐĩĐŗ {tag} ŅĐžĐˇĐ´Đ°ĐŊ", @@ -1806,11 +1964,12 @@ "tag_updated": "ĐĸĐĩĐŗ {tag} иСĐŧĐĩĐŊĐĩĐŊ", "tagged_assets": "ĐĸĐĩĐŗ ĐŊаСĐŊĐ°Ņ‡ĐĩĐŊ Đ´ĐģŅ {count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚Đ°} other {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛}}", "tags": "ĐĸĐĩĐŗĐ¸", + "tap_to_run_job": "НаĐļĐŧĐ¸Ņ‚Đĩ Đ´ĐģŅ СаĐŋ҃ҁĐēа ĐˇĐ°Đ´Đ°Ņ‡Đ¸", "template": "ШайĐģĐžĐŊ", "theme": "ĐĸĐĩĐŧа", "theme_selection": "Đ’Ņ‹ĐąĐžŅ€ Ņ‚ĐĩĐŧŅ‹", - "theme_selection_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ Ņ‚ĐĩĐŧ҃ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ŅĐ¸ŅŅ‚ĐĩĐŧĐŊҋ҅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ˛Đ°ŅˆĐĩĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", - "theme_setting_asset_list_storage_indicator_title": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ иĐŊдиĐēĐ°Ņ‚ĐžŅ€ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŊа ĐŋĐģĐ¸Ņ‚ĐēĐ°Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", + "theme_selection_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒŅŅ‚Đ°ĐŊавĐģĐ¸Đ˛Đ°Ņ‚ŅŒ ŅĐ˛ĐĩŅ‚ĐģŅƒŅŽ иĐģи ҂ґĐŧĐŊŅƒŅŽ Ņ‚ĐĩĐŧ҃ в ĐˇĐ°Đ˛Đ¸ŅĐ¸ĐŧĐžŅŅ‚Đ¸ ĐžŅ‚ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē Đ˛Đ°ŅˆĐĩĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", + "theme_setting_asset_list_storage_indicator_title": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ иĐŊдиĐēĐ°Ņ‚ĐžŅ€ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐŊа ĐŋĐģĐ¸Ņ‚ĐēĐ°Ņ… ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛", "theme_setting_asset_list_tiles_per_row_title": "КоĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ в ŅŅ‚Ņ€ĐžĐēĐĩ ({count})", "theme_setting_colorful_interface_subtitle": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ ĐžŅ‚Ņ‚ĐĩĐŊĐžĐē Đē Ņ„ĐžĐŊ҃.", "theme_setting_colorful_interface_title": "ĐĻвĐĩŅ‚ Ņ„ĐžĐŊа", @@ -1832,13 +1991,16 @@ "to_change_password": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ", "to_favorite": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ в Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐĩ", "to_login": "Đ’Ņ…ĐžĐ´", + "to_multi_select": "Đ˛Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ ĐŊĐĩҁĐēĐžĐģҌĐēĐž", "to_parent": "ВĐĩŅ€ĐŊŅƒŅ‚ŅŒŅŅ ĐŊаСад", + "to_select": "Đ˛Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ", "to_trash": "ĐšĐžŅ€ĐˇĐ¸ĐŊа", "toggle_settings": "ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē", "total": "Đ’ŅĐĩĐŗĐž", "total_usage": "ĐžĐąŅ‰Đ°Ņ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đēа", "trash": "ĐšĐžŅ€ĐˇĐ¸ĐŊа", - "trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ‘", + "trash_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊŅ‹ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃ ({count} ŅˆŅ‚.)", + "trash_all": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "trash_count": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ {count, number}", "trash_delete_asset": "ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚Đ¸Ņ‚ŅŒ в ĐēĐžŅ€ĐˇĐ¸ĐŊ҃", "trash_emptied": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ĐžŅ‡Đ¸Ņ‰ĐĩĐŊа", @@ -1850,14 +2012,17 @@ "trash_page_restore_all": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ˛ŅĐĩ", "trash_page_select_assets_btn": "Đ’Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ", "trash_page_title": "ĐšĐžŅ€ĐˇĐ¸ĐŊа ({count})", - "trashed_items_will_be_permanently_deleted_after": "ĐžĐąŅŠĐĩĐē҂ҋ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} many {# Đ´ĐŊĐĩĐš} other {# Đ´ĐŊŅ}}.", + "trashed_items_will_be_permanently_deleted_after": "ĐžĐąŅŠĐĩĐē҂ҋ, Ņ…Ņ€Đ°ĐŊŅŅ‰Đ¸ĐĩŅŅ в ĐēĐžŅ€ĐˇĐ¸ĐŊĐĩ йОĐģĐĩĐĩ {days, plural, one {# Đ´ĐŊŅ} other {# Đ´ĐŊĐĩĐš}}, ŅƒĐ´Đ°ĐģŅŅŽŅ‚ŅŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи.", + "troubleshoot": "Đ”Đ¸Đ°ĐŗĐŊĐžŅŅ‚Đ¸Đēа", "type": "ĐĸиĐŋ", "unable_to_change_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ иСĐŧĐĩĐŊĐĩĐŊии PIN-ĐēОда", "unable_to_setup_pin_code": "ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ŅĐžĐˇĐ´Đ°ĐŊии PIN-ĐēОда", "unarchive": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ", + "unarchive_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Đ°Ņ€Ņ…Đ¸Đ˛Đ° ({count} ŅˆŅ‚.)", "unarchived_count": "{count, plural, one {# ĐžĐąŅŠĐĩĐēŅ‚ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰Ņ‘ĐŊ} many {# ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž} other {# ĐžĐąŅŠĐĩĐēŅ‚Đ° Đ˛ĐžĐˇĐ˛Ņ€Đ°Ņ‰ĐĩĐŊĐž}} иС Đ°Ņ€Ņ…Đ¸Đ˛Đ°", "undo": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ", "unfavorite": "ĐŖĐ´Đ°ĐģĐ¸Ņ‚ŅŒ иС Đ¸ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž", + "unfavorite_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ŅƒĐ´Đ°ĐģĐĩĐŊŅ‹ иС Đ˜ĐˇĐąŅ€Đ°ĐŊĐŊĐžĐŗĐž ({count} ŅˆŅ‚.)", "unhide_person": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҇ĐĩĐģОвĐĩĐēа", "unknown": "НĐĩиСвĐĩҁ҂ĐŊĐž", "unknown_country": "НĐĩиСвĐĩҁ҂ĐŊĐ°Ņ ŅŅ‚Ņ€Đ°ĐŊа", @@ -1872,18 +2037,24 @@ "unnamed_share": "ĐžĐąŅ‰Đ¸Đš Đ´ĐžŅŅ‚ŅƒĐŋ ĐąĐĩС ĐŊаСваĐŊĐ¸Ņ", "unsaved_change": "НĐĩŅĐžŅ…Ņ€Đ°ĐŊŅ‘ĐŊĐŊĐžĐĩ иСĐŧĐĩĐŊĐĩĐŊиĐĩ", "unselect_all": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ", - "unselect_all_duplicates": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąĐžŅ€ Đ˛ŅĐĩŅ… Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛", + "unselect_all_duplicates": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Đ˛ŅĐĩ Đ´ĐģŅ ŅƒĐ´Đ°ĐģĐĩĐŊĐ¸Ņ", "unselect_all_in": "ĐžŅ‚ĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Đ˛Ņ‹Đ´ĐĩĐģĐĩĐŊиĐĩ в {group}", "unstack": "Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "unstack_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ Ņ€Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊŅ‹ ({count} ŅˆŅ‚.)", "unstacked_assets_count": "{count, plural, one {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊ # ĐžĐąŅŠĐĩĐēŅ‚} many {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛} other {Đ Đ°ĐˇĐŗŅ€ŅƒĐŋĐŋĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž # ĐžĐąŅŠĐĩĐēŅ‚Đ°}}", + "untagged": "БĐĩС Ņ‚ĐĩĐŗĐžĐ˛", "up_next": "ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐĩ", + "update_location_action_prompt": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đĩ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Ņ‹ ҃ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊҋ҅ ĐžĐąŅŠĐĩĐēŅ‚ĐžĐ˛ ({count} ŅˆŅ‚.):", "updated_at": "ОбĐŊОвĐģŅ‘ĐŊ", "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ иСĐŧĐĩĐŊŅ‘ĐŊ", "upload": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ", + "upload_action_prompt": "ĐžĐąŅŠĐĩĐē҂ҋ ĐžĐļĐ¸Đ´Đ°ŅŽŅ‚ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи ({count} ŅˆŅ‚.)", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐģĐĩĐģҌĐŊĐžŅŅ‚ŅŒ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", + "upload_details": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēи", "upload_dialog_info": "ĐĨĐžŅ‚Đ¸Ņ‚Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ Đ˛Ņ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€?", "upload_dialog_title": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐžĐąŅŠĐĩĐēŅ‚", "upload_errors": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа СавĐĩŅ€ŅˆĐĩĐŊа ҁ {count, plural, one {# ĐžŅˆĐ¸ĐąĐēОК} other {# ĐžŅˆĐ¸ĐąĐēаĐŧи}}, ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐˇĐ°ĐŗŅ€ŅƒĐļĐĩĐŊĐŊŅ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ.", + "upload_finished": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа СавĐĩŅ€ŅˆĐĩĐŊа", "upload_progress": "ĐžŅŅ‚Đ°ĐģĐžŅŅŒ {remaining, number} - ĐžĐąŅ€Đ°ĐąĐžŅ‚Đ°ĐŊĐž {processed, number}/{total, number}", "upload_skipped_duplicates": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊ{count, plural, one { # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚} many {Đž # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚ĐžĐ˛} other {Đž # Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ°}}", "upload_status_duplicates": "Đ”ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹", @@ -1892,6 +2063,7 @@ "upload_success": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа ĐŋŅ€ĐžŅˆĐģа ҃ҁĐŋĐĩ҈ĐŊĐž. ОбĐŊĐžĐ˛Đ¸Ņ‚Đĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ, Ņ‡Ņ‚ĐžĐąŅ‹ ŅƒĐ˛Đ¸Đ´ĐĩŅ‚ŅŒ ĐŊĐžĐ˛Ņ‹Đĩ ĐžĐąŅŠĐĩĐē҂ҋ.", "upload_to_immich": "Đ—Đ°ĐŗŅ€ŅƒĐˇĐēа в Immich ({count})", "uploading": "Đ—Đ°ĐŗŅ€ŅƒĐļаĐĩŅ‚ŅŅ", + "uploading_media": "Đ’Ņ‹ĐŋĐžĐģĐŊŅĐĩŅ‚ŅŅ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēа", "url": "URL", "usage": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊиĐĩ", "use_biometric": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ йиОĐŧĐĩŅ‚Ņ€Đ¸ŅŽ", @@ -1900,7 +2072,7 @@ "user": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ", "user_has_been_deleted": "Đ­Ņ‚ĐžŅ‚ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ ĐąŅ‹Đģ ŅƒĐ´Đ°ĐģŅ‘ĐŊ.", "user_id": "ID ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", - "user_liked": "{user} ĐžŅ‚ĐŧĐĩŅ‚Đ¸Đģ(а) {type, select, photo {ŅŅ‚Đž Ņ„ĐžŅ‚Đž} video {ŅŅ‚Đž видĐĩĐž} asset {ŅŅ‚ĐžŅ‚ Ņ€ĐĩŅŅƒŅ€Ņ} other {ŅŅ‚ĐžŅ‚ аĐģŅŒĐąĐžĐŧ}}", + "user_liked": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅŽ {user} ĐŊŅ€Đ°Đ˛Đ¸Ņ‚ŅŅ {type, select, photo {ŅŅ‚Đž Ņ„ĐžŅ‚Đž} video {ŅŅ‚Đž видĐĩĐž} asset {ŅŅ‚ĐžŅ‚ ĐžĐąŅŠĐĩĐēŅ‚} other {ŅŅ‚ĐžŅ‚ аĐģŅŒĐąĐžĐŧ}}", "user_pin_code_settings": "PIN-ĐēОд", "user_pin_code_settings_description": "ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēа PIN-ĐēОда Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē ĐģĐ¸Ņ‡ĐŊОК ĐŋаĐŋĐēĐĩ", "user_privacy": "КоĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊĐžŅŅ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đē҃ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ аĐēĐēĐ°ŅƒĐŊŅ‚Đ°", "username": "ИĐŧŅ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "users": "ПоĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи", + "users_added_to_album_count": "{count, plural, one {# ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌ дОйавĐģĐĩĐŊ} many {# ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš дОйавĐģĐĩĐŊĐž} other {# ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ дОйавĐģĐĩĐŊĐž}} Đē аĐģŅŒĐąĐžĐŧ҃", "utilities": "ĐŖŅ‚Đ¸ĐģĐ¸Ņ‚Ņ‹", "validate": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ŅŒ", "validate_endpoint_error": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐēĐžŅ€Ņ€ĐĩĐēŅ‚ĐŊŅ‹Đš URL", @@ -1927,17 +2100,19 @@ "videos": "ВидĐĩĐž", "videos_count": "{count, plural, one {# видĐĩĐž} other {# видĐĩĐž}}", "view": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€", - "view_album": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ аĐģŅŒĐąĐžĐŧ", + "view_album": "ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ", "view_all": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ Đ˛ŅŅ‘", "view_all_users": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ Đ˛ŅĐĩŅ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš", + "view_details": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ĐŋĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸", "view_in_timeline": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩĐŊĐŊОК ҈ĐēаĐģĐĩ", "view_link": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁҁҋĐģĐē҃", "view_links": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁҁҋĐģĐēи", - "view_name": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ", + "view_name": "Вид", "view_next_asset": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Đš ĐžĐąŅŠĐĩĐēŅ‚", "view_previous_asset": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŋŅ€ĐĩĐ´Ņ‹Đ´ŅƒŅ‰Đ¸Đš ĐžĐąŅŠĐĩĐēŅ‚", "view_qr_code": "ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ QR ĐēОд", - "view_stack": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ҁ҂ĐĩĐē", + "view_similar_photos": "ĐĐ°ĐšŅ‚Đ¸ ĐŋĐžŅ…ĐžĐļиĐĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Đ¸", + "view_stack": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐŗŅ€ŅƒĐŋĐŋ҃", "view_user": "ĐŸŅ€ĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ", "viewer_remove_from_stack": "ĐŖĐąŅ€Đ°Ņ‚ŅŒ иС ĐŗŅ€ŅƒĐŋĐŋŅ‹", "viewer_stack_use_as_main_asset": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ в ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ ĐžŅĐŊОвĐŊĐžĐŗĐž ĐžĐąŅŠĐĩĐēŅ‚Đ°", @@ -1955,5 +2130,6 @@ "yes": "Да", "you_dont_have_any_shared_links": "ĐŖ Đ˛Đ°Ņ ĐŊĐĩŅ‚ ĐŋŅƒĐąĐģĐ¸Ņ‡ĐŊҋ҅ ҁҁҋĐģĐžĐē", "your_wifi_name": "ИĐŧŅ Đ˛Đ°ŅˆĐĩĐš Wi-Fi ҁĐĩŅ‚Đ¸", - "zoom_image": "ĐŸŅ€Đ¸ĐąĐģĐ¸ĐˇĐ¸Ņ‚ŅŒ" + "zoom_image": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐ°ŅŅˆŅ‚Đ°Đą", + "zoom_to_bounds": "ĐŖĐ˛ĐĩĐģĐ¸Ņ‡Đ¸Ņ‚ŅŒ Đ´Đž ĐŗŅ€Đ°ĐŊĐ¸Ņ†" } diff --git a/i18n/sk.json b/i18n/sk.json index cd64c52532..de8f12dfa5 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,8 @@ "add_a_location": "PridaÅĨ polohu", "add_a_name": "PridaÅĨ meno", "add_a_title": "PridaÅĨ nÃĄzov", + "add_birthday": "PridaÅĨ narodeniny", + "add_endpoint": "PridaÅĨ koncovÃŊ bod", "add_exclusion_pattern": "PridaÅĨ vzor vylÃēčenia", "add_import_path": "PridaÅĨ cestu pre import", "add_location": "PridaÅĨ polohu", @@ -25,7 +27,11 @@ "add_to": "PridaÅĨ doâ€Ļ", "add_to_album": "PridaÅĨ do albumu", "add_to_album_bottom_sheet_added": "PridanÊ do {album}", - "add_to_album_bottom_sheet_already_exists": "UÅž v {album}", + "add_to_album_bottom_sheet_already_exists": "UÅž je v {album}", + "add_to_album_bottom_sheet_some_local_assets": "NiektorÊ lokÃĄlne sÃēbory nebolo moÅžnÊ pridaÅĨ do albumu", + "add_to_album_toggle": "PrepnÃēÅĨ vÃŊber pre {album}", + "add_to_albums": "PridaÅĨ do albumov", + "add_to_albums_count": "PridaÅĨ do albumov ({count})", "add_to_shared_album": "PridaÅĨ do zdieÄžanÊho albumu", "add_url": "PridaÅĨ URL", "added_to_archive": "PridanÊ do archívu", @@ -33,6 +39,7 @@ "added_to_favorites_count": "PridanÊ {count, number} do obÄžÃēbenÃŊch", "admin": { "add_exclusion_pattern_description": "PridÃĄvanie vzorov na vylÃēčenie. Globovanie pomocou *, ** a ? je podporovanÊ. Ak chcete ignorovaÅĨ vÅĄetky sÃēbory v akomkoÄžvek adresÃĄri s nÃĄzvom \"Raw\", pouÅžite \"**/Raw/**\". Ak chcete ignorovaÅĨ vÅĄetky sÃēbory končiace na \".tif\", pouÅžite \"**/*.tif\". Ak chcete ignorovaÅĨ absolÃētnu cestu, pouÅžite príkaz \"/cesta/k/ignorovanym/**\".", + "admin_user": "SprÃĄvca", "asset_offline_description": "TÃĄto poloÅžka externej kniÅžnice sa uÅž na disku nenachÃĄdza a bola presunutÃĄ do koÅĄa. PokiaÄž bol sÃēbor presunutÃŊ v rÃĄmci kniÅžnice, skontrolujte časovÃē os a vyhÄžadajte novÊ odpovedajÃēce poloÅžky. Ak chcete tÃēto poloÅžku obnoviÅĨ, uistite sa, Åže je cesta k niÅžÅĄie uvedenÊmu sÃēboru prístupnÃĄ pre aplikÃĄciu Immich a prehÄžadajte kniÅžnicu.", "authentication_settings": "Overovanie a prihlÃĄsenie", "authentication_settings_description": "SpravovaÅĨ heslo, protokol OAuth a ďalÅĄie nastavenia overenia", @@ -42,27 +49,34 @@ "backup_database": "VytvoriÅĨ vÃŊpis databÃĄzy", "backup_database_enable_description": "PovoliÅĨ vÃŊpisy z databÃĄzy", "backup_keep_last_amount": "MnoÅžstvo predchÃĄdzajÃēcich vÃŊpisov, ktorÊ sa majÃē zachovaÅĨ", + "backup_onboarding_1_description": "externÃē kÃŗpiu v cloude alebo na inom fyzickom mieste.", + "backup_onboarding_2_description": "lokÃĄlne kÃŗpie na rôznych zariadeniach. To zahŕňa hlavnÊ sÃēbory a ich lokÃĄlnu zÃĄlohu.", + "backup_onboarding_3_description": "kompletnÊ kÃŗpie vaÅĄich Ãēdajov vrÃĄtane pôvodnÃŊch sÃēborov. Toto zahŕňa 1 externÃē kÃŗpiu a 2 lokÃĄlne kÃŗpie.", + "backup_onboarding_description": "Na ochranu vaÅĄich Ãēdajov sa odporÃēča stratÊgia zÃĄlohovania 3-2-1. Pre komplexnÊ rieÅĄenie zÃĄlohovania by ste mali uchovÃĄvaÅĨ kÃŗpie nahratÃŊch fotografií/videí, ako aj databÃĄzy Immich.", + "backup_onboarding_footer": "ĎalÅĄie informÃĄcie o vytvÃĄraní zÃĄlohy Immich nÃĄjdete v dokumentÃĄcii.", + "backup_onboarding_parts_title": "ZÃĄlohovanie 3-2-1 zahŕňa:", + "backup_onboarding_title": "ZÃĄlohy", "backup_settings": "Nastavenia vÃŊpisu databÃĄzy", - "backup_settings_description": "SprÃĄva nastavení vÃŊpisu databÃĄzy.", - "cleared_jobs": "HotovÊ Ãēlohy pre: {job}", + "backup_settings_description": "SpravovaÅĨ nastavenia vÃŊpisu databÃĄzy.", + "cleared_jobs": "VyčistenÊ Ãēlohy pre: {job}", "config_set_by_file": "KonfigurÃĄcia je v sÃēčasnosti nastavenÃĄ konfiguračnÃŊm sÃēborom", "confirm_delete_library": "Naozaj chcete vymazaÅĨ kniÅžnicu {library}?", - "confirm_delete_library_assets": "Ste si istí, Åže chcete vymazaÅĨ tÃēto kniÅžnicu? Tato operÃĄcia nenÃĄvratne odstrÃĄni {count, plural, one {# contained asset} other {all # contained assets}} sÃēborov z Immich. SÃēbory budÃē ponechanÊ na disku.", + "confirm_delete_library_assets": "Ste si istí, Åže chcete vymazaÅĨ tÃēto kniÅžnicu? Tato operÃĄcia nenÃĄvratne odstrÃĄni {count, plural, one {# zahrnutÃē poloÅžku} few {# zahrnutÊ poloÅžky} other {vÅĄetkÃŊch # zahrnutÃŊch poloÅžiek}} z aplikÃĄcie Immich. SÃēbory budÃē ponechanÊ na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" niÅžÅĄie", "confirm_reprocess_all_faces": "Naozaj chcete spracovaÅĨ vÅĄetky tvÃĄre znova? Tento proces vymaÅže pomenovanÃŊch Äžudí.", - "confirm_user_password_reset": "Naozaj chcete resetovaÅĨ heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete obnoviÅĨ heslo pre {user}?", "confirm_user_pin_code_reset": "Ste si istí, Åže chcete opätovne nastaviÅĨ PIN kÃŗd pouŞívateÄža {user}?", "create_job": "VytvoriÅĨ Ãēlohu", "cron_expression": "VÃŊraz cron", "cron_expression_description": "Nastavte interval skenovania pomocou formÃĄtu cron. Pre viac informÃĄcií navÅĄtívte Crontab Guru", - "cron_expression_presets": "Presety cron vÃŊrazov", + "cron_expression_presets": "PredvoÄžby vÃŊrazov Cron", "disable_login": "ZakÃĄzaÅĨ prihlÃĄsenie", - "duplicate_detection_job_description": "SpustiÅĨ strojovÊ učenie na poloÅžkÃĄch pre detekciu podobnÃŊch obrÃĄzkov. Spolieha sa na inteligentnÊ vyhÄžadÃĄvanie", + "duplicate_detection_job_description": "Spustite strojovÊ učenie na poloÅžkÃĄch pre detekciu podobnÃŊch obrÃĄzkov. Spolieha sa na inteligentnÊ vyhÄžadÃĄvanie", "exclusion_pattern_description": "Vylučovacie vzory VÃĄm umoŞňujÃē ignorovaÅĨ sÃēbory a priečinky pri skenovaní VaÅĄej kniÅžnice. Toto je uÅžitočnÊ, ak mÃĄte priečinky obsahujÃēce sÃēbory, ktorÊ nechcete importovaÅĨ, napríklad RAW sÃēbory.", - "external_library_management": "SprÃĄva Externej KniÅžnice", + "external_library_management": "Spravovanie externej kniÅžnice", "face_detection": "Detekcia tvÃĄrí", - "face_detection_description": "Rozpoznajte tvÃĄre v poloÅžkÃĄch pomocou strojovÊho učenia. V prípade videí sa berie do Ãēvahy len nÃĄhÄžad. „ObnoviÅĨ“ (znovu) spracuje vÅĄetky poloÅžky. „ObnoviÅĨ“ dodatočne vymaÅže vÅĄetky aktuÃĄlne Ãēdaje o tvÃĄrach. „ChÃŊbajÃēce“ zaradí do poradia poloÅžky aktív, ktorÊ eÅĄte neboli spracovanÊ. ZistenÊ tvÃĄre sa po dokončení rozpoznÃĄvania tvÃĄrí zaradia do poradia na rozpoznÃĄvanie tvÃĄrí, pričom sa zoskupia do existujÃēcich alebo novÃŊch osôb.", - "facial_recognition_job_description": "ZoskupovaÅĨ rozpoznanÊ tvÃĄre do osôb. Tento krok sa vykonÃĄ po dokončení rozpoznÃĄvania tvÃĄrí. „ObnoviÅĨ“ (znovu) zoskupí vÅĄetky tvÃĄre. „ChÃŊbajÃēce“ zaradí tvÃĄre, ktorÊ nemajÃē pridelenÃē osobu.", + "face_detection_description": "Rozpoznajte tvÃĄre v poloÅžkÃĄch pomocou strojovÊho učenia. V prípade videí sa berie do Ãēvahy len nÃĄhÄžad. „AktualizovaÅĨ“ (znovu) spracuje vÅĄetky poloÅžky. „ObnoviÅĨ“ dodatočne vymaÅže vÅĄetky aktuÃĄlne Ãēdaje o tvÃĄrach. „ChÃŊbajÃēce“ zaradí do poradia mÊdiÃĄ, ktorÊ eÅĄte neboli spracovanÊ. ZistenÊ tvÃĄre sa po dokončení rozpoznÃĄvania tvÃĄrí zaradia do poradia na rozpoznÃĄvanie tvÃĄrí, pričom sa zoskupia do existujÃēcich alebo novÃŊch osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznanÊ tvÃĄre do osôb. Tento krok sa vykonÃĄ po dokončení rozpoznÃĄvania tvÃĄrí. „ObnoviÅĨ“ (znovu) zoskupí vÅĄetky tvÃĄre. „ChÃŊbajÃēce“ zaradí tvÃĄre, ktorÊ nemajÃē pridelenÃē osobu.", "failed_job_command": "Príkaz {command} zlyhal pre Ãēlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamÅžite odstrÃĄni pouŞívateÄža a vÅĄetky poloÅžky. Tento krok nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ a sÃēbory nebude moÅžnÊ obnoviÅĨ.", "image_format": "FormÃĄt", @@ -84,7 +98,7 @@ "image_resolution_description": "VyÅĄÅĄie rozlÃ­ÅĄenie môŞe zachovaÅĨ viac detailov, ale kÃŗdovanie trvÃĄ dlhÅĄie, sÃēbory sÃē vÃ¤ÄÅĄie a môŞe to zníŞiÅĨ rÃŊchlosÅĨ odozvy aplikÃĄcie.", "image_settings": "ObrÃĄzky", "image_settings_description": "SpravovaÅĨ kvalitu a rozlÃ­ÅĄenie generovanÃŊch obrÃĄzkov", - "image_thumbnail_description": "MalÃĄ miniatÃēra s odstrÃĄnenÃŊmi metadÃĄtami, pouŞívanÊ pri zobrazovaní skupín fotiek ako na hlavnej časovej osi", + "image_thumbnail_description": "MalÃĄ miniatÃēra s odstrÃĄnenÃŊmi metadÃĄtami, ktorÃĄ sa pouŞíva pri prezeraní skupín fotografií ako na hlavnej časovej osi", "image_thumbnail_quality_description": "Kvalita miniatÃēry v stupnici od 1 do 100. VyÅĄÅĄia hodnota znamenÃĄ lepÅĄiu kvalitu, ale produkuje vÃ¤ÄÅĄie sÃēbory a môŞe zníŞiÅĨ odozvu aplikÃĄcie.", "image_thumbnail_title": "MiniatÃēry", "job_concurrency": "SÃēbeÅžnosÅĨ Ãēlohy - {job}", @@ -92,7 +106,7 @@ "job_not_concurrency_safe": "TÃĄto Ãēloha nie je bezpečnÃĄ pre sÃēbeÅžnÊ spracovanie.", "job_settings": "Úlohy", "job_settings_description": "SpravovaÅĨ sÃēbeÅžnosÅĨ Ãēloh", - "job_status": "Stav Úloh", + "job_status": "Stav Ãēloh", "jobs_delayed": "{jobCount, plural, one {# oneskorenÃŊ} few {# oneskorenÊ} other {# oneskorenÃŊch}}", "jobs_failed": "{jobCount, plural, one {# neÃēspeÅĄnÃŊ} few {# neÃēspeÅĄnÊ} other {# neÃēspeÅĄnÃŊch}}", "library_created": "VytvorenÃĄ kniÅžnica: {library}", @@ -103,18 +117,25 @@ "library_scanning_enable_description": "ZapnÃēÅĨ pravidelnÊ skenovanie kniÅžnice", "library_settings": "ExternÃĄ kniÅžnica", "library_settings_description": "SpravovaÅĨ nastavenia externej kniÅžnice", - "library_tasks_description": "VyhÄžadÃĄvanie novÃŊch alebo zmenenÃŊch poloÅžiek v externÃŊch kniÅžniciach", + "library_tasks_description": "VyhÄžadajte novÊ alebo zmenenÊ mÊdiÃĄ v externÃŊch kniÅžniciach", "library_watching_enable_description": "SledovaÅĨ externÊ kniÅžnice pre zmeny v sÃēboroch", "library_watching_settings": "Sledovanie kniÅžnice (EXPERIMENTÁLNE)", "library_watching_settings_description": "Automaticky sledovaÅĨ zmenenÊ sÃēbory", - "logging_enable_description": "PovoliÅĨ logovanie", - "logging_level_description": "Ak je povolenÊ, akÃē Ãēroveň logovania pouÅžiÅĨ.", - "logging_settings": "Logovanie", + "logging_enable_description": "PovoliÅĨ ukladanie zÃĄznamov", + "logging_level_description": "Ak je povolenÊ, akÃē Ãēroveň zÃĄznamov pouÅžiÅĨ.", + "logging_settings": "Ukladanie zÃĄznamov", + "machine_learning_availability_checks": "Kontroly dostupnosti", + "machine_learning_availability_checks_description": "Automaticky zistiÅĨ a uprednostniÅĨ dostupnÊ servery strojovÊho učenia", + "machine_learning_availability_checks_enabled": "PovoliÅĨ kontroly dostupnosti", + "machine_learning_availability_checks_interval": "Interval kontroly", + "machine_learning_availability_checks_interval_description": "Interval v milisekundÃĄch medzi kontrolami dostupnosti", + "machine_learning_availability_checks_timeout": "ČasovÃŊ limit poÅžiadavky", + "machine_learning_availability_checks_timeout_description": "ČasovÃŊ limit v milisekundÃĄch pre kontroly dostupnosti", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "NÃĄzov modelu CLIP je uvedenÃŊ tu. Pamätajte, Åže pri zmene modelu je nutnÊ znovu spustiÅĨ Ãēlohu 'InteligentnÊ vyhÄžadÃĄvanie' pre vÅĄetky obrÃĄzky.", "machine_learning_duplicate_detection": "Detekcia duplikÃĄtov", "machine_learning_duplicate_detection_enabled": "PovoliÅĨ detekciu duplikÃĄtov", - "machine_learning_duplicate_detection_enabled_description": "Ak je vypnutÊ, presne identickÊ poloÅžky budÃē stÃĄle deduplikovanÊ.", + "machine_learning_duplicate_detection_enabled_description": "Ak je vypnutÊ, Ãēplne identickÊ poloÅžky budÃē stÃĄle deduplikovanÊ.", "machine_learning_duplicate_detection_setting_description": "PouÅžiÅĨ CLIP embeddings na identifikÃĄciu pravdepodobnÃŊch duplikÃĄtov", "machine_learning_enabled": "PovoliÅĨ strojovÊ učenie", "machine_learning_enabled_description": "Ak je vypnutÊ, vÅĄetky funkcie strojovÊho učenia (ML) budÃē vypnutÊ, bez ohÄžadu na nastavenia niÅžÅĄie.", @@ -124,10 +145,10 @@ "machine_learning_facial_recognition_model_description": "Modely sÃē zoradenÊ od najvÃ¤ÄÅĄieho po najmenÅĄÃ­. VÃ¤ÄÅĄie modely sÃē pomalÅĄie a vyÅžadujÃē viac pamäte, ale poskytujÃē lepÅĄie vÃŊsledky. Pamätajte, Åže po zmene modelu je potrebnÊ znovu spustiÅĨ Ãēlohu detekcie tvÃĄrí pre vÅĄetky obrÃĄzky.", "machine_learning_facial_recognition_setting": "PovoliÅĨ rozpoznÃĄvanie tvÃĄrí", "machine_learning_facial_recognition_setting_description": "Ak je vypnutÊ, obrÃĄzky nebudÃē spracovanÊ pre rozpoznÃĄvanie tvÃĄrí a nebudÃē sa zobrazovaÅĨ v sekcii ÄŊudia na strÃĄnke PreskÃēmaÅĨ.", - "machine_learning_max_detection_distance": "MaximÃĄlna detekčnÃĄ odchylka", - "machine_learning_max_detection_distance_description": "MaximÃĄlna odchylka medzi dvoma obrÃĄzkami, aby boli povaÅžovanÊ za duplikÃĄty, v rozsahu od 0.001 do 0.1. VyÅĄÅĄie hodnoty odhalia viac duplikÃĄtov, ale môŞu viesÅĨ k faloÅĄnÃŊm pozitívam.", - "machine_learning_max_recognition_distance": "MaximÃĄlna rozpoznÃĄvacia odchylka", - "machine_learning_max_recognition_distance_description": "MaximÃĄlna odchylka medzi dvoma tvÃĄrami, aby boli povaÅžovanÊ za rovnakÃē osobu, v rozsahu od 0 do 2. ZníŞenie tejto hodnoty môŞe zabrÃĄniÅĨ označeniu dvoch Äžudí za tÃē istÃē osobu, zatiaÄž čo zvÃŊÅĄenie môŞe zabrÃĄniÅĨ označeniu jednej osoby za dve rôzne osoby. Pamätajte, Åže je jednoduchÅĄie spojiÅĨ dvoch Äžudí ako rozdeliÅĨ jednu osobu na dve, takÅže je lepÅĄie voliÅĨ niÅžÅĄÃ­ prah, ak je to moÅžnÊ.", + "machine_learning_max_detection_distance": "MaximÃĄlna detekčnÃĄ odchÃŊlka", + "machine_learning_max_detection_distance_description": "MaximÃĄlna odchÃŊlka medzi dvoma obrÃĄzkami, aby boli povaÅžovanÊ za duplikÃĄty, v rozsahu od 0.001 do 0.1. VyÅĄÅĄie hodnoty odhalia viac duplikÃĄtov, ale môŞu viesÅĨ k faloÅĄnÃŊm pozitívam.", + "machine_learning_max_recognition_distance": "MaximÃĄlna rozpoznÃĄvacia odchÃŊlka", + "machine_learning_max_recognition_distance_description": "MaximÃĄlna odchÃŊlka medzi dvomi tvÃĄrami, aby boli povaÅžovanÊ za rovnakÃē osobu, v rozsahu od 0 do 2. ZníŞenie tejto hodnoty môŞe zabrÃĄniÅĨ označeniu dvoch Äžudí za tÃē istÃē osobu, zatiaÄž čo zvÃŊÅĄenie môŞe zabrÃĄniÅĨ označeniu jednej osoby za dve rôzne osoby. Pamätajte, Åže je jednoduchÅĄie spojiÅĨ dvoch Äžudí ako rozdeliÅĨ jednu osobu na dve, takÅže je lepÅĄie voliÅĨ niÅžÅĄÃ­ prah, ak je to moÅžnÊ.", "machine_learning_min_detection_score": "MinimÃĄlne detekčnÊ skÃŗre", "machine_learning_min_detection_score_description": "MinimÃĄlne skÃŗre dôveryhodnosti pre detekciu tvÃĄre v rozsahu od 0 do 1. NiÅžÅĄie hodnoty odhalia viac tvÃĄrí, ale môŞu viesÅĨ k faloÅĄnÃŊm pozitivním vÃŊsledkom.", "machine_learning_min_recognized_faces": "Minimum rozpoznanÃŊch tvÃĄrí", @@ -139,15 +160,15 @@ "machine_learning_smart_search_enabled": "PovoliÅĨ inteligentnÊ vyhÄžadÃĄvanie", "machine_learning_smart_search_enabled_description": "Ak je vypnutÊ, obrÃĄzky nebudÃē spracovanÊ pre inteligentnÊ vyhÄžadÃĄvanie.", "machine_learning_url_description": "URL adresa servera strojovÊho učenia. Ak je zadanÃŊch viacero adries URL, kaÅždÃŊ server bude testovanÃŊ postupne, kÃŊm jeden z nich neodpovie ÃēspeÅĄne, v poradí od prvÊho po poslednÃŊ. Servery, ktorÊ neodpovedajÃē, budÃē dočasne ignorovanÊ, kÃŊm nebudÃē opäÅĨ online.", - "manage_concurrency": "SprÃĄva sÃēbeÅžnosti", - "manage_log_settings": "SpravovaÅĨ nastavenia logovania", + "manage_concurrency": "SpravovaÅĨ sÃēbeÅžnosÅĨ", + "manage_log_settings": "SpravovaÅĨ nastavenia ukladania zÃĄznamov", "map_dark_style": "TmavÃŊ ÅĄtÃŊl", "map_enable_description": "PovoliÅĨ funkcie mapy", - "map_gps_settings": "Mapa & GPS", - "map_gps_settings_description": "SprÃĄva nastavení mÃĄp a GPS reverznÊho geokÃŗdovania", + "map_gps_settings": "Mapa a nastavenia GPS", + "map_gps_settings_description": "SpravovaÅĨ nastavenia mapy a GPS (reverznÊ geokÃŗdovanie)", "map_implications": "TÃĄto funkčnosÅĨ sa spolieha na externÃŊ servis spracovania mapovÃŊch dlaÅždíc (tiles.immich.cloud)", "map_light_style": "SvetlÃŊ ÅĄtÃŊl", - "map_manage_reverse_geocoding_settings": "SprÃĄva nastavení ReverznÊho geokÃŗdovania", + "map_manage_reverse_geocoding_settings": "SpravovaÅĨ nastavenia reverznÊho geokÃŗdovania", "map_reverse_geocoding": "ReverznÊ GeokÃŗdovanie", "map_reverse_geocoding_enable_description": "PovoliÅĨ reverznÊ geokÃŗdovanie", "map_reverse_geocoding_settings": "ReverznÊ geokÃŗdovanie", @@ -157,16 +178,30 @@ "memory_cleanup_job": "VymazÃĄvanie spomienok", "memory_generate_job": "VytvÃĄranie spomienok", "metadata_extraction_job": "ExtrahovaÅĨ metadÃĄta", - "metadata_extraction_job_description": "Vytiahne metadÃĄta z kaÅždej poloÅžky, ako napríklad GPS, tvÃĄre a rozlÃ­ÅĄenie", + "metadata_extraction_job_description": "Získajte informÃĄcie o metadÃĄtach z kaÅždÊho mÊdia, ako sÃē GPS, tvÃĄre a rozlÃ­ÅĄenie", "metadata_faces_import_setting": "PovoliÅĨ import tvÃĄre", - "metadata_faces_import_setting_description": "Importuj tvÃĄre z EXIF dÃĄt obrÃĄzkov a sidecar sÃēborov", + "metadata_faces_import_setting_description": "ImportovaÅĨ tvÃĄre z EXIF Ãēdajov obrÃĄzka a pridruÅženÃŊch sÃēborov", "metadata_settings": "MetadÃĄta", "metadata_settings_description": "SpravovaÅĨ nastavenia metadÃĄt", "migration_job": "MigrÃĄcia", - "migration_job_description": "MigrÃĄcia miniatÃēr poloÅžiek a tvÃĄrí na najnovÅĄiu ÅĄtruktÃēru priečinkov", + "migration_job_description": "PresunÃēÅĨ miniatÃēry pre mÊdiÃĄ a tvÃĄre do najnovÅĄej ÅĄtruktÃēry priečinkov", + "nightly_tasks_cluster_faces_setting_description": "SpustiÅĨ rozpoznÃĄvanie tvÃĄre na novo-zistenÃŊch tvÃĄrach", + "nightly_tasks_cluster_new_faces_setting": "ZoskupiÅĨ novÊ tvÃĄre", + "nightly_tasks_database_cleanup_setting": "Úlohy čistenia databÃĄzy", + "nightly_tasks_database_cleanup_setting_description": "VyčistiÅĨ databÃĄzu od starÃŊch, neplatnÃŊch Ãēdajov", + "nightly_tasks_generate_memories_setting": "VytvoriÅĨ spomienky", + "nightly_tasks_generate_memories_setting_description": "VytvoriÅĨ novÊ spomienky z poloÅžiek", + "nightly_tasks_missing_thumbnails_setting": "VytvoriÅĨ chÃŊbajÃēce nÃĄhÄžady", + "nightly_tasks_missing_thumbnails_setting_description": "ZaradiÅĨ poloÅžky bez nÃĄhÄžadov do poradia na vytvorenie nÃĄhÄžadov", + "nightly_tasks_settings": "Nastavenia nočnÃŊch Ãēloh", + "nightly_tasks_settings_description": "SpravovaÅĨ nočnÊ Ãēlohy", + "nightly_tasks_start_time_setting": "Čas spustenia", + "nightly_tasks_start_time_setting_description": "Čas, kedy server začne vykonÃĄvaÅĨ nočnÊ Ãēlohy", + "nightly_tasks_sync_quota_usage_setting": "SynchronizovaÅĨ vyuÅžitie kvÃŗty", + "nightly_tasks_sync_quota_usage_setting_description": "AktualizovaÅĨ kvÃŗtu ÃēloÅžiska pouŞívateÄža na zÃĄklade aktuÃĄlneho vyuÅžitia", "no_paths_added": "Neboli pridanÊ Åžiadne cesty", "no_pattern_added": "Nebol pridanÃŊ Åžiadny vzor", - "note_apply_storage_label_previous_assets": "PoznÃĄmka: Ak chcete pouÅžiÅĨ Å títkovanie ÃēloÅžiska na predtÃŊm nahranÊ aktíva, spustite príkaz", + "note_apply_storage_label_previous_assets": "PoznÃĄmka: Ak chcete pouÅžiÅĨ ÅĄtítok ÃēloÅžiska na predtÃŊm nahranÊ poloÅžky, spustite príkaz", "note_cannot_be_changed_later": "POZNÁMKA: Toto nie je moÅžnÊ neskôr zmeniÅĨ!", "notification_email_from_address": "Z adresy", "notification_email_from_address_description": "E-mailovÃĄ adresa odosielateÄža, napríklad: \"Immich Foto Server \". Uistite sa, Åže pouŞívate adresu, z ktorej mÃĄte povolenÊ odosielaÅĨ e-maily.", @@ -189,99 +224,105 @@ "oauth_auto_register": "AutomatickÃĄ regristrÃĄcia", "oauth_auto_register_description": "AutomatickÊ zaregistrovanie novÊho poŞívateÄža pri prihlÃĄsení pomocou OAuth", "oauth_button_text": "Text tlačítka", + "oauth_client_secret_description": "VyÅžaduje sa, ak poskytovateÄž OAuth nepodporuje PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "PrihlÃĄsiÅĨ sa pomocou OAuth", "oauth_mobile_redirect_uri": "URI mobilnÊho presmerovania", "oauth_mobile_redirect_uri_override": "Prepísanie URI mobilnÊho presmerovania", "oauth_mobile_redirect_uri_override_description": "PovoÄžte, keď poskytovateÄž protokolu OAuth nepovoÄžuje identifikÃĄtor URI pre mobilnÊ zariadenia, napríklad ''{callback}''", + "oauth_role_claim": "PoÅžiadavka na rolu", + "oauth_role_claim_description": "Automaticky udeliÅĨ prístup sprÃĄvcu na zÃĄklade prítomnosti tejto poÅžiadavky. PoÅžiadavka môŞe maÅĨ príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "SpravovaÅĨ nastavenia prihlÃĄsenia OAuth", - "oauth_settings_more_details": "Pre viac informÃĄcii o tejto funkcii, prejdite na docs.", - "oauth_storage_label_claim": "NÃĄrokovaÅĨ Å títok ÃēloÅžiska", - "oauth_storage_label_claim_description": "Automaticky nastaviÅĨ Å títok ÃēloÅžiska pouŞívateÄža na hodnotu tohto nÃĄroku.", - "oauth_storage_quota_claim": "DeklarÃĄcia kvÃŗty ÃēloÅžiska", - "oauth_storage_quota_claim_description": "Automaticky nastaviÅĨ kvÃŗtu ÃēloÅžiska pouŞívateÄža na hodnotu tejto deklarÃĄcie.", + "oauth_settings_more_details": "Pre viac informÃĄcii o tejto funkcii, prejdite na dokumentÃĄciu.", + "oauth_storage_label_claim": "PoÅžiadavka na ÅĄtítok ÃēloÅžiska", + "oauth_storage_label_claim_description": "Automaticky nastaviÅĨ ÅĄtítok ÃēloÅžiska pouŞívateÄža na hodnotu tejto poÅžiadavky.", + "oauth_storage_quota_claim": "PoÅžiadavka na kvÃŗtu ÃēloÅžiska", + "oauth_storage_quota_claim_description": "Automaticky nastaviÅĨ kvÃŗtu ÃēloÅžiska pouŞívateÄža na hodnotu tejto poÅžiadavky.", "oauth_storage_quota_default": "PredvolenÃŊ limit ÃēloÅžiska (GiB)", "oauth_storage_quota_default_description": "KvÃŗta v GiB, ktorÃĄ sa pouÅžije, ak nie je poskytnutÃĄ Åžiadna poÅžiadavka.", + "oauth_timeout": "ČasovÃŊ limit poÅžiadavky", + "oauth_timeout_description": "ČasovÃŊ limit pre poÅžiadavky v milisekundÃĄch", "password_enable_description": "PrihlÃĄsiÅĨ sa pomocou emailu a hesla", "password_settings": "PrihlÃĄsenie cez heslo", "password_settings_description": "SpravovaÅĨ nastavenia prihlÃĄsenia cez heslo", "paths_validated_successfully": "VÅĄetky cesty boli ÃēspeÅĄne overenÊ", - "person_cleanup_job": "Premazanie osôb", + "person_cleanup_job": "Prečistenie osôb", "quota_size_gib": "VeÄžkosÅĨ kvÃŗty (GiB)", "refreshing_all_libraries": "ObnovujÃē sa vÅĄetky kniÅžnice", "registration": "RegistrÃĄcia administrÃĄtora", "registration_description": "KeďŞe ste prvÃŊm pouŞívateÄžom v systÊme, budÃē vÃĄm pridelenÊ sprÃĄvcovskÊ prÃĄva na vykonÃĄvanie vÅĄetkÃŊch Ãēloh a vrÃĄtane tvorby novÃŊch pouŞívateÄžov.", "require_password_change_on_login": "VyÅžadovaÅĨ od pouŞívateÄža zmenu hesla pri prvom prihlÃĄsení", "reset_settings_to_default": "ObnoviÅĨ pôvodnÊ nastavenia", - "reset_settings_to_recent_saved": "ObnoviÅĨ naposledy uloÅženÊ nastavenia", + "reset_settings_to_recent_saved": "Nastavenia boli obnovenÊ na poslednÊ uloÅženÊ nastavenia", "scanning_library": "KniÅžnica sa skenuje", "search_jobs": "VyhÄžadaÅĨ Ãēlohyâ€Ļ", "send_welcome_email": "OdoslaÅĨ uvítací e-mail", "server_external_domain_settings": "ExternÃĄ domÊna", "server_external_domain_settings_description": "VerejnÃĄ domÊna pre zdieÄžanÊ odkazy, vrÃĄtane http(s)://", - "server_public_users": "Verejní uŞívatelia", - "server_public_users_description": "VÅĄetci uŞívatelia (meno a email) sÃē uvedení pri pridÃĄvaní uŞívateÄža do zdieÄžanÃŊch albumov. Ak je tÃĄto funkcia vypnutÃĄ, zoznam uŞívateÄžov bude dostupnÃŊ iba sprÃĄvcom.", + "server_public_users": "Verejní pouŞívatelia", + "server_public_users_description": "VÅĄetci pouŞívatelia (meno a email) sÃē uvedení pri pridÃĄvaní pouŞívateÄža do zdieÄžanÃŊch albumov. Ak je tÃĄto funkcia vypnutÃĄ, zoznam pouŞívateÄžov bude dostupnÃŊ iba sprÃĄvcom.", "server_settings": "Server", "server_settings_description": "SpravovaÅĨ nastavenia servera", "server_welcome_message": "Uvítacia sprÃĄva", "server_welcome_message_description": "SprÃĄva, ktorÃĄ sa zobrazí na prihlasovacej strÃĄnke.", - "sidecar_job": "Sidecar metadÃĄta", - "sidecar_job_description": "Objavte alebo synchronizujte metadÃĄta Sidecar zo sÃēborovÊho systÊmu", + "sidecar_job": "PridruÅženÊ metadÃĄta", + "sidecar_job_description": "Objavte alebo synchronizujte pridruÅženÊ metadÃĄta zo sÃēborovÊho systÊmu", "slideshow_duration_description": "Čas zobrazenia obrÃĄzku v sekundÃĄch", "smart_search_job_description": "Spustite strojovÊ učenie na mÊdiÃĄch na podporu inteligentnÊho vyhÄžadÃĄvania", "storage_template_date_time_description": "ČasovÃĄ pečiatka vytvorenia poloÅžky sa pouŞíva pre informÃĄcie o dÃĄtume a čase", - "storage_template_date_time_sample": "Čas vzorky {date}", + "storage_template_date_time_sample": "UkÃĄÅžkovÃŊ čas {date}", "storage_template_enable_description": "PovoliÅĨ nÃĄstroj ÅĄablÃŗny ÃēloÅžiska", "storage_template_hash_verification_enabled": "Overenie hash povolenÊ", "storage_template_hash_verification_enabled_description": "Povolí overenie hash, nezakazujte to, pokiaÄž si nie ste istí dôsledkami", "storage_template_migration": "MigrÃĄcia ÅĄablÃŗny ÃēloÅžiska", - "storage_template_migration_description": "PouÅžite aktuÃĄlnu {template} na predtÃŊm nahranÊ mÊdiÃĄ", - "storage_template_migration_info": "Å ablÃŗna ÃēloÅžiska skonvertuje vÅĄetky prípony na malÊ písmenÃĄ. Zmeny ÅĄablÃŗn sa budÃē vzÅĨahovaÅĨ iba na novÊ diela. Ak chcete ÅĄablÃŗnu spätne pouÅžiÅĨ na predtÃŊm nahranÊ mÊdiÃĄ, spustite {job}.", + "storage_template_migration_description": "PouÅžiÅĨ aktuÃĄlnu {template} na predtÃŊm nahranÊ mÊdiÃĄ", + "storage_template_migration_info": "Å ablÃŗna ÃēloÅžiska skonvertuje vÅĄetky prípony na malÊ písmenÃĄ. Zmeny ÅĄablÃŗn sa budÃē vzÅĨahovaÅĨ iba na novÊ poloÅžky. Ak chcete ÅĄablÃŗnu spätne pouÅžiÅĨ na predtÃŊm nahranÊ mÊdiÃĄ, spustite {job}.", "storage_template_migration_job": "Úloha migrÃĄcie ÅĄablÃŗny ÃēloÅžiska", - "storage_template_more_details": "ĎalÅĄie podrobnosti o tejto funkcii nÃĄjdete v Å ablÃŗna ÃēloÅžiska a jej dôsledky", + "storage_template_more_details": "PodrobnejÅĄie informÃĄcie o tejto funkcii nÃĄjdete v časti ÅĄablÃŗna ÃēloÅžiska a jej nÃĄsledky", + "storage_template_onboarding_description_v2": "Ak je tÃĄto funkcia zapnutÃĄ, automaticky usporiada sÃēbory na zÃĄklade ÅĄablÃŗny definovanej pouŞívateÄžom. ĎalÅĄie informÃĄcie nÃĄjdete v dokumentÃĄcii.", "storage_template_path_length": "PribliÅžnÃŊ limit dÄēÅžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Å ablÃŗna ÃēloÅžiska", "storage_template_settings_description": "Spravujte ÅĄtruktÃēru priečinkov a nÃĄzov sÃēboru odovzdanÊho mÊdia", - "storage_template_user_label": "{label} je Å títok ÃēloÅžiska pouŞívateÄža", + "storage_template_user_label": "{label} je ÅĄtítok ÃēloÅžiska pouŞívateÄža", "system_settings": "Nastavenia systÊmu", - "tag_cleanup_job": "Premazanie značiek", - "template_email_available_tags": "V ÅĄablÃŗne môŞeÅĄ pouÅžiÅĨ nasledujÃēce stítky: {tags}", - "template_email_if_empty": "Ak nie je zadanÃĄ Åžiadna ÅĄablÃŗna, bude pouÅžitÃĄ predvolenÃĄ ÅĄablÃŗna.", + "tag_cleanup_job": "Prečistenie ÅĄtítkov", + "template_email_available_tags": "V ÅĄablÃŗne môŞete pouÅžiÅĨ nasledujÃēce premennÊ: {tags}", + "template_email_if_empty": "Ak je ÅĄablÃŗna prÃĄzdna, pouÅžije sa predvolenÃŊ e-mail.", "template_email_invite_album": "Å ablÃŗna PozvÃĄnky do albumu", "template_email_preview": "UkÃĄÅžka", "template_email_settings": "EmailovÊ ÅĄablÃŗny", "template_email_update_album": "UpraviÅĨ ÅĄablÃŗnu albumu", - "template_email_welcome": "Å ablÃŗna uvítajÃēceho emailu", + "template_email_welcome": "Å ablÃŗna uvítacieho e-mailu", "template_settings": "Å ablÃŗna upozornení", "template_settings_description": "Spravovanie vlastnÃŊch ÅĄablÃŗn upozornení", "theme_custom_css_settings": "VlastnÊ CSS", "theme_custom_css_settings_description": "CSS ÅĄtÃŊly umoŞňujÃē prispôsobiÅĨ dizajn Immich.", - "theme_settings": "Motívy", + "theme_settings": "Nastavenia tÊmy", "theme_settings_description": "SpravovaÅĨ prispôsobenie webovÊho rozhrania Immich", - "thumbnail_generation_job": "GenerovaÅĨ MiniatÃēry", - "thumbnail_generation_job_description": "Generuje veÄžkÊ, malÊ a rozostrení miniatÃēry pre kaÅždÃē poloÅžku, ako aj miniatÃēry pre kaÅždÃē osobu", + "thumbnail_generation_job": "GenerovaÅĨ miniatÃēry", + "thumbnail_generation_job_description": "VytvoriÅĨ veÄžkÊ, malÊ a rozmazanÊ nÃĄhÄžady pre kaÅždÃē poloÅžku, ako aj nÃĄhÄžady pre kaÅždÃē osobu", "transcoding_acceleration_api": "API pre akcelerÃĄciu", - "transcoding_acceleration_api_description": "Rozhranie API, ktorÊ bude interagovaÅĨ s vaÅĄÃ­m zariadením s cieÄžom urÃŊchliÅĨ prekÃŗdovanie. Toto nastavenie je „najlepÅĄie Ãēsilie“: pri zlyhaní sa vrÃĄti k softvÊrovÊmu prekÃŗdovaniu. VP9 môŞe alebo nemusí fungovaÅĨ v zÃĄvislosti od vÃĄÅĄho hardvÊru.", + "transcoding_acceleration_api_description": "Rozhranie API, ktorÊ bude spolupracovaÅĨ s vaÅĄÃ­m zariadením s cieÄžom urÃŊchliÅĨ prekÃŗdovanie. Toto nastavenie je „najlepÅĄie Ãēsilie“: pri zlyhaní sa vrÃĄti k softvÊrovÊmu prekÃŗdovaniu. VP9 môŞe alebo nemusí fungovaÅĨ v zÃĄvislosti od vÃĄÅĄho hardvÊru.", "transcoding_acceleration_nvenc": "NVENC (vyÅžaduje NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (vyÅžaduje 7. generÃĄciu Intel CPU alebo novÅĄiu)", "transcoding_acceleration_rkmpp": "RKMPP (iba na Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "AkceptovanÊ zvukovÊ kodeky", - "transcoding_accepted_audio_codecs_description": "Vyberte, ktorÊ zvukovÊ kodeky nie je potrebnÊ prekÃŗdovaÅĨ. PouŞíva sa len pre určitÊ zÃĄsady prekÃŗdovania.", + "transcoding_accepted_audio_codecs_description": "Vyberte, ktorÊ zvukovÊ kodeky nie je potrebnÊ prekÃŗdovaÅĨ. PouŞíva sa len pre určitÊ pravidlÃĄ prekÃŗdovania.", "transcoding_accepted_containers": "AkceptovanÊ kontajnery", - "transcoding_accepted_containers_description": "Vyberte, ktorÊ formÃĄty kontajnerov nie je potrebnÊ remuxovaÅĨ na MP4. PouŞíva sa len pre určitÊ zÃĄsady prekÃŗdovania.", + "transcoding_accepted_containers_description": "Vyberte, ktorÊ formÃĄty kontajnerov nie je potrebnÊ remuxovaÅĨ na MP4. PouŞíva sa len pre určitÊ pravidlÃĄ prekÃŗdovania.", "transcoding_accepted_video_codecs": "AkceptovanÊ video kodeky", - "transcoding_accepted_video_codecs_description": "Vyberte, ktorÊ video kodeky nie je potrebnÊ prekÃŗdovaÅĨ. PouŞíva sa len pre určitÊ zÃĄsady prekÃŗdovania.", + "transcoding_accepted_video_codecs_description": "Vyberte, ktorÊ video kodeky nie je potrebnÊ prekÃŗdovaÅĨ. PouŞíva sa len pre určitÊ pravidlÃĄ prekÃŗdovania.", "transcoding_advanced_options_description": "MoÅžnosti, ktorÊ by vÃ¤ÄÅĄina pouŞívateÄžov nemala meniÅĨ", "transcoding_audio_codec": "ZvukovÃŊ kodek", "transcoding_audio_codec_description": "Opus je najkvalitnejÅĄia moÅžnosÅĨ, ale mÃĄ niÅžÅĄiu kompatibilitu so starÃŊmi zariadeniami alebo softvÊrom.", "transcoding_bitrate_description": "VideÃĄ presahujÃēce maximÃĄlnu bitovÃē rÃŊchlosÅĨ alebo videÃĄ, ktorÊ nie sÃē v akceptovanom formÃĄte", "transcoding_codecs_learn_more": "Ak sa chcete dozvedieÅĨ viac o tu pouÅžitej terminolÃŗgii, pozrite si dokumentÃĄciu FFmpeg pre kodek H.264, kodek HEVC a VP9 kodek.", "transcoding_constant_quality_mode": "ReÅžim konÅĄtantnej kvality", - "transcoding_constant_quality_mode_description": "ICQ je lepÅĄie ako CQP, ale niektorÊ zariadenia na hardvÊrovÃē akcelerÃĄciu tento reÅžim nepodporujÃē. Nastavenie tejto moÅžnosti uprednostní ÅĄpecifikovanÃŊ reÅžim pri pouÅžití kÃŗdovania zaloÅženÊho na kvalite. IgnorovanÊ spoločnosÅĨou NVENC, pretoÅže nepodporuje ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je lepÅĄie ako CQP, ale niektorÊ zariadenia na hardvÊrovÃē akcelerÃĄciu tento reÅžim nepodporujÃē. Nastavenie tejto moÅžnosti uprednostní ÅĄpecifikovanÃŊ reÅžim pri pouÅžití kÃŗdovania zaloÅženÊho na kvalite. IgnorovanÊ funkciou NVENC, pretoÅže nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konÅĄtantnej rÃŊchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. TypickÊ hodnoty sÃē 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. NiÅžÅĄie je lepÅĄie, ale vytvÃĄra vÃ¤ÄÅĄie sÃēbory.", - "transcoding_disabled_description": "NeprekÃŗdujte Åžiadne videÃĄ, na niektorÃŊch klientoch môŞe preruÅĄiÅĨ prehrÃĄvanie", + "transcoding_disabled_description": "NeprekÃŗdovaÅĨ Åžiadne videÃĄ, na niektorÃŊch klientoch môŞe naruÅĄiÅĨ prehrÃĄvanie", "transcoding_encoding_options": "MoÅžnosti kÃŗdovania", "transcoding_encoding_options_description": "Nastavte kodeky, rozlÃ­ÅĄenie, kvalitu a ďalÅĄie moÅžnosti pre kÃŗdovanÊ videÃĄ", "transcoding_hardware_acceleration": "HardvÊrovÃĄ akcelerÃĄcia", @@ -295,17 +336,17 @@ "transcoding_max_keyframe_interval": "MaximÃĄlny interval medzi kÄžÃēčovÃŊmi snímkami", "transcoding_max_keyframe_interval_description": "Nastavuje maximÃĄlnu vzdialenosÅĨ medzi kÄžÃēčovÃŊmi snímkami. NiÅžÅĄie hodnoty zhorÅĄujÃē ÃēčinnosÅĨ kompresie, ale zlepÅĄujÃē časy vyhÄžadÃĄvania a môŞu zlepÅĄiÅĨ kvalitu v scÊnach s rÃŊchlym pohybom. Hodnota 0 nastavuje tÃēto hodnotu automaticky.", "transcoding_optimal_description": "VideÃĄ s vyÅĄÅĄÃ­m ako cieÄžovÃŊm rozlÃ­ÅĄením alebo videÃĄ, ktorÊ nie sÃē v prijateÄžnom formÃĄte", - "transcoding_policy": "Politika prekÃŗdovania", + "transcoding_policy": "PravidlÃĄ prekÃŗdovania", "transcoding_policy_description": "Nastavte, kedy bude video prekÃŗdovanÊ", "transcoding_preferred_hardware_device": "UprednostňovanÊ hardvÊrovÊ zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorÃŊ sa pouŞíva na hardvÊrovÊ prekÃŗdovanie.", - "transcoding_preset_preset": "Prednastavenie (-preset)", + "transcoding_preset_preset": "PredvoÄžba (-preset)", "transcoding_preset_preset_description": "RÃŊchlosÅĨ kompresie. PomalÅĄie predvoÄžby vytvÃĄrajÃē menÅĄie sÃēbory a zvyÅĄujÃē kvalitu, keď sa zameriavajÃē na určitÃŊ dÃĄtovÃŊ tok. VP9 ignoruje rÃŊchlosti vyÅĄÅĄie ako „rÃŊchlejÅĄie“.", "transcoding_reference_frames": "ReferenčnÊ snímky", "transcoding_reference_frames_description": "Počet snímok, na ktorÊ sa mÃĄ odkazovaÅĨ pri kompresii danÊho snímku. VyÅĄÅĄie hodnoty zvyÅĄujÃē ÃēčinnosÅĨ kompresie, ale spomaÄžujÃē kÃŗdovanie. Hodnota 0 sa nastavuje automaticky.", - "transcoding_required_description": "Iba videÃĄ, ktorÊ nie sÃē v prijatom formÃĄte", - "transcoding_settings": "TranskÃŗdovania videa", - "transcoding_settings_description": "Spravujte, ktorÊ videÃĄ sa majÃē prekÃŗdovaÅĨ a ako ich spracovaÅĨ", + "transcoding_required_description": "Iba videÃĄ, ktorÊ nie sÃē v prijateÄžnom formÃĄte", + "transcoding_settings": "Nastavenia prekÃŗdovania videa", + "transcoding_settings_description": "SpravovaÅĨ, ktorÊ videÃĄ sa majÃē prekÃŗdovaÅĨ a ako sa majÃē spracovaÅĨ", "transcoding_target_resolution": "CieÄžovÊ rozlÃ­ÅĄenie", "transcoding_target_resolution_description": "VyÅĄÅĄie rozlÃ­ÅĄenia môŞu zachovaÅĨ viac detailov, ale ich kÃŗdovanie trvÃĄ dlhÅĄie, majÃē vÃ¤ÄÅĄiu veÄžkosÅĨ sÃēborov a môŞu zníŞiÅĨ odozvu aplikÃĄcie.", "transcoding_temporal_aq": "ČasovÊ AQ", @@ -314,30 +355,33 @@ "transcoding_threads_description": "VyÅĄÅĄie hodnoty vedÃē k rÃŊchlejÅĄiemu kÃŗdovaniu, ale ponechÃĄvajÃē serveru menej priestoru na spracovanie inÃŊch Ãēloh počas aktivity. TÃĄto hodnota by nemala byÅĨ vÃ¤ÄÅĄia ako počet jadier CPU. Maximalizuje vyuÅžitie, ak je nastavenÃĄ na hodnotu 0.", "transcoding_tone_mapping": "TÃŗnovÊ mapovanie", "transcoding_tone_mapping_description": "SnaŞí sa zachovaÅĨ vzhÄžad videí HDR pri konverzii na SDR. KaÅždÃŊ algoritmus robí rôzne kompromisy v oblasti farieb, detailov a jasu. Hable zachovÃĄva detaily, Mobius zachovÃĄva farby a Reinhard zachovÃĄva jas.", - "transcoding_transcode_policy": "Politika prekÃŗdovania", - "transcoding_transcode_policy_description": "ZÃĄsady, kedy sa mÃĄ video prekÃŗdovaÅĨ. VideÃĄ HDR sa vÅždy prekÃŗdujÃē (okrem prípadov, keď je prekÃŗdovanie vypnutÊ).", - "transcoding_two_pass_encoding": "DvojpriechodovÊ kÃŗdovanie", - "transcoding_two_pass_encoding_setting_description": "Prekladajte v dvoch priechodoch, aby ste vytvorili lepÅĄie zakÃŗdovanÊ videÃĄ. Keď je povolenÃŊ maximÃĄlny dÃĄtovÃŊ tok (vyÅžaduje sa na prÃĄcu s formÃĄtmi H.264 a HEVC), tento reÅžim pouŞíva rozsah dÃĄtovÊho toku na zÃĄklade maximÃĄlneho dÃĄtovÊho toku a ignoruje CRF. V prípade VP9 sa CRF môŞe pouÅžiÅĨ, ak je max bitrate vypnutÃŊ.", + "transcoding_transcode_policy": "PravidlÃĄ prekÃŗdovania", + "transcoding_transcode_policy_description": "PravidlÃĄ, kedy sa mÃĄ video prekÃŗdovaÅĨ. HDR videÃĄ sa prekÃŗdujÃē vÅždy (okrem prípadov, keď je prekÃŗdovanie vypnutÊ).", + "transcoding_two_pass_encoding": "DvojfÃĄzovÊ kÃŗdovanie", + "transcoding_two_pass_encoding_setting_description": "PrekÃŗdovaÅĨ v dvoch fÃĄzach, aby sa vytvorili lepÅĄie kÃŗdovanÊ videÃĄ. Keď je povolenÃŊ maximÃĄlny dÃĄtovÃŊ tok (vyÅžaduje sa na prÃĄcu s formÃĄtmi H.264 a HEVC), tento reÅžim pouŞíva rozsah dÃĄtovÊho toku na zÃĄklade maximÃĄlneho dÃĄtovÊho toku a ignoruje CRF. V prípade VP9 sa CRF môŞe pouÅžiÅĨ, ak je maximÃĄlny bitrate vypnutÃŊ.", "transcoding_video_codec": "Video kodek", - "transcoding_video_codec_description": "VP9 mÃĄ vysokÃē ÃēčinnosÅĨ a kompatibilitu s webom, ale prekÃŗdovanie trvÃĄ dlhÅĄie. HEVC mÃĄ podobnÃē vÃŊkonnosÅĨ, ale niÅžÅĄiu kompatibilitu s webom. H.264 je ÅĄiroko kompatibilnÃŊ a rÃŊchlo sa prekÃŗdovÃĄva, ale vytvÃĄra oveÄža vÃ¤ÄÅĄie sÃēbory. AV1 je najÃēčinnejÅĄÃ­ kodek, ale chÃŊba mu podpora v starÅĄÃ­ch zariadeniach.", + "transcoding_video_codec_description": "VP9 mÃĄ vysokÃē ÃēčinnosÅĨ a kompatibilitu s webom, ale prekÃŗdovanie trvÃĄ dlhÅĄie. HEVC mÃĄ podobnÃē vÃŊkonnosÅĨ, ale niÅžÅĄiu kompatibilitu s webom. H.264 je ÅĄiroko kompatibilnÃŊ a rÃŊchlo sa prekÃŗduje, ale vytvÃĄra oveÄža vÃ¤ÄÅĄie sÃēbory. AV1 je najÃēčinnejÅĄÃ­ kodek, ale chÃŊba mu podpora v starÅĄÃ­ch zariadeniach.", "trash_enabled_description": "PovoliÅĨ funkcie koÅĄa", "trash_number_of_days": "Počet dní", - "trash_number_of_days_description": "Počet dní, počas ktorÃŊch sa mÃĄ majetok ponechaÅĨ v koÅĄi pred jeho trvalÃŊm odstrÃĄnením", + "trash_number_of_days_description": "Počet dní, počas ktorÃŊch sa majÃē mÊdiÃĄ ponechaÅĨ v koÅĄi pred ich trvalÃŊm odstrÃĄnením", "trash_settings": "KÃ´ÅĄ", "trash_settings_description": "SpravovaÅĨ nastavenia koÅĄa", - "user_cleanup_job": "Premazanie pouŞívateÄžov", - "user_delete_delay": "Konto {user} a jeho mÊdiÃĄ budÃē podÄža plÃĄnu natrvalo vymazanÊ za {delay, plural, one {# day} other {# days}}.", - "user_delete_delay_settings": "OdstrÃĄniÅĨ oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstrÃĄnení na trvalÊ vymazanie Ãēčtu a aktív pouŞívateÄža. Úloha odstraňovania pouŞívateÄžov sa spÃēÅĄÅĨa o polnoci, aby sa skontrolovali pouŞívatelia, ktorí sÃē pripravení na odstrÃĄnenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalÅĄom spustení.", - "user_delete_immediately": "Konto a mÊdiÃĄ {user} budÃē zaradenÊ do frontu na trvalÊ vymazanie okamÅžite.", - "user_delete_immediately_checkbox": "PouŞívateÄž a mÊdiÃĄ budÃē zaradení do frontu na okamÅžitÊ vymazanie", + "unlink_all_oauth_accounts": "OdpojiÅĨ vÅĄetky Ãēčty OAuth", + "unlink_all_oauth_accounts_description": "Nezabudnite odpojiÅĨ vÅĄetky Ãēčty OAuth pred prechodom na novÊho poskytovateÄža.", + "unlink_all_oauth_accounts_prompt": "Naozaj chcete odpojiÅĨ vÅĄetky Ãēčty OAuth? TÃŊmto krokom sa vynuluje identifikÃĄtor OAuth pre kaÅždÊho pouŞívateÄža a tento krok nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ.", + "user_cleanup_job": "Prečistenie pouŞívateÄžov", + "user_delete_delay": "Konto pouŞívateÄža {user} a jeho mÊdiÃĄ budÃē podÄža plÃĄnu natrvalo vymazanÊ za {delay, plural, one {# deň} few {# dni} other {# dní}}.", + "user_delete_delay_settings": "Oneskorenie vymazania", + "user_delete_delay_settings_description": "Počet dní, po ktorÃŊch sa po odstrÃĄnení pouŞívateÄža natrvalo odstrÃĄni jeho Ãēčet a poloÅžky. Úloha odstraňovania pouŞívateÄžov sa spÃēÅĄÅĨa o polnoci, aby sa skontrolovali pouŞívatelia, ktorí sÃē pripravení na odstrÃĄnenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalÅĄom spustení.", + "user_delete_immediately": "Konto a mÊdiÃĄ pouŞívateÄža {user} budÃē zaradenÊ do poradia na trvalÊ vymazanie okamÅžite.", + "user_delete_immediately_checkbox": "ZaradiÅĨ pouŞívateÄža a poloÅžky do poradia na okamÅžitÊ vymazanie", "user_details": "Podrobnosti o pouŞívateÄžovi", - "user_management": "SprÃĄva pouŞívateÄžov", - "user_password_has_been_reset": "Heslo pouŞívateÄža bolo resetovanÊ:", + "user_management": "Spravovanie pouŞívateÄžov", + "user_password_has_been_reset": "Heslo pouŞívateÄža bolo obnovenÊ:", "user_password_reset_description": "Poskytnite pouŞívateÄžovi dočasnÊ heslo a informujte ho, Åže si ho bude musieÅĨ zmeniÅĨ pri ďalÅĄom prihlÃĄsení.", - "user_restore_description": "{user} bude Ãēčet obnovenÃŊ.", + "user_restore_description": "Účet pouŞívateÄža {user} bude znovu obnovenÃŊ.", "user_restore_scheduled_removal": "ObnoviÅĨ pouŞívateÄža - plÃĄnovanÊ odstrÃĄnenie na {date, date, long}", - "user_settings": "PouŞívateÄž", + "user_settings": "Nastavenia pouŞívateÄža", "user_settings_description": "SpravovaÅĨ pouŞívateÄžskÊ nastavenia", "user_successfully_removed": "PouŞívateÄž {email} bol ÃēspeÅĄne odstrÃĄnenÃŊ.", "version_check_enabled_description": "PovoliÅĨ kontrolu verzie", @@ -345,28 +389,37 @@ "version_check_settings": "Kontrola verzie", "version_check_settings_description": "PovoliÅĨ/zakÃĄzaÅĨ upozornenia na novÃē verziu", "video_conversion_job": "PrekÃŗdovaÅĨ videÃĄ", - "video_conversion_job_description": "PrekÃŗdovanie videí pre ÅĄirÅĄiu kompatibilitu s prehliadačmi a zariadeniami" + "video_conversion_job_description": "PrekÃŗdovaÅĨ videÃĄ pre ÅĄirÅĄiu kompatibilitu s prehliadačmi a zariadeniami" }, - "admin_email": "AdministrÃĄtorskÃŊ email", + "admin_email": "Email sprÃĄvcu", "admin_password": "AdministrÃĄtorskÊ heslo", "administration": "AdministrÃĄcia", "advanced": "PokročilÊ", - "advanced_settings_log_level_title": "Úroveň logovania: {level}", - "advanced_settings_prefer_remote_subtitle": "NiektorÊ zariadenia sÃē extrÊmne pomalÊ pre načítavanie miniatÃēr z fotiek na zariadení. PovoÄžte toto nastavenie aby sa namiesto toho načítavali obrÃĄzky zo servera.", - "advanced_settings_prefer_remote_title": "PreferovaÅĨ vzdialenÊ obrÃĄzky", + "advanced_settings_enable_alternate_media_filter_subtitle": "TÃēto moÅžnosÅĨ pouÅžite na filtrovanie mÊdií počas synchronizÃĄcie na zÃĄklade alternatívnych kritÊrií. TÃēto moÅžnosÅĨ vyskÃēÅĄajte len vtedy, ak mÃĄte problÊmy s detekciou vÅĄetkÃŊch albumov v aplikÃĄcii.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNE] PouÅžiÅĨ alternatívny filter synchronizÃĄcie albumu zariadenia", + "advanced_settings_log_level_title": "Úroveň ukladania zÃĄznamov: {level}", + "advanced_settings_prefer_remote_subtitle": "V niektorÃŊch zariadeniach sa miniatÃēry z miestnych poloÅžiek načítavajÃē veÄžmi pomaly. Aktivovaním tohto nastavenia sa namiesto toho načítajÃē vzdialenÊ obrÃĄzky.", + "advanced_settings_prefer_remote_title": "UprednostniÅĨ vzdialenÊ obrÃĄzky", + "advanced_settings_proxy_headers_subtitle": "Určite hlavičky proxy servera, ktorÊ by mal Immich posielaÅĨ s kaÅždou poÅžiadavkou na sieÅĨ", + "advanced_settings_proxy_headers_title": "Proxy hlavičky", + "advanced_settings_readonly_mode_subtitle": "Aktivuje reÅžim iba na čítanie, v ktorom je moÅžnÊ fotografie iba prezeraÅĨ, pričom funkcie ako vÃŊber viacerÃŊch obrÃĄzkov, zdieÄžanie, prenÃĄÅĄanie a mazanie sÃē deaktivovanÊ. AktivÃĄcia/deaktivÃĄcia reÅžimu iba na čítanie prostredníctvom obrÃĄzku pouŞívateÄža na hlavnej obrazovke", + "advanced_settings_readonly_mode_title": "ReÅžim iba na čítanie", "advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikÃĄtom zo strany servera. VyÅžaduje sa pre samo-podpísanÊ certifikÃĄty.", "advanced_settings_self_signed_ssl_title": "PovoliÅĨ samo-podpísanÊ SSL certifikÃĄty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky vymazaÅĨ alebo obnoviÅĨ poloÅžku na tomto zariadení, keď sa tÃĄto akcia vykonÃĄ na webe", + "advanced_settings_sync_remote_deletions_title": "SynchronizovaÅĨ vzdialenÊ vymazania [EXPERIMENTÁLNE]", "advanced_settings_tile_subtitle": "PokročilÊ nastavenia pouŞívateÄža", "advanced_settings_troubleshooting_subtitle": "PovoliÅĨ ďalÅĄie funkcie pre opravu chÃŊb", "advanced_settings_troubleshooting_title": "Oprava chÃŊb", - "age_months": "Vek {months, plural, one {# month} other {# months}}", - "age_year_months": "Vek 1 rok, {months, plural, one {# month} other {# months}}", + "age_months": "Vek {months, plural, one {# mesiac} few {# mesiace} other {# mesiacov}}", + "age_year_months": "Vek 1 rok, {months, plural, one {# mesiac} few {# mesiace} other {# mesiacov}}", "age_years": "{years, plural, other {Vek #}}", "album_added": "Album bol pridanÃŊ", - "album_added_notification_setting_description": "ObdrÅžaÅĨ upozornenie emailom, keď ste pridaní do zdieÄžanÊho albumu", + "album_added_notification_setting_description": "ObdrÅžaÅĨ upozornenie emailom, keď vÃĄs pridajÃē do zdieÄžanÊho albumu", "album_cover_updated": "Obal albumu aktualizovanÃŊ", "album_delete_confirmation": "Ste si istÃŊ, Åže chcete odstrÃĄniÅĨ album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieÄžanÃŊ, ostatní pouŞívatelia k nemu uÅž nebudÃē maÅĨ prístup.", + "album_deleted": "Album bol vymazanÃŊ", "album_info_card_backup_album_excluded": "VYLÚČENÉ", "album_info_card_backup_album_included": "ZAHRNUTÉ", "album_info_updated": "InformÃĄcie albumu aktualizovanÊ", @@ -376,7 +429,9 @@ "album_options": "Nastavenia albumu", "album_remove_user": "OdstrÃĄniÅĨ pouŞívateÄža?", "album_remove_user_confirmation": "Ste si istÃŊ, Åže chcete odstrÃĄniÅĨ pouŞívateÄža {user}?", + "album_search_not_found": "Neboli nÃĄjdenÊ Åžiadne albumy zodpovedajÃēce vÃĄÅĄmu hÄžadaniu", "album_share_no_users": "VyzerÃĄ to, Åže ste tento album zdieÄžali so vÅĄetkÃŊmi pouŞívateÄžmi alebo nemÃĄte Åžiadneho pouŞívateÄža, s ktorÃŊm by ste ho mohli zdieÄžaÅĨ.", + "album_summary": "SÃēhrn albumu", "album_updated": "Album bol aktualizovanÃŊ", "album_updated_setting_description": "ObdrÅžaÅĨ e-mailovÊ upozornenie, keď v zdieÄžanom albume pribudnÃē novÊ poloÅžky", "album_user_left": "Opustil {album}", @@ -391,15 +446,19 @@ "album_viewer_page_share_add_users": "PridaÅĨ pouŞívateÄžov", "album_with_link_access": "UmoÅžnite komukoÄžvek s odkazom pozrieÅĨ si fotky a Äžudí v tomto albume.", "albums": "Albumy", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumov}}", + "albums_count": "{count, plural, one {{count, number} album} few {{count, number} albumy} other {{count, number} albumov}}", + "albums_default_sort_order": "PredvolenÊ poradie albumov", + "albums_default_sort_order_description": "PočiatočnÊ poradie triedenia poloÅžiek pri vytvÃĄraní novÃŊch albumov.", + "albums_feature_description": "Zbierky mÊdií, ktorÊ moÅžno zdieÄžaÅĨ s ostatnÃŊmi pouŞívateÄžmi.", + "albums_on_device_count": "Albumy v zariadení ({count})", "all": "VÅĄetko", "all_albums": "VÅĄetky albumy", "all_people": "VÅĄetci Äžudia", "all_videos": "VÅĄetky videa", "allow_dark_mode": "PovoliÅĨ tmavÃŊ reÅžim", "allow_edits": "PovoliÅĨ Ãēpravy", - "allow_public_user_to_download": "PovoÄžte verejnÊmu pouŞívateÄžovi sÅĨahovaÅĨ", - "allow_public_user_to_upload": "UmoÅžniÅĨ verejnÊmu pouŞívateÄžovi nahrÃĄvaÅĨ", + "allow_public_user_to_download": "PovoliÅĨ verejnÊmu pouŞívateÄžovi stiahnutie", + "allow_public_user_to_upload": "UmoÅžniÅĨ verejnÊmu pouŞívateÄžovi nahraÅĨ", "alt_text_qr_code": "ObrÃĄzok QR kÃŗdu", "anti_clockwise": "Proti smeru hodinovÃŊch ručičiek", "api_key": "API KlÃēč", @@ -409,16 +468,18 @@ "app_bar_signout_dialog_content": "Skutočne sa chcete odhlÃĄsiÅĨ?", "app_bar_signout_dialog_ok": "Áno", "app_bar_signout_dialog_title": "OdhlÃĄsiÅĨ sa", - "app_settings": "Nastavenia AplikÃĄcie", + "app_settings": "Nastavenia aplikÃĄcie", "appears_in": "Vyskytuje sa v", - "archive": "ArchivovaÅĨ", + "apply_count": "PouÅžiÅĨ ({count, number})", + "archive": "Archív", + "archive_action_prompt": "{count} pridanÃŊch do archívu", "archive_or_unarchive_photo": "ArchivÃĄcia alebo odarchivovanie fotografie", "archive_page_no_archived_assets": "ÅŊiadne archivovanÊ mÊdiÃĄ", "archive_page_title": "Archív ({count})", "archive_size": "VeÄžkosÅĨ archívu", - "archive_size_description": "KonfigurÃĄcia veÄžkosti archívu na stiahnutie (v GiB)", + "archive_size_description": "NastaviÅĨ veÄžkosÅĨ archívu na stiahnutie (v GiB)", "archived": "ArchivovanÊ", - "archived_count": "{count, plural, other {ArchivovanÃŊch #}}", + "archived_count": "{count, plural, one {ArchivovanÃŊ #} few {ArchivovanÊ #} other {ArchivovanÃŊch #}}", "are_these_the_same_person": "Ide o tÃē istÃē osobu?", "are_you_sure_to_do_this": "Ste si istÃŊ, Åže to chcete urobiÅĨ?", "asset_action_delete_err_read_only": "NemoÅžno vymazaÅĨ poloÅžku len na čítanie, preskakujem", @@ -436,36 +497,59 @@ "asset_list_layout_settings_group_by_month_day": "Mesiac + deň", "asset_list_layout_sub_title": "Rozvrhnutie", "asset_list_settings_subtitle": "Nastavenia rozloÅženia mrieÅžky fotografií", - "asset_list_settings_title": "FotografickÃĄ mrieÅžka", + "asset_list_settings_title": "MrieÅžka fotografií", "asset_offline": "MÊdium je offline", "asset_offline_description": "Toto externÃŊ obsah sa uÅž nenachÃĄdza na disku. PoÅžiadajte o pomoc svojho sprÃĄvcu Immich.", + "asset_restored_successfully": "PoloÅžky boli ÃēspeÅĄne obnovenÊ", "asset_skipped": "PreskočenÊ", "asset_skipped_in_trash": "V koÅĄi", + "asset_trashed": "PoloÅžka bola vyhodenÃĄ", + "asset_troubleshoot": "RieÅĄenie problÊmov s poloÅžkami", "asset_uploaded": "NahranÊ", "asset_uploading": "NahrÃĄva saâ€Ļ", - "asset_viewer_settings_title": "Zobrazovač poloÅžiek", + "asset_viewer_settings_subtitle": "Spravujte nastavenia prehliadača galÊrie", + "asset_viewer_settings_title": "Prehliadač mÊdií", "assets": "PoloÅžky", "assets_added_count": "{count, plural, one {PridanÃĄ # poloÅžka} few {PridanÊ # poloÅžky} other {PridanÃŊch # poloÅžek}}", "assets_added_to_album_count": "Do albumu {count, plural, one {bola pridanÃĄ # poloÅžka} few {boli pridanÊ # poloÅžky} other {bolo pridanÃŊch # poloÅžiek}}", - "assets_added_to_name_count": "{count, plural, one {PridanÃĄ # poloÅžka} few {PridanÊ # poloÅžky} other {PridanÃŊch # poloÅžiek}} do {hasName, select, true {alba {name}} other {novÊho albumu}}", + "assets_added_to_albums_count": "{assetTotal, plural, one {PridanÃĄ # poloÅžka} few {PridanÊ # poloÅžky} other {PridanÃŊch # poloÅžiek}} do {albumTotal, plural, one {# albumu} other {# albumov}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {poloÅžku} other {poloÅžiek}} nie je moÅžnÊ pridaÅĨ do albumu", + "assets_cannot_be_added_to_albums": "{count, plural, one {poloÅžka} few {poloÅžky} other {poloÅžiek}} nie je moÅžnÊ pridaÅĨ do Åžiadneho albumu", "assets_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžiek}}", + "assets_deleted_permanently": "{count} poloÅžka(iek) natrvalo vymazanÃĄ(ÃŊch)", + "assets_deleted_permanently_from_server": "{count} poloÅžka(iek) natrvalo vymazanÃĄ(ÃŊch) zo servera Immich", + "assets_downloaded_failed": "{count, plural, one {StiahnutÃŊ # sÃēbor - {error} sÃēbor zlyhal} few {StiahnutÊ # sÃēbory - {error} sÃēbory zlyhali} other {StiahnutÃŊch # sÃēborov - {error} sÃēborov zlyhalo}}", + "assets_downloaded_successfully": "{count, plural, one {# sÃēbor bol ÃēspeÅĄne stiahnutÃŊ} few {# sÃēbory boli ÃēspeÅĄne stiahnutÊ} other {# sÃēborov bolo ÃēspeÅĄne stiahnutÃŊch}}", "assets_moved_to_trash_count": "Do koÅĄa {count, plural, one {bola presunutÃĄ # poloÅžka} few {boli presunutÊ # poloÅžky} other {bolo presunutÃŊch # poloÅžiek}}", "assets_permanently_deleted_count": "Trvalo {count, plural, one {vymazanÃĄ # poloÅžka} few {vymazanÊ # poloÅžky} other {vymazanÃŊch # poloÅžiek}}", "assets_removed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", + "assets_removed_permanently_from_device": "{count} poloÅžka(iek) natrvalo vymazanÃĄ(ÃŊch) z vÃĄÅĄho zariadenia", "assets_restore_confirmation": "Naozaj chcete obnoviÅĨ vÅĄetky vyhodenÊ poloÅžky? TÃēto akciu nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ! Upozorňujeme, Åže tÃŊmto spôsobom nie je moÅžnÊ obnoviÅĨ Åžiadne offline poloÅžky.", "assets_restored_count": "{count, plural, one {ObnovenÃĄ # poloÅžka} few {ObnovenÊ # poloÅžky} other {ObnovenÃŊch # poloÅžiek}}", - "assets_restored_successfully": "{count} medií ÃēspeÅĄne obnovenÃŊch", + "assets_restored_successfully": "{count} mÊdií ÃēspeÅĄne obnovenÃŊch", + "assets_trashed": "{count} poloÅžka(iek) vyhodenÃĄ(ÃŊch) do koÅĄa", "assets_trashed_count": "{count, plural, one {OdstrÃĄnenÃĄ # poloÅžka} few {OdstrÃĄnenÊ # poloÅžky} other {OdstrÃĄnenÃŊch # poloÅžiek}}", - "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka bola} other {PoloÅžky boli}} sÃēčasÅĨou albumu", + "assets_trashed_from_server": "{count} poloÅžka(iek) vyhodenÃĄ(ÃŊch) do koÅĄa zo servera Immich", + "assets_were_part_of_album_count": "{count, plural, one {PoloÅžka uÅž bola} other {PoloÅžky uÅž boli}} sÃēčasÅĨou albumu", + "assets_were_part_of_albums_count": "{count, plural, one {poloÅžka} few {poloÅžky} other {poloÅžiek}} sÃē uÅž sÃēčasÅĨou albumov", "authorized_devices": "AutorizovanÊ zariadenia", + "automatic_endpoint_switching_subtitle": "PripojiÅĨ sa lokÃĄlne prostredníctvom určenÊho pripojenia Wi-Fi, ak je k dispozícii, a pouŞívaÅĨ alternatívne pripojenia inde", + "automatic_endpoint_switching_title": "AutomatickÊ prepínanie URL adresy", + "autoplay_slideshow": "AutomatickÊ prehrÃĄvanie prezentÃĄcie", "back": "SpäÅĨ", "back_close_deselect": "SpäÅĨ, zavrieÅĨ alebo zruÅĄiÅĨ vÃŊber", + "background_backup_running_error": "V sÃēčasnosti prebieha zÃĄlohovanie na pozadí, nie je moÅžnÊ spustiÅĨ ručnÊ zÃĄlohovanie", + "background_location_permission": "Povolenie na určenie polohy na pozadí", + "background_location_permission_content": "Aby bolo moÅžnÊ prepínaÅĨ siete pri spustení na pozadí, musí maÅĨ aplikÃĄcia Immich *vÅždy* presnÃŊ prístup k polohe, aby mohla prečítaÅĨ nÃĄzov siete Wi-Fi", + "background_options": "MoÅžnosti pozadia", + "backup": "ZÃĄlohovanie", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na poloÅžku ju zahrniete, dvojitÃŊm ÅĨuknutím ju vylÃēčite", "backup_album_selection_page_assets_scatter": "SÃēbory môŞu byÅĨ roztrÃēsenÊ vo viacerÃŊch albumoch. To umoŞňuje zahrnÃēÅĨ alebo vylÃēčiÅĨ albumy počas procesu zÃĄlohovania.", - "backup_album_selection_page_select_albums": "VybranÊ albumy", + "backup_album_selection_page_select_albums": "VybraÅĨ albumy", "backup_album_selection_page_selection_info": "InformÃĄcie o vÃŊbere", "backup_album_selection_page_total_assets": "CelkovÃŊ počet jedinečnÃŊch sÃēborov", + "backup_albums_sync": "SynchronizÃĄcia zÃĄlohovanÃŊch albumov", "backup_all": "VÅĄetko", "backup_background_service_backup_failed_message": "ZÃĄlohovanie mÊdií zlyhalo. SkÃēÅĄam to znovaâ€Ļ", "backup_background_service_connection_failed_message": "Nepodarilo sa pripojiÅĨ k serveru. SkÃēÅĄam to znovaâ€Ļ", @@ -485,7 +569,7 @@ "backup_controller_page_background_charging": "Len počas nabíjania", "backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovaÅĨ sluÅžbu na pozadí", "backup_controller_page_background_delay": "Oneskorenie zÃĄlohovania novÃŊch mÊdií: {duration}", - "backup_controller_page_background_description": "PovoÄžte sluÅžbu na pozadí na automatickÊ zÃĄlohovanie vÅĄetkÃŊch novÃŊch aktív bez nutnosti otvorenia aplikÃĄcie", + "backup_controller_page_background_description": "PovoÄžte sluÅžbu na pozadí na automatickÊ zÃĄlohovanie vÅĄetkÃŊch novÃŊch poloÅžiek bez nutnosti otvorenia aplikÃĄcie", "backup_controller_page_background_is_off": "AutomatickÊ zÃĄlohovanie na pozadí je vypnutÊ", "backup_controller_page_background_is_on": "AutomatickÊ zÃĄlohovanie na pozadí je zapnutÊ", "backup_controller_page_background_turn_off": "VypnÃēÅĨ zÃĄlohovanie na pozadí", @@ -503,7 +587,7 @@ "backup_controller_page_info": "InformÃĄcie o zÃĄlohovaní", "backup_controller_page_none_selected": "ÅŊiadne vybranÊ", "backup_controller_page_remainder": "ZostÃĄva", - "backup_controller_page_remainder_sub": "ZostÃĄvajÃēce fotografie a videÃĄ, ktorÊ sa majÃē zÃĄlohovaÅĨ z vÃŊbranÃŊch albumov", + "backup_controller_page_remainder_sub": "ZostÃĄvajÃēce fotografie a videÃĄ, ktorÊ sa majÃē zÃĄlohovaÅĨ z vÃŊberu", "backup_controller_page_server_storage": "ServerovÊ ÃēloÅžisko", "backup_controller_page_start_backup": "SpustiÅĨ zÃĄlohovanie", "backup_controller_page_status_off": "AutomatickÊ zÃĄlohovanie na popredí je vypnutÊ", @@ -515,28 +599,36 @@ "backup_controller_page_turn_on": "PovoliÅĨ zÃĄlohovanie na popredí", "backup_controller_page_uploading_file_info": "NahrÃĄvanÃŊ sÃēbor", "backup_err_only_album": "Nie je moÅžnÊ odstrÃĄniÅĨ jedinÃŊ vybranÃŊ album", + "backup_error_sync_failed": "SynchronizÃĄcia sa nepodarila. Nie je moÅžnÊ spracovaÅĨ zÃĄlohu.", "backup_info_card_assets": "poloÅžiek", "backup_manual_cancelled": "ZruÅĄenÊ", "backup_manual_in_progress": "NahrÃĄvanie uÅž prebieha. VyskÃēÅĄajte neskôr", "backup_manual_success": "Úspech", "backup_manual_title": "Stav nahrÃĄvania", + "backup_options": "MoÅžnosti zÃĄlohovania", "backup_options_page_title": "MoÅžnosti zÃĄlohovania", - "backward": "Spätne", + "backup_setting_subtitle": "SpravovaÅĨ nastavenia odosielania na pozadí a v popredí", + "backup_settings_subtitle": "SpravovaÅĨ nastavenia nahrÃĄvania", + "backward": "Dozadu", + "biometric_auth_enabled": "BiometrickÊ overovanie je povolenÊ", + "biometric_locked_out": "Ste vymknutí z biometrickÊho overovania", + "biometric_no_options": "Nie sÃē k dispozícii Åžiadne biometrickÊ moÅžnosti", + "biometric_not_available": "BiometrickÊ overenie nie je v tomto zariadení k dispozícii", "birthdate_saved": "DÃĄtum narodenia bol ÃēspeÅĄne uloÅženÃŊ", "birthdate_set_description": "DÃĄtum narodenia sa pouŞíva na vÃŊpočet veku tejto osoby v čase fotografie.", "blurred_background": "RozmazanÊ pozadie", "bugs_and_feature_requests": "Chyby a poÅžiadavky na funkcie", "build": "Zostava", "build_image": "Obraz zostavy", - "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrÃĄniÅĨ {count, plural, one {# duplikÃĄtnu poloÅžku} few {# duplikÃĄte poloÅžky} other {# duplikÃĄtnych poloÅžiek}}? TÃŊmto sa zachovÃĄ najvÃ¤ÄÅĄia poloÅžka z kaÅždej skupiny a vÅĄetky ostatnÊ duplikÃĄty sa natrvalo odstrÃĄnia. TÃēto akciu nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ!", - "bulk_keep_duplicates_confirmation": "Naozaj chceÅĄ ponechaÅĨ {count, plural, one {# duplicitnÃŊ sÃēbor} other {# duplicitnÊ sÃēbory}}? TÃŊmto sa vysporiadaÅĄ so vÅĄetkÃŊmi duplicitnÃŊmi skupinami bez mazania sÃēborov.", - "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazaÅĨ {count, plural, one {# duplicitnÃŊ sÃēbor} other {# duplicitnÊ sÃēbory}}? TÃŊmto si ponechÃĄÅĄ z kaÅždej skupiny najvÃ¤ÄÅĄÃ­ sÃēbor a vymaÅžeÅĄ vÅĄetky ostatnÊ duplicitnÊ sÃēbory v skupine.", + "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrÃĄniÅĨ {count, plural, one {# duplikÃĄtnu poloÅžku} few {# duplikÃĄtne poloÅžky} other {# duplikÃĄtnych poloÅžiek}}? TÃŊmto sa zachovÃĄ najvÃ¤ÄÅĄia poloÅžka z kaÅždej skupiny a vÅĄetky ostatnÊ duplikÃĄty sa natrvalo odstrÃĄnia. TÃēto akciu nie je moÅžnÊ vrÃĄtiÅĨ späÅĨ!", + "bulk_keep_duplicates_confirmation": "Naozaj chcete ponechaÅĨ {count, plural, one {# duplicitnÃē poloÅžku} few {# duplicitnÊ poloÅžky} other {# duplicitnÃŊch poloÅžiek}}? TÃŊmto sa vyrieÅĄia vÅĄetky duplicitnÊ skupiny bez toho, aby sa čokoÄžvek odstrÃĄnilo.", + "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazaÅĨ {count, plural, one {# duplicitnÃē poloÅžku} few {# duplicitnÊ poloÅžky} other {# duplicitnÃŊch poloÅžiek}}? TÃŊmto sa zachovÃĄ najvÃ¤ÄÅĄia poloÅžka z kaÅždej skupiny a vÅĄetky ostatnÊ duplicitnÊ poloÅžky sa vyhodia.", "buy": "KÃēpiÅĨ Immich", "cache_settings_clear_cache_button": "VymazaÅĨ vyrovnÃĄvaciu pamäÅĨ", "cache_settings_clear_cache_button_title": "VymaÅže vyrovnÃĄvaciu pamäÅĨ aplikÃĄcie. To vÃŊrazne ovplyvní vÃŊkon aplikÃĄcie, kÃŊm sa vyrovnÃĄvacia pamäÅĨ neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", - "cache_settings_duplicated_assets_subtitle": "Fotky a videÃĄ ktorÊ sÃē na čiernej listine zvolenÊ aplikÃĄciou", - "cache_settings_duplicated_assets_title": "DuplikÃĄty ({count})", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videÃĄ, ktorÊ aplikÃĄcia ignoruje podÄža zoznamu", + "cache_settings_duplicated_assets_title": "DuplicitnÊ poloÅžky ({count})", "cache_settings_statistics_album": "KniÅžnica nÃĄhÄžadov", "cache_settings_statistics_full": "KompletnÊ fotografie", "cache_settings_statistics_shared": "ZdieÄžanÊ nÃĄhÄžady albumov", @@ -552,9 +644,12 @@ "cancel": "ZruÅĄiÅĨ", "cancel_search": "ZruÅĄiÅĨ vyhÄžadÃĄvanie", "canceled": "ZruÅĄenÊ", + "canceling": "RuÅĄÃ­ sa", "cannot_merge_people": "Nie je moÅžnÊ zlÃēčiÅĨ Äžudí", "cannot_undo_this_action": "TÃēto akciu nemôŞete vrÃĄtiÅĨ späÅĨ!", "cannot_update_the_description": "Popis nie je moÅžnÊ aktualizovaÅĨ", + "cast": "Prenos (cast)", + "cast_description": "Nastavte dostupnÊ ciele prenosu", "change_date": "UpraviÅĨ dÃĄtum", "change_description": "ZmeniÅĨ popis", "change_display_order": "ZmeniÅĨ poradie zobrazenia", @@ -570,20 +665,30 @@ "change_password_form_password_mismatch": "HeslÃĄ sa nezhodujÃē", "change_password_form_reenter_new_password": "Znova zadajte novÊ heslo", "change_pin_code": "ZmeniÅĨ PIN kÃŗd", - "change_your_password": "Zmeňte si heslo", + "change_your_password": "ZmeniÅĨ heslo", "changed_visibility_successfully": "ViditeÄžnosÅĨ bola ÃēspeÅĄne zmenenÃĄ", + "charging": "Nabíja sa", + "charging_requirement_mobile_backup": "ZÃĄlohovanie na pozadí vyÅžaduje, aby bolo zariadenie nabíjanÊ", + "check_corrupt_asset_backup": "SkontrolovaÅĨ, či nie sÃē poÅĄkodenÊ zÃĄlohy poloÅžiek", "check_corrupt_asset_backup_button": "VykonaÅĨ kontrolu", + "check_corrupt_asset_backup_description": "SpustiÅĨ tÃēto kontrolu len cez Wi-Fi a po zÃĄlohovaní vÅĄetkÃŊch poloÅžiek. Tento postup môŞe trvaÅĨ niekoÄžko minÃēt.", "check_logs": "SkontrolovaÅĨ logy", "choose_matching_people_to_merge": "Vyberte rovnakÃŊch Äžudí na zlÃēčenie", "city": "Mesto", "clear": "VyčistiÅĨ", "clear_all": "VyčistiÅĨ vÅĄetko", "clear_all_recent_searches": "VyčistiÅĨ nedÃĄvne vyhÄžadÃĄvania", + "clear_file_cache": "VyčistiÅĨ vyrovnÃĄvaciu pamäÅĨ sÃēborov", "clear_message": "VymazaÅĨ sprÃĄvu", "clear_value": "VymazaÅĨ hodnotu", "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "ZadaÅĨ heslo", "client_cert_import": "ImportovaÅĨ", + "client_cert_import_success_msg": "CertifikÃĄt klienta je naimportovanÃŊ", + "client_cert_invalid_msg": "NeplatnÃŊ sÃēbor certifikÃĄtu alebo nesprÃĄvne heslo", + "client_cert_remove_msg": "CertifikÃĄt klienta je odstrÃĄnenÃŊ", + "client_cert_subtitle": "Podporuje iba formÃĄt PKCS12 (.p12, .pfx). Importovanie/odstrÃĄnenie certifikÃĄtu je k dispozícii len pred prihlÃĄsením", + "client_cert_title": "SSL certifikÃĄt klienta", "clockwise": "V smere hodinovÃŊch ručičiek", "close": "ZatvoriÅĨ", "collapse": "ZbaliÅĨ", @@ -607,7 +712,8 @@ "confirm_tag_face": "Chcete označiÅĨ tÃēto tvÃĄr ako {name}?", "confirm_tag_face_unnamed": "Chcete označiÅĨ tÃēto tvÃĄr?", "connected_device": "PripojenÊ zariadenie", - "contain": "ObsiahnÃēÅĨ", + "connected_to": "PripojenÊ k", + "contain": "PrispôsobiÅĨ", "context": "Kontext", "continue": "PokračovaÅĨ", "control_bottom_app_bar_create_new_album": "VytvoriÅĨ novÃŊ album", @@ -616,6 +722,7 @@ "control_bottom_app_bar_edit_location": "UpraviÅĨ polohu", "control_bottom_app_bar_edit_time": "UpraviÅĨ dÃĄtum a čas", "control_bottom_app_bar_share_link": "ZdieÄžaÅĨ odkaz", + "control_bottom_app_bar_share_to": "ZdieÄžaÅĨ cez", "control_bottom_app_bar_trash_from_immich": "PresunÃēÅĨ do koÅĄa", "copied_image_to_clipboard": "ObrÃĄzok skopírovanÃŊ do schrÃĄnky.", "copied_to_clipboard": "SkopírovanÊ do schrÃĄnky!", @@ -627,7 +734,7 @@ "copy_password": "SkopírovaÅĨ heslo", "copy_to_clipboard": "SkopírovaÅĨ do schrÃĄnky", "country": "Krajina", - "cover": "Titulka", + "cover": "VyplniÅĨ", "covers": "DlaÅždice", "create": "VytvoriÅĨ", "create_album": "VytvoriÅĨ album", @@ -635,28 +742,32 @@ "create_library": "VytvoriÅĨ kniÅžnicu", "create_link": "VytvoriÅĨ odkaz", "create_link_to_share": "VytvoriÅĨ odkaz na zdieÄžanie", - "create_link_to_share_description": "UmoÅžniÅĨ kaÅždÊmu kto mÃĄ odkaz zobraziÅĨ vybranÊ fotografie", + "create_link_to_share_description": "UmoÅžniÅĨ kaÅždÊmu, kto mÃĄ odkaz, zobraziÅĨ vybranÊ fotografie", "create_new": "VYTVORIŤ NOVÉ", "create_new_person": "VytvoriÅĨ novÃē osobu", "create_new_person_hint": "PriradiÅĨ vybranÊ poloÅžky novej osobe", "create_new_user": "Vytvorenie novÊho pouŞívateÄža", "create_shared_album_page_share_add_assets": "PridaÅĨ poloÅžky", "create_shared_album_page_share_select_photos": "VybraÅĨ fotografie", - "create_tag": "VytvoriÅĨ značku", - "create_tag_description": "Vytvorenie novÊho ÅĄtítku. Pre VnorenÊ ÅĄtítky, prosím, zadaj celÃē cestu ÅĄtítku, vrÃĄtane lomítok vpred.", + "create_shared_link": "VytvoriÅĨ zdieÄžanÃŊ odkaz", + "create_tag": "VytvoriÅĨ ÅĄtítok", + "create_tag_description": "Vytvorte novÃŊ ÅĄtítok. V prípade vnorenÃŊch ÅĄtítkov zadajte celÃē cestu k ÅĄtítku vrÃĄtane lomiek.", "create_user": "VytvoriÅĨ pouŞívateÄža", "created": "VytvorenÊ", "created_at": "VytvorenÊ", + "creating_linked_albums": "VytvÃĄranie prepojenÃŊch albumov...", "crop": "OrezaÅĨ", "curated_object_page_title": "Veci", "current_device": "SÃēčasnÊ zariadenie", "current_pin_code": "AktuÃĄlny PIN kÃŗd", "current_server_address": "AktuÃĄlna adresa servera", - "custom_locale": "VlastnÃĄ LokalizÃĄcia", + "custom_locale": "VlastnÊ nastavenie jazyka", "custom_locale_description": "FormÃĄtovanie dÃĄtumov a čísel podÄža jazyka a regiÃŗnu", + "custom_url": "VlastnÃĄ URL adresa", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", - "dark": "TmavÃŊ", + "dark": "TmavÃĄ", + "dark_theme": "PrepnÃēÅĨ tmavÃē tÊmu", "date_after": "DÃĄtum po", "date_and_time": "DÃĄtum a Čas", "date_before": "DÃĄtum pred", @@ -664,19 +775,22 @@ "date_of_birth_saved": "DÃĄtum narodenia ÃēspeÅĄne uloÅženÃŊ", "date_range": "Rozsah dÃĄtumu", "day": "Deň", + "days": "Dní", "deduplicate_all": "DeduplikovaÅĨ vÅĄetko", "deduplication_criteria_1": "VeÄžkosÅĨ obrÃĄzku v bajtoch", "deduplication_criteria_2": "Počet EXIF Ãēdajov", "deduplication_info": "Info o deduplikÃĄcii", "deduplication_info_description": "Na automatickÃŊ predvÃŊber poloÅžiek a hromadnÊ odstrÃĄnenie duplicít, sa pozerÃĄme do:", - "default_locale": "PredvolenÃĄ LokalizÃĄcia", - "default_locale_description": "FormÃĄtovanie dÃĄtumu a čísel podÄža lokalizÃĄcie vÃĄÅĄho prehliadača", + "default_locale": "PredvolenÊ miestne nastavenie", + "default_locale_description": "FormÃĄtovanie dÃĄtumov a čísel na zÃĄklade miestneho nastavenia prehliadača", "delete": "VymazaÅĨ", + "delete_action_confirmation_message": "Naozaj chcete tÃēto poloÅžku odstrÃĄniÅĨ? TÃĄto akcia presunie poloÅžku do koÅĄa na serveri a zobrazí sa otÃĄzka, či ju chcete odstrÃĄniÅĨ aj lokÃĄlne", + "delete_action_prompt": "{count} vymazanÃŊch", "delete_album": "OdstrÃĄniÅĨ album", "delete_api_key_prompt": "Naozaj chcete odstrÃĄniÅĨ tento API kÄžÃēč?", "delete_dialog_alert": "Tieto poloÅžky budÃē natrvalo odstrÃĄnenÊ z aplikÃĄcie Immich a z vÃĄÅĄho zariadenia", "delete_dialog_alert_local": "Tieto poloÅžky budÃē permanentne vymazanÊ z vaÅĄeho zariadenia, ale budÃē stÃĄle k dispozícií na serveri Immich", - "delete_dialog_alert_local_non_backed_up": "NiektorÊ poloÅžky nie sÃē zÃĄlohovanÊ na Immichi a budÃē permanentne vymazanÊ z vÃĄÅĄho zariadenia", + "delete_dialog_alert_local_non_backed_up": "NiektorÊ poloÅžky nie sÃē zÃĄlohovanÊ na Immich a budÃē permanentne odstrÃĄnenÊ z vÃĄÅĄho zariadenia", "delete_dialog_alert_remote": "Tieto poloÅžky budÃē permanentne vymazanÊ zo serveru Immich", "delete_dialog_ok_force": "Napriek tomu vymazaÅĨ", "delete_dialog_title": "VymazaÅĨ natrvalo", @@ -685,23 +799,27 @@ "delete_key": "OdstrÃĄniÅĨ kÄžÃēč", "delete_library": "VymazaÅĨ kniÅžnicu", "delete_link": "OdstrÃĄniÅĨ odkaz", + "delete_local_action_prompt": "{count} vymazanÊ lokÃĄlne", "delete_local_dialog_ok_backed_up_only": "VymazaÅĨ len zÃĄlohovanÊ", "delete_local_dialog_ok_force": "Napriek tomu vymazaÅĨ", "delete_others": "VymazaÅĨ ostatnÊ", + "delete_permanently": "Natrvalo odstrÃĄniÅĨ", + "delete_permanently_action_prompt": "{count} natrvalo odstrÃĄnenÃŊch", "delete_shared_link": "OdstrÃĄniÅĨ zdieÄžanÃŊ odkaz", "delete_shared_link_dialog_title": "OdstrÃĄniÅĨ zdieÄžanÃŊ odkaz", - "delete_tag": "OdstrÃĄniÅĨ označenie", + "delete_tag": "OdstrÃĄniÅĨ ÅĄtítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrÃĄniÅĨ ÅĄtítok menom {tagName}?", "delete_user": "VymazaÅĨ pouŞívateÄža", "deleted_shared_link": "VymazanÃŊ zdieÄžanÃŊ odkaz", - "deletes_missing_assets": "ChÃŊbajÃē vymazanÊ poloÅžky z disku", + "deletes_missing_assets": "OdstrÃĄni poloÅžky chÃŊbajÃēce na disku", "description": "Popis", "description_input_hint_text": "PridaÅĨ popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", + "deselect_all": "ZruÅĄiÅĨ vÃŊber vÅĄetkÃŊch", "details": "Podrobnosti", "direction": "Smer", "disabled": "VypnutÊ", - "disallow_edits": "ZakÃĄzaÅĨ editovanie", + "disallow_edits": "ZakÃĄzaÅĨ Ãēpravy", "discord": "Discord", "discover": "ObjaviÅĨ", "discovered_devices": "ObjavenÊ zariadenia", @@ -715,30 +833,39 @@ "documentation": "DokumentÃĄcia", "done": "Hotovo", "download": "StiahnuÅĨ", + "download_action_prompt": "SÅĨahuje sa {count} poloÅžiek", "download_canceled": "Stiahnutie zruÅĄenÊ", "download_complete": "Stiahnutie dokončenÊ", + "download_enqueue": "Stiahnutie v poradí", "download_error": "Chyba sÅĨahovania", "download_failed": "Stiahnutie sa nepodarilo", "download_finished": "Stiahnutie dokončenÊ", "download_include_embedded_motion_videos": "VloÅženÊ videÃĄ", "download_include_embedded_motion_videos_description": "ZahrnÃēÅĨ videÃĄ vloÅženÊ do pohyblivÃŊch fotiek ako samostatnÊ sÃēbory", + "download_notfound": "Stiahnutie nebolo nÃĄjdenÊ", "download_paused": "Stiahnutie pozastavenÊ", "download_settings": "StiahnuÅĨ", "download_settings_description": "SpravovaÅĨ nastavenia sÃēvisiace so sÅĨahovaním poloÅžiek", "download_started": "SÅĨahovanie spustenÊ", + "download_sucess": "Stiahnutie ÃēspeÅĄnÊ", "download_sucess_android": "MÊdiÃĄ boli stiahnutÊ do DCIM/Immich", + "download_waiting_to_retry": "ČakÃĄ sa na opakovanie pokusu", "downloading": "SÅĨahuje sa", "downloading_asset_filename": "SÅĨahuje sa poloÅžka {filename}", "downloading_media": "SÅĨahovanie mÊdií", - "drop_files_to_upload": "Hoď sÃēbory kdekoÄžvek, nahrajÃē sa", + "drop_files_to_upload": "Umiestnite sÃēbory kamkoÄžvek na nahratie", "duplicates": "DuplikÃĄty", "duplicates_description": "VysporiadaÅĨ sa s kaÅždou skupinou tak, Åže sa duplicitnÊ označia ako duplicitnÊ", "duration": "Trvanie", "edit": "UpraviÅĨ", "edit_album": "UpraviÅĨ album", - "edit_avatar": "UpraviÅĨ avatar", + "edit_avatar": "UpraviÅĨ profilovÃŊ obrÃĄzok", + "edit_birthday": "UpraviÅĨ narodeniny", "edit_date": "UpraviÅĨ dÃĄtum", "edit_date_and_time": "UpraviÅĨ dÃĄtum a čas", + "edit_date_and_time_action_prompt": "{count} dÃĄtum a čas upravenÃŊ", + "edit_date_and_time_by_offset": "ZmeniÅĨ dÃĄtum podÄža posunu", + "edit_date_and_time_by_offset_interval": "NovÃŊ rozsah dÃĄtumov: {from} - {to}", "edit_description": "UpraviÅĨ popis", "edit_description_prompt": "Vyberte prosím novÃŊ popis:", "edit_exclusion_pattern": "UpraviÅĨ vzor vylÃēčenia", @@ -748,10 +875,11 @@ "edit_key": "UpraviÅĨ kÄžÃēč", "edit_link": "UpraviÅĨ odkaz", "edit_location": "UpraviÅĨ polohu", + "edit_location_action_prompt": "{count} poloha upravenÃĄ", "edit_location_dialog_title": "Poloha", "edit_name": "UpraviÅĨ meno", "edit_people": "UpraviÅĨ osoby", - "edit_tag": "UpraiÅĨ značku", + "edit_tag": "UpraviÅĨ ÅĄtítok", "edit_title": "UpraviÅĨ nÃĄzov", "edit_user": "UpraviÅĨ pouŞívateÄža", "edited": "UpravenÊ", @@ -759,41 +887,48 @@ "editor_close_without_save_prompt": "Úpravy nebudÃē uloÅženÊ", "editor_close_without_save_title": "ZavrieÅĨ editor?", "editor_crop_tool_h2_aspect_ratios": "Pomer strÃĄn", - "editor_crop_tool_h2_rotation": "Rotovanie", + "editor_crop_tool_h2_rotation": "Otočenie", "email": "E-mail", "email_notifications": "E-mailovÊ oznÃĄmenia", "empty_folder": "Tento priečinok je prÃĄzdny", "empty_trash": "VyprÃĄzdniÅĨ kÃ´ÅĄ", "empty_trash_confirmation": "Naozaj chcete vyprÃĄzdniÅĨ kÃ´ÅĄ? NenÃĄvratne sa vymaÅžÃē vÅĄetky poloÅžky z Immich.\nTÃĄto akcia sa nedÃĄ vrÃĄtiÅĨ!", "enable": "AktivovaÅĨ", + "enable_backup": "PovoliÅĨ zÃĄlohovanie", + "enable_biometric_auth_description": "Zadajte svoj PIN kÃŗd, aby ste povolili biometrickÊ overenie", "enabled": "AktivovanÃŊ", "end_date": "KoncovÃŊ dÃĄtum", + "enqueued": "V poradí", "enter_wifi_name": "Zadajte nÃĄzov Wi-Fi", "enter_your_pin_code": "Zadajte svoj PIN kÃŗd", "enter_your_pin_code_subtitle": "Zadaním kÃŗdu PIN získate prístup k zamknutÊmu priečinku", "error": "Chyba", + "error_change_sort_album": "Nepodarilo sa zmeniÅĨ poradie albumu", "error_delete_face": "Chyba pri odstraňovaní tvÃĄre z poloÅžky", + "error_getting_places": "Chyba pri získavaní polôh", "error_loading_image": "Nepodarilo sa načítaÅĨ obrÃĄzok", + "error_loading_partners": "Chyba pri načítaní partnerov: {error}", "error_saving_image": "Chyba: {error}", + "error_tag_face_bounding_box": "Chyba pri označovaní tvÃĄre - nemoÅžno získaÅĨ sÃēradnice ohraničujÃēceho poÄža", "error_title": "Chyba - niečo sa pokazilo", "errors": { - "cannot_navigate_next_asset": "NedokÃĄÅžem prejsÅĨ na ďaÄžÅĄiu poloÅžku", - "cannot_navigate_previous_asset": "NedokÃĄÅžem prejsÅĨ na predoÅĄlÃē poloÅžku", - "cant_apply_changes": "NedokÃĄÅžem aplikovaÅĨ zmeny", - "cant_change_activity": "NodokÃĄÅžem {enabled, select, true {zakÃĄzaÅĨ} other {povoliÅĨ}} aktivitu", - "cant_change_asset_favorite": "NedokÃĄÅžem zmeniÅĨ obÄžÃēbenosÅĨ pre poloÅžku", - "cant_change_metadata_assets_count": "NedokÃĄÅžem zmeniÅĨ metadÃĄta pre {count, plural, one {# tÃēto poloÅžku} other {# tieto poloÅžky}}", + "cannot_navigate_next_asset": "Nie je moÅžnÊ prejsÅĨ na ďalÅĄiu poloÅžku", + "cannot_navigate_previous_asset": "Nie je moÅžnÊ prejsÅĨ na predoÅĄlÃē poloÅžku", + "cant_apply_changes": "Nie je moÅžnÊ pouÅžiÅĨ zmeny", + "cant_change_activity": "Nie je moÅžnÊ {enabled, select, true {zakÃĄzaÅĨ} other {povoliÅĨ}} aktivitu", + "cant_change_asset_favorite": "Nie je moÅžnÊ zmeniÅĨ stav obÄžÃēbenosti pre poloÅžku", + "cant_change_metadata_assets_count": "Nie je moÅžnÊ zmeniÅĨ metadÃĄta pre {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžiek}}", "cant_get_faces": "NedokÃĄÅžem získaÅĨ tvÃĄre", "cant_get_number_of_comments": "NedokÃĄÅžem získaÅĨ počet komentÃĄrov", "cant_search_people": "NedokÃĄÅžem hÄžadaÅĨ osoby", "cant_search_places": "NedokÃĄÅžem hÄžadaÅĨ miesta", "error_adding_assets_to_album": "Nepodarilo sa pridaÅĨ poloÅžky do albumu", - "error_adding_users_to_album": "Nepodarilo sa pridaÅĨ uŞívateÄžov do albumu", - "error_deleting_shared_user": "Nepodarilo sa odstrÃĄniÅĨ zdieÄžanÊho pouŞívateÄža", + "error_adding_users_to_album": "Nepodarilo sa pridaÅĨ pouŞívateÄžov do albumu", + "error_deleting_shared_user": "Chyba pri odstraňovaní zdieÄžanÊho pouŞívateÄža", "error_downloading": "Nepodarilo sa stiahnuÅĨ {filename}", "error_hiding_buy_button": "Nepodarilo sa skryÅĨ tlačidlo kÃēpiÅĨ", "error_removing_assets_from_album": "Nepodarilo sa odstrÃĄniÅĨ poloÅžku z albumu, podrobnejÅĄie informÃĄcie nÃĄjdete v konzole", - "error_selecting_all_assets": "Nepodarilo sa vybraÅĨ poloÅžky", + "error_selecting_all_assets": "Chyba pri vÃŊbere vÅĄetkÃŊch poloÅžiek", "exclusion_pattern_already_exists": "Tento vzor vylÃēčenia uÅž existuje.", "failed_to_create_album": "Nepodarilo sa vytvoriÅĨ album", "failed_to_create_shared_link": "Nepodarilo sa vytvoriÅĨ zdieÄžanÃŊ odkaz", @@ -802,15 +937,19 @@ "failed_to_keep_this_delete_others": "Nepodarilo sa ponechaÅĨ tÃēto poloÅžku a vymazaÅĨ tie ostatnÊ poloÅžky", "failed_to_load_asset": "Nepodarilo sa načítaÅĨ poloÅžku", "failed_to_load_assets": "Nepodarilo sa načítaÅĨ poloÅžky", + "failed_to_load_notifications": "Nepodarilo sa načítaÅĨ oznÃĄmenia", "failed_to_load_people": "Nepodarilo sa načítaÅĨ Äžudí", "failed_to_remove_product_key": "Nepodarilo sa odstrÃĄniÅĨ produktovÃŊ kÄžÃēč", + "failed_to_reset_pin_code": "PIN kÃŗd sa nepodarilo obnoviÅĨ", "failed_to_stack_assets": "Nepodarilo sa zoskupiÅĨ poloÅžky", "failed_to_unstack_assets": "Nepodarilo sa rozdeliÅĨ poloÅžky", + "failed_to_update_notification_status": "Nepodarilo sa aktualizovaÅĨ stav oznÃĄmenia", "import_path_already_exists": "TÃĄto cesta importu uÅž existuje.", "incorrect_email_or_password": "NesprÃĄvny e-mail alebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta zlyhala} few {# cesty zlyhali} other {# ciest zlyhalo}} pri validÃĄcii", "profile_picture_transparent_pixels": "ProfilovÊ obrÃĄzky nemôŞu maÅĨ priehÄžadnÊ pixely. Prosím priblíŞte a/alebo posuňte obrÃĄzok.", "quota_higher_than_disk_size": "Nastavili ste kvÃŗtu vyÅĄÅĄiu ako je veÄžkosÅĨ disku", + "something_went_wrong": "Niečo sa pokazilo", "unable_to_add_album_users": "Nie je moÅžnÊ pridaÅĨ pouŞívateÄžov do albumu", "unable_to_add_assets_to_shared_link": "Nie je moÅžnÊ pridaÅĨ poloÅžky k zdieÄžanÊmu odkazu", "unable_to_add_comment": "Nie je moÅžnÊ pridaÅĨ komentÃĄr", @@ -826,14 +965,14 @@ "unable_to_change_favorite": "Nie je moÅžnÊ zmeniÅĨ obÄžÃēbenÊ pre poloÅžku", "unable_to_change_location": "Nie je moÅžnÊ zmeniÅĨ polohu", "unable_to_change_password": "Nie je moÅžnÊ zmeniÅĨ heslo", - "unable_to_change_visibility": "Nie je moÅžnÊ zmeniÅĨ viditeÄžnosÅĨ pre {count, plural, one {# osobu} other {# Äžudí}}", + "unable_to_change_visibility": "Nie je moÅžnÊ zmeniÅĨ viditeÄžnosÅĨ pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "unable_to_complete_oauth_login": "NemoÅžno dokončiÅĨ prihlÃĄsenie cez OAuth", "unable_to_connect": "Nie je moÅžnÊ sa pripojiÅĨ", "unable_to_copy_to_clipboard": "Nie je moÅžnÊ kopírovaÅĨ do schrÃĄnky, overte si, Åže strÃĄnku navÅĄtevujete cez https", - "unable_to_create_admin_account": "Nie je moÅžnÊ vytvoriÅĨ admin Ãēčet", + "unable_to_create_admin_account": "Nie je moÅžnÊ vytvoriÅĨ Ãēčet sprÃĄvcu", "unable_to_create_api_key": "Nie je moÅžnÊ vytvoriÅĨ novÃŊ API KlÃēč", "unable_to_create_library": "Nie je moÅžnÊ vytvoriÅĨ knihovňu", - "unable_to_create_user": "Nie je moÅžnÊ vytvoriÅĨ uÅživateÄža", + "unable_to_create_user": "Nie je moÅžnÊ vytvoriÅĨ pouŞívateÄža", "unable_to_delete_album": "Nie je moÅžnÊ vymazaÅĨ album", "unable_to_delete_asset": "Nie je moÅžnÊ vymazaÅĨ poloÅžku", "unable_to_delete_assets": "Chyba pri odstraňovaní poloÅžiek", @@ -842,7 +981,7 @@ "unable_to_delete_shared_link": "Nie je moÅžnÊ vymazaÅĨ zdieÄžanÃŊ odkaz", "unable_to_delete_user": "Nie je moÅžnÊ vymazaÅĨ pouŞívateÄža", "unable_to_download_files": "Nie je moÅžnÊ stiahnuÅĨ sÃēbory", - "unable_to_edit_exclusion_pattern": "Nie je moÅžnÊ upravit vzorec vylÃēčenia", + "unable_to_edit_exclusion_pattern": "Nie je moÅžnÊ upraviÅĨ vzorec vylÃēčenia", "unable_to_edit_import_path": "Nie je moÅžnÊ upraviÅĨ cestu importu", "unable_to_empty_trash": "Nie je moÅžnÊ vyprÃĄzdniÅĨ kÃ´ÅĄ", "unable_to_enter_fullscreen": "Nie je moÅžnÊ prejsÅĨ do reÅžimu celej obrazovky", @@ -865,7 +1004,7 @@ "unable_to_remove_library": "Nie je moÅžnÊ odstrÃĄniÅĨ kniÅžnicu", "unable_to_remove_partner": "Nie je moÅžnÊ odstrÃĄniÅĨ partnera", "unable_to_remove_reaction": "Nie je moÅžnÊ odstrÃĄniÅĨ reakciu", - "unable_to_reset_password": "Nie je moÅžnÊ resetovaÅĨ heslo", + "unable_to_reset_password": "Nie je moÅžnÊ obnoviÅĨ heslo", "unable_to_reset_pin_code": "Nie je moÅžnÊ obnoviÅĨ PIN kÃŗd", "unable_to_resolve_duplicate": "Nie je moÅžnÊ vyrieÅĄiÅĨ duplikÃĄt", "unable_to_restore_assets": "Nie je moÅžnÊ obnoviÅĨ poloÅžky", @@ -896,59 +1035,73 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "PridaÅĨ popis...", + "exif_bottom_sheet_description_error": "Chyba pri aktualizÃĄcii popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ÄŊUDIA", "exif_bottom_sheet_person_add_person": "PridaÅĨ meno", - "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", - "exif_bottom_sheet_person_age_years": "Vek {years}", - "exit_slideshow": "OpustiÅĨ Slideshow", + "exit_slideshow": "OpustiÅĨ prezentÃĄciu", "expand_all": "RozbaliÅĨ vÅĄetko", "experimental_settings_new_asset_list_subtitle": "PrebiehajÃēca prÃĄca", "experimental_settings_new_asset_list_title": "Povolenie experimentÃĄlnej mrieÅžky fotografií", "experimental_settings_subtitle": "PouŞívajte na vlastnÊ riziko!", "experimental_settings_title": "ExperimentÃĄlne", - "expire_after": "Expiruje po", + "expire_after": "PlatnosÅĨ vyprÅĄÃ­", "expired": "VyprÅĄalo", "expires_date": "Expiruje {date}", "explore": "PreskÃēmaÅĨ", "explorer": "Prieskumník", "export": "ExportovaÅĨ", "export_as_json": "ExportovaÅĨ do JSON", - "extension": "RozÅĄÃ­renie", + "export_database": "ExportovaÅĨ databÃĄzu", + "export_database_description": "ExportovaÅĨ databÃĄzu SQLite", + "extension": "Prípona", "external": "ExternÃŊ", "external_libraries": "ExternÊ kniÅžnice", "external_network": "ExternÃĄ sieÅĨ", "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikÃĄcia sa pripojí k serveru prostredníctvom prvej z niÅžÅĄie uvedenÃŊch adries URL, na ktorÃē sa dostane, počnÃēc zhora nadol", "face_unassigned": "NepriradenÃĄ", "failed": "NeÃēspeÅĄnÊ", + "failed_to_authenticate": "Nepodarilo sa overiÅĨ", "failed_to_load_assets": "Nepodarilo sa načítaÅĨ poloÅžky", + "failed_to_load_folder": "Nepodarilo sa načítaÅĨ priečinok", "favorite": "ObÄžÃēbenÊ", + "favorite_action_prompt": "{count} pridanÊ do obÄžÃēbenÃŊch", "favorite_or_unfavorite_photo": "OznačiÅĨ fotku ako obÄžÃēbenÃē alebo neobÄžÃēbenÃē", "favorites": "ObÄžÃēbenÊ", "favorites_page_no_favorites": "ÅŊiadne obÄžÃēbenÊ mÊdiÃĄ", "feature_photo_updated": "HlavnÃŊ obrÃĄzok bol aktualizovanÃŊ", "features": "Funkcie", + "features_in_development": "Funkcie vo vÃŊvoji", "features_setting_description": "SpravovaÅĨ funkcie aplikÃĄcie", - "file_name": "Meno sÃēboru", + "file_name": "NÃĄzov sÃēboru", "file_name_or_extension": "NÃĄzov alebo prípona sÃēboru", - "filename": "Meno sÃēboru", + "filename": "NÃĄzov sÃēboru", "filetype": "Typ sÃēboru", "filter": "Filter", "filter_people": "FiltrovaÅĨ Äžudí", "filter_places": "FiltrovaÅĨ miesta", "find_them_fast": "NÃĄjdite ich rÃŊchlejÅĄie podÄža mena", + "first": "PrvÊ", "fix_incorrect_match": "OpraviÅĨ nesprÃĄvnu zhodu", "folder": "Priečinok", + "folder_not_found": "Priečinok nebol nÃĄjdenÃŊ", "folders": "Priečinky", - "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na sÃēborovom systÊme", + "folders_feature_description": "Prezeranie zobrazenia priečinkov fotografií a videí v systÊme sÃēborov", + "forgot_pin_code_question": "Zabudli ste svoj PIN kÃŗd?", "forward": "Dopredu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "TÃĄto funkcia načítava externÊ zdroje zo spoločnosti Google, aby mohla fungovaÅĨ.", "general": "VÅĄeobecnÊ", + "geolocation_instruction_location": "Kliknite na poloÅžku s GPS sÃēradnicami, aby ste pouÅžili jej polohu, alebo vyberte polohu priamo z mapy", "get_help": "ZískaÅĨ pomoc", + "get_wifiname_error": "Nepodarilo sa získaÅĨ nÃĄzov Wi-Fi siete. Uistite sa, Åže ste udelili potrebnÊ oprÃĄvnenia a ste pripojení k sieti Wi-Fi", "getting_started": "Začíname", "go_back": "VrÃĄtiÅĨ sa späÅĨ", "go_to_folder": "PrejsÅĨ do priečinka", "go_to_search": "PrejsÅĨ na vyhÄžadÃĄvanie", + "gps": "GPS", + "gps_missing": "ÅŊiadne GPS", "grant_permission": "UdeliÅĨ povolenie", "group_albums_by": "ZoskupiÅĨ albumy podÄža...", "group_country": "Zoskupenie podÄža krajiny", @@ -959,6 +1112,15 @@ "haptic_feedback_switch": "PovoliÅĨ hmatovÃē odozvu", "haptic_feedback_title": "HmatovÃĄ odozva", "has_quota": "MÃĄ kvÃŗtu", + "hash_asset": "HashovaÅĨ poloÅžku", + "hashed_assets": "HashovanÊ poloÅžky", + "hashing": "Hashovanie", + "header_settings_add_header_tip": "PridaÅĨ hlavičku", + "header_settings_field_validator_msg": "Hodnota nemôŞe byÅĨ prÃĄzdna", + "header_settings_header_name_input": "NÃĄzov hlavičky", + "header_settings_header_value_input": "Hodnota hlavičky", + "headers_settings_tile_subtitle": "Určite hlavičky proxy servera, ktorÊ mÃĄ aplikÃĄcia posielaÅĨ s kaÅždou poÅžiadavkou na sieÅĨ", + "headers_settings_tile_title": "VlastnÊ hlavičky proxy servera", "hi_user": "Ahoj {name} ({email})", "hide_all_people": "SkryÅĨ vÅĄetky osoby", "hide_gallery": "SkryÅĨ galÊriu", @@ -974,25 +1136,33 @@ "home_page_archive_err_partner": "NemoÅžno archivovaÅĨ partnerskÊ poloÅžky, preskakuje sa", "home_page_building_timeline": "VytvÃĄranie časovej osi", "home_page_delete_err_partner": "Nie je moÅžnÊ vymazaÅĨ poloÅžky partnera, preskakuje sa", + "home_page_delete_remote_err_local": "Miestne poloÅžky vo vÃŊbere vzdialenÊho odstrÃĄnenia, preskakuje sa", "home_page_favorite_err_local": "ZatiaÄž nie je moÅžnÊ zaradiÅĨ lokÃĄlne mÊdia medzi obÄžÃēbenÊ, preskakuje sa", "home_page_favorite_err_partner": "Na teraz nemôŞete pridaÅĨ partnerove mÊdiÃĄ medzi obÄžÃēbenÊ", "home_page_first_time_notice": "Ak aplikÃĄciu pouŞívate prvÃŊkrÃĄt, uistite sa, Åže ste si vybrali zÃĄloÅžnÃŊ album, aby sa na časovej osi mohli zobrazovaÅĨ fotografie a videÃĄ", + "home_page_locked_error_local": "Nie je moÅžnÊ presunÃēÅĨ miestne poloÅžky do zamknutÊho priečinka, preskakuje sa", + "home_page_locked_error_partner": "Nie je moÅžnÊ presunÃēÅĨ partnerskÊ poloÅžky do zamknutÊho priečinka, preskakuje sa", "home_page_share_err_local": "NemoÅžno zdieÄžaÅĨ lokÃĄlne mÊdiÃĄ pomocou odkazu", "home_page_upload_err_limit": "Naraz môŞete nahraÅĨ len 30 mÊdií, preskakuje sa", "host": "HostiteÄž", "hour": "Hodina", + "hours": "Hodín", "id": "ID", + "idle": "NečinnÊ", + "ignore_icloud_photos": "IgnorovaÅĨ fotky v sluÅžbe iCloud", + "ignore_icloud_photos_description": "Fotografie uloÅženÊ v sluÅžbe iCloud sa nebudÃē odosielaÅĨ na server Immich", "image": "ObrÃĄzok", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} nasnímanÊ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} nasnímanÊ s {person1} dňa {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} nasnímanÊ s {person1} a {person2} dňa {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} nasnímanÊ s {person1}, {person2} a {person3} dňa {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímanÊ s {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {ObrÃĄzok}} nasnímanÊ v {city}, {country} dňa {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {ObrÃĄzok}} zo dňa {date} v {city}, {country} s {person1}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {ObrÃĄzok}} v {city}, {country} s {person1} a {person2} zo dňa {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {ObrÃĄzok}} zo dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímanÃŊ v {city}, {country} s {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video nasnímanÊ} other {ObrÃĄzok odfotenÃŊ}} s osobami {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video nasnímanÊ} other {ObrÃĄzok odfotenÃŊ}} v {city}, {country} dňa {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video nasnímanÊ} other {ObrÃĄzok odfotenÃŊ}} dňa {date} v {city}, {country} s {person1}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video nasnímanÊ} other {ObrÃĄzok odfotenÃŊ}} v {city}, {country} s {person1} a {person2} dňa {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video nasnímanÊ} other {ObrÃĄzok odfotenÃŊ}} dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video nasnímamÊ} other {ObrÃĄzok odfotenÃŊ}} v {city}, {country} s {person1}, {person2} a {additionalCount, number} inÃŊmi dňa {date}", + "image_saved_successfully": "ObrÃĄzok bol uloÅženÃŊ", "image_viewer_page_state_provider_download_started": "SÅĨahovanie sa začalo", "image_viewer_page_state_provider_download_success": "SÅĨahovanie bolo ÃēspeÅĄnÊ", "image_viewer_page_state_provider_share_error": "Chyba zdieÄžania", @@ -1015,21 +1185,34 @@ "night_at_twoam": "KaÅždÃē noc o 2:00" }, "invalid_date": "NeplatnÃŊ dÃĄtum", + "invalid_date_format": "NeplatnÃŊ formÃĄt dÃĄtumu", "invite_people": "PozvaÅĨ Äžudí", "invite_to_album": "PozvaÅĨ do albumu", + "ios_debug_info_fetch_ran_at": "Načítanie prebehlo {dateTime}", + "ios_debug_info_last_sync_at": "PoslednÃĄ synchronizÃĄcia {dateTime}", + "ios_debug_info_no_processes_queued": "ÅŊiadne procesy nie sÃē v poradí na pozadí", + "ios_debug_info_no_sync_yet": "ZatiaÄž nebola spustenÃĄ Åžiadna Ãēloha synchronizÃĄcie na pozadí", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí v poradí} few {{count} procesy na pozadí v poradí} other {{count} procesov na pozadí v poradí}}", + "ios_debug_info_processing_ran_at": "Spracovanie prebehlo {dateTime}", "items_count": "{count, plural, one {# poloÅžka} few {# poloÅžky} other {# poloÅžiek}}", "jobs": "Úlohy", "keep": "PonechaÅĨ", "keep_all": "PonechaÅĨ vÅĄetko", "keep_this_delete_others": "PonechaÅĨ toto, odstrÃĄniÅĨ ostatnÊ", - "kept_this_deleted_others": "PonechÃĄ tÃēto poloÅžku a odstrÃĄni {count, plural, one {# poloÅžku} other {# poloÅžiek}}", + "kept_this_deleted_others": "Ponechal tÃēto poloÅžku a odstrÃĄnil {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžiek}}", "keyboard_shortcuts": "KlÃĄvesovÊ skratky", "language": "Jazyk", - "language_setting_description": "Vyberte preferovanÃŊ jazyk", + "language_no_results_subtitle": "SkÃēste upraviÅĨ hÄžadanÃŊ vÃŊraz", + "language_no_results_title": "Neboli nÃĄjdenÊ Åžiadne jazyky", + "language_search_hint": "VyhÄžadaÅĨ jazyky...", + "language_setting_description": "Vyberte poÅžadovanÃŊ jazyk", + "large_files": "VeÄžkÊ sÃēbory", + "last": "PoslednÊ", "last_seen": "Naposledy videnÊ", "latest_version": "NajnovÅĄia verzia", "latitude": "ZemepisnÃĄ ÅĄÃ­rka", "leave": "OpustiÅĨ", + "leave_album": "OpustiÅĨ album", "lens_model": "Model objektívu", "let_others_respond": "Nechajte ostatnÃŊch reagovaÅĨ", "level": "Úroveň", @@ -1041,16 +1224,23 @@ "library_page_sort_created": "NajnovÅĄie vytvorenÊ", "library_page_sort_last_modified": "Naposledy upravenÊ", "library_page_sort_title": "PodÄža nÃĄzvu albumu", - "light": "SvetlÃŊ", + "licenses": "Licencie", + "light": "SvetlÃĄ", + "like": "PÃĄÄi sa mi", "like_deleted": "Like odstrÃĄnenÃŊ", "link_motion_video": "PripojiÅĨ pohyblivÊ video", - "link_options": "MoÅžnosti odkazu", "link_to_oauth": "PrepojiÅĨ s OAuth", "linked_oauth_account": "PripojenÃŊ OAuth Ãēčet", "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie vÃŊsledkov hÄžadania sa nepodarilo", + "local": "LokÃĄlne", + "local_asset_cast_failed": "Nie je moÅžnÊ preniesÅĨ mÊdium, ktorÊ nie je nahranÊ na serveri", + "local_assets": "LokÃĄlne poloÅžky", + "local_media_summary": "SÃēhrn lokÃĄlnych mÊdií", "local_network": "Miestna sieÅĨ", + "local_network_sheet_info": "Pri pouÅžití zadanej siete Wi-Fi sa aplikÃĄcia pripojí k serveru prostredníctvom tejto URL adresy", + "location_permission": "Povolenie na určenie polohy", "location_permission_content": "Na pouŞívanie funkcie automatickÊho prepínania potrebuje aplikÃĄcia Immich presnÊ povolenie na určenie polohy, aby mohla prečítaÅĨ nÃĄzov aktuÃĄlnej Wi-Fi siete", "location_picker_choose_on_map": "ZvoÄžte na mape", "location_picker_latitude_error": "Zadajte platnÃē zemepisnÃē ÅĄÃ­rku", @@ -1059,8 +1249,10 @@ "location_picker_longitude_hint": "Zadajte platnÃē zemepisnÃē dÄēÅžku", "lock": "ZamknÃēÅĨ", "locked_folder": "ZamknutÃŊ priečinok", + "log_detail_title": "Podrobnosti o zÃĄzname", "log_out": "OdhlÃĄsiÅĨ sa", "log_out_all_devices": "OdhlÃĄsiÅĨ vÅĄetky zariadenia", + "logged_in_as": "PrihlÃĄsenÃŊ ako {user}", "logged_out_all_devices": "VÅĄetky zariadenia odhlÃĄsenÊ", "logged_out_device": "Zariadenie odhlÃĄsenÊ", "login": "PrihlÃĄsenie", @@ -1069,7 +1261,7 @@ "login_form_back_button_text": "SpäÅĨ", "login_form_email_hint": "tvojmail@email.com", "login_form_endpoint_hint": "http://ip-tvojho-servera:port", - "login_form_endpoint_url": "URL adresa servera", + "login_form_endpoint_url": "URL adresa koncovÊho bodu servera", "login_form_err_http": "Prosím, uveďte http:// alebo https://", "login_form_err_invalid_email": "NeplatnÃŊ e-mail", "login_form_err_invalid_url": "NeplatnÃĄ URL adresa", @@ -1078,7 +1270,7 @@ "login_form_failed_get_oauth_server_config": "Chyba prihlÃĄsenia pomocou OAuth, skontrolujte adresu URL servera", "login_form_failed_get_oauth_server_disable": "Funkcia OAuth nie je na tomto serveri dostupnÃĄ", "login_form_failed_login": "Chyba prihlÃĄsenia, skontrolujte url adresu servera, e-mail a heslo", - "login_form_handshake_exception": "Nastala chyba handshake. Zapnite podoporu samo-podpísanÃŊch certifikÃĄtov v nastaveniach ak pouŞívate samo-podpísanÊ certifikÃĄty.", + "login_form_handshake_exception": "DoÅĄlo k vÃŊnimke Handshake so serverom. Ak pouŞívate certifikÃĄt s vlastnÃŊm podpisom, povoÄžte v nastaveniach podporu certifikÃĄtov s vlastnÃŊm podpisom.", "login_form_password_hint": "heslo", "login_form_save_login": "ZostaÅĨ prihlÃĄsenÃŊ", "login_form_server_empty": "Zadajte URL adresu servera.", @@ -1088,13 +1280,15 @@ "login_password_changed_success": "AktualizÃĄcia hesla prebehla ÃēspeÅĄne", "logout_all_device_confirmation": "Ste si istÃŊ, Åže sa chcete odhlÃĄsiÅĨ zo vÅĄetkÃŊch zariadení?", "logout_this_device_confirmation": "Ste si istÃŊ, Åže sa chcete odhlÃĄsiÅĨ z tohoto zariadenia?", + "logs": "ZÃĄznamy", "longitude": "ZemepisnÃĄ dÄēÅžka", - "look": "Zobrazenie", + "look": "VzhÄžad", "loop_videos": "OpakovaÅĨ videÃĄ", "loop_videos_description": "Povolí prehrÃĄvanie videí v slučke v detailnom zobrazení.", "main_branch_warning": "PouŞívate vÃŊvojÃĄrsku verziu; dôrazne odporÃēčame pouŞívaÅĨ vydanÊ verzie!", "main_menu": "HlavnÃĄ ponuka", "make": "VÃŊrobca", + "manage_geolocation": "SpravovaÅĨ polohu", "manage_shared_links": "SpravovaÅĨ zdieÄžanÊ odkazy", "manage_sharing_with_partners": "SpravovaÅĨ zdieÄžanie s partnermi", "manage_the_app_settings": "SpravovaÅĨ nastavenia aplikÃĄcie", @@ -1103,8 +1297,7 @@ "manage_your_devices": "SpravovaÅĨ vaÅĄe prihlÃĄsenÊ zariadenia", "manage_your_oauth_connection": "SpravovaÅĨ vaÅĄe OAuth spojenia", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotiek", + "map_assets_in_bounds": "{count, plural, =0 {ÅŊiadne fotky v tejto sekcii} one {# fotka} few {# fotky} other {# fotiek}}", "map_cannot_get_user_location": "Nie je moÅžnÊ získaÅĨ polohu pouŞívateÄža", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "PouÅžiÅĨ tÃēto polohu", @@ -1112,7 +1305,6 @@ "map_location_service_disabled_title": "SluÅžba určovania polohy vypnutÃĄ", "map_marker_for_images": "Značka na mape pre obrÃĄzky odfotenÊ v {city}, {country}", "map_marker_with_image": "MapovÃĄ značka pre obrÃĄzok", - "map_no_assets_in_bounds": "Nič tu nie je", "map_no_location_permission_content": "Na zobrazenie poloÅžiek z vaÅĄej aktuÃĄlnej polohy je potrebnÊ povolenie na polohu. Chcete to teraz povoliÅĨ?", "map_no_location_permission_title": "Povolenie polohy zamietnutÊ", "map_settings": "Nastavenia mÃĄp", @@ -1127,7 +1319,11 @@ "map_settings_only_show_favorites": "ZobraziÅĨ iba obÄžÃēbenÊ", "map_settings_theme_settings": "TÊma mapy", "map_zoom_to_see_photos": "OddiaÄžte priblíŞenie aby ste videli fotky", + "mark_all_as_read": "OznačiÅĨ vÅĄetko ako prečítanÊ", + "mark_as_read": "OznačiÅĨ ako prečítanÊ", + "marked_all_as_read": "OznačenÊ vÅĄetko ako prečítanÊ", "matches": "Zhody", + "matching_assets": "VyhovujÃēce poloÅžky", "media_type": "Typ mÊdia", "memories": "Spomienky", "memories_all_caught_up": "Na dnes to je vÅĄetko", @@ -1143,9 +1339,10 @@ "merge_people_limit": "ZlÃēčiÅĨ môŞete naraz najviac 5 tvÃĄrí", "merge_people_prompt": "Chcete zlÃēčiÅĨ tÃŊchto Äžudí? TÃĄto akcia sa nedÃĄ vrÃĄtiÅĨ.", "merge_people_successfully": "ZlÃēčenie Äžudí sa podarilo", - "merged_people_count": "ZlÃēčení {count, plural, one {# človek} other {# Äžudia}}", + "merged_people_count": "{count, plural, one {ZlÃēčenÃĄ # osoba} few {ZlÃēčenÊ # osoby} other {ZlÃēčenÃŊch # osôb}}", "minimize": "MinimalizovaÅĨ", "minute": "MinÃēta", + "minutes": "MinÃēt", "missing": "ChÃŊbajÃēce", "model": "Model", "month": "Mesiac", @@ -1153,15 +1350,24 @@ "more": "Viac", "move": "PresunÃēÅĨ", "move_off_locked_folder": "PresunÃēÅĨ zo zamknutÊho priečinka", + "move_to_lock_folder_action_prompt": "{count} pridanÃŊch do zamknutÊho priečinka", "move_to_locked_folder": "PresunÃēÅĨ do zamknutÊho priečinka", - "move_to_locked_folder_confirmation": "Tieto fotografie a videÃĄ budÃē odstrÃĄnenÊ zo vÅĄetkÃŊch albumov a bude ich moÅžnÊ zobraziÅĨ len v zamknutom priečinku", + "move_to_locked_folder_confirmation": "Tieto fotografie a videÃĄ budÃē odobranÊ zo vÅĄetkÃŊch albumov a bude ich moÅžnÊ zobraziÅĨ len v zamknutom priečinku", + "moved_to_archive": "{count, plural, one {PresunutÃĄ # poloÅžka} few {PresunutÊ # poloÅžky} other {PresunutÃŊch # poloÅžiek}} do archívu", + "moved_to_library": "{count, plural, one {PresunutÃĄ # poloÅžka} few {PresunutÊ # poloÅžky} other {PresunutÃŊch # poloÅžiek}} do kniÅžnice", "moved_to_trash": "PresunutÊ do koÅĄa", "multiselect_grid_edit_date_time_err_read_only": "NemoÅžno upraviÅĨ dÃĄtum poloÅžky len na čítanie, preskakujem", - "multiselect_grid_edit_gps_err_read_only": "Nie je moÅžnÊ upraviÅĨ polohu poloÅžky (poloÅžiek) len na čítanie, preskakuje sa", + "multiselect_grid_edit_gps_err_read_only": "Nie je moÅžnÊ upraviÅĨ polohu poloÅžky (poloÅžiek), ktorÃĄ je len na čítanie, preskakuje sa", "mute_memories": "Vyblednutie spomienok", "my_albums": "Moje albumy", "name": "Meno", "name_or_nickname": "Meno alebo prezÃŊvka", + "network_requirement_photos_upload": "PouÅžiÅĨ mobilnÊ dÃĄta na zÃĄlohovanie fotografií", + "network_requirement_videos_upload": "PouÅžiÅĨ mobilnÊ dÃĄta na zÃĄlohovanie videí", + "network_requirements": "PoÅžiadavky na sieÅĨ", + "network_requirements_updated": "PoÅžiadavky na sieÅĨ sa zmenili, obnovuje sa poradie zÃĄlohovania", + "networking_settings": "SieÅĨ", + "networking_subtitle": "SpravovaÅĨ nastavenia koncovÊho bodu servera", "never": "nikdy", "new_album": "NovÃŊ album", "new_api_key": "NovÃŊ API kÄžÃēč", @@ -1169,35 +1375,47 @@ "new_person": "NovÃĄ osoba", "new_pin_code": "NovÃŊ PIN kÃŗd", "new_pin_code_subtitle": "Toto je vÃĄÅĄ prvÃŊ prístup k zamknutÊmu priečinku. Vytvorte si PIN kÃŗd na bezpečnÃŊ prístup k tejto strÃĄnke", + "new_timeline": "NovÃĄ časovÃĄ os", "new_user_created": "NovÃŊ pouŞívateÄž vytvorenÃŊ", "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", - "newest_first": "NajnovÅĄie prvÊ", + "newest_first": "Najprv najnovÅĄie", "next": "Ďalej", "next_memory": "ĎalÅĄia spomienka", "no": "Nie", "no_albums_message": "Vytvorí album na organizovanie fotiek a videí", "no_albums_with_name_yet": "VyzerÃĄ, Åže zatiaÄž nemÃĄte album s tÃŊmto nÃĄzvom.", "no_albums_yet": "VyzerÃĄ, Åže zatiaÄž nemÃĄte Åžiadne albumy.", - "no_archived_assets_message": "ArchivovaÅĨ fotografie a videÃĄ, aby sa skryli zo zobrazenia Fotografie", + "no_archived_assets_message": "Archivujte fotografie a videÃĄ a skryte ich z vÃĄÅĄho zobrazenia fotografií", "no_assets_message": "KLIKNITE A NAHRAJTE SVOJU PRVÚ FOTKU", "no_assets_to_show": "ÅŊiadne poloÅžky", + "no_cast_devices_found": "NenaÅĄli sa Åžiadne zariadenia na prenos", + "no_checksum_local": "Kontrola sÃēčtu nie je k dispozícii – nie je moÅžnÊ načítaÅĨ lokÃĄlne poloÅžky", + "no_checksum_remote": "Kontrola sÃēčtu nie je k dispozícii – nie je moÅžnÊ načítaÅĨ vzdialenÊ poloÅžky", "no_duplicates_found": "NenaÅĄli sa Åžiadne duplicity.", "no_exif_info_available": "Nie sÃē dostupnÊ exif Ãēdaje", "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vaÅĄej zbierky.", "no_favorites_message": "Pridajte si obÄžÃēbenÊ, aby ste rÃŊchlo naÅĄli svoje najlepÅĄie obrÃĄzky a videÃĄ", "no_libraries_message": "Vytvorí externÃē kniÅžnicu na prezeranie fotiek a videí", + "no_local_assets_found": "Neboli nÃĄjdenÊ Åžiadne lokÃĄlne poloÅžky s touto kontrolnou sumou", "no_locked_photos_message": "Fotografie a videÃĄ v zamknutom priečinku sÃē skrytÊ a nezobrazujÃē sa pri prehÄžadÃĄvaní alebo vyhÄžadÃĄvaní v kniÅžnici.", "no_name": "Bez mena", + "no_notifications": "ÅŊiadne oznÃĄmenia", + "no_people_found": "NenaÅĄli sa Åžiadni vyhovujÃēci Äžudia", "no_places": "Bez miesta", + "no_remote_assets_found": "Neboli nÃĄjdenÊ Åžiadne vzdialenÊ poloÅžky s touto kontrolnou sumou", "no_results": "ÅŊiadne vÃŊsledky", "no_results_description": "SkÃēste synonymum alebo vÅĄeobecnejÅĄÃ­ vÃŊraz", "no_shared_albums_message": "Vytvorí album na zdieÄžanie fotiek a videí s Äžuďmi vo vaÅĄej sieti", + "no_uploads_in_progress": "ÅŊiadne prebiehajÃēce nahrÃĄvanie", + "not_available": "NedostupnÊ", "not_in_any_album": "Nie je v Åžiadnom albume", + "not_selected": "NevybranÊ", "note_apply_storage_label_to_previously_uploaded assets": "PoznÃĄmka: Ak chcete pouÅžiÅĨ Å títok ÃēloÅžiska na predtÃŊm nahranÊ mÊdiÃĄ, spustite príkaz", "notes": "PoznÃĄmky", + "nothing_here_yet": "ZatiaÄž tu nič nie je", "notification_permission_dialog_content": "Ak chcete povoliÅĨ upozornenia, prejdite do Nastavenia a vyberte moÅžnosÅĨ PovoliÅĨ.", - "notification_permission_list_tile_content": "UdeÄžte oprÃĄvnenie k aktivÃĄcii oznÃĄmení.", - "notification_permission_list_tile_enable_button": "PovoliÅĨ upozornenia", + "notification_permission_list_tile_content": "UdeÄžte povolenie na zapnutie oznÃĄmení.", + "notification_permission_list_tile_enable_button": "PovoliÅĨ oznÃĄmenia", "notification_permission_list_tile_title": "Povolenie oznÃĄmení", "notification_toggle_setting_description": "PovoliÅĨ e-mailovÊ upozornenia", "notifications": "OznÃĄmenia", @@ -1205,11 +1423,14 @@ "oauth": "OAuth", "official_immich_resources": "OficiÃĄlne Immich zdroje", "offline": "Offline", + "offset": "Posun", "ok": "OK", - "oldest_first": "NajstarÅĄie prvÊ", + "oldest_first": "Najprv najstarÅĄie", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", + "onboarding_locale_description": "Vyberte poÅžadovanÃŊ jazyk. Neskôr ho môŞete zmeniÅĨ v nastaveniach.", "onboarding_privacy_description": "NasledujÃēce (voliteÄžnÊ) funkcie zÃĄvisia na externÃŊch sluÅžbÃĄch a kedykoÄžvek ich môŞete vypnÃēÅĨ nastaveniach.", + "onboarding_server_welcome_description": "Poďme si nastaviÅĨ vaÅĄu inÅĄtanciu s niekoÄžkÃŊmi beÅžnÃŊmi nastaveniami.", "onboarding_theme_description": "Vyberte farbu tÊmy pre vÃĄÅĄ server. MôŞete to aj neskôr zmeniÅĨ vo vaÅĄich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1221,10 +1442,13 @@ "open_the_search_filters": "OtvoriÅĨ vyhÄžadÃĄvacie filtre", "options": "Nastavenia", "or": "alebo", + "organize_into_albums": "UsporiadaÅĨ do albumov", + "organize_into_albums_description": "VloÅžiÅĨ existujÃēce fotky do albumov podÄža aktuÃĄlnych nastavení synchronizÃĄcie", "organize_your_library": "Usporiadajte svoju kniÅžnicu", "original": "originÃĄl", "other": "OstatnÊ", "other_devices": "ĎalÅĄie zariadenia", + "other_entities": "OstatnÊ subjekty", "other_variables": "OstatnÊ premennÊ", "owned": "VlastnenÊ", "owner": "Vlastník", @@ -1235,7 +1459,7 @@ "partner_list_user_photos": "Fotky pouŞívateÄža {user}", "partner_list_view_all": "ZobraziÅĨ vÅĄetky", "partner_page_empty_message": "VaÅĄe fotky zatiaÄž nie sÃē zdieÄžanÊ so Åžiadnym partnerom.", - "partner_page_no_more_users": "ÅŊiadni ďalÅĄÃ­ uŞívatelia na zdieÄžanie", + "partner_page_no_more_users": "ÅŊiadni ďalÅĄÃ­ pouŞívatelia na pridanie", "partner_page_partner_add_failed": "PridÃĄvanie partnera zlyhalo", "partner_page_select_partner": "ZvoliÅĨ partnera", "partner_page_shared_to_title": "ZdieÄžanÊ pre", @@ -1247,9 +1471,9 @@ "password_required": "Heslo je povinnÊ", "password_reset_success": "Obnovenie hesla ÃēspeÅĄnÊ", "past_durations": { - "days": "{days, plural, one {PoslednÃŊ deň} other {PoslednÃŊch # dní }}", - "hours": "{hours, plural, one {PoslednÃĄ hodina} other {PoslednÃŊch # hodín}}", - "years": "{years, plural, one {PoslednÃŊ rok} other {PoslednÊ # roky}}" + "days": "{days, plural, one {PoslednÃŊ deň} few {PoslednÊ # dni} other {PoslednÃŊch # dní }}", + "hours": "{hours, plural, one {PoslednÃĄ hodina} few {PoslednÊ # hodiny} other {PoslednÃŊch # hodín}}", + "years": "{years, plural, one {PoslednÃŊ rok} few {PoslednÊ # roky} other {PoslednÃŊch # rokov}}" }, "path": "Cesta", "pattern": "Vzor", @@ -1258,17 +1482,18 @@ "paused": "PozastavenÊ", "pending": "ČakajÃēce", "people": "ÄŊudia", - "people_edits_count": "{count, plural, one {UpravenÃĄ # osoba} other {UpravenÃŊch # Äžudí}}", + "people_edits_count": "{count, plural, one {UpravenÃĄ # osoba} few {UpravenÊ # osoby} other {UpravenÃŊch # osôb}}", "people_feature_description": "Prehliadanie fotiek a videí zoskupenÃŊch podÄža Äžudí", - "people_sidebar_description": "Zobrazí odkaz na ÄŊudí v bočnom paneli", + "people_sidebar_description": "ZobraziÅĨ odkaz na ÄŊudí v bočnom paneli", "permanent_deletion_warning": "Varovanie o trvalom zmazaní", "permanent_deletion_warning_setting_description": "ZobraziÅĨ varovanie pri trvalom zmazaní poloÅžky", "permanently_delete": "Trvalo zmazaÅĨ", - "permanently_delete_assets_count": "NavÅždy zmazaÅĨ {count, plural, one {poloÅžku} other {poloÅžky}}", - "permanently_delete_assets_prompt": "Naozaj si prajete navÅždy zmazaÅĨ {count, plural, one {tÃēto poloÅžku?} other {tÃŊchto # poloÅžiek?}} VymaÅžÃē sa aj {count, plural, one {zo svojho albumu} other {zo svojich albumov}}.", + "permanently_delete_assets_count": "Natrvalo vymazaÅĨ {count, plural, one {poloÅžku} few {poloÅžky} other {poloÅžiek}}", + "permanently_delete_assets_prompt": "Ste si istí, Åže chcete natrvalo vymazaÅĨ {count, plural, one {tÃēto poloÅžku?} few {tieto # poloÅžky?} other {tÃŊchto # poloÅžiek?}} TÃŊmto sa odstrÃĄni aj {count, plural, one {z jej albumu} other {zo svojich albumov}}.", "permanently_deleted_asset": "NavÅždy odstrÃĄnenÃĄ poloÅžka", - "permanently_deleted_assets_count": "NavÅždy {count, plural, one {odstrÃĄnenÃĄ # poloÅžka} other {odstrÃĄnenÊ # poloÅžky}}", + "permanently_deleted_assets_count": "Natrvalo {count, plural, one {odstrÃĄnenÃĄ # poloÅžka} few {odstrÃĄnenÊ # poloÅžky} other {odstrÃĄnenÃŊch # poloÅžiek}}", "permission": "Povolenie", + "permission_empty": "VaÅĄe povolenie by nemalo byÅĨ prÃĄzdne", "permission_onboarding_back": "SpäÅĨ", "permission_onboarding_continue_anyway": "PokračovaÅĨ aj tak", "permission_onboarding_get_started": "ZačaÅĨ", @@ -1278,14 +1503,21 @@ "permission_onboarding_permission_limited": "Povolenie obmedzenÊ. Ak chcete, aby Immich zÃĄlohoval a spravoval celÃē vaÅĄu zbierku galÊrie, udeÄžte v Nastaveniach povolenia na fotografie a videÃĄ.", "permission_onboarding_request": "Immich vyÅžaduje povolenie na prezeranie vaÅĄich fotografií a videí.", "person": "Osoba", - "person_birthdate": "NarodenÃŊ dňa {date}", + "person_age_months": "mÃĄ {months, plural, one {# mesiac} few {# mesiace} other {# mesiacov}}", + "person_age_year_months": "mÃĄ 1 rok, {months, plural, one {# mesiac} few {# mesiace} other {# mesiacov}}", + "person_age_years": "mÃĄ {years, plural, one {# rok} few {# roky} other {# rokov}}", + "person_birthdate": "NarodenÃŊ/ÃĄ dňa {date}", "person_hidden": "{name}{hidden, select, true { (skrytÊ)} other {}}", "photo_shared_all_users": "VyzerÃĄ, Åže zdieÄžate svoje fotky so vÅĄetkÃŊmi pouŞívateÄžmi alebo nemÃĄte Åžiadnych pouŞívateÄžov.", "photos": "Fotografie", "photos_and_videos": "Fotografie & Videa", - "photos_count": "{count, plural, one {{count, number} Fotka} other {{count, number} Fotiek}}", + "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotiek}}", "photos_from_previous_years": "Fotky z minulÃŊch rokov", "pick_a_location": "Vyberte polohu", + "pin_code_changed_successfully": "ÚspeÅĄne ste zmenili PIN kÃŗd", + "pin_code_reset_successfully": "ÚspeÅĄne ste obnovili PIN kÃŗd", + "pin_code_setup_successfully": "ÚspeÅĄne ste nastavili PIN kÃŗd", + "pin_verification": "Overenie PIN kÃŗdom", "place": "Miesto", "places": "Miesta", "places_count": "{count, plural, one {{count, number} miesto} few {{count, number} miesta} other {{count, number} miest}}", @@ -1293,21 +1525,28 @@ "play_memories": "PrehraÅĨ spomienky", "play_motion_photo": "PrehraÅĨ pohyblivÃē fotku", "play_or_pause_video": "Pustí alebo pozastaví video", + "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_title": "Preferencie", - "preset": "Prednastavenie", + "preferences_settings_subtitle": "SpravovaÅĨ predvoÄžby aplikÃĄcie", + "preferences_settings_title": "PredvoÄžby", + "preparing": "Pripravuje sa", + "preset": "PredvoÄžba", "preview": "NÃĄhÄžad", "previous": "PredoÅĄlÊ", "previous_memory": "PredoÅĄlÃĄ spomienka", + "previous_or_next_day": "Deň dopredu/dozadu", + "previous_or_next_month": "Mesiac dopredu/dozadu", "previous_or_next_photo": "Fotka ďalÅĄia/predoÅĄlÃĄ", + "previous_or_next_year": "Rok dopredu/dozadu", "primary": "PrimÃĄrne", "privacy": "SÃēkromie", "profile": "Profil", - "profile_drawer_app_logs": "Logy", + "profile_drawer_app_logs": "ZÃĄznamy", "profile_drawer_client_out_of_date_major": "MobilnÃĄ aplikÃĄcia je zastaralÃĄ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_client_out_of_date_minor": "MobilnÃĄ aplikÃĄcia je zastaralÃĄ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_client_server_up_to_date": "Klient a server sÃē aktuÃĄlne", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "ReÅžim iba na čítanie je aktivovanÃŊ. DlhÃŊm stlačením ikony obrÃĄzku pouŞívateÄža reÅžim opustíte.", "profile_drawer_server_out_of_date_major": "Server je zastaralÃŊ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_drawer_server_out_of_date_minor": "Server je zastaralÃŊ. Prosím aktualizujte na najnovÅĄiu verziu.", "profile_image_of_user": "ProfilovÃŊ obrÃĄzok pouŞívateÄža {user}", @@ -1315,7 +1554,7 @@ "public_album": "VerejnÃŊ album", "public_share": "VerejnÊ zdieÄžanie", "purchase_account_info": "PodporovateÄž", - "purchase_activated_subtitle": "Ďakujeme za podporu Immich a softvÊru s otvorenÃŊmi zdrojÃĄkmi", + "purchase_activated_subtitle": "Ďakujeme vÃĄm za podporu aplikÃĄcie Immich a softvÊru s otvorenÃŊm zdrojovÃŊm kÃŗdom", "purchase_activated_time": "AktivovanÊ {date}", "purchase_activated_title": "VÃĄÅĄ kÄžÃēč je ÃēspeÅĄne aktivovanÃŊ", "purchase_button_activate": "AktivovaÅĨ", @@ -1327,14 +1566,14 @@ "purchase_button_select": "VybraÅĨ", "purchase_failed_activation": "AktivÃĄcia sa nepodarila! Prosím skontrolujte email či je sprÃĄvny kÄžÃēč produktu!", "purchase_individual_description_1": "Pre jednotlivca", - "purchase_individual_description_2": "Stav podporovateÄža", + "purchase_individual_description_2": "Å tatÃēt podporovateÄža", "purchase_individual_title": "Jednotlivec", "purchase_input_suggestion": "MÃĄte produktovÃŊ kÄžÃēč? Zadajte ho niÅžÅĄie", "purchase_license_subtitle": "KÃēpte si Immich a podporte neustÃĄly vÃŊvoj tejto sluÅžby", "purchase_lifetime_description": "DoÅživotnÃĄ platnosÅĨ", "purchase_option_title": "MOÅŊNOSTI NÁKUPU", - "purchase_panel_info_1": "VÃŊvoj Immich zaberÃĄ veÄža času a Ãēsilia, a mÃĄme zamestnanÃŊch fulltime inÅžinierov, aby ho spravili ako sa najlepÅĄie dÃĄ. NaÅĄa misia je, aby sa open-source softvÊr a etickÊ biznis praktiky stali udrÅžateÄžnÃŊm zdrojom príjmu pre vÃŊvojÃĄrov a vytvorili ekosystÊm reÅĄpektujÃēci sÃēkromie so skutočnÃŊmi nÃĄhradami voči zneuŞívajÃēcim cloudovÃŊm sluÅžbÃĄm.", - "purchase_panel_info_2": "KeďŞe sme zaviazaní nezavÃĄdzaÅĨ platenÊ verzie, nezískate tÃŊmto nÃĄkupom Åžiadne prídavnÊ funkcie. Spoliehame sa na pouŞívateÄžov, ako ste vy, Åže podporia neustÃĄly vÃŊvoj aplikÃĄcie Immich.", + "purchase_panel_info_1": "VÃŊvoj aplikÃĄcie Immich zaberÃĄ veÄža času a Ãēsilia, pričom na ňom pracujÃē inÅžinieri na plnÃŊ Ãēväzok, aby bol čo najlepÅĄÃ­. NaÅĄÃ­m poslaním je, aby sa softvÊr s otvorenÃŊm zdrojovÃŊm kÃŗdom a etickÊ obchodnÊ postupy stali udrÅžateÄžnÃŊm zdrojom príjmov pre vÃŊvojÃĄrov a aby sme vytvorili ekosystÊm reÅĄpektujÃēci sÃēkromie so skutočnÃŊmi alternatívami k zneuŞívajÃēcim cloudovÃŊm sluÅžbÃĄm.", + "purchase_panel_info_2": "KeďŞe sme zaviazaní nezavÃĄdzaÅĨ platenÊ verzie, nezískate tÃŊmto nÃĄkupom Åžiadne pridanÊ funkcie. Spoliehame sa na pouŞívateÄžov, ako ste vy, Åže podporia neustÃĄly vÃŊvoj aplikÃĄcie Immich.", "purchase_panel_title": "PodporiÅĨ projekt", "purchase_per_server": "Za server", "purchase_per_user": "Za pouŞívateÄža", @@ -1343,46 +1582,57 @@ "purchase_remove_server_product_key": "OdstrÃĄniÅĨ produktovÃŊ kÄžÃēč servera", "purchase_remove_server_product_key_prompt": "Naozaj chcete odstrÃĄniÅĨ produktovÃŊ kÄžÃēč servera?", "purchase_server_description_1": "Pre celÃŊ server", - "purchase_server_description_2": "Stav podporovateÄža", + "purchase_server_description_2": "Å tatÃēt podporovateÄža", "purchase_server_title": "Server", "purchase_settings_server_activated": "ProduktovÃŊ kÄžÃēč servera spravuje admin", + "query_asset_id": "ID poÅžiadavky poloÅžky", + "queue_status": "V poradí {count}/{total}", "rating": "Hodnotenie hviezdičkami", "rating_clear": "VyčistiÅĨ hodnotenie", - "rating_count": "{count, plural, one {# hviezdička} other {# hviezdičky}}", - "rating_description": "Zobrazí EXIF hodnotenie v info paneli", + "rating_count": "{count, plural, one {# hviezdička} few {# hviezdičky} other {# hviezdičiek}}", + "rating_description": "ZobraziÅĨ EXIF hodnotenie v informačnom paneli", "reaction_options": "MoÅžnosti reakcie", "read_changelog": "PrečítaÅĨ zoznam zmien", + "readonly_mode_disabled": "ReÅžim iba na čítanie je vypnutÃŊ", + "readonly_mode_enabled": "ReÅžim iba na čítanie je zapnutÃŊ", + "ready_for_upload": "PripravenÊ na nahratie", "reassign": "PreradiÅĨ", - "reassigned_assets_to_existing_person": "PreradenÊ {count, plural, one {# poloÅžka} other {# poloÅžky}} k {name, select, null {existujÃēcej osobe} other {{name}}}", - "reassigned_assets_to_new_person": "PreradenÊ {count, plural, one {# poloÅžka} other {# poloÅžiek}} novej osobe", + "reassigned_assets_to_existing_person": "Opätovne {count, plural, one {priradenÃĄ # poloÅžka} few {priradenÊ # poloÅžky} other {priradenÃŊch # poloÅžiek}} k {name, select, null {existujÃēcej osobe} other {{name}}}", + "reassigned_assets_to_new_person": "Opätovne {count, plural, one {priradenÃĄ # poloÅžka} few {priradenÊ # poloÅžky} other {priradenÃŊch # poloÅžiek}} novej osobe", "reassing_hint": "Priradí zvolenÃē poloÅžku k existujÃēcej osobe", "recent": "NedÃĄvne", "recent-albums": "PoslednÊ albumy", "recent_searches": "PoslednÊ vyhÄžadÃĄvania", + "recently_added": "NedÃĄvno pridanÊ", "recently_added_page_title": "NedÃĄvno pridanÊ", "recently_taken": "NedÃĄvno nasnímanÊ", "recently_taken_page_title": "NedÃĄvno zhotovenÊ", - "refresh": "ObnoviÅĨ", + "refresh": "AktualizovaÅĨ", "refresh_encoded_videos": "ObnoviÅĨ enkÃŗdovanÊ videÃĄ", "refresh_faces": "ObnoviÅĨ tvÃĄre", "refresh_metadata": "ObnoviÅĨ metadÃĄta", "refresh_thumbnails": "ObnoviÅĨ miniatÃēry", - "refreshed": "AktualizovanÊ", + "refreshed": "ObnovenÊ", "refreshes_every_file": "Znova prečíta vÅĄetky existujÃēce a novÊ sÃēbory", "refreshing_encoded_video": "Obnovovanie enkÃŗdovanÃŊch videí", - "refreshing_faces": "Obnovovnie tvÃĄrí", + "refreshing_faces": "Obnovovanie tvÃĄrí", "refreshing_metadata": "Obnovovanie metadÃĄt", "regenerating_thumbnails": "Pregenerovanie nÃĄhÄžadov", + "remote": "VzdialenÊ", + "remote_assets": "VzdialenÊ poloÅžky", + "remote_media_summary": "SÃēhrn vzdialenÃŊch mÊdií", "remove": "OdstrÃĄniÅĨ", - "remove_assets_album_confirmation": "Naozaj chcete odstrÃĄniÅĨ {count, plural, one {# poloÅžky} other {# poloÅžiek}} z albumu?", - "remove_assets_shared_link_confirmation": "Naozaj chcete odstrÃĄniÅĨ {count, plural, one {# poloÅžku} other {# poloÅžiek}} z tohoto zdieÄžanÊho odkazu?", + "remove_assets_album_confirmation": "Naozaj chcete odstrÃĄniÅĨ {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžiek}} z albumu?", + "remove_assets_shared_link_confirmation": "Naozaj chcete odstrÃĄniÅĨ {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžiek}} z tohoto zdieÄžanÊho odkazu?", "remove_assets_title": "OdstrÃĄniÅĨ poloÅžky?", "remove_custom_date_range": "OdstrÃĄniÅĨ vlastnÃŊ rozsah dÃĄtumov", "remove_deleted_assets": "OdstrÃĄniÅĨ vymazanÊ poloÅžky", "remove_from_album": "OdstrÃĄniÅĨ z albumu", + "remove_from_album_action_prompt": "{count} odstrÃĄnenÊ z albumu", "remove_from_favorites": "OdstrÃĄniÅĨ z obÄžÃēbenÃŊch", - "remove_from_locked_folder": "OdstrÃĄniÅĨ zo zamknutÊho priečinka", - "remove_from_locked_folder_confirmation": "Ste si istí, Åže chcete tieto fotografie a videÃĄ presunÃēÅĨ zo zamknutÊho priečinka? BudÃē viditeÄžnÊ vo vaÅĄej kniÅžnici.", + "remove_from_lock_folder_action_prompt": "{count} odobranÊ zo zamknutÊho priečinka", + "remove_from_locked_folder": "OdobraÅĨ zo zamknutÊho priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, Åže chcete tieto fotografie a videÃĄ odobraÅĨ zo zamknutÊho priečinka? BudÃē viditeÄžnÊ vo vaÅĄej kniÅžnici.", "remove_from_shared_link": "OdstrÃĄniÅĨ zo zdieÄžanÊho odkazu", "remove_memory": "OdstrÃĄniÅĨ spomienku", "remove_photo_from_memory": "OdstrÃĄniÅĨ fotografiu z tejto spomienky", @@ -1395,7 +1645,7 @@ "removed_from_favorites_count": "{count, plural, other {OdstrÃĄnenÃŊch #}} z obÄžÃēbenÃŊch", "removed_memory": "OdstrÃĄnenÃĄ pamäÅĨ", "removed_photo_from_memory": "Fotografia odstrÃĄnenÃĄ z pamäte", - "removed_tagged_assets": "OdstrÃĄnenÃĄ značka z {count, plural, one {# poloÅžky} other {# poloÅžiek}}", + "removed_tagged_assets": "OdstrÃĄnenÃŊ ÅĄtítok z {count, plural, one {# poloÅžky} other {# poloÅžiek}}", "rename": "PremenovaÅĨ", "repair": "OpraviÅĨ", "repair_no_results_message": "NesledovanÊ a chÃŊbajÃēce sÃēbory sa zobrazia tu", @@ -1404,27 +1654,38 @@ "require_password": "VyÅžadovaÅĨ heslo", "require_user_to_change_password_on_first_login": "VyÅžadovaÅĨ zmenu hesla po prvom prihlÃĄsení", "rescan": "OpätovnÊ vyhÄžadÃĄvanie", - "reset": "ResetovaÅĨ", + "reset": "ObnoviÅĨ", "reset_password": "ObnoviÅĨ heslo", - "reset_people_visibility": "ResetovaÅĨ viditeÄžnosÅĨ Äžudí", + "reset_people_visibility": "ObnoviÅĨ viditeÄžnosÅĨ Äžudí", "reset_pin_code": "ObnoviÅĨ PIN kÃŗd", - "reset_to_default": "ResetovaÅĨ na predvolenÊ", + "reset_pin_code_description": "Ak ste zabudli svoj PIN kÃŗd, môŞete kontaktovaÅĨ sprÃĄvcu servera, aby ho obnovil", + "reset_pin_code_success": "PIN kÃŗd bol ÃēspeÅĄne obnovenÃŊ", + "reset_pin_code_with_password": "Svoj PIN kÃŗd môŞete kedykoÄžvek obnoviÅĨ pomocou vÃĄÅĄho hesla", + "reset_sqlite": "ObnoviÅĨ SQLite databÃĄzu", + "reset_sqlite_confirmation": "Ste si istí, Åže chcete obnoviÅĨ SQLite databÃĄzu? Na opätovnÃē synchronizÃĄciu Ãēdajov sa budete musieÅĨ odhlÃĄsiÅĨ a znova prihlÃĄsiÅĨ", + "reset_sqlite_success": "ÚspeÅĄnÊ obnovenie databÃĄzy SQLite", + "reset_to_default": "ObnoviÅĨ na predvolenÊ", "resolve_duplicates": "VyrieÅĄiÅĨ duplicity", "resolved_all_duplicates": "VyrieÅĄenÊ vÅĄetky duplicity", "restore": "NavrÃĄtiÅĨ", - "restore_all": "NavrÃĄtit vÅĄetko", + "restore_all": "NavrÃĄtiÅĨ vÅĄetko", + "restore_trash_action_prompt": "{count} obnovenÃŊch z koÅĄa", "restore_user": "NavrÃĄtiÅĨ pouŞívateÄža", - "restored_asset": "NavrÃĄtenÊ poloÅžky", + "restored_asset": "NavrÃĄtenÃĄ poloÅžka", "resume": "PokračovaÅĨ", + "resume_paused_jobs": "PokračovaÅĨ v {count, plural, one {# pozastavenej Ãēlohe} other {# pozastavenÃŊch ÃēlohÃĄch}}", "retry_upload": "ZopakovaÅĨ nahrÃĄvanie", - "review_duplicates": "PrezrieÅĨ duplikÃĄty", + "review_duplicates": "PreskÃēmaÅĨ duplikÃĄty", + "review_large_files": "SkontrolovaÅĨ veÄžkÊ sÃēbory", "role": "Rola", "role_editor": "Editor", "role_viewer": "DivÃĄk", + "running": "SpustenÊ", "save": "UloÅžiÅĨ", + "save_to_gallery": "UloÅžiÅĨ do galÊrie", "saved_api_key": "UloÅženÃŊ API KÄžÃēč", "saved_profile": "UloÅženÃŊ profil", - "saved_settings": "UloÅženÊ nastavenia", + "saved_settings": "Nastavenia boli uloÅženÊ", "say_something": "NapÃ­ÅĄte niečo", "scaffold_body_error_occurred": "Vyskytla sa chyba", "scan_all_libraries": "PreskenovaÅĨ vÅĄetky kniÅžnice", @@ -1436,7 +1697,7 @@ "search_by_context": "HÄžadaÅĨ s kontextom", "search_by_description": "VyhÄžadÃĄvanie podÄža popisu", "search_by_description_example": "PeÅĄia turistika v Sape", - "search_by_filename": "HÄžadaÅĨ s nÃĄzvom alebo príponou sÃēboru", + "search_by_filename": "HÄžadaÅĨ podÄža nÃĄzvu alebo prípony sÃēboru", "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "HÄžadaÅĨ značku fotoaparÃĄtu...", "search_camera_model": "HÄžadaÅĨ model fotoaparÃĄtu...", @@ -1445,9 +1706,11 @@ "search_filter_apply": "PouÅžiÅĨ filter", "search_filter_camera_title": "Vyberte typ kamery", "search_filter_date": "DÃĄtum", + "search_filter_date_interval": "{start} do {end}", "search_filter_date_title": "Vyberte rozsah dÃĄtumov", "search_filter_display_option_not_in_album": "Mimo albumu", "search_filter_display_options": "MoÅžnosti zobrazenia", + "search_filter_filename": "HÄžadaÅĨ podÄža nÃĄzvu sÃēboru", "search_filter_location": "Poloha", "search_filter_location_title": "Vyberte polohu", "search_filter_media_type": "Typ mÊdia", @@ -1482,7 +1745,7 @@ "search_tags": "HÄžadaÅĨ ÅĄtítky...", "search_timezone": "HÄžadaÅĨ časovÃē zÃŗnu...", "search_type": "Typ hÄžadania", - "search_your_photos": "HÄžadajte svoje fotky", + "search_your_photos": "VyhÄžadÃĄvanie vo vaÅĄich fotografiÃĄch", "searching_locales": "HÄžadÃĄm lokality...", "second": "Sekundy", "see_all_people": "PozrieÅĨ vÅĄetky osoby", @@ -1490,6 +1753,7 @@ "select_album_cover": "Vyberte obal albumu", "select_all": "VybraÅĨ vÅĄetko", "select_all_duplicates": "VybraÅĨ vÅĄetky duplikÃĄty", + "select_all_in": "OznačiÅĨ vÅĄetky v {group}", "select_avatar_color": "Vyberte farbu avatara", "select_face": "Vyberte tvÃĄr", "select_featured_photo": "Vyberte nÃĄhÄžadovÃē fotku", @@ -1502,15 +1766,18 @@ "select_trash_all": "VybraÅĨ zahodiÅĨ vÅĄetky", "select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriÅĨ album", "selected": "VybranÊ", - "selected_count": "{count, plural, other {# vybranÊ}}", + "selected_count": "{count, plural, one {# vybranÃĄ} few {# vybranÊ} other {# vybranÃŊch}}", + "selected_gps_coordinates": "VybranÊ GPS sÃēradnice", "send_message": "OdoslaÅĨ sprÃĄvu", "send_welcome_email": "OdoslaÅĨ uvítací e-mail", + "server_endpoint": "KoncovÃŊ bod servera", "server_info_box_app_version": "Verzia aplikÃĄcie", - "server_info_box_server_url": "URL Serveru", + "server_info_box_server_url": "URL adresa servera", "server_offline": "Server je Offline", "server_online": "Server je Online", - "server_stats": "ServerovÊ Å tatistiky", - "server_version": "Verzia Servera", + "server_privacy": "ZÃĄsady ochrany osobnÃŊch Ãēdajov servera", + "server_stats": "Å tatistiky servera", + "server_version": "Verzia servera", "set": "NastaviÅĨ", "set_as_album_cover": "NastaviÅĨ ako obal albumu", "set_as_featured_photo": "NastaviÅĨ ako hlavnÃē fotku", @@ -1518,14 +1785,16 @@ "set_date_of_birth": "NastaviÅĨ dÃĄtum narodenia", "set_profile_picture": "NastaviÅĨ profilovÃŊ obrÃĄzok", "set_slideshow_to_fullscreen": "NastaviÅĨ prezentÃĄciu na celÃē obrazovku", - "setting_image_viewer_help": "Prehliadač detailov najprv načíta malÃē miniatÃēru, potom načíta nÃĄhÄžad strednej veÄžkosti (ak je povolenÃŊ) a nakoniec načíta originÃĄl (ak je povolenÃŊ).", + "set_stack_primary_asset": "NastaviÅĨ ako primÃĄrnu poloÅžku", + "setting_image_viewer_help": "V detailnom prehliadači sa najprv načíta malÃĄ miniatÃēra, potom sa načíta stredne veÄžkÃŊ nÃĄhÄžad (ak je povolenÃŊ) a nakoniec sa načíta originÃĄl (ak je povolenÃŊ).", "setting_image_viewer_original_subtitle": "Povolením umoÅžníte načítaÅĨ pôvodnÃŊ obrÃĄzok v plnom rozlÃ­ÅĄení (veÄžkÃŊ!). ZakÃĄzaním zníŞite pouŞívania dÃĄt (v sieti, aj v dočasnej pamäte zariadenia).", "setting_image_viewer_original_title": "NačítaÅĨ pôvodnÃŊ obrÃĄzok", "setting_image_viewer_preview_subtitle": "Povolením umoÅžníte načítaÅĨ obrÃĄzok so strednÃŊm rozlÃ­ÅĄením. ZakÃĄÅžte, ak chcete priamo načítaÅĨ originÃĄl alebo pouÅžiÅĨ iba miniatÃēru.", "setting_image_viewer_preview_title": "NačítaÅĨ nÃĄhÄžad obrÃĄzka", "setting_image_viewer_title": "ObrÃĄzky", "setting_languages_apply": "PouÅžiÅĨ", - "setting_notifications_notify_failures_grace_period": "OznÃĄmenie o zlyhaní zÃĄlohovania na pozadí: {duration}", + "setting_languages_subtitle": "ZmeniÅĨ jazyk aplikÃĄcie", + "setting_notifications_notify_failures_grace_period": "UpozorniÅĨ na zlyhanie zÃĄlohovania na pozadí: {duration}", "setting_notifications_notify_hours": "{count} hodín", "setting_notifications_notify_immediately": "okamÅžite", "setting_notifications_notify_minutes": "{count} minÃēt", @@ -1533,14 +1802,18 @@ "setting_notifications_notify_seconds": "{count} sekÃēnd", "setting_notifications_single_progress_subtitle": "PodrobnÊ informÃĄcie o priebehu nahrÃĄvania pre poloÅžku", "setting_notifications_single_progress_title": "ZobraziÅĨ priebeh detailov zÃĄlohovania na pozadí", - "setting_notifications_subtitle": "Prispôsobenie predvolieb oznÃĄmení", + "setting_notifications_subtitle": "Upravte svoje nastavenia oznÃĄmení", "setting_notifications_total_progress_subtitle": "CelkovÃŊ priebeh nahrÃĄvania (nahranÃŊch/celkovo)", "setting_notifications_total_progress_title": "ZobraziÅĨ celkovÃŊ priebeh zÃĄlohovania na pozadí", "setting_video_viewer_looping_title": "Opakovanie", + "setting_video_viewer_original_video_subtitle": "Pri streamovaní videa zo servera prehraÅĨ originÃĄl, aj keď je k dispozícii prekÃŗdovanÊ video. MôŞe to viesÅĨ k preruÅĄovanÊmu prehrÃĄvaniu videa. VideÃĄ dostupnÊ lokÃĄlne sa prehrajÃē v pôvodnej kvalite bez ohÄžadu na toto nastavenie.", + "setting_video_viewer_original_video_title": "VynÃētiÅĨ pôvodnÊ video", "settings": "Nastavenia", "settings_require_restart": "Na pouÅžitie tohto nastavenia reÅĄtartujte Immich", "settings_saved": "Nastavenia boli uloÅženÊ", + "setup_pin_code": "Nastavte si PIN kÃŗd", "share": "ZdieÄžaÅĨ", + "share_action_prompt": "{count} poloÅžiek zdieÄžanÃŊch", "share_add_photos": "PridaÅĨ fotografie", "share_assets_selected": "{count} označenÃŊch", "share_dialog_preparing": "Pripravujem...", @@ -1549,7 +1822,7 @@ "shared_album_activities_input_disable": "KomentÃĄr je zakÃĄzanÃŊ", "shared_album_activity_remove_content": "Chcete vymazaÅĨ tÃēto aktivitu?", "shared_album_activity_remove_title": "VymazaÅĨ aktivitu", - "shared_album_section_people_action_error": "Vyskytla sa chyba pri odchÃĄdzaní / odstraňovaní pouŞívateÄža z albumu", + "shared_album_section_people_action_error": "Vyskytla sa chyba pri opustení/odobratí z albumu", "shared_album_section_people_action_leave": "OdstrÃĄniÅĨ pouŞívateÄža z albumu", "shared_album_section_people_action_remove_user": "OdstrÃĄniÅĨ pouŞívateÄža z albumu", "shared_album_section_people_title": "ÄŊUDIA", @@ -1562,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "SkopírovanÊ do schrÃĄnky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Vyskytla sa chyba behom vytvÃĄrania zdieÄžanÊho odkazu", + "shared_link_custom_url_description": "Prístup k tomuto zdieÄžanÊmu odkazu pomocou vlastnej URL adresy", "shared_link_edit_description_hint": "Zadajte popis zdieÄžania", "shared_link_edit_expire_after_option_day": "1 deň", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1574,22 +1848,24 @@ "shared_link_edit_password_hint": "Zadajte heslo zdieÄžania", "shared_link_edit_submit_button": "AktualizovaÅĨ odkaz", "shared_link_error_server_url_fetch": "NemoÅžno nÃĄjsÅĨ URL severa", - "shared_link_expires_day": "VyprÅĄÃ­ o {count} dní", + "shared_link_expires_day": "VyprÅĄÃ­ o {count} deň", "shared_link_expires_days": "VyprÅĄÃ­ o {count} dní", - "shared_link_expires_hour": "VyprÅĄÃ­ o {count} hodín", + "shared_link_expires_hour": "VyprÅĄÃ­ o {count} hodinu", "shared_link_expires_hours": "VyprÅĄÃ­ o {count} hodín", - "shared_link_expires_minute": "VyprÅĄÃ­ o {count} minÃēt", + "shared_link_expires_minute": "VyprÅĄÃ­ o {count} minÃētu", "shared_link_expires_minutes": "VyprÅĄÃ­ o {count} minÃēt", "shared_link_expires_never": "NevyprÅĄÃ­", - "shared_link_expires_second": "VyprÅĄÃ­ o {count} sekÃēnd", + "shared_link_expires_second": "VyprÅĄÃ­ o {count} sekundu", "shared_link_expires_seconds": "VyprÅĄÃ­ o {count} sekÃēnd", "shared_link_individual_shared": "IndividuÃĄlne zdieÄžanÊ", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "SpravovaÅĨ zdieÄžanÊ odkazy", "shared_link_options": "MoÅžnosti zdieÄžanÃŊch odkazov", + "shared_link_password_description": "VyÅžadovaÅĨ heslo pre prístup k tomuto zdieÄžanÊmu odkazu", "shared_links": "ZdieÄžanÊ odkazy", "shared_links_description": "ZdieÄžanie fotografií a videí pomocou odkazu", - "shared_photos_and_videos_count": "{assetCount, plural, other {# zdieÄžanÊ fotky a videÃĄ.}}", + "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieÄžanÊ fotky a videÃĄ.} other {# zdieÄžanÃŊch fotiek a videí.}}", + "shared_with_me": "ZdieÄžanÊ so mnou", "shared_with_partner": "ZdieÄžanÊ s {partner}", "sharing": "ZdieÄžanie", "sharing_enter_password": "Ak chcete zobraziÅĨ tÃēto strÃĄnku, prosím, zadajte heslo.", @@ -1599,7 +1875,7 @@ "sharing_sidebar_description": "ZobraziÅĨ odkaz na ZdieÄžanie v bočnom paneli", "sharing_silver_appbar_create_shared_album": "VytvoriÅĨ zdieÄžanÃŊ album", "sharing_silver_appbar_share_partner": "ZdieÄžaÅĨ s partnerom", - "shift_to_permanent_delete": "stlačte ⇧ pre nemennÊ zmazanie ploÅžiek", + "shift_to_permanent_delete": "stlačte ⇧ na trvalÊ vymazanie poloÅžky", "show_album_options": "ZobraziÅĨ moÅžnosti albumu", "show_albums": "ZobraziÅĨ albumy", "show_all_people": "ZobraziÅĨ vÅĄetkÃŊch Äžudí", @@ -1608,21 +1884,22 @@ "show_gallery": "ZobraziÅĨ galÊriu", "show_hidden_people": "ZobraziÅĨ skrytÃŊch Äžudí", "show_in_timeline": "ZobraziÅĨ na časovej osi", - "show_in_timeline_setting_description": "Zobrazí fotky a videÃĄ tohoto pouŞívateÄža na časovej osi", + "show_in_timeline_setting_description": "ZobraziÅĨ fotky a videÃĄ tohoto pouŞívateÄža na vaÅĄej časovej osi", "show_keyboard_shortcuts": "ZobraziÅĨ klÃĄvesovÊ skratky", "show_metadata": "ZobraziÅĨ metadÃĄta", - "show_or_hide_info": "Zobrazí alebo skryje info", + "show_or_hide_info": "ZobraziÅĨ alebo skryÅĨ informÃĄcie", "show_password": "ZobraziÅĨ heslo", - "show_person_options": "Zobrazí moÅžnosti osoby", - "show_progress_bar": "Zobrazí ukazovateÄž priebehu", + "show_person_options": "ZobraziÅĨ moÅžnosti osoby", + "show_progress_bar": "ZobraziÅĨ ukazovateÄž priebehu", "show_search_options": "ZobraziÅĨ moÅžnosti vyhÄžadÃĄvania", "show_shared_links": "ZobraziÅĨ zdieÄžanÊ odkazy", - "show_slideshow_transition": "Zobrazí prechody v prezentÃĄcii", + "show_slideshow_transition": "ZobraziÅĨ prechody v prezentÃĄcii", "show_supporter_badge": "Odznak podporovateÄža", "show_supporter_badge_description": "ZobraziÅĨ odznak podporovateÄža", + "show_text_search_menu": "ZobraziÅĨ ponuku vyhÄžadÃĄvania textu", "shuffle": "NÃĄhodnÊ poradie", "sidebar": "BočnÃŊ panel", - "sidebar_display_description": "Zobrazí odkaz na pohÄžad v bočnom paneli", + "sidebar_display_description": "ZobraziÅĨ odkaz na zobrazenie v bočnom paneli", "sign_out": "OdhlÃĄsiÅĨ sa", "sign_up": "RegistrovaÅĨ", "size": "VeÄžkosÅĨ", @@ -1635,30 +1912,35 @@ "sort_created": "DÃĄtum vytvorenia", "sort_items": "Počet poloÅžiek", "sort_modified": "DÃĄtum Ãēpravy", + "sort_newest": "NajnovÅĄia fotka", "sort_oldest": "NajstarÅĄia fotografia", "sort_people_by_similarity": "ZoradiÅĨ Äžudí podÄža podobnosti", "sort_recent": "NajnovÅĄia fotografia", "sort_title": "NÃĄzov", "source": "Zdroj", "stack": "Zoskupenie", + "stack_action_prompt": "{count} zoskupenÃŊch", "stack_duplicates": "ZoskupiÅĨ duplicity", "stack_select_one_photo": "Vyberte jednu hlavnÃē fotku pre zoskupenie", "stack_selected_photos": "ZoskupiÅĨ vybratÊ fotky", - "stacked_assets_count": "{count, plural, one {ZoskupenÃĄ # poloÅžka} other {ZoskupenÃŊch # poloÅžiek}}", + "stacked_assets_count": "{count, plural, one {ZoskupenÃĄ # poloÅžka} few {ZoskupenÊ # poloÅžky} other {ZoskupenÃŊch # poloÅžiek}}", "stacktrace": "VÃŊpis zÃĄsobníku", - "start": "Å tart", - "start_date": "ZačiatočnÃŊ dÃĄtum", + "start": "SpustiÅĨ", + "start_date": "PočiatočnÃŊ dÃĄtum", + "start_date_before_end_date": "DÃĄtum začiatku musí byÅĨ pred dÃĄtumom ukončenia", "state": "Å tÃĄt", "status": "Stav", + "stop_casting": "ZastaviÅĨ prenos", "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "ZastaviÅĨ zdieÄžanie vaÅĄich fotiek?", "stop_photo_sharing_description": "{partner} uÅž nebude maÅĨ prístup k vaÅĄim fotkÃĄm.", - "stop_sharing_photos_with_user": "ZastaviÅĨ zdieÄžanie tÃŊchto fotiek s tÃŊmto pouŞívateÄžom", + "stop_sharing_photos_with_user": "ZastaviÅĨ zdieÄžanie vaÅĄich fotiek s tÃŊmto pouŞívateÄžom", "storage": "Ukladací priestor", "storage_label": "Å títok ÃēloÅžiska", "storage_quota": "ÚloÅžnÃŊ limit", "storage_usage": "VyuÅžitÃŊch {used} z {available}", "submit": "OdoslaÅĨ", + "success": "Úspech", "suggestions": "NÃĄvrhy", "sunrise_on_the_beach": "VÃŊchod slnka na plÃĄÅži", "support": "Podpora", @@ -1667,24 +1949,36 @@ "swap_merge_direction": "VymeniÅĨ smer zlÃēčenia", "sync": "SynchronizovaÅĨ", "sync_albums": "SynchronizovaÅĨ albumy", - "tag": "Značka", - "tag_assets": "PridaÅĨ značku", - "tag_created": "VytvorenÃĄ značka: {tag}", - "tag_feature_description": "Prehliadanie fotiek a videÃĄ zoskupenÃŊch podÄža tematickÃŊch značiek", - "tag_not_found_question": "Neviete nÃĄjsÅĨ značku? Vytvorte novÃē značku.", + "sync_albums_manual_subtitle": "Synchronizujte vÅĄetky nahranÊ videÃĄ a fotografie s vybranÃŊmi zÃĄloÅžnÃŊmi albumami", + "sync_local": "SynchronizovaÅĨ lokÃĄlne", + "sync_remote": "SynchronizovaÅĨ vzdialenÊ", + "sync_status": "Stav synchronizÃĄcie", + "sync_status_subtitle": "ZobraziÅĨ a spravovaÅĨ systÊm synchronizÃĄcie", + "sync_upload_album_setting_subtitle": "VytvÃĄrajte a nahrÃĄvajte svoje fotografie a videÃĄ do vybranÃŊch albumov na Immich", + "tag": "Å títok", + "tag_assets": "PridaÅĨ ÅĄtítky", + "tag_created": "VytvorenÃŊ ÅĄtítok: {tag}", + "tag_feature_description": "Prehliadanie fotiek a videÃĄ zoskupenÃŊch podÄža tematickÃŊch ÅĄtítkov", + "tag_not_found_question": "Neviete nÃĄjsÅĨ ÅĄtítok? Vytvorte novÃŊ ÅĄtítok.", "tag_people": "OznačiÅĨ Äžudí", - "tag_updated": "UpravenÃĄ značka: {tag}", - "tagged_assets": "Značka priradenÃĄ {count, plural, one {# poloÅžke} other {# poloÅžkÃĄm}}", + "tag_updated": "UpravenÃŊ ÅĄtítok: {tag}", + "tagged_assets": "Å títok priradenÃŊ {count, plural, one {# poloÅžke} other {# poloÅžkÃĄm}}", "tags": "Å títky", + "tap_to_run_job": "Ťuknutím na poloÅžku spustíte Ãēlohu", "template": "Å ablÃŗna", "theme": "TÊma", "theme_selection": "VÃŊber tÊmy", - "theme_selection_description": "Automaticky nastaví tÊmu na svetlÃē alebo tmavÃē podÄža systÊmovÃŊch preferencií v prehliadači", - "theme_setting_asset_list_storage_indicator_title": "ZobraziÅĨ indikÃĄtor ÃēloÅžiska na dlaÅždiciach poloÅžiek", + "theme_selection_description": "Automaticky nastaví tÊmu na svetlÃē alebo tmavÃē podÄža systÊmovÃŊch predvolieb v prehliadači", + "theme_setting_asset_list_storage_indicator_title": "ZobraziÅĨ indikÃĄtor ÃēloÅžiska na dlaÅždiciach mÊdií", "theme_setting_asset_list_tiles_per_row_title": "Počet poloÅžiek na riadok ({count})", - "theme_setting_image_viewer_quality_subtitle": "Prispôsobenie kvality prehliadača detailov", + "theme_setting_colorful_interface_subtitle": "PouÅžiÅĨ zÃĄkladnÃē farbu na plochy na pozadí.", + "theme_setting_colorful_interface_title": "FarebnÊ rozhranie", + "theme_setting_image_viewer_quality_subtitle": "Upravte kvalitu detailnÊho prehliadača obrÃĄzkov", "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrÃĄzkov", - "theme_setting_system_theme_switch": "Automaticky (podÄža systemovÊho nastavenia)", + "theme_setting_primary_color_subtitle": "Vyberte si farbu pre zÃĄkladnÊ akcie a dôrazy.", + "theme_setting_primary_color_title": "ZÃĄkladnÃĄ farba", + "theme_setting_system_primary_color_title": "PouÅžiÅĨ systÊmovÃē farbu", + "theme_setting_system_theme_switch": "Automaticky (podÄža systÊmovÊho nastavenia)", "theme_setting_theme_subtitle": "Vyberte nastavenia tÊmy aplikÃĄcie", "theme_setting_three_stage_loading_subtitle": "TrojstupňovÊ načítanie môŞe zvÃŊÅĄiÅĨ vÃŊkonnosÅĨ načítania, ale vedie k vÃŊrazne vyÅĄÅĄiemu zaÅĨaÅženiu siete", "theme_setting_three_stage_loading_title": "Povolenie trojstupňovÊho načítavania", @@ -1697,37 +1991,46 @@ "to_change_password": "ZmeniÅĨ heslo", "to_favorite": "ObÄžÃēbiÅĨ", "to_login": "PrihlÃĄsiÅĨ", + "to_multi_select": "na viacnÃĄsobnÃŊ vÃŊber", "to_parent": "PrejsÅĨ k nadradenÊmu", + "to_select": "na vÃŊber", "to_trash": "KÃ´ÅĄ", "toggle_settings": "PrepnÃēÅĨ nastavenie", "total": "Celkom", "total_usage": "CelkovÊ vyuÅžitie", "trash": "KÃ´ÅĄ", + "trash_action_prompt": "{count} presunutÃŊch do koÅĄa", "trash_all": "VÅĄetko do koÅĄa", "trash_count": "{count, number} do koÅĄa", "trash_delete_asset": "PoloÅžky do koÅĄa/odstrÃĄniÅĨ", + "trash_emptied": "KÃ´ÅĄ vyprÃĄzdnenÃŊ", "trash_no_results_message": "VymazanÊ fotografie a videÃĄ sa zobrazia tu.", "trash_page_delete_all": "VymazaÅĨ vÅĄetky", - "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprÃĄzdniÅĨ kÃ´ÅĄ? Tieto poloÅžky budÃē permanentne odstrÃĄnenÊ z Immichu", + "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprÃĄzdniÅĨ kÃ´ÅĄ? Tieto poloÅžky budÃē permanentne odstrÃĄnenÊ z aplikÃĄcie Immich", "trash_page_info": "MÊdiÃĄ v koÅĄi sa permanentne odstrÃĄnia po {days} dňoch", "trash_page_no_assets": "ÅŊiadne mÊdiÃĄ v koÅĄi", "trash_page_restore_all": "ObnoviÅĨ vÅĄetky", - "trash_page_select_assets_btn": "OznačiÅĨ mÊdiÃĄ", + "trash_page_select_assets_btn": "VybraÅĨ mÊdiÃĄ", "trash_page_title": "KÃ´ÅĄ ({count})", "trashed_items_will_be_permanently_deleted_after": "PoloÅžky v koÅĄi sa natrvalo vymaÅžÃē po {days, plural, one {# dni} other {# dňoch}}.", + "troubleshoot": "RieÅĄenie problÊmov", "type": "Typ", + "unable_to_change_pin_code": "Nie je moÅžnÊ zmeniÅĨ PIN kÃŗd", + "unable_to_setup_pin_code": "Nie je moÅžnÊ nastaviÅĨ PIN kÃŗd", "unarchive": "OdarchivovaÅĨ", + "unarchive_action_prompt": "{count} odstrÃĄnenÊ z archívu", "unarchived_count": "{count, plural, other {OdarchivovanÃŊch #}}", "undo": "SpäÅĨ", "unfavorite": "OdznačiÅĨ ako obÄžÃēbenÊ", - "unhide_person": "OdkryÅĨ osobu", + "unfavorite_action_prompt": "{count} odstrÃĄnenÊ z ObÄžÃēbenÃŊch", + "unhide_person": "Znovu zobraziÅĨ osobu", "unknown": "NeznÃĄme", - "unknown_country": "NeznÃĄmy ÅĄtÃĄt", + "unknown_country": "NeznÃĄma krajina", "unknown_year": "NeznÃĄmy rok", "unlimited": "NeobmedzenÊ", "unlink_motion_video": "OdpojiÅĨ pohyblivÊ video", "unlink_oauth": "OdpojiÅĨ OAuth", - "unlinked_oauth_account": "OdpojiÅĨ OAuth Ãēčet", + "unlinked_oauth_account": "OdpojenÃŊ OAuth Ãēčet", "unmute_memories": "ZruÅĄenie stlmenia spomienok", "unnamed_album": "NepomenovanÃŊ album", "unnamed_album_delete_confirmation": "Ste si istÃŊ, Åže chcete zmazaÅĨ tento album?", @@ -1735,47 +2038,60 @@ "unsaved_change": "NeuloÅženÃĄ zmena", "unselect_all": "ZruÅĄiÅĨ vÃŊber vÅĄetkÃŊch", "unselect_all_duplicates": "ZruÅĄiÅĨ vÃŊber vÅĄetkÃŊch duplicít", + "unselect_all_in": "ZruÅĄiÅĨ vÃŊber vÅĄetkÃŊch v {group}", "unstack": "OdskupiÅĨ", - "unstacked_assets_count": "{count, plural, one {RozloÅženÃĄ # poloÅžka} few {RozloÅženÊ # poloÅžky} other {RozloÅženÃŊch # poloÅžiek}}", + "unstack_action_prompt": "{count} nezoskupenÃŊch", + "unstacked_assets_count": "ZruÅĄenie zoskupenia pre {count, plural, one {# poloÅžku} few {# poloÅžky} other {# poloÅžiek}}", + "untagged": "Bez ÅĄtítku", "up_next": "To je vÅĄetko", + "update_location_action_prompt": "AktualizovaÅĨ polohu {count} vybranÃŊch poloÅžiek pomocou:", "updated_at": "AktualizovanÊ", "updated_password": "Heslo zmenenÊ", "upload": "NahraÅĨ", + "upload_action_prompt": "{count} v poradí na nahratie", "upload_concurrency": "SÃēbeÅžnosÅĨ nahrÃĄvania", + "upload_details": "Podrobnosti o nahrÃĄvaní", "upload_dialog_info": "Chcete zÃĄlohovaÅĨ zvolenÊ mÊdiÃĄ na server?", "upload_dialog_title": "NahraÅĨ mÊdiÃĄ", - "upload_errors": "NahrÃĄvanie ukončenÊ s {count, plural, one {# chybou} other {# chybami}}, obnovte strÃĄnku aby sa zobrazili novÊ poloÅžky.", + "upload_errors": "NahrÃĄvanie ukončenÊ s {count, plural, one {# chybou} other {# chybami}}, obnovte strÃĄnku, aby sa zobrazili novÊ poloÅžky.", + "upload_finished": "NahrÃĄvanie dokončenÊ", "upload_progress": "OstÃĄva {remaining, number} - SpracovanÃŊch {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {PreskočenÃĄ # duplicita} few {PreskočenÊ # duplicity} other {PreskočenÃŊch # duplicít}}", + "upload_skipped_duplicates": "{count, plural, one {PreskočenÃĄ # duplicitnÃĄ poloÅžka} few {PreskočenÊ # duplicitnÊ poloÅžky} other {PreskočenÃŊch # duplicitnÃŊch poloÅžiek}}", "upload_status_duplicates": "DuplikÃĄty", "upload_status_errors": "Chyby", "upload_status_uploaded": "NahranÊ", "upload_success": "NahrÃĄvanie ÃēspeÅĄnÊ, pridanÊ sÃēbory sa zobrazia po obnovení strÃĄnky.", "upload_to_immich": "NahraÅĨ na Immich ({count})", "uploading": "NahrÃĄvanie", + "uploading_media": "NahrÃĄvanie mÊdií", "url": "Odkaz URL", "usage": "PouÅžitie", + "use_biometric": "PouÅžiÅĨ biometrickÊ Ãēdaje", + "use_current_connection": "pouÅžiÅĨ aktuÃĄlne pripojenie", "use_custom_date_range": "PouÅžiÅĨ radÅĄej vlastnÃŊ rozsah dÃĄtumov", "user": "PouŞívateÄž", "user_has_been_deleted": "Tento pouŞívateÄž bol vymazanÃŊ.", - "user_id": "PouŞívateÄžskÊ ID", + "user_id": "ID pouŞívateÄža", "user_liked": "PouŞívateÄžovi {user} sa pÃĄÄi {type, select, photo {tÃĄto fotka} video {toto video} asset {tÃĄto poloÅžka} other {toto}}", "user_pin_code_settings": "PIN kÃŗd", "user_pin_code_settings_description": "Spravujte svoj PIN kÃŗd", + "user_privacy": "Ochrana osobnÃŊch Ãēdajov pouŞívateÄža", "user_purchase_settings": "NÃĄkup", - "user_purchase_settings_description": "SprÃĄva vÃĄÅĄho nÃĄkupu", + "user_purchase_settings_description": "Spravujte svoj nÃĄkup", "user_role_set": "Nastav {user} ako {role}", "user_usage_detail": "Podrobnosti o vyuŞívaní pouŞívateÄžmi", "user_usage_stats": "Å tatistiky vyuÅžitia Ãēčtu", "user_usage_stats_description": "ZobraziÅĨ ÅĄtatistiky vyuÅžitia Ãēčtu", "username": "PouŞívateÄžskÊ meno", "users": "PouŞívatelia", + "users_added_to_album_count": "{count, plural, one {PridanÃŊ # pouŞívateÄž} few {Pridaní # pouŞívatelia} other {PridanÃŊch # pouŞívateÄžov}} do albumu", "utilities": "NÃĄstroje", - "validate": "ValidovaÅĨ", + "validate": "OveriÅĨ", + "validate_endpoint_error": "Zadajte prosím platnÃē URL adresu", "variables": "PremennÊ", "version": "Verzia", "version_announcement_closing": "Tvoj kamarÃĄt, Alex", - "version_announcement_message": "Ahoj! NovÃĄ verzia Immich je dostupnÃĄ. Prosím prečítajte si poznÃĄmky k vydaniu, aby ste sa uistili, Åže inÅĄtalÃĄcia bude aktuÃĄlna bez problÊmov, najmä ak pouŞívate WatchTower alebo akÃŊkoÄžvek spôsob automatickej aktualizÃĄcie Immich servera.", + "version_announcement_message": "Ahoj! K dispozícii je novÃĄ verzia aplikÃĄcie Immich. Prosím, venujte trochu času prečítaniu poznÃĄmok k vydaniu, aby ste sa uistili, Åže vaÅĄa inÅĄtalÃĄcia je aktuÃĄlna a prediÅĄli tak akÃŊmkoÄžvek chybÃĄm v konfigurÃĄcii, najmä ak pouŞívate WatchTower alebo akÃŊkoÄžvek mechanizmus, ktorÃŊ sa starÃĄ o automatickÃē aktualizÃĄciu vaÅĄej inÅĄtancie Immich.", "version_history": "HistÃŗria verzií", "version_history_item": "InÅĄtalovanÃĄ {version} dňa {date}", "video": "Video", @@ -1783,10 +2099,11 @@ "video_hover_setting_description": "PrehrÃĄ video nÃĄhÄžad keď kurzor myÅĄi prejde cez poloÅžku. Aj keď je vypnutÊ, prehrÃĄvanie sa môŞe spustiÅĨ nabehnutí cez ikonu PrehraÅĨ.", "videos": "VideÃĄ", "videos_count": "{count, plural, one {# Video} few {# VideÃĄ} other {# Videí}}", - "view": "ZobraziÅĨ", + "view": "Zobrazenie", "view_album": "ZobraziÅĨ Album", "view_all": "ZobraziÅĨ vÅĄetky", "view_all_users": "ZobraziÅĨ vÅĄetkÃŊch pouŞívateÄžov", + "view_details": "ZobraziÅĨ podrobnosti", "view_in_timeline": "ZobraziÅĨ v časovej osi", "view_link": "ZobraziÅĨ odkaz", "view_links": "ZobraziÅĨ odkazy", @@ -1794,13 +2111,14 @@ "view_next_asset": "ZobraziÅĨ nasledujÃēci sÃēbor", "view_previous_asset": "ZobraziÅĨ predchÃĄdzajÃēci sÃēbor", "view_qr_code": "ZobraziÅĨ QR kÃŗd", + "view_similar_photos": "ZobraziÅĨ podobnÊ fotografie", "view_stack": "ZobraziÅĨ zoskupenie", "view_user": "ZobraziÅĨ pouŞívateÄža", "viewer_remove_from_stack": "OdstrÃĄniÅĨ zo zoskupenia", "viewer_stack_use_as_main_asset": "PouÅžiÅĨ ako hlavnÃē fotku", "viewer_unstack": "OdskupiÅĨ", - "visibility_changed": "ViditeÄžnosÅĨ zmenenÃĄ pre {count, plural, one {# osobu} other {# Äžudí}}", - "waiting": "ČakÃĄ", + "visibility_changed": "ViditeÄžnosÅĨ zmenenÃĄ pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", + "waiting": "ČakajÃēce", "warning": "Varovanie", "week": "TÃŊÅždeň", "welcome": "Vitajte", @@ -1810,7 +2128,8 @@ "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", - "you_dont_have_any_shared_links": "NemÃĄte Åžiadne zdielanÊ linky", + "you_dont_have_any_shared_links": "NemÃĄte Åžiadne zdielanÊ odkazy", "your_wifi_name": "VÃĄÅĄ nÃĄzov siete Wi-Fi", - "zoom_image": "PriblíŞiÅĨ obrÃĄzok" + "zoom_image": "PriblíŞiÅĨ obrÃĄzok", + "zoom_to_bounds": "ZvÃ¤ÄÅĄiÅĨ na okraje" } diff --git a/i18n/sl.json b/i18n/sl.json index 5132d09fc8..bcea0de5cf 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -8,12 +8,13 @@ "actions": "Dejanja", "active": "Aktivno", "activity": "Aktivnost", - "activity_changed": "Aktivnost {enabled, select, true {omogočena} other {onemogočena}}", + "activity_changed": "Aktivnost je {enabled, select, true {omogočena} other {onemogočena}}", "add": "Dodaj", "add_a_description": "Dodaj opis", "add_a_location": "Dodaj lokacijo", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rojstni dan", "add_endpoint": "Dodaj končno točko", "add_exclusion_pattern": "Dodaj vzorec izključitve", "add_import_path": "Dodaj pot uvoza", @@ -27,6 +28,10 @@ "add_to_album": "Dodaj v album", "add_to_album_bottom_sheet_added": "Dodano v {album}", "add_to_album_bottom_sheet_already_exists": "ÅŊe v {album}", + "add_to_album_bottom_sheet_some_local_assets": "Nekaterih lokalnih sredstev ni bilo mogoče dodati v album", + "add_to_album_toggle": "Preklopi izbiro za {album}", + "add_to_albums": "Dodaj v albume", + "add_to_albums_count": "Dodaj v albume ({count})", "add_to_shared_album": "Dodaj k deljenemu albumu", "add_url": "Dodaj URL", "added_to_archive": "Dodano v arhiv", @@ -35,18 +40,25 @@ "admin": { "add_exclusion_pattern_description": "Dodajte vzorec izključitev. Globiranje z uporabo *, ** in ? je podprto. Če Åželite prezreti vse datoteke v katerem koli imeniku z imenom \"Raw\", uporabite \"**/Raw/**\". Če Åželite prezreti vse datoteke, ki se končajo na \".tif\", uporabite \"**/*.tif\". Če Åželite prezreti absolutno pot, uporabite \"/pot/za/ignoriranje/**\".", "admin_user": "SkrbniÅĄki uporabnik", - "asset_offline_description": "Sredstva zunanje knjiÅžnice ni več mogoče najti na disku in je bilo premaknjeno v koÅĄ. Če je bila datoteka premaknjena znotraj knjiÅžnice, preverite svojo časovnico za novo ustrezno sredstvo. Če Åželite obnoviti to sredstvo, zagotovite, da ima Immich dostop do spodnje poti datoteke, in skenirajte knjiÅžnico.", + "asset_offline_description": "Tega sredstva zunanje knjiÅžnice ni več mogoče najti na disku in je bilo premaknjeno v koÅĄ. Če je bila datoteka premaknjena znotraj knjiÅžnice, preverite svojo časovnico za novo ustrezno sredstvo. Če Åželite obnoviti to sredstvo, zagotovite, da ima Immich dostop do spodnje poti datoteke, in skenirajte knjiÅžnico.", "authentication_settings": "Nastavitve preverjanja pristnosti", "authentication_settings_description": "Upravljanje gesel, OAuth in drugih nastavitev preverjanja pristnosti", "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 streÅžniÅĄkega ukaza.", + "authentication_settings_reenable": "Za ponovno omogočanje uporabite streÅžniÅĄki ukaz.", "background_task_job": "Opravila v ozadju", "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_keep_last_amount": "Å tevilo prejÅĄnjih izpisov baze podatkov, ki jih je treba obdrÅžati", + "backup_onboarding_1_description": "kopijo zunaj lokacije v oblaku ali na drugi fizični lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različnih napravah. To vključuje glavne datoteke in lokalno varnostno kopijo teh datotek.", + "backup_onboarding_3_description": "skupno ÅĄtevilo kopij vaÅĄih podatkov, vključno z izvirnimi datotekami. To vključuje 1 kopijo zunaj lokacije in 2 lokalni kopiji.", + "backup_onboarding_description": "Za zaÅĄÄito podatkov priporočamo strategijo varnostnega kopiranja 3-2-1. Za celovito reÅĄitev varnostnega kopiranja hranite kopije naloÅženih fotografij/videoposnetkov in podatkovne baze Immich.", + "backup_onboarding_footer": "Za več informacij o varnostnem kopiranju Immicha glejte dokumentacijo.", + "backup_onboarding_parts_title": "Varnostna kopija 3-2-1 vključuje:", + "backup_onboarding_title": "Varnostne kopije", "backup_settings": "Nastavitve izpisa baze podatkov", "backup_settings_description": "Upravljanje nastavitev izpisa podatkovne baze.", - "cleared_jobs": "RazčiÅĄÄeno opravilo za: {job}", + "cleared_jobs": "RazčiÅĄÄena opravila 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 {# vsebovani vir} two {# vsebovana vira} few {# vsebovane vire} other {vseh # vsebovanih virov}} in tega ni moÅžno razveljaviti. Datoteke bodo ostale na disku.", @@ -61,10 +73,10 @@ "disable_login": "Onemogoči prijavo", "duplicate_detection_job_description": "ZaÅženite strojno učenje na sredstvih, da zaznate podobne slike. ZanaÅĄa se na Pametno Iskanje", "exclusion_pattern_description": "Vzorci izključitev vam omogočajo, da prezrete datoteke in mape pri skeniranju knjiÅžnice. To je uporabno, če imate mape z datotekami, ki jih ne Åželite uvoziti, na primer datoteke RAW.", - "external_library_management": "Upravljanje zunanje knjiÅžnice", + "external_library_management": "Upravljanje zunanjih knjiÅžnic", "face_detection": "Zaznavanje obrazov", - "face_detection_description": "Zaznajte obraze v sredstvih s pomočjo strojnega učenja. Pri videoposnetkih se upoÅĄteva samo sličica. \"Vse\" (ponovno) obdela vsa sredstva. \"Manjkajoče\" postavi v čakalno vrsto sredstva, ki ÅĄe niso bila obdelana. Zaznani obrazi bodo postavljeni v čakalno vrsto za prepoznavanje obrazov, ko bo zaznavanje obrazov končano, in jih bodo zdruÅžili v obstoječe ali nove osebe.", - "facial_recognition_job_description": "ZdruÅži zaznane obraze v osebe. Ta korak se izvede po končanem zaznavanju obrazov. \"Vse\" (ponovno) zdruÅžuje vse obraze. \"Manjkajoče\", doda v čakalno vrsto obraze, ki nimajo dodeljene osebe.", + "face_detection_description": "Zaznavanje obrazov v sredstvih z uporabo strojnega učenja. Pri videoposnetkih se upoÅĄteva samo sličica. ÂģOsveÅžiÂĢ (ponovno) obdela vsa sredstva. ÂģPonastaviÂĢ dodatno izbriÅĄe vse trenutne podatke o obrazih. ÂģManjkajočaÂĢ uvrsti sredstva, ki ÅĄe niso bila obdelana, v čakalno vrsto. Zaznani obrazi bodo po končanem zaznavanju obrazov uvrÅĄÄeni v čakalno vrsto za prepoznavanje obrazov, pri čemer bodo zdruÅženi v obstoječe ali nove osebe.", + "facial_recognition_job_description": "ZdruÅži zaznane obraze v osebe. Ta korak se izvede po končanem zaznavanju obrazov. ÂģPonastaviÂĢ (ponovno) zdruÅži vse obraze. ÂģManjkajočaÂĢ uvrsti obraze, ki jim ni dodeljena oseba, v čakalno vrsto.", "failed_job_command": "Za opravilo {job} ukaz {command} ni uspel", "force_delete_user_warning": "OPOZORILO: S tem boste takoj odstranili uporabnika in vsa sredstva. Tega ni mogoče razveljaviti in datotek ni mogoče obnoviti.", "image_format": "Format", @@ -91,12 +103,12 @@ "image_thumbnail_title": "Nastavitve sličic", "job_concurrency": "{job} sočasnost", "job_created": "Opravilo ustvarjeno", - "job_not_concurrency_safe": "To opravilo ni sočasno-varno.", + "job_not_concurrency_safe": "To delo ni varno za sočasnost.", "job_settings": "Nastavitve opravil", "job_settings_description": "Upravljaj sočasnost opravil", "job_status": "Status opravila", - "jobs_delayed": "{jobCount, plural, other {# zadrÅžan}}", - "jobs_failed": "{jobCount, plural, other {# neuspeÅĄen}}", + "jobs_delayed": "{jobCount, plural, other {# zadrÅžani}}", + "jobs_failed": "{jobCount, plural, other {# neuspeÅĄni}}", "library_created": "Ustvarjena knjiÅžnica: {library}", "library_deleted": "KnjiÅžnica izbrisana", "library_import_path_description": "Določi mapo za uvoz. Ta mapa in njene podmape bodo pregledane za slike in video posnetke.", @@ -112,6 +124,13 @@ "logging_enable_description": "Omogoči dnevnik", "logging_level_description": "Nivo dnevnika, ko je le-ta omogočen.", "logging_settings": "Dnevnik", + "machine_learning_availability_checks": "Preverjanja razpoloÅžljivosti", + "machine_learning_availability_checks_description": "Samodejno zaznavanje in dajanje prednosti razpoloÅžljivim streÅžnikom strojnega učenja", + "machine_learning_availability_checks_enabled": "Omogoči preverjanja razpoloÅžljivosti", + "machine_learning_availability_checks_interval": "Interval preverjanja", + "machine_learning_availability_checks_interval_description": "Interval v milisekundah med preverjanji razpoloÅžljivosti", + "machine_learning_availability_checks_timeout": "Zahteva za časovno omejitev", + "machine_learning_availability_checks_timeout_description": "Časovna omejitev v milisekundah za preverjanje razpoloÅžljivosti", "machine_learning_clip_model": "model CLIP", "machine_learning_clip_model_description": "Ime CLIP modela iz seznama tukaj. Vedite, da boste morali po menjavi modela ponovno zagnati opravilo za 'Pametno iskanje' za vse slike.", "machine_learning_duplicate_detection": "Zaznavanje dvojnikov", @@ -120,7 +139,7 @@ "machine_learning_duplicate_detection_setting_description": "Za iskanje verjetnih dvojnikov uporabite vdelave CLIP", "machine_learning_enabled": "Omogoči strojno učenje", "machine_learning_enabled_description": "Če je onemogočeno, bodo vse funkcije strojnega učenja onemogočene ne glede na spodnje nastavitve.", - "machine_learning_facial_recognition": "Zaznavanje obrazov", + "machine_learning_facial_recognition": "Prepoznavanje obrazov", "machine_learning_facial_recognition_description": "Zaznavanje, prepoznavanje in zdruÅževanje obrazov na slikah", "machine_learning_facial_recognition_model": "Model za prepoznavanje obraza", "machine_learning_facial_recognition_model_description": "Modeli so navedeni v padajočem vrstnem redu glede na velikost. Večji modeli so počasnejÅĄi in uporabljajo več pomnilnika, vendar dajejo boljÅĄe rezultate. UpoÅĄtevajte, da morate po spremembi modela znova zagnati opravilo zaznavanja obrazov za vse slike.", @@ -133,7 +152,7 @@ "machine_learning_min_detection_score": "NajmanjÅĄi rezultat zaznavanja", "machine_learning_min_detection_score_description": "NajmanjÅĄi rezultat zaupanja za zaznavanje obraza od 0-1. NiÅžje vrednosti bodo zaznale več obrazov, vendar lahko povzročijo laÅžne pozitivne rezultate.", "machine_learning_min_recognized_faces": "NajmanjÅĄe ÅĄtevilo prepoznanih obrazov", - "machine_learning_min_recognized_faces_description": "NajmanjÅĄe ÅĄtevilo prepoznanih obrazov za osebo, ki se ustvari. Če to povečate, postane prepoznavanje obraza natančnejÅĄe na račun večje moÅžnosti, da obraz ni dodeljen osebi.", + "machine_learning_min_recognized_faces_description": "NajmanjÅĄe ÅĄtevilo prepoznanih obrazov za osebo, da se ustvari. Če to povečate, postane prepoznavanje obraza natančnejÅĄe na račun večje moÅžnosti, da obraz ni dodeljen osebi.", "machine_learning_settings": "Nastavitve strojnega učenja", "machine_learning_settings_description": "Upravljajte funkcije in nastavitve strojnega učenja", "machine_learning_smart_search": "Pametno iskanje", @@ -165,27 +184,41 @@ "metadata_settings": "Nastavitve metapodatkov", "metadata_settings_description": "Upravljanje nastavitev metapodatkov", "migration_job": "Migracija", - "migration_job_description": "Preselite sličice za sredstva in obraze v najnovejÅĄo strukturo map", + "migration_job_description": "Prenesite sličice za sredstva in obraze v najnovejÅĄo strukturo map", + "nightly_tasks_cluster_faces_setting_description": "ZaÅženi prepoznavanje obrazov na novo zaznanih obrazih", + "nightly_tasks_cluster_new_faces_setting": "ZdruÅžite nove obraze", + "nightly_tasks_database_cleanup_setting": "Naloge čiÅĄÄenja baze podatkov", + "nightly_tasks_database_cleanup_setting_description": "Očistite stare, potekle podatke iz baze podatkov", + "nightly_tasks_generate_memories_setting": "Ustvari spomine", + "nightly_tasks_generate_memories_setting_description": "Ustvari nove spomine iz sredstev", + "nightly_tasks_missing_thumbnails_setting": "Ustvari manjkajoče sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Sredstva brez sličic postavite v čakalno vrsto za ustvarjanje sličic", + "nightly_tasks_settings": "Nastavitve nočnih opravil", + "nightly_tasks_settings_description": "Upravljajte nočne naloge", + "nightly_tasks_start_time_setting": "Začetni čas", + "nightly_tasks_start_time_setting_description": "Čas, ko streÅžnik začne izvajati nočne naloge", + "nightly_tasks_sync_quota_usage_setting": "Posodobi kvoto porabljenega prostora", + "nightly_tasks_sync_quota_usage_setting_description": "Posodobi kvoto shrambe uporabnikov glede na trenutno uporabo", "no_paths_added": "Ni dodanih poti", - "no_pattern_added": "Brez dodanega vzorca", + "no_pattern_added": "Nobenega dodanega vzorca", "note_apply_storage_label_previous_assets": "Opomba: Če Åželite oznako za shranjevanje uporabiti za predhodno naloÅžena sredstva, zaÅženite", "note_cannot_be_changed_later": "OPOMBA: Tega pozneje ni mogoče spremeniti!", - "notification_email_from_address": "Iz naslova", - "notification_email_from_address_description": "E-poÅĄtni naslov poÅĄiljatelja, na primer: \"Immich Photo Server \". Uporabite naslov, s katerega lahko poÅĄiljate e-poÅĄto.", + "notification_email_from_address": "Od naslova", + "notification_email_from_address_description": "PoÅĄiljateljev e-poÅĄtni naslov, na primer: \"Immich Photo Server \". Uporabite naslov, s katerega lahko poÅĄiljate e-poÅĄto.", "notification_email_host_description": "Gostitelj e-poÅĄtnega streÅžnika (npr. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Prezri napake potrdil", "notification_email_ignore_certificate_errors_description": "Prezri napake pri preverjanju potrdila TLS (ni priporočljivo)", "notification_email_password_description": "Geslo za uporabo pri preverjanju pristnosti z e-poÅĄtnim streÅžnikom", "notification_email_port_description": "Vrata e-poÅĄtnega streÅžnika (npr. 25, 465 ali 587)", - "notification_email_sent_test_email_button": "PoÅĄljite testno e-poÅĄto in shranite", + "notification_email_sent_test_email_button": "PoÅĄljite testno e-poÅĄto in shrani", "notification_email_setting_description": "Nastavitve za poÅĄiljanje e-poÅĄtnih obvestil", "notification_email_test_email": "PoÅĄlji testno e-poÅĄto", - "notification_email_test_email_failed": "PoÅĄiljanje testnega e-poÅĄtnega sporočila ni uspelo, preverite svoje vrednosti", + "notification_email_test_email_failed": "PoÅĄiljanje testnega e-poÅĄtnega sporočila ni uspelo, preverite svoje podatke", "notification_email_test_email_sent": "Testno e-poÅĄtno sporočilo je bilo poslano na {email}. Prosimo, preverite svoj nabiralnik.", "notification_email_username_description": "UporabniÅĄko ime za uporabo pri preverjanju pristnosti z e-poÅĄtnim streÅžnikom", "notification_enable_email_notifications": "Omogoči e-poÅĄtna obvestila", "notification_settings": "Nastavitve obvestil", - "notification_settings_description": "Upravljajte nastavitve obvestil, vključno z e-poÅĄto", + "notification_settings_description": "Upravljaj z nastavitvami obvestil, vključno z e-poÅĄto", "oauth_auto_launch": "Samodejni zagon", "oauth_auto_launch_description": "Samodejno zaÅženite tok prijave OAuth, ko obiÅĄÄete stran za prijavo", "oauth_auto_register": "Samodejna registracija", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "Mobilni preusmeritveni URI", "oauth_mobile_redirect_uri_override": "Preglasitev URI preusmeritve za mobilne naprave", "oauth_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je ''{callback}''", + "oauth_role_claim": "Zahteva za vlogo", + "oauth_role_claim_description": "Samodejno dodeli skrbniÅĄki dostop na podlagi prisotnosti tega zahtevka. Zahtevek ima lahko ÂģuporabnikÂĢ ali ÂģskrbnikÂĢ.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", @@ -214,13 +249,13 @@ "person_cleanup_job": "ČiÅĄÄenje osebe", "quota_size_gib": "Velikost kvote (GiB)", "refreshing_all_libraries": "OsveÅževanje vseh knjiÅžnic", - "registration": "Administratorska registracija", + "registration": "Registracija administratorja", "registration_description": "Ker ste prvi uporabnik v sistemu, boste dodeljeni kot skrbnik in ste odgovorni za skrbniÅĄka opravila, dodatne uporabnike pa boste ustvarili sami.", "require_password_change_on_login": "Od uporabnika zahtevajte spremembo gesla ob prvi prijavi", "reset_settings_to_default": "Ponastavi nastavitve na privzete", "reset_settings_to_recent_saved": "Ponastavite nastavitve na nedavno shranjene nastavitve", "scanning_library": "Pregledovanje knjiÅžnice", - "search_jobs": "Iskanje opravilâ€Ļ", + "search_jobs": "IÅĄÄi opravilaâ€Ļ", "send_welcome_email": "PoÅĄlji pozdravno e-poÅĄto", "server_external_domain_settings": "Zunanja domena", "server_external_domain_settings_description": "Domena za javne skupne povezave, vključno s http(s)://", @@ -229,7 +264,7 @@ "server_settings": "Nastavitve streÅžnika", "server_settings_description": "Upravljanje nastavitev streÅžnika", "server_welcome_message": "Pozdravno sporočilo", - "server_welcome_message_description": "Sporočilo, ki se prikaÅže na strani za prijavo.", + "server_welcome_message_description": "Sporočilo prikazano na prijavni strani.", "sidecar_job": "Stranski metapodatki", "sidecar_job_description": "Odkrijte ali sinhronizirajte stranske metapodatke iz datotečnega sistema", "slideshow_duration_description": "Å tevilo sekund za prikaz posamezne slike", @@ -237,7 +272,7 @@ "storage_template_date_time_description": "Časovni Åžig ustvarjanja sredstva se uporablja za informacije o datumu in času", "storage_template_date_time_sample": "Vzorec časa {date}", "storage_template_enable_description": "Omogoči mehanizem predloge za shranjevanje", - "storage_template_hash_verification_enabled": "Preverjanje zgoÅĄÄevanja je omogočeno", + "storage_template_hash_verification_enabled": "Omogočeno preverjanje zgoÅĄÄene vrednosti", "storage_template_hash_verification_enabled_description": "Omogoči preverjanje zgoÅĄÄene vrednosti, tega ne onemogočite, razen če niste prepričani o posledicah", "storage_template_migration": "Selitev predloge za shranjevanje", "storage_template_migration_description": "Uporabi trenutno {template} za predhodno naloÅžena sredstva", @@ -256,7 +291,7 @@ "template_email_invite_album": "Predloga povabila v album", "template_email_preview": "Predogled", "template_email_settings": "E-poÅĄtne predloge", - "template_email_update_album": "Predloga posodobitve albuma", + "template_email_update_album": "Posodobi predlogo albuma", "template_email_welcome": "Predloga pozdravnega e-poÅĄtnega sporočila", "template_settings": "Predloge obvestil", "template_settings_description": "Upravljanje predlog po meri za obvestila", @@ -272,14 +307,14 @@ "transcoding_acceleration_qsv": "Hitra sinhronizacija (zahteva procesor Intel 7. generacije ali novejÅĄi)", "transcoding_acceleration_rkmpp": "RKMPP (samo na Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "Sprejeti zvočni kodeki", + "transcoding_accepted_audio_codecs": "Dovoljeni zvočni kodeki", "transcoding_accepted_audio_codecs_description": "Izberite, katerih zvočnih kodekov ni treba prekodirati. Uporablja se samo za določene politike prekodiranja.", "transcoding_accepted_containers": "Sprejeti zabojniki", "transcoding_accepted_containers_description": "Izberite, katerih formatov zabojnika ni treba ponovno muksirati v MP4. Uporablja se samo za določene politike prekodiranja.", "transcoding_accepted_video_codecs": "Podprti video kodeki", "transcoding_accepted_video_codecs_description": "Izberite, katerih video kodekov ni treba prekodirati. Uporablja se samo za določene politike prekodiranja.", - "transcoding_advanced_options_description": "MoÅžnosti večini uporabnikov ne bi bilo treba spreminjati", - "transcoding_audio_codec": "Avdio kodek", + "transcoding_advanced_options_description": "MoÅžnosti, ki jih večini uporabnikov ne treba spreminjati", + "transcoding_audio_codec": "Zvočni kodek", "transcoding_audio_codec_description": "Opus je najbolj kakovostna moÅžnost, vendar ima slabÅĄo zdruÅžljivost s starimi napravami ali programsko opremo.", "transcoding_bitrate_description": "Videoposnetki, ki presegajo največjo bitno hitrost ali niso v sprejemljivem formatu", "transcoding_codecs_learn_more": "Če Åželite izvedeti več o tukaj uporabljeni terminologiji, glejte dokumentacijo FFmpeg za kodek H.264, kodek HEVC in VP9 kodek.", @@ -331,6 +366,9 @@ "trash_number_of_days_description": "Å tevilo dni za shranjevanje sredstev v smetnjaku, preden jih trajno odstranite", "trash_settings": "Nastavitve smetnjaka", "trash_settings_description": "Upravljanje nastavitev smetnjaka", + "unlink_all_oauth_accounts": "Prekini povezavo z vsemi računi OAuth", + "unlink_all_oauth_accounts_description": "Pred selitvijo k novemu ponudniku ne pozabite prekiniti povezave vseh računov OAuth.", + "unlink_all_oauth_accounts_prompt": "Ali ste prepričani, da Åželite prekiniti povezavo z vsemi računi OAuth? S tem boste ponastavili ID OAuth za vsakega uporabnika in tega ni mogoče razveljaviti.", "user_cleanup_job": "ČiÅĄÄenje uporabnika", "user_delete_delay": "Račun in sredstva {user} bodo načrtovani za trajno brisanje čez {delay, plural, one {# dan} other {# dni}}.", "user_delete_delay_settings": "Zamakni izbris", @@ -360,10 +398,12 @@ "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: {level}", - "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_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz lokalnih sredstev. Aktivirajte to nastavitev, če Åželite namesto tega naloÅžiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", "advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich poÅĄlje ob vsaki mreÅžni zahtevi", "advanced_settings_proxy_headers_title": "Proxy glave", + "advanced_settings_readonly_mode_subtitle": "Omogoči način samo za branje, kjer si je mogoče fotografije samo ogledati, funkcije, kot so izbiranje več slik, deljenje, predvajanje in brisanje, so onemogočene. Omogoči/onemogoči način samo za branje prek uporabniÅĄkega avatarja na glavnem zaslonu", + "advanced_settings_readonly_mode_title": "Način samo za branje", "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", @@ -379,6 +419,7 @@ "album_cover_updated": "Naslovnica albuma posodobljena", "album_delete_confirmation": "Ali ste prepričani, da Åželite izbrisati album {album}?", "album_delete_confirmation_description": "Če je ta album v skupni rabi, drugi uporabniki ne bodo mogli več dostopati do njega.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZKLJUČENO", "album_info_card_backup_album_included": "VKLJUČENO", "album_info_updated": "Podatki o albumu posodobljeni", @@ -388,7 +429,9 @@ "album_options": "MoÅžnosti albuma", "album_remove_user": "Odstrani uporabnika?", "album_remove_user_confirmation": "Ali ste prepričani, da Åželite odstraniti {user}?", + "album_search_not_found": "Ni najdenih albumov, ki bi ustrezali vaÅĄemu iskanju", "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_summary": "Povzetek albuma", "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}", @@ -407,6 +450,7 @@ "albums_default_sort_order": "Privzeti vrstni red razvrÅĄÄanja albumov", "albums_default_sort_order_description": "Začetni vrstni red razvrÅĄÄanja sredstev pri ustvarjanju novih albumov.", "albums_feature_description": "Zbirke sredstev, ki jih je mogoče deliti z drugimi uporabniki.", + "albums_on_device_count": "Albumi v napravi ({count})", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -426,7 +470,9 @@ "app_bar_signout_dialog_title": "Odjava", "app_settings": "Nastavitve aplikacije", "appears_in": "Pojavi se v", + "apply_count": "Uporabi ({count, number})", "archive": "Arhiv", + "archive_action_prompt": "v arhiv je dodanih {count}", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", "archive_page_title": "Arhiv ({count})", @@ -457,6 +503,8 @@ "asset_restored_successfully": "Sredstvo uspeÅĄno obnovljeno", "asset_skipped": "Preskočeno", "asset_skipped_in_trash": "V smetnjak", + "asset_trashed": "Sredstvo je bilo premaknjeno v koÅĄ", + "asset_troubleshoot": "Odpravljanje teÅžav s sredstvi", "asset_uploaded": "NaloÅženo", "asset_uploading": "Nalaganjeâ€Ļ", "asset_viewer_settings_subtitle": "Upravljaj nastavitve pregledovalnika galerije", @@ -464,8 +512,9 @@ "assets": "Sredstva", "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 {{name}} other {new album}}", + "assets_added_to_albums_count": "Dodano {assetTotal, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {albumTotal, plural, one {# album} two {# albuma} few {# albume} other {# albumov}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v album", + "assets_cannot_be_added_to_albums": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v noben album", "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_deleted_permanently": "trajno izrisana sredstva {count}", "assets_deleted_permanently_from_server": "trajno izbrisana sredstva iz streÅžnika Immich {count}", @@ -482,20 +531,25 @@ "assets_trashed_count": "V smetnjak {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_trashed_from_server": "sredstva iz streÅžnika Immich v smetnjaku {count}", "assets_were_part_of_album_count": "{count, plural, one {sredstvo je} two {sredstvi sta} few {sredstva so} other {sredstev je}} Åže del albuma", + "assets_were_part_of_albums_count": "{count, plural, one {Sredstvo je} two {Sredstvi sta} few {Sredstva so} other {Sredstev je}} Åže del albumov", "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", "autoplay_slideshow": "Samodejno predvajanje diaprojekcije", "back": "Nazaj", "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", + "background_backup_running_error": "Varnostno kopiranje v ozadju se trenutno izvaja, ročnega varnostnega kopiranja ni mogoče zagnati", "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", + "background_options": "MoÅžnosti ozadja", + "backup": "Varnostna kopija", "backup_album_selection_page_albums_device": "Albumi v napravi ({count})", "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", "backup_album_selection_page_selection_info": "Informacije o izbiri", "backup_album_selection_page_total_assets": "Skupaj unikatnih sredstev", + "backup_albums_sync": "Sinhronizacija varnostnih kopij albumov", "backup_all": "Vse", "backup_background_service_backup_failed_message": "Varnostno kopiranje sredstev ni uspelo. Ponovno poskuÅĄamâ€Ļ", "backup_background_service_connection_failed_message": "Povezava s streÅžnikom ni uspela. Ponovno poskuÅĄamâ€Ļ", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "Vklopite varnostno kopiranje v ospredju", "backup_controller_page_uploading_file_info": "Nalaganje podatkov o datoteki", "backup_err_only_album": "Edinega albuma ni mogoče odstraniti", + "backup_error_sync_failed": "Sinhronizacija ni uspela. Varnostne kopije ni mogoče obdelati.", "backup_info_card_assets": "sredstva", "backup_manual_cancelled": "Preklicano", "backup_manual_in_progress": "Nalaganje Åže poteka. Poskusite čez nekaj časa", "backup_manual_success": "Uspeh", "backup_manual_title": "Status nalaganja", + "backup_options": "MoÅžnosti varnostnega kopiranja", "backup_options_page_title": "MoÅžnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", + "backup_settings_subtitle": "Upravljanje nastavitev nalaganja", "backward": "Nazaj", "biometric_auth_enabled": "Biometrična avtentikacija omogočena", "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", @@ -570,7 +627,7 @@ "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_subtitle": "Fotografije in videoposnetki, ki so prezrti s strani aplikacije", "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", "cache_settings_statistics_album": "Sličice knjiÅžnice", "cache_settings_statistics_full": "Izvirne slike", @@ -587,6 +644,7 @@ "cancel": "Prekliči", "cancel_search": "Prekliči iskanje", "canceled": "Preklicano", + "canceling": "Preklic", "cannot_merge_people": "Oseb ni mogoče zdruÅžiti", "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", @@ -609,6 +667,8 @@ "change_pin_code": "Spremeni PIN kodo", "change_your_password": "Spremenite geslo", "changed_visibility_successfully": "UspeÅĄno spremenjena vidnost", + "charging": "Polnjenje", + "charging_requirement_mobile_backup": "Za varnostno kopiranje v ozadju je potrebno polnjenje naprave", "check_corrupt_asset_backup": "Preverite poÅĄkodovane varnostne kopije sredstev", "check_corrupt_asset_backup_button": "Izvedi preverjanje", "check_corrupt_asset_backup_description": "To preverjanje zaÅženite samo prek omreÅžja Wi-Fi in potem, ko so vsa sredstva varnostno kopirana. Postopek lahko traja nekaj minut.", @@ -618,6 +678,7 @@ "clear": "Počisti", "clear_all": "Počisti vse", "clear_all_recent_searches": "Počisti vsa nedavna iskanja", + "clear_file_cache": "Počisti predpomnilnik datotek", "clear_message": "Počisti sporočilo", "clear_value": "Počisti vrednost", "client_cert_dialog_msg_confirm": "V redu", @@ -688,11 +749,13 @@ "create_new_user": "Ustvari novega uporabnika", "create_shared_album_page_share_add_assets": "DODAJ SREDSTVA", "create_shared_album_page_share_select_photos": "Izberi fotografije", + "create_shared_link": "Ustvari deljeno povezavo", "create_tag": "Ustvari oznako", "create_tag_description": "Ustvarite novo oznako. Za ugnezdene oznake vnesite celotno pot oznake, vključno s poÅĄevnicami.", "create_user": "Ustvari uporabnika", "created": "Ustvarjeno", "created_at": "Ustvarjeno", + "creating_linked_albums": "Ustvarjanje povezanih albumov ...", "crop": "Obrezovanje", "curated_object_page_title": "Stvari", "current_device": "Trenutna naprava", @@ -700,10 +763,11 @@ "current_server_address": "Trenutni naslov streÅžnika", "custom_locale": "Jezik po meri", "custom_locale_description": "Oblikujte datume in ÅĄtevilke glede na jezik in regijo", + "custom_url": "URL po meri", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", - "darkTheme": "Preklopi na temno temo", + "dark_theme": "Preklopi temno temo", "date_after": "Datum po", "date_and_time": "Datum in ura", "date_before": "Datum pred", @@ -711,6 +775,7 @@ "date_of_birth_saved": "Datum rojstva je uspeÅĄno shranjen", "date_range": "Časovno obdobje", "day": "Dan", + "days": "Dnevi", "deduplicate_all": "Odstrani vse podvojene", "deduplication_criteria_1": "Velikost slike v bajtih", "deduplication_criteria_2": "Å tevilo podatkov EXIF", @@ -719,6 +784,8 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in ÅĄtevilke glede na lokalne nastavitve brskalnika", "delete": "IzbriÅĄi", + "delete_action_confirmation_message": "Ali ste prepričani, da Åželite izbrisati to sredstvo? S tem dejanjem boste sredstvo premaknili v koÅĄ na streÅžniku in vas pozvali, ali ga Åželite izbrisati lokalno", + "delete_action_prompt": "izbrisano {count}", "delete_album": "IzbriÅĄi album", "delete_api_key_prompt": "Ali ste prepričani, da Åželite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaÅĄe naprave", @@ -732,9 +799,12 @@ "delete_key": "IzbriÅĄi ključ", "delete_library": "IzbriÅĄi knjiÅžnico", "delete_link": "IzbriÅĄi povezavo", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "IzbriÅĄi samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriÅĄi", "delete_others": "IzbriÅĄi ostale", + "delete_permanently": "IzbriÅĄi trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "IzbriÅĄi povezavo skupne rabe", "delete_shared_link_dialog_title": "IzbriÅĄi povezavo skupne rabe", "delete_tag": "IzbriÅĄi oznako", @@ -745,6 +815,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", + "deselect_all": "Prekliči vse", "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "Onemogočeno", @@ -762,6 +833,7 @@ "documentation": "Dokumentacija", "done": "Končano", "download": "Prenesi", + "download_action_prompt": "PrenaÅĄanje {count} sredstev", "download_canceled": "Prenos preklican", "download_complete": "Prenos končan", "download_enqueue": "Prenos v čakalni vrsti", @@ -788,8 +860,12 @@ "edit": "Uredi", "edit_album": "Uredi album", "edit_avatar": "Uredi avatar", + "edit_birthday": "Uredi rojstni dan", "edit_date": "Uredi datum", "edit_date_and_time": "Uredi datum in uro", + "edit_date_and_time_action_prompt": "{count} datum in ura urejeno", + "edit_date_and_time_by_offset": "Spremeni datum z odmikom", + "edit_date_and_time_by_offset_interval": "Novo obdobje: {from} - {to}", "edit_description": "Uredi opis", "edit_description_prompt": "Izberite nov opis:", "edit_exclusion_pattern": "Uredi vzorec izključitve", @@ -799,6 +875,7 @@ "edit_key": "Uredi ključ", "edit_link": "Uredi povezavo", "edit_location": "Uredi lokacijo", + "edit_location_action_prompt": "urejenih {count} lokacij", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi osebe", @@ -817,6 +894,7 @@ "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", + "enable_backup": "Omogoči varnostno kopiranje", "enable_biometric_auth_description": "Vnesite svojo PIN kodo, da omogočite biometrično preverjanje pristnosti", "enabled": "Omogočeno", "end_date": "Končni datum", @@ -827,7 +905,9 @@ "error": "Napaka", "error_change_sort_album": "Vrstnega reda albuma ni bilo mogoče spremeniti", "error_delete_face": "Napaka pri brisanju obraza iz sredstva", + "error_getting_places": "Napaka pri pridobivanju mest", "error_loading_image": "Napaka pri nalaganju slike", + "error_loading_partners": "Napaka pri nalaganju partnerjev: {error}", "error_saving_image": "Napaka: {error}", "error_tag_face_bounding_box": "Napaka pri označevanju obraza - ni mogoče pridobiti koordinat omejevalnega okvirja", "error_title": "Napaka - nekaj je ÅĄlo narobe", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "Nalaganje obvestil ni uspelo", "failed_to_load_people": "Oseb ni bilo mogoče naloÅžiti", "failed_to_remove_product_key": "Ključa izdelka ni bilo mogoče odstraniti", + "failed_to_reset_pin_code": "Ponastavitev PIN-kode ni uspela", "failed_to_stack_assets": "Zlaganje sredstev ni uspelo", "failed_to_unstack_assets": "Sredstev ni bilo mogoče razloÅžiti", "failed_to_update_notification_status": "Stanja obvestila ni bilo mogoče posodobiti", @@ -868,6 +949,7 @@ "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", + "something_went_wrong": "Nekaj je ÅĄlo narobe", "unable_to_add_album_users": "Uporabnikov ni mogoče dodati v album", "unable_to_add_assets_to_shared_link": "Povezavi v skupni rabi ni mogoče dodati sredstev", "unable_to_add_comment": "Ni mogoče dodati komentarja", @@ -953,13 +1035,11 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis..", + "exif_bottom_sheet_description_error": "Napaka pri posodabljanju opisa", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", "exif_bottom_sheet_person_add_person": "Dodaj ime", - "exif_bottom_sheet_person_age_months": "Starost {months} mesecev", - "exif_bottom_sheet_person_age_year_months": "Starost 1 leto, {months} mesecev", - "exif_bottom_sheet_person_age_years": "Starost {years}", "exit_slideshow": "Zapustite diaprojekcijo", "expand_all": "RazÅĄiri vse", "experimental_settings_new_asset_list_subtitle": "Delo v teku", @@ -973,6 +1053,8 @@ "explorer": "Raziskovalec", "export": "Izvoz", "export_as_json": "Izvozi kot JSON", + "export_database": "Izvoz baze podatkov", + "export_database_description": "Izvozite bazo podatkov SQLite", "extension": "RazÅĄiritev", "external": "Zunanji", "external_libraries": "Zunanje knjiÅžnice", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "Sredstev ni bilo mogoče naloÅžiti", "failed_to_load_folder": "Mape ni bilo mogoče naloÅžiti", "favorite": "Priljubljen", + "favorite_action_prompt": "med priljubljene je dodanih {count}", "favorite_or_unfavorite_photo": "Priljubljena ali nepriljubljena fotografija", "favorites": "Priljubljene", "favorites_page_no_favorites": "Ni priljubljenih sredstev", "feature_photo_updated": "Funkcijska fotografija je posodobljena", "features": "Funkcije", + "features_in_development": "Funkcije v razvoju", "features_setting_description": "Upravljaj funkcije aplikacije", "file_name": "Ime datoteke", "file_name_or_extension": "Ime ali končnica datoteke", @@ -998,21 +1082,26 @@ "filter_people": "Filtriraj ljudi", "filter_places": "Filtriraj kraje", "find_them_fast": "Z iskanjem jih hitro poiÅĄÄite po imenu", + "first": "Prvi", "fix_incorrect_match": "Popravi napačno ujemanje", "folder": "Mapa", "folder_not_found": "Ne najdem mape", "folders": "Mape", "folders_feature_description": "Brskanje po pogledu mape za fotografije in videoposnetke v datotečnem sistemu", + "forgot_pin_code_question": "Ste pozabili PIN?", "forward": "Naprej", "gcast_enabled": "Google Cast", "gcast_enabled_description": "Ta funkcija za delovanje nalaga zunanje vire iz Googla.", "general": "SploÅĄno", + "geolocation_instruction_location": "Kliknite na sredstvo z GPS koordinatami, da uporabite njegovo lokacijo, ali pa izberite lokacijo neposredno na zemljevidu", "get_help": "PoiÅĄÄite pomoč", "get_wifiname_error": "Imena Wi-Fi ni bilo mogoče dobiti. Prepričajte se, da ste podelili potrebna dovoljenja in ste povezani v omreÅžje Wi-Fi", "getting_started": "Začetek", "go_back": "Pojdi nazaj", "go_to_folder": "Pojdi na mapo", "go_to_search": "Pojdi na iskanje", + "gps": "GPS", + "gps_missing": "Brez GPS-a", "grant_permission": "Podeli dovoljenje", "group_albums_by": "ZdruÅži albume po ...", "group_country": "ZdruÅži po drÅžavah", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "Uporabi haptičen odziv", "haptic_feedback_title": "Haptičen odziv", "has_quota": "Ima kvoto", + "hash_asset": "ZgoÅĄÄeno sredstvo", + "hashed_assets": "ZgoÅĄÄena sredstva", + "hashing": "ZgoÅĄÄevanje", "header_settings_add_header_tip": "Dodaj glavo", "header_settings_field_validator_msg": "Vrednost ne sme biti prazna", "header_settings_header_name_input": "Ime glave", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "Hkrati lahko naloÅžite največ 30 sredstev, preskakujem", "host": "Gostitelj", "hour": "Ura", + "hours": "Ure", "id": "ID", + "idle": "Nedejavnost", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naloÅžene na streÅžnik Immich", "image": "Slika", @@ -1112,10 +1206,13 @@ "language_no_results_title": "Ni najdenih jezikov", "language_search_hint": "Iskanje jezikov...", "language_setting_description": "Izberite Åželeni jezik", + "large_files": "Velike datoteke", + "last": "Zadnji", "last_seen": "Nazadnje viden", "latest_version": "NajnovejÅĄa različica", "latitude": "Zemljepisna ÅĄirina", "leave": "Zapusti", + "leave_album": "Zapusti album", "lens_model": "Model leč", "let_others_respond": "Naj drugi odgovorijo", "level": "Raven", @@ -1127,16 +1224,20 @@ "library_page_sort_created": "Nazadnje ustvarjeno", "library_page_sort_last_modified": "Nazadnje spremenjeno", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svetlo", + "like": "VÅĄeč mi je", "like_deleted": "VÅĄeček izbrisan", "link_motion_video": "Povezava videa gibanja", - "link_options": "MoÅžnosti povezave", "link_to_oauth": "Povezava do OAuth", "linked_oauth_account": "Povezan račun OAuth", "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local": "Lokalno", "local_asset_cast_failed": "Sredstva, ki niso naloÅžena na streÅžnik, ni mogoče predvajati", + "local_assets": "Lokalna sredstva", + "local_media_summary": "Povzetek lokalnih medijev", "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", @@ -1148,6 +1249,7 @@ "location_picker_longitude_hint": "Tukaj vnesi svojo zemljepisno dolÅžino", "lock": "Zaklepanje", "locked_folder": "Zaklenjena mapa", + "log_detail_title": "Podrobnosti dnevnika", "log_out": "Odjava", "log_out_all_devices": "Odjava vseh naprav", "logged_in_as": "Prijavljen kot {user}", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "Geslo je bilo uspeÅĄno posodobljeno", "logout_all_device_confirmation": "Ali ste prepričani, da Åželite odjaviti vse naprave?", "logout_this_device_confirmation": "Ali ste prepričani, da se Åželite odjaviti iz te naprave?", + "logs": "Dnevniki", "longitude": "Zemljepisna dolÅžina", "look": "Izgled", "loop_videos": "Zanka videoposnetkov", @@ -1185,6 +1288,7 @@ "main_branch_warning": "Uporabljate razvojno različico; močno priporočamo uporabo izdajne različice!", "main_menu": "Glavni meni", "make": "Izdelava", + "manage_geolocation": "Upravljanje lokacije", "manage_shared_links": "Upravljanje povezav v skupni rabi", "manage_sharing_with_partners": "Upravljajte skupno rabo s partnerji", "manage_the_app_settings": "Upravljajte nastavitve aplikacije", @@ -1193,8 +1297,7 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{count} slika", - "map_assets_in_bounds": "{count} slik", + "map_assets_in_bounds": "{count, plural, =0 {Na tem območju ni fotografij} one {# slika} 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", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "Lokacijska storitev onemogočena", "map_marker_for_images": "Oznaka zemljevida za slike, posnete v {city}, {country}", "map_marker_with_image": "Oznaka zemljevida s sliko", - "map_no_assets_in_bounds": "Na tem območju ni fotografij", "map_no_location_permission_content": "Za prikaz sredstev z vaÅĄe trenutne lokacije je potrebno dovoljenje za lokacijo. Ali to Åželite takoj dovoliti?", "map_no_location_permission_title": "Dovoljenje za lokacijo je zavrnjeno", "map_settings": "Nastavitve zemljevida", @@ -1221,6 +1323,7 @@ "mark_as_read": "Označi kot prebrano", "marked_all_as_read": "Označeno vse kot prebrano", "matches": "Ujemanja", + "matching_assets": "Ujemajoča se sredstva", "media_type": "Vrsta medija", "memories": "Spomini", "memories_all_caught_up": "Vse dohiteno", @@ -1239,6 +1342,7 @@ "merged_people_count": "ZdruÅženo {count, plural, one {# oseba} two {# osebi} few {# osebe} other {# oseb}}", "minimize": "ZmanjÅĄaj", "minute": "minuta", + "minutes": "Minute", "missing": "manjka", "model": "Model", "month": "Mesec", @@ -1246,6 +1350,7 @@ "more": "Več", "move": "Premakni", "move_off_locked_folder": "Premakni iz zaklenjene mape", + "move_to_lock_folder_action_prompt": "V zaklenjeno mapo je bilo dodanih {count}", "move_to_locked_folder": "Premakni v zaklenjeno mapo", "move_to_locked_folder_confirmation": "Te fotografije in videoposnetki bodo odstranjeni iz vseh albumov in si jih bo mogoče ogledati le v zaklenjeni mapi", "moved_to_archive": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v arhiv", @@ -1257,6 +1362,10 @@ "my_albums": "Moji albumi", "name": "Ime", "name_or_nickname": "Ime ali vzdevek", + "network_requirement_photos_upload": "Uporaba mobilnih podatkov za varnostno kopiranje fotografij", + "network_requirement_videos_upload": "Uporaba mobilnih podatkov za varnostno kopiranje videoposnetkov", + "network_requirements": "OmreÅžne zahteve", + "network_requirements_updated": "OmreÅžne zahteve so se spremenile, ponastavitev čakalne vrste za varnostno kopiranje", "networking_settings": "OmreÅžje", "networking_subtitle": "Upravljaj nastavitve končne točke streÅžnika", "never": "nikoli", @@ -1266,6 +1375,7 @@ "new_person": "Nova oseba", "new_pin_code": "Nova PIN koda", "new_pin_code_subtitle": "To je vaÅĄ prvi dostop do zaklenjene mape. Ustvarite PIN kodo za varen dostop do te strani", + "new_timeline": "Nova časovnica", "new_user_created": "Nov uporabnik ustvarjen", "new_version_available": "NA VOLJO JE NOVA RAZLIČICA", "newest_first": "Najprej najnovejÅĄe", @@ -1279,19 +1389,25 @@ "no_assets_message": "KLIKNITE ZA NALOÅŊITEV SVOJE PRVE FOTOGRAFIJE", "no_assets_to_show": "Ni sredstev za prikaz", "no_cast_devices_found": "Naprav za predvajanje ni bilo mogoče najti", + "no_checksum_local": "Kontrolna vsota ni na voljo – lokalnih sredstev ni mogoče pridobiti", + "no_checksum_remote": "Kontrolna vsota ni na voljo – oddaljenega sredstva ni mogoče pridobiti", "no_duplicates_found": "Najden ni bil noben dvojnik.", "no_exif_info_available": "Podatki o exif niso na voljo", "no_explore_results_message": "NaloÅžite več fotografij, da raziÅĄÄete svojo zbirko.", "no_favorites_message": "Dodajte priljubljene, da hitreje najdete svoje najboljÅĄe slike in videoposnetke", "no_libraries_message": "Ustvarite zunanjo knjiÅžnico za ogled svojih fotografij in videoposnetkov", + "no_local_assets_found": "S to kontrolno vsoto ni bilo najdenih lokalnih sredstev", "no_locked_photos_message": "Fotografije in videoposnetki v zaklenjeni mapi so skriti in se ne bodo prikazali med brskanjem ali iskanjem po knjiÅžnici.", "no_name": "Brez imena", "no_notifications": "Ni obvestil", "no_people_found": "Ni najdenih ustreznih oseb", "no_places": "Ni krajev", + "no_remote_assets_found": "S to kontrolno vsoto ni bilo najdenih oddaljenih sredstev", "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj sploÅĄno ključno besedo", "no_shared_albums_message": "Ustvarite album za skupno rabo fotografij in videoposnetkov z osebami v vaÅĄem omreÅžju", + "no_uploads_in_progress": "Ni nalaganj v teku", + "not_available": "Ni na voljo", "not_in_any_album": "Ni v nobenem albumu", "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če Åželite oznako za shranjevanje uporabiti za predhodno naloÅžena sredstva, zaÅženite", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Immich uradni viri", "offline": "Brez povezave", + "offset": "Odmik", "ok": "V redu", "oldest_first": "Najprej najstarejÅĄi", "on_this_device": "Na tej napravi", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "Odpri iskalne filtre", "options": "MoÅžnosti", "or": "ali", + "organize_into_albums": "Organiziraj v albume", + "organize_into_albums_description": "Dodaj obstoječe fotografije v albume z uporabo trenutnih nastavitev sinhronizacije", "organize_your_library": "Organiziraj svojo knjiÅžnico", "original": "izvirnik", "other": "drugo", "other_devices": "Druge naprave", + "other_entities": "Drugi subjekti", "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", @@ -1360,7 +1480,7 @@ "pause": "Premor", "pause_memories": "Zaustavi spomine", "paused": "Zaustavljeno", - "pending": "V teku", + "pending": "Čakanje", "people": "Osebe", "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", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "Dovoljenje je omejeno. Če Åželite Immichu dovoliti varnostno kopiranje in upravljanje vaÅĄe celotne zbirke galerij, v nastavitvah podelite dovoljenja za fotografije in videoposnetke.", "permission_onboarding_request": "Immich potrebuje dovoljenje za ogled vaÅĄih fotografij in videoposnetkov.", "person": "Oseba", + "person_age_months": "{months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}} star/a", + "person_age_year_months": "1 leto, {months, plural, one {# mesec} two {# meseca} few {# mesece} other {# mesecev}} star/a", + "person_age_years": "{years, plural, two {# leti} few {# leta} other {# let}} star/a", "person_birthdate": "Rojen dne {date}", "person_hidden": "{name}{hidden, select, true { (skrita)} other {}}", "photo_shared_all_users": "Videti je, da ste svoje fotografije delili z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi jih delili.", @@ -1406,6 +1529,7 @@ "port": "Vrata", "preferences_settings_subtitle": "Upravljaj nastavitve aplikacije", "preferences_settings_title": "Nastavitve", + "preparing": "Priprava", "preset": "Prednastavitev", "preview": "Predogled", "previous": "PrejÅĄnj-a/-i", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "Mobilna aplikacija je zastarela. Posodobite na najnovejÅĄo manjÅĄo različico.", "profile_drawer_client_server_up_to_date": "Odjemalec in streÅžnik sta posodobljena", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Način samo za branje je omogočen. Za izhod dolgo pritisnite ikono uporabniÅĄkega avatarja.", "profile_drawer_server_out_of_date_major": "StreÅžnik je zastarel. Posodobite na najnovejÅĄo glavno različico.", "profile_drawer_server_out_of_date_minor": "StreÅžnik je zastarel. Posodobite na najnovejÅĄo manjÅĄo različico.", "profile_image_of_user": "Profilna slika uporabnika {user}", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "Status podpornika", "purchase_server_title": "StreÅžnik", "purchase_settings_server_activated": "Ključ izdelka streÅžnika upravlja skrbnik", + "query_asset_id": "ID sredstva poizvedbe", + "queue_status": "Čakalna vrsta {count}/{total}", "rating": "Ocena z zvezdicami", "rating_clear": "Počisti oceno", "rating_count": "{count, plural, one {# zvezdica} two {# zvezdici} few {# zvezdice} other {# zvezdic}}", "rating_description": "PrikaÅžite oceno EXIF v informacijski ploÅĄÄi", "reaction_options": "MoÅžnosti reakcije", "read_changelog": "Preberi dnevnik sprememb", + "readonly_mode_disabled": "Način samo za branje je onemogočen", + "readonly_mode_enabled": "Način samo za branje je omogočen", + "ready_for_upload": "Pripravljeno za nalaganje", "reassign": "Prerazporedi", "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", @@ -1488,6 +1618,9 @@ "refreshing_faces": "OsveÅževanje obrazev", "refreshing_metadata": "OsveÅževanje metapodatkov", "regenerating_thumbnails": "Obnavljanje sličic", + "remote": "Oddaljeno", + "remote_assets": "Oddaljena sredstva", + "remote_media_summary": "Povzetek oddaljenih medijev", "remove": "Odstrani", "remove_assets_album_confirmation": "Ali ste prepričani, da Åželite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz albuma?", "remove_assets_shared_link_confirmation": "Ali ste prepričani, da Åželite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz te skupne povezave?", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "Odstrani časovno obdobje po meri", "remove_deleted_assets": "Odstrani izbrisana sredstva", "remove_from_album": "Odstrani iz albuma", + "remove_from_album_action_prompt": "{count} odstranjenih iz albuma", "remove_from_favorites": "Odstrani iz priljubljenih", + "remove_from_lock_folder_action_prompt": "iz zaklenjene mape je odstranjenih {count}", "remove_from_locked_folder": "Odstrani iz zaklenjene mape", "remove_from_locked_folder_confirmation": "Ali ste prepričani, da Åželite premakniti te fotografije in videoposnetke iz zaklenjene mape? Vidni bodo v vaÅĄi knjiÅžnici.", "remove_from_shared_link": "Odstrani iz skupne povezave", @@ -1523,19 +1658,29 @@ "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", "reset_pin_code": "Ponastavi PIN kodo", + "reset_pin_code_description": "Če ste pozabili PIN kodo, se lahko za ponastavitev obrnete na skrbnika streÅžnika", + "reset_pin_code_success": "PIN koda je bila uspeÅĄno ponastavljena", + "reset_pin_code_with_password": "PIN-kodo lahko vedno ponastavite z geslom", + "reset_sqlite": "Ponastavi bazo podatkov SQLite", + "reset_sqlite_confirmation": "Ali ste prepričani, da Åželite ponastaviti bazo podatkov SQLite? Za ponovno sinhronizacijo podatkov se boste morali odjaviti in znova prijaviti", + "reset_sqlite_success": "UspeÅĄno ponastavljena baza podatkov SQLite", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "RazreÅĄi dvojnike", "resolved_all_duplicates": "RazreÅĄeni vsi dvojniki", "restore": "Obnovi", "restore_all": "Obnovi vse", + "restore_trash_action_prompt": "{count} obnovljenih iz koÅĄa", "restore_user": "Obnovi uporabnika", "restored_asset": "Obnovljeno sredstvo", "resume": "Nadaljuj", + "resume_paused_jobs": "Nadaljuj {count, plural, one {# zaustavljeno opravilo} two {# zaustavljeni opravili} few {# zaustavljena opravila} other {# zaustavljenih opravil}}", "retry_upload": "Poskusite znova naloÅžiti", "review_duplicates": "Pregled dvojnikov", + "review_large_files": "Pregled velikih datotek", "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", + "running": "V teku", "save": "Shrani", "save_to_gallery": "Shrani v galerijo", "saved_api_key": "Shranjen API ključ", @@ -1622,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Albuma ni bilo mogoče ustvariti", "selected": "Izbrano", "selected_count": "{count, plural, other {# izbranih}}", + "selected_gps_coordinates": "izbrane GPS koordinate", "send_message": "PoÅĄlji sporočilo", "send_welcome_email": "PoÅĄlji pozdravno e-poÅĄto", "server_endpoint": "Končna točka streÅžnika", @@ -1667,6 +1813,7 @@ "settings_saved": "Nastavitve shranjene", "setup_pin_code": "Nastavi PIN kodo", "share": "Deli", + "share_action_prompt": "Deljena sredstva {count}", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} izbrano", "share_dialog_preparing": "Priprava...", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Kopirano v odloÅžiÅĄÄe", "shared_link_clipboard_text": "Povezava: {link}\nGeslo: {password}", "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", + "shared_link_custom_url_description": "Dostop do te deljene povezave z URL-jem po meri", "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": "{count} dni", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", "shared_link_options": "MoÅžnosti skupne povezave", + "shared_link_password_description": "Za dostop do te deljene povezave je potrebno geslo", "shared_links": "Povezave v skupni rabi", "shared_links_description": "Deli fotografije in videoposnetke s povezavo", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljenih fotografij & videoposnetkov.}}", @@ -1747,6 +1896,7 @@ "show_slideshow_transition": "PrikaÅži prehod diaprojekcije", "show_supporter_badge": "Značka podpornika", "show_supporter_badge_description": "PrikaÅži značko podpornika", + "show_text_search_menu": "PrikaÅži meni za iskanje po besedilu", "shuffle": "Naključno", "sidebar": "Stranska vrstica", "sidebar_display_description": "PrikaÅži povezavo do pogleda v stranski vrstici", @@ -1762,12 +1912,14 @@ "sort_created": "Datum nastanka", "sort_items": "Å tevilo predmetov", "sort_modified": "Datum spremembe", + "sort_newest": "NajnovejÅĄa fotografija", "sort_oldest": "NajstarejÅĄa fotografija", "sort_people_by_similarity": "Razvrsti ljudi po podobnosti", "sort_recent": "NajnovejÅĄa fotografija", "sort_title": "Naslov", "source": "Vir", "stack": "Sklad", + "stack_action_prompt": "{count} naloÅženih", "stack_duplicates": "Nabor dvojnikov", "stack_select_one_photo": "Izberite eno glavno fotografijo za nabor", "stack_selected_photos": "Nabor izbranih fotografij", @@ -1775,6 +1927,7 @@ "stacktrace": "Sled nabora", "start": "Začetek", "start_date": "Datum začetka", + "start_date_before_end_date": "Začetni datum mora biti pred končnim datumom", "state": "DeÅžela", "status": "Status", "stop_casting": "Ustavi predvajanje", @@ -1787,6 +1940,7 @@ "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "PredloÅži", + "success": "Uspeh", "suggestions": "Predlogi", "sunrise_on_the_beach": "Sončni vzhod na plaÅži", "support": "Podpora", @@ -1796,6 +1950,10 @@ "sync": "Sinhronizacija", "sync_albums": "Sinhronizacija albumov", "sync_albums_manual_subtitle": "Sinhronizirajte vse naloÅžene videoposnetke in fotografije v izbrane varnostne albume", + "sync_local": "Sinhroniziraj lokalno", + "sync_remote": "Sinhroniziraj oddaljeno", + "sync_status": "Stanje sinhronizacije", + "sync_status_subtitle": "Ogled in upravljanje sistema sinhronizacije", "sync_upload_album_setting_subtitle": "Ustvarite in naloÅžite svoje fotografije in videoposnetke v izbrane albume na Immich", "tag": "Oznaka", "tag_assets": "Označi sredstva", @@ -1806,6 +1964,7 @@ "tag_updated": "Posodobljena oznaka: {tag}", "tagged_assets": "Označeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "tags": "Oznake", + "tap_to_run_job": "Dotaknite se za zagon opravila", "template": "Predloga", "theme": "Tema", "theme_selection": "Izbira teme", @@ -1832,12 +1991,15 @@ "to_change_password": "Spremeni geslo", "to_favorite": "Priljubljen", "to_login": "Prijava", + "to_multi_select": "izbira več elementov", "to_parent": "Pojdi na prvotno", + "to_select": "na izbiro", "to_trash": "Smetnjak", "toggle_settings": "Preklopi na nastavitve", "total": "Skupno", "total_usage": "Skupna poraba", "trash": "Smetnjak", + "trash_action_prompt": "premaknjeno v smetnjak {count}", "trash_all": "Vse v smetnjak", "trash_count": "Smetnjak {count, number}", "trash_delete_asset": "V smetnjak/izbriÅĄi sredstvo", @@ -1851,13 +2013,16 @@ "trash_page_select_assets_btn": "Izberite sredstva", "trash_page_title": "Smetnjak ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementi v smetnjaku bodo trajno izbrisani po {days, plural, one {# dnevu} two {# dnevih} few {# dnevih} other {# dneh}}.", + "troubleshoot": "Odpravljanje teÅžav", "type": "Vrsta", "unable_to_change_pin_code": "PIN kode ni mogoče spremeniti", "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", + "unarchive_action_prompt": "{count} odstranjenih iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", "undo": "Razveljavi", "unfavorite": "Odznači priljubljeno", + "unfavorite_action_prompt": "{count} odstranjenih iz priljubljenih", "unhide_person": "PrikaÅži osebo", "unknown": "Neznano", "unknown_country": "Neznana drÅžava", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "Odznači vse dvojnike", "unselect_all_in": "Prekliči izbor vseh v {group}", "unstack": "Razklad", + "unstack_action_prompt": "{count} razloÅženih", "unstacked_assets_count": "RazloÅži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "untagged": "Neoznačeno", "up_next": "Naslednja", + "update_location_action_prompt": "Posodobi lokacijo izbranih sredstev {count} s/z:", "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "NaloÅži", + "upload_action_prompt": "{count} v čakalni vrsti za nalaganje", "upload_concurrency": "Sočasnost nalaganja", + "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali Åželite varnostno kopirati izbrana sredstva na streÅžnik?", "upload_dialog_title": "NaloÅži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osveÅžite stran, da vidite nova sredstva za nalaganje.", + "upload_finished": "Nalaganje končano", "upload_progress": "Preostalo {remaining, number} - Obdelano {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočeno {count, plural, one {# podvojeno sredstvo} two {# podvojeni sredstvi} few {# podvojena sredstva} other {# podvojenih sredstev}}", "upload_status_duplicates": "Dvojniki", @@ -1892,6 +2063,7 @@ "upload_success": "Nalaganje je uspelo, osveÅžite stran, da vidite nova sredstva za nalaganje.", "upload_to_immich": "NaloÅži v Immich ({count})", "uploading": "Nalagam", + "uploading_media": "Nalaganje medijev", "url": "URL", "usage": "Uporaba", "use_biometric": "Uporabite biometrične podatke", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "Oglejte si statistiko uporabe računa", "username": "UporabniÅĄko ime", "users": "Uporabniki", + "users_added_to_album_count": "V album {count, plural, one {je bil dodan # uporabnik} two {sta bila dodana # uporabnika} few {so bili dodani # uporabniki} other {je bilo dodanih # uporanikov}}", "utilities": "Pripomočki", "validate": "Potrdi", "validate_endpoint_error": "Vnesite veljaven URL", @@ -1930,6 +2103,7 @@ "view_album": "Ogled albuma", "view_all": "Poglej vse", "view_all_users": "Ogled vseh uporabnikov", + "view_details": "Ogled podrobnosti", "view_in_timeline": "Ogled na časovnici", "view_link": "Odpri povezavo", "view_links": "Ogled povezav", @@ -1937,6 +2111,7 @@ "view_next_asset": "Ogled naslednjega sredstva", "view_previous_asset": "Ogled prejÅĄnjega sredstva", "view_qr_code": "Oglej si kodo QR", + "view_similar_photos": "Oglejte si podobne fotografije", "view_stack": "Ogled sklada", "view_user": "Poglej uporabnika", "viewer_remove_from_stack": "Odstrani iz sklada", @@ -1955,5 +2130,6 @@ "yes": "Da", "you_dont_have_any_shared_links": "Nimate nobenih skupnih povezav", "your_wifi_name": "VaÅĄe ime Wi-Fi", - "zoom_image": "Povečava slike" + "zoom_image": "Povečava slike", + "zoom_to_bounds": "Povečaj do meja" } diff --git a/i18n/sq.json b/i18n/sq.json index 0967ef424b..de7c5faa27 100644 --- a/i18n/sq.json +++ b/i18n/sq.json @@ -1 +1,59 @@ -{} +{ + "about": "Rreth", + "account": "Llogari", + "account_settings": "CilÃĢsimet e LlogarisÃĢ", + "acknowledge": "Prano", + "action": "Aksion", + "action_common_update": "PÃĢrditÃĢso", + "actions": "Aksione", + "active": "Aktiv", + "activity": "Aktivitet", + "activity_changed": "Aktiviteti ÃĢshtÃĢ {enabled, select, true {aktivizuar} other {çaktivizuar}}", + "add": "Shto", + "add_a_description": "Shto njÃĢ pÃĢrshkrim", + "add_a_location": "Shto njÃĢ vendndodhje", + "add_a_name": "Shto njÃĢ emÃĢr", + "add_a_title": "Shto njÃĢ titull", + "add_birthday": "Shto njÃĢ ditÃĢlindje", + "add_endpoint": "Shto njÃĢ endpoint", + "add_exclusion_pattern": "Shto model pÃĢrjashtimi", + "add_import_path": "Shto vÃĢnd importimi", + "add_location": "Shto vendndodhje", + "add_more_users": "Shto mÃĢ shumÃĢ pÃĢrdorues", + "add_partner": "Shto partner", + "add_path": "Shto path", + "add_photos": "Shto foto", + "add_tag": "Shto tag", + "add_to": "Shto nÃĢâ€Ļ", + "add_to_album": "Shto nÃĢ album", + "add_to_album_bottom_sheet_added": "Shtuar nÃĢ {album}", + "add_to_album_bottom_sheet_already_exists": "Existon nÃĢ {album}", + "add_to_album_toggle": "Aktivizo/çaktivizo zgjedhjen pÃĢr {album}", + "add_to_albums": "Shto nÃĢ albume", + "add_to_albums_count": "Shto nÃĢ albume ({count})", + "add_to_shared_album": "Shto nÃĢ album tÃĢ hapur", + "add_url": "Shto URL", + "added_to_archive": "Shtuar nÃĢ arkiv", + "added_to_favorites": "Shtuar tek tÃĢ preferuarat", + "added_to_favorites_count": "Shtuar {count, number} nÃĢ tÃĢ preferuarat", + "admin": { + "add_exclusion_pattern_description": "Shto modele pÃĢrjashtimi. MbÃĢshtetet globimi duke pÃĢrdorur *, ** dhe ?. PÃĢr tÃĢ injoruar tÃĢ gjithÃĢ skedarÃĢt nÃĢ Ã§do drejtori tÃĢ quajtur \"Raw\", pÃĢrdorni \"**/Raw/**\". PÃĢr tÃĢ injoruar tÃĢ gjithÃĢ skedarÃĢt qÃĢ mbarojnÃĢ me \".tif\", pÃĢrdorni \"**/*.tif\". PÃĢr tÃĢ injoruar njÃĢ shteg absolut, pÃĢrdorni \"/path/to/ignore/**\".", + "admin_user": "PÃĢrdorues Administrator", + "asset_offline_description": "Ky aset i bibliotekÃĢs sÃĢ jashtme nuk gjendet mÃĢ nÃĢ disk dhe ÃĢshtÃĢ zhvendosur nÃĢ koshin e plehrave. NÃĢse skedari ÃĢshtÃĢ zhvendosur brenda bibliotekÃĢs, kontrolloni kronologjinÃĢ tuaj pÃĢr asetin e ri pÃĢrkatÃĢs. PÃĢr tÃĢ rivendosur kÃĢtÃĢ aset, sigurohuni qÃĢ shtegu i skedarit mÃĢ poshtÃĢ tÃĢ jetÃĢ i arritshÃĢm nga Immich dhe skanoni bibliotekÃĢn.", + "authentication_settings": "CilÃĢsimet e vÃĢrtetimit tÃĢ pÃĢrdoruesit", + "authentication_settings_description": "Manaxho passwordin, OAuth, dhe cilÃĢsime tÃĢ tjera tÃĢ", + "authentication_settings_disable_all": "Je i sigurt qÃĢ dÃĢshiron tÃĢ Ã§aktivizosh tÃĢ gjitha metodat e hyrjes? Hyrja do tÃĢ Ã§aktivizohet plotÃĢsisht.", + "authentication_settings_reenable": "PÃĢr ta riaktivizuar, pÃĢrdorni njÃĢ KomandÃĢ Serveri.", + "background_task_job": "Detyrat nÃĢ Sfond", + "backup_database": "Krijo demp tÃĢ databaseit", + "backup_database_enable_description": "Aktivizo demp-et e bazÃĢs sÃĢ tÃĢ dhÃĢnave", + "backup_keep_last_amount": "Sasia e deponive tÃĢ mÃĢparshme pÃĢr t'u mbajtur", + "backup_onboarding_1_description": "kopje nÃĢ cloud ose nÃĢ njÃĢ vendndodhje tjetÃĢr fizike.", + "backup_onboarding_2_description": "kopje lokale nÃĢ pajisje tÃĢ ndryshme. Kjo pÃĢrfshin skedarÃĢt kryesorÃĢ dhe njÃĢ kopje rezervÃĢ tÃĢ kÃĢtyre skedarÃĢve lokalisht.", + "backup_onboarding_3_description": "kopje totale tÃĢ tÃĢ dhÃĢnave tuaja, duke pÃĢrfshirÃĢ skedarÃĢt origjinalÃĢ. Kjo pÃĢrfshin 1 kopje jashtÃĢ faqes dhe 2 kopje lokale.", + "backup_onboarding_description": "Rekomandohet njÃĢ strategji 3-2-1 pÃĢr ruajtjen e tÃĢ dhÃĢnave tuaja. Duhet tÃĢ ruani kopje tÃĢ fotove/videove tÃĢ ngarkuara, si dhe tÃĢ bazÃĢs sÃĢ tÃĢ dhÃĢnave tÃĢ Immich pÃĢr njÃĢ zgjidhje gjithÃĢpÃĢrfshirÃĢse tÃĢ ruajtjes sÃĢ tÃĢ dhÃĢnave.", + "backup_onboarding_footer": "PÃĢr mÃĢ shumÃĢ informacion pÃĢr tÃĢ krijuar njÃĢ kopje rezervÃĢ tÃĢ Immich, ju lutem referouni tek dokumentimi.", + "backup_onboarding_parts_title": "NjÃĢ kopje rezervÃĢ 3-2-1 ka:", + "backup_onboarding_title": "Kopje rezervÃĢ" + } +} diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index b518bb39af..9c4d01dc3d 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -458,7 +458,6 @@ "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 {{name}} other {ĐŊОви аĐģĐąŅƒĐŧ}}", "assets_count": "{count, plural, one {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа} few {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ} other {# Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа}}", "assets_deleted_permanently": "{count} ĐĩĐģĐĩĐŧĐĩĐŊĐ°Ņ‚Đ° Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°ĐŊĐž", "assets_deleted_permanently_from_server": "{count} Ņ€ĐĩŅŅƒŅ€Ņ(а) Ņ‚Ņ€Đ°Ņ˜ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°ĐŊ(а) ŅĐ° Immich ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", @@ -480,6 +479,7 @@ "back_close_deselect": "Назад, ĐˇĐ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ иĐģи ĐžĐŋĐžĐˇĐžĐ˛Đ¸Ņ‚Đĩ Đ¸ĐˇĐąĐžŅ€", "background_location_permission": "ДозвоĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ҃ ĐŋОСадиĐŊи", "background_location_permission_content": "Да йи ҁĐĩ ĐŧĐĩŅšĐ°ĐģĐĩ ĐŧŅ€ĐĩĐļĐĩ Đ´ĐžĐē ҁĐĩ Ņ€Đ°Đ´Đ¸ ҃ ĐŋОСадиĐŊи, ИĐŧĐ¸Ņ… ĐŧĐžŅ€Đ° *ŅƒĐ˛ĐĩĐē* иĐŧĐ°Ņ‚Đ¸ ĐŋŅ€ĐĩŅ†Đ¸ĐˇĐ°ĐŊ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ¸ ĐēаĐēĐž йи аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŧĐžĐŗĐģа да ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ° иĐŧĐĩ Wi-Fi ĐŧŅ€ĐĩĐļĐĩ", + "backup": "НаĐŋŅ€Đ°Đ˛Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋĐ¸Ņ˜Ņƒ", "backup_album_selection_page_albums_device": "АĐģĐąŅƒĐŧа ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ ({count})", "backup_album_selection_page_albums_tap": "Đ”ĐžĐ´Đ¸Ņ€ĐŊи да ҃ĐēŅ™ŅƒŅ‡Đ¸Ņˆ, Đ´ĐžĐ´Đ¸Ņ€ĐŊи дваĐŋŅƒŅ‚ да Đ¸ŅĐēŅ™ŅƒŅ‡Đ¸Ņˆ", "backup_album_selection_page_assets_scatter": "ЗаĐŋĐ¸ŅĐ¸ ҁĐĩ ĐŧĐžĐŗŅƒ ĐŊĐ°Ņ›Đ¸ ҃ Đ˛Đ¸ŅˆĐĩ Ņ€Đ°ĐˇĐģĐ¸Ņ‡Đ¸Ņ‚Đ¸Ņ… аĐģĐąŅƒĐŧа. ĐžĐ´Đ°Ņ‚ĐģĐĩ аĐģĐąŅƒĐŧи ҁĐĩ ĐŧĐžĐŗŅƒ ҃ĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đ¸ иĐģи Đ¸ŅĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đ¸ Ņ‚ĐžĐēĐžĐŧ ĐŋŅ€ĐžŅ†ĐĩŅĐ° ĐŋŅ€Đ°Đ˛Ņ™ĐĩŅšĐ° ĐŋОСадиĐŊҁĐēĐ¸Ņ… ĐēĐžĐŋĐ¸Ņ˜Đ°.", @@ -927,9 +927,6 @@ "exif_bottom_sheet_location": "ЛОКАĐĻИЈА", "exif_bottom_sheet_people": "ПЕОПЛЕ", "exif_bottom_sheet_person_add_person": "Адд name", - "exif_bottom_sheet_person_age_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ {months} ĐŧĐĩҁĐĩŅ†Đ¸", - "exif_bottom_sheet_person_age_year_months": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ 1 ĐŗĐžĐ´Đ¸ĐŊа, {months} ĐŧĐĩҁĐĩŅ†Đ¸", - "exif_bottom_sheet_person_age_years": "ĐĄŅ‚Đ°Ņ€ĐžŅŅ‚ {years}", "exit_slideshow": "Đ˜ĐˇĐ°Ņ’Đ¸ иС ĐŋŅ€ĐžŅ˜ĐĩĐēŅ†Đ¸Ņ˜Đĩ ҁĐģĐ°Ņ˜Đ´ĐžĐ˛Đ°", "expand_all": "ĐŸŅ€ĐžŅˆĐ¸Ņ€Đ¸ ŅĐ˛Đĩ", "experimental_settings_new_asset_list_subtitle": "ĐŖ Đ¸ĐˇŅ€Đ°Đ´Đ¸", @@ -1086,7 +1083,6 @@ "light": "ХвĐĩŅ‚ĐģĐž", "like_deleted": "Đ›Đ°Ņ˜Đē҃Ҙ Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊĐž", "link_motion_video": "НаĐŋŅ€Đ°Đ˛Đ¸ вĐĩĐˇŅƒ Са видĐĩĐž СаĐŋĐ¸Ņ", - "link_options": "ОĐŋŅ†Đ¸Ņ˜Đĩ вĐĩСĐĩ", "link_to_oauth": "ВĐĩСа Đ´Đž OAuth-а", "linked_oauth_account": "ПовĐĩСаĐŊи OAuth ĐŊаĐģĐžĐŗ", "list": "ИСĐģĐ¸ŅŅ‚Đ°Ņ˜", @@ -1145,7 +1141,6 @@ "manage_your_devices": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜Đ¸Đŧ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™ĐĩĐŊиĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ¸Đŧа", "manage_your_oauth_connection": "ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜ĐžĐŧ OAuth вĐĩСОĐŧ", "map": "МаĐŋа", - "map_assets_in_bound": "{count} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ°", "map_cannot_get_user_location": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ†ĖĐĩ Đ´ĐžĐąĐ¸Ņ‚Đ¸ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ĐēĐžŅ€Đ¸ŅĐŊиĐēа", "map_location_dialog_yes": "Да", @@ -1154,7 +1149,6 @@ "map_location_service_disabled_title": "ĐŖŅĐģŅƒĐŗĐ° ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ ҘĐĩ oneĐŧĐžĐŗŅƒŅ†ĖĐĩĐŊа", "map_marker_for_images": "ОзĐŊĐ°Ņ‡Đ¸Đ˛Đ°Ņ‡ ĐŊа ĐŧаĐŋи Са ҁĐģиĐēĐĩ ҁĐŊиĐŧŅ™ĐĩĐŊĐĩ ҃ {city}, {country}", "map_marker_with_image": "ĐœĐ°Ņ€ĐēĐĩŅ€ ĐŊа ĐŧаĐŋи ŅĐ° ҁĐģиĐēĐžĐŧ", - "map_no_assets_in_bounds": "НĐĩĐŧа Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Đ¸Ņ˜Đ° ҃ ĐžĐ˛ĐžŅ˜ ОйĐģĐ°ŅŅ‚Đ¸", "map_no_location_permission_content": "ĐŸĐžŅ‚Ņ€ĐĩĐąĐŊа ҘĐĩ дОСвОĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ да йи ҁĐĩ ĐŋŅ€Đ¸ĐēаСаĐģи Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ŅĐ° Đ˛Đ°ŅˆĐĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊĐĩ ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đĩ. Да Đģи ĐļĐĩĐģĐ¸Ņ‚Đĩ да ҘĐĩ ŅĐ°Đ´Đ° дОСвОĐģĐ¸Ņ‚Đĩ?", "map_no_location_permission_title": "ДозвоĐģа Са ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Ņƒ ҘĐĩ ĐžĐ´ĐąĐ¸Ņ˜ĐĩĐŊа", "map_settings": "ПодĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ĐŧаĐŋĐĩ", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index aa92e6da49..06a76f8f0a 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -4,6 +4,7 @@ "account_settings": "PodeÅĄavanja za Profil", "acknowledge": "Potvrdi", "action": "Postupak", + "action_common_update": "AÅžuriraj", "actions": "Postupci", "active": "Aktivni", "activity": "Aktivnost", @@ -13,6 +14,7 @@ "add_a_location": "Dodaj Lokaciju", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rođendan", "add_endpoint": "Dodajte krajnju tačku", "add_exclusion_pattern": "Dodajte obrazac izuzimanja", "add_import_path": "Dodaj putanju za preuzimanje", @@ -21,10 +23,14 @@ "add_partner": "Dodaj partner", "add_path": "Dodaj putanju", "add_photos": "Dodaj fotografije", + "add_tag": "Dodaj oznaku", "add_to": "Dodaj uâ€Ļ", "add_to_album": "Dodaj u album", "add_to_album_bottom_sheet_added": "Dodato u {album}", "add_to_album_bottom_sheet_already_exists": "Već u {album}", + "add_to_album_toggle": "Uključi/isključi izbor za {album}", + "add_to_albums": "Dodaj u albume", + "add_to_albums_count": "Dodaj u albume ({count})", "add_to_shared_album": "Dodaj u deljen album", "add_url": "Dodaj URL", "added_to_archive": "Dodato u arhivu", @@ -32,6 +38,7 @@ "added_to_favorites_count": "Dodato {count, number} u favorite", "admin": { "add_exclusion_pattern_description": "Dodajte obrasce isključenja. KoriÅĄtenje *, ** i ? je podrÅžano. Da biste ignorisali sve datoteke u bilo kom direktorijumu pod nazivom „Rav“, koristite „**/Rav/**“. Da biste ignorisali sve datoteke koje se zavrÅĄavaju na „.tif“, koristite „**/*.tif“. Da biste ignorisali apsolutnu putanju, koristite „/path/to/ignore/**“.", + "admin_user": "Administrator", "asset_offline_description": "Ovo eksterno bibliotečko sredstvo se viÅĄe ne nalazi na disku i premeÅĄteno je u smecˁe. Ako je datoteka premeÅĄtena unutar biblioteke, proverite svoju vremensku liniju za novo odgovarajucˁe sredstvo. Da biste vratili ovo sredstvo, uverite se da Immich moÅže da pristupi dole navedenoj putanji datoteke i skenirajte biblioteku.", "authentication_settings": "PodeÅĄavanja za autentifikaciju", "authentication_settings_description": "Upravljajte lozinkom, OAuth-om i drugim podeÅĄavanjima autentifikacije", @@ -41,8 +48,15 @@ "backup_database": "Kreirajte rezervnu kopiju baze podataka", "backup_database_enable_description": "Omogucˁi dampove baze podataka", "backup_keep_last_amount": "Količina prethodnih dampova koje treba zadrÅžati", + "backup_onboarding_1_description": "kopija na oblaku ili na drugoj fizičkoj lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različitim uređajima. Ovo uključuje glavne datoteke i rezervnu kopiju tih datoteka lokalno.", + "backup_onboarding_3_description": "ukupno kopija vaÅĄih podataka, uklučujući originalne datoteke. Ovo uključuje 1 udaljenu kopiju i 2 lokalne kopije.", + "backup_onboarding_description": "3-2-1 strategija rezervnih kopija je preporučena da zaÅĄtiti vaÅĄe podatke. Trebali biste čuvati kopije vaÅĄih otpremljenih slika/videa kao i Immich bazu podataka za sveobuhvatno reÅĄenje za rezervne kopije.", + "backup_onboarding_footer": "Za viÅĄe informacija o pravljenju rezervne kopije Immich-a, molimo vas pogledajte dokumentaciju.", + "backup_onboarding_parts_title": "3-2-1 rezervna kopija uključuje:", + "backup_onboarding_title": "Rezervne kopije", "backup_settings": "PodeÅĄavanja dampa baze podataka", - "backup_settings_description": "Upravljajte podeÅĄavanjima dampa baze podataka. Napomena: Ovi poslovi se ne prate i necˁete biti obaveÅĄteni o neuspehu.", + "backup_settings_description": "Upravljajte podeÅĄavanjima dampa baze podataka.", "cleared_jobs": "OčiÅĄcˁeni poslovi za: {job}", "config_set_by_file": "Konfiguraciju trenutno postavlja konfiguracioni fajl", "confirm_delete_library": "Da li stvarno Åželite da izbriÅĄete biblioteku {library} ?", @@ -163,12 +177,23 @@ "metadata_settings_description": "Upravljajte podeÅĄavanjima metapodataka", "migration_job": "Migracije", "migration_job_description": "Prenesite sličice datoteka i lica u najnoviju strukturu direktorijuma", + "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novodetektovanim licima", + "nightly_tasks_cluster_new_faces_setting": "ZdruÅži nova lica", + "nightly_tasks_database_cleanup_setting": "Zadaci čiÅĄÄenja baze podataka", + "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", + "nightly_tasks_generate_memories_setting": "GeneriÅĄi sjećanja", + "nightly_tasks_generate_memories_setting_description": "Kreiraj nova sjećanja", + "nightly_tasks_missing_thumbnails_setting": "GeneriÅĄi nedostajuće sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Dodajte elemente bez sličica u red za generisanje sličica", + "nightly_tasks_settings": "PodeÅĄavanja noćnih zadataka", + "nightly_tasks_settings_description": "Upravljaj noćnim zadacima", + "nightly_tasks_sync_quota_usage_setting_description": "AÅžurirajte kvotu memorijskog prostora korisnika na osnovu trenutne upotrebe", "no_paths_added": "Nema dodatih putanja", "no_pattern_added": "Nije dodat obrazac", "note_apply_storage_label_previous_assets": "Napomena: Da biste primenili oznaku za skladiÅĄtenje na prethodno otpremljena sredstva, pokrenite", "note_cannot_be_changed_later": "NAPOMENA: Ovo se kasnije ne moÅže promeniti!", "notification_email_from_address": "Sa adrese", - "notification_email_from_address_description": "Adresa e-poÅĄte poÅĄiljaoca, na primer: \"Immich foto server \"", + "notification_email_from_address_description": "Adresa e-poÅĄte poÅĄiljaoca, na primer: \"Immich foto server \". Pobrinite se da koristite adresu sa koje vam je dozovljeno slati e-poÅĄtu.", "notification_email_host_description": "Host servera e-poÅĄte (npr. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Zanemarite greÅĄke sertifikata", "notification_email_ignore_certificate_errors_description": "IgnoriÅĄite greÅĄke u validaciji TLS sertifikata (ne preporučuje se)", @@ -201,7 +226,7 @@ "oauth_storage_quota_claim": "Zahtev za kvotu skladiÅĄtenja", "oauth_storage_quota_claim_description": "Automatski podesite kvotu memorijskog prostora korisnika na vrednost ovog zahteva.", "oauth_storage_quota_default": "Podrazumevana kvota za skladiÅĄtenje (GiB)", - "oauth_storage_quota_default_description": "Kvota u GiB koja se koristi kada nema potraÅživanja (unesite 0 za neograničenu kvotu).", + "oauth_storage_quota_default_description": "Kvota u GiB koja se koristi kada nema potraÅživanja.", "oauth_timeout": "Vremensko ograničenje zahteva", "oauth_timeout_description": "Vremensko ograničenje za zahteve u milisekundama", "password_enable_description": "Prijavite se pomocˁu e-poÅĄte i lozinke", @@ -456,7 +481,6 @@ "assets": "Zapisi", "assets_added_count": "Dodato {count, plural, one {# datoteka} other {# datoteka}}", "assets_added_to_album_count": "Dodato je {count, plural, one {# datoteka} other {# datoteka}} u album", - "assets_added_to_name_count": "Dodato {count, plural, one {# datoteka} other {# datoteke}} u {hasName, select, true {{name}} other {novi album}}", "assets_count": "{count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", "assets_deleted_permanently": "{count} elemenata trajno obrisano", "assets_deleted_permanently_from_server": "{count} resurs(a) trajno obrisan(a) sa Immich servera", @@ -478,6 +502,7 @@ "back_close_deselect": "Nazad, zatvorite ili opozovite izbor", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Da bi se menjale mreÅže dok se radi u pozadini, Imih mora *uvek* imati precizan pristup lokaciji kako bi aplikacija mogla da pročita ime Wi-Fi mreÅže", + "backup": "Napravi rezervnu kopiju", "backup_album_selection_page_albums_device": "Albuma na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirni da uključiÅĄ, dodirni dvaput da isključiÅĄ", "backup_album_selection_page_assets_scatter": "Zapisi se mogu naći u viÅĄe različitih albuma. Odatle albumi se mogu uključiti ili isključiti tokom procesa pravljenja pozadinskih kopija.", @@ -508,7 +533,7 @@ "backup_controller_page_background_turn_off": "Isključi pozadinski servis", "backup_controller_page_background_turn_on": "Uključi pozadinski servis", "backup_controller_page_background_wifi": "Samo na Wi-Fi", - "backup_controller_page_backup": "Napravi rezervnu kopiju", + "backup_controller_page_backup": "Rezervne kopije", "backup_controller_page_backup_selected": "Odabrano: ", "backup_controller_page_backup_sub": "ZavrÅĄeno pravljenje rezervne kopije fotografija i videa", "backup_controller_page_created": "Napravljeno:{date}", @@ -519,8 +544,8 @@ "backup_controller_page_id": "ID:{id}", "backup_controller_page_info": "Informacije", "backup_controller_page_none_selected": "NiÅĄta odabrano", - "backup_controller_page_remainder": "Podsetnik", - "backup_controller_page_remainder_sub": "Ostalo fotografija i videa da se otpremi od selekcije", + "backup_controller_page_remainder": "Ostatak", + "backup_controller_page_remainder_sub": "Ostale fotografije i video snimci za otpremanje od selekcije", "backup_controller_page_server_storage": "Prostor na serveru", "backup_controller_page_start_backup": "Pokreni pravljenje rezervne kopije", "backup_controller_page_status_off": "Automatsko pravljenje rezervnih kopija u prvom planu je isključeno", @@ -913,9 +938,6 @@ "exif_bottom_sheet_description": "Dodaj opis...", "exif_bottom_sheet_details": "DETALJI", "exif_bottom_sheet_location": "LOKACIJA", - "exif_bottom_sheet_person_age_months": "Starost {months} meseci", - "exif_bottom_sheet_person_age_year_months": "Starost 1 godina, {months} meseci", - "exif_bottom_sheet_person_age_years": "Starost {years}", "exit_slideshow": "Izađi iz projekcije slajdova", "expand_all": "ProÅĄiri sve", "experimental_settings_new_asset_list_subtitle": "U izradi", @@ -1071,7 +1093,6 @@ "light": "Svetlo", "like_deleted": "Lajkuj izbrisano", "link_motion_video": "Napravi vezu za video zapis", - "link_options": "Opcije veze", "link_to_oauth": "Veza do OAuth-a", "linked_oauth_account": "Povezani OAuth nalog", "list": "Izlistaj", @@ -1128,7 +1149,6 @@ "manage_your_devices": "Upravljajte svojim prijavljenim uređajima", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Mapa", - "map_assets_in_bound": "{count} fotografija", "map_assets_in_bounds": "{count} fotografija", "map_cannot_get_user_location": "Nije mogucˁe dobiti lokaciju korisnika", "map_location_dialog_yes": "Da", @@ -1137,7 +1157,6 @@ "map_location_service_disabled_title": "Usluga lokacije je onemogucˁena", "map_marker_for_images": "Označivač na mapi za slike snimljene u {city}, {country}", "map_marker_with_image": "Marker na mapi sa slikom", - "map_no_assets_in_bounds": "Nema fotografija u ovoj oblasti", "map_no_location_permission_content": "Potrebna je dozvola za lokaciju da bi se prikazali resursi sa vaÅĄe trenutne lokacije. Da li Åželite da je sada dozvolite?", "map_no_location_permission_title": "Dozvola za lokaciju je odbijena", "map_settings": "PodeÅĄavanja mape", diff --git a/i18n/sv.json b/i18n/sv.json index a4c08f8c22..fba673610a 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -14,6 +14,7 @@ "add_a_location": "Lägg till en plats", "add_a_name": "Lägg till ett namn", "add_a_title": "Lägg till en titel", + "add_birthday": "Lägg till fÃļdelsedag", "add_endpoint": "Lägg till ändpunkt", "add_exclusion_pattern": "Lägg till uteslutningsmÃļnster", "add_import_path": "Lägg till importsÃļkväg", @@ -22,10 +23,15 @@ "add_partner": "Lägg till partner", "add_path": "Lägg till sÃļkväg", "add_photos": "Lägg till foton", + "add_tag": "Lägg till tagg", "add_to": "Lägg till iâ€Ļ", "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", "add_to_album_bottom_sheet_already_exists": "Redan i {album}", + "add_to_album_bottom_sheet_some_local_assets": "Vissa lokala tillgÃĨngar kunde inte läggas till i albumet", + "add_to_album_toggle": "Växla val fÃļr {album}", + "add_to_albums": "Lägg till i album", + "add_to_albums_count": "Lägg till i album ({count})", "add_to_shared_album": "Lägg till i delat album", "add_url": "Lägg till URL", "added_to_archive": "Tillagd i arkiv", @@ -33,6 +39,7 @@ "added_to_favorites_count": "{count, number} tillagda till favoriter", "admin": { "add_exclusion_pattern_description": "Lägg till exkluderande mÃļnster. Matchning med jokertecken *, ** samt ? stÃļdjs. FÃļr att ignorera alla filer i samtliga mappar som heter \"Raw\", använd \"**/Raw/**\". FÃļr att ignorera alla filer som slutar med \".tif\", använd \"**/*.tif\". FÃļr att ignorera en absolut sÃļkväg, använd \"/sÃļkväg/att/ignorera/**\".", + "admin_user": "Adminanvändare", "asset_offline_description": "Denna externa bibliotekstillgÃĨng finns inte längre pÃĨ disken och har flyttats till papperskorgen. Om filen flyttades inom biblioteket, kontrollera din tidslinje fÃļr den nya motsvarande tillgÃĨngen. FÃļr att ÃĨterställa denna tillgÃĨng, se till att filsÃļkvägen nedan kan nÃĨs av Immich och skanna biblioteket.", "authentication_settings": "Autentiseringsinställningar", "authentication_settings_description": "Hantera lÃļsenord, OAuth, och andra autentiseringsinställningar", @@ -42,6 +49,13 @@ "backup_database": "Skapa Databasdump", "backup_database_enable_description": "Aktivera dumpning av databas", "backup_keep_last_amount": "Antal databasdumpar att behÃĨlla", + "backup_onboarding_1_description": "extern kopia i molnet eller pÃĨ en annan fysisk plats.", + "backup_onboarding_2_description": "lokala kopior pÃĨ olika enheter. Detta inkluderar huvudfilerna och en lokal säkerhetskopia av dessa filer.", + "backup_onboarding_3_description": "totala kopior av dina data, inklusive originalfilerna. Detta inkluderar 1 extern kopia och 2 lokala kopior.", + "backup_onboarding_description": "En 3-2-1 säkerhetskopieringsstrategi rekommenderas fÃļr att skydda din data. Du bÃļr även spara kopior av dina uppladdade foton/videor samt Immich-databasen fÃļr en heltäckande säkerhetskopieringslÃļsning.", + "backup_onboarding_footer": "FÃļr mer information om säkerhetskopiering av Immich, se dokumentationen.", + "backup_onboarding_parts_title": "En 3-2-1 säkerhetskopiering inkluderar:", + "backup_onboarding_title": "Säkerhetskopior", "backup_settings": "Inställningar databasdump", "backup_settings_description": "Hantera inställningar fÃļr databasdumpning.", "cleared_jobs": "Rensade jobben fÃļr:{job}", @@ -110,6 +124,13 @@ "logging_enable_description": "Aktivera loggning", "logging_level_description": "Vilken loggnivÃĨ som ska användas vid aktivering.", "logging_settings": "Loggning", + "machine_learning_availability_checks": "Tillgänglighetskontroller", + "machine_learning_availability_checks_description": "Upptäck och fÃļredrar automatiskt tillgängliga maskininlärningsservrar", + "machine_learning_availability_checks_enabled": "Aktivera tillgänglighetskontroller", + "machine_learning_availability_checks_interval": "Kontrollera intervall", + "machine_learning_availability_checks_interval_description": "Intervall i millisekunder mellan tillgänglighetskontroller", + "machine_learning_availability_checks_timeout": "Begär timeout", + "machine_learning_availability_checks_timeout_description": "Timeout i millisekunder fÃļr tillgänglighetskontroller", "machine_learning_clip_model": "CLIP-modell", "machine_learning_clip_model_description": "Namnet pÃĨ en CLIP-modell listad här . Observera att du mÃĨste kÃļra ett \"Smart SÃļkning\" jobb fÃļr alla bilder när du ändrar modell.", "machine_learning_duplicate_detection": "Dubblettdetektering", @@ -164,12 +185,26 @@ "metadata_settings_description": "Hantera metadata-inställningar", "migration_job": "Migrering", "migration_job_description": "Migrera miniatyrbilder fÃļr resurser och ansikten till den senaste mappstrukturen", + "nightly_tasks_cluster_faces_setting_description": "KÃļr ansiktsigenkänning pÃĨ nyligen upptäckta ansikten", + "nightly_tasks_cluster_new_faces_setting": "Kluster nya ansikten", + "nightly_tasks_database_cleanup_setting": "Uppgifter fÃļr databassanering", + "nightly_tasks_database_cleanup_setting_description": "Rensa bort gammal, utgÃĨngen data frÃĨn databasen", + "nightly_tasks_generate_memories_setting": "Generera minnen", + "nightly_tasks_generate_memories_setting_description": "Skapa nya minnen frÃĨn tillgÃĨngar", + "nightly_tasks_missing_thumbnails_setting": "Generera saknade miniatyrbilder", + "nightly_tasks_missing_thumbnails_setting_description": "KÃļa resurser utan miniatyrer fÃļr generering av miniatyrer", + "nightly_tasks_settings": "Inställningar fÃļr nattliga uppgifter", + "nightly_tasks_settings_description": "Hantera nattliga uppgifter", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tidpunkten dÃĨ servern bÃļrjar kÃļra de nattliga uppgifterna", + "nightly_tasks_sync_quota_usage_setting": "Synkroniseringskvotanvändning", + "nightly_tasks_sync_quota_usage_setting_description": "Uppdatera användarlagringskvot baserat pÃĨ aktuell användning", "no_paths_added": "Inga vägar tillagda", "no_pattern_added": "Inga mÃļnster tillagda", "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten pÃĨ tidigare uppladdade tillgÃĨngar kÃļr du", "note_cannot_be_changed_later": "OBS: Detta kan inte ändras i efterhand!", "notification_email_from_address": "FrÃĨn adress", - "notification_email_from_address_description": "Avsändarens epost, t.ex.: \"Immich Fotoserver \"", + "notification_email_from_address_description": "Avsändarens e-post, till exempel: \"Immich Fotoserver \". Säkerställ att du använder en adress som du har tillÃĨtelse att skicka e-post frÃĨn.", "notification_email_host_description": "Värd fÃļr epostservern (t.ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorera certifikatfel", "notification_email_ignore_certificate_errors_description": "Ignorera valideringsfel fÃļr TLS-certifikat (rekommenderas ej)", @@ -194,6 +229,8 @@ "oauth_mobile_redirect_uri": "Telefonomdirigernings-URI", "oauth_mobile_redirect_uri_override": "Telefonomdirigerings-URI Ãļverrskridning", "oauth_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantÃļren inte tillÃĨter mobila URI:er, sÃĨ som ''{callback}''", + "oauth_role_claim": "RollansprÃĨk", + "oauth_role_claim_description": "Bevilja administratÃļrsÃĨtkomst automatiskt baserat pÃĨ fÃļrekomsten av detta pÃĨstÃĨende. PÃĨstÃĨendet kan innehÃĨlla antingen 'user' eller 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "FÃļr ytterligare detaljer om denna funktion, se dokumentationen.", @@ -202,7 +239,7 @@ "oauth_storage_quota_claim": "Användaranknuten lagringskvot", "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", - "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 fÃļr obegränsad kvot).", + "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts.", "oauth_timeout": "Begäran tog fÃļr lÃĨng tid", "oauth_timeout_description": "Timeout fÃļr fÃļrfrÃĨgningar i millisekunder", "password_enable_description": "Logga in med epost och lÃļsenord", @@ -242,6 +279,7 @@ "storage_template_migration_info": "Lagringsmallen kommer konvertera alla filändelser till gemena bokstäver. Ändringar gäller endast fÃļr nya resurser, fÃļr att retoaktivt tillämpa mallen pÃĨ befintliga resurser kÃļr {job}.", "storage_template_migration_job": "Lagringsmall migreringsjobb", "storage_template_more_details": "FÃļr mer information om den här funktionen se Lagringsmall och dess konsekvenser", + "storage_template_onboarding_description_v2": "Denna funktion kommer när den är aktiverad att auto-organisera filer baserat pÃĨ en användardefinierad mall. FÃļr mer information se dokumentationen.", "storage_template_path_length": "Uppskattad längdbegränsning pÃĨ sÃļkväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "Hantera mappstruktur och filnamn fÃļr uppladdade resurser", @@ -328,6 +366,9 @@ "trash_number_of_days_description": "Antal dagar fÃļr att fÃļrvara tillgÃĨngarna i papperskorgen innan de permanent tas bort", "trash_settings": "Papperskorginställningar", "trash_settings_description": "Hantera papperskorginställningar", + "unlink_all_oauth_accounts": "Ta bort länken till alla OAuth-konton", + "unlink_all_oauth_accounts_description": "Kom ihÃĨg att ta bort länken till alla OAuth-konton innan du migrerar till en ny leverantÃļr.", + "unlink_all_oauth_accounts_prompt": "Är du säker pÃĨ att du vill ta bort länken till alla OAuth-konton? Detta ÃĨterställer OAuth-ID:t fÃļr varje användare och kan inte ÃĨngras.", "user_cleanup_job": "Användarrensning", "user_delete_delay": "{user}s konto och tillgÃĨngar kommer att schemaläggas fÃļr permanent radering om {delay, plural, one {# day} other {# days}}.", "user_delete_delay_settings": "BorttagningsfÃļrdrÃļjning", @@ -361,6 +402,8 @@ "advanced_settings_prefer_remote_title": "FÃļredra bilder frÃĨn servern", "advanced_settings_proxy_headers_subtitle": "Definiera proxy-headers som Immich ska skicka med i varje närverksanrop", "advanced_settings_proxy_headers_title": "Proxy-headers", + "advanced_settings_readonly_mode_subtitle": "Aktiverar skrivskyddat läge där foton endast kan visas. Saker som att välja flera bilder, dela, casta och ta bort är alla inaktiverade. Aktivera/inaktivera skrivskyddat läge via användaravatar frÃĨn huvudskärmen", + "advanced_settings_readonly_mode_title": "Skrivskyddat läge", "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_subtitle": "Radera eller ÃĨterställ automatiskt en resurs pÃĨ den här enheten när den ÃĨtgärden utfÃļrs pÃĨ webben", @@ -376,6 +419,7 @@ "album_cover_updated": "Albumomslaget uppdaterat", "album_delete_confirmation": "Är du säker pÃĨ att du vill ta bort albumet {album}?", "album_delete_confirmation_description": "Om det här albumet delas kommer andra användare inte att kunna komma ÃĨt det längre.", + "album_deleted": "Albumet raderat", "album_info_card_backup_album_excluded": "EXKLUDERAD", "album_info_card_backup_album_included": "INKLUDERAD", "album_info_updated": "Albuminformation uppdaterad", @@ -385,7 +429,9 @@ "album_options": "Albumalternativ", "album_remove_user": "Ta bort användare?", "album_remove_user_confirmation": "Är du säker pÃĨ att du vill ta bort {user}?", + "album_search_not_found": "Inga album hittades som matchade din sÃļkning", "album_share_no_users": "Det verkar som att du har delat det här albumet med alla användare eller sÃĨ har du inte nÃĨgon användare att dela med.", + "album_summary": "Albumsammanfattning", "album_updated": "Albumet uppdaterat", "album_updated_setting_description": "FÃĨ ett e-postmeddelande när ett delat album har nya tillgÃĨngar", "album_user_left": "Lämnade {album}", @@ -401,6 +447,10 @@ "album_with_link_access": "LÃĨt alla med länken se foton och personer i det här albumet.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Standard sorteringsordning fÃļr album", + "albums_default_sort_order_description": "Standard sorteringsordning fÃļr mediefiler vid skapande av nytt album.", + "albums_feature_description": "Samlingar av mediefiler som kan delas med andra användare.", + "albums_on_device_count": "Album pÃĨ enheten ({count})", "all": "Allt", "all_albums": "Alla album", "all_people": "Alla personer", @@ -420,7 +470,9 @@ "app_bar_signout_dialog_title": "Logga ut", "app_settings": "Appinställningar", "appears_in": "Visas i", + "apply_count": "Tillämpa ({count, number})", "archive": "Arkiv", + "archive_action_prompt": "{count} adderade till Arkiv", "archive_or_unarchive_photo": "Arkivera eller oarkivera fotot", "archive_page_no_archived_assets": "Inga arkiverade objekt hittade", "archive_page_title": "Arkiv ({count})", @@ -451,6 +503,8 @@ "asset_restored_successfully": "Objekt ÃĨterställt", "asset_skipped": "Överhoppad", "asset_skipped_in_trash": "I papperskorgen", + "asset_trashed": "TillgÃĨng kasserad", + "asset_troubleshoot": "FelsÃļkning av tillgÃĨngar", "asset_uploaded": "Uppladdad", "asset_uploading": "Laddar upp...â€Ļ", "asset_viewer_settings_subtitle": "Hantera inställningar fÃļr gallerivisare", @@ -458,10 +512,14 @@ "assets": "Objekt", "assets_added_count": "La till {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet", - "assets_added_to_name_count": "Lade till {count, plural, one {# objekt} other {# objekt}} till {hasName, select, true {{name}} other {nytt album}}", + "assets_added_to_albums_count": "Lade till {assetTotal, plural, one {# asset} other {# assets}} till {albumTotal, plural, one {# album} other {# albums}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan inte läggas till i albumet", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} kan inte läggas till i nÃĨgot av albumen", "assets_count": "{count, plural, one {# objekt} other {# objekt}}", "assets_deleted_permanently": "{count} objekt har tagits bort permanent", "assets_deleted_permanently_from_server": "{count} objekt har tagits bort permanent frÃĨn Immich-servern", + "assets_downloaded_failed": "{count, plural, one {Nedladdad # fil - {error} fil misslyckades} other {Nedladdade # filer - {error} filer misslyckades}}", + "assets_downloaded_successfully": "{count, plural, one {Nedladdade # fil framgÃĨngsrikt} other {Nedladdade # filer framgÃĨngsrikt}}", "assets_moved_to_trash_count": "Flyttade {count, plural, one {# asset} other {# assets}} till papperskorgen", "assets_permanently_deleted_count": "Raderad permanent {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Tog bort {count, plural, one {# asset} other {# assets}}", @@ -473,19 +531,25 @@ "assets_trashed_count": "Till Papperskorgen {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} objekt raderade frÃĨn Immich-servern", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Asset were}} är redan en del av albumet", + "assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Asset were}} redan del av albumen", "authorized_devices": "Auktoriserade enheter", "automatic_endpoint_switching_subtitle": "Anslut lokalt via det angivna Wi-Fi-nätverket när det är tillgängligt och använd alternativa anslutningar pÃĨ andra platser", "automatic_endpoint_switching_title": "Automatisk URL-växling", + "autoplay_slideshow": "Spela upp bildspel automatiskt", "back": "BakÃĨt", "back_close_deselect": "Tillbaka, stäng eller avmarkera", + "background_backup_running_error": "Bakgrundssäkerhetskopiering kÃļrs fÃļr närvarande, kan inte starta manuell säkerhetskopiering", "background_location_permission": "TillÃĨtelse fÃļr bakgrundsplats", "background_location_permission_content": "FÃļr att kunna byta nätverk när appen kÃļrs i bakgrunden mÃĨste Immich *alltid* ha ÃĨtkomst till exakt plats sÃĨ att appen kan läsa av Wi-Fi-nätverkets namn", + "background_options": "Bakgrundsalternativ", + "backup": "Säkerhetskopiera", "backup_album_selection_page_albums_device": "Album pÃĨ enhet ({count})", "backup_album_selection_page_albums_tap": "Tryck en gÃĨng fÃļr att inkludera, tryck tvÃĨ gÃĨnger fÃļr att exkludera", "backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda Ãļver flera album. DärfÃļr kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen.", "backup_album_selection_page_select_albums": "Välj album", "backup_album_selection_page_selection_info": "Info om valda objekt", "backup_album_selection_page_total_assets": "Antal unika objekt", + "backup_albums_sync": "Säkerhetskopiera album synkronisering", "backup_all": "Allt", "backup_background_service_backup_failed_message": "Säkerhetskopiering av foton och videor misslyckades. FÃļrsÃļker igenâ€Ļ", "backup_background_service_connection_failed_message": "Anslutning till servern misslyckades. FÃļrsÃļker igenâ€Ļ", @@ -535,13 +599,16 @@ "backup_controller_page_turn_on": "Aktivera automatisk säkerhetskopiering", "backup_controller_page_uploading_file_info": "Laddar upp filinformation", "backup_err_only_album": "Kan inte ta bort det enda albumet", + "backup_error_sync_failed": "Synkroniseringen misslyckades. Det gÃĨr inte att bearbeta säkerhetskopian.", "backup_info_card_assets": "objekt", "backup_manual_cancelled": "Avbrutet", "backup_manual_in_progress": "Uppladdning pÃĨgÃĨr redan. FÃļrsÃļk igen om en liten stund", "backup_manual_success": "Klart", "backup_manual_title": "Uppladdningsstatus", + "backup_options": "Säkerhetskopieringsalternativ", "backup_options_page_title": "Säkerhetskopieringsinställningar", "backup_setting_subtitle": "Hantera inställningar fÃļr fÃļr- och bakgrundsuppladdning", + "backup_settings_subtitle": "Hantera uppladdningsinställningar", "backward": "BakÃĨt", "biometric_auth_enabled": "Biometrisk autentisering aktiverad", "biometric_locked_out": "Du är utelÃĨst frÃĨn biometrisk autentisering", @@ -560,7 +627,7 @@ "cache_settings_clear_cache_button": "Rensa cacheminnet", "cache_settings_clear_cache_button_title": "Rensar appens cacheminne. Detta kommer att avsevärt pÃĨverka appens prestanda tills cachen har byggts om.", "cache_settings_duplicated_assets_clear_button": "RENSA", - "cache_settings_duplicated_assets_subtitle": "Foton och videor som är svartlistade av appen", + "cache_settings_duplicated_assets_subtitle": "Foton och videor som är ignorerade, listas av appen", "cache_settings_duplicated_assets_title": "Duplicerade Objekt ({count})", "cache_settings_statistics_album": "Miniatyrbilder fÃļr bibliotek", "cache_settings_statistics_full": "Hela bilder", @@ -577,10 +644,12 @@ "cancel": "Avbryt", "cancel_search": "Avbryt sÃļkning", "canceled": "Avbruten", + "canceling": "Annullerande", "cannot_merge_people": "Kan inte slÃĨ samman personer", "cannot_undo_this_action": "Du kan inte ÃĨngra den här ÃĨtgärden!", "cannot_update_the_description": "Det gÃĨr inte att uppdatera beskrivningen", "cast": "Casta", + "cast_description": "Konfigurera tillgängliga cast-destinationer", "change_date": "Ändra datum", "change_description": "Ändra beskrivning", "change_display_order": "Ändra visningsordning", @@ -598,6 +667,8 @@ "change_pin_code": "Ändra PIN-kod", "change_your_password": "Ändra ditt lÃļsenord", "changed_visibility_successfully": "Synligheten har ändrats", + "charging": "Laddar", + "charging_requirement_mobile_backup": "Bakgrundssäkerhetskopiering kräver att enheten laddas", "check_corrupt_asset_backup": "Kontrollera om det finns korrupta säkerhetskopior av objekt", "check_corrupt_asset_backup_button": "Kontrollera", "check_corrupt_asset_backup_description": "KÃļr kontrollen endast Ãļver Wi-Fi och när alla objekt har säkerhetskopierats. Det kan ta nÃĨgra minuter.", @@ -607,6 +678,7 @@ "clear": "Rensa", "clear_all": "Rensa allt", "clear_all_recent_searches": "Rensa alla senaste sÃļkningar", + "clear_file_cache": "Rensa filcachen", "clear_message": "Rensa meddelande", "clear_value": "Rensa värde", "client_cert_dialog_msg_confirm": "OK", @@ -639,6 +711,7 @@ "confirm_password": "Bekräfta lÃļsenord", "confirm_tag_face": "Vill du tagga det här ansiktet som {name}?", "confirm_tag_face_unnamed": "Vill du tagga detta ansikte?", + "connected_device": "Ansluten enhet", "connected_to": "Ansluten till", "contain": "Anpassa", "context": "Sammanhang", @@ -676,11 +749,13 @@ "create_new_user": "Skapa en ny användare", "create_shared_album_page_share_add_assets": "LÄGG TILL OBJEKT", "create_shared_album_page_share_select_photos": "Välj bilder", + "create_shared_link": "Skapa delad länk", "create_tag": "Skapa tagg", "create_tag_description": "Skapa en ny tagg. FÃļr kapslade taggar anger du hela sÃļkvägen fÃļr taggen inklusive snedstreck.", "create_user": "Skapa användare", "created": "Skapad", "created_at": "Skapad", + "creating_linked_albums": "Skapar länkade album...", "crop": "Beskär", "curated_object_page_title": "Objekt", "current_device": "Aktuell enhet", @@ -688,10 +763,11 @@ "current_server_address": "Aktuell server-adress", "custom_locale": "Anpassad plats", "custom_locale_description": "Formatera datum och siffror baserat pÃĨ sprÃĨket och regionen", + "custom_url": "Anpassad URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "MÃļrk", - "darkTheme": "Växla till mÃļrkt tema", + "dark_theme": "Växla mÃļrkt tema", "date_after": "Datum efter", "date_and_time": "Datum och Tid", "date_before": "Datum fÃļre", @@ -699,6 +775,7 @@ "date_of_birth_saved": "FÃļdelsedatumet har sparats", "date_range": "Datumintervall", "day": "Dag", + "days": "Dagar", "deduplicate_all": "Deduplicera alla", "deduplication_criteria_1": "Bildstorlek i bytes", "deduplication_criteria_2": "Räkning av EXIF-data", @@ -707,6 +784,8 @@ "default_locale": "Standardplats", "default_locale_description": "Formatera datum och siffror baserat pÃĨ din webbläsares sprÃĨkversion", "delete": "Radera", + "delete_action_confirmation_message": "Är du säker pÃĨ att du vill ta bort den här tillgÃĨngen? Den här ÃĨtgärden flyttar tillgÃĨngen till serverns papperskorg och frÃĨgar om du vill ta bort den lokalt", + "delete_action_prompt": "{count} raderade", "delete_album": "Ta bort album", "delete_api_key_prompt": "Är du säker pÃĨ att du vill ta bort denna API-nyckel?", "delete_dialog_alert": "Dessa objekt kommer att raderas permanent frÃĨn Immich och din enhet", @@ -720,9 +799,12 @@ "delete_key": "Ta bort nyckel", "delete_library": "Ta bort bibliotek", "delete_link": "Ta bort länk", + "delete_local_action_prompt": "{count} raderade lokalt", "delete_local_dialog_ok_backed_up_only": "Ta Bara Bort Säkerhetskopierade", "delete_local_dialog_ok_force": "Ta Bort ÄndÃĨ", "delete_others": "Radera fler", + "delete_permanently": "Ta bort permanent", + "delete_permanently_action_prompt": "{count} raderade permanent", "delete_shared_link": "Ta bort delad länk", "delete_shared_link_dialog_title": "Ta Bort Delad Länk", "delete_tag": "Ta bort tagg", @@ -733,12 +815,14 @@ "description": "Beskrivning", "description_input_hint_text": "Lägg till beskrivning...", "description_input_submit_error": "Fel vid uppdatering av beskrivning, se loggen fÃļr fler detaljer", + "deselect_all": "Avmarkera alla", "details": "Detaljer", "direction": "Riktning", "disabled": "Inaktiverad", "disallow_edits": "TillÃĨt inte redigeringar", "discord": "Discord", "discover": "Upptäck", + "discovered_devices": "Funna enheter", "dismiss_all_errors": "Avvisa alla fel", "dismiss_error": "Avvisa fel", "display_options": "Visningsalternativ", @@ -749,6 +833,7 @@ "documentation": "Dokumentation", "done": "Klart", "download": "Ladda ner", + "download_action_prompt": "Laddar ner {count} resurser", "download_canceled": "Nedladdning avbruten", "download_complete": "Nedladdning slutfÃļrd", "download_enqueue": "Nedladdning kÃļad", @@ -775,8 +860,12 @@ "edit": "Redigera", "edit_album": "Redigera album", "edit_avatar": "Redigera avatar", + "edit_birthday": "Redigera fÃļdelsedag", "edit_date": "Redigera datum", "edit_date_and_time": "Redigera datum och tid", + "edit_date_and_time_action_prompt": "{count} datum och tid redigerad", + "edit_date_and_time_by_offset": "Ändra datum med fÃļrskjutning", + "edit_date_and_time_by_offset_interval": "Nytt datumintervall: {from} - {to}", "edit_description": "Redigera beskrivning", "edit_description_prompt": "Vänligen välj en ny beskrivning:", "edit_exclusion_pattern": "Redigera uteslutningsmÃļnster", @@ -786,6 +875,7 @@ "edit_key": "Redigera nyckel", "edit_link": "Redigera länk", "edit_location": "Redigera plats", + "edit_location_action_prompt": "{count} plats redigerad", "edit_location_dialog_title": "Plats", "edit_name": "Redigera namn", "edit_people": "Redigera personer", @@ -797,13 +887,14 @@ "editor_close_without_save_prompt": "Ändringarna kommer inte att sparas", "editor_close_without_save_title": "Stäng redigeraren?", "editor_crop_tool_h2_aspect_ratios": "BildfÃļrhÃĨllande", - "editor_crop_tool_h2_rotation": "Rotation", + "editor_crop_tool_h2_rotation": "Vridning", "email": "Epost", "email_notifications": "E-postaviseringar", "empty_folder": "Mappen är tom", "empty_trash": "TÃļm papperskorg", "empty_trash_confirmation": "Är du säker pÃĨ att du vill tÃļmma papperskorgen? Detta tar bort alla objekt i papperskorgen permanent frÃĨn Immich.\nDu kan inte ÃĨngra den här ÃĨtgärden!", "enable": "Aktivera", + "enable_backup": "Aktivera säkerhetskopiering", "enable_biometric_auth_description": "Skriv in din pinkod fÃļr att aktivera biometrisk autentisering", "enabled": "Aktiverad", "end_date": "Slutdatum", @@ -814,7 +905,9 @@ "error": "Fel", "error_change_sort_album": "Kunde inte ändra sorteringsordning fÃļr album", "error_delete_face": "Fel uppstod när ansikte skulle tas bort frÃĨn objektet", + "error_getting_places": "Det gick inte att hämta platser", "error_loading_image": "Fel vid bildladdning", + "error_loading_partners": "Fel vid inläsning av partner: {error}", "error_saving_image": "Fel: {error}", "error_tag_face_bounding_box": "Fel vid taggning av ansikte – kan inte hämta koordinater fÃļr begränsningsruta", "error_title": "Fel – nÃĨgot gick fel", @@ -847,6 +940,7 @@ "failed_to_load_notifications": "Misslyckades med att ladda notifikationer", "failed_to_load_people": "Det gick inte att ladda personer", "failed_to_remove_product_key": "Det gick inte att ta bort produktnyckeln", + "failed_to_reset_pin_code": "Misslyckades med att ÃĨterställa PIN-koden", "failed_to_stack_assets": "Det gick inte att stapla objekt", "failed_to_unstack_assets": "Det gick inte att avstapla objekt", "failed_to_update_notification_status": "Misslyckades med att uppdatera aviseringens status", @@ -855,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} misslyckades valideringen", "profile_picture_transparent_pixels": "Profilbilder kan inte ha genomskinliga pixlar. Zooma in och/eller flytta bilden.", "quota_higher_than_disk_size": "Du har angett en kvot som är hÃļgre än diskstorleken", + "something_went_wrong": "NÃĨgot gick fel", "unable_to_add_album_users": "Kunde inte lägga till använder i album", "unable_to_add_assets_to_shared_link": "Det gÃĨr inte att lägga till objekt till delad länk", "unable_to_add_comment": "Kunde inte lägga till kommentar", @@ -938,15 +1033,13 @@ "unable_to_update_user": "Kunde inte uppdatera användare", "unable_to_upload_file": "Det gÃĨr inte att ladda upp filen" }, - "exif": "Exif", + "exif": "EXIF", "exif_bottom_sheet_description": "Lägg till beskrivning...", + "exif_bottom_sheet_description_error": "Fel vid uppdatering av beskrivningen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLATS", "exif_bottom_sheet_people": "PERSONER", "exif_bottom_sheet_person_add_person": "Lägg till namn", - "exif_bottom_sheet_person_age_months": "Ålder {months} mÃĨnader", - "exif_bottom_sheet_person_age_year_months": "Ålder 1 ÃĨr, {months} mÃĨnader", - "exif_bottom_sheet_person_age_years": "Ålder {years}", "exit_slideshow": "Avsluta bildspel", "expand_all": "Expandera alla", "experimental_settings_new_asset_list_subtitle": "Under uppbyggnad", @@ -960,6 +1053,8 @@ "explorer": "Utforskare", "export": "Exportera", "export_as_json": "Exportera som JSON", + "export_database": "Exportera databas", + "export_database_description": "Exportera SQLite-databasen", "extension": "Tillägg", "external": "Externt", "external_libraries": "Externa Bibliotek", @@ -971,11 +1066,13 @@ "failed_to_load_assets": "Det gick inte att läsa in resurser", "failed_to_load_folder": "Kunde inte ladda mappen", "favorite": "Favorit", + "favorite_action_prompt": "{count} har lagts till i favoriter", "favorite_or_unfavorite_photo": "Favoritfoto eller icke-favoritfoto", "favorites": "Favoriter", "favorites_page_no_favorites": "Inga favoritobjekt hittades", "feature_photo_updated": "Funktionsfoto uppdaterad", "features": "Funktioner", + "features_in_development": "Funktioner i utveckling", "features_setting_description": "Hantera appens funktioner", "file_name": "Filnamn", "file_name_or_extension": "Filnamn eller -tillägg", @@ -985,21 +1082,26 @@ "filter_people": "Filtrera personer", "filter_places": "Filtrera platser", "find_them_fast": "Hitta dem snabbt efter namn med sÃļk", + "first": "FÃļrst", "fix_incorrect_match": "Fixa inkorrekt matchning", "folder": "Mapp", "folder_not_found": "Mappen hittade inte", "folders": "Mappar", "folders_feature_description": "Bläddra i mappvyn fÃļr foton och videoklipp i filsystemet", + "forgot_pin_code_question": "GlÃļmt din pinkod?", "forward": "FramÃĨt", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google-Cast", "gcast_enabled_description": "Denna funktion läser in externa resurser frÃĨn Google fÃļr att fungera.", "general": "Allmänt", + "geolocation_instruction_location": "Klicka pÃĨ en tillgÃĨng med GPS-koordinater fÃļr att använda dess plats, eller välj en plats direkt frÃĨn kartan", "get_help": "FÃĨ hjälp", "get_wifiname_error": "Kunde inte hämta Wi-Fi-namn. Säkerställ att du tillÃĨtit nÃļdvändiga rättigheter och är ansluten till ett Wi-Fi-nätverk", "getting_started": "Komma igÃĨng", "go_back": "GÃĨ tillbaka", "go_to_folder": "GÃĨ till mapp", "go_to_search": "GÃĨ till sÃļk", + "gps": "GPS", + "gps_missing": "Ingen GPS", "grant_permission": "Ge tillÃĨtelse", "group_albums_by": "Gruppera album efter...", "group_country": "Gruppera per land", @@ -1010,6 +1112,9 @@ "haptic_feedback_switch": "Aktivera haptisk feedback", "haptic_feedback_title": "Haptisk Feedback", "has_quota": "Har kvot", + "hash_asset": "Hash-tillgÃĨng", + "hashed_assets": "Hash-tillgÃĨngar", + "hashing": "Hashning", "header_settings_add_header_tip": "Lägg Till Header", "header_settings_field_validator_msg": "Värdet kan inte vara tomt", "header_settings_header_name_input": "Header-namn", @@ -1041,7 +1146,9 @@ "home_page_upload_err_limit": "Kan bara ladda upp max 30 objekt ÃĨt gÃĨngen, hoppar Ãļver", "host": "Värd", "hour": "Timme", + "hours": "Timmar", "id": "ID", + "idle": "Inaktiv", "ignore_icloud_photos": "Ignorera iCloud-foton", "ignore_icloud_photos_description": "Foton lagrade i iCloud kommer inte laddas upp till Immich-servern", "image": "Bild", @@ -1081,8 +1188,12 @@ "invalid_date_format": "Felaktigt datumformat", "invite_people": "Bjud in personer", "invite_to_album": "Bjuder in till album", + "ios_debug_info_fetch_ran_at": "Hämtning kÃļrdes {dateTime}", "ios_debug_info_last_sync_at": "Senaste synkning {dateTime}", "ios_debug_info_no_processes_queued": "Inga bakgrundsprocesser kÃļade", + "ios_debug_info_no_sync_yet": "Inget bakgrundssynkroniseringsjobb har kÃļrts ännu", + "ios_debug_info_processes_queued": "{count, plural, one {{count} bakgrundsprocess kÃļad} other {{count} bakgrundsprocesser kÃļade}}", + "ios_debug_info_processing_ran_at": "Bearbetningen kÃļrdes {dateTime}", "items_count": "{count, plural, one {# objekt} other {# objekt}}", "jobs": "Jobb", "keep": "BehÃĨll", @@ -1091,11 +1202,17 @@ "kept_this_deleted_others": "BehÃĨll denna tillgÃĨng och borttagna {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Kortkommandon", "language": "SprÃĨk", + "language_no_results_subtitle": "FÃļrsÃļk att justera ditt sÃļkord", + "language_no_results_title": "Inga sprÃĨk funna", + "language_search_hint": "SÃļk sprÃĨkâ€Ļ", "language_setting_description": "Välj Ãļnskat sprÃĨk", + "large_files": "Stora filer", + "last": "Sista", "last_seen": "Senast sedd", "latest_version": "Senaste versionen", "latitude": "Latitud", "leave": "Lämna", + "leave_album": "Lämna albumet", "lens_model": "Objektiv", "let_others_respond": "LÃĨt andra svara", "level": "NivÃĨ", @@ -1107,15 +1224,20 @@ "library_page_sort_created": "Senast skapat", "library_page_sort_last_modified": "Senast ändrad", "library_page_sort_title": "Albumtitel", + "licenses": "Licenser", "light": "Ljus", + "like": "Gilla", "like_deleted": "Gilla borttagen", "link_motion_video": "Länka rÃļrlig video", - "link_options": "Alternativ fÃļr länk", "link_to_oauth": "Länk till OAuth", "linked_oauth_account": "Länkat OAuth konto", "list": "Lista", - "loading": "Laddar", + "loading": "Inläsning", "loading_search_results_failed": "Det gick inte att läsa in sÃļkresultat", + "local": "Lokalt", + "local_asset_cast_failed": "Det gÃĨr inte att casta en tillgÃĨng som inte har laddats upp till servern", + "local_assets": "Lokala tillgÃĨngar", + "local_media_summary": "Sammanfattning av lokala medier", "local_network": "Lokalt nätverk", "local_network_sheet_info": "Appen kommer ansluta till servern via denna URL när det specificerade WiFi-nätverket används", "location_permission": "Plats-rättighet", @@ -1127,8 +1249,10 @@ "location_picker_longitude_hint": "Ange din longitud här", "lock": "LÃĨs", "locked_folder": "LÃĨst Mapp", + "log_detail_title": "Loggdetalj", "log_out": "Logga ut", "log_out_all_devices": "Logga ut alla enheter", + "logged_in_as": "Inloggad som {user}", "logged_out_all_devices": "Loggat ut frÃĨn alla enheter", "logged_out_device": "Loggat ut enheten", "login": "Logga in", @@ -1156,6 +1280,7 @@ "login_password_changed_success": "Uppdatering av lÃļsenord lyckades", "logout_all_device_confirmation": "Är du säker pÃĨ att du vill logga ut frÃĨn alla enheter?", "logout_this_device_confirmation": "Är du säker pÃĨ att du vill logga ut frÃĨn denna enhet?", + "logs": "Loggar", "longitude": "Longitud", "look": "Titta", "loop_videos": "Loopa videor", @@ -1163,6 +1288,7 @@ "main_branch_warning": "Du använder en utvecklingsversion. Vi rekommenderar starkt att du använder en utgiven version!", "main_menu": "Huvudmeny", "make": "Tillverkare", + "manage_geolocation": "Hantera plats", "manage_shared_links": "Hantera Delade länkar", "manage_sharing_with_partners": "Hantera delning med partner", "manage_the_app_settings": "Hantera appinställningarna", @@ -1171,8 +1297,7 @@ "manage_your_devices": "Hantera dina inloggade enheter", "manage_your_oauth_connection": "Hantera din OAuth-anslutning", "map": "Karta", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foton", + "map_assets_in_bounds": "{count, plural, =0 {Inga foton i detta omrÃĨde} one {# photo} other {# photos}}", "map_cannot_get_user_location": "Kan inte hämta användarens plats", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Använd den här platsen", @@ -1180,7 +1305,6 @@ "map_location_service_disabled_title": "Platstjänst inaktiverad", "map_marker_for_images": "Kartmarkering fÃļr bilder tagna i {city}, {country}", "map_marker_with_image": "KartmarkÃļr med bild", - "map_no_assets_in_bounds": "Inga foton i omrÃĨdet", "map_no_location_permission_content": "Platsrättighet är nÃļdvändigt fÃļr att kunna visa objekt frÃĨn din nuvarande plats. Vill du tillÃĨta det nu?", "map_no_location_permission_title": "Platsrättighet nekad", "map_settings": "Kartinställningar", @@ -1199,6 +1323,7 @@ "mark_as_read": "Markera som läst", "marked_all_as_read": "Markerade alla som lästa", "matches": "Matchar", + "matching_assets": "Matchande tillgÃĨngar", "media_type": "Mediatyp", "memories": "Minnen", "memories_all_caught_up": "Du är ikapp", @@ -1217,6 +1342,7 @@ "merged_people_count": "Sammanfogat {count, plural, one {# person} other {# people}}", "minimize": "Minimera", "minute": "Minut", + "minutes": "Minuter", "missing": "Saknade", "model": "Modell", "month": "MÃĨnad", @@ -1224,6 +1350,7 @@ "more": "Mer", "move": "Flytta", "move_off_locked_folder": "Flytta frÃĨn lÃĨst mapp", + "move_to_lock_folder_action_prompt": "{count} adderades till lÃĨst mapp", "move_to_locked_folder": "Flytta till lÃĨst mapp", "move_to_locked_folder_confirmation": "Dessa foton och videor kommer tas bort frÃĨn alla album och gÃĨr endast se i lÃĨsta mappen", "moved_to_archive": "Flyttade {count, plural, one {# resurs} other {# assets}} till arkivet", @@ -1235,6 +1362,10 @@ "my_albums": "Mina album", "name": "Namn", "name_or_nickname": "Namn eller smeknamn", + "network_requirement_photos_upload": "Använd mobildata fÃļr att säkerhetskopiera foton", + "network_requirement_videos_upload": "Använd mobildata fÃļr att säkerhetskopiera videor", + "network_requirements": "Nätverkskrav", + "network_requirements_updated": "Nätverkskraven har ändrats, ÃĨterställer säkerhetskopieringskÃļn", "networking_settings": "Nätverk", "networking_subtitle": "Hantera inställningar fÃļr server-endpointen", "never": "aldrig", @@ -1244,6 +1375,7 @@ "new_person": "Ny person", "new_pin_code": "Ny PIN-kod", "new_pin_code_subtitle": "Det här är fÃļrsta gÃĨngen du Ãļppnar den lÃĨsta mappen. Skapa en PIN-kod fÃļr att säkert fÃĨ ÃĨtkomst till den här sidan", + "new_timeline": "Ny tidslinje", "new_user_created": "Ny användare skapad", "new_version_available": "NY VERSION TILLGÄNGLIG", "newest_first": "Nyast fÃļrst", @@ -1256,19 +1388,26 @@ "no_archived_assets_message": "Arkivera bilder och videor fÃļr att dÃļlja dem frÃĨn bild-vyn", "no_assets_message": "KLICKA FÖR ATT LADDA UPP DIN FÖRSTA BILD", "no_assets_to_show": "Inga objekt att visa", + "no_cast_devices_found": "Inga Cast-enheter hittades", + "no_checksum_local": "Ingen kontrollsumma tillgänglig - kan inte hämta lokala tillgÃĨngar", + "no_checksum_remote": "Ingen kontrollsumma tillgänglig - kan inte hämta fjärrtillgÃĨng", "no_duplicates_found": "Inga dubbletter hittades.", "no_exif_info_available": "EXIF-information ej tillgänglig", "no_explore_results_message": "Ladda upp fler bilder fÃļr att utforska din samling.", "no_favorites_message": "Lägg till favoriter fÃļr att snabbt hitta dina bästa bilder och videor", "no_libraries_message": "Skapa ett externt bibliotek fÃļr att se dina bilder och videor", + "no_local_assets_found": "Inga lokala tillgÃĨngar hittades med denna kontrollsumma", "no_locked_photos_message": "Foton och videor i den lÃĨsta mappen är dolda och visas inte när du bläddrar eller sÃļker i ditt bibliotek.", "no_name": "Inget namn", "no_notifications": "Inga aviseringar", "no_people_found": "Inga matchande personer hittade", "no_places": "Inga platser", + "no_remote_assets_found": "Inga fjärrtillgÃĨngar hittades med denna kontrollsumma", "no_results": "Inga resultat", "no_results_description": "PrÃļva en synonym eller ett annat mer allmänt sÃļkord", "no_shared_albums_message": "Skapa ett album fÃļr att dela bilder och videor med andra personer", + "no_uploads_in_progress": "Inga uppladdningar pÃĨgÃĨr", + "not_available": "N/A", "not_in_any_album": "Inte i nÃĨgot album", "not_selected": "Ej vald", "note_apply_storage_label_to_previously_uploaded assets": "Obs: Om du vill använda lagringsetiketten pÃĨ tidigare uppladdade tillgÃĨngar kÃļr du", @@ -1284,12 +1423,16 @@ "oauth": "OAuth", "official_immich_resources": "Officiella Immich-resurser", "offline": "FrÃĨnkopplad", + "offset": "FÃļrskjutning", "ok": "Ok", "oldest_first": "Äldst fÃļrst", "on_this_device": "PÃĨ enheten", "onboarding": "Introduktion", + "onboarding_locale_description": "Välj ditt Ãļnskade sprÃĨk. Du kan ändra detta senare i dina inställningar.", "onboarding_privacy_description": "FÃļljande (valfria) funktioner är beroende av externa tjänster och kan när som helst avaktiveras i inställningarna.", + "onboarding_server_welcome_description": "LÃĨt oss konfigurera din instans med nÃĨgra vanliga inställningar.", "onboarding_theme_description": "Välj en färg fÃļr din instans. Du kan ändra detta senare via inställningarna.", + "onboarding_user_welcome_description": "Nu sätter vi igÃĨng!", "onboarding_welcome_user": "Välkommen, {user}", "online": "Ansluten", "only_favorites": "Endast favoriter", @@ -1299,10 +1442,13 @@ "open_the_search_filters": "Öppna sÃļkfilter", "options": "Val", "or": "eller", + "organize_into_albums": "Organisera i album", + "organize_into_albums_description": "Lägg befintliga foton i album med aktuella synkroniseringsinställningar", "organize_your_library": "Organisera ditt bibliotek", "original": "original", "other": "Övrigt", "other_devices": "Andra enheter", + "other_entities": "Andra enheter", "other_variables": "Andra variabler", "owned": "Ägd", "owner": "Ägare", @@ -1357,6 +1503,9 @@ "permission_onboarding_permission_limited": "Rättighet begränsad. FÃļr att lÃĨta Immich säkerhetskopiera och hantera hela ditt galleri, tillÃĨt foto- och video-rättigheter i Inställningar.", "permission_onboarding_request": "Immich kräver tillstÃĨnd fÃļr att se dina foton och videor.", "person": "Person", + "person_age_months": "{months, plural, one {# mÃĨnad} other {# mÃĨnader}} gammal", + "person_age_year_months": "1 ÃĨr, {months, plural, one {# mÃĨnad} other {# mÃĨnader}} gammal", + "person_age_years": "{years, plural, other {# ÃĨr}} gammal", "person_birthdate": "FÃļdd {date}", "person_hidden": "{name}{hidden, select, true { (dold)} other {}}", "photo_shared_all_users": "Du har antingen delat dina foton med alla användare eller sÃĨ har du inga användare att dela dem med.", @@ -1380,6 +1529,7 @@ "port": "Port", "preferences_settings_subtitle": "Hantera appens inställningar", "preferences_settings_title": "Inställningar", + "preparing": "FÃļrbereder", "preset": "FÃļrinställt värde", "preview": "FÃļrhandsvisning", "previous": "FÃļregÃĨende", @@ -1392,17 +1542,18 @@ "privacy": "Sekretess", "profile": "Profil", "profile_drawer_app_logs": "Loggar", - "profile_drawer_client_out_of_date_major": "Mobilappen är utdaterad. Uppdatera till senaste huvudversionen.", - "profile_drawer_client_out_of_date_minor": "Mobilappen är utdaterad. Uppdatera till senaste mindre versionen.", + "profile_drawer_client_out_of_date_major": "Mobilappen är fÃļrÃĨldrad. Uppdatera till senaste versionen.", + "profile_drawer_client_out_of_date_minor": "Mobilappen är fÃļrÃĨldrad. Uppdatera till senaste versionen.", "profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade", "profile_drawer_github": "GitHub", - "profile_drawer_server_out_of_date_major": "Servern är utdaterad. Uppdatera till senaste huvudversionen.", - "profile_drawer_server_out_of_date_minor": "Servern är utdaterad. Uppdatera till senaste mindre versionen.", + "profile_drawer_readonly_mode": "Skrivskyddat läge aktiverat. LÃĨngtryck användaravatarikonen fÃļr att avsluta.", + "profile_drawer_server_out_of_date_major": "Servern har en fÃļrÃĨldrad mjukvara. Uppdatera till senaste versionen.", + "profile_drawer_server_out_of_date_minor": "Servern har en fÃļrÃĨldrad mjukvara. Uppdatera till senaste versionen.", "profile_image_of_user": "{user} profilbild", "profile_picture_set": "Profilbild vald.", "public_album": "Publikt album", "public_share": "Offentlig delning", - "purchase_account_info": "Supporter", + "purchase_account_info": "Anhängare", "purchase_activated_subtitle": "Tack fÃļr att du stÃļdjer Immich och open source-mjukvara", "purchase_activated_time": "Aktiverad {date}", "purchase_activated_title": "Aktiveringan av din nyckel lyckades", @@ -1434,12 +1585,17 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnyckeln fÃļr servern hanteras av administratÃļren", + "query_asset_id": "FrÃĨga om objekts-ID", + "queue_status": "KÃļande {count}/{total}", "rating": "Antal stjärnor", "rating_clear": "Ta bort betyg", "rating_count": "{count, plural, one {# stjärna} other {# stjärnor}}", "rating_description": "Visa EXIF betyget i informationspanelen", "reaction_options": "Alternativ fÃļr reaktion", "read_changelog": "Läs ändringslogg", + "readonly_mode_disabled": "Skrivskyddat läge inaktiverat", + "readonly_mode_enabled": "Skrivskyddat läge aktiverat", + "ready_for_upload": "Redo fÃļr uppladdning", "reassign": "OmfÃļrdela", "reassigned_assets_to_existing_person": "Tilldelade om {count, plural, one {# objekt} other {# objekt}} till {name, select, null {an existing person} other {{name}}}", "reassigned_assets_to_new_person": "Tilldelade om {count, plural, one {# objekt} other {# objekt}} till en ny persson", @@ -1462,6 +1618,9 @@ "refreshing_faces": "Återladdar ansikten", "refreshing_metadata": "Återladdar metadata", "regenerating_thumbnails": "Uppdaterar miniatyrer", + "remote": "Fjärrr", + "remote_assets": "FjärrtillgÃĨngar", + "remote_media_summary": "Sammanfattning av fjärrmedia", "remove": "Ta bort", "remove_assets_album_confirmation": "Är du säker pÃĨ att du vill ta bort {count, plural, one {# asset} other {# assets}} frÃĨn albumet?", "remove_assets_shared_link_confirmation": "Är du säker pÃĨ att du vill ta bort {count, plural, one {# asset} other {# assets}} frÃĨn denna delade länk?", @@ -1469,12 +1628,15 @@ "remove_custom_date_range": "Ta bort anpassat datumintervall", "remove_deleted_assets": "Ta bort borttagna tillgÃĨngar", "remove_from_album": "Ta bort frÃĨn album", + "remove_from_album_action_prompt": "{count} borttaget frÃĨn albumet", "remove_from_favorites": "Ta bort frÃĨn favoriter", + "remove_from_lock_folder_action_prompt": "{count} borttagen frÃĨn den lÃĨsta mappen", "remove_from_locked_folder": "Ta bort frÃĨn lÃĨst mapp", "remove_from_locked_folder_confirmation": "Är du säker pÃĨ att du vill flytta dessa foton och videor frÃĨn den lÃĨsta mappen? De kommer att vara synliga i ditt bibliotek.", "remove_from_shared_link": "Ta bort frÃĨn delad länk", "remove_memory": "Ta bort minne", "remove_photo_from_memory": "Ta bort fotot frÃĨn detta minnet", + "remove_tag": "Ta bort taggen", "remove_url": "Ta bort URL", "remove_user": "Ta bort användare", "removed_api_key": "Tog bort API nyckel: {name}", @@ -1496,19 +1658,29 @@ "reset_password": "Nollställ lÃļsenord", "reset_people_visibility": "Återställ personers synlighet", "reset_pin_code": "Återställ PIN-kod", + "reset_pin_code_description": "Om du har glÃļmt din PIN-kod kan du kontakta serveradministratÃļren fÃļr att ÃĨterställa den", + "reset_pin_code_success": "PIN-koden har ÃĨterställts", + "reset_pin_code_with_password": "Du kan alltid ÃĨterställa din PIN-kod med ditt lÃļsenord", + "reset_sqlite": "Återställ SQLite-databasen", + "reset_sqlite_confirmation": "Är du säker pÃĨ att du vill ÃĨterställa SQLite-databasen? Du mÃĨste logga ut och logga in igen fÃļr att synkronisera om data", + "reset_sqlite_success": "Återställde SQLite-databasen", "reset_to_default": "Återställ till standard", "resolve_duplicates": "LÃļs dubletter", "resolved_all_duplicates": "LÃļs alla dubletter", "restore": "Återställ", "restore_all": "Återställ alla", + "restore_trash_action_prompt": "{count} ÃĨterställd frÃĨn papperskorgen", "restore_user": "Återställ användare", "restored_asset": "Återställ tillgÃĨng", "resume": "Återuppta", + "resume_paused_jobs": "Återuppta {count, plural, one {# pausat jobb} other {# pausade jobb}}", "retry_upload": "Ladda upp igen", "review_duplicates": "Granska dubbletter", + "review_large_files": "Granska stora filer", "role": "Roll", "role_editor": "Redigerare", "role_viewer": "Visare", + "running": "IgÃĨngsatt", "save": "Spara", "save_to_gallery": "Spara i galleri", "saved_api_key": "Sparad API-nyckel", @@ -1557,7 +1729,7 @@ "search_page_no_places": "Ingen platsinformation finns tillgänglig", "search_page_screenshots": "Skärmdumpar", "search_page_search_photos_videos": "SÃļk efter dina foton och videor", - "search_page_selfies": "Selfies", + "search_page_selfies": "Självporträtt", "search_page_things": "Objekt", "search_page_view_all_button": "Visa alla", "search_page_your_activity": "Dina aktiviteter", @@ -1581,6 +1753,7 @@ "select_album_cover": "Välj albumomslag", "select_all": "Välj alla", "select_all_duplicates": "Välj alla dubletter", + "select_all_in": "Markera alla i {group}", "select_avatar_color": "Välj färg fÃļr avatar", "select_face": "Välj person", "select_featured_photo": "Välj utvald bild", @@ -1594,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album", "selected": "Valda", "selected_count": "{count, plural, other {# valda}}", + "selected_gps_coordinates": "Valda GPS-koordinater", "send_message": "Skicka meddelande", "send_welcome_email": "Skicka välkomstmejl", "server_endpoint": "Server-endpoint", @@ -1601,6 +1775,7 @@ "server_info_box_server_url": "Server-URL", "server_offline": "Servern offline", "server_online": "Server online", + "server_privacy": "Serversekretess", "server_stats": "Serverstatistik", "server_version": "Serverversion", "set": "Välj", @@ -1610,6 +1785,7 @@ "set_date_of_birth": "Ange fÃļdelsedatum", "set_profile_picture": "Ange som profilbild", "set_slideshow_to_fullscreen": "Ställ in bildspel pÃĨ helskärm", + "set_stack_primary_asset": "Ange som primär tillgÃĨng", "setting_image_viewer_help": "Detaljerad vy laddar miniatyrer fÃļrst. Efter detta laddas den medelstora fÃļrhandsgranskningen av bilden (om detta är aktiverat), och visar slutligen originalet (om detta är aktiverat).", "setting_image_viewer_original_subtitle": "Aktivera fÃļr att ladda originalbilden i full storlek (stor!). Inaktivera fÃļr att minska dataanvändningen (bÃĨde i nätverket och fÃļr enhetscache).", "setting_image_viewer_original_title": "Ladda originalbilden", @@ -1637,6 +1813,7 @@ "settings_saved": "Inställningar sparade", "setup_pin_code": "Konfigurera pinkod", "share": "Dela", + "share_action_prompt": "Delade {count} tillgÃĨngar", "share_add_photos": "Lägg till foton", "share_assets_selected": "{count} valda", "share_dialog_preparing": "FÃļrbereder...", @@ -1658,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "Kopierad till urklipp", "shared_link_clipboard_text": "Länk: {link}\nLÃļsenord: {password}", "shared_link_create_error": "Fel vid skapande av delad länk", + "shared_link_custom_url_description": "FÃĨ ÃĨtkomst till den här delade länken med en anpassad URL", "shared_link_edit_description_hint": "Lägg till delnings-beskrivningen", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dagar", @@ -1683,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Hantera Delade länkar", "shared_link_options": "Alternativ fÃļr delad länk", + "shared_link_password_description": "Kräv ett lÃļsenord fÃļr att komma ÃĨt den här delade länken", "shared_links": "Delade Länkar", "shared_links_description": "Dela foton och videor med en länk", "shared_photos_and_videos_count": "{assetCount, plural, other {# delade foton och videor.}}", @@ -1717,6 +1896,7 @@ "show_slideshow_transition": "Visa bildspelsÃļvergÃĨng", "show_supporter_badge": "Supporteremblem", "show_supporter_badge_description": "Visa supporteremblem", + "show_text_search_menu": "Visa textsÃļkningsmeny", "shuffle": "Blanda", "sidebar": "Sidopanel", "sidebar_display_description": "Visa en länk till vyn i sidofältet", @@ -1732,12 +1912,14 @@ "sort_created": "Skapat datum", "sort_items": "Antal artiklar", "sort_modified": "Datum ändrat", + "sort_newest": "Nyaste foto", "sort_oldest": "Äldsta foto", "sort_people_by_similarity": "Sortera människor efter likhet", "sort_recent": "Senaste fotot", "sort_title": "Rubrik", "source": "Källa", "stack": "Stapel", + "stack_action_prompt": "{count} staplade", "stack_duplicates": "Stapla dubletter", "stack_select_one_photo": "Välj ett huvudfoto fÃļr stapeln", "stack_selected_photos": "Stapla valda foton", @@ -1745,8 +1927,10 @@ "stacktrace": "StapelspÃĨrning", "start": "Starta", "start_date": "Startdatum", + "start_date_before_end_date": "Startdatumet mÃĨste vara fÃļre slutdatumet", "state": "Stat", "status": "Status", + "stop_casting": "Sluta casta", "stop_motion_photo": "Stanna rÃļrligt foto", "stop_photo_sharing": "Sluta dela dina foton?", "stop_photo_sharing_description": "{partner} kommer inte länga ha tillgÃĨng till dina foton.", @@ -1756,15 +1940,20 @@ "storage_quota": "Lagrinkskvot", "storage_usage": "{used} av {available} används", "submit": "Skicka", + "success": "FramgÃĨng", "suggestions": "FÃļrslag", "sunrise_on_the_beach": "SoluppgÃĨng pÃĨ stranden", "support": "Support", - "support_and_feedback": "Support & Feedback", + "support_and_feedback": "Support och Feedback", "support_third_party_description": "Din Immich-installation paketerades av en tredje part. Problem som du upplever kan orsakas av det paketet, sÃĨ vänligen ta upp problem med dem i fÃļrsta hand med hjälp av länkarna nedan.", "swap_merge_direction": "Byt sammanfogningsriktning", "sync": "Synka", "sync_albums": "Synka album", "sync_albums_manual_subtitle": "Synka alla uppladdade videor och foton till valda backup-album", + "sync_local": "Synkronisera lokalt", + "sync_remote": "Synkronisera fjärrserver", + "sync_status": "Synk Status", + "sync_status_subtitle": "Visa och hantera synkroniseringssystemet", "sync_upload_album_setting_subtitle": "Skapa och ladda upp dina foton och videor till de valda albumen pÃĨ Immich", "tag": "Tagg", "tag_assets": "Tagga tillgÃĨngar", @@ -1775,6 +1964,7 @@ "tag_updated": "Uppdaterade tagg: {tag}", "tagged_assets": "Taggade {count, plural, one {# objekt} other {# objekt}}", "tags": "Taggar", + "tap_to_run_job": "Tryck fÃļr att kÃļra jobbet", "template": "Mall", "theme": "Tema", "theme_selection": "Val av tema", @@ -1801,12 +1991,15 @@ "to_change_password": "Ändra lÃļsenord", "to_favorite": "Favorit", "to_login": "Logga in", + "to_multi_select": "fÃļr att välja flera", "to_parent": "GÃĨ till fÃļrälder", + "to_select": "fÃļr att välja", "to_trash": "Papperskorg", "toggle_settings": "Växla inställningar", "total": "Totalt", "total_usage": "Total användning", "trash": "Papperskorg", + "trash_action_prompt": "{count} flyttad till papperskorgen", "trash_all": "Kasta alla", "trash_count": "Papperskorg {count, number}", "trash_delete_asset": "Papperskorgen/Ta bort tillgÃĨng", @@ -1820,12 +2013,16 @@ "trash_page_select_assets_btn": "Välj objekt", "trash_page_title": "Papperskorg ({count})", "trashed_items_will_be_permanently_deleted_after": "Objekt i papperskorgen raderas permanent efter {days, plural, one {# dag} other {# dagar}}.", + "troubleshoot": "FelsÃļk", "type": "Typ", "unable_to_change_pin_code": "Kunde inte ändra pinkod", "unable_to_setup_pin_code": "Kunde inte konfigurera pinkod", "unarchive": "Ångra arkivering", + "unarchive_action_prompt": "{count} borttagen frÃĨn arkivet", "unarchived_count": "{count, plural, one {# borttagen frÃĨn arkiv} other {# borttagna frÃĨn arkiv}}", + "undo": "Ångra", "unfavorite": "Avfavorisera", + "unfavorite_action_prompt": "{count} borttagen frÃĨn Favoriter", "unhide_person": "Visa person", "unknown": "Okänd", "unknown_country": "Okänt Land", @@ -1841,16 +2038,23 @@ "unsaved_change": "Osparade ändringar", "unselect_all": "Avmarkera alla", "unselect_all_duplicates": "Avmarkera alla dubletter", + "unselect_all_in": "Avmarkera alla i {group}", "unstack": "Stapla Av", + "unstack_action_prompt": "{count} ostaplade", "unstacked_assets_count": "Avstaplade {count, plural, one {# asset} other {# assets}}", - "up_next": "Nästa", + "untagged": "Otaggad", + "up_next": "Kommande", + "update_location_action_prompt": "Uppdatera platsen fÃļr {count} valda tillgÃĨngar med:", "updated_at": "Uppdaterat", "updated_password": "LÃļsenordet har uppdaterats", "upload": "Ladda upp", + "upload_action_prompt": "{count} i kÃļ fÃļr uppladdning", "upload_concurrency": "Uppladdning samtidighet", + "upload_details": "Uppladdningsdetaljer", "upload_dialog_info": "Vill du säkerhetskopiera de valda objekten till servern?", "upload_dialog_title": "Ladda Upp Objekt", "upload_errors": "Uppladdning klar med {count, plural, one {# fel} other {# fel}}, ladda om sidan fÃļr att se nya objekt.", + "upload_finished": "Uppladdningen är klar", "upload_progress": "ÅterstÃĨende {remaining, number} - Bearbetade {processed, number}/{total, number}", "upload_skipped_duplicates": "Hoppade Ãļver {count, plural, one {# dublett} other {# dubletter}}", "upload_status_duplicates": "Dubbletter", @@ -1859,6 +2063,7 @@ "upload_success": "Uppladdning lyckades, ladda om sidan fÃļr att se nya objekt.", "upload_to_immich": "Ladda upp till Immich ({count})", "uploading": "Laddar upp", + "uploading_media": "Uppladdning av media", "url": "URL", "usage": "Användning", "use_biometric": "Använd biometri", @@ -1870,6 +2075,7 @@ "user_liked": "{user} gillade {type, select, photo {detta fotot} video {denna filmen} asset {detta objekt} other {detta}}", "user_pin_code_settings": "Pinkod", "user_pin_code_settings_description": "Hantera pinkod", + "user_privacy": "Användarsekretess", "user_purchase_settings": "KÃļp", "user_purchase_settings_description": "Hantera dina kÃļp", "user_role_set": "Sätt {user} som {role}", @@ -1878,6 +2084,7 @@ "user_usage_stats_description": "Se statistik - kontoanvändande", "username": "Användarnamn", "users": "Användare", + "users_added_to_album_count": "Lade till {count, plural, one {# user} other {# users}} i albumet", "utilities": "Verktyg", "validate": "Validera", "validate_endpoint_error": "Ange en giltig URL", @@ -1896,6 +2103,7 @@ "view_album": "Visa Album", "view_all": "Visa alla", "view_all_users": "Visa alla användare", + "view_details": "Visa detaljer", "view_in_timeline": "Visa i tidslinjen", "view_link": "Visa länk", "view_links": "Visa länkar", @@ -1903,6 +2111,7 @@ "view_next_asset": "Visa nästa objekt", "view_previous_asset": "Visa fÃļregÃĨende objekt", "view_qr_code": "Visa QR-kod", + "view_similar_photos": "Visa liknande foton", "view_stack": "Visa Stapel", "view_user": "Visa Användare", "viewer_remove_from_stack": "Ta bort frÃĨn Stapeln", @@ -1921,5 +2130,6 @@ "yes": "Ja", "you_dont_have_any_shared_links": "Du har inga delade länkar", "your_wifi_name": "Ditt Wi-Fi-namn", - "zoom_image": "Zooma bild" + "zoom_image": "Zooma bild", + "zoom_to_bounds": "Zooma till gränser" } diff --git a/i18n/ta.json b/i18n/ta.json index ce0768982d..e97b4b4b72 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -4,15 +4,18 @@ "account_settings": "āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽ…āŽŽā¯ˆāŽĩā¯āŽ•āŽŗā¯", "acknowledge": "āŽ’āŽĒā¯āŽĒā¯āŽ•ā¯āŽ•ā¯ŠāŽŗā¯āŽ•āŽŋāŽąā¯‡āŽŠā¯", "action": "āŽšā¯†āŽ¯āŽ˛ā¯", + "action_common_update": "āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯", "actions": "āŽšā¯†āŽ¯āŽ˛ā¯āŽ•āŽŗā¯", "active": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽ˛ā¯", "activity": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽ•āŽŗā¯", - "activity_changed": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯ {āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽšāŽ°āŽŋ {enabled} āŽŽāŽąā¯āŽąāŽ¤ā¯ {disabled}}", + "activity_changed": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯ {enabled, select, true {āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯} other {āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯}}", "add": "āŽšā¯‡āŽ°ā¯", "add_a_description": "āŽĩāŽŋāŽĩāŽ°āŽŽā¯ āŽšā¯‡āŽ°ā¯", "add_a_location": "āŽ‡āŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_a_name": "āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_a_title": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❁ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "add_birthday": "āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤āŽ¨āŽžāŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "add_endpoint": "āŽšā¯‡āŽĩ❈ āŽ¨āŽŋāŽ°āŽ˛ā¯ˆ āŽšā¯‡āŽ°ā¯", "add_exclusion_pattern": "āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽĩāŽŸāŽŋāŽĩāŽ¤ā¯āŽ¤ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_import_path": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽĒāŽžāŽ¤ā¯ˆāŽ¯ā¯ˆ (āŽ‡āŽŽā¯āŽĒā¯‹āŽ°ā¯āŽŸā¯ āŽĒāŽžāŽ¤ā¯) āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_location": "āŽ‡āŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -20,33 +23,49 @@ "add_partner": "āŽ¤ā¯āŽŖā¯ˆāŽ¯ā¯ˆ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_path": "āŽĒāŽžāŽ¤ā¯ˆ (āŽĒāŽžāŽ¤ā¯) āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "add_photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "add_to": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•...", + "add_tag": "āŽ•ā¯āŽąāŽŋāŽ¯ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•", + "add_to": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯â€Ļ", "add_to_album": "āŽ†āŽ˛ā¯āŽĒāŽŽāŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•", + "add_to_album_bottom_sheet_added": "{album}-āŽ‡āŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "add_to_album_bottom_sheet_already_exists": "āŽāŽąā¯āŽ•āŽŠāŽĩ❇ {album}-āŽ‡āŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", + "add_to_album_bottom_sheet_some_local_assets": "āŽšāŽŋāŽ˛ āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "add_to_album_toggle": "{album} āŽ•ā¯āŽ•āŽžāŽŠ āŽ¤ā¯‡āŽ°ā¯āŽĩ❈ āŽŽāŽžāŽąā¯āŽąā¯", + "add_to_albums": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯", + "add_to_albums_count": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯({count})", "add_to_shared_album": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽŽāŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•", "add_url": "URL āŽāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "added_to_archive": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "added_to_favorites": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ (āŽĒ❇āŽĩāŽ°āŽŋāŽŸā¯āŽ¸ā¯) āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", - "added_to_favorites_count": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ (āŽĒ❇āŽĩāŽ°āŽŋāŽŸā¯āŽ¸ā¯) {count} āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "added_to_favorites_count": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ {count, number} āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "admin": { "add_exclusion_pattern_description": "āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽĩāŽŸāŽŋāŽĩāŽ™ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. *, **, āŽŽāŽąā¯āŽąā¯āŽŽā¯ ? āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯. \"Raw\" āŽŽāŽŠā¯āŽą āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽāŽ¨ā¯āŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯ āŽ‰āŽŗā¯āŽŗ āŽŽāŽ˛ā¯āŽ˛āŽž āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•, \"**/Raw/**\" āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯. \".tif\" āŽ‡āŽ˛ā¯ āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯ āŽŽāŽ˛ā¯āŽ˛āŽž āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•, \"**/*.tif\" āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯. āŽ’āŽ°ā¯ āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽĒāŽžāŽ¤ā¯ˆāŽ¯ā¯ˆ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•, \"/path/to/ignore/**\" āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", - "asset_offline_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ‡āŽŠāŽŋ āŽĩāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯āŽŗā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯, āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒā¯āŽŸā¯ˆāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•, āŽ•ā¯€āŽ´ā¯‡āŽ¯ā¯āŽŗā¯āŽŗ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒāŽžāŽ¤ā¯ˆāŽ¯ā¯ˆ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽŖā¯āŽ•āŽ˛āŽžāŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", - "authentication_settings": "āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ (āŽšā¯†āŽŸā¯āŽŸāŽŋāŽ™ā¯āŽ¸ā¯)", - "authentication_settings_description": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯, OAuth, āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŋāŽą āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", - "authentication_settings_disable_all": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽąā¯ˆāŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽŽā¯āŽŸāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽąā¯āŽąāŽŋāŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", - "authentication_settings_reenable": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•, āŽšāŽ°ā¯āŽĩāŽ°ā¯ āŽ•āŽŸā¯āŽŸāŽŗā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", + "admin_user": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ• āŽĒāŽ¯āŽŠāŽ°ā¯", + "asset_offline_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ•āŽšā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (external library asset) āŽ‡āŽŠāŽŋ āŽ•ā¯‹āŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽ˛ā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯ (trash) āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯.āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗā¯‡ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯, āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ‡āŽŖā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ (corresponding asset) āŽ•āŽŖā¯āŽŸā¯āŽĒāŽŋāŽŸāŽŋāŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ (timeline) āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•, āŽ•ā¯€āŽ´ā¯‡ āŽ‰āŽŗā¯āŽŗ āŽ•ā¯‹āŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽžāŽ¤ā¯ˆ Immich āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽŖā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯āŽ¤āŽž āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¤ā¯, āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ (library) āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ¸ā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯.", + "authentication_settings": "āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ° āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "authentication_settings_description": "āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽšā¯ āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯āŽ•āŽŗā¯ (Password), OAuth āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŋāŽą āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ° āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "authentication_settings_disable_all": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽŖā¯āŽŽā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽąā¯ˆāŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ¯āŽžāŽ• āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", + "authentication_settings_reenable": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽ•āŽŸā¯āŽŸāŽŗā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", "background_task_job": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽĒāŽŖāŽŋāŽ•āŽŗā¯", - "backup_database": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽŽā¯", - "backup_database_enable_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "backup_keep_last_amount": "āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽŗāŽĩ❁", - "backup_settings": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", - "backup_settings_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_database": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ¨āŽ•āŽ˛ā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_database_enable_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_keep_last_amount": "āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽŗāŽĩ❁", + "backup_onboarding_1_description": "āŽŽā¯‡āŽ•āŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĩā¯‡āŽąā¯ āŽ‡āŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¨āŽ•āŽ˛ā¯.", + "backup_onboarding_2_description": "āŽĩ❆āŽĩā¯āŽĩā¯‡āŽąā¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ¨āŽ•āŽ˛ā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗā¯. āŽ‡āŽ¤āŽŋāŽ˛ā¯ āŽŽā¯āŽ•ā¯āŽ•āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ…āŽ¨ā¯āŽ¤āŽ•ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ¨āŽ•āŽ˛ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ†āŽ•āŽŋāŽ¯āŽĩ❈ āŽ…āŽŸāŽ™ā¯āŽ•ā¯āŽŽā¯.", + "backup_onboarding_3_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽ°āŽĩāŽŋāŽŠā¯ āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ…āŽšāŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽ‰āŽŸā¯āŽĒāŽŸ. āŽ‡āŽ¤āŽŋāŽ˛ā¯ 1 āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨āŽ•āŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ 2 āŽšāŽžāŽ¤āŽŠāŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗā¯ āŽ…āŽŸāŽ™ā¯āŽ•ā¯āŽŽā¯.", + "backup_onboarding_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽ°āŽĩ❈ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽ¤āŽąā¯āŽ•āŽžāŽ• āŽ’āŽ°ā¯ 3-2-1 āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ¤ā¯€āŽ°ā¯āŽĩāŽŋāŽąā¯āŽ•āŽžāŽ•, āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯/āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ Immich āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯.", + "backup_onboarding_footer": "Immich-āŽ āŽ¤āŽ°āŽĩ❁ āŽ¨āŽ•āŽ˛ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁ āŽŽāŽŸā¯āŽĒā¯āŽĒāŽ¤ā¯ āŽĒāŽąā¯āŽąāŽŋāŽ¯ āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•ā¯āŽ•ā¯, āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ†āŽĩāŽŖāŽ¤ā¯āŽ¤ā¯ˆ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "backup_onboarding_parts_title": "3-2-1 āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯āŽŋāŽ˛ā¯ āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽĩāŽŠ āŽ…āŽŸāŽ™ā¯āŽ•ā¯āŽŽā¯:", + "backup_onboarding_title": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗā¯", + "backup_settings": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ¤āŽŋāŽŖāŽŋāŽĒā¯āŽĒ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "backup_settings_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗ āŽ¨āŽ•āŽ˛ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "cleared_jobs": "āŽŽā¯āŽŸāŽŋāŽ¤ā¯āŽ¤ āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯: {job}", - "config_set_by_file": "config āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ āŽ’āŽ°ā¯ config āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "config_set_by_file": "āŽ•āŽŸā¯āŽŸāŽŽā¯ˆāŽĒā¯āŽĒ❁, āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ āŽ’āŽ°ā¯ āŽ•āŽŸā¯āŽŸāŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "confirm_delete_library": "{library} āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "confirm_delete_library_assets": "āŽ‡āŽ¨ā¯āŽ¤ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ Immich āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ {count, plural, one {# contained asset} other {all # contained assets}} āŽ¨ā¯€āŽ•ā¯āŽ•āŽŋāŽĩāŽŋāŽŸā¯āŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯. āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĩāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯.", "confirm_email_below": "āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽ•ā¯€āŽ´ā¯‡ \"{email}\" āŽŽāŽŠ āŽ¤āŽŸā¯āŽŸāŽšā¯āŽšā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", "confirm_reprocess_all_faces": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯.", "confirm_user_password_reset": "{user} āŽ‡āŽŠā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "confirm_user_pin_code_reset": "{user} āŽ‡āŽŠā¯ āŽĒāŽŋāŽŠā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "create_job": "āŽĩā¯‡āŽ˛ā¯ˆāŽ¯ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", "cron_expression": "āŽ•ā¯āŽ°ā¯‹āŽŠā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒāŽžāŽŸā¯", "cron_expression_description": "CRON āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋ āŽšā¯āŽ•ā¯‡āŽŠāŽŋāŽ™ā¯ āŽ‡āŽŸā¯ˆāŽĩā¯†āŽŗāŽŋāŽ¯ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•ā¯āŽ•ā¯ āŽŽ.āŽ•āŽž. <āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁> āŽ•ā¯āŽ°ā¯‹āŽŠā¯āŽŸāŽžāŽĒā¯ āŽ•ā¯āŽ°ā¯ ", @@ -62,8 +81,13 @@ "force_delete_user_warning": "āŽŽāŽšā¯āŽšāŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ: āŽ‡āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ‰āŽŸāŽŠāŽŸāŽŋāŽ¯āŽžāŽ• āŽ…āŽ•āŽąā¯āŽąā¯āŽŽā¯. āŽ‡āŽ¤ā¯ˆ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", "image_format": "āŽĩāŽŸāŽŋāŽĩāŽŽā¯", "image_format_description": "WebP, JPEG āŽ āŽĩāŽŋāŽŸ āŽšāŽŋāŽąāŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽŽā¯†āŽ¤ā¯āŽĩāŽžāŽ• āŽ‰āŽŗā¯āŽŗāŽ¤ā¯.", + "image_fullsize_description": "āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯, āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯†āŽŸā¯āŽŸāŽžāŽŸā¯‡āŽŸā¯āŽŸāŽžāŽĩā¯āŽŸāŽŠā¯ āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽŽā¯āŽ´ā¯ āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸāŽŽā¯", + "image_fullsize_enabled": "āŽŽā¯āŽ´ā¯ āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯", + "image_fullsize_enabled_description": "āŽ‡āŽŖā¯ˆāŽ¯ āŽ‡āŽŖāŽ•ā¯āŽ•āŽŽāŽŋāŽ˛ā¯āŽ˛āŽž āŽĩāŽŸāŽŋāŽĩāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽā¯āŽ´ā¯ āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. \"āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋāŽ•ā¯āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ¯ā¯ˆ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒ❁\" āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯, āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋāŽ•ā¯āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ•āŽŗā¯ āŽŽāŽžāŽąā¯āŽąāŽŽāŽŋāŽŠā¯āŽąāŽŋ āŽ¨ā¯‡āŽ°āŽŸāŽŋāŽ¯āŽžāŽ•āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯. JPEG āŽĒā¯‹āŽŠā¯āŽą āŽ‡āŽŖā¯ˆāŽ¯ āŽ¨āŽŸā¯āŽĒ❁ āŽĩāŽŸāŽŋāŽĩāŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽžāŽ¤āŽŋāŽ•ā¯āŽ•āŽžāŽ¤ā¯.", + "image_fullsize_quality_description": "āŽŽā¯āŽ´ā¯ āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸāŽ¤ā¯ āŽ¤āŽ°āŽŽā¯ 1-100 āŽĩāŽ°ā¯ˆ. āŽ…āŽ¤āŽŋāŽ•āŽŽāŽžāŽŠ āŽŽāŽŖā¯ āŽšāŽŋāŽąāŽ¨ā¯āŽ¤āŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽŽā¯.", + "image_fullsize_title": "āŽŽā¯āŽ´ā¯ āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "image_prefer_embedded_preview": "āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ¨ā¯āŽ¤ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯āŽŠā¯āŽŠāŽŋāŽŸā¯", - "image_prefer_embedded_preview_setting_description": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽĒāŽŸ āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•āŽžāŽŠ āŽ‰āŽŗā¯āŽŗā¯€āŽŸāŽžāŽ• āŽŽā¯‚āŽ˛ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋāŽ•ā¯āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯. āŽ‡āŽ¤ā¯ āŽšāŽŋāŽ˛ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŋāŽ•āŽĩā¯āŽŽā¯ āŽ¤ā¯āŽ˛ā¯āŽ˛āŽŋāŽ¯āŽŽāŽžāŽŠ āŽĩāŽŖā¯āŽŖāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ¤āŽ•ā¯āŽ¤āŽŋ āŽ•ā¯‡āŽŽāŽ°āŽž āŽšāŽžāŽ°ā¯āŽ¨ā¯āŽ¤āŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ…āŽ¤āŽŋāŽ• āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽ•āŽ˛ā¯ˆāŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŸā¯āŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", + "image_prefer_embedded_preview_setting_description": "āŽĒāŽŸ āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•āŽžāŽŠ āŽ‰āŽŗā¯āŽŗā¯€āŽŸāŽžāŽ• āŽŽā¯‚āŽ˛ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯, āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯. āŽ‡āŽ¤ā¯ āŽšāŽŋāŽ˛ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŋāŽ•āŽĩā¯āŽŽā¯ āŽ¤ā¯āŽ˛ā¯āŽ˛āŽŋāŽ¯āŽŽāŽžāŽŠ āŽĩāŽŖā¯āŽŖāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ¤āŽ•ā¯āŽ¤āŽŋ āŽ•ā¯‡āŽŽāŽ°āŽž āŽšāŽžāŽ°ā¯āŽ¨ā¯āŽ¤āŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ…āŽ¤āŽŋāŽ• āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽ•āŽ˛ā¯ˆāŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŸā¯āŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "image_prefer_wide_gamut": "āŽ…āŽ•āŽŠā¯āŽą āŽĩāŽŖā¯āŽŖāŽĩāŽ°āŽŽā¯āŽĒ❁ āŽ¤ā¯‡āŽ°ā¯āŽĩ❁", "image_prefer_wide_gamut_setting_description": "āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ•āŽžāŽŸā¯āŽšāŽŋ āŽĒāŽŋ 3 āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯. āŽ‡āŽ¤ā¯ āŽĒāŽ°āŽ¨ā¯āŽ¤ āŽĩāŽŖā¯āŽŖāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽ¤āŽŋāŽ°ā¯āŽĩā¯āŽ•āŽŗā¯ˆ āŽšāŽŋāŽąāŽĒā¯āŽĒāŽžāŽ• āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽĒāŽ´ā¯ˆāŽ¯ āŽ‰āŽ˛āŽžāŽĩāŽŋ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸ āŽĒāŽ´ā¯ˆāŽ¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽŋāŽ¤ā¯āŽ¤āŽŋāŽ¯āŽžāŽšāŽŽāŽžāŽ• āŽ¤ā¯‹āŽŠā¯āŽąāŽ•ā¯āŽ•ā¯‚āŽŸā¯āŽŽā¯. āŽĩāŽŖā¯āŽŖ āŽŽāŽžāŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• SRGB āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ SRGB āŽ†āŽ• āŽĩā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ.", "image_preview_description": "āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯†āŽŸā¯āŽŸāŽžāŽŸā¯‡āŽŸā¯āŽŸāŽžāŽĩā¯āŽŸāŽŠā¯ āŽ¨āŽŸā¯āŽ¤ā¯āŽ¤āŽ° āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽĒāŽŸāŽŽā¯, āŽ’āŽąā¯āŽąā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ‡āŽ¯āŽ¨ā¯āŽ¤āŽŋāŽ° āŽ•āŽąā¯āŽąāŽ˛ā¯āŽ•ā¯āŽ•āŽžāŽ•āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯", @@ -83,8 +107,8 @@ "job_settings": "āŽĩā¯‡āŽ˛ā¯ˆ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "job_settings_description": "āŽĩā¯‡āŽ˛ā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❈ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "job_status": "āŽĩā¯‡āŽ˛ā¯ˆ āŽ¨āŽŋāŽ˛ā¯ˆ", - "jobs_delayed": "{JobCount, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {# āŽ¤āŽžāŽŽāŽ¤āŽŽāŽžāŽŠāŽ¤ā¯}}", - "jobs_failed": "{JobCount, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {# āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯}}", + "jobs_delayed": "{jobCount, plural, other {# āŽ¤āŽžāŽŽāŽ¤āŽŽāŽžāŽŠāŽ¤ā¯}}", + "jobs_failed": "{jobCount, plural, other {# āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯}}", "library_created": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽ¨ā¯‚āŽ˛āŽ•āŽŽā¯: {library}", "library_deleted": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽ¨ā¯‚āŽ˛āŽ•āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "library_import_path_description": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯ āŽ’āŽ°ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸāŽĩā¯āŽŽā¯. āŽ¤ā¯āŽŖā¯ˆāŽ•ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•āŽŗā¯ āŽ‰āŽŸā¯āŽĒāŽŸ āŽ‡āŽ¨ā¯āŽ¤āŽ•ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽ• āŽ¸ā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", @@ -93,13 +117,20 @@ "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": "āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "logging_enable_description": "āŽĒāŽ¤āŽŋāŽĩ❁ āŽšā¯†āŽ¯ā¯āŽĩāŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯", "logging_level_description": "āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽŽāŽ¨ā¯āŽ¤āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩ❁ āŽ¨āŽŋāŽ˛ā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯.", "logging_settings": "āŽĒāŽ¤āŽŋāŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯", + "machine_learning_availability_checks": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆāŽ•āŽŗā¯", + "machine_learning_availability_checks_description": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ‡āŽ¯āŽ¨ā¯āŽ¤āŽŋāŽ° āŽ•āŽąā¯āŽąāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ• āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¨ā¯āŽ¤ā¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ™ā¯āŽ•āŽŗā¯", + "machine_learning_availability_checks_enabled": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆāŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "machine_learning_availability_checks_interval": "āŽ‡āŽŸā¯ˆāŽĩā¯†āŽŗāŽŋāŽ¯ā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "machine_learning_availability_checks_interval_description": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆāŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽŽāŽŋāŽ˛ā¯āŽ˛āŽŋ āŽĩāŽŋāŽ¨āŽžāŽŸāŽŋāŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‡āŽŸā¯ˆāŽĩā¯†āŽŗāŽŋ", + "machine_learning_availability_checks_timeout": "āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", + "machine_learning_availability_checks_timeout_description": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆāŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŋāŽ˛ā¯āŽ˛āŽŋ āŽĩāŽŋāŽ¨āŽžāŽŸāŽŋāŽ•āŽŗāŽŋāŽ˛ā¯ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", "machine_learning_clip_model": "āŽ•āŽŋāŽŗāŽŋāŽĒā¯ āŽŽāŽžāŽŸāŽ˛ā¯", "machine_learning_clip_model_description": "CLIP āŽŽāŽžāŽŸāŽ˛ā¯ āŽĒā¯†āŽ¯āŽ°ā¯ āŽ‡āŽ™ā¯āŽ•ā¯‡ āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽ’āŽ°ā¯ āŽŽāŽžāŽŸāŽ¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽŋāŽ¯āŽĩā¯āŽŸāŽŠā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ 'āŽ¸ā¯āŽŽāŽžāŽ°ā¯āŽŸā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯' āŽĩā¯‡āŽ˛ā¯ˆāŽ¯ā¯ˆ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽŋāŽ˛ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗāŽĩā¯āŽŽā¯.", "machine_learning_duplicate_detection": "āŽ¨āŽ•āŽ˛ā¯ (āŽŸā¯‚āŽĒā¯āŽŗāŽŋāŽ•ā¯‡āŽŸā¯) āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¤āŽ˛ā¯", @@ -128,7 +159,7 @@ "machine_learning_smart_search_description": "CLIP āŽŽāŽžāŽŸā¯†āŽ˛ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋ āŽšā¯ŠāŽąā¯āŽĒā¯ŠāŽ°ā¯āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "machine_learning_smart_search_enabled": "āŽ¸ā¯āŽŽāŽžāŽ°ā¯āŽŸā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯", "machine_learning_smart_search_enabled_description": "āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯, āŽ¸ā¯āŽŽāŽžāŽ°ā¯āŽŸā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯āŽ•ā¯āŽ•āŽžāŽ• āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸāŽžāŽ¤ā¯.", - "machine_learning_url_description": "āŽ‡āŽ¯āŽ¨ā¯āŽ¤āŽŋāŽ° āŽ•āŽąā¯āŽąāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ. āŽ’āŽŠā¯āŽąā¯āŽ•ā¯āŽ•ā¯ āŽŽā¯‡āŽąā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽĩāŽ´āŽ™ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯āŽŽā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽĒāŽ¤āŽŋāŽ˛āŽŗāŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĩāŽ°ā¯ˆ, āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽ•āŽŸā¯ˆāŽšāŽŋ āŽĩāŽ°ā¯ˆ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯.", + "machine_learning_url_description": "āŽ‡āŽ¯āŽ¨ā¯āŽ¤āŽŋāŽ° āŽ•āŽąā¯āŽąāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ. āŽ’āŽŠā¯āŽąā¯āŽ•ā¯āŽ•ā¯ āŽŽā¯‡āŽąā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽĩāŽ´āŽ™ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯āŽŽā¯ āŽ’āŽĩā¯āŽĩā¯ŠāŽŠā¯āŽąāŽžāŽ• āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽĒāŽ¤āŽŋāŽ˛āŽŗāŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĩāŽ°ā¯ˆ, āŽŽā¯āŽ¤āŽ˛āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•āŽŸā¯ˆāŽšāŽŋ āŽĩāŽ°ā¯ˆ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯. āŽĒāŽ¤āŽŋāŽ˛āŽŗāŽŋāŽ•ā¯āŽ•āŽžāŽ¤ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ†āŽŠā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯ āŽĩāŽ°ā¯āŽŽā¯ āŽĩāŽ°ā¯ˆ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ•āŽŽāŽžāŽ•āŽĒā¯ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", "manage_concurrency": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❈ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "manage_log_settings": "āŽĒāŽ¤āŽŋāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "map_dark_style": "āŽ‡āŽ°ā¯āŽŖā¯āŽŸ āŽ¤ā¯€āŽŽā¯", @@ -144,6 +175,8 @@ "map_settings": "āŽŽā¯‡āŽĒā¯ & āŽœāŽŋāŽĒāŽŋāŽŽāŽ¸ā¯ (GPS) āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "map_settings_description": "āŽŽā¯‡āŽĒā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "map_style_description": "style.json āŽŽā¯‡āŽĒā¯ āŽ¤ā¯€āŽŽā¯āŽ•ā¯āŽ•āŽžāŽŠ URL", + "memory_cleanup_job": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯āŽ¤ā¯āŽ¤āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯", + "memory_generate_job": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ• āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽŽā¯", "metadata_extraction_job": "āŽŽā¯†āŽŸā¯āŽŸāŽžāŽŸā¯‡āŽŸā¯āŽŸāŽžāŽĩ❈āŽĒā¯ āŽĒāŽŋāŽ°āŽŋāŽ¤ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "metadata_extraction_job_description": "āŽœāŽŋāŽĒāŽŋāŽŽāŽ¸ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¤ā¯†āŽŗāŽŋāŽĩā¯āŽ¤ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽĒā¯‹āŽŠā¯āŽą āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯ āŽŽā¯†āŽŸā¯āŽŸāŽžāŽŸā¯‡āŽŸā¯āŽŸāŽž āŽ¤āŽ•āŽĩāŽ˛ā¯ˆāŽĒā¯ āŽĒāŽŋāŽ°āŽŋāŽ¤ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "metadata_faces_import_setting": "āŽŽā¯āŽ• āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -152,12 +185,26 @@ "metadata_settings_description": "āŽŽā¯‡āŽŠāŽŋāŽ˛ā¯ˆ āŽ¤āŽ°āŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "migration_job": "āŽ‡āŽŸāŽŽā¯āŽĒā¯†āŽ¯āŽ°ā¯āŽ¤āŽ˛ā¯", "migration_job_description": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ (āŽ¤āŽŽā¯āŽĒā¯āŽŠā¯†āŽ¯āŽŋāŽ˛ā¯) āŽšāŽŽā¯€āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "nightly_tasks_cluster_faces_setting_description": "āŽĒā¯āŽ¤āŽŋāŽ¤āŽžāŽ•āŽ•ā¯ āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŽā¯āŽ• āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯", + "nightly_tasks_cluster_new_faces_setting": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ¤ā¯ŠāŽ•ā¯āŽ¤āŽŋāŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯", + "nightly_tasks_database_cleanup_setting": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯āŽ¤ā¯āŽ¤āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯ āŽĒāŽŖāŽŋāŽ•āŽŗā¯", + "nightly_tasks_database_cleanup_setting_description": "āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒāŽ´ā¯ˆāŽ¯, āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠ āŽ¤āŽ°āŽĩ❈ āŽšā¯āŽ¤ā¯āŽ¤āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "nightly_tasks_generate_memories_setting": "āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", + "nightly_tasks_generate_memories_setting_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", + "nightly_tasks_missing_thumbnails_setting": "āŽĩāŽŋāŽŸā¯āŽĒāŽŸā¯āŽŸ āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", + "nightly_tasks_missing_thumbnails_setting_description": "āŽšāŽŋāŽąā¯āŽĒāŽŸ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•āŽžāŽ• āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛āŽžāŽŽāŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĩāŽ°āŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "nightly_tasks_settings": "āŽ‡āŽ°āŽĩ❁ āŽ¨ā¯‡āŽ°āŽĒā¯ āŽĒāŽŖāŽŋāŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "nightly_tasks_settings_description": "āŽ‡āŽ°āŽĩ❁ āŽ¨ā¯‡āŽ° āŽĒāŽŖāŽŋāŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋ", + "nightly_tasks_start_time_setting": "āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ• āŽ¨ā¯‡āŽ°āŽŽā¯", + "nightly_tasks_start_time_setting_description": "āŽšāŽ°ā¯āŽĩāŽ°ā¯ āŽ‡āŽ°āŽĩ❁ āŽ¨ā¯‡āŽ° āŽĒāŽŖāŽŋāŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽŽā¯ āŽ¨ā¯‡āŽ°āŽŽā¯", + "nightly_tasks_sync_quota_usage_setting": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯", + "nightly_tasks_sync_quota_usage_setting_description": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯, āŽĒāŽ¯āŽŠāŽ°ā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "no_paths_added": "āŽƒāŽĒā¯‹āŽ˛ā¯āŽŸā¯āŽŸāŽ°ā¯ āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "no_pattern_added": "āŽĒā¯‡āŽŸā¯āŽŸāŽ°ā¯āŽŠā¯ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "note_apply_storage_label_previous_assets": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒ❁: āŽŽā¯āŽŠā¯āŽĒ❁ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ˛ā¯‡āŽĒāŽŋāŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽ‡āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "note_cannot_be_changed_later": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒ❁: āŽ‡āŽ¤ā¯ˆ āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯!", "notification_email_from_address": "āŽŽā¯āŽ•āŽĩāŽ°āŽŋāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯", - "notification_email_from_address_description": "āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¨āŽ°āŽŋāŽŠā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ, āŽŽāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽžāŽŸā¯āŽŸāŽžāŽ•: \"āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ \"", + "notification_email_from_address_description": "āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¨āŽ°āŽŋāŽŠā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ, āŽŽāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽžāŽŸā¯āŽŸāŽžāŽ•: \"āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ \". āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽā¯āŽ•āŽĩāŽ°āŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽĩāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯.", "notification_email_host_description": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽšā¯‹āŽ¸ā¯āŽŸā¯ (āŽŽāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽžāŽŸā¯āŽŸāŽžāŽ•: smtp.immich.app)", "notification_email_ignore_certificate_errors": "āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽĒāŽŋāŽ´ā¯ˆāŽ•āŽŗā¯ˆ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "notification_email_ignore_certificate_errors_description": "TLS āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❁ āŽĒāŽŋāŽ´ā¯ˆāŽ•āŽŗā¯ˆ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ (āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ)", @@ -177,11 +224,14 @@ "oauth_auto_register": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽĩ❁", "oauth_auto_register_description": "OAuth āŽ‰āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¨ā¯āŽ¤ āŽĒāŽŋāŽąāŽ•ā¯ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", "oauth_button_text": "āŽĒāŽŸā¯āŽŸāŽŠā¯ āŽ‰āŽ°ā¯ˆ", + "oauth_client_secret_description": "āŽ…āŽĩāŽšāŽŋāŽ¯āŽŽā¯, OAuth āŽĩāŽ´āŽ™ā¯āŽ•ā¯āŽ¨āŽ°āŽžāŽ˛ā¯ PKCE (āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯āŽĒā¯ āŽĒāŽ°āŽŋāŽŽāŽžāŽąā¯āŽąāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•āŽžāŽŠ āŽ†āŽ¤āŽžāŽ° āŽĩāŽŋāŽšā¯ˆ) āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽĩāŽŋāŽŸā¯āŽŸāŽžāŽ˛ā¯", "oauth_enable_description": "OAuth āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ•", "oauth_mobile_redirect_uri": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI", "oauth_mobile_redirect_uri_override": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI āŽŽā¯‡āŽ˛ā¯†āŽ´ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯", - "oauth_mobile_redirect_uri_override_description": "'app.immich:/' āŽ¤āŽĩāŽąāŽžāŽŠ āŽĩāŽ´āŽŋāŽŽāŽžāŽąā¯āŽąā¯ URI āŽ†āŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", - "oauth_settings": "Oauth", + "oauth_mobile_redirect_uri_override_description": "''{callback}'' āŽĒā¯‹āŽŠā¯āŽą āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ URI āŽ OAuth āŽĩāŽ´āŽ™ā¯āŽ•ā¯āŽ¨āŽ°ā¯ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽžāŽ¤āŽĒā¯‹āŽ¤ā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "oauth_role_claim": "āŽĒāŽ¤āŽĩāŽŋ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯", + "oauth_role_claim_description": "āŽ‡āŽ¨ā¯āŽ¤āŽ•ā¯ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽŋāŽŠā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋ āŽ…āŽŖā¯āŽ•āŽ˛ā¯ˆ āŽĩāŽ´āŽ™ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽŋāŽ˛ā¯ 'āŽĒāŽ¯āŽŠāŽ°ā¯' āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ 'āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋ' āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", + "oauth_settings": "āŽ“āŽ†āŽ¤ā¯", "oauth_settings_description": "OAuth āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "oauth_settings_more_details": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽąā¯āŽąāŽŋāŽ¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯, āŽŸāŽžāŽ•ā¯āŽ¸ā¯ āŽāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "oauth_storage_label_claim": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ˛ā¯‡āŽĒāŽŋāŽŗā¯ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯", @@ -189,7 +239,9 @@ "oauth_storage_quota_claim": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯", "oauth_storage_quota_claim_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛āŽŋāŽŠā¯ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "oauth_storage_quota_default": "āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ (GiB)", - "oauth_storage_quota_default_description": "GiB āŽ‡āŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯āŽŽā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽĒā¯‹āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ (āŽĩāŽ°āŽŽā¯āŽĒāŽąā¯āŽą āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸāŽŋāŽąā¯āŽ•ā¯ 0 āŽ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯).", + "oauth_storage_quota_default_description": "GiB āŽ‡āŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ•ā¯‹āŽ°āŽ˛ā¯āŽŽā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽĒā¯‹āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ .", + "oauth_timeout": "āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", + "oauth_timeout_description": "āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ•āŽžāŽ˛āŽ•ā¯āŽ•ā¯†āŽŸā¯ āŽŽāŽŋāŽ˛ā¯āŽ˛āŽŋ āŽĩāŽŋāŽŠāŽžāŽŸāŽŋāŽ•āŽŗāŽŋāŽ˛ā¯", "password_enable_description": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯āŽĩā¯āŽŽā¯", "password_settings": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁", "password_settings_description": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -203,7 +255,7 @@ "reset_settings_to_default": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆāŽ•ā¯āŽ•ā¯ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "reset_settings_to_recent_saved": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "scanning_library": "āŽšā¯āŽ•ā¯‡āŽŠāŽŋāŽ™ā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽŽā¯", - "search_jobs": "āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", + "search_jobs": "āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯â€Ļ", "send_welcome_email": "āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽĒ❁ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒāŽĩā¯āŽŽā¯", "server_external_domain_settings": "āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ•āŽŗāŽŽā¯", "server_external_domain_settings_description": "HTTP (āŽ•āŽŗā¯) āŽ‰āŽŸā¯āŽĒāŽŸ āŽĒā¯ŠāŽ¤ā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽŸā¯ŠāŽŽā¯ˆāŽŠā¯: //", @@ -224,9 +276,10 @@ "storage_template_hash_verification_enabled_description": "āŽšāŽžāŽˇā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❈ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ¤āŽžāŽ•ā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¤ā¯āŽ¤ā¯ āŽ‰āŽąā¯āŽ¤āŽŋāŽ¯āŽžāŽ• āŽ¤ā¯†āŽ°āŽŋāŽ¯āŽžāŽĩāŽŋāŽŸā¯āŽŸāŽžāŽ˛ā¯ āŽ‡āŽ¤ā¯ˆ āŽŽā¯āŽŸāŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸāŽžāŽŽā¯", "storage_template_migration": "āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽ‡āŽŸāŽŽā¯āŽĒā¯†āŽ¯āŽ°ā¯āŽĩ❁", "storage_template_migration_description": "āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ {template} āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", - "storage_template_migration_info": "āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽŽāŽžāŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯. āŽŽā¯āŽŠā¯āŽĒ❁ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, {job} āŽ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "storage_template_migration_info": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨ā¯€āŽŸā¯āŽŸāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽšāŽŋāŽąāŽŋāŽ¯ āŽŽāŽ´ā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽžāŽąā¯āŽąā¯āŽŽā¯. āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽŽāŽžāŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯. āŽŽā¯āŽŠā¯āŽĒ❁ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, {job} āŽ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "storage_template_migration_job": "āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯ āŽ‡āŽŸāŽŽā¯āŽĒā¯†āŽ¯āŽ°ā¯āŽĩ❁ āŽĩā¯‡āŽ˛ā¯ˆ", "storage_template_more_details": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽąā¯āŽąāŽŋāŽ¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯, Storage Template āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ…āŽ¤āŽŠā¯ āŽ¤āŽžāŽ•ā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ āŽāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "storage_template_onboarding_description_v2": "āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯āŽšāŽŽā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽĩāŽ°ā¯ˆāŽ¯āŽąā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ• āŽ’āŽ´ā¯āŽ™ā¯āŽ•āŽŽā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯. āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•ā¯āŽ•ā¯, āŽ†āŽĩāŽŖāŽ™ā¯āŽ•āŽŗā¯ āŽāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "storage_template_path_length": "āŽ¤ā¯‹āŽ°āŽžāŽ¯āŽŽāŽžāŽŠ āŽĒāŽžāŽ¤ā¯ˆ āŽ¨ā¯€āŽŗ āŽĩāŽ°āŽŽā¯āŽĒ❁: {length, number}/{limit, number}", "storage_template_settings": "āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯ āŽŸā¯†āŽŽā¯āŽĒā¯āŽŗā¯‡āŽŸā¯", "storage_template_settings_description": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -241,7 +294,7 @@ "template_email_update_album": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽĩ❈āŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "template_email_welcome": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽĩ❈ āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯", "template_settings": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯", - "template_settings_description": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "template_settings_description": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "theme_custom_css_settings": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ CSS", "theme_custom_css_settings_description": "CSS āŽ…āŽŽā¯āŽšāŽŽā¯ Immich āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠāŽžāŽ•ā¯āŽ• āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "theme_settings": "āŽ¤ā¯€āŽŽā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", @@ -270,8 +323,10 @@ "transcoding_constant_rate_factor": "āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽžāŽŠ āŽĩā¯€āŽ¤ āŽ•āŽžāŽ°āŽŖāŽŋ (-crf)", "transcoding_constant_rate_factor_description": "āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽ¤āŽ° āŽ¨āŽŋāŽ˛ā¯ˆ. āŽĩāŽ´āŽ•ā¯āŽ•āŽŽāŽžāŽŠ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ H.264 āŽ•ā¯āŽ•ā¯ 23, HEVC āŽ•ā¯āŽ•ā¯ 28, VP9 āŽ•ā¯āŽ•ā¯ 31, āŽŽāŽąā¯āŽąā¯āŽŽā¯ AV1 āŽ•ā¯āŽ•ā¯ 35 āŽ†āŽ•ā¯āŽŽā¯. āŽ•ā¯āŽąā¯ˆāŽĩāŽžāŽŠāŽ¤ā¯ āŽšāŽŋāŽąāŽ¨ā¯āŽ¤āŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_disabled_description": "āŽŽāŽ¨ā¯āŽ¤ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽžāŽ¤ā¯€āŽ°ā¯āŽ•āŽŗā¯, āŽšāŽŋāŽ˛ āŽĩāŽžāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽĒāŽŋāŽŗā¯‡āŽĒā¯‡āŽ•ā¯āŽ•ā¯ˆ āŽ‰āŽŸā¯ˆāŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯", + "transcoding_encoding_options": "āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "transcoding_encoding_options_description": "āŽ•ā¯āŽąāŽŋāŽ¯āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ•ā¯‹āŽŸā¯†āŽ•ā¯āŽ•ā¯āŽ•āŽŗā¯, āŽ¤ā¯†āŽŗāŽŋāŽĩā¯āŽ¤ā¯āŽ¤āŽŋāŽąāŽŠā¯, āŽ¤āŽ°āŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŋāŽą āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "transcoding_hardware_acceleration": "āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŽā¯āŽŸā¯āŽ•ā¯āŽ•āŽŽā¯", - "transcoding_hardware_acceleration_description": "āŽšā¯‹āŽ¤āŽŠā¯ˆ; āŽŽāŽŋāŽ• āŽĩā¯‡āŽ•āŽŽāŽžāŽ•, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ…āŽ¤ā¯‡ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤ āŽ¤āŽ•ā¯āŽ¤āŽŋ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯", + "transcoding_hardware_acceleration_description": "āŽĒāŽ°āŽŋāŽšā¯‹āŽ¤āŽŠā¯ˆ: āŽĩā¯‡āŽ•āŽŽāŽžāŽŠ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽ¸ā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯ āŽ†āŽŠāŽžāŽ˛ā¯ āŽ…āŽ¤ā¯‡ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ¤āŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•ā¯āŽąā¯ˆāŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "transcoding_hardware_decoding": "āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŸāŽŋāŽ•ā¯‹āŽŸāŽŋāŽ™ā¯", "transcoding_hardware_decoding_setting_description": "āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽ°ā¯ˆāŽĩ❁āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽ• āŽ‡āŽąā¯āŽ¤āŽŋ āŽŽā¯āŽ¤āŽ˛ā¯ āŽ‡āŽąā¯āŽ¤āŽŋ āŽŽā¯āŽŸā¯āŽ•ā¯āŽ•āŽŽā¯ āŽ†āŽ•āŽŋāŽ¯āŽĩāŽąā¯āŽąā¯ˆ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽŽāŽ˛ā¯āŽ˛āŽž āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛ā¯āŽŽā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯āŽ•ā¯āŽ•ā¯‚āŽŸāŽžāŽ¤ā¯.", "transcoding_max_b_frames": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋ-āŽĒāŽŋāŽ°ā¯‡āŽŽā¯āŽ•āŽŗā¯", @@ -281,6 +336,8 @@ "transcoding_max_keyframe_interval": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽ•ā¯€āŽƒāŽĒā¯āŽ°ā¯‡āŽŽā¯ āŽ‡āŽŸā¯ˆāŽĩā¯†āŽŗāŽŋ", "transcoding_max_keyframe_interval_description": "āŽ•ā¯€āŽƒāŽĒā¯āŽ°ā¯‡āŽŽā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽ°ā¯‡āŽŽā¯ āŽ¤ā¯‚āŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽŽā¯‹āŽšāŽŽāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ¨ā¯‡āŽ°āŽ™ā¯āŽ•āŽŗā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĩā¯‡āŽ•āŽŽāŽžāŽŠ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ•āŽŗāŽŋāŽ˛ā¯ āŽ¤āŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛āŽžāŽŽā¯. 0 āŽ‡āŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❈ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_optimal_description": "āŽ‡āŽ˛āŽ•ā¯āŽ•ā¯ āŽ¤ā¯€āŽ°ā¯āŽŽāŽžāŽŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŸ āŽ‰āŽ¯āŽ°ā¯āŽ¨ā¯āŽ¤ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽāŽąā¯āŽąā¯āŽ•ā¯āŽ•ā¯ŠāŽŗā¯āŽŗāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩāŽŸāŽŋāŽĩāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "transcoding_policy": "āŽ•ā¯āŽąāŽŋāŽŽāŽžāŽąā¯āŽąāŽ•ā¯ āŽ•ā¯ŠāŽŗā¯āŽ•ā¯ˆ", + "transcoding_policy_description": "āŽ’āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽ•ā¯āŽąāŽŋāŽŽāŽžāŽąā¯āŽąāŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "transcoding_preferred_hardware_device": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽŽāŽžāŽŠ āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽŽā¯", "transcoding_preferred_hardware_device_description": "VAAPI āŽŽāŽąā¯āŽąā¯āŽŽā¯ QSV āŽ•ā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯. āŽĩāŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯āŽ•āŽŋāŽąā¯āŽ•ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽŸā¯āŽ°ā¯ˆ āŽŽā¯āŽŠā¯ˆāŽ¯ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_preset_preset": "āŽŽā¯āŽŠā¯āŽŠāŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ (-āŽĒāŽŋāŽ°āŽšā¯†āŽŸā¯)", @@ -289,6 +346,7 @@ "transcoding_reference_frames_description": "āŽ•ā¯ŠāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽŸā¯āŽŸāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸ āŽĩā¯‡āŽŖā¯āŽŸāŽŋāŽ¯ āŽĒāŽŋāŽ°ā¯‡āŽŽā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ. āŽ…āŽ¤āŽŋāŽ• āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯†āŽ¤ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ. 0 āŽ‡āŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❈ āŽ¤āŽžāŽŠāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "transcoding_required_description": "āŽāŽąā¯āŽąā¯āŽ•ā¯āŽ•ā¯ŠāŽŗā¯āŽŗāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩāŽŸāŽŋāŽĩāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛āŽžāŽ¤ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", "transcoding_settings": "āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "transcoding_settings_description": "āŽŽāŽ¨ā¯āŽ¤ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽ¸ā¯āŽ•ā¯‹āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯, āŽ…āŽĩāŽąā¯āŽąā¯ˆ āŽŽāŽĩā¯āŽĩāŽžāŽąā¯ āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "transcoding_target_resolution": "āŽ‡āŽ˛āŽ•ā¯āŽ•ā¯ āŽ¤ā¯€āŽ°ā¯āŽŽāŽžāŽŠāŽŽā¯", "transcoding_target_resolution_description": "āŽ…āŽ¤āŽŋāŽ• āŽ¤ā¯€āŽ°ā¯āŽŽāŽžāŽŠāŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽ¤āŽŋāŽ• āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽ…āŽ¤āŽŋāŽ• āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•ā¯āŽŽā¯, āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽŗāŽĩā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽŽāŽąā¯āŽŽā¯ŠāŽ´āŽŋāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯āŽąā¯ˆāŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯.", "transcoding_temporal_aq": "āŽ¤āŽŽā¯āŽĒā¯‹āŽ°ā¯āŽ˛ā¯", @@ -301,23 +359,28 @@ "transcoding_transcode_policy_description": "āŽ’āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ•ā¯ŠāŽŗā¯āŽ•ā¯ˆ. āŽŽāŽšā¯.āŽŸāŽŋ.āŽ†āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ (āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽ™ā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ āŽ¤āŽĩāŽŋāŽ°).", "transcoding_two_pass_encoding": "āŽ‡āŽ°āŽŖā¯āŽŸā¯-āŽĒāŽžāŽšā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯", "transcoding_two_pass_encoding_setting_description": "āŽšāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ• āŽ‡āŽ°āŽŖā¯āŽŸā¯ āŽĒāŽžāŽšā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯. āŽŽā¯‡āŽ•ā¯āŽšā¯ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ (H.264 āŽŽāŽąā¯āŽąā¯āŽŽā¯ HEVC āŽ‰āŽŸāŽŠā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯ āŽ‡āŽ¤ā¯ āŽ¤ā¯‡āŽĩ❈āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯), āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯āŽŸā¯ˆ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽžāŽ•āŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸ āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽĩāŽ°āŽŽā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąāŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ CRF āŽ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. VP9 āŽāŽĒā¯ āŽĒā¯ŠāŽąā¯āŽ¤ā¯āŽ¤āŽĩāŽ°ā¯ˆ, āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽĒāŽŋāŽŸā¯āŽ°ā¯‡āŽŸā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ CRF āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛āŽžāŽŽā¯.", + "transcoding_video_codec": "āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽ•ā¯‹āŽŸā¯†āŽ•ā¯", "transcoding_video_codec_description": "VP9 āŽ…āŽ¤āŽŋāŽ• āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩāŽ˛ā¯ˆ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¤āŽŠā¯āŽŽā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸāŽŋāŽąā¯āŽ•ā¯ āŽ…āŽ¤āŽŋāŽ• āŽ¨ā¯‡āŽ°āŽŽā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•ā¯āŽŽā¯. HEVC āŽ‡āŽ¤ā¯‡āŽĒā¯‹āŽ˛ā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤ āŽĩāŽ˛ā¯ˆ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¤āŽŠā¯āŽŽā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. H.264 āŽĒāŽ°āŽĩāŽ˛āŽžāŽ• āŽ‡āŽŖāŽ•ā¯āŽ•āŽŽāŽžāŽŠāŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽĩāŽŋāŽ°ā¯ˆāŽĩāŽžāŽŠāŽ¤ā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽŽāŽŋāŽ•āŽĒā¯ āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ.āŽĩāŽŋ 1 āŽŽāŽŋāŽ•āŽĩā¯āŽŽā¯ āŽ¤āŽŋāŽąāŽŽā¯ˆāŽ¯āŽžāŽŠ āŽ•ā¯‹āŽŸā¯†āŽ•ā¯ āŽ†āŽŠāŽžāŽ˛ā¯ āŽĒāŽ´ā¯ˆāŽ¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽ¤āŽĩāŽŋ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ.", "trash_enabled_description": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "trash_number_of_days": "āŽ¨āŽžāŽŸā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", "trash_number_of_days_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽŽā¯āŽŠā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", "trash_settings": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "trash_settings_description": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "unlink_all_oauth_accounts": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ OAuth āŽ•āŽŖāŽ•ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "unlink_all_oauth_accounts_description": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĩāŽ´āŽ™ā¯āŽ•ā¯āŽ¨āŽ°ā¯āŽ•ā¯āŽ•ā¯ āŽŽāŽžāŽąā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽŽā¯āŽŠā¯, āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ OAuth āŽ•āŽŖāŽ•ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽŋāŽ˛ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯.", + "unlink_all_oauth_accounts_prompt": "āŽŽāŽ˛ā¯āŽ˛āŽž OAuth āŽ•āŽŖāŽ•ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ OAuth āŽāŽŸāŽŋāŽ¯ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ‡āŽ¤ā¯ˆ āŽ¤āŽŋāŽ°ā¯āŽŽā¯āŽĒāŽĒā¯ āŽĒā¯†āŽąā¯ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", "user_cleanup_job": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¤ā¯‚āŽ¯ā¯āŽŽā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯", - "user_delete_delay": " {user} āŽ‡āŽŠā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ {āŽ¤āŽžāŽŽāŽ¤āŽŽā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽžāŽŗā¯} āŽŽāŽąā¯āŽą {# āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯}} āŽ‡āŽ˛ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ° āŽ¨ā¯€āŽ•ā¯āŽ• āŽ¤āŽŋāŽŸā¯āŽŸāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", + "user_delete_delay": "{user}āŽ‡āŽŠā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ {delay, plural, one {# āŽ¨āŽžāŽŗā¯} other {# āŽ¨āŽžāŽŗā¯āŽ•āŽŗā¯}}āŽ‡āŽ˛ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ° āŽ¨ā¯€āŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽŋāŽŸā¯āŽŸāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", "user_delete_delay_settings": "āŽ¤āŽžāŽŽāŽ¤āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "user_delete_delay_settings_description": "āŽŽāŽŖā¯ of days after āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯†āŽąā¯āŽ¨āŽ°ā¯ permanently āŽ¨ā¯€āŽ•ā¯āŽ•ā¯ a user's account and assets. āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽ¤āŽ¯āŽžāŽ°āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ• āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽ¨āŽŗā¯āŽŗāŽŋāŽ°āŽĩāŽŋāŽ˛ā¯ āŽ‡āŽ¯āŽ™ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛ā¯ āŽŽāŽžāŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ āŽŽāŽ°āŽŖāŽ¤āŽŖā¯āŽŸāŽŠā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯€āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", "user_delete_immediately": " {user} āŽ‡āŽŠā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ° āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯āŽ•ā¯āŽ•āŽžāŽ• āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ¨āŽŋāŽąā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽ‰āŽŸāŽŠāŽŸāŽŋāŽ¯āŽžāŽ• .", "user_delete_immediately_checkbox": "āŽ‰āŽŸāŽŠāŽŸāŽŋāŽ¯āŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• āŽĒāŽ¯āŽŠāŽ°ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "user_details": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯", "user_management": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽŽā¯‡āŽ˛āŽžāŽŖā¯āŽŽā¯ˆ", "user_password_has_been_reset": "āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯:", "user_password_reset_description": "āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•ā¯āŽ•ā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽĩā¯āŽŽā¯, āŽ…āŽĩāŽ°ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩāŽŋāŽ˛ā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽąā¯ āŽ…āŽĩāŽ°ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽ¤ā¯ āŽ¤ā¯†āŽ°āŽŋāŽĩāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "user_restore_description": " {user} āŽ‡āŽŠā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", - "user_restore_scheduled_removal": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆ - {āŽ¤ā¯‡āŽ¤āŽŋ, āŽ¤ā¯‡āŽ¤āŽŋ, āŽ¨ā¯€āŽŖā¯āŽŸ} āŽ‡āŽ˛ā¯ āŽ¤āŽŋāŽŸā¯āŽŸāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯", + "user_restore_scheduled_removal": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆ - {date, date, long} āŽ‡āŽ˛ā¯ āŽ¤āŽŋāŽŸā¯āŽŸāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯", "user_settings": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "user_settings_description": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "user_successfully_removed": "āŽĒāŽ¯āŽŠāŽ°ā¯ {email} āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯.", @@ -332,29 +395,62 @@ "admin_password": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", "administration": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŽā¯", "advanced": "āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽŸ", - "age_months": "āŽ…āŽ•āŽĩ❈ {āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽŽāŽžāŽ¤āŽŽā¯} āŽŽāŽąā¯āŽą {# āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯}}", - "age_year_months": "āŽ…āŽ•āŽĩ❈ 1 āŽ…āŽ•āŽĩ❈, {āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽŽāŽžāŽ¤āŽŽā¯} āŽŽāŽąā¯āŽą {# āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯}}", - "age_years": "{āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {āŽĩāŽ¯āŽ¤ā¯ #}}", + "advanced_settings_enable_alternate_media_filter_subtitle": "āŽŽāŽžāŽąā¯āŽąā¯ āŽ…āŽŗāŽĩā¯āŽ•ā¯‹āŽ˛ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩāŽŋāŽŠā¯ āŽĒā¯‹āŽ¤ā¯ āŽŽā¯€āŽŸāŽŋāŽ¯āŽžāŽĩ❈ āŽĩāŽŸāŽŋāŽ•āŽŸā¯āŽŸ āŽ‡āŽ¨ā¯āŽ¤ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯. āŽŽāŽ˛ā¯āŽ˛āŽž āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ†āŽĒā¯āŽ¸ā¯ āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽĩāŽ¤āŽŋāŽ˛ā¯ āŽšāŽŋāŽ•ā¯āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ‡āŽ¤ā¯ˆ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "advanced_settings_enable_alternate_media_filter_title": "[āŽĒāŽ°āŽŋāŽšā¯‹āŽ¤āŽŠā¯ˆāŽ•ā¯āŽ•ā¯ āŽ‰āŽŸā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯] āŽŽāŽžāŽąā¯āŽąā¯ āŽšāŽžāŽ¤āŽŠ āŽ†āŽ˛ā¯āŽĒ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽĩāŽŸāŽŋāŽĒā¯āŽĒāŽžāŽŠā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "advanced_settings_log_level_title": "āŽĒāŽ¤āŽŋāŽĩ❁ āŽ¨āŽŋāŽ˛ā¯ˆ: {level}", + "advanced_settings_prefer_remote_subtitle": "āŽšāŽŋāŽ˛ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽŗā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽŽāŽŋāŽ•āŽĩā¯āŽŽā¯ āŽŽā¯†āŽ¤ā¯āŽĩāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯. āŽ…āŽ¤āŽąā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽ• āŽšāŽ°ā¯āŽĩāŽ°ā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽą āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯ˆāŽšā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", + "advanced_settings_prefer_remote_title": "āŽ°āŽŋāŽŽā¯‹āŽŸā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽā¯āŽŠā¯āŽŠā¯āŽ°āŽŋāŽŽā¯ˆ āŽ•ā¯ŠāŽŸā¯", + "advanced_settings_proxy_headers_subtitle": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯ā¯āŽŸāŽŠā¯āŽŽā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ…āŽŠā¯āŽĒā¯āŽĒ āŽĩā¯‡āŽŖā¯āŽŸāŽŋāŽ¯ āŽĒā¯āŽ°āŽžāŽ•ā¯āŽ¸āŽŋ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽĩāŽ°ā¯ˆāŽ¯āŽąā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "advanced_settings_proxy_headers_title": "āŽĒā¯āŽ°āŽžāŽ•ā¯āŽ¸āŽŋ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "advanced_settings_readonly_mode_subtitle": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽąā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒāŽŸāŽŋāŽĒā¯āŽĒāŽ¤āŽąā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽĒāŽ˛ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽĒā¯āŽĒāŽ¤ā¯, āŽĒāŽ•āŽŋāŽ°ā¯āŽ¤āŽ˛ā¯, āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¤āŽ˛ā¯, āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯ āŽĒā¯‹āŽŠā¯āŽą āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽŠ. āŽĒāŽŋāŽ°āŽ¤āŽžāŽŠ āŽ¤āŽŋāŽ°ā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ…āŽĩāŽ¤āŽžāŽ°ā¯ āŽĩāŽ´āŽŋāŽ¯āŽžāŽ• āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯/āŽŽā¯āŽŸāŽ•ā¯āŽ•ā¯", + "advanced_settings_readonly_mode_title": "āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡āŽ¯āŽžāŽŠ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ", + "advanced_settings_self_signed_ssl_subtitle": "āŽšāŽ°ā¯āŽĩāŽ°ā¯ āŽŽāŽŖā¯āŽŸā¯āŽĒāŽžāŽ¯āŽŋāŽŖā¯āŽŸāŽŋāŽąā¯āŽ•āŽžāŽŠ SSL āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❈ āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽšā¯āŽ¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽŸ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽ¤ā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽžāŽŠāŽ¤ā¯.", + "advanced_settings_self_signed_ssl_title": "āŽšā¯āŽ¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽŸ SSL āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋ", + "advanced_settings_sync_remote_deletions_subtitle": "āŽ‡āŽŖā¯ˆāŽ¯āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¨āŽŸāŽĩāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯, āŽ‡āŽ¨ā¯āŽ¤āŽšā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ’āŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "advanced_settings_sync_remote_deletions_title": "āŽ¤ā¯ŠāŽ˛ā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ [āŽĒāŽ°āŽŋāŽšā¯‹āŽ¤āŽŠā¯ˆāŽ•ā¯āŽ•ā¯ āŽ‰āŽŸā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯]", + "advanced_settings_tile_subtitle": "āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "advanced_settings_troubleshooting_subtitle": "āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯āŽ•ā¯āŽ•ā¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "advanced_settings_troubleshooting_title": "āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯", + "age_months": "āŽ…āŽ•āŽĩ❈ {months, plural, one {# āŽ¤āŽŋāŽ™ā¯āŽ•āŽŗā¯} other {# āŽ¤āŽŋāŽ™ā¯āŽ•āŽŗā¯āŽ•āŽŗā¯}}", + "age_year_months": "āŽ…āŽ•āŽĩ❈ 1 āŽ†āŽŖā¯āŽŸā¯, {months, plural, one {# āŽ¤āŽŋāŽ™ā¯āŽ•āŽŗā¯} other {# āŽ¤āŽŋāŽ™ā¯āŽ•āŽŗā¯āŽ•āŽŗā¯}}", + "age_years": "{years, plural, other {āŽ…āŽ•āŽĩ❈ #}}", "album_added": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "album_added_notification_setting_description": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒā¯†āŽąā¯āŽ™ā¯āŽ•āŽŗā¯", "album_cover_updated": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽ•āŽĩāŽ°ā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "album_delete_confirmation": "{album} āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "album_delete_confirmation_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽŽāŽąā¯āŽą āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗāŽžāŽ˛ā¯ āŽ‡āŽ¤ā¯ˆ āŽ…āŽŖā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", + "album_deleted": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "album_info_card_backup_album_excluded": "āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "album_info_card_backup_album_included": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "album_info_updated": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "album_leave": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯?", - "album_leave_confirmation": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• {āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "album_leave_confirmation": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• {album}āŽ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "album_name": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒā¯†āŽ¯āŽ°ā¯", "album_options": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "album_remove_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ…āŽ•āŽąā¯āŽąāŽĩāŽž?", "album_remove_user_confirmation": "{user} āŽ āŽ…āŽ•āŽąā¯āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "album_search_not_found": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯āŽŸāŽŠā¯ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "album_share_no_users": "āŽ‡āŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽ˛ā¯āŽ˛āŽž āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯āŽŽā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽ¤āŽžāŽ•āŽ¤ā¯ āŽ¤ā¯†āŽ°āŽŋāŽ•āŽŋāŽąāŽ¤ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗāŽŋāŽŸāŽŽā¯ āŽŽāŽ¨ā¯āŽ¤ āŽĒāŽ¯āŽŠāŽ°ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ.", + "album_summary": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽšā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŽā¯", "album_updated": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "album_updated_setting_description": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒā¯†āŽąā¯āŽ™ā¯āŽ•āŽŗā¯", "album_user_left": "āŽ‡āŽŸāŽ¤ā¯ {album}", "album_user_removed": "āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {user}", + "album_viewer_appbar_delete_confirm": "āŽ‡āŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽ•ā¯āŽ•āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "album_viewer_appbar_share_err_delete": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "album_viewer_appbar_share_err_leave": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "album_viewer_appbar_share_err_remove": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽšāŽŋāŽ•ā¯āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽ‰āŽŗā¯āŽŗāŽŠ", + "album_viewer_appbar_share_err_title": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❈ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "album_viewer_appbar_share_leave": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąā¯", + "album_viewer_appbar_share_to": "āŽĒāŽ•āŽŋāŽ°ā¯", + "album_viewer_page_share_add_users": "āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "album_with_link_access": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽ‰āŽŗā¯āŽŗ āŽŽāŽĩāŽ°ā¯āŽŽā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽŸā¯āŽŸā¯āŽŽā¯.", "albums": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯", - "albums_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽ†āŽ˛ā¯āŽĒāŽŽā¯} āŽĒāŽŋāŽą {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯}}", + "albums_count": "{count, plural, one {{count, number} āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒ❁} other {{count, number} āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒā¯āŽ•āŽŗā¯}}", + "albums_default_sort_order": "āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯", + "albums_default_sort_order_description": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ†āŽ°āŽŽā¯āŽĒ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛ā¯ āŽĩāŽ°āŽŋāŽšā¯ˆ.", + "albums_feature_description": "āŽĒāŽŋāŽą āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗāŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒā¯āŽ•āŽŗā¯.", + "albums_on_device_count": "āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ ({count})", "all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŽā¯", "all_albums": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯āŽŽā¯", "all_people": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽŽāŽ•ā¯āŽ•āŽŗā¯āŽŽā¯", @@ -363,48 +459,161 @@ "allow_edits": "āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "allow_public_user_to_download": "āŽĒā¯ŠāŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "allow_public_user_to_upload": "āŽĒā¯ŠāŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "alt_text_qr_code": "QR āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯ āŽĒāŽŸāŽŽā¯", "anti_clockwise": "āŽ•āŽŸāŽŋāŽ•āŽžāŽ° āŽŽāŽ¤āŽŋāŽ°ā¯āŽĒā¯āŽĒ❁", "api_key": "āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆ", "api_key_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽ’āŽ°ā¯ āŽŽā¯āŽąā¯ˆ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ•āŽžāŽŖā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯. āŽšāŽžāŽŗāŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯‚āŽŸā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽŽā¯āŽŠā¯ āŽ…āŽ¤ā¯ˆ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽāŽąāŽ•ā¯āŽ•āŽžāŽ¤ā¯€āŽ°ā¯āŽ•āŽŗā¯.", "api_key_empty": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆ āŽĒā¯†āŽ¯āŽ°ā¯ āŽ•āŽžāŽ˛āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽžāŽ¤ā¯", "api_keys": "āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆāŽ•āŽŗā¯", + "app_bar_signout_dialog_content": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "app_bar_signout_dialog_ok": "āŽ†āŽŽā¯", + "app_bar_signout_dialog_title": "āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąā¯", "app_settings": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "appears_in": "āŽ¤ā¯‹āŽŠā¯āŽąā¯āŽŽā¯", + "apply_count": "āŽĒā¯‹āŽŸā¯ ({count, number})", "archive": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽŽā¯", + "archive_action_prompt": "{count} āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "archive_or_unarchive_photo": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽšā¯†āŽ¯āŽ˛āŽąā¯āŽą āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", + "archive_page_no_archived_assets": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "archive_page_title": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽŽā¯ ({count})", "archive_size": "āŽ•āŽžāŽĒā¯āŽĒāŽ• āŽ…āŽŗāŽĩ❁", "archive_size_description": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ•āŽžāŽĒā¯āŽĒāŽ• āŽ…āŽŗāŽĩ❈ āŽ‰āŽŗā¯āŽŗāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ (āŽ•āŽŋāŽĒāŽŋāŽ˛ā¯)", - "archived_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸ #}}", + "archived": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "archived_count": "{count, plural, other {āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ #}}", "are_these_the_same_person": "āŽ‡āŽĩāŽ°ā¯āŽ•āŽŗā¯ āŽ’āŽ°ā¯‡ āŽ¨āŽĒāŽ°āŽž?", "are_you_sure_to_do_this": "āŽ‡āŽ¤ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "asset_action_delete_err_read_only": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯ˆ) āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "asset_action_share_err_offline": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛ā¯āŽ˛āŽžāŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯ˆ) āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "asset_added_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", - "asset_adding_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯ ...", + "asset_adding_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯â€Ļ", "asset_description_updated": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽŽā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "asset_filename_is_offline": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ {filename} āŽ†āŽƒāŽĒā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", "asset_has_unassigned_faces": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", - "asset_hashing": "āŽāŽšāŽŋāŽ™ā¯ ...", + "asset_hashing": "āŽāŽšāŽŋāŽ™ā¯â€Ļ", + "asset_list_group_by_sub_title": "āŽ•ā¯āŽ´ā¯", + "asset_list_layout_settings_dynamic_layout_title": "āŽŽāŽžāŽąā¯āŽŽā¯ āŽ¤āŽŗāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❁", + "asset_list_layout_settings_group_automatically": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ", + "asset_list_layout_settings_group_by": "āŽŽā¯‚āŽ˛āŽŽā¯ āŽ•ā¯āŽ´ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "asset_list_layout_settings_group_by_month_day": "āŽŽāŽžāŽ¤āŽŽā¯ + āŽ¨āŽžāŽŗā¯", + "asset_list_layout_sub_title": "āŽŽāŽŠā¯ˆāŽ¯āŽŽā¯ˆāŽĩ❁", + "asset_list_settings_subtitle": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽ•āŽŸā¯āŽŸāŽŽā¯ āŽ¤āŽŗāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "asset_list_settings_title": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽ•āŽŸā¯āŽŸāŽŽā¯", "asset_offline": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ†āŽƒāŽĒā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯", "asset_offline_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ‡āŽŠāŽŋ āŽĩāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ‰āŽ¤āŽĩāŽŋāŽ•ā¯āŽ•ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯ā¯ˆ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒ❁ āŽ•ā¯ŠāŽŗā¯āŽŗāŽĩā¯āŽŽā¯.", + "asset_restored_successfully": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "asset_skipped": "āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "asset_skipped_in_trash": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¯āŽŋāŽ˛ā¯", + "asset_trashed": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ•ā¯āŽĒā¯āŽĒ❈", + "asset_troubleshoot": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯", "asset_uploaded": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", - "asset_uploading": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ ...", + "asset_uploading": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ¤āŽ˛ā¯â€Ļ", + "asset_viewer_settings_subtitle": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯‡āŽ˛āŽ°āŽŋ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "asset_viewer_settings_title": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯", "assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", - "assets_added_count": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_added_to_album_count": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_added_to_name_count": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} {hasname, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ { {name} } āŽĒāŽŋāŽą {new album}}", - "assets_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_moved_to_trash_count": "āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}}", - "assets_permanently_deleted_count": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_removed_count": "āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", + "assets_added_count": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", + "assets_added_to_album_count": "āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒāŽŋāŽ˛ā¯ {count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_added_to_albums_count": "Added {assetTotal, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} to {albumTotal, plural, one {# āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒ❁} other {# āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒā¯āŽ•āŽŗā¯}}āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_cannot_be_added_to_album_count": "{count, plural, one {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽšā¯†āŽ°ā¯āŽ•ā¯‡āŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} āŽŽāŽ¨ā¯āŽ¤āŽšā¯ āŽšā¯†āŽ°ā¯āŽ•ā¯‡āŽŸā¯āŽŸāŽŋāŽ˛ā¯āŽŽā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "assets_count": "{count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", + "assets_deleted_permanently": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_deleted_permanently_from_server": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_downloaded_failed": "{count, plural, one {# āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ - {error} āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯āŽŸā¯ˆāŽ¨ā¯āŽ¤āŽ¤ā¯} other {# āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŠ - {error} āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯āŽŸā¯ˆāŽ¨ā¯āŽ¤āŽŠ}}", + "assets_downloaded_successfully": "{count, plural, one {# āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ•āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯} other {# āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ•āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯}}", + "assets_moved_to_trash_count": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ {count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_permanently_deleted_count": "{count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_removed_count": "{count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_removed_permanently_from_device": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "assets_restore_confirmation": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ•āŽŗā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯†āŽ¯āŽ˛ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯! āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛ā¯āŽ˛āŽžāŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽĩāŽ´āŽŋāŽ¯āŽŋāŽ˛ā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽŋāŽ˛ā¯ āŽ•ā¯ŠāŽŗā¯āŽ•.", - "assets_restored_count": "āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_trashed_count": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽžāŽŠ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", - "assets_were_part_of_album_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽ°ā¯ {Asset was} āŽŽāŽąā¯āŽą {Assets were}} āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ’āŽ°ā¯ āŽĒāŽ•ā¯āŽ¤āŽŋ", + "assets_restored_count": "{count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_restored_successfully": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_trashed": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽ•ā¯āŽĒā¯āŽĒ❈", + "assets_trashed_count": "{count, plural, one {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¯āŽžāŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "assets_trashed_from_server": "{count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯) āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯", + "assets_were_part_of_album_count": "{count, plural, one {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒāŽŋāŽŠā¯ āŽ’āŽ°ā¯ āŽĒāŽ•ā¯āŽ¤āŽŋ", + "assets_were_part_of_albums_count": "{count, plural, one {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} other {āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ’āŽ°ā¯ āŽĒāŽ•ā¯āŽ¤āŽŋ", "authorized_devices": "āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯", + "automatic_endpoint_switching_subtitle": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ¨āŽŋāŽ¯āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯ˆāŽƒāŽĒ❈ āŽŽā¯€āŽ¤ā¯ āŽ‰āŽŗā¯āŽŗā¯‚āŽ°āŽŋāŽ˛ā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽĩā¯‡āŽąā¯ āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŽāŽžāŽąā¯āŽąā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "automatic_endpoint_switching_title": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽŽāŽžāŽąā¯āŽ¤āŽ˛ā¯", + "autoplay_slideshow": "āŽ†āŽŸā¯āŽŸā¯‹āŽĒāŽŋāŽŗā¯‡ āŽšā¯āŽ˛ā¯ˆāŽŸā¯āŽšā¯‹", "back": "āŽĒāŽŋāŽŠā¯", "back_close_deselect": "āŽĒāŽŋāŽŠā¯, āŽŽā¯‚āŽŸā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽ¨ā¯€āŽ•ā¯āŽ•āŽŽā¯", + "background_backup_running_error": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ āŽ‡āŽ¯āŽ™ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽ•ā¯ˆāŽŽā¯āŽąā¯ˆ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "background_location_permission": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ‡āŽšā¯ˆāŽĩ❁", + "background_location_permission_content": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ¯āŽ™ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽŽāŽžāŽąā¯āŽą, āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ *āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯* āŽ¤ā¯āŽ˛ā¯āŽ˛āŽŋāŽ¯āŽŽāŽžāŽŠ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ…āŽŖā¯āŽ•āŽ˛ā¯ˆāŽ•ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯, āŽŽāŽŠāŽĩ❇ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽĩā¯ˆāŽƒāŽĒ❈ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯ āŽĒā¯†āŽ¯āŽ°ā¯ˆāŽĒā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯", + "background_options": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "backup": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ", + "backup_album_selection_page_albums_device": "āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ ({count})", + "backup_album_selection_page_albums_tap": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽ¤āŽŸā¯āŽŸāŽĩā¯āŽŽā¯, āŽĩāŽŋāŽ˛āŽ•ā¯āŽ• āŽ‡āŽ°āŽŸā¯āŽŸā¯ˆ āŽ¤āŽŸā¯āŽŸāŽĩā¯āŽŽā¯", + "backup_album_selection_page_assets_scatter": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽĒāŽ˛ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽšāŽŋāŽ¤āŽąāŽ•ā¯āŽ•ā¯‚āŽŸā¯āŽŽā¯. āŽŽāŽŠāŽĩ❇, āŽ•āŽžāŽĒā¯āŽĒ❁ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽĒā¯‹āŽ¤ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯.", + "backup_album_selection_page_select_albums": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_album_selection_page_selection_info": "āŽ¤ā¯‡āŽ°ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ", + "backup_album_selection_page_total_assets": "āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽ¤āŽŠāŽŋāŽ¤ā¯āŽ¤ā¯āŽĩāŽŽāŽžāŽŠ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "backup_albums_sync": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁", + "backup_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŽā¯", + "backup_background_service_backup_failed_message": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯. āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽĒā¯āŽĒāŽ¤ā¯â€Ļ", + "backup_background_service_connection_failed_message": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯. āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽĒā¯āŽĒāŽ¤ā¯â€Ļ", + "backup_background_service_current_upload_notification": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ {filename}", + "backup_background_service_default_notification": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯â€Ļ", + "backup_background_service_error_title": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĒāŽŋāŽ´ā¯ˆ", + "backup_background_service_in_progress_notification": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯â€Ļ", + "backup_background_service_upload_failure_notification": "{filename} āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", + "backup_controller_page_albums": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "backup_controller_page_background_app_refresh_disabled_content": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯> āŽĒā¯ŠāŽ¤ā¯> āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒ❁.", + "backup_controller_page_background_app_refresh_disabled_title": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒ❁ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "backup_controller_page_background_app_refresh_enable_button_text": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽšā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", + "backup_controller_page_background_battery_info_link": "āŽŽāŽĒā¯āŽĒāŽŸāŽŋ āŽŽāŽŠā¯āŽąā¯ āŽŽāŽŠāŽ•ā¯āŽ•ā¯āŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "backup_controller_page_background_battery_info_message": "āŽšāŽŋāŽąāŽ¨ā¯āŽ¤ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ…āŽŠā¯āŽĒāŽĩāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯, āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽąā¯āŽ•āŽžāŽŠ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽ•ā¯ āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯ āŽŽāŽ¨ā¯āŽ¤ āŽĒā¯‡āŽŸā¯āŽŸāŽ°āŽŋ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. \n\nāŽ‡āŽ¤ā¯ āŽšāŽžāŽ¤āŽŠāŽŽā¯ āŽšāŽžāŽ°ā¯āŽ¨ā¯āŽ¤āŽ¤āŽžāŽ• āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽžāŽ˛ā¯, āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠ āŽ‰āŽąā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ¯āŽžāŽŗāŽ°ā¯āŽ•ā¯āŽ•ā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽžāŽŠ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯.", + "backup_controller_page_background_battery_info_ok": "āŽšāŽ°āŽŋ", + "backup_controller_page_background_battery_info_title": "āŽĒā¯‡āŽŸā¯āŽŸāŽ°āŽŋ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽ˛ā¯āŽ•āŽŗā¯", + "backup_controller_page_background_charging": "āŽ•āŽŸā¯āŽŸāŽŖāŽŽā¯ āŽĩāŽšā¯‚āŽ˛āŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", + "backup_controller_page_background_configure_error": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯‡āŽĩā¯ˆāŽ¯ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŽā¯ˆāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", + "backup_controller_page_background_delay": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¤āŽžāŽŽāŽ¤āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯: {duration}", + "backup_controller_page_background_description": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽąāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽŋāŽ˛ā¯āŽ˛āŽžāŽŽāŽ˛ā¯ āŽŽāŽ¨ā¯āŽ¤ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤āŽžāŽŠāŽžāŽ• āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ• āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯‡āŽĩā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_background_is_off": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "backup_controller_page_background_is_on": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", + "backup_controller_page_background_turn_off": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯‡āŽĩā¯ˆāŽ¯ā¯ˆ āŽ…āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_background_turn_on": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯‡āŽĩā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_background_wifi": "āŽĩā¯ˆāŽƒāŽĒ❈ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", + "backup_controller_page_backup": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ", + "backup_controller_page_backup_selected": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯: ", + "backup_controller_page_backup_sub": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_created": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯: {date}", + "backup_controller_page_desc_backup": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽąāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¤āŽžāŽŠāŽžāŽ• āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "backup_controller_page_excluded": "āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯: ", + "backup_controller_page_failed": "āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯ ({count})", + "backup_controller_page_filename": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯: {filename} [{size}]", + "backup_controller_page_id": "āŽāŽŸāŽŋ: {id}", + "backup_controller_page_info": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ", + "backup_controller_page_none_selected": "āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "backup_controller_page_remainder": "āŽŽā¯€āŽ¤āŽŽā¯āŽŗā¯āŽŗ", + "backup_controller_page_remainder_sub": "āŽ¤ā¯‡āŽ°ā¯āŽĩāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ• āŽŽā¯€āŽ¤āŽŽā¯āŽŗā¯āŽŗ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯", + "backup_controller_page_server_storage": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❁", + "backup_controller_page_start_backup": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_status_off": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "backup_controller_page_status_on": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", + "backup_controller_page_storage_format": "{total} āŽ‡āŽ˛ā¯ {used} āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "backup_controller_page_to_backup": "āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸ āŽĩā¯‡āŽŖā¯āŽŸāŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "backup_controller_page_total_sub": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¤āŽŠāŽŋāŽ¤ā¯āŽ¤ā¯āŽĩāŽŽāŽžāŽŠ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯", + "backup_controller_page_turn_off": "āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆ āŽ…āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_turn_on": "āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_controller_page_uploading_file_info": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ¤āŽ•āŽĩāŽ˛ā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "backup_err_only_album": "āŽ’āŽ°ā¯‡ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽ•āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "backup_error_sync_failed": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯. āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆ āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", + "backup_info_card_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "backup_manual_cancelled": "āŽ°āŽ¤ā¯āŽ¤ā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "backup_manual_in_progress": "āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯. āŽšāŽŋāŽąāŽŋāŽ¤ā¯ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽ•āŽ´āŽŋāŽ¤ā¯āŽ¤ā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_manual_success": "āŽšā¯†āŽ¯ā¯", + "backup_manual_title": "āŽ¨āŽŋāŽ˛ā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽŽā¯ āŽ¨āŽŋāŽ˛ā¯ˆ", + "backup_options": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "backup_options_page_title": "āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "backup_setting_subtitle": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽā¯āŽŠā¯āŽĒā¯āŽą āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "backup_settings_subtitle": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "backward": "āŽĒāŽŋāŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•ā¯", + "biometric_auth_enabled": "āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯ āŽāŽąā¯āŽĒ❁ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "biometric_locked_out": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯ āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗā¯", + "biometric_no_options": "āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "biometric_not_available": "āŽ‡āŽ¨ā¯āŽ¤ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯ āŽāŽąā¯āŽĒ❁ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "birthdate_saved": "āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "birthdate_set_description": "āŽ’āŽ°ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒā¯‹āŽ¤ā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽ¨āŽĒāŽ°āŽŋāŽŠā¯ āŽĩāŽ¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽŖāŽ•ā¯āŽ•āŽŋāŽŸ āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ¤ā¯‡āŽ¤āŽŋ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "blurred_background": "āŽŽāŽ™ā¯āŽ•āŽ˛āŽžāŽŠ āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ", @@ -415,31 +624,71 @@ "bulk_keep_duplicates_confirmation": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯} be āŽĩā¯ˆāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ āŽŽāŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽžāŽŽāŽ˛ā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯ āŽ•ā¯āŽ´ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯.", "bulk_trash_duplicates_confirmation": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯ŠāŽ¤ā¯āŽ¤āŽŽāŽžāŽ• āŽ•ā¯āŽĒā¯āŽĒ❈ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}}} āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ•ā¯āŽ´ā¯āŽĩāŽŋāŽŠā¯ āŽŽāŽŋāŽ•āŽĒā¯āŽĒā¯†āŽ°āŽŋāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽāŽąā¯āŽą āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯.", "buy": "āŽ‡āŽŽā¯āŽŽāŽŋāŽ¯ā¯ˆ āŽĩāŽžāŽ™ā¯āŽ•āŽĩā¯āŽŽā¯", + "cache_settings_clear_cache_button": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❁", + "cache_settings_clear_cache_button_title": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❈ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ•ā¯‡āŽšā¯ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ•āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽĩāŽ°ā¯ˆ āŽ‡āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽ•āŽŖāŽŋāŽšāŽŽāŽžāŽ• āŽĒāŽžāŽ¤āŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯.", + "cache_settings_duplicated_assets_clear_button": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ", + "cache_settings_duplicated_assets_subtitle": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽžāŽ˛ā¯ āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯", + "cache_settings_duplicated_assets_title": "āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ ({count})", + "cache_settings_statistics_album": "āŽ¨ā¯‚āŽ˛āŽ• āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ™ā¯āŽ•āŽŗā¯", + "cache_settings_statistics_full": "āŽŽā¯āŽ´ā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "cache_settings_statistics_shared": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽšāŽŋāŽąā¯āŽĒāŽŸāŽŽā¯", + "cache_settings_statistics_thumbnail": "āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ™ā¯āŽ•āŽŗā¯", + "cache_settings_statistics_title": "āŽ•ā¯‡āŽšā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯", + "cache_settings_subtitle": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❁ āŽ¨āŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯ˆ āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "cache_settings_tile_subtitle": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ¨āŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯ˆ āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "cache_settings_tile_title": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❁", + "cache_settings_title": "āŽ¤ā¯‡āŽ•ā¯āŽ•āŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "camera": "āŽ•ā¯‡āŽŽāŽ°āŽž", "camera_brand": "āŽ•ā¯‡āŽŽāŽ°āŽž āŽšā¯‚āŽŸā¯āŽŸā¯āŽ•ā¯āŽ•ā¯āŽąāŽŋ", "camera_model": "āŽ•ā¯‡āŽŽāŽ°āŽž āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋ", "cancel": "āŽ°āŽ¤ā¯āŽ¤ā¯āŽšā¯†āŽ¯ā¯", "cancel_search": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ˆ āŽ°āŽ¤ā¯āŽ¤ā¯āŽšā¯†āŽ¯ā¯", + "canceled": "āŽ°āŽ¤ā¯āŽ¤ā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "canceling": "āŽ°āŽ¤ā¯āŽ¤ā¯āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯", "cannot_merge_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽ’āŽŠā¯āŽąāŽŋāŽŖā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", "cannot_undo_this_action": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯†āŽ¯āŽ˛ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯!", "cannot_update_the_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "cast": "āŽ¨āŽŸāŽŋāŽ•āŽ°ā¯āŽ•āŽŗā¯", + "cast_description": "āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒ❁ āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "change_date": "āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "change_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "change_display_order": "āŽ•āŽžāŽŸā¯āŽšāŽŋ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "change_expiration_time": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "change_location": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "change_name": "āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", - "change_name_successfully": "āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "change_name_successfully": "āŽĒā¯†āŽ¯āŽ°ā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "change_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "change_password_description": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽŋāŽŠāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽĩāŽ¤ā¯ āŽ‡āŽ¤ā¯āŽĩ❇ āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽ•ā¯€āŽ´ā¯‡ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", + "change_password_form_confirm_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "change_password_form_description": "āŽ†āŽ¯ā¯ {name}, \n\nāŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽŋāŽŠāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸā¯āŽĩāŽ¤ā¯ āŽ‡āŽ¤ā¯āŽĩ❇ āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯. āŽ•ā¯€āŽ´ā¯‡ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", + "change_password_form_new_password": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", + "change_password_form_password_mismatch": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽąā¯āŽ•āŽŗā¯ āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "change_password_form_reenter_new_password": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "change_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "change_your_password": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "changed_visibility_successfully": "āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "charging": "āŽšāŽžāŽ°ā¯āŽšāŽŋāŽ™ā¯", + "charging_requirement_mobile_backup": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•ā¯āŽ•ā¯ āŽšāŽžāŽ¤āŽŠāŽŽā¯ āŽšāŽžāŽ°ā¯āŽšā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯", + "check_corrupt_asset_backup": "āŽŠāŽ´āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ•āŽŗā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "check_corrupt_asset_backup_button": "āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", + "check_corrupt_asset_backup_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ•āŽžāŽšā¯‹āŽ˛ā¯ˆāŽ¯ā¯ˆ āŽĩā¯ˆāŽƒāŽĒ❈ āŽŽā¯€āŽ¤ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯āŽŽā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩā¯āŽŸāŽŠā¯. āŽšā¯†āŽ¯āŽ˛ā¯āŽŽā¯āŽąā¯ˆ āŽšāŽŋāŽ˛ āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ†āŽ•āŽ˛āŽžāŽŽā¯.", "check_logs": "āŽĒāŽ¤āŽŋāŽĩā¯āŽ•āŽŗā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "choose_matching_people_to_merge": "āŽ’āŽŠā¯āŽąāŽŋāŽŖā¯ˆāŽ•ā¯āŽ• āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•", "city": "āŽ¨āŽ•āŽ°āŽŽā¯", "clear": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ", "clear_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "clear_all_recent_searches": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "clear_file_cache": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❈ āŽ…āŽ´āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "clear_message": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ", "clear_value": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❁", + "client_cert_dialog_msg_confirm": "āŽšāŽ°āŽŋ", + "client_cert_enter_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "client_cert_import": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ", + "client_cert_import_success_msg": "āŽ•āŽŋāŽŗā¯ˆāŽ¯āŽŠā¯āŽŸā¯ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "client_cert_invalid_msg": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ¤āŽĩāŽąāŽžāŽŠ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", + "client_cert_remove_msg": "āŽ•āŽŋāŽŗā¯ˆāŽ¯āŽŠā¯āŽŸā¯ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "client_cert_subtitle": "PKCS12 (.p12, .pfx) āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ/āŽ…āŽ•āŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽŽā¯āŽŠā¯āŽĒā¯āŽ¤āŽžāŽŠā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯", + "client_cert_title": "āŽŽāŽšā¯āŽŽāŽšā¯āŽŽāŽ˛ā¯ āŽ•āŽŋāŽŗā¯ˆāŽ¯āŽŠā¯āŽŸā¯ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯", "clockwise": "Locklowsy", "close": "āŽŽā¯‚āŽŸā¯", "collapse": "āŽšāŽ°āŽŋāŽĩ❁", @@ -450,14 +699,31 @@ "comment_options": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "comments_and_likes": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "comments_are_disabled": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽŠ", + "common_create_new_album": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "common_server_error": "āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽŋāŽŖā¯ˆāŽ¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽšā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽ…āŽŸā¯ˆāŽ¯āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯āŽ¤ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯/āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽĒāŽ¤āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ‡āŽŖāŽ•ā¯āŽ•āŽŽāŽžāŽŠāŽĩ❈ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", + "completed": "āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", "confirm": "āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "confirm_admin_password": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "confirm_delete_face": "āŽšā¯ŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ {name} āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "confirm_delete_shared_link": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "confirm_keep_this_delete_others": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽĩāŽŋāŽ° āŽ…āŽŸā¯āŽ•ā¯āŽ•āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽŽāŽąā¯āŽą āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•āŽŗā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯. āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯ŠāŽŸāŽ° āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "confirm_new_pin_code": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "confirm_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "confirm_tag_face": "āŽ‡āŽ¨ā¯āŽ¤ āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ {āŽĒā¯†āŽ¯āŽ°ā¯ āŽ…āŽšā¯ āŽŽāŽŠāŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "confirm_tag_face_unnamed": "āŽ‡āŽ¨ā¯āŽ¤ āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "connected_device": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽžāŽ¤āŽŠāŽŽā¯", + "connected_to": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "contain": "āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "context": "āŽšā¯‚āŽ´āŽ˛ā¯", "continue": "āŽ¤ā¯ŠāŽŸāŽ°āŽĩā¯āŽŽā¯", + "control_bottom_app_bar_create_new_album": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "control_bottom_app_bar_delete_from_immich": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "control_bottom_app_bar_delete_from_local": "āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "control_bottom_app_bar_edit_location": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "control_bottom_app_bar_edit_time": "āŽ¤ā¯‡āŽ¤āŽŋ & āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "control_bottom_app_bar_share_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ•āŽŋāŽ°āŽĩā¯āŽŽā¯", + "control_bottom_app_bar_share_to": "āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯", + "control_bottom_app_bar_trash_from_immich": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "copied_image_to_clipboard": "āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆāŽĒā¯āŽĒāŽ˛āŽ•ā¯ˆāŽ•ā¯āŽ•ā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ¤ā¯āŽ¤āŽ¤ā¯.", "copied_to_clipboard": "āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆāŽĒā¯āŽĒāŽ˛āŽ•ā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯!", "copy_error": "āŽ¨āŽ•āŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", @@ -472,51 +738,91 @@ "covers": "āŽŽāŽąā¯ˆāŽ¯āŽŽā¯", "create": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", "create_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "create_album_page_untitled": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒāŽŋāŽŸāŽĒā¯āŽĒāŽŸāŽžāŽ¤", "create_library": "āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_link_to_share": "āŽĒāŽ•āŽŋāŽ°ā¯āŽĩā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_link_to_share_description": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽ‰āŽŗā¯āŽŗ āŽŽāŽĩāŽ°ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖāŽŸā¯āŽŸā¯āŽŽā¯)", + "create_new": "āŽĒā¯āŽ¤āŽŋāŽ¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_new_person": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_new_person_hint": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯āŽ•ā¯āŽ•ā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_new_user": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "create_shared_album_page_share_add_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "create_shared_album_page_share_select_photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "create_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_tag": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "create_tag_description": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ‰āŽŗā¯āŽŗāŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽąā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯, āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ āŽšā¯āŽ˛āŽžāŽšā¯āŽ•āŽŗā¯ āŽ‰āŽŸā¯āŽĒāŽŸ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛āŽŋāŽŠā¯ āŽŽā¯āŽ´ā¯ āŽĒāŽžāŽ¤ā¯ˆāŽ¯ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", "create_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", "created": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "created_at": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "creating_linked_albums": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯ ...", + "crop": "āŽĒāŽ¯āŽŋāŽ°ā¯", + "curated_object_page_title": "āŽĩāŽŋāŽšāŽ¯āŽ™ā¯āŽ•āŽŗā¯", "current_device": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽšāŽžāŽ¤āŽŠāŽŽā¯", + "current_pin_code": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯", + "current_server_address": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", "custom_locale": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽ‡āŽŸāŽŽā¯", "custom_locale_description": "āŽŽā¯ŠāŽ´āŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĒāŽŋāŽ°āŽžāŽ¨ā¯āŽ¤āŽŋāŽ¯āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽ¤ā¯‡āŽ¤āŽŋāŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽāŽŖā¯āŽ•āŽŗā¯", + "custom_url": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", + "daily_title_text_date": "E, mmm dd", + "daily_title_text_date_year": "E, mmm dd, yyyy", "dark": "āŽ‡āŽ°ā¯āŽŖā¯āŽŸ", + "dark_theme": "āŽ‡āŽ°ā¯āŽŖā¯āŽŸ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "date_after": "āŽ¤ā¯‡āŽ¤āŽŋ", "date_and_time": "āŽ¤ā¯‡āŽ¤āŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯‡āŽ°āŽŽā¯", "date_before": "āŽŽā¯āŽŠā¯ āŽ¤ā¯‡āŽ¤āŽŋ", + "date_format": "E, lll d, āŽ’āŽ¯ā¯ â€ĸ h: mm a", "date_of_birth_saved": "āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "date_range": "āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽ°āŽŽā¯āŽĒ❁", "day": "āŽ¨āŽžāŽŗā¯", + "days": "āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯", "deduplicate_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽ´āŽŋāŽ¤ā¯āŽ¤āŽ˛ā¯", + "deduplication_criteria_1": "āŽĒā¯ˆāŽŸā¯āŽŸā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŸ āŽ…āŽŗāŽĩ❁", + "deduplication_criteria_2": "EXIF āŽ¤āŽ°āŽĩāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", + "deduplication_info": "āŽ•āŽ´āŽŋāŽ¤ā¯āŽ¤āŽ˛ā¯ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ", + "deduplication_info_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¤āŽžāŽŠāŽžāŽ• āŽŽā¯āŽŠā¯āŽŠā¯†āŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽšā¯ āŽšā¯†āŽ˛ā¯āŽ˛āŽĩā¯āŽŽā¯, āŽŽā¯ŠāŽ¤ā¯āŽ¤āŽŽāŽžāŽ• āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ•āŽąā¯āŽąāŽĩā¯āŽŽā¯, āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯:", "default_locale": "āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽ‡āŽŸāŽŽā¯", "default_locale_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ˛āŽžāŽĩāŽŋ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽĩāŽŸāŽŋāŽĩāŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽ¤ā¯‡āŽ¤āŽŋāŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŽāŽŖā¯āŽ•āŽŗā¯", "delete": "āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_action_confirmation_message": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¨ā¯āŽ¤ āŽ¨āŽŸāŽĩāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽ¤ā¯ˆ āŽ‰āŽŗā¯āŽ¨āŽžāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽŠāŽžāŽ˛ā¯ āŽ•ā¯‡āŽŸā¯āŽ•ā¯āŽŽā¯", + "delete_action_prompt": "{count} āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "delete_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_api_key_prompt": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "delete_dialog_alert": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "delete_dialog_alert_local": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯", + "delete_dialog_alert_local_non_backed_up": "āŽšāŽŋāŽ˛ āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽĩāŽ°ā¯ˆ āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ…āŽĩ❈ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "delete_dialog_alert_remote": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "delete_dialog_ok_force": "āŽŽāŽĒā¯āŽĒāŽŸāŽŋāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_dialog_title": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_duplicates_confirmation": "āŽ‡āŽ¨ā¯āŽ¤ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "delete_face": "āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_key": "āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_library": "āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_local_action_prompt": "{count} āŽ‰āŽŗā¯āŽ¨āŽžāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "delete_local_dialog_ok_backed_up_only": "āŽ¨ā¯€āŽ•ā¯āŽ•ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "delete_local_dialog_ok_force": "āŽŽāŽĒā¯āŽĒāŽŸāŽŋāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_others": "āŽŽāŽąā¯āŽąāŽĩāŽ°ā¯āŽ•āŽŗā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_permanently": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_permanently_action_prompt": "{count} āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "delete_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "delete_shared_link_dialog_title": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_tag": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "delete_tag_confirmation_prompt": "{tagName} āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "delete_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "deleted_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨ā¯€āŽ•ā¯āŽ•āŽŋāŽ¯āŽ¤ā¯", "deletes_missing_assets": "āŽĩāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•āŽžāŽŖāŽžāŽŽāŽ˛ā¯ āŽĒā¯‹āŽŠ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "description": "āŽĩāŽŋāŽĩāŽ°āŽŽā¯", + "description_input_hint_text": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ ...", + "description_input_submit_error": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽĩā¯ˆāŽšā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "deselect_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", "details": "āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯", "direction": "āŽ¤āŽŋāŽšā¯ˆ", "disabled": "āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "disallow_edits": "āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "discord": "āŽŽā¯āŽ°āŽŖā¯āŽĒāŽžāŽŸā¯", "discover": "āŽ•āŽŖā¯āŽŸā¯āŽĒāŽŋāŽŸāŽŋ", + "discovered_devices": "āŽ•āŽŖā¯āŽŸā¯āŽĒāŽŋāŽŸāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯", "dismiss_all_errors": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽĒāŽŋāŽ´ā¯ˆāŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨āŽŋāŽ°āŽžāŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "dismiss_error": "āŽĒāŽŋāŽ´ā¯ˆāŽ¯ā¯ˆ āŽ¨āŽŋāŽ°āŽžāŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "display_options": "āŽ•āŽžāŽŸā¯āŽšāŽŋ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", @@ -527,12 +833,26 @@ "documentation": "āŽ†āŽĩāŽŖāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯", "done": "āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", "download": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯", + "download_action_prompt": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ {count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "download_canceled": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ°āŽ¤ā¯āŽ¤ā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "download_complete": "āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯", + "download_enqueue": "Enqueuted āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•", + "download_error": "āŽĒāŽŋāŽ´ā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "download_failed": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯āŽŸā¯ˆāŽ¨ā¯āŽ¤āŽ¤ā¯", + "download_finished": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", "download_include_embedded_motion_videos": "āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯", "download_include_embedded_motion_videos_description": "āŽŽā¯‹āŽšāŽŠā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽŸā¯āŽĒā¯ŠāŽ¤āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¤āŽŠāŽŋ āŽ•ā¯‹āŽĒā¯āŽĒāŽžāŽ• āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "download_notfound": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "download_paused": "āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "download_settings": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯", "download_settings_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒāŽžāŽŠ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "download_started": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•āŽŋāŽ¯āŽ¤ā¯", + "download_sucess": "āŽĩā¯†āŽąā¯āŽąāŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "download_sucess_android": "āŽŠāŽŸāŽ•āŽ™ā¯āŽ•āŽŗā¯ DCIM/IMMich āŽ•ā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽŠ", + "download_waiting_to_retry": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ• āŽ•āŽžāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "downloading": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "downloading_asset_filename": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ {filename}", + "downloading_media": "āŽŠāŽŸāŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "drop_files_to_upload": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽāŽ™ā¯āŽ•ā¯āŽŽā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽĩāŽŋāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "duplicates": "āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯", "duplicates_description": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ•ā¯āŽ´ā¯āŽĩā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗāŽŋāŽ˛ā¯āŽŽā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸā¯āŽĩāŽ¤āŽŠā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -540,8 +860,14 @@ "edit": "āŽ¤ā¯†āŽžāŽ•ā¯", "edit_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "edit_avatar": "āŽ…āŽĩāŽ¤āŽžāŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", + "edit_birthday": "āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤āŽ¨āŽžāŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "edit_date": "āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "edit_date_and_time": "āŽ¤ā¯‡āŽ¤āŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "edit_date_and_time_action_prompt": "{count} āŽ¤ā¯‡āŽ¤āŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "edit_date_and_time_by_offset": "āŽ†āŽƒāŽĒā¯āŽšā¯†āŽŸā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "edit_date_and_time_by_offset_interval": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽ°āŽŽā¯āŽĒ❁: {from} - {to}", + "edit_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", + "edit_description_prompt": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯:", "edit_exclusion_pattern": "āŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "edit_faces": "āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "edit_import_path": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽĒāŽžāŽ¤ā¯ˆāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", @@ -549,6 +875,8 @@ "edit_key": "āŽ¤āŽŋāŽąāŽŠā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "edit_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "edit_location": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "edit_location_action_prompt": "{count} āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽŽā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "edit_location_dialog_title": "āŽ‡āŽŸāŽŽā¯", "edit_name": "āŽĒā¯†āŽ¯āŽ°ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "edit_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "edit_tag": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", @@ -561,13 +889,27 @@ "editor_crop_tool_h2_aspect_ratios": "āŽ…āŽŽā¯āŽš āŽĩāŽŋāŽ•āŽŋāŽ¤āŽ™ā¯āŽ•āŽŗā¯", "editor_crop_tool_h2_rotation": "āŽšā¯āŽ´āŽąā¯āŽšāŽŋ", "email": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯", + "email_notifications": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "empty_folder": "āŽ‡āŽ¨ā¯āŽ¤ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ•āŽžāŽ˛āŽŋāŽ¯āŽžāŽ• āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", "empty_trash": "āŽĩā¯†āŽąā¯āŽąā¯ āŽ•ā¯āŽĒā¯āŽĒ❈", "empty_trash_confirmation": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•āŽŗā¯ˆ āŽĩā¯†āŽąā¯āŽŽā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¤ā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯āŽŽā¯.\n āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯†āŽ¯āŽ˛ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯!", "enable": "āŽ‡āŽ¯āŽ•ā¯āŽ•ā¯", + "enable_backup": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "enable_biometric_auth_description": "āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯ āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", "enabled": "āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "end_date": "āŽ‡āŽąā¯āŽ¤āŽŋ āŽ¤ā¯‡āŽ¤āŽŋ", + "enqueued": "Enqueuted", + "enter_wifi_name": "āŽĩā¯ˆāŽƒāŽĒ❈ āŽĒā¯†āŽ¯āŽ°ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "enter_your_pin_code": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "enter_your_pin_code_subtitle": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯ā¯ˆ āŽ…āŽŖā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", "error": "āŽĒāŽŋāŽ´ā¯ˆ", + "error_change_sort_album": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽ°āŽŋāŽšā¯ˆ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", + "error_delete_face": "āŽšā¯ŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", + "error_getting_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯†āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", "error_loading_image": "āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", + "error_loading_partners": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ: {error}", + "error_saving_image": "āŽĒāŽŋāŽ´ā¯ˆ: {error}", + "error_tag_face_bounding_box": "āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ āŽĒāŽŋāŽ´ā¯ˆ - āŽŽāŽ˛ā¯āŽ˛ā¯ˆ āŽĒā¯†āŽŸā¯āŽŸāŽŋ āŽ†āŽ¯āŽ¤ā¯āŽ¤ā¯ŠāŽ˛ā¯ˆāŽĩā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", "error_title": "āŽĒāŽŋāŽ´ā¯ˆ - āŽāŽ¤ā¯‹ āŽ¤āŽĩāŽąā¯ āŽ¨āŽŸāŽ¨ā¯āŽ¤āŽ¤ā¯", "errors": { "cannot_navigate_next_asset": "āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•ā¯ āŽšā¯†āŽ˛ā¯āŽ˛ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", @@ -595,15 +937,19 @@ "failed_to_keep_this_delete_others": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĩā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽŽāŽąā¯āŽą āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "failed_to_load_asset": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "failed_to_load_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", + "failed_to_load_notifications": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "failed_to_load_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "failed_to_remove_product_key": "āŽ¤āŽ¯āŽžāŽ°āŽŋāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", + "failed_to_reset_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "failed_to_stack_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŸā¯āŽ•ā¯āŽ•āŽŋ āŽĩ❈āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "failed_to_unstack_assets": "āŽ…āŽŠā¯-āŽšā¯āŽŸāŽžāŽ•ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯", + "failed_to_update_notification_status": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "import_path_already_exists": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽĒāŽžāŽ¤ā¯ˆ āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯.", "incorrect_email_or_password": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", "paths_validation_failed": "{āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽĒāŽžāŽ¤ā¯ˆ} āŽŽāŽąā¯āŽą {# āŽĒāŽžāŽ¤ā¯ˆāŽ•āŽŗā¯}} āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽą āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❁", "profile_picture_transparent_pixels": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽĒā¯ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽžāŽŠ āŽĒāŽŸāŽĒā¯āŽĒā¯āŽŗā¯āŽŗāŽŋāŽ•āŽŗā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯. āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯/āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", "quota_higher_than_disk_size": "āŽĩāŽŸā¯āŽŸā¯ āŽ…āŽŗāŽĩ❈ āŽĩāŽŋāŽŸ āŽ…āŽ¤āŽŋāŽ•āŽŽāŽžāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯āŽŸā¯ˆ āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗā¯€āŽ°ā¯āŽ•āŽŗā¯", + "something_went_wrong": "āŽāŽ¤ā¯‹ āŽ¤āŽĩāŽąā¯ āŽ¨āŽŸāŽ¨ā¯āŽ¤āŽ¤ā¯", "unable_to_add_album_users": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_add_assets_to_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•ā¯āŽ•ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_add_comment": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -615,6 +961,7 @@ "unable_to_archive_unarchive": "{āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ, āŽ‰āŽŖā¯āŽŽā¯ˆ {archive} āŽĒāŽŋāŽą {unarchive}}", "unable_to_change_album_user_role": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽĒāŽžāŽ¤ā¯āŽ¤āŽŋāŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_change_date": "āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "unable_to_change_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_change_favorite": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•ā¯ āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_change_location": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_change_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -658,6 +1005,7 @@ "unable_to_remove_partner": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ˆ āŽ…āŽ•āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_remove_reaction": "āŽŽāŽ¤āŽŋāŽ°ā¯āŽĩāŽŋāŽŠā¯ˆāŽ¯ā¯ˆ āŽ…āŽ•āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_reset_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "unable_to_reset_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_resolve_duplicate": "āŽ¨āŽ•āŽ˛ā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_restore_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_restore_trash": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", @@ -685,8 +1033,19 @@ "unable_to_update_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unable_to_upload_file": "āŽ•ā¯‹āŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ" }, + "exif": "Exif", + "exif_bottom_sheet_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ ...", + "exif_bottom_sheet_description_error": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ", + "exif_bottom_sheet_details": "āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯", + "exif_bottom_sheet_location": "āŽ‡āŽŸāŽŽā¯", + "exif_bottom_sheet_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯", + "exif_bottom_sheet_person_add_person": "āŽĒā¯†āŽ¯āŽ°ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "exit_slideshow": "āŽšā¯āŽ˛ā¯ˆāŽŸā¯āŽšā¯‹āŽĩāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąāŽĩā¯āŽŽā¯", "expand_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽŋāŽ°āŽŋāŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", + "experimental_settings_new_asset_list_subtitle": "āŽĩā¯‡āŽ˛ā¯ˆ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", + "experimental_settings_new_asset_list_title": "āŽšā¯‹āŽ¤āŽŠā¯ˆ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸ āŽ•āŽŸā¯āŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "experimental_settings_subtitle": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯ŠāŽ¨ā¯āŽ¤ āŽ†āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯!", + "experimental_settings_title": "āŽšā¯‹āŽ¤āŽŠā¯ˆ", "expire_after": "āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", "expired": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠ", "expires_date": "{date} āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", @@ -694,37 +1053,74 @@ "explorer": "āŽŽāŽ•ā¯āŽšā¯āŽĒā¯āŽŗā¯‹āŽ°āŽ°ā¯", "export": "āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ", "export_as_json": "āŽšāŽžāŽ¤ā¯ŠāŽĒā¯ŠāŽ•ā¯ āŽ†āŽ• āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ", + "export_database": "āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽŽā¯", + "export_database_description": "SQLITE āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", "extension": "āŽ¨ā¯€āŽŸā¯āŽŸāŽŋāŽĒā¯āŽĒ❁", "external": "āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽąāŽŽā¯", "external_libraries": "āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ•āŽ™ā¯āŽ•āŽŗā¯", + "external_network": "āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽĒāŽŋāŽŖā¯ˆāŽ¯āŽŽā¯", + "external_network_sheet_info": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽŽāŽžāŽŠ āŽĩā¯ˆāŽƒāŽĒ❈ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛āŽžāŽ¤āŽĒā¯‹āŽ¤ā¯, āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ•ā¯€āŽ´ā¯‡ āŽ‰āŽŗā¯āŽŗ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ•āŽŗāŽŋāŽŠā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽĩāŽ´āŽŋāŽ¯āŽžāŽ• āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯, āŽ‡āŽ¤ā¯ āŽŽā¯‡āŽ˛ā¯‡ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯€āŽ´ā¯‡ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "face_unassigned": "āŽ’āŽ¤ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽ¤ā¯", + "failed": "āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯ā¯āŽąā¯āŽąāŽ¤ā¯", + "failed_to_authenticate": "āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "failed_to_load_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", + "failed_to_load_folder": "āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯ā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋ", "favorite": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤", + "favorite_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗāŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "favorite_or_unfavorite_photo": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽšāŽžāŽ¤āŽ•āŽŽāŽąā¯āŽą āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", "favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩ❈", + "favorites_page_no_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "feature_photo_updated": "āŽ…āŽŽā¯āŽš āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "features": "āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯", + "features_in_development": "āŽĩāŽŗāŽ°ā¯āŽšā¯āŽšāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯", "features_setting_description": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "file_name": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯", "file_name_or_extension": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ¨ā¯€āŽŸā¯āŽŸāŽŋāŽĒā¯āŽĒ❁", "filename": "āŽ•ā¯‹āŽĒā¯āŽĒ❁āŽĒā¯āŽĒā¯†āŽ¯āŽ°ā¯", "filetype": "āŽĒā¯ˆāŽ˛ā¯āŽŸā¯ˆāŽĒā¯", + "filter": "āŽĩāŽŸāŽŋāŽĒā¯āŽĒāŽŋ", "filter_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽĩāŽŸāŽŋāŽ•āŽŸā¯āŽŸāŽĩā¯āŽŽā¯", + "filter_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĩāŽŸāŽŋāŽ•āŽŸā¯āŽŸāŽĩā¯āŽŽā¯", "find_them_fast": "āŽ¤ā¯‡āŽŸāŽ˛ā¯āŽŸāŽŠā¯ āŽĒā¯†āŽ¯āŽ°āŽžāŽ˛ā¯ āŽĩā¯‡āŽ•āŽŽāŽžāŽ• āŽ…āŽĩāŽąā¯āŽąā¯ˆāŽ•ā¯ āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¯āŽĩā¯āŽŽā¯", + "first": "āŽŽā¯āŽ¤āŽ˛ā¯", "fix_incorrect_match": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽĒā¯‹āŽŸā¯āŽŸāŽŋāŽ¯ā¯ˆ āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "folder": "āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ", + "folder_not_found": "āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "folders": "āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•āŽŗā¯", "folders_feature_description": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽŽā¯āŽąā¯ˆāŽŽā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ¯ā¯ˆ āŽ‰āŽ˛āŽžāŽĩā¯āŽ¤āŽ˛ā¯", + "forgot_pin_code_question": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽŽāŽąāŽ¨ā¯āŽ¤ā¯āŽĩāŽŋāŽŸā¯āŽŸā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "forward": "āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ", + "gcast_enabled": "āŽ•ā¯‚āŽ•āŽŋāŽŗā¯ āŽ¨āŽŸāŽŋāŽ•āŽ°ā¯āŽ•āŽŗā¯", + "gcast_enabled_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽŽā¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽ• Google āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽĩāŽŗāŽ™ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯.", "general": "āŽĒā¯†āŽžāŽ¤ā¯", + "geolocation_instruction_location": "āŽ…āŽ¤āŽŠā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽšāŽŋ.āŽĒāŽŋ.āŽŽāŽšā¯ āŽ†āŽ¯āŽ¤ā¯āŽ¤ā¯ŠāŽ˛ā¯ˆāŽĩā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽ’āŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯ŠāŽŸā¯āŽ•ā¯āŽ•ā¯ āŽšā¯†āŽ¯ā¯āŽ• āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĩāŽ°ā¯ˆāŽĒāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨ā¯‡āŽ°āŽŸāŽŋāŽ¯āŽžāŽ• āŽ’āŽ°ā¯ āŽ‡āŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "get_help": "āŽ‰āŽ¤āŽĩāŽŋ āŽĒā¯†āŽąā¯", + "get_wifiname_error": "āŽĩā¯ˆāŽƒāŽĒ❈ āŽĒā¯†āŽ¯āŽ°ā¯ˆāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽžāŽŠ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•āŽŗā¯ˆ āŽĩāŽ´āŽ™ā¯āŽ•āŽŋāŽ¯ā¯āŽŗā¯āŽŗā¯€āŽ°ā¯āŽ•āŽŗā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽ•ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯ˆāŽƒāŽĒ❈ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗā¯€āŽ°ā¯āŽ•āŽŗā¯", "getting_started": "āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽ¤āŽ˛ā¯", "go_back": "āŽ¤āŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽšā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", + "go_to_folder": "āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•ā¯āŽ•ā¯āŽšā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", "go_to_search": "āŽ¤ā¯‡āŽŸāŽšā¯ āŽšā¯†āŽ˛ā¯āŽ˛āŽĩā¯āŽŽā¯", + "gps": "āŽ‰āŽ˛āŽ• āŽ‡āŽŸāŽŽā¯ āŽ•āŽžāŽŸā¯āŽŸā¯āŽŽā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❁ (āŽ‡āŽŸāŽŽā¯ āŽ•āŽžāŽŸā¯āŽŸāŽŋ)", + "gps_missing": "āŽšāŽŋ.āŽĒāŽŋ.āŽŽāŽšā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "grant_permission": "āŽ‡āŽšā¯ˆāŽĩ❁ āŽĩāŽ´āŽ™ā¯āŽ•āŽĩā¯āŽŽā¯", "group_albums_by": "āŽ•ā¯āŽ´ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽŋāŽ¯āŽĩāŽ°ā¯ ...", + "group_country": "āŽ¨āŽžāŽŸā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ•ā¯āŽ´ā¯", "group_no": "āŽ•ā¯āŽ´ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "group_owner": "āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ¯āŽžāŽŗāŽ°āŽžāŽ˛ā¯ āŽ•ā¯āŽ´ā¯", + "group_places_by": "āŽ•ā¯āŽ´ā¯ āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ ...", "group_year": "āŽ†āŽŖā¯āŽŸā¯āŽ•ā¯āŽ•ā¯ āŽ•ā¯āŽ´ā¯", + "haptic_feedback_switch": "āŽ†āŽĒā¯āŽŸāŽŋāŽ•ā¯ āŽĒāŽŋāŽŠā¯āŽŠā¯‚āŽŸā¯āŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "haptic_feedback_title": "āŽ†āŽĒā¯āŽŸāŽŋāŽ•ā¯ āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "has_quota": "āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯", + "hash_asset": "āŽ†āŽšā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯", + "hashed_assets": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "hashing": "āŽāŽšāŽŋāŽ™ā¯", + "header_settings_add_header_tip": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "header_settings_field_validator_msg": "āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽ•āŽžāŽ˛āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "header_settings_header_name_input": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯", + "header_settings_header_value_input": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❁ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒ❁", + "headers_settings_tile_subtitle": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽĒāŽŋāŽŖā¯ˆāŽ¯ āŽ•ā¯‹āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯ā¯āŽŸāŽŠā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽ…āŽŠā¯āŽĒā¯āŽĒ āŽĩā¯‡āŽŖā¯āŽŸāŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽŗā¯ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽĩāŽ°ā¯ˆāŽ¯āŽąā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "headers_settings_tile_title": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽŗā¯ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "hi_user": "āŽ†āŽ¯ā¯ {name} ({email})", "hide_all_people": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "hide_gallery": "āŽ•ā¯‡āŽ˛āŽ°āŽŋāŽ¯ā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -732,8 +1128,29 @@ "hide_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "hide_person": "āŽ¨āŽĒāŽ°ā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•", "hide_unnamed_people": "āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽĩāŽ°ā¯āŽ•āŽŗā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_add_to_album_conflicts": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽ†āŽ˛ā¯āŽĒāŽŽā¯ {added} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ {āŽ†āŽ˛ā¯āŽĒāŽŽā¯. {album} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽŠ.", + "home_page_add_to_album_err_local": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_add_to_album_success": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽ†āŽ˛ā¯āŽĒāŽŽā¯ {added} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ {āŽ†āŽ˛ā¯āŽĒāŽŽā¯.", + "home_page_album_err_partner": "āŽ’āŽ°ā¯ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_archive_err_local": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_archive_err_partner": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_building_timeline": "āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯", + "home_page_delete_err_partner": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_delete_remote_err_local": "āŽ¤ā¯ŠāŽ˛ā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆ āŽ¤ā¯‡āŽ°ā¯āŽĩ❈ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "home_page_favorite_err_local": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_favorite_err_partner": "āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤ āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽĩāŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "home_page_first_time_notice": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽĩāŽ¤ā¯ āŽ‡āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤āŽžāŽ˛ā¯, āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•, āŽ‡āŽ¤āŽŠā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆ āŽ…āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽŋāŽ°āŽŋāŽĩ❁āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯", + "home_page_locked_error_local": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĒā¯‚āŽŸā¯āŽŸāŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "home_page_locked_error_partner": "āŽĒāŽ™ā¯āŽ•ā¯āŽ¤āŽžāŽ°āŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĒā¯‚āŽŸā¯āŽŸāŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "home_page_share_err_local": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁, āŽšā¯āŽ•āŽŋāŽĒā¯āŽĒāŽŋāŽ™ā¯ āŽĩāŽ´āŽŋāŽ¯āŽžāŽ• āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĒāŽ•āŽŋāŽ° āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "home_page_upload_err_limit": "āŽ’āŽ°ā¯ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽšāŽŽā¯ 30 āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "host": "āŽĩāŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯‹āŽŽā¯āŽĒāŽŋ", "hour": "āŽŽāŽŖāŽŋ", + "hours": "āŽŽāŽŖāŽŋ", + "id": "āŽāŽŸāŽŋ", + "idle": "āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ•ā¯āŽ•āŽŽā¯", + "ignore_icloud_photos": "ICloud āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "ignore_icloud_photos_description": "ICloud āŽ‡āŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸāŽžāŽ¤ā¯", "image": "āŽĒāŽŸāŽŽā¯", "image_alt_text_date": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {date} āŽ‡āŽ˛ā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "image_alt_text_date_1_person": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {{person1} āŽ‡āŽ˛ā¯ {date}", @@ -744,7 +1161,11 @@ "image_alt_text_date_place_1_person": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {city}, {country} {person1} āŽ‰āŽŸāŽŠā¯ {date}", "image_alt_text_date_place_2_people": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {city}, {country} āŽ‰āŽŸāŽŠā¯ {person1} āŽŽāŽąā¯āŽąā¯āŽŽā¯ {person2} {date}", "image_alt_text_date_place_3_people": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {city}, {country} {person1}, {person2}, āŽŽāŽąā¯āŽąā¯āŽŽā¯ {person3} āŽ‡āŽ˛ā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {date}", - "image_alt_text_date_place_4_or_more_people": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {city}, {country} {person1}, {person2}, āŽŽāŽąā¯āŽąā¯āŽŽā¯ {āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽ•āŽĩā¯āŽŠā¯āŽŸā¯, āŽŽāŽŖā¯} āŽŽāŽąā¯āŽąāŽĩāŽ°ā¯āŽ•āŽŗā¯ {date}", + "image_alt_text_date_place_4_or_more_people": "{isvideo, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {Video} āŽĒāŽŋāŽą {Image}} {city}, {country} {person1}, {person2}, āŽŽāŽąā¯āŽąā¯āŽŽā¯ {āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽ…āŽ•ā¯āŽ•āŽĩā¯āŽŖā¯āŽŸā¯, āŽŽāŽŖā¯} āŽŽāŽąā¯āŽąāŽĩāŽ°ā¯āŽ•āŽŗā¯ {date}", + "image_saved_successfully": "āŽĒāŽŸāŽŽā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "image_viewer_page_state_provider_download_started": "āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽŽā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•āŽŋāŽ¯āŽ¤ā¯", + "image_viewer_page_state_provider_download_success": "āŽĩā¯†āŽąā¯āŽąāŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "image_viewer_page_state_provider_share_error": "āŽĒāŽŋāŽ´ā¯ˆ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩ❁", "immich_logo": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ˛ā¯‹āŽ•ā¯‹", "immich_web_interface": "āŽ‡āŽŽā¯āŽ°āŽŋāŽšā¯ āŽĩāŽ˛ā¯ˆ āŽ‡āŽŸā¯ˆāŽŽā¯āŽ•āŽŽā¯", "import_from_json": "āŽšāŽžāŽ¤ā¯ŠāŽĒā¯ŠāŽ•ā¯ āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ", @@ -755,6 +1176,7 @@ "include_shared_albums": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "include_shared_partner_assets": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "individual_share": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ™ā¯āŽ•ā¯", + "individual_shares": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ™ā¯āŽ•ā¯āŽ•āŽŗā¯", "info": "āŽ¤āŽ•āŽĩāŽ˛ā¯", "interval": { "day_at_onepm": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ¨āŽžāŽŗā¯āŽŽā¯ āŽŽāŽ¤āŽŋāŽ¯āŽŽā¯ 1 āŽŽāŽŖāŽŋāŽ•ā¯āŽ•ā¯", @@ -762,8 +1184,16 @@ "night_at_midnight": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ‡āŽ°āŽĩā¯āŽŽā¯ āŽ¨āŽŗā¯āŽŗāŽŋāŽ°āŽĩāŽŋāŽ˛ā¯", "night_at_twoam": "āŽ’āŽĩā¯āŽĩā¯ŠāŽ°ā¯ āŽ‡āŽ°āŽĩā¯āŽŽā¯ āŽ…āŽ¤āŽŋāŽ•āŽžāŽ˛ā¯ˆ 2 āŽŽāŽŖāŽŋāŽ•ā¯āŽ•ā¯" }, + "invalid_date": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽ¤ā¯‡āŽ¤āŽŋ", + "invalid_date_format": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽŸāŽŋāŽĩāŽŽā¯", "invite_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ´ā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "invite_to_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽ…āŽ´ā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "ios_debug_info_fetch_ran_at": "āŽ“āŽŸāŽŋāŽ¯āŽ¤ā¯ {dateTime}", + "ios_debug_info_last_sync_at": "āŽ•āŽŸā¯ˆāŽšāŽŋ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ {dateTime}", + "ios_debug_info_no_processes_queued": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯†āŽ¯āŽ˛ā¯āŽŽā¯āŽąā¯ˆāŽ•āŽŗā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "ios_debug_info_no_sync_yet": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽĩā¯‡āŽ˛ā¯ˆ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽ‡āŽ¯āŽ™ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "ios_debug_info_processes_queued": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {{count} āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯†āŽ¯āŽ˛ā¯āŽŽā¯āŽąā¯ˆ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯} āŽĒāŽŋāŽą {{count} āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽšā¯†āŽ¯āŽ˛ā¯āŽŽā¯āŽąā¯ˆāŽ•āŽŗā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŠ", + "ios_debug_info_processing_ran_at": "āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ•āŽŽā¯ {dateTime}", "items_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋ} āŽĒāŽŋāŽą {# āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯}}", "jobs": "āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯", "keep": "āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ™ā¯āŽ•āŽŗā¯", @@ -772,38 +1202,93 @@ "kept_this_deleted_others": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĩā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", "keyboard_shortcuts": "āŽĩāŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽ˛āŽ•ā¯ˆ āŽ•ā¯āŽąā¯āŽ•ā¯āŽ•ā¯āŽĩāŽ´āŽŋāŽ•āŽŗā¯", "language": "āŽŽā¯ŠāŽ´āŽŋ", + "language_no_results_subtitle": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤ā¯ˆ āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "language_no_results_title": "āŽŽāŽ¨ā¯āŽ¤ āŽŽā¯ŠāŽ´āŽŋāŽ•āŽŗā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "language_search_hint": "āŽŽā¯ŠāŽ´āŽŋāŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", "language_setting_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽŽāŽžāŽŠ āŽŽā¯ŠāŽ´āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "large_files": "āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "last": "āŽ•āŽŸā¯ˆāŽšāŽŋ", "last_seen": "āŽ•āŽŸā¯ˆāŽšāŽŋāŽ¯āŽžāŽ• āŽĒāŽžāŽ°ā¯āŽ¤ā¯āŽ¤ā¯‡āŽŠā¯", "latest_version": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁", "latitude": "āŽ…āŽ•āŽ˛āŽžāŽ™ā¯āŽ•ā¯", "leave": "āŽĩāŽŋāŽŸā¯āŽĒā¯āŽĒ❁", + "leave_album": "āŽĩāŽŋāŽŸā¯āŽĒā¯āŽĒ❁ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", + "lens_model": "āŽ˛ā¯†āŽŠā¯āŽšā¯ āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋ", "let_others_respond": "āŽŽāŽąā¯āŽąāŽĩāŽ°ā¯āŽ•āŽŗā¯ āŽĒāŽ¤āŽŋāŽ˛āŽŗāŽŋāŽ•ā¯āŽ•āŽŸā¯āŽŸā¯āŽŽā¯", "level": "āŽ¨āŽŋāŽ˛ā¯ˆ", "library": "āŽ¨ā¯‚āŽ˛āŽ•āŽŽā¯", "library_options": "āŽ¨ā¯‚āŽ˛āŽ• āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "library_page_device_albums": "āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "library_page_new_album": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", + "library_page_sort_asset_count": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", + "library_page_sort_created": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¤ā¯‡āŽ¤āŽŋ", + "library_page_sort_last_modified": "āŽ•āŽŸā¯ˆāŽšāŽŋāŽ¯āŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "library_page_sort_title": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❁", + "licenses": "āŽ‰āŽ°āŽŋāŽŽāŽ™ā¯āŽ•āŽŗā¯", "light": "āŽ’āŽŗāŽŋ", + "like": "āŽĒā¯‹āŽŠā¯āŽą", "like_deleted": "āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ˆāŽĒā¯ āŽĒā¯‹āŽ˛", "link_motion_video": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹", - "link_options": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "link_to_oauth": "OAUTH āŽ‰āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁", "linked_oauth_account": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ OAUTH āŽ•āŽŖāŽ•ā¯āŽ•ā¯", "list": "āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛ā¯", "loading": "āŽāŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯", "loading_search_results_failed": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽąā¯āŽĩāŽ¤ā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯āŽŸā¯ˆāŽ¨ā¯āŽ¤āŽ¤ā¯", + "local": "āŽ‰āŽŗā¯āŽŗāŽ•", + "local_asset_cast_failed": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽ’āŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒ āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "local_assets": "āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "local_media_summary": "āŽ‰āŽŗā¯āŽŗāŽ• āŽŠāŽŸāŽ• āŽšā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŽā¯", + "local_network": "āŽ‰āŽŗā¯āŽŗāŽ• āŽĒāŽŋāŽŖā¯ˆāŽ¯āŽŽā¯", + "local_network_sheet_info": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸā¯āŽŸ āŽĩā¯ˆāŽƒāŽĒ❈ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽŽā¯‚āŽ˛āŽŽā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "location_permission": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ‡āŽšā¯ˆāŽĩ❁", + "location_permission_content": "āŽ†āŽŸā¯āŽŸā¯‹-āŽšā¯āŽĩāŽŋāŽŸā¯āŽšāŽŋāŽ™ā¯ āŽ…āŽŽā¯āŽšāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽ‡āŽŽā¯āŽŽāŽŋāŽ•ā¯āŽ•ā¯ āŽ¤ā¯āŽ˛ā¯āŽ˛āŽŋāŽ¯āŽŽāŽžāŽŠ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ‡āŽšā¯ˆāŽĩ❁ āŽ¤ā¯‡āŽĩ❈, āŽŽāŽŠāŽĩ❇ āŽ‡āŽ¤ā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽĩā¯ˆāŽƒāŽĒ❈ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽŠā¯ āŽĒā¯†āŽ¯āŽ°ā¯ˆāŽĒā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯", + "location_picker_choose_on_map": "āŽĩāŽ°ā¯ˆāŽĒāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "location_picker_latitude_error": "āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽĒāŽŸāŽŋāŽ¯āŽžāŽ•ā¯āŽŽā¯ āŽ…āŽŸā¯āŽšāŽ°ā¯‡āŽ•ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "location_picker_latitude_hint": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŸā¯āŽšāŽ°ā¯‡āŽ•ā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ™ā¯āŽ•ā¯‡ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "location_picker_longitude_error": "āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽĒāŽŸāŽŋāŽ¯āŽžāŽ•ā¯āŽŽā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•āŽ°ā¯‡āŽ•ā¯ˆāŽ¯ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "location_picker_longitude_hint": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•āŽ°ā¯‡āŽ•ā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ™ā¯āŽ•ā¯‡ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "lock": "āŽĒā¯‚āŽŸā¯āŽŸā¯", + "locked_folder": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆ", + "log_detail_title": "āŽĒāŽ¤āŽŋāŽĩ❁ āŽĩāŽŋāŽĩāŽ°āŽŽā¯", "log_out": "āŽĩāŽŋāŽŸā¯āŽĒāŽ¤āŽŋāŽ•ā¯ˆ", "log_out_all_devices": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąāŽĩā¯āŽŽā¯", + "logged_in_as": "{user}", "logged_out_all_devices": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąā¯āŽąāŽŋāŽŠā¯‡āŽŠā¯", "logged_out_device": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¨ā¯āŽ¤ āŽšāŽžāŽ¤āŽŠāŽŽā¯", "login": "āŽĒā¯āŽ•ā¯āŽĒāŽ¤āŽŋāŽĩ❁", + "login_disabled": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "login_form_api_exception": "āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽ¤āŽŋāŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯. āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "login_form_back_button_text": "āŽĒāŽŋāŽŠā¯", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http: // your-server-ip: āŽ¤ā¯āŽąā¯ˆāŽŽā¯āŽ•āŽŽā¯", + "login_form_endpoint_url": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽāŽŖā¯āŽŸā¯āŽĒāŽžāŽ¯āŽŋāŽŖā¯āŽŸā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", + "login_form_err_http": "Http: // āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ https: // āŽāŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽŋāŽŸāŽĩā¯āŽŽā¯", + "login_form_err_invalid_email": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯", + "login_form_err_invalid_url": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", + "login_form_err_leading_whitespace": "āŽŽā¯āŽŠā¯āŽŠāŽŖāŽŋ āŽĩāŽŋāŽŸā¯āŽšā¯āŽĒā¯‡āŽšā¯", + "login_form_err_trailing_whitespace": "āŽĒāŽŋāŽŠā¯āŽ¤āŽŋāŽ™ā¯ āŽĩā¯ˆāŽŸā¯āŽšā¯āŽĒā¯‡āŽšā¯", + "login_form_failed_get_oauth_server_config": "OAuth āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋ āŽĒāŽ¤āŽŋāŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯, āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "login_form_failed_get_oauth_server_disable": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ OAuth āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "login_form_failed_login": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ, āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• URL, āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "login_form_handshake_exception": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽāŽŖā¯āŽŸā¯āŽšā¯‡āŽ•ā¯ āŽĩāŽŋāŽ¤āŽŋāŽĩāŽŋāŽ˛āŽ•ā¯āŽ•ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤āŽ¤ā¯. āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽŠā¯āŽĩāŽ¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗā¯ āŽŽāŽŠā¯āŽąāŽžāŽ˛ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ¤āŽŠā¯āŽĩāŽ¯ āŽ•ā¯ˆāŽ¯ā¯ŠāŽĒā¯āŽĒāŽŽāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽžāŽŠā¯āŽąāŽŋāŽ¤āŽ´ā¯ āŽ†āŽ¤āŽ°āŽĩ❈ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "login_form_password_hint": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", + "login_form_save_login": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¨ā¯āŽ¤āŽŋāŽ°ā¯āŽ™ā¯āŽ•āŽŗā¯", + "login_form_server_empty": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", + "login_form_server_error": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ.", "login_has_been_disabled": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯.", + "login_password_changed_error": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ āŽāŽąā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "login_password_changed_success": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "logout_all_device_confirmation": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "logout_this_device_confirmation": "āŽ‡āŽ¨ā¯āŽ¤ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŸā¯āŽŸā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "logs": "āŽĒāŽ¤āŽŋāŽĩā¯āŽ•āŽŗā¯", "longitude": "āŽ¨ā¯†āŽŸā¯āŽŸāŽžāŽ™ā¯āŽ•ā¯", "look": "āŽĒāŽžāŽ°ā¯", "loop_videos": "āŽ˛ā¯‚āŽĒā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯", "loop_videos_description": "āŽĩāŽŋāŽ°āŽŋāŽĩāŽžāŽŠ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°āŽŋāŽ˛ā¯ āŽ’āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽĩ❈ āŽ¤āŽžāŽŠāŽžāŽ• āŽĩāŽŗā¯ˆāŽ¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", - "main_branch_warning": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯‡āŽŽā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗā¯; āŽĩā¯†āŽŗāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸā¯āŽŽā¯ˆāŽ¯āŽžāŽ• āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•ā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯!", + "main_branch_warning": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ’āŽ°ā¯ āŽŽā¯‡āŽŽā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗā¯; āŽĩā¯†āŽŗāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸā¯āŽŽā¯ˆāŽ¯āŽžāŽ• āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•ā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯!", + "main_menu": "āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛ā¯ āŽĩāŽŋāŽŗā¯ˆāŽ¯āŽžāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "make": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯", + "manage_geolocation": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "manage_shared_links": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "manage_sharing_with_partners": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩ❈ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "manage_the_app_settings": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -812,13 +1297,40 @@ "manage_your_devices": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¨ā¯āŽ¤ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "manage_your_oauth_connection": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ OAuth āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "map": "āŽĩāŽ°ā¯ˆāŽĒāŽŸāŽŽā¯", + "map_assets_in_bounds": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, = 0 {No photos in this area} āŽ’āŽŠā¯āŽąā¯ {# āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯} āŽŽāŽąā¯āŽą {# āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯}}", + "map_cannot_get_user_location": "āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "map_location_dialog_yes": "āŽ†āŽŽā¯", + "map_location_picker_page_use_location": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "map_location_service_disabled_content": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽĒāŽŖāŽŋ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯. āŽ‡āŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽ…āŽ¤ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "map_location_service_disabled_title": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽĒāŽŖāŽŋ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "map_marker_for_images": "{city}, {country}", "map_marker_with_image": "āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯āŽŸāŽŠā¯ āŽĩāŽ°ā¯ˆāŽĒāŽŸ āŽŽāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽ°ā¯", + "map_no_location_permission_content": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ‡āŽšā¯ˆāŽĩ❁ āŽ¤ā¯‡āŽĩ❈. āŽ‡āŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽ…āŽ¤ā¯ˆ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "map_no_location_permission_title": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸ āŽ‡āŽšā¯ˆāŽĩ❁ āŽŽāŽąā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "map_settings": "āŽĩāŽ°ā¯ˆāŽĒāŽŸ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "map_settings_dark_mode": "āŽ‡āŽ°ā¯āŽŖā¯āŽŸ āŽŽā¯āŽąā¯ˆ", + "map_settings_date_range_option_day": "āŽ•āŽŸāŽ¨ā¯āŽ¤ 24 āŽŽāŽŖāŽŋ āŽ¨ā¯‡āŽ°āŽŽā¯", + "map_settings_date_range_option_days": "āŽ•āŽŸāŽ¨ā¯āŽ¤ {days} āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯", + "map_settings_date_range_option_year": "āŽ•āŽŸāŽ¨ā¯āŽ¤ āŽ†āŽŖā¯āŽŸā¯", + "map_settings_date_range_option_years": "āŽ•āŽŸāŽ¨ā¯āŽ¤ {years} āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯", + "map_settings_dialog_title": "āŽĩāŽ°ā¯ˆāŽĒāŽŸ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "map_settings_include_show_archived": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩāŽ°ā¯", + "map_settings_include_show_partners": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "map_settings_only_show_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽ¤ā¯ˆ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "map_settings_theme_settings": "āŽĩāŽ°ā¯ˆāŽĒāŽŸ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯", + "map_zoom_to_see_photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "mark_all_as_read": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĒāŽŸāŽŋ āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "mark_as_read": "āŽĒāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĒāŽŸāŽŋ āŽ•ā¯āŽąāŽŋ", + "marked_all_as_read": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩāŽžāŽšāŽŋāŽĒā¯āŽĒāŽ¤āŽžāŽ• āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "matches": "āŽĒā¯‹āŽŸā¯āŽŸāŽŋāŽ•āŽŗā¯", + "matching_assets": "āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", "media_type": "āŽŠāŽŸāŽ• āŽĩāŽ•ā¯ˆ", "memories": "āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯", + "memories_all_caught_up": "āŽ…āŽŠā¯ˆāŽĩāŽ°ā¯āŽŽā¯ āŽĒāŽŋāŽŸāŽŋāŽĒāŽŸā¯āŽŸāŽŠāŽ°ā¯", + "memories_check_back_tomorrow": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ¨āŽžāŽŗā¯ˆ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "memories_setting_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "memories_start_over": "āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ•", + "memories_swipe_to_close": "āŽŽā¯‚āŽŸā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽšā¯āŽĩ❈āŽĒā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", "memory": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ•āŽŽā¯", "memory_lane_title": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ• āŽ˛ā¯‡āŽŠā¯ {title}", "menu": "āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛ā¯", @@ -830,19 +1342,40 @@ "merged_people_count": "āŽ’āŽŠā¯āŽąāŽŋāŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽĒāŽ°ā¯} āŽŽāŽąā¯āŽą {# āŽŽāŽ•ā¯āŽ•āŽŗā¯}}", "minimize": "āŽ•ā¯āŽąā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "minute": "āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "minutes": "āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗā¯", "missing": "āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "model": "āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋāŽ¯ā¯āŽ°ā¯", "month": "āŽŽāŽžāŽ¤āŽŽā¯", + "monthly_title_text_date_format": "Mmmm āŽ’āŽ¯ā¯", "more": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯", + "move": "āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "move_off_locked_folder": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąāŽĩā¯āŽŽā¯", + "move_to_lock_folder_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "move_to_locked_folder": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", + "move_to_locked_folder_confirmation": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯āŽŽā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŽā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯ā¯āŽŽā¯", + "moved_to_archive": "āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "moved_to_library": "āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "moved_to_trash": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "multiselect_grid_edit_date_time_err_read_only": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯ˆ) āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽŸāŽŋāŽ¤ā¯āŽ¤ āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯, āŽ¤āŽĩāŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "multiselect_grid_edit_gps_err_read_only": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯ˆ) āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽŸāŽŋāŽ•ā¯āŽ•, āŽšā¯āŽ•āŽŋāŽĒā¯āŽĒāŽŋāŽ™ā¯ āŽ†āŽ•āŽŋāŽ¯āŽĩāŽąā¯āŽąā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "mute_memories": "āŽŽā¯āŽŸāŽ•ā¯āŽ•ā¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯", "my_albums": "āŽŽāŽŠāŽ¤ā¯ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "name": "āŽĒā¯†āŽ¯āŽ°ā¯", "name_or_nickname": "āŽĒā¯†āŽ¯āŽ°ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒā¯āŽŠā¯ˆāŽĒā¯āŽĒā¯†āŽ¯āŽ°ā¯", + "network_requirement_photos_upload": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ˛āŽžāŽ°ā¯ āŽ¤āŽ°āŽĩ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "network_requirement_videos_upload": "āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ˛āŽžāŽ°ā¯ āŽ¤āŽ°āŽĩ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "network_requirements": "āŽĒāŽŋāŽŖā¯ˆāŽ¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ•āŽŗā¯", + "network_requirements_updated": "āŽĒāŽŋāŽŖā¯ˆāŽ¯āŽŽā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ•āŽŗā¯ āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽŠ, āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "networking_settings": "āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽ™ā¯", + "networking_subtitle": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽ‡āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒā¯āŽŗā¯āŽŗāŽŋ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "never": "āŽ’āŽ°ā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯", "new_album": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", "new_api_key": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆ", "new_password": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", "new_person": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯", + "new_pin_code": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯", + "new_pin_code_subtitle": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯ā¯ˆ āŽ…āŽŖā¯āŽ• āŽ‡āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆāŽ¯āŽžāŽ•ā¯āŽŽā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽžāŽ• āŽ…āŽŖā¯āŽ• āŽ’āŽ°ā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "new_timeline": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆ", "new_user_created": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "new_version_available": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "newest_first": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽ¤āŽ˛ā¯", @@ -854,42 +1387,68 @@ "no_albums_yet": "āŽ‰āŽ™ā¯āŽ•āŽŗāŽŋāŽŸāŽŽā¯ āŽ‡āŽ¤ā¯āŽĩāŽ°ā¯ˆ āŽŽāŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽŠā¯āŽąā¯ āŽ¤ā¯†āŽ°āŽŋāŽ•āŽŋāŽąāŽ¤ā¯.", "no_archived_assets_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽĩāŽąā¯āŽąā¯ˆ āŽŽāŽąā¯ˆāŽ•ā¯āŽ• āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "no_assets_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽ¤āŽ˛ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽšā¯ŠāŽŸā¯āŽ•ā¯āŽ•ā¯ āŽšā¯†āŽ¯ā¯āŽ•", + "no_assets_to_show": "āŽ•āŽžāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "no_cast_devices_found": "āŽ¨āŽŸāŽŋāŽ•āŽ°ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "no_checksum_local": "āŽšā¯†āŽ•ā¯āŽšāŽŽā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ - āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "no_checksum_remote": "āŽšā¯†āŽ•ā¯āŽšāŽŽā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ - āŽ¤ā¯ŠāŽ˛ā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", "no_duplicates_found": "āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ.", "no_exif_info_available": "EXIF āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "no_explore_results_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒ❈ āŽ†āŽ°āŽžāŽ¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯.", "no_favorites_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽŋāŽąāŽ¨ā¯āŽ¤ āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĩāŽŋāŽ°ā¯ˆāŽĩāŽžāŽ•āŽ•ā¯ āŽ•āŽŖā¯āŽŸā¯āŽĒāŽŋāŽŸāŽŋāŽ•ā¯āŽ• āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "no_libraries_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "no_local_assets_found": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯†āŽ•ā¯āŽšāŽŽā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽŗā¯āŽŗāŽ• āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "no_locked_photos_message": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽŠ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ˛āŽžāŽĩā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ•āŽžāŽŖā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ā¯.", "no_name": "āŽĒā¯†āŽ¯āŽ°ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "no_notifications": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "no_people_found": "āŽĒā¯ŠāŽ°ā¯āŽ¨ā¯āŽ¤āŽ•ā¯āŽ•ā¯‚āŽŸāŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "no_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "no_remote_assets_found": "āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯†āŽ•ā¯āŽšāŽŽā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯ŠāŽ˛ā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽžāŽŖāŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "no_results": "āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "no_results_description": "āŽ’āŽ°ā¯ āŽ’āŽ¤ā¯āŽ¤ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒā¯ŠāŽ¤ā¯āŽĩāŽžāŽŠ āŽŽā¯āŽ•ā¯āŽ•āŽŋāŽ¯ āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "no_shared_albums_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽĩāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗ āŽ’āŽ°ā¯ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "no_uploads_in_progress": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "not_available": "āŽ‡āŽ¤āŽąā¯āŽ•āŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "not_in_any_album": "āŽŽāŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "not_selected": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "note_apply_storage_label_to_previously_uploaded assets": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒ❁: āŽŽā¯āŽŠā¯āŽŠāŽ°ā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ˛ā¯‡āŽĒāŽŋāŽŗā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "notes": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "nothing_here_yet": "āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽ‡āŽ™ā¯āŽ•ā¯‡ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "notification_permission_dialog_content": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•, āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽšā¯ āŽšā¯†āŽŠā¯āŽąā¯ āŽ‡āŽšā¯ˆāŽĩ❁ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "notification_permission_list_tile_content": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽ‡āŽšā¯ˆāŽĩ❁ āŽĩāŽ´āŽ™ā¯āŽ•āŽĩā¯āŽŽā¯.", + "notification_permission_list_tile_enable_button": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "notification_permission_list_tile_title": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽ‡āŽšā¯ˆāŽĩ❁", "notification_toggle_setting_description": "āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "notifications": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "notifications_setting_description": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "oauth": "Oauth", "official_immich_resources": "āŽ‰āŽ¤ā¯āŽ¤āŽŋāŽ¯ā¯‹āŽ•āŽĒā¯‚āŽ°ā¯āŽĩ āŽ‡āŽŽā¯āŽŽāŽž āŽĩāŽŗāŽ™ā¯āŽ•āŽŗā¯", "offline": "āŽ‡āŽŖā¯ˆāŽ¯āŽŽāŽŋāŽ˛ā¯āŽ˛āŽžāŽŽāŽ˛ā¯", + "offset": "āŽˆāŽŸā¯āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯", "ok": "āŽšāŽ°āŽŋ", "oldest_first": "āŽŽā¯āŽ¤āŽ˛āŽŋāŽ˛ā¯ āŽĒāŽ´āŽŽā¯ˆāŽ¯āŽžāŽŠāŽ¤ā¯", + "on_this_device": "āŽ‡āŽ¨ā¯āŽ¤ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯", "onboarding": "āŽ†āŽŠā¯ āŽĒā¯‹āŽ°ā¯āŽŸāŽŋāŽ™ā¯", - "onboarding_privacy_description": "āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽŽā¯ (āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽŠāŽžāŽ˛ā¯) āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽšā¯‡āŽĩā¯ˆāŽ•āŽŗā¯ˆ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗāŽŠ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯.", + "onboarding_locale_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽŽāŽžāŽŠ āŽŽā¯ŠāŽ´āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ‡āŽ¤ā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽŽāŽžāŽąā¯āŽąāŽ˛āŽžāŽŽā¯.", + "onboarding_privacy_description": "āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽŽā¯ (āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽŠāŽžāŽ˛ā¯) āŽ¨āŽąā¯āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽ™ā¯āŽ•āŽŗā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒā¯āŽą āŽšā¯‡āŽĩā¯ˆāŽ•āŽŗā¯ˆ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗāŽŠ, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯.", + "onboarding_server_welcome_description": "āŽšāŽŋāŽ˛ āŽĒā¯ŠāŽ¤ā¯āŽĩāŽžāŽŠ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽ•āŽ´ā¯āŽĩ❈ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯‹āŽŽā¯.", "onboarding_theme_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ¤āŽžāŽ°āŽŖāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽĩāŽŖā¯āŽŖ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•. āŽ‡āŽ¤ā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽŽāŽžāŽąā¯āŽąāŽ˛āŽžāŽŽā¯.", + "onboarding_user_welcome_description": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽĩā¯‹āŽŽā¯!", "onboarding_welcome_user": "āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯, {user}", "online": "āŽ†āŽŠā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯", "only_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩ❈ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡", + "open": "āŽ¤āŽŋāŽą", "open_in_map_view": "āŽĩāŽ°ā¯ˆāŽĒāŽŸāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽ¤āŽŋāŽąāŽ¨ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯", "open_in_openstreetmap": "OpenStreetMap āŽ‡āŽ˛ā¯ āŽ¤āŽŋāŽąāŽ¨ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯", "open_the_search_filters": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽĩāŽŸāŽŋāŽĒā¯āŽĒāŽžāŽŠā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽąāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "options": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "or": "āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯", + "organize_into_albums": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽžāŽ• āŽ’āŽ´ā¯āŽ™ā¯āŽ•āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "organize_into_albums_description": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋ āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ‰āŽŗā¯āŽŗ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽĩā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "organize_your_library": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ’āŽ´ā¯āŽ™ā¯āŽ•āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "original": "āŽ…āŽšāŽ˛ā¯", "other": "āŽŽāŽąā¯āŽąā¯ŠāŽŠā¯āŽąā¯", "other_devices": "āŽĒāŽŋāŽą āŽšāŽžāŽ¤āŽŠāŽ™ā¯āŽ•āŽŗā¯", + "other_entities": "āŽĒāŽŋāŽą āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽ™ā¯āŽ•āŽŗā¯", "other_variables": "āŽĒāŽŋāŽą āŽŽāŽžāŽąāŽŋāŽ•āŽŗā¯", "owned": "āŽšā¯ŠāŽ¨ā¯āŽ¤āŽŽāŽžāŽŠāŽ¤ā¯", "owner": "āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯", @@ -897,6 +1456,14 @@ "partner_can_access": "{partner} āŽ…āŽŖā¯āŽ•āŽ˛āŽžāŽŽā¯", "partner_can_access_assets": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩ❈ āŽ¤āŽĩāŽŋāŽ° āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŽā¯", "partner_can_access_location": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŸāŽŽā¯", + "partner_list_user_photos": "{āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "partner_list_view_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŖā¯āŽ•", + "partner_page_empty_message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŠā¯āŽŠā¯āŽŽā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽŸāŽŠā¯āŽŽā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸāŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ.", + "partner_page_no_more_users": "āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽ‡āŽŠāŽŋ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "partner_page_partner_add_failed": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", + "partner_page_select_partner": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "partner_page_shared_to_title": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "partner_page_stop_sharing_content": "{āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ your āŽ‡āŽŠāŽŋ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŖā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", "partner_sharing": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩ❁", "partners": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽ•āŽŗā¯", "password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", @@ -922,10 +1489,24 @@ "permanent_deletion_warning_setting_description": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ’āŽ°ā¯ āŽŽāŽšā¯āŽšāŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "permanently_delete": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "permanently_delete_assets_count": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {asset} āŽŽāŽąā¯āŽą {assets}}", - "permanently_delete_assets_prompt": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯?} āŽŽāŽąā¯āŽą {āŽ‡āŽ¨ā¯āŽ¤ # āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯? } āŽ…āŽĩāŽ°ā¯āŽ•āŽŗāŽŋāŽŠā¯}} āŽ†āŽ˛ā¯āŽĒāŽŽā¯ (āŽ•āŽŗā¯) āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯.", + "permanently_delete_assets_prompt": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽšā¯āŽšāŽ¯āŽŽāŽžāŽ• {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯?} āŽŽāŽąā¯āŽą {āŽ‡āŽ¨ā¯āŽ¤ # āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯?", "permanently_deleted_asset": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯", "permanently_deleted_assets_count": "āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", + "permission": "āŽ‡āŽšā¯ˆāŽĩ❁", + "permission_empty": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽšā¯ˆāŽĩ❁ āŽ•āŽžāŽ˛āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽžāŽ¤ā¯", + "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": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽšā¯ˆāŽĩ❁ āŽ¤ā¯‡āŽĩ❈.", "person": "āŽ†āŽŗā¯", + "person_age_months": "{āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽŽāŽžāŽ¤āŽŽā¯} āŽŽāŽąā¯āŽą {# āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯}} āŽĒāŽ´ā¯ˆāŽ¯āŽ¤ā¯", + "person_age_year_months": "1 āŽ†āŽŖā¯āŽŸā¯, {āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽŽāŽžāŽ¤āŽŽā¯} āŽŽāŽąā¯āŽą {# āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯}} āŽĒāŽ´ā¯ˆāŽ¯āŽ¤ā¯", + "person_age_years": "{āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {# āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯}} āŽĒāŽ´ā¯ˆāŽ¯āŽ¤ā¯", + "person_birthdate": "{date} āŽ‡āŽ˛ā¯ āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤āŽžāŽ°ā¯", "person_hidden": "{name} {āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽ‰āŽŖā¯āŽŽā¯ˆ {(āŽŽāŽąā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ)} āŽĒāŽŋāŽą {}}", "photo_shared_all_users": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽŽāŽ˛ā¯āŽ˛āŽž āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯āŽŽā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽ¤āŽžāŽ•āŽ¤ā¯ āŽ¤ā¯†āŽ°āŽŋāŽ•āŽŋāŽąāŽ¤ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗāŽŋāŽŸāŽŽā¯ āŽŽāŽ¨ā¯āŽ¤ āŽĒāŽ¯āŽŠāŽ°ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ.", "photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", @@ -933,20 +1514,41 @@ "photos_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯} āŽĒāŽŋāŽą {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯}}", "photos_from_previous_years": "āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", "pick_a_location": "āŽ’āŽ°ā¯ āŽ‡āŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "pin_code_changed_successfully": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽąāŽŋāŽ¯āŽ¤ā¯", + "pin_code_reset_successfully": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "pin_code_setup_successfully": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "pin_verification": "āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒ❁", "place": "āŽ‡āŽŸāŽŽā¯", "places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯", + "places_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽ‡āŽŸāŽŽā¯} āŽĒāŽŋāŽą {{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯} āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯}}", "play": "āŽĩāŽŋāŽŗā¯ˆāŽ¯āŽžāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "play_memories": "āŽĒāŽŋāŽŗā¯‡āŽŽā¯†āŽŽāŽ°āŽŋāŽ•āŽŗā¯", "play_motion_photo": "āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽĩāŽŋāŽŗā¯ˆāŽ¯āŽžāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "play_or_pause_video": "āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽĩ❈ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "please_auth_to_access": "āŽ…āŽŖā¯āŽ•āŽ˛ā¯ˆ āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "port": "āŽ¤ā¯āŽąā¯ˆāŽŽā¯āŽ•āŽŽā¯", + "preferences_settings_subtitle": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "preferences_settings_title": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽ•āŽŗā¯", + "preparing": "āŽ¤āŽ¯āŽžāŽ°āŽžāŽ•āŽŋāŽąāŽ¤ā¯", "preset": "āŽŽā¯āŽŠā¯āŽŠāŽŽā¯ˆāŽĩ❁", "preview": "āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽŽā¯", "previous": "āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯", "previous_memory": "āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ•āŽŽā¯", - "previous_or_next_photo": "āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", + "previous_or_next_day": "āŽ¨āŽžāŽŗā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ/āŽĒāŽŋāŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ", + "previous_or_next_month": "āŽŽāŽžāŽ¤āŽŽā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ/āŽĒāŽŋāŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ", + "previous_or_next_photo": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ/āŽĒāŽŋāŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ", + "previous_or_next_year": "āŽ†āŽŖā¯āŽŸā¯ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ/āŽĒāŽŋāŽŠā¯āŽŠā¯‹āŽ•ā¯āŽ•āŽŋ", "primary": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ", "privacy": "āŽ¤āŽŠāŽŋāŽ¯ā¯āŽ°āŽŋāŽŽā¯ˆ", + "profile": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽŽā¯", + "profile_drawer_app_logs": "āŽĒāŽ¤āŽŋāŽĩā¯āŽ•āŽŗā¯", + "profile_drawer_client_out_of_date_major": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠāŽ¤ā¯. āŽ¤āŽ¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽšāŽŽā¯€āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽ•ā¯āŽ•āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "profile_drawer_client_out_of_date_minor": "āŽŽā¯ŠāŽĒā¯ˆāŽ˛ā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠāŽ¤ā¯. āŽ¤āŽ¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽšāŽŽā¯€āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ¯ āŽšāŽŋāŽąāŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "profile_drawer_client_server_up_to_date": "āŽĩāŽžāŽ™ā¯āŽ•āŽŋ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ¤ā¯āŽ¤ āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽŠ", + "profile_drawer_github": "āŽ•āŽŋāŽŸā¯āŽšāŽĒā¯", + "profile_drawer_readonly_mode": "āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯. āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽą āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ…āŽĩāŽ¤āŽžāŽ°ā¯ āŽāŽ•āŽžāŽŠā¯ˆ āŽ¨ā¯€āŽŖā¯āŽŸ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽ…āŽ´ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", + "profile_drawer_server_out_of_date_major": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠāŽ¤ā¯. āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽŽā¯āŽ•ā¯āŽ•āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "profile_drawer_server_out_of_date_minor": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽŠāŽ¤ā¯. āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽšāŽŋāŽąāŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•ā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", "profile_image_of_user": "{āŽĒāŽ¯āŽŠāŽ°āŽŋāŽŠā¯ āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽĒā¯ āŽĒāŽŸāŽŽā¯", "profile_picture_set": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽĒā¯ āŽĒāŽŸ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒ❁.", "public_album": "āŽĒā¯ŠāŽ¤ā¯ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", @@ -971,7 +1573,7 @@ "purchase_lifetime_description": "āŽĩāŽžāŽ´ā¯āŽ¨āŽžāŽŗā¯ āŽ•ā¯ŠāŽŗā¯āŽŽā¯āŽ¤āŽ˛ā¯", "purchase_option_title": "āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽĩāŽžāŽ™ā¯āŽ•āŽĩā¯āŽŽā¯", "purchase_panel_info_1": "āŽ‡āŽŽā¯āŽŽāŽŋāŽ¯ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽ¨āŽŋāŽąā¯ˆāŽ¯ āŽ¨ā¯‡āŽ°āŽŽā¯āŽŽā¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽĩ❈āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽŽā¯āŽ´ā¯āŽ¨ā¯‡āŽ° āŽĒā¯ŠāŽąāŽŋāŽ¯āŽŋāŽ¯āŽ˛āŽžāŽŗāŽ°ā¯āŽ•āŽŗā¯ āŽ…āŽ¤ā¯ˆ āŽŽāŽ™ā¯āŽ•āŽŗāŽžāŽ˛ā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽĩāŽ°ā¯ˆ āŽšāŽŋāŽąāŽĒā¯āŽĒāŽžāŽ•āŽšā¯ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩā¯‡āŽ˛ā¯ˆ āŽšā¯†āŽ¯ā¯āŽ•āŽŋāŽąāŽžāŽ°ā¯āŽ•āŽŗā¯. āŽŽāŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‹āŽ•ā¯āŽ•āŽŽā¯ āŽ¤āŽŋāŽąāŽ¨ā¯āŽ¤ āŽŽā¯‚āŽ˛ āŽŽā¯†āŽŠā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨ā¯†āŽąāŽŋāŽŽā¯āŽąā¯ˆ āŽĩāŽŖāŽŋāŽ• āŽ¨āŽŸā¯ˆāŽŽā¯āŽąā¯ˆāŽ•āŽŗā¯ āŽŸā¯†āŽĩāŽ˛āŽĒā¯āŽĒāŽ°ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽžāŽŠ āŽĩāŽ°ā¯āŽŽāŽžāŽŠ āŽ†āŽ¤āŽžāŽ°āŽŽāŽžāŽ• āŽŽāŽžāŽąā¯āŽĩāŽ¤ā¯āŽŽā¯, āŽšā¯āŽ°āŽŖā¯āŽŸāŽ˛ā¯ āŽŽā¯āŽ•āŽŋāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‰āŽŖā¯āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽŽāŽžāŽąā¯āŽąā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽ¤āŽŠāŽŋāŽ¯ā¯āŽ°āŽŋāŽŽā¯ˆ-āŽŽāŽ°āŽŋāŽ¯āŽžāŽ¤ā¯ˆāŽ•ā¯āŽ•ā¯āŽ°āŽŋāŽ¯ āŽšā¯āŽąā¯āŽąā¯āŽšā¯āŽšā¯‚āŽ´āŽ˛ā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽĩāŽ¤ā¯āŽŽā¯ āŽ†āŽ•ā¯āŽŽā¯.", - "purchase_panel_info_2": "āŽĒ❇āŽĩāŽžāŽ˛ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽžāŽŽāŽ˛ā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽŽā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤āŽžāŽ˛ā¯, āŽ‡āŽ¨ā¯āŽ¤ āŽ•ā¯ŠāŽŗā¯āŽŽā¯āŽ¤āŽ˛ā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛ā¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽžāŽ¤ā¯. āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽŠā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽĩāŽŗāŽ°ā¯āŽšā¯āŽšāŽŋāŽ¯ā¯ˆ āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯‹āŽŠā¯āŽą āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗā¯‹āŽŽā¯.", + "purchase_panel_info_2": "āŽĒ❇āŽĩāŽžāŽ˛ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽžāŽŽāŽ˛ā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽŋāŽ˛ā¯ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽąā¯āŽ¤āŽŋāŽ¯āŽžāŽ• āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤āŽžāŽ˛ā¯, āŽ‡āŽ¨ā¯āŽ¤ āŽ•ā¯ŠāŽŗā¯āŽŽā¯āŽ¤āŽ˛ā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛ā¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽĩāŽ´āŽ™ā¯āŽ•āŽžāŽ¤ā¯. āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽŠā¯ āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽĩāŽŗāŽ°ā¯āŽšā¯āŽšāŽŋāŽ¯ā¯ˆ āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯‹āŽŠā¯āŽą āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆ āŽ¨āŽžāŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŽā¯āŽĒāŽŋāŽ¯ā¯āŽŗā¯āŽŗā¯‹āŽŽā¯.", "purchase_panel_title": "āŽ¤āŽŋāŽŸā¯āŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ†āŽ¤āŽ°āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "purchase_per_server": "āŽ’āŽ°ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯", "purchase_per_user": "āŽ’āŽ°ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•ā¯āŽ•ā¯", @@ -983,19 +1585,28 @@ "purchase_server_description_2": "āŽ†āŽ¤āŽ°āŽĩāŽžāŽŗāŽ°ā¯ āŽ¨āŽŋāŽ˛ā¯ˆ", "purchase_server_title": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯", "purchase_settings_server_activated": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽ¤āŽ¯āŽžāŽ°āŽŋāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽšā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯āŽžāŽ˛ā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "query_asset_id": "āŽĩāŽŋāŽŠāŽĩāŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽŽā¯", + "queue_status": "āŽĩāŽ°āŽŋāŽšā¯ˆ {count}/{total}", "rating": "āŽ¨āŽŸā¯āŽšāŽ¤ā¯āŽ¤āŽŋāŽ° āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯€āŽŸā¯", "rating_clear": "āŽ¤ā¯†āŽŗāŽŋāŽĩāŽžāŽŠ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯€āŽŸā¯", "rating_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽŸā¯āŽšāŽ¤ā¯āŽ¤āŽŋāŽ°āŽŽā¯} āŽŽāŽąā¯āŽą {# āŽ¨āŽŸā¯āŽšāŽ¤ā¯āŽ¤āŽŋāŽ°āŽ™ā¯āŽ•āŽŗā¯}}", "rating_description": "āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽ•ā¯āŽ´ā¯āŽĩāŽŋāŽ˛ā¯ EXIF āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯€āŽŸā¯āŽŸā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽĒāŽŋ", "reaction_options": "āŽŽāŽ¤āŽŋāŽ°ā¯āŽĩāŽŋāŽŠā¯ˆ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "read_changelog": "āŽšā¯‡āŽžā¯āŽšā¯āŽ˛āŽžāŽ•ā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", - "reassign": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "readonly_mode_disabled": "āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "readonly_mode_enabled": "āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽŽāŽŸā¯āŽŸā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽŽā¯āŽąā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "ready_for_upload": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽ¤ā¯ āŽ¤āŽ¯āŽžāŽ°ā¯", + "reassign": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯", "reassigned_assets_to_existing_person": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽĒā¯†āŽąā¯āŽ¨āŽ°ā¯ {āŽĒā¯†āŽ¯āŽ°ā¯āŽ•ā¯āŽ•ā¯, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯, āŽšā¯āŽ´āŽŋāŽ¯ {an existing person} āŽĒāŽŋāŽą {{name}}}", "reassigned_assets_to_new_person": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}} āŽ’āŽ°ā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¨āŽĒāŽ°ā¯āŽ•ā¯āŽ•ā¯", "reassing_hint": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽāŽąā¯āŽ•āŽŠāŽĩ❇ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ¨āŽĒāŽ°ā¯āŽ•ā¯āŽ•ā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", "recent": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛", "recent-albums": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", "recent_searches": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽ¤ā¯‡āŽŸāŽ˛ā¯āŽ•āŽŗā¯", + "recently_added": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "recently_added_page_title": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "recently_taken": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "recently_taken_page_title": "āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽŽāŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "refresh": "āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒ❁", "refresh_encoded_videos": "āŽ•ā¯āŽąāŽŋāŽ¯āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "refresh_faces": "āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -1007,6 +1618,9 @@ "refreshing_faces": "āŽĒā¯āŽ¤ā¯āŽ¤ā¯āŽŖāŽ°ā¯āŽšā¯āŽšāŽŋāŽ¯ā¯‚āŽŸā¯āŽŸā¯āŽŽā¯ āŽŽā¯āŽ•āŽ™ā¯āŽ•āŽŗā¯", "refreshing_metadata": "āŽĒā¯āŽ¤ā¯āŽ¤ā¯āŽŖāŽ°ā¯āŽšā¯āŽšāŽŋāŽ¯ā¯‚āŽŸā¯āŽŸā¯āŽŽā¯ āŽŽā¯‡āŽŠāŽŋāŽ˛ā¯ˆ āŽ¤āŽ°āŽĩ❁", "regenerating_thumbnails": "āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "remote": "āŽ¤ā¯ŠāŽ˛ā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆ", + "remote_assets": "āŽ¤ā¯ŠāŽ˛ā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "remote_media_summary": "āŽ¤ā¯ŠāŽ˛ā¯ˆ āŽŠāŽŸāŽ• āŽšā¯āŽ°ā¯āŽ•ā¯āŽ•āŽŽā¯", "remove": "āŽ…āŽ•āŽąā¯āŽąā¯", "remove_assets_album_confirmation": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯} your āŽ āŽ…āŽ•āŽąā¯āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "remove_assets_shared_link_confirmation": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯} your āŽ āŽ…āŽ•āŽąā¯āŽą āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", @@ -1014,14 +1628,23 @@ "remove_custom_date_range": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽ°āŽŽā¯āŽĒ❈ āŽ…āŽ•āŽąā¯āŽąā¯", "remove_deleted_assets": "āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ…āŽ•āŽąā¯āŽąāŽĩā¯āŽŽā¯", "remove_from_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_from_album_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "remove_from_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_from_lock_folder_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "remove_from_locked_folder": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_from_locked_folder_confirmation": "āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽąā¯ˆāŽ¯āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ…āŽĩ❈ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¤ā¯†āŽ°āŽŋāŽ¯ā¯āŽŽā¯.", "remove_from_shared_link": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_memory": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_photo_from_memory": "āŽ‡āŽ¨ā¯āŽ¤ āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", + "remove_tag": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", "remove_url": "āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ āŽ…āŽ•āŽąā¯āŽąā¯", "remove_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", "removed_api_key": "āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆ: {name}", "removed_from_archive": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "removed_from_favorites": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "removed_from_favorites_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ #}} āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "removed_memory": "āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽ•āŽŽā¯", + "removed_photo_from_memory": "āŽ¨āŽŋāŽŠā¯ˆāŽĩāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽ•āŽąā¯āŽąāŽŋāŽ¯āŽ¤ā¯", "removed_tagged_assets": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯} āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯", "rename": "āŽŽāŽąā¯āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "repair": "āŽĒāŽ´ā¯āŽ¤ā¯", @@ -1030,27 +1653,41 @@ "repository": "āŽ•āŽŗāŽžā¯āŽšāŽŋāŽ¯āŽŽā¯", "require_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¤ā¯‡āŽĩ❈", "require_user_to_change_password_on_first_login": "āŽŽā¯āŽ¤āŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩāŽŋāŽ˛ā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¤ā¯‡āŽĩ❈", + "rescan": "āŽ°ā¯†āŽšā¯āŽ•āŽžāŽŠā¯", "reset": "āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆ", "reset_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "reset_people_visibility": "āŽŽāŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "reset_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "reset_pin_code_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽāŽąāŽ¨ā¯āŽ¤ā¯āŽĩāŽŋāŽŸā¯āŽŸāŽžāŽ˛ā¯, āŽ…āŽ¤ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯ā¯ˆ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒ❁ āŽ•ā¯ŠāŽŗā¯āŽŗāŽ˛āŽžāŽŽā¯", + "reset_pin_code_success": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "reset_pin_code_with_password": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽāŽĒā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽ˛āŽžāŽŽā¯", + "reset_sqlite": "SQLite āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "reset_sqlite_confirmation": "SQLITE āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ¤āŽ°āŽĩ❈ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ• āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąāŽŋ āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯", + "reset_sqlite_success": "SQLITE āŽ¤āŽ°āŽĩā¯āŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽĩā¯†āŽąā¯āŽąāŽŋāŽ•āŽ°āŽŽāŽžāŽ• āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "reset_to_default": "āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆāŽ•ā¯āŽ•ā¯ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "resolve_duplicates": "āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "resolved_all_duplicates": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯€āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯", "restore": "āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆ", "restore_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "restore_trash_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "restore_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "restored_asset": "āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯", "resume": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯", + "resume_paused_jobs": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯‡āŽ˛ā¯ˆ} āŽŽāŽąā¯āŽą {# āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩā¯‡āŽ˛ā¯ˆāŽ•āŽŗā¯}}", "retry_upload": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "review_duplicates": "āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒāŽžāŽ¯ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "review_large_files": "āŽĒā¯†āŽ°āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽŽāŽ¤āŽŋāŽĒā¯āŽĒāŽžāŽ¯ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", "role": "āŽĒāŽ™ā¯āŽ•ā¯", "role_editor": "āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤āŽŋ", "role_viewer": "āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯", + "running": "āŽ‡āŽ¯āŽ™ā¯āŽ•ā¯āŽŽā¯", "save": "āŽšā¯‡āŽŽāŽŋ", + "save_to_gallery": "āŽ•ā¯‡āŽ˛āŽ°āŽŋāŽ¯āŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "saved_api_key": "āŽšā¯‡āŽŽāŽŋāŽ¤ā¯āŽ¤ āŽĒāŽ¨āŽŋāŽ‡ āŽĩāŽŋāŽšā¯ˆ", "saved_profile": "āŽšā¯‡āŽŽāŽŋāŽ¤ā¯āŽ¤ āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽŽā¯", "saved_settings": "āŽšā¯‡āŽŽāŽŋāŽ¤ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", "say_something": "āŽāŽ¤āŽžāŽĩāŽ¤ā¯ āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", + "scaffold_body_error_occurred": "āŽĒāŽŋāŽ´ā¯ˆ āŽāŽąā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "scan_all_libraries": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨ā¯‚āŽ˛āŽ•āŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽšā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", "scan_library": "āŽšā¯āŽ•ā¯‡āŽŠā¯", "scan_settings": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽšā¯āŽ•ā¯‡āŽŠā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", @@ -1058,20 +1695,53 @@ "search": "āŽ¤ā¯‡āŽŸāŽ˛ā¯", "search_albums": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "search_by_context": "āŽšā¯‚āŽ´āŽ˛āŽžāŽ˛ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_by_description": "āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_by_description_example": "āŽšāŽĒā¯āŽĒāŽžāŽĩāŽŋāŽ˛ā¯ āŽ¨āŽŸā¯ˆāŽĒāŽ¯āŽŖāŽŽā¯", "search_by_filename": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ¨ā¯€āŽŸā¯āŽŸāŽŋāŽĒā¯āŽĒ❁ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "search_by_filename_example": "I.E. IMG_1234.JPG āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ PNG", "search_camera_make": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ•ā¯‡āŽŽāŽ°āŽž āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯ ...", "search_camera_model": "āŽ•ā¯‡āŽŽāŽ°āŽž āŽŽāŽžāŽ¤āŽŋāŽ°āŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", "search_city": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ¨āŽ•āŽ°āŽŽā¯ ...", "search_country": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ¨āŽžāŽŸā¯ ...", + "search_filter_apply": "āŽĩāŽŸāŽŋāŽ•āŽŸā¯āŽŸāŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_filter_camera_title": "āŽ•ā¯‡āŽŽāŽ°āŽž āŽĩāŽ•ā¯ˆāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "search_filter_date": "āŽ¤āŽŋāŽ•āŽ¤āŽŋ", + "search_filter_date_interval": "{start} āŽĒā¯†āŽąā¯āŽ¨āŽ°ā¯ {end}", + "search_filter_date_title": "āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽ°āŽŽā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "search_filter_display_option_not_in_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "search_filter_display_options": "āŽ•āŽžāŽŸā¯āŽšāŽŋ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "search_filter_filename": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĒā¯†āŽ¯āŽ°ā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_filter_location": "āŽ‡āŽŸāŽŽā¯", + "search_filter_location_title": "āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "search_filter_media_type": "āŽŠāŽŸāŽ• āŽĩāŽ•ā¯ˆ", + "search_filter_media_type_title": "āŽŽā¯€āŽŸāŽŋāŽ¯āŽž āŽĩāŽ•ā¯ˆāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "search_filter_people_title": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "search_for": "āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "search_for_existing_person": "āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ¨āŽĒāŽ°ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_no_more_result": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "search_no_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", "search_no_people_named": "\"{name}\" āŽŽāŽŠā¯āŽąā¯ āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩāŽ°ā¯āŽ•āŽŗā¯ āŽ¯āŽžāŽ°ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "search_no_result": "āŽŽā¯āŽŸāŽŋāŽĩā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ, āŽĩā¯‡āŽąā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ•āŽžāŽ˛āŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ•āŽ˛āŽĩā¯ˆāŽ¯ā¯ˆ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "search_options": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "search_page_categories": "āŽĩāŽ•ā¯ˆāŽ•āŽŗā¯", + "search_page_motion_photos": "āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "search_page_no_objects": "āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "search_page_no_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "search_page_screenshots": "āŽ¤āŽŋāŽ°ā¯ˆāŽ•ā¯āŽ•āŽžāŽŸā¯āŽšāŽŋāŽ•āŽŗā¯", + "search_page_search_photos_videos": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_page_selfies": "āŽšā¯†āŽ˛ā¯āŽƒāŽĒāŽŋāŽ•āŽŗā¯", + "search_page_things": "āŽĩāŽŋāŽšāŽ¯āŽ™ā¯āŽ•āŽŗā¯", + "search_page_view_all_button": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŖā¯āŽ•", + "search_page_your_activity": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯", + "search_page_your_map": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽ°ā¯ˆāŽĒāŽŸāŽŽā¯", "search_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "search_places": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", + "search_rating": "āŽŽāŽ¤āŽŋāŽĒā¯āŽĒā¯€āŽŸā¯āŽŸāŽŋāŽŠā¯ āŽŽā¯‚āŽ˛āŽŽā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", + "search_result_page_new_search_hint": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯", "search_settings": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯", "search_state": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ¨āŽŋāŽ˛ā¯ˆ ...", + "search_suggestion_list_smart_search_hint_1": "āŽŽā¯†āŽŸā¯āŽŸāŽžāŽŸā¯‡āŽŸā¯āŽŸāŽžāŽĩā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸ, āŽ¨āŽŋāŽ•āŽ´ā¯āŽĩ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤, āŽ…āŽąāŽŋāŽĩā¯āŽŗā¯āŽŗ āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽžāŽ• āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯ ", + "search_suggestion_list_smart_search_hint_2": "āŽŽāŽŽā¯: āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤ā¯‡āŽŸāŽ˛ā¯-āŽ•āŽžāŽ˛āŽ¨āŽŋāŽ˛ā¯ˆ", "search_tags": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽąā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", "search_timezone": "āŽ¨ā¯‡āŽ° āŽŽāŽŖā¯āŽŸāŽ˛āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯ ...", "search_type": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽĩāŽ•ā¯ˆ", @@ -1079,9 +1749,11 @@ "searching_locales": "āŽ‡āŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯ ...", "second": "āŽ‡āŽ°āŽŖā¯āŽŸāŽžāŽĩāŽ¤ā¯", "see_all_people": "āŽŽāŽ˛ā¯āŽ˛āŽž āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽžāŽ°ā¯āŽ™ā¯āŽ•āŽŗā¯", + "select": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯", "select_album_cover": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽ…āŽŸā¯āŽŸā¯ˆāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽšā¯†āŽ¯ā¯", "select_all_duplicates": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "select_all_in": "{āŽ•ā¯āŽ´ā¯āŽĩāŽŋāŽ˛ā¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_avatar_color": "āŽ…āŽĩāŽ¤āŽžāŽ°ā¯ āŽ¨āŽŋāŽąāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_face": "āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_featured_photo": "āŽĒāŽŋāŽ°āŽ¤ā¯āŽ¯ā¯‡āŽ• āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -1089,14 +1761,21 @@ "select_keep_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_library_owner": "āŽ¨ā¯‚āŽ˛āŽ• āŽ‰āŽ°āŽŋāŽŽā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_new_face": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽŽā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "select_person_to_tag": "āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ• āŽ’āŽ°ā¯ āŽ¨āŽĒāŽ°ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "select_trash_all": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ¯ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "select_user_for_sharing_page_err_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽ¤ā¯ āŽ¤āŽĩāŽąāŽŋāŽĩāŽŋāŽŸā¯āŽŸāŽ¤ā¯", "selected": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "selected_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {# āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ}}", + "selected_gps_coordinates": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšāŽŋ.āŽĒāŽŋ.āŽŽāŽšā¯ āŽ†āŽ¯āŽ¤ā¯āŽ¤ā¯ŠāŽ˛ā¯ˆāŽĩā¯āŽ•āŽŗā¯", "send_message": "āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽ…āŽŠā¯āŽĒā¯āŽĒāŽĩā¯āŽŽā¯", "send_welcome_email": "āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽĒ❁ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒāŽĩā¯āŽŽā¯", + "server_endpoint": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽ‡āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒā¯āŽŗā¯āŽŗāŽŋ", + "server_info_box_app_version": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁", + "server_info_box_server_url": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", "server_offline": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒāŽŋāŽ˛ā¯āŽ˛āŽžāŽ¤", "server_online": "āŽ†āŽŠā¯āŽ˛ā¯ˆāŽŠāŽŋāŽ˛ā¯ āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽŽā¯", + "server_privacy": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽ¤āŽŠāŽŋāŽ¯ā¯āŽ°āŽŋāŽŽā¯ˆ", "server_stats": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽĒā¯āŽŗā¯āŽŗāŽŋāŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯", "server_version": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁", "set": "āŽ•āŽŖāŽŽā¯", @@ -1106,21 +1785,96 @@ "set_date_of_birth": "āŽĒāŽŋāŽąāŽ¨ā¯āŽ¤ āŽ¤ā¯‡āŽ¤āŽŋāŽ¯ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "set_profile_picture": "āŽšā¯āŽ¯āŽĩāŽŋāŽĩāŽ°āŽĒā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "set_slideshow_to_fullscreen": "āŽšā¯āŽ˛ā¯ˆāŽŸā¯āŽšā¯‹āŽĩ❈ āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ•ā¯āŽ•ā¯ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "set_stack_primary_asset": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽšā¯ŠāŽ¤ā¯āŽ¤āŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "setting_image_viewer_help": "āŽĩāŽŋāŽĩāŽ°āŽŽā¯ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ āŽŽā¯āŽ¤āŽ˛āŽŋāŽ˛ā¯ āŽšāŽŋāŽąāŽŋāŽ¯ āŽšāŽŋāŽąā¯ āŽ‰āŽ°ā¯āŽĩāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽžāŽ°ā¯, āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯ āŽ¨āŽŸā¯āŽ¤ā¯āŽ¤āŽ° āŽ…āŽŗāŽĩāŽŋāŽ˛āŽžāŽŠ āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽžāŽ°ā¯ (āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯), āŽ‡āŽąā¯āŽ¤āŽŋāŽ¯āŽžāŽ• āŽ…āŽšāŽ˛ā¯ˆ āŽāŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯ (āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ˛ā¯).", + "setting_image_viewer_original_subtitle": "āŽ…āŽšāŽ˛ā¯ āŽŽā¯āŽ´ā¯ āŽ¤ā¯†āŽŗāŽŋāŽĩā¯āŽ¤ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąāŽĩā¯āŽŽā¯ (āŽĒā¯†āŽ°āŽŋāŽ¯āŽ¤ā¯!). āŽ¤āŽ°āŽĩ❁ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽ•ā¯ āŽ•ā¯āŽąā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽ•ā¯āŽ•ā¯ (āŽĒāŽŋāŽŖā¯ˆāŽ¯āŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽšāŽžāŽ¤āŽŠ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ• āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒ❁ āŽ‡āŽ°āŽŖā¯āŽŸā¯āŽŽā¯).", + "setting_image_viewer_original_title": "āŽ…āŽšāŽ˛ā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "setting_image_viewer_preview_subtitle": "āŽ¨āŽŸā¯āŽ¤ā¯āŽ¤āŽ°-āŽ¤ā¯†āŽŗāŽŋāŽĩā¯āŽ¤ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąāŽĩā¯āŽŽā¯. āŽ…āŽšāŽ˛ā¯ˆ āŽ¨ā¯‡āŽ°āŽŸāŽŋāŽ¯āŽžāŽ• āŽāŽąā¯āŽą āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽšāŽŋāŽąā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯.", + "setting_image_viewer_preview_title": "āŽŽā¯āŽŠā¯āŽŠā¯‹āŽŸā¯āŽŸāŽŽā¯ āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "setting_image_viewer_title": "āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "setting_languages_apply": "āŽ‡āŽŸā¯", + "setting_languages_subtitle": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽŽā¯ŠāŽ´āŽŋāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", + "setting_notifications_notify_failures_grace_period": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ•āŽŗā¯ˆ āŽ…āŽąāŽŋāŽĩāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯: {duration}", + "setting_notifications_notify_hours": "{count} āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽŽā¯", + "setting_notifications_notify_immediately": "āŽ‰āŽŸāŽŠāŽŸāŽŋāŽ¯āŽžāŽ•", + "setting_notifications_notify_minutes": "{count} āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "setting_notifications_notify_never": "āŽ’āŽ°ā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯", + "setting_notifications_notify_seconds": "{count} āŽĩāŽŋāŽ¨āŽžāŽŸāŽŋāŽ•āŽŗā¯", + "setting_notifications_single_progress_subtitle": "āŽĩāŽŋāŽ°āŽŋāŽĩāŽžāŽŠ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽŽā¯ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽą āŽšā¯†āŽ¯ā¯āŽ¤āŽŋ āŽ’āŽ°ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•ā¯", + "setting_notifications_single_progress_title": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽĩāŽŋāŽĩāŽ°āŽŽā¯ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "setting_notifications_subtitle": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "setting_notifications_total_progress_subtitle": "āŽ’āŽŸā¯āŽŸā¯āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽŽā¯ (āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯/āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯)", + "setting_notifications_total_progress_title": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "setting_video_viewer_looping_title": "āŽ˛ā¯‚āŽĒā¯āŽĒāŽŋāŽ™ā¯", + "setting_video_viewer_original_video_subtitle": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ’āŽ°ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽĩ❈ āŽšā¯āŽŸā¯āŽ°ā¯€āŽŽāŽŋāŽ™ā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯, āŽ’āŽ°ā¯ āŽŸāŽŋāŽ°āŽžāŽŠā¯āŽšā¯āŽ•ā¯‹āŽŸā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ•ā¯‚āŽŸ āŽ…āŽšāŽ˛ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. āŽ‡āŽŸā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽĩāŽ´āŽŋāŽĩāŽ•ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒā¯ŠāŽ°ā¯āŽŸā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽžāŽŽāŽ˛ā¯ āŽ‰āŽŗā¯āŽ¨āŽžāŽŸā¯āŽŸāŽŋāŽ˛ā¯ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽ…āŽšāŽ˛ā¯ āŽ¤āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽŠā¯āŽąāŽŠ.", + "setting_video_viewer_original_video_title": "āŽ…āŽšāŽ˛ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽĩ❈ āŽ•āŽŸā¯āŽŸāŽžāŽ¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", "settings": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "settings_require_restart": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽ‡āŽŽā¯āŽŽāŽŋāŽ¯ā¯ˆ āŽŽāŽąā¯āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", "settings_saved": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŠ", + "setup_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "share": "āŽĒāŽ™ā¯āŽ•ā¯", + "share_action_prompt": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ {count} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", + "share_add_photos": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "share_assets_selected": "{count} āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "share_dialog_preparing": "āŽ¤āŽ¯āŽžāŽ°āŽŋāŽ¤ā¯āŽ¤āŽ˛ā¯ ...", + "share_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ•āŽŋāŽ°āŽĩā¯āŽŽā¯", "shared": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "shared_album_activities_input_disable": "āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", + "shared_album_activity_remove_content": "āŽ‡āŽ¨ā¯āŽ¤āŽšā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "shared_album_activity_remove_title": "āŽšā¯†āŽ¯āŽ˛ā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "shared_album_section_people_action_error": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡āŽąā¯āŽ¤āŽ˛ā¯/āŽ¨ā¯€āŽ•ā¯āŽ•ā¯āŽ¤āŽ˛ā¯", + "shared_album_section_people_action_leave": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", + "shared_album_section_people_action_remove_user": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ˆ āŽ…āŽ•āŽąā¯āŽąā¯", + "shared_album_section_people_title": "āŽŽāŽ•ā¯āŽ•āŽŗā¯", "shared_by": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "shared_by_user": "{āŽĒāŽ¯āŽŠāŽ°āŽžāŽ˛ā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "shared_by_you": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŖā¯āŽŸāŽžāŽ°ā¯", "shared_from_partner": "{partner} āŽ‡āŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "shared_intent_upload_button_progress_text": "{current} / {total} āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "shared_link_app_bar_title": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "shared_link_clipboard_copied_massage": "āŽ‡āŽŸā¯ˆāŽ¨āŽŋāŽ˛ā¯ˆāŽĒā¯āŽĒāŽ˛āŽ•ā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "shared_link_clipboard_text": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁: {link} \nāŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯: {password}", + "shared_link_create_error": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽĒā¯‹āŽ¤ā¯ āŽĒāŽŋāŽ´ā¯ˆ", + "shared_link_custom_url_description": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽ‰āŽŸāŽŠā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ…āŽŖā¯āŽ•āŽĩā¯āŽŽā¯", + "shared_link_edit_description_hint": "āŽĒāŽ™ā¯āŽ•ā¯ āŽĩāŽŋāŽŗāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "shared_link_edit_expire_after_option_day": "1 āŽ¨āŽžāŽŗā¯", + "shared_link_edit_expire_after_option_days": "{count} āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯", + "shared_link_edit_expire_after_option_hour": "1 āŽŽāŽŖāŽŋ āŽ¨ā¯‡āŽ°āŽŽā¯", + "shared_link_edit_expire_after_option_hours": "{count} āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽŽā¯", + "shared_link_edit_expire_after_option_minute": "1 āŽŽāŽŖāŽŋāŽ¤ā¯āŽ¤ā¯āŽŗāŽŋ", + "shared_link_edit_expire_after_option_minutes": "{count} āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗā¯", + "shared_link_edit_expire_after_option_months": "{count} āŽŽāŽžāŽ¤āŽ™ā¯āŽ•āŽŗā¯", + "shared_link_edit_expire_after_option_year": "{count} āŽ†āŽŖā¯āŽŸā¯", + "shared_link_edit_password_hint": "āŽĒāŽ•āŽŋāŽ°ā¯āŽĩ❁ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", + "shared_link_edit_submit_button": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "shared_link_error_server_url_fetch": "āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ• āŽŽā¯āŽ•āŽĩāŽ°āŽŋ āŽāŽĒā¯ āŽĒā¯†āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯", + "shared_link_expires_day": "{count} āŽ¨āŽžāŽŗā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_days": "{count} āŽ¨āŽžāŽŸā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_hour": "{count} āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_hours": "{count} āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_minute": "{count} āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_minutes": "{count} āŽ¨āŽŋāŽŽāŽŋāŽŸāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_never": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_second": "{count} āŽ‡āŽ°āŽŖā¯āŽŸāŽžāŽĩāŽ¤āŽžāŽ• āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_expires_seconds": "{count} āŽĩāŽŋāŽ¨āŽžāŽŸāŽŋāŽ•āŽŗāŽŋāŽ˛ā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋāŽ¯āŽžāŽ•āŽŋāŽąāŽ¤ā¯", + "shared_link_individual_shared": "āŽ¤āŽŠāŽŋāŽ¨āŽĒāŽ°ā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩāŽ°ā¯", + "shared_link_info_chip_metadata": "Exif", + "shared_link_manage_links": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "shared_link_options": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "shared_link_password_description": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ…āŽŖā¯āŽ• āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¤ā¯‡āŽĩ❈", "shared_links": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯", + "shared_links_description": "āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°āŽĩā¯āŽŽā¯", "shared_photos_and_videos_count": "{ASSETCOUNT, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {# āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯.}}", + "shared_with_me": "āŽŽāŽŠā¯āŽŠā¯āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "shared_with_partner": "{āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ with āŽ‰āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "sharing": "āŽĒāŽ•āŽŋāŽ°ā¯āŽĩ❁", "sharing_enter_password": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯.", + "sharing_page_album": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯", + "sharing_page_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯†āŽŸā¯āŽĩā¯ŠāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽĩāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "sharing_page_empty_list": "āŽĩā¯†āŽąā¯āŽąā¯ āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛ā¯", "sharing_sidebar_description": "āŽĒāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩāŽ¤āŽąā¯āŽ•āŽžāŽŠ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽĒāŽŋ", + "sharing_silver_appbar_create_shared_album": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", + "sharing_silver_appbar_share_partner": "āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯āŽŸāŽŠā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ•ā¯ŠāŽŗā¯āŽŗā¯āŽ™ā¯āŽ•āŽŗā¯", "shift_to_permanent_delete": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ• ⇧ āŽ āŽ…āŽ´ā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "show_album_options": "āŽ†āŽ˛ā¯āŽĒāŽŽā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "show_albums": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", @@ -1138,9 +1892,11 @@ "show_person_options": "āŽ¨āŽĒāŽ°ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "show_progress_bar": "āŽŽā¯āŽŠā¯āŽŠā¯‡āŽąā¯āŽąāŽĒā¯ āŽĒāŽŸā¯āŽŸāŽŋāŽ¯ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "show_search_options": "āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "show_shared_links": "āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "show_slideshow_transition": "āŽšā¯āŽ˛ā¯ˆāŽŸā¯āŽšā¯‹ āŽŽāŽžāŽąā¯āŽąāŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "show_supporter_badge": "āŽ†āŽ¤āŽ°āŽĩāŽžāŽŗāŽ°ā¯ āŽ’āŽŸā¯āŽŸā¯", "show_supporter_badge_description": "āŽ’āŽ°ā¯ āŽ†āŽ¤āŽ°āŽĩāŽžāŽŗāŽ°ā¯ āŽĒā¯‡āŽŸā¯āŽšā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "show_text_search_menu": "āŽ‰āŽ°ā¯ˆ āŽ¤ā¯‡āŽŸāŽ˛ā¯ āŽŽā¯†āŽŠā¯āŽĩā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "shuffle": "āŽ•āŽ˛āŽ•ā¯āŽ•ā¯", "sidebar": "āŽĒāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋ", "sidebar_display_description": "āŽĒāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽŋāŽ˛ā¯ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ•ā¯āŽ•ā¯ āŽ’āŽ°ā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽĒāŽŋ", @@ -1156,12 +1912,14 @@ "sort_created": "āŽ¤ā¯‡āŽ¤āŽŋ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "sort_items": "āŽĒā¯ŠāŽ°ā¯āŽŸā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ", "sort_modified": "āŽ¤ā¯‡āŽ¤āŽŋ āŽŽāŽžāŽąā¯āŽąāŽŋāŽ¯āŽŽā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", + "sort_newest": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", "sort_oldest": "āŽĒāŽ´āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", "sort_people_by_similarity": "āŽ’āŽąā¯āŽąā¯āŽŽā¯ˆāŽ¯āŽžāŽ˛ā¯ āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽĩāŽ°āŽŋāŽšā¯ˆāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", "sort_recent": "āŽŽāŽŋāŽ• āŽ…āŽŖā¯āŽŽā¯ˆāŽ•ā¯ āŽ•āŽžāŽ˛ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯", "sort_title": "āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒ❁", "source": "āŽŽā¯‚āŽ˛āŽŽā¯", "stack": "āŽ…āŽŸā¯āŽ•ā¯āŽ•ā¯", + "stack_action_prompt": "{count} āŽ…āŽŸā¯āŽ•ā¯āŽ•āŽŋ āŽĩā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯", "stack_duplicates": "āŽ…āŽŸā¯āŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯", "stack_select_one_photo": "āŽ…āŽŸā¯āŽ•ā¯āŽ•ā¯āŽ•ā¯āŽ•ā¯ āŽ’āŽ°ā¯ āŽŽā¯āŽ•ā¯āŽ•āŽŋāŽ¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "stack_selected_photos": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŸā¯āŽ•ā¯āŽ•āŽŋ āŽĩā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -1169,16 +1927,20 @@ "stacktrace": "āŽšā¯āŽŸāŽžāŽ•ā¯ āŽŸā¯āŽ°ā¯‡āŽšā¯", "start": "āŽ¤ā¯ŠāŽŸāŽ™ā¯āŽ•ā¯", "start_date": "āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ• āŽ¤ā¯‡āŽ¤āŽŋ", + "start_date_before_end_date": "āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ• āŽ¤ā¯‡āŽ¤āŽŋ āŽ‡āŽąā¯āŽ¤āŽŋ āŽ¤ā¯‡āŽ¤āŽŋāŽ•ā¯āŽ•ā¯ āŽŽā¯āŽŠā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯", "state": "āŽŽāŽžāŽ¨āŽŋāŽ˛āŽŽā¯", "status": "āŽ¨āŽŋāŽ˛ā¯ˆ", + "stop_casting": "āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯ˆ āŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", "stop_motion_photo": "āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤ā¯", "stop_photo_sharing": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩāŽ¤ā¯ˆ āŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤āŽĩāŽž?", "stop_photo_sharing_description": "{āŽ•ā¯‚āŽŸā¯āŽŸāŽžāŽŗāŽ°ā¯ your āŽ‡āŽŠāŽŋ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ…āŽŖā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯.", "stop_sharing_photos_with_user": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ¯āŽŠāŽ°ā¯āŽŸāŽŠā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ•āŽŋāŽ°ā¯āŽĩāŽ¤ā¯ˆ āŽ¨āŽŋāŽąā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", "storage": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ‡āŽŸāŽŽā¯", "storage_label": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽšāŽŋāŽŸā¯āŽŸā¯ˆ", + "storage_quota": "āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯€āŽŸā¯", "storage_usage": "{used} āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯", "submit": "āŽšāŽŽāŽ°ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "success": "āŽšā¯†āŽ¯ā¯", "suggestions": "āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•āŽŗā¯", "sunrise_on_the_beach": "āŽ•āŽŸāŽąā¯āŽ•āŽ°ā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯‚āŽ°āŽŋāŽ¯ āŽ¤ā¯‹āŽŠā¯āŽąā¯āŽ•ā¯ˆ", "support": "āŽ‰āŽ¤āŽĩāŽŋ", @@ -1186,18 +1948,40 @@ "support_third_party_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽ˛ā¯ āŽŽā¯‚āŽŠā¯āŽąāŽžāŽŽā¯ āŽ¤āŽ°āŽĒā¯āŽĒāŽŋāŽŠāŽ°āŽžāŽ˛ā¯ āŽ¤ā¯ŠāŽ•ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯. āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŠā¯āŽĒāŽĩāŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽšāŽŋāŽ•ā¯āŽ•āŽ˛ā¯āŽ•āŽŗā¯ āŽ…āŽ¨ā¯āŽ¤ āŽ¤ā¯ŠāŽ•ā¯āŽĒā¯āŽĒāŽžāŽ˛ā¯ āŽāŽąā¯āŽĒāŽŸāŽ˛āŽžāŽŽā¯, āŽŽāŽŠāŽĩ❇ āŽ•ā¯€āŽ´ā¯‡āŽ¯ā¯āŽŗā¯āŽŗ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋ āŽŽā¯āŽ¤āŽ˛ā¯ āŽšāŽ¨ā¯āŽ¤āŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ…āŽĩāŽ°ā¯āŽ•āŽŗā¯āŽŸāŽŠā¯ āŽšāŽŋāŽ•ā¯āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆ āŽŽāŽ´ā¯āŽĒā¯āŽĒā¯āŽ™ā¯āŽ•āŽŗā¯.", "swap_merge_direction": "āŽ’āŽŠā¯āŽąāŽŋāŽŖā¯ˆāŽ•ā¯āŽ•ā¯āŽŽā¯ āŽ¤āŽŋāŽšā¯ˆāŽ¯ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "sync": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁", + "sync_albums": "āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_albums_manual_subtitle": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŋāŽ¯ āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_local": "āŽ‰āŽŗā¯āŽŗāŽ• āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_remote": "āŽ¤ā¯ŠāŽ˛ā¯ˆāŽ¤ā¯‚āŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_status": "āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯ā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_status_subtitle": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽŋāŽŸāŽĩā¯āŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "sync_upload_album_setting_subtitle": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ†āŽ˛ā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽŋ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯", "tag": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯", "tag_assets": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯", "tag_created": "āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯: {tag}", "tag_feature_description": "āŽ¤āŽ°ā¯āŽ•ā¯āŽ•āŽ°ā¯€āŽ¤āŽŋāŽ¯āŽžāŽŠ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¤āŽ˛ā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗāŽžāŽ˛ā¯ āŽ¤ā¯ŠāŽ•ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ˆ āŽ‰āŽ˛āŽžāŽĩā¯āŽ¤āŽ˛ā¯", "tag_not_found_question": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆāŽ•ā¯ āŽ•āŽŖā¯āŽŸā¯āŽĒāŽŋāŽŸāŽŋāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆāŽ¯āŽž? <āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁> āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯. ", + "tag_people": "āŽŽāŽ•ā¯āŽ•āŽŗā¯ˆ āŽ•ā¯āŽąāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "tag_updated": "āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯ŠāŽ˛ā¯: {tag}", "tagged_assets": "āŽ•ā¯āŽąāŽŋāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗāŽžāŽ°ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽŽāŽąā¯āŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", "tags": "āŽ•ā¯āŽąāŽŋāŽšā¯āŽšā¯†āŽžāŽąā¯āŽ•āŽŗā¯", + "tap_to_run_job": "āŽĩā¯‡āŽ˛ā¯ˆāŽ¯ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ• āŽ¤āŽŸā¯āŽŸāŽĩā¯āŽŽā¯", "template": "āŽĩāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ°ā¯", "theme": "āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯", "theme_selection": "āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩ❁", "theme_selection_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽ˛āŽžāŽĩāŽŋāŽ¯āŽŋāŽŠā¯ āŽ•āŽŖāŽŋāŽŠāŽŋ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽ’āŽŗāŽŋ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‡āŽ°ā¯āŽŸā¯āŽŸāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "theme_setting_asset_list_storage_indicator_title": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ āŽ“āŽŸā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ• āŽ•ā¯āŽąāŽŋāŽ•āŽžāŽŸā¯āŽŸāŽŋāŽ¯ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "theme_setting_asset_list_tiles_per_row_title": "āŽ’āŽ°ā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ ({count})", + "theme_setting_colorful_interface_subtitle": "āŽĒāŽŋāŽŠā¯āŽŠāŽŖāŽŋ āŽŽā¯‡āŽąā¯āŽĒāŽ°āŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽĩāŽŖā¯āŽŖāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯.", + "theme_setting_colorful_interface_title": "āŽĩāŽŖā¯āŽŖāŽŽāŽ¯āŽŽāŽžāŽŠ āŽ‡āŽŸā¯ˆāŽŽā¯āŽ•āŽŽā¯", + "theme_setting_image_viewer_quality_subtitle": "āŽĩāŽŋāŽĩāŽ°āŽŽā¯ āŽĒāŽŸ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°āŽŋāŽŠā¯ āŽ¤āŽ°āŽ¤ā¯āŽ¤ā¯ˆ āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¯āŽĩā¯āŽŽā¯", + "theme_setting_image_viewer_quality_title": "āŽĒāŽŸ āŽĒāŽžāŽ°ā¯āŽĩā¯ˆāŽ¯āŽžāŽŗāŽ°ā¯ āŽ¤āŽ•ā¯āŽ¤āŽŋ", + "theme_setting_primary_color_subtitle": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽšā¯†āŽ¯āŽ˛ā¯āŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ‰āŽšā¯āŽšāŽ°āŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ’āŽ°ā¯ āŽĩāŽŖā¯āŽŖāŽ¤ā¯āŽ¤ā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ™ā¯āŽ•āŽŗā¯.", + "theme_setting_primary_color_title": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ¨āŽŋāŽąāŽŽā¯", + "theme_setting_system_primary_color_title": "āŽ•āŽŖāŽŋāŽŠāŽŋ āŽ¨āŽŋāŽąāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ™ā¯āŽ•āŽŗā¯", + "theme_setting_system_theme_switch": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•āŽŋ (āŽ•āŽŖāŽŋāŽŠāŽŋ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽŋāŽŠā¯āŽĒāŽąā¯āŽąāŽĩā¯āŽŽā¯)", + "theme_setting_theme_subtitle": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ•āŽ°ā¯āŽĒā¯āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ•", + "theme_setting_three_stage_loading_subtitle": "āŽŽā¯‚āŽŠā¯āŽąā¯-āŽ¨āŽŋāŽ˛ā¯ˆ āŽāŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ āŽāŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽŋāŽąāŽŠā¯ˆ āŽ…āŽ¤āŽŋāŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸā¯āŽŽā¯, āŽ†āŽŠāŽžāŽ˛ā¯ āŽ•āŽŖāŽŋāŽšāŽŽāŽžāŽ• āŽ…āŽ¤āŽŋāŽ• āŽĒāŽŋāŽŖā¯ˆāŽ¯ āŽšā¯āŽŽā¯ˆāŽ¯ā¯ˆ āŽāŽąā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "theme_setting_three_stage_loading_title": "āŽŽā¯‚āŽŠā¯āŽąā¯-āŽ¨āŽŋāŽ˛ā¯ˆ āŽāŽąā¯āŽąā¯āŽ¤āŽ˛ā¯ˆ āŽ‡āŽ¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "they_will_be_merged_together": "āŽ…āŽĩāŽ°ā¯āŽ•āŽŗā¯ āŽ’āŽŠā¯āŽąāŽžāŽ• āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽĩāŽžāŽ°ā¯āŽ•āŽŗā¯", "third_party_resources": "āŽŽā¯‚āŽŠā¯āŽąāŽžāŽŽā¯ āŽ¤āŽ°āŽĒā¯āŽĒ❁ āŽĩāŽŗāŽ™ā¯āŽ•āŽŗā¯", "time_based_memories": "āŽ¨ā¯‡āŽ° āŽ…āŽŸāŽŋāŽĒā¯āŽĒāŽŸā¯ˆāŽ¯āŽŋāŽ˛āŽžāŽŠ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯", @@ -1207,53 +1991,91 @@ "to_change_password": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "to_favorite": "āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤", "to_login": "āŽĒā¯āŽ•ā¯āŽĒāŽ¤āŽŋāŽĩ❁", + "to_multi_select": "āŽĒāŽ˛-āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ", "to_parent": "āŽĒā¯†āŽąā¯āŽąā¯‹āŽ°āŽŋāŽŸāŽŽā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽ™ā¯āŽ•āŽŗā¯", + "to_select": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•", "to_trash": "āŽ•ā¯āŽĒā¯āŽĒ❈", "toggle_settings": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯", "total": "āŽŽā¯ŠāŽ¤ā¯āŽ¤āŽŽā¯", "total_usage": "āŽŽā¯ŠāŽ¤ā¯āŽ¤ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯", "trash": "āŽ•ā¯āŽĒā¯āŽĒ❈", + "trash_action_prompt": "{count} āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ•ā¯āŽ•ā¯ āŽ¨āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "trash_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•ā¯āŽĒā¯āŽĒ❈", "trash_count": "āŽ•ā¯āŽĒā¯āŽĒ❈ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽŽāŽŖā¯}", "trash_delete_asset": "āŽ•ā¯āŽĒā¯āŽĒ❈/āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "trash_emptied": "āŽ•āŽžāŽ˛āŽŋāŽ¯āŽžāŽ• āŽ•ā¯āŽĒā¯āŽĒ❈", "trash_no_results_message": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ•āŽŗā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽ•ā¯āŽ•āŽŗā¯ āŽ‡āŽ™ā¯āŽ•ā¯‡ āŽ•āŽžāŽŖā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", + "trash_page_delete_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", + "trash_page_empty_trash_dialog_content": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽĒā¯āŽĒ❈ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆ āŽĩā¯†āŽąā¯āŽŽā¯ˆ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "trash_page_info": "āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ•āŽŗā¯ {days} āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯āŽĒā¯ āŽĒāŽŋāŽąāŽ•ā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯", + "trash_page_no_assets": "āŽ•ā¯āŽĒā¯āŽĒ❈ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", + "trash_page_restore_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "trash_page_select_assets_btn": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "trash_page_title": "({count})", "trashed_items_will_be_permanently_deleted_after": "{āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽžāŽŗā¯} āŽĒāŽŋāŽą {# āŽ¨āŽžāŽŸā¯āŽ•āŽŗā¯}} āŽ•ā¯āŽ•ā¯āŽĒā¯ āŽĒāŽŋāŽąāŽ•ā¯ āŽ•ā¯āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯ŠāŽŸā¯āŽŸāŽŋāŽ•āŽŗā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", + "troubleshoot": "āŽšāŽ°āŽŋāŽšā¯†āŽ¯ā¯āŽ¤āŽ˛ā¯", "type": "āŽĩāŽ•ā¯ˆ", + "unable_to_change_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "unable_to_setup_pin_code": "āŽŽā¯āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", "unarchive": "āŽ…āŽŠā¯āŽ•āŽžāŽŠā¯", + "unarchive_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "unarchived_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽĒāŽŋāŽą {āŽ…āŽ˛ā¯āŽ˛āŽžāŽ¤ #}}", + "undo": "āŽšā¯†āŽ¯āŽ˛ā¯āŽ¤āŽĩāŽŋāŽ°ā¯", "unfavorite": "āŽŽāŽžāŽąāŽžāŽ¤", + "unfavorite_action_prompt": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ the āŽĒāŽŋāŽŸāŽŋāŽ¤ā¯āŽ¤āŽĩā¯ˆāŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "unhide_person": "āŽ…āŽ°ā¯āŽĩāŽ°ā¯āŽĒā¯āŽĒāŽžāŽŠ āŽ¨āŽĒāŽ°ā¯", "unknown": "āŽ¤ā¯†āŽ°āŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ", + "unknown_country": "āŽ¤ā¯†āŽ°āŽŋāŽ¯āŽžāŽ¤ āŽ¨āŽžāŽŸā¯", "unknown_year": "āŽ¤ā¯†āŽ°āŽŋāŽ¯āŽžāŽ¤ āŽ†āŽŖā¯āŽŸā¯", "unlimited": "āŽĩāŽ°āŽŽā¯āŽĒāŽąā¯āŽąāŽ¤ā¯", "unlink_motion_video": "āŽ‡āŽ¯āŽ•ā¯āŽ• āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹āŽĩ❈ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "unlink_oauth": "OAUTH āŽ āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "unlinked_oauth_account": "āŽ‡āŽŖā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ OAUTH āŽ•āŽŖāŽ•ā¯āŽ•ā¯", + "unmute_memories": "āŽŠāŽŸā¯āŽ°ā¯āŽĩāŽ˛ā¯ āŽ¨āŽŋāŽŠā¯ˆāŽĩā¯āŽ•āŽŗā¯", "unnamed_album": "āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽ†āŽ˛ā¯āŽĒāŽŽā¯", "unnamed_album_delete_confirmation": "āŽ‡āŽ¨ā¯āŽ¤ āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "unnamed_share": "āŽĒā¯†āŽ¯āŽ°āŽŋāŽŸāŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽĒāŽ™ā¯āŽ•ā¯", "unsaved_change": "āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ āŽŽāŽžāŽąā¯āŽąāŽŽā¯", "unselect_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽĩ❁ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽ™ā¯āŽ•āŽŗā¯", "unselect_all_duplicates": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "unselect_all_in": "{group}", "unstack": "āŽ…āŽŠā¯-āŽšā¯āŽŸāŽžāŽ•ā¯", + "unstack_action_prompt": "{count} āŽ¤āŽŸā¯ˆāŽ¯āŽŋāŽŠā¯āŽąāŽŋ", "unstacked_assets_count": "āŽ…āŽŠā¯-āŽšā¯āŽŸāŽžāŽ•ā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", + "untagged": "āŽ…āŽĩāŽŋāŽ´ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤āŽ¤ā¯", "up_next": "āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ā¯", + "update_location_action_prompt": "{count} āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗāŽŋāŽŠā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽŋāŽŸāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯:", + "updated_at": "āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "updated_password": "āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯", "upload": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽŽā¯", + "upload_action_prompt": "{count} āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽĩāŽ¤āŽąā¯āŽ•ā¯ āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ¨āŽŋāŽąā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "upload_concurrency": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❈āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯", + "upload_details": "āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯", + "upload_dialog_info": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ (āŽ•āŽŗā¯ˆ) āŽšā¯‡āŽĩā¯ˆāŽ¯āŽ•āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽ•āŽžāŽĒā¯āŽĒ❁āŽĒā¯ āŽĒāŽŋāŽ°āŽ¤āŽŋ āŽŽāŽŸā¯āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", + "upload_dialog_title": "āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯", "upload_errors": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŽā¯ {āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽĒāŽŋāŽ´ā¯ˆ} āŽŽāŽąā¯āŽą {# āŽĒāŽŋāŽ´ā¯ˆāŽ•āŽŗā¯}} āŽ‰āŽŸāŽŠā¯ āŽŽā¯āŽŸāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯, āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "upload_finished": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽŽā¯ āŽŽā¯āŽŸāŽŋāŽ¨ā¯āŽ¤āŽ¤ā¯", "upload_progress": "āŽŽā¯€āŽ¤āŽŽā¯āŽŗā¯āŽŗ {āŽŽā¯€āŽ¤āŽŽā¯āŽŗā¯āŽŗ, āŽŽāŽŖā¯} - āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ {āŽšā¯†āŽ¯āŽ˛āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯, āŽŽāŽŖā¯}/{āŽŽā¯ŠāŽ¤ā¯āŽ¤āŽŽā¯, āŽŽāŽŖā¯}", "upload_skipped_duplicates": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯} āŽĒāŽŋāŽą {# āŽ¨āŽ•āŽ˛ā¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯}}", "upload_status_duplicates": "āŽ¨āŽ•āŽ˛ā¯āŽ•āŽŗā¯", "upload_status_errors": "āŽĒāŽŋāŽ´ā¯ˆāŽ•āŽŗā¯", "upload_status_uploaded": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "upload_success": "āŽĩā¯†āŽąā¯āŽąāŽŋāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąāŽĩā¯āŽŽā¯, āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽą āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯.", + "upload_to_immich": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽąā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯ ({count})", + "uploading": "āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯", + "uploading_media": "āŽŽā¯€āŽŸāŽŋāŽ¯āŽžāŽĩ❈āŽĒā¯ āŽĒāŽ¤āŽŋāŽĩā¯‡āŽąā¯āŽąā¯āŽ•āŽŋāŽąāŽ¤ā¯", "url": "āŽŽā¯āŽ•āŽĩāŽ°āŽŋ", "usage": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯", + "use_biometric": "āŽĒāŽ¯ā¯‹āŽŽā¯†āŽŸā¯āŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "use_current_connection": "āŽ¤āŽąā¯āŽĒā¯‹āŽ¤ā¯ˆāŽ¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "use_custom_date_range": "āŽ…āŽ¤āŽąā¯āŽ•ā¯ āŽĒāŽ¤āŽŋāŽ˛āŽžāŽ• āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽ¤ā¯‡āŽ¤āŽŋ āŽĩāŽ°āŽŽā¯āŽĒ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", "user": "āŽĒāŽ¯āŽŠāŽ°ā¯", + "user_has_been_deleted": "āŽ‡āŽ¨ā¯āŽ¤āŽĒā¯ āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽžāŽ°ā¯.", "user_id": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽāŽŸāŽŋ", "user_liked": "{user} āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒāŽŋāŽŠāŽžāŽ°ā¯ {āŽĩāŽ•ā¯ˆ, āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯, āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽŽā¯ {this photo} āŽĩā¯€āŽŸāŽŋāŽ¯ā¯‹ {this video} āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ {this asset} āŽĒāŽŋāŽą {it}}", + "user_pin_code_settings": "āŽĒāŽŋāŽŠā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯", + "user_pin_code_settings_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒāŽŋāŽŠā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "user_privacy": "āŽĒāŽ¯āŽŠāŽ°ā¯ āŽ¤āŽŠāŽŋāŽ¯ā¯āŽ°āŽŋāŽŽā¯ˆ", "user_purchase_settings": "āŽĩāŽžāŽ™ā¯āŽ•", "user_purchase_settings_description": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽžāŽ™ā¯āŽ•ā¯āŽ¤āŽ˛ā¯ˆ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "user_role_set": "{user} {āŽĒāŽžāŽ¤ā¯āŽ¤āŽŋāŽ°āŽŽāŽžāŽ• āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", @@ -1262,12 +2084,14 @@ "user_usage_stats_description": "āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽ‰āŽĒāŽ¯ā¯‹āŽ•āŽĒā¯ āŽĒā¯āŽŗā¯āŽŗāŽŋāŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•", "username": "āŽĒāŽ¯āŽŠāŽ°ā¯āŽĒā¯†āŽ¯āŽ°ā¯", "users": "āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯", + "users_added_to_album_count": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽĒāŽ¯āŽŠāŽ°ā¯} āŽŽāŽąā¯āŽą {# āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯}} āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "utilities": "āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯āŽ•āŽŗā¯", "validate": "āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "validate_endpoint_error": "āŽ¤āŽ¯āŽĩā¯āŽšā¯†āŽ¯ā¯āŽ¤ā¯ āŽ’āŽ°ā¯ āŽšā¯†āŽ˛ā¯āŽ˛ā¯āŽĒāŽŸāŽŋāŽ¯āŽžāŽ•ā¯āŽŽā¯ URL āŽ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯", "variables": "āŽŽāŽžāŽąāŽŋāŽ•āŽŗā¯", "version": "āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁", "version_announcement_closing": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŖā¯āŽĒāŽ°ā¯, āŽ…āŽ˛ā¯†āŽ•ā¯āŽšā¯", - "version_announcement_message": "āŽ†āŽ¯ā¯! āŽ‡āŽŽā¯āŽŽāŽŋāŽ¯āŽŋāŽŠā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽ¤āŽĩāŽąāŽžāŽŠ āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤āŽŸā¯āŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ¤ā¯āŽ¤ āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽšā¯†āŽ¯ā¯āŽ¯ <āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁> āŽĩā¯†āŽŗāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯āŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽāŽĒā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽšāŽŋāŽąāŽŋāŽ¤ā¯ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯, āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽžāŽ• āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽĩāŽąā¯āŽ•ā¯‹āŽĒā¯āŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽŠāŽžāŽ˛ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ¨āŽŋāŽ•āŽ´ā¯āŽĩ❈ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒāŽ¤ā¯ˆāŽ•ā¯ āŽ•ā¯ˆāŽ¯āŽžāŽŗā¯āŽŽā¯ āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽĒā¯ŠāŽąāŽŋāŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽŠāŽžāŽ˛ā¯.", + "version_announcement_message": "āŽĩāŽŖāŽ•ā¯āŽ•āŽŽā¯! āŽ‡āŽŽā¯āŽŽāŽŋāŽ¯āŽŋāŽŠā¯ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽ¤āŽĩāŽąāŽžāŽŠ āŽ•āŽ°ā¯āŽ¤ā¯āŽ¤ā¯āŽ•ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤āŽŸā¯āŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❁ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ¤ā¯āŽ¤ āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ‡āŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽšā¯†āŽ¯ā¯āŽ¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯āŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽāŽĒā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ• āŽšāŽŋāŽąāŽŋāŽ¤ā¯ āŽ¨ā¯‡āŽ°āŽŽā¯ āŽ’āŽ¤ā¯āŽ•ā¯āŽ•ā¯āŽ™ā¯āŽ•āŽŗā¯, āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒāŽžāŽ• āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽĩāŽąā¯āŽ•ā¯‹āŽĒā¯āŽ°āŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽŠāŽžāŽ˛ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯ āŽ¨āŽŋāŽ•āŽ´ā¯āŽĩ❈ āŽ¤āŽžāŽŠāŽžāŽ•āŽĩ❇ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽĒā¯āŽĒāŽ¤ā¯ˆāŽ•ā¯ āŽ•ā¯ˆāŽ¯āŽžāŽŗā¯āŽŽā¯ āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽĒā¯ŠāŽąāŽŋāŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽŠāŽžāŽ˛ā¯.", "version_history": "āŽĒāŽ¤āŽŋāŽĒā¯āŽĒ❁ āŽĩāŽ°āŽ˛āŽžāŽąā¯", "version_history_item": "{version} āŽ‡āŽ˛ā¯ {date} āŽ¨āŽŋāŽąā¯āŽĩāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "video": "āŽ’āŽŗāŽŋāŽ¤ā¯‹āŽąā¯āŽąāŽŽā¯", @@ -1279,21 +2103,33 @@ "view_album": "āŽ†āŽ˛ā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽ•", "view_all": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŖā¯āŽ•", "view_all_users": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽĒāŽ¯āŽŠāŽ°ā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŖā¯āŽ•", + "view_details": "āŽĩāŽŋāŽĩāŽ°āŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯", "view_in_timeline": "āŽ•āŽžāŽ˛āŽĩāŽ°āŽŋāŽšā¯ˆāŽ¯āŽŋāŽ˛ā¯ āŽ•āŽžāŽŖā¯āŽ•", + "view_link": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽ•", "view_links": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽ•", "view_name": "āŽĒāŽžāŽ°ā¯āŽĩ❈", "view_next_asset": "āŽ…āŽŸā¯āŽ¤ā¯āŽ¤ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ•āŽžāŽŖā¯āŽ•", "view_previous_asset": "āŽŽā¯āŽ¨ā¯āŽ¤ā¯ˆāŽ¯ āŽšā¯ŠāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "view_qr_code": "QR āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", + "view_similar_photos": "āŽ‡āŽ¤ā¯‡ āŽĒā¯‹āŽŠā¯āŽą āŽĒā¯āŽ•ā¯ˆāŽĒā¯āŽĒāŽŸāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯", "view_stack": "āŽ•āŽžāŽŖā¯āŽ• āŽ…āŽŸā¯āŽ•ā¯āŽ•ā¯", + "view_user": "āŽĒāŽ¯āŽŠāŽ°ā¯ˆāŽĒā¯ āŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", + "viewer_remove_from_stack": "āŽ…āŽŸā¯āŽ•ā¯āŽ•āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ…āŽ•āŽąā¯āŽąā¯", + "viewer_stack_use_as_main_asset": "āŽĒāŽŋāŽ°āŽ¤āŽžāŽŠ āŽšā¯ŠāŽ¤ā¯āŽ¤āŽžāŽ•āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯", + "viewer_unstack": "āŽ…āŽŸā¯āŽ•ā¯āŽ•ā¯ˆ āŽ¨ā¯€āŽ•ā¯āŽ•ā¯", "visibility_changed": "{āŽŽāŽŖā¯āŽŖāŽŋāŽ•ā¯āŽ•ā¯ˆ, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ¨āŽĒāŽ°ā¯} āŽŽāŽąā¯āŽą {# āŽ¨āŽĒāŽ°ā¯āŽ•āŽŗā¯} āŽ•ā¯āŽ•ā¯ āŽ•ā¯āŽ•ā¯ āŽ¤ā¯†āŽ°āŽŋāŽĩā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯", "waiting": "āŽ•āŽžāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯", "warning": "āŽŽāŽšā¯āŽšāŽ°āŽŋāŽ•ā¯āŽ•ā¯ˆ", "week": "āŽĩāŽžāŽ°āŽŽā¯", "welcome": "āŽĩāŽ°āŽĩā¯‡āŽąā¯āŽ•āŽŋāŽąā¯‹āŽŽā¯", "welcome_to_immich": "āŽ‡āŽŽā¯āŽŽāŽŋāŽšā¯āŽšāŽŋāŽąā¯āŽ•ā¯ āŽĩāŽ°ā¯āŽ•", + "wifi_name": "āŽĩā¯ˆāŽƒāŽĒ❈ āŽĒā¯†āŽ¯āŽ°ā¯", + "wrong_pin_code": "āŽ¤āŽĩāŽąāŽžāŽŠ āŽĒāŽŋāŽŠā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯", "year": "āŽ†āŽŖā¯āŽŸā¯", - "years_ago": "{āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯, āŽĒāŽŠā¯āŽŽā¯ˆ, āŽ’āŽŠā¯āŽąā¯ {# āŽ†āŽŖā¯āŽŸā¯} āŽŽāŽąā¯āŽą {# āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯}}} āŽŽā¯āŽŠā¯āŽĒ❁", + "years_ago": "{years, plural, one {# āŽ†āŽŖā¯āŽŸā¯} other {# āŽ†āŽŖā¯āŽŸā¯āŽ•āŽŗā¯}} āŽŽā¯āŽŠā¯āŽĒ❁", "yes": "āŽ†āŽŽā¯", "you_dont_have_any_shared_links": "āŽ‰āŽ™ā¯āŽ•āŽŗāŽŋāŽŸāŽŽā¯ āŽĒāŽ•āŽŋāŽ°āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ", - "zoom_image": "āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ• āŽĒāŽŸāŽŽā¯" + "your_wifi_name": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĩā¯ˆāŽƒāŽĒ❈ āŽĒā¯†āŽ¯āŽ°ā¯", + "zoom_image": "āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ• āŽĒāŽŸāŽŽā¯", + "zoom_to_bounds": "āŽŽāŽ˛ā¯āŽ˛ā¯ˆāŽ•ā¯āŽ•ā¯ āŽĒā¯†āŽ°āŽŋāŽ¤āŽžāŽ•ā¯āŽ•ā¯" } diff --git a/i18n/te.json b/i18n/te.json index 8bd57bc3bc..0d5242891e 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -23,6 +23,8 @@ "add_photos": "ā°Ģāą‹ā°Ÿāą‹ā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "add_to": "ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋâ€Ļ", "add_to_album": "ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°•āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", + "add_to_album_bottom_sheet_added": "ā°†ā°˛āąā°Ŧā°Žāąā°•āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", + "add_to_album_bottom_sheet_already_exists": "ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°˛āą‹ ā°‡ā°Ēāąā°ĒⰟā°ŋā°•āą‡ ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "add_to_shared_album": "ā°­ā°žā°—ā°¸āąā°ĩā°žā°Žāąā°¯ ā°†ā°˛āąā°Ŧā°Žāąâ€Œā°•āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "add_url": "URLā°¨ā°ŋ ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "added_to_archive": "ā°†ā°°āąā°•āąˆā°ĩāąâ€Œā°•ā°ŋ ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", @@ -30,17 +32,18 @@ "added_to_favorites_count": "ā°‡ā°ˇāąā°Ÿā°Žāąˆā°¨ ā°ĩā°žā°Ÿā°ŋā°•ā°ŋ {count, number} ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "admin": { "add_exclusion_pattern_description": "ā°Žā°ŋā°¨ā°šā°žā°¯ā°ŋā°‚ā°Ēāą ā°¨ā°Žāą‚ā°¨ā°žā°˛ā°¨āą ā°œāą‹ā°Ąā°ŋā°‚ā°šā°‚ā°Ąā°ŋ. *, ** ā°Žā°°ā°ŋā°¯āą ?ā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°ŋ ā°—āąā°˛āą‹ā°Ŧā°ŋā°‚ā°—āąâ€Œā°•āą ā°Žā°Ļāąā°Ļā°¤āą ⰉⰂā°Ļā°ŋ. \"Raw\" ā°…ā°¨āą‡ ā°Ēāą‡ā°°āą ā°—ā°˛ ā°ā°Ļāąˆā°¨ā°ž ā°Ąāąˆā°°āą†ā°•āąā°Ÿā°°āą€ā°˛āą‹ā°¨ā°ŋ ā°…ā°¨āąā°¨ā°ŋ ā°Ģāąˆā°˛āąâ€Œā°˛ā°¨āą ā°ĩā°ŋā°¸āąā°Žā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ, \"**/Raw/**\"ā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ. \".tif\"ā°¤āą‹ ā°Žāąā°—ā°ŋā°¸āą‡ ā°…ā°¨āąā°¨ā°ŋ ā°Ģāąˆā°˛āąâ€Œā°˛ā°¨āą ā°ĩā°ŋā°¸āąā°Žā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ, \"**/*.tif\"ā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ. ⰏⰂā°Ēāą‚ā°°āąā°Ŗ ā°Žā°žā°°āąā°—ā°žā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°¸āąā°Žā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ, \"/path/to/ignore/**\"ā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ.", + "admin_user": "ā°¨ā°ŋā°°āąā°ĩā°žā°šā°•āąā°Ąāą", "asset_offline_description": "Ⰸ ā°Ŧā°žā°šāąā°¯ ā°˛āąˆā°Ŧāąā°°ā°°āą€ ā°Ģāąˆā°˛āą ⰇⰕā°Ēāąˆ ā°Ąā°ŋā°¸āąā°•āąâ€Œā°˛āą‹ ā°•ā°¨āąā°—āąŠā°¨ā°Ŧā°Ąā°˛āą‡ā°Ļāą ā°Žā°°ā°ŋā°¯āą ā°Ÿāąā°°ā°žā°ˇāąâ€Œā°•āą ⰤⰰⰞā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ. ā°Ģāąˆā°˛āą ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°˛āą‹ā°•ā°ŋ ⰤⰰⰞā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°¤āą‡, ā°•āąŠā°¤āąā°¤ ⰏⰂā°Ŧā°‚ā°§ā°ŋā°¤ ā°Ģāąˆā°˛āą ā°•āą‹ā°¸ā°‚ ā°Žāą€ ā°Ÿāąˆā°Žāąâ€Œā°˛āąˆā°¨āąâ€Œā°¨āą ⰤⰍā°ŋā°–āą€ ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ. Ⰸ ā°Ģāąˆā°˛āąā°¨ā°ŋ ā°Ēāąā°¨ā°°āąā°Ļāąā°§ā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ, ā°Ļā°¯ā°šāą‡ā°¸ā°ŋ ā°Ļā°ŋā°—āąā°ĩā°¨ ā°‰ā°¨āąā°¨ ā°Ģāąˆā°˛āą ā°Ēā°žā°¤āąâ€Œā°¨āą Immich ā°¯ā°žā°•āąā°¸āą†ā°¸āą ā°šāą‡ā°¯ā°—ā°˛ā°Ļā°¨ā°ŋ ā°¨ā°ŋā°°āąā°§ā°žā°°ā°ŋā°‚ā°šāąā°•āą‹ā°‚ā°Ąā°ŋ ā°Žā°°ā°ŋā°¯āą ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°¨ā°ŋ ā°¸āąā°•ā°žā°¨āą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ.", "authentication_settings": "ā°Ēāąā°°ā°Žā°žā°Ŗāą€ā°•ā°°ā°Ŗ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛āą", "authentication_settings_description": "ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāą, OAuth ā°Žā°°ā°ŋā°¯āą ⰇⰤⰰ ā°Ēāąā°°ā°Žā°žā°Ŗāą€ā°•ā°°ā°Ŗ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "authentication_settings_disable_all": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž ā°…ā°¨āąā°¨ā°ŋ ā°˛ā°žā°—ā°ŋā°¨āą ā°Ēā°Ļāąā°§ā°¤āąā°˛ā°¨āą ā°¨ā°ŋā°˛ā°ŋā°Ēā°ŋā°ĩāą‡ā°¯ā°žā°˛ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž? ā°˛ā°žā°—ā°ŋā°¨āą ā°Ēāą‚ā°°āąā°¤ā°ŋā°—ā°ž ā°¨ā°ŋā°˛ā°ŋā°Ēā°ŋā°ĩāą‡ā°¯ā°Ŧā°Ąāąā°¤āąā°‚ā°Ļā°ŋ.", "authentication_settings_reenable": "ā°Žā°ŗāąā°˛āą€ ā°Ēāąā°°ā°žā°°ā°‚ā°Ŧā°ŋā°‚ā°šā°Ÿā°žā°¨ā°ŋā°•ā°ŋ, Server Commandā°¨ā°ŋ ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°‚ā°Ąā°ŋ.", "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": "ā°Ąāą‡ā°Ÿā°žā°Ŧāą‡ā°¸āą ā°Ēā°Ąā°ĩāą†ā°¸āą‡ ā°¸āą†ā°Ÿāąā°Ÿā°ŋā°‚ā°—āąâ€Œā°˛ā°¨āą ā°¨ā°ŋā°°āąā°ĩā°šā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "cleared_jobs": "ā°Ļāą€ā°¨ā°ŋ ā°•āą‹ā°¸ā°‚ ā°‰ā°Ļāąā°¯āą‹ā°—ā°žā°˛āą ā°•āąā°˛ā°ŋā°¯ā°°āą ā°šāą‡ā°¯ā°Ŧā°Ąāąā°Ąā°žā°¯ā°ŋ: {job}", "config_set_by_file": "ā°•ā°žā°¨āąā°Ģā°ŋā°—ā°°āą‡ā°ˇā°¨āą ā°Ēāąā°°ā°¸āąā°¤āąā°¤ā°‚ ā°•ā°žā°¨āąā°Ģā°ŋā°—ā°°āą‡ā°ˇā°¨āą ā°Ģāąˆā°˛āą ā°Ļāąā°ĩā°žā°°ā°ž ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "confirm_delete_library": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž {library} ā°˛āąˆā°Ŧāąā°°ā°°āą€ā°¨ā°ŋ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°žā°˛ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž?", @@ -48,6 +51,7 @@ "confirm_email_below": "ā°¨ā°ŋā°°āąā°§ā°žā°°ā°ŋā°‚ā°šā°Ąā°žā°¨ā°ŋā°•ā°ŋ, ā°•āąā°°ā°ŋā°‚ā°Ļ \"{email}\" ā°Ÿāąˆā°Ēāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "confirm_reprocess_all_faces": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž ā°…ā°¨āąā°¨ā°ŋ ā°Žāąā°–ā°žā°˛ā°¨āą ā°°āą€ā°Ēāąā°°ā°žā°¸āą†ā°¸āą ā°šāą‡ā°¯ā°žā°˛ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž? ā°‡ā°Ļā°ŋ ā°Ēāą‡ā°°āąā°¨āąā°¨ ā°ĩāąā°¯ā°•āąā°¤āąā°˛ā°¨āą ā°•āą‚ā°Ąā°ž ā°•āąā°˛ā°ŋā°¯ā°°āą ā°šāą‡ā°¸āąā°¤āąā°‚ā°Ļā°ŋ.", "confirm_user_password_reset": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž {user} ā°Ēā°žā°¸āąâ€Œā°ĩā°°āąā°Ąāąâ€Œā°¨ā°ŋ ā°°āą€ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°žā°˛ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž?", + "confirm_user_pin_code_reset": "ā°Žāą€ā°°āą ā°–ā°šāąā°šā°ŋā°¤ā°‚ā°—ā°ž {user} ā°¯āąŠā°•āąā°• ā°Ēā°ŋā°¨āą ā°•āą‹ā°Ąāą ā°¨āą€ ā°°āą€ā°¸āą†ā°Ÿāą ā°šāą‡ā°Ļāąā°Ļā°žā°Žā°¨ā°ŋā°…ā°¨āąā°•āąā°‚ā°Ÿāąā°¨āąā°¨ā°žā°°ā°ž?", "create_job": "ā°Ēā°¨ā°ŋā°¨ā°ŋ ā°¸āąƒā°ˇāąā°Ÿā°ŋā°‚ā°šā°‚ā°Ąā°ŋ", "cron_expression": "ā°•āąā°°ā°žā°¨āą ā°ĩāąā°¯ā°•āąā°¤āą€ā°•ā°°ā°Ŗ", "cron_expression_description": "ā°•āąā°°ā°žā°¨āą ā°Ģā°žā°°āąā°Žā°žā°Ÿāą ā°‰ā°Ēā°¯āą‹ā°—ā°ŋā°‚ā°šā°ŋ ā°¸āąā°•ā°žā°¨ā°ŋā°‚ā°—āą ā°ĩā°ŋā°°ā°žā°Žā°žā°¨āąā°¨ā°ŋ ā°¸āą†ā°Ÿāą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ. ā°Žā°°ā°ŋā°¨āąā°¨ā°ŋ ā°ĩā°ŋā°ĩā°°ā°žā°˛ ā°•āą‹ā°¸ā°‚ ā°Ļā°¯ā°šāą‡ā°¸ā°ŋ ā°‰ā°Ļā°ž. ā°•āąā°°āą‹ā°‚ā°Ÿā°žā°Ŧāą ā°—āąā°°āą ā°šāą‚ā°Ąā°‚ā°Ąā°ŋ", @@ -402,7 +406,6 @@ "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 {{name}} other {ā°•āąŠā°¤āąā°¤ ā°†ā°˛āąā°Ŧā°Žāą}}ā°•ā°ŋ ā°œāą‹ā°Ąā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°¨ā°ĩā°ŋ", "assets_count": "{count, plural, one {# ā°†ā°¸āąā°¤ā°ŋ} other {# ā°†ā°¸āąā°¤āąā°˛āą}}", "assets_moved_to_trash_count": "{count, plural, one {# ā°†ā°¸āąā°¤ā°ŋ} other {# ā°†ā°¸āąā°¤āąā°˛āą}} ā°šāą†ā°¤āąā°¤ā°Ŧāąā°Ÿāąā°Ÿā°˛āą‹ā°•ā°ŋ ⰤⰰⰞā°ŋā°‚ā°šā°žā°°āą", "assets_permanently_deleted_count": "{count, plural, one {# ā°†ā°¸āąā°¤ā°ŋ} other {# ā°†ā°¸āąā°¤āąā°˛āą}} ā°ļā°žā°ļāąā°ĩā°¤ā°‚ā°—ā°ž ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°¨ā°ĩā°ŋ", @@ -807,7 +810,6 @@ "light": "ā°ĩāą†ā°˛āąā°¤āąā°°āą", "like_deleted": "ā°‡ā°¸āąā°Ÿā°‚ ā°¤āąŠā°˛ā°—ā°ŋā°‚ā°šā°Ŧā°Ąā°ŋā°‚ā°Ļā°ŋ", "link_motion_video": "ā°Žāą‹ā°ˇā°¨āą ā°ĩāą€ā°Ąā°ŋā°¯āą‹ ā°˛ā°ŋā°‚ā°•āą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", - "link_options": "ā°˛ā°ŋā°‚ā°•āą ā°Žā°‚ā°Ēā°ŋā°•ā°˛āą", "link_to_oauth": "OAuth ā°•ā°ŋ ā°˛ā°ŋā°‚ā°•āą ā°šāą‡ā°¯ā°‚ā°Ąā°ŋ", "linked_oauth_account": "ā°˛ā°ŋā°‚ā°•āą ā°šāą‡ā°¯ā°Ŧā°Ąā°ŋā°¨ OAuth ā°–ā°žā°¤ā°ž", "list": "ā°œā°žā°Ŧā°ŋā°¤ā°ž", diff --git a/i18n/th.json b/i18n/th.json index 13d9514d63..809d9e24af 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,14 +14,16 @@ "add_a_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "add_a_name": "āš€ā¸žā¸´āšˆā¸Ąā¸Šā¸ˇāšˆā¸­", "add_a_title": "āš€ā¸žā¸´āšˆā¸Ąā¸Ģā¸ąā¸§ā¸‚āš‰ā¸­", - "add_endpoint": "āš€ā¸žā¸´āšˆā¸Ąā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸›ā¸Ĩ⏞ā¸ĸ⏗⏞⏇", + "add_birthday": "āš€ā¸žā¸´āšˆā¸Ąā¸§ā¸ąā¸™āš€ā¸ā¸´ā¸”", + "add_endpoint": "āš€ā¸žā¸´āšˆā¸Ąā¸›ā¸Ĩ⏞ā¸ĸ⏗⏞⏇", "add_exclusion_pattern": "āš€ā¸žā¸´āšˆā¸Ąā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "add_import_path": "āš€ā¸žā¸´āšˆā¸Ąāš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛", "add_location": "āš€ā¸žā¸´āšˆā¸Ąā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "add_more_users": "āš€ā¸žā¸´āšˆā¸Ąā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", - "add_partner": "āš€ā¸žā¸´āšˆā¸Ąā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", + "add_partner": "āš€ā¸žā¸´āšˆā¸Ąā¸„ā¸šāšˆā¸Ģā¸š", "add_path": "āš€ā¸žā¸´āšˆā¸Ąā¸žā¸˛ā¸—ā¸—ā¸ĩāšˆā¸•ā¸ąāš‰ā¸‡", "add_photos": "āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", + "add_tag": "āš€ā¸žā¸´āšˆā¸Ąāšā¸—āš‡ā¸", "add_to": "āš€ā¸žā¸´āšˆā¸Ąāš„ā¸›ā¸ĸā¸ąā¸‡ â€Ļ", "add_to_album": "āš€ā¸žā¸´āšˆā¸Ąāš„ā¸›ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "add_to_album_bottom_sheet_added": "āš€ā¸žā¸´āšˆā¸Ąāš„ā¸›ā¸ĸā¸ąā¸‡ {album}", @@ -33,7 +35,8 @@ "added_to_favorites_count": "{count, number} ā¸Ŗā¸šā¸›ā¸–ā¸šā¸āš€ā¸žā¸´āšˆā¸Ąāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", "admin": { "add_exclusion_pattern_description": "āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸šā¸›āšā¸šā¸šā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™ ā¸Ŗā¸­ā¸‡ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ *, ** āšā¸Ĩ⏰ ? ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸°āš€ā¸§āš‰ā¸™āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸™āš„ā¸”āš€ā¸Ŗāš‡ā¸ā¸—ā¸­ā¸Ŗā¸ĩ⏗ā¸ĩāšˆā¸Šā¸ˇāšˆā¸­ā¸§āšˆā¸˛ \"Raw\" āšƒā¸Ģāš‰āšƒā¸Šāš‰ \"**/Raw/**\" ā¸–āš‰ā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸°āš€ā¸§āš‰ā¸™āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸—ā¸ĩāšˆā¸Ĩā¸‡ā¸—āš‰ā¸˛ā¸ĸā¸”āš‰ā¸§ā¸ĸ \".tif\" āšƒā¸Ģāš‰āšƒā¸Šāš‰ \"**/*.tif\" ā¸–āš‰ā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸°āš€ā¸§āš‰ā¸™ā¸žā¸˛ā¸˜ā¸—ā¸ĩāšˆāš€ā¸Ŗā¸´āšˆā¸Ąā¸ˆā¸˛ā¸āš„ā¸”āš€ā¸Ŗā¸ā¸—ā¸­ā¸Ŗā¸ĩā¸šā¸™ā¸Ēā¸¸ā¸”āšƒā¸Ģāš‰āšƒā¸Šāš‰ \"/ā¸žā¸˛ā¸˜/⏗ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗ/ā¸Ĩā¸°āš€ā¸§āš‰ā¸™/**\"", - "asset_offline_description": "āš„ā¸Ÿā¸ĨāšŒ Asset ā¸‚ā¸­ā¸‡āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏠⏞ā¸ĸ⏙⏭⏁⏙ā¸ĩāš‰āš„ā¸Ąāšˆā¸žā¸šāšƒā¸™ā¸”ā¸´ā¸Ēā¸āšŒāšā¸Ĩāš‰ā¸§ āšā¸Ĩā¸°ā¸–ā¸šā¸ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›ā¸—ā¸ĩāšˆā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰ ā¸Ģā¸˛ā¸āš„ā¸Ÿā¸ĨāšŒā¸–ā¸šā¸ā¸ĸāš‰ā¸˛ā¸ĸ⏠⏞ā¸ĸāšƒā¸™āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāš„ā¸—ā¸ĄāšŒāš„ā¸Ĩā¸™āšŒā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­ā¸Ģā¸˛āšā¸­ā¸Ēāš€ā¸‹āš‡ā¸•ā¸—ā¸ĩāšˆāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸‚āš‰ā¸­ā¸‡āšƒā¸Ģā¸Ąāšˆ ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸šāš‰ā¸„ā¸ˇā¸™ Asset ⏙ā¸ĩāš‰ āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸Ģāš‰āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ Immich ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡āš„ā¸Ÿā¸ĨāšŒā¸”āš‰ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸‡āš„ā¸”āš‰ āšā¸Ĩ⏰⏗⏺⏁⏞⏪ā¸Ēāšā¸ā¸™āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏭ā¸ĩā¸ā¸„ā¸Ŗā¸ąāš‰ā¸‡", + "admin_user": "ā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ", + "asset_offline_description": "āš„ā¸Ąāšˆā¸žā¸šāš„ā¸Ÿā¸ĨāšŒā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏠⏞ā¸ĸ⏙⏭⏁⏙ā¸ĩāš‰āšƒā¸™ā¸”ā¸´ā¸Ēā¸āšŒ āšā¸Ĩā¸°ā¸–ā¸šā¸ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›ā¸—ā¸ĩāšˆā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āšā¸Ĩāš‰ā¸§ ā¸Ģā¸˛ā¸āš„ā¸Ÿā¸ĨāšŒā¸–ā¸šā¸ā¸ĸāš‰ā¸˛ā¸ĸ⏠⏞ā¸ĸāšƒā¸™āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāš„ā¸—ā¸ĄāšŒāš„ā¸Ĩā¸™āšŒā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­ā¸Ģ⏞ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸‚āš‰ā¸­ā¸‡āšƒā¸Ģā¸Ąāšˆ ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸šāš‰ā¸„ā¸ˇā¸™ā¸Ēā¸ˇāšˆā¸­ā¸™ā¸ĩāš‰ āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸§āšˆā¸˛ Immich ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡āš„ā¸Ÿā¸ĨāšŒā¸”āš‰ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸‡āš„ā¸”āš‰ āšā¸Ĩ⏰⏗⏺⏁⏞⏪ā¸Ēāšā¸ā¸™āš„ā¸Ĩ⏚⏪⏞⏪ā¸ĩ⏭ā¸ĩā¸ā¸„ā¸Ŗā¸ąāš‰ā¸‡", "authentication_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇", "authentication_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™, OAuth, āšā¸Ĩā¸°ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸­ā¸ˇāšˆā¸™āš†", "authentication_settings_disable_all": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸›ā¸´ā¸”ā¸§ā¸´ā¸˜ā¸ĩ⏁⏞⏪ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ? ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸ˆā¸°ā¸–ā¸šā¸ā¸›ā¸´ā¸”ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", @@ -42,8 +45,10 @@ "backup_database": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_database_enable_description": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_keep_last_amount": "ā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸—ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡āš€ā¸āš‡ā¸šāš„ā¸§āš‰", + "backup_onboarding_footer": "ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąā¸—ā¸ĩāšˆāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ⏂⏭⏇ Immich āš‚ā¸›ā¸Ŗā¸”ā¸”ā¸šā¸—ā¸ĩāšˆ documentation", + "backup_onboarding_title": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_settings": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", - "backup_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ ⏇⏞⏙ā¸Ē⏺⏪⏭⏇⏙ā¸ĩāš‰ā¸ˆā¸°āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšā¸Ĩā¸°ā¸„ā¸¸ā¸“ā¸ˆā¸°āš„ā¸Ąāšˆāš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ąā¸ąā¸™ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "backup_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸ā¸˛ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "cleared_jobs": "āš€ā¸„ā¸Ĩā¸ĩā¸ĸā¸ŖāšŒā¸‡ā¸˛ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸š: {job}", "config_set_by_file": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸„ā¸­ā¸™ā¸Ÿā¸´ā¸ā¸ā¸ŗā¸Ĩā¸ąā¸‡ā¸–ā¸šā¸ā¸ā¸ŗā¸Ģā¸™ā¸”āš‚ā¸”ā¸ĸāš„ā¸Ÿā¸ĨāšŒā¸„ā¸­ā¸™ā¸Ÿā¸´ā¸", "confirm_delete_library": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸­ā¸ĸ⏞⏁ā¸Ĩā¸šā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž {library} ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", @@ -164,12 +169,14 @@ "metadata_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ Metadata", "migration_job": "ā¸ā¸˛ā¸Ŗāš‚ā¸ĸ⏁ā¸ĸāš‰ā¸˛ā¸ĸ", "migration_job_description": "ā¸ĸāš‰ā¸˛ā¸ĸā¸ ā¸˛ā¸žā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ēā¸ˇāšˆā¸­āšā¸Ĩā¸°āšƒā¸šā¸Ģā¸™āš‰ā¸˛āš„ā¸›ā¸ĸā¸ąā¸‡āš‚ā¸„ā¸Ŗā¸‡ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", + "nightly_tasks_cluster_new_faces_setting": "⏄ā¸Ĩā¸ąā¸Ēāš€ā¸•ā¸­ā¸ŖāšŒāšƒā¸šā¸Ģā¸™āš‰ā¸˛āšƒā¸Ģā¸Ąāšˆ", + "nightly_tasks_generate_memories_setting": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸„ā¸§ā¸˛ā¸Ąā¸—ā¸Ŗā¸‡ā¸ˆā¸ŗ", "no_paths_added": "āš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸žā¸´āšˆā¸Ąā¸žā¸˛ā¸˜", "no_pattern_added": "āš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸šā¸›āšā¸šā¸š", "note_apply_storage_label_previous_assets": "ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ Storage Label ā¸ā¸ąā¸šāš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ āšƒā¸Ģāš‰ā¸Ŗā¸ąā¸™ā¸„ā¸ŗā¸Ēā¸ąāšˆā¸‡ā¸™ā¸ĩāš‰", "note_cannot_be_changed_later": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ģ⏕⏏: āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏠⏞ā¸ĸā¸Ģā¸Ĩā¸ąā¸‡āš„ā¸”āš‰!", "notification_email_from_address": "ā¸ˆā¸˛ā¸ā¸—ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆ", - "notification_email_from_address_description": "⏭ā¸ĩāš€ā¸Ąā¸Ĩā¸œā¸šāš‰ā¸Ēāšˆā¸‡ ⏭ā¸ĸāšˆā¸˛ā¸‡āš€ā¸Šāšˆā¸™ \"Immich Photo Server \"", + "notification_email_from_address_description": "⏗ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆā¸­ā¸ĩāš€ā¸Ąā¸Ĩā¸œā¸šāš‰ā¸Ēāšˆā¸‡ ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡ \"Immich Photo Server \" (⏁⏪⏏⏓⏞ā¸ĸā¸ĩ⏙ā¸ĸā¸ąā¸™ā¸§āšˆā¸˛ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ēāšˆā¸‡āš€ā¸Ąā¸Ĩā¸ˆā¸˛ā¸ā¸—ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆā¸™ā¸ĩāš‰āš„ā¸”āš‰)", "notification_email_host_description": "⏗ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆāš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸­ā¸ĩāš€ā¸Ąā¸Ĩ (āš€ā¸Šāšˆā¸™ smtp.immich.app)", "notification_email_ignore_certificate_errors": "āš„ā¸Ąāšˆā¸Ēā¸™āšƒā¸ˆā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡", "notification_email_ignore_certificate_errors_description": "āš„ā¸Ąāšˆā¸Ēā¸™āšƒā¸ˆā¸ā¸˛ā¸Ŗā¸ĸ⏎⏙ā¸ĸā¸ąā¸™āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ TLS ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ (āš„ā¸Ąāšˆāšā¸™ā¸°ā¸™ā¸ŗ)", @@ -193,7 +200,7 @@ "oauth_enable_description": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_mobile_redirect_uri": "URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", "oauth_mobile_redirect_uri_override": "āšā¸—ā¸™ā¸—ā¸ĩāšˆ URI āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸šā¸™āš‚ā¸—ā¸Ŗā¸¨ā¸ąā¸žā¸—āšŒ", - "oauth_mobile_redirect_uri_override_description": "āš€ā¸›ā¸´ā¸”āš€ā¸Ąā¸ˇāšˆā¸­ 'app.immich:/' āš€ā¸›āš‡ā¸™ URI ⏗ā¸ĩāšˆāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", + "oauth_mobile_redirect_uri_override_description": "āš€ā¸›ā¸´ā¸”āš€ā¸Ąā¸ˇāšˆā¸­ā¸œā¸šāš‰āšƒā¸Ģāš‰ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗ OAuth āš„ā¸Ąāšˆā¸­ā¸™ā¸¸ā¸ā¸˛ā¸• URI āš€ā¸Šāšˆā¸™ \"{callback}\"", "oauth_settings": "OAuth", "oauth_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸œāšˆā¸˛ā¸™ OAuth", "oauth_settings_more_details": "ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą āšƒā¸Ģāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš€ā¸­ā¸ā¸Ē⏞⏪", @@ -202,7 +209,7 @@ "oauth_storage_quota_claim": "ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š", "oauth_storage_quota_claim_description": "ā¸•ā¸ąāš‰ā¸‡āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚ā¸­ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸•ā¸˛ā¸Ąā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸—ā¸ĩāšˆāšƒā¸Šāš‰ā¸­āš‰ā¸˛ā¸‡ā¸–ā¸ļā¸‡āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "oauth_storage_quota_default": "āš‚ā¸„ā¸§ā¸•āš‰ā¸˛ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™ (GiB)", - "oauth_storage_quota_default_description": "āš‚ā¸„ā¸§ā¸•āš‰ā¸˛āšƒā¸™ā¸Ģā¸™āšˆā¸§ā¸ĸ GiB ⏗ā¸ĩāšˆā¸ˆā¸°āšƒā¸Šāš‰āš€ā¸Ąā¸ˇāšˆā¸­āš„ā¸Ąāšˆā¸Ąā¸ĩā¸ā¸˛ā¸Ŗā¸­āš‰ā¸˛ā¸‡ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒ (ā¸›āš‰ā¸­ā¸™ 0 ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāš‚ā¸„ā¸§ā¸•āš‰ā¸˛āš„ā¸Ąāšˆā¸ˆā¸ŗā¸ā¸ąā¸”)", + "oauth_storage_quota_default_description": "āš‚ā¸„ā¸§ā¸•āš‰ā¸˛āšƒā¸™ā¸Ģā¸™āšˆā¸§ā¸ĸ GiB ⏗ā¸ĩāšˆā¸ˆā¸°āšƒā¸Šāš‰āš€ā¸Ąā¸ˇāšˆā¸­āš„ā¸Ąāšˆā¸Ąā¸ĩā¸ā¸˛ā¸Ŗā¸­āš‰ā¸˛ā¸‡ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒ", "oauth_timeout": "ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛ā¸ā¸˛ā¸Ŗā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­", "oauth_timeout_description": "⏪⏰ā¸ĸā¸°āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­ (ā¸Ģā¸™āšˆā¸§ā¸ĸāš€ā¸›āš‡ā¸™ā¸Ąā¸´ā¸Ĩā¸Ĩ⏴⏧⏴⏙⏞⏗ā¸ĩ)", "password_enable_description": "ā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™ā¸ā¸ąā¸šā¸­ā¸ĩāš€ā¸Ąā¸Ĩāšā¸Ĩ⏰⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", @@ -242,6 +249,7 @@ "storage_template_migration_info": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸‚ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸ˆā¸°āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗāš€ā¸›āš‡ā¸™ā¸•ā¸ąā¸§ā¸žā¸´ā¸Ąā¸žāšŒāš€ā¸Ĩāš‡ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸” ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩā¸‡āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ˆā¸°ā¸Ąā¸ĩ⏜ā¸Ĩā¸ā¸ąā¸šāšā¸­ā¸Ēāš€ā¸‹āš‡ā¸•āšƒā¸Ģā¸Ąāšˆāš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸™ā¸ŗāš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•āš„ā¸›āšƒā¸Šāš‰ā¸ā¸ąā¸š Asset ⏗ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ āšƒā¸Ģāš‰ā¸Ŗā¸ąā¸™ {job}.", "storage_template_migration_job": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕⏁⏞⏪ Migration ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "storage_template_more_details": "ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ āš‚ā¸›ā¸Ŗā¸”ā¸”ā¸šā¸—ā¸ĩāšˆ Storage Template āšā¸Ĩ⏰ ⏜ā¸Ĩā¸ā¸Ŗā¸°ā¸—ā¸š", + "storage_template_onboarding_description_v2": "āš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸” ⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸ˆā¸°ā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸•ā¸˛ā¸Ąāš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕⏗ā¸ĩāšˆā¸œā¸šāš‰āšƒā¸Šāš‰ā¸ā¸ŗā¸Ģ⏙⏔ ā¸­āšˆā¸˛ā¸™āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą", "storage_template_path_length": "⏂ā¸ĩā¸”ā¸ˆā¸ŗā¸ā¸ąā¸”ā¸‚ā¸­ā¸‡ā¸„ā¸§ā¸˛ā¸Ąā¸ĸā¸˛ā¸§ā¸žā¸˛ā¸˜āš‚ā¸”ā¸ĸā¸›ā¸Ŗā¸°ā¸Ąā¸˛ā¸“: {length, number}/{limit, number}", "storage_template_settings": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "storage_template_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗāš‚ā¸„ā¸Ŗā¸‡ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒāšā¸Ĩā¸°ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔", @@ -256,7 +264,7 @@ "template_email_update_album": "ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "template_email_welcome": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩ⏕ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸­ā¸ĩāš€ā¸Ąā¸Ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸š", "template_settings": "āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", - "template_settings_description": "ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•āšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", + "template_settings_description": "ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡āš€ā¸—ā¸Ąāš€ā¸žā¸Ĩā¸•ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™", "theme_custom_css_settings": "CSS ā¸āšā¸˛ā¸Ģā¸™ā¸”āš€ā¸­ā¸‡", "theme_custom_css_settings_description": "Cascading Style Sheets ā¸Šāšˆā¸§ā¸ĸāšƒā¸Ģāš‰ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡āš€ā¸„āš‰ā¸˛āš‚ā¸„ā¸Ŗā¸‡ Immich āš„ā¸”āš‰", "theme_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸˜ā¸ĩā¸Ą", @@ -362,7 +370,7 @@ "advanced_settings_proxy_headers_subtitle": "⏁⏺ā¸Ģ⏙⏔ proxy headers ⏗ā¸ĩāšˆ Immich ⏄⏧⏪ā¸Ēāšˆā¸‡ā¸žā¸Ŗāš‰ā¸­ā¸Ąā¸ā¸ąā¸šāšā¸•āšˆā¸Ĩā¸°ā¸„ā¸ŗā¸‚ā¸­āš€ā¸„ā¸Ŗā¸ˇā¸­ā¸‚āšˆā¸˛ā¸ĸ", "advanced_settings_proxy_headers_title": "ā¸žāš‡ā¸­ā¸ā¸‹ā¸ĩāšˆ āš€ā¸Žā¸”āš€ā¸”ā¸­ā¸ŖāšŒ", "advanced_settings_self_signed_ssl_subtitle": "ā¸‚āš‰ā¸˛ā¸Ąā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡āšā¸šā¸š self-signed", - "advanced_settings_self_signed_ssl_title": "ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL āšā¸šā¸š self-signed ", + "advanced_settings_self_signed_ssl_title": "ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL āšā¸šā¸š self-signed", "advanced_settings_sync_remote_deletions_subtitle": "⏚ā¸Ģā¸Ŗā¸ˇā¸­ā¸ā¸šāš‰ā¸„ā¸ˇā¸™āš„ā¸Ÿā¸ĨāšŒā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸™ā¸ĩāš‰āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸Ąā¸ˇāšˆā¸­ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸”ā¸ąā¸‡ā¸ā¸Ĩāšˆā¸˛ā¸§ā¸œāšˆā¸˛ā¸™āš€ā¸§āš‡ā¸š", "advanced_settings_sync_remote_deletions_title": "ā¸‹ā¸´ā¸‡ā¸āšŒā¸ā¸˛ā¸Ŗā¸Ĩ⏚⏈⏞⏁⏪⏰ā¸ĸā¸°āš„ā¸ā¸Ĩ [⏄⏏⏓ā¸Ēā¸Ąā¸šā¸ąā¸•ā¸´ā¸—ā¸”ā¸Ĩ⏭⏇]", "advanced_settings_tile_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡", @@ -401,6 +409,9 @@ "album_with_link_access": "ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸Ģāš‰ā¸—ā¸¸ā¸ā¸„ā¸™ā¸—ā¸ĩāšˆā¸Ąā¸ĩā¸Ĩā¸´ā¸‡ā¸āšŒā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸”ā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩā¸°ā¸œā¸šāš‰ā¸„ā¸™ā¸—ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸™ā¸ĩāš‰", "albums": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "albums_count": "{count, plural, one {{count, number} ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą} other {{count, number} ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą}}", + "albums_default_sort_order": "ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸Ŗā¸ĩā¸ĸā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™", + "albums_default_sort_order_description": "ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸Ŗā¸ĩā¸ĸā¸‡āšā¸­ā¸Ēāš€ā¸‹āš‡ā¸•āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšƒā¸Ģā¸Ąāšˆ", + "albums_feature_description": "⏁ā¸Ĩā¸¸āšˆā¸Ąā¸‚ā¸­ā¸‡āšā¸­ā¸Ēāš€ā¸‹āš‡ā¸•ā¸—ā¸ĩāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ēāšˆā¸‡āšƒā¸Ģāš‰ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸­ā¸ˇāšˆā¸™āš„ā¸”āš‰", "all": "ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "all_albums": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "all_people": "⏗⏏⏁⏄⏙", @@ -413,8 +424,8 @@ "anti_clockwise": "ā¸—ā¸§ā¸™āš€ā¸‚āš‡ā¸Ąā¸™ā¸˛ā¸Ŧ⏴⏁⏞", "api_key": "API key", "api_key_description": "ā¸„āšˆā¸˛ā¸™ā¸ĩāš‰ā¸ˆā¸°āšā¸Ēā¸”ā¸‡āš€ā¸žā¸ĩā¸ĸā¸‡ā¸„ā¸Ŗā¸ąāš‰ā¸‡āš€ā¸”ā¸ĩā¸ĸ⏧ āš‚ā¸›ā¸Ŗā¸”ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸ā¸āšˆā¸­ā¸™ā¸›ā¸´ā¸”ā¸Ģā¸™āš‰ā¸˛ā¸•āšˆā¸˛ā¸‡", - "api_key_empty": "ā¸Šā¸ˇāšˆā¸­ā¸„ā¸ĩā¸ĸāšŒ API ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸„ā¸§ā¸Ŗā¸§āšˆā¸˛ā¸‡āš€ā¸›ā¸Ĩāšˆā¸˛", - "api_keys": "API ⏄ā¸ĩā¸ĸāšŒ", + "api_key_empty": "ā¸Šā¸ˇāšˆā¸­ API Key ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸„ā¸§ā¸Ŗā¸§āšˆā¸˛ā¸‡āš€ā¸›ā¸Ĩāšˆā¸˛", + "api_keys": "API Key", "app_bar_signout_dialog_content": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸­ā¸ĸ⏞⏁⏭⏭⏁⏈⏞⏁⏪⏰⏚⏚", "app_bar_signout_dialog_ok": "āšƒā¸Šāšˆ", "app_bar_signout_dialog_title": "⏭⏭⏁⏈⏞⏁⏪⏰⏚⏚", @@ -428,7 +439,7 @@ "archive_size_description": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸™ā¸˛ā¸”ā¸Ēā¸šā¸‡ā¸Ē⏏⏔ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ (GiB)", "archived": "āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗāšā¸Ĩāš‰ā¸§", "archived_count": "{count, plural, other {āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ # ⏪⏞ā¸ĸ⏁⏞⏪}}", - "are_these_the_same_person": "āš€ā¸›āš‡ā¸™ā¸„ā¸™āš€ā¸”ā¸ĩā¸ĸā¸§ā¸ā¸ąā¸™ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", + "are_these_the_same_person": "āš€ā¸›āš‡ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩāš€ā¸”ā¸ĩā¸ĸā¸§ā¸ā¸ąā¸™ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", "are_you_sure_to_do_this": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸—ā¸ŗā¸Ēā¸´āšˆā¸‡ā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?", "asset_action_delete_err_read_only": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸā¸§āš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "asset_action_share_err_offline": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸”ā¸ļā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", @@ -448,24 +459,42 @@ "asset_list_settings_title": "ā¸•ā¸˛ā¸Ŗā¸˛ā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "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 {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", - "assets_added_to_name_count": "āš€ā¸žā¸´āšˆā¸Ą {count, plural, one {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ {hasName, select, true {{name}} other {new album}}", + "assets_cannot_be_added_to_album_count": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ą {count, plural, one {ā¸Ēā¸ˇāšˆā¸­} other {ā¸Ēā¸ˇāšˆā¸­}} āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", + "assets_count": "{count, plural, one { ā¸Ēā¸ˇāšˆā¸­} other { ā¸Ēā¸ˇāšˆā¸­}}", + "assets_deleted_permanently": "{count} ā¸Ēā¸ˇāšˆā¸­ā¸–ā¸šā¸ā¸Ĩ⏚⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ", + "assets_deleted_permanently_from_server": "ā¸Ĩ⏚ {count} ā¸Ēā¸ˇāšˆā¸­ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ Immich ⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ", + "assets_downloaded_failed": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ {count, plural, one {āš„ā¸Ÿā¸ĨāšŒ} other {āš„ā¸Ÿā¸ĨāšŒ}} āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ - {error}", + "assets_downloaded_successfully": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ {count, plural, one {āš„ā¸Ÿā¸ĨāšŒ} other {āš„ā¸Ÿā¸ĨāšŒ}} ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "assets_moved_to_trash_count": "ā¸ĸāš‰ā¸˛ā¸ĸ {count, plural, one {# asset} other {# assets}} āš„ā¸›ā¸ĸā¸ąā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āšā¸Ĩāš‰ā¸§", "assets_permanently_deleted_count": "ā¸Ĩ⏚ {count, plural, one {# asset} other {# assets}} ā¸—ā¸´āš‰ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ā¸–ā¸šā¸ā¸Ĩā¸šāšā¸Ĩāš‰ā¸§", + "assets_removed_permanently_from_device": "⏙⏺ {count} ā¸Ēā¸ˇāšˆā¸­ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ", "assets_restore_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸šāš‰ā¸„ā¸ˇā¸™ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸—ā¸´āš‰ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”? ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰āš„ā¸”āš‰! āš‚ā¸›ā¸Ŗā¸”ā¸—ā¸Ŗā¸˛ā¸šā¸§āšˆā¸˛ā¸Ēā¸ˇāšˆā¸­ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒāšƒā¸”āš† āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ā¸šāš‰ā¸„ā¸ˇā¸™āš„ā¸”āš‰ā¸”āš‰ā¸§ā¸ĸ⏧⏴⏘ā¸ĩ⏙ā¸ĩāš‰", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} ā¸„ā¸ˇā¸™ā¸„āšˆā¸˛", + "assets_restored_successfully": "ā¸ā¸šāš‰ā¸„ā¸ˇā¸™ {count} ā¸Ēā¸ˇāšˆā¸­ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "assets_trashed": "ā¸ĸāš‰ā¸˛ā¸ĸ {count} ā¸Ēā¸ˇāšˆā¸­āš„ā¸›ā¸ĸā¸ąā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ā¸–ā¸šā¸ā¸Ĩ⏚", + "assets_trashed_from_server": "ā¸ĸāš‰ā¸˛ā¸ĸ {count} ā¸Ēā¸ˇāšˆā¸­ā¸ˆā¸˛ā¸ Immich āš„ā¸›ā¸ĸā¸ąā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} ⏭ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸­ā¸ĸā¸šāšˆāšā¸Ĩāš‰ā¸§", "authorized_devices": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸—ā¸ĩāšˆāš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•", + "automatic_endpoint_switching_subtitle": "āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸”āš‰ā¸§ā¸ĸ LAN ⏠⏞ā¸ĸāšƒā¸™ā¸§ā¸‡ Wi-Fi ⏗ā¸ĩāšˆā¸Ŗā¸°ā¸šā¸¸āš„ā¸§āš‰ āšā¸Ĩā¸°āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸”āš‰ā¸§ā¸ĸ⏧⏴⏘ā¸ĩā¸­ā¸ˇāšˆā¸™āš€ā¸Ąā¸ˇāšˆā¸­ā¸­ā¸ĸā¸šāšˆā¸™ā¸­ā¸ Wi-Fi ⏗ā¸ĩāšˆā¸Ŗā¸°ā¸šā¸¸āš„ā¸§āš‰", + "automatic_endpoint_switching_title": "ā¸Ēā¸Ĩā¸ąā¸š URL ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", + "autoplay_slideshow": "āš€ā¸Ĩāšˆā¸™ā¸Ēāš„ā¸Ĩā¸”āšŒāš‚ā¸Šā¸§āšŒ", "back": "⏁ā¸Ĩā¸ąā¸š", "back_close_deselect": "ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸š, ⏛⏴⏔, ā¸Ģ⏪⏎⏭ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸ā¸˛ā¸Ŗāš€ā¸Ĩ⏎⏭⏁", + "background_location_permission": "ā¸ā¸˛ā¸Ŗā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•ā¸Ŗā¸°ā¸šā¸¸ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡", + "background_location_permission_content": "āš€ā¸žā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ēā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸‚ā¸“ā¸°ā¸—ā¸ĩāšˆā¸Ŗā¸ąā¸™āšƒā¸™ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡ Immich ā¸•āš‰ā¸­ā¸‡ā¸Ŗā¸šāš‰ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸—ā¸ĩāšˆāšā¸Ąāšˆā¸ĸ⏺⏕ā¸Ĩā¸­ā¸”āš€ā¸§ā¸Ĩ⏞ āš€ā¸žā¸ˇāšˆā¸­ā¸ˆā¸°ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­āšˆā¸˛ā¸™ā¸Šā¸ˇāšˆā¸­ Wi-Fi", + "backup": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_album_selection_page_albums_device": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸šā¸™āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ ({count})", "backup_album_selection_page_albums_tap": "ā¸ā¸”āš€ā¸žā¸ˇāšˆā¸­ā¸Ŗā¸§ā¸Ą ⏁⏔ā¸Ēā¸­ā¸‡ā¸„ā¸Ŗā¸ąāš‰ā¸‡āš€ā¸žā¸ˇāšˆā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "backup_album_selection_page_assets_scatter": "ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏞⏪ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ā¸Ŗā¸°ā¸ˆā¸˛ā¸ĸāš„ā¸›āšƒā¸™ā¸Ģā¸Ĩ⏞ā¸ĸā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸–ā¸šā¸ā¸Ŗā¸§ā¸Ąā¸Ģ⏪⏎⏭ā¸ĸā¸āš€ā¸§āš‰ā¸™āšƒā¸™ā¸ā¸Ŗā¸°ā¸šā¸§ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", @@ -496,15 +525,16 @@ "backup_controller_page_background_is_on": "⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸›ā¸´ā¸”ā¸­ā¸ĸā¸šāšˆ", "backup_controller_page_background_turn_off": "ā¸›ā¸´ā¸”ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", "backup_controller_page_background_turn_on": "āš€ā¸›ā¸´ā¸”ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗāš€ā¸šā¸ˇāš‰ā¸­ā¸‡ā¸Ģā¸Ĩā¸ąā¸‡", - "backup_controller_page_background_wifi": "ā¸šā¸™ WiFi āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", + "backup_controller_page_background_wifi": "ā¸šā¸™ Wi-Fi āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", "backup_controller_page_backup": "ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_controller_page_backup_selected": "⏗ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁: ", "backup_controller_page_backup_sub": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸Ēā¸ŗā¸Ŗā¸­ā¸‡āšā¸Ĩāš‰ā¸§", "backup_controller_page_created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­: {date}", "backup_controller_page_desc_backup": "āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛āš€ā¸žā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšƒā¸Ģā¸Ąāšˆāš„ā¸›ā¸ĸā¸ąā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒāš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”āšā¸­ā¸ž", - "backup_controller_page_excluded": "ā¸–ā¸šā¸ā¸ĸā¸āš€ā¸§āš‰ā¸™: ", + "backup_controller_page_excluded": "ā¸ĸā¸āš€ā¸§āš‰ā¸™: ", "backup_controller_page_failed": "ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧ ({count})", "backup_controller_page_filename": "ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", "backup_controller_page_none_selected": "āš„ā¸Ąāšˆā¸Ąā¸ĩ⏗ā¸ĩāšˆāš€ā¸Ĩ⏎⏭⏁", "backup_controller_page_remainder": "⏗ā¸ĩāšˆāš€ā¸Ģā¸Ĩ⏎⏭", @@ -526,7 +556,12 @@ "backup_manual_success": "ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "backup_manual_title": "ā¸Ēā¸–ā¸˛ā¸™ā¸°ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔", "backup_options_page_title": "ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁⏁⏞⏪ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ", + "backup_setting_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”āšƒā¸™ā¸‰ā¸˛ā¸ā¸Ģā¸™āš‰ā¸˛ āšā¸Ĩā¸°ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡", "backward": "⏁ā¸Ĩā¸ąā¸šā¸Ģā¸Ĩā¸ąā¸‡", + "biometric_auth_enabled": "ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒāš€ā¸žā¸ˇāšˆā¸­ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸–ā¸šā¸āš€ā¸›ā¸´ā¸”", + "biometric_locked_out": "ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒāš€ā¸žā¸ˇāšˆā¸­ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸–ā¸šā¸ā¸Ĩāš‡ā¸­ā¸„", + "biometric_no_options": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒāš€ā¸žā¸ˇāšˆā¸­ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", + "biometric_not_available": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒāš€ā¸žā¸ˇāšˆā¸­ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸šā¸¸ā¸„ā¸„ā¸Ĩāš„ā¸”āš‰ā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸™ā¸ĩāš‰", "birthdate_saved": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸ā¸§ā¸ąā¸™āš€ā¸ā¸´ā¸”āšā¸Ĩāš‰ā¸§", "birthdate_set_description": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš€ā¸ā¸´ā¸”ā¸ˆā¸°ā¸™ā¸ŗā¸Ąā¸˛āšƒā¸Šāš‰āšƒā¸™ā¸ā¸˛ā¸Ŗā¸„ā¸ŗā¸™ā¸§ā¸“ā¸­ā¸˛ā¸ĸā¸¸ā¸‚ā¸­ā¸‡ā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏙ā¸ĩāš‰āšƒā¸™ā¸‚ā¸“ā¸°ā¸—ā¸ĩāšˆā¸–āšˆā¸˛ā¸ĸā¸Ŗā¸šā¸›", "blurred_background": "ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡āšā¸šā¸šāš€ā¸šā¸Ĩ⏭", @@ -556,14 +591,19 @@ "camera_model": "ā¸Ŗā¸¸āšˆā¸™ā¸ā¸Ĩāš‰ā¸­ā¸‡", "cancel": "ā¸ĸā¸āš€ā¸Ĩ⏴⏁", "cancel_search": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", + "canceled": "ā¸ĸā¸āš€ā¸Ĩ⏴⏁", "cannot_merge_people": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ŗā¸§ā¸Ąā¸ā¸Ĩā¸¸āšˆā¸Ąā¸„ā¸™āš„ā¸”āš‰", "cannot_undo_this_action": "⏁⏞⏪⏁⏪⏰⏗⏺⏙ā¸ĩāš‰āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šāš„ā¸”āš‰!", "cannot_update_the_description": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸žāš€ā¸”ā¸—ā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸā¸”āš„ā¸”āš‰", + "cast": "āšā¸„ā¸Ēā¸•āšŒ", + "cast_description": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸›ā¸Ĩ⏞ā¸ĸā¸—ā¸˛ā¸‡āšā¸„ā¸Ēā¸•āšŒ", "change_date": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ", + "change_description": "āšā¸āš‰āš„ā¸‚ā¸„ā¸ŗā¸­ā¸˜ā¸´ā¸šā¸˛ā¸ĸ", + "change_display_order": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙ā¸Ĩā¸ŗā¸”ā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸Ēā¸”ā¸‡ā¸œā¸Ĩ", "change_expiration_time": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏", "change_location": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸•āšā¸˛āšā¸Ģā¸™āšˆā¸‡", "change_name": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸Šā¸ˇāšˆā¸­", - "change_name_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸Šā¸ˇāšˆā¸­āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", + "change_name_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸Šā¸ˇāšˆā¸­ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "change_password": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "change_password_description": "ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸„ā¸Ŗā¸ąāš‰ā¸‡āšā¸Ŗā¸ ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸ˆā¸•āš‰ā¸­ā¸‡āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸ āš‚ā¸›ā¸Ŗā¸”ā¸›āš‰ā¸­ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆā¸”āš‰ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸‡", "change_password_form_confirm_password": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", @@ -574,6 +614,9 @@ "change_pin_code": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "change_your_password": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "changed_visibility_successfully": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗā¸Ąā¸­ā¸‡āš€ā¸Ģāš‡ā¸™āš€ā¸Ŗā¸ĩā¸ĸā¸šā¸Ŗāš‰ā¸­ā¸ĸāšā¸Ĩāš‰ā¸§", + "check_corrupt_asset_backup": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚ā¸Ē⏺⏪⏭⏇ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸œā¸´ā¸”ā¸›ā¸ā¸•ā¸´", + "check_corrupt_asset_backup_button": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚", + "check_corrupt_asset_backup_description": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ Wi-Fi āšā¸Ĩ⏰ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸–ā¸šā¸ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāšā¸Ĩāš‰ā¸§āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸­ā¸˛ā¸ˆāšƒā¸Šāš‰āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ĩ⏞ā¸ĸ⏙⏞⏗ā¸ĩ", "check_logs": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸šā¸ąā¸™ā¸—ā¸ļ⏁", "choose_matching_people_to_merge": "āš€ā¸Ĩ⏎⏭⏁⏄⏙⏗ā¸ĩāšˆā¸•ā¸Ŗā¸‡ā¸ā¸ąā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸Ŗā¸§ā¸Ąāš€ā¸‚āš‰ā¸˛ā¸”āš‰ā¸§ā¸ĸā¸ā¸ąā¸™", "city": "āš€ā¸Ąā¸ˇā¸­ā¸‡", @@ -582,6 +625,14 @@ "clear_all_recent_searches": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸›ā¸Ŗā¸°ā¸§ā¸ąā¸•ā¸´ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", "clear_message": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ą", "clear_value": "ā¸Ĩāš‰ā¸˛ā¸‡ā¸„āšˆā¸˛", + "client_cert_dialog_msg_confirm": "āš€ā¸Ēā¸Ŗāš‡ā¸ˆ", + "client_cert_enter_password": "āšƒā¸Ēāšˆā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", + "client_cert_import": "ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛", + "client_cert_import_success_msg": "ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "client_cert_invalid_msg": "āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ ā¸Ģ⏪⏎⏭⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", + "client_cert_remove_msg": "ā¸Ĩā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "client_cert_subtitle": "ā¸Ŗā¸­ā¸‡ā¸Ŗā¸ąā¸šāš€ā¸‰ā¸žā¸˛ā¸° PKCS12 (.p12, .pfx) āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ ā¸ā¸˛ā¸Ŗā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛/ā¸Ĩā¸šāšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸—ā¸ŗāš„ā¸”āš‰ā¸āšˆā¸­ā¸™ā¸Ĩāš‡ā¸­ā¸„ā¸­ā¸´ā¸™āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", + "client_cert_title": "āšƒā¸šā¸Ŗā¸ąā¸šā¸Ŗā¸­ā¸‡ SSL āš„ā¸„ā¸Ĩāš€ā¸­ā¸™ā¸•āšŒ", "clockwise": "ā¸•ā¸˛ā¸Ąāš€ā¸‚āš‡ā¸Ąā¸™ā¸˛ā¸Ŧ⏴⏁⏞", "close": "⏛⏴⏔", "collapse": "ā¸ĸāšˆā¸­", @@ -602,6 +653,10 @@ "confirm_keep_this_delete_others": "⏈⏰ā¸Ĩā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸™ā¸Ŗā¸˛ā¸ĸ⏁⏞⏪ āšā¸Ĩ⏰ā¸ĸā¸āš€ā¸§āš‰ā¸™ā¸Ēā¸ˇāšˆā¸­ā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆāšƒā¸Šāšˆāš„ā¸Ģā¸Ąā¸—ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•āšˆā¸­?", "confirm_new_pin_code": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "confirm_password": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", + "confirm_tag_face": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāšā¸—āš‡ā¸āšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ā¸”āš‰ā¸§ā¸ĸā¸Šā¸ˇāšˆā¸­ {name} ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ", + "confirm_tag_face_unnamed": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāšā¸—āš‡ā¸āšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ", + "connected_device": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸—ā¸ĩāšˆāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­āšā¸Ĩāš‰ā¸§", + "connected_to": "āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­āš„ā¸›ā¸ĸā¸ąā¸‡", "contain": "ā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆ", "context": "ā¸šā¸Ŗā¸´ā¸šā¸—", "continue": "ā¸•āšˆā¸­āš„ā¸›", @@ -610,6 +665,7 @@ "control_bottom_app_bar_delete_from_local": "ā¸Ĩā¸šā¸ˆā¸˛ā¸āš€ā¸Ŗā¸ˇāšˆā¸­ā¸‡", "control_bottom_app_bar_edit_location": "āšā¸āš‰āš„ā¸‚ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", "control_bottom_app_bar_edit_time": "āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™āšā¸Ĩā¸°āš€ā¸§ā¸Ĩ⏞", + "control_bottom_app_bar_share_link": "āšā¸Šā¸ŖāšŒā¸Ĩā¸´ā¸‡ā¸„āšŒ", "control_bottom_app_bar_share_to": "āšā¸Šā¸ŖāšŒāšƒā¸Ģāš‰", "control_bottom_app_bar_trash_from_immich": "ā¸ĸāš‰ā¸˛ā¸ĸāš€ā¸‚āš‰ā¸˛ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "copied_image_to_clipboard": "ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸ā¸ ā¸˛ā¸žāš„ā¸›ā¸ĸā¸ąā¸‡ā¸„ā¸Ĩā¸´ā¸›ā¸šā¸­ā¸ŖāšŒā¸”āšā¸Ĩāš‰ā¸§", @@ -631,6 +687,7 @@ "create_link": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ĩā¸´ā¸‡ā¸āšŒ", "create_link_to_share": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ĩā¸´ā¸‡ā¸āšŒāš€ā¸žā¸ˇāšˆā¸­āšā¸Šā¸ŖāšŒ", "create_link_to_share_description": "ā¸œā¸šāš‰ā¸—ā¸ĩāšˆā¸Ąā¸ĩā¸Ĩā¸´ā¸‡ā¸āšŒ ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸”ā¸šā¸Ŗā¸šā¸›ā¸—ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āš„ā¸”āš‰", + "create_new": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšƒā¸Ģā¸Ąāšˆ", "create_new_person": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", "create_new_person_hint": "⏁⏺ā¸Ģ⏙⏔ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āšƒā¸Ģāš‰ā¸ā¸ąā¸šā¸„ā¸™āšƒā¸Ģā¸Ąāšˆ", "create_new_user": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆ", @@ -640,9 +697,12 @@ "create_tag_description": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸—āš‡ā¸āšƒā¸Ģā¸Ąāšˆ ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāšā¸—āš‡ā¸ā¸—ā¸ĩāšˆā¸‹āš‰ā¸­ā¸™ā¸ā¸ąā¸™ āš‚ā¸›ā¸Ŗā¸”ā¸›āš‰ā¸­ā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸‚ā¸­ā¸‡āšā¸—āš‡ā¸ ā¸Ŗā¸§ā¸Ąā¸–ā¸ļā¸‡āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ģā¸Ąā¸˛ā¸ĸā¸—ā¸ąā¸š", "create_user": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰", "created": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸Ĩāš‰ā¸§", + "created_at": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āš€ā¸Ąā¸ˇāšˆā¸­", + "crop": "⏄⏪⏭⏛", "curated_object_page_title": "ā¸Ēā¸´āšˆā¸‡ā¸‚ā¸­ā¸‡", "current_device": "ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", "current_pin_code": "⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN) ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", + "current_server_address": "⏗ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆāš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", "custom_locale": "ā¸›ā¸Ŗā¸ąā¸šā¸ ā¸˛ā¸Šā¸˛ā¸—āš‰ā¸­ā¸‡ā¸–ā¸´āšˆā¸™āš€ā¸­ā¸‡", "custom_locale_description": "āšƒā¸Šāš‰ā¸Ŗā¸šā¸›āšā¸šā¸šā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāšā¸Ĩā¸°ā¸•ā¸ąā¸§āš€ā¸Ĩā¸‚ā¸ˆā¸˛ā¸ā¸ ā¸˛ā¸Šā¸˛āšā¸Ĩā¸°ā¸‚ā¸­ā¸šāš€ā¸‚ā¸•", "daily_title_text_date": "E dd MMM", @@ -694,6 +754,7 @@ "disallow_edits": "āš„ā¸Ąāšˆā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸Ģāš‰āšā¸āš‰āš„ā¸‚", "discord": "⏔⏴ā¸Ēā¸„ā¸­ā¸ŖāšŒā¸”", "discover": "ā¸„āš‰ā¸™ā¸žā¸š", + "discovered_devices": "ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒ", "dismiss_all_errors": "ā¸›ā¸ā¸´āš€ā¸Ēā¸˜ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "dismiss_error": "ā¸›ā¸ā¸´āš€ā¸Ēā¸˜ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "display_options": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗāšā¸Ē⏔⏇", @@ -704,12 +765,25 @@ "documentation": "āš€ā¸­ā¸ā¸Ē⏞⏪", "done": "ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "download": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", + "download_canceled": "ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ā¸ĸā¸āš€ā¸Ĩ⏴⏁", + "download_complete": "ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš€ā¸Ēā¸Ŗāš‡ā¸ˆā¸Ēā¸´āš‰ā¸™", + "download_enqueue": "ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔⏭ā¸ĸā¸šāšˆāšƒā¸™ā¸„ā¸´ā¸§", + "download_error": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", + "download_failed": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "download_finished": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš€ā¸Ēā¸Ŗāš‡ā¸ˆā¸Ēā¸´āš‰ā¸™", "download_include_embedded_motion_videos": "ā¸Ŗā¸§ā¸Ąā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸ā¸ąā¸‡ā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸ ā¸˛ā¸žāš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", "download_include_embedded_motion_videos_description": "ā¸Ŗā¸§ā¸Ąā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸ā¸ąā¸‡ā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸ ā¸˛ā¸žāš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģā¸§āš€ā¸Ąā¸ˇāšˆā¸­ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", + "download_notfound": "āš„ā¸Ąāšˆā¸žā¸šā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", + "download_paused": "ā¸Ģā¸ĸā¸¸ā¸”ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”ā¸Šā¸ąāšˆā¸§ā¸„ā¸Ŗā¸˛ā¸§", "download_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", "download_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", + "download_started": "āš€ā¸Ŗā¸´āšˆā¸Ąā¸ā¸˛ā¸Ŗā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", + "download_sucess": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", + "download_sucess_android": "ā¸Ēā¸ˇāšˆā¸­ā¸–ā¸šā¸ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš„ā¸›ā¸ĸā¸ąā¸‡ DCIM/Immich", + "download_waiting_to_retry": "⏪⏭ā¸Ĩā¸­ā¸‡āšƒā¸Ģā¸Ąāšˆ", "downloading": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔", "downloading_asset_filename": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ {filename}", + "downloading_media": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­", "drop_files_to_upload": "ā¸§ā¸˛ā¸‡āš„ā¸Ÿā¸ĨāšŒāšƒā¸™ā¸Šāšˆā¸­ā¸‡ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔", "duplicates": "⏪⏞ā¸ĸ⏁⏞⏪⏗ā¸ĩāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™", "duplicates_description": "āšā¸āš‰āš„ā¸‚āšā¸•āšˆā¸Ĩ⏰⏁ā¸Ĩā¸¸āšˆā¸Ąāš‚ā¸”ā¸ĸā¸Ŗā¸°ā¸šā¸¸ā¸§āšˆā¸˛ā¸ā¸Ĩā¸¸āšˆā¸Ąāšƒā¸”ā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ā¸Ģā¸˛ā¸ā¸Ąā¸ĩ", @@ -719,6 +793,8 @@ "edit_avatar": "āšā¸āš‰āš„ā¸‚ā¸•ā¸ąā¸§ā¸Ĩ⏰⏄⏪", "edit_date": "āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ", "edit_date_and_time": "āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāšā¸Ĩā¸°āš€ā¸§ā¸Ĩ⏞", + "edit_description": "āšā¸āš‰āš„ā¸‚ā¸„ā¸ŗā¸­ā¸˜ā¸´ā¸šā¸˛ā¸ĸ", + "edit_description_prompt": "āš‚ā¸›ā¸Ŗā¸”āš€ā¸Ĩā¸ˇāšˆā¸­ā¸ā¸„ā¸ŗā¸­ā¸˜ā¸´ā¸šā¸˛ā¸ĸāšƒā¸Ģā¸Ąāšˆ", "edit_exclusion_pattern": "āšā¸āš‰āš„ā¸‚ā¸‚āš‰ā¸­ā¸ĸā¸āš€ā¸§āš‰ā¸™", "edit_faces": "āšā¸āš‰āš„ā¸‚ā¸Ģā¸™āš‰ā¸˛", "edit_import_path": "āšā¸āš‰āš„ā¸‚ā¸žā¸˛ā¸˜ā¸™āšā¸˛āš€ā¸‚āš‰ā¸˛", @@ -739,15 +815,24 @@ "editor_crop_tool_h2_aspect_ratios": "ā¸­ā¸ąā¸•ā¸Ŗā¸˛ā¸Ēāšˆā¸§ā¸™ā¸ ā¸˛ā¸ž", "editor_crop_tool_h2_rotation": "⏁⏞⏪ā¸Ģā¸Ąā¸¸ā¸™", "email": "⏭ā¸ĩāš€ā¸Ąā¸Ĩ", + "email_notifications": "āšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™ā¸œāšˆā¸˛ā¸™ā¸­ā¸ĩāš€ā¸Ąā¸Ĩ", + "empty_folder": "āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ā¸§āšˆā¸˛ā¸‡āš€ā¸›ā¸Ĩāšˆā¸˛", "empty_trash": "ā¸—ā¸´āš‰ā¸‡ā¸ˆā¸˛ā¸ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "empty_trash_confirmation": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩāš‰ā¸˛ā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰ ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸Ĩā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰⏭⏭⏁⏈⏞⏁ Immich ⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ\nā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰āš„ā¸”āš‰!", "enable": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", + "enable_biometric_auth_description": "āšƒā¸Ēāšˆā¸žā¸´ā¸™āš€ā¸žā¸ˇāšˆā¸­āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒāš€ā¸žā¸ˇāšˆā¸­ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", "enabled": "āš€ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "end_date": "ā¸§ā¸ąā¸™ā¸Ēā¸´āš‰ā¸™ā¸Ē⏏⏔", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "⏪⏭⏄⏴⏧", + "enter_wifi_name": "āšƒā¸Ēāšˆā¸Šā¸ˇāšˆā¸­ Wi-Fi", + "enter_your_pin_code": "āšƒā¸Ēāšˆā¸žā¸´ā¸™āš‚ā¸„āš‰ā¸”", + "enter_your_pin_code_subtitle": "āšƒā¸Ēāšˆā¸žā¸´ā¸™āš‚ā¸„āš‰ā¸”āš€ā¸žā¸ˇāšˆā¸­āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„", "error": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", + "error_change_sort_album": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸ĩā¸ĸ⏇ā¸Ĩā¸ŗā¸”ā¸ąā¸šā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "error_delete_face": "āš€ā¸ā¸´ā¸”āš€ā¸­ā¸­āš€ā¸Ŗā¸­ā¸ŖāšŒ āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸­ā¸­ā¸āš„ā¸”āš‰", "error_loading_image": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔⏪⏰ā¸Ģā¸§āšˆā¸˛ā¸‡āš‚ā¸Ģā¸Ĩā¸”ā¸ ā¸˛ā¸ž", + "error_saving_image": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔⏪⏰ā¸Ģā¸§āšˆā¸˛ā¸‡āš€ā¸‹ā¸Ÿā¸ ā¸˛ā¸ž: {error}", + "error_tag_face_bounding_box": "ā¸ā¸˛ā¸Ŗāšā¸—āš‡ā¸āšƒā¸šā¸Ģā¸™āš‰ā¸˛ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ - āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸ĩā¸ā¸Ŗā¸­ā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛āš„ā¸”āš‰", "error_title": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "errors": { "cannot_navigate_next_asset": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āš€ā¸Ēāš‰ā¸™ā¸—ā¸˛ā¸‡āš„ā¸”āš‰", @@ -755,7 +840,7 @@ "cant_apply_changes": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩ⏇", "cant_change_activity": "Can't {enabled, select, true {disable} other {enable}} activity", "cant_change_asset_favorite": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆā¸Šā¸ˇāšˆā¸™ā¸Šā¸­ā¸šāš„ā¸”āš‰", - "cant_change_metadata_assets_count": "Can't change metadata of {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ metadata ⏂⏭⏇ {count, plural, one {# ā¸Ēā¸ˇāšˆā¸­} other {# ā¸Ēā¸ˇāšˆā¸­}}", "cant_get_faces": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸ĩā¸ĸā¸ā¸”ā¸šāšƒā¸šā¸Ģā¸™āš‰ā¸˛", "cant_get_number_of_comments": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Ŗā¸ĩā¸ĸā¸ā¸”ā¸šā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸„ā¸§ā¸˛ā¸Ąā¸„ā¸´ā¸”āš€ā¸Ģāš‡ā¸™āš„ā¸”āš‰", "cant_search_people": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸„ā¸™āš„ā¸”āš‰", @@ -775,10 +860,12 @@ "failed_to_keep_this_delete_others": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸āš‡ā¸šā¸Ģ⏪⏎⏭ā¸Ĩā¸šāš„ā¸”āš‰", "failed_to_load_asset": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­āš„ā¸”āš‰", "failed_to_load_assets": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­āš„ā¸”āš‰", + "failed_to_load_notifications": "āš‚ā¸Ģā¸Ĩā¸”ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "failed_to_load_people": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš‚ā¸Ģā¸Ĩā¸”ā¸šā¸¸ā¸„ā¸„ā¸Ĩāš„ā¸”āš‰", "failed_to_remove_product_key": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩ⏚ product key āš„ā¸”āš‰", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "ā¸­ā¸ąā¸žāš€ā¸”ā¸—ā¸Ēā¸–ā¸˛ā¸™ā¸°ā¸ā¸˛ā¸Ŗāšā¸ˆāš‰ā¸‡āš€ā¸•ā¸ˇā¸­ā¸™āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "import_path_already_exists": "ā¸žā¸˛ā¸˜ā¸™ā¸ŗāš€ā¸‚āš‰ā¸˛ā¸™ā¸ĩāš‰ā¸Ąā¸ĩ⏭ā¸ĸā¸šāšˆāšā¸Ĩāš‰ā¸§", "incorrect_email_or_password": "⏭ā¸ĩāš€ā¸Ąā¸Ĩā¸Ģ⏪⏎⏭⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "paths_validation_failed": "ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚ {paths, plural, one {# path} other {# paths}} ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", @@ -795,6 +882,7 @@ "unable_to_archive_unarchive": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸—ā¸ŗā¸Ŗā¸˛ā¸ĸ⏁⏞⏪ {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸šā¸—ā¸šā¸˛ā¸—ā¸œā¸šāš‰āšƒā¸Šāš‰āšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāš„ā¸”āš‰", "unable_to_change_date": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš„ā¸”āš‰", + "unable_to_change_description": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸„ā¸ŗā¸­ā¸˜ā¸´ā¸šā¸˛ā¸ĸ", "unable_to_change_favorite": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩ⏇ā¸Ēā¸ˇāšˆā¸­ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”āš„ā¸”āš‰", "unable_to_change_location": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸•āšā¸˛āšā¸Ģā¸™āšˆā¸‡āš„ā¸”āš‰", "unable_to_change_password": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸”āš‰", @@ -838,6 +926,7 @@ "unable_to_remove_partner": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩā¸šā¸„ā¸šāšˆā¸Ģā¸šāš„ā¸”āš‰", "unable_to_remove_reaction": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ĩ⏚ reaction āš„ā¸”āš‰", "unable_to_reset_password": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšƒā¸Ģā¸Ąāšˆāš„ā¸”āš‰", + "unable_to_reset_pin_code": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ŗā¸ĩāš€ā¸‹āš‡ā¸•ā¸žā¸´ā¸™āš‚ā¸„āš‰ā¸”", "unable_to_resolve_duplicate": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸‚ā¸­ā¸‡ā¸‹āš‰ā¸ŗāš„ā¸”āš‰", "unable_to_restore_assets": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Ŗā¸ĩā¸ĸ⏁⏄⏎⏙ā¸Ēā¸ˇāšˆā¸­āš„ā¸”āš‰", "unable_to_restore_trash": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸Ŗā¸ĩā¸ĸā¸ā¸„ā¸ˇā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āš„ā¸”āš‰", @@ -865,6 +954,7 @@ "unable_to_update_user": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸žāš€ā¸”ā¸—ā¸œā¸šāš‰āšƒā¸Šāš‰āš„ā¸”āš‰", "unable_to_upload_file": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āš„ā¸”āš‰" }, + "exif": "Exif", "exif_bottom_sheet_description": "āš€ā¸žā¸´āšˆā¸Ąā¸„ā¸ŗā¸­ā¸˜ā¸´ā¸šā¸˛ā¸ĸ", "exif_bottom_sheet_details": "⏪⏞ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔", "exif_bottom_sheet_location": "ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", @@ -886,9 +976,13 @@ "extension": "ā¸Ēāšˆā¸§ā¸™ā¸•āšˆā¸­ā¸‚ā¸ĸ⏞ā¸ĸ", "external": "⏠⏞ā¸ĸ⏙⏭⏁", "external_libraries": "⏠⏞ā¸ĸ⏙⏭⏁⏄ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", - "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": "ā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸ ā¸˛ā¸ĸ⏙⏭⏁", + "external_network_sheet_info": "āš€ā¸Ąā¸ˇāšˆā¸­āš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ Wi-Fi ⏗ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āš„ā¸§āš‰ āšā¸­ā¸žā¸ˆā¸°āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸œāšˆā¸˛ā¸™ URL ā¸”āš‰ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸‡ā¸•ā¸˛ā¸Ąā¸Ĩā¸ŗā¸”ā¸ąā¸š", "face_unassigned": "āš„ā¸Ąāšˆā¸ā¸ŗā¸Ģā¸™ā¸”ā¸Ąā¸­ā¸šā¸Ģā¸Ąā¸˛ā¸ĸ", + "failed": "ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", + "failed_to_authenticate": "⏁⏞⏪ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸•ā¸™āš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "failed_to_load_assets": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­", + "failed_to_load_folder": "āš‚ā¸Ģā¸Ĩā¸”āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒāš„ā¸Ąāšˆā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "favorite": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", "favorite_or_unfavorite_photo": "āš‚ā¸›ā¸Ŗā¸”ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆāš‚ā¸›ā¸Ŗā¸”ā¸ ā¸˛ā¸ž", "favorites": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", @@ -900,18 +994,26 @@ "file_name_or_extension": "ā¸™ā¸˛ā¸Ąā¸Ē⏁⏏ā¸Ĩā¸Ģā¸Ŗā¸ˇā¸­ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ", "filename": "ā¸Šā¸ˇāšˆā¸­āš„ā¸Ÿā¸ĨāšŒ", "filetype": "ā¸Šā¸™ā¸´ā¸”āš„ā¸Ÿā¸ĨāšŒ", + "filter": "ā¸•ā¸ąā¸§ā¸ā¸Ŗā¸­ā¸‡", "filter_people": "ā¸ā¸Ŗā¸­ā¸‡ā¸œā¸šāš‰ā¸„ā¸™", + "filter_places": "⏁⏪⏭⏇ā¸Ē⏖⏞⏙⏗ā¸ĩāšˆ", "find_them_fast": "ā¸„āš‰ā¸™ā¸Ģā¸˛āš‚ā¸”ā¸ĸā¸Šā¸ˇāšˆā¸­ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ŗā¸§ā¸”āš€ā¸Ŗāš‡ā¸§", "fix_incorrect_match": "āšā¸āš‰āš„ā¸‚ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸šā¸„ā¸šāšˆā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", + "folder": "āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒ", + "folder_not_found": "āš„ā¸Ąāšˆā¸žā¸šāš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒ", "folders": "āš‚ā¸Ÿā¸ĨāšŒāš€ā¸”ā¸­ā¸ŖāšŒ", "folders_feature_description": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸ĩā¸ĸā¸ā¸”ā¸šā¸Ąā¸¸ā¸Ąā¸Ąā¸­ā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸ ā¸˛ā¸žā¸–āšˆā¸˛ā¸ĸāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­āšƒā¸™ā¸Ŗā¸°ā¸šā¸šāš„ā¸Ÿā¸ĨāšŒ", "forward": "āš„ā¸›ā¸‚āš‰ā¸˛ā¸‡ā¸Ģā¸™āš‰ā¸˛", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸™ā¸ĩāš‰ā¸•āš‰ā¸­ā¸‡āš‚ā¸Ģā¸Ĩā¸”ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏈⏞⏁ Google āš€ā¸žā¸ˇāšˆā¸­ā¸—ā¸ŗā¸‡ā¸˛ā¸™", "general": "ā¸—ā¸ąāšˆā¸§āš„ā¸›", "get_help": "ā¸‚ā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸Šāšˆā¸§ā¸ĸāš€ā¸Ģā¸Ĩ⏎⏭", + "get_wifiname_error": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ŗā¸ąā¸šā¸Šā¸ˇāšˆā¸­ Wi-Fi ⏁⏪⏏⏓⏞ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸ā¸˛ā¸Ŗāšƒā¸Ģāš‰ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšā¸­ā¸ž āšā¸Ĩ⏰ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸§āšˆā¸˛ Wi-Fi āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸­ā¸ĸā¸šāšˆ", "getting_started": "āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "go_back": "⏁ā¸Ĩā¸ąā¸š", "go_to_folder": "āš„ā¸›ā¸—ā¸ĩāšˆāš‚ā¸Ÿā¸ĨāšŒāš€ā¸”ā¸­ā¸ŖāšŒ", "go_to_search": "⏁ā¸Ĩā¸ąā¸šāš„ā¸›ā¸ĸā¸ąā¸‡ā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞", + "grant_permission": "āšƒā¸Ģāš‰ā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•", "group_albums_by": "ā¸ˆā¸ąā¸”ā¸ā¸Ĩā¸¸āšˆā¸Ąā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸•ā¸˛ā¸Ą", "group_country": "ā¸ˆā¸ąā¸”āš€ā¸Ŗā¸ĩā¸ĸ⏇⏁ā¸Ĩā¸¸āšˆā¸Ąā¸•ā¸˛ā¸Ąā¸›ā¸Ŗā¸°āš€ā¸—ā¸¨", "group_no": "āš„ā¸Ąāšˆā¸ˆā¸ąā¸”ā¸ā¸Ĩā¸¸āšˆā¸Ą", @@ -921,6 +1023,11 @@ "haptic_feedback_switch": "āš€ā¸›ā¸´ā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸­ā¸šā¸Ēā¸™ā¸­ā¸‡āšā¸šā¸šā¸Ēā¸ąā¸Ąā¸œā¸ąā¸Ē", "haptic_feedback_title": "ā¸ā¸˛ā¸Ŗā¸•ā¸­ā¸šā¸Ēā¸™ā¸­ā¸‡āšā¸šā¸šā¸Ēā¸ąā¸Ąā¸œā¸ąā¸Ē", "has_quota": "āš€ā¸Ģā¸Ĩā¸ˇā¸­ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆ", + "header_settings_add_header_tip": "āš€ā¸žā¸´āšˆā¸Ą Header", + "header_settings_field_validator_msg": "ā¸„āšˆā¸˛ā¸•āš‰ā¸­ā¸‡āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸‡āš€ā¸›ā¸Ĩāšˆā¸˛", + "header_settings_header_name_input": "ā¸Šā¸ˇāšˆā¸­ Header", + "header_settings_header_value_input": "ā¸„āšˆā¸˛ Header", + "headers_settings_tile_title": "ā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡ proxy headers", "hi_user": "ā¸Ēā¸§ā¸ąā¸Ē⏔ā¸ĩ⏄⏏⏓ {name} {email}", "hide_all_people": "ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "hide_gallery": "ā¸‹āšˆā¸­ā¸™ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", @@ -929,22 +1036,28 @@ "hide_person": "ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", "hide_unnamed_people": "ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆāš„ā¸Ąāšˆāš„ā¸”āš‰ā¸Ŗā¸°ā¸šā¸¸ā¸Šā¸ˇāšˆā¸­", "home_page_add_to_album_conflicts": "āš€ā¸žā¸´āšˆā¸Ą {added} ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą {album}. {failed} ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪⏭ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸­ā¸ĸā¸šāšˆāšā¸Ĩāš‰ā¸§", - "home_page_add_to_album_err_local": " ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸šā¸™āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡āš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", + "home_page_add_to_album_err_local": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸Ēā¸ˇāšˆā¸­ā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒāš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą ā¸‚āš‰ā¸˛ā¸Ą", "home_page_add_to_album_success": "āš€ā¸žā¸´āšˆā¸Ąā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ {added} āš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą {album}", - "home_page_album_err_partner": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", + "home_page_album_err_partner": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡ā¸„ā¸šāšˆā¸Ģā¸šāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "home_page_archive_err_local": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", - "home_page_archive_err_partner": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸āš‡ā¸šā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", + "home_page_archive_err_partner": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸āš‡ā¸šā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡ā¸„ā¸šāšˆā¸Ģā¸šāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "home_page_building_timeline": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ timeline", - "home_page_delete_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_favorite_err_partner": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡ā¸„ā¸šāšˆā¸Ģā¸šāšƒā¸™ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”āš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "home_page_first_time_notice": "ā¸–āš‰ā¸˛ā¸„ā¸Ŗā¸ąāš‰ā¸‡ā¸™ā¸ĩāš‰āš€ā¸›āš‡ā¸™ā¸„ā¸Ŗā¸ąāš‰ā¸‡āšā¸Ŗā¸ā¸—ā¸ĩāšˆāšƒā¸Šāš‰āšā¸­ā¸›ā¸™ā¸ĩāš‰ ā¸ā¸Ŗā¸¸ā¸“ā¸˛āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ēā¸ŗā¸Ŗā¸­ā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ āš„ā¸—ā¸ĄāšŒāš„ā¸Ĩā¸™āšŒā¸ˆā¸°āš„ā¸”āš‰āš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", + "home_page_locked_error_local": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸ĸāš‰ā¸˛ā¸ĸā¸Ēā¸ˇāšˆā¸­ā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒāš„ā¸›ā¸ĸā¸ąā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„ ā¸‚āš‰ā¸˛ā¸Ą", + "home_page_locked_error_partner": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸žā¸´āšˆā¸Ąā¸Ēā¸ˇāšˆā¸­ā¸‚ā¸­ā¸‡ā¸„ā¸šāšˆā¸Ģā¸šāš„ā¸›ā¸ĸā¸ąā¸‡āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„āš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "home_page_share_err_local": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸Šā¸ŖāšŒā¸œāšˆā¸˛ā¸™ā¸Ĩā¸´ā¸‡ā¸„āšŒāš„ā¸”āš‰ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "home_page_upload_err_limit": "ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”āš„ā¸”āš‰ā¸Ąā¸˛ā¸ā¸Ēā¸¸ā¸”ā¸„ā¸Ŗā¸ąāš‰ā¸‡ā¸Ĩ⏰ 30 ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "host": "āš‚ā¸Žā¸Ēā¸•āšŒ", "hour": "ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡", + "id": "āš„ā¸­ā¸”ā¸ĩ", + "ignore_icloud_photos": "ā¸‚āš‰ā¸˛ā¸Ąā¸ ā¸˛ā¸žā¸šā¸™ iCloud", + "ignore_icloud_photos_description": "ā¸ ā¸˛ā¸žā¸—ā¸ĩāšˆā¸–ā¸šā¸āš€ā¸āš‡ā¸šā¸šā¸™ iCloud ā¸ˆā¸°āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔⏂ā¸ļāš‰ā¸™ Immich", "image": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", + "image_alt_text_date": "{isVideo, select, true {⏧⏴⏔ā¸ĩāš‚ā¸­} other {ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž}}ā¸–ā¸šā¸ā¸–āšˆā¸˛ā¸ĸāš€ā¸Ąā¸ˇāšˆā¸­ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸā¸ā¸ąā¸š {person1} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸā¸ā¸ąā¸š {person1} āšā¸Ĩ⏰ {person2} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸā¸ā¸ąā¸š {person1}, {person2},āšā¸Ĩ⏰ {person3} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", @@ -954,6 +1067,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1} āšā¸Ĩ⏰ {person2} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1}, {person2},āšā¸Ĩ⏰ {person3} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ā¸–āšˆā¸˛ā¸ĸāšƒā¸™ {city}, {country} ā¸ā¸ąā¸š {person1}, {person2}, āšā¸Ĩ⏰ {additionalCount, number} āšƒā¸™ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", + "image_saved_successfully": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸–ā¸šā¸āš€ā¸‹ā¸Ÿ", "image_viewer_page_state_provider_download_started": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩā¸”āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™", "image_viewer_page_state_provider_download_success": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ", "image_viewer_page_state_provider_share_error": "āšā¸Šā¸ŖāšŒā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", @@ -974,8 +1088,16 @@ "night_at_midnight": "ā¸—ā¸¸ā¸āš€ā¸—ā¸ĩāšˆā¸ĸ⏇⏄⏎⏙", "night_at_twoam": "ā¸—ā¸¸ā¸ā¸§ā¸ąā¸™āš€ā¸§ā¸Ĩ⏞⏕ā¸ĩ 2" }, + "invalid_date": "ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", + "invalid_date_format": "ā¸Ŗā¸šā¸›āšā¸šā¸šā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "invite_people": "āš€ā¸Šā¸´ā¸ā¸œā¸šāš‰ā¸„ā¸™", "invite_to_album": "āš€ā¸Šā¸´ā¸āš€ā¸‚āš‰ā¸˛ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", + "ios_debug_info_fetch_ran_at": "ā¸Ŗā¸ąā¸šā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸Ąā¸ˇāšˆā¸­ {dateTime}", + "ios_debug_info_last_sync_at": "ā¸‹ā¸´ā¸‡ā¸„āšŒā¸Ĩāšˆā¸˛ā¸Ē⏏⏔ {dateTime}", + "ios_debug_info_no_processes_queued": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸„ā¸´ā¸§āšƒā¸™ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡", + "ios_debug_info_no_sync_yet": "ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸Ąā¸ĩā¸‡ā¸˛ā¸™ā¸‹ā¸´ā¸‡ā¸„āšŒā¸Ŗā¸ąā¸™āšƒā¸™ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡", + "ios_debug_info_processes_queued": "{count} āš‚ā¸žā¸Ŗāš€ā¸‹ā¸Ēā¸Ŗā¸­ā¸„ā¸´ā¸§āšƒā¸™ā¸žā¸ˇāš‰ā¸™ā¸Ģā¸Ĩā¸ąā¸‡", + "ios_debug_info_processing_ran_at": "āš‚ā¸žā¸Ŗāš€ā¸‹ā¸Ēā¸Ŗā¸ąā¸™āš€ā¸Ąā¸ˇāšˆā¸­ {dateTime}", "items_count": "{count, plural, one {# ⏪⏞ā¸ĸ⏁⏞⏪} other {#⏪⏞ā¸ĸ⏁⏞⏪}}", "jobs": "⏇⏞⏙", "keep": "āš€ā¸āš‡ā¸š", @@ -984,6 +1106,9 @@ "kept_this_deleted_others": "āš€ā¸āš‡ā¸šāš€ā¸™ā¸ˇāš‰ā¸­ā¸Ģ⏞⏙ā¸ĩāš‰āšā¸Ĩ⏰ā¸Ĩ⏚ {count, plural, one {# Asset} other {# Asset}}", "keyboard_shortcuts": "ā¸›ā¸¸āšˆā¸Ąā¸žā¸´ā¸Ąā¸žāšŒā¸Ĩā¸ąā¸”", "language": "ā¸ ā¸˛ā¸Šā¸˛", + "language_no_results_subtitle": "ā¸ā¸Ŗā¸¸ā¸“ā¸˛ā¸›ā¸Ŗā¸ąā¸šāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸„ā¸ŗā¸„āš‰ā¸™ā¸Ģ⏞", + "language_no_results_title": "āš„ā¸Ąāšˆā¸žā¸šā¸ ā¸˛ā¸Šā¸˛", + "language_search_hint": "ā¸„āš‰ā¸™ā¸Ģā¸˛ā¸ ā¸˛ā¸Šā¸˛...", "language_setting_description": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ ā¸˛ā¸Šā¸˛ā¸—ā¸ĩāšˆā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗ", "last_seen": "āš€ā¸Ģāš‡ā¸™ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", "latest_version": "āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", @@ -1003,20 +1128,26 @@ "light": "ā¸Ēā¸§āšˆā¸˛ā¸‡", "like_deleted": "ā¸Ĩā¸šā¸—ā¸ĩāšˆā¸–ā¸šā¸āšƒā¸ˆāšā¸Ĩāš‰ā¸§", "link_motion_video": "ā¸Ĩā¸´ā¸‡ā¸āšŒā¸§ā¸´ā¸”ā¸ĩāš‚ā¸­āš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", - "link_options": "ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁ā¸Ĩā¸´ā¸‡ā¸āšŒ", "link_to_oauth": "ā¸Ĩā¸´ā¸‡ā¸āšŒāš„ā¸›ā¸ĸā¸ąā¸‡ OAuth", "linked_oauth_account": "ā¸Ĩā¸´ā¸‡ā¸āšŒā¸šā¸ąā¸ā¸Šā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰ OAuth", "list": "⏪⏞ā¸ĸ⏁⏞⏪", "loading": "⏁⏺ā¸Ĩā¸ąā¸‡āš‚ā¸Ģā¸Ĩ⏔", "loading_search_results_failed": "āš‚ā¸Ģā¸Ĩā¸”ā¸œā¸Ĩā¸ā¸˛ā¸Ŗā¸„āš‰ā¸™ā¸Ģ⏞ā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", - "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_asset_cast_failed": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸„ā¸Ēā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸–ā¸šā¸ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩā¸”āš„ā¸›ā¸ĸā¸ąā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", + "local_network": "āš€ā¸„ā¸Ŗā¸ˇā¸­ā¸‚āšˆā¸˛ā¸ĸ⏪⏰ā¸ĸā¸°āšƒā¸ā¸Ĩāš‰", + "local_network_sheet_info": "āšā¸­ā¸žā¸ˆā¸°ā¸—ā¸ŗā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­āš„ā¸›ā¸ĸā¸ąā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒā¸œāšˆā¸˛ā¸™ URL ⏙ā¸ĩāš‰āš€ā¸Ąā¸ˇāšˆā¸­āš€ā¸Šā¸ˇāšˆā¸­ā¸•āšˆā¸­ā¸ā¸ąā¸š Wi-Fi ⏗ā¸ĩāšˆāš€ā¸Ĩā¸ˇā¸­ā¸āš„ā¸§āš‰", + "location_permission": "ā¸ā¸˛ā¸Ŗā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡", + "location_permission_content": "āš€ā¸žā¸ˇāšˆā¸­āšƒā¸Šāš‰ā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸ā¸˛ā¸Ŗā¸Ēā¸ąā¸šāš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ Immich ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸•āšˆā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸—ā¸ĩāšˆāšā¸Ąāšˆā¸™ā¸ĸā¸ŗāš€ā¸žā¸ˇāšˆā¸­ā¸­āšˆā¸˛ā¸™ā¸Šā¸ˇāšˆā¸­ Wi-Fi ⏗ā¸ĩāšˆāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸­ā¸ĸā¸šāšˆ", "location_picker_choose_on_map": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸šā¸™āšā¸œā¸™ā¸—ā¸ĩāšˆ", "location_picker_latitude_error": "ā¸ā¸Ŗā¸¸ā¸“ā¸˛āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸°ā¸•ā¸´ā¸ˆā¸šā¸•ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "location_picker_latitude_hint": "āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸°ā¸•ā¸´ā¸ˆā¸šā¸•ā¸•ā¸Ŗā¸‡ā¸™ā¸ĩāš‰", "location_picker_longitude_error": "ā¸ā¸Ŗā¸¸ā¸“ā¸˛āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸­ā¸‡ā¸ˆā¸´ā¸ˆā¸šā¸•ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "location_picker_longitude_hint": "āš€ā¸žā¸´āšˆā¸Ąā¸Ĩā¸­ā¸‡ā¸ˆā¸´ā¸ˆā¸šā¸•ā¸•ā¸Ŗā¸‡ā¸™ā¸ĩāš‰", + "lock": "ā¸Ĩāš‡ā¸­ā¸„", + "locked_folder": "āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„", "log_out": "⏭⏭⏁⏈⏞⏁⏪⏰⏚⏚", "log_out_all_devices": "āšƒā¸Ģāš‰ā¸—ā¸¸ā¸ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸°ā¸šā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", + "logged_in_as": "{user} ⏁⏺ā¸Ĩā¸ąā¸‡ā¸Ĩāš‡ā¸­ā¸„ā¸­ā¸´ā¸™", "logged_out_all_devices": "ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸°ā¸šā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”āšā¸Ĩāš‰ā¸§", "logged_out_device": "ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸°ā¸šā¸šāšā¸Ĩāš‰ā¸§", "login": "āš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š", @@ -1059,7 +1190,6 @@ "manage_your_devices": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "manage_your_oauth_connection": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ OAuth ⏂⏭⏇⏄⏏⏓", "map": "āšā¸œā¸™ā¸—ā¸ĩāšˆ", - "map_assets_in_bound": "{count} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "map_assets_in_bounds": "{count} ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "map_cannot_get_user_location": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸Ģā¸˛ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰", "map_location_dialog_yes": "āšƒā¸Šāšˆ", @@ -1068,7 +1198,6 @@ "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_title": "ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸–ā¸šā¸ā¸›ā¸ā¸´āš€ā¸Ē⏘", "map_settings": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸œā¸™ā¸—ā¸ĩāšˆ", @@ -1079,7 +1208,7 @@ "map_settings_date_range_option_years": "{years} ⏛ā¸ĩā¸œāšˆā¸˛ā¸™ā¸Ąā¸˛", "map_settings_dialog_title": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛āšā¸œā¸™ā¸—ā¸ĩāšˆ", "map_settings_include_show_archived": "ā¸Ŗā¸§ā¸Ąāš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", - "map_settings_include_show_partners": "ā¸Ŗā¸˛ā¸Ąā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", + "map_settings_include_show_partners": "ā¸Ŗā¸§ā¸Ąā¸„ā¸šāšˆā¸Ģā¸š", "map_settings_only_show_favorites": "āšā¸Ē⏔⏇⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™", "map_settings_theme_settings": "⏘ā¸ĩā¸Ąāšā¸œā¸™ā¸—ā¸ĩāšˆ", "map_zoom_to_see_photos": "ā¸‹ā¸šā¸Ąā¸­ā¸­ā¸āš€ā¸žā¸ˇāšˆā¸­ā¸”ā¸šā¸Ŗā¸šā¸›", @@ -1106,12 +1235,17 @@ "model": "āš‚ā¸Ąāš€ā¸”ā¸Ĩ", "month": "āš€ā¸”ā¸ˇā¸­ā¸™", "more": "āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą", + "move": "ā¸ĸāš‰ā¸˛ā¸ĸ", + "move_off_locked_folder": "ā¸ĸāš‰ā¸˛ā¸ĸā¸­ā¸­ā¸ā¸ˆā¸˛ā¸āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„", + "move_to_locked_folder": "ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒā¸Ĩāš‡ā¸­ā¸„", "moved_to_trash": "ā¸—ā¸´āš‰ā¸‡ā¸Ĩā¸‡ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°āšā¸Ĩāš‰ā¸§", "multiselect_grid_edit_date_time_err_read_only": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰āš„ā¸‚ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸ⏧ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "multiselect_grid_edit_gps_err_read_only": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšā¸āš‰ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸‚ā¸­ā¸‡ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗāšā¸šā¸šā¸­āšˆā¸˛ā¸™ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸”ā¸ĩā¸ĸ⏧ ⏁⏺ā¸Ĩā¸ąā¸‡ā¸‚āš‰ā¸˛ā¸Ą", "my_albums": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸‚ā¸­ā¸‡ā¸‰ā¸ąā¸™", "name": "ā¸Šā¸ˇāšˆā¸­", "name_or_nickname": "ā¸Šā¸ˇāšˆā¸­ā¸Ģā¸Ŗā¸ˇā¸­ā¸Šā¸ˇāšˆā¸­āš€ā¸Ĩāšˆā¸™", + "networking_settings": "ā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­", + "networking_subtitle": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸›ā¸Ĩ⏞ā¸ĸā¸—ā¸˛ā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "never": "āš„ā¸Ąāšˆāš€ā¸„ā¸ĸ", "new_album": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąāšƒā¸Ģā¸Ąāšˆ", "new_api_key": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ API ⏄ā¸ĩā¸ĸāšŒāšƒā¸Ģā¸Ąāšˆ", @@ -1155,7 +1289,7 @@ "ok": "⏕⏁ā¸Ĩ⏇", "oldest_first": "āš€ā¸Ŗā¸ĩā¸ĸā¸‡āš€ā¸āšˆā¸˛ā¸Ēā¸¸ā¸”ā¸āšˆā¸­ā¸™", "onboarding": "ā¸ā¸˛ā¸Ŗāš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", - "onboarding_privacy_description": "⏄⏏⏓ā¸Ĩā¸ąā¸ā¸Šā¸“ā¸° (āš„ā¸Ąāšˆā¸ˆā¸ŗāš€ā¸›āš‡ā¸™) ā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸•āš‰ā¸­ā¸‡ā¸­ā¸˛ā¸¨ā¸ąā¸ĸ⏚⏪⏴⏁⏞⏪⏠⏞ā¸ĸ⏙⏭⏁ āšā¸Ĩ⏰ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰ā¸•ā¸Ĩā¸­ā¸”āš€ā¸§ā¸Ĩā¸˛āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚", + "onboarding_privacy_description": "⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒ (ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁) ā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸•āš‰ā¸­ā¸‡ā¸­ā¸˛ā¸¨ā¸ąā¸ĸ⏚⏪⏴⏁⏞⏪⏠⏞ā¸ĸ⏙⏭⏁ āšā¸Ĩ⏰ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸›ā¸´ā¸”āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰ā¸•ā¸Ĩā¸­ā¸”āš€ā¸§ā¸Ĩā¸˛āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗ", "onboarding_theme_description": "āš€ā¸Ĩ⏎⏭⏁⏘ā¸ĩā¸Ąā¸Ēā¸ĩ ⏄⏏⏓ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩā¸‡āš„ā¸”āš‰āšƒā¸™ā¸ ā¸˛ā¸ĸā¸Ģā¸Ĩā¸ąā¸‡āšƒā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", "onboarding_welcome_user": "ā¸ĸ⏴⏙⏔ā¸ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸šā¸„ā¸¸ā¸“ {user}", "online": "ā¸­ā¸­ā¸™āš„ā¸Ĩā¸™āšŒ", @@ -1172,20 +1306,20 @@ "other_variables": "ā¸•ā¸ąā¸§āšā¸›ā¸Ŗā¸­ā¸ˇāšˆā¸™", "owned": "āš€ā¸›āš‡ā¸™āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡", "owner": "āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡", - "partner": "ā¸žā¸˛ā¸ŖāšŒā¸—āš€ā¸™ā¸­ā¸ŖāšŒ", + "partner": "ā¸„ā¸šāšˆā¸Ģā¸š", "partner_can_access": "{partner} ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇", "partner_can_access_assets": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žāšā¸Ĩ⏰⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸ĸā¸āš€ā¸§āš‰ā¸™ā¸—ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆāšƒā¸™āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗāšā¸Ĩā¸°ā¸–ā¸šā¸ā¸Ĩā¸šā¸—ā¸´āš‰ā¸‡", "partner_can_access_location": "ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸—ā¸ĩāšˆā¸Ŗā¸šā¸›ā¸–ā¸šā¸ā¸–āšˆā¸˛ā¸ĸ", "partner_list_user_photos": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ {user}", "partner_list_view_all": "ā¸”ā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", - "partner_page_empty_message": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸–ā¸šā¸āšā¸Šā¸ŖāšŒā¸ā¸ąā¸šā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", + "partner_page_empty_message": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ĸā¸ąā¸‡āš„ā¸Ąāšˆā¸–ā¸šā¸āšā¸Šā¸ŖāšŒā¸ā¸ąā¸šā¸„ā¸šāšˆā¸Ģā¸š", "partner_page_no_more_users": "āš„ā¸Ąāšˆā¸Ąā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšƒā¸Ģāš‰āš€ā¸žā¸´āšˆā¸Ą", - "partner_page_partner_add_failed": "ā¸ā¸˛ā¸Ŗāš€ā¸žā¸´āšˆā¸Ąā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", - "partner_page_select_partner": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", + "partner_page_partner_add_failed": "ā¸ā¸˛ā¸Ŗāš€ā¸žā¸´āšˆā¸Ąā¸„ā¸šāšˆā¸Ģā¸šā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", + "partner_page_select_partner": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸„ā¸šāšˆā¸Ģā¸š", "partner_page_shared_to_title": "āšā¸Šā¸ŖāšŒā¸ā¸ąā¸š", "partner_page_stop_sharing_content": "{partner} ā¸ˆā¸°āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“", - "partner_sharing": "āšā¸Šā¸ŖāšŒā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸žā¸˛ā¸ŖāšŒā¸—āš€ā¸™ā¸­ā¸ŖāšŒ", - "partners": "ā¸žā¸˛ā¸ŖāšŒā¸—āš€ā¸™ā¸­ā¸ŖāšŒ", + "partner_sharing": "āšā¸Šā¸ŖāšŒā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸„ā¸šāšˆā¸Ģā¸š", + "partners": "ā¸„ā¸šāšˆā¸Ģā¸š", "password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "password_does_not_match": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸Ąāšˆā¸•ā¸Ŗā¸‡ā¸ā¸ąā¸™", "password_required": "ā¸ˆā¸ŗāš€ā¸›āš‡ā¸™ā¸•āš‰ā¸­ā¸‡ā¸Ąā¸ĩ⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", @@ -1276,7 +1410,7 @@ "purchase_lifetime_description": "ā¸‹ā¸ˇāš‰ā¸­ā¸•ā¸Ĩā¸­ā¸”ā¸Šā¸ĩā¸ž", "purchase_option_title": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗā¸‹ā¸ˇāš‰ā¸­", "purchase_panel_info_1": "⏗⏞⏇⏗ā¸ĩā¸Ą Immich ā¸•āš‰ā¸­ā¸‡āšƒā¸Šāš‰āš€ā¸§ā¸Ĩā¸˛āšā¸Ĩā¸°ā¸„ā¸§ā¸˛ā¸Ąā¸žā¸ĸ⏞ā¸ĸā¸˛ā¸Ąā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ąā¸˛ā¸āšƒā¸™ā¸ā¸˛ā¸Ŗā¸žā¸ąā¸’ā¸™ā¸˛ā¸Ŗā¸°ā¸šā¸šā¸™ā¸ĩāš‰ā¸‚ā¸ļāš‰ā¸™ā¸Ąā¸˛ āšā¸Ĩā¸°āš€ā¸Ŗā¸˛ā¸Ąā¸ĩ⏧⏴⏍⏧⏁⏪⏗ā¸ĩāšˆā¸—ā¸ŗā¸‡ā¸˛ā¸™āš€ā¸•āš‡ā¸Ąāš€ā¸§ā¸Ĩā¸˛āš€ā¸žā¸ˇāšˆā¸­ā¸žā¸ąā¸’ā¸™ā¸˛āšƒā¸Ģāš‰ā¸”ā¸ĩ⏗ā¸ĩāšˆā¸Ēā¸¸ā¸”āš€ā¸—āšˆā¸˛ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸—ā¸ŗāš„ā¸”āš‰ ā¸ ā¸˛ā¸Ŗā¸ā¸´ā¸ˆā¸‚ā¸­ā¸‡āš€ā¸Ŗā¸˛ā¸„ā¸ˇā¸­ā¸ā¸˛ā¸Ŗā¸—ā¸ŗāšƒā¸Ģāš‰ā¸‹ā¸­ā¸Ÿā¸•āšŒāšā¸§ā¸ŖāšŒāš‚ā¸­āš€ā¸žāšˆā¸™ā¸‹ā¸­ā¸ŖāšŒā¸Ēāšā¸Ĩā¸°āšā¸™ā¸§ā¸—ā¸˛ā¸‡ā¸›ā¸ā¸´ā¸šā¸ąā¸•ā¸´ā¸—ā¸˛ā¸‡ā¸˜ā¸¸ā¸Ŗā¸ā¸´ā¸ˆā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡ā¸•ā¸˛ā¸Ąā¸ˆā¸Ŗā¸´ā¸ĸā¸˜ā¸Ŗā¸Ŗā¸Ąā¸ā¸Ĩ⏞ā¸ĸāš€ā¸›āš‡ā¸™āšā¸Ģā¸Ĩāšˆā¸‡ā¸Ŗā¸˛ā¸ĸāš„ā¸”āš‰ā¸—ā¸ĩāšˆā¸ĸā¸ąāšˆā¸‡ā¸ĸ⏎⏙ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸™ā¸ąā¸ā¸žā¸ąā¸’ā¸™ā¸˛ āšā¸Ĩ⏰ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸°ā¸šā¸šā¸™ā¸´āš€ā¸§ā¸¨ā¸—ā¸ĩāšˆāš€ā¸„ā¸˛ā¸Ŗā¸žā¸„ā¸§ā¸˛ā¸Ąāš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸•ā¸ąā¸§ā¸žā¸Ŗāš‰ā¸­ā¸Ąā¸—ā¸˛ā¸‡āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸ˇāšˆā¸™ā¸—ā¸ĩāšˆāš€ā¸›āš‡ā¸™ā¸Ŗā¸šā¸›ā¸˜ā¸Ŗā¸Ŗā¸Ąāšā¸—ā¸™ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗā¸„ā¸Ĩā¸˛ā¸§ā¸”āšŒā¸—ā¸ĩāšˆāš€ā¸­ā¸˛ā¸Ŗā¸ąā¸”āš€ā¸­ā¸˛āš€ā¸›ā¸Ŗā¸ĩā¸ĸ⏚", - "purchase_panel_info_2": "āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸āš€ā¸Ŗā¸˛āšƒā¸Ģāš‰ā¸„ā¸ŗā¸Ąā¸ąāšˆā¸™ā¸§āšˆā¸˛ ā¸ˆā¸°āš„ā¸Ąāšˆāš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸°ā¸šā¸šā¸Šā¸ŗā¸Ŗā¸°āš€ā¸‡ā¸´ā¸™āšƒā¸™ā¸Ŗā¸°ā¸šā¸šā¸‚ā¸­ā¸‡āš€ā¸Ŗā¸˛ ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™ā¸ā¸˛ā¸Ŗā¸‹ā¸ˇāš‰ā¸­ā¸„ā¸Ŗā¸ąāš‰ā¸‡ā¸™ā¸ĩāš‰ā¸ˆā¸°āš„ā¸Ąāšˆā¸—ā¸ŗāšƒā¸Ģāš‰ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāšƒā¸™ Immich āš€ā¸›āš‡ā¸™ā¸žā¸´āš€ā¸¨ā¸Š āš€ā¸Ŗā¸˛ā¸­ā¸˛ā¸¨ā¸ąā¸ĸā¸œā¸šāš‰ā¸„ā¸™āšā¸šā¸šā¸—āšˆā¸˛ā¸™āšƒā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸™ā¸ąā¸šā¸Ēā¸™ā¸¸ā¸™ā¸ā¸˛ā¸Ŗā¸žā¸ąā¸’ā¸™ā¸˛ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸•āšˆā¸­āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸‚ā¸­ā¸‡ Immich", + "purchase_panel_info_2": "āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸āš€ā¸Ŗā¸˛āšƒā¸Ģāš‰ā¸„ā¸ŗā¸Ąā¸ąāšˆā¸™ā¸§āšˆā¸˛ ā¸ˆā¸°āš„ā¸Ąāšˆāš€ā¸žā¸´āšˆā¸Ąā¸Ŗā¸°ā¸šā¸šā¸Šā¸ŗā¸Ŗā¸°āš€ā¸‡ā¸´ā¸™āšƒā¸™ā¸Ŗā¸°ā¸šā¸šā¸‚ā¸­ā¸‡āš€ā¸Ŗā¸˛ ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™ā¸ā¸˛ā¸Ŗā¸‹ā¸ˇāš‰ā¸­ā¸„ā¸Ŗā¸ąāš‰ā¸‡ā¸™ā¸ĩāš‰ā¸ˆā¸°āš„ā¸Ąāšˆā¸—ā¸ŗāšƒā¸Ģāš‰ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāšƒā¸™ Immich āš€ā¸›āš‡ā¸™ā¸žā¸´āš€ā¸¨ā¸Š āš€ā¸Ŗā¸˛ā¸­ā¸˛ā¸¨ā¸ąā¸ĸā¸œā¸šāš‰ā¸„ā¸™āšā¸šā¸šā¸„ā¸¸ā¸“āšƒā¸™ā¸ā¸˛ā¸Ŗā¸Ēā¸™ā¸ąā¸šā¸Ēā¸™ā¸¸ā¸™ā¸ā¸˛ā¸Ŗā¸žā¸ąā¸’ā¸™ā¸˛ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸•āšˆā¸­āš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸‚ā¸­ā¸‡ Immich", "purchase_panel_title": "ā¸Ēā¸™ā¸ąā¸šā¸Ēā¸™ā¸¸ā¸™āš‚ā¸„ā¸Ŗā¸‡ā¸ā¸˛ā¸Ŗā¸™ā¸ĩāš‰", "purchase_per_server": "ā¸•āšˆā¸­āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "purchase_per_user": "ā¸•āšˆā¸­ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", @@ -1421,6 +1555,7 @@ "select_keep_all": "āš€ā¸Ĩā¸ˇā¸­ā¸āš€ā¸āš‡ā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "select_library_owner": "āš€ā¸Ĩā¸ˇā¸­ā¸āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸Ĩā¸ąā¸‡ā¸ ā¸˛ā¸ž", "select_new_face": "āš€ā¸Ĩā¸ˇā¸­ā¸āšƒā¸šā¸Ģā¸™āš‰ā¸˛āšƒā¸Ģā¸Ąāšˆ", + "select_person_to_tag": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", "select_photos": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", "select_trash_all": "āš€ā¸Ĩā¸ˇā¸­ā¸āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸā¸°ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "select_user_for_sharing_page_err_album": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸Ĩāš‰ā¸Ąāš€ā¸Ģā¸Ĩ⏧", @@ -1428,12 +1563,14 @@ "selected_count": "{count, plural, other {# āš€ā¸Ĩā¸ˇā¸­ā¸āšā¸Ĩāš‰ā¸§}}", "send_message": "ā¸Ēāšˆā¸‡ā¸‚āš‰ā¸­ā¸„ā¸§ā¸˛ā¸Ą", "send_welcome_email": "ā¸Ēāšˆā¸‡ā¸­ā¸ĩāš€ā¸Ąā¸Ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸š", + "server_endpoint": "⏛ā¸Ĩ⏞ā¸ĸā¸—ā¸˛ā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "server_info_box_app_version": "āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšā¸­ā¸ž", "server_info_box_server_url": "URL āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "server_offline": "Server ā¸­ā¸­ā¸Ÿāš„ā¸Ĩā¸™āšŒ", "server_online": "Server ā¸­ā¸­ā¸™āš„ā¸Ĩā¸™āšŒ", + "server_privacy": "ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸•ā¸ąā¸§āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "server_stats": "ā¸Ēā¸–ā¸´ā¸•ā¸´āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", - "server_version": "āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸‚ā¸­ā¸‡ Server", + "server_version": "āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™ā¸‚ā¸­ā¸‡āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ", "set": "ā¸•ā¸ąāš‰ā¸‡", "set_as_album_cover": "ā¸•ā¸ąāš‰ā¸‡āš€ā¸›āš‡ā¸™ā¸ ā¸˛ā¸žā¸›ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "set_as_featured_photo": "ā¸•ā¸ąāš‰ā¸‡āš€ā¸›āš‡ā¸™ā¸Ŗā¸šā¸›ā¸Ēā¸ŗā¸„ā¸ąā¸", @@ -1518,7 +1655,7 @@ "sharing_page_empty_list": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗā¸§āšˆā¸˛ā¸‡āš€ā¸›ā¸Ĩāšˆā¸˛", "sharing_sidebar_description": "āšā¸Ē⏔⏇ā¸Ĩā¸´ā¸‡ā¸āšŒā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāšƒā¸™āšā¸–ā¸šā¸”āš‰ā¸˛ā¸™ā¸‚āš‰ā¸˛ā¸‡", "sharing_silver_appbar_create_shared_album": "ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ąā¸—ā¸ĩāšˆāšā¸Šā¸ŖāšŒāšƒā¸Ģā¸Ąāšˆ", - "sharing_silver_appbar_share_partner": "āšā¸Šā¸ŖāšŒā¸ā¸ąā¸šā¸žā¸ąā¸™ā¸˜ā¸Ąā¸´ā¸•ā¸Ŗ", + "sharing_silver_appbar_share_partner": "āšā¸Šā¸ŖāšŒā¸ā¸ąā¸šā¸„ā¸šāšˆā¸Ģā¸š", "shift_to_permanent_delete": "⏁⏔ ⇧ to ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸Ĩ⏚ā¸Ēā¸ˇāšˆā¸­ā¸–ā¸˛ā¸§ā¸Ŗ", "show_album_options": "āšā¸Ēā¸”ā¸‡ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "show_albums": "āšā¸Ēā¸”ā¸‡ā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", @@ -1570,7 +1707,7 @@ "status": "ā¸Ē⏖⏞⏙⏰", "stop_motion_photo": "ā¸ ā¸˛ā¸žā¸§ā¸ąā¸•ā¸–ā¸¸āš€ā¸„ā¸Ĩā¸ˇāšˆā¸­ā¸™āš„ā¸Ģ⏧", "stop_photo_sharing": "ā¸Ģā¸ĸā¸¸ā¸”āšā¸Šā¸ŖāšŒā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž?", - "stop_photo_sharing_description": "{partner}ā¸ˆā¸°āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ŗā¸šā¸›ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸­ā¸ĩ⏁", + "stop_photo_sharing_description": "{partner} ā¸ˆā¸°āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ŗā¸šā¸›ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸­ā¸ĩ⏁", "stop_sharing_photos_with_user": "ā¸Ģā¸ĸā¸¸ā¸”ā¸ā¸˛ā¸Ŗāšā¸Šā¸ŖāšŒā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ā¸ąā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸™ā¸ĩāš‰", "storage": "ā¸žā¸ˇāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š", "storage_label": "āš€ā¸™ā¸ˇāš‰ā¸­ā¸—ā¸ĩāšˆā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š", @@ -1597,6 +1734,8 @@ "theme_setting_asset_list_tiles_per_row_title": "ā¸ˆā¸ŗā¸™ā¸§ā¸™ā¸—ā¸Ŗā¸ąā¸žā¸ĸā¸˛ā¸ā¸Ŗā¸•āšˆā¸­āšā¸–ā¸§ ({count})", "theme_setting_image_viewer_quality_subtitle": "ā¸›ā¸Ŗā¸ąā¸šā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸‚ā¸­ā¸•ā¸ąā¸§ā¸”ā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔", "theme_setting_image_viewer_quality_title": "ā¸„ā¸¸ā¸“ā¸ ā¸˛ā¸žā¸•ā¸ąā¸‡ā¸”ā¸šā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸ž", + "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": "ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩā¸”āšā¸šā¸šā¸Ēā¸˛ā¸Ąā¸‚ā¸ąāš‰ā¸™ā¸•ā¸­ā¸™ā¸­ā¸˛ā¸ˆāš€ā¸žā¸´āšˆā¸Ąā¸›ā¸Ŗā¸°ā¸Ēā¸´ā¸—ā¸˜ā¸´ā¸ ā¸˛ā¸žāšƒā¸™ā¸ā¸˛ā¸Ŗāš‚ā¸Ģā¸Ĩā¸”āšā¸•āšˆā¸ˆā¸°ā¸—ā¸ŗāšƒā¸Ģāš‰āš‚ā¸Ģā¸Ĩā¸”āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‚āšˆā¸˛ā¸ĸāš€ā¸žā¸´āšˆā¸Ąā¸‚ā¸ļāš‰ā¸™ā¸Ąā¸˛ā¸", @@ -1616,6 +1755,7 @@ "total": "ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "total_usage": "ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ŗā¸§ā¸Ą", "trash": "ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", + "trash_action_prompt": "{count} ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "trash_all": "ā¸—ā¸´āš‰ā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "trash_count": "{count, number} āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰", "trash_no_results_message": "ā¸Ŗā¸šā¸›ā¸ ā¸˛ā¸žā¸Ģ⏪⏎⏭⏧⏴⏔ā¸ĩāš‚ā¸­ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸Ĩ⏚⏈⏰⏭ā¸ĸā¸šāšˆā¸—ā¸ĩāšˆā¸™ā¸ĩāšˆ", @@ -1631,9 +1771,11 @@ "unable_to_change_pin_code": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "unable_to_setup_pin_code": "āš„ā¸Ąāšˆā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "unarchive": "ā¸™ā¸ŗā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸—ā¸ĩāšˆāš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗ", + "undo": "āš€ā¸Ĩ⏴⏁⏗⏺", "unfavorite": "ā¸™ā¸ŗā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”", "unhide_person": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸‹āšˆā¸­ā¸™ā¸šā¸¸ā¸„ā¸„ā¸Ĩ", "unknown": "āš„ā¸Ąāšˆā¸—ā¸Ŗā¸˛ā¸š", + "unknown_country": "āš„ā¸Ąāšˆā¸—ā¸Ŗā¸˛ā¸šā¸›ā¸Ŗā¸°āš€ā¸—ā¸¨", "unknown_year": "āš„ā¸Ąāšˆā¸—ā¸Ŗā¸˛ā¸šā¸›ā¸ĩ", "unlimited": "āš„ā¸Ąāšˆā¸ˆā¸ŗā¸ā¸ąā¸”", "unlink_oauth": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸āš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ OAuth", @@ -1644,6 +1786,7 @@ "unselect_all": "ā¸ĸā¸āš€ā¸Ĩā¸´ā¸ā¸ā¸˛ā¸Ŗāš€ā¸Ĩā¸ˇā¸­ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "unstack": "ā¸Ģā¸ĸā¸¸ā¸”ā¸‹āš‰ā¸­ā¸™", "up_next": "ā¸•āšˆā¸­āš„ā¸›", + "updated_at": "ā¸­ā¸ąā¸žāš€ā¸”ā¸—", "updated_password": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸Ĩāš‰ā¸§", "upload": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔", "upload_concurrency": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”ā¸žā¸Ŗāš‰ā¸­ā¸Ąā¸ā¸ąā¸™", @@ -1653,9 +1796,14 @@ "upload_status_errors": "ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔", "upload_status_uploaded": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩā¸”āšā¸Ĩāš‰ā¸§", "upload_success": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ŗāš€ā¸Ŗāš‡ā¸ˆ, ⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Šā¸Ģā¸™āš‰ā¸˛ā¸™ā¸ĩāš‰āšƒā¸Ģā¸Ąāšˆā¸„ā¸¸ā¸“ā¸ˆā¸°āš€ā¸Ģāš‡ā¸™ā¸Ēā¸ˇāšˆā¸­ā¸—ā¸ĩāšˆāš€ā¸žā¸´āšˆā¸Ąā¸Ĩāšˆā¸˛ā¸Ē⏏⏔", + "uploading": "⏁⏺ā¸Ĩā¸ąā¸‡ā¸­ā¸ąā¸žāš‚ā¸Ģā¸Ĩ⏔", + "uploading_media": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔ā¸Ēā¸ˇāšˆā¸­", "usage": "ā¸ā¸˛ā¸Ŗāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", + "use_biometric": "āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸žā¸´ā¸Ēā¸šā¸ˆā¸™āšŒā¸­ā¸ąā¸•ā¸Ĩā¸ąā¸ā¸Šā¸“āšŒ", + "use_current_connection": "āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąā¸•āšˆā¸­ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", "use_custom_date_range": "āšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗā¸›ā¸Ŗā¸ąā¸šāšā¸•āšˆā¸‡ā¸Šāšˆā¸§ā¸‡āš€ā¸§ā¸Ĩ⏞", "user": "ā¸œā¸šāš‰āšƒā¸Šāš‰", + "user_has_been_deleted": "ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸Ŗā¸˛ā¸ĸ⏙ā¸ĩāš‰ā¸–ā¸šā¸ā¸Ĩā¸šāš„ā¸›āšā¸Ĩāš‰ā¸§", "user_id": "āš„ā¸­ā¸”ā¸ĩā¸œā¸šāš‰āšƒā¸Šāš‰", "user_pin_code_settings": "⏪ā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", "user_pin_code_settings_description": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ŗā¸Ģā¸ąā¸Ēā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ (PIN)", @@ -1669,8 +1817,10 @@ "users": "ā¸œā¸šāš‰āšƒā¸Šāš‰", "utilities": "āš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ąā¸ˇā¸­", "validate": "ā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚", + "validate_endpoint_error": "ā¸ā¸Ŗā¸¸ā¸“ā¸˛ā¸Ŗā¸°ā¸šā¸¸ URL ⏗ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "variables": "ā¸•ā¸ąā¸§āšā¸›ā¸Ŗ", "version": "ā¸Ŗā¸¸āšˆā¸™", + "version_announcement_closing": "āš€ā¸žā¸ˇāšˆā¸­ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ ā¸­āš€ā¸Ĩāš‡ā¸ā¸‹āšŒ", "version_announcement_message": "ā¸Ēā¸§ā¸ąā¸Ē⏔ā¸ĩ! Immich āš€ā¸§ā¸­ā¸ŖāšŒā¸Šā¸ąā¸™āšƒā¸Ģā¸Ąāšˆā¸žā¸Ŗāš‰ā¸­ā¸Ąāšƒā¸Ģāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āšā¸Ĩāš‰ā¸§ āš‚ā¸›ā¸Ŗā¸”āšƒā¸Šāš‰āš€ā¸§ā¸Ĩ⏞ā¸Ēā¸ąā¸ā¸„ā¸Ŗā¸šāšˆāš€ā¸žā¸ˇāšˆā¸­ā¸­āšˆā¸˛ā¸™ ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ģā¸•ā¸¸ā¸ā¸˛ā¸Ŗāš€ā¸œā¸ĸāšā¸žā¸Ŗāšˆ āš€ā¸žā¸ˇāšˆā¸­āšƒā¸Ģāš‰āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš€ā¸”ā¸•āšā¸Ĩāš‰ā¸§ āš€ā¸žā¸ˇāšˆā¸­ā¸›āš‰ā¸­ā¸‡ā¸ā¸ąā¸™ā¸ā¸˛ā¸Ŗā¸ā¸ŗā¸Ģā¸™ā¸”ā¸„āšˆā¸˛ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ āš‚ā¸”ā¸ĸāš€ā¸‰ā¸žā¸˛ā¸°ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸ĸā¸´āšˆā¸‡ā¸Ģā¸˛ā¸ā¸„ā¸¸ā¸“āšƒā¸Šāš‰ WatchTower ā¸Ģ⏪⏎⏭⏁ā¸Ĩāš„ā¸ā¸­ā¸ˇāšˆā¸™āš† ⏗ā¸ĩāšˆā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸­ā¸´ā¸™ā¸Ēāšā¸•ā¸™ā¸‹āšŒ Immich ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´", "version_history": "ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™āšā¸›ā¸Ĩ⏇", "version_history_item": "ā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡ {version} ā¸§ā¸ąā¸™ā¸—ā¸ĩāšˆ {date}", @@ -1682,10 +1832,14 @@ "view_album": "ā¸”ā¸šā¸­ā¸ąā¸Ĩā¸šā¸ąāš‰ā¸Ą", "view_all": "ā¸”ā¸šā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "view_all_users": "ā¸”ā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", + "view_details": "ā¸”ā¸šā¸Ŗā¸˛ā¸ĸā¸Ĩā¸°āš€ā¸­ā¸ĩā¸ĸ⏔", "view_in_timeline": "ā¸”ā¸šāš„ā¸—ā¸ĄāšŒāš„ā¸Ĩā¸™āšŒ", + "view_link": "ā¸”ā¸šā¸Ĩā¸´ā¸‡ā¸āšŒ", "view_links": "ā¸”ā¸šā¸Ĩā¸´ā¸‡ā¸āšŒ", "view_next_asset": "ā¸”ā¸šā¸Ēā¸ˇāšˆā¸­ā¸–ā¸ąā¸”āš„ā¸›", "view_previous_asset": "ā¸”ā¸šā¸Ēā¸ˇāšˆā¸­ā¸āšˆā¸­ā¸™ā¸Ģā¸™āš‰ā¸˛", + "view_qr_code": "ā¸”ā¸šā¸„ā¸´ā¸§ā¸­ā¸˛ā¸ŖāšŒāš‚ā¸„āš‰ā¸”", + "view_user": "ā¸”ā¸šā¸œā¸šāš‰āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™", "viewer_remove_from_stack": "āš€ā¸­ā¸˛ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸ā¸—ā¸ĩāšˆā¸‹āš‰ā¸­ā¸™", "viewer_stack_use_as_main_asset": "āšƒā¸Šāš‰āš€ā¸›āš‡ā¸™ā¸—ā¸Ŗā¸ąā¸žā¸ĸ⏞⏁⏪ā¸Ģā¸Ĩā¸ąā¸", "viewer_unstack": "ā¸Ģā¸ĸā¸¸ā¸”ā¸‹āš‰ā¸­ā¸™", @@ -1695,11 +1849,12 @@ "week": "ā¸Ēā¸ąā¸›ā¸”ā¸˛ā¸ĢāšŒ", "welcome": "ā¸ĸ⏴⏙⏔ā¸ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸š", "welcome_to_immich": "ā¸ĸ⏴⏙⏔ā¸ĩā¸•āš‰ā¸­ā¸™ā¸Ŗā¸ąā¸šā¸Ēā¸šāšˆ immich", - "wifi_name": "WiFi Name", + "wifi_name": "ā¸Šā¸ˇāšˆā¸­ Wi-Fi", + "wrong_pin_code": "⏪ā¸Ģā¸ąā¸Ē PIN āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "year": "⏛ā¸ĩ", "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/tr.json b/i18n/tr.json index ddb99ca019..a962420980 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -11,15 +11,16 @@ "activity_changed": "Etkinlik {enabled, select, true {etkin} other {devre dÄąÅŸÄą}}", "add": "Ekle", "add_a_description": "AÃ§Äąklama ekle", - "add_a_location": "Lokasyon ekle", + "add_a_location": "Bir konum ekle", "add_a_name": "İsim ekle", "add_a_title": "BaşlÄąk ekle", + "add_birthday": "Doğum gÃŧnÃŧ ekle", "add_endpoint": "Uç nokta ekle", "add_exclusion_pattern": "Hariç tutma deseni ekle", "add_import_path": "İçe aktarma yolu ekle", - "add_location": "Lokasyon ekle", + "add_location": "Konum ekle", "add_more_users": "Daha fazla kullanÄącÄą ekle", - "add_partner": "Partner ekle", + "add_partner": "Ortak ekle", "add_path": "Yol ekle", "add_photos": "Fotoğraf ekle", "add_tag": "Etiket ekle", @@ -27,6 +28,10 @@ "add_to_album": "AlbÃŧme ekle", "add_to_album_bottom_sheet_added": "{album} albÃŧmÃŧne eklendi", "add_to_album_bottom_sheet_already_exists": "Zaten {album} albÃŧmÃŧne ekli", + "add_to_album_bottom_sheet_some_local_assets": "BazÄą yerel Ãļğeler albÃŧme eklenemedi", + "add_to_album_toggle": "{album} için seçimi değiştir", + "add_to_albums": "AlbÃŧmlere ekle", + "add_to_albums_count": "{count} albÃŧmlerine ekle", "add_to_shared_album": "PaylaÅŸÄąlan albÃŧme ekle", "add_url": "URL ekle", "added_to_archive": "Arşive eklendi", @@ -34,29 +39,37 @@ "added_to_favorites_count": "{count, number} fotoğraf favorilere eklendi", "admin": { "add_exclusion_pattern_description": "Hariç tutma desenleri ekleyin. *, ** ve ? kullanÄąlarak Globbing (temsili yer doldurucu karakter) desteklenir. Farzedelim \"Raw\" adlÄą bir dizininiz var, içinde ki tÃŧm dosyalarÄą yoksaymak için \"**/Raw/**\" şeklinde yazabilirsiniz. \".tif\" ile biten tÃŧm dosyalarÄą yoksaymak için \"**/*.tif\" yazabilirsiniz. Mutlak yolu yoksaymak için \"/yoksayÄąlacak/olan/yol/**\" şeklinde yazabilirsiniz.", - "asset_offline_description": "Bu harici kÃŧtÃŧphane varlığı artÄąk diskte bulunmuyor ve çÃļp kutusuna taÅŸÄąndÄą. Dosya kÃŧtÃŧphane içinde taÅŸÄąndÄąysa, yeni karÅŸÄąlÄąk gelen varlÄąk için zaman çizelgenizi kontrol edin. Bu varlığı geri yÃŧklemek için lÃŧtfen aşağıdaki dosya yolunun Immich tarafÄąndan erişilebilir olduğundan emin olun ve kÃŧtÃŧphaneyi tarayÄąn.", + "admin_user": "YÃļnetici KullanÄącÄą", + "asset_offline_description": "Bu harici kÃŧtÃŧphane Ãļğesi artÄąk diskte bulunmuyor ve çÃļp kutusuna taÅŸÄąndÄą. Dosya kÃŧtÃŧphane içinde taÅŸÄąndÄąysa, yeni karÅŸÄąlÄąk gelen Ãļğe için zaman çizelgenizi kontrol edin. Bu Ãļğeyi geri yÃŧklemek için lÃŧtfen aşağıdaki dosya yolunun Immich tarafÄąndan erişilebilir olduğundan emin olun ve kÃŧtÃŧphaneyi tarayÄąn.", "authentication_settings": "Yetkilendirme AyarlarÄą", "authentication_settings_description": "Şifre, OAuth, ve diğer yetkilendirme ayarlarÄąnÄą yÃļnet", "authentication_settings_disable_all": "TÃŧm giriş yÃļntemlerini devre dÄąÅŸÄą bÄąrakmak istediğinize emin misiniz? Giriş yapma fonksiyonu tamamen devre dÄąÅŸÄą bÄąrakÄąlacak.", "authentication_settings_reenable": "Yeniden aktif etmek için Sunucu Komutu'nu kullanÄąn.", "background_task_job": "Arka Plan GÃļrevleri", - "backup_database": "VeritabanÄą yığınÄą oluştur", + "backup_database": "VeritabanÄą YığınÄą Oluştur", "backup_database_enable_description": "VeritabanÄą yığınlarÄąnÄą etkinleştir", "backup_keep_last_amount": "TutulmasÄą gereken geçmiş yığınÄą miktarÄą", - "backup_settings": "VeritabanÄą yığınÄą ayarlarÄą", - "backup_settings_description": "VeritabanÄą Yedekleme AyarlarÄąnÄą YÃļnet", + "backup_onboarding_1_description": "bulutta veya başka bir fiziksel konumda bulunan yedek kopya.", + "backup_onboarding_2_description": "farklÄą cihazlarda yerel kopyalar. Bu, ana dosyalarÄą ve bu dosyalarÄąn yerel yedeklerini içerir.", + "backup_onboarding_3_description": "Verilerinizin toplam kopyalarÄą, orijinal dosyalar dahil. Bu, 1 adet dÄąÅŸ mekan kopyasÄą ve 2 adet yerel kopya içerir.", + "backup_onboarding_description": "Verilerinizi korumak için 3-2-1 yedekleme stratejisi Ãļnerilir. KapsamlÄą bir yedekleme çÃļzÃŧmÃŧ için, yÃŧklediğiniz fotoğraflarÄąn/videolarÄąn yanÄą sÄąra Immich veritabanÄąnÄąn kopyalarÄąnÄą da saklamalÄąsÄąnÄąz.", + "backup_onboarding_footer": "Immich'i yedekleme hakkÄąnda daha fazla bilgi için lÃŧtfen belgelere bakÄąn.", + "backup_onboarding_parts_title": "3-2-1 yedekleme şunlarÄą içerir:", + "backup_onboarding_title": "Yedeklemeler", + "backup_settings": "VeritabanÄą YığınÄą AyarlarÄą", + "backup_settings_description": "VeritabanÄą dÃļkÃŧm ayarlarÄąnÄą yÃļnet.", "cleared_jobs": "{job} için işler temizlendi", "config_set_by_file": "Ayarlar şuanda config dosyasÄą tarafÄąndan ayarlanmÄąÅŸtÄąr", "confirm_delete_library": "{library} kÃŧtÃŧphanesini silmek istediğinize emin misiniz?", - "confirm_delete_library_assets": "Bu kÃŧtÃŧphaneyi silmek istediğinize emin misiniz? Bu işlem {count, plural, one {# tane varlığı} other {all # tane varlığı}} Immich'den silecek ve bu işlem geri alÄąnamaz. Silinen dosyalar diskten silinmeyecek.", + "confirm_delete_library_assets": "Bu kÃŧtÃŧphaneyi silmek istediğinize emin misiniz? Bu işlem {count, plural, one {# tane Ãļğeyi} other {all # tane Ãļğeyi}} Immich'den silecek ve bu işlem geri alÄąnamaz. Dosyalar diskte kalacaktÄąr.", "confirm_email_below": "Onaylamak için aşağıya {email} yazÄąn", "confirm_reprocess_all_faces": "TÃŧm yÃŧzleri tekrardan işlemek istediğinize emin misiniz? Bu işlem isimlendirilmiş insanlarÄą da silecek.", "confirm_user_password_reset": "{user} adlÄą kullanÄącÄąnÄąn şifresini sÄąfÄąrlamak istediğinize emin misiniz?", "confirm_user_pin_code_reset": "{user} adlÄą kullanÄącÄąnÄąn PIN kodunu sÄąfÄąrlamak istediğinize emin misiniz?", "create_job": "GÃļrev oluştur", - "cron_expression": "Cron İfadesi", + "cron_expression": "Cron ifadesi", "cron_expression_description": "Cron formatÄąnÄą kullanarak tarama aralığınÄą belirle. Daha fazla bilgi için Ãļrneğin Crontab Guru’ya bakÄąn", - "cron_expression_presets": "Cron İfadesi ÖnayarlarÄą", + "cron_expression_presets": "Cron ifadesi Ãļn ayarlarÄą", "disable_login": "Girişi devre dÄąÅŸÄą bÄąrak", "duplicate_detection_job_description": "Benzer fotoğraflarÄą bulmak için makine Ãļğrenmesini çalÄąÅŸtÄąr. Bu işlem AkÄąllÄą Arama'ya bağlÄądÄąr", "exclusion_pattern_description": "KÃŧtÃŧphaneyi tararken dosya ve klasÃļrleri gÃļrmezden gelmek için dÄąÅŸlama desenlerini kullanabilirsiniz. RAW dosyalarÄą gibi bazÄą dosya ve klasÃļrleri içe aktarmak istemediğinizde bu seçeneği kullanabilirsiniz.", @@ -65,23 +78,25 @@ "face_detection_description": "Makine Ãļğrenimi kullanarak varlÄąklardaki yÃŧzleri tespit et. Videolar için sadece kÃŧçÃŧk resim (thumbnail) dikkate alÄąnÄąr. 'Yenile' tÃŧm varlÄąklarÄą yeniden işler. 'SÄąfÄąrla', mevcut tÃŧm yÃŧz verilerini temizleyerek işlemi yeniden başlatÄąr. 'Eksik' henÃŧz işlenmemiş varlÄąklarÄą sÄąraya alÄąr. Tespit edilen yÃŧzler, YÃŧz TanÄąma işlemi tamamlandÄąktan sonra mevcut ya da yeni kişilere gruplanmak Ãŧzere YÃŧz TanÄąma için sÄąraya alÄąnacaktÄąr.", "facial_recognition_job_description": "AlgÄąlanan yÃŧzleri kişilere grupla. Bu adÄąm, YÃŧz Tespit işlemi tamamlandÄąktan sonra çalÄąÅŸÄąr. \"SÄąfÄąrla\", tÃŧm yÃŧzleri yeniden gruplandÄąrÄąr. \"Eksik\" ise henÃŧz bir kişiye atanmamÄąÅŸ yÃŧzleri sÄąraya alÄąr.", "failed_job_command": "{job} gÃļrevi için {command} komutu başarÄąsÄąz", - "force_delete_user_warning": "UYARI: Bu işlem kullanÄącÄąyÄą ve tÃŧm varlÄąklarÄą anÄąnda kaldÄąracaktÄąr. Bu geri alÄąnamaz ve dosyalar geri getirilemez.", + "force_delete_user_warning": "UYARI: Bu işlem kullanÄącÄąyÄą ve tÃŧm Ãļğeleri anÄąnda kaldÄąracaktÄąr. Bu geri alÄąnamaz ve dosyalar geri getirilemez.", "image_format": "Biçim", "image_format_description": "WebP, JPEG'e gÃļre daha kÃŧçÃŧk dosya boyutu sunar fakat işlemesi daha uzun sÃŧrer.", + "image_fullsize_description": "YakÄąnlaştÄąrÄąldığında kullanÄąlan, meta verileri kaldÄąrÄąlmÄąÅŸ tam boyutlu gÃļrÃŧntÃŧ", "image_fullsize_enabled": "Tam boyutlu gÃļrÃŧntÃŧ Ãŧretimini etkinleştir", + "image_fullsize_enabled_description": "Yerleşik Ãļnizlemeyi tercih et” seçeneği etkinleştirildiğinde, yerleşik Ãļnizlemeler dÃļnÃŧştÃŧrme yapÄąlmadan doğrudan kullanÄąlÄąr. JPEG gibi web dostu formatlar bu ayardan etkilenmez.", "image_fullsize_quality_description": "1-100 arasÄąnda tam boyutlu gÃļrÃŧntÃŧ kalitesi. Daha yÃŧksek kalitelidir, ancak daha bÃŧyÃŧk dosyalar Ãŧretir.", - "image_fullsize_title": "Tam boyutlu gÃļrÃŧntÃŧ ayarlarÄą", - "image_prefer_embedded_preview": "GÃļmÃŧlÃŧ Ãļnizlemeyi tercih et", - "image_prefer_embedded_preview_setting_description": "RAtoğraflarÄą için mÃŧmkÃŧn olduğunda gÃļmÃŧlÃŧ Ãļnizlemeyi kullan. Bu, bazÄą fotoğraflarda daha gerçekçi renkler n kameraya bağlÄądÄąr ve fotoğrafta normalden daha fazla gÃļrÃŧntÃŧ bozukluklarÄąna sebep olabilir.", + "image_fullsize_title": "Tam Boyutlu GÃļrÃŧntÃŧ AyarlarÄą", + "image_prefer_embedded_preview": "GÃļmÃŧlÃŧ Ãļn izlemeyi tercih et", + "image_prefer_embedded_preview_setting_description": "RAW fotoğraflarÄą için mÃŧmkÃŧn olduğunda gÃļmÃŧlÃŧ Ãļn izlemeyi kullan. Bu, bazÄą fotoğraflarda daha gerçekçi renkler n kameraya bağlÄądÄąr ve fotoğrafta normalden daha fazla gÃļrÃŧntÃŧ bozukluklarÄąna sebep olabilir.", "image_prefer_wide_gamut": "Geniş renk aralığınÄą tercih et", "image_prefer_wide_gamut_setting_description": "Önizleme gÃļrseli için P3 renk paletini tercih et. Bu, geniş renk paletli fotoğraflarda renk canlÄąlığınÄą daha iyi korur, fakat fotoğraflar eski tarayÄącÄąlarda ve eski cihazlarda daha farklÄą gÃļrÃŧnebilir. sRGB fotoğraflar renk paletini korumak için sRGB olarak tutulur.", - "image_preview_description": "Orta boyutlu gÃļrÃŧntÃŧ, meta verisi Ã§ÄąkarÄąlmÄąÅŸ, tekil bir varlÄąk gÃļrÃŧntÃŧlenirken ve makine Ãļğrenimi için kullanÄąlÄąr", + "image_preview_description": "Orta boyutlu gÃļrÃŧntÃŧ, meta verisi Ã§ÄąkarÄąlmÄąÅŸ, tekil bir Ãļğe gÃļrÃŧntÃŧlenirken ve makine Ãļğrenimi için kullanÄąlÄąr", "image_preview_quality_description": "Ön izleme kalitesi 1-100 arasÄądÄąr. YÃŧksek değerler daha iyi kalite sağlar, ancak daha bÃŧyÃŧk dosyalar Ãŧretir ve uygulama yanÄąt verme hÄązÄąnÄą dÃŧşÃŧrebilir. DÃŧşÃŧk bir değer belirlemek, makine Ãļğrenimi kalitesini etkileyebilir.", - "image_preview_title": "Ön izleme AyarlarÄą", + "image_preview_title": "Ön İzleme AyarlarÄą", "image_quality": "Kalite", "image_resolution": "ÇÃļzÃŧnÃŧrlÃŧk", "image_resolution_description": "Daha yÃŧksek çÃļzÃŧnÃŧrlÃŧkle, daha fazla detayÄą koruyabilir ancak kodlanmasÄą daha uzun sÃŧrer, daha bÃŧyÃŧk dosya boyutlarÄąna sahip olur ve uygulamanÄąn yanÄąt verme hÄązÄąnÄą azaltabilir.", - "image_settings": "Fotoğraf ayarlarÄą", + "image_settings": "Fotoğraf AyarlarÄą", "image_settings_description": "Oluşturulan fotoğraflarÄąn kalite ve çÃļzÃŧnÃŧrlÃŧklerini yÃļnet", "image_thumbnail_description": "Meta verisi Ã§ÄąkarÄąlmÄąÅŸ kÃŧçÃŧk boyutlu kÃŧçÃŧk resim, ana zaman çizelgesi gibi fotoğraf gruplarÄąnÄą gÃļrÃŧntÃŧlerken kullanÄąlÄąr", "image_thumbnail_quality_description": "KÃŧçÃŧk resim kalitesi 1-100 arasÄąnda. Daha yÃŧksek değerler daha iyidir, ancak daha bÃŧyÃŧk dosyalar Ãŧretir ve uygulamanÄąn yanÄąt hÄązÄąnÄą azaltabilir.", @@ -91,37 +106,44 @@ "job_not_concurrency_safe": "Bu işlem eşzamanlama için uygun değil.", "job_settings": "GÃļrev AyarlarÄą", "job_settings_description": "AynÄą anda çalÄąÅŸacak gÃļrevleri yÃļnet", - "job_status": "GÃļrev StatÃŧleri", + "job_status": "GÃļrev Durumu", "jobs_delayed": "{jobCount, plural, other {# gecikmeli}}", "jobs_failed": "{jobCount, plural, other {# BaşarÄąsÄąz}}", - "library_created": "{library} kÃŧtÃŧphanesi oluşturuldu", + "library_created": "Oluşturulan kÃŧtÃŧphane : {library}", "library_deleted": "KÃŧtÃŧphane silindi", "library_import_path_description": "Belirtilecek klasÃļrÃŧ içe aktarÄąn. Bu klasÃļr, alt klasÃļrler dahil olmak Ãŧzere, gÃļrÃŧntÃŧler ve videolar için taranacaktÄąr.", "library_scanning": "Periyodik Tarama", "library_scanning_description": "Periyodik kÃŧtÃŧphane taramasÄąnÄą yÃļnet", "library_scanning_enable_description": "Periyodik kÃŧtÃŧphane taramasÄąnÄą etkinleştir", - "library_settings": "Harici kÃŧtÃŧphane", + "library_settings": "Harici KÃŧtÃŧphane", "library_settings_description": "Harici kÃŧtÃŧphane ayarlarÄąnÄą yÃļnet", - "library_tasks_description": "Yeni yada değiştirilmiş varlÄąklar için dÄąÅŸ kÃŧtÃŧphaneleri tara", + "library_tasks_description": "Yeni yada değiştirilmiş Ãļğeler için dÄąÅŸ kÃŧtÃŧphaneleri tara", "library_watching_enable_description": "Harici kÃŧtÃŧphanelerdeki dosya değişikliklerini izle", "library_watching_settings": "KÃŧtÃŧphane izleme (DENEYSEL)", "library_watching_settings_description": "Değişen dosyalar için otomatik olarak izle", - "logging_enable_description": "GÃŧnlÃŧğÃŧ aktifleştir", + "logging_enable_description": "GÃŧnlÃŧğÃŧ etkinleştir", "logging_level_description": "Etkinleştirildiğinde hangi gÃŧnlÃŧk seviyesi kullanÄąlÄąr.", - "logging_settings": "GÃŧnlÃŧk tutma", + "logging_settings": "GÃŧnlÃŧk Tutma", + "machine_learning_availability_checks": "KullanÄąlabilirlik kontrolleri", + "machine_learning_availability_checks_description": "KullanÄąlabilir makine Ãļğrenimi sunucularÄąnÄą otomatik olarak algÄąlayÄąn ve tercih edin", + "machine_learning_availability_checks_enabled": "KullanÄąlabilirlik kontrollerini etkinleştir", + "machine_learning_availability_checks_interval": "Kontrol aralığı", + "machine_learning_availability_checks_interval_description": "KullanÄąlabilirlik kontrolleri arasÄąndaki milisaniye cinsinden aralÄąk", + "machine_learning_availability_checks_timeout": "İstek zaman aÅŸÄąmÄą", + "machine_learning_availability_checks_timeout_description": "KullanÄąlabilirlik kontrolleri için milisaniye cinsinden zaman aÅŸÄąmÄą", "machine_learning_clip_model": "CLIP modeli", "machine_learning_clip_model_description": "Link burada listelenen CLIP modelinin adÄą. Bu Ãļzelliği değiştirdikten sonra \"AkÄąllÄą Arama\" işini tÃŧm fotoğraflar için tekrardan çalÄąÅŸtÄąrmalÄąsÄąnÄąz.", - "machine_learning_duplicate_detection": "Kopya fotoğraf tespiti", + "machine_learning_duplicate_detection": "Kopya Fotoğraf Tespiti", "machine_learning_duplicate_detection_enabled": "Kopya fotoğraf tespitini etkinleştir", - "machine_learning_duplicate_detection_enabled_description": "Devre dÄąÅŸÄą bÄąrakÄąlÄąrsa aynÄą Ãļgeler yine de temizlenecek.", + "machine_learning_duplicate_detection_enabled_description": "Devre dÄąÅŸÄą bÄąrakÄąlÄąrsa aynÄą Ãļğeler yine de temizlenecek.", "machine_learning_duplicate_detection_setting_description": "Birbirinin kopyasÄą olan varlÄąklarÄą bulmak için CLIP kullan", "machine_learning_enabled": "Makine Ãļğrenmesini etkinleştir", "machine_learning_enabled_description": "Eğer devre dÄąÅŸÄą bÄąrakÄąlÄąrsa bÃŧtÃŧn Makine Öğrenmesi Ãļzellikleri devre dÄąÅŸÄą bÄąrakÄąlacak.", "machine_learning_facial_recognition": "YÃŧz TanÄąma", "machine_learning_facial_recognition_description": "Fotoğraflardaki yÃŧzleri tara, tanÄą ve gruplandÄąr", - "machine_learning_facial_recognition_model": "YÃŧz TanÄąma Modeli", + "machine_learning_facial_recognition_model": "YÃŧz tanÄąma modeli", "machine_learning_facial_recognition_model_description": "Modeller, azalan boyut sÄąrasÄąna gÃļre listelenmiştir. Daha bÃŧyÃŧk modeller daha yavaştÄąr ve daha fazla bellek kullanÄąr, ancak daha iyi sonuçlar Ãŧretir. Bir modeli değiştirdikten sonra tÃŧm gÃļrÃŧntÃŧler için yÃŧz algÄąlama işini yeniden çalÄąÅŸtÄąrmanÄąz gerektiğini unutmayÄąn.", - "machine_learning_facial_recognition_setting": "YÃŧz TanÄąmayÄą etkinleştir", + "machine_learning_facial_recognition_setting": "YÃŧz tanÄąmayÄą etkinleştir", "machine_learning_facial_recognition_setting_description": "Devre dÄąÅŸÄą bÄąrakÄąldığında fotoğraflar yÃŧz tanÄąma için işlenmeyecek ve Keşfet sayfasÄąndaki Kişiler sekmesini doldurmayacak.", "machine_learning_max_detection_distance": "Maksimum tespit uzaklığı", "machine_learning_max_detection_distance_description": "Resimleri birbirinin çifti saymak için hesap edilecek azami benzerlik ÃļlçÃŧsÃŧ, 0.001-0.1 aralığında. Daha yÃŧksek değer daha hassas olup daha fazla çift tespit eder ancak çift olmayan resimleri birbirinin çifti sayabilir.", @@ -156,23 +178,37 @@ "memory_cleanup_job": "AnÄą temizliği", "memory_generate_job": "AnÄą oluşturma", "metadata_extraction_job": "Meta verilerinden AyÄąkla", - "metadata_extraction_job_description": "GPS ve çÃļzÃŧnÃŧrlÃŧk gibi ger bir varlığın meta veri bilgilerini ayÄąklayÄąn", + "metadata_extraction_job_description": "GPS, yÃŧzler ve çÃļzÃŧnÃŧrlÃŧk gibi her bir Ãļğeden meta veri bilgilerini Ã§ÄąkarÄąn", "metadata_faces_import_setting": "YÃŧz içe aktarmayÄą etkinleştir", "metadata_faces_import_setting_description": "YÃŧzleri, EXIF verileri ve sidecar dosyalardan getir", "metadata_settings": "Metaveri AyarlarÄą", "metadata_settings_description": "Metaveri ayarlarÄąnÄą yÃļnet", "migration_job": "Birleştirme", - "migration_job_description": "VarlÄąklar ve yÃŧzler için resim çerçeve Ãļnizlemelerini en yeni klasÃļr yapÄąsÄąna aktar", + "migration_job_description": "Öğeler ve yÃŧzler için kÃŧçÃŧk resimleri en son klasÃļr yapÄąsÄąna taÅŸÄąyÄąn", + "nightly_tasks_cluster_faces_setting_description": "Yeni algÄąlanan yÃŧzlerde yÃŧz tanÄąma işlemini çalÄąÅŸtÄąrÄąn", + "nightly_tasks_cluster_new_faces_setting": "Yeni yÃŧzleri bir araya getirin", + "nightly_tasks_database_cleanup_setting": "VeritabanÄą temizleme gÃļrevleri", + "nightly_tasks_database_cleanup_setting_description": "VeritabanÄąndan eski, sÃŧresi dolmuş verileri temizleyin", + "nightly_tasks_generate_memories_setting": "AnÄąlar oluşturun", + "nightly_tasks_generate_memories_setting_description": "Öğelerden yeni anÄąlar yaratÄąn", + "nightly_tasks_missing_thumbnails_setting": "Eksik kÃŧçÃŧk resimleri oluştur", + "nightly_tasks_missing_thumbnails_setting_description": "KÃŧçÃŧk resim oluşturmak için kÃŧçÃŧk resim içermeyen Ãļğeleri sÄąraya alÄąn", + "nightly_tasks_settings": "Gece GÃļrevleri AyarlarÄą", + "nightly_tasks_settings_description": "Gece gÃļrevlerini yÃļnet", + "nightly_tasks_start_time_setting": "BaşlangÄąÃ§ saati", + "nightly_tasks_start_time_setting_description": "Sunucunun gece gÃļrevlerini çalÄąÅŸtÄąrmaya başladığı saat", + "nightly_tasks_sync_quota_usage_setting": "Kota kullanÄąmÄąnÄą eşzamanla", + "nightly_tasks_sync_quota_usage_setting_description": "Mevcut kullanÄąma gÃļre kullanÄącÄą depolama kotasÄąnÄą gÃŧncelle", "no_paths_added": "Yol eklenmedi", "no_pattern_added": "Desen eklenmedi", - "note_apply_storage_label_previous_assets": "Not: Daha Ãļnce yÃŧklenen varlÄąklara Depolama Etiketi uygulamak için şu komutu çalÄąÅŸtÄąrÄąn", + "note_apply_storage_label_previous_assets": "Not: Daha Ãļnce yÃŧklenen Ãļğelere Depolama Etiketi uygulamak için şu komutu çalÄąÅŸtÄąrÄąn", "note_cannot_be_changed_later": "NOT: Bu daha sonra değiştirilemez!", "notification_email_from_address": "Şu adresten", - "notification_email_from_address_description": "GÃļndericinin email adresi, Ãļrnek: \"Immich Fotoğraf Sunucusu \"", + "notification_email_from_address_description": "GÃļnderen e-posta adresi, Ãļrneğin: \"Immich GÃļrsel Sunucusu \". E-posta gÃļnderilmesine izin verdiğiniz bir adres kullandığınÄązdan emin olun.", "notification_email_host_description": "E-posta sunucusunun ana bilgisayarÄą (Ãļrneğin, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Sertifika hatalarÄąnÄą gÃļrmezden gel", "notification_email_ignore_certificate_errors_description": "TLS sertifika doğrulama ayarlarÄąnÄą gÃļrmezden gel (Önerilmez)", - "notification_email_password_description": "Email sunucusuyla doğrulama için kullanÄąlacak olan şifre", + "notification_email_password_description": "E-posta sunucusunda kimlik doğrulama yaparken kullanÄąlacak şifre", "notification_email_port_description": "Email sunucusunun port numarasÄą (25, 465, 587 gibi)", "notification_email_sent_test_email_button": "Test emaili yolla ve kaydet", "notification_email_setting_description": "Email yollama bildirim ayarlarÄą", @@ -181,17 +217,20 @@ "notification_email_test_email_sent": "Test emaili {email} adresine yollandÄą. LÃŧtfen gelen kutunuzu kontrol edin.", "notification_email_username_description": "Email sunucu doğrulamasÄąnda kullanÄąlacak olan kullanÄącÄą adÄą", "notification_enable_email_notifications": "Email bildirimlerini etkinleştir", - "notification_settings": "Bildirim ayarlarÄą", + "notification_settings": "Bildirim AyarlarÄą", "notification_settings_description": "Email ve bildirim ayarlarÄąnÄą yÃļnet", "oauth_auto_launch": "Otomatik başlat", "oauth_auto_launch_description": "Giriş sayfasÄąna girildiğinde OAuth akÄąÅŸÄąnÄą otomatik olarak başlat", "oauth_auto_register": "Otomatik kayÄąt", "oauth_auto_register_description": "OAuth ile giriş yapan yeni kullanÄącÄąlarÄą otomatik kaydet", "oauth_button_text": "Buton yazÄąsÄą", + "oauth_client_secret_description": "OAuth sağlayÄącÄąsÄą PKCE (Kod Değişimi İçin KanÄąt AnahtarÄą) desteği sunmuyorsa gereklidir", "oauth_enable_description": "OAuth ile giriş yap", "oauth_mobile_redirect_uri": "Mobil yÃļnlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanÄąlacak YÃļnlendirme Adresi", "oauth_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayÄącÄąsÄą olduğunda etkinleştir, Ãļrneğin ''{callback}''", + "oauth_role_claim": "Rol Talebi", + "oauth_role_claim_description": "Bu iddianÄąn varlığına gÃļre otomatik olarak yÃļnetici erişimi verin. İddia, 'kullanÄącÄą' veya 'yÃļnetici' olabilir.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth giriş ayarlarÄąnÄą yÃļnet", "oauth_settings_more_details": "Bu Ãļzellik hakkÄąnda daha fazla bilgi için bu sayfayÄą ziyaret edin DÃļkÃŧmanlar.", @@ -200,17 +239,17 @@ "oauth_storage_quota_claim": "Depolama kotasÄą talebi", "oauth_storage_quota_claim_description": "KullanÄącÄąya depolama kotasÄą koymak için kullanÄąlacak değer (en: OAuth claim).", "oauth_storage_quota_default": "VarsayÄąlan depolama kotasÄą (GiB)", - "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse konulacak kota. GiB cinsinden, sÄąnÄąrsÄąz kota için 0 kullanÄąn.", - "oauth_timeout": "İstek zaman aÅŸÄąmÄą", + "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse GiB cinsinden konulacak kota.", + "oauth_timeout": "İstek Zaman AÅŸÄąmÄą", "oauth_timeout_description": "Milisaniye cinsinden istek zaman aÅŸÄąmÄą", - "password_enable_description": "Email ve şifre ile giriş yap", - "password_settings": "Şifre giriş", + "password_enable_description": "E-posta ve şifre ile giriş yapÄąn", + "password_settings": "Şifre ile Giriş", "password_settings_description": "Şifre giriş ayarlarÄąnÄą yÃļnet", "paths_validated_successfully": "TÃŧm yollar başarÄąyla doğrulandÄą", "person_cleanup_job": "Kişi temizleme", - "quota_size_gib": "Kota boyutu (GiB)", + "quota_size_gib": "Kota Boyutu (GiB)", "refreshing_all_libraries": "TÃŧm kÃŧtÃŧphaneler yenileniyor", - "registration": "YÃļnetici kaydÄą", + "registration": "YÃļnetici KaydÄą", "registration_description": "Sistemdeki ilk kullanÄącÄą olduğunuz için hesabÄąnÄąz YÃļnetici olarak ayarlandÄą. Yeni oluşturulan Ãŧyeliklerin, ve yÃļnetici gÃļrevlerinin sorumlusu olarak atandÄąnÄąz.", "require_password_change_on_login": "KullanÄącÄąnÄąn ilk girişinde şifre değiştirmesini zorunlu kÄąl", "reset_settings_to_default": "AyarlarÄą varsayÄąlana sÄąfÄąrla", @@ -222,27 +261,28 @@ "server_external_domain_settings_description": "PaylaÅŸÄąlan fotoğraflar için domain, http(s):// dahil", "server_public_users": "Harici KullanÄącÄąlar", "server_public_users_description": "PaylaÅŸÄąlan albÃŧmlere bir kullanÄącÄą eklenirken tÃŧm kullanÄącÄąlar (ad ve e-posta) listelenir. Devre dÄąÅŸÄą bÄąrakÄąldığında, kullanÄącÄą listesi yalnÄązca yÃļnetici kullanÄącÄąlar tarafÄąndan kullanÄąlabilir.", - "server_settings": "Sunucu ayarlarÄą", + "server_settings": "Sunucu AyarlarÄą", "server_settings_description": "Sunucu ayarlarÄąnÄą yÃļnet", "server_welcome_message": "Hoş geldin mesajÄą", "server_welcome_message_description": "Giriş sayfasÄąnda gÃļsterilen mesaj.", "sidecar_job": "Ek dosya ile taÅŸÄąnan metadata", - "sidecar_job_description": "Ek dosyalardaki metadatalarÄą bul ve gÃŧncelle", + "sidecar_job_description": "Dosya sisteminden yan araç meta verilerini keşfedin veya eşzamanlayÄąn", "slideshow_duration_description": "Her fotoğrafÄąn kaç saniye gÃļrÃŧntÃŧleneceği", - "smart_search_job_description": "AkÄąllÄą aramayÄą desteklemek için tÃŧm varlÄąklarda makine Ãļğrenmesini çalÄąÅŸtÄąrÄąn", - "storage_template_date_time_description": "DosyanÄąn yaratÄąlma tarihini, varlığın yaratÄąlma tarihi olarak kullanÄąlacak", + "smart_search_job_description": "AkÄąllÄą aramayÄą desteklemek için tÃŧm Ãļğelerde makine Ãļğrenmesini çalÄąÅŸtÄąrÄąn", + "storage_template_date_time_description": "Öğenin oluşturulma zaman damgasÄą, tarih ve saat bilgisi için kullanÄąlÄąr", "storage_template_date_time_sample": "Örnek tarih {date}", "storage_template_enable_description": "Depolama şablon motorunu etkinleştir", "storage_template_hash_verification_enabled": "Hash doğrulama etkinleştirildi", "storage_template_hash_verification_enabled_description": "Hash doğrulamayÄą etkinleştirir, eğer ne işe yaradığınÄą bilmiyorsanÄąz bunu devre dÄąÅŸÄą bÄąrakmayÄąn", "storage_template_migration": "Depolama şablonu birleştirme", - "storage_template_migration_description": "Geçerli {template} ayarlarÄąnÄą daha Ãļnce yÃŧklenmiş olan varlÄąklara uygula", - "storage_template_migration_info": "Şablon ayarlarÄąndaki değişiklikler sadece yeni varlÄąklara uygulanacak. Şablon ayarlarÄąnÄą daha Ãļnce yÃŧklenmiş olan varlÄąklara uygulamak için {job} çalÄąÅŸtÄąrÄąn.", + "storage_template_migration_description": "Geçerli {template} ayarlarÄąnÄą daha Ãļnce yÃŧklenmiş olan Ãļğelere uygula", + "storage_template_migration_info": "Depolama şablonu tÃŧm dosya uzantÄąlarÄąnÄą kÃŧçÃŧk harfe dÃļnÃŧştÃŧrecektir. Şablon ayarlarÄąndaki değişiklikler sadece yeni Ãļğelere uygulanacak. Şablon ayarlarÄąnÄą daha Ãļnce yÃŧklenmiş olan Ãļğelere uygulamak için {job} çalÄąÅŸtÄąrÄąn.", "storage_template_migration_job": "Depolama Adreslerini Değiştirme GÃļrevi", "storage_template_more_details": "Bu Ãļzellik hakkÄąnda daha fazla bilgi için, Depolama Şablonu ve onun etkileri kÄąsmÄąna bakÄąn", + "storage_template_onboarding_description_v2": "Etkinleştirildiğinde, bu Ãļzellik dosyalarÄą kullanÄącÄą tanÄąmlÄą bir şablona gÃļre otomatik olarak organize eder. Daha fazla bilgi için lÃŧtfen belgelere bakÄąn.", "storage_template_path_length": "Tahmini dosya adresi uzunluğu: {length, number}/{limit, number}", "storage_template_settings": "Depolama Şablonu", - "storage_template_settings_description": "YÃŧklenen dosyanÄąn ismini ve klasÃļr yapÄąsÄąnÄą dÃŧzenle", + "storage_template_settings_description": "YÃŧklenen Ãļğenin ismini ve klasÃļr yapÄąsÄąnÄą dÃŧzenle", "storage_template_user_label": "{label} kullanÄącÄąnÄą dosyalarÄą için kullanÄąlan alt klasÃļrdÃŧr", "system_settings": "Sistem AyarlarÄą", "tag_cleanup_job": "Etiket temizleme", @@ -254,17 +294,17 @@ "template_email_update_album": "AlbÃŧm Şablonunu GÃŧncelle", "template_email_welcome": "Hoş geldiniz e-posta şablonu", "template_settings": "Bildirim ŞablonlarÄą", - "template_settings_description": "Bildirim şablonlarÄąnÄą yÃļnet.", + "template_settings_description": "Bildirim şablonlarÄąnÄą yÃļnet", "theme_custom_css_settings": "Özel CSS", "theme_custom_css_settings_description": "CSS (Cascading Style Sheets) kullanÄąlarak Immich'in tasarÄąmÄą değiştirilebilir.", - "theme_settings": "Tema ayarlarÄą", + "theme_settings": "Tema AyarlarÄą", "theme_settings_description": "Immich web arayÃŧzÃŧnÃŧn Ãļzelleştirilmesi ayarlarÄąnÄą yÃļnet", "thumbnail_generation_job": "Önizlemeleri oluştur", - "thumbnail_generation_job_description": "Her kişi ve obje için bÃŧyÃŧk, kÃŧçÃŧk ve bulanÄąk thumbnail (kÃŧçÃŧk resim) oluştur", + "thumbnail_generation_job_description": "Her bir Ãļğe için bÃŧyÃŧk, kÃŧçÃŧk ve bulanÄąk kÃŧçÃŧk resimler ile her kişi için kÃŧçÃŧk resimler oluşturun", "transcoding_acceleration_api": "HÄązlandÄąrma API", "transcoding_acceleration_api_description": "Video formatÄą çevriminde kullanÄąlacak API. Bu ayara 'mÃŧmkÃŧn olduğunca' uyulmaktadÄąr; seçilen API'da sorun Ã§Äąkarsa yazÄąlÄąm tabanlÄą çevirime dÃļnÃŧlÃŧr. VP9 donanÄąmÄąnÄąza bağlÄą olarak çalÄąÅŸmayabilir.", "transcoding_acceleration_nvenc": "NVENC (NVIDIA GPU gerektirir)", - "transcoding_acceleration_qsv": "Quick Sync (7. nesil veya daha yeni bir Intel CPU gerektirir)", + "transcoding_acceleration_qsv": "HÄązlÄą Eşzamanlama (7. nesil veya daha yeni bir Intel CPU gerektirir)", "transcoding_acceleration_rkmpp": "RKMPP (Sadece Rockchip SOC'ler)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Kabul edilen ses kodekleri", @@ -286,13 +326,13 @@ "transcoding_encoding_options": "Kodlama Seçenekleri", "transcoding_encoding_options_description": "KodlanmÄąÅŸ videolar için kodekleri, çÃļzÃŧnÃŧrlÃŧğÃŧ, kaliteyi ve diğer seçenekleri ayarlayÄąn", "transcoding_hardware_acceleration": "DonanÄąm HÄązlandÄąrma", - "transcoding_hardware_acceleration_description": "Deneysel; daha hÄązlÄą, fakat aynÄą bitrate ayarlarÄąnda daha dÃŧşÃŧk kaliteye sahip", + "transcoding_hardware_acceleration_description": "Deneysel: daha hÄązlÄą dÃļnÃŧştÃŧrme, ancak aynÄą bit hÄązÄąnda kaliteyi dÃŧşÃŧrebilir", "transcoding_hardware_decoding": "DonanÄąm çÃļzÃŧcÃŧ", "transcoding_hardware_decoding_setting_description": "Uçtan uca hÄązlandÄąrmayÄą, sadece kodlamayÄą hÄązlandÄąrmanÄąn yerine etkinleştirir. TÃŧm videolarda çalÄąÅŸmayabilir.", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yÃŧksek değerler sÄąkÄąÅŸtÄąrma verimliliğini artÄąrÄąr, ancak kodlamayÄą yavaşlatÄąr. Eski cihazlarda donanÄąm hÄązlandÄąrma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dÄąÅŸÄą bÄąrakÄąr, -1 ise bu değeri otomatik olarak ayarlar.", "transcoding_max_bitrate": "Maksimum bitrate", - "transcoding_max_bitrate_description": "Maksimum bit hÄązÄą ayarlamak, kaliteye kÃŧçÃŧk bir maliyetle dosya boyutlarÄąnÄą daha ÃļngÃļrÃŧlebilir hale getirebilir.", + "transcoding_max_bitrate_description": "Maksimum bit hÄązÄą ayarlamak, kaliteyi az bir maliyetle dÃŧşÃŧrerek dosya boyutlarÄąnÄą daha ÃļngÃļrÃŧlebilir hale getirebilir. 720p çÃļzÃŧnÃŧrlÃŧkte, tipik değerler VP9 veya HEVC için 2600 kbit/s, H.264 için ise 4500 kbit/s’dir. 0 olarak ayarlanÄąrsa devre dÄąÅŸÄą bÄąrakÄąlÄąr.", "transcoding_max_keyframe_interval": "Maksimum ana kare aralığı", "transcoding_max_keyframe_interval_description": "Ana kareler arasÄąndaki maksimum kare mesafesini ayarlar. DÃŧşÃŧk değerler sÄąkÄąÅŸtÄąrma verimliliğini kÃļtÃŧleştirir, ancak arama sÃŧrelerini iyileştirir ve hÄązlÄą hareket içeren sahnelerde kaliteyi artÄąrabilir. 0 bu değeri otomatik olarak ayarlar.", "transcoding_optimal_description": "Hedef çÃļzÃŧnÃŧrlÃŧkten yÃŧksek veya kabul edilen formatta olmayan videolar", @@ -323,15 +363,18 @@ "transcoding_video_codec_description": "VP9 yÃŧksek verimliliğe ve web uyumluluğuna sahiptir, ancak kod dÃļnÃŧştÃŧrme işlemi daha uzun sÃŧrer. HEVC benzer performans gÃļsterir ancak web uyumluluğu daha dÃŧşÃŧktÃŧr. H.264 geniş çapta uyumludur ve kod dÃļnÃŧştÃŧrmesi hÄązlÄądÄąr, ancak çok daha bÃŧyÃŧk dosyalar Ãŧretir. AV1 en verimli codec'tir ancak eski cihazlarda desteği yoktur.", "trash_enabled_description": "ÇÃļp Ãļzelliklerini etkinleştir", "trash_number_of_days": "GÃŧn sayÄąsÄą", - "trash_number_of_days_description": "VarlÄąklarÄąn kalÄącÄą olarak silinmeden Ãļnce çÃļpte kaç gÃŧn tutulacağı", - "trash_settings": "ÇÃļp ayarlarÄą", - "trash_settings_description": "ÇÃļp ayarlarÄąnÄą yÃļnet", + "trash_number_of_days_description": "Öğeleri kalÄącÄą olarak silmeden Ãļnce çÃļp kutusunda tutma sÃŧresi (gÃŧn)", + "trash_settings": "ÇÃļp Kutusu AyarlarÄą", + "trash_settings_description": "ÇÃļp kutusu ayarlarÄąnÄą yÃļnet", + "unlink_all_oauth_accounts": "TÃŧm OAuth hesaplarÄąnÄąn bağlantÄąsÄąnÄą kaldÄąr", + "unlink_all_oauth_accounts_description": "Yeni bir sağlayÄącÄąya geçmeden Ãļnce tÃŧm OAuth hesaplarÄąnÄą kaldÄąrÄąlmayÄą unutmayÄąn.", + "unlink_all_oauth_accounts_prompt": "TÃŧm OAuth hesaplarÄąnÄą kaldÄąrmak istediğinizden emin misiniz? Bu, her kullanÄącÄą için OAuth kimliğini sÄąfÄąrlar ve geri alÄąnamaz.", "user_cleanup_job": "KullanÄącÄą temizleme", - "user_delete_delay": "{user} hesabÄą ve varlÄąklarÄą {delay, plural, one {# day} other {# days}} gÃŧn içinde kalÄącÄą olarak silinmek için planlandÄą.", + "user_delete_delay": "{user} hesabÄą ve Ãļğeleri {delay, plural, one {# day} other {# days}} gÃŧn içinde kalÄącÄą olarak silinecektir.", "user_delete_delay_settings": "Silme gecikmesi", - "user_delete_delay_settings_description": "Bir kullanÄącÄąnÄąn hesabÄąnÄą ve varlÄąklarÄąnÄą kalÄącÄą olarak silmek için kaldÄąrÄąldÄąktan sonra gereken gÃŧn sayÄąsÄą. KullanÄącÄą silme işi, silinmeye hazÄąr kullanÄącÄąlarÄą kontrol etmek için gece yarÄąsÄą çalÄąÅŸÄąr. Bu ayardaki değişiklikler bir sonraki yÃŧrÃŧtmede değerlendirilecektir.", - "user_delete_immediately": "{user}'in hesabÄą ve varlÄąklarÄą hemen kalÄącÄą olarak silinmek Ãŧzere sÄąraya alÄąnacak.", - "user_delete_immediately_checkbox": "KullanÄącÄą ve varlÄąklarÄą hemen silinmek Ãŧzere sÄąraya al", + "user_delete_delay_settings_description": "Bir kullanÄącÄąnÄąn hesabÄąnÄą ve Ãļğelerini kalÄącÄą olarak silmek için kaldÄąrÄąldÄąktan sonra gereken gÃŧn sayÄąsÄą. KullanÄącÄą silme işi, silinmeye hazÄąr kullanÄącÄąlarÄą kontrol etmek için gece yarÄąsÄą çalÄąÅŸÄąr. Bu ayardaki değişiklikler bir sonraki yÃŧrÃŧtmede değerlendirilecektir.", + "user_delete_immediately": "{user}'in hesabÄą ve Ãļğeleri hemen kalÄącÄą olarak silinmek Ãŧzere sÄąraya alÄąnacak.", + "user_delete_immediately_checkbox": "KullanÄącÄą ve Ãļğeleri hemen silmek için sÄąraya alÄąn", "user_details": "KullanÄącÄą AyrÄąntÄąlarÄą", "user_management": "KullanÄącÄą YÃļnetimi", "user_password_has_been_reset": "KullanÄącÄąnÄąn şifresi sÄąfÄąrlandÄą:", @@ -339,28 +382,32 @@ "user_restore_description": "{user} kullanÄącÄąsÄą geri yÃŧklenecek.", "user_restore_scheduled_removal": "KullanÄącÄąyÄą geri yÃŧkle - {date, date, long} tarihinde planlanan kaldÄąrma", "user_settings": "KullanÄącÄą AyarlarÄą", - "user_settings_description": "KullanÄącÄą AyarlarÄąnÄą YÃļnet", + "user_settings_description": "KullanÄącÄą ayarlarÄąnÄą yÃļnet", "user_successfully_removed": "KullanÄącÄą {email} başarÄąyla kaldÄąrÄąldÄą.", "version_check_enabled_description": "SÃŧrÃŧm kontrolÃŧ etkin", "version_check_implications": "SÃŧrÃŧm kontrol Ãļzelliği, github.com ile periyodik iletişime dayanÄąr", - "version_check_settings": "Versiyon kontrolÃŧ", + "version_check_settings": "SÃŧrÃŧm KontrolÃŧ", "version_check_settings_description": "Yeni sÃŧrÃŧm bildirimini etkinleştir/devre dÄąÅŸÄą bÄąrak", "video_conversion_job": "VideolarÄą dÃļnÃŧştÃŧr", "video_conversion_job_description": "TarayÄącÄąlar ve cihazlarla daha geniş uyumluluk için videolarÄą dÃļnÃŧştÃŧr" }, - "admin_email": "YÃļnetici Emaili", + "admin_email": "YÃļnetici E-postasÄą", "admin_password": "YÃļnetici Şifresi", "administration": "YÃļnetim", "advanced": "Gelişmiş", - "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albÃŧm eşleme sÃŧzgeci kullanÄąn", + "advanced_settings_enable_alternate_media_filter_subtitle": "Eşzamanlama sÄąrasÄąnda medyayÄą alternatif ÃļlçÃŧtlere gÃļre sÃŧzgeçten geçirmek için bu seçeneği kullanÄąn. UygulamanÄąn tÃŧm albÃŧmleri algÄąlamasÄąnda sorun yaÅŸÄąyorsanÄąz yalnÄązca bu durumda deneyin.", + "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albÃŧm eşzamanlama sÃŧzgeci kullanÄąn", "advanced_settings_log_level_title": "GÃŧnlÃŧk dÃŧzeyi: {level}", - "advanced_settings_prefer_remote_subtitle": "BazÄą cihazlar, cihazdaki Ãļğelerin kÃŧçÃŧk resimlerini gÃļstermekte çok yavaştÄąr. Bunun yerine sunucudaki kÃŧçÃŧk resimleri gÃļstermek için bu ayarÄą etkinleştirin.", + "advanced_settings_prefer_remote_subtitle": "BazÄą cihazlar yerel Ãļğelerden kÃŧçÃŧk resimleri yÃŧklerken çok yavaş çalÄąÅŸÄąr. Bunun yerine uzak gÃļrÃŧntÃŧleri yÃŧklemek için bu ayarÄą etkinleştirin.", "advanced_settings_prefer_remote_title": "Uzak gÃļrÃŧntÃŧleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte gÃļndermesi gereken proxy header'larÄą tanÄąmlayÄąn", "advanced_settings_proxy_headers_title": "Proxy Header'lar", + "advanced_settings_readonly_mode_subtitle": "FotoğraflarÄąn yalnÄązca gÃļrÃŧntÃŧlenebildiği salt okunur modu etkinleştirir; birden fazla gÃļrÃŧntÃŧ seçme, paylaşma, aktarma, silme gibi işlemler devre dÄąÅŸÄą bÄąrakÄąlÄąr. Ana ekrandan kullanÄącÄą avatarÄą aracÄąlığıyla salt okunur modu Etkinleştirin/Devre dÄąÅŸÄą bÄąrakÄąn", + "advanced_settings_readonly_mode_title": "Salt okunur Mod", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktasÄą için SSL sertifika doğrulamasÄąnÄą atlar. Kendinden imzalÄą sertifikalar için gereklidir.", "advanced_settings_self_signed_ssl_title": "Kendi kendine imzalanmÄąÅŸ SSL sertifikalarÄąna izin ver", - "advanced_settings_sync_remote_deletions_title": "Uzaktan silinmeleri eşle [DENEYSEL]", + "advanced_settings_sync_remote_deletions_subtitle": "Web Ãŧzerinde işlem yapÄąldığında, bu aygÄąttaki Ãļğeyi otomatik olarak sil veya geri yÃŧkle", + "advanced_settings_sync_remote_deletions_title": "Uzaktan silmeleri eşzamanla [DENEYSEL]", "advanced_settings_tile_subtitle": "Gelişmiş kullanÄącÄą ayarlarÄą", "advanced_settings_troubleshooting_subtitle": "Sorun giderme için ek Ãļzellikleri etkinleştirin", "advanced_settings_troubleshooting_title": "Sorun Giderme", @@ -372,6 +419,7 @@ "album_cover_updated": "AlbÃŧm Kapağı gÃŧncellendi", "album_delete_confirmation": "{album} albÃŧmÃŧnÃŧ silmek istediğinize emin misiniz?", "album_delete_confirmation_description": "AlbÃŧm paylaÅŸÄąlÄąyorsa, diğer kullanÄącÄąlar artÄąk bu albÃŧme erişemeyecektir.", + "album_deleted": "AlbÃŧm silindi", "album_info_card_backup_album_excluded": "HARİÇ", "album_info_card_backup_album_included": "DAHİL", "album_info_updated": "AlbÃŧm bilgisi gÃŧncellendi", @@ -381,22 +429,28 @@ "album_options": "AlbÃŧm seçenekleri", "album_remove_user": "KullanÄącÄąyÄą kaldÄąr?", "album_remove_user_confirmation": "{user} kullanÄącÄąsÄąnÄą kaldÄąrmak istediğinize emin misiniz?", + "album_search_not_found": "AramanÄązla eşleşen albÃŧm bulunamadÄą", "album_share_no_users": "GÃļrÃŧnÃŧşe gÃļre bu albÃŧmÃŧ tÃŧm kullanÄącÄąlarla paylaştÄąnÄąz veya paylaşacak herhangi bir başka kullanÄącÄąnÄąz yok.", + "album_summary": "AlbÃŧm Ãļzeti", "album_updated": "AlbÃŧm gÃŧncellendi", - "album_updated_setting_description": "PaylaÅŸÄąlan bir albÃŧme yeni bir varlÄąk eklendiğinde email bildirimi alÄąn", + "album_updated_setting_description": "PaylaÅŸÄąlan bir albÃŧme yeni bir Ãļğe eklendiğinde e-posta bildirimi alÄąn", "album_user_left": "{album}den ayrÄąldÄąnÄąz", "album_user_removed": "{user} kaldÄąrÄąldÄą", "album_viewer_appbar_delete_confirm": "Bu albÃŧmÃŧ hesabÄąnÄązdan silmek istediğinizden emin misiniz?", "album_viewer_appbar_share_err_delete": "AlbÃŧm silinemedi", "album_viewer_appbar_share_err_leave": "AlbÃŧmden Ã§ÄąkÄąlamadÄą", - "album_viewer_appbar_share_err_remove": "AlbÃŧmden Ãļğeleri kaldÄąrmada sorunlar var", + "album_viewer_appbar_share_err_remove": "AlbÃŧmden Ãļğeler kaldÄąrÄąrken sorunlar yaşanÄąyor", "album_viewer_appbar_share_err_title": "AlbÃŧm başlığı değiştirilemedi", "album_viewer_appbar_share_leave": "AlbÃŧmden Ã§Äąk", - "album_viewer_appbar_share_to": "Paylaş:", + "album_viewer_appbar_share_to": "Paylaşma", "album_viewer_page_share_add_users": "KullanÄącÄą ekle", "album_with_link_access": "Link'e sahip olan herhangi bir kişinin bu albÃŧmdeki fotoğraflarÄą ve kişileri gÃļrmesine izin ver.", "albums": "AlbÃŧmler", "albums_count": "{count, plural, one {{count, number} AlbÃŧm} other {{count, number} AlbÃŧm}}", + "albums_default_sort_order": "VarsayÄąlan albÃŧm sÄąralama dÃŧzeni", + "albums_default_sort_order_description": "Yeni albÃŧm oluştururken kullanÄąlacak başlangÄąÃ§ Ãļğe sÄąralama dÃŧzeni.", + "albums_feature_description": "Diğer kullanÄącÄąlarla paylaÅŸÄąlabilen Ãļğe koleksiyonlarÄą.", + "albums_on_device_count": "Cihazdaki albÃŧmler ({count})", "all": "TÃŧmÃŧ", "all_albums": "TÃŧm AlbÃŧmler", "all_people": "TÃŧm Kişiler", @@ -416,7 +470,9 @@ "app_bar_signout_dialog_title": "Ã‡ÄąkÄąÅŸ", "app_settings": "Uygulama AyarlarÄą", "appears_in": "Şurada gÃļrÃŧnÃŧr", + "apply_count": "Uygula ({count, number})", "archive": "Arşiv", + "archive_action_prompt": "{count} arşive eklendi", "archive_or_unarchive_photo": "FotoğrafÄą arşivle/arşivden Ã§Äąkar", "archive_page_no_archived_assets": "Arşivlenmiş Ãļğe bulunamadÄą", "archive_page_title": "Arşiv ({count})", @@ -430,9 +486,9 @@ "asset_action_share_err_offline": "ÇevrimdÄąÅŸÄą Ãļğeler alÄąnamÄąyor, atlanÄąyor", "asset_added_to_album": "AlbÃŧme eklendi", "asset_adding_to_album": "AlbÃŧme ekleniyorâ€Ļ", - "asset_description_updated": "VarlÄąk aÃ§ÄąklamasÄą gÃŧncellendi", - "asset_filename_is_offline": "VarlÄąk {filename} çevrimdÄąÅŸÄą", - "asset_has_unassigned_faces": "VarlÄąk, atanmamÄąÅŸ yÃŧzler içeriyor", + "asset_description_updated": "Öğe aÃ§ÄąklamasÄą gÃŧncellendi", + "asset_filename_is_offline": "Öğe {filename} çevrimdÄąÅŸÄą", + "asset_has_unassigned_faces": "Öğe, atanmamÄąÅŸ yÃŧzler içeriyor", "asset_hashing": "Karma (hashleme) oluşturuluyorâ€Ļ", "asset_list_group_by_sub_title": "Grupla", "asset_list_layout_settings_dynamic_layout_title": "Dinamik dÃŧzen", @@ -442,54 +498,65 @@ "asset_list_layout_sub_title": "DÃŧzen", "asset_list_settings_subtitle": "Fotoğraf Äązgara dÃŧzeni ayarlarÄą", "asset_list_settings_title": "Fotoğraf IzgarasÄą", - "asset_offline": "VarlÄąk Çevrim DÄąÅŸÄą", - "asset_offline_description": "Bu harici varlÄąk artÄąk diskte bulunmuyor. YardÄąm için lÃŧtfen Immich yÃļneticinizle iletişime geçin.", + "asset_offline": "Öğe Çevrim DÄąÅŸÄą", + "asset_offline_description": "Bu harici Ãļğe artÄąk diskte bulunmuyor. YardÄąm için lÃŧtfen Immich yÃļneticinizle iletişime geçin.", "asset_restored_successfully": "Öğe başarÄąyla geri yÃŧklendi", "asset_skipped": "AtlandÄą", "asset_skipped_in_trash": "ÇÃļpte", + "asset_trashed": "Öğe çÃļpe atÄąldÄą", + "asset_troubleshoot": "Öğe Sorun Giderme", "asset_uploaded": "YÃŧklendi", "asset_uploading": "YÃŧkleniyorâ€Ļ", "asset_viewer_settings_subtitle": "Galeri gÃļrÃŧntÃŧleyici ayarlarÄąnÄą dÃŧzenle", "asset_viewer_settings_title": "İçerik GÃļrÃŧntÃŧleyici", - "assets": "VarlÄąklar", - "assets_added_count": "{count, plural, one {# varlÄąk eklendi} other {# varlÄąk eklendi}}", - "assets_added_to_album_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} albÃŧme eklendi", - "assets_added_to_name_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} {hasName, select, true {{name}} other {yeni albÃŧm}} içine eklendi", - "assets_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}}", + "assets": "Öğeler", + "assets_added_count": "Eklendi {count, plural, one {# asset} other {# assets}}", + "assets_added_to_album_count": "AlbÃŧme {count, plural, one {# asset} other {# assets}} eklendi", + "assets_added_to_albums_count": "Eklendi {assetTotal, plural, one {# asset} other {# assets}} buraya {albumTotal, plural, one {# album} other {# albums}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} albÃŧme eklenemiyor", + "assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} hiçbir albÃŧme eklenemez", + "assets_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}}", "assets_deleted_permanently": "{count} Ãļğe kalÄącÄą olarak silindi", "assets_deleted_permanently_from_server": "{count} Ãļğe kalÄącÄą olarak Immich sunucusundan silindi", - "assets_moved_to_trash_count": "{count, plural, one {# varlÄąk} other {# varlÄąk}} çÃļpe taÅŸÄąndÄą", - "assets_permanently_deleted_count": "KalÄącÄą olarak silindi {count, plural, one {# varlÄąk} other {# varlÄąklar}}", - "assets_removed_count": "KaldÄąrÄąldÄą {count, plural, one {# varlÄąk} other {# varlÄąklar}}", + "assets_downloaded_failed": "{count, plural, one {İndirilen # dosya - {error} dosya başarÄąsÄąz} other {İndirilen # dosyalar - {error} dosyalar başarÄąsÄąz oldu}}", + "assets_downloaded_successfully": "{count, plural, one {# dosya başarÄąyla indirildi} other {# dosya başarÄąyla indirildi}}", + "assets_moved_to_trash_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} çÃļpe taÅŸÄąndÄą", + "assets_permanently_deleted_count": "KalÄącÄą olarak silindi {count, plural, one {# Ãļğe} other {# Ãļğeler}}", + "assets_removed_count": "KaldÄąrÄąldÄą {count, plural, one {# Ãļğe} other {# Ãļğeler}}", "assets_removed_permanently_from_device": "{count} Ãļğe cihazÄąnÄązdan kalÄącÄą olarak silindi", - "assets_restore_confirmation": "TÃŧm çÃļp kutusundaki varlÄąklarÄąnÄązÄą geri yÃŧklemek istediğinizden emin misiniz? Bu işlemi geri alamazsÄąnÄąz! AyrÄąca, çevrim dÄąÅŸÄą olan varlÄąklarÄąn bu şekilde geri yÃŧklenemeyeceğini unutmayÄąn.", - "assets_restored_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}} geri yÃŧklendi", - "assets_restored_successfully": "{count} Ãļğe geri yÃŧklendi", + "assets_restore_confirmation": "TÃŧm çÃļp kutusundaki Ãļğeleri geri yÃŧklemek istediğinizden emin misiniz? Bu işlemi geri alamazsÄąnÄąz! AyrÄąca, çevrim dÄąÅŸÄą olan Ãļğelerin bu şekilde geri yÃŧklenemeyeceğini unutmayÄąn.", + "assets_restored_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} geri yÃŧklendi", + "assets_restored_successfully": "{count} Ãļğe başarÄąyla geri yÃŧklendi", "assets_trashed": "{count} Ãļğe çÃļpe atÄąldÄą", - "assets_trashed_count": "{count, plural, one {# varlÄąk} other {# varlÄąklar}} çÃļp kutusuna taÅŸÄąndÄą", - "assets_trashed_from_server": "{count} Ãļğe Immich sunucusunda çÃļpe atÄąldÄą", - "assets_were_part_of_album_count": "{count, plural, one {VarlÄąk zaten} other {VarlÄąklar zaten}} albÃŧmÃŧn parçasÄąydÄą", + "assets_trashed_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} çÃļp kutusuna taÅŸÄąndÄą", + "assets_trashed_from_server": "{count} Ãļğe Immich sunucusundan çÃļpe atÄąldÄą", + "assets_were_part_of_album_count": "{count, plural, one {Öğe zaten} other {Öğeler zaten}} albÃŧmÃŧn parçasÄąydÄą", + "assets_were_part_of_albums_count": "{count, plural, one {Öğe zaten} other {Öğeler zaten}} albÃŧmlerin bir parçasÄąydÄą", "authorized_devices": "Yetki Verilmiş Cihazlar", "automatic_endpoint_switching_subtitle": "Belirlenmiş Wi-Fi ağına bağlÄąyken yerel olarak bağlanÄąp başka yerlerde alternatif bağlantÄąyÄą kullan", "automatic_endpoint_switching_title": "Otomatik URL değiştirme", "autoplay_slideshow": "Otomatik slayt gÃļsterisi", "back": "Geri", "back_close_deselect": "Geri, kapat veya seçimi kaldÄąr", + "background_backup_running_error": "Arka plan yedekleme şu anda çalÄąÅŸÄąyor, manuel yedekleme başlatÄąlamÄąyor", "background_location_permission": "Arka plan konum izni", "background_location_permission_content": "Arka planda çalÄąÅŸÄąrken ağ değiştirmek için Immich'in *her zaman* tam konum erişimine sahip olmasÄą gerekir, bÃļylece uygulama Wi-Fi ağınÄąn adÄąnÄą okuyabilir", + "background_options": "Arka Plan Seçenekleri", + "backup": "Yedekle", "backup_album_selection_page_albums_device": "Cihazdaki albÃŧmler ({count})", "backup_album_selection_page_albums_tap": "Seçmek için dokunun, hariç tutmak için çift dokunun", - "backup_album_selection_page_assets_scatter": "VarlÄąklar birden fazla albÃŧme dağılabilir. Bu nedenle, yedekleme işlemi sÄąrasÄąnda albÃŧmler dahil edilebilir veya hariç tutulabilir.", + "backup_album_selection_page_assets_scatter": "Öğeler birden fazla albÃŧme dağılabilir. Bu nedenle, yedekleme işlemi sÄąrasÄąnda albÃŧmler dahil edilebilir veya hariç tutulabilir.", "backup_album_selection_page_select_albums": "AlbÃŧm seç", "backup_album_selection_page_selection_info": "Seçim Bilgileri", "backup_album_selection_page_total_assets": "Toplam eşsiz Ãļğeler", + "backup_albums_sync": "Yedekleme albÃŧmlerinin senkronizasyonu", "backup_all": "TÃŧmÃŧ", - "backup_background_service_backup_failed_message": "Yedekleme başarÄąsÄąz. Tekrar deneniyor...", - "backup_background_service_connection_failed_message": "Sunucuya bağlanÄąlamadÄą. Tekrar deneniyor...", + "backup_background_service_backup_failed_message": "Yedekleme başarÄąsÄąz. Tekrar deneniyorâ€Ļ", + "backup_background_service_connection_failed_message": "Sunucuya bağlanÄąlamadÄą. Tekrar deneniyorâ€Ļ", "backup_background_service_current_upload_notification": "{filename} yÃŧkleniyor", "backup_background_service_default_notification": "Yeni Ãļğeler kontrol ediliyorâ€Ļ", "backup_background_service_error_title": "Yedekleme hatasÄą", - "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor...", + "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyorâ€Ļ", "backup_background_service_upload_failure_notification": "{filename} yÃŧklemesi başarÄąsÄąz oldu", "backup_controller_page_albums": "Yedekleme AlbÃŧmleri", "backup_controller_page_background_app_refresh_disabled_content": "Arka planda yedeklemeyi kullanabilmek için Ayarlar > Genel > Arka Planda Uygulama Yenileme bÃļlÃŧmÃŧnden arka planda uygulama yenilemeyi etkinleştirin.", @@ -508,16 +575,16 @@ "backup_controller_page_background_turn_off": "Arka plan hizmetini kapat", "backup_controller_page_background_turn_on": "Arka plan hizmetini aç", "backup_controller_page_background_wifi": "Sadece Wi-Fi", - "backup_controller_page_backup": "Yedekle", + "backup_controller_page_backup": "Yedek", "backup_controller_page_backup_selected": "Seçilen: ", - "backup_controller_page_backup_sub": "Yedeklenen Ãļğeler", + "backup_controller_page_backup_sub": "Yedeklenen fotoğraflar ve videolar", "backup_controller_page_created": "Oluşturma tarihi: {date}", "backup_controller_page_desc_backup": "UygulamayÄą açtığınÄązda yeni Ãļğelerin sunucuya otomatik olarak yÃŧklenmesi için Ãļn planda yedeklemeyi aÃ§Äąn.", "backup_controller_page_excluded": "Hariç tutuldu: ", "backup_controller_page_failed": "BaşarÄąsÄąz ({count})", "backup_controller_page_filename": "Dosya adÄą: {filename} [{size}]", "backup_controller_page_id": "KNu: {id}", - "backup_controller_page_info": "Yedekleme bilgileri", + "backup_controller_page_info": "Yedekleme Bilgileri", "backup_controller_page_none_selected": "Hiçbiri seçilmedi", "backup_controller_page_remainder": "Kalan", "backup_controller_page_remainder_sub": "Seçili albÃŧmlerden yedeklenecek kalan Ãļğeler", @@ -532,13 +599,16 @@ "backup_controller_page_turn_on": "Ön planda yedeklemeyi aç", "backup_controller_page_uploading_file_info": "Dosya bilgisi yÃŧkleniyor", "backup_err_only_album": "Tek albÃŧm kaldÄąrÄąlamaz", + "backup_error_sync_failed": "Senkronizasyon başarÄąsÄąz. Yedekleme işlemi gerçekleştirilemiyor.", "backup_info_card_assets": "Ãļğeler", "backup_manual_cancelled": "İptal Edildi", "backup_manual_in_progress": "YÃŧkleme halihazÄąrda devam ediyor. Bir sÃŧre sonra deneyin", "backup_manual_success": "BaşarÄąlÄą", "backup_manual_title": "YÃŧkleme durumu", + "backup_options": "Yedekleme Seçenekleri", "backup_options_page_title": "Yedekleme seçenekleri", "backup_setting_subtitle": "Arka planda ve Ãļn planda yÃŧkleme ayarlarÄąnÄą dÃŧzenle", + "backup_settings_subtitle": "YÃŧkleme ayarlarÄąnÄą yÃļnet", "backward": "Geriye doğru", "biometric_auth_enabled": "Biyometrik kimlik doğrulama etkin", "biometric_locked_out": "Biyometrik kimlik doğrulamasÄą kilitli", @@ -557,7 +627,7 @@ "cache_settings_clear_cache_button": "Önbelleği temizle", "cache_settings_clear_cache_button_title": "UygulamanÄąn Ãļnbelleğini temizleyin. Önbellek yeniden oluşturulana kadar uygulamanÄąn performansÄąnÄą Ãļnemli ÃļlçÃŧde etkileyecektir.", "cache_settings_duplicated_assets_clear_button": "TEMİZLE", - "cache_settings_duplicated_assets_subtitle": "Uygulama tarafÄąndan kara listeye alÄąnan Ãļğeler", + "cache_settings_duplicated_assets_subtitle": "Uygulama tarafÄąndan yok sayÄąlan fotoğraflar ve videolar", "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({count})", "cache_settings_statistics_album": "KÃŧtÃŧphane kÃŧçÃŧk resimleri", "cache_settings_statistics_full": "Tam çÃļzÃŧnÃŧrlÃŧkte resimler", @@ -574,9 +644,12 @@ "cancel": "İptal", "cancel_search": "AramayÄą iptal et", "canceled": "İptal edildi", + "canceling": "Vazgeçmek", "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alÄąnamaz!", "cannot_update_the_description": "AÃ§Äąklama gÃŧncellenemiyor", + "cast": "YansÄąt", + "cast_description": "KullanÄąlabilir yansÄątma hedeflerini yapÄąlandÄąr", "change_date": "Tarihi değiştir", "change_description": "AÃ§ÄąklamayÄą değiştir", "change_display_order": "GÃļrÃŧntÃŧleme sÄąrasÄąnÄą değiştir", @@ -585,31 +658,34 @@ "change_name": "İsim değiştir", "change_name_successfully": "AdÄą başarÄąyla değiştirildi", "change_password": "Şifre Değiştir", - "change_password_description": "Bu ya sistemdeki ilk oturum aÃ§ÄąÅŸÄąnÄąz ya da şifre değişikliği için bir talepte bulunuldu. LÃŧtfen yeni şifreyi aşağıya yazÄąnÄąz.", - "change_password_form_confirm_password": "Parola OnayÄą", - "change_password_form_description": "Merhaba {name},\n\nBu sisteme ilk kez giriş yaptÄąnÄąz veya parolanÄązÄą değiştirmeniz için bir talepte bulunuldu. LÃŧtfen aşağıya yeni parolanÄązÄą girin.", - "change_password_form_new_password": "Yeni Parola", - "change_password_form_password_mismatch": "Parolalar eşleşmiyor", - "change_password_form_reenter_new_password": "Tekrar Yeni Parola", + "change_password_description": "Bu sisteme ilk kez giriş yapÄąyorsunuz veya şifrenizi değiştirmek için bir istekte bulunuldu. LÃŧtfen aşağıya yeni şifrenizi girin.", + "change_password_form_confirm_password": "Şifreyi Onayla", + "change_password_form_description": "Merhaba {name},\n\nBu sisteme ilk kez giriş yapÄąyorsunuz veya şifrenizi değiştirmek için bir istekte bulunuldu. LÃŧtfen aşağıya yeni şifrenizi girin.", + "change_password_form_new_password": "Yeni Şifre", + "change_password_form_password_mismatch": "Şifreler eşleşmiyor", + "change_password_form_reenter_new_password": "Yeni Şifreyi Tekrar Giriniz", "change_pin_code": "PIN kodunu değiştirin", "change_your_password": "Şifreni değiştir", "changed_visibility_successfully": "GÃļrÃŧnÃŧrlÃŧk başarÄąyla değiştirildi", - "check_corrupt_asset_backup": "Bozuk yedek dosyalarÄąnÄą kontrol et", + "charging": "Şarj oluyor", + "charging_requirement_mobile_backup": "Arka plan yedekleme için cihazÄąn şarjda olmasÄą gerekir", + "check_corrupt_asset_backup": "Bozuk Ãļğe yedeklemelerini kontrol et", "check_corrupt_asset_backup_button": "Kontrol et", - "check_corrupt_asset_backup_description": "Bu kontrolÃŧ yalnÄązca Wi-Fi Ãŧzerinden ve tÃŧm dosyalar yedeklendikten sonra çalÄąÅŸtÄąrÄąn. İşlem birkaç dakika sÃŧrebilir.", + "check_corrupt_asset_backup_description": "Bu kontrolÃŧ yalnÄązca Wi-Fi Ãŧzerinden ve tÃŧm Ãļğeler yedeklendikten sonra çalÄąÅŸtÄąrÄąn. İşlem birkaç dakika sÃŧrebilir.", "check_logs": "GÃŧnlÃŧkleri Kontrol Et", "choose_matching_people_to_merge": "Birleştirmek için eşleşen kişileri seçiniz", "city": "Şehir", - "clear": "Temiz", + "clear": "Temizle", "clear_all": "Hepsini temizle", "clear_all_recent_searches": "Son aramalarÄąn hepsini temizle", - "clear_message": "MesajÄą Temizle", - "clear_value": "Değeri Temizle", + "clear_file_cache": "Dosya Önbelleği Temizle", + "clear_message": "MesajÄą temizle", + "clear_value": "Değeri temizle", "client_cert_dialog_msg_confirm": "Tamam", - "client_cert_enter_password": "Parola Gir", + "client_cert_enter_password": "Şifreyi Girin", "client_cert_import": "İçe Aktar", "client_cert_import_success_msg": "İstemci sertifikasÄą içe aktarÄąldÄą", - "client_cert_invalid_msg": "Geçersiz sertifika dosyasÄą veya yanlÄąÅŸ parola", + "client_cert_invalid_msg": "Geçersiz sertifika dosyasÄą veya yanlÄąÅŸ şifre", "client_cert_remove_msg": "İstemci sertifikasÄą kaldÄąrÄąldÄą", "client_cert_subtitle": "YalnÄązca PKCS12 (.p12, .pfx) biçimini destekler. Sertifika İçe Aktarma/KaldÄąrma yalnÄązca oturum açmadan Ãļnce kullanÄąlabilir", "client_cert_title": "SSL İstemci SertifikasÄą", @@ -628,14 +704,15 @@ "completed": "TamamlandÄą", "confirm": "Onayla", "confirm_admin_password": "YÃļnetici Şifresini Onayla", - "confirm_delete_face": "VarlÄąktan {name} yÃŧzÃŧnÃŧ silmek istediğinizden emin misiniz?", + "confirm_delete_face": "Öğeden {name} yÃŧzÃŧnÃŧ silmek istediğinizden emin misiniz?", "confirm_delete_shared_link": "Bu paylaÅŸÄąlan bağlantÄąyÄą silmek istediğinizden emin misiniz?", - "confirm_keep_this_delete_others": "Yığındaki diğer tÃŧm Ãļğeler bu varlÄąk haricinde silinecektir. Devam etmek istediğinizden emin misiniz?", + "confirm_keep_this_delete_others": "Bu Ãļğe hariç, yığındaki diğer tÃŧm Ãļğeler silinecektir. Devam etmek istediğinizden emin misiniz?", "confirm_new_pin_code": "Yeni PIN kodunu onaylayÄąn", "confirm_password": "Şifreyi onayla", "confirm_tag_face": "Bu yÃŧzÃŧ {name} olarak etiketlemek ister misiniz?", "confirm_tag_face_unnamed": "Bu yÃŧzÃŧ etiketlemek ister misin?", "connected_device": "Cihaz bağlandÄą", + "connected_to": "BağlÄą", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", @@ -644,8 +721,8 @@ "control_bottom_app_bar_delete_from_local": "Cihazdan sil", "control_bottom_app_bar_edit_location": "Konumu DÃŧzenle", "control_bottom_app_bar_edit_time": "Tarih ve Saati DÃŧzenle", - "control_bottom_app_bar_share_link": "İlişimi paylaş", - "control_bottom_app_bar_share_to": "Paylaş:", + "control_bottom_app_bar_share_link": "BağlantÄąyÄą Paylaş", + "control_bottom_app_bar_share_to": "Paylaşma", "control_bottom_app_bar_trash_from_immich": "ÇÃļp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandÄą.", "copied_to_clipboard": "Panoya kopyalandÄą!", @@ -654,7 +731,7 @@ "copy_image": "Resmi Kopyala", "copy_link": "BağlantÄąyÄą kopyala", "copy_link_to_clipboard": "BağlantÄąyÄą panoya kopyala", - "copy_password": "ParolayÄą kopyala", + "copy_password": "Şifreyi kopyala", "copy_to_clipboard": "Panoya Kopyala", "country": "Ülke", "cover": "Kapla", @@ -668,15 +745,17 @@ "create_link_to_share_description": "BağlantÄąya sahip olan herkesin seçilen fotoğraflarÄą gÃļrmesine izin ver", "create_new": "YENİ OLUŞTUR", "create_new_person": "Yeni kişi oluştur", - "create_new_person_hint": "Seçili varlÄąklarÄą yeni bir kişiye atayÄąn", + "create_new_person_hint": "Seçili Ãļğeleri yeni bir kişiye atayÄąn", "create_new_user": "Yeni kullanÄącÄą oluştur", - "create_shared_album_page_share_add_assets": "İÇERİK EKLE", + "create_shared_album_page_share_add_assets": "ÖĞELER EKLE", "create_shared_album_page_share_select_photos": "FotoğraflarÄą Seç", + "create_shared_link": "PaylaÅŸÄąlan bağlantÄą oluştur", "create_tag": "Etiket oluştur", "create_tag_description": "Yeni bir etiket oluşturun. İç içe geçmiş etiketler için, etiketi tam yolu ve eğik çizgileri de dahil ederek giriniz.", "create_user": "KullanÄącÄą oluştur", "created": "Oluşturuldu", "created_at": "Oluşturuldu", + "creating_linked_albums": "BağlantÄąlÄą albÃŧmler oluşturuluyor...", "crop": "Kes", "curated_object_page_title": "Nesneler", "current_device": "Mevcut cihaz", @@ -684,9 +763,11 @@ "current_server_address": "Mevcut sunucu adresi", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayÄąlarÄą dile ve bÃļlgeye gÃļre biçimlendirin", + "custom_url": "Özel URL", "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", + "dark_theme": "KaranlÄąk temaya geç", "date_after": "Sonraki tarih", "date_and_time": "Tarih ve Zaman", "date_before": "Önceki tarih", @@ -694,14 +775,17 @@ "date_of_birth_saved": "Doğum gÃŧnÃŧ başarÄą ile kaydedildi", "date_range": "Tarih aralığı", "day": "GÃŧn", + "days": "GÃŧnler", "deduplicate_all": "TÃŧm kopyalarÄą kaldÄąr", "deduplication_criteria_1": "Resim boyutu (bayt olarak)", "deduplication_criteria_2": "EXIF veri sayÄąsÄą", "deduplication_info": "Tekilleştirme Bilgileri", - "deduplication_info_description": "VarlÄąklarÄą otomatik olarak Ãļnceden seçmek ve yinelenenleri toplu olarak kaldÄąrmak için şunlara bakÄąyoruz:", + "deduplication_info_description": "Öğeleri otomatik olarak Ãļnceden seçmek ve yinelenenleri toplu olarak kaldÄąrmak için şunlara bakÄąyoruz:", "default_locale": "VarsayÄąlan Yerel Ayar", "default_locale_description": "Tarihleri ve sayÄąlarÄą tarayÄącÄąnÄązÄąn yerel ayarÄąna gÃļre biçimlendirin", "delete": "Sil", + "delete_action_confirmation_message": "Bu Ãļğeyi silmek istediğinizden emin misiniz? Bu işlem, Ãļğeyi sunucunun çÃļp kutusuna taÅŸÄąyacak ve yerel olarak silmek isteyip istemediğinizi soracaktÄąr", + "delete_action_prompt": "{count} silindi", "delete_album": "AlbÃŧmÃŧ sil", "delete_api_key_prompt": "Bu API anahtarÄąnÄą silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu Ãļğeler cihazÄąnÄązdan ve Immich'ten kalÄącÄą olarak silinecektir", @@ -715,35 +799,41 @@ "delete_key": "AnahtarÄą sil", "delete_library": "KÃŧtÃŧphaneyi sil", "delete_link": "BağlantÄąyÄą sil", + "delete_local_action_prompt": "{count} yerel olarak silindi", "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", - "delete_shared_link": "PaylaÅŸÄąlmÄąÅŸ linki sil", - "delete_shared_link_dialog_title": "PaylaÅŸÄąlan BağlantÄą Sil", + "delete_permanently": "KalÄącÄą olarak sil", + "delete_permanently_action_prompt": "{count} kalÄącÄą olarak silindi", + "delete_shared_link": "PaylaÅŸÄąlan bağlantÄąyÄą sil", + "delete_shared_link_dialog_title": "PaylaÅŸÄąlan BağlantÄąyÄą Sil", "delete_tag": "Etiketi sil", "delete_tag_confirmation_prompt": "{tagName} etiketini silmek istediğinizden emin misiniz?", "delete_user": "KullanÄącÄąyÄą sil", "deleted_shared_link": "PaylaÅŸÄąlan bağlantÄą silindi", - "deletes_missing_assets": "Diskte eksik olan varlÄąklarÄą siler", + "deletes_missing_assets": "Diskte eksik olan Ãļğeleri siler", "description": "AÃ§Äąklama", "description_input_hint_text": "AÃ§Äąklama ekle...", "description_input_submit_error": "AÃ§Äąklama gÃŧncellenirken hata oluştu, daha fazla ayrÄąntÄą için gÃŧnlÃŧğÃŧ kontrol edin", + "deselect_all": "TÃŧmÃŧnÃŧ Seçimi KaldÄąr", "details": "Detaylar", "direction": "YÃļn", "disabled": "Devre dÄąÅŸÄą bÄąrakÄąldÄą", "disallow_edits": "Değişikliklere izin verme", "discord": "Discord", "discover": "Keşfet", + "discovered_devices": "Keşfedilen aygÄątlar", "dismiss_all_errors": "TÃŧm hatalarÄą yoksay", "dismiss_error": "HatayÄą yoksay", "display_options": "GÃļrÃŧntÃŧleme seçenekleri", "display_order": "GÃļsterim sÄąralamasÄą", "display_original_photos": "Orijinal fotoğraflarÄą gÃļster", - "display_original_photos_setting_description": "Orijinal varlÄąk web uyumlu olduğunda, bir varlığı gÃļrÃŧntÃŧlerken kÃŧçÃŧk resimler yerine orijinal fotoğrafÄą gÃļrÃŧntÃŧlemeyi tercih edin. Bu, fotoğraf gÃļrÃŧntÃŧleme hÄązlarÄąnÄąn yavaşlamasÄąna neden olabilir.", + "display_original_photos_setting_description": "Orijinal Ãļğe web uyumlu olduğunda, bir Ãļğeyi gÃļrÃŧntÃŧlerken kÃŧçÃŧk resimler yerine orijinal fotoğrafÄą gÃļrÃŧntÃŧlemeyi tercih edin. Bu, fotoğraf gÃļrÃŧntÃŧleme hÄązlarÄąnÄąn yavaşlamasÄąna neden olabilir.", "do_not_show_again": "Bu mesajÄą bir daha gÃļsterme", "documentation": "DokÃŧmantasyon", "done": "Bitti", "download": "İndir", + "download_action_prompt": "{count} Ãļğe indiriliyor", "download_canceled": "İndirme iptal edildi", "download_complete": "İndirme tamamlandÄą", "download_enqueue": "İndirme sÄąraya alÄąndÄą", @@ -755,13 +845,13 @@ "download_notfound": "İndirme bulunamadÄą", "download_paused": "İndirme duraklatÄąldÄą", "download_settings": "İndir", - "download_settings_description": "VarlÄąk indirme ile ilgili ayarlarÄą yÃļnetin", + "download_settings_description": "Öğe indirme ile ilgili ayarlarÄą yÃļnetin", "download_started": "İndirme başladÄą", "download_sucess": "İndirme başarÄąlÄą", "download_sucess_android": "Medya DCIM/Immich klasÃļrÃŧne indirildi", "download_waiting_to_retry": "Yeniden denemek için bekleniyor", "downloading": "İndiriliyor", - "downloading_asset_filename": "VarlÄąk indiriliyor {filename}", + "downloading_asset_filename": "Öğe indiriliyor {filename}", "downloading_media": "Medya indiriliyor", "drop_files_to_upload": "DosyalarÄą yÃŧklemek için herhangi bir yere bÄąrakÄąn", "duplicates": "Kopyalar", @@ -770,8 +860,12 @@ "edit": "DÃŧzenle", "edit_album": "AlbÃŧmÃŧ dÃŧzenle", "edit_avatar": "AvatarÄą DÃŧzenle", + "edit_birthday": "Doğum gÃŧnÃŧnÃŧ dÃŧzenle", "edit_date": "Tarihi DÃŧzenle", "edit_date_and_time": "Tarih ve zamanÄą dÃŧzenleyin", + "edit_date_and_time_action_prompt": "{count} tarih ve zaman dÃŧzenlendi", + "edit_date_and_time_by_offset": "Tarihi ofset ile değiştir", + "edit_date_and_time_by_offset_interval": "Yeni tarih aralığı: {from}'dan {to}'a kadar", "edit_description": "AÃ§ÄąklamayÄą dÃŧzenle", "edit_description_prompt": "LÃŧtfen yeni bir aÃ§Äąklama seçin:", "edit_exclusion_pattern": "Hariç tutma desenini dÃŧzenle", @@ -781,6 +875,7 @@ "edit_key": "AnahtarÄą dÃŧzenle", "edit_link": "BağlantÄąyÄą dÃŧzenle", "edit_location": "Lokasyonu dÃŧzenleyin", + "edit_location_action_prompt": "{count} konum dÃŧzenlendi", "edit_location_dialog_title": "Konum", "edit_name": "İsmi dÃŧzenleyin", "edit_people": "Kişileri dÃŧzenle", @@ -797,59 +892,66 @@ "email_notifications": "E-posta bildirimleri", "empty_folder": "Bu klasÃļr boş", "empty_trash": "ÇÃļpÃŧ boşalt", - "empty_trash_confirmation": "ÇÃļp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çÃļp kutusundaki tÃŧm varlÄąklarÄą kalÄącÄą olarak silecektir.\nBu işlemi geri alamazsÄąnÄąz!", + "empty_trash_confirmation": "ÇÃļp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, çÃļp kutusundaki tÃŧm varlÄąklarÄą Immich'ten kalÄącÄą olarak silecektir.\nBu işlemi geri alamazsÄąnÄąz!", "enable": "Etkinleştir", + "enable_backup": "Yedeklemeyi Etkinleştir", "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasÄąnÄą etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", + "enqueued": "Kuyruğa alÄąndÄą", "enter_wifi_name": "Wi-Fi adÄąnÄą girin", - "enter_your_pin_code": "Pin kodu girin", + "enter_your_pin_code": "PIN kodunuzu girin", "enter_your_pin_code_subtitle": "Kilitli klasÃļre erişmek için PIN kodunuzu girin", "error": "Hata", "error_change_sort_album": "AlbÃŧm sÄąralama dÃŧzeni değiştirilemedi", - "error_delete_face": "YÃŧzÃŧ varlÄąktan silme hatasÄą", + "error_delete_face": "Öğeden yÃŧz silme hatasÄą", + "error_getting_places": "Konum bilgisi alÄąnÄąrken hata oluştu", "error_loading_image": "Resim yÃŧklenirken hata oluştu", + "error_loading_partners": "OrtaklarÄą yÃŧkleme hatasÄą: {error}", "error_saving_image": "Hata: {error}", + "error_tag_face_bounding_box": "YÃŧz etiketleme hatasÄą – sÄąnÄąrlayÄącÄą kutu koordinatlarÄą alÄąnamadÄą", "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { - "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapÄąlamÄąyor", - "cannot_navigate_previous_asset": "Önceki varlığa geçiş yapÄąlamÄąyor", + "cannot_navigate_next_asset": "Sonraki Ãļğeye geçiş yapÄąlamÄąyor", + "cannot_navigate_previous_asset": "Önceki Ãļğeye geçiş yapÄąlamÄąyor", "cant_apply_changes": "Değişiklikler uygulanamÄąyor", "cant_change_activity": "Etkinliği {enabled, select, true {devre dÄąÅŸÄą bÄąrakamÄąyor} other {etkinleştiremiyor}}", - "cant_change_asset_favorite": "Varlığın favori durumunu değiştiremiyor", - "cant_change_metadata_assets_count": "{count, plural, one {# varlığın} other {# varlÄąklarÄąn}} meta verisi değiştirilemiyor", + "cant_change_asset_favorite": "Öğenin favori durumu değiştirilemiyor", + "cant_change_metadata_assets_count": "{count, plural, one {# Ãļğenin} other {# Ãļğelerin}} meta verisi değiştirilemiyor", "cant_get_faces": "YÃŧzler alÄąnamadÄą", "cant_get_number_of_comments": "YorumlarÄąn sayÄąsÄą alÄąnamadÄą", "cant_search_people": "Kişiler aranamÄąyor", "cant_search_places": "Mekanlar aranamÄąyor", - "error_adding_assets_to_album": "AlbÃŧme varlÄąk ekleme hatasÄą", + "error_adding_assets_to_album": "AlbÃŧme Ãļğe ekleme hatasÄą", "error_adding_users_to_album": "AlbÃŧme kullanÄącÄą ekleme hatasÄą", "error_deleting_shared_user": "PaylaÅŸÄąlan kullanÄącÄą silme hatasÄą", "error_downloading": "{filename} indirme hatasÄą", "error_hiding_buy_button": "SatÄąn alma butonu gizleme hatasÄą", - "error_removing_assets_from_album": "Varlığı albÃŧmden silme hatasÄą, daha fazla detay için konsolu kontrol et", - "error_selecting_all_assets": "BÃŧtÃŧn varlÄąklarÄą seçme hatasÄą", + "error_removing_assets_from_album": "Öğeyi albÃŧmden silme hatasÄą, daha fazla detay için konsolu kontrol et", + "error_selecting_all_assets": "TÃŧm Ãļğeleri seçerken hata oluştu", "exclusion_pattern_already_exists": "Bu dÄąÅŸlama modeli halihazÄąrda mevcut.", "failed_to_create_album": "AlbÃŧm oluşturulamadÄą", "failed_to_create_shared_link": "PaylaÅŸÄąlan bağlantÄą oluşturulamadÄą", "failed_to_edit_shared_link": "PaylaÅŸÄąlan bağlantÄą dÃŧzenlenemedi", "failed_to_get_people": "Kişiler alÄąnamadÄą", "failed_to_keep_this_delete_others": "Bu Ãļğenin tutulmasÄą ve diğer Ãļğenin silinmesi başarÄąsÄąz oldu", - "failed_to_load_asset": "VarlÄąk yÃŧklenemedi", - "failed_to_load_assets": "VarlÄąklar yÃŧklenemedi", + "failed_to_load_asset": "Öğe yÃŧklenemedi", + "failed_to_load_assets": "Öğeler yÃŧklenemedi", "failed_to_load_notifications": "Bildirim yÃŧklenemedi", "failed_to_load_people": "Kişiler yÃŧklenemedi", "failed_to_remove_product_key": "ÜrÃŧn anahtarÄą kaldÄąrÄąlamadÄą", - "failed_to_stack_assets": "VarlÄąklar yığınlanamadÄą", - "failed_to_unstack_assets": "VarlÄąklarÄąn yığınÄą kaldÄąrÄąlamadÄą", + "failed_to_reset_pin_code": "PIN kodu sÄąfÄąrlanamadÄą", + "failed_to_stack_assets": "Öğeler yığınlanamadÄą", + "failed_to_unstack_assets": "Öğelerin yığınÄą kaldÄąrÄąlamadÄą", "failed_to_update_notification_status": "Bildirim durumu gÃŧncellenemedi", "import_path_already_exists": "Bu içe aktarma yolu halihazÄąrda mevcut.", "incorrect_email_or_password": "YanlÄąÅŸ e-posta veya şifre", "paths_validation_failed": "{paths, plural, one {# Yol} other {# Yollar}} doğrulanamadÄą", "profile_picture_transparent_pixels": "Profil resimleri şeffaf piksele sahip olamaz. LÃŧtfen resme yakÄąnlaştÄąrÄąn ve/veya resmi hareket ettirin.", "quota_higher_than_disk_size": "Disk boyutundan daha yÃŧksek bir kota belirlediniz", + "something_went_wrong": "Bir şeyler ters gitti", "unable_to_add_album_users": "KullanÄącÄąlar albÃŧme eklenemiyor", - "unable_to_add_assets_to_shared_link": "VarlÄąklar paylaÅŸÄąlan bağlantÄąya eklenemiyor", + "unable_to_add_assets_to_shared_link": "Öğeler paylaÅŸÄąlan bağlantÄąya eklenemiyor", "unable_to_add_comment": "Yorum eklenemiyor", "unable_to_add_exclusion_pattern": "Hariç tutma modeli eklenemiyor", "unable_to_add_import_path": "İçe aktarma yolu eklenemiyor", @@ -859,6 +961,7 @@ "unable_to_archive_unarchive": "{archived, select, true {Arşivleme} other {Arşivden Ã§Äąkarma}} işlemi yapÄąlamÄąyor", "unable_to_change_album_user_role": "AlbÃŧm kullanÄącÄą rolÃŧ değiştirilemiyor", "unable_to_change_date": "Tarih değiştirilemiyor", + "unable_to_change_description": "AÃ§Äąklama değiştirilemiyor", "unable_to_change_favorite": "Favori durumu değiştirilemiyor", "unable_to_change_location": "Konum değiştirilemiyor", "unable_to_change_password": "Şifre değiştirilemiyor", @@ -871,8 +974,8 @@ "unable_to_create_library": "KÃŧtÃŧphane oluşturulamÄąyor", "unable_to_create_user": "KullanÄącÄą oluşturulamÄąyor", "unable_to_delete_album": "AlbÃŧm silinemiyor", - "unable_to_delete_asset": "VarlÄąk silinemiyor", - "unable_to_delete_assets": "VarlÄąklar silinemiyor", + "unable_to_delete_asset": "Öğe silinemiyor", + "unable_to_delete_assets": "Öğeler silinemiyor", "unable_to_delete_exclusion_pattern": "Hariç tutma deseni silinemiyor", "unable_to_delete_import_path": "İçe aktarma yolu silinemiyor", "unable_to_delete_shared_link": "PaylaÅŸÄąlan bağlantÄą silinemiyor", @@ -892,18 +995,19 @@ "unable_to_log_out_device": "Cihazdan Ã§ÄąkÄąÅŸ yapÄąlamÄąyor", "unable_to_login_with_oauth": "OAuth ile giriş yapÄąlamÄąyor", "unable_to_play_video": "Video oynatÄąlamÄąyor", - "unable_to_reassign_assets_existing_person": "VarlÄąklar {name, select, null {mevcut bir kişiye} other {{name}}} yeniden atanamÄąyor", - "unable_to_reassign_assets_new_person": "VarlÄąklar yeni bir kişiye yeniden atanamÄąyor", + "unable_to_reassign_assets_existing_person": "Öğeler {name, select, null {mevcut bir kişiye} other {{name}}} yeniden atanamÄąyor", + "unable_to_reassign_assets_new_person": "Öğeler yeni bir kişiye yeniden atanamÄąyor", "unable_to_refresh_user": "KullanÄącÄą yenilenemiyor", "unable_to_remove_album_users": "AlbÃŧm kullanÄącÄąlarÄą kaldÄąrÄąlamÄąyor", "unable_to_remove_api_key": "API anahtarÄą kaldÄąrÄąlamÄąyor", - "unable_to_remove_assets_from_shared_link": "VarlÄąklar paylaÅŸÄąlan bağlantÄądan kaldÄąrÄąlamÄąyor", + "unable_to_remove_assets_from_shared_link": "Öğeler paylaÅŸÄąlan bağlantÄądan kaldÄąrÄąlamÄąyor", "unable_to_remove_library": "KÃŧtÃŧphane kaldÄąrÄąlamadÄą", "unable_to_remove_partner": "Ortak kaldÄąrÄąlamÄąyor", "unable_to_remove_reaction": "Reaksiyon kaldÄąrÄąlamÄąyor", "unable_to_reset_password": "Şifre sÄąfÄąrlanamÄąyor", + "unable_to_reset_pin_code": "PIN kodu sÄąfÄąrlanamÄąyor", "unable_to_resolve_duplicate": "Çiftler çÃļzÃŧmlenemiyor", - "unable_to_restore_assets": "VarlÄąklar geri yÃŧklenemiyor", + "unable_to_restore_assets": "Öğeler geri yÃŧklenemiyor", "unable_to_restore_trash": "ÇÃļp geri yÃŧklenemiyor", "unable_to_restore_user": "KullanÄącÄą geri yÃŧklenemiyor", "unable_to_save_album": "AlbÃŧm kaydedilemiyor", @@ -917,7 +1021,7 @@ "unable_to_set_feature_photo": "Özellikli fotoğraf ayarlanamÄąyor", "unable_to_set_profile_picture": "Profil resmi ayarlanamÄąyor", "unable_to_submit_job": "GÃļrev gÃļnderilemiyor", - "unable_to_trash_asset": "VarlÄąk çÃļp kutusuna taÅŸÄąnamÄąyor", + "unable_to_trash_asset": "Öğe çÃļp kutusuna taÅŸÄąnamÄąyor", "unable_to_unlink_account": "Hesap bağlantÄąsÄą kaldÄąrÄąlamÄąyor", "unable_to_unlink_motion_video": "Hareket videosunun bağlantÄąsÄą kaldÄąrÄąlamÄąyor", "unable_to_update_album_cover": "AlbÃŧm resmi gÃŧncellenemiyor", @@ -931,6 +1035,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "AÃ§Äąklama Ekle...", + "exif_bottom_sheet_description_error": "AÃ§Äąklama gÃŧncelleme hatasÄą", "exif_bottom_sheet_details": "DETAYLAR", "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", @@ -948,19 +1053,26 @@ "explorer": "Geçmiş", "export": "DÄąÅŸa Aktar", "export_as_json": "JSON olarak DÄąÅŸa Aktar", + "export_database": "VeritabanÄąnÄą DÄąÅŸa Aktar", + "export_database_description": "SQLite veritabanÄąnÄą dÄąÅŸa aktarÄąn", "extension": "UzantÄą", "external": "Harici", "external_libraries": "Harici kÃŧtÃŧphaneler", "external_network": "Harici ağlar", - "external_network_sheet_info": "Belirlenmiş WiFi ağına bağlÄą olmadığında uygulama, yukarÄądan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracÄąlığıyla sunucuya bağlanacaktÄąr", + "external_network_sheet_info": "Belirlenmiş Wi-Fi ağına bağlÄą olmadığında uygulama, yukarÄądan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracÄąlığıyla sunucuya bağlanacaktÄąr", "face_unassigned": "YÃŧz atanmadÄą", - "failed_to_load_assets": "VarlÄąklar yÃŧklenemedi", + "failed": "BaşarÄąsÄąz", + "failed_to_authenticate": "Kimlik doğrulamasÄą yapÄąlamadÄą", + "failed_to_load_assets": "Öğeler yÃŧklenemedi", + "failed_to_load_folder": "KlasÃļr yÃŧklenemedi", "favorite": "Favori", - "favorite_or_unfavorite_photo": "Favoriye ekle veya Ã§Äąkar", + "favorite_action_prompt": "{count} Favorilere eklendi", + "favorite_or_unfavorite_photo": "Favorilere ekle veya Ã§Äąkar", "favorites": "Favoriler", "favorites_page_no_favorites": "Favori Ãļğe bulunamadÄą", - "feature_photo_updated": "Özellikli fotoğraf gÃŧncellendi", + "feature_photo_updated": "Öne Ã§Äąkan fotoğraf gÃŧncellendi", "features": "Özellikler", + "features_in_development": "Geliştirme AşamasÄąndaki Özellikler", "features_setting_description": "UygulamanÄąn Ãļzelliklerini yÃļnet", "file_name": "Dosya adÄą", "file_name_or_extension": "Dosya adÄą veya uzantÄą", @@ -968,18 +1080,28 @@ "filetype": "Dosya tipi", "filter": "Filtre", "filter_people": "Kişileri filtrele", + "filter_places": "Yerleri sÃŧz", "find_them_fast": "AdlarÄąna gÃļre hÄązlÄąca bul", + "first": "İlk", "fix_incorrect_match": "YanlÄąÅŸ eşleştirmeyi dÃŧzelt", + "folder": "KlasÃļr", + "folder_not_found": "KlasÃļr bulunamadÄą", "folders": "KlasÃļrler", "folders_feature_description": "Dosya sistemindeki fotoğraf ve videolarÄą klasÃļr gÃļrÃŧnÃŧmÃŧyle keşfedin", + "forgot_pin_code_question": "PIN kodunuzu mu unuttunuz?", "forward": "İleri", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Bu Ãļzellik, çalÄąÅŸabilmek için Google'dan harici kaynaklar yÃŧkler.", "general": "Genel", + "geolocation_instruction_location": "GPS koordinatlarÄą olan bir Ãļğeyi tÄąklayarak konumunu kullanÄąn veya haritadan doğrudan bir konum seçin", "get_help": "YardÄąm Al", "get_wifiname_error": "Wi-Fi adÄą alÄąnamadÄą. Gerekli izinleri verdiğinizden ve bir Wi-Fi ağına bağlÄą olduğunuzdan emin olun", "getting_started": "Başlarken", "go_back": "Geri git", "go_to_folder": "KlasÃļre git", "go_to_search": "Aramaya git", + "gps": "GPS", + "gps_missing": "GPS yok", "grant_permission": "İzin ver", "group_albums_by": "AlbÃŧmleri gruplandÄąr...", "group_country": "Ülkeye gÃļre grupla", @@ -990,11 +1112,14 @@ "haptic_feedback_switch": "Dokunsal geri bildirimi aç", "haptic_feedback_title": "Dokunsal Geri Bildirim (Haptic Feedback)", "has_quota": "Kota var", + "hash_asset": "Karma Ãļğe", + "hashed_assets": "Karma Ãļğeler", + "hashing": "Hashleme", "header_settings_add_header_tip": "Header Ekle", "header_settings_field_validator_msg": "Değer boş olamaz", "header_settings_header_name_input": "Header adÄą", "header_settings_header_value_input": "Header değeri", - "headers_settings_tile_subtitle": "UygulamanÄąn her ağ isteğiyle birlikte gÃļndermesi gereken proxy header'larÄą tanÄąmlayÄąn", + "headers_settings_tile_subtitle": "UygulamanÄąn her ağ isteğinde gÃļndermesi gereken proxy başlÄąklarÄąnÄą tanÄąmlayÄąn", "headers_settings_tile_title": "Özel proxy headers", "hi_user": "Merhaba {name} {email}", "hide_all_people": "TÃŧm kişileri gizle", @@ -1003,21 +1128,27 @@ "hide_password": "Şifreyi gizle", "hide_person": "Kişiyi gizle", "hide_unnamed_people": "İsimsiz kişileri gizle", - "home_page_add_to_album_conflicts": "{album} albÃŧmÃŧne {added} Ãļğe eklendi. {failed} varlÄąk zaten albÃŧmdeydi.", + "home_page_add_to_album_conflicts": "{album} albÃŧmÃŧne {added} Ãļğe eklendi. {failed} Ãļğe zaten albÃŧmdeydi.", "home_page_add_to_album_err_local": "Yerel Ãļğeler henÃŧz albÃŧmlere eklenemiyor, atlanÄąyor", "home_page_add_to_album_success": "{album} albÃŧmÃŧne {added} Ãļğe eklendi.", - "home_page_album_err_partner": "Partner Ãļğeleri henÃŧz bir albÃŧme eklenemiyor, atlanÄąyor", + "home_page_album_err_partner": "Ortak Ãļğeler henÃŧz bir albÃŧme eklenemiyor, atlanÄąyor", "home_page_archive_err_local": "Yerel Ãļğeler henÃŧz arşivlenemiyor, atlanÄąyor", - "home_page_archive_err_partner": "Partner Ãļğeleri henÃŧz arşivlenemiyor, atlanÄąyor", + "home_page_archive_err_partner": "Ortak Ãļğeler henÃŧz arşivlenemiyor, atlanÄąyor", "home_page_building_timeline": "Zaman çizelgesi oluşturuluyor", - "home_page_delete_err_partner": "Partner Ãļğeleri silinemez, atlanÄąyor", + "home_page_delete_err_partner": "Ortak Ãļğeler silinemez, atlanÄąyor", "home_page_delete_remote_err_local": "Uzaktan silme seçimindeki yerel Ãļğeler atlanÄąyor", "home_page_favorite_err_local": "Yerel Ãļğeler henÃŧz favorilere eklenemiyor, atlanÄąyor", - "home_page_favorite_err_partner": "Partner Ãļğeleri henÃŧz favorilere eklenemiyor, atlanÄąyor", - "home_page_first_time_notice": "UygulamayÄą ilk kez kullanÄąyorsanÄąz, zaman çizelgesinin albÃŧmlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lÃŧtfen yedekleme için albÃŧm(ler) seçtiğinizden emin olun.", + "home_page_favorite_err_partner": "Ortak Ãļğeler henÃŧz favorilere eklenemiyor, atlanÄąyor", + "home_page_first_time_notice": "UygulamayÄą ilk kez kullanÄąyorsanÄąz, zaman çizelgesinin albÃŧmlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lÃŧtfen yedekleme için albÃŧm seçtiğinizden emin olun", + "home_page_locked_error_local": "Yerel Ãļğeler kilitli klasÃļre taÅŸÄąnamÄąyor, atlanÄąyor", + "home_page_locked_error_partner": "Ortak Ãļğeler kilitli klasÃļre taÅŸÄąnamÄąyor, atlanÄąyor", "home_page_share_err_local": "Yerel Ãļğeler bağlantÄą ile paylaÅŸÄąlamaz, atlanÄąyor", "home_page_upload_err_limit": "AynÄą anda en fazla 30 Ãļğe yÃŧklenebilir, atlanabilir", + "host": "Ana bilgisayar", "hour": "Saat", + "hours": "Saatler", + "id": "ID", + "idle": "Boşta", "ignore_icloud_photos": "iCloud FotoğraflarÄąnÄą Yok Say", "ignore_icloud_photos_description": "iCloud'a yÃŧklenmiş fotoğraflar Immich sunucusuna yÃŧklenmesin", "image": "Resim", @@ -1043,7 +1174,7 @@ "in_archive": "Arşivde", "include_archived": "Arşivlenenleri dahil et", "include_shared_albums": "PaylaÅŸÄąlmÄąÅŸ albÃŧmleri dahil et", - "include_shared_partner_assets": "PaylaÅŸÄąlan ortak varlÄąklarÄą dahil et", + "include_shared_partner_assets": "PaylaÅŸÄąlan ortak Ãļğeleri dahil et", "individual_share": "Bireysel paylaÅŸÄąm", "individual_shares": "Kişisel paylaÅŸÄąmlar", "info": "Bilgi", @@ -1057,19 +1188,31 @@ "invalid_date_format": "Geçersiz tarih formatÄą", "invite_people": "Kişileri Davet Et", "invite_to_album": "AlbÃŧme davet et", + "ios_debug_info_fetch_ran_at": "Veri çekme {dateTime} tarihinde çalÄąÅŸtÄąrÄąldÄą", + "ios_debug_info_last_sync_at": "Son eşzamanlama {dateTime}", + "ios_debug_info_no_processes_queued": "Hiçbir arka plan işlemi kuyruğa alÄąnmadÄą", + "ios_debug_info_no_sync_yet": "HenÃŧz arka plan eşzamanlama gÃļrevi çalÄąÅŸtÄąrÄąlmadÄą", + "ios_debug_info_processes_queued": "{count, plural, one {{count} arka plan işlemi kuyruğa alÄąndÄą} other {{count} arka plan işlemi kuyruğa alÄąndÄą}}", + "ios_debug_info_processing_ran_at": "İşleme {dateTime} tarihinde çalÄąÅŸtÄąrÄąldÄą", "items_count": "{count, plural, one {# Öğe} other {# Öğe}}", "jobs": "GÃļrevler", "keep": "Koru", "keep_all": "Hepsini koru", "keep_this_delete_others": "Bunu sakla, diğerlerini sil", - "kept_this_deleted_others": "Bu varlÄąk tutuldu ve {count, plural, one {# varlÄąk} other {# varlÄąk}} silindi", + "kept_this_deleted_others": "Bu Ãļğe tutuldu ve {count, plural, one {# varlÄąk} other {# varlÄąk}} silindi", "keyboard_shortcuts": "Klavye kÄąsayollarÄą", "language": "Dil", + "language_no_results_subtitle": "Arama teriminizi değiştirmeyi deneyin", + "language_no_results_title": "Dil bulunamadÄą", + "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", + "large_files": "BÃŧyÃŧk Dosyalar", + "last": "Son", "last_seen": "Son gÃļrÃŧlme", - "latest_version": "En son versiyon", + "latest_version": "En Son SÃŧrÃŧm", "latitude": "Enlem", "leave": "AyrÄąl", + "leave_album": "AlbÃŧmden Ã§Äąk", "lens_model": "Mercek modeli", "let_others_respond": "Diğerlerinin yanÄąt vermesine izin ver", "level": "Seviye", @@ -1081,26 +1224,35 @@ "library_page_sort_created": "Oluşturma tarihi", "library_page_sort_last_modified": "Son dÃŧzenleme", "library_page_sort_title": "AlbÃŧm başlığı", + "licenses": "Lisanslar", "light": "AÃ§Äąk", + "like": "Beğen", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", - "link_options": "BağlantÄą seçenekleri", "link_to_oauth": "OAuth'a bağla", "linked_oauth_account": "BağlÄą OAuth hesabÄą", "list": "Liste", "loading": "YÃŧkleniyor", "loading_search_results_failed": "Arama sonuçlarÄą yÃŧklenemedi", - "local_network": "Yerel Wi-Fi", + "local": "Yerel", + "local_asset_cast_failed": "Sunucuya yÃŧklenmemiş bir Ãļğe yansÄątÄąlamaz", + "local_assets": "Yerel Öğeler", + "local_media_summary": "Yerel Medya Özeti", + "local_network": "Yerel ağ", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağınÄą kullanÄąrken bu URL Ãŧzerinden sunucuya bağlanacaktÄąr", "location_permission": "Konum izni", - "location_permission_content": "Otomatik geçiş Ãļzelliğinin çalÄąÅŸabilmesi için Immich'in mevcut Wi-Fi ağınÄąn adÄąnÄą bilmesi, bunu sağlamak için de tam konum iznine ihtiyacÄą vardÄąr.", + "location_permission_content": "Otomatik geçiş Ãļzelliğinin çalÄąÅŸabilmesi için Immich'in mevcut Wi-Fi ağınÄąn adÄąnÄą bilmesi, bunu sağlamak için de tam konum iznine ihtiyacÄą vardÄąr", "location_picker_choose_on_map": "Haritada seç", "location_picker_latitude_error": "Geçerli bir enlem yazÄąn", "location_picker_latitude_hint": "Buraya enlem yazÄąn", "location_picker_longitude_error": "Geçerli bir boylam yazÄąn", "location_picker_longitude_hint": "Buraya boylam yazÄąn", + "lock": "Kilitle", + "locked_folder": "Kilitli KlasÃļr", + "log_detail_title": "GÃŧnlÃŧk AyrÄąntÄąlarÄą", "log_out": "Oturumu kapat", "log_out_all_devices": "TÃŧm Cihazlarda Oturumu Kapat", + "logged_in_as": "{user} olarak oturum aÃ§ÄąldÄą", "logged_out_all_devices": "TÃŧm cihazlarda oturum kapatÄąldÄą", "logged_out_device": "Oturum kapatÄąlmÄąÅŸ cihaz", "login": "Giriş yap", @@ -1117,23 +1269,26 @@ "login_form_err_trailing_whitespace": "Sondaki boşluk", "login_form_failed_get_oauth_server_config": "OAuth kullanÄąrken bir hata oluştu, sunucu URL'sini kontrol edin", "login_form_failed_get_oauth_server_disable": "OAuth Ãļzelliği bu sunucuda mevcut değil", - "login_form_failed_login": "Giriş yaparken hata oluştu, sunucu URL'sini, e-postayÄą ve parolayÄą kontrol edin", + "login_form_failed_login": "Giriş yaparken hata oluştu, sunucu URL'sini, e-postayÄą ve şifreyi kontrol edin", "login_form_handshake_exception": "Sunucuda bir El SÄąkÄąÅŸma İstisnasÄą vardÄą. Kendi kendine imzalanmÄąÅŸ bir sertifika kullanÄąyorsanÄąz, ayarlar menÃŧsÃŧnden kendi kendine imzalanmÄąÅŸ sertifikalara izin verin.", - "login_form_password_hint": "parola", + "login_form_password_hint": "şifre", "login_form_save_login": "Oturum aÃ§Äąk kalsÄąn", - "login_form_server_empty": "Sunucu URL'si girin", + "login_form_server_empty": "Sunucu URL'si girin.", "login_form_server_error": "Sunucuya bağlanÄąlamadÄą.", "login_has_been_disabled": "Giriş devre dÄąÅŸÄą bÄąrakÄąldÄą.", - "login_password_changed_error": "Parola gÃŧncellenirken bir hata oluştu.", - "login_password_changed_success": "Parola gÃŧncellendi", + "login_password_changed_error": "Şifreniz gÃŧncellenirken bir hata oluştu", + "login_password_changed_success": "Şifre başarÄąyla gÃŧncellendi", "logout_all_device_confirmation": "TÃŧm cihazlarda oturum kapatmak istediğinizden emin misiniz?", "logout_this_device_confirmation": "Bu cihazda oturum kapatmak istediğinizden emin misiniz?", + "logs": "KayÄątlar", "longitude": "Boylam", "look": "GÃļrÃŧnÃŧm", "loop_videos": "VideolarÄą dÃļngÃŧye al", "loop_videos_description": "AyrÄąntÄą gÃļrÃŧnÃŧmÃŧnde videolarÄąn otomatik dÃļngÃŧye alÄąnmasÄąnÄą etkinleştir.", "main_branch_warning": "Geliştirme sÃŧrÃŧmÃŧ kullanÄąyorsunuz. YayÄąnlanan bir sÃŧrÃŧm kullanmanÄązÄą Ãļnemle tavsiye ederiz!", + "main_menu": "Ana menÃŧ", "make": "Marka", + "manage_geolocation": "Konumu yÃļnet", "manage_shared_links": "PaylaÅŸÄąlan bağlantÄąlarÄą yÃļnet", "manage_sharing_with_partners": "Ortaklarla paylaÅŸÄąmÄą yÃļnet", "manage_the_app_settings": "Uygulama ayarlarÄąnÄą yÃļnet", @@ -1142,8 +1297,7 @@ "manage_your_devices": "CihazlarÄąnÄązÄą yÃļnetin", "manage_your_oauth_connection": "OAuth bağlantÄąnÄązÄą yÃļnetin", "map": "Harita", - "map_assets_in_bound": "{count} fotoğraf", - "map_assets_in_bounds": "{count} fotoğraf", + "map_assets_in_bounds": "{count, plural, =0 {Bu alanda fotoğraf yok} one {# photo} other {# photos}}", "map_cannot_get_user_location": "KullanÄącÄąnÄąn konumu alÄąnamÄąyor", "map_location_dialog_yes": "Evet", "map_location_picker_page_use_location": "Bu konumu kullan", @@ -1151,7 +1305,6 @@ "map_location_service_disabled_title": "Konum hizmeti devre dÄąÅŸÄą bÄąrakÄąldÄą", "map_marker_for_images": "{city}, {country} şehrinde çekilen fotoğraflar için harita işaretleyicisi", "map_marker_with_image": "Resimli harita işaretleyicisi", - "map_no_assets_in_bounds": "Bu alanda fotoğraf yok", "map_no_location_permission_content": "Mevcut konumunuzdan Ãļğeleri gÃļrÃŧntÃŧlemek için konum iznine ihtiyaç var. Şimdi izin vermek istiyor musunuz?", "map_no_location_permission_title": "Konum izni reddedildi", "map_settings": "Harita ayarlarÄą", @@ -1162,11 +1315,15 @@ "map_settings_date_range_option_years": "Son {years} yÄąl", "map_settings_dialog_title": "Harita AyarlarÄą", "map_settings_include_show_archived": "Arşivdekileri dahil et", - "map_settings_include_show_partners": "Partnerleri Dahil Et", + "map_settings_include_show_partners": "OrtaklarÄą Dahil Et", "map_settings_only_show_favorites": "Sadece Favorileri GÃļster", "map_settings_theme_settings": "Harita TemasÄą", "map_zoom_to_see_photos": "FotoğraflarÄą gÃļrmek için uzaklaştÄąrÄąn", + "mark_all_as_read": "TÃŧmÃŧnÃŧ okundu olarak işaretle", + "mark_as_read": "Okundu olarak işaretle", + "marked_all_as_read": "TÃŧmÃŧ okundu olarak işaretlendi", "matches": "Eşleşenler", + "matching_assets": "Eşleşen Öğeler", "media_type": "Medya tÃŧrÃŧ", "memories": "AnÄąlar", "memories_all_caught_up": "TÃŧmÃŧ gÃļrÃŧldÃŧ", @@ -1185,9 +1342,19 @@ "merged_people_count": "{count, plural, one {# kişi} other {# kişi}} birleştirildi", "minimize": "KÃŧçÃŧlt", "minute": "Dakika", + "minutes": "Dakikalar", "missing": "Eksik", + "model": "Model", "month": "Ay", + "monthly_title_text_date_format": "MMMM y", "more": "Daha fazla", + "move": "TaÅŸÄą", + "move_off_locked_folder": "Kilitli klasÃļrden taÅŸÄą", + "move_to_lock_folder_action_prompt": "{count} kilitli klasÃļre eklendi", + "move_to_locked_folder": "Kilitli klasÃļre taÅŸÄą", + "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tÃŧm albÃŧmlerden kaldÄąrÄąlacak ve yalnÄązca kilitli klasÃļrden gÃļrÃŧntÃŧlenebilecektir", + "moved_to_archive": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} arşive taÅŸÄąndÄą", + "moved_to_library": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} kitaplığa taÅŸÄąndÄą", "moved_to_trash": "ÇÃļp kutusuna taÅŸÄąndÄą", "multiselect_grid_edit_date_time_err_read_only": "Salt okunur Ãļğelerin tarihi dÃŧzenlenemedi, atlanÄąyor", "multiselect_grid_edit_gps_err_read_only": "Salt okunur Ãļğelerin konumu dÃŧzenlenemedi, atlanÄąyor", @@ -1195,6 +1362,10 @@ "my_albums": "AlbÃŧmlerim", "name": "İsim", "name_or_nickname": "İsim veya takma isim", + "network_requirement_photos_upload": "FotoğraflarÄą yedeklemek için mobil veriyi kullan", + "network_requirement_videos_upload": "VideolarÄą yedeklemek için mobil veriyi kullan", + "network_requirements": "Ağ Gereksinimleri", + "network_requirements_updated": "Ağ durumu değişti, yedekleme kuyruğu sÄąfÄąrlandÄą", "networking_settings": "Ağ AyarlarÄą", "networking_subtitle": "Sunucu uç nokta ayarlarÄąnÄą dÃŧzenle", "never": "Asla", @@ -1203,8 +1374,10 @@ "new_password": "Yeni şifre", "new_person": "Yeni kişi", "new_pin_code": "Yeni PIN kodu", + "new_pin_code_subtitle": "Kilitli klasÃļre ilk kez erişiyorsunuz. Bu sayfaya gÃŧvenli erişim için bir PIN kodu oluşturun", + "new_timeline": "Yeni Zaman Çizelgesi", "new_user_created": "Yeni kullanÄącÄą oluşturuldu", - "new_version_available": "YENİ VERSİYON MEVCUT", + "new_version_available": "YENİ SÜRÜM MEVCUT", "newest_first": "Önce en yeniler", "next": "Sonraki", "next_memory": "Sonraki anÄą", @@ -1215,19 +1388,31 @@ "no_archived_assets_message": "Fotoğraf gÃļrÃŧnÃŧmÃŧnÃŧzden kaldÄąrmak için fotoğraflarÄą ve videolarÄą arşivleyin", "no_assets_message": "İLK FOTOĞRAFINIZI YÜKLEMEK İÇİN TIKLAYIN", "no_assets_to_show": "GÃļsterilecek Ãļğe yok", + "no_cast_devices_found": "YansÄątÄąlacak cihaz bulunamadÄą", + "no_checksum_local": "Sağlama toplamÄą mevcut değil - yerel varlÄąklarÄą alamÄąyor", + "no_checksum_remote": "Sağlama toplamÄą mevcut değil - uzak varlÄąk alÄąnamÄąyor", "no_duplicates_found": "Çift bulunamadÄą.", "no_exif_info_available": "EXIF bilgisi mevcut değil", "no_explore_results_message": "Koleksiyonunuzu keşfetmek için daha fazla fotoğraf yÃŧkleyin.", - "no_favorites_message": "En sevdiğiniz fotoğraf ve videolarÄą hÄązlÄąca bulmak için favoriler ekleyin", + "no_favorites_message": "En sevdiğiniz fotoğraf ve videolarÄą hÄązlÄąca bulmak için favorilere ekleyin", "no_libraries_message": "Fotoğraf ve videolarÄąnÄązÄą gÃļrmek için bir harici kÃŧtÃŧphane oluşturun", + "no_local_assets_found": "Bu sağlama toplamÄą ile yerel varlÄąk bulunamadÄą", + "no_locked_photos_message": "Kilitli klasÃļrdeki fotoğraf ve videolar gizlidir; kitaplığınÄązda gezinirken veya arama yaparken gÃļrÃŧnmezler.", "no_name": "İsim yok", + "no_notifications": "Bildirim yok", + "no_people_found": "Eşleşen kişi bulunamadÄą", "no_places": "Yer yok", + "no_remote_assets_found": "Bu sağlama toplamÄą ile uzaktaki varlÄąk bulunamadÄą", "no_results": "Sonuç bulunamadÄą", "no_results_description": "Eş anlamlÄą ya da daha genel anlamlÄą bir kelime deneyin", "no_shared_albums_message": "FotoğraflarÄą ve videolarÄą ağınÄązdaki kişilerle paylaşmak için bir albÃŧm oluşturun", + "no_uploads_in_progress": "YÃŧkleme işlemi yok", + "not_available": "YOK", "not_in_any_album": "Hiçbir albÃŧmde değil", - "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha Ãļnce yÃŧklenen varlÄąklar için bir depolama yolu etiketi uygulamak Ãŧzere şunu başlatÄąn", + "not_selected": "Seçilmedi", + "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha Ãļnce yÃŧklenen Ãļğeler için bir depolama yolu etiketi uygulamak Ãŧzere şunu başlatÄąn", "notes": "Notlar", + "nothing_here_yet": "Burada henÃŧz bir şey yok", "notification_permission_dialog_content": "Bildirimleri etkinleştirmek için cihaz ayarlarÄąna gidin ve izin verin.", "notification_permission_list_tile_content": "Bildirimleri etkinleştirmek için izin verin.", "notification_permission_list_tile_enable_button": "Bildirimleri Etkinleştir", @@ -1235,26 +1420,35 @@ "notification_toggle_setting_description": "E-posta bildirimlerine izin ver", "notifications": "Bildirimler", "notifications_setting_description": "Bildirimleri yÃļnetin", + "oauth": "OAuth", "official_immich_resources": "Resmi Immich KaynaklarÄą", "offline": "Çevrim dÄąÅŸÄą", + "offset": "Ofset", "ok": "Tamam", "oldest_first": "Eski olan Ãļnce", "on_this_device": "Bu cihazda", "onboarding": "Uyum SÃŧreci", - "onboarding_privacy_description": "Şu (isteğe bağlÄą) Ãļzellikler harici hizmetlere dayanÄąr ve yÃļnetim ayarlarÄąndan herhangi bir zamanda devre dÄąÅŸÄą bÄąrakÄąlabilir.", + "onboarding_locale_description": "Tercih ettiğiniz dili seçin. Bu ayarÄą daha sonra değiştirebilirsiniz.", + "onboarding_privacy_description": "Şu (isteğe bağlÄą) Ãļzellikler harici hizmetlere dayanÄąr ve ayarlardan herhangi bir zamanda devre dÄąÅŸÄą bÄąrakÄąlabilir.", + "onboarding_server_welcome_description": "Örneğinizi bazÄą yaygÄąn ayarlarla ayarlayalÄąm.", "onboarding_theme_description": "İnstance’ınÄąz için bir renk temasÄą seçin. Bunu daha sonra ayarlarÄąnÄązdan değiştirebilirsiniz.", + "onboarding_user_welcome_description": "Haydi başlayalÄąm!", "onboarding_welcome_user": "Hoş geldin, {user}", "online": "Çevrimiçi", "only_favorites": "Sadece favoriler", + "open": "Aç", "open_in_map_view": "Harita gÃļrÃŧnÃŧmÃŧnde aç", "open_in_openstreetmap": "OpenStreetMap'te Aç", "open_the_search_filters": "Arama filtrelerini aç", "options": "Seçenekler", "or": "veya", + "organize_into_albums": "AlbÃŧmler halinde dÃŧzenle", + "organize_into_albums_description": "Mevcut eşzamanlama ayarlarÄąnÄą kullanarak mevcut fotoğraflarÄą albÃŧmlere ekleyin", "organize_your_library": "KÃŧtÃŧphanenizi dÃŧzenleyin", "original": "orijinal", "other": "Diğer", "other_devices": "Diğer cihazlar", + "other_entities": "Diğer kuruluşlar", "other_variables": "Diğer değişkenler", "owned": "Sahip olunan", "owner": "Sahip", @@ -1264,17 +1458,17 @@ "partner_can_access_location": "Fotoğraf ve videolarÄąnÄązÄąn çekildiği konum", "partner_list_user_photos": "{user} fotoğraflarÄą", "partner_list_view_all": "TÃŧmÃŧnÃŧ gÃļr", - "partner_page_empty_message": "FotoğraflarÄąnÄąz henÃŧz hiçbir partnerle paylaÅŸÄąlmadÄą.", + "partner_page_empty_message": "FotoğraflarÄąnÄąz henÃŧz hiçbir ortakla paylaÅŸÄąlmadÄą.", "partner_page_no_more_users": "Eklenecek başka kullanÄącÄą yok", - "partner_page_partner_add_failed": "Partner eklenemedi", - "partner_page_select_partner": "Partner seç", - "partner_page_shared_to_title": "PaylaÅŸÄąldÄą:", + "partner_page_partner_add_failed": "Ortak eklenemedi", + "partner_page_select_partner": "Ortak seç", + "partner_page_shared_to_title": "PaylaÅŸÄąldÄą", "partner_page_stop_sharing_content": "{partner} artÄąk fotoğraflarÄąnÄąza erişemeyecek.", - "partner_sharing": "Ortak paylaÅŸÄąmÄą", + "partner_sharing": "Ortak PaylaÅŸÄąmÄą", "partners": "Ortaklar", "password": "Şifre", - "password_does_not_match": "Şifreler eşleşmiyor", - "password_required": "Şifre gereklidir", + "password_does_not_match": "Şifre eşleşmiyor", + "password_required": "Şifre Gerekiyor", "password_reset_success": "Şifre başarÄąyla sÄąfÄąrlandÄą", "past_durations": { "days": "{days, plural, one {DÃŧn} other {Son # gÃŧn}}", @@ -1294,19 +1488,25 @@ "permanent_deletion_warning": "KalÄącÄą silme uyarÄąsÄą", "permanent_deletion_warning_setting_description": "Nesneleri kalÄącÄą olarak silerken uyarÄą gÃļster", "permanently_delete": "KalÄącÄą olarak sil", - "permanently_delete_assets_count": "{count, plural, one {Dosya} other {Dosyalar}} kalÄącÄą olarak silindi", - "permanently_delete_assets_prompt": "Bu {count, plural, one {dosyayÄą} other {# dosyalarÄą}} kalÄącÄą olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu dosyayÄą} other {bu dosyalarÄą}} albÃŧmlerinizden de kaldÄąrÄąr.", - "permanently_deleted_asset": "KalÄącÄą olarak silinmiş Ãļgeler", - "permanently_deleted_assets_count": "{count, plural, one {# dosya} other {# dosya}} kalÄącÄą olarak silindi", + "permanently_delete_assets_count": "{count, plural, one {Ãļğe} other {Ãļğeler}} kalÄącÄą olarak silindi", + "permanently_delete_assets_prompt": "Bu {count, plural, one {Ãļğeyi} other {# Ãļğeleri}} kalÄącÄą olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu Ãļğeyi} other {bu Ãļğeleri}} albÃŧmlerinizden de kaldÄąrÄąr.", + "permanently_deleted_asset": "KalÄącÄą olarak silinmiş Ãļğeler", + "permanently_deleted_assets_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} kalÄącÄą olarak silindi", + "permission": "İzin", + "permission_empty": "İzniniz boş olmamalÄą", "permission_onboarding_back": "Geri", "permission_onboarding_continue_anyway": "Yine de devam et", "permission_onboarding_get_started": "Haydi başlayalÄąm", "permission_onboarding_go_to_settings": "Ayarlara git", "permission_onboarding_permission_denied": "İzin reddedildi. Immich'i kullanmak için Ayarlar'da fotoğraf ve video izinlerini verin.", - "permission_onboarding_permission_granted": "İzin verildi. ArtÄąk hazÄąrsÄąnÄąz!", + "permission_onboarding_permission_granted": "İzin verildi! ArtÄąk hazÄąrsÄąnÄąz.", "permission_onboarding_permission_limited": "SÄąnÄąrlÄą izin. Immich'in tÃŧm fotoğrav ve videolarÄąnÄązÄą yedeklemesine ve yÃļnetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.", "permission_onboarding_request": "Immich'in fotoğraflarÄąnÄązÄą ve videolarÄąnÄązÄą gÃļrÃŧntÃŧleyebilmesi için izne ihtiyacÄą var.", "person": "Kişi", + "person_age_months": "{months, plural, one {# month} other {# months}} eski", + "person_age_year_months": "1 yÄąl, {months, plural, one {# month} other {# months}} eski", + "person_age_years": "{years, plural, other {# sene}} Ãļnce", + "person_birthdate": "{date} tarihinde doğdu", "person_hidden": "{name}{hidden, select, true { (gizli)} other {}}", "photo_shared_all_users": "FotoğraflarÄąnÄązÄą tÃŧm kullanÄącÄąlarla paylaştÄąnÄąz gibi gÃļrÃŧnÃŧyor veya paylaşacak kullanÄącÄą bulunmuyor.", "photos": "Fotoğraflar", @@ -1317,26 +1517,36 @@ "pin_code_changed_successfully": "PIN kodu başarÄąyla değiştirildi", "pin_code_reset_successfully": "PIN kodu başarÄąyla sÄąfÄąrlandÄą", "pin_code_setup_successfully": "PIN kodu başarÄąyla ayarlandÄą", + "pin_verification": "PIN kodu doğrulama", "place": "Konum", "places": "Konumlar", "places_count": "{count, plural, one {{count, number} yer} other {{count, number} yer}}", "play": "Oynat", "play_memories": "AnÄąlarÄą oynat", - "play_motion_photo": "Hareketli fotoğrafÄą oynat", + "play_motion_photo": "Hareketli FotoğrafÄą Oynat", "play_or_pause_video": "Videoyu oynat ya da durdur", + "please_auth_to_access": "Erişim için lÃŧtfen kimliğinizi doğrulayÄąn", + "port": "Port", "preferences_settings_subtitle": "Uygulama tercihlerini dÃŧzenle", "preferences_settings_title": "Tercihler", + "preparing": "HazÄąrlanÄąyor", "preset": "Ön ayar", "preview": "Önizleme", "previous": "Önceki", "previous_memory": "Önceki anÄą", - "previous_or_next_photo": "Önceki ya da sonraki fotoğraf", + "previous_or_next_day": "GÃŧn ileri/geri", + "previous_or_next_month": "Ay ileri/geri", + "previous_or_next_photo": "Fotoğraf ileri/geri", + "previous_or_next_year": "YÄąl ileri/geri", "primary": "Birincil", "privacy": "Gizlilik", + "profile": "Profil", "profile_drawer_app_logs": "GÃŧnlÃŧkler", "profile_drawer_client_out_of_date_major": "Mobil uygulama gÃŧncel değil. LÃŧtfen en son ana sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_client_out_of_date_minor": "Mobil uygulama gÃŧncel değil. LÃŧtfen en son sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_client_server_up_to_date": "Uygulama ve sunucu gÃŧncel", + "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Salt okunur mod etkinleştirildi. Ã‡Äąkmak için kullanÄącÄą avatar simgesine uzun basÄąn.", "profile_drawer_server_out_of_date_major": "Sunucu gÃŧncel değil. LÃŧtfen en son ana sÃŧrÃŧme gÃŧncelleyin.", "profile_drawer_server_out_of_date_minor": "Sunucu gÃŧncel değil. LÃŧtfen en son sÃŧrÃŧme gÃŧncelleyin.", "profile_image_of_user": "{user} kullanÄącÄąsÄąnÄąn profil resmi", @@ -1363,7 +1573,7 @@ "purchase_lifetime_description": "ÖmÃŧr boyu geçerli", "purchase_option_title": "SATIN ALMA SEÇENEKLERİ", "purchase_panel_info_1": "Immich'in gelişimi zaman ve çaba gerektiriyor ve tam zamanlÄą geliştiricilerimiz var. AmacÄąmÄąz, aÃ§Äąk kaynak yazÄąlÄąmÄą sÃŧrdÃŧrÃŧlebilir bir gelir kaynağı haline getirmek.", - "purchase_panel_info_2": "Bu satÄąn alma işlemi Immich'te ek işlevsellik açmayacak. Immich'in gelişimini desteklemek için size gÃŧveniyoruz.", + "purchase_panel_info_2": "Ücretli Ãļzellikler (paywall) eklememeye kararlÄą olduğumuz için, bu satÄąn alma işlemi Immich'te ek işlevsellik sağlamaz. Immich'in sÃŧrekli gelişimini desteklemek için sizin gibi kullanÄącÄąlara gÃŧveniyoruz.", "purchase_panel_title": "Projeyi destekleyin", "purchase_per_server": "Sunucu baÅŸÄąna", "purchase_per_user": "KullanÄącÄą baÅŸÄąna", @@ -1375,21 +1585,28 @@ "purchase_server_description_2": "Destekçi statÃŧsÃŧ", "purchase_server_title": "Sunucu", "purchase_settings_server_activated": "Sunucu ÃŧrÃŧn anahtarÄą, yÃļnetici tarafÄąndan yÃļnetilir", + "query_asset_id": "Öğe Kimliği Sorgulama", + "queue_status": "SÄąrada {count}/{total}", "rating": "Derecelendirme", "rating_clear": "Derecelendirmeyi temizle", "rating_count": "{count, plural, one {# yÄąldÄąz} other {# yÄąldÄąz}}", "rating_description": "EXIF derecelendirmesini bilgi panelinde gÃļster", "reaction_options": "Tepki seçenekleri", "read_changelog": "Değişiklik gÃŧnlÃŧğÃŧnÃŧ oku", + "readonly_mode_disabled": "Salt okunur mod devre dÄąÅŸÄą", + "readonly_mode_enabled": "Salt okunur mod etkin", + "ready_for_upload": "YÃŧklemeye hazÄąr", "reassign": "Yeniden ata", - "reassigned_assets_to_existing_person": "{count, plural, one {# dosya} other {# dosya}} {name, select, null {mevcut bir kişiye} other {{name}}} atandÄą", - "reassigned_assets_to_new_person": "{count, plural, one {# dosya} other {# dosya}} yeni bir kişiye atandÄą", - "reassing_hint": "Seçili dosyalarÄą mevcut bir kişiye atayÄąn", + "reassigned_assets_to_existing_person": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} {name, select, null {mevcut bir kişiye} other {{name}}} atandÄą", + "reassigned_assets_to_new_person": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} yeni bir kişiye atandÄą", + "reassing_hint": "Seçili Ãļğeleri mevcut bir kişiye atayÄąn", "recent": "Son", "recent-albums": "Son kaydedilen albÃŧmler", "recent_searches": "Son aramalar", "recently_added": "Son eklenenler", "recently_added_page_title": "Son Eklenenler", + "recently_taken": "Son çekilenler", + "recently_taken_page_title": "Son Çekilenler", "refresh": "Yenile", "refresh_encoded_videos": "KodlanmÄąÅŸ videolarÄą yenile", "refresh_faces": "YÃŧzleri yenile", @@ -1401,15 +1618,25 @@ "refreshing_faces": "YÃŧzler yenileniyor", "refreshing_metadata": "Meta veriler yenileniyor", "regenerating_thumbnails": "KÃŧçÃŧk resimler yeniden oluşturuluyor", + "remote": "Uzaktan", + "remote_assets": "Uzak Öğeler", + "remote_media_summary": "Uzaktan Medya Özeti", "remove": "KaldÄąr", - "remove_assets_album_confirmation": "{count, plural, one {# dosyayÄą} other {# dosyayÄą}} albÃŧmden Ã§Äąkarmak istediğinizden emin misiniz?", - "remove_assets_shared_link_confirmation": "{count, plural, one {# dosyayÄą} other {# dosyayÄą}} bu paylaÅŸÄąlan bağlantÄądan Ã§Äąkarmak istediğinizden emin misiniz?", - "remove_assets_title": "DosyalarÄą Ã§Äąkar?", + "remove_assets_album_confirmation": "{count, plural, one {# Ãļğeyi} other {# Ãļğeleri}} albÃŧmden Ã§Äąkarmak istediğinizden emin misiniz?", + "remove_assets_shared_link_confirmation": "{count, plural, one {# Ãļğeyi} other {# Ãļğeleri}} bu paylaÅŸÄąlan bağlantÄądan Ã§Äąkarmak istediğinizden emin misiniz?", + "remove_assets_title": "Öğeleri Ã§Äąkar?", "remove_custom_date_range": "Özel tarih aralığınÄą kaldÄąr", - "remove_deleted_assets": "ÇevrimdÄąÅŸÄą dosyalarÄą kaldÄąr", + "remove_deleted_assets": "Silinen Öğeleri KaldÄąr", "remove_from_album": "AlbÃŧmden Ã§Äąkar", + "remove_from_album_action_prompt": "{count} albÃŧmden kaldÄąrÄąldÄą", "remove_from_favorites": "Favorilerden Ã§Äąkar", + "remove_from_lock_folder_action_prompt": "{count} kilitli klasÃļrden kaldÄąrÄąldÄą", + "remove_from_locked_folder": "Kilitli klasÃļrden kaldÄąr", + "remove_from_locked_folder_confirmation": "Bu fotoğraf ve videolarÄą kilitli klasÃļrden Ã§Äąkarmak istediğinizden emin misiniz? Ã‡ÄąkarÄąldÄąklarÄąnda kitaplığınÄązda gÃļrÃŧnÃŧr olacaklar.", "remove_from_shared_link": "PaylaÅŸÄąlan bağlantÄądan Ã§Äąkar", + "remove_memory": "AnÄąyÄą kaldÄąr", + "remove_photo_from_memory": "Bu anÄądan fotoğrafÄą kaldÄąr", + "remove_tag": "Etiketi kaldÄąr", "remove_url": "BağlantÄąyÄą kaldÄąr", "remove_user": "KullanÄącÄąyÄą Ã§Äąkar", "removed_api_key": "API anahtarÄą {name} kaldÄąrÄąldÄą", @@ -1418,32 +1645,42 @@ "removed_from_favorites_count": "{count, plural, other {#}} favorilerden Ã§ÄąkarÄąldÄą", "removed_memory": "AnÄą kaldÄąrÄąldÄą", "removed_photo_from_memory": "Fotoğraf anÄądan kaldÄąrÄąldÄą", - "removed_tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketleri kaldÄąrÄąldÄą", + "removed_tagged_assets": "{count, plural, one {# Ãļğenin} other {# Ãļğelerin}} etiketleri kaldÄąrÄąldÄą", "rename": "Yeniden adlandÄąr", "repair": "Onar", "repair_no_results_message": "Bulunamayan ve eksik dosyalar burada listelenecektir", "replace_with_upload": "YÃŧkleme ile değiştir", "repository": "Depo", - "require_password": "Şifre gerekli", + "require_password": "Şifre gerekiyor", "require_user_to_change_password_on_first_login": "KullanÄącÄą ilk girişte şifreyi değiştirmeli", "rescan": "Yeniden tara", "reset": "SÄąfÄąrla", "reset_password": "Şifreyi sÄąfÄąrla", "reset_people_visibility": "Kişilerin gÃļrÃŧnÃŧrlÃŧğÃŧnÃŧ sÄąfÄąrla", "reset_pin_code": "PIN kodunu sÄąfÄąrlayÄąn", + "reset_pin_code_description": "PIN kodunuzu unuttuysanÄąz, sÄąfÄąrlamak için sunucu yÃļneticisiyle iletişime geçebilirsiniz", + "reset_pin_code_success": "PIN kodu başarÄąyla sÄąfÄąrlandÄą", + "reset_pin_code_with_password": "PIN kodunuzu her zaman şifrenizle sÄąfÄąrlayabilirsiniz", + "reset_sqlite": "SQLite VeritabanÄąnÄą SÄąfÄąrla", + "reset_sqlite_confirmation": "SQLite veritabanÄąnÄą sÄąfÄąrlamak istediğinizden emin misiniz? Verileri yeniden eşzamanlamak için oturumu kapatÄąp tekrar oturum açmanÄąz gerekecektir", + "reset_sqlite_success": "SQLite veritabanÄąnÄą başarÄąyla sÄąfÄąrladÄąnÄąz", "reset_to_default": "VarsayÄąlana sÄąfÄąrla", "resolve_duplicates": "Çiftleri çÃļz", "resolved_all_duplicates": "TÃŧm çiftler çÃļzÃŧldÃŧ", "restore": "Geri yÃŧkle", "restore_all": "TÃŧmÃŧnÃŧ geri yÃŧkle", + "restore_trash_action_prompt": "{count} çÃļp kutusundan geri yÃŧklendi", "restore_user": "KullanÄącÄąyÄą geri yÃŧkle", - "restored_asset": "Dosya geri yÃŧklendi", + "restored_asset": "Öğe geri yÃŧklendi", "resume": "Devam et", + "resume_paused_jobs": "SÃŧrdÃŧr {count, plural, one {# duraklatÄąlmÄąÅŸ iş} other {# duraklatÄąlmÄąÅŸ işler}}", "retry_upload": "Yeniden yÃŧklemeyi dene", "review_duplicates": "Çiftleri gÃļzden geçir", + "review_large_files": "BÃŧyÃŧk dosyalarÄą inceleyin", "role": "Rol", "role_editor": "DÃŧzenleyici", "role_viewer": "GÃļrÃŧntÃŧleyici", + "running": "ÇalÄąÅŸÄąyor", "save": "Kaydet", "save_to_gallery": "Fotoğraflar'a kaydet", "saved_api_key": "API anahtarÄą kaydedildi", @@ -1460,7 +1697,7 @@ "search_by_context": "Bağlama gÃļre ara", "search_by_description": "AÃ§Äąklamaya gÃļre ara", "search_by_description_example": "Sapa'da yÃŧrÃŧyÃŧş gÃŧnÃŧ", - "search_by_filename": "Dosya adÄąna gÃļre ara", + "search_by_filename": "Dosya adÄąna veya uzantÄąsÄąna gÃļre ara", "search_by_filename_example": "Örn. IMG_1234.JPG veya PNG", "search_camera_make": "Kamera markasÄąna gÃļre ara...", "search_camera_model": "Kamera modeline gÃļre ara...", @@ -1473,6 +1710,7 @@ "search_filter_date_title": "Tarih aralığı seç", "search_filter_display_option_not_in_album": "AlbÃŧmde değil", "search_filter_display_options": "GÃļrÃŧntÃŧ Seçenekleri", + "search_filter_filename": "Dosya adÄąna gÃļre ara", "search_filter_location": "Konum", "search_filter_location_title": "Konum seç", "search_filter_media_type": "Medya TÃŧrÃŧ", @@ -1480,8 +1718,10 @@ "search_filter_people_title": "Kişi seç", "search_for": "AraştÄąr", "search_for_existing_person": "Mevcut bir kişiyi ara", + "search_no_more_result": "Daha fazla sonuç yok", "search_no_people": "Kişi yok", "search_no_people_named": "\"{name}\" isimli bir kişi yok", + "search_no_result": "Sonuç bulunamadÄą. FarklÄą bir arama terimi veya kombinasyon deneyin", "search_options": "Arama seçenekleri", "search_page_categories": "Kategoriler", "search_page_motion_photos": "CanlÄą Fotoğraflar", @@ -1500,7 +1740,7 @@ "search_result_page_new_search_hint": "Yeni Arama", "search_settings": "AyarlarÄą ara", "search_state": "Eyalet/İl ara...", - "search_suggestion_list_smart_search_hint_1": "AkÄąllÄą arama varsayÄąlan olarak etkindir, meta verileri aramak için syntax kullanÄąn", + "search_suggestion_list_smart_search_hint_1": "AkÄąllÄą arama varsayÄąlan olarak etkindir, meta verileri aramak için şu sÃļzdizimini kullanÄąn ", "search_suggestion_list_smart_search_hint_2": "m:meta-veri-aramasÄą", "search_tags": "Etiketleri ara...", "search_timezone": "Saat dilimi ara...", @@ -1509,9 +1749,11 @@ "searching_locales": "Yerleri arÄąyor...", "second": "Saniye", "see_all_people": "TÃŧm kişileri gÃļr", + "select": "Seç", "select_album_cover": "AlbÃŧm kapağı seç", "select_all": "TÃŧmÃŧnÃŧ seç", "select_all_duplicates": "TÃŧm çiftleri seç", + "select_all_in": "{group} içindekilerin tÃŧmÃŧnÃŧ seç", "select_avatar_color": "Avatar rengini seç", "select_face": "YÃŧzÃŧ seç", "select_featured_photo": "Öne Ã§Äąkan fotoğrafÄą seç", @@ -1519,11 +1761,13 @@ "select_keep_all": "Hepsini sakla", "select_library_owner": "KÃŧtÃŧphane sahibini seç", "select_new_face": "Yeni yÃŧz seç", + "select_person_to_tag": "Etiketlemek için bir kişi seçin", "select_photos": "FotoğraflarÄą seç", "select_trash_all": "Hepsini çÃļpe at", "select_user_for_sharing_page_err_album": "AlbÃŧm oluşturulamadÄą", "selected": "Seçildi", "selected_count": "{count, plural, other {# seçildi}}", + "selected_gps_coordinates": "Seçilen GPS KoordinatlarÄą", "send_message": "Mesaj gÃļnder", "send_welcome_email": "Hoş geldin e-postasÄą gÃļnder", "server_endpoint": "Sunucu Uç NoktasÄą", @@ -1531,8 +1775,9 @@ "server_info_box_server_url": "Sunucu URL", "server_offline": "Sunucu çevrimdÄąÅŸÄą", "server_online": "Sunucu çevrimiçi", + "server_privacy": "Sunucu Gizliliği", "server_stats": "Sunucu istatistikleri", - "server_version": "Sunucu versiyonu", + "server_version": "Sunucu SÃŧrÃŧmÃŧ", "set": "Ayarla", "set_as_album_cover": "AlbÃŧm resmi olarak ayarla", "set_as_featured_photo": "Öne Ã§Äąkan fotoğraf olarak ayarla", @@ -1540,8 +1785,9 @@ "set_date_of_birth": "Doğum tarihini ayarla", "set_profile_picture": "Profil resmini ayarla", "set_slideshow_to_fullscreen": "Slayt gÃļsterisini tam ekran yap", + "set_stack_primary_asset": "Birincil Ãļğe olarak ayarla", "setting_image_viewer_help": "GÃļrÃŧntÃŧleyici Ãļnce kÃŧçÃŧk resmi gÃļsterir, ardÄąndan orta boy Ãļnizlemeyi (etkinleştirilmişse) ve son olarak orijinali (etkinleştirilmişse) gÃļsterir.", - "setting_image_viewer_original_subtitle": "Orijinal tam çÃļzÃŧnÃŧrlÃŧklÃŧ gÃļrÃŧntÃŧyÃŧ gÃļstermek için etkinleştirin. Veri kullanÄąmÄąnÄą azaltmak için devre dÄąÅŸÄą bÄąrakÄąn (hem ağ hem de cihaz Ãļnbelleği).", + "setting_image_viewer_original_subtitle": "Orijinal tam çÃļzÃŧnÃŧrlÃŧklÃŧ gÃļrÃŧntÃŧyÃŧ (bÃŧyÃŧk!) yÃŧklemek için etkinleştirin. Veri kullanÄąmÄąnÄą azaltmak için devre dÄąÅŸÄą bÄąrakÄąn (hem ağ hem de cihaz Ãļnbelleği).", "setting_image_viewer_original_title": "Orijinal gÃļrÃŧntÃŧyÃŧ gÃļster", "setting_image_viewer_preview_subtitle": "Orta çÃļzÃŧnÃŧrlÃŧklÃŧ bir gÃļrÃŧntÃŧ gÃļstermek için etkinleştirin. Orijinali doğrudan gÃļstermek veya yalnÄązca kÃŧçÃŧk resmi kullanmak için devre dÄąÅŸÄą bÄąrakÄąn.", "setting_image_viewer_preview_title": "Önizleme gÃļrÃŧntÃŧsÃŧ gÃļster", @@ -1560,14 +1806,18 @@ "setting_notifications_total_progress_subtitle": "Toplam yÃŧkleme ilerlemesi (tamamlanan/toplam)", "setting_notifications_total_progress_title": "Arkaplan yedeklemesi toplam ilerlemesini gÃļster", "setting_video_viewer_looping_title": "DÃļngÃŧ", + "setting_video_viewer_original_video_subtitle": "Sunucudan video aktarÄąlÄąrken, transcode (dÃļnÃŧştÃŧrÃŧlmÃŧş) sÃŧrÃŧm mevcut olsa bile orijinal dosya oynatÄąlÄąr. Bu durum, arabelleğe alma (buffering) sorunlarÄąna yol açabilir. Videolar yerel olarak mevcutsa, bu ayardan bağımsÄąz olarak orijinal kalitede oynatÄąlÄąr.", + "setting_video_viewer_original_video_title": "Orijinal videoyu zorla", "settings": "Ayarlar", "settings_require_restart": "Bu ayarÄą uygulamak için lÃŧtfen Immich'i yeniden başlatÄąn", "settings_saved": "Ayarlar kaydedildi", "setup_pin_code": "PIN kodunu ayarlayÄąn", "share": "Paylaş", + "share_action_prompt": "PaylaÅŸÄąlan {count} Ãļğe", "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "HazÄąrlanÄąyor...", + "share_link": "BağlantÄąyÄą Paylaş", "shared": "PaylaÅŸÄąlan", "shared_album_activities_input_disable": "Yoruma kapalÄą", "shared_album_activity_remove_content": "Bu etkinliği silmek istiyor musunuz?", @@ -1580,10 +1830,12 @@ "shared_by_user": "{user} tarafÄąndan paylaÅŸÄąldÄą", "shared_by_you": "Senin tarafÄąndan paylaÅŸÄąldÄą", "shared_from_partner": "{partner} tarafÄąndan paylaÅŸÄąlan fotoğraflar", + "shared_intent_upload_button_progress_text": "{current} / {total} YÃŧklendi", "shared_link_app_bar_title": "PaylaÅŸÄąlan BağlantÄąlar", "shared_link_clipboard_copied_massage": "Panoya kopyalandÄą", - "shared_link_clipboard_text": "BağlantÄą: {link}\nParola: {password}", + "shared_link_clipboard_text": "BağlantÄą: {link}\nŞifre: {password}", "shared_link_create_error": "PaylaÅŸÄąm bağlantÄąsÄą oluşturulurken hata oluştu", + "shared_link_custom_url_description": "Özel bir URL ile bu paylaÅŸÄąlan bağlantÄąya erişin", "shared_link_edit_description_hint": "AÃ§Äąklama yazÄąn", "shared_link_edit_expire_after_option_day": "1 gÃŧn", "shared_link_edit_expire_after_option_days": "{count} gÃŧn", @@ -1593,7 +1845,7 @@ "shared_link_edit_expire_after_option_minutes": "{count} dakika", "shared_link_edit_expire_after_option_months": "{count} ay", "shared_link_edit_expire_after_option_year": "{count} yÄąl", - "shared_link_edit_password_hint": "PaylaÅŸÄąm parolasÄąnÄą girin", + "shared_link_edit_password_hint": "PaylaÅŸÄąm şifresini girin", "shared_link_edit_submit_button": "BağlantÄąyÄą gÃŧncelle", "shared_link_error_server_url_fetch": "Sunucu URL'si alÄąnamadÄą", "shared_link_expires_day": "SÃŧresi {count} gÃŧn içinde doluyor", @@ -1606,11 +1858,13 @@ "shared_link_expires_second": "SÃŧresi {count} saniye içinde doluyor", "shared_link_expires_seconds": "{count} sanyei içinde sÃŧresi doluyor", "shared_link_individual_shared": "Bireysel paylaÅŸÄąmlÄą", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "PaylaÅŸÄąlan BağlantÄąlarÄą YÃļnet", "shared_link_options": "PaylaÅŸÄąlan bağlantÄą seçenekleri", + "shared_link_password_description": "Bu paylaÅŸÄąlan bağlantÄąya erişmek için şifre gereklidir", "shared_links": "PaylaÅŸÄąlan bağlantÄąlar", "shared_links_description": "Fotoğraf ve videolarÄą bir bağlantÄą ile paylaş", - "shared_photos_and_videos_count": "{assetCount, plural, one {# paylaÅŸÄąlan fotoğraf veya video.} other {# paylaÅŸÄąlan fotoğraf & video.}}", + "shared_photos_and_videos_count": "{assetCount, plural, other {# paylaÅŸÄąlan fotoğraflar & videolar.}}", "shared_with_me": "Benimle paylaÅŸÄąlanlar", "shared_with_partner": "{partner} ile paylaÅŸÄąldÄą", "sharing": "PaylaÅŸÄąlÄąyor", @@ -1620,8 +1874,8 @@ "sharing_page_empty_list": "LİSTEYİ BOŞALT", "sharing_sidebar_description": "Yan panelde paylaÅŸÄąlanlara kÄąsa yol gÃļster", "sharing_silver_appbar_create_shared_album": "Yeni paylaÅŸÄąlan albÃŧm", - "sharing_silver_appbar_share_partner": "Partnerle paylaş", - "shift_to_permanent_delete": "DosyayÄą kalÄącÄą olarak silmek için ⇧ tuşuna basÄąn", + "sharing_silver_appbar_share_partner": "Ortakla paylaş", + "shift_to_permanent_delete": "Öğeyi kalÄącÄą olarak silmek için ⇧ tuşuna basÄąn", "show_album_options": "AlbÃŧm ayarlarÄąnÄą gÃļster", "show_albums": "AlbÃŧmleri gÃļster", "show_all_people": "TÃŧm kişileri gÃļster", @@ -1642,6 +1896,7 @@ "show_slideshow_transition": "Slayt geçişini gÃļster", "show_supporter_badge": "Destekçi rozeti", "show_supporter_badge_description": "Destekçi rozetini gÃļster", + "show_text_search_menu": "Metin arama menÃŧsÃŧnÃŧ gÃļster", "shuffle": "KarÄąÅŸtÄąr", "sidebar": "Yan panel", "sidebar_display_description": "Yan panelde gÃļrÃŧnÃŧme kÄąsa yol gÃļster", @@ -1657,53 +1912,64 @@ "sort_created": "Oluşturulma tarihi", "sort_items": "Öğe sayÄąsÄą", "sort_modified": "Değişiklik tarihi", + "sort_newest": "En yeni fotoğraf", "sort_oldest": "En eski fotoğraf", "sort_people_by_similarity": "İnsanlarÄą benzerliğe gÃļre sÄąrala", "sort_recent": "En yeni fotoğraf", "sort_title": "BaşlÄąk", "source": "Kaynak", "stack": "Yığın", + "stack_action_prompt": "{count} istiflenmiş", "stack_duplicates": "Çiftleri yığınla", "stack_select_one_photo": "Yığın için ana fotoğrafÄą seç", "stack_selected_photos": "Seçili fotoğraflarÄą yığınla", - "stacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığınlandÄą", + "stacked_assets_count": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} yığınlandÄą", "stacktrace": "Yığın izi", "start": "Başlat", "start_date": "BaşlangÄąÃ§ tarihi", + "start_date_before_end_date": "BaşlangÄąÃ§ tarihi bitiş tarihinden Ãļnce olmalÄądÄąr", "state": "Eyalet/İl", "status": "Durum", + "stop_casting": "YansÄątmayÄą durdur", "stop_motion_photo": "Hareketli fotoğrafÄą durdur", "stop_photo_sharing": "FotoğraflarÄąnÄązÄą paylaşmayÄą durdurmak mÄą istiyorsunuz?", "stop_photo_sharing_description": "{partner} artÄąk fotoğraflarÄąnÄąza erişemeyecek.", "stop_sharing_photos_with_user": "Bu kullanÄącÄą ile fotoğraflarÄąnÄązÄą paylaşmayÄą durdurun", "storage": "Depolama alanÄą", "storage_label": "Depolama yolu", + "storage_quota": "Depolama KotasÄą", "storage_usage": "{used} / {available} kullanÄąldÄą", "submit": "GÃļnder", + "success": "BaşarÄąlÄą", "suggestions": "Öneriler", "sunrise_on_the_beach": "Plajda gÃŧn doğumu", "support": "Destek", "support_and_feedback": "Destek & Geri Bildirim", "support_third_party_description": "Immich kurulumu ÃŧçÃŧncÃŧ bir tarafça yapÄąldÄą. YaşadığınÄąz sorunlar bu paketle ilgili olabilir. LÃŧtfen Ãļncelikli olarak aşağıdaki bağlantÄąlarÄą kullanarak bu sağlayÄącÄąyla iletişime geçin.", "swap_merge_direction": "Birleştirme yÃļnÃŧnÃŧ değiştir", - "sync": "Senkronize et", + "sync": "Eşzamanla", "sync_albums": "AlbÃŧmleri eşzamanla", "sync_albums_manual_subtitle": "YÃŧklenmiş fotoğraf ve videolarÄą yedekleme için seçili albÃŧmler ile eşzamanlayÄąn", - "sync_upload_album_setting_subtitle": "Seçili albÃŧmleri Immich'te oluşturun ve içindekileri Immich'e yÃŧkleyin.", + "sync_local": "Yerel Eşzamanlama", + "sync_remote": "Uzaktan Eşzamanlama", + "sync_status": "Eşzamanlama Durumu", + "sync_status_subtitle": "Eşzamanlama sistemini gÃļrÃŧntÃŧleyin ve yÃļnetin", + "sync_upload_album_setting_subtitle": "FotoğraflarÄąnÄązÄą ve videolarÄąnÄązÄą oluşturun ve Immich'te seçtiğiniz albÃŧmlere yÃŧkleyin", "tag": "Etiket", - "tag_assets": "DosyalarÄą etiketle", + "tag_assets": "Öğeleri etiketle", "tag_created": "Etiket oluşturuldu: {tag}", "tag_feature_description": "Etiket temalarÄąna gÃļre gruplandÄąrÄąlmÄąÅŸ fotoğraf ve videolarÄą keşfedin", "tag_not_found_question": "Etiket bulunamadÄą mÄą? Yeni bir etiket oluşturun.", "tag_people": "İnsanlarÄą etiketle", "tag_updated": "Etiket gÃŧncellendi: {tag}", - "tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketlendi", + "tagged_assets": "{count, plural, one {# Ãļğe} other {# Ãļğeler}} etiketlendi", "tags": "Etiketler", + "tap_to_run_job": "Başlatmak için dokunun", "template": "Şablon", "theme": "Tema", "theme_selection": "Tema seçimi", "theme_selection_description": "TemayÄą otomatik olarak tarayÄącÄąnÄązÄąn sistem tercihine gÃļre aÃ§Äąk veya koyu ayarlayÄąn", - "theme_setting_asset_list_storage_indicator_title": "Öğelerin kÃŧçÃŧk resimlerinde depolama gÃļstergesini gÃļster", + "theme_setting_asset_list_storage_indicator_title": "Öğe kutucuklarÄąnda depolama gÃļstergesini gÃļster", "theme_setting_asset_list_tiles_per_row_title": "SatÄąr baÅŸÄąna Ãļğe sayÄąsÄą ({count})", "theme_setting_colorful_interface_subtitle": "Birincil rengi arka plan yÃŧzeylerine uygulayÄąn.", "theme_setting_colorful_interface_title": "Renkli arayÃŧz", @@ -1714,7 +1980,7 @@ "theme_setting_system_primary_color_title": "Sistem rengini kullan", "theme_setting_system_theme_switch": "Otomatik (sistem ayarÄąna gÃļre)", "theme_setting_theme_subtitle": "Uygulama temasÄą seç", - "theme_setting_three_stage_loading_subtitle": "Üç aşamalÄą yÃŧkleme yÃŧkleme performansÄąnÄą artÄąrabilir ancak Ãļnemli ÃļlçÃŧde daha yÃŧksek ağ yÃŧkÃŧne sebep olur.", + "theme_setting_three_stage_loading_subtitle": "Üç aşamalÄą yÃŧkleme, yÃŧkleme performansÄąnÄą artÄąrabilir ancak ağ yÃŧkÃŧnÃŧ Ãļnemli ÃļlçÃŧde artÄąrÄąr", "theme_setting_three_stage_loading_title": "Üç aşamalÄą yÃŧklemeyi etkinleştir", "they_will_be_merged_together": "Birlikte birleştirilecekler", "third_party_resources": "ÜçÃŧncÃŧ taraf kaynaklar", @@ -1725,87 +1991,111 @@ "to_change_password": "Şifreyi değiştir", "to_favorite": "Favorilere ekle", "to_login": "Oturum aç", + "to_multi_select": "çoklu seçim için", "to_parent": "Üst Ãļğeye git", + "to_select": "seçmek için", "to_trash": "ÇÃļpe taÅŸÄą", "toggle_settings": "AyarlarÄą değiştir", "total": "Toplam", "total_usage": "Toplam kullanÄąm", "trash": "ÇÃļp", + "trash_action_prompt": "{count} çÃļp kutusuna taÅŸÄąndÄą", "trash_all": "Hepsini sil", "trash_count": "ÇÃļp kutusu {count, number}", - "trash_delete_asset": "Ögeyi Sil/ÇÃļpe gÃļnder", + "trash_delete_asset": "Öğeyi Sil/ÇÃļpe GÃļnder", "trash_emptied": "ÇÃļp kutusu temizlendi", "trash_no_results_message": "Silinen fotoğraf ve videolar burada listelenecektir.", "trash_page_delete_all": "TÃŧmÃŧnÃŧ Sil", "trash_page_empty_trash_dialog_content": "ÇÃļp kutusuna atÄąlmÄąÅŸ Ãļğeleri silmek istediğinize emin misiniz? Bu Ãļğeler Immich'ten kalÄącÄą olarak silinecek", "trash_page_info": "ÇÃļp kutusuna atÄąlan Ãļğeler {days} gÃŧn sonra kalÄącÄą olarak silinecektir", - "trash_page_no_assets": "ÇÃļp kutusu boş", - "trash_page_restore_all": "TÃŧmÃŧnÃŧ geri yÃŧkle", - "trash_page_select_assets_btn": "İçerik seç", + "trash_page_no_assets": "ÇÃļp kutusuna atÄąlmÄąÅŸ Ãļğe yok", + "trash_page_restore_all": "TÃŧmÃŧnÃŧ Geri YÃŧkle", + "trash_page_select_assets_btn": "Öğeleri seç", "trash_page_title": "ÇÃļp Kutusu ({count})", "trashed_items_will_be_permanently_deleted_after": "Silinen Ãļğeler {days, plural, one {# gÃŧn} other {# gÃŧn}} sonra kalÄącÄą olarak silinecek.", + "troubleshoot": "Sorun giderme", "type": "TÃŧr", "unable_to_change_pin_code": "PIN kodu değiştirilemedi", "unable_to_setup_pin_code": "PIN kodu ayarlanamadÄą", "unarchive": "Arşivden Ã§Äąkar", + "unarchive_action_prompt": "{count} Arşivden kaldÄąrÄąldÄą", "unarchived_count": "{count, plural, other {# arşivden Ã§ÄąkarÄąldÄą}}", + "undo": "Geri al", "unfavorite": "Favorilerden kaldÄąr", + "unfavorite_action_prompt": "{count} Favorilerden kaldÄąrÄąldÄą", "unhide_person": "Kişiyi gÃļster", "unknown": "Bilinmeyen", - "unknown_country": "Bilinmeyen Ãŧlke", - "unknown_year": "Bilinmeyen YIl", + "unknown_country": "Bilinmeyen Ülke", + "unknown_year": "Bilinmeyen YÄąl", "unlimited": "SÄąnÄąrsÄąz", "unlink_motion_video": "Hareketli video bağlantÄąsÄąnÄą kaldÄąr", "unlink_oauth": "OAuth bağlantÄąsÄąnÄą kaldÄąr", "unlinked_oauth_account": "BağlantÄąsÄą kaldÄąrÄąlmÄąÅŸ OAuth hesabÄą", - "unmute_memories": "AnÄąlarÄąn sesini aç", + "unmute_memories": "AnÄąlarÄąn Sesini Aç", "unnamed_album": "İsimsiz AlbÃŧm", "unnamed_album_delete_confirmation": "Bu albÃŧmÃŧ silmek istediğinizden emin misiniz?", - "unnamed_share": "İsimsiz paylaÅŸÄąm", + "unnamed_share": "İsimsiz PaylaÅŸÄąm", "unsaved_change": "Kaydedilmemiş değişiklik", "unselect_all": "TÃŧmÃŧnÃŧ seçimini kaldÄąr", "unselect_all_duplicates": "TÃŧm çiftlerin seçimini kaldÄąr", + "unselect_all_in": "{group} içindeki tÃŧm seçimleri kaldÄąr", "unstack": "YığınÄą kaldÄąr", - "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığınÄą kaldÄąrÄąldÄą", + "unstack_action_prompt": "{count} istiflenmemiş", + "unstacked_assets_count": "{count, plural, one {# Ãļğenin} other {# Ãļğelerin}} yığınÄą kaldÄąrÄąldÄą", + "untagged": "Etiketlenmemiş", "up_next": "SÄąradaki", - "updated_password": "Şifreyi gÃŧncelle", + "update_location_action_prompt": "Seçilen {count} Ãļğenin konumunu şu şekilde gÃŧncelleyin:", + "updated_at": "GÃŧncellenme", + "updated_password": "GÃŧncellenen şifre", "upload": "YÃŧkle", + "upload_action_prompt": "{count} yÃŧkleme için sÄąraya alÄąndÄą", "upload_concurrency": "YÃŧkleme eşzamanlÄąlığı", + "upload_details": "YÃŧkleme AyrÄąntÄąlarÄą", "upload_dialog_info": "Seçili Ãļğeleri sunucuya yedeklemek istiyor musunuz?", "upload_dialog_title": "Öğe YÃŧkle", - "upload_errors": "{count, plural, one {# hata} other {# hatayla}} yÃŧkleme tamamlandÄą, yeni yÃŧklenen dosyalarÄą gÃļrmek için sayfayÄą gÃŧncelleyin.", + "upload_errors": "{count, plural, one {# hata} other {# hatayla}} yÃŧkleme tamamlandÄą, yeni yÃŧklenen Ãļğeleri gÃļrmek için sayfayÄą gÃŧncelleyin.", + "upload_finished": "YÃŧkleme tamamlandÄą", "upload_progress": "{remaining, number} kalan - {processed, number}/{total, number} işlendi", - "upload_skipped_duplicates": "{count, plural, one {# çift dosya} other {# çift dosya}} atlandÄą", + "upload_skipped_duplicates": "{count, plural, one {# yinelenen Ãļğe} other {# yinelenen Ãļğeler}} atlandÄą", "upload_status_duplicates": "Çiftler", "upload_status_errors": "Hatalar", "upload_status_uploaded": "YÃŧklendi", - "upload_success": "YÃŧkleme başarÄąlÄą, yÃŧklenen yeni Ãļgeleri gÃļrebilmek için sayfayÄą yenileyin.", + "upload_success": "YÃŧkleme başarÄąlÄą, yÃŧklenen yeni Ãļğeleri gÃļrebilmek için sayfayÄą yenileyin.", + "upload_to_immich": "Immich'e YÃŧkle ({count})", + "uploading": "YÃŧkleniyor", + "uploading_media": "Medya yÃŧkleme", + "url": "URL", "usage": "KullanÄąm", + "use_biometric": "Biyometri kullan", "use_current_connection": "mevcut bağlantÄąyÄą kullan", "use_custom_date_range": "Bunun yerine Ãļzel tarih aralığınÄą kullan", "user": "KullanÄącÄą", + "user_has_been_deleted": "Bu kullanÄącÄą silindi.", "user_id": "KullanÄącÄą ID", - "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu dosya} other {Bu}} {user} tarafÄąndan beğenildi", + "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu Ãļğe} other {Bu}} {user} tarafÄąndan beğenildi", "user_pin_code_settings": "PIN Kodu", "user_pin_code_settings_description": "PIN kodunuzu yÃļnetin", + "user_privacy": "KullanÄącÄą Gizliliği", "user_purchase_settings": "SatÄąn Alma", "user_purchase_settings_description": "SatÄąn alma işlemlerini yÃļnet", "user_role_set": "{user}, {role} olarak ayarlandÄą", "user_usage_detail": "KullanÄącÄą kullanÄąm detayÄą", "user_usage_stats": "Hesap kullanÄąm istatistikleri", - "user_usage_stats_description": "hesap kullanÄąm istatistiklerini gÃļster", + "user_usage_stats_description": "Hesap kullanÄąm istatistiklerini gÃļster", "username": "KullanÄącÄą adÄą", "users": "KullanÄącÄąlar", - "utilities": "YardÄąmcÄąlar", + "users_added_to_album_count": "AlbÃŧme {count, plural, one {# user} other {# users}} eklendi", + "utilities": "YardÄąmcÄą Programlar", "validate": "Doğrula", "validate_endpoint_error": "LÃŧtfen geçerli bir URL girin", "variables": "Değişkenler", - "version": "Versiyon", + "version": "SÃŧrÃŧm", "version_announcement_closing": "ArkadaÅŸÄąnÄąz, Alex", "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 sÃŧrÃŧm notlarÄąnÄą 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_history": "Versiyon geçmişi", + "version_history": "SÃŧrÃŧm Geçmişi", "version_history_item": "{version}, {date} tarihinde kuruldu", - "video_hover_setting": "Üzerinde durulduğunda video Ãļnizlemesi oynat", + "video": "Video", + "video_hover_setting": "Üzerinde durulduğunda video Ãļn izlemesi oynat", "video_hover_setting_description": "Öğe Ãŧzerinde fareyle durulduğunda video kÃŧçÃŧk resmini oynatÄąr. Bu Ãļzellik devre dÄąÅŸÄąyken, oynatma simgesine fareyle gidilerek oynatma başlatÄąlabilir.", "videos": "Videolar", "videos_count": "{count, plural, one {# video} other {# video}}", @@ -1813,13 +2103,17 @@ "view_album": "AlbÃŧmÃŧ gÃļrÃŧntÃŧle", "view_all": "TÃŧmÃŧnÃŧ gÃļr", "view_all_users": "TÃŧm kullanÄącÄąlarÄą gÃļrÃŧntÃŧle", + "view_details": "AyrÄąntÄąlarÄą GÃļrÃŧntÃŧle", "view_in_timeline": "Zaman çizelgesinde gÃļrÃŧntÃŧle", "view_link": "BağlantÄąyÄą gÃļster", "view_links": "BağlantÄąlarÄą gÃļster", "view_name": "GÃļster", - "view_next_asset": "Sonraki dosyayÄą gÃļrÃŧntÃŧle", - "view_previous_asset": "Önceki dosyayÄą gÃļrÃŧntÃŧle", + "view_next_asset": "Sonraki Ãļğeyi gÃļrÃŧntÃŧle", + "view_previous_asset": "Önceki Ãļğeyi gÃļrÃŧntÃŧle", + "view_qr_code": "QR kodu gÃļrÃŧntÃŧle", + "view_similar_photos": "Benzer fotoğraflarÄą gÃļrÃŧntÃŧle", "view_stack": "YığınÄą gÃļrÃŧntÃŧle", + "view_user": "KullanÄącÄąyÄą GÃļrÃŧntÃŧle", "viewer_remove_from_stack": "Yığından KaldÄąr", "viewer_stack_use_as_main_asset": "Ana fotoğraf olarak kullan", "viewer_unstack": "YığınÄą KaldÄąr", @@ -1830,10 +2124,12 @@ "welcome": "Hoş geldiniz", "welcome_to_immich": "Immich'e hoş geldiniz", "wifi_name": "Wi-Fi AdÄą", + "wrong_pin_code": "YanlÄąÅŸ PIN kodu", "year": "YÄąl", "years_ago": "{years, plural, one {bir yÄąl} other {# yÄąl}} Ãļnce", "yes": "Evet", "you_dont_have_any_shared_links": "Herhangi bir paylaÅŸÄąlan bağlantÄąnÄąz yok", "your_wifi_name": "Wi-Fi AdÄąnÄąz", - "zoom_image": "GÃļrÃŧntÃŧyÃŧ yakÄąnlaştÄąr" + "zoom_image": "GÃļrÃŧntÃŧyÃŧ yakÄąnlaştÄąr", + "zoom_to_bounds": "SÄąnÄąrlara yakÄąnlaştÄąr" } diff --git a/i18n/uk.json b/i18n/uk.json index 7b843ceff9..a664fc9aa0 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -6,27 +6,32 @@ "action": "Đ”Ņ–Ņ", "action_common_update": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸", "actions": "Đ”Ņ–Ņ—", - "active": "АĐēŅ‚Đ¸Đ˛ĐŊиК", + "active": "ВиĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ", "activity": "АĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ", "activity_changed": "АĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ {enabled, select, true {ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž} other {виĐŧĐēĐŊĐĩĐŊĐž}}", "add": "Đ”ĐžĐ´Đ°Ņ‚Đ¸", - "add_a_description": "Додади ĐžĐŋĐ¸Ņ", + "add_a_description": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", "add_a_location": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", - "add_a_name": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ–Đŧ'Ņ", + "add_a_name": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃ Ņ–Đŧ'Ņ", "add_a_title": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ", - "add_endpoint": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐēŅ–ĐŊ҆ĐĩĐ˛Ņƒ Ņ‚ĐžŅ‡Đē҃", - "add_exclusion_pattern": "Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊ виĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ", + "add_birthday": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ´ĐĩĐŊҌ ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ", + "add_endpoint": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ°Đ´Ņ€Đĩҁ҃ ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", + "add_exclusion_pattern": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ виĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ", "add_import_path": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ", - "add_location": "Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", + "add_location": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "add_more_users": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛", "add_partner": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "add_path": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҈ĐģŅŅ…", - "add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи", + "add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "add_tag": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ‚ĐĩĐŗ", "add_to": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃â€Ļ", "add_to_album": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃ аĐģŅŒĐąĐžĐŧ", "add_to_album_bottom_sheet_added": "ДодаĐŊĐž Đ´Đž {album}", "add_to_album_bottom_sheet_already_exists": "ВĐļĐĩ Ņ” в {album}", + "add_to_album_bottom_sheet_some_local_assets": "ДĐĩŅĐēŅ– ĐģĐžĐēаĐģҌĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐŊĐĩ вдаĐģĐžŅŅ Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", + "add_to_album_toggle": "ПĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ Đ˛Đ¸ĐąĐžŅ€Ņƒ Đ´ĐģŅ {album}", + "add_to_albums": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž аĐģŅŒĐąĐžĐŧŅ–Đ˛", + "add_to_albums_count": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž аĐģŅŒĐąĐžĐŧŅ–Đ˛ ({count})", "add_to_shared_album": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ҃ ҁĐŋŅ–ĐģҌĐŊиК аĐģŅŒĐąĐžĐŧ", "add_url": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ URL", "added_to_archive": "ДодаĐŊĐž Đ´Đž Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", @@ -35,7 +40,7 @@ "admin": { "add_exclusion_pattern_description": "Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ ŅˆĐ°ĐąĐģĐžĐŊи виĐēĐģŅŽŅ‡ĐĩĐŊҌ. ĐŸŅ–Đ´ŅŅ‚Đ°ĐŊОвĐēа С виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅĐŧ *, ** Ņ‚Đ° ? ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚ŅŒŅŅ. ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛ŅŅ–Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛ ҃ ĐąŅƒĐ´ŅŒ-ŅĐēĐžĐŧ҃ ĐēĐ°Ņ‚Đ°ĐģĐžĐˇŅ– С Ņ–Đŧ'ŅĐŧ ÂĢRawÂģ, виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"**/Raw/**\". ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛ŅŅ–Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛, Ņ‰Đž СаĐēŅ–ĐŊŅ‡ŅƒŅŽŅ‚ŅŒŅŅ ĐŊа \".tif\", виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"**/*.tif\". ДĐģŅ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ°ĐąŅĐžĐģŅŽŅ‚ĐŊĐžĐŗĐž ҈ĐģŅŅ…Ņƒ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ \"/path/to/ignore/**\".", "admin_user": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€", - "asset_offline_description": "ĐĻĐĩĐš СОвĐŊŅ–ŅˆĐŊŅ–Đš ĐąŅ–ĐąĐģŅ–ĐžŅ‚Đĩ҇ĐŊиК аĐēŅ‚Đ¸Đ˛ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐē҃ Ņ– ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК Đ´Đž ҁĐŧŅ–Ņ‚ĐŊиĐēа. Đ¯ĐēŅ‰Đž Ņ„Đ°ĐšĐģ ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК ҃ ĐŧĐĩĐļĐ°Ņ… ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи, ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ ŅĐ˛Ņ–Đš Ņ‚Đ°ĐšĐŧĐģаКĐŊ ĐŊа ĐŊĐ°ŅĐ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´ĐŊĐžĐŗĐž аĐēŅ‚Đ¸Đ˛Ņƒ. ЊОй Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҆ĐĩĐš аĐēŅ‚Đ¸Đ˛, ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ҈ĐģŅŅ… Ņ„Đ°ĐšĐģ҃ ĐŊиĐļ҇Đĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиК Đ´ĐģŅ Immich, Ņ– ĐŋŅ€ĐžŅĐēаĐŊŅƒĐšŅ‚Đĩ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃.", + "asset_offline_description": "ĐĻĐĩĐš Ņ„Đ°ĐšĐģ СОвĐŊŅ–ŅˆĐŊŅŒĐžŅ— ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐē҃ Ņ– ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК Đ´Đž ĐēĐžŅˆĐ¸Đēа. Đ¯ĐēŅ‰Đž Ņ„Đ°ĐšĐģ ĐąŅƒĐ˛ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊиК ҃ ĐŧĐĩĐļĐ°Ņ… ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи, ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ ŅĐ˛ĐžŅŽ ҁ҂ҀҖ҇Đē҃ ĐŊа ĐŊĐ°ŅĐ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŊĐžĐ˛ĐžĐŗĐž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´ĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģ҃. ЊОй Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҆ĐĩĐš Ņ„Đ°ĐšĐģ, ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ҈ĐģŅŅ… Đ´Đž Ņ„Đ°ĐšĐģ҃ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиК Đ´ĐģŅ Immich, Ņ– ĐŋŅ€ĐžŅĐēаĐŊŅƒĐšŅ‚Đĩ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃.", "authentication_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", "authentication_settings_description": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅ–ĐŊĐŊŅ ĐŋĐ°Ņ€ĐžĐģŅĐŧи, OAuth Ņ‚Đ° Ņ–ĐŊŅˆĐ¸Đŧи ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", "authentication_settings_disable_all": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐŧĐēĐŊŅƒŅ‚Đ¸ Đ˛ŅŅ– ĐŧĐĩŅ‚ĐžĐ´Đ¸ Đ˛Ņ…ĐžĐ´Ņƒ? Đ’Ņ…Ņ–Đ´ ĐąŅƒĐ´Đĩ ĐŋОвĐŊŅ–ŅŅ‚ŅŽ виĐŧĐēĐŊĐĩĐŊиК.", @@ -44,6 +49,13 @@ "backup_database": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ даĐŧĐŋ йаСи даĐŊĐ¸Ņ…", "backup_database_enable_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ даĐŧĐŋи йаСи даĐŊĐ¸Ņ…", "backup_keep_last_amount": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Ņ… даĐŧĐŋŅ–Đ˛, ŅĐēŅ– СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸", + "backup_onboarding_1_description": "Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊа ĐēĐžĐŋŅ–Ņ ҃ Ņ…ĐŧĐ°Ņ€Ņ– айО в Ņ–ĐŊŅˆĐžĐŧ҃ Ņ„Ņ–ĐˇĐ¸Ņ‡ĐŊĐžĐŧ҃ ĐŧҖҁ҆Җ.", + "backup_onboarding_2_description": "ĐģĐžĐēаĐģҌĐŊŅ– ĐēĐžĐŋŅ–Ņ— ĐŊа Ņ€Ņ–ĐˇĐŊĐ¸Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŅ…. ĐĻĐĩ вĐēĐģŅŽŅ‡Đ°Ņ” ĐžŅĐŊОвĐŊŅ– Ņ„Đ°ĐšĐģи Ņ– Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ Ņ†Đ¸Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛ ĐģĐžĐēаĐģҌĐŊĐž.", + "backup_onboarding_3_description": "ĐˇĐ°ĐŗĐ°ĐģҌĐŊŅ– ĐēĐžĐŋŅ–Ņ— Đ˛Đ°ŅˆĐ¸Ņ… даĐŊĐ¸Ņ…, вĐēĐģŅŽŅ‡Đ°ŅŽŅ‡Đ¸ ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģҌĐŊŅ– Ņ„Đ°ĐšĐģи. ĐĻĐĩ вĐēĐģŅŽŅ‡Đ°Ņ” 1 Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊ҃ ĐēĐžĐŋŅ–ŅŽ Ņ– 2 ĐģĐžĐēаĐģҌĐŊŅ– ĐēĐžĐŋŅ–Ņ—.", + "backup_onboarding_description": "Đ ĐĩĐēĐžĐŧĐĩĐŊдОваĐŊĐž Đ´ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚Đ¸ŅŅ ŅŅ‚Ņ€Đ°Ņ‚ĐĩĐŗŅ–Ņ— Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ 3-2-1 Đ´ĐģŅ ĐˇĐ°Ņ…Đ¸ŅŅ‚Ņƒ Đ˛Đ°ŅˆĐ¸Ņ… даĐŊĐ¸Ņ…. ЗбĐĩŅ€Ņ–ĐŗĐ°ĐšŅ‚Đĩ ĐēĐžĐŋŅ–Ņ— СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐ¸Ņ… Ņ„ĐžŅ‚Đž Đš Đ˛Ņ–Đ´ĐĩĐž, а Ņ‚Đ°ĐēĐžĐļ йаСи даĐŊĐ¸Ņ… Immich, Ņ‰ĐžĐą СайĐĩСĐŋĐĩŅ‡Đ¸Ņ‚Đ¸ ĐŋОвĐŊĐžŅ†Ņ–ĐŊĐŊиК ĐˇĐ°Ņ…Đ¸ŅŅ‚ Ņ‚Đ° Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐŊŅ.", + "backup_onboarding_footer": "ДоĐēĐģадĐŊŅ–ŅˆĐĩ ĐŋŅ€Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Immich ĐŧĐžĐļĐŊа Đ´Ņ–ĐˇĐŊĐ°Ņ‚Đ¸ŅŅ С Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ—.", + "backup_onboarding_parts_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Са ŅŅ‚Ņ€Đ°Ņ‚ĐĩĐŗŅ–Ņ”ŅŽ 3-2-1 вĐēĐģŅŽŅ‡Đ°Ņ”:", + "backup_onboarding_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ—", "backup_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ даĐŧĐŋа йаСи даĐŊĐ¸Ņ…", "backup_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи даĐŧĐŋа йаСи даĐŊĐ¸Ņ….", "cleared_jobs": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊŅ– СавдаĐŊĐŊŅ Đ´ĐģŅ: {job}", @@ -59,14 +71,14 @@ "cron_expression_description": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Ņ–ĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҁĐēаĐŊŅƒĐ˛Đ°ĐŊĐŊŅ, виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅŽŅ‡Đ¸ Ņ„ĐžŅ€ĐŧĐ°Ņ‚ cron. ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛ĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž ĐŊаĐŋŅ€. Crontab Guru", "cron_expression_presets": "ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ cron Đ˛Đ¸Ņ€Đ°ĐˇŅ–Đ˛", "disable_login": "ВиĐŧĐēĐŊŅƒŅ‚Đ¸ Đ˛Ņ…Ņ–Đ´", - "duplicate_detection_job_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐĩ ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ ĐŊа аĐēŅ‚Đ¸Đ˛Đ°Ņ… Đ´ĐģŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ŅŅ…ĐžĐļĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ. ЗаĐģĐĩĐļĐ¸Ņ‚ŅŒ Đ˛Ņ–Đ´ Ņ–ĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃", + "duplicate_detection_job_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐĩ ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ ĐŊа Ņ€ĐĩŅŅƒŅ€ŅĐ°Ņ… Đ´ĐģŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ŅŅ…ĐžĐļĐ¸Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ. ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ” Ņ–ĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģҌĐŊиК ĐŋĐžŅˆŅƒĐē", "exclusion_pattern_description": "ШайĐģĐžĐŊи виĐēĐģŅŽŅ‡ĐĩĐŊҌ дОСвОĐģŅŅŽŅ‚ŅŒ Ņ–ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи Ņ‚Đ° ĐŋаĐŋĐēи ĐŋŅ–Đ´ Ņ‡Đ°Ņ ҁĐēаĐŊŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžŅ— ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи. ĐĻĐĩ ĐēĐžŅ€Đ¸ŅĐŊĐž, ŅĐēŅ‰Đž ҃ Đ˛Đ°Ņ Ņ” ĐŋаĐŋĐēи, ŅĐēŅ– ĐŧŅ–ŅŅ‚ŅŅ‚ŅŒ Ņ„Đ°ĐšĐģи, ŅĐēŅ– ви ĐŊĐĩ Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Ņ–ĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸, ĐŊаĐŋŅ€Đ¸ĐēĐģад, RAW-Ņ„Đ°ĐšĐģи.", "external_library_management": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ СОвĐŊŅ–ŅˆĐŊŅ–Đŧи ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēаĐŧи", "face_detection": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡Ņ‡Ņ", "face_detection_description": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡ ĐŊа ĐŧĐĩĐ´Ņ–Đ°Ņ„Đ°ĐšĐģĐ°Ņ… Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ. ДĐģŅ Đ˛Ņ–Đ´ĐĩĐž ĐžĐąŅ€ĐžĐąĐģŅŅ”Ņ‚ŅŒŅŅ ĐģĐ¸ŅˆĐĩ ĐĩҁĐēŅ–Đˇ. \"ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸\" ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐžĐąŅ€ĐžĐąĐģŅŅ” Đ˛ŅŅ– Ņ„Đ°ĐšĐģи. \"ĐĄĐēиĐŊŅƒŅ‚Đ¸\" Đ´ĐžĐ´Đ°Ņ‚ĐēОвО ĐžŅ‡Đ¸Ņ‰Đ°Ņ” Đ˛ŅŅ– ĐŋĐžŅ‚ĐžŅ‡ĐŊŅ– даĐŊŅ– ĐŋŅ€Đž ОйĐģĐ¸Ņ‡Ņ‡Ņ. \"Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–\" ŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ ҃ ҇ĐĩŅ€ĐŗŅƒ Ņ„Đ°ĐšĐģи, ŅĐēŅ– ҉Đĩ ĐŊĐĩ ĐąŅƒĐģи ĐžĐąŅ€ĐžĐąĐģĐĩĐŊŅ–. Đ’Đ¸ŅĐ˛ĐģĐĩĐŊŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊŅ– в ҇ĐĩŅ€ĐŗŅƒ Đ´ĐģŅ Ņ€ĐžĐˇĐŋŅ–ĐˇĐŊаваĐŊĐŊŅ ĐŋҖҁĐģŅ СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ, ĐŗŅ€ŅƒĐŋŅƒŅŽŅ‡Đ¸ Ņ—Ņ… ҃ вĐļĐĩ ҖҁĐŊŅƒŅŽŅ‡Đ¸Ņ… айО ĐŊĐžĐ˛Đ¸Ņ… ĐģŅŽĐ´ĐĩĐš.", "facial_recognition_job_description": "Đ“Ņ€ŅƒĐŋŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐ¸Ņ… ОйĐģĐ¸Ņ‡ ҃ ĐģŅŽĐ´ĐĩĐš. ĐĻĐĩĐš ĐēŅ€ĐžĐē виĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ ĐŋҖҁĐģŅ СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡. \"ĐĄĐēиĐŊŅƒŅ‚Đ¸\" ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž ĐēĐģĐ°ŅŅ‚ĐĩŅ€Đ¸ĐˇŅƒŅ” Đ˛ŅŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ. \"Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–\" ŅŅ‚Đ°Đ˛Đ¸Ņ‚ŅŒ ҃ ҇ĐĩŅ€ĐŗŅƒ ОйĐģĐ¸Ņ‡Ņ‡Ņ, ŅĐēиĐŧ ҉Đĩ ĐŊĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž ĐģŅŽĐ´Đ¸ĐŊ҃.", "failed_job_command": "КоĐŧаĐŊда {command} ĐŊĐĩ виĐēĐžĐŊаĐģĐ°ŅŅ Đ´ĐģŅ СавдаĐŊĐŊŅ: {job}", - "force_delete_user_warning": "ĐŸĐžĐŸĐ•Đ Đ•Đ”Đ–Đ•ĐĐĐ¯: ĐĻĐĩ ĐŊĐĩĐŗĐ°ĐšĐŊĐž ĐŋŅ€Đ¸ĐˇĐ˛ĐĩĐ´Đĩ Đ´Đž видаĐģĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° Ņ– Đ˛ŅŅ–Ņ… аĐēŅ‚Đ¸Đ˛Ņ–Đ˛. ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸, Ņ– Ņ„Đ°ĐšĐģи ĐŊĐĩ ĐŧĐžĐļĐŊа ĐąŅƒĐ´Đĩ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸.", + "force_delete_user_warning": "ĐŸĐžĐŸĐ•Đ Đ•Đ”Đ–Đ•ĐĐĐ¯: ĐĻĐĩ ĐŊĐĩĐŗĐ°ĐšĐŊĐž ĐŋŅ€Đ¸ĐˇĐ˛ĐĩĐ´Đĩ Đ´Đž видаĐģĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° Ņ– Đ˛ŅŅ–Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛. ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸, Ņ– Ņ„Đ°ĐšĐģи ĐŊĐĩ ĐŧĐžĐļĐŊа ĐąŅƒĐ´Đĩ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸.", "image_format": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚", "image_format_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ WebP Đ˛Đ¸Ņ€ĐžĐąĐģŅŅ” ĐŧĐĩĐŊŅŒŅˆŅ– Ņ„Đ°ĐšĐģŅ–Đ˛, ĐŊŅ–Đļ JPEG, аĐģĐĩ ĐšĐžĐŗĐž ĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ виĐŧĐ°ĐŗĐ°Ņ” ĐąŅ–ĐģҌ҈Đĩ Ņ‡Đ°ŅŅƒ.", "image_fullsize_description": "ПовĐŊĐžŅ€ĐžĐˇĐŧŅ–Ņ€ĐŊĐĩ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С видаĐģĐĩĐŊиĐŧи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊиĐŧи, ŅĐēŅ– виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅŽŅ‚ŅŒŅŅ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐˇĐąŅ–ĐģҌ҈ĐĩĐŊĐŊŅ", @@ -112,6 +124,13 @@ "logging_enable_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ вĐĩĐ´ĐĩĐŊĐŊŅ ĐļŅƒŅ€ĐŊаĐģ҃", "logging_level_description": "КоĐģи ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž, ŅĐēиК Ņ€Ņ–Đ˛ĐĩĐŊҌ ĐļŅƒŅ€ĐŊаĐģŅŽĐ˛Đ°ĐŊĐŊŅ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸.", "logging_settings": "Đ–ŅƒŅ€ĐŊаĐģŅŽĐ˛Đ°ĐŊĐŊŅ", + "machine_learning_availability_checks": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Ņ–", + "machine_learning_availability_checks_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž Đ˛Đ¸ŅĐ˛ĐģŅŅ‚Đ¸ Ņ‚Đ° ĐŊĐ°Đ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋĐĩŅ€ĐĩĐ˛Đ°ĐŗŅƒ Đ´ĐžŅŅ‚ŅƒĐŋĐŊиĐŧ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°Đŧ ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ", + "machine_learning_availability_checks_enabled": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Ņ–", + "machine_learning_availability_checks_interval": "ІĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи", + "machine_learning_availability_checks_interval_description": "ІĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ҃ ĐŧŅ–ĐģҖҁĐĩĐē҃ĐŊĐ´Đ°Ņ… ĐŧŅ–Đļ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€ĐēаĐŧи Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Ņ–", + "machine_learning_availability_checks_timeout": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ СаĐŋĐ¸Ņ‚Ņƒ", + "machine_learning_availability_checks_timeout_description": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ ҃ ĐŧŅ–ĐģҖҁĐĩĐē҃ĐŊĐ´Đ°Ņ… Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đēи Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžŅŅ‚Ņ–", "machine_learning_clip_model": "МодĐĩĐģҌ CLIP", "machine_learning_clip_model_description": "ІĐŧ'Ņ ОдĐŊҖҔҗ С ĐŧОдĐĩĐģĐĩĐš CLIP, ŅĐēа ĐŋĐĩŅ€ĐĩŅ€Đ°Ņ…ĐžĐ˛Đ°ĐŊа Ņ‚ŅƒŅ‚. Đ—Đ°ŅƒĐ˛Đ°ĐļŅ‚Đĩ, Ņ‰Đž ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž СĐŊĐžĐ˛Ņƒ СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ СавдаĐŊĐŊŅ ÂĢĐ ĐžĐˇŅƒĐŧĐŊиК ĐŋĐžŅˆŅƒĐēÂģ Đ´ĐģŅ Đ˛ŅŅ–Ņ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ ĐŋҖҁĐģŅ СĐŧŅ–ĐŊи ĐŧОдĐĩĐģŅ–.", "machine_learning_duplicate_detection": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛", @@ -166,6 +185,20 @@ "metadata_settings_description": "КĐĩŅ€ŅƒĐš ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐ¸Ņ…", "migration_job": "ĐœŅ–ĐŗŅ€Đ°Ņ†Ņ–Ņ", "migration_job_description": "ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Ņ–Ņ‚ŅŒ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛ Ņ‚Đ° ОйĐģĐ¸Ņ‡Ņ‡Ņ Đ´Đž ĐžĐŊОвĐģĐĩĐŊĐžŅ— ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ¸ ĐŋаĐŋĐžĐē", + "nightly_tasks_cluster_faces_setting_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ Ņ€ĐžĐˇĐŋŅ–ĐˇĐŊаваĐŊĐŊŅ ОйĐģĐ¸Ņ‡ ĐŊа Ņ‰ĐžĐšĐŊĐž Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐ¸Ņ… ОйĐģĐ¸Ņ‡Ņ‡ŅŅ…", + "nightly_tasks_cluster_new_faces_setting": "Đ“Ņ€ŅƒĐŋŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– ОйĐģĐ¸Ņ‡Ņ‡Ņ", + "nightly_tasks_database_cleanup_setting": "ЗавдаĐŊĐŊŅ С ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐŊŅ йаСи даĐŊĐ¸Ņ…", + "nightly_tasks_database_cleanup_setting_description": "ВидаĐģĐĩĐŊĐŊŅ ŅŅ‚Đ°Ņ€Đ¸Ņ… Ņ– ĐŋŅ€ĐžŅŅ‚Ņ€ĐžŅ‡ĐĩĐŊĐ¸Ņ… даĐŊĐ¸Ņ… Ņ–Đˇ йаСи даĐŊĐ¸Ņ…", + "nightly_tasks_generate_memories_setting": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ҁĐŋĐžĐŗĐ°Đ´Đ¸", + "nightly_tasks_generate_memories_setting_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ŅŅ‚Đ˛ĐžŅ€ŅŽĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– ҁĐŋĐžĐŗĐ°Đ´Đ¸ С ĐŊĐ°ŅĐ˛ĐŊĐ¸Ņ… Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž Ņ‰ĐžĐŊĐžŅ‡Ņ–", + "nightly_tasks_missing_thumbnails_setting": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ– ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸", + "nightly_tasks_missing_thumbnails_setting_description": "ЧĐĩŅ€ĐŗĐ° Đ´ĐģŅ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ Đ´ĐģŅ Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ĐąĐĩС ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€", + "nightly_tasks_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊҖ҇ĐŊĐ¸Ņ… СавдаĐŊҌ", + "nightly_tasks_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊҖ҇ĐŊиĐŧи СавдаĐŊĐŊŅĐŧи", + "nightly_tasks_start_time_setting": "Đ§Đ°Ņ ĐŋĐžŅ‡Đ°Ņ‚Đē҃", + "nightly_tasks_start_time_setting_description": "Đ§Đ°Ņ, ĐēĐžĐģи ҁĐĩŅ€Đ˛ĐĩŅ€ ĐŋĐžŅ‡Đ¸ĐŊĐ°Ņ” виĐēĐžĐŊŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊҖ҇ĐŊŅ– СавдаĐŊĐŊŅ", + "nightly_tasks_sync_quota_usage_setting": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ ĐēĐ˛ĐžŅ‚Đ¸", + "nightly_tasks_sync_quota_usage_setting_description": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐēĐ˛ĐžŅ‚Ņƒ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŊа ĐžŅĐŊĐžĐ˛Ņ– ĐŋĐžŅ‚ĐžŅ‡ĐŊĐžĐŗĐž виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "no_paths_added": "ШĐģŅŅ…Đ¸ ĐŊĐĩ дОдаĐŊĐž", "no_pattern_added": "ШайĐģĐžĐŊ ĐŊĐĩ дОдаĐŊĐž", "note_apply_storage_label_previous_assets": "ĐŸŅ€Đ¸ĐŧŅ–Ņ‚Đēа: ЊОй ĐˇĐ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧŅ–Ņ‚Đē҃ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ Đ´Đž Ņ€Đ°ĐŊŅ–ŅˆĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛, СаĐŋŅƒŅŅ‚Ņ–Ņ‚ŅŒ", @@ -196,6 +229,8 @@ "oauth_mobile_redirect_uri": "URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "oauth_mobile_redirect_uri_override": "ПĐĩŅ€ĐĩвиСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ URI ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŊаĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "oauth_mobile_redirect_uri_override_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸, ŅĐēŅ‰Đž OAuth-ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€ ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” ĐŧĐžĐąŅ–ĐģҌĐŊиК URI, ŅĐē ''{callback}''", + "oauth_role_claim": "ĐŅ‚Ņ€Đ¸ĐąŅƒŅ‚ Ņ€ĐžĐģŅ–", + "oauth_role_claim_description": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŊĐ°Đ´Đ°Đ˛Đ°Ņ‚Đ¸ ĐŋŅ€Đ°Đ˛Đ° адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ĐŊа ĐžŅĐŊĐžĐ˛Ņ– ĐŊĐ°ŅĐ˛ĐŊĐžŅŅ‚Ņ– Ņ†ŅŒĐžĐŗĐž Đ°Ņ‚Ņ€Đ¸ĐąŅƒŅ‚Ņƒ. ĐĻĐĩĐš Đ°Ņ‚Ņ€Đ¸ĐąŅƒŅ‚ ĐŧĐžĐļĐĩ ĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ‘user’ айО ‘admin’.", "oauth_settings": "OAuth", "oauth_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ˛Ņ…ĐžĐ´Ņƒ ҇ĐĩŅ€ĐĩС OAuth", "oauth_settings_more_details": "ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛ĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ—.", @@ -231,10 +266,10 @@ "server_welcome_message": "Đ’Ņ–Ņ‚Đ°ĐģҌĐŊĐĩ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ", "server_welcome_message_description": "ĐŸĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ, ŅĐēĐĩ Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ”Ņ‚ŅŒŅŅ ĐŊа ŅŅ‚ĐžŅ€Ņ–ĐŊ҆Җ Đ˛Ņ…ĐžĐ´Ņƒ.", "sidecar_job": "МĐĩŅ‚Đ°Đ´Đ°ĐŊŅ– С sidecar-Ņ„Đ°ĐšĐģŅ–Đ˛", - "sidecar_job_description": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ айО ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐ¸Ņ… Đ´ĐžĐ´Đ°Ņ‚ĐēŅ–Đ˛ С Ņ„Đ°ĐšĐģĐžĐ˛ĐžŅ— ŅĐ¸ŅŅ‚ĐĩĐŧи", + "sidecar_job_description": "ĐŸĐžŅˆŅƒĐē айО ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ ŅĐ°ĐšĐ´ĐēĐ°Ņ€-ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐ¸Ņ… С Ņ„Đ°ĐšĐģĐžĐ˛ĐžŅ— ŅĐ¸ŅŅ‚ĐĩĐŧи", "slideshow_duration_description": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ҁĐĩĐē҃ĐŊĐ´ Đ´ĐģŅ Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐēĐžĐļĐŊĐžĐŗĐž ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "smart_search_job_description": "ЗаĐŋ҃ҁĐē ĐŧĐ°ŅˆĐ¸ĐŊĐŊĐžĐŗĐž ĐŊĐ°Đ˛Ņ‡Đ°ĐŊĐŊŅ Đ´ĐģŅ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛ Đ´ĐģŅ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēи Ņ€ĐžĐˇŅƒĐŧĐŊĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃", - "storage_template_date_time_description": "ПозĐŊĐ°Ņ‡Đēа Ņ‡Đ°ŅŅƒ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ аĐēŅ‚Đ¸Đ˛Ņƒ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ Đ´ĐģŅ Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Đ´Đ°Ņ‚Ņƒ Đš Ņ‡Đ°Ņ", + "storage_template_date_time_description": "ПозĐŊĐ°Ņ‡Đēа Ņ‡Đ°ŅŅƒ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ Ņ€ĐĩŅŅƒŅ€ŅŅƒ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ Đ´ĐģŅ Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Đ´Đ°Ņ‚Ņƒ Đš Ņ‡Đ°Ņ", "storage_template_date_time_sample": "Đ§Đ°Ņ Đ˛Đ¸ĐąŅ–Ņ€Đēи {date}", "storage_template_enable_description": "Đ’Đ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ĐŧĐĩŅ…Đ°ĐŊŅ–ĐˇĐŧ ŅˆĐ°ĐąĐģĐžĐŊŅ–Đ˛ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "storage_template_hash_verification_enabled": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃ Ņ…Đĩ҈҃", @@ -244,6 +279,7 @@ "storage_template_migration_info": "ШайĐģĐžĐŊ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ ĐēĐžĐŊвĐĩŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ĐŧĐĩ Đ˛ŅŅ– Ņ€ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ ҃ ĐŊиĐļĐŊŅ–Đš Ņ€ĐĩĐŗŅ–ŅŅ‚Ņ€. ЗĐŧŅ–ĐŊи ŅˆĐ°ĐąĐģĐžĐŊ҃ ĐˇĐ°ŅŅ‚ĐžŅĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ĐŧŅƒŅ‚ŅŒŅŅ ĐģĐ¸ŅˆĐĩ Đ´Đž ĐŊĐžĐ˛Đ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛. ЊОй ĐˇĐ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ Đ´Đž Ņ€Đ°ĐŊŅ–ŅˆĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛, СаĐŋŅƒŅŅ‚Ņ–Ņ‚ŅŒ {job}.", "storage_template_migration_job": "ЗавдаĐŊĐŊŅ ĐŧŅ–ĐŗŅ€Đ°Ņ†Ņ–Ņ— ŅˆĐ°ĐąĐģĐžĐŊ҃ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ", "storage_template_more_details": "ДĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ Đ´ĐĩŅ‚Đ°ĐģҌĐŊŅ–ŅˆĐžŅ— Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ, СвĐĩŅ€Ņ‚Đ°ĐšŅ‚ĐĩҁҌ Đ´Đž ШайĐģĐžĐŊ҃ СйĐĩŅ€Ņ–ĐŗĐ°ĐŊĐŊŅ Ņ‚Đ° ĐšĐžĐŗĐž ĐŊĐ°ŅĐģŅ–Đ´ĐēŅ–Đ˛", + "storage_template_onboarding_description_v2": "Đ¯ĐēŅ‰Đž Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž, Ņ„Đ°ĐšĐģи ĐąŅƒĐ´ŅƒŅ‚ŅŒ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž вĐŋĐžŅ€ŅĐ´ĐēĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ŅŅ Са ŅˆĐ°ĐąĐģĐžĐŊĐžĐŧ, виСĐŊĐ°Ņ‡ĐĩĐŊиĐŧ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡ĐĩĐŧ. ДоĐēĐģадĐŊŅ–ŅˆĐĩ Đ´Đ¸Đ˛Ņ–Ņ‚ŅŒŅŅ в Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ—.", "storage_template_path_length": "ĐŸŅ€Đ¸ĐąĐģиСĐŊа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊа дОвĐļиĐŊа ҈ĐģŅŅ…Ņƒ: {length, number}/{limit, number}", "storage_template_settings": "ШайĐģĐžĐŊ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "storage_template_settings_description": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ŅŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€ĐžŅŽ Ņ‚ĐĩĐē Ņ‚Đ° Ņ–ĐŧĐĩĐŊĐĩĐŧ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐžĐŗĐž Ņ„Đ°ĐšĐģ҃", @@ -312,7 +348,7 @@ "transcoding_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ–Đ´ĐĩĐž", "transcoding_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐēŅ– Đ˛Ņ–Đ´ĐĩĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ– ŅĐē Ņ—Ņ… ĐžĐąŅ€ĐžĐąĐģŅŅ‚Đ¸", "transcoding_target_resolution": "Đ ĐžĐˇĐ´Ņ–ĐģҌĐŊа ĐˇĐ´Đ°Ņ‚ĐŊŅ–ŅŅ‚ŅŒ", - "transcoding_target_resolution_description": "Đ’Đ¸Ņ‰Ņ– Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ– ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸ ĐąŅ–ĐģҌ҈Đĩ Đ´ĐĩŅ‚Đ°ĐģĐĩĐš, аĐģĐĩ СаКĐŧĐ°ŅŽŅ‚ŅŒ ĐąŅ–ĐģҌ҈Đĩ Ņ‡Đ°ŅŅƒ ĐŊа ĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ, ĐŧĐ°ŅŽŅ‚ŅŒ ĐąŅ–ĐģŅŒŅˆŅ– Ņ€ĐžĐˇĐŧŅ–Ņ€Đ¸ Ņ„Đ°ĐšĐģŅ–Đ˛ Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ СĐŧĐĩĐŊŅˆĐ¸Ņ‚Đ¸ ŅˆĐ˛Đ¸Đ´ĐēŅ–ŅŅ‚ŅŒ Ņ€ĐžĐąĐžŅ‚Đ¸ Đ´ĐžĐ´Đ°Ņ‚Đē҃.", + "transcoding_target_resolution_description": "Đ’Đ¸Ņ‰Ņ– Ņ€ĐžĐˇĐ´Ņ–ĐģҌĐŊŅ– ĐˇĐ´Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸ ĐąŅ–ĐģҌ҈Đĩ Đ´ĐĩŅ‚Đ°ĐģĐĩĐš, аĐģĐĩ СаКĐŧĐ°ŅŽŅ‚ŅŒ ĐąŅ–ĐģҌ҈Đĩ Ņ‡Đ°ŅŅƒ ĐŊа ĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ, ĐŧĐ°ŅŽŅ‚ŅŒ ĐąŅ–ĐģŅŒŅˆŅ– Ņ€ĐžĐˇĐŧŅ–Ņ€Đ¸ Ņ„Đ°ĐšĐģŅ–Đ˛ Ņ– ĐŧĐžĐļŅƒŅ‚ŅŒ СĐŧĐĩĐŊŅˆĐ¸Ņ‚Đ¸ ŅˆĐ˛Đ¸Đ´ĐēŅ–ŅŅ‚ŅŒ Ņ€ĐžĐąĐžŅ‚Đ¸ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃.", "transcoding_temporal_aq": "ĐĸиĐŧŅ‡Đ°ŅĐžĐ˛Đĩ AQ", "transcoding_temporal_aq_description": "ĐĻĐĩ ĐˇĐ°ŅŅ‚ĐžŅĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ ĐģĐ¸ŅˆĐĩ Đ´Đž NVENC. ĐŸŅ–Đ´Đ˛Đ¸Ņ‰ŅƒŅ” ŅĐēŅ–ŅŅ‚ŅŒ ҁ҆ĐĩĐŊ С вĐĩĐģиĐēĐžŅŽ Đ´ĐĩŅ‚Đ°ĐģŅ–ĐˇĐ°Ņ†Ņ–Ņ”ŅŽ Ņ‚Đ° ĐŊĐ¸ĐˇŅŒĐēиĐŧ Ņ€ŅƒŅ…ĐžĐŧ. МоĐļĐĩ ĐąŅƒŅ‚Đ¸ ĐŊĐĩҁ҃ĐŧҖҁĐŊиĐŧ ĐˇŅ– ŅŅ‚Đ°Ņ€Đ¸Đŧи ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи.", "transcoding_threads": "ĐŸĐžŅ‚ĐžĐēи", @@ -325,11 +361,14 @@ "transcoding_two_pass_encoding_setting_description": "ĐĸŅ€Đ°ĐŊҁĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ Са двОĐŧа ĐŋŅ€ĐžŅ…ĐžĐ´Đ°Đŧи Đ´ĐģŅ ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ ĐēŅ€Đ°Ņ‰Đ¸Ņ… СаĐēОдОваĐŊĐ¸Ņ… Đ˛Ņ–Đ´ĐĩĐž. КоĐģи Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК ĐąŅ–Ņ‚Ņ€ĐĩĐšŅ‚ (ĐŊĐĩĐžĐąŅ…Ņ–Đ´ĐŊиК Đ´ĐģŅ Ņ€ĐžĐąĐžŅ‚Đ¸ С H.264 Ņ‚Đ° HEVC), ҆ĐĩĐš Ņ€ĐĩĐļиĐŧ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ” Đ´Ņ–Đ°ĐŋаСОĐŊ ĐąŅ–Ņ‚Ņ€ĐĩĐšŅ‚Ņƒ, ĐˇĐ°ŅĐŊОваĐŊиК ĐŊа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊĐžĐŧ҃ ĐąŅ–Ņ‚Ņ€ĐĩĐšŅ‚Ņ–, Ņ– Ņ–ĐŗĐŊĐžŅ€ŅƒŅ” CRF. ДĐģŅ VP9 ĐŧĐžĐļĐŊа виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ CRF, ŅĐēŅ‰Đž виĐŧĐēĐŊĐĩĐŊĐž ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК ĐąŅ–Ņ‚Ņ€ĐĩĐšŅ‚.", "transcoding_video_codec": "Đ’Ņ–Đ´ĐĩĐžĐēОдĐĩĐē", "transcoding_video_codec_description": "VP9 ĐŧĐ°Ņ” Đ˛Đ¸ŅĐžĐē҃ ĐĩŅ„ĐĩĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ Ņ– ҁ҃ĐŧҖҁĐŊŅ–ŅŅ‚ŅŒ С вĐĩйОĐŧ, аĐģĐĩ ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒŅ” ĐąŅ–ĐģҌ҈Đĩ Ņ‡Đ°ŅŅƒ ĐŊа Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´ŅƒĐ˛Đ°ĐŊĐŊŅ. HEVC ĐŋŅ€Đ°Ņ†ŅŽŅ” ŅŅ…ĐžĐļĐĩ, аĐģĐĩ ĐŧĐ°Ņ” ĐŧĐĩĐŊ҈҃ ҁ҃ĐŧҖҁĐŊŅ–ŅŅ‚ŅŒ С вĐĩйОĐŧ. H.264 ĐŧĐ°Ņ” ŅˆĐ¸Ņ€ĐžĐē҃ ҁ҃ĐŧҖҁĐŊŅ–ŅŅ‚ŅŒ Ņ– ŅˆĐ˛Đ¸Đ´ĐēĐž Ņ‚Ņ€Đ°ĐŊҁĐēĐžĐ´ŅƒŅ”Ņ‚ŅŒŅŅ, аĐģĐĩ ŅŅ‚Đ˛ĐžŅ€ŅŽŅ” СĐŊĐ°Ņ‡ĐŊĐž ĐąŅ–ĐģŅŒŅˆŅ– Ņ„Đ°ĐšĐģи. AV1 - ĐŊаКĐĩŅ„ĐĩĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅˆĐ¸Đš ĐēОдĐĩĐē, аĐģĐĩ ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚ŅŒŅŅ ĐŊа ŅŅ‚Đ°Ņ€Ņ–ŅˆĐ¸Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŅ….", - "trash_enabled_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐŊŅ ҁĐŧŅ–Ņ‚ĐŊиĐēа", + "trash_enabled_description": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐŊŅ ĐēĐžŅˆĐ¸Đēа", "trash_number_of_days": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ Đ´ĐŊŅ–Đ˛", - "trash_number_of_days_description": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ Đ´ĐŊŅ–Đ˛, Ņ‰ĐžĐą СаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҁĐŧŅ–Ņ‚ĐŊиĐē҃ ĐŋĐĩŅ€ĐĩĐ´ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊиĐŧ Ņ—Ņ… видаĐģĐĩĐŊĐŊŅĐŧ", - "trash_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŧŅ–Ņ‚ĐŊиĐēа", - "trash_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ҁĐŧŅ–Ņ‚ĐŊиĐēа", + "trash_number_of_days_description": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ Đ´ĐŊŅ–Đ˛, ĐŋŅ€ĐžŅ‚ŅĐŗĐžĐŧ ŅĐēĐžŅ— СаĐģĐ¸ŅˆĐ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ĐēĐžŅˆĐ¸Đē҃ ĐŋĐĩŅ€ĐĩĐ´ Ņ—Ņ… ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊиĐŧ видаĐģĐĩĐŊĐŊŅĐŧ", + "trash_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐēĐžŅˆĐ¸Đēа", + "trash_settings_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐēĐžŅˆĐ¸Đēа", + "unlink_all_oauth_accounts": "Đ’Ņ–Đ´â€™Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ Đ˛ŅŅ– ОйĐģŅ–ĐēĐžĐ˛Ņ– СаĐŋĐ¸ŅĐ¸ OAuth", + "unlink_all_oauth_accounts_description": "НĐĩ ĐˇĐ°ĐąŅƒĐ´ŅŒŅ‚Đĩ Đ˛Ņ–Đ´â€™Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ Đ˛ŅŅ– ОйĐģŅ–ĐēĐžĐ˛Ņ– СаĐŋĐ¸ŅĐ¸ OAuth ĐŋĐĩŅ€ĐĩĐ´ ĐŋĐĩŅ€ĐĩŅ…ĐžĐ´ĐžĐŧ Đ´Đž ĐŊĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅ‚Đ°Ņ‡Đ°ĐģҌĐŊиĐēа.", + "unlink_all_oauth_accounts_prompt": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Ņ–Đ´â€™Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ Đ˛ŅŅ– ОйĐģŅ–ĐēĐžĐ˛Ņ– СаĐŋĐ¸ŅĐ¸ OAuth? ĐĻĐĩ ҁĐēиĐŊĐĩ Ņ–Đ´ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ĐžŅ€ OAuth Đ´ĐģŅ ĐēĐžĐļĐŊĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°, Ņ– Ņ†ŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ĐąŅƒĐ´Đĩ ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸.", "user_cleanup_job": "ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "user_delete_delay": "АĐēĐ°ŅƒĐŊŅ‚ {user} Ņ– ĐšĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ СаĐŋĐģаĐŊОваĐŊŅ– Đ´ĐģŅ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐžĐŗĐž видаĐģĐĩĐŊĐŊŅ ҇ĐĩŅ€ĐĩС {delay, plural, one {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ–} many {# Đ´ĐŊŅ–Đ˛} other {# Đ´ĐŊŅ–Đ˛}}.", "user_delete_delay_settings": "Đ’Ņ–Đ´ĐēĐģадĐĩĐŊĐĩ видаĐģĐĩĐŊĐŊŅ", @@ -356,13 +395,15 @@ "admin_password": "ĐŸĐ°Ņ€ĐžĐģҌ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", "administration": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ", "advanced": "Đ ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊŅ–", - "advanced_settings_enable_alternate_media_filter_subtitle": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš Đ˛Đ°Ņ€Ņ–Đ°ĐŊŅ‚ Đ´ĐģŅ ҄ҖĐģŅŒŅ‚Ņ€Đ°Ņ†Ņ–Ņ— ĐŧĐĩĐ´Ņ–Đ°Ņ„Đ°ĐšĐģŅ–Đ˛ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— Са аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊиĐŧи ĐēŅ€Đ¸Ņ‚ĐĩŅ€Ņ–ŅĐŧи. ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ҆Đĩ, ŅĐēŅ‰Đž ҃ Đ˛Đ°Ņ виĐŊиĐēĐ°ŅŽŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧи С Ņ‚Đ¸Đŧ, Ņ‰Đž Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŊĐĩ Đ˛Đ¸ŅĐ˛ĐģŅŅ” Đ˛ŅŅ– аĐģŅŒĐąĐžĐŧи.", + "advanced_settings_enable_alternate_media_filter_subtitle": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš Đ˛Đ°Ņ€Ņ–Đ°ĐŊŅ‚ Đ´ĐģŅ ҄ҖĐģŅŒŅ‚Ņ€Đ°Ņ†Ņ–Ņ— ĐŧĐĩĐ´Ņ–Đ°Ņ„Đ°ĐšĐģŅ–Đ˛ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— Са аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊиĐŧи ĐēŅ€Đ¸Ņ‚ĐĩŅ€Ņ–ŅĐŧи. ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ҆Đĩ, ŅĐēŅ‰Đž ҃ Đ˛Đ°Ņ виĐŊиĐēĐ°ŅŽŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧи С Ņ‚Đ¸Đŧ, Ņ‰Đž ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŊĐĩ Đ˛Đ¸ŅĐ˛ĐģŅŅ” Đ˛ŅŅ– аĐģŅŒĐąĐžĐŧи.", "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНĐĸАЛĐŦНИЙ] ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊиК ҄ҖĐģŅŒŅ‚Ņ€ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— аĐģŅŒĐąĐžĐŧŅ–Đ˛ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "advanced_settings_log_level_title": "Đ Ņ–Đ˛ĐĩĐŊҌ ĐģĐžĐŗŅƒĐ˛Đ°ĐŊĐŊŅ: {level}", - "advanced_settings_prefer_remote_subtitle": "ДĐĩŅĐēŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— вĐĩĐģҌĐŧи ĐŋĐžĐ˛Ņ–ĐģҌĐŊĐž СаваĐŊŅ‚Đ°ĐļŅƒŅŽŅ‚ŅŒ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Ņ–Đˇ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—. АĐēŅ‚Đ¸Đ˛ŅƒĐšŅ‚Đĩ Đ´ĐģŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊĐ¸Ņ… ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€ ĐŊĐ°Ņ‚ĐžĐŧŅ–ŅŅ‚ŅŒ.", + "advanced_settings_prefer_remote_subtitle": "ДĐĩŅĐēŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— вĐĩĐģҌĐŧи ĐŋĐžĐ˛Ņ–ĐģҌĐŊĐž СаваĐŊŅ‚Đ°ĐļŅƒŅŽŅ‚ŅŒ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸ Ņ–Đˇ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—. АĐēŅ‚Đ¸Đ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ, Ņ‰ĐžĐą СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ С ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ.", "advanced_settings_prefer_remote_title": "ПĐĩŅ€ĐĩĐ˛Đ°ĐŗĐ° Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊиĐŧ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅĐŧ", "advanced_settings_proxy_headers_subtitle": "ВизĐŊĐ°Ņ‡Ņ‚Đĩ ĐˇĐ°ĐŗĐžĐģОвĐēи ĐŋŅ€ĐžĐēҁҖ-ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, ŅĐēŅ– Immich ĐŧĐ°Ņ” ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚Đ¸ С ĐēĐžĐļĐŊиĐŧ ĐŧĐĩŅ€ĐĩĐļĐĩвиĐŧ СаĐŋĐ¸Ņ‚ĐžĐŧ", "advanced_settings_proxy_headers_title": "ĐŸŅ€ĐžĐēҁҖ-ĐˇĐ°ĐŗĐžĐģОвĐēи", + "advanced_settings_readonly_mode_subtitle": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐŊŅ Ņ€ĐĩĐļиĐŧ҃ ҂ҖĐģҌĐēи Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ, в ŅĐēĐžĐŧ҃ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— ĐŧĐžĐļĐŊа ҂ҖĐģҌĐēи ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Đ°Ņ‚Đ¸, а Ņ‚Đ°ĐēŅ– Ņ„ŅƒĐŊĐē҆Җҗ, ŅĐē Đ˛Đ¸ĐąŅ–Ņ€ Đ´ĐĩĐēŅ–ĐģҌĐēĐžŅ… ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊҌ, ҁĐŋŅ–ĐģҌĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ, ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‡Đ°, видаĐģĐĩĐŊĐŊŅ, виĐŧĐēĐŊĐĩĐŊŅ–. ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐŊŅ/виĐŧĐēĐŊĐĩĐŊĐŊŅ Ņ€ĐĩĐļиĐŧ҃ ҂ҖĐģҌĐēи Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° ĐŊа ĐŗĐžĐģОвĐŊĐžĐŧ҃ ĐĩĐēŅ€Đ°ĐŊŅ–", + "advanced_settings_readonly_mode_title": "Đ ĐĩĐļиĐŧ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ", "advanced_settings_self_signed_ssl_subtitle": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ” ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃ SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°. ĐŸĐžŅ‚Ņ€Ņ–ĐąĐŊĐĩ Đ´ĐģŅ ŅĐ°ĐŧĐžĐŋŅ–Đ´ĐŋĐ¸ŅĐ°ĐŊĐ¸Ņ… ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Ņ–Đ˛.", "advanced_settings_self_signed_ssl_title": "ДозвоĐģĐ¸Ņ‚Đ¸ ŅĐ°ĐŧĐžĐŋŅ–Đ´ĐŋĐ¸ŅĐ°ĐŊŅ– SSL-ҁĐĩŅ€Ņ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚Đ¸", "advanced_settings_sync_remote_deletions_subtitle": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž видаĐģŅŅ‚Đ¸ айО Đ˛Ņ–Đ´ĐŊОвĐģŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ ĐŊа Ņ†ŅŒĐžĐŧ҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—, ĐēĐžĐģи Ņ†Ņ Đ´Ņ–Ņ виĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ в вĐĩĐą-Ņ–ĐŊŅ‚ĐĩҀ҄ĐĩĐšŅŅ–", @@ -378,6 +419,7 @@ "album_cover_updated": "ОбĐēĐģадиĐŊĐēа аĐģŅŒĐąĐžĐŧ҃ ĐžĐŊОвĐģĐĩĐŊа", "album_delete_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ {album}?", "album_delete_confirmation_description": "Đ¯ĐēŅ‰Đž аĐģŅŒĐąĐžĐŧ ĐąŅƒĐ˛ ҁĐŋŅ–ĐģҌĐŊиĐŧ, Ņ–ĐŊŅˆŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ– ĐŊĐĩ СĐŧĐžĐļŅƒŅ‚ŅŒ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐŊŅŒĐžĐŗĐž.", + "album_deleted": "АĐģŅŒĐąĐžĐŧ видаĐģĐĩĐŊĐž", "album_info_card_backup_album_excluded": "Đ’Đ˜Đ›ĐŖĐ§Đ•ĐĐ˜Đ™", "album_info_card_backup_album_included": "ВКЛЮЧЕНИЙ", "album_info_updated": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž аĐģŅŒĐąĐžĐŧ ĐžĐŊОвĐģĐĩĐŊа", @@ -387,7 +429,9 @@ "album_options": "ОĐŋ҆Җҗ аĐģŅŒĐąĐžĐŧ҃", "album_remove_user": "ВидаĐģĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°?", "album_remove_user_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ {user}?", + "album_search_not_found": "АĐģŅŒĐąĐžĐŧŅ–Đ˛, Ņ‰Đž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°ŅŽŅ‚ŅŒ Đ˛Đ°ŅˆĐžĐŧ҃ СаĐŋĐ¸Ņ‚Ņƒ, ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", "album_share_no_users": "ĐĄŅ…ĐžĐļĐĩ, ви ĐŋĐžĐ´Ņ–ĐģиĐģĐ¸ŅŅ Ņ†Đ¸Đŧ аĐģŅŒĐąĐžĐŧĐžĐŧ С ŅƒŅŅ–Đŧа ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи айО ҃ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ĐļОдĐŊĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°, С ŅĐēиĐŧ ĐŧĐžĐļĐŊа ĐąŅƒĐģĐž Đą ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ.", + "album_summary": "ĐšĐžŅ€ĐžŅ‚ĐēиК ĐžĐŋĐ¸Ņ аĐģŅŒĐąĐžĐŧ҃", "album_updated": "АĐģŅŒĐąĐžĐŧ ĐžĐŊОвĐģĐĩĐŊĐž", "album_updated_setting_description": "ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐšŅ‚Đĩ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐŊа ĐĩĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊ҃ ĐŋĐžŅˆŅ‚Ņƒ, ĐēĐžĐģи ҃ ҁĐŋŅ–ĐģҌĐŊĐžĐŧ҃ аĐģŅŒĐąĐžĐŧŅ– С'ŅĐ˛ĐģŅŅŽŅ‚ŅŒŅŅ ĐŊĐžĐ˛Ņ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "album_user_left": "Ви ĐŋĐžĐēиĐŊ҃Đģи {album}", @@ -406,6 +450,7 @@ "albums_default_sort_order": "ĐŸĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧŅ–Đ˛ Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊŅĐŧ", "albums_default_sort_order_description": "ĐŸĐžŅ‡Đ°Ņ‚ĐēОвиК ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ĐŊĐžĐ˛Đ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛.", "albums_feature_description": "КоĐģĐĩĐē҆Җҗ Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛, ŅĐēŅ– ĐŧĐžĐļĐŊа ҁĐŋŅ–ĐģҌĐŊĐž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ С Ņ–ĐŊŅˆĐ¸Đŧи ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи.", + "albums_on_device_count": "АĐģŅŒĐąĐžĐŧи ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ({count})", "all": "ĐŖŅŅ–", "all_albums": "ĐŖŅŅ– аĐģŅŒĐąĐžĐŧи", "all_people": "ĐŖŅŅ– ĐģŅŽĐ´Đ¸", @@ -425,7 +470,9 @@ "app_bar_signout_dialog_title": "Đ’Đ¸ĐšŅ‚Đ¸ С аĐēĐēĐ°ŅƒĐŊŅ‚Đ°", "app_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи", "appears_in": "З'ŅĐ˛ĐģŅŅ”Ņ‚ŅŒŅŅ в", + "apply_count": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸ ({count, number})", "archive": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", + "archive_action_prompt": "{count} дОдаĐŊĐž Đ´Đž Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", "archive_or_unarchive_photo": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ айО Ņ€ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "archive_page_no_archived_assets": "НĐĩĐŧĐ°Ņ” Đ°Ņ€Ņ…Ņ–Đ˛ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", "archive_page_title": "ĐŅ€Ņ…Ņ–Đ˛ ({count})", @@ -449,13 +496,15 @@ "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_subtitle": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ¸ĐŗĐģŅĐ´Ņƒ ҁҖ҂Đēи Ņ„ĐžŅ‚Đž", "asset_list_settings_title": "Đ¤ĐžŅ‚Đž-ҁҖ҂Đēа", - "asset_offline": "АĐēŅ‚Đ¸Đ˛ виĐŧĐēĐŊĐĩĐŊĐž", - "asset_offline_description": "ĐĻĐĩĐš СОвĐŊŅ–ŅˆĐŊŅ–Đš аĐēŅ‚Đ¸Đ˛ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐē҃. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Immich Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ.", + "asset_offline": "Đ ĐĩŅŅƒŅ€Ņ ĐžŅ„ĐģаКĐŊ", + "asset_offline_description": "ĐĻĐĩĐš СОвĐŊŅ–ŅˆĐŊŅ–Đš Ņ€ĐĩŅŅƒŅ€Ņ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐŊа Đ´Đ¸ŅĐē҃. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Immich Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ.", "asset_restored_successfully": "ЕĐģĐĩĐŧĐĩĐŊŅ‚ ҃ҁĐŋŅ–ŅˆĐŊĐž Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž", "asset_skipped": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", - "asset_skipped_in_trash": "ĐŖ ҁĐŧŅ–Ņ‚ĐŊиĐē҃", + "asset_skipped_in_trash": "ĐŖ ĐēĐžŅˆĐ¸Đē҃", + "asset_trashed": "Об'Ņ”ĐēŅ‚ видаĐģĐĩĐŊĐž С ĐēĐžŅˆĐ¸Đēа", + "asset_troubleshoot": "Đ’Đ¸Ņ€Ņ–ŅˆĐĩĐŊĐŊŅ ĐŋŅ€ĐžĐąĐģĐĩĐŧ С аĐēŅ‚Đ¸Đ˛Đ°Đŧи", "asset_uploaded": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž", "asset_uploading": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅâ€Ļ", "asset_viewer_settings_subtitle": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Đ°Ņ‡Đ° ĐŗĐ°ĐģĐĩŅ€ĐĩŅ—", @@ -463,38 +512,44 @@ "assets": "ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "assets_added_count": "ДодаĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_added_to_album_count": "ДодаĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} Đ´Đž аĐģŅŒĐąĐžĐŧ҃", - "assets_added_to_name_count": "ДодаĐŊĐž {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}} Đ´Đž {hasName, select, true {{name}} other {ĐŊĐžĐ˛ĐžĐŗĐž аĐģŅŒĐąĐžĐŧ҃}}", + "assets_added_to_albums_count": "ДодаĐŊĐž {assetTotal, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} other {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸}} Đ´Đž {albumTotal, plural, one {# аĐģŅŒĐąĐžĐŧ} other {# аĐģŅŒĐąĐžĐŧ}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Đ ĐĩŅŅƒŅ€Ņ} other {Đ ĐĩŅŅƒŅ€ŅĐ¸}} ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", + "assets_cannot_be_added_to_albums": "{count, plural, one {ЕĐģĐĩĐŧĐĩĐŊŅ‚} other {ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸}} ĐŊĐĩ ĐŧĐžĐļĐŊа Đ´ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ĐļОдĐŊĐžĐŗĐž С аĐģŅŒĐąĐžĐŧŅ–Đ˛", "assets_count": "{count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_deleted_permanently": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž", "assets_deleted_permanently_from_server": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊĐž ĐŊаСавĐļди С ҁĐĩŅ€Đ˛ĐĩŅ€Đ° Immich", "assets_downloaded_failed": "{count, plural, one {ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģ — {error} Ņ„Đ°ĐšĐģ ĐŊĐĩ вдаĐģĐžŅŅ} other {ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģŅ–Đ˛ — {error} Ņ„Đ°ĐšĐģŅ–Đ˛ ĐŊĐĩ вдаĐģĐžŅŅ}}", "assets_downloaded_successfully": "{count, plural, one {ĐŖŅĐŋŅ–ŅˆĐŊĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģ} other {ĐŖŅĐŋŅ–ŅˆĐŊĐž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž # Ņ„Đ°ĐšĐģŅ–Đ˛}}", - "assets_moved_to_trash_count": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ҃ ҁĐŧŅ–Ņ‚ĐŊиĐē", + "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": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) видаĐģĐĩĐŊŅ– ĐŊаСавĐļди С Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", - "assets_restore_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ŅĐ˛ĐžŅ— аĐēŅ‚Đ¸Đ˛Đ¸ С ҁĐŧŅ–Ņ‚ĐŊиĐēа? ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸! ЗвĐĩŅ€ĐŊŅ–Ņ‚ŅŒ ŅƒĐ˛Đ°ĐŗŅƒ, Ņ‰Đž ĐąŅƒĐ´ŅŒ-ŅĐēŅ– ĐžŅ„ĐģаКĐŊ-аĐēŅ‚Đ¸Đ˛Đ¸ ĐŊĐĩ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊŅ– Ņ‚Đ°ĐēиĐŧ Ņ‡Đ¸ĐŊĐžĐŧ.", + "assets_restore_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ŅĐ˛ĐžŅ— ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ С ĐēĐžŅˆĐ¸Đēа? ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸! ЗвĐĩŅ€ĐŊŅ–Ņ‚ŅŒ ŅƒĐ˛Đ°ĐŗŅƒ, Ņ‰Đž ĐļОдĐŊŅ– ĐžŅ„ĐģаКĐŊ Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐŊĐĩ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊŅ– Ņ‚Đ°ĐēиĐŧ Ņ‡Đ¸ĐŊĐžĐŧ.", "assets_restored_count": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_restored_successfully": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ҃ҁĐŋŅ–ŅˆĐŊĐž Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž", "assets_trashed": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа", - "assets_trashed_count": "ПоĐŧҖ҉ĐĩĐŊĐž в ҁĐŧŅ–Ņ‚ĐŊиĐē {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", + "assets_trashed_count": "ПоĐŧҖ҉ĐĩĐŊĐž в ĐēĐžŅˆĐ¸Đē {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "assets_trashed_from_server": "{count} ĐĩĐģĐĩĐŧĐĩĐŊŅ‚(и) ĐŋĐžĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ Immich", "assets_were_part_of_album_count": "{count, plural, one {Đ ĐĩŅŅƒŅ€Ņ ĐąŅƒĐ˛} few {Đ ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐģи} other {Đ ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐģи}} вĐļĐĩ Ņ‡Đ°ŅŅ‚Đ¸ĐŊĐžŅŽ аĐģŅŒĐąĐžĐŧ҃", + "assets_were_part_of_albums_count": "{count, plural, one {ЕĐģĐĩĐŧĐĩĐŊŅ‚ вĐļĐĩ ĐąŅƒĐ˛} other {ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ вĐļĐĩ ĐąŅƒĐģи}} Ņ‡Đ°ŅŅ‚Đ¸ĐŊĐžŅŽ аĐģŅŒĐąĐžĐŧŅ–Đ˛", "authorized_devices": "ĐĐ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°ĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "automatic_endpoint_switching_subtitle": "ĐŸŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ŅŅ ĐģĐžĐēаĐģҌĐŊĐž ҇ĐĩŅ€ĐĩС СаСĐŊĐ°Ņ‡ĐĩĐŊ҃ Wi-Fi ĐŧĐĩŅ€ĐĩĐļ҃, ĐēĐžĐģи ҆Đĩ ĐŧĐžĐļĐģивО, Ņ– виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊŅ– С'Ņ”Đ´ĐŊаĐŊĐŊŅ в Ņ–ĐŊŅˆĐ¸Ņ… виĐŋадĐēĐ°Ņ…", "automatic_endpoint_switching_title": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ ĐŋĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ URL", "autoplay_slideshow": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Đ˛Ņ–Đ´Ņ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ҁĐģĐ°ĐšĐ´ŅˆĐžŅƒ", "back": "Назад", "back_close_deselect": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅ, СаĐēŅ€Đ¸Ņ‚Đ¸ айО ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€", + "background_backup_running_error": "ĐĐ°Ņ€Đ°ĐˇŅ– виĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ Ņ„ĐžĐŊОвĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ, ĐŊĐĩĐŧĐžĐļĐģивО Ņ€ĐžĐˇĐŋĐžŅ‡Đ°Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ€ŅƒŅ‡ĐŊ҃", "background_location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ Ņ„ĐžĐŊŅ–", "background_location_permission_content": "ЊОй ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ‚Đ¸ ĐŧĐĩŅ€ĐĩĐļŅ– ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–, Immich ĐŧĐ°Ņ” *СавĐļди* ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ— ĐŗĐĩĐžĐģĐžĐēĐ°Ņ†Ņ–Ņ—, Ņ‰ĐžĐą ĐˇŅ‡Đ¸Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", + "background_options": "ĐŸĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ¸ Ņ„ĐžĐŊ҃", + "backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_album_selection_page_albums_device": "АĐģŅŒĐąĐžĐŧи ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ({count})", "backup_album_selection_page_albums_tap": "ĐĸĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ, Ņ‰ĐžĐą вĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸, Đ´Đ˛Ņ–Ņ‡Ņ–, Ņ‰ĐžĐą виĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸", "backup_album_selection_page_assets_scatter": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŧĐžĐļŅƒŅ‚ŅŒ ĐŊаĐģĐĩĐļĐ°Ņ‚Đ¸ Đ´Đž ĐēŅ–ĐģҌĐēĐžŅ… аĐģŅŒĐąĐžĐŧŅ–Đ˛ вОдĐŊĐžŅ‡Đ°Ņ. ĐĸаĐēиĐŧ Ņ‡Đ¸ĐŊĐžĐŧ, аĐģŅŒĐąĐžĐŧи ĐŧĐžĐļŅƒŅ‚ŅŒ ĐąŅƒŅ‚Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊŅ– айО виĐģŅƒŅ‡ĐĩĐŊŅ– ĐŋŅ–Đ´ Ņ‡Đ°Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ.", "backup_album_selection_page_select_albums": "ОбĐĩŅ€Ņ–Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧи", "backup_album_selection_page_selection_info": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž ĐžĐąŅ€Đ°ĐŊĐĩ", "backup_album_selection_page_total_assets": "Đ—Đ°ĐŗĐ°ĐģҌĐŊа ĐēŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ҃ĐŊŅ–ĐēаĐģҌĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", + "backup_albums_sync": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐ¸Ņ… ĐēĐžĐŋŅ–Đš аĐģŅŒĐąĐžĐŧŅ–Đ˛", "backup_all": "ĐŖŅŅ–", "backup_background_service_backup_failed_message": "НĐĩ вдаĐģĐžŅŅ ĐˇŅ€ĐžĐąĐ¸Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽâ€Ļ", "backup_background_service_connection_failed_message": "НĐĩ вдаĐģĐžŅŅ Св'ŅĐˇĐ°Ņ‚Đ¸ŅŅ Ņ–Đˇ ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ. ĐŸĐžĐ˛Ņ‚ĐžŅ€ŅŽŅŽâ€Ļ", @@ -522,7 +577,7 @@ "backup_controller_page_background_wifi": "Đ›Đ¸ŅˆĐĩ ĐŊа Wi-Fi", "backup_controller_page_backup": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_controller_page_backup_selected": "ĐžĐąŅ€Đ°ĐŊĐž: ", - "backup_controller_page_backup_sub": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", + "backup_controller_page_backup_sub": "Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "backup_controller_page_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž: {date}", "backup_controller_page_desc_backup": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŊа ĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŧ҃ ĐŋĐģаĐŊŅ–, Ņ‰ĐžĐą Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€ ĐŋŅ–Đ´ Ņ‡Đ°Ņ Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Ņ‚Ņ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи.", "backup_controller_page_excluded": "ВиĐģŅƒŅ‡ĐĩĐŊĐž: ", @@ -532,25 +587,28 @@ "backup_controller_page_info": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ", "backup_controller_page_none_selected": "ĐŅ–Ņ‡ĐžĐŗĐž ĐŊĐĩ ĐžĐąŅ€Đ°ĐŊĐž", "backup_controller_page_remainder": "ЗаĐģĐ¸ŅˆĐžĐē", - "backup_controller_page_remainder_sub": "Đ ĐĩŅˆŅ‚Đ° СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ С Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ…", + "backup_controller_page_remainder_sub": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž, Ņ‰Đž СаĐģĐ¸ŅˆĐ¸ĐģĐ¸ŅŅ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ С Đ˛Đ¸ĐąŅ€Đ°ĐŊĐžĐŗĐž", "backup_controller_page_server_storage": "ĐĄŅ…ĐžĐ˛Đ¸Ņ‰Đĩ ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "backup_controller_page_start_backup": "ĐŸĐžŅ‡Đ°Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_controller_page_status_off": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ– виĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_status_on": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ– Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", "backup_controller_page_storage_format": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐž: {used} С {total}", "backup_controller_page_to_backup": "АĐģŅŒĐąĐžĐŧи Đ´Đž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", - "backup_controller_page_total_sub": "ĐŖŅŅ– ҃ĐŊŅ–ĐēаĐģҌĐŊŅ– СĐŊŅ–ĐŧĐēи Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž С Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛", + "backup_controller_page_total_sub": "ĐŖŅŅ– ҃ĐŊŅ–ĐēаĐģҌĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž С Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛", "backup_controller_page_turn_off": "ВиĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–", "backup_controller_page_turn_on": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ в аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–", "backup_controller_page_uploading_file_info": "ЗаваĐŊŅ‚Đ°ĐļŅƒŅŽ Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–ŅŽ ĐŋŅ€Đž Ņ„Đ°ĐšĐģ", "backup_err_only_album": "НĐĩ ĐŧĐžĐļ҃ видаĐģĐ¸Ņ‚Đ¸ Ņ”Đ´Đ¸ĐŊиК аĐģŅŒĐąĐžĐŧ", + "backup_error_sync_failed": "ПоĐŧиĐģĐēа ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—. НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐžĐąŅ€ĐžĐąĐ¸Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ.", "backup_info_card_assets": "ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "backup_manual_cancelled": "ĐĄĐēĐ°ŅĐžĐ˛Đ°ĐŊĐž", "backup_manual_in_progress": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ вĐļĐĩ Đ˛Ņ–Đ´ĐąŅƒĐ˛Đ°Ņ”Ņ‚ŅŒŅŅ. ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ ĐˇĐŗĐžĐ´ĐžĐŧ", "backup_manual_success": "ĐŖŅĐŋŅ–Ņ…", "backup_manual_title": "ĐĄŅ‚Đ°ĐŊ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", + "backup_options": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_options_page_title": "Đ ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "backup_setting_subtitle": "ĐŖĐŋŅ€Đ°Đ˛ĐģŅ–ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ‚Đ° аĐēŅ‚Đ¸Đ˛ĐŊĐžĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–", + "backup_settings_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "backward": "Đ—Đ˛ĐžŅ€ĐžŅ‚ĐŊŅ–Đš", "biometric_auth_enabled": "Đ‘Ņ–ĐžĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊа Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊа", "biometric_locked_out": "ВаĐŧ СаĐēŅ€Đ¸Ņ‚Đž Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐąŅ–ĐžĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐžŅ— Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", @@ -564,12 +622,12 @@ "build_image": "ВĐĩŅ€ŅŅ–Ņ ĐˇĐąŅ–Ņ€Đēи", "bulk_delete_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŧĐ°ŅĐžĐ˛Đž видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}? ĐĻĐĩ Đ´Ņ–Ņ СаĐģĐ¸ŅˆĐ¸Ņ‚ŅŒ ĐŊĐ°ĐšĐąŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ ҃ ĐēĐžĐļĐŊŅ–Đš ĐŗŅ€ŅƒĐŋŅ– Ņ– ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ– Ņ–ĐŊŅˆŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸. ĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩĐŧĐžĐļĐģивО ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸!", "bulk_keep_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ СаĐģĐ¸ŅˆĐ¸Ņ‚Đ¸ {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}? ĐĻĐĩ дОСвОĐģĐ¸Ņ‚ŅŒ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŗŅ€ŅƒĐŋи Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛ ĐąĐĩС видаĐģĐĩĐŊĐŊŅ Ņ‡ĐžĐŗĐž-ĐŊĐĩĐąŅƒĐ´ŅŒ.", - "bulk_trash_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐēиĐŊŅƒŅ‚Đ¸ в ҁĐŧŅ–Ņ‚ĐŊиĐē {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ĐŧĐ°ŅĐžĐ˛Đž? ĐĻĐĩ СаĐģĐ¸ŅˆĐ¸Ņ‚ŅŒ ĐŊĐ°ĐšĐąŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ ҃ ĐēĐžĐļĐŊŅ–Đš ĐŗŅ€ŅƒĐŋŅ– Ņ– виĐēиĐŊĐĩ в ҁĐŧŅ–Ņ‚ĐŊиĐē Đ˛ŅŅ– Ņ–ĐŊŅˆŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸.", + "bulk_trash_duplicates_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ виĐēиĐŊŅƒŅ‚Đ¸ в ĐēĐžŅˆĐ¸Đē {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} ĐŧĐ°ŅĐžĐ˛Đž? ĐĻĐĩ СаĐģĐ¸ŅˆĐ¸Ņ‚ŅŒ ĐŊĐ°ĐšĐąŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐĩŅŅƒŅ€Ņ ҃ ĐēĐžĐļĐŊŅ–Đš ĐŗŅ€ŅƒĐŋŅ– Ņ– виĐēиĐŊĐĩ в ĐēĐžŅˆĐ¸Đē Đ˛ŅŅ– Ņ–ĐŊŅˆŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸.", "buy": "ĐŸŅ€Đ¸Đ´ĐąĐ°ĐšŅ‚Đĩ Immich", "cache_settings_clear_cache_button": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐĩ҈", "cache_settings_clear_cache_button_title": "ĐžŅ‡Đ¸Ņ‰Đ°Ņ” ĐēĐĩ҈ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи. ĐĻĐĩ ŅŅƒŅ‚Ņ‚Ņ”Đ˛Đž СĐŊĐ¸ĐˇĐ¸Ņ‚ŅŒ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи, Đ´ĐžĐēи ĐēĐĩ҈ ĐŊĐĩ ĐąŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩĐąŅƒĐ´ĐžĐ˛Đ°ĐŊĐž.", "cache_settings_duplicated_assets_clear_button": "ОЧИСĐĸИĐĸИ", - "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž, СаĐŊĐĩҁĐĩĐŊŅ– Đ´ĐžĐ´Đ°Ņ‚ĐēĐžĐŧ ҃ Ņ‡ĐžŅ€ĐŊиК ҁĐŋĐ¸ŅĐžĐē", + "cache_settings_duplicated_assets_subtitle": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž, ŅĐēŅ– Ņ–ĐŗĐŊĐžŅ€ŅƒŅŽŅ‚ŅŒŅŅ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐēĐžĐŧ", "cache_settings_duplicated_assets_title": "Đ”ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ({count})", "cache_settings_statistics_album": "Đ‘Ņ–ĐąĐģŅ–ĐžŅ‚Đĩ҇ĐŊŅ– ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€Đ¸", "cache_settings_statistics_full": "ПовĐŊĐžŅ€ĐˇĐžĐŧŅ–Ņ€ĐŊŅ– ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", @@ -586,6 +644,7 @@ "cancel": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸", "cancel_search": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐē", "canceled": "ĐĄĐēĐ°ŅĐžĐ˛Đ°ĐŊĐž", + "canceling": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°ĐŊĐŊŅ", "cannot_merge_people": "НĐĩĐŧĐžĐļĐģивО Ой'Ņ”Đ´ĐŊĐ°Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš", "cannot_undo_this_action": "Ви ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚Đĩ ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Ņ†ŅŽ Đ´Ņ–ŅŽ!", "cannot_update_the_description": "НĐĩĐŧĐžĐļĐģивО ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", @@ -608,15 +667,18 @@ "change_pin_code": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ PIN-ĐēОд", "change_your_password": "ЗĐŧŅ–ĐŊŅ–Ņ‚ŅŒ ŅĐ˛Ņ–Đš ĐŋĐ°Ņ€ĐžĐģҌ", "changed_visibility_successfully": "ВидиĐŧŅ–ŅŅ‚ŅŒ ҃ҁĐŋŅ–ŅˆĐŊĐž СĐŧŅ–ĐŊĐĩĐŊĐž", - "check_corrupt_asset_backup": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸ ĐŊа ĐŋĐžŅˆĐēОдĐļĐĩĐŊŅ– Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— аĐēŅ‚Đ¸Đ˛Ņ–Đ˛", + "charging": "Đ—Đ°Ņ€ŅĐ´Đēа", + "charging_requirement_mobile_backup": "ДĐģŅ Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ĐŋОвиĐŊĐĩĐŊ ĐˇĐ°Ņ€ŅĐ´ĐļĐ°Ņ‚Đ¸ŅŅ", + "check_corrupt_asset_backup": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸ ĐŊа ĐŋĐžŅˆĐēОдĐļĐĩĐŊŅ– Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛", "check_corrupt_asset_backup_button": "ВиĐēĐžĐŊĐ°Ņ‚Đ¸ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃", - "check_corrupt_asset_backup_description": "ЗаĐŋŅƒŅŅ‚Ņ–Ņ‚ŅŒ Ņ†ŅŽ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃ ĐģĐ¸ŅˆĐĩ ҇ĐĩŅ€ĐĩС Wi-Fi Ņ‚Đ° ĐŋҖҁĐģŅ Ņ‚ĐžĐŗĐž, ŅĐē Đ˛ŅŅ– аĐēŅ‚Đ¸Đ˛Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€. ĐŸŅ€ĐžŅ†Đĩҁ ĐŧĐžĐļĐĩ СаКĐŊŅŅ‚Đ¸ ĐēŅ–ĐģҌĐēа Ņ…Đ˛Đ¸ĐģиĐŊ.", + "check_corrupt_asset_backup_description": "ЗаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ Ņ†ŅŽ ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃ ĐģĐ¸ŅˆĐĩ ҇ĐĩŅ€ĐĩС Wi-Fi Ņ‚Đ° ĐŋҖҁĐģŅ Ņ‚ĐžĐŗĐž, ŅĐē Đ˛ŅŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€. ĐŸŅ€ĐžŅ†Đĩҁ ĐŧĐžĐļĐĩ СаКĐŊŅŅ‚Đ¸ ĐēŅ–ĐģҌĐēа Ņ…Đ˛Đ¸ĐģиĐŊ.", "check_logs": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸ ĐļŅƒŅ€ĐŊаĐģи", "choose_matching_people_to_merge": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš Đ´ĐģŅ Ой'Ņ”Đ´ĐŊаĐŊĐŊŅ", "city": "ĐœŅ–ŅŅ‚Đž", "clear": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸", "clear_all": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅĐĩ", "clear_all_recent_searches": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐžŅŅ‚Đ°ĐŊĐŊŅ– ĐŋĐžŅˆŅƒĐēĐžĐ˛Ņ– СаĐŋĐ¸Ņ‚Đ¸", + "clear_file_cache": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐĩ҈ Ņ„Đ°ĐšĐģŅ–Đ˛", "clear_message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ", "clear_value": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ", "client_cert_dialog_msg_confirm": "ОĐē", @@ -642,7 +704,7 @@ "completed": "ЗавĐĩŅ€ŅˆĐĩĐŊĐž", "confirm": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Ņ–Ņ‚ŅŒ", "confirm_admin_password": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°", - "confirm_delete_face": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ОйĐģĐ¸Ņ‡Ņ‡Ņ {name} С аĐēŅ‚Đ¸Đ˛Ņƒ?", + "confirm_delete_face": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ОйĐģĐ¸Ņ‡Ņ‡Ņ {name} С ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņƒ?", "confirm_delete_shared_link": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆Đĩ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ?", "confirm_keep_this_delete_others": "ĐŖŅŅ– Ņ–ĐŊŅˆŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҁ҂ĐĩĐē҃ ĐąŅƒĐ´Đĩ видаĐģĐĩĐŊĐž, ĐžĐēҀҖĐŧ Ņ†ŅŒĐžĐŗĐž Ņ€ĐĩŅŅƒŅ€ŅŅƒ. Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸?", "confirm_new_pin_code": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ĐŊОвиК PIN-ĐēОд", @@ -683,15 +745,17 @@ "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_select_photos": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ЗĐŊŅ–ĐŧĐēи", + "create_shared_album_page_share_select_photos": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", + "create_shared_link": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "create_tag": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ Ņ‚ĐĩĐŗ", "create_tag_description": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŊОвиК Ņ‚ĐĩĐŗ. ДĐģŅ вĐēĐģадĐĩĐŊĐ¸Ņ… Ņ‚ĐĩĐŗŅ–Đ˛ вĐēаĐļŅ–Ņ‚ŅŒ ĐŋОвĐŊиК ҈ĐģŅŅ… Ņ‚ĐĩĐŗĐ°, вĐēĐģŅŽŅ‡Đ°ŅŽŅ‡Đ¸ ҁĐģĐĩŅˆŅ–.", "create_user": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž", "created_at": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž", + "creating_linked_albums": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ĐŋĐžĐ˛â€™ŅĐˇĐ°ĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛...", "crop": "ĐšĐ°Đ´Ņ€ŅƒĐ˛Đ°Ņ‚Đ¸", "curated_object_page_title": "Đ Đĩ҇Җ", "current_device": "ĐŸĐžŅ‚ĐžŅ‡ĐŊиК ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš", @@ -699,10 +763,11 @@ "current_server_address": "ĐŸĐžŅ‚ĐžŅ‡ĐŊа Đ°Đ´Ņ€ĐĩŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "custom_locale": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēиК Ņ€ĐĩĐŗŅ–ĐžĐŊ", "custom_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Đ¸ Ņ‚Đ° Ņ‡Đ¸ŅĐģа С ŅƒŅ€Đ°Ņ…ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ĐŧОви Ņ‚Đ° Ņ€ĐĩĐŗŅ–ĐžĐŊ҃", + "custom_url": "ВĐģĐ°ŅĐŊа URL-Đ°Đ´Ņ€ĐĩŅĐ°", "daily_title_text_date": "Е, МММ Đ´Đ´", "daily_title_text_date_year": "Е, МММ Đ´Đ´, ҀҀҀҀ", - "dark": "ĐĸĐĩĐŧĐŊиК", - "darkTheme": "ПĐĩŅ€ĐĩĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ‚ĐĩĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", + "dark": "ĐĸĐĩĐŧĐŊа", + "dark_theme": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ‚ĐĩĐŧĐŊ҃ Ņ‚ĐĩĐŧ҃", "date_after": "Đ”Đ°Ņ‚Đ° ĐŋҖҁĐģŅ", "date_and_time": "Đ”Đ°Ņ‚Đ° Ņ– Ņ‡Đ°Ņ", "date_before": "Đ”Đ°Ņ‚Đ° Đ´Đž", @@ -710,6 +775,7 @@ "date_of_birth_saved": "Đ”Đ°Ņ‚Đ° ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐž СйĐĩŅ€ĐĩĐļĐĩĐŊа", "date_range": "ĐŸŅ€ĐžĐŧŅ–ĐļĐžĐē Ņ‡Đ°ŅŅƒ", "day": "ДĐĩĐŊҌ", + "days": "ДĐŊŅ–", "deduplicate_all": "ВидаĐģĐ¸Ņ‚Đ¸ Đ˛ŅŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", "deduplication_criteria_1": "РОСĐŧŅ–Ņ€ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ в ĐąĐ°ĐšŅ‚Đ°Ņ…", "deduplication_criteria_2": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ даĐŊĐ¸Ņ… EXIF", @@ -718,6 +784,8 @@ "default_locale": "Đ”Đ°Ņ‚Đ° Ņ– Ņ‡Đ°Ņ Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ", "default_locale_description": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Đ¸ Ņ‚Đ° Ņ‡Đ¸ŅĐģа С ŅƒŅ€Đ°Ņ…ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ĐŧОви Đ˛Đ°ŅˆĐžĐŗĐž ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°", "delete": "ВидаĐģĐ¸Ņ‚Đ¸", + "delete_action_confirmation_message": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆ĐĩĐš Ņ„Đ°ĐšĐģ? Đ™ĐžĐŗĐž ĐąŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ, а Ņ‚Đ°ĐēĐžĐļ СĘŧŅĐ˛Đ¸Ņ‚ŅŒŅŅ СаĐŋĐ¸Ņ‚ ĐŊа ĐšĐžĐŗĐž видаĐģĐĩĐŊĐŊŅ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", + "delete_action_prompt": "{count} видаĐģĐĩĐŊĐž", "delete_album": "ВидаĐģĐ¸Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ", "delete_api_key_prompt": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆ĐĩĐš ĐēĐģŅŽŅ‡ API?", "delete_dialog_alert": "ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ Immich Ņ‚Đ° Đ˛Đ°ŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", @@ -731,24 +799,28 @@ "delete_key": "ВидаĐģĐ¸Ņ‚Đ¸ ĐēĐģŅŽŅ‡", "delete_library": "ВидаĐģĐ¸Ņ‚Đ¸ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃", "delete_link": "ВидаĐģĐ¸Ņ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", + "delete_local_action_prompt": "{count} видаĐģĐĩĐŊĐž С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ", "delete_local_dialog_ok_backed_up_only": "ВидаĐģĐ¸Ņ‚Đ¸ ĐģĐ¸ŅˆĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ—", "delete_local_dialog_ok_force": "Đ’ŅĐĩ ОдĐŊĐž видаĐģĐ¸Ņ‚Đ¸", "delete_others": "ВидаĐģĐ¸Ņ‚Đ¸ Ņ–ĐŊŅˆŅ–", + "delete_permanently": "ВидаĐģĐ¸Ņ‚Đ¸ ĐŊаСавĐļди", + "delete_permanently_action_prompt": "{count} видаĐģĐĩĐŊĐž ĐŊаСавĐļди", "delete_shared_link": "ВидаĐģĐ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "delete_shared_link_dialog_title": "ВидаĐģĐ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊĐĩ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", - "delete_tag": "ВидаĐģĐ¸Ņ‚Đ¸ Ņ‚ĐĩĐŗ", + "delete_tag": "ВидаĐģĐ¸Ņ‚Đ¸ ĐĸĐĩĐŗ", "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": "ПоĐŧиĐģĐēа ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐžĐŋĐ¸ŅŅƒ, ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Ņ‚Đĩ ĐģĐžĐŗĐ¸ Đ´ĐģŅ ĐŋĐžĐ´Ņ€ĐžĐąĐ¸Ņ†ŅŒ", + "deselect_all": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€ ŅƒŅŅ–Ņ…", "details": "ПОДРОБИĐĻІ", "direction": "НаĐŋŅ€ŅĐŧ", "disabled": "ВиĐŧĐēĐŊĐĩĐŊĐž", "disallow_edits": "Đ—Đ°ĐąĐžŅ€ĐžĐŊĐ¸Ņ‚Đ¸ Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°ĐŊĐŊŅ", - "discord": "Discord", + "discord": "Discord'", "discover": "Đ’Đ¸ŅĐ˛Đ¸Ņ‚Đ¸", "discovered_devices": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "dismiss_all_errors": "ĐŸŅ€ĐžĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ĐŋĐžĐŧиĐģĐēи", @@ -761,6 +833,7 @@ "documentation": "ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ", "done": "Đ“ĐžŅ‚ĐžĐ˛Đž", "download": "ĐĄĐēĐ°Ņ‡Đ°Ņ‚Đ¸", + "download_action_prompt": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ {count} Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "download_canceled": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ҁĐēĐ°ŅĐžĐ˛Đ°ĐŊĐž", "download_complete": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СаĐēŅ–ĐŊ҇ĐĩĐŊĐž", "download_enqueue": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐžŅŅ‚Đ°Đ˛ĐģĐĩĐŊĐž в ҇ĐĩŅ€ĐŗŅƒ", @@ -787,8 +860,12 @@ "edit": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸", "edit_album": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ", "edit_avatar": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ°Đ˛Đ°Ņ‚Đ°Ņ€", + "edit_birthday": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ ĐŊĐ°Ņ€ĐžĐ´ĐļĐĩĐŊĐŊŅ", "edit_date": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ", "edit_date_and_time": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ Ņ‚Đ° Ņ‡Đ°Ņ", + "edit_date_and_time_action_prompt": "{count} Đ´Đ°Ņ‚Ņƒ Ņ‚Đ° Ņ‡Đ°Ņ СĐŧŅ–ĐŊĐĩĐŊĐž", + "edit_date_and_time_by_offset": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ Са СĐŧҖ҉ĐĩĐŊĐŊŅĐŧ", + "edit_date_and_time_by_offset_interval": "Новий Đ´Ņ–Đ°ĐŋаСОĐŊ Đ´Đ°Ņ‚: {from} - {to}", "edit_description": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ", "edit_description_prompt": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, вийĐĩŅ€Ņ–Ņ‚ŅŒ ĐŊОвиК ĐžĐŋĐ¸Ņ:", "edit_exclusion_pattern": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ виĐēĐģŅŽŅ‡ĐĩĐŊҌ", @@ -798,6 +875,7 @@ "edit_key": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐēĐģŅŽŅ‡", "edit_link": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "edit_location": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", + "edit_location_action_prompt": "ЗĐŧŅ–ĐŊĐĩĐŊĐž ĐŧŅ–ŅŅ†ŅŒ СКОĐŧĐēи: {count}", "edit_location_dialog_title": "ĐœŅ–ŅŅ†ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "edit_name": "Đ’Ņ–Đ´Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Ņ–Đŧ'Ņ", "edit_people": "Đ ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš", @@ -813,9 +891,10 @@ "email": "ЕĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊа ĐŋĐžŅˆŅ‚Đ°", "email_notifications": "ĐĄĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ ĐĩĐģ. ĐŋĐžŅˆŅ‚ĐžŅŽ", "empty_folder": "ĐĻŅ ĐŋаĐŋĐēа ĐŋĐžŅ€ĐžĐļĐŊŅ", - "empty_trash": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ҁĐŧŅ–Ņ‚ĐŊиĐē", - "empty_trash_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ҁĐŧŅ–Ņ‚ĐŊиĐē? ĐĻĐĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ҁĐŧŅ–Ņ‚ĐŊиĐē҃ С Immich.\nĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸!", + "empty_trash": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐžŅˆĐ¸Đē", + "empty_trash_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐžŅˆĐ¸Đē? ĐĻĐĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐ¸Ņ‚ŅŒ Đ˛ŅŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ в ĐēĐžŅˆĐ¸Đē҃ С Immich.\nĐĻŅŽ Đ´Ņ–ŅŽ ĐŊĐĩ ĐŧĐžĐļĐŊа ҁĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸!", "enable": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸", + "enable_backup": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", "enable_biometric_auth_description": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ŅĐ˛Ņ–Đš PIN-ĐēОд, Ņ‰ĐžĐą ŅƒĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ĐąŅ–ĐžĐŧĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊ҃ Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–ŅŽ", "enabled": "ĐŖĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", "end_date": "Đ”Đ°Ņ‚Đ° СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ", @@ -825,8 +904,10 @@ "enter_your_pin_code_subtitle": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ŅĐ˛Ņ–Đš PIN-ĐēОд, Ņ‰ĐžĐą ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи", "error": "ПоĐŧиĐģĐēа", "error_change_sort_album": "НĐĩ вдаĐģĐžŅŅ СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐžŅ€ŅĐ´ĐžĐē ŅĐžŅ€Ņ‚ŅƒĐ˛Đ°ĐŊĐŊŅ аĐģŅŒĐąĐžĐŧ҃", - "error_delete_face": "ПоĐŧиĐģĐēа ĐŋŅ€Đ¸ видаĐģĐĩĐŊĐŊŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ С аĐēŅ‚Đ¸Đ˛Ņƒ", + "error_delete_face": "ПоĐŧиĐģĐēа ĐŋŅ€Đ¸ видаĐģĐĩĐŊĐŊŅ– ОйĐģĐ¸Ņ‡Ņ‡Ņ С ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņƒ", + "error_getting_places": "ПоĐŧиĐģĐēа ĐžŅ‚Ņ€Đ¸ĐŧаĐŊĐŊŅ ĐŧŅ–ŅŅ†ŅŒ", "error_loading_image": "ПоĐŧиĐģĐēа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", + "error_loading_partners": "ПоĐŧиĐģĐēа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Ņ–Đ˛: {error}", "error_saving_image": "ПоĐŧиĐģĐēа: {error}", "error_tag_face_bounding_box": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋОСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡Ņ‡Ņ – ĐŊĐĩ вдаĐģĐžŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸ Ņ€Đ°ĐŧĐēи", "error_title": "ПоĐŧиĐģĐēа: Ņ‰ĐžŅŅŒ ĐŋŅ–ŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", @@ -859,6 +940,7 @@ "failed_to_load_notifications": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", "failed_to_load_people": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš", "failed_to_remove_product_key": "НĐĩ вдаĐģĐžŅŅ видаĐģĐ¸Ņ‚Đ¸ ĐēĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Ņƒ", + "failed_to_reset_pin_code": "НĐĩ вдаĐģĐžŅŅ ҁĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд", "failed_to_stack_assets": "НĐĩ вдаĐģĐžŅŅ ĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_unstack_assets": "НĐĩ вдаĐģĐžŅŅ Ņ€ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_update_notification_status": "НĐĩ вдаĐģĐžŅŅ ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅŅ‚Đ°Ņ‚ŅƒŅ ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ", @@ -867,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {# ҈ĐģŅŅ…} few {# ҈ĐģŅŅ…Đ¸} many {# ҈ĐģŅŅ…Ņ–Đ˛} other {# ҈ĐģŅŅ…Ņƒ}} ĐŊĐĩ ĐŋŅ€ĐžĐšŅˆĐģĐž ĐŋĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đē҃", "profile_picture_transparent_pixels": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ ĐŋŅ€ĐžĐˇĐžŅ€Đ¸Ņ… ĐŋŅ–ĐēҁĐĩĐģŅ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐˇĐąŅ–ĐģŅŒŅˆŅ–Ņ‚ŅŒ ĐŧĐ°ŅŅˆŅ‚Đ°Đą Ņ‚Đ°/айО ĐŋĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Ņ–Ņ‚ŅŒ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ.", "quota_higher_than_disk_size": "Ви Đ˛ŅŅ‚Đ°ĐŊОвиĐģи ĐēĐ˛ĐžŅ‚Ņƒ, Ņ‰Đž ĐŋĐĩŅ€ĐĩĐ˛Đ¸Ņ‰ŅƒŅ” Ņ€ĐžĐˇĐŧŅ–Ņ€ Đ´Đ¸ŅĐēа", + "something_went_wrong": "ĐŠĐžŅŅŒ ĐŋŅ–ŅˆĐģĐž ĐŊĐĩ Ņ‚Đ°Đē", "unable_to_add_album_users": "НĐĩĐŧĐžĐļĐģивО Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛ Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "unable_to_add_assets_to_shared_link": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ Đ´ĐžĐ´Đ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸ Đ´Đž ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "unable_to_add_comment": "НĐĩĐŧĐžĐļĐģивО Đ´ĐžĐ´Đ°Ņ‚Đ¸ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€", @@ -900,7 +983,7 @@ "unable_to_download_files": "НĐĩĐŧĐžĐļĐģивО СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ„Đ°ĐšĐģи", "unable_to_edit_exclusion_pattern": "НĐĩ вдаĐģĐžŅŅ Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ŅˆĐ°ĐąĐģĐžĐŊ виĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ", "unable_to_edit_import_path": "НĐĩĐŧĐžĐļĐģивО Đ˛Ņ–Đ´Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ҈ĐģŅŅ… Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ", - "unable_to_empty_trash": "НĐĩĐŧĐžĐļĐģивО ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ҁĐŧŅ–Ņ‚ĐŊиĐē", + "unable_to_empty_trash": "НĐĩĐŧĐžĐļĐģивО ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐžŅˆĐ¸Đē", "unable_to_enter_fullscreen": "НĐĩĐŧĐžĐļĐģивО ŅƒĐ˛Ņ–ĐšŅ‚Đ¸ в ĐŋОвĐŊĐžĐĩĐēŅ€Đ°ĐŊĐŊиК Ņ€ĐĩĐļиĐŧ", "unable_to_exit_fullscreen": "НĐĩĐŧĐžĐļĐģивО Đ˛Đ¸ĐšŅ‚Đ¸ С ĐŋОвĐŊĐžĐĩĐēŅ€Đ°ĐŊĐŊĐžĐŗĐž Ņ€ĐĩĐļиĐŧ҃", "unable_to_get_comments_number": "НĐĩ вдаĐģĐžŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐēŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ–Đ˛", @@ -924,7 +1007,7 @@ "unable_to_reset_password": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ҁĐēиĐŊŅƒŅ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "unable_to_reset_pin_code": "НĐĩĐŧĐžĐļĐģивО ҁĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд", "unable_to_resolve_duplicate": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ Đ˛Đ¸Ņ€Ņ–ŅˆĐ¸Ņ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚", - "unable_to_restore_assets": "НĐĩĐŧĐžĐļĐģивО Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ аĐēŅ‚Đ¸Đ˛Đ¸", + "unable_to_restore_assets": "НĐĩĐŧĐžĐļĐģивО Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "unable_to_restore_trash": "НĐĩ вдаĐģĐžŅŅ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ вĐŧҖҁ҂", "unable_to_restore_user": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "unable_to_save_album": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ СйĐĩŅ€ĐĩĐŗŅ‚Đ¸ аĐģŅŒĐąĐžĐŧ", @@ -938,7 +1021,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": "НĐĩĐŧĐžĐļĐģивО ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ОйĐēĐģадиĐŊĐē҃ аĐģŅŒĐąĐžĐŧ҃", @@ -950,19 +1033,17 @@ "unable_to_update_user": "НĐĩĐŧĐžĐļĐģивО ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ даĐŊŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "unable_to_upload_file": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ„Đ°ĐšĐģ" }, - "exif": "Exif", + "exif": "Exif'", "exif_bottom_sheet_description": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐžĐŋĐ¸Ņ...", + "exif_bottom_sheet_description_error": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐžĐŋĐ¸ŅŅƒ", "exif_bottom_sheet_details": "ПОДРОБИĐĻІ", "exif_bottom_sheet_location": "МІСĐĻЕ", "exif_bottom_sheet_people": "ЛЮДИ", "exif_bottom_sheet_person_add_person": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ–Đŧ'Ņ", - "exif_bottom_sheet_person_age_months": "Đ’Ņ–Đē {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", - "exif_bottom_sheet_person_age_year_months": "Đ’Ņ–Đē 1 ҀҖĐē, {months} ĐŧŅ–ŅŅŅ†Ņ–Đ˛", - "exif_bottom_sheet_person_age_years": "Đ’Ņ–Đē {years}", "exit_slideshow": "Đ’Đ¸ĐšŅ‚Đ¸ ĐˇŅ– ҁĐģаКд-ŅˆĐžŅƒ", "expand_all": "Đ ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ Đ˛ŅĐĩ", "experimental_settings_new_asset_list_subtitle": "В Ņ€ĐžĐˇŅ€ĐžĐąŅ†Ņ–", - "experimental_settings_new_asset_list_title": "ЕĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģҌĐŊиК ĐŧаĐēĐĩŅ‚ СĐŊŅ–ĐŧĐēŅ–Đ˛", + "experimental_settings_new_asset_list_title": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ ĐĩĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģҌĐŊ҃ ҁҖ҂Đē҃ Ņ„ĐžŅ‚Đž", "experimental_settings_subtitle": "На вĐģĐ°ŅĐŊиК Ņ€Đ¸ĐˇĐ¸Đē!", "experimental_settings_title": "ЕĐēҁĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģҌĐŊŅ–", "expire_after": "ĐĸĐĩŅ€ĐŧŅ–ĐŊ Đ´Ņ–Ņ— СаĐēŅ–ĐŊŅ‡ŅƒŅ”Ņ‚ŅŒŅŅ ҇ĐĩŅ€ĐĩС", @@ -972,23 +1053,27 @@ "explorer": "ĐŸŅ€ĐžĐ˛Ņ–Đ´ĐŊиĐē", "export": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸", "export_as_json": "ЕĐēҁĐŋĐžŅ€Ņ‚ в JSON", + "export_database": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐąĐ°ĐˇŅƒ даĐŊĐ¸Ņ…", + "export_database_description": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐąĐ°ĐˇŅƒ даĐŊĐ¸Ņ… SQLite", "extension": "Đ ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ", "external": "ЗовĐŊŅ–ŅˆĐŊŅ–", "external_libraries": "ЗовĐŊŅ–ŅˆĐŊŅ– ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи", "external_network": "ЗовĐŊŅ–ŅˆĐŊŅ ĐŧĐĩŅ€ĐĩĐļа", - "external_network_sheet_info": "КоĐģи ви ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž ĐŋĐĩŅ€ĐĩваĐļĐŊĐžŅ— ĐŧĐĩŅ€ĐĩĐļŅ– Wi-Fi, Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ĐŋĐĩŅ€ŅˆŅƒ С ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… ĐŊиĐļ҇Đĩ URL-Đ°Đ´Ņ€Đĩҁ, ŅĐē҃ Đ˛Ņ–ĐŊ СĐŧĐžĐļĐĩ Đ´ĐžŅŅĐŗŅ‚Đ¸, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ СвĐĩŅ€Ņ…Ņƒ вĐŊиС", + "external_network_sheet_info": "КоĐģи ви ĐŊĐĩ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž ĐžĐąŅ€Đ°ĐŊĐžŅ— ĐŧĐĩŅ€ĐĩĐļŅ– Wi-Fi, ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ĐŋĐĩŅ€ŅˆŅƒ С ĐŊавĐĩĐ´ĐĩĐŊĐ¸Ņ… ĐŊиĐļ҇Đĩ URL-Đ°Đ´Ņ€Đĩҁ, ŅĐē҃ Đ˛Ņ–ĐŊ СĐŧĐžĐļĐĩ Đ´ĐžŅŅĐŗŅ‚Đ¸, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ СвĐĩŅ€Ņ…Ņƒ вĐŊиС", "face_unassigned": "НĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž", "failed": "НĐĩ вдаĐģĐžŅŅ", "failed_to_authenticate": "ПоĐŧиĐģĐēа Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—", "failed_to_load_assets": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "failed_to_load_folder": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐŋаĐŋĐē҃", "favorite": "До ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ…", + "favorite_action_prompt": "{count} дОдаĐŊĐž Đ´Đž ĐžĐąŅ€Đ°ĐŊĐžĐŗĐž", "favorite_or_unfavorite_photo": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Đ´Đž ĐžĐąŅ€Đ°ĐŊĐ¸Ņ… айО видаĐģĐ¸Ņ‚Đ¸ С ĐžĐąŅ€Đ°ĐŊĐ¸Ņ… Ņ„ĐžŅ‚Đž", "favorites": "ĐŖĐģŅŽĐąĐģĐĩĐŊŅ–", "favorites_page_no_favorites": "НĐĩĐŧĐ°Ņ” ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", "feature_photo_updated": "Đ’Đ¸ĐąŅ€Đ°ĐŊĐĩ Ņ„ĐžŅ‚Đž ĐžĐŊОвĐģĐĩĐŊĐž", "features": "Đ”ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Ņ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ–", - "features_setting_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēОвиĐŧи ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚ŅĐŧи Đ´ĐžĐ´Đ°Ņ‚Đēа", + "features_in_development": "Đ¤ŅƒĐŊĐē҆Җҗ в Ņ€ĐžĐˇŅ€ĐžĐąŅ†Ņ–", + "features_setting_description": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´ĐžĐ´Đ°Ņ‚ĐēОвиĐŧи ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚ŅĐŧи ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", "file_name": "ІĐŧ'Ņ Ņ„Đ°ĐšĐģ҃", "file_name_or_extension": "ІĐŧ'Ņ Ņ„Đ°ĐšĐģ҃ айО Ņ€ĐžĐˇŅˆĐ¸Ņ€ĐĩĐŊĐŊŅ", "filename": "ІĐŧ'Ņ Ņ„Đ°ĐšĐģ҃", @@ -997,21 +1082,26 @@ "filter_people": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€ ĐŋĐž ĐģŅŽĐ´ŅŅ…", "filter_places": "Đ¤Ņ–ĐģŅŒŅ‚Ņ€ ĐŋĐž ĐŧŅ–ŅŅ†ŅŅ…", "find_them_fast": "ШвидĐēĐž СĐŊĐ°Ņ…ĐžĐ´ŅŒŅ‚Đĩ Ņ—Ņ… Са ĐŊĐ°ĐˇĐ˛ĐžŅŽ Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ ĐŋĐžŅˆŅƒĐē҃", + "first": "ПĐĩŅ€ŅˆĐ¸Đš", "fix_incorrect_match": "ВиĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊиК ĐˇĐąŅ–Đŗ", "folder": "ПаĐŋĐēа", "folder_not_found": "ПаĐŋĐē҃ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", "folders": "ПаĐŋĐēи", "folders_feature_description": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ ĐŋаĐŋĐžĐē Đ´ĐģŅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš Ņ– Đ˛Ņ–Đ´ĐĩĐž ҃ Ņ„Đ°ĐšĐģĐžĐ˛Ņ–Đš ŅĐ¸ŅŅ‚ĐĩĐŧŅ–", + "forgot_pin_code_question": "Đ—Đ°ĐąŅƒĐģи ŅĐ˛Ņ–Đš PIN-ĐēОд?", "forward": "ПĐĩŅ€ĐĩҁĐģĐ°Ņ‚Đ¸", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google Cast'", "gcast_enabled_description": "ĐĻŅ Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ СаваĐŊŅ‚Đ°ĐļŅƒŅ” СОвĐŊŅ–ŅˆĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ С Google Đ´ĐģŅ ŅĐ˛ĐžŅ”Ņ— Ņ€ĐžĐąĐžŅ‚Đ¸.", "general": "Đ—Đ°ĐŗĐ°ĐģҌĐŊŅ–", + "geolocation_instruction_location": "ĐĐ°Ņ‚Đ¸ŅĐŊŅ–Ņ‚ŅŒ ĐŊа Ой'Ņ”ĐēŅ‚ Ņ–Đˇ GPS-ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ°Đŧи, Ņ‰ĐžĐą виĐēĐžŅ€Đ¸ŅŅ‚Đ°Ņ‚Đ¸ ĐšĐžĐŗĐž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ, айО вийĐĩŅ€Ņ–Ņ‚ŅŒ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ĐąĐĩСĐŋĐžŅĐĩŅ€ĐĩĐ´ĐŊŅŒĐž ĐŊа ĐēĐ°Ņ€Ņ‚Ņ–", "get_help": "ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ Đ´ĐžĐŋĐžĐŧĐžĐŗŅƒ", "get_wifiname_error": "НĐĩ вдаĐģĐžŅŅ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi. ПĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ, Ņ‰Đž ви ĐŊадаĐģи ĐŊĐĩĐžĐąŅ…Ņ–Đ´ĐŊŅ– дОСвОĐģи Ņ‚Đ° ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊŅ– Đ´Đž Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", "getting_started": "ĐŸĐžŅ‡Đ°Ņ‚ĐžĐē", "go_back": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅ ĐŊаСад", "go_to_folder": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŋаĐŋĐēи", "go_to_search": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŋĐžŅˆŅƒĐē҃", + "gps": "GPS", + "gps_missing": "НĐĩĐŧĐ°Ņ” GPS", "grant_permission": "ĐĐ°Đ´Đ°Ņ‚Đ¸ Đ´ĐžĐˇĐ˛Ņ–Đģ", "group_albums_by": "Đ“Ņ€ŅƒĐŋŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧи Са...", "group_country": "Đ“Ņ€ŅƒĐŋŅƒĐ˛Đ°Ņ‚Đ¸ Са ĐēŅ€Đ°Ņ—ĐŊĐžŅŽ", @@ -1022,6 +1112,9 @@ "haptic_feedback_switch": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ‚Đ°ĐēŅ‚Đ¸ĐģҌĐŊ҃ Đ˛Ņ–Đ´Đ´Đ°Ņ‡Ņƒ", "haptic_feedback_title": "ĐĸаĐēŅ‚Đ¸ĐģҌĐŊа Đ˛Ņ–Đ´Đ´Đ°Ņ‡Đ°", "has_quota": "ĐšĐ˛ĐžŅ‚Đ°", + "hash_asset": "ГĐĩŅˆŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģ", + "hashed_assets": "ГĐĩŅˆĐžĐ˛Đ°ĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", + "hashing": "ĐĨĐĩŅˆŅƒĐ˛Đ°ĐŊĐŊŅ", "header_settings_add_header_tip": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ ĐˇĐ°ĐŗĐžĐģОвОĐē", "header_settings_field_validator_msg": "ЗĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐąŅƒŅ‚Đ¸ ĐŋĐžŅ€ĐžĐļĐŊŅ–Đŧ", "header_settings_header_name_input": "ІĐŧ'Ņ ĐˇĐ°ĐŗĐžĐģОвĐē҃", @@ -1046,14 +1139,16 @@ "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_locked_error_local": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ ĐģĐžĐēаĐģҌĐŊŅ– Ņ„Đ°ĐšĐģи Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи, ĐŋŅ€ĐžĐŋ҃ҁĐēĐ°Ņ”Ņ‚ŅŒŅŅ", "home_page_locked_error_partner": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ ĐŋĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€ŅŅŒĐēŅ– Ņ„Đ°ĐšĐģи Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи, ĐŋŅ€ĐžĐŋ҃ҁĐēĐ°Ņ”Ņ‚ŅŒŅŅ", "home_page_share_err_local": "НĐĩĐŧĐžĐļĐģивО ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ĐģĐžĐēаĐģҌĐŊиĐŧи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧи ҇ĐĩŅ€ĐĩС ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "home_page_upload_err_limit": "МоĐļĐŊа ваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ĐŊĐĩ ĐąŅ–ĐģҌ҈Đĩ 30 ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ вОдĐŊĐžŅ‡Đ°Ņ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "host": "ĐĨĐžŅŅ‚", "hour": "ГодиĐŊа", + "hours": "ГодиĐŊи", "id": "ID", + "idle": "ĐŸŅ€ĐžŅŅ‚Ņ–Đš", "ignore_icloud_photos": "ĐŸŅ€ĐžĐŋ҃ҁĐēĐ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи С iCloud", "ignore_icloud_photos_description": "НĐĩ СаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ Ņ„Đ°ĐšĐģи в Immich, ŅĐēŅ‰Đž вОĐŊи СйĐĩŅ€Ņ–ĐŗĐ°ŅŽŅ‚ŅŒŅŅ в iCloud", "image": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", @@ -1079,7 +1174,7 @@ "in_archive": "В Đ°Ņ€Ņ…Ņ–Đ˛Ņ–", "include_archived": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ Đ°Ņ€Ņ…Ņ–Đ˛", "include_shared_albums": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊŅ– аĐģŅŒĐąĐžĐŧи", - "include_shared_partner_assets": "ВĐēĐģŅŽŅ‡Đ°ĐšŅ‚Đĩ ҁĐŋŅ–ĐģҌĐŊŅ– ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€ŅŅŒĐēŅ– аĐēŅ‚Đ¸Đ˛Đ¸", + "include_shared_partner_assets": "ВĐēĐģŅŽŅ‡Đ°ĐšŅ‚Đĩ ҁĐŋŅ–ĐģҌĐŊŅ– ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€ŅŅŒĐēŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸", "individual_share": "ІĐŊĐ´Đ¸Đ˛Ņ–Đ´ŅƒĐ°ĐģҌĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ", "individual_shares": "ОĐēŅ€ĐĩĐŧŅ– ҁĐŋŅ–ĐģҌĐŊŅ– Đ´ĐžŅŅ‚ŅƒĐŋи", "info": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ", @@ -1111,10 +1206,13 @@ "language_no_results_title": "Мови ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", "language_search_hint": "ĐŸĐžŅˆŅƒĐē ĐŧОв...", "language_setting_description": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŧĐžĐ˛Ņƒ, ŅĐēŅ–Đš ви ĐŊĐ°Đ´Đ°Ņ”Ņ‚Đĩ ĐŋĐĩŅ€ĐĩĐ˛Đ°ĐŗŅƒ", + "large_files": "ВĐĩĐģиĐēŅ– Ņ„Đ°ĐšĐģи", + "last": "ĐžŅŅ‚Đ°ĐŊĐŊŅ–Đš", "last_seen": "Đ’ĐžŅŅ‚Đ°ĐŊĐŊŅ” ĐąĐ°Ņ‡Đ¸Đģи", "latest_version": "ĐžŅŅ‚Đ°ĐŊĐŊŅ вĐĩŅ€ŅŅ–Ņ", "latitude": "Đ¨Đ¸Ņ€ĐžŅ‚Đ°", "leave": "ПоĐēиĐŊŅƒŅ‚Đ¸", + "leave_album": "Đ’Đ¸ĐšŅ‚Đ¸ С аĐģŅŒĐąĐžĐŧ҃", "lens_model": "МодĐĩĐģҌ Ой'Ņ”ĐēŅ‚Đ¸Đ˛Đ°", "let_others_respond": "ДозвоĐģĐ¸Ņ‚Đ¸ Ņ–ĐŊŅˆĐ¸Đŧ Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°Ņ‚Đ¸", "level": "Đ Ņ–Đ˛ĐĩĐŊҌ", @@ -1126,18 +1224,22 @@ "library_page_sort_created": "НĐĩŅ‰ĐžĐ´Đ°Đ˛ĐŊĐž ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊŅ–", "library_page_sort_last_modified": "ĐžŅŅ‚Đ°ĐŊĐŊŅ СĐŧŅ–ĐŊа", "library_page_sort_title": "Назва аĐģŅŒĐąĐžĐŧ҃", + "licenses": "Đ›Ņ–Ņ†ĐĩĐŊĐˇŅ–Ņ—", "light": "ĐĄĐ˛Ņ–Ņ‚Đģа", + "like": "ĐŸĐžĐ´ĐžĐąĐ°Ņ”Ņ‚ŅŒŅŅ", "like_deleted": "ЛайĐē видаĐģĐĩĐŊĐž", "link_motion_video": "ĐŸĐžŅĐ¸ĐģаĐŊĐŊŅ ĐŊа Ņ€ŅƒŅ…ĐžĐŧĐĩ Đ˛Ņ–Đ´ĐĩĐž", - "link_options": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "link_to_oauth": "ĐŸŅ€Đ¸Ņ”Đ´ĐŊаĐŊĐŊŅ Đ´Đž OAuth", "linked_oauth_account": "ĐŸŅ€Đ¸Ņ”Đ´ĐŊаĐŊиК аĐēĐ°ŅƒĐŊŅ‚ OAuth", "list": "ПĐĩŅ€ĐĩĐģŅ–Đē", "loading": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "loading_search_results_failed": "НĐĩ вдаĐģĐžŅŅ СаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đ¸ ĐŋĐžŅˆŅƒĐē҃", + "local": "На ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", "local_asset_cast_failed": "НĐĩĐŧĐžĐļĐģивО Ņ‚Ņ€Đ°ĐŊҁĐģŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ, ŅĐēиК ĐŊĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐž ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€", + "local_assets": "ЛоĐēаĐģҌĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", + "local_media_summary": "ЗвĐĩĐ´ĐĩĐŊĐŊŅ ĐŧҖҁ҆ĐĩĐ˛Đ¸Ņ… ЗМІ", "local_network": "ЛоĐēаĐģҌĐŊа ĐŧĐĩŅ€ĐĩĐļа", - "local_network_sheet_info": "Đ”ĐžĐ´Đ°Ņ‚ĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ҆ĐĩĐš URL, ĐēĐžĐģи виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ вĐēаСаĐŊа Wi-Fi ĐŧĐĩŅ€ĐĩĐļа", + "local_network_sheet_info": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŋŅ–Đ´ĐēĐģŅŽŅ‡Đ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ Đ´Đž ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ҇ĐĩŅ€ĐĩС ҆ĐĩĐš URL, ĐēĐžĐģи виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ вĐēаСаĐŊа Wi-Fi ĐŧĐĩŅ€ĐĩĐļа", "location_permission": "Đ”ĐžĐˇĐ˛Ņ–Đģ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "location_permission_content": "ЊОй ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ‚Đ¸ ĐŧĐĩŅ€ĐĩĐļŅ– ҃ Ņ„ĐžĐŊОвОĐŧ҃ Ņ€ĐĩĐļиĐŧŅ–, Immich ĐŧĐ°Ņ” СавĐļди ĐŧĐ°Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ‚ĐžŅ‡ĐŊĐžŅ— ĐŗĐĩĐžĐģĐžĐēĐ°Ņ†Ņ–Ņ—, Ņ‰ĐžĐą ĐˇŅ‡Đ¸Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐ°ĐˇĐ˛Ņƒ Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", "location_picker_choose_on_map": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐŊа ĐŧаĐŋŅ–", @@ -1147,6 +1249,7 @@ "location_picker_longitude_hint": "ВĐēаĐļŅ–Ņ‚ŅŒ Đ´ĐžĐ˛ĐŗĐžŅ‚Ņƒ", "lock": "ЗабĐģĐžĐēŅƒĐ˛Đ°Ņ‚Đ¸", "locked_folder": "ĐžŅĐžĐąĐ¸ŅŅ‚Đ° ĐŋаĐŋĐēа", + "log_detail_title": "ДĐĩŅ‚Đ°ĐģŅ– ĐļŅƒŅ€ĐŊаĐģ҃", "log_out": "Đ’Đ¸ĐšŅ‚Đ¸", "log_out_all_devices": "Đ’Đ¸ĐšŅ‚Đ¸ С ŅƒŅŅ–Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—Đ˛", "logged_in_as": "Đ’Ņ…Ņ–Đ´ виĐēĐžĐŊаĐŊĐž ŅĐē {user}", @@ -1157,8 +1260,8 @@ "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", - "login_form_endpoint_url": "ĐĐ´Ņ€ĐĩŅĐ° Ņ‚ĐžŅ‡Đēи Đ´ĐžŅŅƒĐŋ҃ ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ", + "login_form_endpoint_hint": "http://your-server-ip:port'", + "login_form_endpoint_url": "ĐĐ´Ņ€ĐĩŅĐ° ҁĐĩŅ€Đ˛ĐĩŅ€Ņƒ", "login_form_err_http": "ВĐēаĐļŅ–Ņ‚ŅŒ http:// айО https://", "login_form_err_invalid_email": "ĐĨийĐŊиК Ņ–ĐŧĐĩĐšĐģ", "login_form_err_invalid_url": "ĐĨийĐŊиК URL", @@ -1177,6 +1280,7 @@ "login_password_changed_success": "ĐŸĐ°Ņ€ĐžĐģҌ ĐžĐŊОвĐģĐĩĐŊĐž ҃ҁĐŋŅ–ŅˆĐŊĐž", "logout_all_device_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Đ¸ĐšŅ‚Đ¸ С ŅƒŅŅ–Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—Đ˛?", "logout_this_device_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ˛Đ¸ĐšŅ‚Đ¸ С Ņ†ŅŒĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ?", + "logs": "Đ–ŅƒŅ€ĐŊаĐģи", "longitude": "Đ”ĐžĐ˛ĐŗĐžŅ‚Đ°", "look": "Đ”Đ¸Đ˛Đ¸Ņ‚Đ¸ŅŅ", "loop_videos": "ĐĻиĐēĐģҖ҇ĐŊŅ– Đ˛Ņ–Đ´ĐĩĐž", @@ -1184,6 +1288,7 @@ "main_branch_warning": "Ви виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚Đĩ вĐĩŅ€ŅŅ–ŅŽ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐŊиĐēŅ–Đ˛; ĐŊĐ°ŅŅ‚Ņ–ĐšĐŊĐž Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒŅ”ĐŧĐž виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩĐģŅ–ĐˇĐŊ҃ вĐĩŅ€ŅŅ–ŅŽ!", "main_menu": "ГоĐģОвĐŊĐĩ ĐŧĐĩĐŊŅŽ", "make": "Đ’Đ¸Ņ€ĐžĐąĐŊиĐē", + "manage_geolocation": "КĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅĐŧ", "manage_shared_links": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊиĐŧи ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧи", "manage_sharing_with_partners": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ҁĐŋŅ–ĐģҌĐŊиĐŧ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅĐŧ С ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°Đŧи", "manage_the_app_settings": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐŋŅ€ĐžĐŗŅ€Đ°Đŧи", @@ -1192,8 +1297,7 @@ "manage_your_devices": "КĐĩŅ€ŅƒĐšŅ‚Đĩ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи, ŅĐēŅ– ŅƒĐ˛Ņ–ĐšŅˆĐģи в ŅĐ¸ŅŅ‚ĐĩĐŧ҃", "manage_your_oauth_connection": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ–Đ´ĐēĐģŅŽŅ‡ĐĩĐŊĐžĐŗĐž OAuth", "map": "МаĐŋа", - "map_assets_in_bound": "{count} Ņ„ĐžŅ‚Đž", - "map_assets_in_bounds": "{count} Ņ„ĐžŅ‚Đž", + "map_assets_in_bounds": "{count, plural, =0 {НĐĩĐŧĐ°Ņ” Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš ҃ Ņ†Ņ–Đš ĐŧҖҁ҆ĐĩĐ˛ĐžŅŅ‚Ņ–} one {# Ņ„ĐžŅ‚Đž} other {# Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—}}", "map_cannot_get_user_location": "НĐĩ ĐŧĐžĐļ҃ ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "map_location_dialog_yes": "ĐĸаĐē", "map_location_picker_page_use_location": "ĐĻĐĩ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", @@ -1201,7 +1305,6 @@ "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_title": "ПоĐŧиĐģĐēа Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "map_settings": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŧаĐŋи", @@ -1212,14 +1315,15 @@ "map_settings_date_range_option_years": "МиĐŊ҃ĐģŅ– {years} Ņ€ĐžĐēи", "map_settings_dialog_title": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŧаĐŋи", "map_settings_include_show_archived": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ Đ°Ņ€Ņ…Ņ–Đ˛", - "map_settings_include_show_partners": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", + "map_settings_include_show_partners": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž ĐŋĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ°", "map_settings_only_show_favorites": "Đ›Đ¸ŅˆĐĩ ҃ĐģŅŽĐąĐĩĐŊŅ–", "map_settings_theme_settings": "ĐĸĐĩĐŧа ĐēĐ°Ņ€Ņ‚Đ¸", - "map_zoom_to_see_photos": "ЗĐŧĐĩĐŊŅˆŅ–Ņ‚ŅŒ, айи ĐŋĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ СĐŊŅ–ĐŧĐēи", + "map_zoom_to_see_photos": "ЗĐŧĐĩĐŊŅˆŅ‚Đĩ ĐŧĐ°ŅŅˆŅ‚Đ°Đą, Ņ‰ĐžĐą ĐŋĐžĐąĐ°Ņ‡Đ¸Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "mark_all_as_read": "ПозĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ Đ˛ŅŅ– ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊŅ–", "mark_as_read": "ПозĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸ ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊĐĩ", "marked_all_as_read": "ПозĐŊĐ°Ņ‡ĐĩĐŊĐž Đ˛ŅŅ– ŅĐē ĐŋŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐŊŅ–", "matches": "Đ—ĐąŅ–ĐŗĐ¸", + "matching_assets": "Đ’Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´ĐŊŅ– аĐēŅ‚Đ¸Đ˛Đ¸", "media_type": "ĐĸиĐŋ ĐŧĐĩĐ´Ņ–Đ°", "memories": "ĐĄĐŋĐžĐŗĐ°Đ´Đ¸", "memories_all_caught_up": "ĐĻĐĩ Đ˛ŅĐĩ ĐŊа ŅŅŒĐžĐŗĐžĐ´ĐŊŅ–", @@ -1238,6 +1342,7 @@ "merged_people_count": "Об'Ņ”Đ´ĐŊаĐŊĐž {count, plural, one {# ĐžŅĐžĐąĐ°} few {# ĐžŅĐžĐąĐ¸} many {# ĐžŅŅ–Đą} other {# ĐģŅŽĐ´ĐĩĐš}}", "minimize": "ĐœŅ–ĐŊŅ–ĐŧŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸", "minute": "ĐĨвиĐģиĐŊĐē҃", + "minutes": "ĐĨвиĐģиĐŊи", "missing": "Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", "model": "МодĐĩĐģҌ", "month": "ĐœŅ–ŅŅŅ†ŅŒ", @@ -1245,17 +1350,22 @@ "more": "Đ‘Ņ–ĐģҌ҈Đĩ", "move": "ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸", "move_off_locked_folder": "Đ’Đ¸ĐšŅ‚Đ¸ С ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи", + "move_to_lock_folder_action_prompt": "{count} дОдаĐŊĐž Đ´Đž ĐˇĐ°Ņ…Đ¸Ņ‰ĐĩĐŊĐžŅ— Ņ‚ĐĩĐēи", "move_to_locked_folder": "ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи", "move_to_locked_folder_confirmation": "ĐĻŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ĐąŅƒĐ´Đĩ видаĐģĐĩĐŊĐž ĐˇŅ– Đ˛ŅŅ–Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛ Ņ– Ņ—Ņ… ĐŧĐžĐļĐŊа ĐąŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Đ°Ņ‚Đ¸ ĐģĐ¸ŅˆĐĩ в ĐžŅĐžĐąĐ¸ŅŅ‚Ņ–Đš ĐŋаĐŋ҆Җ", - "moved_to_archive": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Ņ–Đ˛}} в Đ°Ņ€Ņ…Ņ–Đ˛", - "moved_to_library": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Ņ–Đ˛}} в ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃", - "moved_to_trash": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž Đ´Đž ҁĐŧŅ–Ņ‚ĐŊиĐēа", + "moved_to_archive": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}} в Đ°Ņ€Ņ…Ņ–Đ˛", + "moved_to_library": "ПĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž {count, plural, one {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚} other {# ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛}} в ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃", + "moved_to_trash": "ПĐĩŅ€ĐĩĐŊĐĩҁĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа", "multiselect_grid_edit_date_time_err_read_only": "НĐĩĐŧĐžĐļĐģивО Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ Đ´Đ°Ņ‚Ņƒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "multiselect_grid_edit_gps_err_read_only": "НĐĩĐŧĐžĐļĐģивО Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ, ĐŋŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž", "mute_memories": "ĐŸŅ€Đ¸ĐŗĐģŅƒŅˆĐ¸Ņ‚Đ¸ ҁĐŋĐžĐŗĐ°Đ´Đ¸", "my_albums": "ĐœĐžŅ— аĐģŅŒĐąĐžĐŧи", "name": "ІĐŧ'Ņ", "name_or_nickname": "ІĐŧ'Ņ айО ĐŋҁĐĩвдОĐŊŅ–Đŧ", + "network_requirement_photos_upload": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ҁ҂ҖĐģҌĐŊиĐēĐžĐ˛Ņ– даĐŊŅ– Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Ņ„ĐžŅ‚Đž", + "network_requirement_videos_upload": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ҁ҂ҖĐģҌĐŊиĐēĐžĐ˛Ņ– даĐŊŅ– Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Đ˛Ņ–Đ´ĐĩĐž", + "network_requirements": "ВиĐŧĐžĐŗĐ¸ Đ´Đž ĐŧĐĩŅ€ĐĩĐļŅ–", + "network_requirements_updated": "ВиĐŧĐžĐŗĐ¸ Đ´Đž ĐŧĐĩŅ€ĐĩĐļŅ– СĐŧŅ–ĐŊиĐģĐ¸ŅŅ, ҇ĐĩŅ€ĐŗĐ° Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐžŅ‡Đ¸Ņ‰ĐĩĐŊа", "networking_settings": "МĐĩŅ€ĐĩĐļĐĩĐ˛Ņ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "networking_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐēŅ–ĐŊ҆ĐĩĐ˛ĐžŅ— Ņ‚ĐžŅ‡Đēи ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "never": "ĐŊŅ–ĐēĐžĐģи", @@ -1265,6 +1375,7 @@ "new_person": "Нова ĐģŅŽĐ´Đ¸ĐŊа", "new_pin_code": "Новий PIN-ĐēОд", "new_pin_code_subtitle": "Ви вĐŋĐĩŅ€ŅˆĐĩ ĐžŅ‚Ņ€Đ¸ĐŧŅƒŅ”Ņ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи. ĐĄŅ‚Đ˛ĐžŅ€Ņ–Ņ‚ŅŒ PIN-ĐēОд Đ´ĐģŅ ĐąĐĩСĐŋĐĩ҇ĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž ҆ҖҔҗ ŅŅ‚ĐžŅ€Ņ–ĐŊĐēи", + "new_timeline": "Нова Ņ…Ņ€ĐžĐŊĐžĐģĐžĐŗŅ–Ņ", "new_user_created": "ĐĄŅ‚Đ˛ĐžŅ€ĐĩĐŊĐž ĐŊĐžĐ˛ĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "new_version_available": "ДОСĐĸĐŖĐŸĐĐ НОВА Đ’Đ•Đ ĐĄĐ†Đ¯", "newest_first": "ĐĄĐŋĐžŅ‡Đ°Ņ‚Đē҃ ĐŊĐžĐ˛Ņ–", @@ -1278,19 +1389,25 @@ "no_assets_message": "НАĐĸИСНІĐĸĐŦ, ЩОБ ЗАВАНĐĸАЖИĐĸИ ВАШЕ ПЕРШЕ ФОĐĸО", "no_assets_to_show": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", "no_cast_devices_found": "ĐŸŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— Đ´ĐģŅ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Ņ–Ņ— ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", + "no_checksum_local": "КоĐŊŅ‚Ņ€ĐžĐģҌĐŊа ҁ҃Đŧа ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа – ĐŊĐĩĐŧĐžĐļĐģивО ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ ĐģĐžĐēаĐģҌĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸", + "no_checksum_remote": "КоĐŊŅ‚Ņ€ĐžĐģҌĐŊа ҁ҃Đŧа ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа – ĐŊĐĩĐŧĐžĐļĐģивО ĐžŅ‚Ņ€Đ¸ĐŧĐ°Ņ‚Đ¸ Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ", "no_duplicates_found": "Đ”ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛ ĐŊĐĩ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐž.", "no_exif_info_available": "Đ’Ņ–Đ´ŅŅƒŅ‚ĐŊŅ Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž exif", "no_explore_results_message": "ЗаваĐŊŅ‚Đ°ĐļŅƒĐšŅ‚Đĩ ĐąŅ–ĐģҌ҈Đĩ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš, Ņ‰ĐžĐą ĐŊĐ°ŅĐžĐģОдĐļŅƒĐ˛Đ°Ņ‚Đ¸ŅŅ Đ˛Đ°ŅˆĐžŅŽ ĐēĐžĐģĐĩĐēŅ†Ņ–Ņ”ŅŽ.", "no_favorites_message": "Đ”ĐžĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ ҃ĐģŅŽĐąĐģĐĩĐŊŅ– Ņ„Đ°ĐšĐģи, Ņ‰ĐžĐą ŅˆĐ˛Đ¸Đ´ĐēĐž СĐŊĐ°Ņ…ĐžĐ´Đ¸Ņ‚Đ¸ Đ˛Đ°ŅˆŅ– ĐŊаКĐēŅ€Đ°Ņ‰Ņ– ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "no_libraries_message": "ĐĄŅ‚Đ˛ĐžŅ€Ņ–Ņ‚ŅŒ СОвĐŊŅ–ŅˆĐŊŅŽ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃ Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš Ņ– Đ˛Ņ–Đ´ĐĩĐž", + "no_local_assets_found": "З Ņ†Ņ–Ņ”ŅŽ ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊĐžŅŽ ҁ҃ĐŧĐžŅŽ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž ĐģĐžĐēаĐģҌĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛", "no_locked_photos_message": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž в ĐžŅĐžĐąĐ¸ŅŅ‚Ņ–Đš ĐŋаĐŋ҆Җ ĐŋŅ€Đ¸Ņ…ĐžĐ˛Đ°ĐŊŅ– Ņ– ĐŊĐĩ Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐ°ŅŽŅ‚ŅŒŅŅ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ Ņ‡Đ¸ ĐŋĐžŅˆŅƒĐē҃ ҃ Đ˛Đ°ŅˆŅ–Đš ĐąŅ–ĐąĐģŅ–ĐžŅ‚Đĩ҆Җ.", "no_name": "БĐĩС Ņ–ĐŧĐĩĐŊŅ–", "no_notifications": "НĐĩĐŧĐ°Ņ” ҁĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊҌ", "no_people_found": "Đ›ŅŽĐ´ĐĩĐš, Ņ‰Đž Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´Đ°ŅŽŅ‚ŅŒ СаĐŋĐ¸Ņ‚Ņƒ, ĐŊĐĩ СĐŊаКдĐĩĐŊĐž", "no_places": "ĐœŅ–ŅŅ†ŅŒ ĐŊĐĩĐŧĐ°Ņ”", + "no_remote_assets_found": "З Ņ†Ņ–Ņ”ŅŽ ĐēĐžĐŊŅ‚Ņ€ĐžĐģҌĐŊĐžŅŽ ҁ҃ĐŧĐžŅŽ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛", "no_results": "НĐĩĐŧĐ°Ņ” Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛", "no_results_description": "ĐĄĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ŅĐ¸ĐŊĐžĐŊŅ–Đŧ айО ĐąŅ–ĐģҌ҈ ĐˇĐ°ĐŗĐ°ĐģҌĐŊĐĩ ĐēĐģŅŽŅ‡ĐžĐ˛Đĩ ҁĐģОвО", "no_shared_albums_message": "ĐĄŅ‚Đ˛ĐžŅ€Ņ–Ņ‚ŅŒ аĐģŅŒĐąĐžĐŧ, Ņ‰ĐžĐą Đ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–ŅĐŧи Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž С ĐģŅŽĐ´ŅŒĐŧи ҃ Đ˛Đ°ŅˆŅ–Đš ĐŧĐĩŅ€ĐĩĐļŅ–", + "no_uploads_in_progress": "НĐĩĐŧĐ°Ņ” аĐēŅ‚Đ¸Đ˛ĐŊĐ¸Ņ… СаваĐŊŅ‚Đ°ĐļĐĩĐŊҌ", + "not_available": "НĐĩĐŧĐ°Ņ” даĐŊĐ¸Ņ…", "not_in_any_album": "ĐŖ ĐļОдĐŊĐžĐŧ҃ аĐģŅŒĐąĐžĐŧŅ–", "not_selected": "НĐĩ Đ˛Đ¸ĐąŅ€Đ°ĐŊĐž", "note_apply_storage_label_to_previously_uploaded assets": "ĐŸŅ€Đ¸ĐŧŅ–Ņ‚Đēа: ЊОй ĐˇĐ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸ ĐŧŅ–Ņ‚Đē҃ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° Đ´Đž Ņ€Đ°ĐŊŅ–ŅˆĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛, виĐēĐžĐŊĐ°ĐšŅ‚Đĩ ĐēĐžĐŧаĐŊĐ´Ņƒ", @@ -1306,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "ĐžŅ„Ņ–Ņ†Ņ–ĐšĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸ Immich", "offline": "ĐžŅ„ĐģаКĐŊ", + "offset": "Đ—ŅŅƒĐ˛", "ok": "ОК", "oldest_first": "ĐĄĐŋĐžŅ‡Đ°Ņ‚Đē҃ ĐŊĐ°ĐšŅŅ‚Đ°Ņ€ŅˆŅ–", "on_this_device": "На Ņ†ŅŒĐžĐŧ҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", @@ -1324,10 +1442,13 @@ "open_the_search_filters": "Đ’Ņ–Đ´ĐēŅ€Đ¸ĐšŅ‚Đĩ ҄ҖĐģŅŒŅ‚Ņ€Đ¸ ĐŋĐžŅˆŅƒĐē҃", "options": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ", "or": "айО", + "organize_into_albums": "ĐŖĐŋĐžŅ€ŅĐ´ĐēŅƒĐ˛Đ°Ņ‚Đ¸ в аĐģŅŒĐąĐžĐŧи", + "organize_into_albums_description": "ПоĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ ĐŊĐ°ŅĐ˛ĐŊŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— в аĐģŅŒĐąĐžĐŧи, виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅŽŅ‡Đ¸ ĐŋĐžŅ‚ĐžŅ‡ĐŊŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—", "organize_your_library": "ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇŅƒĐšŅ‚Đĩ ŅĐ˛ĐžŅŽ ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐē҃", "original": "ĐžŅ€Đ¸ĐŗŅ–ĐŊаĐģ", "other": "ІĐŊ҈Đĩ", "other_devices": "ІĐŊŅˆŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", + "other_entities": "ІĐŊŅˆŅ– Ой'Ņ”ĐēŅ‚Đ¸", "other_variables": "ІĐŊŅˆŅ– СĐŧŅ–ĐŊĐŊŅ–", "owned": "ВĐģĐ°ŅĐŊŅ–", "owner": "ВĐģĐ°ŅĐŊиĐē", @@ -1337,12 +1458,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} Đ˛Ņ‚Ņ€Đ°Ņ‚Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛.", + "partner_page_stop_sharing_content": "{partner} ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ ĐŧĐ°Ņ‚Đ¸ĐŧĐĩ Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚Đž.", "partner_sharing": "ĐĄĐŋŅ–ĐģҌĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "partners": "ĐŸĐ°Ņ€Ņ‚ĐŊĐĩŅ€Đ¸", "password": "ĐŸĐ°Ņ€ĐžĐģҌ", @@ -1369,7 +1490,7 @@ "permanently_delete": "ВидаĐģĐ¸Ņ‚Đ¸ ĐŊаСавĐļди", "permanently_delete_assets_count": "ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {Ņ€ĐĩŅŅƒŅ€Ņ} other {Ņ€ĐĩŅŅƒŅ€ŅĐ¸}}", "permanently_delete_assets_prompt": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŊаСавĐļди видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {҆ĐĩĐš Ņ€ĐĩŅŅƒŅ€Ņ?} other {҆Җ # Ņ€ĐĩŅŅƒŅ€ŅĐ¸?}} ĐĻĐĩ Ņ‚Đ°ĐēĐžĐļ видаĐģĐ¸Ņ‚ŅŒ {count, plural, one {ĐšĐžĐŗĐž С ĐšĐžĐŗĐž} other {Ņ—Ņ… С Ņ—Ņ…ĐŊŅ–Ņ…}} аĐģŅŒĐąĐžĐŧ҃(Ņ–Đ˛).", - "permanently_deleted_asset": "ВидаĐģĐ¸Ņ‚Đ¸ ĐŊаСавĐļди", + "permanently_deleted_asset": "ФаКĐģ видаĐģĐĩĐŊĐž ĐŊаСавĐļди", "permanently_deleted_assets_count": "ВидаĐģĐĩĐŊĐž ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "permission": "ДозвоĐģи", "permission_empty": "ДозвоĐģи ĐŊĐĩ ĐŋОвиĐŊŅ– ĐąŅƒŅ‚Đ¸ ĐŋĐžŅ€ĐžĐļĐŊŅ–Đŧи", @@ -1379,13 +1500,16 @@ "permission_onboarding_go_to_settings": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ", "permission_onboarding_permission_denied": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐˇĐ°ĐąĐžŅ€ĐžĐŊĐĩĐŊĐž. ДĐģŅ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ Immich ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ дОСвОĐģи Đ´Đž \"Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž\" в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", "permission_onboarding_permission_granted": "Đ”ĐžŅŅ‚ŅƒĐŋ ĐŊадаĐŊĐž! Đ’ŅĐĩ ĐŗĐžŅ‚ĐžĐ˛Đž.", - "permission_onboarding_permission_limited": "ОбĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ. Айи дОСвОĐģĐ¸Ņ‚Đ¸ Immich Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ Ņ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžŅŽ ĐŗĐ°ĐģĐĩŅ€ĐĩŅ”ŅŽ, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ҃ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", - "permission_onboarding_request": "Immich ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒŅ” Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛ Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.", + "permission_onboarding_permission_limited": "Đ”ĐžŅŅ‚ŅƒĐŋ ОйĐŧĐĩĐļĐĩĐŊĐž. ЊОйи дОСвОĐģĐ¸Ņ‚Đ¸ Immich ŅŅ‚Đ˛ĐžŅ€ŅŽĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊŅ– ĐēĐžĐŋŅ–Ņ— Ņ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛ŅŅ–Ņ”ŅŽ ĐŗĐ°ĐģĐĩŅ€ĐĩŅ”ŅŽ, ĐŊĐ°Đ´Đ°ĐšŅ‚Đĩ дОСвОĐģи ĐŊа Ņ„ĐžŅ‚Đž Đš Đ˛Ņ–Đ´ĐĩĐž в ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ….", + "permission_onboarding_request": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐŊĐē҃ Immich ĐŋĐžŅ‚Ņ€Ņ–ĐąĐĩĐŊ Đ´ĐžĐˇĐ˛Ņ–Đģ Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.", "person": "Đ›ŅŽĐ´Đ¸ĐŊа", + "person_age_months": "{months, plural, one {# ĐŧŅ–ŅŅŅ†ŅŒ} other {# ĐŧŅ–ŅŅŅ†Ņ–}}", + "person_age_year_months": "1 year , {months, plural, one {# ĐŧŅ–ŅŅŅ†ŅŒ} other {# ĐŧŅ–ŅŅŅ†Ņ–}}", + "person_age_years": "{years, plural, other {# Ņ€ĐžĐēŅ–Đ˛}}", "person_birthdate": "ĐĐ°Ņ€ĐžĐ´Đ¸Đ˛ŅŅ {date}", "person_hidden": "{name}{hidden, select, true { (ĐŋŅ€Đ¸Ņ…ĐžĐ˛Đ°ĐŊĐž)} other {}}", "photo_shared_all_users": "Đ’Đ¸ĐŗĐģŅĐ´Đ°Ņ” Ņ‚Đ°Đē, Ņ‰Đž ви ĐŋĐžĐ´Ņ–ĐģиĐģĐ¸ŅŅ ŅĐ˛ĐžŅ—Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–ŅĐŧи С ŅƒŅŅ–Đŧа ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°Đŧи айО ҃ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ĐļОдĐŊĐžĐŗĐž ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°, С ŅĐēиĐŧ ĐŧĐžĐļĐŊа ĐŋĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ.", - "photos": "ЗĐŊŅ–ĐŧĐēи", + "photos": "Đ¤ĐžŅ‚Đž", "photos_and_videos": "Đ¤ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", "photos_count": "{count, plural, one {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ} few {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—} many {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš} other {{count, number} Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš}}", "photos_from_previous_years": "Đ¤ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— ĐŧиĐŊ҃ĐģĐ¸Ņ… Ņ€ĐžĐēŅ–Đ˛ ҃ ҆ĐĩĐš Đ´ĐĩĐŊҌ", @@ -1403,8 +1527,9 @@ "play_or_pause_video": "Đ’Ņ–Đ´Ņ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ айО ĐŋŅ€Đ¸ĐˇŅƒĐŋиĐŊĐĩĐŊĐŊŅ Đ˛Ņ–Đ´ĐĩĐž", "please_auth_to_access": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐŋŅ€ĐžĐšĐ´Ņ–Ņ‚ŅŒ Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–ŅŽ", "port": "ĐŸĐžŅ€Ņ‚", - "preferences_settings_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи Đ´ĐžĐ´Đ°Ņ‚Đē҃", + "preferences_settings_subtitle": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅĐŧи ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", "preferences_settings_title": "ĐŸĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ¸", + "preparing": "ĐŸŅ–Đ´ĐŗĐžŅ‚ĐžĐ˛Đēа", "preset": "ПĐĩŅ€ĐĩĐ´Đ˛ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ", "preview": "ĐŸŅ€Đĩв'ŅŽ", "previous": "ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ”", @@ -1417,10 +1542,11 @@ "privacy": "КоĐŊŅ„Ņ–Đ´ĐĩĐŊŅ†Ņ–ĐšĐŊŅ–ŅŅ‚ŅŒ", "profile": "ĐŸŅ€ĐžŅ„Ņ–ĐģҌ", "profile_drawer_app_logs": "Đ–ŅƒŅ€ĐŊаĐģ", - "profile_drawer_client_out_of_date_major": "ĐœĐžĐąŅ–ĐģҌĐŊиК Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧаĐļĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", - "profile_drawer_client_out_of_date_minor": "ĐœĐžĐąŅ–ĐģҌĐŊиК Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧŅ–ĐŊĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", + "profile_drawer_client_out_of_date_major": "ĐœĐžĐąŅ–ĐģҌĐŊиК ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧаĐļĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", + "profile_drawer_client_out_of_date_minor": "ĐœĐžĐąŅ–ĐģҌĐŊиК ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧŅ–ĐŊĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_drawer_client_server_up_to_date": "КĐģŅ–Ņ”ĐŊŅ‚ Ņ‚Đ° ҁĐĩŅ€Đ˛ĐĩŅ€ — аĐēŅ‚ŅƒĐ°ĐģҌĐŊŅ–", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "Đ ĐĩĐļиĐŧ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž. ЊОй Đ˛Đ¸ĐšŅ‚Đ¸, Đ´ĐžĐ˛ĐŗĐž ĐŊĐ°Ņ‚Đ¸ŅĐŊŅ–Ņ‚ŅŒ СĐŊĐ°Ņ‡ĐžĐē Đ°Đ˛Đ°Ņ‚Đ°Ņ€Đ° ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°.", "profile_drawer_server_out_of_date_major": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧаĐļĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_drawer_server_out_of_date_minor": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–Đ˛. Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ Đ´Đž ĐžŅŅ‚Đ°ĐŊĐŊŅŒĐžŅ— ĐŧŅ–ĐŊĐžŅ€ĐŊĐžŅ— вĐĩҀҁҖҗ.", "profile_image_of_user": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ {user}", @@ -1459,12 +1585,17 @@ "purchase_server_description_2": "ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēи", "purchase_server_title": "ĐĄĐĩŅ€Đ˛ĐĩŅ€", "purchase_settings_server_activated": "КĐģŅŽŅ‡ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Ņƒ ҁĐĩŅ€Đ˛ĐĩŅ€Đ° ĐēĐĩŅ€ŅƒŅ”Ņ‚ŅŒŅŅ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ", + "query_asset_id": "ІдĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ĐžŅ€ Ņ€ĐĩŅŅƒŅ€ŅŅƒ СаĐŋĐ¸Ņ‚Ņƒ", + "queue_status": "ĐŖ ҇ĐĩŅ€ĐˇŅ– {count} С {total}", "rating": "Đ—ĐžŅ€ŅĐŊиК Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ", "rating_clear": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ", "rating_count": "{count, plural, one {# ĐˇŅ–Ņ€Đēа} few {# ĐˇŅ–Ņ€Đēи} many {# ĐˇŅ–Ņ€ĐžĐē} other {# ĐˇŅ–Ņ€ĐžĐē}}", "rating_description": "ПоĐēĐ°ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ Ņ€ĐĩĐšŅ‚Đ¸ĐŊĐŗ EXIF ĐŊа Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–ĐšĐŊŅ–Đš ĐŋаĐŊĐĩĐģŅ–", "reaction_options": "ОĐŋ҆Җҗ Ņ€ĐĩаĐē҆Җҗ", "read_changelog": "ĐŸŅ€ĐžŅ‡Đ¸Ņ‚Đ°Ņ‚Đ¸ СĐŧŅ–ĐŊи в ĐžĐŊОвĐģĐĩĐŊĐŊŅ–", + "readonly_mode_disabled": "Đ ĐĩĐļиĐŧ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ виĐŧĐēĐŊĐĩĐŊĐž", + "readonly_mode_enabled": "Đ ĐĩĐļиĐŧ ĐģĐ¸ŅˆĐĩ Đ´ĐģŅ Ņ‡Đ¸Ņ‚Đ°ĐŊĐŊŅ Đ˛Đ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž", + "ready_for_upload": "Đ“ĐžŅ‚ĐžĐ˛Đž Đ´Đž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "reassign": "ПĐĩŅ€ĐĩĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡Đ¸Ņ‚Đ¸", "reassigned_assets_to_existing_person": "ПĐĩŅ€ĐĩĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} {name, select, null {ҖҁĐŊŅƒŅŽŅ‡Ņ–Đš ĐžŅĐžĐąŅ–} other {{name}}}", "reassigned_assets_to_new_person": "ПĐĩŅ€ĐĩĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊĐž {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} other {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸}} ĐŊĐžĐ˛Ņ–Đš ĐžŅĐžĐąŅ–", @@ -1487,6 +1618,9 @@ "refreshing_faces": "ОĐŊОвĐģĐĩĐŊĐŊŅ ОйĐģĐ¸Ņ‡", "refreshing_metadata": "ОĐŊОвĐģĐĩĐŊĐŊŅ ĐŧĐĩŅ‚Đ°Đ´Đ°ĐŊĐ¸Ņ…", "regenerating_thumbnails": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊĐŊŅ ĐŧŅ–ĐŊŅ–Đ°Ņ‚ŅŽŅ€", + "remote": "На ҁĐĩŅ€Đ˛ĐĩҀҖ", + "remote_assets": "Đ’Ņ–Đ´Đ´Đ°ĐģĐĩĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž", + "remote_media_summary": "ЗвĐĩĐ´ĐĩĐŊĐŊŅ Đ˛Ņ–Đ´Đ´Đ°ĐģĐĩĐŊĐ¸Ņ… ĐŧĐĩĐ´Ņ–Đ°Ņ„Đ°ĐšĐģŅ–Đ˛", "remove": "ВиĐģŅƒŅ‡Đ¸Ņ‚Đ¸", "remove_assets_album_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} С аĐģŅŒĐąĐžĐŧ҃?", "remove_assets_shared_link_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}} С Ņ†ŅŒĐžĐŗĐž ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ?", @@ -1494,7 +1628,9 @@ "remove_custom_date_range": "ВидаĐģĐ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ†ŅŒĐēиК Đ´Ņ–Đ°ĐŋаСОĐŊ Đ´Đ°Ņ‚", "remove_deleted_assets": "ВидаĐģĐĩĐŊĐŊŅ Đ°Đ˛Ņ‚ĐžĐŊĐžĐŧĐŊĐ¸Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛", "remove_from_album": "ВидаĐģĐ¸Ņ‚Đ¸ С аĐģŅŒĐąĐžĐŧ҃", + "remove_from_album_action_prompt": "{count} видаĐģĐĩĐŊĐž С аĐģŅŒĐąĐžĐŧ҃", "remove_from_favorites": "ВидаĐģĐ¸Ņ‚Đ¸ С ĐžĐąŅ€Đ°ĐŊĐžĐŗĐž", + "remove_from_lock_folder_action_prompt": "{count} виĐģŅƒŅ‡ĐĩĐŊĐž С ĐˇĐ°Ņ…Đ¸Ņ‰ĐĩĐŊĐžŅ— Ņ‚ĐĩĐēи", "remove_from_locked_folder": "ВидаĐģĐ¸Ņ‚Đ¸ С ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи", "remove_from_locked_folder_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ ҆Җ Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž С ĐžŅĐžĐąĐ¸ŅŅ‚ĐžŅ— ĐŋаĐŋĐēи? ВоĐŊи ĐąŅƒĐ´ŅƒŅ‚ŅŒ видиĐŧŅ– ҃ Đ˛Đ°ŅˆŅ–Đš ĐąŅ–ĐąĐģŅ–ĐžŅ‚Đĩ҆Җ.", "remove_from_shared_link": "ВидаĐģĐ¸Ņ‚Đ¸ ĐˇŅ– ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", @@ -1509,7 +1645,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": "НĐĩĐ˛Ņ–Đ´ŅŅ‚ĐĩĐļŅƒĐ˛Đ°ĐŊŅ– Ņ‚Đ° Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ– Ņ„Đ°ĐšĐģи ĐąŅƒĐ´ŅƒŅ‚ŅŒ Đ˛Ņ–Đ´ĐžĐąŅ€Đ°ĐļĐĩĐŊŅ– Ņ‚ŅƒŅ‚", @@ -1518,23 +1654,33 @@ "require_password": "ВиĐŧĐ°ĐŗĐ°Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "require_user_to_change_password_on_first_login": "ВиĐŧĐ°ĐŗĐ°Ņ‚Đ¸ Đ˛Ņ–Đ´ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ° СĐŧŅ–ĐŊŅŽĐ˛Đ°Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€Đ¸ ĐŋĐĩŅ€ŅˆĐžĐŧ҃ Đ˛Ņ…ĐžĐ´Ņ–", "rescan": "ПĐĩŅ€ĐĩҁĐēаĐŊŅƒĐ˛Đ°ĐŊĐŊŅ", - "reset": "ĐĄĐēидаĐŊĐŊŅ", + "reset": "ĐĄĐēиĐŊŅƒŅ‚Đ¸", "reset_password": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "reset_people_visibility": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ видиĐŧŅ–ŅŅ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "reset_pin_code": "ĐĄĐēиĐŊŅƒŅ‚Đ¸ PIN-ĐēОд", + "reset_pin_code_description": "Đ¯ĐēŅ‰Đž ви ĐˇĐ°ĐąŅƒĐģи ŅĐ˛Ņ–Đš PIN-ĐēОд, ви ĐŧĐžĐļĐĩŅ‚Đĩ СвĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅ Đ´Đž адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ҁĐĩŅ€Đ˛ĐĩŅ€Đ°, Ņ‰ĐžĐą ҁĐēиĐŊŅƒŅ‚Đ¸ ĐšĐžĐŗĐž", + "reset_pin_code_success": "PIN-ĐēОд ҃ҁĐŋŅ–ŅˆĐŊĐž ҁĐēиĐŊŅƒŅ‚Đž", + "reset_pin_code_with_password": "Ви СавĐļди ĐŧĐžĐļĐĩŅ‚Đĩ ҁĐēиĐŊŅƒŅ‚Đ¸ ŅĐ˛Ņ–Đš PIN-ĐēОд Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ ĐŋĐ°Ņ€ĐžĐģŅ", + "reset_sqlite": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐąĐ°ĐˇŅƒ даĐŊĐ¸Ņ… SQLite", + "reset_sqlite_confirmation": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐąĐ°ĐˇŅƒ даĐŊĐ¸Ņ… SQLite? ĐŸŅ–ŅĐģŅ Ņ†ŅŒĐžĐŗĐž ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž ĐąŅƒĐ´Đĩ Đ˛Đ¸ĐšŅ‚Đ¸ С аĐēĐ°ŅƒĐŊŅ‚Đ° Ņ‚Đ° ŅƒĐ˛Ņ–ĐšŅ‚Đ¸ СĐŊĐžĐ˛Ņƒ Đ´ĐģŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐžŅ— ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— даĐŊĐ¸Ņ…", + "reset_sqlite_success": "Đ‘Đ°ĐˇŅƒ даĐŊĐ¸Ņ… SQLite ҃ҁĐŋŅ–ŅˆĐŊĐž ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐž", "reset_to_default": "ĐĄĐēидаĐŊĐŊŅ Đ´Đž ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ Са СаĐŧĐžĐ˛Ņ‡ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ", "resolve_duplicates": "ĐŖŅŅƒĐŊŅƒŅ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", "resolved_all_duplicates": "ĐŖŅŅ– Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸ ҃ҁ҃ĐŊŅƒŅ‚Đž", "restore": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸", "restore_all": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ˛ŅĐĩ", + "restore_trash_action_prompt": "{count} Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž С ĐēĐžŅˆĐ¸Đēа", "restore_user": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", - "restored_asset": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊиК аĐēŅ‚Đ¸Đ˛", + "restored_asset": "Đ’Ņ–Đ´ĐŊОвĐģĐĩĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ", "resume": "ĐŸŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸", + "resume_paused_jobs": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ {count, plural, one {# ĐŋŅ€Đ¸ĐˇŅƒĐŋиĐŊĐĩĐŊĐĩ СавдаĐŊĐŊŅ} other {# ĐŋŅ€Đ¸ĐˇŅƒĐŋиĐŊĐĩĐŊŅ– СавдаĐŊĐŊŅ}}", "retry_upload": "ĐŸĐžĐ˛Ņ‚ĐžŅ€Đ¸Ņ‚Đ¸ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "review_duplicates": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", + "review_large_files": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ вĐĩĐģиĐēĐ¸Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛", "role": "Đ ĐžĐģҌ", "role_editor": "Đ ĐĩдаĐēŅ‚ĐžŅ€", "role_viewer": "ГĐģŅĐ´Đ°Ņ‡", + "running": "ВиĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ", "save": "ЗбĐĩŅ€ĐĩĐŗŅ‚Đ¸", "save_to_gallery": "ЗбĐĩŅ€ĐĩĐŗŅ‚Đ¸ в ĐŗĐ°ĐģĐĩŅ€ĐĩŅŽ", "saved_api_key": "ЗбĐĩŅ€ĐĩĐļĐĩĐŊŅ– ĐēĐģŅŽŅ‡Ņ– API", @@ -1567,8 +1713,8 @@ "search_filter_filename": "ĐŸĐžŅˆŅƒĐē Са ĐŊĐ°ĐˇĐ˛ĐžŅŽ Ņ„Đ°ĐšĐģ҃", "search_filter_location": "ĐœŅ–ŅŅ†ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", "search_filter_location_title": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŧҖҁ҆ĐĩСĐŊĐ°Ņ…ĐžĐ´ĐļĐĩĐŊĐŊŅ", - "search_filter_media_type": "ĐĸиĐŋ ĐŊĐžŅŅ–Ņ", - "search_filter_media_type_title": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ Ņ‚Đ¸Đŋ ĐŊĐžŅŅ–Ņ", + "search_filter_media_type": "ĐĸиĐŋ ĐŧĐĩĐ´Ņ–Đ°", + "search_filter_media_type_title": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ Ņ‚Đ¸Đŋ ĐŧĐĩĐ´Ņ–Đ°", "search_filter_people_title": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐģŅŽĐ´ĐĩĐš", "search_for": "Đ¨ŅƒĐēĐ°Ņ‚Đ¸ Đ´ĐģŅ", "search_for_existing_person": "ĐŸĐžŅˆŅƒĐē ҖҁĐŊŅƒŅŽŅ‡ĐžŅ— ĐžŅĐžĐąĐ¸", @@ -1578,7 +1724,7 @@ "search_no_result": "Đ ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛ ĐŊĐĩ СĐŊаКдĐĩĐŊĐž, ҁĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Ņ–ĐŊŅˆĐ¸Đš СаĐŋĐ¸Ņ‚ айО ĐēĐžĐŧĐąŅ–ĐŊĐ°Ņ†Ņ–ŅŽ", "search_options": "ОĐŋ҆Җҗ ĐŋĐžŅˆŅƒĐē҃", "search_page_categories": "ĐšĐ°Ņ‚ĐĩĐŗĐžŅ€Ņ–Ņ—", - "search_page_motion_photos": "Đ ŅƒŅ…ĐžĐŧŅ– СĐŊŅ–ĐŧĐēи", + "search_page_motion_photos": "Đ–Đ¸Đ˛Ņ– Ņ„ĐžŅ‚Đž", "search_page_no_objects": "НĐĩĐŧĐ°Ņ” Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ— ĐŋŅ€Đž Ой'Ņ”ĐēŅ‚Đ¸", "search_page_no_places": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž ĐŧŅ–ŅŅ†Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа", "search_page_screenshots": "ЗĐŊŅ–ĐŧĐēи ĐĩĐēŅ€Đ°ĐŊ҃", @@ -1599,7 +1745,7 @@ "search_tags": "ĐŸĐžŅˆŅƒĐē Ņ‚ĐĩĐŗŅ–Đ˛...", "search_timezone": "ĐŸĐžŅˆŅƒĐē Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅŅƒ...", "search_type": "ĐĸиĐŋ ĐŋĐžŅˆŅƒĐē҃", - "search_your_photos": "Đ¨ŅƒĐēĐ°Ņ‚Đ¸ Đ˛Đ°ŅˆŅ– СĐŊŅ–ĐŧĐēи", + "search_your_photos": "ĐŸĐžŅˆŅƒĐē ҁĐĩŅ€ĐĩĐ´ Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚Đž", "searching_locales": "ĐĸŅ€Đ¸Đ˛Đ°Ņ” ĐŋĐžŅˆŅƒĐē ĐŋĐĩŅ€ĐĩĐēĐģĐ°Đ´Ņ–Đ˛...", "second": "ĐĄĐĩĐē҃ĐŊда", "see_all_people": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ Đ˛ŅŅ–Ņ… ĐģŅŽĐ´ĐĩĐš", @@ -1616,15 +1762,16 @@ "select_library_owner": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ вĐģĐ°ŅĐŊиĐēа ĐąŅ–ĐąĐģŅ–ĐžŅ‚ĐĩĐēи", "select_new_face": "ĐžĐąŅ€Đ°Ņ‚Đ¸ ĐŊОвĐĩ ОйĐģĐ¸Ņ‡Ņ‡Ņ", "select_person_to_tag": "ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐģŅŽĐ´Đ¸ĐŊ҃ Đ´ĐģŅ ĐŋОСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ", - "select_photos": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ЗĐŊŅ–ĐŧĐēи", + "select_photos": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "select_trash_all": "ВидаĐģĐ¸Ņ‚Đ¸ Đ˛ŅĐĩ Đ˛Đ¸ĐąŅ€Đ°ĐŊĐĩ", "select_user_for_sharing_page_err_album": "НĐĩ вдаĐģĐžŅŅ ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧ", "selected": "ĐžĐąŅ€Đ°ĐŊĐž", "selected_count": "{count, plural, one {# ĐžĐąŅ€Đ°ĐŊиК} other {# ĐžĐąŅ€Đ°ĐŊĐ¸Ņ…}}", + "selected_gps_coordinates": "Đ’Đ¸ĐąŅ€Đ°ĐŊŅ– GPS-ĐēĐžĐžŅ€Đ´Đ¸ĐŊĐ°Ņ‚Đ¸", "send_message": "ĐĐ°Đ´Ņ–ŅĐģĐ°Ņ‚Đ¸ ĐŋĐžĐ˛Ņ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ", "send_welcome_email": "ĐĐ°Đ´Ņ–ŅˆĐģŅ–Ņ‚ŅŒ Đ˛Ņ–Ņ‚Đ°ĐģҌĐŊиК ĐģĐ¸ŅŅ‚", "server_endpoint": "ĐšŅ–ĐŊ҆Đĩва Ņ‚ĐžŅ‡Đēа ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", - "server_info_box_app_version": "ВĐĩŅ€ŅŅ–Ņ Đ´ĐžĐ´Đ°Ņ‚Đēа", + "server_info_box_app_version": "ВĐĩŅ€ŅŅ–Ņ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", "server_info_box_server_url": "URL ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", "server_offline": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐžŅ„ĐģаКĐŊ", "server_online": "ĐĄĐĩŅ€Đ˛ĐĩŅ€ ĐžĐŊĐģаКĐŊ", @@ -1646,7 +1793,7 @@ "setting_image_viewer_preview_title": "ЗаваĐŊŅ‚Đ°ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅŒĐžĐŗĐž ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´Ņƒ", "setting_image_viewer_title": "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", "setting_languages_apply": "Đ—Đ°ŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚Đ¸", - "setting_languages_subtitle": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŧĐžĐ˛Ņƒ Đ´ĐžĐ´Đ°Ņ‚Đē҃", + "setting_languages_subtitle": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŧĐžĐ˛Ņƒ ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", "setting_notifications_notify_failures_grace_period": "ĐŸĐžĐ˛Ņ–Đ´ĐžĐŧĐ¸Ņ‚Đ¸ ĐŋŅ€Đž ĐŋĐžĐŧиĐģĐēи Ņ„ĐžĐŊĐžĐ˛ĐžĐŗĐž Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ: {duration}", "setting_notifications_notify_hours": "{count} ĐŗĐžĐ´Đ¸ĐŊ", "setting_notifications_notify_immediately": "ĐŊĐĩĐŗĐ°ĐšĐŊĐž", @@ -1666,7 +1813,8 @@ "settings_saved": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ СйĐĩŅ€ĐĩĐļĐĩĐŊŅ–", "setup_pin_code": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ PIN-ĐēОд", "share": "ĐŸĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ", - "share_add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ СĐŊŅ–ĐŧĐēи", + "share_action_prompt": "{count} Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ĐŊĐ°Đ´Ņ–ŅĐģаĐŊĐž", + "share_add_photos": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ„ĐžŅ‚Đž", "share_assets_selected": "{count} ĐžĐąŅ€Đ°ĐŊĐž", "share_dialog_preparing": "ĐŸŅ–Đ´ĐŗĐžŅ‚ĐžĐ˛Đēа...", "share_link": "ĐŸĐžĐ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧ", @@ -1687,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "ĐĄĐēĐžĐŋŅ–ĐšĐžĐ˛Đ°ĐŊĐž в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧŅ–ĐŊ҃", "shared_link_clipboard_text": "ĐŸĐžŅĐ¸ĐģаĐŊĐŊŅ: {link}\nĐŸĐ°Ņ€ĐžĐģҌ: {password}", "shared_link_create_error": "ПоĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", + "shared_link_custom_url_description": "ĐžŅ‚Ņ€Đ¸ĐŧĐ°ĐšŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ†ŅŒĐžĐŗĐž ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ Са вĐģĐ°ŅĐŊĐžŅŽ URL-Đ°Đ´Ņ€ĐĩŅĐžŅŽ", "shared_link_edit_description_hint": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐžĐŋĐ¸Ņ Đ´ĐģŅ ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž Đ´ĐžŅŅ‚ŅƒĐŋ҃", "shared_link_edit_expire_after_option_day": "1 Đ´ĐĩĐŊҌ", "shared_link_edit_expire_after_option_days": "{count} Đ´ĐŊŅ–Đ˛", @@ -1712,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐŋŅ–ĐģҌĐŊиĐŧи ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧи", "shared_link_options": "ОĐŋ҆Җҗ ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… ĐŋĐžŅĐ¸ĐģаĐŊҌ", + "shared_link_password_description": "ВиĐŧĐ°ĐŗĐ°Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Ņ†ŅŒĐžĐŗĐž ҁĐŋŅ–ĐģҌĐŊĐžĐŗĐž ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "shared_links": "ĐĄĐŋŅ–ĐģҌĐŊŅ– ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "shared_links_description": "Đ”Ņ–ĐģŅ–Ņ‚ŅŒŅŅ Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž Са ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅĐŧ", "shared_photos_and_videos_count": "{assetCount, plural, other {# ҁĐŋŅ–ĐģҌĐŊŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.}}", @@ -1720,7 +1870,7 @@ "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": "ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊиК аĐģŅŒĐąĐžĐŧ", @@ -1746,6 +1896,7 @@ "show_slideshow_transition": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ ĐŋĐĩŅ€ĐĩŅ…Ņ–Đ´ ҁĐģаКд-ŅˆĐžŅƒ", "show_supporter_badge": "ЗĐŊĐ°Ņ‡ĐžĐē ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēи", "show_supporter_badge_description": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ СĐŊĐ°Ņ‡ĐžĐē ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēи", + "show_text_search_menu": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ ĐŧĐĩĐŊŅŽ Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛ĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃", "shuffle": "ПĐĩŅ€ĐĩĐŧŅ–ŅˆĐ°Ņ‚Đ¸", "sidebar": "Đ‘Ņ–Ņ‡ĐŊа ĐŋаĐŊĐĩĐģҌ", "sidebar_display_description": "Đ’Ņ–Đ´ĐžĐąŅ€Đ°ĐˇĐ¸Ņ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ ĐŊа ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´ ҃ ĐąŅ–Ņ‡ĐŊŅ–Đš ĐŋаĐŊĐĩĐģŅ–", @@ -1761,12 +1912,14 @@ "sort_created": "Đ”Đ°Ņ‚Đ° ŅŅ‚Đ˛ĐžŅ€ĐĩĐŊĐŊŅ", "sort_items": "ĐšŅ–ĐģҌĐēŅ–ŅŅ‚ŅŒ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛", "sort_modified": "Đ”Đ°Ņ‚Đ° СĐŧŅ–ĐŊи", + "sort_newest": "НайĐŊĐžĐ˛Ņ–ŅˆĐĩ Ņ„ĐžŅ‚Đž", "sort_oldest": "ĐĄŅ‚Đ°Ņ€Ņ– Ņ„ĐžŅ‚Đž", "sort_people_by_similarity": "ĐĄĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ĐģŅŽĐ´ĐĩĐš Са ŅŅ…ĐžĐļŅ–ŅŅ‚ŅŽ", "sort_recent": "НĐĩŅ‰ĐžĐ´Đ°Đ˛ĐŊŅ–", "sort_title": "Đ—Đ°ĐŗĐžĐģОвОĐē", "source": "Đ’Đ¸Ņ…Ņ–Đ´ĐŊиК ĐēОд", "stack": "ĐŖ ŅŅ‚ĐžĐŋĐē҃", + "stack_action_prompt": "Đ—ĐŗŅ€ŅƒĐŋОваĐŊĐž: {count}", "stack_duplicates": "Đ“Ņ€ŅƒĐŋŅƒĐ˛Đ°Ņ‚Đ¸ Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", "stack_select_one_photo": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ОдĐŊĐĩ ĐžŅĐŊОвĐŊĐĩ Ņ„ĐžŅ‚Đž Đ´ĐģŅ ĐŗŅ€ŅƒĐŋи", "stack_selected_photos": "ĐĄĐŗŅ€ŅƒĐŋŅƒĐ˛Đ°Ņ‚Đ¸ ĐžĐąŅ€Đ°ĐŊŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—", @@ -1774,11 +1927,12 @@ "stacktrace": "ĐĄŅ‚ĐĩĐē виĐēĐģиĐēŅ–Đ˛", "start": "ĐĄŅ‚Đ°Ņ€Ņ‚", "start_date": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅ‡Đ°Ņ‚Đē҃", + "start_date_before_end_date": "Đ”Đ°Ņ‚Đ° ĐŋĐžŅ‡Đ°Ņ‚Đē҃ ĐŧĐ°Ņ” ĐąŅƒŅ‚Đ¸ Ņ€Đ°ĐŊŅ–ŅˆĐĩ Đ´Đ°Ņ‚Đ¸ СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ", "state": "Đ ĐĩĐŗŅ–ĐžĐŊ", "status": "ĐĄŅ‚Đ°ĐŊ", "stop_casting": "Đ—ŅƒĐŋиĐŊĐ¸Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁĐģŅŅ†Ņ–ŅŽ", "stop_motion_photo": "Đ¤ĐžŅ‚Đž \"ĐĄŅ‚ĐžĐŋ-ĐŧĐžŅƒŅˆĐĩĐŊ\"", - "stop_photo_sharing": "ĐŸŅ€Đ¸ĐŋиĐŊĐ¸Ņ‚Đ¸ ĐŊадаĐŊĐŊŅ Đ˛Đ°ŅˆĐ¸Ņ… СĐŊŅ–ĐŧĐēŅ–Đ˛?", + "stop_photo_sharing": "Đ—ŅƒĐŋиĐŊĐ¸Ņ‚Đ¸ ҁĐŋŅ–ĐģҌĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚Đž?", "stop_photo_sharing_description": "{partner} ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ ĐŧĐ°Ņ‚Đ¸ĐŧĐĩ Đ´ĐžŅŅ‚ŅƒĐŋ҃ Đ´Đž Đ˛Đ°ŅˆĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš.", "stop_sharing_photos_with_user": "ĐŸŅ€Đ¸ĐŋиĐŊĐ¸Ņ‚Đ¸ Đ´Ņ–ĐģĐ¸Ņ‚Đ¸ŅŅ ŅĐ˛ĐžŅ—Đŧи Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–ŅĐŧи С Ņ†Đ¸Đŧ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡ĐĩĐŧ", "storage": "ĐĄŅ…ĐžĐ˛Đ¸Ņ‰Đĩ", @@ -1786,6 +1940,7 @@ "storage_quota": "ĐžĐąŅŅĐŗ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ°", "storage_usage": "{used} С {available} Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐ¸Ņ…", "submit": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đ¸", + "success": "ĐŖŅĐŋŅ–ŅˆĐŊĐž", "suggestions": "ĐŸŅ€ĐžĐŋĐžĐˇĐ¸Ņ†Ņ–Ņ—", "sunrise_on_the_beach": "ĐĄĐ˛Ņ–Ņ‚Đ°ĐŊĐžĐē ĐŊа ĐŋĐģŅĐļŅ–", "support": "ĐŸŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēа", @@ -1795,6 +1950,10 @@ "sync": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸", "sync_albums": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ аĐģŅŒĐąĐžĐŧи", "sync_albums_manual_subtitle": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛ŅŅ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ҃ Đ˛Đ¸ĐąŅ€Đ°ĐŊŅ– аĐģŅŒĐąĐžĐŧи Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ", + "sync_local": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—", + "sync_remote": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ С ҁĐĩŅ€Đ˛ĐĩŅ€ĐžĐŧ", + "sync_status": "ĐĄŅ‚Đ°ĐŊ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—", + "sync_status_subtitle": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ Ņ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐ¸ŅŅ‚ĐĩĐŧĐžŅŽ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—", "sync_upload_album_setting_subtitle": "ĐĄŅ‚Đ˛ĐžŅ€ŅŽĐšŅ‚Đĩ Ņ‚Đ° СаваĐŊŅ‚Đ°ĐļŅƒĐšŅ‚Đĩ ŅĐ˛ĐžŅ— Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ— Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž Đ´Đž Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… аĐģŅŒĐąĐžĐŧŅ–Đ˛ ĐŊа ҁĐĩŅ€Đ˛ĐĩŅ€ Immich", "tag": "ĐĸĐĩĐŗ", "tag_assets": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ Ņ‚ĐĩĐŗĐ¸", @@ -1803,8 +1962,9 @@ "tag_not_found_question": "НĐĩ Đ˛Đ´Đ°Ņ”Ņ‚ŅŒŅŅ СĐŊĐ°ĐšŅ‚Đ¸ Ņ‚ĐĩĐŗ? ĐĄŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ ĐŊОвиК Ņ‚ĐĩĐŗ.", "tag_people": "ĐĸĐĩĐŗ ĐģŅŽĐ´ĐĩĐš", "tag_updated": "ОĐŊОвĐģĐĩĐŊĐž Ņ‚ĐĩĐŗ: {tag}", - "tagged_assets": "ПозĐŊĐ°Ņ‡ĐĩĐŊĐž Ņ‚ĐĩĐŗĐžĐŧ {count, plural, one {# аĐēŅ‚Đ¸Đ˛} other {# аĐēŅ‚Đ¸Đ˛Đ¸}}", + "tagged_assets": "ПозĐŊĐ°Ņ‡ĐĩĐŊĐž Ņ‚ĐĩĐŗĐžĐŧ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} other {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸}}", "tags": "ĐĸĐĩĐŗĐ¸", + "tap_to_run_job": "ĐĸĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ, Ņ‰ĐžĐą СаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ СавдаĐŊĐŊŅ", "template": "ШайĐģĐžĐŊ", "theme": "ĐĸĐĩĐŧа", "theme_selection": "Đ’Đ¸ĐąŅ–Ņ€ Ņ‚ĐĩĐŧи", @@ -1819,7 +1979,7 @@ "theme_setting_primary_color_title": "ĐžŅĐŊОвĐŊиК ĐēĐžĐģŅ–Ņ€", "theme_setting_system_primary_color_title": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ĐēĐžĐģŅ–Ņ€ ŅĐ¸ŅŅ‚ĐĩĐŧи", "theme_setting_system_theme_switch": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž (ŅĐē ҃ ŅĐ¸ŅŅ‚ĐĩĐŧŅ–)", - "theme_setting_theme_subtitle": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ‚ĐĩĐŧи Đ´ĐžĐ´Đ°Ņ‚Đēа", + "theme_setting_theme_subtitle": "НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ‚ĐĩĐŧи ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃", "theme_setting_three_stage_loading_subtitle": "ĐĸŅ€Đ¸ĐĩŅ‚Đ°ĐŋĐŊĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŧĐžĐļĐĩ ĐŋŅ–Đ´Đ˛Đ¸Ņ‰Đ¸Ņ‚Đ¸ ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ, аĐģĐĩ ҁĐŋŅ€Đ¸Ņ‡Đ¸ĐŊĐ¸Ņ‚ŅŒ СĐŊĐ°Ņ‡ĐŊĐž ĐąŅ–ĐģҌ҈Đĩ ĐŊаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŊа ĐŧĐĩŅ€ĐĩĐļ҃", "theme_setting_three_stage_loading_title": "ĐŖĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚Đ¸ Ņ‚Ņ€Đ¸ĐĩŅ‚Đ°ĐŋĐŊĐĩ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "they_will_be_merged_together": "ВоĐŊи ĐąŅƒĐ´ŅƒŅ‚ŅŒ Ой'Ņ”Đ´ĐŊаĐŊŅ– Ņ€Đ°ĐˇĐžĐŧ", @@ -1831,32 +1991,38 @@ "to_change_password": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŋĐ°Ņ€ĐžĐģҌ", "to_favorite": "ĐžĐąŅ€Đ°ĐŊĐĩ", "to_login": "Đ’Ņ…Ņ–Đ´", + "to_multi_select": "Đ´ĐģŅ ĐąĐ°ĐŗĐ°Ņ‚ĐžŅ€Đ°ĐˇĐžĐ˛ĐžĐŗĐž Đ˛Đ¸ĐąĐžŅ€Ņƒ", "to_parent": "ПовĐĩŅ€ĐŊŅƒŅ‚Đ¸ŅŅŒ ĐŊаСад", - "to_trash": "ĐĄĐŧŅ–Ņ‚ĐŊиĐē", + "to_select": "Đ˛Đ¸ĐąŅ€Đ°Ņ‚Đ¸", + "to_trash": "ĐšĐžŅˆĐ¸Đē", "toggle_settings": "ПĐĩŅ€ĐĩĐŧиĐēаĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ", "total": "ĐŖŅŅŒĐžĐŗĐž", "total_usage": "Đ—Đ°ĐŗĐ°ĐģҌĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", - "trash": "ĐĄĐŧŅ–Ņ‚ĐŊиĐē", + "trash": "ĐšĐžŅˆĐ¸Đē", + "trash_action_prompt": "{count} ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž Đ´Đž ĐēĐžŅˆĐ¸Đēа", "trash_all": "ВидаĐģĐ¸Ņ‚Đ¸ Đ˛ŅĐĩ", "trash_count": "ВидаĐģĐ¸Ņ‚Đ¸ {count, number}", - "trash_delete_asset": "ĐĄĐŧŅ–Ņ‚ĐŊиĐē/ВидаĐģĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ", - "trash_emptied": "ĐšĐžŅˆĐ¸Đē ĐžŅ‡Đ¸Ņ‰ĐĩĐŊиК", + "trash_delete_asset": "ĐŖ ĐēĐžŅˆĐ¸Đē/ВидаĐģĐ¸Ņ‚Đ¸ Ņ€ĐĩŅŅƒŅ€Ņ", + "trash_emptied": "ĐšĐžŅˆĐ¸Đē ĐžŅ‡Đ¸Ņ‰ĐĩĐŊĐž", "trash_no_results_message": "ĐĸŅƒŅ‚ С'ŅĐ˛ĐģŅŅ‚Đ¸ĐŧŅƒŅ‚ŅŒŅŅ видаĐģĐĩĐŊŅ– Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.", - "trash_page_delete_all": "ВидаĐģĐ¸Ņ‚Đ¸ ŅƒŅŅ–", + "trash_page_delete_all": "ВидаĐģĐ¸Ņ‚Đ¸ ҃ҁĐĩ", "trash_page_empty_trash_dialog_content": "Ви Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đ¸ ĐēĐžŅˆĐ¸Đē? ĐĻŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– С Immich", "trash_page_info": "ПоĐŧҖ҉ĐĩĐŊŅ– ҃ ĐēĐžŅˆĐ¸Đē ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´Đĩ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž ҇ĐĩŅ€ĐĩС {days} Đ´ĐŊŅ–Đ˛", - "trash_page_no_assets": "Đ’Ņ–Đ´Đ´Đ°ĐģĐĩĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", - "trash_page_restore_all": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ŅƒŅŅ–", - "trash_page_select_assets_btn": "Đ’Đ¸ĐąŅ€Đ°ĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", + "trash_page_no_assets": "ВидаĐģĐĩĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ Đ˛Ņ–Đ´ŅŅƒŅ‚ĐŊŅ–", + "trash_page_restore_all": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ҃ҁĐĩ", + "trash_page_select_assets_btn": "Đ’Đ¸ĐąŅ€Đ°Ņ‚Đ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "trash_page_title": "ĐšĐžŅˆĐ¸Đē ({count})", "trashed_items_will_be_permanently_deleted_after": "ВидаĐģĐĩĐŊŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐąŅƒĐ´ŅƒŅ‚ŅŒ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊŅ– ҇ĐĩŅ€ĐĩС {days, plural, one {# Đ´ĐĩĐŊҌ} few {# Đ´ĐŊŅ–} many {# Đ´ĐŊŅ–Đ˛} other {# Đ´ĐŊŅ–Đ˛}}.", + "troubleshoot": "ВиĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ ĐŊĐĩĐŋĐžĐģадОĐē", "type": "ĐĸиĐŋ", "unable_to_change_pin_code": "НĐĩĐŧĐžĐļĐģивО СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ PIN-ĐēОд", "unable_to_setup_pin_code": "НĐĩĐŧĐžĐļĐģивО ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚Đ¸ PIN-ĐēОд", "unarchive": "Đ ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸", + "unarchive_action_prompt": "{count} виĐģŅƒŅ‡ĐĩĐŊĐž С Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ", "unarchived_count": "{count, plural, other {ПовĐĩŅ€ĐŊŅƒŅ‚Đž С Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ #}}", "undo": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸", "unfavorite": "ВидаĐģĐ¸Ņ‚Đ¸ С ҃ĐģŅŽĐąĐģĐĩĐŊĐ¸Ņ…", + "unfavorite_action_prompt": "{count} виĐģŅƒŅ‡ĐĩĐŊĐž С ĐžĐąŅ€Đ°ĐŊĐžĐŗĐž", "unhide_person": "РОСĐēŅ€Đ¸Ņ‚Đ¸ ĐžŅĐžĐąŅƒ", "unknown": "НĐĩĐ˛Ņ–Đ´ĐžĐŧĐž", "unknown_country": "НĐĩĐ˛Ņ–Đ´ĐžĐŧа ĐēŅ€Đ°Ņ—ĐŊа", @@ -1874,15 +2040,21 @@ "unselect_all_duplicates": "ĐĄĐēĐ°ŅŅƒĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€ ŅƒŅŅ–Ņ… Đ´ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Ņ–Đ˛", "unselect_all_in": "ЗĐŊŅŅ‚Đ¸ Đ˛Đ¸ĐąŅ–Ņ€ ҃ Đ˛ŅŅŒĐžĐŧ҃ {group}", "unstack": "Đ ĐžĐˇŅ–ĐąŅ€Đ°Ņ‚Đ¸ ҁ҂ĐĩĐē", + "unstack_action_prompt": "{count} Ņ€ĐžĐˇâ€™Ņ”Đ´ĐŊаĐŊĐž", "unstacked_assets_count": "Đ ĐžĐˇĐŗĐžŅ€ĐŊŅƒŅ‚Đ¸ {count, plural, one {# Ņ€ĐĩŅŅƒŅ€Ņ} few {# Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", + "untagged": "БĐĩС Ņ‚ĐĩĐŗŅ–Đ˛", "up_next": "ĐĐ°ŅŅ‚ŅƒĐŋĐŊĐĩ", + "update_location_action_prompt": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Ņ€ĐžĐˇŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… ĐžĐąâ€™Ņ”ĐēŅ‚Ņ–Đ˛ ({count}) Са Đ´ĐžĐŋĐžĐŧĐžĐŗĐžŅŽ:", "updated_at": "ОĐŊОвĐģĐĩĐŊĐž", "updated_password": "ĐŸĐ°Ņ€ĐžĐģҌ ĐžĐŊОвĐģĐĩĐŊĐž", "upload": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸", + "upload_action_prompt": "{count} ҃ ҇ĐĩŅ€ĐˇŅ– ĐŊа СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "upload_concurrency": "ĐŸĐ°Ņ€Đ°ĐģĐĩĐģҌĐŊŅ–ŅŅ‚ŅŒ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", + "upload_details": "ДĐĩŅ‚Đ°ĐģŅ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "upload_dialog_info": "БаĐļĐ°Ņ”Ņ‚Đĩ ŅŅ‚Đ˛ĐžŅ€Đ¸Ņ‚Đ¸ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊ҃ ĐēĐžĐŋŅ–ŅŽ Đ˛Đ¸ĐąŅ€Đ°ĐŊĐ¸Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐŊа ҁĐĩŅ€Đ˛ĐĩҀҖ?", "upload_dialog_title": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸", "upload_errors": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СавĐĩŅ€ŅˆĐĩĐŊĐž С {count, plural, one {# ĐŋĐžĐŧиĐģĐēĐžŅŽ} few {# ĐŋĐžĐŧиĐģĐēаĐŧи} many {# ĐŋĐžĐŧиĐģĐēаĐŧи} other {# ĐŋĐžĐŧиĐģĐēаĐŧи}}, ĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ŅŅ‚ĐžŅ€Ņ–ĐŊĐē҃, Ņ‰ĐžĐą ĐŋĐžĐąĐ°Ņ‡Đ¸Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸.", + "upload_finished": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СавĐĩŅ€ŅˆĐĩĐŊĐž", "upload_progress": "ЗаĐģĐ¸ŅˆĐ¸ĐģĐžŅŅŒ {remaining, number} - ОĐŋŅ€Đ°Ņ†ŅŒĐžĐ˛Đ°ĐŊĐž {processed, number}/{total, number}", "upload_skipped_duplicates": "ĐŸŅ€ĐžĐŋŅƒŅ‰ĐĩĐŊĐž {count, plural, one {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ} few {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸} many {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛} other {# Đ´ŅƒĐąĐģŅŒĐžĐ˛Đ°ĐŊĐ¸Ņ… Ņ€ĐĩŅŅƒŅ€ŅŅ–Đ˛}}", "upload_status_duplicates": "Đ”ŅƒĐąĐģŅ–ĐēĐ°Ņ‚Đ¸", @@ -1891,6 +2063,7 @@ "upload_success": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ҃ҁĐŋŅ–ŅˆĐŊĐĩ. ОĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ŅŅ‚ĐžŅ€Ņ–ĐŊĐē҃, Ņ‰ĐžĐą ĐŋĐžĐąĐ°Ņ‡Đ¸Ņ‚Đ¸ ĐŊĐžĐ˛Ņ– СаваĐŊŅ‚Đ°ĐļĐĩĐŊŅ– Ņ€ĐĩŅŅƒŅ€ŅĐ¸.", "upload_to_immich": "ЗаваĐŊŅ‚Đ°ĐļĐ¸Ņ‚Đ¸ в Immich ({count})", "uploading": "ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", + "uploading_media": "ВиĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ", "url": "URL", "usage": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ", "use_biometric": "ВиĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ĐąŅ–ĐžĐŧĐĩŅ‚Ņ€Ņ–ŅŽ", @@ -1911,6 +2084,7 @@ "user_usage_stats_description": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ŅŅ‚Đ°Ņ‚Đ¸ŅŅ‚Đ¸Đē҃ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ аĐēĐ°ŅƒĐŊŅ‚Đ°", "username": "ІĐŧ'Ņ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "users": "ĐšĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–", + "users_added_to_album_count": "{count, plural, one {# ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°} few {# ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–} many {# ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛} other {# ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛}} дОдаĐŊĐž Đ´Đž аĐģŅŒĐąĐžĐŧ҃", "utilities": "ĐŖŅ‚Đ¸ĐģŅ–Ņ‚Đ¸", "validate": "ПĐĩŅ€ĐĩĐ˛Ņ–Ņ€Đ¸Ņ‚Đ¸", "validate_endpoint_error": "Đ‘ŅƒĐ´ŅŒ ĐģĐ°ŅĐēа, ввĐĩĐ´Ņ–Ņ‚ŅŒ Đ´Ņ–ĐšŅĐŊ҃ URL-Đ°Đ´Ņ€Đĩҁ҃", @@ -1929,6 +2103,7 @@ "view_album": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ аĐģŅŒĐąĐžĐŧ", "view_all": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ŅƒŅŅ–", "view_all_users": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ Đ˛ŅŅ–Ņ… ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛", + "view_details": "ДĐĩŅ‚Đ°ĐģҌĐŊŅ–ŅˆĐĩ", "view_in_timeline": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ в Ņ…Ņ€ĐžĐŊĐžĐģĐžĐŗŅ–Ņ—", "view_link": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", "view_links": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ", @@ -1936,6 +2111,7 @@ "view_next_asset": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊиК Ņ€ĐĩŅŅƒŅ€Ņ", "view_previous_asset": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš Ņ€ĐĩŅŅƒŅ€Ņ", "view_qr_code": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ QR-ĐēОд", + "view_similar_photos": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ŅŅ…ĐžĐļŅ– Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Ņ—", "view_stack": "ПĐĩŅ€ĐĩĐŗĐģŅĐ´ ҁ҂ĐĩĐē҃", "view_user": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Đ°", "viewer_remove_from_stack": "ВидаĐģĐ¸Ņ‚Đ¸ ĐˇŅ– ҁ҂ĐĩĐē҃", @@ -1954,5 +2130,6 @@ "yes": "ĐĸаĐē", "you_dont_have_any_shared_links": "ĐŖ Đ˛Đ°Ņ ĐŊĐĩĐŧĐ°Ņ” ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… ĐŋĐžŅĐ¸ĐģаĐŊҌ", "your_wifi_name": "Назва Đ˛Đ°ŅˆĐžŅ— Wi-Fi ĐŧĐĩŅ€ĐĩĐļŅ–", - "zoom_image": "Đ—ĐąŅ–ĐģŅŒŅˆĐ¸Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ" + "zoom_image": "Đ—ĐąŅ–ĐģŅŒŅˆĐ¸Ņ‚Đ¸ ĐˇĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ", + "zoom_to_bounds": "Đ—ĐąŅ–ĐģŅŒŅˆĐ¸Ņ‚Đ¸ ĐŧĐ°ŅŅˆŅ‚Đ°Đą Đ´Đž ĐŧĐĩĐļ" } diff --git a/i18n/ur.json b/i18n/ur.json index d4a51b7ad1..357d0b6304 100644 --- a/i18n/ur.json +++ b/i18n/ur.json @@ -4,16 +4,17 @@ "account_settings": "Ø§ÚŠØ§Ø¤Ų†Ųš ÚŠÛŒ ØĒØąØĒیباØĒ", "acknowledge": "ØĒØŗŲ„ÛŒŲ… ÚŠØąŲ†Ø§", "action": "ØšŲ…Ų„", - "action_common_update": "Ø§Ųž ÚˆÛŒŲš", + "action_common_update": "Ø§ŲžÚˆÛŒŲš ÚŠØąÛŒÚē", "actions": "Ø§ØšŲ…Ø§Ų„", "active": "ŲØšØ§Ų„", "activity": "ØŗØąÚ¯ØąŲ…ÛŒ", "activity_changed": "ØŗØąÚ¯ØąŲ…ÛŒ {enabled, select, true {ŲØšØ§Ų„ ہے} other {ØēÛŒØą ŲØšØ§Ų„ ہے}}", "add": "Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_a_description": "ØĒ؁ØĩÛŒŲ„ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_a_location": "ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", + "add_a_location": "Ų…Ų‚Ø§Ų… Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_a_name": "Ų†Ø§Ų… ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", "add_a_title": "ØšŲ†ŲˆØ§Ų† ڊا Ø§Ų†Ø¯ØąØ§ØŦ ÚŠØąÛŒÚē", + "add_birthday": "ØŗØ§Ų„Ú¯ØąÛ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_endpoint": "Ø§ÛŒŲ†Úˆ ŲžŲˆØ§ØĻŲ†Ųš Ø¯ØąØŦ ÚŠØąÛŒÚē", "add_exclusion_pattern": "ØŽØ§ØąØŦ ÚŠØąŲ†Û’ ڊا Ų†Ų…ŲˆŲ†Û Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_import_path": "Ø¯ØąØĸŲ…Ø¯ ڊا ØąØ§ØŗØĒہ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", @@ -22,26 +23,31 @@ "add_partner": "ØŗØ§ØĒÚžÛŒ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_path": "ØąØ§ØŗØĒہ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_photos": "ØĒØĩØ§ŲˆÛŒØą Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "add_to": "Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚēâ€Ļ", + "add_tag": "ŲšÛŒÚ¯ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", + "add_to": "Ø§Øŗ Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚēâ€Ļ", "add_to_album": "Ø§Ų„Ø¨Ų… Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_to_album_bottom_sheet_added": "{album} Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąØ¯ÛŒØ§Ú¯ÛŒØ§", - "add_to_album_bottom_sheet_already_exists": "ŲžÛŲ„Û’ ہی {album} Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ہے", + "add_to_album_bottom_sheet_already_exists": "ŲžÛŲ„Û’ ØŗÛ’ ہی {album} Ų…ÛŒÚē Ų…ŲˆØŦŲˆØ¯ ہے", "add_to_shared_album": "Ų…Ø´ØĒØąÚŠÛ Ø§Ų„Ø¨Ų… Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", "add_url": "URL Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē", - "added_to_archive": "Ų…Ø­ŲŲˆØ¸ شدہ Ø¯ØŗØĒØ§ŲˆÛŒØ˛Ø§ØĒ Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ڊیا گیا", - "added_to_favorites": "ŲžØŗŲ†Ø¯ÛŒØ¯Û Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ڊیا گیا", + "added_to_archive": "ØĸØąÚŠØ§ØĻÛŒŲˆ Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØą دیا گیا", + "added_to_favorites": "ŲžØŗŲ†Ø¯ÛŒØ¯Û Ų…ÛŒÚē Ø´Ø§Ų…Ų„ ÚŠØąØ¯ÛŒØ§ گیا", "added_to_favorites_count": "ŲžØŗŲ†Ø¯ÛŒØ¯Û Ų…ÛŒÚē {count, number} Ø´Ø§Ų…Ų„ ڊیے Ú¯ØĻے", "admin": { "add_exclusion_pattern_description": "Ø§ØŽØąØ§ØŦ ÚŠÛ’ Ų†Ų…ŲˆŲ†Û’ Ø´Ø§Ų…Ų„ ÚŠØąÛŒÚē۔ *، **، Ø§ŲˆØą ? ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąØĒے ÛŲˆØĻے Globbing ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ڊیا ØŦا ØŗÚŠØĒا ہے۔ \"RAW\" Ų†Ø§Ų…ÛŒ ÚŠØŗÛŒ بڞی ڈاØĻØąÛŒÚŠŲšØąÛŒ Ų…ÛŒÚē ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/Raw/**\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ \".tif\" ØŗÛ’ ØŽØĒŲ… ÛŲˆŲ†Û’ ŲˆØ§Ų„ÛŒ ØĒŲ…Ø§Ų… ŲØ§ØĻŲ„ŲˆÚē ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"**/*.tif\" Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔ ÚŠØŗÛŒ Ų…ØˇŲ„Ų‚ ØąØ§ØŗØĒے ÚŠŲˆ Ų†Ø¸Øą Ø§Ų†Ø¯Ø§Ø˛ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ \"/path/to/ignore/**\" ڊا Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē۔", + "admin_user": "Ø§ÛŒÚˆŲ…Ų† ØĩØ§ØąŲ", "asset_offline_description": "Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ڊا یہ Ø¨ÛŒØąŲˆŲ†ÛŒ اØĢاØĢہ اب ÚˆØŗÚŠ ŲžØą Ų†ÛÛŒÚē Ų…Ų„Ø§ Ø§ŲˆØą Ø§ØŗÛ’ ÚŠŲˆÚ‘Û’ Ø¯Ø§Ų† Ų…ÛŒÚē ÚˆØ§Ų„ دیا گیا ہے۔ Ø§Ú¯Øą ŲØ§ØĻŲ„ Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠÛ’ Ø§Ų†Ø¯Øą Ų…Ų†ØĒŲ‚Ų„ ÚŠÛŒ Ú¯ØĻی ØĒڞی، ØĒ؈ Ų†ØĻے Ų…ØĒØšŲ„Ų‚Û اØĢاØĢے ÚŠÛ’ Ų„ÛŒÛ’ Ø§ŲžŲ†ÛŒ ŲšØ§ØĻŲ… Ų„Ø§ØĻŲ† چیڊ ÚŠØąÛŒÚē۔ Ø§Øŗ اØĢاØĢے ÚŠŲˆ Ø¨Ø­Ø§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ Ø¨ØąØ§Û ÚŠØąŲ… ÛŒŲ‚ÛŒŲ†ÛŒ Ø¨Ų†Ø§ØĻیÚē ڊہ Ų†ÛŒÚ†Û’ دیے Ú¯ØĻے ŲØ§ØĻŲ„ ÚŠÛ’ ØąØ§ØŗØĒے ØĒÚŠ Immich ÚŠŲˆ ØąØŗØ§ØĻی حاØĩŲ„ ہے Ø§ŲˆØą Ų„Ø§ØĻØ¨ØąÛŒØąÛŒ ÚŠŲˆ Ø§ØŗÚŠÛŒŲ† ÚŠØąÛŒÚē۔", - "authentication_settings": "ØĒ؈ØĢÛŒŲ‚ ÚŠÛŒ ØĒØąØĒیباØĒ", - "authentication_settings_description": "ŲžØ§Øŗ ŲˆØąÚˆØŒ OAuth، Ø§ŲˆØą ØĒØĩØ¯ÛŒŲ‚ ÚŠÛŒ Ø¯ÛŒÚ¯Øą ØĒØąØĒیباØĒ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē", + "authentication_settings": "ØĒØĩØ¯ÛŒŲ‚ ÚŠÛŒ ØĒØąØĒیباØĒ", + "authentication_settings_description": "ŲžØ§Øŗ ŲˆØąÚˆØŒ OAuth، Ø§ŲˆØą Ø¯ÛŒÚ¯Øą ØĒØĩØ¯ÛŒŲ‚ÛŒ ØĒØąØĒیباØĒ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē", "authentication_settings_disable_all": "ڊیا ØĸŲž ŲˆØ§Ų‚ØšÛŒ Ų„Ø§Ú¯ Ø§Ų† ÚŠÛ’ ØĒŲ…Ø§Ų… ØˇØąÛŒŲ‚ŲˆÚē ÚŠŲˆ ØēÛŒØą ŲØšØ§Ų„ ÚŠØąŲ†Ø§ چاہØĒے ہیÚ稟 Ų„Ø§Ú¯ Ø§Ų† Ų…ÚŠŲ…Ų„ ØˇŲˆØą ŲžØą ØēÛŒØą ŲØšØ§Ų„ ÛŲˆ ØŦاØĻے گا۔", - "authentication_settings_reenable": "Ø¯ŲˆØ¨Ø§ØąÛ ŲØšØ§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ ØŗØąŲˆØą ÚŠŲ…Ø§Ų†Úˆ Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē", + "authentication_settings_reenable": "Ø¯ŲˆØ¨Ø§ØąÛ ŲØšØ§Ų„ ÚŠØąŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ØŒ ایڊ ØŗØąŲˆØą ÚŠŲ…Ø§Ų†Úˆ Ø§ØŗØĒØšŲ…Ø§Ų„ ÚŠØąÛŒÚē", "background_task_job": "ŲžØŗ Ų…Ų†Ø¸Øą ÚŠÛ’ ÚŠØ§Ų…", - "backup_database": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ڊا ÚˆŲ…Ųž Ø¨Ų†Ø§ØĻیÚē", - "backup_database_enable_description": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠŲˆ ŲØšØ§Ų„ ÚŠØąÛŒÚē", - "backup_keep_last_amount": "ØąÚŠÚžŲ†Û’ ÚŠÛ’ Ų„ÛŒÛ’ ŲžÚ†ÚžŲ„Û’ ÚˆŲ…Ųž ÚŠÛŒ Ų…Ų‚Ø¯Ø§Øą", + "backup_database": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ڊا بیڊ Ø§Ųž Ø¨Ų†Ø§ØĻیÚē", + "backup_database_enable_description": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚŠÛ’ بیڊ Ø§Ųž ÚŠŲˆ ŲØšØ§Ų„ ÚŠØąÛŒÚē", + "backup_keep_last_amount": "ŲžØąØ§Ų†Û’ بیڊ Ø§Ųž ÚŠŲˆ ØąÚŠÚžŲ†Û’ ÚŠÛŒ Ų…Ų‚Ø¯Ø§Øą", + "backup_onboarding_1_description": "Øĸ؁ ØŗØ§ØĻŲš ÚŠØ§ŲžÛŒ ÚŠŲ„Ø§Ø¤Úˆ Ų…ÛŒÚē یا ÚŠØŗÛŒ Ø§ŲˆØą Ų…Ų‚Ø§Ų… ŲžØąÛ”", + "backup_onboarding_2_description": "Ų…ØŽØĒ؄؁ ØĸŲ„Ø§ØĒ ŲžØą Ų…Ų‚Ø§Ų…ÛŒ ÚŠØ§ŲžÛŒØ§Úē۔ Ø§Øŗ Ų…ÛŒÚē Ø¨Ų†ÛŒØ§Ø¯ÛŒ ŲØ§ØĻŲ„ÛŒÚē Ø§ŲˆØą Ų…Ų‚Ø§Ų…ÛŒ ØˇŲˆØą ŲžØą Ø§Ų† ŲØ§ØĻŲ„ŲˆÚē ڊا بیڊ Ø§Ųž Ø´Ø§Ų…Ų„ ہے۔", + "backup_onboarding_3_description": "اØĩŲ„ ŲØ§ØĻŲ„ŲˆÚē ØŗŲ…ÛŒØĒ ØĸŲž ÚŠÛ’ ÚˆÛŒŲšØ§ ÚŠÛŒ ÚŠŲ„ ÚŠØ§ŲžÛŒØ§Úē۔ Ø§Øŗ Ų…ÛŒÚē 1 Øĸ؁ ØŗØ§ØĻŲš ÚŠØ§ŲžÛŒ Ø§ŲˆØą 2 Ų…Ų‚Ø§Ų…ÛŒ ÚŠØ§ŲžÛŒØ§Úē Ø´Ø§Ų…Ų„ ہیÚē۔", "backup_settings": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠÛŒ ØĒØąØĒیباØĒ", "backup_settings_description": "ÚˆÛŒŲšØ§ Ø¨ÛŒØŗ ÚˆŲ…Ųž ÚŠÛŒ ØĒØąØĒیباØĒ ڊا Ų†Ø¸Ų… ÚŠØąÛŒÚē۔ Ų†ŲˆŲš: Ø§Ų† Ų…Ų„Ø§Ø˛Ų…ØĒ؈Úē ÚŠÛŒ Ų†Ú¯ØąØ§Ų†ÛŒ Ų†ÛÛŒÚē ÚŠÛŒ ØŦاØĒی ہے Ø§ŲˆØą ØĸŲž ÚŠŲˆ Ų†Ø§ÚŠØ§Ų…ÛŒ ÚŠÛŒ Ø§ØˇŲ„Ø§Øš Ų†ÛÛŒÚē Ø¯ÛŒ ØŦاØĻے گی", "cleared_jobs": "Ų…Ų„Ø§Ø˛Ų…ØĒیÚē Ø§Øŗ ÚŠÛ’ Ų„ÛŒÛ’ ØĩØ§Ų ÚŠÛŒ Ú¯ØĻیÚē: {job}", diff --git a/i18n/vi.json b/i18n/vi.json index 5890ef87db..b9ee8cfcc9 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -5,34 +5,40 @@ "acknowledge": "Ghi nháē­n", "action": "Hành đáģ™ng", "action_common_update": "Cáē­p nháē­t", - "actions": "CÃĄc hành đáģ™ng", + "actions": "hành đáģ™ng", "active": "Đang hoáēĄt đáģ™ng", "activity": "HoáēĄt đáģ™ng", "activity_changed": "HoáēĄt đáģ™ng Ä‘ÃŖ đưáģŖc {enabled, select, true {báē­t} other {táē¯t}}", "add": "ThÃĒm", "add_a_description": "ThÃĒm mô táēŖ", - "add_a_location": "ThÃĒm váģ‹ trí", + "add_a_location": "ThÃĒm đáģ‹a điáģƒm", "add_a_name": "ThÃĒm tÃĒn", "add_a_title": "ThÃĒm tiÃĒu đáģ", - "add_endpoint": "ThÃĒm đáģ‹a cháģ‰ mÃĄy cháģ§", + "add_birthday": "ThÃĒm ngày sinh", + "add_endpoint": "ThÃĒm endpoint", "add_exclusion_pattern": "ThÃĒm quy táē¯c loáēĄi tráģĢ", "add_import_path": "ThÃĒm đưáģng dáēĢn nháē­p", - "add_location": "ThÃĒm váģ‹ trí", + "add_location": "ThÃĒm đáģ‹a điáģƒm", "add_more_users": "ThÃĒm ngưáģi dÚng", "add_partner": "ThÃĒm ngưáģi thÃĸn", "add_path": "ThÃĒm đưáģng dáēĢn", "add_photos": "ThÃĒm áēŖnh", + "add_tag": "ThÃĒm tháēģ", "add_to": "ThÃĒm vàoâ€Ļ", "add_to_album": "ThÃĒm vào album", - "add_to_album_bottom_sheet_added": "ThÃĒm vào {album}", + "add_to_album_bottom_sheet_added": "ÄÃŖ thÃĒm vào {album}", "add_to_album_bottom_sheet_already_exists": "ÄÃŖ cÃŗ sáēĩn trong {album}", + "add_to_album_toggle": "Báē­t táē¯t tÚy cháģn cho {album}", + "add_to_albums": "ThÃĒm vào albums", + "add_to_albums_count": "ÄÃŖ thÃĒm {count} vào albums", "add_to_shared_album": "ThÃĒm vào album chia sáēģ", "add_url": "ThÃĒm URL", - "added_to_archive": "ÄÃŖ thÃĒm vào Kho lưu tráģ¯", - "added_to_favorites": "ÄÃŖ thÃĒm vào MáģĨc yÃĒu thích", - "added_to_favorites_count": "ÄÃŖ thÃĒm {count, number} vào MáģĨc yÃĒu thích", + "added_to_archive": "ÄÃŖ lưu tráģ¯", + "added_to_favorites": "ÄÃŖ thích", + "added_to_favorites_count": "ÄÃŖ thích {count, number} máģĨc", "admin": { "add_exclusion_pattern_description": "ThÃĒm quy táē¯c loáēĄi tráģĢ. Háģ— tráģŖ sáģ­ dáģĨng kÃŊ táģą *, **, và ?. Đáģƒ báģ qua táēĨt cáēŖ cÃĄc táē­p tin báēĨt káģŗ trong thư máģĨc tÃĒn \"Raw\", hÃŖy dÚng \"**/Raw/**\". Đáģƒ báģ qua cÃĄc táē­p tin cÃŗ đuôi \".tif\", hÃŖy dÚng \"**/*.tif\". Đáģƒ báģ qua máģ™t đưáģng dáēĢn cáģ‘ Ä‘áģ‹nh, hÃŖy dÚng \"/path/to/ignore/**\".", + "admin_user": "Ngưáģi dÚng quáēŖn tráģ‹", "asset_offline_description": "áēĸnh thư viáģ‡n bÃĒn ngoài này không cÃ˛n trÃĒn áģ• Ä‘ÄŠa và Ä‘ÃŖ báģ‹ chuyáģƒn vào thÚng rÃĄc. Náēŋu áēŖnh Ä‘ÃŖ báģ‹ di chuyáģƒn trong thư viáģ‡n, kiáģƒm tra dÃ˛ng tháģi gian cáģ§a báēĄn đáģƒ tÃŦm áēŖnh máģ›i tÆ°ÆĄng áģŠng. Đáģƒ khôi pháģĨc, hÃŖy đáēŖm báēŖo Immich cÃŗ tháģƒ truy cáē­p đưáģng dáēĢn áēŖnh bÃĒn dưáģ›i và quÊt láēĄi thư viáģ‡n.", "authentication_settings": "Đăng nháē­p", "authentication_settings_description": "QuáēŖn lÃŊ máē­t kháēŠu, OAuth và cÃĄc cài đáēˇt xÃĄc tháģąc khÃĄc", @@ -461,7 +467,6 @@ "assets": "CÃĄc táē­p tin", "assets_added_count": "ÄÃŖ thÃĒm {count, plural, one {# máģĨc} other {# máģĨc}}", "assets_added_to_album_count": "ÄÃŖ thÃĒm {count, plural, one {# máģĨc} other {# máģĨc}} vào album", - "assets_added_to_name_count": "ÄÃŖ thÃĒm {count, plural, one {# máģĨc} other {# máģĨc}} vào {hasName, select, true {{name}} other {album máģ›i}}", "assets_count": "{count, plural, one {# máģĨc} other {# máģĨc}}", "assets_deleted_permanently": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {count} máģĨc", "assets_deleted_permanently_from_server": "ÄÃŖ xoÃĄ vÄŠnh viáģ…n {count} máģĨc kháģi mÃĄy cháģ§ Immich", @@ -478,7 +483,7 @@ "assets_trashed_count": "ÄÃŖ chuyáģƒn {count, plural, one {# máģĨc} other {# máģĨc}} vào thÚng rÃĄc", "assets_trashed_from_server": "ÄÃŖ chuyáģƒn {count} máģĨc táģĢ mÃĄy cháģ§ Immich vào thÚng rÃĄc", "assets_were_part_of_album_count": "{count, plural, one {MáģĨc Ä‘ÃŖ} other {CÃĄc máģĨc Ä‘ÃŖ}} cÃŗ trong album", - "authorized_devices": "Thiáēŋt báģ‹ Ä‘Æ°áģŖc áģ§y quyáģn", + "authorized_devices": "Thiáēŋt báģ‹", "automatic_endpoint_switching_subtitle": "Káēŋt náģ‘i náģ™i báģ™ qua Wi-Fi đưáģŖc cháģ‰ Ä‘áģ‹nh khi káēŋt náģ‘i đưáģŖc và sáģ­ dáģĨng cÃĄc káēŋt náģ‘i thay tháēŋ áģŸ nÆĄi khÃĄc", "automatic_endpoint_switching_title": "Táģą Ä‘áģ™ng chuyáģƒn đáģ•i đáģ‹a cháģ‰ mÃĄy cháģ§", "autoplay_slideshow": "Táģą Ä‘áģ™ng phÃĄt trÃŦnh chiáēŋu", @@ -486,6 +491,7 @@ "back_close_deselect": "Quay láēĄi, Ä‘Ãŗng, hoáēˇc báģ cháģn", "background_location_permission": "Quyáģn truy cáē­p váģ‹ trí áģŸ náģn", "background_location_permission_content": "Đáģƒ chuyáģƒn đáģ•i máēĄng khi cháēĄy áģŸ cháēŋ đáģ™ náģn, Immich *luôn* pháēŖi cÃŗ quyáģn truy cáē­p váģ‹ trí chính xÃĄc đáģƒ áģŠng dáģĨng cÃŗ tháģƒ Ä‘áģc tÃĒn máēĄng Wi-Fi", + "backup": "Sao lưu", "backup_album_selection_page_albums_device": "Album trÃĒn thiáēŋt báģ‹ ({count})", "backup_album_selection_page_albums_tap": "NháēĨn đáģƒ cháģn, nháēĨn đÃēp đáģƒ báģ qua", "backup_album_selection_page_assets_scatter": "áēĸnh cÃŗ tháģƒ cÃŗ trong nhiáģu album khÃĄc nhau. Trong quÃĄ trÃŦnh sao lưu, báēĄn cÃŗ tháģƒ cháģn đáģƒ sao lưu táēĨt cáēŖ cÃĄc album hoáēˇc cháģ‰ máģ™t sáģ‘ album nháēĨt đáģ‹nh.", @@ -534,7 +540,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 {used} cáģ§a {total}", + "backup_controller_page_storage_format": "ÄÃŖ dÚng {used} cáģ§a {total}", "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", @@ -548,6 +554,7 @@ "backup_manual_title": "TráēĄng thÃĄi táēŖi lÃĒn", "backup_options_page_title": "CÃĄc tÚy cháģn sao lưu", "backup_setting_subtitle": "QuáēŖn lÃŊ cài đáēˇt táēŖi lÃĒn áģŸ cháēŋ đáģ™ náģn và khi đang máģŸ", + "backup_settings_subtitle": "Cài đáēˇt viáģ‡c táēŖi lÃĒn", "backward": "LÚi láēĄi", "biometric_auth_enabled": "ÄÃŖ báē­t xÃĄc tháģąc sinh tráē¯c háģc", "biometric_locked_out": "BáēĄn Ä‘ÃŖ báģ‹ khÃŗa xÃĄc tháģąc báēąng sinh tráē¯c háģc", @@ -583,6 +590,7 @@ "cancel": "Háģ§y", "cancel_search": "Háģ§y tÃŦm kiáēŋm", "canceled": "Huáģˇ báģ", + "canceling": "Đang háģ§y", "cannot_merge_people": "Không tháģƒ háģŖp nháēĨt ngưáģi", "cannot_undo_this_action": "BáēĄn không tháģƒ hoàn tÃĄc hành đáģ™ng này!", "cannot_update_the_description": "Không tháģƒ cáē­p nháē­t mô táēŖ", @@ -590,7 +598,7 @@ "cast_description": "CáēĨu hÃŦnh cÃĄc thiáēŋt báģ‹ chiáēŋu kháēŖ dáģĨng", "change_date": "Thay đáģ•i ngày", "change_description": "Thay đáģ•i mô táēŖ", - "change_display_order": "Thay đáģ•i tháģŠ táģą hiáģƒu tháģ‹", + "change_display_order": "Sáē¯p xáēŋp hiáģƒn tháģ‹", "change_expiration_time": "Thay đáģ•i tháģi gian háēŋt háēĄn", "change_location": "Thay đáģ•i váģ‹ trí", "change_name": "Thay đáģ•i tÃĒn", @@ -669,7 +677,7 @@ "copy_to_clipboard": "Sao chÊp vào clipboard", "country": "Quáģ‘c gia", "cover": "BÃŦa", - "covers": "CÃĄc bÃŦa", + "covers": "áēĸnh bÃŦa", "create": "TáēĄo", "create_album": "TáēĄo album", "create_album_page_untitled": "Không tiÃĒu đáģ", @@ -683,6 +691,7 @@ "create_new_user": "TáēĄo ngưáģi dÚng máģ›i", "create_shared_album_page_share_add_assets": "THÊM áēĸNH", "create_shared_album_page_share_select_photos": "Cháģn áēŖnh", + "create_shared_link": "Chia sáēģ qua liÃĒn káēŋt", "create_tag": "TáēĄo tháēģ", "create_tag_description": "TáēĄo tháēģ máģ›i. Váģ›i cÃĄc tháēģ láģ“ng nhau, vui lÃ˛ng nháē­p đưáģng dáēĢn đáē§y đáģ§ cáģ§a tháēģ bao gáģ“m dáēĨu gáēĄch chÊo.", "create_user": "TáēĄo ngưáģi dÚng", @@ -695,10 +704,11 @@ "current_server_address": "Đáģ‹a cháģ§ mÃĄy cháģ§ hiáģ‡n táēĄi", "custom_locale": "Ngôn ngáģ¯ và khu váģąc tÚy cháģ‰nh", "custom_locale_description": "Đáģ‹nh dáēĄng ngày và sáģ‘ dáģąa trÃĒn ngôn ngáģ¯ và khu váģąc", + "custom_url": "URL tÚy cháģ‰nh", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Táģ‘i", - "darkTheme": "Chuyáģƒn đáģ•i cháģ§ Ä‘áģ táģ‘i", + "dark_theme": "Đáģ•i giao diáģ‡n", "date_after": "Ngày sau", "date_and_time": "Ngày và giáģ", "date_before": "Ngày trưáģ›c", @@ -711,8 +721,8 @@ "deduplication_criteria_2": "Sáģ‘ lưáģŖng dáģ¯ liáģ‡u EXIF", "deduplication_info": "Thông tin loáēĄi báģ dáģ¯ liáģ‡u trÚng láēˇp", "deduplication_info_description": "Đáģƒ táģą Ä‘áģ™ng cháģn trưáģ›c và loáēĄi báģ cÃĄc máģĨc trÚng láēˇp hàng loáēĄt, chÃēng tôi sáēŊ xem xÊt dáģąa trÃĒn:", - "default_locale": "Ngôn ngáģ¯ và khu váģąc máēˇc đáģ‹nh", - "default_locale_description": "Đáģ‹nh dáēĄng ngày và sáģ‘ dáģąa trÃĒn ngôn ngáģ¯ trÃŦnh duyáģ‡t cáģ§a báēĄn", + "default_locale": "Đáģ‹nh dáēĄng ngày giáģ", + "default_locale_description": "Dáģąa theo trÃŦnh duyáģ‡t cáģ§a báēĄn", "delete": "XÃŗa", "delete_album": "XÃŗa album", "delete_api_key_prompt": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa khÃŗa API này không?", @@ -817,7 +827,7 @@ "enqueued": "ÄÃŖ xáēŋp hàng", "enter_wifi_name": "Nháē­p tÃĒn Wi-Fi", "enter_your_pin_code": "Nháē­p mÃŖ PIN cáģ§a báēĄn", - "enter_your_pin_code_subtitle": "Nháē­p mÃŖ PIN cáģ§a báēĄn đáģƒ truy cáē­p Thư máģĨc báēŖo máē­t", + "enter_your_pin_code_subtitle": "Nháē­p mÃŖ PIN cáģ§a báēĄn đáģƒ truy cáē­p thư máģĨc KhÃŗa", "error": "Láģ—i", "error_change_sort_album": "Thay đáģ•i tháģŠ táģą sáē¯p xáēŋp album tháēĨt báēĄi", "error_delete_face": "Láģ—i khi xÃŗa khuôn máēˇt kháģi áēŖnh", @@ -948,12 +958,9 @@ "exif": "Exif", "exif_bottom_sheet_description": "ThÃĒm mô táēŖ...", "exif_bottom_sheet_details": "CHI TIáēžT", - "exif_bottom_sheet_location": "VáģŠ TRÍ", + "exif_bottom_sheet_location": "ĐáģŠA ĐIáģ‚M", "exif_bottom_sheet_people": "MáģŒI NGƯáģœI", "exif_bottom_sheet_person_add_person": "ThÃĒm tÃĒn", - "exif_bottom_sheet_person_age_months": "{months} thÃĄng tuáģ•i", - "exif_bottom_sheet_person_age_year_months": "1 tuáģ•i, {months} thÃĄng", - "exif_bottom_sheet_person_age_years": "{years} tuáģ•i", "exit_slideshow": "ThoÃĄt trÃŦnh chiáēŋu", "expand_all": "MáģŸ ráģ™ng táēĨt cáēŖ", "experimental_settings_new_asset_list_subtitle": "Đang trong quÃĄ trÃŦnh phÃĄt triáģƒn", @@ -1016,7 +1023,7 @@ "group_year": "NhÃŗm theo năm", "haptic_feedback_switch": "Báē­t pháēŖn háģ“i haptic", "haptic_feedback_title": "PháēŖn háģ“i Hapic", - "has_quota": "CÃŗ háēĄn máģŠc", + "has_quota": "HáēĄn máģŠc", "header_settings_add_header_tip": "ThÃĒm Header", "header_settings_field_validator_msg": "Trưáģng này không đưáģŖc đáģƒ tráģ‘ng", "header_settings_header_name_input": "TÃĒn Header", @@ -1042,8 +1049,8 @@ "home_page_favorite_err_local": "Không tháģƒ yÃĒu thích áēŖnh cáģĨc báģ™, báģ qua", "home_page_favorite_err_partner": "Không tháģƒ yÃĒu thích áēŖnh cáģ§a ngưáģi thÃĸn, báģ qua", "home_page_first_time_notice": "Náēŋu đÃĸy là láē§n đáē§u báēĄn dÚng áģŠng dáģĨng, hÃŖy cháģn máģ™t album sao lưu đáģƒ dÃ˛ng tháģi gian cÃŗ tháģƒ hiáģƒn tháģ‹ áēŖnh và video cáģ§a báēĄn", - "home_page_locked_error_local": "Không tháģƒ di chuyáģƒn áēŖnh cáģĨc báģ™ Ä‘áēŋn thư máģĨc báēŖo máē­t, báģ qua", - "home_page_locked_error_partner": "Không tháģƒ di chuyáģƒn áēŖnh ngưáģi thÃĸn đáēŋn thư máģĨc báēŖo máē­t, báģ qua", + "home_page_locked_error_local": "Không tháģƒ di chuyáģƒn áēŖnh cáģĨc báģ™ Ä‘áēŋn thư máģĨc KhÃŗa, báģ qua", + "home_page_locked_error_partner": "Không tháģƒ di chuyáģƒn áēŖnh ngưáģi thÃĸn đáēŋn thư máģĨc KhÃŗa, báģ qua", "home_page_share_err_local": "Không tháģƒ chia sáēģ áēŖnh cáģĨc báģ™ qua liÃĒn káēŋt, báģ qua", "home_page_upload_err_limit": "Cháģ‰ cÃŗ tháģƒ táēŖi lÃĒn táģ‘i đa 30 áēŖnh cÚng máģ™t lÃēc, báģ qua", "host": "MÃĄy cháģ§", @@ -1111,7 +1118,7 @@ "latitude": "VÄŠ đáģ™", "leave": "Ráģi kháģi", "lens_model": "Kiáģƒu tháēĨu kính", - "let_others_respond": "Cho phÊp ngưáģi khÃĄc pháēŖn háģ“i", + "let_others_respond": "Cho phÊp bÃŦnh luáē­n", "level": "CáēĨp đáģ™", "library": "Thư viáģ‡n", "library_options": "TÚy cháģn thư viáģ‡n", @@ -1121,10 +1128,10 @@ "library_page_sort_created": "Máģ›i táēĄo gáē§n đÃĸy", "library_page_sort_last_modified": "Sáģ­a đáģ•i láē§n cuáģ‘i", "library_page_sort_title": "TiÃĒu đáģ album", + "licenses": "GiáēĨy phÊp", "light": "SÃĄng", "like_deleted": "ÄÃŖ xoÃĄ thích", "link_motion_video": "LiÃĒn káēŋt video chuyáģƒn đáģ™ng", - "link_options": "TÚy cháģn liÃĒn káēŋt", "link_to_oauth": "LiÃĒn káēŋt đáēŋn OAuth", "linked_oauth_account": "Tài khoáēŖn OAuth Ä‘ÃŖ liÃĒn káēŋt", "list": "Danh sÃĄch", @@ -1140,7 +1147,7 @@ "location_picker_longitude_error": "Nháē­p kinh đáģ™ háģŖp láģ‡", "location_picker_longitude_hint": "Nháē­p kinh đáģ™ cáģ§a báēĄn", "lock": "KhÃŗa", - "locked_folder": "Thư máģĨc báēŖo máē­t", + "locked_folder": "KhÃŗa", "log_out": "Đăng xuáēĨt", "log_out_all_devices": "Đăng xuáēĨt táēĨt cáēŖ cÃĄc thiáēŋt báģ‹", "logged_out_all_devices": "TáēĨt cáēŖ cÃĄc thiáēŋt báģ‹ Ä‘ÃŖ đăng xuáēĨt", @@ -1185,7 +1192,6 @@ "manage_your_devices": "QuáēŖn lÃŊ cÃĄc thiáēŋt báģ‹ Ä‘ÃŖ đăng nháē­p cáģ§a báēĄn", "manage_your_oauth_connection": "QuáēŖn lÃŊ káēŋt náģ‘i OAuth cáģ§a báēĄn", "map": "BáēŖn đáģ“", - "map_assets_in_bound": "{count} áēŖnh", "map_assets_in_bounds": "{count} áēŖnh", "map_cannot_get_user_location": "Không tháģƒ xÃĄc đáģ‹nh váģ‹ trí cáģ§a báēĄn", "map_location_dialog_yes": "CÃŗ", @@ -1194,7 +1200,6 @@ "map_location_service_disabled_title": "Dáģ‹ch váģĨ váģ‹ trí báģ‹ vô hiáģ‡u hoÃĄ", "map_marker_for_images": "ÄÃĄnh dáēĨu báēŖn đáģ“ cho hÃŦnh áēŖnh cháģĨp táēĄi {city}, {country}", "map_marker_with_image": "ÄÃĄnh dáēĨu báēŖn đáģ“ váģ›i hÃŦnh áēŖnh", - "map_no_assets_in_bounds": "Không cÃŗ áēŖnh trong khu váģąc này", "map_no_location_permission_content": "Cáē§n quyáģn truy cáē­p váģ‹ trí đáģƒ hiáģƒn tháģ‹ áēŖnh táģĢ váģ‹ trí hiáģ‡n táēĄi cáģ§a báēĄn. BáēĄn cÃŗ muáģ‘n cho phÊp ngay bÃĸy giáģ không?", "map_no_location_permission_title": "áģ¨ng dáģĨng không đưáģŖc phÊp truy cáē­p váģ‹ trí", "map_settings": "Cài đáēˇt báēŖn đáģ“", @@ -1231,15 +1236,17 @@ "merged_people_count": "ÄÃŖ háģŖp nháēĨt {count, plural, one {# ngưáģi} other {# ngưáģi}}", "minimize": "Thu nháģ", "minute": "PhÃēt", + "minutes": "PhÃēt", "missing": "Thiáēŋu", "model": "DÃ˛ng", "month": "ThÃĄng", "monthly_title_text_date_format": "MMMM y", "more": "ThÃĒm", "move": "Di chuyáģƒn", - "move_off_locked_folder": "Di chuyáģƒn ra kháģi thư máģĨc báēŖo máē­t", - "move_to_locked_folder": "Di chuyáģƒn đáēŋn thư máģĨc báēŖo máē­t", - "move_to_locked_folder_confirmation": "áēĸnh và video này sáēŊ báģ‹ xÃŗa kháģi cÃĄc album, cháģ‰ cÃŗ tháģƒ xem đưáģŖc trong thư máģĨc báēŖo máē­t", + "move_off_locked_folder": "Di chuyáģƒn ra kháģi thư máģĨc KhÃŗa", + "move_to_lock_folder_action_prompt": "{count} Ä‘ÃŖ đưáģŖc thÃĒm vào thư máģĨc KhÃŗa", + "move_to_locked_folder": "Di chuyáģƒn đáēŋn thư máģĨc KhÃŗa", + "move_to_locked_folder_confirmation": "áēĸnh và video này sáēŊ báģ‹ xÃŗa kháģi cÃĄc album, cháģ‰ cÃŗ tháģƒ xem đưáģŖc trong thư máģĨc KhÃŗa", "moved_to_archive": "ÄÃŖ di chuyáģƒn {count, plural, one {# áēŖnh} other {# áēŖnh}} đáēŋn lưu tráģ¯", "moved_to_library": "ÄÃŖ di chuyáģƒn {count, plural, one {# áēŖnh} other {# áēŖnh}} đáēŋn thư viáģ‡n", "moved_to_trash": "ÄÃŖ chuyáģƒn vào thÚng rÃĄc", @@ -1249,6 +1256,8 @@ "my_albums": "Album cáģ§a tôi", "name": "TÃĒn", "name_or_nickname": "TÃĒn hoáēˇc biáģ‡t danh", + "network_requirement_photos_upload": "DÚng dáģ¯ liáģ‡u di đáģ™ng sao lưu áēŖnh", + "network_requirement_videos_upload": "DÚng dáģ¯ liáģ‡u di đáģ™ng sao lưu video", "networking_settings": "MáēĄng", "networking_subtitle": "QuáēŖn lÃŊ cÃĄc đáģ‹a cháģ‰ mÃĄy cháģ§", "never": "Không bao giáģ", @@ -1257,7 +1266,7 @@ "new_password": "Máē­t kháēŠu máģ›i", "new_person": "Ngưáģi máģ›i", "new_pin_code": "MÃŖ PIN máģ›i", - "new_pin_code_subtitle": "ĐÃĸy là láē§n đáē§u báēĄn vào thư máģĨc báēŖo máē­t. HÃŖy táēĄo mÃŖ PIN đáģƒ truy cáē­p an toàn", + "new_pin_code_subtitle": "ĐÃĸy là láē§n đáē§u báēĄn vào thư máģĨc KhÃŗa. HÃŖy táēĄo mÃŖ PIN đáģƒ truy cáē­p an toàn", "new_user_created": "Ngưáģi dÚng máģ›i Ä‘ÃŖ đưáģŖc táēĄo", "new_version_available": "CÓ PHIÊN BáēĸN MáģšI", "newest_first": "Máģ›i nháēĨt trưáģ›c", @@ -1275,7 +1284,7 @@ "no_explore_results_message": "TáēŖi thÃĒm áēŖnh lÃĒn đáģƒ khÃĄm phÃĄ báģ™ sưu táē­p cáģ§a báēĄn.", "no_favorites_message": "ThÃĒm áēŖnh yÃĒu thích đáģƒ nhanh chÃŗng tÃŦm tháēĨy nháģ¯ng báģŠc áēŖnh và video đáēšp nháēĨt cáģ§a báēĄn", "no_libraries_message": "TáēĄo máģ™t thư viáģ‡n bÃĒn ngoài đáģƒ xem áēŖnh và video cáģ§a báēĄn", - "no_locked_photos_message": "áēĸnh và video trong thư máģĨc báēŖo máē­t sáēŊ đưáģŖc áēŠn đi và không hiáģƒn tháģ‹ khi báēĄn duyáģ‡t hay tÃŦm kiáēŋm trong thư viáģ‡n.", + "no_locked_photos_message": "áēĸnh và video trong thư máģĨc KhÃŗa sáēŊ đưáģŖc áēŠn đi và không hiáģƒn tháģ‹ khi báēĄn duyáģ‡t hay tÃŦm kiáēŋm trong thư viáģ‡n.", "no_name": "Không cÃŗ tÃĒn", "no_notifications": "Không cÃŗ thông bÃĄo", "no_people_found": "Không cÃŗ ngưáģi nào kháģ›p váģ›i tÃŦm kiáēŋm", @@ -1319,7 +1328,7 @@ "organize_your_library": "Sáē¯p xáēŋp thư viáģ‡n cáģ§a báēĄn", "original": "Gáģ‘c", "other": "KhÃĄc", - "other_devices": "CÃĄc thiáēŋt báģ‹ khÃĄc", + "other_devices": "Thiáēŋt báģ‹ khÃĄc", "other_variables": "CÃĄc tham sáģ‘ khÃĄc", "owned": "SáģŸ háģ¯u", "owner": "Cháģ§ sáģŸ háģ¯u", @@ -1335,7 +1344,7 @@ "partner_page_select_partner": "Cháģn ngưáģi thÃĸn", "partner_page_shared_to_title": "Chia sáēģ váģ›i", "partner_page_stop_sharing_content": "{partner} sáēŊ không tháģƒ truy cáē­p áēŖnh cáģ§a báēĄn.", - "partner_sharing": "Chia sáēģ váģ›i ngưáģi thÃĸn", + "partner_sharing": "Ngưáģi thÃĸn", "partners": "Ngưáģi thÃĸn", "password": "Máē­t kháēŠu", "password_does_not_match": "Máē­t kháēŠu không kháģ›p", @@ -1426,7 +1435,7 @@ "purchase_button_activate": "Kích hoáēĄt", "purchase_button_buy": "Mua", "purchase_button_buy_immich": "Mua Immich", - "purchase_button_never_show_again": "Không hiáģƒn tháģ‹ láēĄi", + "purchase_button_never_show_again": "Không hiáģ‡n láēĄi", "purchase_button_reminder": "Nháē¯c tôi trong 30 ngày", "purchase_button_remove_key": "XÃŗa khÃŗa", "purchase_button_select": "Cháģn", @@ -1435,7 +1444,7 @@ "purchase_individual_description_2": "TráēĄng thÃĄi ngưáģi háģ— tráģŖ", "purchase_individual_title": "CÃĄ nhÃĸn", "purchase_input_suggestion": "CÃŗ khÃŗa sáēŖn pháēŠm? Nháē­p khÃŗa bÃĒn dưáģ›i", - "purchase_license_subtitle": "Mua Immich đáģƒ háģ— tráģŖ sáģą phÃĄt triáģƒn liÃĒn táģĨc cáģ§a dáģ‹ch váģĨ", + "purchase_license_subtitle": "Mua Immich đáģƒ háģ— tráģŖ dáģ‹ch váģĨ liÃĒn táģĨc phÃĄt triáģƒn", "purchase_lifetime_description": "Mua tráģn đáģi", "purchase_option_title": "TÙY CHáģŒN MUA HÀNG", "purchase_panel_info_1": "Viáģ‡c xÃĸy dáģąng Immich táģ‘n nhiáģu tháģi gian và công sáģŠc, và chÃēng tôi cÃŗ cÃĄc káģš sư toàn tháģi gian làm viáģ‡c đáģƒ làm cho nÃŗ táģ‘t nháēĨt cÃŗ tháģƒ. SáģŠ máģ‡nh cáģ§a chÃēng tôi là pháē§n máģm mÃŖ nguáģ“n máģŸ và cÃĄc hoáēĄt đáģ™ng kinh doanh cÃŗ đáēĄo đáģŠc tráģŸ thành nguáģ“n thu nháē­p báģn váģ¯ng cho cÃĄc nhà phÃĄt triáģƒn, đáģ“ng tháģi táēĄo ra máģ™t háģ‡ sinh thÃĄi báēŖo váģ‡ quyáģn riÃĒng tư váģ›i cÃĄc láģąa cháģn thay tháēŋ tháģąc sáģą cho cÃĄc dáģ‹ch váģĨ Ä‘ÃĄm mÃĸy láģŖi dáģĨng ngưáģi dÚng.", @@ -1451,7 +1460,7 @@ "purchase_server_description_2": "TráēĄng thÃĄi ngưáģi háģ— tráģŖ", "purchase_server_title": "MÃĄy cháģ§", "purchase_settings_server_activated": "KhÃŗa sáēŖn pháēŠm mÃĄy cháģ§ Ä‘Æ°áģŖc quáēŖn lÃŊ báģŸi quáēŖn tráģ‹ viÃĒn", - "rating": "Xáēŋp háēĄng sao", + "rating": "ÄÃĄnh giÃĄ sao", "rating_clear": "XÃŗa xáēŋp háēĄng", "rating_count": "{count, plural, one {# sao} other {# sao}}", "rating_description": "Hiáģƒn tháģ‹ xáēŋp háēĄng EXIF trong báēŖng thông tin", @@ -1487,8 +1496,9 @@ "remove_deleted_assets": "LoáēĄi báģ táē­p tin ngoáēĄi tuyáēŋn", "remove_from_album": "XÃŗa kháģi album", "remove_from_favorites": "XÃŗa kháģi MáģĨc yÃĒu thích", - "remove_from_locked_folder": "XÃŗa kháģi thư máģĨc báēŖo máē­t", - "remove_from_locked_folder_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n di chuyáģƒn áēŖnh và video này kháģi thư máģĨc báēŖo máē­t không? ChÃēng sáēŊ hiáģ‡n trong thư viáģ‡n cáģ§a báēĄn.", + "remove_from_lock_folder_action_prompt": "{count} Ä‘ÃŖ đưáģŖc xÃŗa kháģi thư máģĨc KhÃŗa", + "remove_from_locked_folder": "XÃŗa kháģi thư máģĨc KhÃŗa", + "remove_from_locked_folder_confirmation": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n di chuyáģƒn áēŖnh và video này kháģi thư máģĨc KhÃŗa không? ChÃēng sáēŊ hiáģ‡n trong thư viáģ‡n cáģ§a báēĄn.", "remove_from_shared_link": "XÃŗa kháģi liÃĒn káēŋt chia sáēģ", "remove_memory": "XÃŗa káģˇ niáģ‡m", "remove_photo_from_memory": "XÃŗa áēŖnh kháģi káģˇ niáģ‡m này", @@ -1504,7 +1514,7 @@ "rename": "Đáģ•i tÃĒn", "repair": "Sáģ­a cháģ¯a", "repair_no_results_message": "CÃĄc táē­p tin không đưáģŖc theo dÃĩi và báģ‹ máēĨt sáēŊ xuáēĨt hiáģ‡n áģŸ Ä‘Ãĸy", - "replace_with_upload": "Thay tháēŋ báēąng táē­p tin táēŖi lÃĒn", + "replace_with_upload": "Thay tháēŋ táē­p tin khÃĄc", "repository": "Kho lưu tráģ¯", "require_password": "YÃĒu cáē§u máē­t kháēŠu", "require_user_to_change_password_on_first_login": "YÃĒu cáē§u ngưáģi dÚng thay đáģ•i máē­t kháēŠu áģŸ láē§n đáē§u đăng nháē­p", @@ -1522,7 +1532,8 @@ "restored_asset": "áēĸnh Ä‘ÃŖ đưáģŖc khôi pháģĨc", "resume": "Tiáēŋp táģĨc", "retry_upload": "Tháģ­ táēŖi lÃĒn láēĄi", - "review_duplicates": "Xem xÊt cÃĄc máģĨc trÚng láēˇp", + "review_duplicates": "Xem láēĄi cÃĄc máģĨc trÚng láēˇp", + "review_large_files": "Xem láēĄi táē­p tin dung lưáģŖng láģ›n", "role": "Vai trÃ˛", "role_editor": "Ngưáģi cháģ‰nh sáģ­a", "role_viewer": "Ngưáģi xem", @@ -1573,7 +1584,7 @@ "search_page_no_objects": "Không cÃŗ thông tin sáģą váē­t", "search_page_no_places": "Không cÃŗ thông tin đáģ‹a điáģƒm nào", "search_page_screenshots": "áēĸnh màn hÃŦnh", - "search_page_search_photos_videos": "TÃŦm hÃŦnh áēŖnh và video cáģ§a báēĄn", + "search_page_search_photos_videos": "TÃŦm áēŖnh và video cáģ§a báēĄn", "search_page_selfies": "áēĸnh selfie", "search_page_things": "Sáģą váē­t", "search_page_view_all_button": "Xem táēĨt cáēŖ", @@ -1615,9 +1626,9 @@ "send_welcome_email": "Gáģ­i email chào máģĢng", "server_endpoint": "Đáģ‹a cháģ‰ mÃĄy cháģ§", "server_info_box_app_version": "PhiÃĒn báēŖn áģŠng dáģĨng", - "server_info_box_server_url": "Đáģ‹a cháģ‰ mÃĄy cháģ§", + "server_info_box_server_url": "URL mÃĄy cháģ§", "server_offline": "MÃĄy cháģ§ ngoáēĄi tuyáēŋn", - "server_online": "MÃĄy cháģ§ tráģąc tuyáēŋn", + "server_online": "PhiÃĒn báēŖn", "server_privacy": "Quyáģn riÃĒng tư mÃĄy cháģ§", "server_stats": "Tháģ‘ng kÃĒ mÃĄy cháģ§", "server_version": "PhiÃĒn báēŖn mÃĄy cháģ§", @@ -1676,6 +1687,7 @@ "shared_link_clipboard_copied_massage": "ÄÃŖ sao chÊp táģ›i báēŖn ghi táēĄm", "shared_link_clipboard_text": "LiÃĒn káēŋt: {link}\nMáē­t kháēŠu: {password}", "shared_link_create_error": "TáēĄo liÃĒn káēŋt chia sáēģ không thành công", + "shared_link_custom_url_description": "Truy cáē­p liÃĒn káēŋt chia sáēģ váģ›i máģ™t URL tÚy cháģ‰nh", "shared_link_edit_description_hint": "Nháē­p mô táēŖ chia sáēģ", "shared_link_edit_expire_after_option_day": "1 ngày", "shared_link_edit_expire_after_option_days": "{count} ngày", @@ -1701,6 +1713,7 @@ "shared_link_info_chip_metadata": "Dáģ¯ liáģ‡u EXIF", "shared_link_manage_links": "QuáēŖn lÃŊ liÃĒn káēŋt đưáģŖc chia sáēģ", "shared_link_options": "TÚy cháģn liÃĒn káēŋt chia sáēģ", + "shared_link_password_description": "Báē¯t buáģ™c đáģƒ truy cáē­p liÃĒn káēŋt chia sáēģ này", "shared_links": "LiÃĒn káēŋt chia sáēģ", "shared_links_description": "Chia sáēģ áēŖnh và video báēąng liÃĒn káēŋt", "shared_photos_and_videos_count": "{assetCount, plural, other {# áēŖnh & video Ä‘ÃŖ chia sáēģ.}}", @@ -1715,7 +1728,7 @@ "sharing_silver_appbar_create_shared_album": "TáēĄo album chia sáēģ", "sharing_silver_appbar_share_partner": "Chia sáēģ váģ›i ngưáģi thÃĸn", "shift_to_permanent_delete": "nháēĨn ⇧ đáģƒ xÃŗa vÄŠnh viáģ…n áēŖnh", - "show_album_options": "Hiáģƒn tháģ‹ tÚy cháģn album", + "show_album_options": "Hiáģ‡n tÚy cháģn album", "show_albums": "Hiáģƒn tháģ‹ album", "show_all_people": "Hiáģƒn tháģ‹ táēĨt cáēŖ máģi ngưáģi", "show_and_hide_people": "Hiáģƒn tháģ‹ & áēŠn ngưáģi", @@ -1724,13 +1737,13 @@ "show_hidden_people": "Hiáģƒn tháģ‹ ngưáģi báģ‹ áēŠn", "show_in_timeline": "Hiáģƒn tháģ‹ trÃĒn dÃ˛ng tháģi gian", "show_in_timeline_setting_description": "Hiáģƒn tháģ‹ áēŖnh và video táģĢ ngưáģi dÚng này trong dÃ˛ng tháģi gian cáģ§a báēĄn", - "show_keyboard_shortcuts": "Hiáģƒn tháģ‹ phím táē¯t", + "show_keyboard_shortcuts": "Hiáģ‡n phím táē¯t", "show_metadata": "Hiáģƒn tháģ‹ siÃĒu dáģ¯ liáģ‡u", "show_or_hide_info": "Hiáģƒn tháģ‹ hoáēˇc áēŠn thông tin", "show_password": "Hiáģƒn tháģ‹ máē­t kháēŠu", - "show_person_options": "Hiáģƒn tháģ‹ tÚy cháģn ngưáģi", + "show_person_options": "Hiáģ‡n tÚy cháģn ngưáģi", "show_progress_bar": "Hiáģƒn tháģ‹ thanh tiáēŋn trÃŦnh", - "show_search_options": "Hiáģƒn tháģ‹ tÚy cháģn tÃŦm kiáēŋm", + "show_search_options": "Hiáģ‡n tÚy cháģn tÃŦm kiáēŋm", "show_shared_links": "Hiáģƒn tháģ‹ cÃĄc liÃĒn káēŋt đưáģŖc chia sáēģ", "show_slideshow_transition": "Hiáģƒn tháģ‹ hiáģ‡u áģŠng chuyáģƒn tiáēŋp", "show_supporter_badge": "Huy hiáģ‡u ngưáģi áģ§ng háģ™", @@ -1750,6 +1763,7 @@ "sort_created": "Ngày táēĄo", "sort_items": "Sáģ‘ lưáģŖng máģĨc", "sort_modified": "Ngày sáģ­a đáģ•i", + "sort_newest": "áēĸnh máģ›i nháēĨt", "sort_oldest": "áēĸnh cÅŠ nháēĨt", "sort_people_by_similarity": "Sáē¯p xáēŋp ngưáģi theo đáģ™ tÆ°ÆĄng đáģ“ng", "sort_recent": "áēĸnh gáē§n đÃĸy nháēĨt", @@ -1771,8 +1785,8 @@ "stop_sharing_photos_with_user": "DáģĢng chia sáēģ áēŖnh cáģ§a báēĄn váģ›i ngưáģi dÚng này", "storage": "Báģ™ nháģ›", "storage_label": "NhÃŖn lưu tráģ¯", - "storage_quota": "Giáģ›i háēĄn Dung lưáģŖng", - "storage_usage": "ÄÃŖ sáģ­ dáģĨng {used} cáģ§a {available}", + "storage_quota": "HáēĄn máģŠc dung lưáģŖng", + "storage_usage": "ÄÃŖ dÚng {used} cáģ§a {available}", "submit": "Gáģ­i", "suggestions": "GáģŖi ÃŊ", "sunrise_on_the_beach": "BÃŦnh minh trÃĒn bÃŖi biáģƒn", @@ -1795,8 +1809,8 @@ "tags": "Tháēģ", "template": "MáēĢu", "theme": "Cháģ§ Ä‘áģ", - "theme_selection": "Cháģ§ Ä‘áģ táģ•ng tháģƒ", - "theme_selection_description": "Táģą Ä‘áģ™ng đáēˇt cháģ§ Ä‘áģ sÃĄng hoáēˇc táģ‘i dáģąa trÃĒn tÚy cháģn háģ‡ tháģ‘ng cáģ§a trÃŦnh duyáģ‡t cáģ§a báēĄn", + "theme_selection": "Cháģ§ Ä‘áģ", + "theme_selection_description": "Dáģąa theo trÃŦnh duyáģ‡t cáģ§a báēĄn", "theme_setting_asset_list_storage_indicator_title": "Hiáģƒn tháģ‹ tráēĄng thÃĄi sao lưu áēŖnh trÃĒn hÃŦnh thu nháģ", "theme_setting_asset_list_tiles_per_row_title": "Sáģ‘ lưáģŖng áēŖnh trÃĒn máģ™t dÃ˛ng ({count})", "theme_setting_colorful_interface_subtitle": "Áp dáģĨng màu cháģ§ Ä‘áēĄo cho náģn áģŠng dáģĨng.", @@ -1806,7 +1820,7 @@ "theme_setting_primary_color_subtitle": "Cháģn màu cho cÃĄc hành đáģ™ng chính và điáģƒm nháēĨn.", "theme_setting_primary_color_title": "Màu cháģ§ Ä‘áēĄo", "theme_setting_system_primary_color_title": "DÚng màu háģ‡ tháģ‘ng", - "theme_setting_system_theme_switch": "Táģą Ä‘áģ™ng (Theo cài đáēˇt háģ‡ tháģ‘ng)", + "theme_setting_system_theme_switch": "Táģą Ä‘áģ™ng (Giáģ‘ng thiáēŋt báģ‹)", "theme_setting_theme_subtitle": "Cháģn cài đáēˇt giao diáģ‡n áģŠng dáģĨng", "theme_setting_three_stage_loading_subtitle": "TáēŖi ba giai đoáēĄn cÃŗ tháģƒ tăng táģ‘c đáģ™ táēŖi áēŖnh nhưng sáēŊ táģ‘n dáģ¯ liáģ‡u máēĄng Ä‘ÃĄng káģƒ", "theme_setting_three_stage_loading_title": "Báē­t táēŖi ba giai đoáēĄn", @@ -1819,7 +1833,7 @@ "to_change_password": "Đáģ•i máē­t kháēŠu", "to_favorite": "YÃĒu thích", "to_login": "Đăng nháē­p", - "to_parent": "Đi táģ›i thư máģĨc cha", + "to_parent": "Váģ thư máģĨc gáģ‘c", "to_trash": "XÃŗa", "toggle_settings": "Chuyáģƒn đáģ•i cài đáēˇt", "total": "Táģ•ng cáģ™ng", @@ -1894,7 +1908,7 @@ "user_purchase_settings_description": "QuáēŖn lÃŊ máģĨc mua cáģ§a báēĄn", "user_role_set": "Đáēˇt {user} làm {role}", "user_usage_detail": "Chi tiáēŋt sáģ­ dáģĨng cáģ§a ngưáģi dÚng", - "user_usage_stats": "Tháģ‘ng kÃĒ sáģ­ dáģĨng cáģ§a tài khoáēŖn", + "user_usage_stats": "Tháģ‘ng kÃĒ", "user_usage_stats_description": "Xem tháģ‘ng kÃĒ sáģ­ dáģĨng cáģ§a tài khoáēŖn", "username": "TÃĒn ngưáģi dÚng", "users": "Ngưáģi dÚng", @@ -1908,7 +1922,7 @@ "version_history": "Láģ‹ch sáģ­ phiÃĒn báēŖn", "version_history_item": "ÄÃŖ cài đáēˇt {version} vào {date}", "video": "Video", - "video_hover_setting": "PhÃĄt đoáēĄn video xem trưáģ›c khi di chuáģ™t", + "video_hover_setting": "Xem trưáģ›c video khi di chuáģ™t lÃĒn", "video_hover_setting_description": "PhÃĄt đoáēĄn video xem trưáģ›c khi di chuáģ™t qua máģĨc. Ngay cáēŖ khi táē¯t cháģŠc năng này, váēĢn cÃŗ tháģƒ báē¯t đáē§u phÃĄt video báēąng cÃĄch di chuáģ™t qua biáģƒu tưáģŖng phÃĄt.", "videos": "Video", "videos_count": "{count, plural, one {# Video} other {# Video}}", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index e5a8a65e25..1f57b2a7ae 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -8,12 +8,13 @@ "actions": "é€˛čĄŒå‹•äŊœ", "active": "處ᐆ䏭", "activity": "動態", - "activity_changed": "動態{enabled, select, true {開啟} other {關閉}}", + "activity_changed": "å‹•æ…‹åˇ˛{enabled, select, true {開啟} other {關閉}}", "add": "加å…Ĩ", "add_a_description": "加å…Ĩ文字čĒĒæ˜Ž", "add_a_location": "新åĸžåœ°éģž", "add_a_name": "加å…Ĩ姓名", "add_a_title": "新åĸžæ¨™éĄŒ", + "add_birthday": "新åĸžį”Ÿæ—Ĩ", "add_endpoint": "新åĸžį̝éģž", "add_exclusion_pattern": "加å…Ĩį¯Šé¸æĸäģļ", "add_import_path": "新åĸžåŒ¯å…Ĩčˇ¯åž‘", @@ -27,6 +28,10 @@ "add_to_album": "加å…Ĩåˆ°į›¸į°ŋ", "add_to_album_bottom_sheet_added": "新åĸžåˆ° {album}", "add_to_album_bottom_sheet_already_exists": "厞圍 {album} 中", + "add_to_album_bottom_sheet_some_local_assets": "į„Ąæŗ•å°‡æŸä盿œŦåœ°čŗ‡į”ĸæˇģåŠ åˆ°į›¸å†Œ", + "add_to_album_toggle": "é¸æ“‡į›¸į°ŋ{album}", + "add_to_albums": "加å…Ĩᛏį°ŋ", + "add_to_albums_count": "將({count})å€‹é …į›ŽåŠ å…Ĩᛏį°ŋ", "add_to_shared_album": "åŠ åˆ°å…ąäēĢᛏį°ŋ", "add_url": "åģēį̋逪įĩ", "added_to_archive": "į§ģč‡ŗå°å­˜", @@ -35,167 +40,197 @@ "admin": { "add_exclusion_pattern_description": "新åĸžæŽ’除æĸäģļ。支援äŊŋį”¨ã€Œ*」、「 **」、「?」䞆扞尋įŦĻ合čĻå‰‡įš„å­—ä¸˛ã€‚åĻ‚æžœčρ圍äģģäŊ•名į‚ē「Rawã€įš„į›ŽéŒ„å…§æŽ’é™¤æ‰€æœ‰įŦĻ合æĸäģļįš„æĒ”æĄˆīŧŒčĢ‹äŊŋį”¨ã€Œ**/Raw/**」。åĻ‚æžœčĻæŽ’é™¤æ‰€æœ‰ã€Œ.tif」įĩå°žįš„æĒ”æĄˆīŧŒčĢ‹äŊŋį”¨ã€Œ**/*.tif」。åĻ‚æžœčĻæŽ’é™¤æŸå€‹įĩ•å°čˇ¯åž‘īŧŒčĢ‹äŊŋį”¨ã€Œ/path/to/ignore/**」。", "admin_user": "įŽĄį†å“Ą", - "asset_offline_description": "᪁įĸŸä¸Šæ‰žä¸åˆ°æ­¤å¤–éƒ¨į›¸į°ŋæĒ”æĄˆīŧŒä¸”厞į§ģč‡ŗåžƒåœžæĄļ。åĻ‚æžœæĒ”æĄˆåœ¨į›¸į°ŋ內čĸĢį§ģ動īŧŒčĢ‹æĒĸæŸĨ時間čģ¸ä¸­æ˜¯åĻæœ‰æ–°įš„į›¸æ‡‰įš„æĒ”æĄˆã€‚č‹ĨčĻé‚„åŽŸé€™äģŊæĒ”æĄˆīŧŒčĢ‹įĸēäŋ Immich 可äģĨå¯Ģå…Ĩ下列æĒ”æĄˆčˇ¯åž‘īŧŒä¸ĻčŽ€å–æŽƒæį›¸į°ŋ內厚。", + "asset_offline_description": "此外部åĒ’éĢ”åēĢé …į›Žåˇ˛į„Ąæŗ•åœ¨įŖįĸŸä¸Šæ‰žåˆ°īŧŒä¸Ļ厞į§ģč‡ŗåžƒåœžæĄļ。č‹Ĩ芲æĒ”æĄˆæ˜¯åœ¨åĒ’éĢ”åēĢ內į§ģ動īŧŒčĢ‹åœ¨æ™‚é–“čģ¸ä¸­æŸĨįœ‹æ–°įš„å°æ‡‰é …į›Žã€‚č‹ĨčĻé‚„åŽŸæ­¤é …į›ŽīŧŒčĢ‹įĸēäŋä¸‹æ–šįš„æĒ”æĄˆčˇ¯åž‘å¯äž› Immich 存取īŧŒä¸Ļ重新掃描åĒ’éĢ”åēĢ。", "authentication_settings": "éŠ—č­‰č¨­åŽš", "authentication_settings_description": "įŽĄį†å¯†įĸŧ、OAuth 與å…ļäģ–éŠ—č­‰č¨­åŽš", "authentication_settings_disable_all": "įĸē厚čĻåœį”¨æ‰€æœ‰į™ģå…Ĩæ–šåŧå—ŽīŧŸé€™æ¨ŖæœƒåŽŒå…¨į„Ąæŗ•į™ģå…Ĩ。", "authentication_settings_reenable": "åĻ‚éœ€é‡æ–°å•Ÿį”¨īŧŒčĢ‹äŊŋᔍ äŧ翜å™¨æŒ‡äģ¤ ã€‚", - "background_task_job": "čƒŒæ™¯åŸˇčĄŒ", + "background_task_job": "čƒŒæ™¯åˇĨäŊœ", "backup_database": "åģēįĢ‹čŗ‡æ–™åēĢ備äģŊ", - "backup_database_enable_description": "é–‹å•Ÿčŗ‡æ–™åēĢ備äģŊ", + "backup_database_enable_description": "å•Ÿį”¨čŗ‡æ–™åēĢ備äģŊ", "backup_keep_last_amount": "äŋį•™å…ˆå‰å‚™äģŊįš„æ•¸é‡", + "backup_onboarding_1_description": "åœ¨é›˛įĢ¯æˆ–å…ļäģ–å¯ĻéĢ”äŊįŊŽįš„į•°åœ°å‚™äģŊ副æœŦ。", + "backup_onboarding_2_description": "å„˛å­˜åœ¨ä¸åŒčŖįŊŽä¸Šįš„æœŦ地副æœŦ。這包æ‹Ŧä¸ģčρæĒ”æĄˆåŠå…ļæœŦ地備äģŊ。", + "backup_onboarding_3_description": "æ‚¨čŗ‡æ–™įš„į¸Ŋ備äģŊäģŊ數īŧŒåŒ…æ‹Ŧ原始æĒ”æĄˆåœ¨å…§ã€‚é€™åŒ…æ‹Ŧ 1 äģŊį•°åœ°å‚™äģŊ與 2 äģŊæœŦ地副æœŦ。", + "backup_onboarding_description": "åģēč­°æŽĄį”¨ 3-2-1 備äģŊį­–į•Ĩ 來äŋč­ˇæ‚¨įš„čŗ‡æ–™ã€‚æ‚¨æ‡‰äŋį•™åˇ˛ä¸Šå‚ŗįš„ᅧቇ/åŊąį‰‡å‰¯æœŦīŧŒäģĨ及 Immich čŗ‡æ–™åēĢīŧŒäģĨåģēįĢ‹åŽŒæ•´įš„å‚™äģŊæ–šæĄˆã€‚", + "backup_onboarding_footer": "更多備äģŊ Immich čŗ‡č¨ŠīŧŒčĢ‹åƒč€ƒčĒĒæ˜Žæ–‡äģļ。", + "backup_onboarding_parts_title": "éĩåžžå‚™äģŊ原則 3-2-1īŧš", + "backup_onboarding_title": "備äģŊ", "backup_settings": "čŗ‡æ–™åēĢ備äģŊč¨­åŽš", "backup_settings_description": "įŽĄį†čŗ‡æ–™åēĢ備äģŊč¨­åŽšã€‚", "cleared_jobs": "厞åˆĒ除「{job}」äģģ務", - "config_set_by_file": "åˇ˛é€éŽč¨­åŽšæĒ”æ›´æ–°č¨­åޚ", - "confirm_delete_library": "įĸē厚čρåˆĒ除 {library} ᛏį°ŋ嗎īŧŸ", - "confirm_delete_library_assets": "您įĸē厚čρåˆĒé™¤æ­¤į›¸į°ŋ嗎īŧŸé€™å°‡åžž Immich 中åˆĒ除 {count, plural, one {å€‹é …į›Ž} other {å€‹é …į›Ž}} īŧŒä¸”į„Ąæŗ•åžŠåŽŸã€‚æĒ”æĄˆäģæœƒäŋį•™åœ¨įĄŦįĸŸä¸­ã€‚", - "confirm_email_below": "čĢ‹åœ¨åē•下čŧ¸å…Ĩ {email} 來įĸēčĒ", - "confirm_reprocess_all_faces": "įĸē厚čĻé‡æ–°č™•į†æ‰€æœ‰č‡‰å­”å—ŽīŧŸé€™æœƒæ¸…除厞å‘Ŋåįš„äēēį‰Šã€‚", + "config_set_by_file": "į›Žå‰įš„č¨­åŽšæ˜¯į”ąč¨­åŽšæĒ”設åޚ", + "confirm_delete_library": "您įĸē厚čρåˆĒ除外部åĒ’éĢ”åēĢ {library} 嗎īŧŸ", + "confirm_delete_library_assets": "您įĸē厚čρåˆĒ除此外部åĒ’éĢ”åēĢ嗎īŧŸé€™å°‡åžž Immich 中åˆĒ除 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}} īŧŒä¸”į„Ąæŗ•åžŠåŽŸã€‚æĒ”æĄˆäģæœƒäŋį•™åœ¨įĄŦįĸŸä¸­ã€‚", + "confirm_email_below": "čĢ‹åœ¨åē•下čŧ¸å…Ĩ {email} äģĨįĸēčĒ", + "confirm_reprocess_all_faces": "您įĸē厚čĻé‡æ–°č™•į†æ‰€æœ‰č‡‰å­”å—ŽīŧŸé€™æœƒæ¸…除厞å‘Ŋåįš„äēēį‰Šã€‚", "confirm_user_password_reset": "您įĸē厚čĻé‡č¨­ {user} įš„å¯†įĸŧ嗎īŧŸ", - "confirm_user_pin_code_reset": "įĸē厚čĻé‡įŊŽ {user} įš„ PIN įĸŧ嗎īŧŸ", + "confirm_user_pin_code_reset": "įĸē厚čĻé‡č¨­ {user} įš„ PIN įĸŧ嗎īŧŸ", "create_job": "åģēįĢ‹äģģ務", - "cron_expression": "Cron é‹įŽ—åŧ", - "cron_expression_description": "äģĨ Cron æ ŧåŧč¨­åŽšæŽƒææ™‚æŽĩã€‚čŠŗį´°čŗ‡č¨ŠčĢ‹åƒé–ą Crontab Guru", - "cron_expression_presets": "įžæˆįš„ Cron é‹įŽ—åŧ", + "cron_expression": "Cron 表達åŧ", + "cron_expression_description": "äŊŋᔍ Cron æ ŧåŧč¨­åŽšæŽƒæé–“éš”ã€‚æ›´å¤ščŗ‡č¨ŠčĢ‹åƒé–ą Crontab Guru", + "cron_expression_presets": "Cron 表達åŧé č¨­į¯„æœŦ", "disable_login": "åœį”¨į™ģå…Ĩ", - "duplicate_detection_job_description": "䞝靠æ™ēæ…§æœå°‹ã€‚åŸˇčĄŒæŠŸå™¨å­¸įŋ’å°é …į›Žäž†åĩæ¸Ŧᛏäŧŧåœ–į‰‡", - "exclusion_pattern_description": "排除čĻå‰‡čŽ“æ‚¨åœ¨æŽƒæčŗ‡æ–™åēĢæ™‚åŋŊį•Ĩį‰šåŽšæ–‡äģļ和文äģļå¤žã€‚į”¨æ–ŧį•ļæ‚¨æœ‰ä¸æƒŗå°Žå…Ĩįš„æ–‡äģļīŧˆäž‹åĻ‚ RAW 文äģļīŧ‰æˆ–æ–‡äģļ夞。", - "external_library_management": "å¤–éƒ¨į›¸į°ŋįŽĄį†", + "duplicate_detection_job_description": "䞝靠æ™ēæ…§æœå°‹ã€‚å°é …į›ŽåŸˇčĄŒæŠŸå™¨å­¸įŋ’䞆åĩæ¸Ŧᛏäŧŧåœ–į‰‡", + "exclusion_pattern_description": "排除čĻå‰‡å¯čŽ“æ‚¨åœ¨æŽƒæåĒ’éĢ”åēĢæ™‚åŋŊį•Ĩį‰šåŽšįš„æĒ”æĄˆå’Œčŗ‡æ–™å¤žã€‚é€™åœ¨æ‚¨æœ‰äē›čŗ‡æ–™å¤žåŒ…åĢä¸æƒŗåŒ¯å…Ĩįš„æĒ”æĄˆīŧˆäž‹åĻ‚ RAW æĒ”īŧ‰æ™‚į‰šåˆĨæœ‰į”¨ã€‚", + "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": "č­Ļ告īŧšé€™å°‡įĢ‹åŗåˆĒ除äŊŋį”¨č€…åŠæ‰€æœ‰é …į›Žã€‚į„Ąæŗ•é‚„åŽŸåˆĒé™¤įš„æĒ”æĄˆã€‚", + "force_delete_user_warning": "č­Ļ告īŧšé€™å°‡įĢ‹åŗåˆĒ除äŊŋį”¨č€…åŠå…￉€æœ‰é …į›Žã€‚æ­¤æ“äŊœį„Ąæŗ•æ’¤éŠˇä¸Ļä¸”į„Ąæŗ•é‚„åŽŸåˆĒé™¤įš„æĒ”æĄˆã€‚", "image_format": "æ ŧåŧ", "image_format_description": "WebP čƒŊį”ĸį”Ÿį›¸å°æ–ŧ JPEG æ›´å°įš„æĒ”æĄˆīŧŒäŊ†įˇ¨įĸŧ速åēĻčŧƒæ…ĸ。", - "image_fullsize_description": "į§ģé™¤čŠŽé‡‹čŗ‡æ–™įš„å…¨å°ēå¯¸åœ–į‰‡īŧŒåœ¨æ”žå¤§åœ–į‰‡æ™‚äŊŋᔍ", - "image_fullsize_enabled": "開啟全å°ēå¯¸åœ–į‰‡į”Ÿæˆ", - "image_fullsize_enabled_description": "į‚ē非įļ˛čˇ¯å‹åĨŊåœ–į‰‡æ ŧåŧįš„åœ–į‰‡į”Ÿæˆå…¨å°ē寸圖像。在開啟 “偏åĨŊåĩŒå…Ĩįš„é čĻŊ” įš„é¸é …åžŒīŧŒåĩŒå…Ĩ預čĻŊæœƒåœ¨æ˛’æœ‰čŊ‰æ›æ ŧåŧä¸‹įš„į‹€æŗčĸĢäŊŋį”¨ã€‚é€™é …é¸é …ä¸åŊąéŸŋJPEGį­‰įļ˛čˇ¯å‹åĨŊåœ–į‰‡æ ŧåŧã€‚", - "image_fullsize_quality_description": "åžž1-100įš„å…¨å°ēå¯¸åœ–į‰‡å“čŗĒ。čļŠéĢ˜įš„æ•¸å­—äģŖčĄ¨č‘—į”ĸå‡ēįš„å“čŗĒčļŠé̘īŧŒæĒ”æĄˆæ›´å¤§ã€‚", - "image_fullsize_title": "全å°ēå¯¸åœ–į‰‡č¨­åŽš", + "image_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_embedded_preview_setting_description": "åœ¨å¯čĄŒįš„æƒ…æŗä¸‹īŧŒå°‡ RAW į…§į‰‡ä¸­įš„å…§åĩŒé čĻŊᔍäŊœåŊąåƒč™•į†įš„čŧ¸å…Ĩ來æēã€‚這對某äē›åŊąåƒčƒŊį”ĸį”Ÿæ›´æē–įĸēįš„č‰˛åŊŠīŧŒäŊ†é čĻŊįš„å“čŗĒ取æąēæ–ŧį›¸æŠŸīŧŒåŊąåƒå¯čƒŊ會å‡ēįžæ›´å¤šåŖ“į¸Žį‘•į–ĩ。", "image_prefer_wide_gamut": "偏åĨŊåģŖč‰˛åŸŸ", - "image_prefer_wide_gamut_setting_description": "äŊŋᔍ Display P3 來čŖŊäŊœį¸Žåœ–。這可äģĨ更åĨŊ地äŋį•™åģŖč‰˛åŸŸåœ–į‰‡įš„éŽŽčą”åēĻīŧŒäŊ†åœ¨čˆŠį‰ˆį€čĻŊå™¨æˆ–čˆŠč¨­å‚™ä¸ŠīŧŒåœ–į‰‡å¯čƒŊæœƒéĄ¯į¤ē不同。sRGB åœ–į‰‡æœƒįļ­æŒ sRGB äģĨéŋå…éĄč‰˛čŽŠåŒ–ã€‚", - "image_preview_description": "åˆĒ除䏭ᭉå°ēå¯¸åœ–į‰‡įš„čŠŗį´°čŗ‡æ–™īŧŒį•ļé¸æ“‡įœ‹æŒ‡åŽšé …į›Žå’ŒæŠŸå™¨å­¸įŋ’時äŊŋᔍ", - "image_preview_quality_description": "預čĻŊ品čŗĒį‚ē 1 īŊž 100。數å€ŧčļŠå¤§å“čŗĒčļŠé̘īŧŒäŊ†æœƒį”ĸį”Ÿčŧƒå¤§įš„æĒ”æĄˆīŧŒä¸”可čƒŊ降äŊŽæ‡‰į”¨į¨‹åŧįš„回應速åēĻã€‚č€Œæ•¸å€ŧčŧƒå°å¯čƒŊ會åŊąéŸŋ抟器學įŋ’品čŗĒ。", + "image_prefer_wide_gamut_setting_description": "äŊŋᔍ Display P3 來čŖŊäŊœį¸Žåœ–。這čƒŊ更åĨŊ地äŋį•™å¯ŦåģŖč‰˛åŸŸåŊąåƒįš„鎎蹔åēĻīŧŒäŊ†åœ¨čˆŠčŖįŊŽčˆ‡čˆŠį‰ˆæœŦį€čĻŊ器上īŧŒåŊąåƒå¯čƒŊæœƒå‘ˆįžä¸åŒįš„æ•ˆæžœã€‚sRGB åŊąåƒæœƒäŋæŒį‚ē 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_concurrency": "{job}äŊĩį™ŧ", "job_created": "厞åģēįĢ‹äģģ務", - "job_not_concurrency_safe": "這個äģģ務ä¸Ļ行ä¸Ļ不厉全。", + "job_not_concurrency_safe": "這個äģģ務äŊĩį™ŧä¸Ļ不厉全。", "job_settings": "äģģå‹™č¨­åŽš", - "job_settings_description": "ä¸Ļ行äģģå‹™įŽĄį†", + "job_settings_description": "äŊĩį™ŧäģģå‹™įŽĄį†", "job_status": "äģģå‹™į‹€æ…‹", - "jobs_delayed": "厞åģļ垌 {jobCount, plural, other {# 項äģģ務}}", - "jobs_failed": "{jobCount, plural, other {# 項äģģå‹™å¤ąæ•—}}", - "library_created": "厞åģēįĢ‹åœ–åēĢīŧš{library}", - "library_deleted": "ᛏį°ŋ厞åˆĒ除", - "library_import_path_description": "選取čρčŧ‰å…Ĩįš„čŗ‡æ–™å¤žã€‚äģĨæŽƒæčŗ‡æ–™å¤žīŧˆåĢå­čŗ‡æ–™å¤žīŧ‰å…§įš„åŊąåƒå’ŒåŊąį‰‡ã€‚", + "jobs_delayed": "{jobCount, plural, other {# 項äģģ務厞åģļ垌}}", + "jobs_failed": "{jobCount, plural, other {# 項äģģå‹™åˇ˛å¤ąæ•—}}", + "library_created": "厞åģēįĢ‹åĒ’éĢ”åēĢīŧš{library}", + "library_deleted": "åĒ’éĢ”åēĢ厞åˆĒ除", + "library_import_path_description": "指厚čρ匝å…Ĩįš„čŗ‡æ–™å¤žã€‚įŗģįĩ࿜ƒæŽƒææ­¤čŗ‡æ–™å¤žåŠå…ļå­čŗ‡æ–™å¤žä¸­įš„æ‰€æœ‰åŊąåƒčˆ‡åŊąį‰‡ã€‚", "library_scanning": "厚期掃描", - "library_scanning_description": "厚期圖åēĢæŽƒæč¨­åޚ", - "library_scanning_enable_description": "å•Ÿį”¨åœ–åēĢ厚期掃描", - "library_settings": "外部圖åēĢ", - "library_settings_description": "įŽĄį†å¤–éƒ¨åœ–åēĢč¨­åŽš", - "library_tasks_description": "æŽƒæå¤–éƒ¨čŗ‡æēäģĨ尋扞新åĸžæˆ–æ›´æ”šįš„é …į›Ž", - "library_watching_enable_description": "į›ŖæŽ§å¤–éƒ¨åœ–åēĢįš„æĒ”æĄˆčŽŠåŒ–", - "library_watching_settings": "圖åēĢį›ŖæŽ§īŧˆå¯Ļ驗中īŧ‰", + "library_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": "記錄æĒ”", + "logging_enable_description": "å•Ÿį”¨æ—ĨčĒŒč¨˜éŒ„", + "logging_level_description": "å•Ÿį”¨æ™‚įš„æ—ĨčĒŒåą¤į´šã€‚", + "logging_settings": "æ—Ĩčnj", + "machine_learning_availability_checks": "å¯į”¨æ€§æĒĸæŸĨ", + "machine_learning_availability_checks_description": "č‡Ē動æĒĸæ¸Ŧä¸Ļå„Ēå…ˆé¸æ“‡å¯į”¨įš„æŠŸå™¨å­¸įŋ’服務器", + "machine_learning_availability_checks_enabled": "å•Ÿį”¨å¯į”¨æ€§æĒĸæŸĨ", + "machine_learning_availability_checks_interval": "æĒĸæŸĨ間隔", + "machine_learning_availability_checks_interval_description": "å¯į”¨æ€§æĒĸæŸĨäš‹é–“įš„é–“éš”īŧˆæ¯Ģį§’īŧ‰", + "machine_learning_availability_checks_timeout": "čĢ‹æą‚čļ…æ™‚", + "machine_learning_availability_checks_timeout_description": "å¯į”¨æ€§æĒĸæŸĨčļ…æ™‚īŧˆæ¯Ģį§’īŧ‰", "machine_learning_clip_model": "CLIP æ¨Ąåž‹", - "machine_learning_clip_model_description": "é€™čŖĄæœ‰äģŊ CLIP æ¨Ąåž‹åå–Žã€‚*č¨ģīŧšæ›´æ›æ¨Ąåž‹åžŒé ˆå°æ‰€æœ‰åœ–į‰‡é‡æ–°åŸˇčĄŒã€Œæ™ē慧搜尋」äģģ務。", + "machine_learning_clip_model_description": "é€™čŖĄæœ‰äģŊ CLIP æ¨Ąåž‹åå–Žã€‚æŗ¨æ„īŧšæ›´æ›æ¨Ąåž‹åžŒé ˆå°æ‰€æœ‰åœ–į‰‡é‡æ–°åŸˇčĄŒã€Œæ™ē慧搜尋」äģģ務。", "machine_learning_duplicate_detection": "é‡č¤‡é …į›Žåĩæ¸Ŧ", "machine_learning_duplicate_detection_enabled": "å•Ÿį”¨é‡č¤‡é …į›Žåĩæ¸Ŧ", - "machine_learning_duplicate_detection_enabled_description": "é—œé–‰čŠ˛åŠŸčƒŊ會åŋŊį•Ĩæœ‰é‡č¤‡įš„é …į›Žã€‚", - "machine_learning_duplicate_detection_setting_description": "ᔍ CLIP 向量比對æŊ›åœ¨é‡č¤‡", + "machine_learning_duplicate_detection_enabled_description": "č‹Ĩåœį”¨īŧŒåŽŒå…¨į›¸åŒįš„åĒ’éĢ”æĒ”æĄˆäģæœƒé€˛čĄŒé‡č¤‡čŗ‡æ–™åˆĒ除。", + "machine_learning_duplicate_detection_setting_description": "äŊŋᔍ CLIP 向量比對尋扞可čƒŊįš„é‡č¤‡é …į›Ž", "machine_learning_enabled": "å•Ÿį”¨æŠŸå™¨å­¸įŋ’", "machine_learning_enabled_description": "č‹Ĩåœį”¨īŧŒå‰‡į„ĄčĻ–ä¸‹æ–šįš„č¨­åŽšīŧŒæ‰€æœ‰æŠŸå™¨å­¸įŋ’įš„åŠŸčƒŊéƒŊå°‡åœį”¨ã€‚", - "machine_learning_facial_recognition": "臉部辨識", - "machine_learning_facial_recognition_description": "åĩæ¸Ŧ、čĒå‡ēä¸Ļå°åœ–į‰‡ä¸­įš„č‡‰å­”åˆ†įĩ„", + "machine_learning_facial_recognition": "äēē臉辨識", + "machine_learning_facial_recognition_description": "åĩæ¸Ŧã€čž¨č­˜ä¸Ļå°åœ–į‰‡ä¸­įš„č‡‰å­”åˆ†éĄž", "machine_learning_facial_recognition_model": "äēēč‡‰čž¨č­˜æ¨Ąåž‹", "machine_learning_facial_recognition_model_description": "æ¨Ąåž‹é †åēį”ąå¤§č‡ŗå°æŽ’åˆ—ã€‚å¤§įš„æ¨Ąåž‹čŧƒæ…ĸ且äŊŋᔍčŧƒå¤šč¨˜æ†ļéĢ”īŧŒäŊ†æˆæ•ˆčŧƒäŊŗã€‚æ›´æ›æ¨Ąåž‹åžŒéœ€å°æ‰€æœ‰åŊąåƒé‡æ–°åŸˇčĄŒã€Œäēēč‡‰čž¨č­˜ã€ã€‚", "machine_learning_facial_recognition_setting": "å•Ÿį”¨äēē臉辨識", - "machine_learning_facial_recognition_setting_description": "č‹Ĩåœį”¨īŧŒåŊąåƒå°‡ä¸æœƒį”ĸį”Ÿäēēč‡‰į‰šåžĩᎍįĸŧīŧŒåžžč€Œã€ŒæŽĸį´ĸ」頁éĸ不會有「äēēį‰Šã€åŠŸčƒŊ。", + "machine_learning_facial_recognition_setting_description": "č‹Ĩåœį”¨īŧŒåŊąåƒå°‡ä¸æœƒį”ĸį”Ÿäēēč‡‰čž¨č­˜įˇ¨įĸŧīŧŒä¸Ļ且在「æŽĸį´ĸ」頁éĸ不會有「äēēį‰Šã€åŠŸčƒŊ。", "machine_learning_max_detection_distance": "åĩæ¸Ŧ距é›ĸ上限", "machine_learning_max_detection_distance_description": "č‹Ĩå…ŠåŧĩåŊąåƒé–“įš„čˇé›ĸ小æ–ŧ此將čĸĢåˆ¤æ–ˇį‚ēį›¸åŒīŧŒį¯„圍į‚ē 0.001-0.1。數å€ŧčļŠé̘čƒŊåĩæ¸Ŧ到čļŠå¤šé‡č¤‡īŧŒäŊ†äšŸæ›´æœ‰å¯čƒŊčĒ¤åˆ¤ã€‚", - "machine_learning_max_recognition_distance": "åˆ†čž¨čˇé›ĸ上限", - "machine_learning_max_recognition_distance_description": "č‹Ĩå…Šåŧĩäēēč‡‰é–“įš„čˇé›ĸ小æ–ŧ此將čĸĢåˆ¤æ–ˇį‚ēį›¸åŒäēēį‰ŠīŧŒį¯„圍į‚ē 0-2。數å€ŧ降äŊŽčƒŊ減少兊äēēčĸĢæˇˇåœ¨ä¸€čĩˇįš„可čƒŊ性īŧŒæ•¸å€ŧ提升čƒŊ減少同一äēēčĸĢį•ļäŊœä¸åŒč‡‰įš„可čƒŊæ€§ã€‚į”ąæ–ŧ合ä¸Ļ比拆分厚易īŧŒåģēč­°å°‡æ•¸å€ŧčĒŋ小。", - "machine_learning_min_detection_score": "最äŊŽæĒĸæ¸Ŧ分數", - "machine_learning_min_detection_score_description": "最äŊŽäŋĄäģģåˆ†čž¨įŽ‡īŧŒåžž0到1。äŊŽå€ŧ會åĩæ¸Ŧæ›´å¤šįš„éĸ孔īŧŒäŊ†å¯čƒŊå°Žč‡´čĒ¤å ąã€‚", + "machine_learning_max_recognition_distance": "辨識距é›ĸ上限", + "machine_learning_max_recognition_distance_description": "å…Šåŧĩ臉孔čĸĢčĻ–į‚ē同一äēēį‰Šįš„æœ€å¤§čˇé›ĸīŧŒį¯„圍į‚ē 0 臺 2。降äŊŽæ­¤æ•¸å€ŧ可éŋ免將不同äēēį‰Šæ¨™č¨˜į‚ē同一äēēīŧ›æéĢ˜æ­¤æ•¸å€ŧ則可éŋ免將同一äēēį‰Šæ¨™č¨˜į‚ēå…Šå€‹ä¸åŒįš„äēē。čĢ‹æŗ¨æ„īŧŒåˆäŊĩ兊個äēēį‰Šæ¯”å°‡ä¸€å€‹äēēį‰Šæ‹†åˆ†æˆå…Šå€‹æ›´åŽšæ˜“īŧŒå› æ­¤åœ¨å¯čƒŊįš„æƒ…æŗä¸‹īŧŒåģēč­°å°‡æ­¤é–žå€ŧč¨­åŽšåž—čŧƒäŊŽã€‚", + "machine_learning_min_detection_score": "最äŊŽåĩæ¸Ŧ分數", + "machine_learning_min_detection_score_description": "臉孔åĩæ¸Ŧįš„æœ€äŊŽäŋĄåŋƒåˆ†æ•¸īŧŒį¯„圍į‚ē 0 臺 1。數å€ŧčŧƒäŊŽæ™‚會åĩæ¸Ŧåˆ°æ›´å¤šč‡‰å­”īŧŒäŊ†å¯čƒŊå°Žč‡´čĒ¤åˆ¤ã€‚", "machine_learning_min_recognized_faces": "最äŊŽč‡‰éƒ¨čž¨č­˜æ•¸é‡", - "machine_learning_min_recognized_faces_description": "æ­¸į´å‡ē新äēēį‰Šįš„æœ€äŊŽč‡‰éƒ¨æ•¸é‡ã€‚čĒŋéĢ˜æ­¤æ•¸å€ŧ可äģĨčŽ“č‡‰éƒ¨čž¨č­˜æ›´æē–įĸēīŧŒäŊ†äšŸå¯čƒŊæœƒčŽ“čŧƒå°‘å‡ēįžįš„č‡‰å­”į„Ąæŗ•čĸĢæ­¸į´åˆ°äēēį‰Šæ¸…å–Žã€‚", + "machine_learning_min_recognized_faces_description": "åģēįĢ‹æ–°äēēį‰Šæ‰€éœ€įš„æœ€äŊŽåˇ˛čž¨č­˜č‡‰å­”數量。提éĢ˜æ­¤æ•¸å€ŧå¯čŽ“č‡‰å­”čž¨č­˜æ›´į˛žįĸēīŧŒäŊ†åŒæ™‚會åĸžåР臉孔æœĒčĸĢæŒ‡æ´žįĩĻäģģäŊ•äēēį‰Šįš„å¯čƒŊ性。", "machine_learning_settings": "抟器學įŋ’設åޚ", "machine_learning_settings_description": "įŽĄį†æŠŸå™¨å­¸įŋ’įš„åŠŸčƒŊå’Œč¨­åŽš", "machine_learning_smart_search": "æ™ē慧搜尋", - "machine_learning_smart_search_description": "äŊŋᔍ CLIP åĩŒå…Ĩ進行čĒžįžŠåœ–åƒæœå°‹", + "machine_learning_smart_search_description": "äŊŋᔍ CLIP åĩŒå…Ĩ向量äģĨčĒžæ„æ–šåŧæœå°‹åŊąåƒ", "machine_learning_smart_search_enabled": "å•Ÿį”¨æ™ē慧搜尋", - "machine_learning_smart_search_enabled_description": "åĻ‚æžœåœį”¨īŧŒåœ–į‰‡å°‡ä¸æœƒčĸĢᎍįĸŧäģĨ進行æ™ēčƒŊ搜尋。", - "machine_learning_url_description": "抟器學įŋ’äŧ翜å™¨įš„ URL。åĻ‚æžœæäž›å¤šå€‹ URLīŧŒå°‡äžåēå˜—čŠĻ每個äŧ翜å™¨īŧŒį›´åˆ°æœ‰ä¸€å€‹æˆåŠŸå›žæ‡‰īŧŒåžžįŦŦ一個到最垌一個。æœĒå›žæ‡‰įš„äŧ翜å™¨å°‡æšĢ時čĸĢåŋŊį•ĨīŧŒį›´åˆ°åŽƒå€‘æĸåžŠįˇšä¸Šã€‚", - "manage_concurrency": "įŽĄį†ä¸Ļ行", + "machine_learning_smart_search_enabled_description": "åĻ‚æžœåœį”¨īŧŒåŊąåƒå°‡ä¸æœƒčĸĢᎍįĸŧäģĨ進行æ™ē慧搜尋。", + "machine_learning_url_description": "抟器學įŋ’äŧ翜å™¨įš„ URL。č‹Ĩ提䞛多個 URLīŧŒįŗģįĩ࿜ƒäžåēé€ä¸€å˜—čŠĻīŧŒį›´åˆ°å…ļ中一台成功回應į‚ēæ­ĸīŧˆį”ąå‰åˆ°åžŒīŧ‰ã€‚æœĒå›žæ‡‰įš„äŧ翜å™¨å°‡čĸĢæšĢ時åŋŊį•ĨīŧŒį›´åˆ°å…ļé‡æ–°ä¸Šįˇšã€‚", + "manage_concurrency": "įŽĄį†äŊĩį™ŧ", "manage_log_settings": "įŽĄį†æ—ĨčĒŒč¨­åŽš", "map_dark_style": "æˇąč‰˛æ¨Ŗåŧ", "map_enable_description": "å•Ÿį”¨åœ°åœ–åŠŸčƒŊ", "map_gps_settings": "åœ°åœ–čˆ‡ GPS č¨­åŽš", - "map_gps_settings_description": "įŽĄį†åœ°åœ–å’Œ GPSīŧˆé€†å‘åœ°į†įˇ¨įĸŧīŧ‰č¨­åޚ", - "map_implications": "地圖功čƒŊäžčŗ´å¤–éƒ¨åšŗč˛ŧ服務īŧˆtiles.immich.cloudīŧ‰", + "map_gps_settings_description": "įŽĄį†åœ°åœ–čˆ‡ GPSīŧˆåå‘åœ°į†įˇ¨įĸŧīŧ‰č¨­åޚ", + "map_implications": "地圖功čƒŊäžčŗ´å¤–éƒ¨åœ–įŖšæœå‹™īŧˆtiles.immich.cloudīŧ‰", "map_light_style": "æˇēč‰˛æ¨Ŗåŧ", "map_manage_reverse_geocoding_settings": "įŽĄį†é€†å‘åœ°į†įˇ¨įĸŧč¨­åŽš", - "map_reverse_geocoding": "é€†å‘åœ°į†įˇ¨įĸŧ", - "map_reverse_geocoding_enable_description": "å•Ÿį”¨é€†å‘åœ°į†įˇ¨įĸŧ", - "map_reverse_geocoding_settings": "é€†å‘åœ°į†įˇ¨įĸŧč¨­åŽš", + "map_reverse_geocoding": "åå‘åœ°į†įˇ¨įĸŧ", + "map_reverse_geocoding_enable_description": "å•Ÿį”¨åå‘åœ°į†įˇ¨įĸŧ", + "map_reverse_geocoding_settings": "åå‘åœ°į†įˇ¨įĸŧč¨­åŽš", "map_settings": "地圖", "map_settings_description": "įŽĄį†åœ°åœ–č¨­åŽš", "map_style_description": "地圖ä¸ģ題īŧˆstyle.jsonīŧ‰įš„įļ˛å€", "memory_cleanup_job": "回æ†ļæ¸…į†", - "memory_generate_job": "回æ†ļį”ĸį”Ÿ", - "metadata_extraction_job": "æ“ˇå–čŠŽé‡‹čŗ‡æ–™", - "metadata_extraction_job_description": "æ“ˇå–æ‰€æœ‰æĒ”æĄˆįš„ GPSã€č‡‰å­”ã€č§ŖæžåēĻį­‰åŽŸå§‹čŠŗį´°čŗ‡æ–™", + "memory_generate_job": "į”ĸį”Ÿå›žæ†ļ", + "metadata_extraction_job": "æ“ˇå–ä¸­įšŧčŗ‡æ–™", + "metadata_extraction_job_description": "垞每個åĒ’éĢ”æĒ”æĄˆä¸­æ“ˇå–ä¸­įšŧčŗ‡æ–™čŗ‡č¨ŠīŧŒäž‹åĻ‚ GPSã€č‡‰å­”čˆ‡č§ŖæžåēĻ", "metadata_faces_import_setting": "å•Ÿį”¨č‡‰å­”åŒ¯å…Ĩ", - "metadata_faces_import_setting_description": "åžžåœ–į‰‡įš„ EXIF čŗ‡æ–™å’Œå´æŽĨæĒ”æĄˆåŒ¯å…Ĩ臉孔", - "metadata_settings": "čŠŗį´°čŗ‡æ–™č¨­åŽš", - "metadata_settings_description": "įŽĄį†čŠŽé‡‹čŗ‡æ–™č¨­åŽš", + "metadata_faces_import_setting_description": "åžžåŊąåƒ EXIF čŗ‡æ–™čˆ‡å´æŽĨæĒ”æĄˆåŒ¯å…Ĩ臉孔", + "metadata_settings": "中įšŧčŗ‡æ–™č¨­åŽš", + "metadata_settings_description": "įŽĄį†ä¸­įšŧčŗ‡æ–™č¨­åŽš", "migration_job": "遡į§ģ", - "migration_job_description": "å°‡é …į›Žå’Œč‡‰å­”įš„į¸Žåœ–į§ģåˆ°æ–°įš„åģļäŧ¸čŗ‡æ–™å¤ž", - "no_paths_added": "æœĒæˇģåŠ čˇ¯åž‘", - "no_pattern_added": "æœĒæˇģ加pattern", - "note_apply_storage_label_previous_assets": "*č¨ģīŧšåŸˇčĄŒåĨ—į”¨å„˛å­˜æ¨™įą¤å‰å…ˆä¸Šå‚ŗé …į›Ž", - "note_cannot_be_changed_later": "*č¨ģīŧšäš‹åžŒį„Ąæŗ•äŋŽæ”šīŧ", + "migration_job_description": "將åĒ’éĢ”æĒ”æĄˆčˆ‡č‡‰å­”įš„į¸Žåœ–éˇį§ģč‡ŗæœ€æ–°įš„čŗ‡æ–™å¤žįĩæ§‹", + "nightly_tasks_cluster_faces_setting_description": "對新åĩæ¸Ŧåˆ°įš„č‡‰å­”åŸˇčĄŒč‡‰å­”čž¨č­˜", + "nightly_tasks_cluster_new_faces_setting": "į‚ēæ–°č‡‰å­”é€˛čĄŒåˆ†įž¤", + "nightly_tasks_database_cleanup_setting": "čŗ‡æ–™åēĢæ¸…ᐆäŊœæĨ­", + "nightly_tasks_database_cleanup_setting_description": "æ¸…é™¤čŗ‡æ–™åēĢä¸­čˆŠįš„čˆ‡åˇ˛éŽæœŸįš„čŗ‡æ–™", + "nightly_tasks_generate_memories_setting": "į”ĸį”Ÿå›žæ†ļ", + "nightly_tasks_generate_memories_setting_description": "åžžåĒ’éĢ”æĒ”æĄˆåģēįĢ‹æ–°å›žæ†ļ", + "nightly_tasks_missing_thumbnails_setting": "į”ĸį”Ÿįŧēå°‘įš„į¸Žåœ–", + "nightly_tasks_missing_thumbnails_setting_description": "å°‡æ˛’æœ‰į¸Žåœ–įš„åĒ’éĢ”æĒ”æĄˆæŽ’å…ĨäŊ‡åˆ—äģĨį”ĸį”Ÿį¸Žåœ–", + "nightly_tasks_settings": "夜間äģģå‹™č¨­åŽš", + "nightly_tasks_settings_description": "įŽĄį†å¤œé–“äģģ務", + "nightly_tasks_start_time_setting": "開始時間", + "nightly_tasks_start_time_setting_description": "äŧ翜å™¨é–‹å§‹åŸˇčĄŒå¤œé–“äģģå‹™įš„æ™‚é–“", + "nightly_tasks_sync_quota_usage_setting": "同æ­Ĩ配額äŊŋį”¨æƒ…æŗ", + "nightly_tasks_sync_quota_usage_setting_description": "æ šæ“šį›Žå‰įš„äŊŋį”¨é‡æ›´æ–°äŊŋį”¨č€…įš„å„˛å­˜é…éĄ", + "no_paths_added": "æ˛’æœ‰åˇ˛æˇģåŠ įš„čˇ¯åž‘", + "no_pattern_added": "尚æœĒ新åĸžæŽ’除čĻå‰‡", + "note_apply_storage_label_previous_assets": "提į¤ēīŧšč‹ĨčĻå°‡å„˛å­˜æ¨™įą¤åĨ—į”¨åˆ°å…ˆå‰ä¸Šå‚ŗįš„åĒ’éĢ”æĒ”æĄˆīŧŒčĢ‹åŸˇčĄŒ", + "note_cannot_be_changed_later": "æŗ¨æ„īŧ𿭤荭åޚæ—ĨåžŒį„Ąæŗ•čŽŠæ›´īŧ", "notification_email_from_address": "寄äģļ地址", - "notification_email_from_address_description": "寄äģļ者é›ģ子éƒĩäģļ地址īŧŒäž‹īŧšImmich Photo Server 。čĢ‹įĸēäŋå¯„äģļ者äŋĄįŽąå…č¨ąį™ŧ送到æ”ļäģļ者é›ģ子éƒĩäģļįš„åœ°å€ã€‚", - "notification_email_host_description": "é›ģ子éƒĩäģļäŧ翜å™¨ä¸ģ抟īŧˆäž‹īŧšsmtp.immich.appīŧ‰", + "notification_email_from_address_description": "寄äģļ者é›ģ子éƒĩäģļ地址īŧŒäž‹åĻ‚īŧš\"Immich Photo Server \"。čĢ‹įĸēäŋäŊŋį”¨įš„æ˜¯æ‚¨æœ‰æŦŠé™å¯„送éƒĩäģļįš„åœ°å€ã€‚", + "notification_email_host_description": "é›ģ子éƒĩäģļäŧ翜å™¨ä¸ģ抟īŧˆäž‹åĻ‚īŧšsmtp.immich.appīŧ‰", "notification_email_ignore_certificate_errors": "åŋŊį•Ĩæ†‘č­‰éŒ¯čǤ", "notification_email_ignore_certificate_errors_description": "åŋŊį•Ĩ TLS æ†‘č­‰éŠ—č­‰éŒ¯čǤīŧˆä¸åģēč­°īŧ‰", - "notification_email_password_description": "äģĨé›ģ子éƒĩäģļäŧ翜å™¨éŠ—č­‰čēĢäģŊæ™‚įš„å¯†įĸŧ", - "notification_email_port_description": "é›ģ子éƒĩäģļäŧ翜å™¨åŸ åŖīŧˆåĻ‚īŧš 25、465 或 587īŧ‰", + "notification_email_password_description": "ᔍæ–ŧ與é›ģ子éƒĩäģļäŧ翜å™¨éŠ—č­‰įš„å¯†įĸŧ", + "notification_email_port_description": "é›ģ子éƒĩäģļäŧ翜å™¨åŸ åŖīŧˆäž‹åĻ‚ 25、465 或 587īŧ‰", "notification_email_sent_test_email_button": "傺送æ¸ŦčŠĻé›ģ子éƒĩäģļä¸Ļå„˛å­˜", - "notification_email_setting_description": "į™ŧ送é›ģ子éƒĩäģļ通įŸĨįš„č¨­įŊŽ", + "notification_email_setting_description": "寄送é›ģ子éƒĩäģļ通įŸĨįš„č¨­åŽš", "notification_email_test_email": "傺送æ¸ŦčŠĻé›ģ子éƒĩäģļ", - "notification_email_test_email_failed": "į„Ąæŗ•į™ŧ送æ¸ŦčŠĻé›ģ子éƒĩäģļīŧŒčĢ‹æĒĸæŸĨæ‚¨įš„č¨­įŊŽå€ŧ", - "notification_email_test_email_sent": "æ¸ŦčŠĻé›ģ子éƒĩäģļ厞į™ŧ送臺 {email}。čĢ‹æĒĸæŸĨæ‚¨įš„æ”ļäģļįŽąã€‚", - "notification_email_username_description": "äģĨé›ģ子éƒĩäģļäŧ翜å™¨éŠ—č­‰čēĢäģŊæ™‚įš„äŊŋį”¨č€…åį¨ą", + "notification_email_test_email_failed": "į„Ąæŗ•į™ŧ送æ¸ŦčŠĻé›ģ子éƒĩäģļīŧŒčĢ‹æĒĸæŸĨæ‚¨įš„č¨­åŽščŗ‡č¨Š", + "notification_email_test_email_sent": "æ¸ŦčŠĻé›ģ子éƒĩäģļ厞į™ŧ送臺 {email}。čĢ‹æĒĸæŸĨæ‚¨įš„æ”ļäģļåŒŖã€‚", + "notification_email_username_description": "ᔍæ–ŧ與é›ģ子éƒĩäģļäŧ翜å™¨éŠ—č­‰įš„äŊŋį”¨č€…åį¨ą", "notification_enable_email_notifications": "å•Ÿį”¨é›ģ子éƒĩäģļ通įŸĨ", - "notification_settings": "通įŸĨ", - "notification_settings_description": "įŽĄį†é€šįŸĨ設įŊŽīŧŒåŒ…æ‹Ŧé›ģ子éƒĩäģļ通įŸĨ", + "notification_settings": "通įŸĨč¨­åŽš", + "notification_settings_description": "įŽĄį†é€šįŸĨ設įŊŽīŧŒåŒ…æ‹Ŧé›ģ子éƒĩäģļ", "oauth_auto_launch": "č‡Ē動啟動", - "oauth_auto_launch_description": "導čĻŊ臺į™ģå…Ĩ頁éĸ垌č‡Ēå‹•é€˛čĄŒ OAuth į™ģå…Ĩæĩį¨‹", + "oauth_auto_launch_description": "進å…Ĩį™ģå…Ĩ頁éĸ時īŧŒč‡Ē動啟動 OAuth į™ģå…Ĩæĩį¨‹", "oauth_auto_register": "č‡Ē動č¨ģ冊", - "oauth_auto_register_description": "äŊŋᔍ OAuth į™ģ錄垌č‡Ē動č¨ģå†Šæ–°į”¨æˆļ", + "oauth_auto_register_description": "äŊŋᔍ OAuth į™ģå…Ĩ垌č‡Ē動č¨ģ冊新äŊŋᔍ者", "oauth_button_text": "按鈕文字", "oauth_client_secret_description": "åĻ‚æžœ OAuth æäž›č€…ä¸æ”¯æ´ PKCEīŧˆæŽˆæŦŠįĸŧ驗證įĸŧä礿›æŠŸåˆļīŧ‰īŧŒå‰‡æ­¤į‚ēåŋ…åĄĢé …į›Ž", - "oauth_enable_description": "ᔍ OAuth į™ģå…Ĩ", - "oauth_mobile_redirect_uri": "į§ģ動įĢ¯é‡åŽšå‘ URI", - "oauth_mobile_redirect_uri_override": "į§ģ動įĢ¯é‡åŽšå‘ URI čφ蓋", - "oauth_mobile_redirect_uri_override_description": "į•ļ OAuth æäž›č€…ä¸å…č¨ąäŊŋį”¨čĄŒå‹• URIīŧˆåĻ‚ã€Œ''{callback}''」īŧ‰æ™‚å•Ÿį”¨", + "oauth_enable_description": "äŊŋᔍ OAuth į™ģå…Ĩ", + "oauth_mobile_redirect_uri": "čĄŒå‹•įĢ¯é‡æ–°å°Žå‘ URI", + "oauth_mobile_redirect_uri_override": "čĻ†č“‹čĄŒå‹•įĢ¯é‡æ–°å°Žå‘ URI", + "oauth_mobile_redirect_uri_override_description": "į•ļ OAuth æäž›č€…ä¸å…č¨ąäŊŋį”¨čĄŒå‹•į̝ URIīŧˆäž‹åĻ‚ ''{callback}''īŧ‰æ™‚å•Ÿį”¨", + "oauth_role_claim": "č§’č‰˛åŽŖå‘Š", + "oauth_role_claim_description": "æ šæ“šæ­¤åŽŖå‘Šįš„å­˜åœ¨īŧŒč‡Ē動授äēˆįŽĄį†å“ĄæŦŠé™ã€‚čŠ˛åŽŖå‘Šįš„å€ŧ可äģĨ是 'user' 或 'admin'。", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģå…Ĩč¨­åŽš", "oauth_settings_more_details": "æŦ˛įž­č§Ŗæ­¤åŠŸčƒŊīŧŒčĢ‹åƒé–ąčĒĒæ˜Žæ›¸ã€‚", @@ -204,181 +239,187 @@ "oauth_storage_quota_claim": "å„˛å­˜é…éĄåŽŖå‘Š", "oauth_storage_quota_claim_description": "č‡Ē動將äŊŋį”¨č€…įš„å„˛å­˜é…éĄåŽšį‚ēæ­¤åŽŖå‘Šäš‹å€ŧ。", "oauth_storage_quota_default": "é č¨­å„˛å­˜é…éĄīŧˆGiBīŧ‰", - "oauth_storage_quota_default_description": "æœĒåŽŖå‘Šæ™‚æ‰€äŊŋį”¨įš„é…éĄīŧˆå–ŽäŊīŧšGiBīŧ‰ã€‚", + "oauth_storage_quota_default_description": "æœĒæäž›åŽŖå‘Šæ™‚æ‰€äŊŋį”¨įš„é…éĄīŧˆGiBīŧ‰ã€‚", "oauth_timeout": "čĢ‹æą‚é€žæ™‚", "oauth_timeout_description": "čĢ‹æą‚įš„é€žæ™‚æ™‚é–“īŧˆæ¯Ģį§’īŧ‰", - "password_enable_description": "ᔍé›ģ子éƒĩäģļ和密įĸŧį™ģå…Ĩ", + "password_enable_description": "äŊŋᔍé›ģ子éƒĩäģļ和密įĸŧį™ģå…Ĩ", "password_settings": "密įĸŧį™ģå…Ĩ", "password_settings_description": "įŽĄį†å¯†įĸŧį™ģå…Ĩč¨­åŽš", "paths_validated_successfully": "æ‰€æœ‰čˇ¯åž‘éŠ—č­‰æˆåŠŸ", "person_cleanup_job": "æ¸…į†äēēį‰Š", - "quota_size_gib": "配額īŧˆGiBīŧ‰", - "refreshing_all_libraries": "æ­Ŗåœ¨é‡æ–°æ•´į†æ‰€æœ‰åœ–åēĢ", + "quota_size_gib": "é…éĄå¤§å°īŧˆGiBīŧ‰", + "refreshing_all_libraries": "æ­Ŗåœ¨é‡æ–°æ•´į†æ‰€æœ‰åĒ’éĢ”åēĢ", "registration": "įŽĄį†č€…č¨ģ冊", - "registration_description": "į”ąæ–ŧ您是æœŦįŗģįĩąįš„éĻ–äŊäŊŋᔍ者īŧŒå› æ­¤å°‡æ‚¨æŒ‡æ´žį‚ē負č˛ŦįŽĄį†æœŦįŗģįĩąįš„įŽĄį†č€…īŧŒå…ļäģ–äŊŋį”¨č€…é ˆį”ąæ‚¨å”åŠŠåģēįĢ‹å¸ŗč™Ÿã€‚", - "require_password_change_on_login": "čĻæą‚äŊŋį”¨č€…åœ¨éĻ–æŦĄį™ģå…Ĩ時更攚密įĸŧ", - "reset_settings_to_default": "å°‡č¨­åŽšé‡č¨­å›žé č¨­", - "reset_settings_to_recent_saved": "åˇ˛č¨­å›žæœ€åžŒå„˛å­˜įš„č¨­åŽš", - "scanning_library": "掃描圖åēĢ", + "registration_description": "į”ąæ–ŧ您是įŗģįĩąä¸Šįš„įŦŦ一äŊäŊŋᔍ者īŧŒæ‚¨å°‡čĸĢæŒ‡æ´žį‚ēįŗģįĩąįŽĄį†å“ĄīŧŒä¸Ļ負č˛ŦįŽĄį†į›¸é—œäē‹å‹™īŧŒåžŒįēŒįš„å…ļäģ–äŊŋį”¨č€…äšŸå°‡į”ąæ‚¨åģēįĢ‹ã€‚", + "require_password_change_on_login": "čĻæą‚äŊŋį”¨č€…åœ¨éĻ–æŦĄį™ģå…Ĩæ™‚čŽŠæ›´å¯†įĸŧ", + "reset_settings_to_default": "å°‡č¨­åŽšé‡č¨­į‚ē預設å€ŧ", + "reset_settings_to_recent_saved": "å°‡č¨­åŽšé‡č¨­į‚ē最čŋ‘å„˛å­˜įš„č¨­åޚ", + "scanning_library": "掃描åĒ’éĢ”åēĢ", "search_jobs": "搜尋äģģ務â€Ļ", "send_welcome_email": "å‚ŗé€æ­ĄčŋŽé›ģ子éƒĩäģļ", "server_external_domain_settings": "外部įļ˛åŸŸ", - "server_external_domain_settings_description": "å…Ŧ開įļ˛å€īŧŒåŒ…åĢ http(s)://", - "server_public_users": "č¨ĒåŽĸäŊŋᔍ者", - "server_public_users_description": "將äŊŋį”¨č€…æ–°åĸžč‡ŗå…ąį”¨į›¸į°ŋ時īŧŒæœƒåˆ—å‡ē所有äŊŋᔍ者īŧˆå§“名、emailīŧ‰ã€‚關閉時īŧŒäŊŋį”¨č€…åˆ—čĄ¨åƒ…å°įŽĄį†č€…į”Ÿæ•ˆã€‚", - "server_settings": "äŧ翜å™¨", + "server_external_domain_settings_description": "å…Ŧ開分äēĢ逪įĩįš„įļ˛åŸŸīŧŒåŒ…åĢ http(s)://", + "server_public_users": "å…Ŧ開äŊŋᔍ者", + "server_public_users_description": "在將äŊŋį”¨č€…æ–°åĸžåˆ°å…ąäēĢᛏį°ŋ時īŧŒæœƒåˆ—å‡ē所有äŊŋį”¨č€…įš„å§“åčˆ‡é›ģ子éƒĩäģļã€‚åœį”¨æ­¤åŠŸčƒŊ垌īŧŒäŊŋį”¨č€…æ¸…å–Žå°‡åƒ…äž›įŗģįĩąįŽĄį†å“ĄæŸĨįœ‹ã€‚", + "server_settings": "äŧ翜å™¨č¨­åޚ", "server_settings_description": "įŽĄį†äŧ翜å™¨č¨­åޚ", "server_welcome_message": "æ­ĄčŋŽč¨Šæ¯", "server_welcome_message_description": "在į™ģå…Ĩ頁éĸéĄ¯į¤ēįš„č¨Šæ¯ã€‚", - "sidecar_job": "邊č슿¨ĄåŧčŠŽé‡‹čŗ‡æ–™", - "sidecar_job_description": "åžžæĒ”æĄˆįŗģįĩ࿐œį´ĸ或同æ­Ĩ邊č슿¨ĄåŧčŠŽé‡‹čŗ‡æ–™", + "sidecar_job": "側æŽĨæĒ”æĄˆä¸­įšŧčŗ‡æ–™", + "sidecar_job_description": "åžžæĒ”æĄˆįŗģįĩąåĩæ¸Ŧ或同æ­Ĩ側æŽĨæĒ”æĄˆä¸­įšŧčŗ‡æ–™", "slideshow_duration_description": "每åŧĩåœ–į‰‡æ”žæ˜ įš„į§’æ•¸", "smart_search_job_description": "åŸˇčĄŒæŠŸå™¨å­¸įŋ’有劊æ–ŧæ™ē慧搜尋", - "storage_template_date_time_description": "æĒ”æĄˆįš„å‰ĩåģēæ™‚æˆŗæœƒį”¨æ–ŧåˆ¤æ–ˇæ™‚é–“čŗ‡č¨Š", - "storage_template_date_time_sample": "æ™‚é–“æ¨Ŗåŧ {date}", - "storage_template_enable_description": "å•Ÿį”¨å­˜å„˛æ¨Ąæŋåŧ•擎", - "storage_template_hash_verification_enabled": "æ•Ŗåˆ—å‡Ŋæ•°éŠ—č­‰åˇ˛å•Ÿį”¨", - "storage_template_hash_verification_enabled_description": "å•Ÿį”¨æ•Ŗåˆ—å‡Ŋæ•°éŠ—č­‰īŧŒé™¤éžæ‚¨åžˆæ¸…æĨšåœ°įŸĨé“é€™å€‹é¸é …įš„äŊœį”¨īŧŒåĻ則čĢ‹å‹ŋåœį”¨æ­¤åŠŸčƒŊ", - "storage_template_migration": "å­˜å„˛æ¨Ąæŋ遡į§ģ", - "storage_template_migration_description": "åĨ—į”¨å‰ {template} å…ˆä¸Šå‚ŗé …į›Ž", - "storage_template_migration_info": "é€į”¨å„˛å­˜į¯„äž‹å°‡å°‡æŠŠæ‰€æœ‰æĒ”æĄˆå‰¯æĒ”åæ”šįˆ˛å°å¯Ģã€‚æ¨Ąæŋæ›´æ–°åƒ…éŠį”¨æ–ŧæ–°é …į›Žã€‚č‹ĨčρåĨ—į”¨éŽåŽģį¯„äž‹čĢ‹å…ˆä¸Šå‚ŗé …į›ŽīŧŒčĢ‹åŸˇčĄŒ {job}。", - "storage_template_migration_job": "å­˜å„˛æ¨Ąæŋ遡į§ģäģģ務", - "storage_template_more_details": "æŦ˛äē†č§Ŗæ›´å¤šæœ‰é—œæ­¤åŠŸčƒŊįš„čŠŗį´°äŋĄæ¯īŧŒčĢ‹åƒé–ą å­˜å„˛æ¨Ąæŋ 及å…ļ åŊąéŸŋ", - "storage_template_onboarding_description_v2": "å•Ÿį”¨æ­¤åŠŸčƒŊ垌īŧŒįŗģįĩąå°‡æ šæ“šäŊŋᔍ者č‡Ēč¨‚įš„į¯„æœŦč‡Ēå‹•æ•´į†æĒ”æĄˆã€‚æœ‰é—œæ›´å¤šäŋĄæ¯īŧŒčĢ‹åƒé–ąæ–‡æĒ” 。", - "storage_template_path_length": "å¤§č‡´čˇ¯åž‘é•ˇåēĻ限åˆļīŧš{length, number}/{limit, number}", - "storage_template_settings": "å­˜å„˛æ¨Ąæŋ", + "storage_template_date_time_description": "æĒ”æĄˆįš„åģēįĢ‹æ™‚é–“æˆŗæœƒį”¨æ–ŧæ—ĨæœŸčˆ‡æ™‚é–“čŗ‡č¨Š", + "storage_template_date_time_sample": "æŽĄæ¨Ŗæ™‚é–“ {date}", + "storage_template_enable_description": "å•Ÿį”¨å„˛å­˜į¯„æœŦåŧ•擎", + "storage_template_hash_verification_enabled": "雜暊å‡Ŋæ•°éŠ—č­‰åˇ˛å•Ÿį”¨", + "storage_template_hash_verification_enabled_description": "å•Ÿį”¨é›œæšŠå‡Ŋæ•°éŠ—č­‰īŧŒé™¤éžæ‚¨åžˆæ¸…æĨšåœ°įŸĨé“é€™å€‹é¸é …įš„äŊœį”¨īŧŒåĻ則čĢ‹å‹ŋåœį”¨æ­¤åŠŸčƒŊ", + "storage_template_migration": "å„˛å­˜į¯„æœŦ遡į§ģ", + "storage_template_migration_description": "å°‡į›Žå‰įš„ {template} åĨ—į”¨åˆ°å…ˆå‰ä¸Šå‚ŗįš„é …į›Ž", + "storage_template_migration_info": "å„˛å­˜į¯„æœŦ會將所有副æĒ”名čŊ‰æ›į‚ē小å¯Ģã€‚į¯„æœŦčŽŠæ›´åĒ會åĨ—į”¨åˆ°æ–°įš„é …į›Žã€‚č‹Ĩčρ將ᝄæœŦčŋŊæē¯åĨ—į”¨åˆ°å…ˆå‰ä¸Šå‚ŗįš„é …į›ŽīŧŒčĢ‹åŸˇčĄŒ {job}。", + "storage_template_migration_job": "å„˛å­˜į¯„æœŦ遡į§ģäģģ務", + "storage_template_more_details": "關æ–ŧ此功čƒŊįš„æ›´å¤ščŠŗį´°čŗ‡č¨ŠīŧŒčĢ‹åƒé–ąå„˛å­˜į¯„æœŦ及å…ļåŊąéŸŋ", + "storage_template_onboarding_description_v2": "å•Ÿį”¨åžŒīŧŒæ­¤åŠŸčƒŊ會䞝äŊŋᔍ者č‡Ēč¨‚įš„į¯„æœŦč‡Ēå‹•æ•´į†æĒ”æĄˆã€‚æ›´å¤ščŗ‡č¨ŠčĢ‹åƒé–ąčĒĒæ˜Žæ–‡äģļ。", + "storage_template_path_length": "預äŧ°čˇ¯åž‘镡åēĻ上限īŧš{length, number}/{limit, number}", + "storage_template_settings": "å„˛å­˜į¯„æœŦ", "storage_template_settings_description": "įŽĄį†ä¸Šå‚ŗæĒ”æĄˆįš„čŗ‡æ–™å¤žįĩæ§‹å’ŒæĒ”名", "storage_template_user_label": "{label} 是äŊŋį”¨č€…įš„å„˛å­˜æ¨™įą¤", "system_settings": "įŗģįĩąč¨­åޚ", - "tag_cleanup_job": "æ¸…į†æ¨™č¨˜", + "tag_cleanup_job": "æ¸…į†æ¨™įą¤", "template_email_available_tags": "您可äģĨåœ¨æ‚¨įš„į¯„æœŦ中äŊŋᔍäģĨä¸‹čŽŠæ•¸īŧš{tags}", - "template_email_if_empty": "åĻ‚æžœį¯„æœŦį‚ēįŠēīŧŒå°‡äŊŋᔍ預荭é›ģ子éƒĩäģļ。", - "template_email_invite_album": "邀čĢ‹é …į›Žį¯„æœŦ", + "template_email_if_empty": "åĻ‚æžœį¯„æœŦį‚ēįŠēīŧŒå°‡äŊŋᔍ預荭é›ģ子éƒĩäģļᝄæœŦ。", + "template_email_invite_album": "ᛏį°ŋ邀č̋ᝄæœŦ", "template_email_preview": "預čĻŊ", - "template_email_settings": "EmailᝄæœŦ", - "template_email_update_album": "更新向æœŦᝄæœŦ", - "template_email_welcome": "æ­ĄčŋŽEmailᝄæœŦ", + "template_email_settings": "é›ģ子éƒĩäģļᝄæœŦ", + "template_email_update_album": "ᛏį°ŋæ›´æ–°į¯„æœŦ", + "template_email_welcome": "æ­ĄčŋŽéƒĩäģļᝄæœŦ", "template_settings": "通įŸĨᝄæœŦ", "template_settings_description": "įŽĄį†é€šįŸĨįš„č‡ĒåŽšįžŠį¯„æœŦ", - "theme_custom_css_settings": "č‡Ē訂 CSS", + "theme_custom_css_settings": "č‡ĒåŽšįžŠ CSS", "theme_custom_css_settings_description": "可äģĨį”¨åą¤į–Šæ¨ŖåŧčĄ¨īŧˆCSSīŧ‰äž†č‡Ē訂 Immich įš„č¨­č¨ˆã€‚", - "theme_settings": "ä¸ģ題", + "theme_settings": "ä¸ģéĄŒč¨­åŽš", "theme_settings_description": "č‡Ē訂 Immich įš„įļ˛é į•Œéĸ", "thumbnail_generation_job": "į”ĸį”Ÿį¸Žåœ–", "thumbnail_generation_job_description": "į‚ē每個æĒ”æĄˆį”ĸį”Ÿå¤§ã€å°åŠæ¨ĄįŗŠį¸Žåœ–īŧŒäšŸį‚ē每äŊäēēį‰Šį”ĸį”Ÿį¸Žåœ–", "transcoding_acceleration_api": "加速 API", - "transcoding_acceleration_api_description": "API 將ᔍæ–ŧįĄŦéĢ”åŠ é€Ÿã€‚č¨­åŽšå„Ē先äŊŋᔍīŧšå¤ąæ•—會äŊŋᔍčģŸéĢ”čŊ‰įĸŧ。是åĻ支援 VP9 ᎍįĸŧæ ŧåŧäžį…§æ‚¨įš„įĄŦéĢ”æ”¯æ´č€ŒåŽšã€‚", + "transcoding_acceleration_api_description": "æ­¤ API 會äŊŋį”¨æ‚¨įš„įĄŦéĢ”äģĨ加速čŊ‰įĸŧæĩį¨‹ã€‚æ­¤č¨­åŽšæŽĄã€Œį›ĄåŠ›č€Œį‚ēã€æ¨Ąåŧâ€”—č‹ĨčŊ‰įĸŧå¤ąæ•—īŧŒå°‡æœƒå›žé€€č‡ŗčģŸéĢ”čŊ‰įĸŧ。VP9 是åĻčƒŊ運äŊœīŧŒå–æąēæ–ŧæ‚¨įš„įĄŦéĢ”é…įŊŽã€‚", "transcoding_acceleration_nvenc": "NVENCīŧˆéœ€čρ NVIDIA GPUīŧ‰", - "transcoding_acceleration_qsv": "åŋĢ速同æ­Ĩīŧˆéœ€čρįŦŦ七äģŖæˆ–é̘æ–ŧįŦŦ七äģŖįš„ Intel CPUīŧ‰", - "transcoding_acceleration_rkmpp": "RKMPPīŧˆåƒ…éŠį”¨æ–ŧ Rockchip SoCīŧ‰", + "transcoding_acceleration_qsv": "Quick Syncīŧˆéœ€čρįŦŦ 7 äģŖæˆ–æ›´æ–°įš„ Intel 處ᐆ噍īŧ‰", + "transcoding_acceleration_rkmpp": "RKMPPīŧˆåƒ…éŠį”¨æ–ŧ Rockchip SOCsīŧ‰", "transcoding_acceleration_vaapi": "VAAPI", - "transcoding_accepted_audio_codecs": "æ”¯æ´įš„éŸŗč¨Šįˇ¨įĸŧ器", - "transcoding_accepted_audio_codecs_description": "選擇不需čρčŊ‰įĸŧįš„éŸŗč¨Šįˇ¨įĸŧæ ŧåŧã€‚åƒ…éŠį”¨æ–ŧ某äē›čŊ‰įĸŧį­–į•Ĩ。", - "transcoding_accepted_containers": "æŽĨå—įš„åŽšå™¨æ ŧåŧ", - "transcoding_accepted_containers_description": "選擇不需čĻé‡æ–°å°čŖį‚ē MP4 įš„åŽšå™¨æ ŧåŧã€‚åƒ…į”¨æ–ŧ某äē›čŊ‰įĸŧį­–į•Ĩ。", - "transcoding_accepted_video_codecs": "æ”¯æ´įš„åŊąį‰‡įˇ¨įĸŧ器", - "transcoding_accepted_video_codecs_description": "選擇不需čρčŊ‰įĸŧįš„čĻ–č¨Šįˇ¨įĸŧæ ŧåŧã€‚åƒ…éŠį”¨æ–ŧ某äē›čŊ‰įĸŧį­–į•Ĩ。", - "transcoding_advanced_options_description": "大多數äŊŋį”¨č€…ä¸éœ€čĻæ›´æ”šįš„é¸é …", - "transcoding_audio_codec": "韺荊ᎍįĸŧ器", - "transcoding_audio_codec_description": "Opus æ˜¯éŸŗčŗĒ最åĨŊįš„é¸é …īŧŒäŊ†čˆ‡čˆŠč¨­å‚™æˆ–čˆŠį‰ˆčģŸéĢ”įš„į›¸åŽšæ€§čŧƒäŊŽã€‚", - "transcoding_bitrate_description": "é̘æ–ŧ最大äŊå…ƒé€ŸįŽ‡æˆ–æ ŧåŧä¸čĸĢæ”¯æ´įš„åŊąį‰‡", - "transcoding_codecs_learn_more": "æŦ˛įž­č§Ŗæ­¤č™•äŊŋį”¨įš„čĄ“čĒžīŧŒčĢ‹åƒé–ą FFmpeg čĒĒæ˜Žæ›¸ä¸­įš„ H.264 ᎍ觪įĸŧ器、HEVC ᎍ觪įĸŧ器和 VP9 ᎍ觪įĸŧ器。", - "transcoding_constant_quality_mode": "å›ē厚品čŗĒæ¨Ąåŧ", - "transcoding_constant_quality_mode_description": "ICQ 比 CQP 更åĨŊīŧŒäŊ†æŸäē›įĄŦéĢ”åŠ é€Ÿč¨­å‚™ä¸æ”¯æ´æ­¤æ¨Ąåŧã€‚č¨­åŽšæ­¤é¸é …æ™‚īŧŒæœƒåœ¨äŊŋᔍåŸēæ–ŧ品čŗĒįš„įˇ¨įĸŧ時偏åĨŊæŒ‡åŽšįš„æ¨Ąåŧã€‚此選項對 NVENC į„Ąæ•ˆīŧŒå› į‚ē NVENC 不支援 ICQ。", + "transcoding_accepted_audio_codecs": "可æŽĨå—įš„éŸŗč¨Šįˇ¨č§Ŗįĸŧ器", + "transcoding_accepted_audio_codecs_description": "選擇å“Ēäē›éŸŗč¨Šįˇ¨č§Ŗįĸŧ器不需čρčŊ‰įĸŧã€‚æ­¤č¨­åŽšåƒ…éŠį”¨æ–ŧį‰šåŽšįš„čŊ‰įĸŧį­–į•Ĩ。", + "transcoding_accepted_containers": "可æŽĨå—įš„å°čŖæ ŧåŧ", + "transcoding_accepted_containers_description": "選擇å“Ēäē›å°čŖæ ŧåŧä¸éœ€čĻé‡æ–°å°čŖīŧˆremuxīŧ‰į‚ē MP4ã€‚æ­¤č¨­åŽšåƒ…éŠį”¨æ–ŧį‰šåŽšįš„čŊ‰įĸŧį­–į•Ĩ。", + "transcoding_accepted_video_codecs": "æŽĨå—įš„åŊąį‰‡įˇ¨č§Ŗįĸŧ器", + "transcoding_accepted_video_codecs_description": "選擇å“Ēäē›čĻ–č¨Šįˇ¨č§Ŗįĸŧ器不需čρčŊ‰įĸŧã€‚æ­¤č¨­åŽšåƒ…éŠį”¨æ–ŧį‰šåŽšįš„čŊ‰įĸŧį­–į•Ĩ。", + "transcoding_advanced_options_description": "大多數äŊŋį”¨č€…ä¸éœ€æ›´å‹•įš„é¸é …", + "transcoding_audio_codec": "韺荊ᎍ觪įĸŧ器", + "transcoding_audio_codec_description": "æ˜¯éŸŗčŗĒ最äŊŗįš„選項īŧŒäŊ†čˆ‡čˆŠčŖįŊŽæˆ–čˆŠį‰ˆčģŸéĢ”įš„į›¸åŽšæ€§čŧƒäŊŽã€‚", + "transcoding_bitrate_description": "äŊå…ƒįއé̘æ–ŧ最大å€ŧ或æ ŧåŧä¸åœ¨å¯æŽĨå—į¯„åœįš„åŊąį‰‡", + "transcoding_codecs_learn_more": "åĻ‚éœ€é€˛ä¸€æ­Ĩäē†č§Ŗæ­¤č™•äŊŋį”¨įš„čĄ“čĒžīŧŒčĢ‹åƒé–ą FFmpeg 文äģļ中關æ–ŧ H.264 ᎍ觪įĸŧ器、HEVC ᎍ觪įĸŧ器 及 VP9 ᎍ觪įĸŧ器 įš„čĒĒæ˜Žã€‚", + "transcoding_constant_quality_mode": "恆厚品čŗĒæ¨Ąåŧ", + "transcoding_constant_quality_mode_description": "ICQ įš„æ•ˆæžœå„Ēæ–ŧ CQPīŧŒäŊ†éƒ¨åˆ†įĄŦéĢ”åŠ é€ŸčŖįŊŽä¸æ”¯æ´æ­¤æ¨Ąåŧã€‚č¨­åŽšæ­¤é¸é …æ™‚īŧŒåœ¨äŊŋᔍäģĨ品čŗĒį‚ēåŸēæē–įš„įˇ¨įĸŧ時會å„Ēå…ˆæŽĄį”¨æ‰€æŒ‡åŽšįš„æ¨Ąåŧã€‚NVENC 不支援 ICQīŧŒå› æ­¤æ­¤č¨­åޚ圍 NVENC 下會čĸĢåŋŊį•Ĩ。", "transcoding_constant_rate_factor": "æ†åŽšé€ŸįŽ‡å› å­īŧˆ-crfīŧ‰", "transcoding_constant_rate_factor_description": "čĻ–č¨Šå“čŗĒį­‰į´šã€‚å…¸åž‹å€ŧį‚ē H.264 įš„ 23、HEVC įš„ 28、VP9 įš„ 31 和 AV1 įš„ 35。數å€ŧčļŠäŊŽīŧŒå“čŗĒčļŠåĨŊīŧŒäŊ†æœƒį”ĸį”Ÿčŧƒå¤§įš„æĒ”æĄˆã€‚", - "transcoding_disabled_description": "不čŊ‰įĸŧåŊąį‰‡īŧŒå¯čƒŊæœƒčŽ“æŸäē›åŽĸæˆļįĢ¯į„Ąæŗ•æ­Ŗå¸¸æ’­æ”ž", + "transcoding_disabled_description": "不對äģģäŊ•åŊąį‰‡é€˛čĄŒčŊ‰įĸŧīŧŒå¯čƒŊæœƒå°Žč‡´éƒ¨åˆ†åŽĸæˆļįĢ¯į„Ąæŗ•æ­Ŗå¸¸æ’­æ”ž", "transcoding_encoding_options": "ᎍįĸŧ選項", "transcoding_encoding_options_description": "č¨­åŽšįˇ¨įĸŧåŊąį‰‡įš„ᎍ觪įĸŧå™¨ã€č§ŖæžåēĻ、品čŗĒ和å…ļäģ–選項", "transcoding_hardware_acceleration": "įĄŦéĢ”åŠ é€Ÿ", - "transcoding_hardware_acceleration_description": "å¯Ļ銗性功čƒŊīŧšæ›´åŋĢįš„čŊ‰įĸŧ速åēĻīŧŒäŊ†åœ¨į›¸åŒäŊå…ƒįއ䏋品čŗĒčŧƒåˇŽ", + "transcoding_hardware_acceleration_description": "å¯Ļ銗性功čƒŊīŧšå¯åŠ åŋĢčŊ‰įĸŧ速åēĻīŧŒäŊ†åœ¨į›¸åŒäŊå…ƒįŽ‡ä¸‹å¯čƒŊ降äŊŽå“čŗĒ", "transcoding_hardware_decoding": "įĄŦé̔觪įĸŧ", - "transcoding_hardware_decoding_setting_description": "不åĒåŠ é€Ÿįˇ¨įĸŧīŧŒé‚„å•Ÿį”¨įĢ¯å°įĢ¯åŠ é€Ÿã€‚å¯čƒŊ不支援某äē›åŊąį‰‡ã€‚", + "transcoding_hardware_decoding_setting_description": "å•Ÿį”¨į̝到įĢ¯åŠ é€ŸīŧŒč€Œéžåƒ…åŠ é€Ÿįˇ¨įĸŧ。此功čƒŊ可čƒŊä¸éŠį”¨æ–ŧ所有åŊąį‰‡ã€‚", "transcoding_max_b_frames": "最大 B 嚀數", - "transcoding_max_b_frames_description": "čŧƒéĢ˜įš„æ•¸å€ŧå¯æå‡åŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†æœƒé™äŊŽįˇ¨įĸŧ速åēĻ。可čƒŊ與čŧƒčˆŠč¨­å‚™įš„įĄŦéĢ”åŠ é€Ÿä¸į›¸åŽšã€‚č¨­åŽšį‚ē 0 æ™‚æœƒåœį”¨ B-framesīŧŒč€Œ -1 則會č‡Ēå‹•č¨­åŽšæ­¤æ•¸å€ŧ。", + "transcoding_max_b_frames_description": "čŧƒéĢ˜įš„æ•¸å€ŧå¯æå‡åŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†æœƒé™äŊŽįˇ¨įĸŧ速åēĻ。在čŧƒčˆŠįš„čŖįŊŽä¸ŠīŧŒå¯čƒŊ與įĄŦéĢ”åŠ é€Ÿä¸į›¸åŽšã€‚0 äģŖčĄ¨åœį”¨ B 嚀īŧŒč€Œ -1 則會č‡Ēå‹•č¨­åŽšæ­¤æ•¸å€ŧ。", "transcoding_max_bitrate": "最大äŊå…ƒé€Ÿįއ", - "transcoding_max_bitrate_description": "č¨­åŽšæœ€å¤§äŊå…ƒé€ŸįŽ‡å¯äģĨäŊŋæĒ”æĄˆå¤§å°æ›´å…ˇå¯é æ¸Ŧ性īŧŒäŊ†æœƒį¨åžŽé™äŊŽå“čŗĒ。在 720p č§ŖæžåēĻ下īŧŒå…¸åž‹å€ŧį‚ē VP9 或 HEVC įš„ 2600 kbit/sīŧŒæˆ– H.264 įš„ 4500 kbit/sã€‚č¨­įŊŽį‚ē 0 åœį”¨æ­¤åŠŸčƒŊ。", + "transcoding_max_bitrate_description": "č¨­åŽšæœ€å¤§äŊå…ƒįŽ‡å¯äģĨ在čŧ•åžŽįŠ§į‰˛å“čŗĒįš„æƒ…æŗä¸‹īŧŒčŽ“æĒ”æĄˆå¤§å°æ›´åŽšæ˜“é æ¸Ŧ。在 720p č§ŖæžåēĻ下īŧŒVP9 或 HEVC įš„å…¸åž‹å€ŧį‚ē 2600 kbit/sīŧŒH.264 則į‚ē 4500 kbit/sã€‚č¨­į‚ē 0 å‰‡åœį”¨æ­¤åŠŸčƒŊ。", "transcoding_max_keyframe_interval": "最大關éĩ嚀間隔", "transcoding_max_keyframe_interval_description": "設įŊŽé—œéĩåš€äš‹é–“įš„æœ€å¤§åš€čˇã€‚čŧƒäŊŽįš„å€ŧ會降äŊŽåŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†å¯äģĨ攚善搜尋時間īŧŒä¸Ļ有可čƒŊ會攚善åŋĢé€ŸčŽŠå‹•å ´æ™¯įš„å“čŗĒ。0 會č‡Ē動荭įŊŽæ­¤å€ŧ。", - "transcoding_optimal_description": "é̘æ–ŧį›Žæ¨™č§ŖæžåēĻæˆ–æ ŧåŧä¸čĸĢæ”¯æ´įš„åŊąį‰‡", + "transcoding_optimal_description": "é̘æ–ŧį›Žæ¨™č§ŖæžåēĻæˆ–æ ŧåŧä¸åœ¨å¯æŽĨå—į¯„åœįš„åŊąį‰‡", "transcoding_policy": "čŊ‰įĸŧį­–į•Ĩ", "transcoding_policy_description": "č¨­åŽšåŊąį‰‡é€˛čĄŒčŊ‰įĸŧįš„æĸäģļ", "transcoding_preferred_hardware_device": "éϖ遏įĄŦé̔荭備", - "transcoding_preferred_hardware_device_description": "åƒ…éŠį”¨æ–ŧ VAAPI 和 QSVã€‚č¨­åŽšį”¨æ–ŧįĄŦéĢ”čŊ‰įĸŧįš„ DRI ᝀéģžã€‚", + "transcoding_preferred_hardware_device_description": "åƒ…éŠį”¨æ–ŧ VAAPI 和 QSVã€‚č¨­åŽšį”¨æ–ŧįĄŦéĢ”čŊ‰įĸŧįš„ dri ᝀéģžã€‚", "transcoding_preset_preset": "預設å€ŧīŧˆ-presetīŧ‰", - "transcoding_preset_preset_description": "åŖ“į¸Žé€ŸåēĻã€‚åœ¨é‡å°į‰šåŽšäŊå…ƒé€ŸįŽ‡æ™‚īŧŒčŧƒæ…ĸįš„é č¨­å€ŧ會減少æĒ”æĄˆå¤§å°ä¸Ļ提éĢ˜å“čŗĒ。VP9 會åŋŊį•Ĩé̘æ–ŧ「fasterã€įš„é€ŸåēĻ。", - "transcoding_reference_frames": "åƒč€ƒåš€æ•¸", - "transcoding_reference_frames_description": "åŖ“į¸ŽįĩĻåŽšåš€æ™‚åƒč€ƒįš„åš€æ•¸ã€‚čŧƒéĢ˜įš„å€ŧ可äģĨ提éĢ˜åŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†æœƒé™äŊŽįˇ¨įĸŧ速åēĻ。0 會č‡Ē動荭įŊŽæ­¤å€ŧ。", - "transcoding_required_description": "僅限æ ŧåŧä¸čĸĢæ”¯æ´įš„åŊąį‰‡", - "transcoding_settings": "åŊąį‰‡čŊ‰įĸŧ", - "transcoding_settings_description": "įŽĄį†åŊąį‰‡įš„č§ŖæžåēĻå’Œįˇ¨įĸŧčŗ‡č¨Š", + "transcoding_preset_preset_description": "åŖ“į¸Žé€ŸåēĻ。čŧƒæ…ĸįš„é č¨­å€ŧ會į”ĸį”Ÿčŧƒå°įš„æĒ”æĄˆīŧŒä¸Ļ在鎖厚äŊå…ƒįŽ‡æ™‚æå‡å“čŗĒ。VP9 在速åēĻé̘æ–ŧ「faster」時將åŋŊį•Ĩč¨­åŽšã€‚", + "transcoding_reference_frames": "åƒč€ƒåš€", + "transcoding_reference_frames_description": "åœ¨åŖ“į¸Žį‰šåŽšåš€æ™‚æ‰€åƒč€ƒįš„åš€æ•¸é‡ã€‚æ•¸å€ŧčļŠéĢ˜å¯æå‡åŖ“į¸Žæ•ˆįŽ‡īŧŒäŊ†æœƒé™äŊŽįˇ¨įĸŧ速åēĻã€‚č¨­į‚ē 0 則č‡Ē動æąē厚此數å€ŧ。", + "transcoding_required_description": "僅限æ ŧåŧä¸čĸĢæŽĨå—įš„åŊąį‰‡", + "transcoding_settings": "åŊąį‰‡čŊ‰įĸŧč¨­åŽš", + "transcoding_settings_description": "įŽĄį†čρčŊ‰įĸŧįš„åŊąį‰‡åŠå…ļč™•į†æ–šåŧ", "transcoding_target_resolution": "į›Žæ¨™č§ŖæžåēĻ", "transcoding_target_resolution_description": "čŧƒéĢ˜įš„č§ŖæžåēĻ可äģĨäŋį•™æ›´å¤šį´°į¯€īŧŒäŊ†įˇ¨įĸŧ時間čŧƒé•ˇīŧŒæĒ”æĄˆäšŸčŧƒå¤§īŧŒä¸”可čƒŊ降äŊŽæ‡‰į”¨į¨‹åŧįš„回應速åēĻ。", "transcoding_temporal_aq": "時間č‡Ē遊應量化īŧˆTemporal AQīŧ‰", "transcoding_temporal_aq_description": "åƒ…éŠį”¨æ–ŧ NVENCīŧŒå¯æå‡éĢ˜į´°į¯€ã€äŊŽå‹•æ…‹å ´æ™¯įš„į•ĢčŗĒ。可čƒŊ與čŧƒčˆŠįš„č¨­å‚™ä¸į›¸åŽšã€‚", "transcoding_threads": "åŸˇčĄŒįˇ’æ•¸é‡", - "transcoding_threads_description": "čŧƒéĢ˜įš„å€ŧ會加åŋĢᎍįĸŧ速åēĻīŧŒäŊ†æœƒæ¸›å°‘äŧ翜å™¨åœ¨é‹čĄŒéŽį¨‹ä¸­č™•ᐆå…ļäģ–äģģå‹™įš„įŠē間。此å€ŧ不應čļ…過 CPU æ ¸åŋƒæ•¸ã€‚設įŊŽį‚ē 0 可äģĨæœ€å¤§åŒ–åˆŠį”¨įŽ‡ã€‚", + "transcoding_threads_description": "čŧƒéĢ˜įš„å€ŧ會加åŋĢᎍįĸŧ速åēĻīŧŒäŊ†æœƒæ¸›å°‘äŧ翜å™¨åœ¨é‹čĄŒéŽį¨‹ä¸­č™•ᐆå…ļäģ–äģģå‹™įš„įŠē間。此å€ŧ不應čļ…過 CPU æ ¸åŋƒæ•¸ã€‚設åޚį‚ē 0 可äģĨæœ€å¤§åŒ–åˆŠį”¨įŽ‡ã€‚", "transcoding_tone_mapping": "色čĒŋ映射", "transcoding_tone_mapping_description": "在將 HDR åŊąį‰‡čŊ‰æ›į‚ē SDR 時īŧŒį›Ąé‡įļ­æŒåŽŸå§‹č§€æ„Ÿã€‚æ¯į¨Žæŧ”įŽ—æŗ•åœ¨č‰˛åŊŠã€į´°į¯€å’ŒäēŽåēĻæ–šéĸéƒŊæœ‰ä¸åŒįš„æŦŠčĄĄã€‚Hable äŋį•™į´°į¯€īŧŒMobius äŋį•™č‰˛åŊŠīŧŒReinhard äŋį•™äēŽåēĻ。", "transcoding_transcode_policy": "čŊ‰įĸŧį­–į•Ĩ", "transcoding_transcode_policy_description": "åŊąį‰‡äŊ•æ™‚æ‡‰é€˛čĄŒčŊ‰įĸŧįš„į­–į•Ĩ。HDR åŊąį‰‡ä¸€åŽšæœƒčŊ‰įĸŧīŧˆé™¤éžåœį”¨čŊ‰įĸŧīŧ‰ã€‚", "transcoding_two_pass_encoding": "兊階æŽĩᎍįĸŧ", "transcoding_two_pass_encoding_setting_description": "äŊŋį”¨å…ŠéšŽæŽĩᎍįĸŧ來į”ĸį”Ÿå“čŗĒ更äŊŗįš„ᎍįĸŧåŊąį‰‡ã€‚į•ļå•Ÿį”¨æœ€å¤§äŊå…ƒé€ŸįŽ‡æ™‚īŧˆH.264 和 HEVC åŋ…é ˆå•Ÿį”¨æ­¤é¸é …æ‰čƒŊ運äŊœīŧ‰īŧŒæ­¤æ¨ĄåŧæœƒäģĨ最大äŊå…ƒé€ŸįŽ‡äž†čĒŋ整äŊå…ƒé€ŸįŽ‡į¯„åœīŧŒä¸ĻåŋŊį•Ĩ CRF。對æ–ŧ VP9īŧŒåĻ‚æžœåœį”¨æœ€å¤§äŊå…ƒé€ŸįއīŧŒå¯äģĨäŊŋᔍ CRF。", - "transcoding_video_codec": "čĻ–č¨Šįˇ¨įĸŧ器", - "transcoding_video_codec_description": "VP9 å…ˇæœ‰éĢ˜æ•ˆčƒŊä¸”į›¸åŽšæ–ŧįļ˛é īŧŒäŊ†čŊ‰įĸŧ時間čŧƒé•ˇã€‚HEVC įš„æ•ˆčƒŊᛏčŋ‘īŧŒäŊ†įļ˛é į›¸åŽšæ€§čŧƒäŊŽã€‚H.264 å…ˇæœ‰åģŖæŗ›įš„į›¸åŽšæ€§ä¸”čŊ‰įĸŧ速åēĻåŋĢīŧŒäŊ†į”ĸį”Ÿįš„æĒ”æĄˆčŧƒå¤§ã€‚AV1 æ˜¯į›Žå‰æ•ˆįŽ‡æœ€åĨŊįš„įˇ¨č§Ŗįĸŧ器īŧŒäŊ†čŧƒčˆŠč¨­å‚™ä¸æ”¯æ´ã€‚", + "transcoding_video_codec": "åŊąį‰‡įˇ¨č§Ŗįĸŧ器", + "transcoding_video_codec_description": "VP9 å…ˇæœ‰éĢ˜åŖ“į¸Žæ•ˆįŽ‡čˆ‡č‰¯åĨŊįš„įļ˛é į›¸åŽšæ€§īŧŒäŊ†čŊ‰įĸŧ速åēĻčŧƒæ…ĸ。HEVC įš„æ•ˆčƒŊ類äŧŧīŧŒäŊ†įļ˛é į›¸åŽšæ€§čŧƒåˇŽã€‚H.264 兎備åģŖæŗ›įš„į›¸åŽšæ€§ä¸”čŊ‰įĸŧ速åēĻåŋĢīŧŒäŊ†į”ĸį”Ÿįš„æĒ”æĄˆéĢ”įŠčŧƒå¤§ã€‚AV1 æ˜¯æ•ˆįŽ‡æœ€éĢ˜įš„įˇ¨č§Ŗįĸŧ器īŧŒäŊ†åœ¨čˆŠčŖįŊŽä¸Šįŧē䚏支援。", "trash_enabled_description": "å•Ÿį”¨åžƒåœžæĄļ功čƒŊ", - "trash_number_of_days": "æ—Ĩ數", - "trash_number_of_days_description": "永䚅åˆĒé™¤å‰é …į›Žå°‡äŋį•™åœ¨åžƒåœžæĄļ中數夊", - "trash_settings": "垃圞æĄļ", + "trash_number_of_days": "夊數", + "trash_number_of_days_description": "åĒ’éĢ”åœ¨åžƒåœžæĄļ中äŋį•™įš„夊數īŧŒé€žæœŸåžŒå°‡æ°¸äš…åˆĒ除", + "trash_settings": "垃圞æĄļč¨­åŽš", "trash_settings_description": "įŽĄį†åžƒåœžæĄļč¨­åŽš", + "unlink_all_oauth_accounts": "č§Ŗé™¤æ‰€æœ‰ OAuth å¸ŗč™Ÿįš„é€Ŗįĩ", + "unlink_all_oauth_accounts_description": "圍過į§ģč‡ŗæ–°įš„æœå‹™æäž›č€…å‰īŧŒčĢ‹ä¸čρåŋ˜č¨˜čĻå…ˆč§Ŗé™¤æ‰€æœ‰čˆ‡ OAuth 叺æˆļįš„é€Ŗįĩã€‚", + "unlink_all_oauth_accounts_prompt": "您是åĻįĸēčĒčĻč§Ŗé™¤æ‰€æœ‰čˆ‡ OAuth 叺æˆļįš„é€Ŗįĩ? æ‰€æœ‰į›¸é—œįš„äŊŋᔍ者čēĢäģŊ會čĸĢ重設īŧŒä¸Ļ且不čƒŊčĸĢ還原。", "user_cleanup_job": "æ¸…į†äŊŋᔍ者", - "user_delete_delay": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žæœƒåœ¨ {delay, plural, other {# 夊}} 垌永䚅åˆĒ除。", + "user_delete_delay": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žæœƒåœ¨ {delay, plural, one {# 夊} other {# 夊}} 垌永䚅åˆĒ除。", "user_delete_delay_settings": "åģļ垌åˆĒ除", - "user_delete_delay_settings_description": "夊數垌將永䚅åˆĒé™¤å¸ŗč™Ÿčˆ‡é …į›Žã€‚åˆĒ除äģģ務會在 00:00 垌æĒĸæŸĨ可äģĨåˆĒé™¤įš„äŊŋį”¨č€…ã€‚čŽŠæ›´č¨­åŽšåžŒæœƒåœ¨ä¸‹æŦĄåŸˇčĄŒæĒĸæŸĨ。", - "user_delete_immediately": "{user} įš„å¸ŗč™Ÿå’Œé …į›Žå°‡ įĢ‹åŗ 永䚅åˆĒ除。", - "user_delete_immediately_checkbox": "將äŊŋį”¨č€…å’Œé …į›ŽįĢ‹åŗåˆĒ除", - "user_details": "ᔍæˆļčŠŗį´°äŋĄæ¯", + "user_delete_delay_settings_description": "č‡Ēį§ģ除垌čĩˇįŽ—įš„å¤Šæ•¸īŧŒé€žæœŸåžŒå°‡æ°¸äš…åˆĒ除äŊŋį”¨č€…å¸ŗč™Ÿčˆ‡åĒ’éĢ”ã€‚äŊŋᔍ者åˆĒ除äŊœæĨ­æœƒåœ¨æ¯æ—Ĩåˆå¤œåŸˇčĄŒīŧŒäģĨæĒĸæŸĨįŦĻ合åˆĒ除æĸäģļįš„å¸ŗč™Ÿã€‚æ­¤č¨­åŽšįš„čŽŠæ›´æœƒåœ¨ä¸‹ä¸€æŦĄåŸˇčĄŒæ™‚į”Ÿæ•ˆã€‚", + "user_delete_immediately": "{user} įš„å¸ŗč™Ÿčˆ‡åĒ’é̔將įĢ‹åŗæŽ’å…Ĩ永䚅åˆĒé™¤įš„äŊ‡åˆ—。", + "user_delete_immediately_checkbox": "įĢ‹åŗå°‡äŊŋį”¨č€…čˆ‡čŗ‡į”ĸ排å…Ĩ永䚅åˆĒ除äŊ‡åˆ—", + "user_details": "äŊŋį”¨č€…čŠŗį´°čŗ‡č¨Š", "user_management": "äŊŋį”¨č€…įŽĄį†", "user_password_has_been_reset": "äŊŋᔍ者坆įĸŧåˇ˛é‡č¨­īŧš", - "user_password_reset_description": "čĢ‹æäž›äŊŋį”¨č€…č‡¨æ™‚å¯†įĸŧīŧŒä¸Ļ告įŸĨ下æŦĄį™ģå…Ĩ時需čĻæ›´æ”šå¯†įĸŧ。", + "user_password_reset_description": "čĢ‹å°‡č‡¨æ™‚å¯†įĸŧ提䞛įĩĻ芲äŊŋᔍ者īŧŒä¸Ļ告įŸĨäģ–們在下æŦĄį™ģå…Ĩ時需čĻčŽŠæ›´å¯†įĸŧ。", "user_restore_description": "{user} įš„å¸ŗč™Ÿå°‡čĸĢ還原。", "user_restore_scheduled_removal": "還原äŊŋᔍ者 - 預厚æ–ŧ {date, date, long} į§ģ除", - "user_settings": "äŊŋᔍ者", + "user_settings": "äŊŋį”¨č€…č¨­åŽš", "user_settings_description": "įŽĄį†äŊŋį”¨č€…č¨­åŽš", - "user_successfully_removed": "åˇ˛æˆåŠŸį§ģ除 {email}īŧˆäŊŋᔍ者īŧ‰ã€‚", + "user_successfully_removed": "äŊŋᔍ者 {email} åˇ˛æˆåŠŸį§ģ除。", "version_check_enabled_description": "å•Ÿį”¨į‰ˆæœŦæĒĸæŸĨ", "version_check_implications": "į‰ˆæœŦæĒĸæŸĨ功čƒŊæœƒåŽšæœŸčˆ‡ github.com 通訊", "version_check_settings": "į‰ˆæœŦæĒĸæŸĨ", "version_check_settings_description": "å•Ÿį”¨ / åœį”¨æ–°į‰ˆæœŦ通įŸĨ", - "video_conversion_job": "čŊ‰įĸŧåŊąį‰‡", - "video_conversion_job_description": "對åŊąį‰‡čŊ‰įĸŧīŧŒį›¸åŽšæ›´å¤šį€čĻŊå™¨å’ŒčŖįŊŽ" + "video_conversion_job": "åŊąį‰‡čŊ‰įĸŧ", + "video_conversion_job_description": "čŊ‰įĸŧåŊąį‰‡äģĨæå‡čˆ‡į€čĻŊå™¨åŠčŖįŊŽįš„į›¸åŽšæ€§" }, - "admin_email": "įŽĄį†č€…é›ģ子éƒĩäģļ", - "admin_password": "įŽĄį†č€…å¯†įĸŧ", + "admin_email": "įŽĄį†å“Ąé›ģ子éƒĩäģļ", + "admin_password": "įŽĄį†å“Ąå¯†įĸŧ", "administration": "įŽĄį†", "advanced": "進階", - "advanced_settings_enable_alternate_media_filter_subtitle": "äŊŋį”¨æ­¤é¸é …å¯åœ¨åŒæ­Ĩæ™‚äžį…§æ›ŋäģŖæĸäģļį¯Šé¸åĒ’éĢ”ã€‚åƒ…į•ᅦ‰į”¨į¨‹åŧåœ¨åĩæ¸Ŧæ‰€æœ‰į›¸į°ŋ時å‡ēįžå•éĄŒæ™‚æ‰åģēč­°äŊŋį”¨ã€‚", - "advanced_settings_enable_alternate_media_filter_title": "[å¯Ļ驗]äŊŋᔍå…ļäģ–įš„čŖįŊŽį›¸į°ŋ同æ­Ĩį¯Šé¸å™¨", + "advanced_settings_enable_alternate_media_filter_subtitle": "äŊŋį”¨æ­¤é¸é …å¯åœ¨åŒæ­Ĩ時䞝å…ļäģ–æĸäģļį¯Šé¸åĒ’éĢ”ã€‚åƒ…åœ¨æ‡‰į”¨į¨‹åŧį„Ąæŗ•åĩæ¸Ŧåˆ°æ‰€æœ‰į›¸į°ŋ時再嘗čŠĻäŊŋį”¨ã€‚", + "advanced_settings_enable_alternate_media_filter_title": "[å¯Ļ銗性] äŊŋᔍæ›ŋäģŖįš„čŖįŊŽį›¸į°ŋ同æ­Ĩį¯Šé¸å™¨", "advanced_settings_log_level_title": "æ—ĨčĒŒį­‰į´šīŧš{level}", - "advanced_settings_prefer_remote_subtitle": "į‰šåŽščŖįŊŽčŧ‰å…Ĩį¸Žåœ–įš„é€ŸåēĻéžå¸¸įˇŠæ…ĸ。開啟čŧ‰å…Ĩ遠įĢ¯é …į›Žįš„åŠŸčƒŊ。", - "advanced_settings_prefer_remote_title": "å„Ēå…ˆé™čˇé …į›Ž", - "advanced_settings_proxy_headers_subtitle": "åŽšįžŠäģŖį†æ¨™é ­īŧŒåĨ—ᔍæ–ŧImmichįš„æ¯æŦĄįļ˛įĩĄčĢ‹æą‚", + "advanced_settings_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_readonly_mode_subtitle": "é–‹å•Ÿå”¯čŽ€æ¨ĄåŧåžŒīŧŒį…§į‰‡åĒčƒŊį€čĻŊīŧŒåƒæ˜¯å¤šé¸åŊąåƒã€åˆ†äēĢ、投攞、åˆĒé™¤į­‰åŠŸčƒŊéƒŊ會關閉。可在ä¸ģį•Ģéĸ透過äŊŋį”¨č€…é ­åƒäž†é–‹å•Ÿ/é—œé–‰å”¯čŽ€æ¨Ąåŧ", + "advanced_settings_readonly_mode_title": "å”¯čŽ€æ¨Ąåŧ", + "advanced_settings_self_signed_ssl_subtitle": "į•Ĩ過äŧ翜å™¨į̝éģžįš„ SSL æ†‘č­‰éŠ—č­‰ã€‚č‡Ēį°Ŋæ†‘č­‰æ™‚åŋ…é ˆå•Ÿį”¨æ­¤č¨­åŽšã€‚", + "advanced_settings_self_signed_ssl_title": "å…č¨ąč‡Ēį°Ŋįš„ SSL æ†‘č­‰", + "advanced_settings_sync_remote_deletions_subtitle": "į•ļ在įļ˛é įĢ¯åŸˇčĄŒåˆĒ除或還原操äŊœæ™‚īŧŒč‡Ēå‹•åœ¨æ­¤čŖįŊŽä¸ŠåˆĒé™¤æˆ–é‚„åŽŸčŠ˛åĒ’éĢ”", "advanced_settings_sync_remote_deletions_title": "同æ­Ĩ遠į̝åˆĒ除 [å¯Ļ銗性]", "advanced_settings_tile_subtitle": "é€˛éšŽį”¨æˆļč¨­åŽš", - "advanced_settings_troubleshooting_subtitle": "啓ᔍᔍæ–ŧæ•…éšœæŽ’é™¤įš„éĄå¤–åŠŸčƒŊ", - "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, other {# 個月大}}", - "age_year_months": "1 æ­˛īŧŒ{months, plural, other {# 個月}}", + "advanced_settings_troubleshooting_subtitle": "å•Ÿį”¨éĄå¤–åŠŸčƒŊäģĨé€˛čĄŒį–‘é›ŖæŽ’č§Ŗ", + "advanced_settings_troubleshooting_title": "į–‘é›ŖæŽ’č§Ŗ", + "age_months": "{months, plural, one {# 個月} other {# 個月}}", + "age_year_months": "1 æ­˛īŧŒ{months, plural, one {# 個月} other {# 個月}}", "age_years": "{years, plural, other {# æ­˛}}", - "album_added": "加å…Ĩᛏį°ŋ時", + "album_added": "čĸĢ加å…Ĩåˆ°į›¸į°ŋ", "album_added_notification_setting_description": "į•ļ我čĸĢ加å…Ĩå…ąäēĢᛏį°ŋ時īŧŒį”¨é›ģ子éƒĩäģļ通įŸĨ我", "album_cover_updated": "åˇ˛æ›´æ–°į›¸į°ŋ封éĸ", - "album_delete_confirmation": "įĸē厚čρåˆĒ除「{album}」īŧˆį›¸į°ŋīŧ‰å—ŽīŧŸ", - "album_delete_confirmation_description": "åĻ‚æžœåˇ˛åˆ†äēĢæ­¤į›¸į°ŋīŧŒå…ļäģ–äŊŋį”¨č€…å°ąį„Ąæŗ•å†å­˜å–é€™æœŦᛏį°ŋäē†ã€‚", + "album_delete_confirmation": "äŊ įĸē厚čρåˆĒ除ᛏį°ŋ {album} 嗎īŧŸ", + "album_delete_confirmation_description": "åĻ‚æžœæ­¤į›¸į°ŋ厞čĸĢ分äēĢīŧŒå…ļäģ–äŊŋį”¨č€…å°‡į„Ąæŗ•å†å­˜å–ã€‚", + "album_deleted": "ᛏį°ŋ厞åˆĒ除", "album_info_card_backup_album_excluded": "åˇ˛æŽ’é™¤", "album_info_card_backup_album_included": "厞遏䏭", "album_info_updated": "åˇ˛æ›´æ–°į›¸į°ŋčŗ‡č¨Š", @@ -388,50 +429,55 @@ "album_options": "ᛏį°ŋ選項", "album_remove_user": "į§ģ除äŊŋᔍ者īŧŸ", "album_remove_user_confirmation": "įĸē厚čρį§ģ除 {user} 嗎īŧŸ", + "album_search_not_found": "扞不到įŦĻ合搜尋æĸäģļįš„į›¸į°ŋ", "album_share_no_users": "įœ‹äž†æ‚¨čˆ‡æ‰€æœ‰äŊŋį”¨č€…å…ąäēĢäē†é€™æœŦᛏį°ŋīŧŒæˆ–æ˛’æœ‰å…ļäģ–äŊŋį”¨č€…å¯äž›åˆ†äēĢ。", + "album_summary": "į›¸å†Œæ‘˜čρ", "album_updated": "æ›´æ–°į›¸į°ŋ時", "album_updated_setting_description": "į•ļå…ąäēĢᛏį°ŋæœ‰æ–°é …į›Žæ™‚į”¨é›ģ子éƒĩäģļ通įŸĨ我", "album_user_left": "é›ĸ開 {album}", "album_user_removed": "į§ģ除 {user}", - "album_viewer_appbar_delete_confirm": "įĸē厚čĻåžžå¸ŗč™Ÿä¸­åˆĒé™¤æ­¤į›¸į°ŋ嗎īŧŸ", - "album_viewer_appbar_share_err_delete": "į„Ąæŗ•åˆĒ除ᛏį°ŋ", - "album_viewer_appbar_share_err_leave": "į„Ąæŗ•é›ĸ開ᛏį°ŋ", + "album_viewer_appbar_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_title": "ᎍčŧ¯į›¸į°ŋæ¨™éĄŒå¤ąæ•—", "album_viewer_appbar_share_leave": "é›ĸ開ᛏį°ŋ", "album_viewer_appbar_share_to": "分äēĢįĩĻ", "album_viewer_page_share_add_users": "邀čĢ‹å…ļäģ–äēē", - "album_with_link_access": "įŸĨ道逪įĩįš„äŊŋᔍ者éƒŊ可äģĨæŸĨįœ‹é€™æœŦᛏį°ŋä¸­įš„į›¸į‰‡å’ŒäŊŋį”¨č€…ã€‚", + "album_with_link_access": "äģģäŊ•æ“æœ‰é€Ŗįĩįš„äēēéƒŊčƒŊæŸĨįœ‹æ­¤į›¸į°ŋä¸­įš„į…§į‰‡čˆ‡äŊŋį”¨č€…ã€‚", "albums": "ᛏį°ŋ", - "albums_count": "{count, plural, one {{count, number} æœŦᛏį°ŋ} other {{count, number} æœŦᛏį°ŋ}}", + "albums_count": "{count, plural, one {{count, number} 個ᛏį°ŋ} other {{count, number} 個ᛏį°ŋ}}", "albums_default_sort_order": "預荭ᛏį°ŋ排åē", - "albums_default_sort_order_description": "åģēįĢ‹æ–°į›¸į°ŋ時čĻåˆå§‹åŒ–é …į›ŽæŽ’åēã€‚", + "albums_default_sort_order_description": "åģēįĢ‹æ–°į›¸į°ŋ時čĻåˆå§‹åŒ–é …į›ŽæŽ’åēæ–šåŧã€‚", "albums_feature_description": "一įŗģ列可äģĨ分äēĢįĩĻå…ļäģ–ᔍæˆļįš„é …į›Žã€‚", + "albums_on_device_count": "æ­¤čŖįŊŽæœ‰ ({count}) 個ᛏį°ŋ", "all": "全部", "all_albums": "æ‰€æœ‰į›¸į°ŋ", "all_people": "所有äēēį‰Š", "all_videos": "所有åŊąį‰‡", "allow_dark_mode": "å…č¨ąæˇąč‰˛æ¨Ąåŧ", "allow_edits": "å…č¨ąįˇ¨čŧ¯", - "allow_public_user_to_download": "開攞äŊŋᔍ者䏋čŧ‰", - "allow_public_user_to_upload": "開攞äŊŋį”¨č€…ä¸Šå‚ŗ", + "allow_public_user_to_download": "å…č¨ąå…Ŧ開äŊŋᔍ者䏋čŧ‰", + "allow_public_user_to_upload": "å…č¨ąå…Ŧ開äŊŋį”¨č€…ä¸Šå‚ŗ", "alt_text_qr_code": "QR code åœ–į‰‡", "anti_clockwise": "逆時針", "api_key": "API 金鑰", "api_key_description": "æ­¤é‡‘é‘°åƒ…éĄ¯į¤ē一æŦĄã€‚čĢ‹åœ¨é—œé–‰å‰č¤‡čŖŊ厃。", "api_key_empty": "æ‚¨įš„ API é‡‘é‘°åį¨ąä¸čƒŊį‚ēįŠēå€ŧ", "api_keys": "API 金鑰", - "app_bar_signout_dialog_content": "您įĸē厚čρį™ģå‡ēīŧŸ", + "app_bar_signout_dialog_content": "您įĸē厚čρį™ģå‡ē嗎īŧŸ", "app_bar_signout_dialog_ok": "是", "app_bar_signout_dialog_title": "į™ģå‡ē", "app_settings": "æ‡‰į”¨į¨‹åŧč¨­åޚ", - "appears_in": "地éģž", + "appears_in": "å‡ēįžæ–ŧ", + "apply_count": "æ‡‰į”¨({count, number})", "archive": "封存", + "archive_action_prompt": "厞將 ({count}) 個加å…Ĩé€˛å°å­˜", "archive_or_unarchive_photo": "封存或取æļˆå°å­˜į…§į‰‡", - "archive_page_no_archived_assets": "æœĒæ‰žåˆ°å°å­˜é …į›Ž", + "archive_page_no_archived_assets": "æœĒ扞到封存åĒ’éĢ”", "archive_page_title": "封存 ({count})", - "archive_size": "封存æĒ”æĄˆå¤§å°", - "archive_size_description": "č¨­åŽščρ䏋čŧ‰įš„封存æĒ”æĄˆå¤§å° (å–ŽäŊīŧš GB)", + "archive_size": "封存大小", + "archive_size_description": "č¨­åŽščρ䏋čŧ‰įš„封存æĒ”æĄˆå¤§å° (å–ŽäŊīŧš GiB)", "archived": "厞封存", "archived_count": "{count, plural, other {厞封存 # å€‹é …į›Ž}}", "are_these_the_same_person": "同一äŊäēēį‰ŠīŧŸ", @@ -440,143 +486,157 @@ "asset_action_share_err_offline": "į•ĨéŽį„Ąæŗ•å–åž—įš„é›ĸįˇšé …į›Ž", "asset_added_to_album": "厞åģēį̋ᛏį°ŋ", "asset_adding_to_album": "新åĸžåˆ°į›¸į°ŋâ€Ļ", - "asset_description_updated": "é …į›ŽčĒĒæ˜Žåˇ˛æ›´æ–°", - "asset_filename_is_offline": "é …į›Ž {filename} 厞é›ĸ᎚", - "asset_has_unassigned_faces": "é …į›Žæœ‰æœĒ新åĸžč‡‰å­”", - "asset_hashing": "č¨ˆįŽ—é›œæšŠå€ŧâ€Ļ", + "asset_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_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_skipped": "čˇŗéŽ", - "asset_skipped_in_trash": "į§ģč‡ŗåžƒåœžæĄļ", + "asset_list_layout_sub_title": "å¸ƒåą€", + "asset_list_settings_subtitle": "ᛏቇæ ŧį‹€å¸ƒåą€č¨­åŽš", + "asset_list_settings_title": "ᛏቇæ ŧį‹€æĒĸčĻ–", + "asset_offline": "åĒ’éĢ”é›ĸ᎚", + "asset_offline_description": "此外部åĒ’éĢ”åˇ˛į„Ąæŗ•åœ¨įŖįĸŸä¸­æ‰žåˆ°ã€‚č̋聝įĩĄæ‚¨įš„ Immich įŽĄį†å“ĄäģĨ取垗協劊。", + "asset_restored_successfully": "åĒ’éĢ”åžŠåŽŸæˆåŠŸ", + "asset_skipped": "åˇ˛čˇŗéŽ", + "asset_skipped_in_trash": "åˇ˛åœ¨åžƒåœžæĄļ", + "asset_trashed": "čŗ‡į”ĸčĸĢä¸ŸæŖ„", + "asset_troubleshoot": "čŗ‡į”ĸ故障排除", "asset_uploaded": "åˇ˛ä¸Šå‚ŗ", "asset_uploading": "ä¸Šå‚ŗä¸­â€Ļ", - "asset_viewer_settings_subtitle": "įŽĄį†į›¸į°ŋį€čĻŊč¨­åŽš", - "asset_viewer_settings_title": "é …į›Žį€čĻŊ", - "assets": "é …į›Ž", - "assets_added_count": "åˇ˛æ–°åĸž {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", - "assets_added_to_album_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}加å…Ĩᛏį°ŋ", - "assets_added_to_name_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}加å…Ĩ{hasName, select, true {{name}} other {æ–°į›¸į°ŋ}}", - "assets_cannot_be_added_to_album_count": "{count. plural, one {個} other {個}} é …į›ŽæœĒčƒŊčĸĢæˇģåŠ č‡ŗį›¸į°ŋ", - "assets_count": "{count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", - "assets_deleted_permanently": "{count} å€‹é …į›Žåˇ˛čĸĢæ°¸äš…åˆĒ除", - "assets_deleted_permanently_from_server": "åˇ˛åžžäŧ翜å™¨ä¸­æ°¸äš…į§ģ除 {count} å€‹é …į›Ž", - "assets_moved_to_trash_count": "厞將 {count, plural, other {# å€‹é …į›Ž}}ä¸Ÿé€˛åžƒåœžæĄļ", - "assets_permanently_deleted_count": "永䚅åˆĒ除 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", - "assets_removed_count": "į§ģ除 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", - "assets_removed_permanently_from_device": "åžžčŖįŊŽä¸­æ°¸äš…į§ģ除 {count} å€‹é …į›Ž", - "assets_restore_confirmation": "įĸē厚čĻé‚„åŽŸæ‰€æœ‰æ¨æŖ„é …į›Žå—ŽīŧŸæ­¤æ­ĨéŠŸį„Ąæŗ•é‚„åŽŸīŧ(*č¨ģīŧšé€™į„Ąæŗ•還原äģģäŊ•é›ĸįˇšé …į›Ž)", - "assets_restored_count": "åˇ˛é‚„åŽŸ {count, plural, other {# å€‹é …į›Ž}}", - "assets_restored_successfully": "成功垊原 {count} å€‹é …į›Ž", - "assets_trashed": "æ¨æŖ„ {count} å€‹é …į›Ž", - "assets_trashed_count": "æ¨æŖ„ {count, plural, other {# å€‹é …į›Ž}}", - "assets_trashed_from_server": "{count} å€‹é …į›Žį§ģč‡ŗåžƒåœžæĄļ", - "assets_were_part_of_album_count": "{count, plural, one {é …į›Žåˇ˛} other {é …į›Žåˇ˛}} åˇ˛åœ¨į›¸į°ŋ", - "authorized_devices": "授æŦŠčŖįŊŽ", - "automatic_endpoint_switching_subtitle": "å„Ē先äŊŋᔍ Wi-Fi é€ŖįˇšīŧŒå…ļäģ–į‹€æŗäŊŋᔍå…ļäģ–é€Ŗįˇšæ–šåŧ", - "automatic_endpoint_switching_title": "č‡Ēå‹•åˆ‡æ›é€Ŗįĩ", + "asset_viewer_settings_subtitle": "įŽĄį†æ‚¨įš„åĒ’éĢ”åēĢæĒĸčĻ–å™¨č¨­åŽš", + "asset_viewer_settings_title": "åĒ’éĢ”æĒĸčϖ噍", + "assets": "åĒ’éĢ”", + "assets_added_count": "åˇ˛æ–°åĸž {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}", + "assets_added_to_album_count": "厞將 {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}加å…Ĩᛏį°ŋ", + "assets_added_to_albums_count": "åˇ˛æ–°åĸž {assetTotal, plural, one {# 個} other {# 個}}é …į›Žåˆ° {albumTotal, plural, one {# 個} other {# 個}}ᛏį°ŋ中", + "assets_cannot_be_added_to_album_count": "į„Ąæŗ•å°‡ {count, plural, one {åĒ’éĢ”} other {åĒ’éĢ”}} 加å…Ĩ臺ᛏį°ŋ", + "assets_cannot_be_added_to_albums": "{count, plural, one {個} other {個}}é …į›Žį„Ąæŗ•čĸĢ加å…Ĩᛏį°ŋ", + "assets_count": "{count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}", + "assets_deleted_permanently": "{count} 個åĒ’é̔厞čĸĢæ°¸äš…åˆĒ除", + "assets_deleted_permanently_from_server": "åˇ˛åžž Immich äŧ翜å™¨ä¸­æ°¸äš…į§ģ除 {count} 個åĒ’éĢ”", + "assets_downloaded_failed": "{count, plural, one {厞䏋čŧ‰ # 個æĒ”æĄˆ - {error} 個æĒ”æĄˆå¤ąæ•—} other {厞䏋čŧ‰ # 個æĒ”æĄˆ - {error} 個æĒ”æĄˆå¤ąæ•—}}", + "assets_downloaded_successfully": "{count, plural, one {åˇ˛æˆåŠŸä¸‹čŧ‰ # 個æĒ”æĄˆ} other {åˇ˛æˆåŠŸä¸‹čŧ‰ # 個æĒ”æĄˆ}}", + "assets_moved_to_trash_count": "厞將 {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}į§ģå‹•é€˛åžƒåœžæĄļ", + "assets_permanently_deleted_count": "åˇ˛æ°¸äš…åˆĒ除 {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}", + "assets_removed_count": "厞į§ģ除 {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}", + "assets_removed_permanently_from_device": "åˇ˛åžžæ‚¨įš„čŖįŊŽæ°¸äš…į§ģ除 {count} 個åĒ’éĢ”", + "assets_restore_confirmation": "您įĸē厚čĻé‚„åŽŸæ‰€æœ‰åžƒåœžæĄļä¸­įš„åĒ’éĢ”å—ŽīŧŸæ­¤æ“äŊœį„Ąæŗ•垊原īŧčĢ‹æŗ¨æ„īŧŒäģģäŊ•é›ĸ᎚åĒ’éĢ”éƒŊį„Ąæŗ•é€éŽæ­¤æ–šåŧé‚„原。", + "assets_restored_count": "åˇ˛é‚„åŽŸ {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}}", + "assets_restored_successfully": "åˇ˛æˆåŠŸé‚„åŽŸ {count} 個åĒ’éĢ”", + "assets_trashed": "厞將 {count} 個åĒ’éĢ”į§ģč‡ŗåžƒåœžæĄļ", + "assets_trashed_count": "厞將 {count, plural, one {# 個åĒ’éĢ”} other {# 個åĒ’éĢ”}} į§ģč‡ŗåžƒåœžæĄļ", + "assets_trashed_from_server": "åˇ˛åžž Immich äŧ翜å™¨å°‡ {count} 個åĒ’éĢ”į§ģč‡ŗåžƒåœžæĄļ", + "assets_were_part_of_album_count": "{count, plural, one {芲åĒ’é̔厞} other {這äē›åĒ’é̔厞}}åœ¨į›¸į°ŋ中", + "assets_were_part_of_albums_count": "{count, plural, one {個} other {個}}é …į›Žåˇ˛čĸĢå„˛å­˜åœ¨į›¸į°ŋ中", + "authorized_devices": "åˇ˛æŽˆæŦŠčŖįŊŽ", + "automatic_endpoint_switching_subtitle": "į•ļå¯į”¨æ™‚īŧŒé€éŽæŒ‡åŽšįš„ Wi-Fi 在æœŦåœ°é€ŖįˇšīŧŒå…ļäģ–æƒ…æŗå‰‡äŊŋᔍæ›ŋäģŖé€Ŗįˇš", + "automatic_endpoint_switching_title": "č‡Ē動 URL 切換", + "autoplay_slideshow": "č‡Ē動播攞åšģį‡ˆį‰‡", "back": "čŋ”回", "back_close_deselect": "čŋ”回、關閉及取æļˆé¸å–", + "background_backup_running_error": "垌č‡ē備äģŊį•ļå‰æ­Ŗåœ¨é‹čĄŒīŧŒį„Ąæŗ•啟動手動備äģŊ", "background_location_permission": "čƒŒæ™¯å­˜å–äŊįŊŽæŦŠé™", - "background_location_permission_content": "é–‹å•ŸčƒŒæ™¯åŸˇčĄŒæ™‚č‡Ē動切換įļ˛čˇ¯īŧŒčĢ‹å……č¨ą Immich ä¸€åž‹å……č¨ąäŊŋį”¨į˛žįĸēäŊįŊŽæŦŠé™īŧŒäģĨįĸēčĒ Wi-Fi įļ˛čˇ¯åį¨ą", + "background_location_permission_content": "į‚ēäē†åœ¨čƒŒæ™¯åŸˇčĄŒæ™‚切換įļ˛čˇ¯īŧŒImmich åŋ…須始įĩ‚å…ˇæœ‰į˛žįĸēäŊįŊŽå­˜å–æŦŠé™īŧŒæ‰čƒŊčŽ€å– Wi-Fi įļ˛čˇ¯åį¨ą", + "background_options": "čƒŒæ™¯é¸é …", + "backup": "備äģŊ", "backup_album_selection_page_albums_device": "čŖįŊŽä¸Šįš„ᛏį°ŋīŧˆ{count}īŧ‰", - "backup_album_selection_page_albums_tap": "éģžæ“Šé¸å–īŧŒé€ŖįēŒéģžæ“Šå…ŠæŦĄå–æļˆ", - "backup_album_selection_page_assets_scatter": "é …į›Žæœƒåˆ†æ•Ŗåœ¨ä¸åŒį›¸į°ŋ。因此īŧŒå¯äģĨč¨­åŽščρ備äģŊįš„į›¸į°ŋ。", - "backup_album_selection_page_select_albums": "é¸æ“‡į›¸į°ŋ", - "backup_album_selection_page_selection_info": "é¸æ“‡čŗ‡č¨Š", - "backup_album_selection_page_total_assets": "į¸Ŋč¨ˆé …į›Ž", + "backup_album_selection_page_albums_tap": "éģžä¸€ä¸‹äģĨ選取īŧŒéģžå…Šä¸‹äģĨ排除", + "backup_album_selection_page_assets_scatter": "åĒ’éĢ”å¯äģĨåˆ†æ•Ŗåœ¨å¤šå€‹į›¸į°ŋ中īŧŒå› æ­¤åœ¨å‚™äģŊéŽį¨‹ä¸­å¯äģĨé¸æ“‡į´å…Ĩæˆ–æŽ’é™¤į›¸į°ŋ。", + "backup_album_selection_page_select_albums": "é¸å–į›¸į°ŋ", + "backup_album_selection_page_selection_info": "é¸å–čŗ‡č¨Š", + "backup_album_selection_page_total_assets": "į¸Ŋ不重複åĒ’éĢ”æ•¸", + "backup_albums_sync": "備äģŊį›¸å†ŒåŒæ­Ĩ", "backup_all": "全部", - "backup_background_service_backup_failed_message": "備äģŊå¤ąæ•—īŧŒé‡æ–°å‚™äģŊ中â€Ļ", - "backup_background_service_connection_failed_message": "į„Ąæŗ•é€Ŗįˇšäŧ翜å™¨īŧŒé‡æ–°é€Ŗįˇšä¸­â€Ļ", + "backup_background_service_backup_failed_message": "備äģŊåĒ’éĢ”å¤ąæ•—ã€‚æ­Ŗåœ¨é‡čŠĻâ€Ļ", + "backup_background_service_connection_failed_message": "é€Ŗįˇšäŧ翜å™¨å¤ąæ•—ã€‚æ­Ŗåœ¨é‡čŠĻâ€Ļ", "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šå‚ŗ {filename}", - "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": "į„Ąæŗ•ä¸Šå‚ŗ {filename}", + "backup_background_service_in_progress_notification": "æ­Ŗåœ¨å‚™äģŊæ‚¨įš„åĒ’éĢ”â€Ļ", + "backup_background_service_upload_failure_notification": "{filename} ä¸Šå‚ŗå¤ąæ•—", "backup_controller_page_albums": "備äģŊᛏį°ŋ", - "backup_controller_page_background_app_refresh_disabled_content": "é–‹å•Ÿæ‡‰į”¨į¨‹åŧčƒŒæ™¯č‡Ēå‹•é‡æ–°æ•´į†īŧŒčĢ‹åœ¨ã€Œč¨­åŽš>備äģŊ>čƒŒæ™¯é‡æ–°æ•´į†ã€é–‹å•ŸčƒŒæ™¯é‡æ–°æ•´į†ã€‚", - "backup_controller_page_background_app_refresh_disabled_title": "é—œé–‰æ‡‰į”¨į¨‹åŧčƒŒæ™¯é‡æ–°æ•´į†", + "backup_controller_page_background_app_refresh_disabled_content": "čĢ‹åœ¨ã€Œč¨­åŽšã€>「一čˆŦ」>ã€ŒčƒŒæ™¯ App é‡æ–°æ•´į†ã€ä¸­å•Ÿį”¨īŧŒäģĨäŊŋį”¨čƒŒæ™¯å‚™äģŊ功čƒŊ。", + "backup_controller_page_background_app_refresh_disabled_title": "čƒŒæ™¯ App é‡æ–°æ•´į†åˇ˛åœį”¨", "backup_controller_page_background_app_refresh_enable_button_text": "å‰åž€č¨­åŽš", - "backup_controller_page_background_battery_info_link": "怎éēŧ做", - "backup_controller_page_background_battery_info_message": "į‚ēäē†į˛åž—最äŊŗįš„čƒŒæ™¯å‚™äģŊéĢ”éŠ—īŧŒčĢ‹įĻį”¨æœƒäģģäŊ•限åˆļ Immich čƒŒæ™¯æ´ģå‹•įš„é›ģæą å„Ē化。\n\nį”ąæ–ŧé€™æ˜¯čŖįŊŽį›¸é—œįš„īŧŒå› æ­¤čĢ‹æŸĨæ‰žčŖįŊŽčŖŊé€ å•†æäž›įš„čŗ‡č¨Šé€˛čĄŒæ“äŊœã€‚", + "backup_controller_page_background_battery_info_link": "å‘Šč¨´æˆ‘æ€Žéēŧ做", + "backup_controller_page_background_battery_info_message": "į‚ēį˛åž—æœ€äŊŗįš„čƒŒæ™¯å‚™äģŊéĢ”éŠ—īŧŒčĢ‹åœį”¨äģģäŊ•會限åˆļ Immich čƒŒæ™¯æ´ģå‹•įš„é›ģæą æœ€äŊŗåŒ–č¨­åŽšã€‚\n\nį”ąæ–ŧé€™čˆ‡čŖįŊŽåž‹č™Ÿį›¸é—œīŧŒčĢ‹æŸĨčŠĸæ‚¨čŖįŊŽčŖŊé€ å•†įš„į›¸é—œčĒĒæ˜Žã€‚", "backup_controller_page_background_battery_info_ok": "我įŸĨ道äē†", "backup_controller_page_background_battery_info_title": "é›ģæą æœ€äŊŗåŒ–", "backup_controller_page_background_charging": "僅在充é›ģ時", - "backup_controller_page_background_configure_error": "č¨­åŽščƒŒæ™¯å¤ąæ•—", - "backup_controller_page_background_delay": "åģļ遲 {duration} 垌備äģŊ", - "backup_controller_page_background_description": "æ‰“é–‹čƒŒæ™¯æœå‹™äģĨč‡Ē動備äģŊäģģäŊ•æ–°é …į›ŽīŧŒä¸”į„Ąéœ€æ‰“é–‹åĨ—ᔍ", + "backup_controller_page_background_configure_error": "čƒŒæ™¯æœå‹™č¨­åŽšå¤ąæ•—", + "backup_controller_page_background_delay": "新åĒ’é̔備äģŊåģļ遲īŧš{duration}", + "backup_controller_page_background_description": "é–‹å•ŸčƒŒæ™¯æœå‹™īŧŒåŗå¯åœ¨ä¸éœ€æ‰“é–‹ App įš„æƒ…æŗä¸‹īŧŒč‡Ē動備äģŊ所有新åĒ’éĢ”", "backup_controller_page_background_is_off": "čƒŒæ™¯č‡Ē動備äģŊåˇ˛é—œé–‰", - "backup_controller_page_background_is_on": "čƒŒæ™¯č‡Ē動備äģŊ厞開啓", + "backup_controller_page_background_is_on": "čƒŒæ™¯č‡Ē動備äģŊåˇ˛é–‹å•Ÿ", "backup_controller_page_background_turn_off": "é—œé–‰čƒŒæ™¯æœå‹™", - "backup_controller_page_background_turn_on": "é–‹å•“čƒŒæ™¯æœå‹™", + "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_sub": "厞備äģŊįš„į…§į‰‡å’ŒįŸ­į‰‡", - "backup_controller_page_created": "新åĸžæ™‚é–“: {date}", - "backup_controller_page_desc_backup": "打開前台備äģŊīŧŒäģĨæœŦፋåŧé‹čĄŒæ™‚č‡Ē動備äģŊæ–°é …į›Žã€‚", - "backup_controller_page_excluded": "åˇ˛æŽ’é™¤: ", + "backup_controller_page_backup_selected": "厞遏䏭īŧš ", + "backup_controller_page_backup_sub": "厞備äģŊįš„į…§į‰‡å’ŒåŊąį‰‡", + "backup_controller_page_created": "åģēįĢ‹æ™‚é–“īŧš{date}", + "backup_controller_page_desc_backup": "開啟前台備äģŊīŧŒåœ¨æ‰“é–‹ App 時č‡Ē動將新åĒ’éĢ”ä¸Šå‚ŗč‡ŗäŧ翜å™¨ã€‚", + "backup_controller_page_excluded": "åˇ˛æŽ’é™¤īŧš ", "backup_controller_page_failed": "å¤ąæ•—īŧˆ{count}īŧ‰", - "backup_controller_page_filename": "文äģļåį¨ą: {filename} [{size}]", + "backup_controller_page_filename": "æĒ”æĄˆåį¨ąīŧš{filename} [{size}]", "backup_controller_page_id": "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_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_on": "前台č‡Ē動備äģŊåˇ˛é–‹å•Ÿ", "backup_controller_page_storage_format": "{used} / {total} 厞äŊŋᔍ", "backup_controller_page_to_backup": "čρ備äģŊįš„į›¸į°ŋ", - "backup_controller_page_total_sub": "遏䏭ᛏį°ŋä¸­æ‰€æœ‰ä¸é‡č¤‡įš„įŸ­į‰‡å’Œåœ–į‰‡", + "backup_controller_page_total_sub": "åˇ˛é¸å–į›¸į°ŋä¸­įš„æ‰€æœ‰ä¸é‡č¤‡įš„į…§į‰‡čˆ‡åŊąį‰‡", "backup_controller_page_turn_off": "關閉前台備äģŊ", - "backup_controller_page_turn_on": "開啓前台備äģŊ", - "backup_controller_page_uploading_file_info": "æ­Ŗåœ¨ä¸Šå‚ŗä¸­įš„æ–‡äģļčŗ‡č¨Š", + "backup_controller_page_turn_on": "開啟前台備äģŊ", + "backup_controller_page_uploading_file_info": "ä¸Šå‚ŗä¸­įš„æĒ”æĄˆčŗ‡č¨Š", "backup_err_only_album": "不čƒŊį§ģé™¤å”¯ä¸€įš„į›¸į°ŋ", - "backup_info_card_assets": "項", + "backup_error_sync_failed": "同æ­Ĩå¤ąæ•—ã€‚ į„Ąæŗ•č™•į†å‚™äģŊ。", + "backup_info_card_assets": "個åĒ’éĢ”", "backup_manual_cancelled": "åˇ˛å–æļˆ", "backup_manual_in_progress": "ä¸Šå‚ŗæ­Ŗåœ¨é€˛čĄŒä¸­īŧŒčĢ‹į¨åžŒå†čŠĻ", "backup_manual_success": "成功", "backup_manual_title": "ä¸Šå‚ŗį‹€æ…‹", + "backup_options": "備äģŊ選項", "backup_options_page_title": "備äģŊ選項", - "backup_setting_subtitle": "įŽĄį†čƒŒæ™¯čˆ‡å‰æ™¯ä¸Šå‚ŗč¨­åŽš", - "backward": "倒čŊ‰", + "backup_setting_subtitle": "įŽĄį†čƒŒæ™¯čˆ‡å‰å°ä¸Šå‚ŗč¨­åŽš", + "backup_settings_subtitle": "įŽĄį†ä¸Šå‚ŗč¨­åŽš", + "backward": "į”ąčˆŠč‡ŗæ–°", "biometric_auth_enabled": "į”Ÿį‰Ščž¨č­˜éŠ—č­‰åˇ˛å•Ÿį”¨", "biometric_locked_out": "æ‚¨åˇ˛čĸĢéŽ–åŽšį„Ąæŗ•äŊŋį”¨į”Ÿį‰Ščž¨č­˜éŠ—č­‰", - "biometric_no_options": "į„Ąį”Ÿį‰Ščž¨č­˜é¸é …å¯į”¨", + "biometric_no_options": "æ˛’æœ‰į”Ÿį‰Ščž¨č­˜é¸é …å¯į”¨", "biometric_not_available": "æ­¤č¨­å‚™ä¸Šį„Ąæŗ•äŊŋį”¨į”Ÿį‰Ščž¨č­˜éŠ—č­‰", "birthdate_saved": "å‡ēį”Ÿæ—ĨæœŸå„˛å­˜æˆåŠŸ", - "birthdate_set_description": "å‡ēį”Ÿæ—ĨæœŸæœƒį”¨äž†č¨ˆįŽ—æ­¤äēēæ‹į…§æ™‚įš„æ­˛æ•¸ã€‚", - "blurred_background": "æ¨ĄįŗŠčƒŒæ™¯", + "birthdate_set_description": "å‡ēį”Ÿæ—ĨæœŸį”¨æ–ŧč¨ˆįŽ—æ­¤äēēåœ¨į…§į‰‡æ‹æ”æ™‚įš„åš´éŊĄã€‚", + "blurred_background": "čƒŒæ™¯æ¨ĄįŗŠ", "bugs_and_feature_requests": "錯čĒ¤åŠåŠŸčƒŊčĢ‹æą‚", "build": "åģēįŊŽįˇ¨č™Ÿ", "build_image": "åģēįŊŽæ˜ åƒ", - "bulk_delete_duplicates_confirmation": "您įĸē厚čĻæ‰šé‡åˆĒ除 {count, plural, one {# å€‹é‡č¤‡æĒ”æĄˆ} other {# å€‹é‡č¤‡æĒ”æĄˆ}} 嗎īŧŸé€™å°‡äŋį•™æ¯įĩ„ä¸­įš„æœ€å¤§æĒ”æĄˆīŧŒä¸Ļ永䚅åˆĒ除所有å…ļäģ–é‡č¤‡é …ã€‚æ­¤æ“äŊœį„Ąæŗ•æ’¤éŠˇīŧ", - "bulk_keep_duplicates_confirmation": "您įĸē厚čρäŋį•™ {count, plural, one {# å€‹é‡č¤‡æĒ”æĄˆ} other {# å€‹é‡č¤‡æĒ”æĄˆ}} 嗎īŧŸé€™å°‡č§Ŗæąēæ‰€æœ‰é‡č¤‡įĩ„č€Œä¸åˆĒ除äģģäŊ•內厚。", - "bulk_trash_duplicates_confirmation": "įĸē厚čρ䏀æŦĄä¸ŸæŽ‰ {count, plural, other {# å€‹é‡č¤‡įš„æĒ”æĄˆ}}嗎īŧŸé€™æ¨Ŗæ¯įĩ„é‡č¤‡įš„æĒ”æĄˆä¸­īŧŒæœ€å¤§įš„æœƒį•™ä¸‹äž†īŧŒå…ļåŽƒįš„æœƒčĸĢä¸Ÿé€˛åžƒåœžæĄļ。", - "buy": "čŗŧįŊŽ Immich", - "cache_settings_clear_cache_button": "æ¸…é™¤įˇŠå­˜", - "cache_settings_clear_cache_button_title": "清除åĨ—į”¨įˇŠå­˜ã€‚åœ¨é‡æ–°į”ŸæˆįˇŠå­˜äš‹å‰īŧŒå°‡éĄ¯č‘—åŊąéŸŋåĨ—į”¨įš„æ€§čƒŊ。", + "bulk_delete_duplicates_confirmation": "您įĸē厚čĻæ‰šé‡åˆĒ除 {count, plural, one {# å€‹é‡č¤‡åĒ’éĢ”} other {# å€‹é‡č¤‡åĒ’éĢ”}} 嗎īŧŸįŗģįĩąå°‡äŋį•™æ¯įĩ„ä¸­å¤§å°æœ€å¤§įš„åĒ’éĢ”īŧŒä¸Ļ永䚅åˆĒ除所有å…ļäģ–é‡č¤‡é …į›Žã€‚æ­¤æ“äŊœį„Ąæŗ•垊原īŧ", + "bulk_keep_duplicates_confirmation": "您įĸē厚čρäŋį•™ {count, plural, one {# å€‹é‡č¤‡åĒ’éĢ”} other {# å€‹é‡č¤‡åĒ’éĢ”}} 嗎īŧŸé€™å°‡åœ¨ä¸åˆĒ除äģģäŊ•é …į›Žįš„æƒ…æŗä¸‹č§Ŗæąēæ‰€æœ‰é‡č¤‡įž¤įĩ„。", + "bulk_trash_duplicates_confirmation": "您įĸē厚čĻæ‰šæŦĄå°‡ {count, plural, one {# å€‹é‡č¤‡åĒ’éĢ”} other {# å€‹é‡č¤‡åĒ’éĢ”}}į§ģč‡ŗåžƒåœžæĄļ嗎īŧŸįŗģįĩąå°‡äŋį•™æ¯įĩ„ä¸­å¤§å°æœ€å¤§įš„åĒ’éĢ”īŧŒä¸Ļ將所有å…ļäģ–é‡č¤‡é …į›Žį§ģč‡ŗåžƒåœžæĄļ。", + "buy": "čŗŧ財 Immich", + "cache_settings_clear_cache_button": "清除åŋĢ取", + "cache_settings_clear_cache_button_title": "清除 App įš„åŋĢ取。此動äŊœæœƒåœ¨åŋĢ取重新åģēįĢ‹å‰īŧŒéĄ¯č‘—åŊąéŸŋ App įš„æ•ˆčƒŊ。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "厞加å…Ĩéģ‘åå–Žįš„į…§į‰‡å’ŒįŸ­į‰‡", + "cache_settings_duplicated_assets_subtitle": "čĸĢæ‡‰į”¨į¨‹åŧåŠ å…ĨåŋŊį•Ĩæ¸…å–Žįš„į…§į‰‡čˆ‡åŊąį‰‡", "cache_settings_duplicated_assets_title": "é‡č¤‡é …į›Žīŧˆ{count}īŧ‰", - "cache_settings_statistics_album": "圖åēĢį¸Žåœ–", + "cache_settings_statistics_album": "åĒ’éĢ”åēĢį¸Žåœ–", "cache_settings_statistics_full": "åŽŒæ•´åœ–į‰‡", "cache_settings_statistics_shared": "å…ąäēĢᛏį°ŋį¸Žåœ–", "cache_settings_statistics_thumbnail": "į¸Žåœ–", - "cache_settings_statistics_title": "įˇŠå­˜äŊŋį”¨æƒ…æŗ", - "cache_settings_subtitle": "控åˆļ Immich app įš„įˇŠå­˜čĄŒį‚ē", - "cache_settings_tile_subtitle": "č¨­åŽšæœŦåœ°å­˜å„˛čĄŒį‚ē", - "cache_settings_tile_title": "æœŦåœ°å­˜å„˛", + "cache_settings_statistics_title": "åŋĢ取äŊŋį”¨æƒ…æŗ", + "cache_settings_subtitle": "控åˆļ Immich čĄŒå‹•æ‡‰į”¨į¨‹åŧįš„åŋĢå–čĄŒį‚ē", + "cache_settings_tile_subtitle": "č¨­åŽšæœŦæŠŸå„˛å­˜čĄŒį‚ē", + "cache_settings_tile_title": "æœŦæŠŸå„˛å­˜įŠē間", "cache_settings_title": "įˇŠå­˜č¨­åŽš", "camera": "į›¸æŠŸ", "camera_brand": "į›¸æŠŸå“į‰Œ", @@ -584,36 +644,41 @@ "cancel": "取æļˆ", "cancel_search": "取æļˆæœå°‹", "canceled": "åˇ˛å–æļˆ", + "canceling": "取æļˆä¸­", "cannot_merge_people": "į„Ąæŗ•åˆäŊĩäēēį‰Š", - "cannot_undo_this_action": "æ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", + "cannot_undo_this_action": "此操äŊœį„Ąæŗ•垊原īŧ", "cannot_update_the_description": "į„Ąæŗ•æ›´æ–°æčŋ°", "cast": "投åŊą", - "change_date": "更攚æ—Ĩ期", - "change_description": "更攚描čŋ°", + "cast_description": "č¨­åŽšå¯į”¨įš„æŠ•æ”žčŖįŊŽ", + "change_date": "čŽŠæ›´æ—Ĩ期", + "change_description": "čŽŠæ›´æčŋ°", "change_display_order": "čŽŠæ›´éĄ¯į¤ē順åē", - "change_expiration_time": "æ›´æ”šå¤ąæ•ˆæœŸé™", - "change_location": "更攚äŊįŊŽ", - "change_name": "攚名", - "change_name_successfully": "åˇ˛æˆåŠŸčŽŠæ›´åå­—", - "change_password": "更攚密įĸŧ", - "change_password_description": "這是您įŦŦ一æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–您čĸĢčĻæą‚æ›´æ”šå¯†įĸŧ。čĢ‹åœ¨ä¸‹éĸčŧ¸å…Ĩ新密įĸŧ。", + "change_expiration_time": "čŽŠæ›´åˆ°æœŸæ™‚é–“", + "change_location": "čŽŠæ›´äŊįŊŽ", + "change_name": "čŽŠæ›´åį¨ą", + "change_name_successfully": "čŽŠæ›´åį¨ąæˆåŠŸ", + "change_password": "čŽŠæ›´å¯†įĸŧ", + "change_password_description": "這是您éĻ–æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–æ˜¯åˇ˛æ”ļåˆ°čŽŠæ›´å¯†įĸŧįš„čĢ‹æą‚ã€‚čĢ‹åœ¨ä¸‹æ–ščŧ¸å…Ĩ新密įĸŧ。", "change_password_form_confirm_password": "įĸēčĒå¯†įĸŧ", - "change_password_form_description": "您åĨŊ {name} īŧš\n\n這是您éĻ–æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–čĸĢįŽĄį†å“ĄčĻæą‚æ›´æ”šå¯†įĸŧ。čĢ‹åœ¨ä¸‹æ–ščŧ¸å…Ĩ新密įĸŧ。", + "change_password_form_description": "您åĨŊ {name}īŧŒ\n\n這是您éĻ–æŦĄį™ģå…ĨįŗģįĩąīŧŒæˆ–æ˜¯åˇ˛æ”ļåˆ°čŽŠæ›´å¯†įĸŧįš„čĢ‹æą‚ã€‚čĢ‹åœ¨ä¸‹æ–ščŧ¸å…Ĩ新密įĸŧ。", "change_password_form_new_password": "新密įĸŧ", "change_password_form_password_mismatch": "密įĸŧ不一致", "change_password_form_reenter_new_password": "再æŦĄčŧ¸å…Ĩ新密įĸŧ", - "change_pin_code": "更攚PINįĸŧ", - "change_your_password": "æ›´æ”šæ‚¨įš„å¯†įĸŧ", - "changed_visibility_successfully": "åˇ˛æˆåŠŸæ›´æ”šå¯čĻ‹æ€§", + "change_pin_code": "čŽŠæ›´ PIN įĸŧ", + "change_your_password": "čŽŠæ›´æ‚¨įš„å¯†įĸŧ", + "changed_visibility_successfully": "åˇ˛æˆåŠŸčŽŠæ›´å¯čĻ‹æ€§", + "charging": "充é›ģ", + "charging_requirement_mobile_backup": "垌č‡ē備äģŊčĻæą‚č¨­å‚™æ­Ŗåœ¨å……é›ģ", "check_corrupt_asset_backup": "æĒĸæŸĨææ¯€įš„å‚™äģŊé …į›Ž", "check_corrupt_asset_backup_button": "åŸˇčĄŒæĒĸæŸĨ", - "check_corrupt_asset_backup_description": "åƒ…åœ¨åˇ˛å‚™äģŊæ‰€æœ‰é …į›Žä¸”é€ŖæŽĨ Wi-Fi æ™‚åŸˇčĄŒæ­¤æĒĸæŸĨã€‚æ­¤į¨‹åēå¯čƒŊ需čĻåšžåˆ†é˜ã€‚", + "check_corrupt_asset_backup_description": "åƒ…åœ¨é€ŖæŽĨ Wi-Fi 且所有åĒ’éĢ”åˇ˛åŽŒæˆå‚™äģŊåžŒåŸˇčĄŒæ­¤æĒĸæŸĨã€‚æ­¤į¨‹åēå¯čƒŊ需čĻæ•¸åˆ†é˜ã€‚", "check_logs": "æĒĸæŸĨæ—Ĩčnj", - "choose_matching_people_to_merge": "選擇čρ合äŊĩįš„åŒšé…äēēį‰Š", + "choose_matching_people_to_merge": "選擇čρ合äŊĩįš„į›¸įŦĻäēēį‰Š", "city": "城市", "clear": "清įŠē", "clear_all": "全部清除", "clear_all_recent_searches": "清除所有最čŋ‘įš„æœå°‹", + "clear_file_cache": "清除文äģļåŋĢ取", "clear_message": "æ¸…é™¤č¨Šæ¯", "clear_value": "清除å€ŧ", "client_cert_dialog_msg_confirm": "įĸē厚", @@ -623,7 +688,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": "æŠ˜į–Š", @@ -634,32 +699,32 @@ "comment_options": "ᕙ荀遏項", "comments_and_likes": "į•™č¨€čˆ‡å–œæ­Ą", "comments_are_disabled": "į•™č¨€åˇ˛åœį”¨", - "common_create_new_album": "新åĸžį›¸į°ŋ", - "common_server_error": "čĢ‹æĒĸæŸĨæ‚¨įš„įļ˛įĩĄé€ŖæŽĨīŧŒįĸēäŋäŧ翜å™¨å¯é€ŖæŽĨīŧŒä¸”æœŦፋåŧčˆ‡äŧ翜å™¨į‰ˆæœŦå…ŧ厚。", + "common_create_new_album": "åģēįĢ‹æ–°į›¸į°ŋ", + "common_server_error": "čĢ‹æĒĸæŸĨæ‚¨įš„įļ˛čˇ¯é€ŖįˇšīŧŒįĸēäŋäŧ翜å™¨å¯é€ŖįˇšīŧŒä¸ĻįĸēčĒ App 與äŧ翜å™¨į‰ˆæœŦį›¸åŽšã€‚", "completed": "åˇ˛åŽŒæˆ", "confirm": "įĸēčĒ", - "confirm_admin_password": "įĸēčĒįŽĄį†č€…å¯†įĸŧ", - "confirm_delete_face": "您įĸē厚čĻåžžé …į›Žä¸­åˆĒ除 {name} įš„č‡‰å­”å—ŽīŧŸ", - "confirm_delete_shared_link": "įĸē厚åˆĒ除逪įĩå—ŽīŧŸ", - "confirm_keep_this_delete_others": "æ‰€æœ‰įš„å…ļäģ–å †į–Šé …į›Žå°‡čĸĢåˆĒ除。įĸē厚įšŧįēŒå—ŽīŧŸ", + "confirm_admin_password": "įĸēčĒįŽĄį†å“Ąå¯†įĸŧ", + "confirm_delete_face": "您įĸē厚čĻåžžčŠ˛åĒ’é̔䏭åˆĒ除{name}įš„č‡‰å­”å—ŽīŧŸ", + "confirm_delete_shared_link": "您įĸē厚čρåˆĒé™¤é€™å€‹å…ąäēĢ逪įĩå—ŽīŧŸ", + "confirm_keep_this_delete_others": "除此åĒ’é̔外īŧŒå †į–Šä¸­įš„å…ļäģ–åĒ’éĢ”éƒŊ將čĸĢåˆĒ除。您įĸē厚čρįšŧįēŒå—ŽīŧŸ", "confirm_new_pin_code": "įĸēčĒæ–° PIN įĸŧ", "confirm_password": "įĸēčĒå¯†įĸŧ", - "confirm_tag_face": "æ‚¨æƒŗčĻå°‡æ­¤č‡‰å­”æ¨™č¨˜į‚ē {name} 嗎īŧŸ", - "confirm_tag_face_unnamed": "æ‚¨æƒŗæ¨™č¨˜é€™åŧĩč‡‰å—ŽīŧŸ", + "confirm_tag_face": "æ‚¨æƒŗčĻå°‡æ­¤č‡‰å­”æ¨™įą¤į‚ē {name} 嗎īŧŸ", + "confirm_tag_face_unnamed": "æ‚¨æƒŗæ¨™įą¤é€™åŧĩč‡‰å—ŽīŧŸ", "connected_device": "厞逪įĩčŖįŊŽ", "connected_to": "厞逪æŽĨ到", - "contain": "包åĢ", - "context": "情åĸƒ", + "contain": "į­‰æ¯”įŊŽå…Ĩ", + "context": "內厚上下文", "continue": "įšŧįēŒ", - "control_bottom_app_bar_create_new_album": "新åĸžį›¸į°ŋ", - "control_bottom_app_bar_delete_from_immich": "åžžImmichäŧ翜å™¨ä¸­åˆĒ除", - "control_bottom_app_bar_delete_from_local": "åžžį§ģå‹•čŖįŊŽä¸­åˆĒ除", + "control_bottom_app_bar_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_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": "åœ–į‰‡åˇ˛č¤‡čŖŊ到å‰Ēč˛ŧį°ŋ。", + "control_bottom_app_bar_share_to": "分äēĢįĩĻ", + "control_bottom_app_bar_trash_from_immich": "į§ģå…Ĩ垃圞æĄļ", + "copied_image_to_clipboard": "åˇ˛å°‡åœ–į‰‡č¤‡čŖŊ到å‰Ēč˛ŧį°ŋ。", "copied_to_clipboard": "厞複čŖŊ到å‰Ēč˛ŧį°ŋīŧ", "copy_error": "複čŖŊ錯čǤ", "copy_file_path": "複čŖŊæĒ”æĄˆčˇ¯åž‘", @@ -674,145 +739,162 @@ "create": "åģēįĢ‹", "create_album": "åģēį̋ᛏį°ŋ", "create_album_page_untitled": "æœĒå‘Ŋ名", - "create_library": "åģēįĢ‹åœ–åēĢ", + "create_library": "åģēįĢ‹åĒ’éĢ”åēĢ", "create_link": "åģēį̋逪įĩ", "create_link_to_share": "åģēįĢ‹å…ąäēĢ逪įĩ", - "create_link_to_share_description": "įŸĨ道逪įĩįš„äŊŋᔍ者éƒŊ可äģĨæŸĨįœ‹é€™æœŦᛏį°ŋä¸­įš„į›¸į‰‡", + "create_link_to_share_description": "äģģäŊ•æŒæœ‰é€Ŗįĩįš„äēēéƒŊå…č¨ąæŸĨįœ‹æ‰€é¸į›¸į‰‡", "create_new": "新åĸž", - "create_new_person": "å‰ĩåģēæ–°äēēį‰Š", - "create_new_person_hint": "å°‡é¸åŽšįš„æĒ”æĄˆåˆ†é…įĩĻæ–°äēēį‰Š", + "create_new_person": "åģēįĢ‹æ–°äēēį‰Š", + "create_new_person_hint": "å°‡é¸åŽšįš„åĒ’éĢ”åˆ†é…įĩĻæ–°äēēį‰Š", "create_new_user": "åģēįĢ‹æ–°äŊŋᔍ者", - "create_shared_album_page_share_add_assets": "新åĸžé …į›Ž", - "create_shared_album_page_share_select_photos": "é¸æ“‡é …į›Ž", - "create_tag": "åģēįĢ‹æ¨™č¨˜", - "create_tag_description": "åģēįĢ‹æ–°įš„æ¨™įą¤ã€‚č‹ĨčρåģēįĢ‹ä¸åŒįž¤įĩ„åˆ†éĄžæ¨™įą¤īŧŒčĢ‹čŧ¸å…ĨåŽŒæ•´įš„æ¨™įą¤čˇ¯åž‘(包æ‹Ŧæ­Ŗæ–œįˇš / )。", + "create_shared_album_page_share_add_assets": "新åĸžč†œéĢ”", + "create_shared_album_page_share_select_photos": "é¸æ“‡į…§į‰‡", + "create_shared_link": "åģēįĢ‹å…ąäēĢ逪įĩ", + "create_tag": "åģēįĢ‹æ¨™įą¤", + "create_tag_description": "åģēįĢ‹æ–°æ¨™įą¤ã€‚č‹ĨčρåģēįĢ‹åˇĸį‹€æ¨™įą¤īŧŒčĢ‹čŧ¸å…Ĩ包åĢæ­Ŗæ–œįˇšįš„åŽŒæ•´æ¨™įą¤čˇ¯åž‘ã€‚", "create_user": "åģēįĢ‹äŊŋᔍ者", "created": "åģēįĢ‹æ–ŧ", "created_at": "åģēįĢ‹æ–ŧ", + "creating_linked_albums": "å‰ĩåģē逪įĩį›¸å†Œ ...", "crop": "誁å‰Ē", "curated_object_page_title": "äē‹į‰Š", - "current_device": "æ­¤čŖįŊŽ", - "current_pin_code": "į•ļ前 PIN įĸŧ", + "current_device": "į›Žå‰čŖįŊŽ", + "current_pin_code": "į›Žå‰ PIN įĸŧ", "current_server_address": "į›Žå‰įš„äŧ翜å™¨äŊå€", - "custom_locale": "č‡Ēč¨‚å€åŸŸ", - "custom_locale_description": "䞝čĒžč¨€å’Œå€åŸŸč¨­åŽšæ—Ĩ期和數字æ ŧåŧ", + "custom_locale": "č‡Ēč¨‚åœ°å€č¨­åŽš", + "custom_locale_description": "栚據čĒžč¨€čˆ‡åœ°å€æ ŧåŧåŒ–æ—ĨæœŸčˆ‡æ•¸å­—", + "custom_url": "č‡ĒåŽšįžŠ URL", "daily_title_text_date": "E, MMM dd", - "daily_title_text_date_year": "YYYYåš´M月Dæ—Ĩ (E)", + "daily_title_text_date_year": "YYYY åš´ M 月 D æ—Ĩ (E)", "dark": "æˇąč‰˛", - "darkTheme": "åˆ‡æ›įˆ˛æˇąč‰˛ä¸ģ題", - "date_after": "æ—Ĩ期䚋垌", + "dark_theme": "åˆ‡æ›æˇąč‰˛ä¸ģ題", + "date_after": "čĩˇå§‹æ—Ĩ期", "date_and_time": "æ—ĨæœŸčˆ‡æ™‚é–“", - "date_before": "æ—Ĩ期䚋前", - "date_format": "yåš´M月dæ—Ĩ (E) h:mm a", + "date_before": "įĩæŸæ—Ĩ期", + "date_format": "y åš´ M 月 d æ—Ĩ (E) h:mm a", "date_of_birth_saved": "å‡ēį”Ÿæ—ĨæœŸå„˛å­˜æˆåŠŸ", "date_range": "æ—ĨæœŸį¯„åœ", "day": "æ—Ĩ", + "days": "æ—Ĩ", "deduplicate_all": "åˆĒé™¤æ‰€æœ‰é‡č¤‡é …į›Ž", - "deduplication_criteria_1": "圖像大小īŧˆäģĨäŊå…ƒįĩ„į‚ēå–ŽäŊīŧ‰", + "deduplication_criteria_1": "åŊąåƒå¤§å°īŧˆäģĨäŊå…ƒįĩ„į‚ēå–ŽäŊīŧ‰", "deduplication_criteria_2": "EXIF čŗ‡æ–™æ•¸é‡", "deduplication_info": "é‡č¤‡čŗ‡æ–™åˆĒé™¤čŗ‡č¨Š", - "deduplication_info_description": "į‚ēäē†č‡Ēå‹•é é¸é …į›Žä¸Ļ大量åˆĒé™¤é‡č¤‡é …į›ŽīŧŒæˆ‘們æŸĨįœ‹īŧš", - "default_locale": "é č¨­å€åŸŸ", - "default_locale_description": "äžį€čĻŊå™¨å€åŸŸč¨­åŽšæ—Ĩ期和數字æ ŧåŧ", + "deduplication_info_description": "čρč‡Ē動預先選取åĒ’éĢ”ä¸Ļ扚æŦĄį§ģé™¤é‡č¤‡é …į›ŽīŧŒæˆ‘們會æĒĸæŸĨīŧš", + "default_locale": "é č¨­åœ°å€", + "default_locale_description": "äžį…§æ‚¨įš„į€čĻŊå™¨åœ°å€č¨­åŽšæ ŧåŧåŒ–æ—ĨæœŸčˆ‡æ•¸å­—", "delete": "åˆĒ除", + "delete_action_confirmation_message": "您įĸē厚čρåˆĒ除此åĒ’éĢ”å—ŽīŧŸæ­¤æ“äŊœæœƒå°‡čОåĒ’éĢ”į§ģ臺äŧ翜å™¨įš„垃圞æĄļīŧŒä¸Ļ會提į¤ē您是åĻčρ圍æœŦ地同時åˆĒ除", + "delete_action_prompt": "{count} 個厞åˆĒ除", "delete_album": "åˆĒ除ᛏį°ŋ", - "delete_api_key_prompt": "您įĸē厚čρåˆĒ除這個 API Key嗎īŧŸ", - "delete_dialog_alert": "這äē›é …į›Žå°‡åžž Immich å’Œæ‚¨įš„čŖįŊŽä¸­æ°¸äš…åˆĒ除", - "delete_dialog_alert_local": "這äē›é …į›Žå°‡åžžæ‚¨įš„į§ģå‹•čŖįŊŽä¸­æ°¸äš…åˆĒ除īŧŒäŊ†äģį„ļ可äģĨåžžImmichäŧ翜å™¨ä¸­å†æŦĄį˛å–", - "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†é …į›Žé‚„æœĒ備äģŊ臺Immichäŧ翜å™¨īŧŒå°‡åžžæ‚¨įš„į§ģå‹•čŖįŊŽä¸­æ°¸äš…åˆĒ除", - "delete_dialog_alert_remote": "這äē›é …į›Žå°‡åžžImmichäŧ翜å™¨ä¸­æ°¸äš…åˆĒ除", + "delete_api_key_prompt": "您įĸē厚čρåˆĒ除這個 API 金鑰嗎īŧŸ", + "delete_dialog_alert": "這äē›é …į›Žå°‡åžž Immich äģĨåŠæ‚¨įš„čŖįŊŽä¸­æ°¸äš…åˆĒ除", + "delete_dialog_alert_local": "這äē›é …į›Žå°‡æœƒåžžæ‚¨įš„čŖįŊŽä¸­æ°¸äš…į§ģ除īŧŒäŊ†äģå¯åœ¨ Immich äŧ翜å™¨ä¸Šå­˜å–", + "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†é …į›Žå°šæœĒ備äģŊ臺 ImmichīŧŒä¸”å°‡æœƒåžžæ‚¨įš„čŖįŊŽä¸­æ°¸äš…į§ģ除", + "delete_dialog_alert_remote": "這äē›é …į›Žå°‡åžž Immich äŧ翜å™¨ä¸­æ°¸äš…åˆĒ除", "delete_dialog_ok_force": "įĸēčĒåˆĒ除", "delete_dialog_title": "永䚅åˆĒ除", - "delete_duplicates_confirmation": "您įĸē厚čĻæ°¸äš…åˆĒ除這äē›é‡č¤‡é …å—ŽīŧŸ", - "delete_face": "åˆĒ除臉部", - "delete_key": "åˆĒ除密鑰", + "delete_duplicates_confirmation": "您įĸē厚čĻæ°¸äš…åˆĒ除這äē›é‡č¤‡é …į›Žå—ŽīŧŸ", + "delete_face": "åˆĒ除臉孔", + "delete_key": "åˆĒ除金鑰", "delete_library": "åˆĒ除圖åēĢ", - "delete_link": "åˆĒ除鏈įĩ", - "delete_local_dialog_ok_backed_up_only": "僅åˆĒ除厞備äģŊé …į›Ž", + "delete_link": "åˆĒ除逪įĩ", + "delete_local_action_prompt": "厞圍æœŦ地åˆĒ除 {count} å€‹é …į›Ž", + "delete_local_dialog_ok_backed_up_only": "僅åˆĒ除厞備äģŊįš„é …į›Ž", "delete_local_dialog_ok_force": "įĸēčĒåˆĒ除", "delete_others": "åˆĒ除å…ļäģ–", - "delete_shared_link": "åˆĒé™¤å…ąäēĢ鏈įĩ", - "delete_shared_link_dialog_title": "åˆĒé™¤å…ąäēĢ鏈æŽĨ", - "delete_tag": "åˆĒé™¤æ¨™č¨˜", - "delete_tag_confirmation_prompt": "įĸē厚čρåˆĒ除「{tagName}」īŧˆæ¨™č¨˜īŧ‰å—ŽīŧŸ", + "delete_permanently": "永䚅åˆĒ除", + "delete_permanently_action_prompt": "åˇ˛æ°¸äš…åˆĒ除 {count} å€‹é …į›Ž", + "delete_shared_link": "åˆĒé™¤å…ąäēĢ逪įĩ", + "delete_shared_link_dialog_title": "åˆĒé™¤å…ąäēĢ逪įĩ", + "delete_tag": "åˆĒé™¤æ¨™įą¤", + "delete_tag_confirmation_prompt": "您įĸē厚čρåˆĒ除「{tagName}ã€æ¨™įą¤å—ŽīŧŸ", "delete_user": "åˆĒ除äŊŋᔍ者", - "deleted_shared_link": "厞åˆĒé™¤å…ąäēĢ鏈įĩ", - "deletes_missing_assets": "åˆĒ除᪁įĸŸéēå¤ąé …į›Ž", + "deleted_shared_link": "å…ąäēĢ逪įĩåˇ˛åˆĒ除", + "deletes_missing_assets": "åˆĒ除᪁įĸŸä¸­éēå¤ąįš„åĒ’éĢ”", "description": "描čŋ°", "description_input_hint_text": "新åĸžæčŋ°...", - "description_input_submit_error": "更新描čŋ°æ™‚å‡ē錯īŧŒčĢ‹æĒĸæŸĨæ—ĨčnjäģĨį˛å–æ›´å¤ščŠŗį´°čŗ‡č¨Š", + "description_input_submit_error": "更新描čŋ°æ™‚į™ŧį”ŸéŒ¯čǤīŧŒčĢ‹æĒĸæŸĨæ—ĨčnjäģĨå–åž—æ›´å¤ščŠŗį´°čŗ‡č¨Š", + "deselect_all": "取æļˆå…¨é¸", "details": "čŠŗį´°čŗ‡č¨Š", "direction": "斚向", - "disabled": "åœį”¨", + "disabled": "åˇ˛åœį”¨", "disallow_edits": "ä¸å…č¨ąįˇ¨čŧ¯", "discord": "Discord", "discover": "æŽĸį´ĸ", - "discovered_devices": "厞æŽĸį´ĸå¤ščŖįŊŽ", + "discovered_devices": "厞æŽĸį´ĸįš„č¨­å‚™", "dismiss_all_errors": "åŋŊį•Ĩ所有錯čǤ", "dismiss_error": "åŋŊį•Ĩ錯čǤ", "display_options": "éĄ¯į¤ē選項", "display_order": "éĄ¯į¤ē順åē", "display_original_photos": "éĄ¯į¤ēåŽŸå§‹į…§į‰‡", - "display_original_photos_setting_description": "在įļ˛é čˆ‡åŽŸå§‹æĒ”æĄˆį›¸åŽšįš„æƒ…æŗä¸‹īŧŒæŸĨįœ‹æĒ”æĄˆæ™‚å„Ēå…ˆéĄ¯į¤ē原始æĒ”æĄˆč€Œéžį¸Žåœ–ã€‚é€™å¯čƒŊæœƒčŽ“į…§į‰‡éĄ¯į¤ē速åēĻ變æ…ĸ。", + "display_original_photos_setting_description": "在æĒĸčĻ–åĒ’éĢ”æ™‚īŧŒč‹Ĩ原始åĒ’éĢ”čˆ‡įļ˛é į›¸åŽšīŧŒå‰‡å„Ēå…ˆéĄ¯į¤ēåŽŸå§‹į›¸į‰‡č€Œéžį¸Žåœ–ã€‚é€™å¯čƒŊæœƒå°Žč‡´į…§į‰‡éĄ¯į¤ē速åēĻ變æ…ĸ。", "do_not_show_again": "ä¸å†éĄ¯į¤ēæ­¤č¨Šæ¯", - "documentation": "čĒĒæ˜Žæ›¸", + "documentation": "čĒĒæ˜Žæ–‡äģļ", "done": "厌成", "download": "下čŧ‰", + "download_action_prompt": "æ­Ŗåœ¨ä¸‹čŧ‰ {count} 個åĒ’éĢ”", "download_canceled": "下čŧ‰åˇ˛å–æļˆ", "download_complete": "下čŧ‰åŽŒæˆ", - "download_enqueue": "厞加å…Ĩ下čŧ‰éšŠåˆ—", - "download_error": "下čŧ‰å‡ē錯", + "download_enqueue": "厞加å…Ĩ下čŧ‰äŊ‡åˆ—", + "download_error": "下čŧ‰æ™‚į™ŧį”ŸéŒ¯čǤ", "download_failed": "下čŧ‰å¤ąæ•—", "download_finished": "下čŧ‰åŽŒæˆ", "download_include_embedded_motion_videos": "åĩŒå…ĨåŊąį‰‡", - "download_include_embedded_motion_videos_description": "把åĩŒå…Ĩå‹•æ…‹į…§į‰‡įš„åŊąį‰‡äŊœį‚ēå–Žį¨įš„æĒ”æĄˆåŒ…åĢ在內", + "download_include_embedded_motion_videos_description": "å°‡å‹•æ…‹į›¸į‰‡ä¸­å…§åĩŒįš„åŊąį‰‡åĻ存į‚ēį¨įĢ‹æĒ”æĄˆ", "download_notfound": "į„Ąæŗ•æ‰žåˆ°ä¸‹čŧ‰", "download_paused": "下čŧ‰åˇ˛æšĢ停", "download_settings": "下čŧ‰", - "download_settings_description": "įŽĄį†čˆ‡æĒ”æĄˆä¸‹čŧ‰į›¸é—œįš„設åޚ", - "download_started": "開始下čŧ‰", + "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": "é‡č¤‡é …į›Ž", - "duplicates_description": "通過指į¤ē每一įĩ„é‡č¤‡įš„æĒ”æĄˆīŧˆåĻ‚æžœæœ‰īŧ‰äž†č§Ŗæąēå•éĄŒ", - "duration": "æ™‚é•ˇ", + "duplicates_description": "逐一æĒĸæŸĨæ¯å€‹įž¤įĩ„īŧŒä¸Ļ標į¤ēå…ļ中是åĻæœ‰é‡č¤‡åĒ’éĢ”", + "duration": "éĄ¯į¤ēæ™‚é•ˇ", "edit": "ᎍčŧ¯", "edit_album": "ᎍčŧ¯į›¸į°ŋ", - "edit_avatar": "ᎍčŧ¯åŊĸ蹥", + "edit_avatar": "ᎍčŧ¯å€‹äēēčŗ‡æ–™åœ–į‰‡", + "edit_birthday": "ᎍčŧ¯į”Ÿæ—Ĩ", "edit_date": "ᎍčŧ¯æ—Ĩ期", "edit_date_and_time": "ᎍčŧ¯æ—ĨæœŸčˆ‡æ™‚é–“", + "edit_date_and_time_action_prompt": "厞ᎍčŧ¯äē† {count} 個æ—ĨæœŸčˆ‡æ™‚é–“", + "edit_date_and_time_by_offset": "䞝偏į§ģé‡čŽŠæ›´æ—Ĩ期", + "edit_date_and_time_by_offset_interval": "æ–°įš„æ—ĨæœŸį¯„åœīŧš{from} 臺 {to}", "edit_description": "ᎍčŧ¯æčŋ°", "edit_description_prompt": "čĢ‹é¸æ“‡æ–°įš„æčŋ°īŧš", - "edit_exclusion_pattern": "ᎍčŧ¯æŽ’é™¤æ¨Ąåŧ", + "edit_exclusion_pattern": "ᎍčŧ¯æŽ’除æĸäģļ", "edit_faces": "ᎍčŧ¯č‡‰å­”", "edit_import_path": "ᎍčŧ¯åŒ¯å…Ĩčˇ¯åž‘", "edit_import_paths": "ᎍčŧ¯åŒ¯å…Ĩčˇ¯åž‘", - "edit_key": "ᎍčŧ¯å¯†é‘°", - "edit_link": "ᎍčŧ¯éˆįĩ", - "edit_location": "įŧ–čž‘äŊįŊŽäŋĄæ¯", + "edit_key": "ᎍčŧ¯é‡‘é‘°", + "edit_link": "ᎍčŧ¯é€Ŗįĩ", + "edit_location": "ᎍčŧ¯äŊįŊŽ", + "edit_location_action_prompt": "{count} 個äŊįŊŽåˇ˛įˇ¨čŧ¯", "edit_location_dialog_title": "äŊįŊŽ", "edit_name": "ᎍčŧ¯åį¨ą", "edit_people": "ᎍčŧ¯äēēį‰Š", - "edit_tag": "ᎍčŧ¯æ¨™č¨˜", + "edit_tag": "ᎍčŧ¯æ¨™įą¤", "edit_title": "ᎍčŧ¯æ¨™éĄŒ", "edit_user": "ᎍčŧ¯äŊŋᔍ者", "edited": "åˇąįˇ¨čŧ¯", "editor": "ᎍčŧ¯å™¨", - "editor_close_without_save_prompt": "ᎍčŧ¯éŽįš„å…§åŽšä¸æœƒå„˛å­˜čĩˇäž†", + "editor_close_without_save_prompt": "æ­¤čŽŠæ›´å°‡ä¸æœƒčĸĢå„˛å­˜", "editor_close_without_save_title": "čĻé—œé–‰įˇ¨čŧ¯å™¨å—ŽīŧŸ", "editor_crop_tool_h2_aspect_ratios": "長å¯Ŧ比", "editor_crop_tool_h2_rotation": "旋čŊ‰", "email": "é›ģ子éƒĩäģļ", "email_notifications": "Email 通įŸĨ", - "empty_folder": "æ­¤čŗ‡æ–™å¤žį‚ēįŠē", + "empty_folder": "é€™å€‹čŗ‡æ–™å¤žæ˜¯įŠēįš„", "empty_trash": "清įŠē垃圞æĄļ", - "empty_trash_confirmation": "įĸē厚čĻæ¸…įŠē垃圞æĄļ嗎īŧŸé€™æœƒæ°¸äš…åˆĒ除 Immich 垃圞æĄļä¸­æ‰€æœ‰įš„æĒ”æĄˆã€‚\næ­¤æ­ĨéŠŸį„Ąæŗ•å–æļˆå–”īŧ", + "empty_trash_confirmation": "您įĸē厚čĻæ¸…įŠē垃圞æĄļ嗎īŧŸé€™æœƒæ°¸äš…åˆĒ除 Immich 垃圞æĄļä¸­æ‰€æœ‰įš„åĒ’éĢ”ã€‚\næ‚¨į„Ąæŗ•æ’¤éŠˇæ­¤čŽŠæ›´īŧ", "enable": "å•Ÿį”¨", + "enable_backup": "å•Ÿį”¨å‚™äģŊ", "enable_biometric_auth_description": "čŧ¸å…Ĩæ‚¨įš„ PIN įĸŧäģĨå•Ÿį”¨į”Ÿį‰Ščž¨č­˜éŠ—č­‰", "enabled": "åˇąå•Ÿį”¨", "end_date": "įĩæŸæ—Ĩ期", @@ -821,81 +903,85 @@ "enter_your_pin_code": "čŧ¸å…Ĩæ‚¨įš„ PIN įĸŧ", "enter_your_pin_code_subtitle": "čŧ¸å…Ĩæ‚¨įš„ PIN įĸŧäģĨå­˜å–éŽ–åŽšįš„čŗ‡æ–™å¤ž", "error": "錯čǤ", - "error_change_sort_album": "į„Ąæŗ•æ”ščŽŠį›¸į°ŋ排åē", - "error_delete_face": "åžžé …į›Žä¸­åˆĒé™¤č‡‰å­”æ™‚į™ŧį”ŸéŒ¯čǤ", - "error_loading_image": "čŧ‰å…Ĩåœ–į‰‡æ™‚å‡ē錯", - "error_saving_image": "錯čǤ: {error}", + "error_change_sort_album": "čŽŠæ›´į›¸į°ŋ排åēå¤ąæ•—", + "error_delete_face": "åžžåĒ’éĢ”åˆĒé™¤č‡‰å­”æ™‚å¤ąæ•—", + "error_getting_places": "į˛å–äŊįŊŽæ™‚å‡ē錯", + "error_loading_image": "åœ–į‰‡čŧ‰å…Ĩ錯čǤ", + "error_loading_partners": "加čŧ‰åˆäŊœå¤Ĩäŧ´æ™‚å‡ē錯īŧš{error}", + "error_saving_image": "錯čǤīŧš{error}", "error_tag_face_bounding_box": "æ¨™č¨˜č‡‰éƒ¨éŒ¯čǤ - į„Ąæŗ•å–åž—é‚Šį•ŒæĄ†åæ¨™", - "error_title": "錯čǤ - å‡ēå•éĄŒäē†", + "error_title": "錯čǤ - į™ŧį”ŸéŒ¯čǤ", "errors": { - "cannot_navigate_next_asset": "į„Ąæŗ•į€čĻŊ下一個æĒ”æĄˆ", - "cannot_navigate_previous_asset": "į„Ąæŗ•į€čĻŊ上一個æĒ”æĄˆ", - "cant_apply_changes": "į„Ąæŗ•åĨ—į”¨æ›´æ”š", + "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, other {# 個æĒ”æĄˆ}}įš„čŠŗį´°čŗ‡æ–™", + "cant_change_asset_favorite": "į„Ąæŗ•čŽŠæ›´æĒ”æĄˆįš„æ”ļč—į‹€æ…‹", + "cant_change_metadata_assets_count": "į„Ąæŗ•čŽŠæ›´ {count, plural, other {# 個æĒ”æĄˆ}}įš„ä¸­įšŧčŗ‡æ–™", "cant_get_faces": "į„Ąæŗ•å–åž—č‡‰å­”", "cant_get_number_of_comments": "į„Ąæŗ•å–åž—į•™č¨€æ•¸é‡", - "cant_search_people": "æœĒ搜尋到äēēį‰Š", + "cant_search_people": "į„Ąæŗ•æœå°‹äēēį‰Š", "cant_search_places": "į„Ąæŗ•æœå°‹åœ°éģž", - "error_adding_assets_to_album": "將æĒ”æĄˆåŠ å…Ĩᛏį°ŋ時å‡ē錯", - "error_adding_users_to_album": "將äŊŋį”¨č€…åŠ å…Ĩᛏį°ŋ時å‡ē錯", - "error_deleting_shared_user": "åˆĒé™¤å…ąäēĢäŊŋį”¨č€…æ™‚å‡ē錯", - "error_downloading": "下čŧ‰ {filename} 時å‡ē錯", - "error_hiding_buy_button": "隱藏čŗŧįŊŽæŒ‰éˆ•時å‡ē錯", - "error_removing_assets_from_album": "åžžį›¸į°ŋ中į§ģ除æĒ”æĄˆæ™‚å‡ē錯äē†īŧŒčĢ‹åˆ°æŽ§åˆļč‡ēįž­č§ŖčŠŗį´°čŗ‡č¨Š", - "error_selecting_all_assets": "選擇所有æĒ”æĄˆæ™‚å‡ē錯", + "error_adding_assets_to_album": "將åĒ’éĢ”åŠ å…Ĩᛏį°ŋ時į™ŧį”ŸéŒ¯čǤ", + "error_adding_users_to_album": "將äŊŋį”¨č€…åŠ å…Ĩᛏį°ŋ時į™ŧį”ŸéŒ¯čǤ", + "error_deleting_shared_user": "åˆĒé™¤å…ąäēĢäŊŋį”¨č€…æ™‚į™ŧį”ŸéŒ¯čǤ", + "error_downloading": "下čŧ‰ {filename} 時į™ŧį”ŸéŒ¯čǤ", + "error_hiding_buy_button": "隱藏čŗŧč˛ˇæŒ‰éˆ•æ™‚į™ŧį”ŸéŒ¯čǤ", + "error_removing_assets_from_album": "åžžį›¸į°ŋį§ģ除åĒ’éĢ”æ™‚į™ŧį”ŸéŒ¯čǤīŧŒčĢ‹æĒĸæŸĨä¸ģ控台äģĨå–åž—æ›´å¤ščŠŗį´°čŗ‡č¨Š", + "error_selecting_all_assets": "選取所有æĒ”æĄˆæ™‚į™ŧį”ŸéŒ¯čǤ", "exclusion_pattern_already_exists": "æ­¤æŽ’é™¤æ¨Ąåŧåˇ˛å­˜åœ¨ã€‚", "failed_to_create_album": "ᛏį°ŋåģēįĢ‹å¤ąæ•—", "failed_to_create_shared_link": "åģēįĢ‹å…ąäēĢ逪įĩå¤ąæ•—", "failed_to_edit_shared_link": "ᎍčŧ¯å…ąäēĢ逪įĩå¤ąæ•—", - "failed_to_get_people": "į„Ąæŗ•į˛å–äēēį‰Š", - "failed_to_keep_this_delete_others": "į„Ąæŗ•äŋį•™æ­¤é …į›Žä¸ĻåˆĒ除å…ļäģ–é …į›Ž", - "failed_to_load_asset": "æĒ”æĄˆčŧ‰å…Ĩå¤ąæ•—", - "failed_to_load_assets": "æĒ”æĄˆčŧ‰å…Ĩå¤ąæ•—", - "failed_to_load_notifications": "į„Ąæŗ•čŧ‰å…Ĩ通įŸĨ", - "failed_to_load_people": "į„Ąæŗ•čŧ‰å…Ĩäēēį‰Š", - "failed_to_remove_product_key": "į„Ąæŗ•į§ģ除į”ĸ品密鑰", - "failed_to_stack_assets": "į„Ąæŗ•å †į–ŠæĒ”æĄˆ", - "failed_to_unstack_assets": "į„Ąæŗ•č§Ŗé™¤å †į–ŠæĒ”æĄˆ", + "failed_to_get_people": "į„Ąæŗ•å–åž—äēēį‰Š", + "failed_to_keep_this_delete_others": "į„Ąæŗ•äŋį•™æ­¤åĒ’éĢ”ä¸ĻåˆĒ除å…ļäģ–åĒ’éĢ”", + "failed_to_load_asset": "åĒ’éĢ”čŧ‰å…Ĩå¤ąæ•—", + "failed_to_load_assets": "åĒ’éĢ”čŧ‰å…Ĩå¤ąæ•—", + "failed_to_load_notifications": "čŧ‰å…Ĩ通įŸĨå¤ąæ•—", + "failed_to_load_people": "čŧ‰å…Ĩäēēį‰Šå¤ąæ•—", + "failed_to_remove_product_key": "į§ģ除į”ĸå“é‡‘é‘°å¤ąæ•—", + "failed_to_reset_pin_code": "重įŊŽ PIN įĸŧå¤ąæ•—", + "failed_to_stack_assets": "į„Ąæŗ•åĒ’éĢ”å †į–Š", + "failed_to_unstack_assets": "觪除åĒ’éĢ”å †į–Šå¤ąæ•—", "failed_to_update_notification_status": "į„Ąæŗ•æ›´æ–°é€šįŸĨį‹€æ…‹", "import_path_already_exists": "此匯å…Ĩčˇ¯åž‘åˇ˛å­˜åœ¨ã€‚", - "incorrect_email_or_password": "é›ģ子éƒĩäģ￈–密įĸŧ有čǤ", + "incorrect_email_or_password": "é›ģ子éƒĩäģ￈–密įĸŧ錯čǤ", "paths_validation_failed": "{paths, plural, one {# å€‹čˇ¯åž‘} other {# å€‹čˇ¯åž‘}} éŠ—č­‰å¤ąæ•—", - "profile_picture_transparent_pixels": "個äēē頭像不čƒŊæœ‰é€æ˜Žåƒį´ ã€‚čĢ‹æ”žå¤§ä¸Ļ/或į§ģ動圖像。", - "quota_higher_than_disk_size": "æ‚¨åŽšįš„é…éĄé̘æ–ŧ᪁įĸŸåŽšé‡", + "profile_picture_transparent_pixels": "個äēēčŗ‡æ–™åœ–į‰‡ä¸čƒŊæœ‰é€æ˜Žåƒį´ ã€‚čĢ‹æ”žå¤§ä¸Ļ/或į§ģ動åŊąåƒã€‚", + "quota_higher_than_disk_size": "æ‚¨æ‰€č¨­åŽšįš„é…éĄå¤§æ–ŧ᪁įĸŸå¤§å°", + "something_went_wrong": "į™ŧį”ŸéŒ¯čǤ", "unable_to_add_album_users": "į„Ąæŗ•å°‡äŊŋį”¨č€…åŠ å…Ĩᛏį°ŋ", - "unable_to_add_assets_to_shared_link": "į„Ąæŗ•åŠ å…Ĩé …į›Žåˆ°å…ąäēĢ逪įĩ", + "unable_to_add_assets_to_shared_link": "į„Ąæŗ•åŠ å…ĨåĒ’éĢ”åˆ°å…ąäēĢ逪įĩ", "unable_to_add_comment": "į„Ąæŗ•æ–°åĸžį•™č¨€", - "unable_to_add_exclusion_pattern": "į„Ąæŗ•æˇģåŠ æŽ’é™¤æ¨Ąåŧ", + "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_description": "į„Ąæŗ•æ›´æ”šæčŋ°", - "unable_to_change_favorite": "į„Ąæŗ•æ›´æ”šæĒ”æĄˆįš„æ”ļč—į‹€æ…‹", - "unable_to_change_location": "į„Ąæŗ•æ›´æ”šäŊįŊŽ", - "unable_to_change_password": "į„Ąæŗ•æ›´æ”šå¯†įĸŧ", - "unable_to_change_visibility": "į„Ąæŗ•æ›´æ”š {count, plural, one {# äŊäēēåŖĢ} other {# äŊäēēåŖĢ}} įš„å¯čĻ‹æ€§", + "unable_to_change_album_user_role": "į„Ąæŗ•čŽŠæ›´į›¸į°ŋäŊŋį”¨č€…įš„č§’č‰˛", + "unable_to_change_date": "į„Ąæŗ•čŽŠæ›´æ—Ĩ期", + "unable_to_change_description": "į„Ąæŗ•čŽŠæ›´æčŋ°", + "unable_to_change_favorite": "į„Ąæŗ•čŽŠæ›´åĒ’éĢ”įš„æ”ļč—į‹€æ…‹", + "unable_to_change_location": "į„Ąæŗ•čŽŠæ›´äŊįŊŽ", + "unable_to_change_password": "į„Ąæŗ•čŽŠæ›´å¯†įĸŧ", + "unable_to_change_visibility": "į„Ąæŗ•čŽŠæ›´ {count, plural, one {# äŊäēēį‰Š} other {# äŊäēēį‰Š}} įš„å¯čĻ‹æ€§", "unable_to_complete_oauth_login": "į„Ąæŗ•åŽŒæˆ OAuth į™ģå…Ĩ", "unable_to_connect": "į„Ąæŗ•é€ŖæŽĨ", - "unable_to_copy_to_clipboard": "į„Ąæŗ•č¤‡čŖŊ到å‰Ēč˛ŧæŋīŧŒčĢ‹įĸēäŋæ‚¨äģĨ https å­˜å–čŠ˛é éĸ", - "unable_to_create_admin_account": "į„Ąæŗ•åģēįĢ‹įŽĄį†č€…å¸ŗč™Ÿ", + "unable_to_copy_to_clipboard": "į„Ąæŗ•č¤‡čŖŊ到å‰Ēč˛ŧį°ŋīŧŒčĢ‹įĸēäŋæ‚¨æ˜¯äģĨ https 存取æœŦ頁éĸ", + "unable_to_create_admin_account": "į„Ąæŗ•åģēįĢ‹įŽĄį†å“Ąå¸ŗč™Ÿ", "unable_to_create_api_key": "į„Ąæŗ•åģēįĢ‹æ–°įš„ API 金鑰", - "unable_to_create_library": "į„Ąæŗ•åģēįĢ‹čŗ‡æ–™åēĢ", + "unable_to_create_library": "į„Ąæŗ•åģēįĢ‹åĒ’éĢ”åēĢ", "unable_to_create_user": "į„Ąæŗ•åģēįĢ‹äŊŋᔍ者", "unable_to_delete_album": "į„Ąæŗ•åˆĒ除ᛏį°ŋ", - "unable_to_delete_asset": "į„Ąæŗ•åˆĒ除æĒ”æĄˆ", - "unable_to_delete_assets": "åˆĒ除æĒ”æĄˆæ™‚į™ŧį”ŸéŒ¯čǤ", - "unable_to_delete_exclusion_pattern": "į„Ąæŗ•åˆĒé™¤æŽ’é™¤æ¨Ąåŧ", + "unable_to_delete_asset": "į„Ąæŗ•åˆĒ除åĒ’éĢ”", + "unable_to_delete_assets": "åˆĒ除åĒ’éĢ”æ™‚į™ŧį”ŸéŒ¯čǤ", + "unable_to_delete_exclusion_pattern": "į„Ąæŗ•åˆĒé™¤į¯Šé¸æĸäģļ", "unable_to_delete_import_path": "į„Ąæŗ•åˆĒ除匯å…Ĩčˇ¯åž‘", "unable_to_delete_shared_link": "åˆĒé™¤å…ąäēĢ逪įĩå¤ąæ•—", "unable_to_delete_user": "į„Ąæŗ•åˆĒ除äŊŋᔍ者", "unable_to_download_files": "į„Ąæŗ•ä¸‹čŧ‰æĒ”æĄˆ", - "unable_to_edit_exclusion_pattern": "į„Ąæŗ•įˇ¨čŧ¯æŽ’é™¤æ¨Ąåŧ", + "unable_to_edit_exclusion_pattern": "į„Ąæŗ•įˇ¨čŧ¯į¯Šé¸æĸäģļ", "unable_to_edit_import_path": "į„Ąæŗ•įˇ¨čŧ¯åŒ¯å…Ĩčˇ¯åž‘", "unable_to_empty_trash": "į„Ąæŗ•æ¸…įŠē垃圞æĄļ", "unable_to_enter_fullscreen": "į„Ąæŗ•é€˛å…Ĩ全čžĸåš•", @@ -903,26 +989,26 @@ "unable_to_get_comments_number": "į„Ąæŗ•å–åž—į•™č¨€æ•¸é‡", "unable_to_get_shared_link": "å–åž—å…ąäēĢ逪įĩå¤ąæ•—", "unable_to_hide_person": "į„Ąæŗ•éšąč—äēēį‰Š", - "unable_to_link_motion_video": "į„Ąæŗ•éˆįĩå‹•æ…‹åŊąį‰‡", - "unable_to_link_oauth_account": "取垗 OAuth 授æŦŠå¤ąæ•—", + "unable_to_link_motion_video": "į„Ąæŗ•é€Ŗįĩå‹•æ…‹åŊąį‰‡", + "unable_to_link_oauth_account": "į„Ąæŗ•é€ŖæŽĨ OAuth å¸ŗč™Ÿ", "unable_to_log_out_all_devices": "į„Ąæŗ•į™ģå‡ēæ‰€æœ‰čŖįŊŽ", "unable_to_log_out_device": "į„Ąæŗ•į™ģå‡ēčŖįŊŽ", "unable_to_login_with_oauth": "į„Ąæŗ•äŊŋᔍ OAuth į™ģå…Ĩ", "unable_to_play_video": "į„Ąæŗ•æ’­æ”žåŊąį‰‡", "unable_to_reassign_assets_existing_person": "į„Ąæŗ•å°‡æĒ”æĄˆé‡æ–°æŒ‡æ´žįĩĻ {name, select, null {įžæœ‰įš„äēēå“Ą} other {{name}}}", - "unable_to_reassign_assets_new_person": "į„Ąæŗ•å°‡æĒ”æĄˆé‡æ–°æŒ‡æ´žįĩĻæ–°įš„äēēå“Ą", + "unable_to_reassign_assets_new_person": "į„Ąæŗ•å°‡åĒ’éĢ”é‡æ–°æŒ‡æ´žįĩĻæ–°įš„äēēį‰Š", "unable_to_refresh_user": "į„Ąæŗ•é‡æ–°æ•´į†äŊŋᔍ者", "unable_to_remove_album_users": "į„Ąæŗ•åžžį›¸į°ŋ中į§ģ除äŊŋᔍ者", "unable_to_remove_api_key": "į„Ąæŗ•į§ģ除 API 金鑰", - "unable_to_remove_assets_from_shared_link": "åˆĒé™¤å…ąäēĢ逪įĩä¸­æĒ”æĄˆå¤ąæ•—", - "unable_to_remove_library": "į„Ąæŗ•į§ģé™¤čŗ‡æ–™åēĢ", + "unable_to_remove_assets_from_shared_link": "åˆĒé™¤å…ąäēĢ逪įĩä¸­åĒ’éĢ”å¤ąæ•—", + "unable_to_remove_library": "į„Ąæŗ•į§ģ除åĒ’éĢ”åēĢ", "unable_to_remove_partner": "į„Ąæŗ•į§ģ除čĻĒæœ‹åĨŊ友", "unable_to_remove_reaction": "į„Ąæŗ•į§ģ除反應", "unable_to_reset_password": "į„Ąæŗ•é‡č¨­å¯†įĸŧ", "unable_to_reset_pin_code": "į„Ąæŗ•é‡įŊŽ PIN įĸŧ", - "unable_to_resolve_duplicate": "į„Ąæŗ•č§Ŗæąē重複項", - "unable_to_restore_assets": "į„Ąæŗ•é‚„åŽŸæĒ”æĄˆ", - "unable_to_restore_trash": "į„Ąæŗ•é‚„åŽŸåžƒåœžæĄļä¸­įš„é …į›Ž", + "unable_to_resolve_duplicate": "į„Ąæŗ•č§Ŗæąēé‡č¤‡é …į›Ž", + "unable_to_restore_assets": "į„Ąæŗ•é‚„åŽŸåĒ’éĢ”", + "unable_to_restore_trash": "į„Ąæŗ•é‚„åŽŸåžƒåœžæĄļ", "unable_to_restore_user": "į„Ąæŗ•é‚„åŽŸäŊŋᔍ者", "unable_to_save_album": "į„Ąæŗ•å„˛å­˜į›¸į°ŋ", "unable_to_save_api_key": "į„Ąæŗ•å„˛å­˜ API 金鑰", @@ -930,158 +1016,171 @@ "unable_to_save_name": "į„Ąæŗ•å„˛å­˜åį¨ą", "unable_to_save_profile": "į„Ąæŗ•å„˛å­˜å€‹äēēčŗ‡æ–™", "unable_to_save_settings": "į„Ąæŗ•å„˛å­˜č¨­åŽš", - "unable_to_scan_libraries": "į„Ąæŗ•æŽƒæåœ–åēĢ", - "unable_to_scan_library": "į„Ąæŗ•æŽƒæåœ–åēĢ", - "unable_to_set_feature_photo": "į„Ąæŗ•č¨­įŊŽį‰šč‰˛į…§į‰‡", - "unable_to_set_profile_picture": "į„Ąæŗ•č¨­įŊŽå€‹äēē頭像", + "unable_to_scan_libraries": "į„Ąæŗ•æŽƒæåĒ’éĢ”åēĢ", + "unable_to_scan_library": "į„Ąæŗ•æŽƒæåĒ’éĢ”åēĢ", + "unable_to_set_feature_photo": "į„Ąæŗ•č¨­åŽšå°éĸåœ–į‰‡", + "unable_to_set_profile_picture": "į„Ąæŗ•č¨­åŽšå€‹äēēčŗ‡æ–™åœ–į‰‡", "unable_to_submit_job": "į„Ąæŗ•æäē¤äģģ務", - "unable_to_trash_asset": "į„Ąæŗ•å°‡æĒ”æĄˆä¸Ÿé€˛åžƒåœžæĄļ", - "unable_to_unlink_account": "į„Ąæŗ•å°å¸ŗč™Ÿå–æļˆé€ŖæŽĨ", - "unable_to_unlink_motion_video": "į„Ąæŗ•å–æļˆéˆįĩå‹•æ…‹åŊąį‰‡", + "unable_to_trash_asset": "į„Ąæŗ•å°‡åĒ’éĢ”ä¸Ÿé€˛åžƒåœžæĄļ", + "unable_to_unlink_account": "į„Ąæŗ•å–æļˆå¸ŗč™Ÿįš„逪æŽĨ", + "unable_to_unlink_motion_video": "į„Ąæŗ•å–æļˆé€ŖæŽĨ動態åŊąį‰‡", "unable_to_update_album_cover": "į„Ąæŗ•æ›´æ–°į›¸į°ŋ封éĸ", "unable_to_update_album_info": "į„Ąæŗ•æ›´æ–°į›¸į°ŋčŗ‡č¨Š", - "unable_to_update_library": "į„Ąæŗ•æ›´æ–°čŗ‡æ–™åēĢ", + "unable_to_update_library": "į„Ąæŗ•æ›´æ–°åĒ’éĢ”åēĢ", "unable_to_update_location": "į„Ąæŗ•æ›´æ–°äŊįŊŽ", "unable_to_update_settings": "į„Ąæŗ•æ›´æ–°č¨­åŽš", "unable_to_update_timeline_display_status": "į„Ąæŗ•æ›´æ–°æ™‚é–“čģ¸éĄ¯į¤ēį‹€æ…‹", "unable_to_update_user": "į„Ąæŗ•æ›´æ–°äŊŋᔍ者", "unable_to_upload_file": "į„Ąæŗ•ä¸Šå‚ŗæĒ”æĄˆ" }, - "exif": "EXIF", + "exif": "EXIF 可ä礿›åœ–像文äģļæ ŧåŧ", "exif_bottom_sheet_description": "新åĸžæčŋ°...", - "exif_bottom_sheet_details": "čŠŗæƒ…", + "exif_bottom_sheet_description_error": "更新描čŋ°æ™‚į™ŧį”ŸéŒ¯čǤ", + "exif_bottom_sheet_details": "čŠŗį´°čŗ‡æ–™", "exif_bottom_sheet_location": "äŊįŊŽ", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "新åĸžå§“名", - "exif_bottom_sheet_person_age_months": "åš´éŊĄ {months} 月", - "exif_bottom_sheet_person_age_year_months": "1 æ­˛ {months} 個月", - "exif_bottom_sheet_person_age_years": "{years} æ­˛", "exit_slideshow": "退å‡ēåšģį‡ˆį‰‡", "expand_all": "åą•é–‹å…¨éƒ¨", "experimental_settings_new_asset_list_subtitle": "æ­Ŗåœ¨č™•į†", - "experimental_settings_new_asset_list_title": "啓ᔍå¯ĻéŠ—æ€§į…§į‰‡į ŧ", + "experimental_settings_new_asset_list_title": "å•Ÿį”¨å¯ĻéŠ—æ€§į›¸į‰‡æ ŧį‹€å¸ƒåą€", "experimental_settings_subtitle": "äŊŋᔍéĸ¨éšĒč‡Ē負īŧ", "experimental_settings_title": "å¯Ļ銗性功čƒŊ", "expire_after": "å¤ąæ•ˆæ™‚é–“", "expired": "åˇ˛éŽæœŸ", - "expires_date": "å¤ąæ•ˆæœŸé™īŧš{date}", + "expires_date": "å¤ąæ•ˆæ–ŧ {date}", "explore": "æŽĸį´ĸ", - "explorer": "į¸Ŋæ”Ŧ", + "explorer": "į¸ŊčĻŊ", "export": "匯å‡ē", - "export_as_json": "匯å‡ē JSON", + "export_as_json": "匯å‡ēį‚ē JSON", + "export_database": "匯å‡ēčŗ‡æ–™åēĢ", + "export_database_description": "匯å‡ē SQLite čŗ‡æ–™åēĢ", "extension": "副æĒ”名", "external": "外部", - "external_libraries": "外部圖åēĢ", + "external_libraries": "外部åĒ’éĢ”åēĢ", "external_network": "外部įļ˛čˇ¯", "external_network_sheet_info": "č‹ĨæœĒ逪æŽĨ偏åĨŊįš„ Wi-FiīŧŒå°‡äžåˆ—čĄ¨åžžä¸Šåˆ°ä¸‹é¸æ“‡å¯é€Ŗįˇšįš„äŧ翜å™¨įļ˛å€", "face_unassigned": "æœĒ指厚", "failed": "å¤ąæ•—", "failed_to_authenticate": "čēĢäģŊéŠ—č­‰å¤ąæ•—", - "failed_to_load_assets": "į„Ąæŗ•åŠ čŧ‰æĒ”æĄˆ", + "failed_to_load_assets": "į„Ąæŗ•čŧ‰å…ĨåĒ’éĢ”", "failed_to_load_folder": "į„Ąæŗ•čŧ‰å…Ĩčŗ‡æ–™å¤ž", "favorite": "æ”ļ藏", + "favorite_action_prompt": "åˇ˛æ–°åĸž {count} 個到æ”ļ藏", "favorite_or_unfavorite_photo": "æ”ļč—æˆ–å–æļˆæ”ļč—į…§į‰‡", "favorites": "æ”ļ藏", "favorites_page_no_favorites": "æœĒ扞到æ”ļč—é …į›Ž", "feature_photo_updated": "į‰šč‰˛į…§į‰‡åˇ˛æ›´æ–°", "features": "功čƒŊ", + "features_in_development": "į™ŧåą•ä¸­įš„į‰šéģž", "features_setting_description": "įŽĄį†æ‡‰į”¨į¨‹åŧåŠŸčƒŊ", - "file_name": "æĒ”名", - "file_name_or_extension": "æĒ”名或副æĒ”名", + "file_name": "æĒ”æĄˆåį¨ą", + "file_name_or_extension": "æĒ”æĄˆåį¨ąæˆ–å‰¯æĒ”名", "filename": "æĒ”æĄˆåį¨ą", "filetype": "æĒ”æĄˆéĄžåž‹", "filter": "æŋžéĄ", "filter_people": "į¯Šé¸äēēį‰Š", "filter_places": "į¯Šé¸åœ°éģž", - "find_them_fast": "æœå°‹åį¨ąīŧŒåŋĢ速扞äēē", + "find_them_fast": "é€éŽæœå°‹åį¨ąåŋĢ速扞到äģ–們", + "first": "įŦŦ一個", "fix_incorrect_match": "äŋŽåžŠä¸į›¸įŦĻįš„", "folder": "čŗ‡æ–™å¤ž", - "folder_not_found": "æœĒæ‰žåˆ°čŗ‡æ–™å¤ž", + "folder_not_found": "æ‰žä¸åˆ°čŗ‡æ–™å¤ž", "folders": "čŗ‡æ–™å¤ž", - "folders_feature_description": "äģĨčŗ‡æ–™å¤žį€čĻŊæĒ”æĄˆįŗģįĩąä¸­įš„į…§į‰‡å’ŒåŊąį‰‡", - "forward": "順åē", + "folders_feature_description": "é€éŽčŗ‡æ–™å¤žæĒĸčĻ–į€čĻŊæĒ”æĄˆįŗģįĩąä¸­įš„į›¸į‰‡čˆ‡åŊąį‰‡", + "forgot_pin_code_question": "åŋ˜č¨˜æ‚¨įš„ PIN įĸŧīŧŸ", + "forward": "į”ąæ–°č‡ŗčˆŠ", "gcast_enabled": "Google Cast", + "gcast_enabled_description": "此功čƒŊ需čĻåžž Google čŧ‰å…Ĩå¤–éƒ¨čŗ‡æēæ‰čƒŊæ­Ŗå¸¸é‹äŊœã€‚", "general": "一čˆŦ", + "geolocation_instruction_location": "éģžæ“Šå…ˇæœ‰ GPS åē§æ¨™įš„é …į›ŽäģĨäŊŋᔍå…ļäŊįŊŽīŧŒæˆ–į›´æŽĨ垞地圖中選擇地éģž", "get_help": "įˇšä¸Šæą‚åŠŠ", "get_wifiname_error": "į„Ąæŗ•å–åž— Wi-Fi åį¨ąã€‚čĢ‹įĸēčĒæ‚¨åˇ˛æŽˆäēˆåŋ…čĻįš„æŦŠé™īŧŒä¸Ļ厞逪æŽĨ臺 Wi-Fi įļ˛čˇ¯", "getting_started": "開始äŊŋᔍ", "go_back": "čŋ”回", - "go_to_folder": "čŊ‰č‡ŗčŗ‡æ–™å¤ž", + "go_to_folder": "å‰åž€čŗ‡æ–™å¤ž", "go_to_search": "前垀搜尋", + "gps": "GPS", + "gps_missing": "į„ĄGPS", "grant_permission": "授ä爿ŦŠé™", "group_albums_by": "åˆ†éĄžįž¤įĩ„įš„æ–šåŧ...", - "group_country": "按國åŽļ分įĩ„", - "group_no": "į„Ąåˆ†įĩ„", - "group_owner": "æŒ‰æ“æœ‰č€…åˆ†įĩ„", + "group_country": "æŒ‰į…§åœ‹åŽļåˆ†éĄž", + "group_no": "æ˛’æœ‰åˆ†éĄž", + "group_owner": "æŒ‰æ“æœ‰č€…åˆ†éĄž", "group_places_by": "åˆ†éĄžåœ°éģžįš„æ–šåŧ...", - "group_year": "按嚴äģŊ分įĩ„", - "haptic_feedback_switch": "å•“į”¨æŒ¯å‹•åéĨ‹", - "haptic_feedback_title": "振動反éĨ‹", - "has_quota": "配額", + "group_year": "按嚴äģŊåˆ†éĄž", + "haptic_feedback_switch": "å•Ÿį”¨éœ‡å‹•å›žéĨ‹", + "haptic_feedback_title": "震動回éĨ‹", + "has_quota": "åˇ˛č¨­åŽšé…éĄ", + "hash_asset": "雜暊åĒ’éĢ”", + "hashed_assets": "åˇ˛é›œæšŠįš„åĒ’éĢ”", + "hashing": "æ­Ŗåœ¨č¨ˆįŽ—é›œæšŠå€ŧ", "header_settings_add_header_tip": "新åĸžæ¨™é ­", - "header_settings_field_validator_msg": "č¨­åŽšä¸å¯į‚ēįŠē", + "header_settings_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": "éšąč—æ‰€æœ‰äēēį‰Š", - "hide_gallery": "隱藏į•ĢåģŠ", + "hide_gallery": "隱藏åĒ’éĢ”åēĢ", "hide_named_person": "隱藏 {name}", "hide_password": "éšąč—å¯†įĸŧ", "hide_person": "隱藏äēēį‰Š", - "hide_unnamed_people": "隱藏æœĒå‘Ŋ名äēēį‰Š", - "home_page_add_to_album_conflicts": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} å€‹é …į›Žã€‚å…ļ中 {failed} å€‹é …į›Žåˇ˛įļ“åœ¨į›¸į°ŋ中。", - "home_page_add_to_album_err_local": "æšĢ不čƒŊ將æœŦåœ°é …į›Žæ–°åĸžåˆ°į›¸į°ŋ中īŧŒį•Ĩ過", - "home_page_add_to_album_success": "åˇ˛åœ¨į›¸į°ŋ {album} 中新åĸž {added} å€‹é …į›Žã€‚", - "home_page_album_err_partner": "æšĢį„Ąæŗ•å°‡čĻĒæœ‹åĨŊå‹įš„é …į›Žæ–°åĸžåˆ°į›¸į°ŋīŧŒį•Ĩ過", - "home_page_archive_err_local": "æšĢį„Ąæŗ•å°å­˜æœŦåœ°é …į›ŽīŧŒį•Ĩ過", - "home_page_archive_err_partner": "į„Ąæŗ•å°å­˜čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", - "home_page_building_timeline": "æ­Ŗåœ¨į”Ÿæˆæ™‚é–“įˇš", - "home_page_delete_err_partner": "į„Ąæŗ•åˆĒ除čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", - "home_page_delete_remote_err_local": "é™čˇé …į›ŽåˆĒé™¤æ¨ĄåŧīŧŒį•Ĩ過æœŦåœ°é …į›Ž", + "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": "厞圍 {album} ᛏį°ŋ中新åĸž {added} 個åĒ’éĢ”ã€‚", + "home_page_album_err_partner": "æšĢ時不čƒŊį„Ąæŗ•å°‡čĻĒæœ‹åĨŊå‹įš„åĒ’éĢ”æ–°åĸžåˆ°į›¸į°ŋīŧŒåˇ˛į•Ĩ過", + "home_page_archive_err_local": "æšĢ時不čƒŊ封存æœŦ地åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", + "home_page_archive_err_partner": "į„Ąæŗ•å°å­˜čĻĒæœ‹åĨŊå‹įš„åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", + "home_page_building_timeline": "æ­Ŗåœ¨åģēįĢ‹æ™‚é–“čģ¸", + "home_page_delete_err_partner": "į„Ąæŗ•åˆĒ除čĻĒæœ‹åĨŊå‹įš„åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", + "home_page_delete_remote_err_local": "åˆĒ除遠į̝åĒ’éĢ”įš„é¸å–ä¸­åŒ…åĢæœŦ抟åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", "home_page_favorite_err_local": "æšĢ不čƒŊæ”ļ藏æœŦåœ°é …į›ŽīŧŒį•Ĩ過", "home_page_favorite_err_partner": "æšĢį„Ąæŗ•æ”ļ藏čĻĒæœ‹åĨŊå‹įš„é …į›ŽīŧŒį•Ĩ過", "home_page_first_time_notice": "åĻ‚æžœé€™æ˜¯æ‚¨įŦŦ一æŦĄäŊŋᔍæœŦፋåŧīŧŒčĢ‹įĸēäŋé¸æ“‡ä¸€å€‹čρ備äģŊįš„į›¸į°ŋīŧŒäģĨå°‡į…§į‰‡čˆ‡åŊąį‰‡åŠ å…Ĩ時間čģ¸", "home_page_locked_error_local": "į„Ąæŗ•į§ģ動æœŦ抟æĒ”æĄˆč‡ŗéŽ–åŽšįš„čŗ‡æ–™å¤žīŧŒåˇ˛į•Ĩ過", - "home_page_locked_error_partner": "į„Ąæŗ•į§ģ動äģ–äēē分äēĢįš„æĒ”æĄˆč‡ŗéŽ–åŽšįš„čŗ‡æ–™å¤žīŧŒåˇ˛į•Ĩ過", - "home_page_share_err_local": "æšĢį„Ąæŗ•é€šéŽéˆæŽĨå…ąäēĢæœŦåœ°é …į›ŽīŧŒį•Ĩ過", - "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊä¸Šå‚ŗ 30 å€‹é …į›ŽīŧŒį•Ĩ過", + "home_page_locked_error_partner": "į„Ąæŗ•į§ģ動čĻĒæœ‹åĨŊ友分äēĢįš„åĒ’éĢ”č‡ŗéŽ–åŽšįš„čŗ‡æ–™å¤žīŧŒåˇ˛į•Ĩ過", + "home_page_share_err_local": "į„Ąæŗ•é€šéŽé€Ŗįĩå…ąäēĢæœŦ地åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", + "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊä¸Šå‚ŗ 30 個åĒ’éĢ”īŧŒåˇ˛į•Ĩ過", "host": "ä¸ģ抟", - "hour": "時", + "hour": "小時", + "hours": "小時", "id": "ID", - "ignore_icloud_photos": "åŋŊį•ĨiCloudᅧቇ", - "ignore_icloud_photos_description": "å­˜å„˛åœ¨iCloudä¸­įš„į…§į‰‡ä¸æœƒä¸Šå‚ŗč‡ŗImmichäŧ翜å™¨", + "idle": "閒įŊŽ", + "ignore_icloud_photos": "åŋŊį•Ĩ iCloud ᅧቇ", + "ignore_icloud_photos_description": "å„˛å­˜åœ¨ iCloud ä¸­įš„į…§į‰‡ä¸æœƒä¸Šå‚ŗč‡ŗ Immich äŧ翜å™¨", "image": "åœ–į‰‡", "image_alt_text_date": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}拍攝æ–ŧ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 與 {person1} 一同æ–ŧ {date} 拍攝", - "image_alt_text_date_2_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 與 {person1} 和 {person2} 一同æ–ŧ {date} 拍攝", - "image_alt_text_date_3_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 與 {person1}、{person2} 和 {person3} 一同æ–ŧ {date} 拍攝", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 與 {person1}、{person2} 和å…ļäģ– {additionalCount, number} äēēæ–ŧ {date} 拍攝", - "image_alt_text_date_place": "{date}在 {country} - {city} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} æ–ŧ {city}、{country}īŧŒčˆ‡ {person1} 一同在 {date} 拍攝", - "image_alt_text_date_place_2_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 在 {city}、{country}īŧŒčˆ‡ {person1} 和 {person2} 一同æ–ŧ {date} 拍攝", - "image_alt_text_date_place_3_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 在 {city}、{country}īŧŒčˆ‡ {person1}、{person2} 和 {person3} 一同æ–ŧ {date} 拍攝", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}} 在 {city}、{country}īŧŒčˆ‡ {person1}、{person2} 和å…ļäģ– {additionalCount, number} äēēæ–ŧ {date} 拍攝", - "image_saved_successfully": "åœ–į‰‡åˇ˛å„˛å­˜", - "image_viewer_page_state_provider_download_started": "下čŧ‰å•“å‹•", + "image_alt_text_date_2_people": "{person1} 和 {person2} 一同æ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_3_people": "{person1}、{person2} 和 {person3} 一同æ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_4_or_more_people": "{person1}、{person2} 和å…ļäģ– {additionalCount, number} äēēæ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_place": "æ–ŧ {date} 在 {country} - {city} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_place_1_person": "在 {country} - {city}īŧŒčˆ‡ {person1} 一同æ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_place_2_people": "在 {country} - {city} 與 {person1} 和 {person2} 一同æ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_place_3_people": "在 {country} - {city} 與 {person1}、{person2} 和 {person3} 一同æ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_alt_text_date_place_4_or_more_people": "在 {country} - {city} 與 {person1}、{person2} 和å…ļäģ– {additionalCount, number} äēēæ–ŧ {date} æ‹æ”įš„{isVideo, select, true {åŊąį‰‡} other {åœ–į‰‡}}", + "image_saved_successfully": "åˇ˛å„˛å­˜åœ–į‰‡", + "image_viewer_page_state_provider_download_started": "下čŧ‰åˇ˛å•Ÿå‹•", "image_viewer_page_state_provider_download_success": "下čŧ‰æˆåŠŸ", - "image_viewer_page_state_provider_share_error": "å…ąäēĢå‡ē錯", + "image_viewer_page_state_provider_share_error": "分äēĢæ™‚į™ŧį”ŸéŒ¯čǤ", "immich_logo": "Immich 標čnj", "immich_web_interface": "Immich įļ˛é äģ‹éĸ", - "import_from_json": "匯å…Ĩ JSON", + "import_from_json": "åžž JSON 匯å…Ĩ", "import_path": "匯å…Ĩčˇ¯åž‘", - "in_albums": "在 {count, plural, other {# æœŦᛏį°ŋ}}中", - "in_archive": "厞封存", + "in_albums": "在 {count, plural, one {# æœŦᛏį°ŋ} other {# æœŦᛏį°ŋ}}中", + "in_archive": "在封存中", "include_archived": "包åĢ厞封存", "include_shared_albums": "包åĢå…ąäēĢᛏį°ŋ", - "include_shared_partner_assets": "包æ‹Ŧå…ąäēĢčĻĒæœ‹åĨŊ友æĒ”æĄˆ", + "include_shared_partner_assets": "包æ‹Ŧå…ąäēĢčĻĒæœ‹åĨŊå‹įš„åĒ’éĢ”", "individual_share": "個åˆĨ分äēĢ", "individual_shares": "個åˆĨ分äēĢ", "info": "čŗ‡č¨Š", "interval": { "day_at_onepm": "每夊下午 1 éģž", - "hours": "每 {hours, plural, other {{hours, number} 小時}}", + "hours": "每 {hours, plural, one {小時} other {{hours, number} 小時}}", "night_at_midnight": "每晚午夜", "night_at_twoam": "每晚凌晨 2 éģž" }, @@ -1089,7 +1188,13 @@ "invalid_date_format": "į„Ąæ•ˆįš„æ—Ĩ期æ ŧåŧ", "invite_people": "邀čĢ‹äēēå“Ą", "invite_to_album": "邀č̋臺ᛏį°ŋ", - "items_count": "{count, plural, other {# å€‹é …į›Ž}}", + "ios_debug_info_fetch_ran_at": "æŠ“å–åˇ˛æ–ŧ {dateTime} åŸˇčĄŒ", + "ios_debug_info_last_sync_at": "上æŦĄåŒæ­Ĩæ–ŧ {dateTime}", + "ios_debug_info_no_processes_queued": "į„ĄæŽ’į¨‹ä¸­įš„čƒŒæ™¯į¨‹åē", + "ios_debug_info_no_sync_yet": "尚æœĒåŸˇčĄŒäģģäŊ•čƒŒæ™¯åŒæ­Ĩäģģ務", + "ios_debug_info_processes_queued": "{count, plural, one {{count} å€‹čƒŒæ™¯į¨‹åēåˇ˛æŽ’ፋ} other {{count} å€‹čƒŒæ™¯į¨‹åēåˇ˛æŽ’ፋ}}", + "ios_debug_info_processing_ran_at": "æ–ŧ {dateTime} åŸˇčĄŒč™•į†", + "items_count": "{count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", "jobs": "äģģ務", "keep": "äŋį•™", "keep_all": "全部äŋį•™", @@ -1097,11 +1202,17 @@ "kept_this_deleted_others": "äŋį•™é€™å€‹é …į›Žä¸ĻåˆĒ除{count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "éĩᛤåŋĢæˇéĩ", "language": "čĒžč¨€", + "language_no_results_subtitle": "čŠĻ著čĒŋæ•´æ‚¨įš„æœå°‹čŠžåŊ™", + "language_no_results_title": "æœĒ扞到čĒžč¨€", + "language_search_hint": "搜尋čĒžč¨€...", "language_setting_description": "é¸æ“‡æ‚¨įš„éϖ遏čĒžč¨€", + "large_files": "大型æĒ”æĄˆ", + "last": "最垌一個", "last_seen": "æœ€åžŒä¸Šįˇš", "latest_version": "æœ€æ–°į‰ˆæœŦ", "latitude": "᎝åēĻ", "leave": "é›ĸ開", + "leave_album": "é›ĸ開ᛏį°ŋ", "lens_model": "éĄé ­åž‹č™Ÿ", "let_others_respond": "å…č¨ąäģ–äēē回čφ", "level": "į­‰į´š", @@ -1113,15 +1224,20 @@ "library_page_sort_created": "新åĸžæ—Ĩ期", "library_page_sort_last_modified": "上æŦĄäŋŽæ”š", "library_page_sort_title": "ᛏį°ŋæ¨™éĄŒ", + "licenses": "授æŦŠ", "light": "æˇē色", + "like": "å–œæ­Ą", "like_deleted": "厞åˆĒé™¤įš„æ”ļ藏", "link_motion_video": "鏈įĩå‹•æ…‹åŊąį‰‡", - "link_options": "鏈įĩé¸é …", "link_to_oauth": "逪æŽĨ OAuth", "linked_oauth_account": "厞逪æŽĨ OAuth å¸ŗč™Ÿ", "list": "åˆ—čĄ¨", "loading": "čŧ‰å…Ĩ中", "loading_search_results_failed": "čŧ‰å…Ĩ搜尋įĩæžœå¤ąæ•—", + "local": "æœŦ地", + "local_asset_cast_failed": "į„Ąæŗ•čŊ‰æ›æœĒä¸Šå‚ŗč‡ŗäŧ翜å™¨įš„é …į›Ž", + "local_assets": "æœŦåœ°é …į›Ž", + "local_media_summary": "į•ļ地åĒ’éĢ”æ‘˜čρ", "local_network": "æœŦ地įļ˛čˇ¯", "local_network_sheet_info": "į•ļäŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įļ˛čˇ¯æ™‚īŧŒæ‡‰į”¨į¨‹åŧå°‡é€éŽæ­¤įļ˛å€é€Ŗįˇšč‡ŗäŧ翜å™¨", "location_permission": "äŊįŊŽæŦŠé™", @@ -1133,8 +1249,10 @@ "location_picker_longitude_hint": "čĢ‹åœ¨æ­¤č™•čŧ¸å…Ĩæ‚¨įš„įļ“åēĻå€ŧ", "lock": "鎖厚", "locked_folder": "éŽ–åŽšįš„čŗ‡æ–™å¤ž", + "log_detail_title": "æ—ĨčĒŒčŠŗį´°čŗ‡č¨Š", "log_out": "į™ģå‡ē", "log_out_all_devices": "į™ģå‡ēæ‰€æœ‰čŖįŊŽ", + "logged_in_as": "äģĨ{user}čēĢ分į™ģå…Ĩ", "logged_out_all_devices": "厞į™ģå‡ēæ‰€æœ‰čŖįŊŽ", "logged_out_device": "厞į™ģå‡ēčŖįŊŽ", "login": "į™ģå…Ĩ", @@ -1162,13 +1280,15 @@ "login_password_changed_success": "密įĸŧ更新成功", "logout_all_device_confirmation": "您įĸē厚čρį™ģå‡ēæ‰€æœ‰čŖįŊŽå—ŽīŧŸ", "logout_this_device_confirmation": "čρį™ģå‡ē這č‡ēčŖįŊŽå—ŽīŧŸ", + "logs": "æ—Ĩčnj", "longitude": "įļ“åēĻ", "look": "æ¨Ŗč˛Œ", "loop_videos": "重播åŊąį‰‡", "loop_videos_description": "å•Ÿį”¨åžŒīŧŒåŊąį‰‡įĩæŸæœƒč‡Ē動重播。", - "main_branch_warning": "įžåœ¨äŊŋį”¨įš„æ˜¯é–‹į™ŧį‰ˆæœŦīŧ›æˆ‘們åŧˇįƒˆåģēč­°äŊŋį”¨æ­Ŗåŧį™ŧčĄŒį‰ˆīŧ", + "main_branch_warning": "æ‚¨įžåœ¨äŊŋį”¨įš„æ˜¯é–‹į™ŧį‰ˆæœŦīŧ›æˆ‘們åŧˇįƒˆæ‚¨åģēč­°äŊŋį”¨æ­Ŗåŧį™ŧčĄŒį‰ˆīŧ", "main_menu": "ä¸ģ頁éĸ", "make": "čŖŊ造商", + "manage_geolocation": "įŽĄį†äŊįŊŽ", "manage_shared_links": "įŽĄį†å…ąäēĢ逪įĩ", "manage_sharing_with_partners": "įŽĄį†čˆ‡čĻĒæœ‹åĨŊå‹įš„åˆ†äēĢ", "manage_the_app_settings": "įŽĄį†æ‡‰į”¨į¨‹åŧč¨­åޚ", @@ -1177,16 +1297,14 @@ "manage_your_devices": "įŽĄį†åˇ˛į™ģå…Ĩįš„čŖįŊŽ", "manage_your_oauth_connection": "įŽĄį†æ‚¨įš„ OAuth 逪æŽĨ", "map": "地圖", - "map_assets_in_bound": "{count} åŧĩᅧቇ", - "map_assets_in_bounds": "{count} åŧĩᅧቇ", - "map_cannot_get_user_location": "į„Ąæŗ•į˛å–į”¨æˆļäŊįŊŽ", + "map_assets_in_bounds": "{count, plural, one {# åŧĩᅧቇ} other {# åŧĩᅧቇ}}", + "map_cannot_get_user_location": "į„Ąæŗ•å–åž—äŊŋᔍ者äŊįŊŽ", "map_location_dialog_yes": "įĸē厚", "map_location_picker_page_use_location": "äŊŋį”¨æ­¤äŊįŊŽ", "map_location_service_disabled_content": "需čĻå•“į”¨åŽšäŊæœå‹™æ‰čƒŊéĄ¯į¤ēį•ļ前äŊįŊŽį›¸é—œįš„é …į›Žã€‚čĻįžåœ¨å•“į”¨å—ŽīŧŸ", "map_location_service_disabled_title": "厚äŊæœå‹™åˇ˛įρᔍ", "map_marker_for_images": "在 {city}、{country} æ‹æ”åœ–åƒįš„åœ°åœ–æ¨™č¨˜", "map_marker_with_image": "å¸ļæœ‰åœ–åƒįš„åœ°åœ–æ¨™č¨˜", - "map_no_assets_in_bounds": "æ­¤å€åŸŸä¸­æ˛’æœ‰į›¸é—œé …į›Ž", "map_no_location_permission_content": "需čρäŊįŊŽæŦŠé™æ‰čƒŊéĄ¯į¤ē與į•ļ前äŊįŊŽã€‚čĻįžåœ¨å°ąæŽˆäēˆäŊįŊŽæŦŠé™å—ŽīŧŸ", "map_no_location_permission_title": "æ˛’æœ‰äŊįŊŽæŦŠé™", "map_settings": "åœ°åœ–č¨­åŽš", @@ -1205,6 +1323,7 @@ "mark_as_read": "æ¨™č¨˜į‚ēåˇ˛čŽ€", "marked_all_as_read": "åˇ˛å…¨éƒ¨æ¨™č¨˜į‚ēåˇ˛čŽ€", "matches": "ᛏįŦĻ", + "matching_assets": "åŒšé…čŗ‡į”ĸ", "media_type": "åĒ’éĢ”éĄžåž‹", "memories": "回æ†ļ", "memories_all_caught_up": "åˇ˛å…¨éƒ¨įœ‹åŽŒ", @@ -1223,6 +1342,7 @@ "merged_people_count": "合äŊĩäē† {count, plural, one {# äŊäēēåŖĢ} other {# äŊäēēåŖĢ}}", "minimize": "最小化", "minute": "分", + "minutes": "分鐘", "missing": "排å…ĨæœĒ處ᐆ", "model": "åž‹č™Ÿ", "month": "月", @@ -1230,6 +1350,7 @@ "more": "更多", "move": "į§ģ動", "move_off_locked_folder": "į§ģå‡ēéŽ–åŽšįš„čŗ‡æ–™å¤ž", + "move_to_lock_folder_action_prompt": "{count} åˇ˛æ–°åĸžč‡ŗéŽ–åŽšįš„čŗ‡æ–™å¤žä¸­", "move_to_locked_folder": "į§ģč‡ŗéŽ–åŽšįš„čŗ‡æ–™å¤ž", "move_to_locked_folder_confirmation": "這äē›į…§į‰‡å’ŒåŊąį‰‡å°‡åžžæ‰€æœ‰į›¸į°ŋ中į§ģ除īŧŒä¸Ļåƒ…å¯åžžéŽ–åŽšįš„čŗ‡æ–™å¤žæĒĸčĻ–", "moved_to_archive": "厞封存 {count, plural, one {# å€‹é …į›Ž} other {# å€‹é …į›Ž}}", @@ -1241,6 +1362,10 @@ "my_albums": "æˆ‘įš„į›¸į°ŋ", "name": "åį¨ą", "name_or_nickname": "åį¨ąæˆ–æšąį¨ą", + "network_requirement_photos_upload": "äŊŋį”¨čĄŒå‹•įļ˛čˇ¯æĩé‡å‚™äģŊᅧቇ", + "network_requirement_videos_upload": "äŊŋį”¨čĄŒå‹•įļ˛čˇ¯æĩé‡å‚™äģŊåŊąį‰‡", + "network_requirements": "įļ˛įĩĄčĻæą‚", + "network_requirements_updated": "įļ˛įĩĄéœ€æą‚åˇ˛čŽŠæ›´īŧŒįžé‡įŊŽå‚™äģŊäŊ‡åˆ—", "networking_settings": "įļ˛čˇ¯", "networking_subtitle": "įŽĄį†äŧ翜å™¨į̝éģžč¨­åޚ", "never": "æ°¸ä¸å¤ąæ•ˆ", @@ -1250,10 +1375,11 @@ "new_person": "æ–°įš„äēēį‰Š", "new_pin_code": "新 PIN įĸŧ", "new_pin_code_subtitle": "這是您įŦŦ一æŦĄå­˜å–éŽ–åŽšįš„čŗ‡æ–™å¤žã€‚åģēįĢ‹ PIN įĸŧäģĨ厉全存取此頁éĸ", + "new_timeline": "新時間čģ¸", "new_user_created": "厞åģēįĢ‹æ–°äŊŋᔍ者", "new_version_available": "æ–°į‰ˆæœŦ厞į™ŧ布", "newest_first": "最新å„Ē先", - "next": "下一åŧĩ", + "next": "下一æ­Ĩ", "next_memory": "下一åŧĩ回æ†ļ", "no": "åĻ", "no_albums_message": "åģēį̋ᛏį°ŋäž†æ•´į†į…§į‰‡å’ŒåŊąį‰‡", @@ -1263,19 +1389,25 @@ "no_assets_message": "æŒ‰é€™čŖĄä¸Šå‚ŗæ‚¨įš„įŦŦ一åŧĩᅧቇ", "no_assets_to_show": "į„Ąé …į›Žåą•į¤ē", "no_cast_devices_found": "æ˛’æœ‰æ‰žåˆ° Google Cast čŖįŊŽ", + "no_checksum_local": "æ˛’æœ‰å¯į”¨įš„æ ĄéŠ—å’Œ-į„Ąæŗ•į˛å–æœŦåœ°čŗ‡į”ĸ", + "no_checksum_remote": "æ˛’æœ‰å¯į”¨įš„æ ĄéŠ—å’Œ-į„Ąæŗ•į˛å–é į¨‹čŗ‡į”ĸ", "no_duplicates_found": "æ˛’į™ŧįžé‡č¤‡é …į›Žã€‚", "no_exif_info_available": "æ˛’æœ‰å¯į”¨įš„ Exif čŗ‡č¨Š", "no_explore_results_message": "ä¸Šå‚ŗæ›´å¤šį…§į‰‡äģĨ刊æŽĸį´ĸ。", "no_favorites_message": "加å…Ĩæ”ļ藏īŧŒåŠ é€Ÿå°‹æ‰žåŊąåƒ", - "no_libraries_message": "åģēįĢ‹å¤–éƒ¨åœ–åēĢ來æŸĨįœ‹æ‚¨įš„į…§į‰‡å’ŒåŊąį‰‡", + "no_libraries_message": "åģēįĢ‹å¤–éƒ¨åĒ’éĢ”åēĢäģĨæŸĨįœ‹æ‚¨įš„į…§į‰‡å’ŒåŊąį‰‡", + "no_local_assets_found": "æœĒæ‰žåˆ°å…ˇæœ‰æ­¤æ ĄéŠ—å’Œįš„æœŦåœ°čŗ‡į”ĸ", "no_locked_photos_message": "éŽ–åŽšįš„čŗ‡æ–™å¤žä¸­įš„į…§į‰‡å’ŒåŊąį‰‡æœƒčĸĢ隱藏īŧŒį•ļæ‚¨į€čĻŊ或搜尋圖åēĢæ™‚ä¸æœƒéĄ¯į¤ē。", "no_name": "į„Ąå", "no_notifications": "æ˛’æœ‰é€šįŸĨ", "no_people_found": "扞不到įŦĻåˆįš„äēēį‰Š", "no_places": "æ˛’æœ‰åœ°éģž", + "no_remote_assets_found": "æœĒæ‰žåˆ°å…ˇæœ‰æ­¤æ ĄéŠ—å’Œįš„é į¨‹čŗ‡į”ĸ", "no_results": "æ˛’æœ‰įĩæžœ", "no_results_description": "čŠĻčŠĻåŒįžŠčŠžæˆ–æ›´é€šį”¨įš„é—œéĩ字吧", "no_shared_albums_message": "åģēį̋ᛏį°ŋ分äēĢį…§į‰‡å’ŒåŊąį‰‡", + "no_uploads_in_progress": "æ˛’æœ‰æ­Ŗåœ¨ä¸Šå‚ŗįš„é …į›Ž", + "not_available": "ä¸éŠį”¨", "not_in_any_album": "不在äģģäŊ•ᛏį°ŋ中", "not_selected": "æœĒ選擇", "note_apply_storage_label_to_previously_uploaded assets": "*č¨ģīŧšåŸˇčĄŒåĨ—į”¨å„˛å­˜æ¨™įą¤å‰å…ˆä¸Šå‚ŗé …į›Ž", @@ -1291,13 +1423,16 @@ "oauth": "OAuth", "official_immich_resources": "厘斚 Immich čŗ‡æē", "offline": "é›ĸ᎚", + "offset": "į§ģ動", "ok": "įĸē厚", "oldest_first": "į”ąčˆŠč‡ŗæ–°", "on_this_device": "åœ¨æ­¤čŖįŊŽ", "onboarding": "å…Ĩ門指南", - "onboarding_locale_description": "選擇äŊ æƒŗčĻéĄ¯į¤ēįš„čĒžįŗģã€‚č¨­åŽšåŽŒæˆäš‹åžŒį”Ÿæ•ˆã€‚", - "onboarding_privacy_description": "äģĨ下īŧˆå¯é¸īŧ‰åŠŸčƒŊäžčŗ´å¤–éƒ¨æœå‹™īŧŒå¯éš¨æ™‚åœ¨įŽĄį†č¨­åŽšä¸­åœį”¨ã€‚", - "onboarding_theme_description": "åšĢå¯Ļ例選色åŊŠä¸ģéĄŒã€‚äš‹åžŒäšŸå¯äģĨåœ¨č¨­åŽšä¸­æ›´æ”šã€‚", + "onboarding_locale_description": "é¸æ“‡æ‚¨æƒŗčĻéĄ¯į¤ēįš„čĒžč¨€ã€‚č¨­åŽšåŽŒæˆäš‹åžŒį”Ÿæ•ˆã€‚", + "onboarding_privacy_description": "äģĨ下īŧˆå¯é¸īŧ‰åŠŸčƒŊäžčŗ´å¤–éƒ¨æœå‹™īŧŒå¯éš¨æ™‚åœ¨č¨­åŽšä¸­åœį”¨ã€‚", + "onboarding_server_welcome_description": "čŽ“æˆ‘å€‘į‚ēæ‚¨įš„įŗģįĩąé€˛čĄŒä¸€äē›åŸēæœŦč¨­åŽšã€‚", + "onboarding_theme_description": "åšĢå¯Ļ例選色åŊŠä¸ģéĄŒã€‚äš‹åžŒäšŸå¯äģĨåœ¨č¨­åŽšä¸­čŽŠæ›´ã€‚", + "onboarding_user_welcome_description": "čŽ“æˆ‘å€‘é–‹å§‹å§!", "onboarding_welcome_user": "æ­ĄčŋŽīŧŒ{user}", "online": "圍᎚", "only_favorites": "åƒ…éĄ¯į¤ēåˇąæ”ļ藏", @@ -1307,10 +1442,13 @@ "open_the_search_filters": "é–‹å•Ÿæœå°‹į¯Šé¸å™¨", "options": "選項", "or": "或", + "organize_into_albums": "æ•´į†æˆį›¸å†Œ", + "organize_into_albums_description": "äŊŋᔍį•ļ前同æ­Ĩč¨­åŽšå°‡įžæœ‰į…§į‰‡æ”žå…Ĩį›¸å†Œ", "organize_your_library": "æ•´į†æ‚¨įš„åœ–åēĢ", "original": "原圖", "other": "å…ļäģ–", "other_devices": "å…ļåŽƒčŖįŊŽ", + "other_entities": "å…ļäģ–é …į›Ž", "other_variables": "å…ļäģ–čŽŠæ•¸", "owned": "æˆ‘įš„", "owner": "æ‰€æœ‰č€…", @@ -1345,7 +1483,7 @@ "pending": "åž…č™•į†", "people": "äēēį‰Š", "people_edits_count": "ᎍčŧ¯äē† {count, plural, one {# äŊäēēåŖĢ} other {# äŊäēēåŖĢ}}", - "people_feature_description": "äģĨäēēį‰Šåˆ†įĩ„į€čĻŊį…§į‰‡å’ŒåŊąį‰‡", + "people_feature_description": "äģĨäēēį‰Šåˆ†éĄžį€čĻŊį…§į‰‡å’ŒåŊąį‰‡", "people_sidebar_description": "在側邊æŦ„éĄ¯į¤ē「äēēį‰Šã€įš„é€Ŗįĩ", "permanent_deletion_warning": "永䚅åˆĒ除č­Ļ告", "permanent_deletion_warning_setting_description": "在永䚅åˆĒ除æĒ”æĄˆæ™‚éĄ¯į¤ēč­Ļ告", @@ -1355,7 +1493,7 @@ "permanently_deleted_asset": "永䚅åˆĒé™¤įš„æĒ”æĄˆ", "permanently_deleted_assets_count": "永䚅åˆĒé™¤įš„ {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", "permission": "æŦŠé™", - "permission_empty": "äŊ æ˛’有æŦŠé™", + "permission_empty": "æŦŠé™ä¸čƒŊį‚ēįŠē", "permission_onboarding_back": "čŋ”回", "permission_onboarding_continue_anyway": "įĸēčĒįšŧįēŒ", "permission_onboarding_get_started": "開始äŊŋᔍ", @@ -1365,6 +1503,9 @@ "permission_onboarding_permission_limited": "åĻ‚čρįšŧįēŒīŧŒčĢ‹å…č¨ą Immich 備äģŊå’ŒįŽĄį†æ‚¨įš„į›¸į°ŋæ”ļ藏īŧŒåœ¨č¨­åŽšä¸­æŽˆäēˆį›¸į‰‡å’ŒåŊąį‰‡æŦŠé™ã€‚", "permission_onboarding_request": "Immich 需čρæŦŠé™æ‰čƒŊæŸĨįœ‹æ‚¨įš„į›¸į‰‡å’ŒįŸ­į‰‡ã€‚", "person": "äēēį‰Š", + "person_age_months": "{months, plural, one {# 個月} other {# 個月}}前", + "person_age_year_months": "1 åš´ {months, plural, one {# 個月} other {# 個月}}前", + "person_age_years": "{years, plural, other {# æ­˛}}", "person_birthdate": "į”Ÿæ–ŧ {date}", "person_hidden": "{name}{hidden, select, true {īŧˆéšąč—īŧ‰} other {}}", "photo_shared_all_users": "įœ‹äž†æ‚¨čˆ‡æ‰€æœ‰äŊŋį”¨č€…åˆ†äēĢäē†į…§į‰‡īŧŒæˆ–æ˛’æœ‰å…ļäģ–äŊŋį”¨č€…å¯äž›åˆ†äēĢ。", @@ -1388,11 +1529,15 @@ "port": "åŸ åŖ", "preferences_settings_subtitle": "įŽĄį†æ‡‰į”¨į¨‹åŧååĨŊč¨­åŽš", "preferences_settings_title": "偏åĨŊč¨­åŽš", + "preparing": "æē–å‚™", "preset": "預設", "preview": "預čĻŊ", "previous": "上一åŧĩ", "previous_memory": "上一åŧĩ回æ†ļ", - "previous_or_next_photo": "上、下一åŧĩᅧቇ", + "previous_or_next_day": "前一夊/垌一夊", + "previous_or_next_month": "上一個月/下一個月", + "previous_or_next_photo": "上一åŧĩᅧቇ/下一åŧĩᅧቇ", + "previous_or_next_year": "上一嚴/下一嚴", "primary": "éĻ–čρ", "privacy": "éšąį§", "profile": "叺æˆļč¨­åŽš", @@ -1401,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "åŽĸæˆļįĢ¯æœ‰å°į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_server_up_to_date": "åŽĸæˆļįĢ¯å’Œæœå‹™į̝éƒŊæ˜¯æœ€æ–°įš„", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "å”¯čŽ€æ¨Ąåŧåˇ˛é–‹å•Ÿã€‚čĢ‹é•ˇæŒ‰äŊŋį”¨č€…é ­åƒåœ–į¤ēäģĨ退å‡ē。", "profile_drawer_server_out_of_date_major": "服務įĢ¯æœ‰å¤§į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_server_out_of_date_minor": "服務įĢ¯æœ‰å°į‰ˆæœŦå‡į´šīŧŒčĢ‹į›ĄåŋĢå‡į´šč‡ŗæœ€æ–°į‰ˆã€‚", "profile_image_of_user": "{user} įš„å€‹äēēčŗ‡æ–™åœ–į‰‡", @@ -1439,12 +1585,17 @@ "purchase_server_description_2": "æ“č­ˇč€…į‹€æ…‹", "purchase_server_title": "äŧ翜å™¨", "purchase_settings_server_activated": "äŧ翜å™¨į”ĸå“é‡‘é‘°æ˜¯į”ąįŽĄį†č€…įŽĄį†įš„", + "query_asset_id": "æŸģčŠĸčŗ‡į”ĸID", + "queue_status": "處ᐆ䏭 {count}/{total}", "rating": "čŠ•æ˜Ÿ", "rating_clear": "æ¸…é™¤čŠ•į­‰", "rating_count": "{count, plural, other {# 星}}", "rating_description": "åœ¨čŗ‡č¨Šéĸæŋä¸­éĄ¯į¤ē EXIF čŠ•į­‰", "reaction_options": "反應選項", "read_changelog": "閱čĻŊčŽŠæ›´æ—Ĩčnj", + "readonly_mode_disabled": "å”¯čŽ€æ¨Ąåŧåˇ˛é—œé–‰", + "readonly_mode_enabled": "å”¯čŽ€æ¨Ąåŧåˇ˛é–‹å•Ÿ", + "ready_for_upload": "厞æē–å‚™åĨŊä¸Šå‚ŗ", "reassign": "重新指厚", "reassigned_assets_to_existing_person": "厞將 {count, plural, other {# 個æĒ”æĄˆ}}重新指厚įĩĻ{name, select, null {įžæœ‰įš„äēē} other {{name}}}", "reassigned_assets_to_new_person": "厞將 {count, plural, other {# 個æĒ”æĄˆ}}重新指厚įĩĻ一äŊæ–°äēēį‰Š", @@ -1459,14 +1610,17 @@ "refresh": "é‡æ–°æ•´į†", "refresh_encoded_videos": "é‡æ–°æ•´į†åˇ˛įˇ¨įĸŧįš„åŊąį‰‡", "refresh_faces": "重整éĸéƒ¨čŗ‡æ–™", - "refresh_metadata": "é‡æ–°æ•´į†čŠŗį´°čŗ‡æ–™", + "refresh_metadata": "é‡æ–°æ•´į†ä¸­įšŧčŗ‡æ–™", "refresh_thumbnails": "é‡æ–°æ•´į†į¸Žåœ–", "refreshed": "é‡æ–°æ•´į†åŽŒį•ĸ", "refreshes_every_file": "é‡æ–°čŽ€å–įžæœ‰įš„æ‰€æœ‰æĒ”æĄˆå’Œæ–°æĒ”æĄˆ", "refreshing_encoded_video": "æ­Ŗåœ¨é‡æ–°æ•´į†åˇ˛įˇ¨įĸŧįš„åŊąį‰‡", "refreshing_faces": "重整éĸéƒ¨čŗ‡æ–™ä¸­", - "refreshing_metadata": "æ­Ŗåœ¨é‡æ–°æ•´į†čŠŗį´°čŗ‡æ–™", + "refreshing_metadata": "æ­Ŗåœ¨é‡æ–°æ•´į†ä¸­įšŧčŗ‡æ–™", "regenerating_thumbnails": "重新į”ĸį”Ÿį¸Žåœ–ä¸­", + "remote": "遠į̝", + "remote_assets": "遠įĢ¯é …į›Ž", + "remote_media_summary": "遠ፋåĒ’éĢ”æ‘˜čρ", "remove": "į§ģ除", "remove_assets_album_confirmation": "įĸē厚čĻåžžį›¸į°ŋ中į§ģ除 {count, plural, other {# 個æĒ”æĄˆ}}嗎īŧŸ", "remove_assets_shared_link_confirmation": "įĸē厚åˆĒé™¤å…ąäēĢ逪įĩä¸­{count, plural, other {# å€‹é …į›Ž}}嗎īŧŸ", @@ -1474,7 +1628,9 @@ "remove_custom_date_range": "į§ģ除č‡Ē訂æ—ĨæœŸį¯„åœ", "remove_deleted_assets": "į§ģ除é›ĸ᎚æĒ”æĄˆ", "remove_from_album": "åžžį›¸į°ŋ中į§ģ除", + "remove_from_album_action_prompt": "åˇ˛åžžį›¸į°ŋ中į§ģ除äē† {count} å€‹é …į›Ž", "remove_from_favorites": "åžžæ”ļ藏中į§ģ除", + "remove_from_lock_folder_action_prompt": "åˇ˛åžžéŽ–åŽšįš„čŗ‡æ–™å¤žä¸­į§ģ除äē† {count} å€‹é …į›Ž", "remove_from_locked_folder": "åžžéŽ–åŽšįš„čŗ‡æ–™å¤žä¸­į§ģ除", "remove_from_locked_folder_confirmation": "您įĸē厚čρ將這äē›į…§į‰‡å’ŒåŊąį‰‡į§ģå‡ēéŽ–åŽšįš„čŗ‡æ–™å¤žå—ŽīŧŸé€™äē›å…§åŽšå°‡æœƒéĄ¯į¤ēåœ¨æ‚¨įš„åœ–åēĢ中。", "remove_from_shared_link": "åžžå…ąäēĢ逪įĩä¸­į§ģ除", @@ -1489,38 +1645,48 @@ "removed_from_favorites_count": "厞į§ģ除æ”ļč—įš„ {count, plural, other {# å€‹é …į›Ž}}", "removed_memory": "厞į§ģ除記æ†ļ", "removed_photo_from_memory": "åˇ˛åžžč¨˜æ†ļ中į§ģ除ᅧቇ", - "removed_tagged_assets": "厞į§ģ除 {count, plural, other {# 個æĒ”æĄˆ}}įš„æ¨™č¨˜", + "removed_tagged_assets": "厞į§ģ除 {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}įš„æ¨™įą¤", "rename": "攚名", "repair": "įŗžæ­Ŗ", "repair_no_results_message": "æœĒčĸĢčŋŊčš¤åŠæœĒč™•į†įš„æĒ”æĄˆæœƒéĄ¯į¤ēåœ¨é€™čŖĄ", "replace_with_upload": "į”¨ä¸Šå‚ŗįš„æĒ”æĄˆå–äģŖ", "repository": "å„˛å­˜åēĢ", "require_password": "需čρ坆įĸŧ", - "require_user_to_change_password_on_first_login": "čĻæą‚äŊŋį”¨č€…åœ¨éĻ–æŦĄį™ģå…Ĩ時更攚密įĸŧ", + "require_user_to_change_password_on_first_login": "čĻæą‚äŊŋį”¨č€…åœ¨éĻ–æŦĄį™ģå…Ĩæ™‚čŽŠæ›´å¯†įĸŧ", "rescan": "重新掃描", "reset": "重設", "reset_password": "é‡č¨­å¯†įĸŧ", "reset_people_visibility": "重設äēēį‰Šå¯čĻ‹æ€§", "reset_pin_code": "重įŊŽ PIN įĸŧ", + "reset_pin_code_description": "č‹Ĩåŋ˜č¨˜äē†PIN įĸŧīŧŒé–Ŗä¸‹å¯čĻæą‚įŗģįĩąäŧ翜å™¨įŽĄį†å“Ąį‚ē您重įŊŽ", + "reset_pin_code_success": "é–Ŗä¸‹åˇ˛æˆåŠŸé‡č¨­PINįĸŧ", + "reset_pin_code_with_password": "您可隨時äŊŋį”¨æ‚¨įš„å¯†įĸŧ來重設 PIN įĸŧ", + "reset_sqlite": "重設 SQLite čŗ‡æ–™åēĢ", + "reset_sqlite_confirmation": "įĸē厚čĻé‡č¨­ SQLite čŗ‡æ–™åēĢ嗎īŧŸé–Ŗä¸‹éœ€į™ģå‡ēä¸Ļ重新į™ģå…Ĩ才čƒŊ重新同æ­Ĩčŗ‡æ–™", + "reset_sqlite_success": "åˇ˛æˆåŠŸé‡č¨­ SQLite čŗ‡æ–™åēĢ", "reset_to_default": "é‡č¨­å›žé č¨­", "resolve_duplicates": "č§Ŗæąē重複項", "resolved_all_duplicates": "厞觪æąēæ‰€æœ‰é‡č¤‡é …į›Ž", "restore": "還原", "restore_all": "全部還原", + "restore_trash_action_prompt": "åˇ˛åžžåžƒåœžæĄļ垊原äē† {count} å€‹é …į›Ž", "restore_user": "還原äŊŋᔍ者", "restored_asset": "åˇ˛é‚„åŽŸæĒ”æĄˆ", "resume": "įšŧįēŒ", + "resume_paused_jobs": "æĸ垊 {count, plural, one {# æšĢåœįš„äģģ務} other {# æšĢåœįš„äģģ務}}", "retry_upload": "é‡æ–°ä¸Šå‚ŗ", - "review_duplicates": "æŸĨæ ¸é‡č¤‡é …į›Ž", + "review_duplicates": "æĒĸčĻ–é‡č¤‡é …į›Ž", + "review_large_files": "æĒĸčĻ–å¤§åž‹æ–‡äģļ", "role": "角色", "role_editor": "ᎍčŧ¯č€…", "role_viewer": "æĒĸčϖ者", + "running": "運行中", "save": "å„˛å­˜", "save_to_gallery": "å„˛å­˜åˆ°åœ–åēĢ", - "saved_api_key": "åˇ˛å„˛å­˜įš„ API 密鑰", + "saved_api_key": "åˇ˛å„˛å­˜ API 金鑰", "saved_profile": "åˇ˛å„˛å­˜å€‹äēēčŗ‡æ–™", "saved_settings": "åˇ˛å„˛å­˜č¨­åŽš", - "say_something": "čĒĒčĒĒäŊ įš„æƒŗæŗ•吧", + "say_something": "čĒĒčĒĒæ‚¨įš„æƒŗæŗ•吧", "scaffold_body_error_occurred": "į™ŧį”ŸéŒ¯čǤ", "scan_all_libraries": "掃描所有圖åēĢ", "scan_library": "掃描", @@ -1567,14 +1733,14 @@ "search_page_things": "äē‹į‰Š", "search_page_view_all_button": "æŸĨįœ‹å…¨éƒ¨", "search_page_your_activity": "æ‚¨įš„æ´ģ動", - "search_page_your_map": "äŊ įš„čļŗčˇĄ", + "search_page_your_map": "æ‚¨įš„čļŗčˇĄ", "search_people": "搜尋äēēį‰Š", "search_places": "搜尋地éģž", "search_rating": "æŒ‰čŠ•åˆ†æœå°‹...", "search_result_page_new_search_hint": "æœå°‹æ–°įš„", "search_settings": "æœå°‹č¨­åŽš", "search_state": "搜尋地區â€Ļ", - "search_suggestion_list_smart_search_hint_1": "æ™ē慧搜尋功čƒŊé č¨­åˇ˛å•Ÿį”¨īŧŒåĻ‚čĻæœå°‹čŠŽé‡‹čŗ‡æ–™īŧŒčĢ‹äŊŋᔍčĒžæŗ• ", + "search_suggestion_list_smart_search_hint_1": "æ™ē慧搜尋功čƒŊé č¨­åˇ˛å•Ÿį”¨īŧŒåĻ‚čĻæœå°‹ä¸­įšŧčŗ‡æ–™īŧŒčĢ‹äŊŋᔍčĒžæŗ• ", "search_suggestion_list_smart_search_hint_2": "m:æ‚¨įš„æœå°‹é—œéĩ詞", "search_tags": "æœå°‹æ¨™įą¤...", "search_timezone": "搜尋時區â€Ļ", @@ -1587,7 +1753,8 @@ "select_album_cover": "é¸æ“‡į›¸į°ŋ封éĸ", "select_all": "選擇全部", "select_all_duplicates": "äŋį•™æ‰€æœ‰é‡č¤‡é …", - "select_avatar_color": "選擇åŊĸ象顏色", + "select_all_in": "選擇在 {group} ä¸­įš„æ‰€æœ‰é …į›Ž", + "select_avatar_color": "選擇個äēēčŗ‡æ–™åœ–į‰‡éĄč‰˛", "select_face": "é¸æ“‡č‡‰å­”", "select_featured_photo": "é¸æ“‡į‰šč‰˛į…§į‰‡", "select_from_computer": "åžžé›ģč…Ļ中選取", @@ -1600,6 +1767,7 @@ "select_user_for_sharing_page_err_album": "新åĸžį›¸į°ŋå¤ąæ•—", "selected": "åˇ˛é¸æ“‡", "selected_count": "{count, plural, other {選äē† # 項}}", + "selected_gps_coordinates": "é¸åŽšįš„GPSåē§æ¨™", "send_message": "å‚ŗč¨Šæ¯", "send_welcome_email": "å‚ŗé€æ­ĄčŋŽé›ģ子éƒĩäģļ", "server_endpoint": "äŧ翜å™¨į̝éģž", @@ -1607,6 +1775,7 @@ "server_info_box_server_url": "äŧ翜å™¨åœ°å€", "server_offline": "äŧ翜å™¨åˇ˛é›ĸ᎚", "server_online": "äŧ翜å™¨åˇ˛ä¸Šįˇš", + "server_privacy": "äŧ翜å™¨éšąį§", "server_stats": "äŧ翜å™¨įĩąč¨ˆ", "server_version": "į›Žå‰į‰ˆæœŦ", "set": "č¨­åŽš", @@ -1616,6 +1785,7 @@ "set_date_of_birth": "č¨­åŽšå‡ēį”Ÿæ—Ĩ期", "set_profile_picture": "設įŊŽå€‹äēēčŗ‡æ–™åœ–į‰‡", "set_slideshow_to_fullscreen": "äģĨ全čžĸ嚕攞映åšģį‡ˆį‰‡", + "set_stack_primary_asset": "設įŊŽå †į–Šįš„éĻ–čĻé …į›Ž", "setting_image_viewer_help": "čŠŗį´°čŗ‡č¨ŠæŸĨįœ‹å™¨éĻ–å…ˆčŧ‰å…Ĩå°į¸Žåœ–īŧŒį„ļ垌čŧ‰å…Ĩä¸­į­‰å¤§å°įš„é čĻŊ圖īŧˆč‹Ĩ啓ᔍīŧ‰īŧŒæœ€åžŒčŧ‰å…ĨåŽŸå§‹åœ–į‰‡ã€‚", "setting_image_viewer_original_subtitle": "啓ᔍäģĨčŧ‰å…Ĩ原圖īŧŒįρᔍäģĨ減少數據äŊŋį”¨é‡īŧˆåŒ…æ‹Ŧįļ˛įĩĄå’ŒčŖįŊŽįˇŠå­˜īŧ‰ã€‚", "setting_image_viewer_original_title": "čŧ‰å…Ĩ原圖", @@ -1643,6 +1813,7 @@ "settings_saved": "č¨­åŽšåˇ˛å„˛å­˜", "setup_pin_code": "č¨­åŽš PIN įĸŧ", "share": "分äēĢ", + "share_action_prompt": "åˇ˛åˆ†äēĢäē† {count} å€‹é …į›Ž", "share_add_photos": "新åĸžé …į›Ž", "share_assets_selected": "{count} åˇ˛é¸æ“‡", "share_dialog_preparing": "æ­Ŗåœ¨æē–å‚™...", @@ -1657,13 +1828,14 @@ "shared_album_section_people_title": "äēēį‰Š", "shared_by": "å…ąäēĢč‡Ē", "shared_by_user": "į”ą {user} 分äēĢ", - "shared_by_you": "į”ąäŊ åˆ†äēĢ", + "shared_by_you": "į”ąæ‚¨åˆ†äēĢ", "shared_from_partner": "來č‡Ē {partner} įš„į…§į‰‡", "shared_intent_upload_button_progress_text": "{current} / {total} åˇ˛ä¸Šå‚ŗ", "shared_link_app_bar_title": "å…ąäēĢ鏈æŽĨ", "shared_link_clipboard_copied_massage": "複čŖŊ到å‰Ēč˛ŧæŋ", "shared_link_clipboard_text": "逪įĩīŧš {link}\n密įĸŧīŧš {password}", - "shared_link_create_error": "新åĸžå…ąäēĢ鏈æŽĨå‡ē錯", + "shared_link_create_error": "新åĸžå…ąäēĢ逪įĩæ™‚į™ŧį”ŸéŒ¯čǤ", + "shared_link_custom_url_description": "äŊŋᔍč‡ĒåŽšįžŠįš„ URL 逪įĩ", "shared_link_edit_description_hint": "ᎍčŧ¯å…ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", "shared_link_edit_expire_after_option_days": "{count} 夊", @@ -1674,8 +1846,8 @@ "shared_link_edit_expire_after_option_months": "{count} 個月", "shared_link_edit_expire_after_option_year": "{count} åš´", "shared_link_edit_password_hint": "čŧ¸å…Ĩå…ąäēĢ密įĸŧ", - "shared_link_edit_submit_button": "更新鏈æŽĨ", - "shared_link_error_server_url_fetch": "į„Ąæŗ•į˛å–äŧ翜å™¨åœ°å€", + "shared_link_edit_submit_button": "æ›´æ–°é€Ŗįĩ", + "shared_link_error_server_url_fetch": "į„Ąæŗ•å–åž—äŧ翜å™¨åœ°å€", "shared_link_expires_day": "{count} 夊垌過期", "shared_link_expires_days": "{count} 夊垌過期", "shared_link_expires_hour": "{count} 小時垌過期", @@ -1689,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "įŽĄį†å…ąäēĢ鏈æŽĨ", "shared_link_options": "å…ąäēĢ逪įĩé¸é …", + "shared_link_password_description": "čĻæą‚åœ¨č¨Ēå•æ­¤é€Ŗįĩæ™‚提䞛密įĸŧ", "shared_links": "å…ąäēĢ逪įĩ", "shared_links_description": "äģĨ逪įĩåˆ†äēĢį…§į‰‡å’ŒåŊąį‰‡", "shared_photos_and_videos_count": "{assetCount, plural, other {åˇ˛åˆ†äēĢ # åŧĩį…§į‰‡åŠåŊąį‰‡ã€‚}}", @@ -1713,7 +1886,7 @@ "show_in_timeline": "在時間čģ¸ä¸­éĄ¯į¤ē", "show_in_timeline_setting_description": "åœ¨æ‚¨įš„æ™‚é–“čģ¸ä¸­éĄ¯į¤ē這äŊäŊŋį”¨č€…įš„į…§į‰‡å’ŒåŊąį‰‡", "show_keyboard_shortcuts": "éĄ¯į¤ēéĩᛤåŋĢæˇéĩ", - "show_metadata": "éĄ¯į¤ēčŠŗį´°čŗ‡æ–™", + "show_metadata": "éĄ¯į¤ē中įšŧčŗ‡æ–™", "show_or_hide_info": "éĄ¯į¤ēæˆ–éšąč—čŗ‡č¨Š", "show_password": "éĄ¯į¤ē密įĸŧ", "show_person_options": "éĄ¯į¤ēäēēį‰Šé¸é …", @@ -1723,6 +1896,7 @@ "show_slideshow_transition": "éĄ¯į¤ēåšģį‡ˆį‰‡čŊ‰å ´", "show_supporter_badge": "æ“č­ˇč€…åžŊįĢ ", "show_supporter_badge_description": "éĄ¯į¤ēæ“č­ˇč€…åžŊįĢ ", + "show_text_search_menu": "éĄ¯į¤ēæ–‡å­—č’į´ĸ選喎", "shuffle": "隨抟排åē", "sidebar": "側邊æŦ„", "sidebar_display_description": "在側邊æŦ„ä¸­éĄ¯į¤ē鏈įĩ", @@ -1738,12 +1912,14 @@ "sort_created": "åģēįĢ‹æ—Ĩ期", "sort_items": "é …į›Žæ•¸é‡", "sort_modified": "æ—ĨæœŸåˇ˛äŋŽæ”š", + "sort_newest": "æœ€æ–°įš„į›¸į‰‡", "sort_oldest": "æœ€čˆŠįš„į…§į‰‡", "sort_people_by_similarity": "æŒ‰į›¸äŧŧåēϿޒåēäēēå“Ą", "sort_recent": "æœ€æ–°įš„į…§į‰‡", "sort_title": "æ¨™éĄŒ", "source": "來æē", "stack": "堆叠", + "stack_action_prompt": "åˇ˛å †į–Šäē†{count} å€‹é …į›Ž", "stack_duplicates": "å †į–Šé‡č¤‡é …į›Ž", "stack_select_one_photo": "į‚ēå †į–Šé¸ä¸€åŧĩä¸ģčρᅧቇ", "stack_selected_photos": "å †į–Šæ‰€é¸įš„į…§į‰‡", @@ -1751,17 +1927,20 @@ "stacktrace": "å †į–ŠčŋŊ蚤", "start": "開始", "start_date": "開始æ—Ĩ期", + "start_date_before_end_date": "開始æ—Ĩ期åŋ…須旊æ–ŧįĩæŸæ—Ĩ期", "state": "地區", "status": "į‹€æ…‹", + "stop_casting": "停æ­ĸcasting", "stop_motion_photo": "停æ­ĸå‹•æ…‹į…§į‰‡", "stop_photo_sharing": "čρ停æ­ĸ分äēĢæ‚¨įš„į…§į‰‡å—ŽīŧŸ", - "stop_photo_sharing_description": "{partner} å°‡į„Ąæŗ•å†č¨Ē問äŊ įš„į…§į‰‡ã€‚", - "stop_sharing_photos_with_user": "停æ­ĸčˆ‡æ­¤į”¨æˆļå…ąäēĢäŊ įš„ᅧቇ", + "stop_photo_sharing_description": "{partner} å°‡į„Ąæŗ•å†č¨Ēå•æ‚¨įš„į…§į‰‡ã€‚", + "stop_sharing_photos_with_user": "停æ­ĸčˆ‡æ­¤į”¨æˆļå…ąäēĢæ‚¨įš„ᅧቇ", "storage": "å„˛å­˜įŠē間", "storage_label": "å„˛å­˜æ¨™įą¤", "storage_quota": "å„˛å­˜įŠē間", "storage_usage": "ᔍäē† {used} / å…ą {available}", "submit": "提äē¤", + "success": "成功", "suggestions": "åģēč­°", "sunrise_on_the_beach": "æ—Ĩå‡ēįš„æĩˇį˜", "support": "支援", @@ -1771,16 +1950,21 @@ "sync": "同æ­Ĩ", "sync_albums": "同æ­Ĩᛏį°ŋ", "sync_albums_manual_subtitle": "å°‡æ‰€æœ‰ä¸Šå‚ŗįš„įŸ­į‰‡å’Œį…§į‰‡åŒæ­Ĩåˆ°é¸åŽšįš„å‚™äģŊᛏį°ŋ", + "sync_local": "同æ­ĨæœŦ抟", + "sync_remote": "同æ­Ĩ遠į̝", + "sync_status": "同æ­ĨįŠļ态", + "sync_status_subtitle": "æŸĨįœ‹å’ŒįŽĄį†åŒæ­Ĩįŗģįĩą", "sync_upload_album_setting_subtitle": "新åĸžį…§į‰‡å’ŒįŸ­į‰‡ä¸Ļä¸Šå‚ŗåˆ° Immich ä¸Šįš„é¸åŽšį›¸į°ŋ中", "tag": "æ¨™įą¤", "tag_assets": "æ¨™č¨˜æĒ”æĄˆ", - "tag_created": "厞åģēįĢ‹æ¨™č¨˜īŧš{tag}", - "tag_feature_description": "äģĨ邏čŧ¯æ¨™č¨˜čĻæ—¨åˆ†įĩ„į€čĻŊį…§į‰‡å’ŒåŊąį‰‡", - "tag_not_found_question": "æ‰žä¸åˆ°æ¨™č¨˜īŧŸåģēįĢ‹æ–°æ¨™č¨˜å§ã€‚", - "tag_people": "æ¨™č¨˜äēēį‰Š", - "tag_updated": "åˇ˛æ›´æ–°æ¨™č¨˜īŧš{tag}", - "tagged_assets": "åˇ˛æ¨™č¨˜ {count, plural, other {# 個æĒ”æĄˆ}}", + "tag_created": "厞åģēįĢ‹æ¨™įą¤īŧš{tag}", + "tag_feature_description": "äģĨ邏čŧ¯æ¨™č¨˜čĻæ—¨åˆ†éĄžį€čĻŊį…§į‰‡å’ŒåŊąį‰‡", + "tag_not_found_question": "æ‰žä¸åˆ°æ¨™įą¤īŧŸåģēįĢ‹æ–°æ¨™įą¤ã€‚", + "tag_people": "æ¨™įą¤äēēį‰Š", + "tag_updated": "åˇ˛æ›´æ–°æ¨™įą¤īŧš{tag}", + "tagged_assets": "åˇ˛æ¨™įą¤ {count, plural, one {# 個æĒ”æĄˆ} other {# 個æĒ”æĄˆ}}", "tags": "æ¨™įą¤", + "tap_to_run_job": "éģžæ“ŠäģĨ進行äŊœæĨ­", "template": "æ¨Ąæŋ", "theme": "ä¸ģ題", "theme_selection": "ä¸ģ題選項", @@ -1796,7 +1980,7 @@ "theme_setting_system_primary_color_title": "äŊŋᔍįŗģįĩąéĄč‰˛", "theme_setting_system_theme_switch": "č‡Ē動īŧˆčˇŸéš¨įŗģįĩąč¨­åޚīŧ‰", "theme_setting_theme_subtitle": "選擇åĨ—ᔍä¸ģ題", - "theme_setting_three_stage_loading_subtitle": "三æŽĩåŧčŧ‰å…Ĩ可čƒŊ會提升čŧ‰å…Ĩ性čƒŊīŧŒäŊ†å¯čƒŊæœƒå°Žč‡´æ›´éĢ˜įš„įļ˛įĩĄč˛ čŧ‰", + "theme_setting_three_stage_loading_subtitle": "三æŽĩåŧčŧ‰å…Ĩ可čƒŊ提升čŧ‰å…Ĩ效čƒŊīŧŒäŊ†æœƒå¤§åš…åĸžåŠ įļ˛čˇ¯č˛ čŧ‰", "theme_setting_three_stage_loading_title": "啓ᔍ䏉æŽĩåŧčŧ‰å…Ĩ", "they_will_be_merged_together": "厃們將會čĸĢ合äŊĩ在一čĩˇ", "third_party_resources": "įŦŦä¸‰æ–ščŗ‡æē", @@ -1804,15 +1988,18 @@ "timeline": "時間čģ¸", "timezone": "時區", "to_archive": "封存", - "to_change_password": "更攚密įĸŧ", + "to_change_password": "čŽŠæ›´å¯†įĸŧ", "to_favorite": "æ”ļ藏", "to_login": "į™ģå…Ĩ", + "to_multi_select": "é€˛čĄŒå¤šé¸", "to_parent": "åˆ°ä¸Šä¸€į´š", + "to_select": "选拊", "to_trash": "垃圞æĄļ", "toggle_settings": "åˆ‡æ›č¨­åŽš", "total": "įĩąč¨ˆ", "total_usage": "į¸Ŋį”¨é‡", "trash": "垃圞æĄļ", + "trash_action_prompt": "{count} 個丟å…Ĩ垃圞æĄļ", "trash_all": "全部丟掉", "trash_count": "丟掉 {count, number} 個æĒ”æĄˆ", "trash_delete_asset": "將æĒ”æĄˆä¸Ÿé€˛åžƒåœžæĄļ / åˆĒ除", @@ -1826,13 +2013,16 @@ "trash_page_select_assets_btn": "é¸æ“‡é …į›Ž", "trash_page_title": "垃圞æĄļ ({count})", "trashed_items_will_be_permanently_deleted_after": "垃圞æĄļä¸­įš„é …į›Žæœƒåœ¨ {days, plural, other {# 夊}}垌永䚅åˆĒ除。", + "troubleshoot": "į–‘éšžč§Ŗį­”", "type": "éĄžåž‹", "unable_to_change_pin_code": "į„Ąæŗ•čŽŠæ›´ PIN įĸŧ", "unable_to_setup_pin_code": "į„Ąæŗ•č¨­åŽš PIN įĸŧ", "unarchive": "取æļˆå°å­˜", + "unarchive_action_prompt": "åˇ˛åžžå°å­˜įš„æĒ”æĄˆä¸­į§ģ除äē† {count} å€‹é …į›Ž", "unarchived_count": "{count, plural, other {åˇ˛å–æļˆå°å­˜ # å€‹é …į›Ž}}", "undo": "還原", "unfavorite": "取æļˆæ”ļ藏", + "unfavorite_action_prompt": "{count} 個į§ģ除æ”ļ藏", "unhide_person": "取æļˆéšąč—äēēį‰Š", "unknown": "æœĒįŸĨ", "unknown_country": "æœĒįŸĨ國åŽļ", @@ -1845,19 +2035,26 @@ "unnamed_album": "æœĒå‘Ŋåį›¸į°ŋ", "unnamed_album_delete_confirmation": "įĸē厚čρåˆĒ除這æœŦᛏį°ŋ嗎īŧŸ", "unnamed_share": "æœĒå‘Ŋ名分äēĢ", - "unsaved_change": "更攚æœĒå„˛å­˜", + "unsaved_change": "čŽŠæ›´æœĒå„˛å­˜", "unselect_all": "取æļˆå…¨é¸", "unselect_all_duplicates": "取æļˆé¸å–æ‰€æœ‰įš„é‡č¤‡é …į›Ž", + "unselect_all_in": "{group} 全不選", "unstack": "取æļˆå †å ", + "unstack_action_prompt": "{count} 個取æļˆå †į–Š", "unstacked_assets_count": "åˇ˛č§Ŗé™¤å †į–Š {count, plural, other {# 個æĒ”æĄˆ}}", + "untagged": "į„Ąæ¨™įą¤", "up_next": "下一個", + "update_location_action_prompt": "äŊŋᔍäģĨ下å‘Ŋä줿›´æ–°{count}å€‹æ‰€é¸čŗ‡į”ĸįš„äŊįŊŽīŧš", "updated_at": "更新æ–ŧ", "updated_password": "åˇ˛æ›´æ–°å¯†įĸŧ", "upload": "ä¸Šå‚ŗ", + "upload_action_prompt": "{count} å€‹åˇ˛åŠ å…Ĩä¸Šå‚ŗäŊ‡åˆ—", "upload_concurrency": "ä¸Šå‚ŗä¸Ļ行", + "upload_details": "ä¸Šå‚ŗčŠŗį´°čŗ‡č¨Š", "upload_dialog_info": "是åĻčĻå°‡æ‰€é¸é …į›Žå‚™äģŊ到äŧ翜å™¨īŧŸ", "upload_dialog_title": "ä¸Šå‚ŗé …į›Ž", - "upload_errors": "ä¸Šå‚ŗåŽŒæˆīŧŒäŊ†æœ‰ {count, plural, other {# 處å‡ē錯}}īŧŒčρæŸĨįœ‹æ–°ä¸Šå‚ŗįš„æĒ”æĄˆčĢ‹é‡æ–°æ•´į†é éĸ。", + "upload_errors": "ä¸Šå‚ŗåŽŒæˆīŧŒäŊ†æœ‰ {count, plural, other {# č™•æ™‚į™ŧį”ŸéŒ¯čǤ}}īŧŒčρæŸĨįœ‹æ–°ä¸Šå‚ŗįš„æĒ”æĄˆčĢ‹é‡æ–°æ•´į†é éĸ。", + "upload_finished": "ä¸Šå‚ŗåŽŒæˆ", "upload_progress": "削餘 {remaining, number} - 厞處ᐆ {processed, number}/{total, number}", "upload_skipped_duplicates": "厞į•Ĩ過 {count, plural, other {# å€‹é‡č¤‡įš„æĒ”æĄˆ}}", "upload_status_duplicates": "é‡č¤‡é …į›Ž", @@ -1866,32 +2063,35 @@ "upload_success": "ä¸Šå‚ŗæˆåŠŸīŧŒčρæŸĨįœ‹æ–°ä¸Šå‚ŗįš„æĒ”æĄˆčĢ‹é‡æ–°æ•´į†é éĸ。", "upload_to_immich": "ä¸Šå‚ŗč‡ŗ Immich ({count})", "uploading": "ä¸Šå‚ŗä¸­", + "uploading_media": "åĒ’éĢ”ä¸Šå‚ŗä¸­", "url": "įļ˛å€", "usage": "į”¨é‡", "use_biometric": "äŊŋį”¨į”Ÿį‰Ščž¨č­˜", "use_current_connection": "äŊŋį”¨į›Žå‰įš„é€Ŗįˇš", "use_custom_date_range": "æ”šį”¨č‡Ē訂æ—ĨæœŸį¯„åœ", "user": "äŊŋᔍ者", - "user_has_been_deleted": "æ­¤į”¨æˆļäģĨčĸĢåˆĒ除", + "user_has_been_deleted": "æ­¤į”¨æˆļ厞čĸĢåˆĒ除。", "user_id": "äŊŋᔍ者 ID", "user_liked": "{user} å–œæ­Ąäē† {type, select, photo {這åŧĩᅧቇ} video {這æŽĩåŊąį‰‡} asset {這個æĒ”æĄˆ} other {厃}}", "user_pin_code_settings": "PIN įĸŧ", - "user_pin_code_settings_description": "įŽĄį†äŊ įš„ PIN įĸŧ", + "user_pin_code_settings_description": "įŽĄį†æ‚¨įš„ PIN įĸŧ", + "user_privacy": "äŊŋį”¨č€…éšąį§", "user_purchase_settings": "čŗŧįŊŽ", - "user_purchase_settings_description": "įŽĄį†äŊ įš„čŗŧ財", + "user_purchase_settings_description": "įŽĄį†æ‚¨įš„čŗŧ財", "user_role_set": "設 {user} į‚ē{role}", "user_usage_detail": "äŊŋį”¨č€…į”¨é‡čŠŗį´°čŗ‡č¨Š", "user_usage_stats": "å¸ŗč™ŸäŊŋį”¨é‡įĩąč¨ˆ", "user_usage_stats_description": "æŸĨįœ‹å¸ŗč™ŸäŊŋį”¨é‡", "username": "äŊŋį”¨č€…åį¨ą", "users": "admin", + "users_added_to_album_count": "åˇ˛åœ¨æ­¤į›¸į°ŋ中新åĸžäē† {count, plural, one {# 個} other {# 個}} äŊŋᔍ者", "utilities": "åˇĨå…ˇ", "validate": "驗證", "validate_endpoint_error": "čĢ‹čŧ¸å…Ĩæœ‰æ•ˆįš„é€Ŗįĩ", "variables": "čŽŠæ•¸", "version": "į‰ˆæœŦ", "version_announcement_closing": "æ•ŦįĨé †åŋƒīŧŒAlex", - "version_announcement_message": "嗨īŊžæ–°į‰ˆæœŦįš„ Immich 推å‡ēäē†ã€‚į‚ē防æ­ĸ配įŊŽå‡ē錯īŧŒčĢ‹čŠąéģžæ™‚間閹莀į™ŧ行čĒĒæ˜ŽīŧŒä¸Ļįĸēäŋč¨­åŽšæ˜¯æœ€æ–°įš„īŧŒį‰šåˆĨ是äŊŋᔍ WatchTower į­‰č‡Ē動更新åˇĨå…ˇæ™‚ã€‚", + "version_announcement_message": "嗨īŊžæ–°į‰ˆæœŦįš„ Immich 推å‡ēäē†ã€‚į‚ē防æ­ĸč¨­åŽšį™ŧį”ŸéŒ¯čǤīŧŒčĢ‹čŠąéģžæ™‚間閹莀į™ŧ行čĒĒæ˜ŽīŧŒä¸Ļįĸēäŋč¨­åŽšæ˜¯æœ€æ–°įš„īŧŒį‰šåˆĨ是äŊŋᔍ WatchTower į­‰č‡Ē動更新åˇĨå…ˇæ™‚ã€‚", "version_history": "į‰ˆæœŦį´€éŒ„", "version_history_item": "{date} åŽ‰čŖäē† {version}", "video": "åŊąį‰‡", @@ -1903,6 +2103,7 @@ "view_album": "æŸĨįœ‹į›¸į°ŋ", "view_all": "į€čĻŊ全部", "view_all_users": "æŸĨįœ‹æ‰€æœ‰äŊŋᔍ者", + "view_details": "æĒĸčĻ–čŠŗį´°čŗ‡č¨Š", "view_in_timeline": "在時間čģ¸ä¸­æŸĨįœ‹", "view_link": "æŸĨįœ‹é€Ŗįĩ", "view_links": "æĒĸčĻ–éˆįĩ", @@ -1910,12 +2111,13 @@ "view_next_asset": "æŸĨįœ‹ä¸‹ä¸€é …", "view_previous_asset": "æŸĨįœ‹ä¸Šä¸€é …", "view_qr_code": "æŸĨįœ‹ QR code", + "view_similar_photos": "æŸĨįœ‹į›¸äŧŧᅧቇ", "view_stack": "æŸĨįœ‹å †į–Š", "view_user": "éĄ¯į¤ēäŊŋᔍ者", "viewer_remove_from_stack": "åžžå †į–Šä¸­į§ģ除", "viewer_stack_use_as_main_asset": "äŊœį‚ēä¸ģé …į›ŽäŊŋᔍ", "viewer_unstack": "取æļˆå †į–Š", - "visibility_changed": "åˇ˛æ›´æ”š {count, plural, other {# äŊäēēį‰Š}}įš„å¯čĻ‹æ€§", + "visibility_changed": "åˇ˛čŽŠæ›´ {count, plural, other {# äŊäēēį‰Š}}įš„å¯čĻ‹æ€§", "waiting": "åž…č™•į†", "warning": "č­Ļ告", "week": "週", @@ -1928,5 +2130,6 @@ "yes": "是", "you_dont_have_any_shared_links": "æ‚¨æ˛’æœ‰äģģäŊ•å…ąäēĢ逪įĩ", "your_wifi_name": "æ‚¨įš„ Wi-Fi åį¨ą", - "zoom_image": "į¸Žæ”žåœ–į‰‡" + "zoom_image": "į¸Žæ”žåœ–į‰‡", + "zoom_to_bounds": "į¸Žæ”žåˆ°é‚Šį•Œ" } diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 17c6954ab7..3ed4a14a2b 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -2,7 +2,7 @@ "about": "å…ŗäēŽ", "account": "č´Ļæˆˇ", "account_settings": "č´ĻæˆˇčŽžįŊŽ", - "acknowledge": "我įŸĨ道äē†", + "acknowledge": "厞įŸĨ悉", "action": "操äŊœ", "action_common_update": "更新", "actions": "操äŊœ", @@ -14,6 +14,7 @@ "add_a_location": "æˇģ加äŊįŊŽ", "add_a_name": "æˇģåŠ åį§°", "add_a_title": "æˇģ加标éĸ˜", + "add_birthday": "æˇģåŠ į”Ÿæ—Ĩ", "add_endpoint": "æˇģåŠ æœåŠĄå™¨ URL", "add_exclusion_pattern": "æˇģåŠ æŽ’é™¤č§„åˆ™", "add_import_path": "æˇģ加å¯ŧå…Ĩčˇ¯åž„", @@ -25,8 +26,12 @@ "add_tag": "æˇģåŠ æ ‡į­ž", "add_to": "æˇģ加到â€Ļ", "add_to_album": "æˇģåŠ åˆ°į›¸å†Œ", - "add_to_album_bottom_sheet_added": "æˇģ加到 {album}", - "add_to_album_bottom_sheet_already_exists": "厞圍 {album} 中", + "add_to_album_bottom_sheet_added": "æˇģåŠ åˆ°į›¸å†Œ “{album}”", + "add_to_album_bottom_sheet_already_exists": "åˇ˛åœ¨į›¸å†Œâ€œ {album} ” 中", + "add_to_album_bottom_sheet_some_local_assets": "某ä盿œŦ地čĩ„äē§æ— æŗ•æˇģåŠ åˆ°į›¸å†Œ", + "add_to_album_toggle": "é€‰æ‹Šį›¸å†Œ {album}", + "add_to_albums": "æˇģåŠ åˆ°į›¸å†Œ", + "add_to_albums_count": "æˇģåŠ åˆ°į›¸å†Œīŧˆ{count}ä¸Ēīŧ‰", "add_to_shared_album": "æˇģåŠ åˆ°å…ąäēĢį›¸å†Œ", "add_url": "æˇģ加 URL", "added_to_archive": "æˇģ加到åŊ’æĄŖ", @@ -44,16 +49,23 @@ "backup_database": "创åģēæ•°æŽåē“备äģŊ", "backup_database_enable_description": "å¯į”¨æ•°æŽåē“å¯ŧå‡ē备äģŊ", "backup_keep_last_amount": "čρäŋį•™įš„åŽ†å˛å¯ŧå‡ē数量", + "backup_onboarding_1_description": "äē‘įĢ¯æˆ–å…ļäģ–į‰Šį†äŊįŊŽįš„åŧ‚地副æœŦ。", + "backup_onboarding_2_description": "åœ¨ä¸åŒčŽžå¤‡ä¸Šįš„æœŦ地副æœŦ。čŋ™åŒ…æ‹Ŧä¸ģ文äģļ及å…ļæœŦ地备äģŊ。", + "backup_onboarding_3_description": "æ‚¨įš„æ•°æŽīŧˆåŒ…æ‹Ŧ原始文äģļīŧ‰įš„æ€ģ副æœŦ数。å…ļ中包æ‹Ŧ 1 äģŊåŧ‚地副æœŦ和 2 äģŊæœŦ地副æœŦ。", + "backup_onboarding_description": "åģēčŽŽé‡‡į”¨3-2-1备äģŊį­–į•ĨæĨäŋæŠ¤æ‚¨įš„æ•°æŽã€‚您åē”č¯Ĩäŋį•™åˇ˛ä¸Šäŧ į…§į‰‡/视éĸ‘äģĨ及 Immich 数捎åē“įš„å‰¯æœŦīŧŒäģĨčŽˇåž—å…¨éĸįš„å¤‡äģŊč§Ŗå†ŗæ–šæĄˆã€‚", + "backup_onboarding_footer": "æœ‰å…ŗå¤‡äģŊ Immich įš„æ›´å¤šäŋĄæ¯īŧŒč¯ˇå‚é˜…æ–‡æĄŖã€‚", + "backup_onboarding_parts_title": "3-2-1 备äģŊ包æ‹Ŧīŧš", + "backup_onboarding_title": "备äģŊ", "backup_settings": "数捎åē“å¯ŧå‡ē莞įŊŽ", "backup_settings_description": "įŽĄį†æ•°æŽåē“备äģŊ莞įŊŽã€‚", "cleared_jobs": "åˇ˛æ¸…į†äģģåŠĄīŧš{job}", "config_set_by_file": "åŊ“前配įŊŽåˇ˛é€ščŋ‡é…įŊŽæ–‡äģļ莞įŊŽ", - "confirm_delete_library": "įĄŽåŽščĻåˆ é™¤å›žåē““{library}”吗īŧŸ", + "confirm_delete_library": "是åĻįĄŽåŽšåˆ é™¤å›žåē““{library}”īŧŸ", "confirm_delete_library_assets": "įĄŽåŽščĻåˆ é™¤č¯Ĩ回åē“吗īŧŸčŋ™å°†åˆ é™¤æ‰€æœ‰åŒ…åĢ在 Immich ä¸­įš„{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}īŧŒä¸”æ— æŗ•æ’¤é”€ã€‚äŊ†æ–‡äģļäģå°†äŋį•™åœ¨įŖį›˜ä¸­ã€‚", "confirm_email_below": "č¯ˇčž“å…Ĩ“{email}”äģĨčŋ›čĄŒįĄŽčޤ", "confirm_reprocess_all_faces": "įĄŽåŽščĻå¯šå…¨éƒ¨į…§į‰‡é‡æ–°čŋ›čĄŒéĸéƒ¨č¯†åˆĢ吗īŧŸčŋ™å°†åŒæ—ᅬ…é™¤æ‰€æœ‰åˇ˛å‘Ŋ名äēēį‰Šã€‚", "confirm_user_password_reset": "įĄŽåŽščĻé‡įŊŽį”¨æˆˇâ€œ{user}â€įš„å¯†į å—īŧŸ", - "confirm_user_pin_code_reset": "įĄŽåŽščĻé‡įŊŽ{user}įš„PIN᠁吗īŧŸ", + "confirm_user_pin_code_reset": "įĄŽåŽščĻé‡įŊŽį”¨æˆˇâ€œ{user}â€įš„PIN᠁吗īŧŸ", "create_job": "创åģēäģģåŠĄ", "cron_expression": "Cron 襨螞åŧ", "cron_expression_description": "äŊŋᔍ Cron æ ŧåŧčŽžįŊŽæ‰Ģ描间隔。更多č¯Ļįģ†äŋĄæ¯č¯ˇå‚阅 Crontab Guru", @@ -64,7 +76,7 @@ "external_library_management": "外部回åē“įŽĄį†", "face_detection": "äēēč„¸æŖ€æĩ‹", "face_detection_description": "äŊŋᔍæœē器å­Ļäš æŖ€æĩ‹éĄšį›Žä¸­įš„äēē脸īŧˆč§†éĸ‘åĒæŖ€æĩ‹å…ļįŧŠį•Ĩå›žä¸­įš„äēē脸īŧ‰ã€‚é€‰æ‹Šâ€œåˆˇæ–°â€å°†äŧšīŧˆé‡æ–°īŧ‰å¤„į†æ‰€æœ‰éĄšį›Žã€‚é€‰æ‹Šâ€œé‡įŊŽâ€čŋ˜äŧšæ¸…é™¤æ‰€æœ‰åŊ“前éĸ部数捎。选拊“įŧēå¤ąâ€å°†å°šæœĒå¤„į†įš„éĄšį›Žčŋ›čĄŒæŽ’é˜Ÿå¤„į†ã€‚äēēč„¸æŖ€æĩ‹åŽŒæˆåŽīŧŒæŖ€æĩ‹åˆ°įš„äēēč„¸å°†æŽ’é˜Ÿčŋ›čĄŒéĸéƒ¨č¯†åˆĢīŧŒå°†åރäģŦ分įģ„åˆ°įŽ°æœ‰įš„æˆ–æ–°įš„äēēį‰Šä¸­ã€‚", - "facial_recognition_job_description": "å°†æŖ€æĩ‹åˆ°įš„äēēč„¸æŒ‰į…§äēēį‰Šåˆ†įģ„。čŋ™ä¸€æ­Ĩ将在äēēč„¸æŖ€æĩ‹åŽŒæˆåŽæ‰§čĄŒã€‚é€‰æ‹Šâ€œé‡įŊŽâ€å°†äŧšīŧˆé‡æ–°īŧ‰åˆ†į섿‰€æœ‰éĸ孔。选拊“įŧēå¤ąâ€å°†å°šæœĒåˆ†é…įš„äēē脸įŊŽäēŽé˜Ÿåˆ—中。", + "facial_recognition_job_description": "å°†æŖ€æĩ‹åˆ°įš„äēēč„¸æŒ‰į…§äēēį‰Šåˆ†įģ„。čŋ™ä¸€æ­Ĩ将在äēēč„¸æŖ€æĩ‹åŽŒæˆåŽæ‰§čĄŒã€‚é€‰æ‹Šâ€œé‡įŊŽâ€å°†äŧšīŧˆé‡æ–°īŧ‰åˆ†į섿‰€æœ‰äēēč„¸ã€‚é€‰æ‹Šâ€œįŧēå¤ąâ€å°†å°šæœĒåˆ†é…įš„äēē脸įŊŽäēŽé˜Ÿåˆ—中。", "failed_job_command": "{command}å‘Ŋä줿‰§čĄŒå¤ąč´Ĩįš„äģģåŠĄīŧš{job}", "force_delete_user_warning": "č­Ļ告īŧščŋ™å°†įĢ‹åŗį§ģé™¤į”¨æˆˇäģĨ及å…￉€æœ‰éĄšį›Žã€‚č¯Ĩ操äŊœæ— æŗ•撤销且文äģļæ— æŗ•æĸ复。", "image_format": "æ ŧåŧ", @@ -89,7 +101,7 @@ "image_thumbnail_description": "å‰ĨįĻģå…ƒæ•°æŽįš„å°įŧŠį•Ĩ回īŧŒį”¨äēŽæĩč§ˆä¸ģæ—ļ间įēŋᭉᅧቇįģ„", "image_thumbnail_quality_description": "įŧŠį•Ĩå›žč´¨é‡äģŽ 1 到 100。čļŠé̘čļŠåĨŊīŧŒäŊ†äŧšäē§į”Ÿæ›´å¤§įš„æ–‡äģļīŧŒåšļ且äŧšé™äŊŽįŗģįģŸįš„响åē”čƒŊ力。", "image_thumbnail_title": "įŧŠį•Ĩå›žčŽžįŊŽ", - "job_concurrency": "{job}åšļ发", + "job_concurrency": "{job}äģģåŠĄåšļ发", "job_created": "äģģåŠĄåˇ˛åˆ›åģē", "job_not_concurrency_safe": "æ­¤äģģåŠĄåšļ发åšļ不厉全。", "job_settings": "äģģåŠĄčŽžįŊŽ", @@ -110,13 +122,20 @@ "library_watching_settings": "į›‘æŽ§å›žåē“īŧˆåŽžéĒŒæ€§īŧ‰", "library_watching_settings_description": "č‡ĒåŠ¨į›‘æŽ§æ–‡äģļ变化", "logging_enable_description": "吝ᔍæ—Ĩåŋ—čްåŊ•", - "logging_level_description": "å¯į”¨įš„æ—Ĩåŋ—įē§åˆĢ。", + "logging_level_description": "吝ᔍæ—ļīŧŒčρäŊŋį”¨įš„æ—Ĩåŋ—įē§åˆĢ。", "logging_settings": "æ—Ĩåŋ—", + "machine_learning_availability_checks": "å¯į”¨æ€§æŖ€æŸĨ", + "machine_learning_availability_checks_description": "č‡ĒåŠ¨æŖ€æĩ‹åšļäŧ˜å…ˆé€‰æ‹Šå¯į”¨įš„æœē器å­Ļäš æœåŠĄå™¨", + "machine_learning_availability_checks_enabled": "å¯į”¨å¯į”¨æ€§æŖ€æŸĨ", + "machine_learning_availability_checks_interval": "æŖ€æŸĨ间隔", + "machine_learning_availability_checks_interval_description": "两æŦĄå¯į”¨æ€§æŖ€æŸĨäš‹é—´įš„é—´éš”īŧˆæ¯Ģį§’īŧ‰", + "machine_learning_availability_checks_timeout": "č¯ˇæą‚čļ…æ—ļ", + "machine_learning_availability_checks_timeout_description": "ᔍäēŽå¯į”¨æ€§æŖ€æŸĨįš„čļ…æ—ļæ—ļ间īŧˆæ¯Ģį§’īŧ‰", "machine_learning_clip_model": "CLIP æ¨Ąåž‹", "machine_learning_clip_model_description": "蝎äēŽ æ­¤å¤„æŸĨįœ‹æ”¯æŒįš„ CLIP æ¨Ąåž‹åį§°ã€‚æŗ¨æ„īŧŒæ›´æĸæ¨Ąåž‹åŽéœ€čĻå¯šæ‰€æœ‰å›žį‰‡é‡æ–°čŋčĄŒâ€œæ™ēčƒŊ搜į´ĸ”äģģåŠĄã€‚", "machine_learning_duplicate_detection": "é‡å¤éĄšæŖ€æĩ‹", "machine_learning_duplicate_detection_enabled": "å¯į”¨é‡å¤æŖ€æĩ‹", - "machine_learning_duplicate_detection_enabled_description": "åĻ‚æžœįĻį”¨æ­¤åŠŸčƒŊīŧŒåŽŒå…¨į›¸åŒįš„éĄšį›Žäģå°†čĸĢåŽģ重。", + "machine_learning_duplicate_detection_enabled_description": "åĻ‚æžœįρᔍīŧŒåŽŒå…¨į›¸åŒįš„éĄšį›Žäģå°†čĸĢåŽģ重。", "machine_learning_duplicate_detection_setting_description": "äŊŋᔍ CLIP 向量匚配īŧˆå…ŗé”Žč¯į›¸äŧŧåēĻīŧ‰æĨæŸĨ扞可čƒŊįš„é‡å¤éĄš", "machine_learning_enabled": "吝ᔍæœē器å­Ļäš ", "machine_learning_enabled_description": "åĻ‚æžœįρᔍīŧŒæ— čŽēäģĨ下åĻ‚äŊ•莞įŊŽīŧŒæ‰€æœ‰æœē器å­Ļ䚠功čƒŊ将čĸĢįĻį”¨ã€‚", @@ -129,11 +148,11 @@ "machine_learning_max_detection_distance": "æœ€å¤§æŖ€æĩ‹čˇįĻģ", "machine_learning_max_detection_distance_description": "两åŧ å›žį‰‡čĸĢ莤ä¸ēæ˜¯é‡å¤įš„æœ€å¤§čˇįĻģčŒƒå›´æ˜¯0.001到0.1ã€‚čžƒéĢ˜įš„å€ŧå°†æŖ€æĩ‹å‡ēæ›´å¤šįš„é‡å¤å›žį‰‡īŧŒäŊ†å¯čƒŊå¯ŧ致蝝æŠĨ。", "machine_learning_max_recognition_distance": "æœ€å¤§č¯†åˆĢ距įĻģ", - "machine_learning_max_recognition_distance_description": "将此阈å€ŧčŽžåŽšåœ¨0到2之间īŧŒå¯äģĨäŧ˜åŒ–įŗģįģŸįš„蝆åˆĢį˛žåēĻ。选拊一ä¸Ē螃äŊŽįš„阈å€ŧīŧŒæœ‰åŠŠäēŽäŋæŒéĸå­”įš„į‹Ŧį‰šæ€§īŧŒéŋå…é”™č¯¯åœ°å°†ä¸¤ä¸Ēä¸åŒįš„äēē蝆åˆĢä¸ē同一äēēã€‚į›¸åīŧŒé€‚åŊ“提é̘阈å€ŧ可äģĨ减少将同一äēēč¯¯åˆ†ä¸ē多ä¸Ēéĸå­”įš„æƒ…å†ĩ。在čŋ™ä¸Ē选拊čŋ‡į¨‹ä¸­īŧŒæˆ‘äģŦ倞向äēŽæ›´äŊŽįš„阈å€ŧīŧŒå› ä¸ē合åšļ错蝝蝆åˆĢįš„éĸ孔čĻæ¯”åˆ†įĻģ同一ä¸Ēéĸå­”ä¸­įš„å¤šä¸Ēäēēæ›´įŽ€å•ã€‚", + "machine_learning_max_recognition_distance_description": "将此阈å€ŧčŽžåŽšåœ¨0到2之间īŧŒå¯äģĨäŧ˜åŒ–įŗģįģŸįš„蝆åˆĢį˛žåēĻ。选拊一ä¸Ē螃äŊŽįš„阈å€ŧīŧŒæœ‰åŠŠäēŽäŋæŒäēēč„¸įš„į‹Ŧį‰šæ€§īŧŒéŋå…é”™č¯¯åœ°å°†ä¸¤ä¸Ēä¸åŒįš„äēē蝆åˆĢä¸ē同一äēēã€‚į›¸åīŧŒé€‚åŊ“提é̘阈å€ŧ可äģĨ减少将同一äēēč¯¯åˆ†ä¸ē多ä¸Ēäēēč„¸įš„æƒ…å†ĩ。在čŋ™ä¸Ē选拊čŋ‡į¨‹ä¸­īŧŒæˆ‘äģŦ倞向äēŽæ›´äŊŽįš„阈å€ŧīŧŒå› ä¸ē合åšļ错蝝蝆åˆĢįš„äēē脸čĻæ¯”åˆ†įĻģ同一ä¸Ēäēēč„¸ä¸­įš„å¤šä¸Ēäēēæ›´įŽ€å•ã€‚", "machine_learning_min_detection_score": "最äŊŽæŖ€æĩ‹åˆ†æ•°", - "machine_learning_min_detection_score_description": "éĸ部čĸĢæŖ€æĩ‹åˆ°įš„æœ€å°įŊŽäŋĄåēĻåˆ†æ•°čŒƒå›´æ˜¯0到1ã€‚čžƒäŊŽįš„å€ŧå°†æŖ€æĩ‹åˆ°æ›´å¤šįš„éĸ孔īŧŒäŊ†å¯čƒŊå¯ŧ致蝝æŠĨ。", + "machine_learning_min_detection_score_description": "æŖ€æĩ‹åˆ°äēēč„¸įš„æœ€å°įŊŽäŋĄåˆ†æ•°ä¸ē0-1ã€‚čžƒäŊŽįš„å€ŧå°†æŖ€æĩ‹åˆ°æ›´å¤šäēē脸īŧŒäŊ†å¯čƒŊå¯ŧ致蝝æŠĨ。", "machine_learning_min_recognized_faces": "蝆åˆĢįš„æœ€å°‘äēēč„¸æ•°", - "machine_learning_min_recognized_faces_description": "创åģēæ–°äēēį‰Šæ‰€éœ€æœ€å°‘č¯†åˆĢįš„æ•°é‡ã€‚æé̘čŋ™ä¸Ēå€ŧ可äģĨäŊŋéĸéƒ¨č¯†åˆĢæ›´į˛žįĄŽīŧŒäŊ†äšŸåĸžåŠ äē†éĸ孔æœĒčƒŊčĸĢ分配到寚åē”äēēį‰Šįš„å¯čƒŊ性。", + "machine_learning_min_recognized_faces_description": "创åģē一ä¸Ēäē翉€éœ€č¯†åˆĢįš„æœ€å°‘äēēč„¸æ•°é‡ã€‚æé̘čŋ™ä¸Ēå€ŧ可äģĨäŊŋäēē脏蝆åˆĢæ›´į˛žįĄŽīŧŒäŊ†äšŸåĸžåŠ äē†äēē脸æœĒčƒŊčĸĢåˆ†é…åˆ°į›¸å¯šåē”äēēį‰Šįš„å¯čƒŊ性。", "machine_learning_settings": "æœē器å­Ļ䚠莞įŊŽ", "machine_learning_settings_description": "įŽĄį†æœē器å­Ļ䚠功čƒŊå’ŒčŽžįŊŽ", "machine_learning_smart_search": "æ™ēčƒŊ搜į´ĸ", @@ -147,7 +166,7 @@ "map_enable_description": "å¯į”¨åœ°å›žåŠŸčƒŊ", "map_gps_settings": "地回与 GPS 莞įŊŽ", "map_gps_settings_description": "įŽĄį†åœ°å›žä¸Ž GPSīŧˆåå‘åœ°į†įŧ–᠁īŧ‰čŽžįŊŽ", - "map_implications": "地回功čƒŊ䞝čĩ–äēŽå¤–部地åŊĸč´´å›žæœåŠĄīŧˆtiles.immich.cloudīŧ‰", + "map_implications": "地回功čƒŊ䞝čĩ–äēŽå¤–部地回į“Ļį‰‡æœåŠĄīŧˆtiles.immich.cloudīŧ‰", "map_light_style": "æĩ…č‰˛æ¨Ąåŧ", "map_manage_reverse_geocoding_settings": "įŽĄį†åå‘åœ°į†įŧ–į čŽžįŊŽ", "map_reverse_geocoding": "åå‘åœ°į†įŧ–᠁", @@ -166,6 +185,20 @@ "metadata_settings_description": "įŽĄį†å…ƒæ•°æŽčŽžįŊŽ", "migration_job": "čŋį§ģ", "migration_job_description": "å°†éĄšį›Žå’Œäēē脏蝆åˆĢįš„įŧŠį•Ĩ回čŋį§ģåˆ°æœ€æ–°įš„æ–‡äģļ多į쓿ž„", + "nightly_tasks_cluster_faces_setting_description": "å¯šæ–°æŖ€æĩ‹åˆ°įš„éĸ部čŋ›čĄŒéĸéƒ¨č¯†åˆĢ", + "nightly_tasks_cluster_new_faces_setting": "įž¤į섿–°äēē脸", + "nightly_tasks_database_cleanup_setting": "数捎å瓿¸…ᐆäģģåŠĄ", + "nightly_tasks_database_cleanup_setting_description": "æ¸…į†æ•°æŽåē“中čŋ‡æœŸįš„æ—§æ•°æŽ", + "nightly_tasks_generate_memories_setting": "į”Ÿæˆå›žåŋ†", + "nightly_tasks_generate_memories_setting_description": "äģŽéĄšį›Žä¸­į”Ÿæˆæ–°įš„回åŋ†", + "nightly_tasks_missing_thumbnails_setting": "į”Ÿæˆįŧēå¤ąįš„įŧŠį•Ĩ回", + "nightly_tasks_missing_thumbnails_setting_description": "ä¸ēį”ŸæˆįŧŠį•Ĩ回队列无įŧŠį•Ĩå›žįš„éĄšį›Ž", + "nightly_tasks_settings": "夜间äģģåŠĄčŽžįŊŽ", + "nightly_tasks_settings_description": "įŽĄį†å¤œé—´äģģåŠĄ", + "nightly_tasks_start_time_setting": "åŧ€å§‹æ—ļ间", + "nightly_tasks_start_time_setting_description": "æœåŠĄå™¨åŧ€å§‹čŋčĄŒå¤œé—´äģģåŠĄįš„æ—ļ间", + "nightly_tasks_sync_quota_usage_setting": "同æ­Ĩ配éĸäŊŋį”¨æƒ…å†ĩ", + "nightly_tasks_sync_quota_usage_setting_description": "栚捎åŊ“前äŊŋį”¨æƒ…å†ĩæ›´æ–°į”¨æˆˇå­˜å‚¨é…éĸ", "no_paths_added": "æ— åˇ˛æˇģåŠ čˇ¯åž„", "no_pattern_added": "æ— åˇ˛æˇģåŠ č§„åˆ™", "note_apply_storage_label_previous_assets": "提į¤ēīŧščĻå°†å­˜å‚¨æ ‡į­žåē”ᔍäēŽäš‹å‰ä¸Šäŧ įš„éĄšį›ŽīŧŒéœ€čρčŋčĄŒ", @@ -176,7 +209,7 @@ "notification_email_ignore_certificate_errors": "åŋŊį•Ĩ蝁äšĻ错蝝", "notification_email_ignore_certificate_errors_description": "åŋŊį•Ĩ TLS 蝁äšĻéĒŒč¯é”™č¯¯īŧˆä¸åģē莎īŧ‰", "notification_email_password_description": "与邮äģᅵåŠĄå™¨čŋ›čĄŒčēĢäģŊénj蝁æ—ļäŊŋį”¨įš„å¯†į ", - "notification_email_port_description": "邮äģᅵåŠĄå™¨įĢ¯åŖīŧˆäž‹åĻ‚25、465或587īŧ‰", + "notification_email_port_description": "邮äģᅵåŠĄå™¨įĢ¯åŖīŧˆäž‹åĻ‚ 25、465 或 587īŧ‰", "notification_email_sent_test_email_button": "发送æĩ‹č¯•邎äģļåšļäŋå­˜", "notification_email_setting_description": "发送邎äģļ通įŸĨ莞įŊŽ", "notification_email_test_email": "发送æĩ‹č¯•邎äģļ", @@ -195,7 +228,9 @@ "oauth_enable_description": "äŊŋᔍ OAuth į™ģåŊ•", "oauth_mobile_redirect_uri": "į§ģ动įĢ¯é‡åŽšå‘ URI", "oauth_mobile_redirect_uri_override": "į§ģ动įĢ¯é‡åŽšå‘ URI čφᛖ", - "oauth_mobile_redirect_uri_override_description": "åŊ“ OAuth æäž›å•†ä¸å…čŽ¸äŊŋᔍį§ģ动 URI æ—ļ吝ᔍīŧŒåĻ‚â€œ''{callback}''”", + "oauth_mobile_redirect_uri_override_description": "åŊ“ OAuth æäž›å•†ä¸å…čŽ¸äŊŋᔍį§ģ动 URI æ—ļ吝ᔍīŧŒåĻ‚â€œ{callback}”", + "oauth_role_claim": "č§’č‰˛åŖ°æ˜Ž", + "oauth_role_claim_description": "æ šæŽæ­¤åŖ°æ˜Žįš„å­˜åœ¨č‡Ē动授äēˆįŽĄį†å‘˜čŽŋé—Žæƒé™ã€‚åŖ°æ˜Žå¯äģĨ是“user”īŧˆį”¨æˆˇīŧ‰æˆ–“admin”īŧˆįŽĄį†å‘˜īŧ‰ã€‚", "oauth_settings": "OAuth", "oauth_settings_description": "įŽĄį† OAuth į™ģåŊ•莞įŊŽ", "oauth_settings_more_details": "å…ŗäēŽæ­¤åŠŸčƒŊįš„æ›´å¤šč¯Ļįģ†äŋĄæ¯īŧŒč¯ˇæŸĨįœ‹į›¸å…ŗæ–‡æĄŖã€‚", @@ -203,8 +238,8 @@ "oauth_storage_label_claim_description": "č‡ĒåŠ¨å°†į”¨æˆˇįš„å­˜å‚¨æ ‡į­žčŽžįŊŽä¸ēæ­¤éĄšįš„å€ŧ。", "oauth_storage_quota_claim": "存储配éĸåŖ°æ˜Ž", "oauth_storage_quota_claim_description": "č‡ĒåŠ¨å°†į”¨æˆˇįš„å­˜å‚¨é…éĸčŽžįŊŽä¸ēæ­¤éĄšįš„å€ŧ。", - "oauth_storage_quota_default": "éģ˜čŽ¤å­˜å‚¨é…éĸīŧˆGBīŧ‰", - "oauth_storage_quota_default_description": "æœĒæäž›åŖ°æ˜Žæ—ļäŊŋį”¨įš„é…éĸīŧˆGBīŧ‰ã€‚", + "oauth_storage_quota_default": "éģ˜čŽ¤å­˜å‚¨é…éĸīŧˆGiBīŧ‰", + "oauth_storage_quota_default_description": "æœĒæäž›åŖ°æ˜Žæ—ļäŊŋį”¨įš„é…éĸīŧˆGiBīŧ‰ã€‚", "oauth_timeout": "č¯ˇæą‚čļ…æ—ļ", "oauth_timeout_description": "č¯ˇæą‚čļ…æ—ļīŧˆæ¯Ģį§’īŧ‰", "password_enable_description": "äŊŋį”¨é‚ŽįŽąå’Œå¯†į į™ģåŊ•", @@ -212,7 +247,7 @@ "password_settings_description": "įŽĄį†å¯†į į™ģåŊ•莞įŊŽ", "paths_validated_successfully": "æ‰€æœ‰čˇ¯åž„éĒŒč¯æˆåŠŸ", "person_cleanup_job": "æ¸…į†äēēį‰Š", - "quota_size_gib": "配éĸå¤§å°īŧˆGBīŧ‰", + "quota_size_gib": "配éĸå¤§å°īŧˆGiBīŧ‰", "refreshing_all_libraries": "åˆˇæ–°æ‰€æœ‰å›žåē“", "registration": "æŗ¨å†ŒįŽĄį†å‘˜", "registration_description": "į”ąäēŽæ‚¨æ˜¯įŗģįģŸä¸Šįš„įŦŦ一ä¸Ēį”¨æˆˇīŧŒæ‚¨å°†čĸĢæŒ‡åޚä¸ēįŽĄį†å‘˜åšļč´Ÿč´ŖįŽĄį†äģģåŠĄīŧŒį”ąæ‚¨æĨ创åģēæ–°įš„į”¨æˆˇã€‚", @@ -331,10 +366,13 @@ "trash_number_of_days_description": "čĸĢæ°¸äš…删除䚋前īŧŒéĄšį›Žåœ¨å›žæ”ļį̙䏭äŋį•™įš„夊数", "trash_settings": "回æ”ļįĢ™čŽžįŊŽ", "trash_settings_description": "įŽĄį†å›žæ”ļįĢ™čŽžįŊŽ", + "unlink_all_oauth_accounts": "č§Ŗé™¤æ‰€æœ‰ä¸Ž OAuth å¸æˆˇįš„é“žæŽĨ", + "unlink_all_oauth_accounts_description": "在čŋį§ģč‡ŗæ–°įš„æœåŠĄæäž›å•†å‰īŧŒč¯ˇä¸čρåŋ˜čްčĻå…ˆč§Ŗé™¤æ‰€æœ‰ä¸Ž OAuth å¸æˆˇįš„é“žæŽĨ。", + "unlink_all_oauth_accounts_prompt": "您是åĻįĄŽčŽ¤čĻč§Ŗé™¤æ‰€æœ‰ä¸Ž OAuth å¸æˆˇįš„é“žæŽĨ? æ‰€æœ‰į›¸å…ŗįš„äŊŋᔍ者čēĢäģŊäŧščĸĢ重设īŧŒåšļ且不čƒŊčĸĢčŋ˜åŽŸã€‚", "user_cleanup_job": "æ¸…į†į”¨æˆˇ", "user_delete_delay": "{user}įš„č´ĻæˆˇåŠéĄšį›Žå°†åœ¨{delay, plural, one {#夊} other {#夊}}后č‡Ē动永䚅删除。", "user_delete_delay_settings": "åģᅵŸåˆ é™¤", - "user_delete_delay_settings_description": "永䚅删除č´ĻæˆˇåŠå…￉€æœ‰éĄšį›Žäš‹å‰æ‰€äŋį•™įš„å¤Šæ•°ã€‚į”¨æˆˇåˆ é™¤äŊœä¸šäŧšåœ¨åˆå¤œæŖ€æŸĨ是åĻæœ‰į”¨æˆˇå¯äģĨ删除。寚č¯Ĩ莞įŊŽįš„æ›´æ”šå°†åœ¨ä¸‹æŦĄæ‰§čĄŒæ—ļį”Ÿæ•ˆã€‚", + "user_delete_delay_settings_description": "åˆ é™¤åŽæ°¸äš…åˆ é™¤į”¨æˆˇå¸æˆˇå’Œčĩ„äē§įš„å¤Šæ•°ã€‚į”¨æˆˇåˆ é™¤äŊœä¸šäŧšåœ¨åˆå¤œæŖ€æŸĨ是åĻæœ‰į”¨æˆˇå¯äģĨ删除。寚č¯Ĩ莞įŊŽįš„æ›´æ”šå°†åœ¨ä¸‹æŦĄæ‰§čĄŒæ—ļį”Ÿæ•ˆã€‚", "user_delete_immediately": "{user}įš„č´ĻæˆˇåŠéĄšį›Žå°†įĢ‹åŗæ°¸äš…åˆ é™¤ã€‚", "user_delete_immediately_checkbox": "įĢ‹åŗåˆ é™¤æŖ€į´ĸåˆ°įš„į”¨æˆˇåŠéĄšį›Ž", "user_details": "į”¨æˆˇč¯Ļ情", @@ -364,6 +402,8 @@ "advanced_settings_prefer_remote_title": "äŧ˜å…ˆčŋœį¨‹éĄšį›Ž", "advanced_settings_proxy_headers_subtitle": "厚䚉äģŖį†æ ‡å¤´īŧŒåē”ᔍäēŽ Immich įš„æ¯æŦĄįŊ‘įģœč¯ˇæą‚", "advanced_settings_proxy_headers_title": "äģŖį†æ ‡å¤´", + "advanced_settings_readonly_mode_subtitle": "吝ᔍåĒč¯ģæ¨ĄåŧīŧŒåœ¨č¯Ĩæ¨Ąåŧä¸‹åĒčƒŊæŸĨįœ‹į…§į‰‡īŧŒå¤šé€‰ã€å…ąäēĢã€æŠ•åąã€åˆ é™¤į­‰æ“äŊœéƒŊčĸĢįĻį”¨ã€‚äģŽä¸ģåąåš•é€ščŋ‡į”¨æˆˇå¤´åƒå¯į”¨/įρᔍåĒč¯ģ", + "advanced_settings_readonly_mode_title": "åĒč¯ģæ¨Ąåŧ", "advanced_settings_self_signed_ssl_subtitle": "莺čŋ‡å¯šæœåŠĄå™¨ įš„ SSL 蝁äšĻénj蝁īŧˆč¯Ĩé€‰éĄšé€‚į”¨äēŽäŊŋᔍč‡Ēį­žåč¯äšĻįš„æœåŠĄå™¨īŧ‰ã€‚", "advanced_settings_self_signed_ssl_title": "å…čŽ¸č‡Ēį­žå SSL 蝁äšĻ", "advanced_settings_sync_remote_deletions_subtitle": "在įŊ‘éĄĩä¸Šæ‰§čĄŒæ“äŊœæ—ļīŧŒč‡Ē动删除或čŋ˜åŽŸč¯ĨčŽžå¤‡ä¸­įš„éĄšį›Ž", @@ -371,7 +411,7 @@ "advanced_settings_tile_subtitle": "é̘įē§į”¨æˆˇčŽžįŊŽ", "advanced_settings_troubleshooting_subtitle": "吝ᔍᔍäēŽæ•…éšœæŽ’é™¤įš„éĸå¤–功čƒŊ", "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, one {#月鞄} other {#月鞄}}", + "age_months": "{months, plural, one {#ä¸Ē月} other {#ä¸Ē月}}", "age_year_months": "1垁{months, plural, one {#ä¸Ē月} other {#ä¸Ē月}}", "age_years": "{years, plural, other {#垁}}", "album_added": "čĸĢæˇģåŠ åˆ°į›¸å†Œ", @@ -379,6 +419,7 @@ "album_cover_updated": "į›¸å†Œå°éĸåˇ˛æ›´æ–°", "album_delete_confirmation": "įĄŽåŽščĻåˆ é™¤į›¸å†Œâ€œ{album}”吗īŧŸ", "album_delete_confirmation_description": "åĻ‚æžœč¯Ĩį›¸å†Œæ˜¯å…ąäēĢįš„īŧŒå…ļäģ–į”¨æˆˇå°†æ— æŗ•å†čŽŋ闎厃。", + "album_deleted": "į›¸å†Œåˇ˛åˆ é™¤", "album_info_card_backup_album_excluded": "åˇ˛æŽ’é™¤", "album_info_card_backup_album_included": "厞选䏭", "album_info_updated": "į›¸å†ŒäŋĄæ¯åˇ˛æ›´æ–°", @@ -388,7 +429,9 @@ "album_options": "į›¸å†ŒčŽžįŊŽ", "album_remove_user": "į§ģé™¤į”¨æˆˇīŧŸ", "album_remove_user_confirmation": "įĄŽåŽščρį§ģ除“{user}”吗īŧŸ", + "album_search_not_found": "æœĒ扞到įŦĻ合搜į´ĸæĄäģļįš„į›¸å†Œ", "album_share_no_users": "įœ‹čĩˇæĨæ‚¨åˇ˛ä¸Žæ‰€æœ‰į”¨æˆˇå…ąäēĢä熿­¤į›¸å†ŒīŧŒæˆ–č€…æ‚¨æ šæœŦæ˛Ąæœ‰äģģäŊ•į”¨æˆˇå¯å…ąäēĢ。", + "album_summary": "į›¸å†Œæ‘˜čρ", "album_updated": "į›¸å†Œæœ‰æ›´æ–°", "album_updated_setting_description": "åŊ“å…ąäēĢį›¸å†Œæœ‰æ–°éĄšį›Žæ—ļæŽĨæ”ļ邮äģļ通įŸĨ", "album_user_left": "įĻģåŧ€â€œ{album}”", @@ -398,15 +441,16 @@ "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_leave": "退å‡ēį›¸å†Œ", "album_viewer_appbar_share_to": "å…ąäēĢįģ™", - "album_viewer_page_share_add_users": "创åģēį”¨æˆˇ", + "album_viewer_page_share_add_users": "邀蝎äģ–äēē", "album_with_link_access": "æ‹Ĩ有此铞æŽĨįš„äģģäŊ•äēē均可æŸĨįœ‹æœŦį›¸å†Œä¸­įš„į…§į‰‡å’Œäēēį‰Šã€‚", "albums": "į›¸å†Œ", "albums_count": "{count, plural, one {{count, number} ä¸Ēį›¸å†Œ} other {{count, number} ä¸Ēį›¸å†Œ}}", "albums_default_sort_order": "éģ˜čŽ¤į›¸å†ŒæŽ’åēæ–šåŧ", "albums_default_sort_order_description": "创åģēæ–°į›¸å†Œæ—ļįš„éĄšį›Žåˆå§‹æŽ’åēæ–šåŧã€‚", "albums_feature_description": "可与å…ļäģ–į”¨æˆˇå…ąäēĢįš„éĄšį›Žæ”ļč—ã€‚", + "albums_on_device_count": "čŽžå¤‡ä¸Šįš„į›¸å†Œīŧˆ{count} ä¸Ēīŧ‰", "all": "全部", "all_albums": "æ‰€æœ‰į›¸å†Œ", "all_people": "全部äēēį‰Š", @@ -419,23 +463,25 @@ "anti_clockwise": "逆æ—ļ针", "api_key": "API 密é’Ĩ", "api_key_description": "č¯Ĩåē”ᔍ坆é’ĨåĒäŧšæ˜žį¤ē一æŦĄã€‚č¯ˇįĄŽäŋåœ¨å…ŗé—­įĒ—åŖå‰å¤åˆļ下æĨ。", - "api_key_empty": "API 密é’Ĩįš„åį§°ä¸å¯äģĨä¸ēįŠē", + "api_key_empty": "API 密é’Ĩåį§°ä¸å¯ä¸ēįŠē", "api_keys": "API 密é’Ĩ", - "app_bar_signout_dialog_content": "æ‚¨įĄŽåŽščρ退å‡ē吗īŧŸ", + "app_bar_signout_dialog_content": "是åĻįĄŽåŽšé€€å‡ēį™ģåŊ•īŧŸ", "app_bar_signout_dialog_ok": "是", "app_bar_signout_dialog_title": "退å‡ēį™ģåŊ•", "app_settings": "åē”į”¨čŽžįŊŽ", "appears_in": "å‡ēįŽ°äēŽ", + "apply_count": "åē”ᔍ ({count, number}ä¸Ēčĩ„äē§)", "archive": "åŊ’æĄŖ", + "archive_action_prompt": "厞将 {count} 饚æˇģ加到åŊ’æĄŖ", "archive_or_unarchive_photo": "åŊ’æĄŖæˆ–å–æļˆåŊ’æĄŖį…§į‰‡", "archive_page_no_archived_assets": "æœĒ扞到åŊ’æĄŖéĄšį›Ž", "archive_page_title": "åŊ’æĄŖīŧˆ{count}īŧ‰", "archive_size": "åŊ’æĄŖå¤§å°", - "archive_size_description": "配įŊŽä¸‹čŊŊåŊ’æĄŖå¤§å°īŧˆGBīŧ‰", - "archived": "åˇ˛å­˜æĄŖ", + "archive_size_description": "配įŊŽä¸‹čŊŊåŊ’æĄŖå¤§å°īŧˆGiBīŧ‰", + "archived": "厞åŊ’æĄŖ", "archived_count": "{count, plural, other {厞åŊ’æĄŖ # 饚}}", "are_these_the_same_person": "äģ–äģŦ是同一äŊäēē吗īŧŸ", - "are_you_sure_to_do_this": "įĄŽåŽščρčŋ™æ ˇåšå—īŧŸ", + "are_you_sure_to_do_this": "įĄŽåŽšæ‰§čĄŒæ­¤æ“äŊœīŧŸ", "asset_action_delete_err_read_only": "æ— æŗ•åˆ é™¤åĒč¯ģéĄšį›ŽīŧŒčˇŗčŋ‡", "asset_action_share_err_offline": "æ— æŗ•čŽˇå–įĻģįēŋéĄšį›ŽīŧŒčˇŗčŋ‡", "asset_added_to_album": "厞æˇģåŠ č‡ŗį›¸å†Œ", @@ -457,6 +503,8 @@ "asset_restored_successfully": "åˇ˛æˆåŠŸæĸå¤æ‰€æœ‰éĄšį›Ž", "asset_skipped": "厞莺čŋ‡", "asset_skipped_in_trash": "åˇ˛å›žæ”ļ", + "asset_trashed": "čĩ„äē§åˇ˛čĸĢ删除", + "asset_troubleshoot": "čĩ„äē§æ•…障排除", "asset_uploaded": "厞䏊äŧ ", "asset_uploading": "上äŧ ä¸­â€Ļ", "asset_viewer_settings_subtitle": "įŽĄį†å›žå瓿ĩč§ˆå™¨čŽžįŊŽ", @@ -464,8 +512,9 @@ "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 {{name}} other {æ–°į›¸å†Œ}}", + "assets_added_to_albums_count": "厞æˇģ加 {assetTotal, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}到 {albumTotal, plural, one {# ä¸Ēį›¸å†Œ} other {# ä¸Ēį›¸å†Œ}}", "assets_cannot_be_added_to_album_count": "æ— æŗ•æˇģ加 {count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}} åˆ°į›¸å†Œä¸­", + "assets_cannot_be_added_to_albums": "æ— æŗ•æˇģ加 {count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}} åˆ°į›¸å†Œ", "assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "assets_deleted_permanently": "{count} ä¸ĒéĄšį›Žåˇ˛čĸĢæ°¸äš…删除", "assets_deleted_permanently_from_server": "åˇ˛æ°¸äš…į§ģ除 {count} ä¸ĒéĄšį›Ž", @@ -482,27 +531,32 @@ "assets_trashed_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛æ”žå…Ĩ回æ”ļįĢ™", "assets_trashed_from_server": "{count} ä¸ĒéĄšį›Žåˇ˛æ”žå…Ĩ回æ”ļįĢ™", "assets_were_part_of_album_count": "{count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}}厞įģåœ¨į›¸å†Œä¸­", + "assets_were_part_of_albums_count": "{count, plural, one {ä¸ĒéĄšį›Ž} other {ä¸ĒéĄšį›Ž}} åˇ˛åœ¨į›¸å†Œä¸­", "authorized_devices": "åˇ˛æŽˆæƒčŽžå¤‡", - "automatic_endpoint_switching_subtitle": "åŊ“čŋžæŽĨåˆ°æŒ‡åŽšįš„ Wi-Fi æ—ļäŊŋᔍæœŦ地čŋžæŽĨīŧŒåœ¨å…ļåŽƒįŽ¯åĸƒä¸‹äŊŋᔍæ›ŋäģŖčŋžæŽĨ", + "automatic_endpoint_switching_subtitle": "čŋžæŽĨ指厚 Wi-Fi æ—ļäŊŋᔍæœŦ地įŊ‘įģœīŧŒåĻ则äŊŋį”¨å¤–éƒ¨įŊ‘įģœ", "automatic_endpoint_switching_title": "č‡Ē动切æĸ URL", "autoplay_slideshow": "č‡Ē动播攞åšģၝቇ", "back": "čŋ”回", "back_close_deselect": "čŋ”å›žã€å…ŗé—­æˆ–åé€‰", + "background_backup_running_error": "后台备äģŊæ­Ŗåœ¨čŋčĄŒīŧŒæ— æŗ•启动手动备äģŊ", "background_location_permission": "后台厚äŊæƒé™", - "background_location_permission_content": "ä¸ēäē†åœ¨åŽå°čŋčĄŒæ—ļ切æĸįŊ‘įģœīŧŒImmich åŋ…éĄģ*始įģˆ*æ‹Ĩæœ‰į˛žįĄŽįš„äŊįŊŽčŽŋ闎权限īŧŒčŋ™æ ˇåē”ᔍፋåēæ‰čƒŊč¯ģ取 Wi-Fi įŊ‘įģœįš„åį§°", + "background_location_permission_content": "ä¸ēįĄŽäŋåŽå°čŋčĄŒæ—ļč‡Ē动切æĸįŊ‘įģœīŧŒéœ€æŽˆäēˆ Immich *始įģˆå…čŽ¸į˛žįĄŽåŽšäŊ* 权限īŧŒäģĨ蝆åˆĢ Wi-Fi įŊ‘įģœåį§°", + "background_options": "čƒŒæ™¯é€‰éĄš", + "backup": "备äģŊ", "backup_album_selection_page_albums_device": "čŽžå¤‡ä¸Šįš„į›¸å†Œīŧˆ{count}īŧ‰", "backup_album_selection_page_albums_tap": "单å‡ģ选中īŧŒåŒå‡ģ取æļˆ", "backup_album_selection_page_assets_scatter": "éĄšį›Žäŧšåˆ†æ•Ŗåœ¨å¤šä¸Ēį›¸å†Œä¸­ã€‚å› æ­¤īŧŒå¯äģĨ在备äģŊčŋ‡į¨‹ä¸­åŒ…åĢæˆ–æŽ’é™¤į›¸å†Œã€‚", "backup_album_selection_page_select_albums": "é€‰æ‹Šį›¸å†Œ", "backup_album_selection_page_selection_info": "选拊äŋĄæ¯", "backup_album_selection_page_total_assets": "æ€ģ莥", + "backup_albums_sync": "备äģŊį›¸å†ŒåŒæ­Ĩ", "backup_all": "全部", "backup_background_service_backup_failed_message": "备äģŊå¤ąč´ĨīŧŒæ­Ŗåœ¨é‡č¯•â€Ļ", "backup_background_service_connection_failed_message": "čŋžæŽĨæœåŠĄå™¨å¤ąč´ĨīŧŒæ­Ŗåœ¨é‡č¯•â€Ļ", "backup_background_service_current_upload_notification": "æ­Ŗåœ¨ä¸Šäŧ  {filename}", "backup_background_service_default_notification": "æ­Ŗåœ¨æŖ€æŸĨæ–°éĄšį›Žâ€Ļ", "backup_background_service_error_title": "备äģŊå¤ąč´Ĩ", - "backup_background_service_in_progress_notification": "æ­Ŗåœ¨å¤‡äģŊâ€Ļ", + "backup_background_service_in_progress_notification": "æ­Ŗåœ¨å¤‡äģŊæ‚¨įš„čĩ„äē§â€Ļ", "backup_background_service_upload_failure_notification": "{filename}上äŧ å¤ąč´Ĩ", "backup_controller_page_albums": "备äģŊį›¸å†Œ", "backup_controller_page_background_app_refresh_disabled_content": "čρäŊŋį”¨åŽå°å¤‡äģŊ功čƒŊīŧŒč¯ˇåœ¨â€œčŽžįŊŽâ€>â€œå¸¸č§„â€>“后台åē”į”¨åˆˇæ–°â€ä¸­å¯į”¨åŽå°åē”ᔍፋåēåˆˇæ–°ã€‚", @@ -545,13 +599,16 @@ "backup_controller_page_turn_on": "åŧ€å¯å‰å°å¤‡äģŊ", "backup_controller_page_uploading_file_info": "æ­Ŗåœ¨ä¸Šäŧ ä¸­įš„æ–‡äģļäŋĄæ¯", "backup_err_only_album": "不čƒŊį§ģé™¤å”¯ä¸€įš„ä¸€ä¸Ēį›¸å†Œ", + "backup_error_sync_failed": "同æ­Ĩå¤ąč´Ĩã€‚æ— æŗ•å¤„į†å¤‡äģŊ。", "backup_info_card_assets": "饚", "backup_manual_cancelled": "åˇ˛å–æļˆ", "backup_manual_in_progress": "上äŧ æ­Ŗåœ¨čŋ›čĄŒä¸­īŧŒč¯ˇį¨åŽå†č¯•", "backup_manual_success": "成功", "backup_manual_title": "上äŧ įŠļ态", + "backup_options": "备äģŊ选项", "backup_options_page_title": "备äģŊ选项", "backup_setting_subtitle": "įŽĄį†åŽå°å’Œå‰å°ä¸Šäŧ čŽžįŊŽ", + "backup_settings_subtitle": "įŽĄį†ä¸Šäŧ čŽžįŊŽ", "backward": "后退", "biometric_auth_enabled": "į”Ÿį‰Šč¯†åˆĢčēĢäģŊéĒŒč¯åˇ˛å¯į”¨", "biometric_locked_out": "您čĸĢé”åŽšåœ¨į”Ÿį‰Šč¯†åˆĢčēĢäģŊéĒŒč¯äš‹å¤–", @@ -570,7 +627,7 @@ "cache_settings_clear_cache_button": "清除įŧ“å­˜", "cache_settings_clear_cache_button_title": "清除åē”ᔍįŧ“å­˜ã€‚åœ¨é‡æ–°į”Ÿæˆįŧ“存䚋前īŧŒå°†æ˜žč‘—åŊąå“åē”į”¨įš„æ€§čƒŊ。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "厞加å…Ĩéģ‘åå•įš„į…§į‰‡å’Œč§†éĸ‘", + "cache_settings_duplicated_assets_subtitle": "åē”ᔍፋåēåŋŊį•Ĩįš„į…§į‰‡å’Œč§†éĸ‘", "cache_settings_duplicated_assets_title": "é‡å¤éĄšį›Žīŧˆ{count}īŧ‰", "cache_settings_statistics_album": "回åē“įŧŠį•Ĩ回", "cache_settings_statistics_full": "厌整回像", @@ -587,6 +644,7 @@ "cancel": "取æļˆ", "cancel_search": "取æļˆæœį´ĸ", "canceled": "åˇ˛å–æļˆ", + "canceling": "取æļˆä¸­", "cannot_merge_people": "æ— æŗ•åˆåšļäēēį‰Š", "cannot_undo_this_action": "æŗ¨æ„īŧšč¯Ĩ操äŊœæ— æŗ•čĸĢæ’¤æļˆīŧ", "cannot_update_the_description": "æ— æŗ•æ›´æ–°æčŋ°", @@ -609,6 +667,8 @@ "change_pin_code": "äŋŽæ”šPIN᠁", "change_your_password": "äŋŽæ”šæ‚¨įš„å¯†į ", "changed_visibility_successfully": "æ›´æ”šå¯č§æ€§æˆåŠŸ", + "charging": "充į”ĩ", + "charging_requirement_mobile_backup": "后台备äģŊ需čĻčŽžå¤‡å¤„äēŽå……į”ĩįŠļ态", "check_corrupt_asset_backup": "æŖ€æŸĨ备äģŊ是åĻ损坏", "check_corrupt_asset_backup_button": "æ‰§čĄŒæŖ€æŸĨ", "check_corrupt_asset_backup_description": "äģ…在čŋžæŽĨ到 Wi-Fi åšļåŽŒæˆæ‰€æœ‰éĄšį›Žå¤‡äģŊåŽæ‰§čĄŒæ­¤æŖ€æŸĨ。č¯Ĩčŋ‡į¨‹å¯čƒŊ需čĻå‡ åˆ†é’Ÿã€‚", @@ -618,6 +678,7 @@ "clear": "清įŠē", "clear_all": "清įŠē全部", "clear_all_recent_searches": "清除所有最čŋ‘搜į´ĸ", + "clear_file_cache": "清除文äģļįŧ“å­˜", "clear_message": "清įŠēæļˆæ¯", "clear_value": "删除内厚", "client_cert_dialog_msg_confirm": "įĄŽåŽš", @@ -645,11 +706,11 @@ "confirm_admin_password": "įĄŽčŽ¤įŽĄį†å‘˜å¯†į ", "confirm_delete_face": "æ‚¨įĄŽåŽščρäģŽčĩ„äē§ä¸­åˆ é™¤ {name} įš„äēē脸äŋĄæ¯å—īŧŸ", "confirm_delete_shared_link": "įĄŽåŽščĻåˆ é™¤æ­¤å…ąäēĢ链æŽĨ吗īŧŸ", - "confirm_keep_this_delete_others": "é™¤æ­¤éĄšį›Žå¤–īŧŒå †å ä¸­įš„æ‰€æœ‰å…ļåŽƒéĄšį›ŽéƒŊ将čĸĢåˆ é™¤ã€‚įĄŽåŽščρįģ§įģ­å—īŧŸ", + "confirm_keep_this_delete_others": "除äē†čŋ™ä¸Ēčĩ„äē§äš‹å¤–īŧŒå †æ ˆä¸­įš„æ‰€æœ‰å…ļäģ–čĩ„äē§éƒŊ将čĸĢåˆ é™¤ã€‚æ‚¨įĄŽåŽščρįģ§įģ­å—īŧŸ", "confirm_new_pin_code": "įĄŽčŽ¤æ–°įš„PIN᠁", "confirm_password": "įĄŽčŽ¤å¯†į ", - "confirm_tag_face": "æ‚¨æƒŗå°†čŋ™åŧ č„¸æ ‡čްä¸ē{name}吗īŧŸ", - "confirm_tag_face_unnamed": "äŊ æƒŗæ ‡čްčŋ™åŧ č„¸å—īŧŸ", + "confirm_tag_face": "æ‚¨æƒŗå°†čŋ™ä¸Ēäēēč„¸æ ‡čŽ°ä¸ē“{name}”吗īŧŸ", + "confirm_tag_face_unnamed": "是åĻæ ‡čŽ°æ­¤äēē脸īŧŸ", "connected_device": "厞čŋžæŽĨčŽžå¤‡", "connected_to": "厞čŋžæŽĨ到", "contain": "包åĢ", @@ -657,12 +718,12 @@ "continue": "įģ§įģ­", "control_bottom_app_bar_create_new_album": "新åģēį›¸å†Œ", "control_bottom_app_bar_delete_from_immich": "äģŽ Immich æœåŠĄå™¨ä¸­åˆ é™¤", - "control_bottom_app_bar_delete_from_local": "äģŽį§ģåŠ¨čŽžå¤‡ä¸­åˆ é™¤", + "control_bottom_app_bar_delete_from_local": "äģŽæ‰‹æœē中删除", "control_bottom_app_bar_edit_location": "įŧ–čž‘äŊįŊŽäŋĄæ¯", "control_bottom_app_bar_edit_time": "įŧ–čž‘æ—Ĩ期和æ—ļ间", - "control_bottom_app_bar_share_link": "分äēĢ链æŽĨ", + "control_bottom_app_bar_share_link": "å…ąäēĢ链æŽĨ", "control_bottom_app_bar_share_to": "发送įģ™", - "control_bottom_app_bar_trash_from_immich": "攞å…Ĩ回æ”ļįĢ™", + "control_bottom_app_bar_trash_from_immich": "į§ģå…Ĩ回æ”ļįĢ™", "copied_image_to_clipboard": "åˇ˛å¤åˆļå›žį‰‡č‡ŗå‰Ē切æŋ。", "copied_to_clipboard": "åˇ˛å¤åˆļ到å‰Ē切æŋīŧ", "copy_error": "复åˆļå‡ē错", @@ -681,18 +742,20 @@ "create_library": "创åģē回åē“", "create_link": "创åģē链æŽĨ", "create_link_to_share": "创åģēå…ąäēĢ链æŽĨ", - "create_link_to_share_description": "čŽˇåž—æ­¤é“žæŽĨįš„äģģäŊ•äēēéƒŊå¯įœ‹åˆ°é€‰æ‹Šįš„į…§į‰‡", + "create_link_to_share_description": "čŽˇåž—æ­¤é“žæŽĨįš„äēē均可æŸĨįœ‹æ‰€é€‰į…§į‰‡", "create_new": "新åģē", "create_new_person": "创åģēæ–°äēēį‰Š", "create_new_person_hint": "æŒ‡æ´žåˇ˛é€‰æ‹ŠéĄšį›Žåˆ°æ–°įš„äēēį‰Š", "create_new_user": "创åģēæ–°į”¨æˆˇ", "create_shared_album_page_share_add_assets": "æˇģåŠ éĄšį›Ž", "create_shared_album_page_share_select_photos": "é€‰æ‹ŠéĄšį›Ž", + "create_shared_link": "创åģēå…ąäēĢ链æŽĨ", "create_tag": "创åģēæ ‡į­ž", "create_tag_description": "创åģē一ä¸Ēæ–°æ ‡į­žã€‚å¯šäēŽåĩŒåĨ—æ ‡į­žīŧŒč¯ˇčž“å…Ĩæ ‡į­žįš„åŽŒæ•´čˇ¯åž„īŧŒåŒ…æ‹Ŧæ­Ŗæ–œæ īŧˆ/īŧ‰ã€‚", "create_user": "创åģēį”¨æˆˇ", "created": "åˇ˛åˆ›åģē", "created_at": "åˇ˛åˆ›åģē", + "creating_linked_albums": "æ­Ŗåœ¨åˆ›åģēį›¸å†Œé“žæŽĨâ€Ļ", "crop": "誁å‰Ē", "curated_object_page_title": "äē‹į‰Š", "current_device": "åŊ“å‰čŽžå¤‡", @@ -700,10 +763,11 @@ "current_server_address": "åŊ“å‰æœåŠĄå™¨åœ°å€", "custom_locale": "č‡Ē厚䚉地åŒē", "custom_locale_description": "æ—Ĩ期和数字昞į¤ēæ ŧåŧčˇŸéšč¯­č¨€å’Œåœ°åŒē", + "custom_url": "č‡Ē厚䚉URL", "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYYåš´M月Dæ—Ĩ (E)", "dark": "æˇąč‰˛", - "darkTheme": "æš—č‰˛ä¸ģéĸ˜", + "dark_theme": "切æĸæˇąč‰˛ä¸ģéĸ˜", "date_after": "åŧ€å§‹æ—Ĩ期", "date_and_time": "æ—Ĩ期与æ—ļ间", "date_before": "į쓿Ÿæ—Ĩ期", @@ -711,6 +775,7 @@ "date_of_birth_saved": "å‡ēį”Ÿæ—Ĩ期äŋå­˜æˆåŠŸ", "date_range": "æ—ĨæœŸčŒƒå›´", "day": "æ—Ĩ", + "days": "夊", "deduplicate_all": "åˆ é™¤æ‰€æœ‰é‡å¤éĄš", "deduplication_criteria_1": "回像大小īŧˆå­—节īŧ‰", "deduplication_criteria_2": "EXIF æ•°æŽčŽĄæ•°", @@ -719,8 +784,10 @@ "default_locale": "éģ˜čޤ地åŒē", "default_locale_description": "æ šæŽæ‚¨įš„æĩč§ˆå™¨åœ°åŒē莞įŊŽæ—Ĩ期和数字昞į¤ēæ ŧåŧ", "delete": "删除", + "delete_action_confirmation_message": "æ‚¨įĄŽåŽščĻåˆ é™¤æ­¤čĩ„äē§å—īŧŸæ­¤æ“äŊœäŧšå°†čĩ„äē§į§ģč‡ŗæœåŠĄå™¨å›žæ”ļįĢ™īŧŒåšļäŧšæį¤ē您是åĻčρ圍æœŦ地删除厃", + "delete_action_prompt": "åˇ˛åˆ é™¤ {count} 饚", "delete_album": "åˆ é™¤į›¸å†Œ", - "delete_api_key_prompt": "įĄŽåŽšåˆ é™¤æ­¤ API 密é’Ĩ吗īŧŸ", + "delete_api_key_prompt": "是åĻįĄŽčŽ¤åˆ é™¤æ­¤ API 密é’ĨīŧŸ", "delete_dialog_alert": "čŋ™äē›éĄšį›Žå°†äģŽ Immich å’Œæ‚¨įš„čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", "delete_dialog_alert_local": "čŋ™äē›éĄšį›Žå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤īŧŒäŊ†äģį„ļ可äģĨäģŽ Immich æœåŠĄå™¨ä¸­å†æŦĄčŽˇå–", "delete_dialog_alert_local_non_backed_up": "éƒ¨åˆ†éĄšį›Žčŋ˜æœĒ备äģŊ臺 Immich æœåŠĄå™¨īŧŒå°†äģŽæ‚¨įš„į§ģåŠ¨čŽžå¤‡ä¸­æ°¸äš…åˆ é™¤", @@ -732,9 +799,12 @@ "delete_key": "删除密é’Ĩ", "delete_library": "删除回åē“", "delete_link": "删除铞æŽĨ", + "delete_local_action_prompt": "åˇ˛åˆ é™¤æœŦåœ°éĄšį›Ž{count}饚", "delete_local_dialog_ok_backed_up_only": "äģ…åˆ é™¤åˇ˛å¤‡äģŊéĄšį›Ž", "delete_local_dialog_ok_force": "įĄŽčŽ¤åˆ é™¤", "delete_others": "删除å…ļ厃", + "delete_permanently": "永䚅删除", + "delete_permanently_action_prompt": "åˇ˛æ°¸äš…åˆ é™¤ {count} 饚", "delete_shared_link": "åˆ é™¤å…ąäēĢ链æŽĨ", "delete_shared_link_dialog_title": "åˆ é™¤å…ąäēĢ链æŽĨ", "delete_tag": "åˆ é™¤æ ‡į­ž", @@ -745,6 +815,7 @@ "description": "描čŋ°", "description_input_hint_text": "æˇģ加描čŋ°...", "description_input_submit_error": "更新描čŋ°æ—ļå‡ē错īŧŒč¯ˇæŖ€æŸĨæ—Ĩåŋ—äģĨčŽˇå–æ›´å¤šč¯Ļįģ†äŋĄæ¯", + "deselect_all": "取æļˆå…¨é€‰", "details": "č¯Ļ情", "direction": "斚向", "disabled": "厞įρᔍ", @@ -758,10 +829,11 @@ "display_order": "昞į¤ēéĄēåē", "display_original_photos": "昞į¤ēåŽŸå§‹į…§į‰‡", "display_original_photos_setting_description": "在įŊ‘įģœä¸ŽåŽŸå§‹æ ŧåŧå…ŧåŽšįš„æƒ…å†ĩ下īŧŒæŸĨįœ‹å›žį‰‡æˆ–č§†éĸ‘æ—ļäŧ˜å…ˆæ˜žį¤ē原始文äģļč€Œä¸æ˜¯įŧŠį•Ĩ回。čŋ™å¯čƒŊå¯ŧč‡´į…§į‰‡æ˜žį¤ē速åēĻ变æ…ĸ。", - "do_not_show_again": "不再昞į¤ēč¯ĨäŋĄæ¯", + "do_not_show_again": "不再昞į¤ēæ­¤äŋĄæ¯", "documentation": "å¸ŽåŠŠæ–‡æĄŖ", "done": "厌成", "download": "下čŊŊ", + "download_action_prompt": "æ­Ŗåœ¨ä¸‹čŊŊ {count} ä¸ĒéĄšį›Ž", "download_canceled": "下čŊŊåˇ˛å–æļˆ", "download_complete": "下čŊŊ厌成", "download_enqueue": "厞加å…Ĩ下čŊŊ队列", @@ -788,8 +860,12 @@ "edit": "įŧ–čž‘", "edit_album": "įŧ–čž‘į›¸å†Œ", "edit_avatar": "įŧ–čž‘å¤´åƒ", + "edit_birthday": "įŧ–čž‘į”Ÿæ—Ĩ", "edit_date": "įŧ–čž‘æ—Ĩ期", "edit_date_and_time": "įŧ–čž‘æ—Ĩ期和æ—ļ间", + "edit_date_and_time_action_prompt": "厞įŧ–čž‘ {count} 饚æ—Ĩ期æ—ļ间", + "edit_date_and_time_by_offset": "按æ—ļ间偏į§ģ量更攚æ—Ĩ期", + "edit_date_and_time_by_offset_interval": "新æ—ĨæœŸįš„čŒƒå›´īŧš{from} - {to}", "edit_description": "äŋŽæ”šæčŋ°", "edit_description_prompt": "č¯ˇé€‰æ‹Šæ–°įš„æčŋ°īŧš", "edit_exclusion_pattern": "įŧ–čž‘æŽ’é™¤č§„åˆ™", @@ -799,6 +875,7 @@ "edit_key": "įŧ–čž‘ API 密é’Ĩ", "edit_link": "įŧ–螑铞æŽĨ", "edit_location": "įŧ–čž‘äŊįŊŽ", + "edit_location_action_prompt": "{count} ä¸ĒäŊįŊŽåˇ˛įŧ–čž‘", "edit_location_dialog_title": "äŊįŊŽ", "edit_name": "įŧ–čž‘åį§°", "edit_people": "įŧ–čž‘äēēį‰Š", @@ -813,10 +890,11 @@ "editor_crop_tool_h2_rotation": "旋čŊŦ", "email": "é‚ŽįŽą", "email_notifications": "邮äģļ通įŸĨ", - "empty_folder": "文äģļ多ä¸ēįŠē", + "empty_folder": "此文äģļ多ä¸ēįŠē", "empty_trash": "清įŠē回æ”ļįĢ™", "empty_trash_confirmation": "įĄŽåŽščĻæ¸…įŠē回æ”ļįĢ™īŧŸčŋ™å°†æ°¸äš…删除回æ”ļįĢ™ä¸­įš„æ‰€æœ‰éĄšį›Žã€‚\næŗ¨æ„īŧšč¯Ĩ操äŊœæ— æŗ•æ’¤æļˆīŧ", "enable": "吝ᔍ", + "enable_backup": "吝ᔍ备äģŊ", "enable_biometric_auth_description": "输å…Ĩæ‚¨įš„PIN᠁äģĨå¯į”¨į”Ÿį‰Šč¯†åˆĢčēĢäģŊénj蝁", "enabled": "厞吝ᔍ", "end_date": "į쓿Ÿæ—Ĩ期", @@ -827,14 +905,16 @@ "error": "错蝝", "error_change_sort_album": "æ›´æ”šį›¸å†ŒæŽ’åēå¤ąč´Ĩ", "error_delete_face": "删除äēēč„¸å¤ąč´Ĩ", + "error_getting_places": "čŽˇå–äŊįŊŽæ—ļå‡ē错", "error_loading_image": "加čŊŊå›žį‰‡æ—ļå‡ē错", + "error_loading_partners": "加čŊŊ同äŧ´æ—ļå‡ē错īŧš{error}", "error_saving_image": "错蝝īŧš{error}", "error_tag_face_bounding_box": "æ ‡čŽ°äēē脸å‡ē错 - æ— æŗ•čŽˇå–äēēč„¸æĄ†åæ ‡", "error_title": "错蝝 - åĨŊ像å‡ēäē†é—Žéĸ˜", "errors": { "cannot_navigate_next_asset": "æ— æŗ•å¯ŧčˆĒ到下一ä¸ĒéĄšį›Ž", "cannot_navigate_previous_asset": "æ— æŗ•å¯ŧčˆĒ到上一ä¸ĒéĄšį›Ž", - "cant_apply_changes": "æ— į”¨åē”į”¨æ›´æ”š", + "cant_apply_changes": "æ— æŗ•åē”į”¨æ›´æ”š", "cant_change_activity": "æ— æŗ•{enabled, select, true {įρᔍ} other {吝ᔍ}}æ´ģ动", "cant_change_asset_favorite": "æ— æŗ•äŋŽæ”šéĄšį›Žįš„æ”ļč—åąžæ€§", "cant_change_metadata_assets_count": "æ— æŗ•äŋŽæ”š{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}įš„å…ƒæ•°æŽ", @@ -860,6 +940,7 @@ "failed_to_load_notifications": "加čŊŊ通įŸĨå¤ąč´Ĩ", "failed_to_load_people": "加čŊŊäēēį‰Šå¤ąč´Ĩ", "failed_to_remove_product_key": "į§ģ除äē§å“å¯†é’Ĩå¤ąč´Ĩ", + "failed_to_reset_pin_code": "重įŊŽPINį å¤ąč´Ĩ", "failed_to_stack_assets": "æ— æŗ•å †å éĄšį›Ž", "failed_to_unstack_assets": "æ— æŗ•å–æļˆå †å éĄšį›Ž", "failed_to_update_notification_status": "更新通įŸĨįŠļæ€å¤ąč´Ĩ", @@ -868,6 +949,7 @@ "paths_validation_failed": "{paths, plural, one {#æĄčˇ¯åž„} other {#æĄčˇ¯åž„}} æ ĄéĒŒå¤ąč´Ĩ", "profile_picture_transparent_pixels": "ä¸Ēäēēčĩ„æ–™å›žį‰‡ä¸å¯äģĨ包åĢé€æ˜Žåƒį´ ã€‚č¯ˇæ”žå¤§æˆ–į§ģåŠ¨æ­¤å›žį‰‡ã€‚", "quota_higher_than_disk_size": "莞įŊŽįš„配éĸå¤§äēŽįŖį›˜åŽšé‡", + "something_went_wrong": "å‡ēäē†į‚šé—Žéĸ˜", "unable_to_add_album_users": "æ— æŗ•æˇģåŠ į”¨æˆˇč‡ŗį›¸å†Œ", "unable_to_add_assets_to_shared_link": "æ— æŗ•æˇģåŠ éĄšį›Žåˆ°å…ąäēĢ链æŽĨ", "unable_to_add_comment": "æ— æŗ•æˇģåŠ č¯„čŽē", @@ -953,13 +1035,11 @@ }, "exif": "Exif äŋĄæ¯", "exif_bottom_sheet_description": "æˇģ加描čŋ°...", + "exif_bottom_sheet_description_error": "更新描čŋ°æ—ļå‡ē错", "exif_bottom_sheet_details": "č¯Ļ情", "exif_bottom_sheet_location": "äŊįŊŽ", "exif_bottom_sheet_people": "äēēį‰Š", "exif_bottom_sheet_person_add_person": "æˇģ加姓名", - "exif_bottom_sheet_person_age_months": "{months} 月鞄", - "exif_bottom_sheet_person_age_year_months": "1垁 {months} ä¸Ē月", - "exif_bottom_sheet_person_age_years": "{years} 垁", "exit_slideshow": "退å‡ēåšģį¯į‰‡æ”žæ˜ ", "expand_all": "å…¨éƒ¨åą•åŧ€", "experimental_settings_new_asset_list_subtitle": "æ­Ŗåœ¨å¤„į†", @@ -973,6 +1053,8 @@ "explorer": "čĩ„æēįŽĄį†å™¨", "export": "å¯ŧå‡ē", "export_as_json": "å¯ŧå‡ēä¸ē JSON", + "export_database": "å¯ŧå‡ē数捎åē“", + "export_database_description": "å¯ŧå‡ē SQLite 数捎åē“", "extension": "æ‰Šåą•", "external": "å¤–éƒ¨įš„", "external_libraries": "外部回åē“", @@ -984,11 +1066,13 @@ "failed_to_load_assets": "加čŊŊéĄšį›Žå¤ąč´Ĩ", "failed_to_load_folder": "加čŊŊ文äģļå¤šå¤ąč´Ĩ", "favorite": "æ”ļ藏", + "favorite_action_prompt": "厞将 {count} 饚æˇģ加到æ”ļ藏", "favorite_or_unfavorite_photo": "æ”ļč—æˆ–å–æļˆæ”ļč—į…§į‰‡", "favorites": "æ”ļč—å¤š", "favorites_page_no_favorites": "æœĒ扞到æ”ļč—éĄšį›Ž", "feature_photo_updated": "äēēį‰Šå¤´åƒåˇ˛æ›´æ–°", "features": "功čƒŊ", + "features_in_development": "åŧ€å‘ä¸­įš„åŠŸčƒŊ", "features_setting_description": "įŽĄį† App 功čƒŊ", "file_name": "文äģļ名", "file_name_or_extension": "文äģļ名", @@ -998,21 +1082,26 @@ "filter_people": "čŋ‡æģ¤äēēį‰Š", "filter_places": "į­›é€‰åœ°į‚š", "find_them_fast": "æŒ‰åį§°åŋĢ速搜į´ĸ", + "first": "įŦŦ一ä¸Ē", "fix_incorrect_match": "äŋŽå¤ä¸æ­ŖįĄŽįš„匚配", "folder": "文äģļ多", "folder_not_found": "æœĒ扞到文äģļ多", "folders": "文äģļ多", "folders_feature_description": "在文äģļå¤šč§†å›žä¸­æĩč§ˆæ–‡äģļįŗģįģŸä¸Šįš„į…§į‰‡å’Œč§†éĸ‘", + "forgot_pin_code_question": "åŋ˜čŽ°æ‚¨įš„PIN᠁äē†īŧŸ", "forward": "向前", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google Cast æŠ•åą", "gcast_enabled_description": "č¯Ĩ功čƒŊ需čρ加čŊŊæĨč‡Ē Google įš„å¤–éƒ¨čĩ„æēã€‚", "general": "é€šį”¨", + "geolocation_instruction_location": "į‚šå‡ģå¸Ļ有GPSåæ ‡įš„čĩ„äē§äģĨäŊŋᔍå…ļäŊįŊŽīŧŒæˆ–į›´æŽĨäģŽåœ°å›žä¸Šé€‰æ‹ŠäŊįŊŽ", "get_help": "čŽˇå–å¸ŽåŠŠ", "get_wifiname_error": "æ— æŗ•čŽˇå– Wi-Fi åį§°ã€‚įĄŽäŋåˇ˛æŽˆäēˆåŋ…čĻįš„æƒé™īŧŒåšļ厞čŋžæŽĨ到 Wi-Fi įŊ‘įģœ", "getting_started": "å…Ĩ门", "go_back": "čŋ”回", "go_to_folder": "čŋ›å…Ĩ文äģļ多", "go_to_search": "前垀搜į´ĸ", + "gps": "有GPSäŋĄæ¯", + "gps_missing": "无GPSäŋĄæ¯", "grant_permission": "čŽˇå–æƒé™", "group_albums_by": "į›¸å†Œåˆ†įģ„䞝捎...", "group_country": "按å›ŊåŽļ分įģ„", @@ -1023,6 +1112,9 @@ "haptic_feedback_switch": "å¯į”¨æŒ¯åŠ¨åéψ", "haptic_feedback_title": "振动反éψ", "has_quota": "配éĸå¤§å°", + "hash_asset": "å“ˆå¸ŒéĄšį›Ž", + "hashed_assets": "åˇ˛å“ˆå¸Œįš„éĄšį›Ž", + "hashing": "æ­Ŗåœ¨å“ˆå¸Œ", "header_settings_add_header_tip": "æˇģ加标头", "header_settings_field_validator_msg": "莞įŊŽä¸å¯ä¸ēįŠē", "header_settings_header_name_input": "æ ‡å¤´åį§°", @@ -1054,7 +1146,9 @@ "home_page_upload_err_limit": "一æŦĄæœ€å¤šåĒčƒŊ上äŧ  30 ä¸ĒéĄšį›ŽīŧŒčˇŗčŋ‡", "host": "æœåŠĄå™¨", "hour": "æ—ļ", + "hours": "小æ—ļ", "id": "ID", + "idle": "įŠē闲", "ignore_icloud_photos": "åŋŊį•Ĩ iCloud ᅧቇ", "ignore_icloud_photos_description": "存储在 iCloud ä¸­įš„į…§į‰‡ä¸äŧšä¸Šäŧ č‡ŗ Immich æœåŠĄå™¨", "image": "å›žį‰‡", @@ -1103,8 +1197,8 @@ "items_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}", "jobs": "äģģåŠĄ", "keep": "äŋį•™", - "keep_all": "äŋį•™æ‰€æœ‰", - "keep_this_delete_others": "äŋį•™čŋ™ä¸ĒīŧŒåˆ é™¤å…ļ厃", + "keep_all": "全部äŋį•™", + "keep_this_delete_others": "äŋį•™æ­¤éĄšīŧŒå…ļäŊ™åˆ é™¤", "kept_this_deleted_others": "äŋį•™č¯ĨéĄšį›Žåšļ删除 {count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}", "keyboard_shortcuts": "é”Žį›˜åŋĢæˇé”Ž", "language": "蝭荀", @@ -1112,10 +1206,13 @@ "language_no_results_title": "æœĒ扞到寚åē”蝭荀", "language_search_hint": "搜į´ĸ蝭荀...", "language_setting_description": "é€‰æ‹Šæ‚¨įš„č¯­č¨€ååĨŊ", + "large_files": "大文äģļ", + "last": "最后一ä¸Ē", "last_seen": "最后上įēŋäēŽ", "latest_version": "æœ€æ–°į‰ˆæœŦ", "latitude": "įēŦåēĻ", "leave": "įĻģåŧ€", + "leave_album": "įĻģåŧ€į›¸å†Œ", "lens_model": "é•œå¤´åž‹åˇ", "let_others_respond": "å…čŽ¸äģ–äēē回åē”", "level": "į­‰įē§", @@ -1127,27 +1224,32 @@ "library_page_sort_created": "创åģēæ—Ĩ期", "library_page_sort_last_modified": "上æŦĄäŋŽæ”š", "library_page_sort_title": "į›¸å†Œæ ‡éĸ˜", + "licenses": "čŽ¸å¯č¯", "light": "æĩ…色", + "like": "喜æŦĸ", "like_deleted": "åˇ˛åˆ é™¤įš„æ”ļ藏", "link_motion_video": "链æŽĨåŠ¨æ€č§†éĸ‘", - "link_options": "链æŽĨ选项", "link_to_oauth": "įģ‘åޚ OAuth", "linked_oauth_account": "厞įģ‘åޚ OAuth č´Ļæˆˇ", "list": "åˆ—čĄ¨", "loading": "加čŊŊ中", "loading_search_results_failed": "加čŊŊ搜į´ĸį쓿žœå¤ąč´Ĩ", + "local": "æœŦ地", "local_asset_cast_failed": "æ— æŗ•æŠ•æ”žæœĒ上äŧ č‡ŗæœåŠĄå™¨įš„éĄšį›Ž", + "local_assets": "æœŦåœ°éĄšį›Ž", + "local_media_summary": "æœŦ地åĒ’äŊ“摘čρ", "local_network": "æœŦ地įŊ‘įģœ", "local_network_sheet_info": "åŊ“äŊŋį”¨æŒ‡åŽšįš„ Wi-Fi įŊ‘į윿—ļīŧŒåē”ᔍፋåēå°†é€ščŋ‡æ­¤ URL čŽŋé—ŽæœåŠĄå™¨", "location_permission": "厚äŊæƒé™", - "location_permission_content": "ä¸ēäŊŋᔍč‡Ē动切æĸ功čƒŊīŧŒImmich 需čĻį˛žįĄŽįš„åŽšäŊæƒé™īŧŒčŋ™æ ˇæ‰čƒŊč¯ģ取åŊ“前 Wi-Fi įŊ‘įģœįš„åį§°", - "location_picker_choose_on_map": "在地回上选拊", - "location_picker_latitude_error": "输å…Ĩæœ‰æ•ˆįš„įēŦåēĻå€ŧ", - "location_picker_latitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…Ĩæ‚¨įš„įēŦåēĻå€ŧ", - "location_picker_longitude_error": "输å…Ĩæœ‰æ•ˆįš„įģåēĻå€ŧ", - "location_picker_longitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…Ĩæ‚¨įš„įģåēĻå€ŧ", + "location_permission_content": "äŊŋᔍč‡Ē动切æĸ功čƒŊīŧŒImmich 需čĻį˛žįĄŽåŽšäŊæƒé™īŧŒäģĨčŽˇå–åŊ“前 Wi-Fi įŊ‘įģœåį§°", + "location_picker_choose_on_map": "在地回上厚äŊ", + "location_picker_latitude_error": "č¯ˇčž“å…Ĩæœ‰æ•ˆįš„įēŦåēĻ", + "location_picker_latitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…ĨįēŦåēĻ", + "location_picker_longitude_error": "č¯ˇčž“å…Ĩæœ‰æ•ˆįš„įģåēĻ", + "location_picker_longitude_hint": "č¯ˇåœ¨æ­¤å¤„čž“å…ĨįģåēĻ", "lock": "锁厚", "locked_folder": "锁厚文äģļ多", + "log_detail_title": "æ—Ĩåŋ—č¯Ļįģ†äŋĄæ¯", "log_out": "æŗ¨é”€", "log_out_all_devices": "æŗ¨é”€æ‰€æœ‰čŽžå¤‡", "logged_in_as": "äģĨ {user} čēĢäģŊį™ģåŊ•", @@ -1178,6 +1280,7 @@ "login_password_changed_success": "å¯†į æ›´æ–°æˆåŠŸ", "logout_all_device_confirmation": "įĄŽåŽščρäģŽæ‰€æœ‰čŽžå¤‡æŗ¨é”€īŧŸ", "logout_this_device_confirmation": "įĄŽåŽščρäģŽæœŦčŽžå¤‡æŗ¨é”€īŧŸ", + "logs": "æ—Ĩåŋ—", "longitude": "įģåēĻ", "look": "æ ˇåŧ", "loop_videos": "åžĒįŽ¯č§†éĸ‘", @@ -1185,6 +1288,7 @@ "main_branch_warning": "您åŊ“前äŊŋį”¨įš„æ˜¯åŧ€å‘į‰ˆīŧ›æˆ‘äģŦåŧē჈åģēčŽŽæ‚¨äŊŋį”¨æ­Ŗåŧå‘čĄŒį‰ˆīŧˆreleaseį‰ˆīŧ‰īŧ", "main_menu": "ä¸ģčœå•", "make": "å“į‰Œ", + "manage_geolocation": "įŽĄį†åæ ‡äŊįŊŽ", "manage_shared_links": "įŽĄį†å…ąäēĢ链æŽĨ", "manage_sharing_with_partners": "įŽĄį†ä¸ŽåŒäŧ´įš„å…ąäēĢ", "manage_the_app_settings": "įŽĄį†åē”į”¨čŽžįŊŽ", @@ -1193,8 +1297,7 @@ "manage_your_devices": "įŽĄį†åˇ˛į™ģåŊ•čŽžå¤‡", "manage_your_oauth_connection": "įŽĄį†æ‚¨įš„ OAuth įģ‘åޚ", "map": "地回", - "map_assets_in_bound": "{count} åŧ į…§į‰‡", - "map_assets_in_bounds": "{count} åŧ į…§į‰‡", + "map_assets_in_bounds": "{count, plural, =0 {æ­¤å¤„æ— į…§į‰‡} one {# åŧ į…§į‰‡} other {# åŧ į…§į‰‡}}", "map_cannot_get_user_location": "æ— æŗ•čŽˇå–į”¨æˆˇäŊįŊŽ", "map_location_dialog_yes": "是", "map_location_picker_page_use_location": "äŊŋį”¨æ­¤äŊįŊŽ", @@ -1202,7 +1305,6 @@ "map_location_service_disabled_title": "厚äŊæœåŠĄåˇ˛įρᔍ", "map_marker_for_images": "{country} {city}įš„å›žåƒåœ°å›žæ ‡čŽ°", "map_marker_with_image": "å¸Ļå›žåƒįš„åœ°å›žæ ‡čŽ°", - "map_no_assets_in_bounds": "æ­¤åŒēåŸŸä¸­æ˛Ąæœ‰į›¸å…ŗéĄšį›Ž", "map_no_location_permission_content": "需čρäŊįŊŽæƒé™æ‰čƒŊ昞į¤ē与åŊ“前äŊįŊŽį›¸å…ŗįš„éĄšį›Žã€‚čĻįŽ°åœ¨å°ąæŽˆäēˆäŊįŊŽæƒé™å—īŧŸ", "map_no_location_permission_title": "äŊįŊŽæƒé™čĸĢæ‹’įģ", "map_settings": "åœ°å›žčŽžįŊŽ", @@ -1213,7 +1315,7 @@ "map_settings_date_range_option_years": "{years} 嚴前", "map_settings_dialog_title": "åœ°å›žčŽžįŊŽ", "map_settings_include_show_archived": "包æ‹Ŧ厞åŊ’æĄŖéĄšį›Ž", - "map_settings_include_show_partners": "包åĢäŧ™äŧ´", + "map_settings_include_show_partners": "包åĢ同äŧ´", "map_settings_only_show_favorites": "äģ…æ˜žį¤ēæ”ļč—įš„éĄšį›Ž", "map_settings_theme_settings": "地回ä¸ģéĸ˜", "map_zoom_to_see_photos": "įŧŠå°äģĨæŸĨįœ‹éĄšį›Ž", @@ -1221,6 +1323,7 @@ "mark_as_read": "æ ‡čŽ°ä¸ē厞č¯ģ", "marked_all_as_read": "åˇ˛å…¨éƒ¨æ ‡čŽ°ä¸ē厞č¯ģ", "matches": "匚配", + "matching_assets": "匚配čĩ„äē§", "media_type": "åĒ’äŊ“įąģ型", "memories": "回åŋ†", "memories_all_caught_up": "åˇ˛å…¨éƒ¨įœ‹åŽŒ", @@ -1234,11 +1337,12 @@ "merge": "合åšļ", "merge_people": "合åšļäēēį‰Š", "merge_people_limit": "每æŦĄæœ€å¤šåĒčƒŊ合åšļ 5 ä¸Ēäēē", - "merge_people_prompt": "įĄŽåŽščρ合åšļčŋ™äē›äēē吗īŧŸč¯Ĩ操äŊœä¸å¯é€†īŧˆæ— æŗ•čĸĢæ’¤é”€īŧ‰ã€‚", + "merge_people_prompt": "是åĻįĄŽåŽšåˆåšļ所选äēēį‰ŠīŧŸč¯Ĩ操äŊœæ— æŗ•撤销。", "merge_people_successfully": "合åšļäēēį‰ŠæˆåŠŸ", - "merged_people_count": "厞合åšļ{count, plural, one {#ä¸Ēäēē} other {#ä¸Ēäēē}}", + "merged_people_count": "厞合åšļ{count, plural, one {# ä¸Ēäēē} other {# ä¸Ēäēē}}", "minimize": "最小化", "minute": "分", + "minutes": "分钟", "missing": "įŧēå¤ą", "model": "åž‹åˇ", "month": "月", @@ -1246,6 +1350,7 @@ "more": "更多", "move": "į§ģ动", "move_off_locked_folder": "į§ģå‡ē锁厚文äģļ多", + "move_to_lock_folder_action_prompt": "厞将 {count} 饚æˇģ加到锁厚文äģļ多", "move_to_locked_folder": "į§ģ动到锁厚文äģļ多", "move_to_locked_folder_confirmation": "čŋ™äē›į…§į‰‡å’Œč§†éĸ‘å°†äģŽæ‰€æœ‰į›¸å†Œä¸­į§ģ除īŧŒåĒčƒŊ在锁厚文äģļ多中æŸĨįœ‹", "moved_to_archive": "厞åŊ’æĄŖ {count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}", @@ -1257,6 +1362,10 @@ "my_albums": "æˆ‘įš„į›¸å†Œ", "name": "åį§°", "name_or_nickname": "åį§°æˆ–æ˜ĩį§°", + "network_requirement_photos_upload": "äŊŋį”¨čœ‚įĒæ•°æŽå¤‡äģŊᅧቇ", + "network_requirement_videos_upload": "äŊŋį”¨čœ‚įĒæ•°æŽå¤‡äģŊ视éĸ‘", + "network_requirements": "įŊ‘įģœčĻæą‚", + "network_requirements_updated": "įŊ‘įģœčĻæą‚å‘į”Ÿå˜åŒ–īŧŒæ­Ŗåœ¨é‡įŊŽå¤‡äģŊ队列", "networking_settings": "įŊ‘įģœ", "networking_subtitle": "įŽĄį†æœåŠĄå™¨æŽĨåŖčŽžįŊŽ", "never": "永不čŋ‡æœŸ", @@ -1266,6 +1375,7 @@ "new_person": "新äēēį‰Š", "new_pin_code": "æ–°įš„PIN᠁", "new_pin_code_subtitle": "čŋ™æ˜¯æ‚¨įŦŦ一æŦĄčŽŋ闎此锁厚文äģļ多。创åģē一ä¸ĒPIN᠁äģĨ厉全čŽŋé—Žæ­¤éĄĩéĸ", + "new_timeline": "新åģēæ—ļ间čŊ´", "new_user_created": "åˇ˛åˆ›åģēæ–°į”¨æˆˇ", "new_version_available": "æœ‰æ–°į‰ˆæœŦ发布å•Ļ", "newest_first": "最新äŧ˜å…ˆ", @@ -1277,21 +1387,27 @@ "no_albums_yet": "螌äŧŧ您čŋ˜æ˛Ąæœ‰åˆ›åģēį›¸å†Œã€‚", "no_archived_assets_message": "åŊ’æĄŖį…§į‰‡å’Œč§†éĸ‘äģĨäžŋåœ¨į…§į‰‡č§†å›žä¸­éšč—åŽƒäģŦ", "no_assets_message": "į‚šå‡ģ上äŧ æ‚¨įš„įŦŦ一åŧ į…§į‰‡", - "no_assets_to_show": "æ— éĄšį›Žåą•į¤ē", + "no_assets_to_show": "æ˛Ąæœ‰čĻæ˜žį¤ēįš„čĩ„äē§", "no_cast_devices_found": "æœĒæ‰žåˆ°æŠ•æ”žčŽžå¤‡", + "no_checksum_local": "æ˛Ąæœ‰å¯į”¨įš„æ ĄéĒŒå’Œ-æ— æŗ•čŽˇå–æœŦ地čĩ„äē§", + "no_checksum_remote": "æ˛Ąæœ‰å¯į”¨įš„æ ĄéĒŒå’Œ-æ— æŗ•čŽˇå–čŋœį¨‹čĩ„äē§", "no_duplicates_found": "æœĒå‘įŽ°é‡å¤éĄšã€‚", "no_exif_info_available": "æ˛Ąæœ‰å¯į”¨įš„ EXIF äŋĄæ¯", "no_explore_results_message": "上äŧ æ›´å¤šį…§į‰‡æĨæŽĸį´ĸ。", "no_favorites_message": "æˇģ加到æ”ļč—å¤šīŧŒåŋĢ速æŸĨ扞最äŊŗå›žį‰‡å’Œč§†éĸ‘", "no_libraries_message": "创åģē外部回å瓿ĨæŸĨįœ‹æ‚¨įš„į…§į‰‡å’Œč§†éĸ‘", + "no_local_assets_found": "æœĒæ‰žåˆ°å…ˇæœ‰æ­¤æ ĄéĒŒå’Œįš„æœŦ地čĩ„äē§", "no_locked_photos_message": "锁厚文äģļå¤šä¸­įš„į…§į‰‡å’Œč§†éĸ‘å°†čĸĢ隐藏īŧŒä¸äŧšåœ¨æ‚¨æĩč§ˆã€æœį´ĸ回å瓿—ļå‡ēįŽ°ã€‚", "no_name": "æœĒå‘Ŋ名", "no_notifications": "æ˛Ąæœ‰é€šįŸĨ", "no_people_found": "æœĒæ‰žåˆ°åŒšé…įš„äēēį‰Š", "no_places": "无äŊįŊŽ", + "no_remote_assets_found": "æœĒæ‰žåˆ°å…ˇæœ‰æ­¤æ ĄéĒŒå’Œįš„čŋœį¨‹čĩ„äē§", "no_results": "无į쓿žœ", "no_results_description": "å°č¯•äŊŋį”¨åŒäš‰č¯æˆ–æ›´é€šį”¨įš„å…ŗé”Žč¯", "no_shared_albums_message": "创åģēį›¸å†ŒäģĨå…ąäēĢį…§į‰‡å’Œč§†éĸ‘", + "no_uploads_in_progress": "æ˛Ąæœ‰æ­Ŗåœ¨čŋ›čĄŒįš„上äŧ ", + "not_available": "ä¸é€‚į”¨", "not_in_any_album": "不在äģģäŊ•į›¸å†Œä¸­", "not_selected": "æœĒ选拊", "note_apply_storage_label_to_previously_uploaded assets": "提į¤ēīŧščĻå°†å­˜å‚¨æ ‡į­žåē”ᔍäēŽäš‹å‰ä¸Šäŧ įš„éĄšį›ŽīŧŒéœ€čρčŋčĄŒ", @@ -1307,6 +1423,7 @@ "oauth": "OAuth", "official_immich_resources": "Immich 厘斚čĩ„æē", "offline": "įĻģįēŋ", + "offset": "偏į§ģ量", "ok": "įĄŽåŽš", "oldest_first": "最旧äŧ˜å…ˆ", "on_this_device": "åœ¨æ­¤čŽžå¤‡", @@ -1315,7 +1432,7 @@ "onboarding_privacy_description": "äģĨ下īŧˆå¯é€‰īŧ‰åŠŸčƒŊ䞝čĩ–å¤–éƒ¨æœåŠĄīŧŒå¯éšæ—ļåœ¨čŽžįŊŽä¸­įĻį”¨ã€‚", "onboarding_server_welcome_description": "čŽŠæˆ‘äģŦä¸ēæ‚¨čŽžįŊŽįŗģįģŸåšļ配įŊŽä¸€äē›é€šį”¨čŽžįŊŽã€‚", "onboarding_theme_description": "é€‰æ‹ŠæœåŠĄįš„éĸœč‰˛ä¸ģéĸ˜ã€‚į¨åŽå¯äģĨåœ¨čŽžįŊŽä¸­čŋ›čĄŒäŋŽæ”šã€‚", - "onboarding_user_welcome_description": "我äģŦå‡ē发吧īŧ", + "onboarding_user_welcome_description": "åŗåˆģčĩˇį¨‹īŧ", "onboarding_welcome_user": "æŦĸčŋŽīŧŒ{user}", "online": "在įēŋ", "only_favorites": "äģ…æ˜žį¤ē厞æ”ļ藏", @@ -1325,10 +1442,13 @@ "open_the_search_filters": "打åŧ€æœį´ĸčŋ‡æģ¤å™¨", "options": "选项", "or": "或", + "organize_into_albums": "æ•´į†æˆį›¸å†Œ", + "organize_into_albums_description": "äŊŋᔍåŊ“前同æ­Ĩ莞įŊŽå°†įŽ°æœ‰į…§į‰‡æ”žå…Ĩį›¸å†Œ", "organize_your_library": "æ•´į†æ‚¨įš„å›žåē“", "original": "原回", "other": "å…ļ厃", "other_devices": "å…ļåŽƒčŽžå¤‡", + "other_entities": "å…ļäģ–厞äŊ“", "other_variables": "å…ļ厃变量", "owned": "æˆ‘įš„", "owner": "æ‰€æœ‰č€…", @@ -1369,11 +1489,11 @@ "permanent_deletion_warning_setting_description": "åŊ“æ°¸äš…åˆ é™¤éĄšį›Žæ—ļ昞į¤ēč­Ļ告", "permanently_delete": "永䚅删除", "permanently_delete_assets_count": "永䚅删除{count, plural, one {éĄšį›Ž} other {éĄšį›Ž}}", - "permanently_delete_assets_prompt": "įĄŽåŽščĻæ°¸äš…åˆ é™¤ {count, plural, one {æ­¤éĄšį›ŽīŧŸ} other {čŋ™#ä¸ĒéĄšį›ŽīŧŸ}} č¯Ĩ操äŊœäŧšåŒæ—ļ将 {count, plural, one {厃} other {厃äģŦ}} äģŽå…￉€åœ¨į›¸å†Œä¸­į§ģ除。", + "permanently_delete_assets_prompt": "įĄŽåŽščĻæ°¸äš…åˆ é™¤{count, plural, other {čŋ™#ä¸ĒéĄšį›ŽīŧŸ}}此操äŊœäŧšä¸€åšļ将{count, plural, one {厃} other {厃äģŦ}}äģŽå…￉€åąžį›¸å†Œä¸­į§ģ除。", "permanently_deleted_asset": "æ°¸äš…åˆ é™¤įš„éĄšį›Ž", "permanently_deleted_assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛åˆ é™¤", "permission": "权限", - "permission_empty": "æ‚¨įš„æƒé™ä¸čƒŊä¸ēįŠē", + "permission_empty": "权限不čƒŊä¸ēįŠē", "permission_onboarding_back": "čŋ”回", "permission_onboarding_continue_anyway": "äģį„ļįģ§įģ­", "permission_onboarding_get_started": "åŧ€å§‹äŊŋᔍ", @@ -1383,6 +1503,9 @@ "permission_onboarding_permission_limited": "权限受限īŧščĻčŽŠ Immich 备äģŊå’ŒįŽĄį†æ‚¨įš„æ•´ä¸Ē回å瓿”ļ藏īŧŒč¯ˇåœ¨â€œčŽžįŊŽâ€ä¸­æŽˆäēˆį…§į‰‡å’Œč§†éĸ‘权限。", "permission_onboarding_request": "Immich 需čĻæƒé™æ‰čƒŊæŸĨįœ‹æ‚¨įš„į…§į‰‡å’Œč§†éĸ‘。", "person": "äēēį‰Š", + "person_age_months": "{months, plural, one {# ä¸Ē月} other {# ä¸Ē月}}", + "person_age_year_months": "1 垁, {months, plural, one {# ä¸Ē月} other {# ä¸Ē月}}", + "person_age_years": "{years, plural, other {# 垁}}", "person_birthdate": "å‡ēį”ŸäēŽ{date}", "person_hidden": "{name}{hidden, select, true {īŧˆåˇ˛éšč—īŧ‰} other {}}", "photo_shared_all_users": "įœ‹čĩˇæĨæ‚¨åˇ˛ä¸Žæ‰€æœ‰į”¨æˆˇå…ąäēĢä熿­¤į›¸å†ŒīŧŒæˆ–č€…æ‚¨æ šæœŦæ˛Ąæœ‰äģģäŊ•į”¨æˆˇå¯å…ąäēĢ。", @@ -1406,6 +1529,7 @@ "port": "įĢ¯åŖ", "preferences_settings_subtitle": "įŽĄį†åē”į”¨įš„ååĨŊ莞įŊŽ", "preferences_settings_title": "偏åĨŊ莞įŊŽ", + "preparing": "准备中", "preset": "éĸ„莞", "preview": "éĸ„č§ˆ", "previous": "上一ä¸Ē", @@ -1422,6 +1546,7 @@ "profile_drawer_client_out_of_date_minor": "åŽĸæˆˇįĢ¯æœ‰å°į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_client_server_up_to_date": "åŽĸæˆˇįĢ¯å’ŒæœåŠĄį̝éƒŊæ˜¯æœ€æ–°įš„", "profile_drawer_github": "GitHub", + "profile_drawer_readonly_mode": "åĒč¯ģæ¨Ąåŧåˇ˛å¯į”¨ã€‚é•ŋæŒ‰į”¨æˆˇå¤´åƒå›žæ ‡é€€å‡ē。", "profile_drawer_server_out_of_date_major": "æœåŠĄįĢ¯æœ‰å¤§į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_drawer_server_out_of_date_minor": "æœåŠĄįĢ¯æœ‰å°į‰ˆæœŦ升įē§īŧŒč¯ˇå°ŊåŋĢ升įē§č‡ŗæœ€æ–°į‰ˆã€‚", "profile_image_of_user": "{user}įš„ä¸Ēäēēčĩ„æ–™å›žį‰‡", @@ -1460,12 +1585,17 @@ "purchase_server_description_2": "æ”¯æŒč€…įŠļ态", "purchase_server_title": "æœåŠĄå™¨", "purchase_settings_server_activated": "æœåŠĄå™¨äē§å“å¯†é’Ĩæ­Ŗåœ¨į”ąįŽĄį†å‘˜įŽĄį†", + "query_asset_id": "æŸĨč¯ĸčĩ„äē§ID", + "queue_status": "排队中 {count}/{total}", "rating": "星įē§", "rating_clear": "删除星įē§", "rating_count": "{count, plural, one {#星} other {#星}}", "rating_description": "在äŋĄæ¯éĸæŋä¸­åą•į¤ē EXIF 星įē§", "reaction_options": "回åē”选饚", "read_changelog": "阅č¯ģ更新æ—Ĩåŋ—", + "readonly_mode_disabled": "åĒč¯ģæ¨Ąåŧåˇ˛įρᔍ", + "readonly_mode_enabled": "åĒč¯ģæ¨Ąåŧåˇ˛å¯į”¨", + "ready_for_upload": "准备上äŧ ", "reassign": "重新指洞", "reassigned_assets_to_existing_person": "重新指洞{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}到{name, select, null {åˇ˛å­˜åœ¨įš„äēēį‰Š} other {{name}}}", "reassigned_assets_to_new_person": "重新指洞{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˆ°æ–°įš„äēēį‰Š", @@ -1488,6 +1618,9 @@ "refreshing_faces": "æ­Ŗåœ¨éĸéƒ¨é‡æ–°č¯†åˆĢ", "refreshing_metadata": "æ­Ŗåœ¨åˆˇæ–°å…ƒæ•°æŽ", "regenerating_thumbnails": "æ­Ŗåœ¨é‡æ–°į”ŸæˆįŧŠį•Ĩ回", + "remote": "čŋœį¨‹", + "remote_assets": "čŋœį¨‹éĄšį›Ž", + "remote_media_summary": "čŋœį¨‹åĒ’äŊ“摘čρ", "remove": "į§ģ除", "remove_assets_album_confirmation": "įĄŽåŽščρäģŽå›žåē“中į§ģ除{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}īŧŸ", "remove_assets_shared_link_confirmation": "įĄŽåŽščρäģŽå…ąäēĢ链æŽĨ中į§ģ除{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}īŧŸ", @@ -1495,7 +1628,9 @@ "remove_custom_date_range": "取æļˆč‡Ē厚䚉æ—ĨæœŸčŒƒå›´", "remove_deleted_assets": "åŊģåē•删除文äģļ", "remove_from_album": "äģŽį›¸å†Œä¸­į§ģ除", + "remove_from_album_action_prompt": "äģŽį›¸å†Œä¸­į§ģ除äē† {count} 饚", "remove_from_favorites": "į§ģå‡ēæ”ļ藏", + "remove_from_lock_folder_action_prompt": "厞äģŽé”åŽšįš„æ–‡äģļ多中į§ģ除 {count} 饚", "remove_from_locked_folder": "äģŽé”åŽšæ–‡äģļ多中į§ģ除", "remove_from_locked_folder_confirmation": "æ‚¨įĄŽåŽščρ将čŋ™äē›į…§į‰‡å’Œč§†éĸ‘į§ģå‡ē锁厚文äģļ多吗īŧŸį§ģå‡ē后厃äģŦ将äŧšåœ¨æ‚¨įš„回åē“ä¸­å¯č§ã€‚", "remove_from_shared_link": "äģŽå…ąäēĢ链æŽĨ中į§ģ除", @@ -1523,19 +1658,29 @@ "reset_password": "重įŊŽå¯†į ", "reset_people_visibility": "重įŊŽäēēį‰Šč¯†åˆĢ", "reset_pin_code": "重įŊŽPIN᠁", + "reset_pin_code_description": "åĻ‚æžœæ‚¨åŋ˜čްä熿‚¨įš„PIN᠁īŧŒæ‚¨å¯äģĨ联įŗģæœåŠĄå™¨įŽĄį†å‘˜čŋ›čĄŒé‡įŊŽ", + "reset_pin_code_success": "成功重įŊŽPIN᠁", + "reset_pin_code_with_password": "您始įģˆå¯äģĨäŊŋį”¨æ‚¨įš„å¯†į æĨ重įŊŽæ‚¨įš„PIN᠁", + "reset_sqlite": "重įŊŽ SQLite 数捎åē“", + "reset_sqlite_confirmation": "æ‚¨įĄŽåŽščĻé‡įŊŽ SQLite 数捎åē“吗īŧŸæ‚¨éœ€čĻæŗ¨é”€åšļ重新į™ģåŊ•才čƒŊ重新同æ­Ĩ数捎", + "reset_sqlite_success": "åˇ˛æˆåŠŸé‡įŊŽ SQLite 数捎åē“", "reset_to_default": "æĸ复éģ˜čޤå€ŧ", "resolve_duplicates": "å¤„į†é‡å¤éĄš", "resolved_all_duplicates": "å¤„į†æ‰€æœ‰é‡å¤éĄš", "restore": "æĸ复", "restore_all": "æĸ复全部", + "restore_trash_action_prompt": "äģŽå›žæ”ļį̙䏭æĸ复äē† {count} 饚", "restore_user": "æĸå¤į”¨æˆˇ", "restored_asset": "厞æĸå¤éĄšį›Ž", "resume": "įģ§įģ­", + "resume_paused_jobs": "įģ§įģ­ {count, plural, one {# åˇ˛æš‚åœįš„äģģåŠĄ} other {# åˇ˛æš‚åœįš„äģģåŠĄ}}", "retry_upload": "重新上äŧ ", "review_duplicates": "æŖ€æŸĨé‡å¤éĄš", + "review_large_files": "æŸĨįœ‹å¤§æ–‡äģļ", "role": "é€‰æ‹Šį”¨æˆˇæƒé™", "role_editor": "可įŧ–čž‘", "role_viewer": "äģ…æŸĨįœ‹", + "running": "æ­Ŗåœ¨čŋčĄŒ", "save": "äŋå­˜", "save_to_gallery": "äŋå­˜åˆ°å›žåē“", "saved_api_key": "厞äŋå­˜įš„ API 密é’Ĩ", @@ -1615,13 +1760,14 @@ "select_from_computer": "äģŽčŽĄįŽ—æœē中选拊", "select_keep_all": "全部äŋį•™", "select_library_owner": "选拊回å瓿‰€æœ‰č€…", - "select_new_face": "选拊新éĸ孔", + "select_new_face": "选拊新äēē脸", "select_person_to_tag": "选拊čĻæ ‡čŽ°įš„äēēį‰Š", "select_photos": "é€‰æ‹Šį…§į‰‡", "select_trash_all": "全部删除", "select_user_for_sharing_page_err_album": "创åģēį›¸å†Œå¤ąč´Ĩ", "selected": "åˇ˛é€‰æ‹Š", "selected_count": "{count, plural, other {#éĄšåˇ˛é€‰æ‹Š}}", + "selected_gps_coordinates": "åˇ˛é€‰åŽšįš„GPS坐标", "send_message": "发送æļˆæ¯", "send_welcome_email": "发送æŦĸčŋŽé‚Žäģļ", "server_endpoint": "æœåŠĄå™¨ URL", @@ -1666,14 +1812,15 @@ "settings_require_restart": "č¯ˇé‡å¯ Immich äģĨäŊŋ莞įŊŽį”Ÿæ•ˆ", "settings_saved": "莞įŊŽåˇ˛äŋå­˜", "setup_pin_code": "莞įŊŽPIN᠁", - "share": "å…ąäēĢ", + "share": "分äēĢ", + "share_action_prompt": "åˇ˛å…ąäēĢ {count} éĄšį›Ž", "share_add_photos": "æˇģåŠ éĄšį›Ž", "share_assets_selected": "{count} åˇ˛é€‰æ‹Š", - "share_dialog_preparing": "æ­Ŗåœ¨å‡†å¤‡...", + "share_dialog_preparing": "准备中...", "share_link": "分äēĢ链æŽĨ", "shared": "å…ąäēĢ", "shared_album_activities_input_disable": "蝄čŽē厞įρᔍ", - "shared_album_activity_remove_content": "æ‚¨įĄŽåŽščĻåˆ é™¤æ­¤æ´ģ动吗īŧŸ", + "shared_album_activity_remove_content": "是åĻ删除此æ´ģ动īŧŸ", "shared_album_activity_remove_title": "删除æ´ģ动", "shared_album_section_people_action_error": "退å‡ē/åˆ é™¤į›¸å†Œå¤ąč´Ĩ", "shared_album_section_people_action_leave": "äģŽį›¸å†Œä¸­åˆ é™¤į”¨æˆˇ", @@ -1688,6 +1835,7 @@ "shared_link_clipboard_copied_massage": "复åˆļ到å‰Ēč´´æŋ", "shared_link_clipboard_text": "链æŽĨīŧš{link}\n坆᠁īŧš{password}", "shared_link_create_error": "创åģēå…ąäēĢ链æŽĨå‡ē错", + "shared_link_custom_url_description": "äŊŋᔍč‡Ē厚䚉URLčŽŋé—Žæ­¤å…ąäēĢ链æŽĨ", "shared_link_edit_description_hint": "įŧ–čž‘å…ąäēĢæčŋ°", "shared_link_edit_expire_after_option_day": "1夊", "shared_link_edit_expire_after_option_days": "{count} 夊", @@ -1695,7 +1843,7 @@ "shared_link_edit_expire_after_option_hours": "{count} 小æ—ļ", "shared_link_edit_expire_after_option_minute": "1分钟", "shared_link_edit_expire_after_option_minutes": "{count} 分钟", - "shared_link_edit_expire_after_option_months": "{count} 月鞄", + "shared_link_edit_expire_after_option_months": "{count} ä¸Ē月", "shared_link_edit_expire_after_option_year": "{count} åš´", "shared_link_edit_password_hint": "输å…Ĩå…ąäēĢ坆᠁", "shared_link_edit_submit_button": "更新铞æŽĨ", @@ -1713,6 +1861,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "įŽĄį†å…ąäēĢ链æŽĨ", "shared_link_options": "å…ąäēĢ链æŽĨ选项", + "shared_link_password_description": "需čĻå¯†į æ‰čƒŊčŽŋé—Žæ­¤å…ąäēĢ链æŽĨ", "shared_links": "å…ąäēĢ链æŽĨ", "shared_links_description": "通čŋ‡é“žæŽĨ分äēĢį…§į‰‡å’Œč§†éĸ‘", "shared_photos_and_videos_count": "{assetCount, plural, other {#éĄšåˇ˛å…ąäēĢᅧቇ&视éĸ‘。}}", @@ -1747,10 +1896,11 @@ "show_slideshow_transition": "昞į¤ēåšģၝቇčŋ‡æ¸Ąæ•ˆæžœ", "show_supporter_badge": "æ”¯æŒč€…åžŊįĢ ", "show_supporter_badge_description": "åą•į¤ēæ”¯æŒč€…åžŊįĢ ", + "show_text_search_menu": "昞į¤ē文æœŦ搜į´ĸčœå•", "shuffle": "随æœē", "sidebar": "äž§čžšæ ", "sidebar_display_description": "åœ¨äž§čžšæ ä¸­æ˜žį¤ē链æŽĨ", - "sign_out": "æŗ¨é”€", + "sign_out": "退å‡ēį™ģåŊ•", "sign_up": "æŗ¨å†Œ", "size": "大小", "skip_to_content": "莺čŊŦ到内厚", @@ -1762,12 +1912,14 @@ "sort_created": "创åģēæ—Ĩ期", "sort_items": "éĄšį›Žæ•°é‡", "sort_modified": "äŋŽæ”šæ—Ĩ期", + "sort_newest": "æœ€æ–°į…§į‰‡", "sort_oldest": "æœ€æ—Šįš„į…§į‰‡", "sort_people_by_similarity": "æŒ‰į›¸äŧŧ性寚äēēį‰Ščŋ›čĄŒæŽ’åē", "sort_recent": "æœ€æ–°įš„į…§į‰‡", "sort_title": "标éĸ˜", "source": "GitHub æēäģŖį ", "stack": "堆叠", + "stack_action_prompt": "{count} ä¸Ēåˇ˛å †å ", "stack_duplicates": "å †å é‡å¤éĄšį›Ž", "stack_select_one_photo": "ä¸ē堆叠选拊一åŧ åą•į¤ē回", "stack_selected_photos": "å †å é€‰åŽšįš„į…§į‰‡", @@ -1775,6 +1927,7 @@ "stacktrace": "å †æ ˆčˇŸč¸Ē", "start": "åŧ€å§‹", "start_date": "åŧ€å§‹æ—Ĩ期", + "start_date_before_end_date": "åŧ€å§‹æ—Ĩ期åŋ…éĄģ在į쓿Ÿæ—Ĩ期䚋前", "state": "ᜁäģŊ", "status": "įŠļ态", "stop_casting": "停æ­ĸ投攞", @@ -1787,15 +1940,20 @@ "storage_quota": "存储配éĸ", "storage_usage": "厞ᔍīŧš{used}/{available}", "submit": "提äē¤", + "success": "成功", "suggestions": "åģē莎", "sunrise_on_the_beach": "æĩˇæģŠä¸Šįš„æ—Ĩå‡ē", "support": "支持", "support_and_feedback": "支持和反éψ", - "support_third_party_description": "æ‚¨įš„ Immich åŽ‰čŖ…į¨‹åēæ˜¯į”ąįŦŦä¸‰æ–šæ‰“åŒ…įš„ã€‚æ‚¨é‡åˆ°įš„é—Žéĸ˜å¯čƒŊæ˜¯į”ąčŊ¯äģļ包åŧ•čĩˇįš„īŧŒå› æ­¤č¯ˇäŧ˜å…ˆäŊŋᔍ䏋éĸįš„é“žæŽĨ提å‡ē Issue 或 Bug。", + "support_third_party_description": "æ‚¨įš„ Immich åŽ‰čŖ…į¨‹åēį”ąįŦŦ三斚打包。åŊ“前闎éĸ˜å¯čƒŊį”ąč¯ĨčŊ¯äģļ包åŧ•čĩˇīŧŒå› æ­¤č¯ˇäŧ˜å…ˆé€ščŋ‡ä¸‹æ–šé“žæŽĨ向寚åē”äŊœč€…提äē¤ Issue。", "swap_merge_direction": "äē’æĸ合åšļ斚向", "sync": "同æ­Ĩ", "sync_albums": "同æ­Ĩį›¸å†Œ", "sync_albums_manual_subtitle": "将所有上äŧ įš„视éĸ‘å’Œį…§į‰‡åŒæ­Ĩåˆ°é€‰åŽšįš„å¤‡äģŊį›¸å†Œ", + "sync_local": "同æ­ĨæœŦ地", + "sync_remote": "同æ­Ĩčŋœį¨‹", + "sync_status": "同æ­ĨįŠļ态", + "sync_status_subtitle": "æŸĨįœ‹å’ŒįŽĄį†åŒæ­ĨįŗģįģŸ", "sync_upload_album_setting_subtitle": "创åģēį…§į‰‡å’Œč§†éĸ‘åšļ上äŧ åˆ° Immich ä¸Šįš„é€‰åŽšį›¸å†Œä¸­", "tag": "æ ‡į­ž", "tag_assets": "æ ‡čŽ°éĄšį›Ž", @@ -1806,6 +1964,7 @@ "tag_updated": "åˇ˛æ›´æ–°æ ‡į­žīŧš{tag}", "tagged_assets": "{count, plural, one {# ä¸ĒéĄšį›Ž} other {# ä¸ĒéĄšį›Ž}}čĸĢåŠ ä¸Šæ ‡į­ž", "tags": "æ ‡į­ž", + "tap_to_run_job": "į‚šå‡ģčŋčĄŒäģģåŠĄ", "template": "æ¨Ąį‰ˆ", "theme": "ä¸ģéĸ˜", "theme_selection": "ä¸ģéĸ˜é€‰éĄš", @@ -1832,32 +1991,38 @@ "to_change_password": "äŋŽæ”šå¯†į ", "to_favorite": "æ”ļ藏", "to_login": "į™ģåŊ•", + "to_multi_select": "多选", "to_parent": "čŋ”回上一įē§", + "to_select": "选拊", "to_trash": "攞å…Ĩ回æ”ļįĢ™", "toggle_settings": "切æĸ莞įŊŽ", "total": "æ€ģ莥", "total_usage": "æ€ģį”¨é‡", "trash": "回æ”ļįĢ™", + "trash_action_prompt": "厞将 {count} 饚į§ģč‡ŗå›žæ”ļįĢ™", "trash_all": "全部删除", "trash_count": "删除{count, number}饚", "trash_delete_asset": "å°†éĄšį›Žæ”žå…Ĩ回æ”ļįĢ™/删除", "trash_emptied": "įŠē回æ”ļįĢ™", "trash_no_results_message": "åˆ é™¤įš„į…§į‰‡å’Œč§†éĸ‘å°†åœ¨æ­¤å¤„åą•į¤ē。", "trash_page_delete_all": "删除全部", - "trash_page_empty_trash_dialog_content": "是åĻ清įŠē回æ”ļįĢ™īŧŸčŋ™äē›éĄšį›Žå°†čĸĢäģŽ Immich 中永䚅删除", + "trash_page_empty_trash_dialog_content": "是åĻ清įŠē回æ”ļįĢ™īŧŸčŋ™äē›éĄšį›Žå°†äģŽ Immich 中永䚅删除", "trash_page_info": "回æ”ļįĢ™ä¸­éĄšį›Žå°†åœ¨ {days} 夊后永䚅删除", "trash_page_no_assets": "æš‚æ— åˇ˛åˆ é™¤éĄšį›Ž", "trash_page_restore_all": "æĸ复全部", "trash_page_select_assets_btn": "é€‰æ‹ŠéĄšį›Ž", "trash_page_title": "回æ”ļįĢ™ ({count})", "trashed_items_will_be_permanently_deleted_after": "回æ”ļįĢ™ä¸­įš„éĄšį›Žå°†åœ¨{days, plural, one {#夊} other {#夊}}后čĸĢæ°¸äš…删除。", + "troubleshoot": "故障排除", "type": "įąģ型", "unable_to_change_pin_code": "æ— æŗ•äŋŽæ”šPIN᠁", "unable_to_setup_pin_code": "æ— æŗ•čŽžįŊŽPIN᠁", "unarchive": "取æļˆåŊ’æĄŖ", + "unarchive_action_prompt": "厞äģŽåŊ’æĄŖä¸­į§ģ除 {count} 饚", "unarchived_count": "{count, plural, other {取æļˆåŊ’æĄŖ # 饚}}", "undo": "撤销", "unfavorite": "取æļˆæ”ļ藏", + "unfavorite_action_prompt": "厞äģŽæ”ļ藏中į§ģ除 {count} 饚", "unhide_person": "昞į¤ēäēēį‰Š", "unknown": "æœĒįŸĨ", "unknown_country": "æœĒįŸĨįš„å›ŊåŽļ", @@ -1875,15 +2040,21 @@ "unselect_all_duplicates": "取æļˆé€‰æ‹Šæ‰€æœ‰é‡å¤éĄš", "unselect_all_in": "取æļˆé€‰æ‹Š {group} ä¸­įš„æ‰€æœ‰å†…åŽš", "unstack": "取æļˆå †å ", + "unstack_action_prompt": "{count} ä¸ĒæœĒ堆叠", "unstacked_assets_count": "{count, plural, one {#ä¸ĒéĄšį›Ž} other {#ä¸ĒéĄšį›Ž}}åˇ˛å–æļˆå †å ", + "untagged": "æ— æ ‡į­ž", "up_next": "下一ä¸Ē", + "update_location_action_prompt": "更新 {count} ä¸Ē所选čĩ„äē§įš„äŊįŊŽīŧš", "updated_at": "åˇ˛æ›´æ–°", "updated_password": "æ›´æ–°å¯†į ", "upload": "上äŧ ", + "upload_action_prompt": "有{count}ä¸Ē垅上äŧ ", "upload_concurrency": "上äŧ åšļ发", + "upload_details": "上äŧ č¯Ļ情", "upload_dialog_info": "是åĻčĻå°†æ‰€é€‰éĄšį›Žå¤‡äģŊåˆ°æœåŠĄå™¨īŧŸ", "upload_dialog_title": "上äŧ éĄšį›Ž", "upload_errors": "上äŧ åŽŒæˆīŧŒå‡ēįŽ°{count, plural, one {#ä¸Ē错蝝} other {#ä¸Ē错蝝}}īŧŒåˆˇæ–°éĄĩéĸäģĨæŸĨįœ‹æ–°ä¸Šäŧ įš„éĄšį›Žã€‚", + "upload_finished": "上äŧ åŽŒæˆ", "upload_progress": "削äŊ™{remaining, number} - 厞处ᐆ {processed, number}/{total, number}", "upload_skipped_duplicates": "厞莺čŋ‡{count, plural, one {#ä¸Ēé‡å¤éĄš} other {#ä¸Ēé‡å¤éĄš}}", "upload_status_duplicates": "é‡å¤éĄš", @@ -1892,6 +2063,7 @@ "upload_success": "上äŧ æˆåŠŸīŧŒåˆˇæ–°éĄĩéĸæŸĨįœ‹æ–°ä¸Šäŧ įš„éĄšį›Žã€‚", "upload_to_immich": "上äŧ č‡ŗ Immichīŧˆ{count}īŧ‰", "uploading": "æ­Ŗåœ¨ä¸Šäŧ ", + "uploading_media": "文äģļ上äŧ ä¸­", "url": "URL", "usage": "į”¨é‡", "use_biometric": "äŊŋį”¨į”Ÿį‰Šč¯†åˆĢ", @@ -1912,6 +2084,7 @@ "user_usage_stats_description": "æŸĨįœ‹å¸æˆˇäŊŋᔍįģŸčŽĄäŋĄæ¯", "username": "į”¨æˆˇå", "users": "į”¨æˆˇ", + "users_added_to_album_count": "厞将 {count, plural, one {# ä¸Ēį”¨æˆˇ} other {# ä¸Ēį”¨æˆˇ}} æˇģåŠ åˆ°į›¸å†Œ", "utilities": "åŽžį”¨åˇĨå…ˇ", "validate": "énj蝁", "validate_endpoint_error": "č¯ˇčž“å…Ĩæœ‰æ•ˆįš„ URL", @@ -1930,6 +2103,7 @@ "view_album": "æŸĨįœ‹į›¸å†Œ", "view_all": "æŸĨįœ‹å…¨éƒ¨", "view_all_users": "æŸĨįœ‹å…¨éƒ¨į”¨æˆˇ", + "view_details": "æŸĨįœ‹č¯Ļ情", "view_in_timeline": "在æ—ļ间čŊ´ä¸­æŸĨįœ‹", "view_link": "æŸĨįœ‹é“žæŽĨ", "view_links": "æŸĨįœ‹é“žæŽĨ", @@ -1937,6 +2111,7 @@ "view_next_asset": "æŸĨįœ‹ä¸‹ä¸€éĄš", "view_previous_asset": "æŸĨįœ‹ä¸Šä¸€éĄš", "view_qr_code": "æŸĨįœ‹äēŒįģ´į ", + "view_similar_photos": "æŸĨįœ‹į›¸äŧŧᅧቇ", "view_stack": "æŸĨįœ‹å †å éĄšį›Ž", "view_user": "æŸĨįœ‹į”¨æˆˇ", "viewer_remove_from_stack": "äģŽå †å ä¸­į§ģ除", @@ -1955,5 +2130,6 @@ "yes": "是", "you_dont_have_any_shared_links": "æ‚¨æ˛Ąæœ‰äģģäŊ•å…ąäēĢ链æŽĨ", "your_wifi_name": "æ‚¨įš„ Wi-Fi åį§°", - "zoom_image": "įŧŠæ”žå›žåƒ" + "zoom_image": "įŧŠæ”žå›žåƒ", + "zoom_to_bounds": "įŧŠæ”žåˆ°čžšį•Œ" } diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 372982af67..e4ed643375 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:ce3b954c9285a7a145cba620bae03db836ab890b6b9e0d05a3ca522ea00dfbc9 AS builder-cpu +FROM python:3.11-bookworm@sha256:fc1f2e357c307c4044133952b203e66a47e7726821a664f603a180a0c5823844 AS builder-cpu FROM builder-cpu AS builder-openvino @@ -22,7 +22,7 @@ FROM builder-cpu AS builder-rknn # Warning: 25GiB+ disk space required to pull this image # TODO: find a way to reduce the image size -FROM rocm/dev-ubuntu-22.04:6.3.4-complete@sha256:1f7e92ca7e3a3785680473329ed1091fc99db3e90fcb3a1688f2933e870ed76b AS builder-rocm +FROM rocm/dev-ubuntu-22.04:6.4.3-complete@sha256:1f7e92ca7e3a3785680473329ed1091fc99db3e90fcb3a1688f2933e870ed76b AS builder-rocm # renovate: datasource=github-releases depName=Microsoft/onnxruntime ARG ONNXRUNTIME_VERSION="v1.20.1" @@ -59,7 +59,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:9653efd4380d5a0e5511e337dcfc3b8ba5bc4e6ea7fa3be7716598261d5503fa /uv /uvx /bin/ +COPY --from=ghcr.io/astral-sh/uv:0.8.15@sha256:a5727064a0de127bdb7c9d3c1383f3a9ac307d9f2d8a391edc7896c54289ced0 /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 \ @@ -68,11 +68,11 @@ RUN if [ "$DEVICE" = "rocm" ]; then \ uv pip install /opt/onnxruntime_rocm-*.whl; \ fi -FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-cpu +FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-cpu ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 -FROM python:3.11-slim-bookworm@sha256:9e1912aab0a30bbd9488eb79063f68f42a68ab0946cbe98fecf197fe5b085506 AS prod-openvino +FROM python:3.11-slim-bookworm@sha256:873f91540d53b36327ed4fb018c9669107a4e2a676719720edb4209c4b15d029 AS prod-openvino RUN apt-get update && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ @@ -99,7 +99,7 @@ COPY --from=builder-cuda /usr/local/bin/python3 /usr/local/bin/python3 COPY --from=builder-cuda /usr/local/lib/python3.11 /usr/local/lib/python3.11 COPY --from=builder-cuda /usr/local/lib/libpython3.11.so /usr/local/lib/libpython3.11.so -FROM rocm/dev-ubuntu-22.04:6.3.4-complete@sha256:1f7e92ca7e3a3785680473329ed1091fc99db3e90fcb3a1688f2933e870ed76b AS prod-rocm +FROM rocm/dev-ubuntu-22.04:6.4.3-complete@sha256:1f7e92ca7e3a3785680473329ed1091fc99db3e90fcb3a1688f2933e870ed76b AS prod-rocm FROM prod-cpu AS prod-armnn diff --git a/machine-learning/immich_ml/__main__.py b/machine-learning/immich_ml/__main__.py index d15b0fb321..8d575a58d5 100644 --- a/machine-learning/immich_ml/__main__.py +++ b/machine-learning/immich_ml/__main__.py @@ -1,6 +1,7 @@ import os import signal import subprocess +from ipaddress import ip_address from pathlib import Path from .config import log, non_prefixed_settings, settings @@ -12,6 +13,19 @@ else: module_dir = Path(__file__).parent + +def is_ipv6(host: str) -> bool: + try: + return ip_address(host).version == 6 + except ValueError: + return False + + +bind_host = non_prefixed_settings.immich_host +if is_ipv6(bind_host): + bind_host = f"[{bind_host}]" +bind_address = f"{bind_host}:{non_prefixed_settings.immich_port}" + try: with subprocess.Popen( [ @@ -24,7 +38,7 @@ try: "-c", module_dir / "gunicorn_conf.py", "-b", - f"{non_prefixed_settings.immich_host}:{non_prefixed_settings.immich_port}", + bind_address, "-w", str(settings.workers), "-t", diff --git a/machine-learning/immich_ml/models/transforms.py b/machine-learning/immich_ml/models/transforms.py index e70763a07f..3f9d93bed3 100644 --- a/machine-learning/immich_ml/models/transforms.py +++ b/machine-learning/immich_ml/models/transforms.py @@ -36,7 +36,7 @@ def to_numpy(img: Image.Image) -> NDArray[np.float32]: def normalize( img: NDArray[np.float32], mean: float | NDArray[np.float32], std: float | NDArray[np.float32] ) -> NDArray[np.float32]: - return np.divide(img - mean, std, dtype=np.float32) + return (img - mean) / std def get_pil_resampling(resample: str) -> Image.Resampling: @@ -58,11 +58,13 @@ def decode_pil(image_bytes: bytes | IO[bytes] | Image.Image) -> Image.Image: def decode_cv2(image_bytes: NDArray[np.uint8] | bytes | Image.Image) -> NDArray[np.uint8]: - if isinstance(image_bytes, bytes): - image_bytes = decode_pil(image_bytes) # pillow is much faster than cv2 - if isinstance(image_bytes, Image.Image): - return pil_to_cv2(image_bytes) - return image_bytes + match image_bytes: + case bytes() | memoryview() | bytearray(): + return pil_to_cv2(decode_pil(image_bytes)) # pillow is much faster than cv2 + case Image.Image(): + return pil_to_cv2(image_bytes) + case _: + return image_bytes def clean_text(text: str, canonicalize: bool = False) -> str: diff --git a/machine-learning/immich_ml/schemas.py b/machine-learning/immich_ml/schemas.py index 6f9076807d..7ad1e215d6 100644 --- a/machine-learning/immich_ml/schemas.py +++ b/machine-learning/immich_ml/schemas.py @@ -112,8 +112,4 @@ def has_profiling(obj: Any) -> TypeGuard[HasProfiling]: return hasattr(obj, "profiling") and isinstance(obj.profiling, dict) -def is_ndarray(obj: Any, dtype: "type[np._DTypeScalar_co]") -> "TypeGuard[npt.NDArray[np._DTypeScalar_co]]": - return isinstance(obj, np.ndarray) and obj.dtype == dtype - - T = TypeVar("T") diff --git a/machine-learning/pyproject.toml b/machine-learning/pyproject.toml index ca8f432ae2..9eb4f7c0f6 100644 --- a/machine-learning/pyproject.toml +++ b/machine-learning/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "immich-ml" -version = "1.129.0" +version = "2.0.1" description = "" authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }] requires-python = ">=3.10,<4.0" @@ -12,6 +12,7 @@ dependencies = [ "gunicorn>=21.1.0", "huggingface-hub>=0.20.1,<1.0", "insightface>=0.7.3,<1.0", + "numpy<2", "opencv-python-headless>=4.7.0.72,<5.0", "orjson>=3.9.5", "pillow>=9.5.0,<11.0", diff --git a/machine-learning/scripts/healthcheck.py b/machine-learning/scripts/healthcheck.py index 82c6cad790..38c0a522f1 100644 --- a/machine-learning/scripts/healthcheck.py +++ b/machine-learning/scripts/healthcheck.py @@ -1,12 +1,22 @@ import os import sys +from ipaddress import ip_address import requests port = os.getenv("IMMICH_PORT", 3003) host = os.getenv("IMMICH_HOST", "0.0.0.0") + +def is_ipv6(host: str) -> bool: + try: + return ip_address(host).version == 6 + except ValueError: + return False + + host = "localhost" if host == "0.0.0.0" else host +host = f"[{host}]" if is_ipv6(host) else host try: response = requests.get(f"http://{host}:{port}/ping", timeout=2) diff --git a/machine-learning/uv.lock b/machine-learning/uv.lock index 7da2fd3920..c30120a40b 100644 --- a/machine-learning/uv.lock +++ b/machine-learning/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10, <4.0" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'darwin'", @@ -38,7 +38,8 @@ dependencies = [ { name = "pyyaml" }, { name = "qudida" }, { name = "scikit-image" }, - { name = "scipy" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] 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 = [ @@ -69,6 +70,15 @@ wheels = [ { 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 = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + [[package]] name = "bidict" version = "0.23.1" @@ -357,8 +367,13 @@ wheels = [ name = "contourpy" version = "1.2.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -395,62 +410,184 @@ wheels = [ ] [[package]] -name = "coverage" -version = "7.6.4" +name = "contourpy" +version = "1.3.3" 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, upload-time = "2024-10-20T22:57:39.682Z" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } 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, 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" }, + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "coverage" +version = "7.10.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025, upload-time = "2025-08-29T15:32:57.169Z" }, + { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419, upload-time = "2025-08-29T15:32:59.908Z" }, + { url = "https://files.pythonhosted.org/packages/e7/16/b800640b7a43e7c538429e4d7223e0a94fd72453a1a048f70bf766f12e96/coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", size = 244180, upload-time = "2025-08-29T15:33:01.608Z" }, + { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992, upload-time = "2025-08-29T15:33:03.239Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851, upload-time = "2025-08-29T15:33:04.603Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891, upload-time = "2025-08-29T15:33:06.176Z" }, + { url = "https://files.pythonhosted.org/packages/93/a9/51be09b75c55c4f6c16d8d73a6a1d46ad764acca0eab48fa2ffaef5958fe/coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", size = 243909, upload-time = "2025-08-29T15:33:07.74Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786, upload-time = "2025-08-29T15:33:08.965Z" }, + { url = "https://files.pythonhosted.org/packages/d0/4c/37ed872374a21813e0d3215256180c9a382c3f5ced6f2e5da0102fc2fd3e/coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", size = 219521, upload-time = "2025-08-29T15:33:10.599Z" }, + { url = "https://files.pythonhosted.org/packages/8e/36/9311352fdc551dec5b973b61f4e453227ce482985a9368305880af4f85dd/coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", size = 220417, upload-time = "2025-08-29T15:33:11.907Z" }, + { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931, upload-time = "2025-08-29T15:33:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, + { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, + { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714, upload-time = "2025-08-29T15:33:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562, upload-time = "2025-08-29T15:33:24.717Z" }, + { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453, upload-time = "2025-08-29T15:33:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127, upload-time = "2025-08-29T15:33:27.777Z" }, + { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053, upload-time = "2025-08-29T15:33:32.041Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, + { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974, upload-time = "2025-08-29T15:33:38.175Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, + { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724, upload-time = "2025-08-29T15:33:41.172Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536, upload-time = "2025-08-29T15:33:42.524Z" }, + { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171, upload-time = "2025-08-29T15:33:43.974Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, + { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, + { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, + { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, + { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, + { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, + { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, + { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, + { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, + { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, + { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, + { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, + { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, + { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, + { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, + { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, + { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, + { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, + { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, + { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, + { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, + { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, + { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, + { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, ] [package.optional-dependencies] @@ -517,16 +654,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.14" +version = "0.116.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/53/8c38a874844a8b0fa10dd8adf3836ac154082cf88d3f22b544e9ceea0a15/fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739", size = 296263, upload-time = "2025-06-26T15:29:08.21Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/50/b1222562c6d270fea83e9c9075b8e8600b8479150a18e4516a6138b980d1/fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca", size = 95514, upload-time = "2025-06-26T15:29:06.49Z" }, + { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, ] [[package]] @@ -821,17 +958,17 @@ wheels = [ [[package]] name = "hf-xet" -version = "1.1.2" +version = "1.1.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/be/58f20728a5b445f8b064e74f0618897b3439f5ef90934da1916b9dfac76f/hf_xet-1.1.2.tar.gz", hash = "sha256:3712d6d4819d3976a1c18e36db9f503e296283f9363af818f50703506ed63da3", size = 467009, upload-time = "2025-05-16T20:44:34.944Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/0a/a0f56735940fde6dd627602fec9ab3bad23f66a272397560abd65aba416e/hf_xet-1.1.7.tar.gz", hash = "sha256:20cec8db4561338824a3b5f8c19774055b04a8df7fff0cb1ff2cb1a0c1607b80", size = 477719, upload-time = "2025-08-06T00:30:55.741Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/ae/f1a63f75d9886f18a80220ba31a1c7b9c4752f03aae452f358f538c6a991/hf_xet-1.1.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dfd1873fd648488c70735cb60f7728512bca0e459e61fcd107069143cd798469", size = 2642559, upload-time = "2025-05-16T20:44:30.217Z" }, - { url = "https://files.pythonhosted.org/packages/50/ab/d2c83ae18f1015d926defd5bfbe94c62d15e93f900e6a192e318ee947105/hf_xet-1.1.2-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:29b584983b2d977c44157d9241dcf0fd50acde0b7bff8897fe4386912330090d", size = 2541360, upload-time = "2025-05-16T20:44:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/a7/693dc9f34f979e30a378125e2150a0b2d8d166e6d83ce3950eeb81e560aa/hf_xet-1.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b29ac84298147fe9164cc55ad994ba47399f90b5d045b0b803b99cf5f06d8ec", size = 5183081, upload-time = "2025-05-16T20:44:27.505Z" }, - { url = "https://files.pythonhosted.org/packages/3d/23/c48607883f692a36c0a7735f47f98bad32dbe459a32d1568c0f21576985d/hf_xet-1.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d921ba32615676e436a0d15e162331abc9ed43d440916b1d836dc27ce1546173", size = 5356100, upload-time = "2025-05-16T20:44:25.681Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5b/b2316c7f1076da0582b52ea228f68bea95e243c388440d1dc80297c9d813/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d9b03c34e13c44893ab6e8fea18ee8d2a6878c15328dd3aabedbdd83ee9f2ed3", size = 5647688, upload-time = "2025-05-16T20:44:31.867Z" }, - { url = "https://files.pythonhosted.org/packages/2c/98/e6995f0fa579929da7795c961f403f4ee84af36c625963f52741d56f242c/hf_xet-1.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01b18608955b3d826307d37da8bd38b28a46cd2d9908b3a3655d1363274f941a", size = 5322627, upload-time = "2025-05-16T20:44:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/59/40/8f1d5a44a64d8bf9e3c19576e789f716af54875b46daae65426714e75db1/hf_xet-1.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:3562902c81299b09f3582ddfb324400c6a901a2f3bc854f83556495755f4954c", size = 2739542, upload-time = "2025-05-16T20:44:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7c/8d7803995caf14e7d19a392a486a040f923e2cfeff824e9b800b92072f76/hf_xet-1.1.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:60dae4b44d520819e54e216a2505685248ec0adbdb2dd4848b17aa85a0375cde", size = 2761743, upload-time = "2025-08-06T00:30:50.634Z" }, + { url = "https://files.pythonhosted.org/packages/51/a3/fa5897099454aa287022a34a30e68dbff0e617760f774f8bd1db17f06bd4/hf_xet-1.1.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b109f4c11e01c057fc82004c9e51e6cdfe2cb230637644ade40c599739067b2e", size = 2624331, upload-time = "2025-08-06T00:30:49.212Z" }, + { url = "https://files.pythonhosted.org/packages/86/50/2446a132267e60b8a48b2e5835d6e24fd988000d0f5b9b15ebd6d64ef769/hf_xet-1.1.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efaaf1a5a9fc3a501d3e71e88a6bfebc69ee3a716d0e713a931c8b8d920038f", size = 3183844, upload-time = "2025-08-06T00:30:47.582Z" }, + { url = "https://files.pythonhosted.org/packages/20/8f/ccc670616bb9beee867c6bb7139f7eab2b1370fe426503c25f5cbb27b148/hf_xet-1.1.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:751571540f9c1fbad9afcf222a5fb96daf2384bf821317b8bfb0c59d86078513", size = 3074209, upload-time = "2025-08-06T00:30:45.509Z" }, + { url = "https://files.pythonhosted.org/packages/21/0a/4c30e1eb77205565b854f5e4a82cf1f056214e4dc87f2918ebf83d47ae14/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:18b61bbae92d56ae731b92087c44efcac216071182c603fc535f8e29ec4b09b8", size = 3239602, upload-time = "2025-08-06T00:30:52.41Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1e/fc7e9baf14152662ef0b35fa52a6e889f770a7ed14ac239de3c829ecb47e/hf_xet-1.1.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:713f2bff61b252f8523739969f247aa354ad8e6d869b8281e174e2ea1bb8d604", size = 3348184, upload-time = "2025-08-06T00:30:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/a3/73/e354eae84ceff117ec3560141224724794828927fcc013c5b449bf0b8745/hf_xet-1.1.7-cp37-abi3-win_amd64.whl", hash = "sha256:2e356da7d284479ae0f1dea3cf5a2f74fdf925d6dca84ac4341930d892c7cb34", size = 2820008, upload-time = "2025-08-06T00:30:57.056Z" }, ] [[package]] @@ -900,7 +1037,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.33.2" +version = "0.34.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -912,9 +1049,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637, upload-time = "2025-07-02T06:26:05.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/c9/bdbe19339f76d12985bc03572f330a01a93c04dffecaaea3061bdd7fb892/huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c", size = 459768, upload-time = "2025-08-08T09:14:52.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373, upload-time = "2025-07-02T06:26:03.072Z" }, + { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" }, ] [[package]] @@ -962,6 +1099,7 @@ dependencies = [ { name = "gunicorn" }, { name = "huggingface-hub" }, { name = "insightface" }, + { name = "numpy" }, { name = "opencv-python-headless" }, { name = "orjson" }, { name = "pillow" }, @@ -1041,10 +1179,11 @@ requires-dist = [ { name = "gunicorn", specifier = ">=21.1.0" }, { name = "huggingface-hub", specifier = ">=0.20.1,<1.0" }, { name = "insightface", specifier = ">=0.7.3,<1.0" }, + { name = "numpy", specifier = "<2" }, { name = "onnxruntime", marker = "extra == 'armnn'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'cpu'", specifier = ">=1.15.0,<2" }, { name = "onnxruntime", marker = "extra == 'rknn'", specifier = ">=1.15.0,<2" }, - { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" }, + { name = "onnxruntime-gpu", marker = "extra == 'cuda'", specifier = ">=1.17.0,<2", index = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" }, { name = "onnxruntime-openvino", marker = "extra == 'openvino'", specifier = ">=1.17.1,<1.19.0" }, { name = "opencv-python-headless", specifier = ">=4.7.0.72,<5.0" }, { name = "orjson", specifier = ">=3.9.5" }, @@ -1118,15 +1257,18 @@ dependencies = [ { name = "albumentations" }, { name = "cython" }, { name = "easydict" }, - { name = "matplotlib" }, + { name = "matplotlib", version = "3.8.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.10.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "numpy" }, { name = "onnx" }, { name = "pillow" }, { name = "prettytable" }, { name = "requests" }, { name = "scikit-image" }, - { name = "scikit-learn" }, - { name = "scipy" }, + { name = "scikit-learn", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tqdm" }, ] 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" } @@ -1225,7 +1367,7 @@ wheels = [ [[package]] name = "locust" -version = "2.37.11" +version = "2.40.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1237,22 +1379,25 @@ dependencies = [ { name = "locust-cloud" }, { name = "msgpack" }, { name = "psutil" }, + { name = "pytest" }, + { name = "python-engineio" }, + { name = "python-socketio", extra = ["client"] }, { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "pyzmq" }, { name = "requests" }, { name = "setuptools" }, { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/19/66cdab585f7d4385be615d3792402fc75a1bed7519e5283adbe7133dbc78/locust-2.37.11.tar.gz", hash = "sha256:89c79bc599aa57160bd41dd3876e35d8b9dee5abded78e35008d01fd8f1640ed", size = 2252602, upload-time = "2025-06-23T08:22:23.922Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/e0/a99401e233ad1b9ad26265ad8f45f2466abb6ef954e7747e8484864eb6df/locust-2.40.2.tar.gz", hash = "sha256:9ffdf900d1ad949d4c5809e2a4e526bba582175f025f24da2755f43f4b5cb23e", size = 1411854, upload-time = "2025-09-08T12:55:28.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2d/e5ae05911521bf84113be349d51b16d54589e986837d2d518f63434ea3ec/locust-2.37.11-py3-none-any.whl", hash = "sha256:b826f95fbfd5d9a32df6ab1b74672b88e65bbc33ec99fdc10af98079952ad517", size = 2269179, upload-time = "2025-06-23T08:22:21.067Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/85ddb125d91b3a2bfa2a52eeae2d4c7da062239aaa475d6aebddb5688f41/locust-2.40.2-py3-none-any.whl", hash = "sha256:c8f0060d2bd8479034e9e61e6473669c4c8216930d99ee61ec0e627340b89d3e", size = 1430483, upload-time = "2025-09-08T12:55:25.659Z" }, ] [[package]] name = "locust-cloud" -version = "1.24.2" +version = "1.26.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "configargparse" }, @@ -1262,9 +1407,9 @@ dependencies = [ { name = "python-socketio", extra = ["client"] }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/77/bda24167a2b763ba5d3cad1f3fa2a938f5273e51a61bffdbc8dc2e3ba24d/locust_cloud-1.24.2.tar.gz", hash = "sha256:a2656537ff367e6d4d4673477ba9e81ed73a8423a71573cd2512248740eded77", size = 451122, upload-time = "2025-06-23T11:08:00.558Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/ad/10b299b134068a4250a9156e6832a717406abe1dfea2482a07ae7bdca8f3/locust_cloud-1.26.3.tar.gz", hash = "sha256:587acfd4d2dee715fb5f0c3c2d922770babf0b7cff7b2927afbb693a9cd193cc", size = 456042, upload-time = "2025-07-15T19:51:53.791Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/38/8cda8aa1c6dfe5c5abbf69a9b10c03585c37eff64ca92733a291806052ac/locust_cloud-1.24.2-py3-none-any.whl", hash = "sha256:64a5e6f2bf0a1a012d9805291d44fb57e57535c2b5c0fa5bc87ba0d7cce9ef9c", size = 408594, upload-time = "2025-06-23T11:07:59.092Z" }, + { url = "https://files.pythonhosted.org/packages/50/6a/276fc50a9d170e7cbb6715735480cb037abb526639bca85491576e6eee4a/locust_cloud-1.26.3-py3-none-any.whl", hash = "sha256:8cb4b8bb9adcd5b99327bc8ed1d98cf67a29d9d29512651e6e94869de6f1faa8", size = 410023, upload-time = "2025-07-15T19:51:52.056Z" }, ] [[package]] @@ -1321,16 +1466,21 @@ wheels = [ name = "matplotlib" version = "3.8.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, + { name = "contourpy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cycler", marker = "python_full_version < '3.11'" }, + { name = "fonttools", marker = "python_full_version < '3.11'" }, + { name = "kiwisolver", marker = "python_full_version < '3.11'" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pillow", marker = "python_full_version < '3.11'" }, + { name = "pyparsing", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -1354,6 +1504,93 @@ wheels = [ { 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 = "matplotlib" +version = "3.10.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler", marker = "python_full_version >= '3.11'" }, + { name = "fonttools", marker = "python_full_version >= '3.11'" }, + { name = "kiwisolver", marker = "python_full_version >= '3.11'" }, + { name = "numpy", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pillow", marker = "python_full_version >= '3.11'" }, + { name = "pyparsing", marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/91/f2939bb60b7ebf12478b030e0d7f340247390f402b3b189616aad790c366/matplotlib-3.10.5.tar.gz", hash = "sha256:352ed6ccfb7998a00881692f38b4ca083c691d3e275b4145423704c34c909076", size = 34804044, upload-time = "2025-07-31T18:09:33.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/89/5355cdfe43242cb4d1a64a67cb6831398b665ad90e9702c16247cbd8d5ab/matplotlib-3.10.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5d4773a6d1c106ca05cb5a5515d277a6bb96ed09e5c8fab6b7741b8fcaa62c8f", size = 8229094, upload-time = "2025-07-31T18:07:36.507Z" }, + { url = "https://files.pythonhosted.org/packages/34/bc/ba802650e1c69650faed261a9df004af4c6f21759d7a1ec67fe972f093b3/matplotlib-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc88af74e7ba27de6cbe6faee916024ea35d895ed3d61ef6f58c4ce97da7185a", size = 8091464, upload-time = "2025-07-31T18:07:38.864Z" }, + { url = "https://files.pythonhosted.org/packages/ac/64/8d0c8937dee86c286625bddb1902efacc3e22f2b619f5b5a8df29fe5217b/matplotlib-3.10.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:64c4535419d5617f7363dad171a5a59963308e0f3f813c4bed6c9e6e2c131512", size = 8653163, upload-time = "2025-07-31T18:07:41.141Z" }, + { url = "https://files.pythonhosted.org/packages/11/dc/8dfc0acfbdc2fc2336c72561b7935cfa73db9ca70b875d8d3e1b3a6f371a/matplotlib-3.10.5-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a277033048ab22d34f88a3c5243938cef776493f6201a8742ed5f8b553201343", size = 9490635, upload-time = "2025-07-31T18:07:42.936Z" }, + { url = "https://files.pythonhosted.org/packages/54/02/e3fdfe0f2e9fb05f3a691d63876639dbf684170fdcf93231e973104153b4/matplotlib-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e4a6470a118a2e93022ecc7d3bd16b3114b2004ea2bf014fff875b3bc99b70c6", size = 9539036, upload-time = "2025-07-31T18:07:45.18Z" }, + { url = "https://files.pythonhosted.org/packages/c1/29/82bf486ff7f4dbedfb11ccc207d0575cbe3be6ea26f75be514252bde3d70/matplotlib-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:7e44cada61bec8833c106547786814dd4a266c1b2964fd25daa3804f1b8d4467", size = 8093529, upload-time = "2025-07-31T18:07:49.553Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c7/1f2db90a1d43710478bb1e9b57b162852f79234d28e4f48a28cc415aa583/matplotlib-3.10.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dcfc39c452c6a9f9028d3e44d2d721484f665304857188124b505b2c95e1eecf", size = 8239216, upload-time = "2025-07-31T18:07:51.947Z" }, + { url = "https://files.pythonhosted.org/packages/82/6d/ca6844c77a4f89b1c9e4d481c412e1d1dbabf2aae2cbc5aa2da4a1d6683e/matplotlib-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:903352681b59f3efbf4546985142a9686ea1d616bb054b09a537a06e4b892ccf", size = 8102130, upload-time = "2025-07-31T18:07:53.65Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1e/5e187a30cc673a3e384f3723e5f3c416033c1d8d5da414f82e4e731128ea/matplotlib-3.10.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:080c3676a56b8ee1c762bcf8fca3fe709daa1ee23e6ef06ad9f3fc17332f2d2a", size = 8666471, upload-time = "2025-07-31T18:07:55.304Z" }, + { url = "https://files.pythonhosted.org/packages/03/c0/95540d584d7d645324db99a845ac194e915ef75011a0d5e19e1b5cee7e69/matplotlib-3.10.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b4984d5064a35b6f66d2c11d668565f4389b1119cc64db7a4c1725bc11adffc", size = 9500518, upload-time = "2025-07-31T18:07:57.199Z" }, + { url = "https://files.pythonhosted.org/packages/ba/2e/e019352099ea58b4169adb9c6e1a2ad0c568c6377c2b677ee1f06de2adc7/matplotlib-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3967424121d3a46705c9fa9bdb0931de3228f13f73d7bb03c999c88343a89d89", size = 9552372, upload-time = "2025-07-31T18:07:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/b7/81/3200b792a5e8b354f31f4101ad7834743ad07b6d620259f2059317b25e4d/matplotlib-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:33775bbeb75528555a15ac29396940128ef5613cf9a2d31fb1bfd18b3c0c0903", size = 8100634, upload-time = "2025-07-31T18:08:01.801Z" }, + { url = "https://files.pythonhosted.org/packages/52/46/a944f6f0c1f5476a0adfa501969d229ce5ae60cf9a663be0e70361381f89/matplotlib-3.10.5-cp311-cp311-win_arm64.whl", hash = "sha256:c61333a8e5e6240e73769d5826b9a31d8b22df76c0778f8480baf1b4b01c9420", size = 7978880, upload-time = "2025-07-31T18:08:03.407Z" }, + { url = "https://files.pythonhosted.org/packages/66/1e/c6f6bcd882d589410b475ca1fc22e34e34c82adff519caf18f3e6dd9d682/matplotlib-3.10.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:00b6feadc28a08bd3c65b2894f56cf3c94fc8f7adcbc6ab4516ae1e8ed8f62e2", size = 8253056, upload-time = "2025-07-31T18:08:05.385Z" }, + { url = "https://files.pythonhosted.org/packages/53/e6/d6f7d1b59413f233793dda14419776f5f443bcccb2dfc84b09f09fe05dbe/matplotlib-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee98a5c5344dc7f48dc261b6ba5d9900c008fc12beb3fa6ebda81273602cc389", size = 8110131, upload-time = "2025-07-31T18:08:07.293Z" }, + { url = "https://files.pythonhosted.org/packages/66/2b/bed8a45e74957549197a2ac2e1259671cd80b55ed9e1fe2b5c94d88a9202/matplotlib-3.10.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a17e57e33de901d221a07af32c08870ed4528db0b6059dce7d7e65c1122d4bea", size = 8669603, upload-time = "2025-07-31T18:08:09.064Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a7/315e9435b10d057f5e52dfc603cd353167ae28bb1a4e033d41540c0067a4/matplotlib-3.10.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97b9d6443419085950ee4a5b1ee08c363e5c43d7176e55513479e53669e88468", size = 9508127, upload-time = "2025-07-31T18:08:10.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d9/edcbb1f02ca99165365d2768d517898c22c6040187e2ae2ce7294437c413/matplotlib-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ceefe5d40807d29a66ae916c6a3915d60ef9f028ce1927b84e727be91d884369", size = 9566926, upload-time = "2025-07-31T18:08:13.186Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d9/6dd924ad5616c97b7308e6320cf392c466237a82a2040381163b7500510a/matplotlib-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:c04cba0f93d40e45b3c187c6c52c17f24535b27d545f757a2fffebc06c12b98b", size = 8107599, upload-time = "2025-07-31T18:08:15.116Z" }, + { url = "https://files.pythonhosted.org/packages/0e/f3/522dc319a50f7b0279fbe74f86f7a3506ce414bc23172098e8d2bdf21894/matplotlib-3.10.5-cp312-cp312-win_arm64.whl", hash = "sha256:a41bcb6e2c8e79dc99c5511ae6f7787d2fb52efd3d805fff06d5d4f667db16b2", size = 7978173, upload-time = "2025-07-31T18:08:21.518Z" }, + { url = "https://files.pythonhosted.org/packages/8d/05/4f3c1f396075f108515e45cb8d334aff011a922350e502a7472e24c52d77/matplotlib-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:354204db3f7d5caaa10e5de74549ef6a05a4550fdd1c8f831ab9bca81efd39ed", size = 8253586, upload-time = "2025-07-31T18:08:23.107Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2c/e084415775aac7016c3719fe7006cdb462582c6c99ac142f27303c56e243/matplotlib-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b072aac0c3ad563a2b3318124756cb6112157017f7431626600ecbe890df57a1", size = 8110715, upload-time = "2025-07-31T18:08:24.675Z" }, + { url = "https://files.pythonhosted.org/packages/52/1b/233e3094b749df16e3e6cd5a44849fd33852e692ad009cf7de00cf58ddf6/matplotlib-3.10.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d52fd5b684d541b5a51fb276b2b97b010c75bee9aa392f96b4a07aeb491e33c7", size = 8669397, upload-time = "2025-07-31T18:08:26.778Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ec/03f9e003a798f907d9f772eed9b7c6a9775d5bd00648b643ebfb88e25414/matplotlib-3.10.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee7a09ae2f4676276f5a65bd9f2bd91b4f9fbaedf49f40267ce3f9b448de501f", size = 9508646, upload-time = "2025-07-31T18:08:28.848Z" }, + { url = "https://files.pythonhosted.org/packages/91/e7/c051a7a386680c28487bca27d23b02d84f63e3d2a9b4d2fc478e6a42e37e/matplotlib-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ba6c3c9c067b83481d647af88b4e441d532acdb5ef22178a14935b0b881188f4", size = 9567424, upload-time = "2025-07-31T18:08:30.726Z" }, + { url = "https://files.pythonhosted.org/packages/36/c2/24302e93ff431b8f4173ee1dd88976c8d80483cadbc5d3d777cef47b3a1c/matplotlib-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:07442d2692c9bd1cceaa4afb4bbe5b57b98a7599de4dabfcca92d3eea70f9ebe", size = 8107809, upload-time = "2025-07-31T18:08:33.928Z" }, + { url = "https://files.pythonhosted.org/packages/0b/33/423ec6a668d375dad825197557ed8fbdb74d62b432c1ed8235465945475f/matplotlib-3.10.5-cp313-cp313-win_arm64.whl", hash = "sha256:48fe6d47380b68a37ccfcc94f009530e84d41f71f5dae7eda7c4a5a84aa0a674", size = 7978078, upload-time = "2025-07-31T18:08:36.764Z" }, + { url = "https://files.pythonhosted.org/packages/51/17/521fc16ec766455c7bb52cc046550cf7652f6765ca8650ff120aa2d197b6/matplotlib-3.10.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b80eb8621331449fc519541a7461987f10afa4f9cfd91afcd2276ebe19bd56c", size = 8295590, upload-time = "2025-07-31T18:08:38.521Z" }, + { url = "https://files.pythonhosted.org/packages/f8/12/23c28b2c21114c63999bae129fce7fd34515641c517ae48ce7b7dcd33458/matplotlib-3.10.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47a388908e469d6ca2a6015858fa924e0e8a2345a37125948d8e93a91c47933e", size = 8158518, upload-time = "2025-07-31T18:08:40.195Z" }, + { url = "https://files.pythonhosted.org/packages/81/f8/aae4eb25e8e7190759f3cb91cbeaa344128159ac92bb6b409e24f8711f78/matplotlib-3.10.5-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b6b49167d208358983ce26e43aa4196073b4702858670f2eb111f9a10652b4b", size = 8691815, upload-time = "2025-07-31T18:08:42.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ba/450c39ebdd486bd33a359fc17365ade46c6a96bf637bbb0df7824de2886c/matplotlib-3.10.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a8da0453a7fd8e3da114234ba70c5ba9ef0e98f190309ddfde0f089accd46ea", size = 9522814, upload-time = "2025-07-31T18:08:44.914Z" }, + { url = "https://files.pythonhosted.org/packages/89/11/9c66f6a990e27bb9aa023f7988d2d5809cb98aa39c09cbf20fba75a542ef/matplotlib-3.10.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52c6573dfcb7726a9907b482cd5b92e6b5499b284ffacb04ffbfe06b3e568124", size = 9573917, upload-time = "2025-07-31T18:08:47.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/69/8b49394de92569419e5e05e82e83df9b749a0ff550d07631ea96ed2eb35a/matplotlib-3.10.5-cp313-cp313t-win_amd64.whl", hash = "sha256:a23193db2e9d64ece69cac0c8231849db7dd77ce59c7b89948cf9d0ce655a3ce", size = 8181034, upload-time = "2025-07-31T18:08:48.943Z" }, + { url = "https://files.pythonhosted.org/packages/47/23/82dc435bb98a2fc5c20dffcac8f0b083935ac28286413ed8835df40d0baa/matplotlib-3.10.5-cp313-cp313t-win_arm64.whl", hash = "sha256:56da3b102cf6da2776fef3e71cd96fcf22103a13594a18ac9a9b31314e0be154", size = 8023337, upload-time = "2025-07-31T18:08:50.791Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e0/26b6cfde31f5383503ee45dcb7e691d45dadf0b3f54639332b59316a97f8/matplotlib-3.10.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:96ef8f5a3696f20f55597ffa91c28e2e73088df25c555f8d4754931515512715", size = 8253591, upload-time = "2025-07-31T18:08:53.254Z" }, + { url = "https://files.pythonhosted.org/packages/c1/89/98488c7ef7ea20ea659af7499628c240a608b337af4be2066d644cfd0a0f/matplotlib-3.10.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:77fab633e94b9da60512d4fa0213daeb76d5a7b05156840c4fd0399b4b818837", size = 8112566, upload-time = "2025-07-31T18:08:55.116Z" }, + { url = "https://files.pythonhosted.org/packages/52/67/42294dfedc82aea55e1a767daf3263aacfb5a125f44ba189e685bab41b6f/matplotlib-3.10.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27f52634315e96b1debbfdc5c416592edcd9c4221bc2f520fd39c33db5d9f202", size = 9513281, upload-time = "2025-07-31T18:08:56.885Z" }, + { url = "https://files.pythonhosted.org/packages/e7/68/f258239e0cf34c2cbc816781c7ab6fca768452e6bf1119aedd2bd4a882a3/matplotlib-3.10.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:525f6e28c485c769d1f07935b660c864de41c37fd716bfa64158ea646f7084bb", size = 9780873, upload-time = "2025-07-31T18:08:59.241Z" }, + { url = "https://files.pythonhosted.org/packages/89/64/f4881554006bd12e4558bd66778bdd15d47b00a1f6c6e8b50f6208eda4b3/matplotlib-3.10.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1f5f3ec4c191253c5f2b7c07096a142c6a1c024d9f738247bfc8e3f9643fc975", size = 9568954, upload-time = "2025-07-31T18:09:01.244Z" }, + { url = "https://files.pythonhosted.org/packages/06/f8/42779d39c3f757e1f012f2dda3319a89fb602bd2ef98ce8faf0281f4febd/matplotlib-3.10.5-cp314-cp314-win_amd64.whl", hash = "sha256:707f9c292c4cd4716f19ab8a1f93f26598222cd931e0cd98fbbb1c5994bf7667", size = 8237465, upload-time = "2025-07-31T18:09:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f8/153fd06b5160f0cd27c8b9dd797fcc9fb56ac6a0ebf3c1f765b6b68d3c8a/matplotlib-3.10.5-cp314-cp314-win_arm64.whl", hash = "sha256:21a95b9bf408178d372814de7baacd61c712a62cae560b5e6f35d791776f6516", size = 8108898, upload-time = "2025-07-31T18:09:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/c4b082a382a225fe0d2a73f1f57cf6f6f132308805b493a54c8641006238/matplotlib-3.10.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a6b310f95e1102a8c7c817ef17b60ee5d1851b8c71b63d9286b66b177963039e", size = 8295636, upload-time = "2025-07-31T18:09:07.306Z" }, + { url = "https://files.pythonhosted.org/packages/30/73/2195fa2099718b21a20da82dfc753bf2af58d596b51aefe93e359dd5915a/matplotlib-3.10.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:94986a242747a0605cb3ff1cb98691c736f28a59f8ffe5175acaeb7397c49a5a", size = 8158575, upload-time = "2025-07-31T18:09:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e9/a08cdb34618a91fa08f75e6738541da5cacde7c307cea18ff10f0d03fcff/matplotlib-3.10.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ff10ea43288f0c8bab608a305dc6c918cc729d429c31dcbbecde3b9f4d5b569", size = 9522815, upload-time = "2025-07-31T18:09:11.191Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/34d8b7e0d1bb6d06ef45db01dfa560d5a67b1c40c0b998ce9ccde934bb09/matplotlib-3.10.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6adb644c9d040ffb0d3434e440490a66cf73dbfa118a6f79cd7568431f7a012", size = 9783514, upload-time = "2025-07-31T18:09:13.307Z" }, + { url = "https://files.pythonhosted.org/packages/12/09/d330d1e55dcca2e11b4d304cc5227f52e2512e46828d6249b88e0694176e/matplotlib-3.10.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4fa40a8f98428f789a9dcacd625f59b7bc4e3ef6c8c7c80187a7a709475cf592", size = 9573932, upload-time = "2025-07-31T18:09:15.335Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3b/f70258ac729aa004aca673800a53a2b0a26d49ca1df2eaa03289a1c40f81/matplotlib-3.10.5-cp314-cp314t-win_amd64.whl", hash = "sha256:95672a5d628b44207aab91ec20bf59c26da99de12b88f7e0b1fb0a84a86ff959", size = 8322003, upload-time = "2025-07-31T18:09:17.416Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/3601f8ce6d76a7c81c7f25a0e15fde0d6b66226dd187aa6d2838e6374161/matplotlib-3.10.5-cp314-cp314t-win_arm64.whl", hash = "sha256:2efaf97d72629e74252e0b5e3c46813e9eeaa94e011ecf8084a971a31a97f40b", size = 8153849, upload-time = "2025-07-31T18:09:19.673Z" }, + { url = "https://files.pythonhosted.org/packages/e4/eb/7d4c5de49eb78294e1a8e2be8a6ecff8b433e921b731412a56cd1abd3567/matplotlib-3.10.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b5fa2e941f77eb579005fb804026f9d0a1082276118d01cc6051d0d9626eaa7f", size = 8222360, upload-time = "2025-07-31T18:09:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/16/8a/e435db90927b66b16d69f8f009498775f4469f8de4d14b87856965e58eba/matplotlib-3.10.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1fc0d2a3241cdcb9daaca279204a3351ce9df3c0e7e621c7e04ec28aaacaca30", size = 8087462, upload-time = "2025-07-31T18:09:23.504Z" }, + { url = "https://files.pythonhosted.org/packages/0b/dd/06c0e00064362f5647f318e00b435be2ff76a1bdced97c5eaf8347311fbe/matplotlib-3.10.5-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8dee65cb1424b7dc982fe87895b5613d4e691cc57117e8af840da0148ca6c1d7", size = 8659802, upload-time = "2025-07-31T18:09:25.256Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d6/e921be4e1a5f7aca5194e1f016cb67ec294548e530013251f630713e456d/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:160e125da27a749481eaddc0627962990f6029811dbeae23881833a011a0907f", size = 8233224, upload-time = "2025-07-31T18:09:27.512Z" }, + { url = "https://files.pythonhosted.org/packages/ec/74/a2b9b04824b9c349c8f1b2d21d5af43fa7010039427f2b133a034cb09e59/matplotlib-3.10.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac3d50760394d78a3c9be6b28318fe22b494c4fcf6407e8fd4794b538251899b", size = 8098539, upload-time = "2025-07-31T18:09:29.629Z" }, + { url = "https://files.pythonhosted.org/packages/fc/66/cd29ebc7f6c0d2a15d216fb572573e8fc38bd5d6dec3bd9d7d904c0949f7/matplotlib-3.10.5-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c49465bf689c4d59d174d0c7795fb42a21d4244d11d70e52b8011987367ac61", size = 8672192, upload-time = "2025-07-31T18:09:31.407Z" }, +] + [[package]] name = "mdurl" version = "0.1.2" @@ -1415,7 +1652,7 @@ wheels = [ [[package]] name = "mypy" -version = "1.16.1" +version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, @@ -1423,33 +1660,39 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747, upload-time = "2025-06-16T16:51:35.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644, upload-time = "2025-06-16T16:51:11.649Z" }, - { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033, upload-time = "2025-06-16T16:35:30.089Z" }, - { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645, upload-time = "2025-06-16T16:35:48.49Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986, upload-time = "2025-06-16T16:48:39.526Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632, upload-time = "2025-06-16T16:36:08.195Z" }, - { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391, upload-time = "2025-06-16T16:37:56.151Z" }, - { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557, upload-time = "2025-06-16T16:37:21.421Z" }, - { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921, upload-time = "2025-06-16T16:51:28.659Z" }, - { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887, upload-time = "2025-06-16T16:50:53.627Z" }, - { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658, upload-time = "2025-06-16T16:33:55.002Z" }, - { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486, upload-time = "2025-06-16T16:37:03.301Z" }, - { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482, upload-time = "2025-06-16T16:47:37.48Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493, upload-time = "2025-06-16T16:47:01.683Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687, upload-time = "2025-06-16T16:48:19.367Z" }, - { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723, upload-time = "2025-06-16T16:49:20.912Z" }, - { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980, upload-time = "2025-06-16T16:37:40.929Z" }, - { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328, upload-time = "2025-06-16T16:34:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321, upload-time = "2025-06-16T16:48:58.823Z" }, - { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480, upload-time = "2025-06-16T16:47:56.205Z" }, - { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538, upload-time = "2025-06-16T16:46:43.92Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839, upload-time = "2025-06-16T16:36:28.039Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634, upload-time = "2025-06-16T16:50:34.441Z" }, - { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584, upload-time = "2025-06-16T16:34:54.857Z" }, - { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886, upload-time = "2025-06-16T16:36:43.589Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923, upload-time = "2025-06-16T16:48:02.366Z" }, + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, ] [[package]] @@ -1534,7 +1777,7 @@ wheels = [ [[package]] name = "onnxruntime" -version = "1.22.0" +version = "1.22.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coloredlogs" }, @@ -1545,30 +1788,30 @@ dependencies = [ { name = "sympy" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/67/3c/c99b21646a782b89c33cffd96fdee02a81bc43f0cb651de84d58ec11e30e/onnxruntime-1.22.0-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:85d8826cc8054e4d6bf07f779dc742a363c39094015bdad6a08b3c18cfe0ba8c", size = 34273493, upload-time = "2025-05-09T20:25:55.66Z" }, - { url = "https://files.pythonhosted.org/packages/54/ab/fd9a3b5285008c060618be92e475337fcfbf8689787953d37273f7b52ab0/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:468c9502a12f6f49ec335c2febd22fdceecc1e4cc96dfc27e419ba237dff5aff", size = 14445346, upload-time = "2025-05-09T20:25:41.322Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ca/a5625644bc079e04e3076a5ac1fb954d1e90309b8eb987a4f800732ffee6/onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:681fe356d853630a898ee05f01ddb95728c9a168c9460e8361d0a240c9b7cb97", size = 16392959, upload-time = "2025-05-09T20:26:09.047Z" }, - { url = "https://files.pythonhosted.org/packages/6d/6b/8267490476e8d4dd1883632c7e46a4634384c7ff1c35ae44edc8ab0bb7a9/onnxruntime-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:20bca6495d06925631e201f2b257cc37086752e8fe7b6c83a67c6509f4759bc9", size = 12689974, upload-time = "2025-05-12T21:26:09.704Z" }, - { url = "https://files.pythonhosted.org/packages/7a/08/c008711d1b92ff1272f4fea0fbee57723171f161d42e5c680625535280af/onnxruntime-1.22.0-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:8d6725c5b9a681d8fe72f2960c191a96c256367887d076b08466f52b4e0991df", size = 34282151, upload-time = "2025-05-09T20:25:59.246Z" }, - { url = "https://files.pythonhosted.org/packages/3e/8b/22989f6b59bc4ad1324f07a945c80b9ab825f0a581ad7a6064b93716d9b7/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef17d665a917866d1f68f09edc98223b9a27e6cb167dec69da4c66484ad12fd", size = 14446302, upload-time = "2025-05-09T20:25:44.299Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d5/aa83d084d05bc8f6cf8b74b499c77431ffd6b7075c761ec48ec0c161a47f/onnxruntime-1.22.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b978aa63a9a22095479c38371a9b359d4c15173cbb164eaad5f2cd27d666aa65", size = 16393496, upload-time = "2025-05-09T20:26:11.588Z" }, - { url = "https://files.pythonhosted.org/packages/89/a5/1c6c10322201566015183b52ef011dfa932f5dd1b278de8d75c3b948411d/onnxruntime-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:03d3ef7fb11adf154149d6e767e21057e0e577b947dd3f66190b212528e1db31", size = 12691517, upload-time = "2025-05-12T21:26:13.354Z" }, - { url = "https://files.pythonhosted.org/packages/4d/de/9162872c6e502e9ac8c99a98a8738b2fab408123d11de55022ac4f92562a/onnxruntime-1.22.0-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:f3c0380f53c1e72a41b3f4d6af2ccc01df2c17844072233442c3a7e74851ab97", size = 34298046, upload-time = "2025-05-09T20:26:02.399Z" }, - { url = "https://files.pythonhosted.org/packages/03/79/36f910cd9fc96b444b0e728bba14607016079786adf032dae61f7c63b4aa/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8601128eaef79b636152aea76ae6981b7c9fc81a618f584c15d78d42b310f1c", size = 14443220, upload-time = "2025-05-09T20:25:47.078Z" }, - { url = "https://files.pythonhosted.org/packages/8c/60/16d219b8868cc8e8e51a68519873bdb9f5f24af080b62e917a13fff9989b/onnxruntime-1.22.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6964a975731afc19dc3418fad8d4e08c48920144ff590149429a5ebe0d15fb3c", size = 16406377, upload-time = "2025-05-09T20:26:14.478Z" }, - { url = "https://files.pythonhosted.org/packages/36/b4/3f1c71ce1d3d21078a6a74c5483bfa2b07e41a8d2b8fb1e9993e6a26d8d3/onnxruntime-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:c0d534a43d1264d1273c2d4f00a5a588fa98d21117a3345b7104fa0bbcaadb9a", size = 12692233, upload-time = "2025-05-12T21:26:16.963Z" }, - { url = "https://files.pythonhosted.org/packages/a9/65/5cb5018d5b0b7cba820d2c4a1d1b02d40df538d49138ba36a509457e4df6/onnxruntime-1.22.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:fe7c051236aae16d8e2e9ffbfc1e115a0cc2450e873a9c4cb75c0cc96c1dae07", size = 34298715, upload-time = "2025-05-09T20:26:05.634Z" }, - { url = "https://files.pythonhosted.org/packages/e1/89/1dfe1b368831d1256b90b95cb8d11da8ab769febd5c8833ec85ec1f79d21/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a6bbed10bc5e770c04d422893d3045b81acbbadc9fb759a2cd1ca00993da919", size = 14443266, upload-time = "2025-05-09T20:25:49.479Z" }, - { url = "https://files.pythonhosted.org/packages/1e/70/342514ade3a33ad9dd505dcee96ff1f0e7be6d0e6e9c911fe0f1505abf42/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe45ee3e756300fccfd8d61b91129a121d3d80e9d38e01f03ff1295badc32b8", size = 16406707, upload-time = "2025-05-09T20:26:17.454Z" }, - { url = "https://files.pythonhosted.org/packages/3e/89/2f64e250945fa87140fb917ba377d6d0e9122e029c8512f389a9b7f953f4/onnxruntime-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:5a31d84ef82b4b05d794a4ce8ba37b0d9deb768fd580e36e17b39e0b4840253b", size = 12691777, upload-time = "2025-05-12T21:26:20.19Z" }, - { url = "https://files.pythonhosted.org/packages/9f/48/d61d5f1ed098161edd88c56cbac49207d7b7b149e613d2cd7e33176c63b3/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2ac5bd9205d831541db4e508e586e764a74f14efdd3f89af7fd20e1bf4a1ed", size = 14454003, upload-time = "2025-05-09T20:25:52.287Z" }, - { url = "https://files.pythonhosted.org/packages/c3/16/873b955beda7bada5b0d798d3a601b2ff210e44ad5169f6d405b93892103/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64845709f9e8a2809e8e009bc4c8f73b788cee9c6619b7d9930344eae4c9cd36", size = 16427482, upload-time = "2025-05-09T20:26:20.376Z" }, + { url = "https://files.pythonhosted.org/packages/76/b9/664a1ffee62fa51529fac27b37409d5d28cadee8d97db806fcba68339b7e/onnxruntime-1.22.1-cp310-cp310-macosx_13_0_universal2.whl", hash = "sha256:80e7f51da1f5201c1379b8d6ef6170505cd800e40da216290f5e06be01aadf95", size = 34319864, upload-time = "2025-07-10T19:15:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/b9/64/bc7221e92c994931024e22b22401b962c299e991558c3d57f7e34538b4b9/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89ddfdbbdaf7e3a59515dee657f6515601d55cb21a0f0f48c81aefc54ff1b73", size = 14472246, upload-time = "2025-07-10T19:15:19.403Z" }, + { url = "https://files.pythonhosted.org/packages/84/57/901eddbfb59ac4d008822b236450d5765cafcd450c787019416f8d3baf11/onnxruntime-1.22.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bddc75868bcf6f9ed76858a632f65f7b1846bdcefc6d637b1e359c2c68609964", size = 16459905, upload-time = "2025-07-10T19:15:21.749Z" }, + { url = "https://files.pythonhosted.org/packages/de/90/d6a1eb9b47e66a18afe7d1cf7cf0b2ef966ffa6f44d9f32d94c2be2860fb/onnxruntime-1.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:01e2f21b2793eb0c8642d2be3cee34cc7d96b85f45f6615e4e220424158877ce", size = 12689001, upload-time = "2025-07-10T19:15:23.848Z" }, + { url = "https://files.pythonhosted.org/packages/82/ff/4a1a6747e039ef29a8d4ee4510060e9a805982b6da906a3da2306b7a3be6/onnxruntime-1.22.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:f4581bccb786da68725d8eac7c63a8f31a89116b8761ff8b4989dc58b61d49a0", size = 34324148, upload-time = "2025-07-10T19:15:26.584Z" }, + { url = "https://files.pythonhosted.org/packages/0b/05/9f1929723f1cca8c9fb1b2b97ac54ce61362c7201434d38053ea36ee4225/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae7526cf10f93454beb0f751e78e5cb7619e3b92f9fc3bd51aa6f3b7a8977e5", size = 14473779, upload-time = "2025-07-10T19:15:30.183Z" }, + { url = "https://files.pythonhosted.org/packages/59/f3/c93eb4167d4f36ea947930f82850231f7ce0900cb00e1a53dc4995b60479/onnxruntime-1.22.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6effa1299ac549a05c784d50292e3378dbbf010346ded67400193b09ddc2f04", size = 16460799, upload-time = "2025-07-10T19:15:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/a8/01/e536397b03e4462d3260aee5387e6f606c8fa9d2b20b1728f988c3c72891/onnxruntime-1.22.1-cp311-cp311-win_amd64.whl", hash = "sha256:f28a42bb322b4ca6d255531bb334a2b3e21f172e37c1741bd5e66bc4b7b61f03", size = 12689881, upload-time = "2025-07-10T19:15:35.501Z" }, + { url = "https://files.pythonhosted.org/packages/48/70/ca2a4d38a5deccd98caa145581becb20c53684f451e89eb3a39915620066/onnxruntime-1.22.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:a938d11c0dc811badf78e435daa3899d9af38abee950d87f3ab7430eb5b3cf5a", size = 34342883, upload-time = "2025-07-10T19:15:38.223Z" }, + { url = "https://files.pythonhosted.org/packages/29/e5/00b099b4d4f6223b610421080d0eed9327ef9986785c9141819bbba0d396/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984cea2a02fcc5dfea44ade9aca9fe0f7a8a2cd6f77c258fc4388238618f3928", size = 14473861, upload-time = "2025-07-10T19:15:42.911Z" }, + { url = "https://files.pythonhosted.org/packages/0a/50/519828a5292a6ccd8d5cd6d2f72c6b36ea528a2ef68eca69647732539ffa/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d39a530aff1ec8d02e365f35e503193991417788641b184f5b1e8c9a6d5ce8d", size = 16475713, upload-time = "2025-07-10T19:15:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/5d/54/7139d463bb0a312890c9a5db87d7815d4a8cce9e6f5f28d04f0b55fcb160/onnxruntime-1.22.1-cp312-cp312-win_amd64.whl", hash = "sha256:6a64291d57ea966a245f749eb970f4fa05a64d26672e05a83fdb5db6b7d62f87", size = 12690910, upload-time = "2025-07-10T19:15:47.478Z" }, + { url = "https://files.pythonhosted.org/packages/e0/39/77cefa829740bd830915095d8408dce6d731b244e24b1f64fe3df9f18e86/onnxruntime-1.22.1-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:d29c7d87b6cbed8fecfd09dca471832384d12a69e1ab873e5effbb94adc3e966", size = 34342026, upload-time = "2025-07-10T19:15:50.266Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a6/444291524cb52875b5de980a6e918072514df63a57a7120bf9dfae3aeed1/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:460487d83b7056ba98f1f7bac80287224c31d8149b15712b0d6f5078fcc33d0f", size = 14474014, upload-time = "2025-07-10T19:15:53.991Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/45a995437879c18beff26eacc2322f4227224d04c6ac3254dce2e8950190/onnxruntime-1.22.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0c37070268ba4e02a1a9d28560cd00cd1e94f0d4f275cbef283854f861a65fa", size = 16475427, upload-time = "2025-07-10T19:15:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/9c765e66ad32a7e709ce4cb6b95d7eaa9cb4d92a6e11ea97c20ffecaf765/onnxruntime-1.22.1-cp313-cp313-win_amd64.whl", hash = "sha256:70980d729145a36a05f74b573435531f55ef9503bcda81fc6c3d6b9306199982", size = 12690841, upload-time = "2025-07-10T19:15:58.337Z" }, + { url = "https://files.pythonhosted.org/packages/52/8c/02af24ee1c8dce4e6c14a1642a7a56cebe323d2fa01d9a360a638f7e4b75/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a7980bbc4b7f446bac26c3785652fe8730ed02617d765399e89ac7d44e0f7d", size = 14479333, upload-time = "2025-07-10T19:16:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/5d/15/d75fd66aba116ce3732bb1050401394c5ec52074c4f7ee18db8838dd4667/onnxruntime-1.22.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7e823624b015ea879d976cbef8bfaed2f7e2cc233d7506860a76dd37f8f381", size = 16477261, upload-time = "2025-07-10T19:16:03.226Z" }, ] [[package]] name = "onnxruntime-gpu" version = "1.19.2" -source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple" } +source = { registry = "https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/" } dependencies = [ { name = "coloredlogs" }, { name = "flatbuffers" }, @@ -1624,68 +1867,79 @@ wheels = [ [[package]] name = "orjson" -version = "3.10.18" +version = "3.11.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/0b/fea456a3ffe74e70ba30e01ec183a9b26bec4d497f61dcfce1b601059c60/orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53", size = 5422810, upload-time = "2025-04-29T23:30:08.423Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/4d/8df5f83256a809c22c4d6792ce8d43bb503be0fb7a8e4da9025754b09658/orjson-3.11.3.tar.gz", hash = "sha256:1c0603b1d2ffcd43a411d64797a19556ef76958aef1c182f22dc30860152a98a", size = 5482394, upload-time = "2025-08-26T17:46:43.171Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/16/2ceb9fb7bc2b11b1e4a3ea27794256e93dee2309ebe297fd131a778cd150/orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402", size = 248927, upload-time = "2025-04-29T23:28:08.643Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e1/d3c0a2bba5b9906badd121da449295062b289236c39c3a7801f92c4682b0/orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c", size = 136995, upload-time = "2025-04-29T23:28:11.503Z" }, - { url = "https://files.pythonhosted.org/packages/d7/51/698dd65e94f153ee5ecb2586c89702c9e9d12f165a63e74eb9ea1299f4e1/orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92", size = 132893, upload-time = "2025-04-29T23:28:12.751Z" }, - { url = "https://files.pythonhosted.org/packages/b3/e5/155ce5a2c43a85e790fcf8b985400138ce5369f24ee6770378ee6b691036/orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13", size = 137017, upload-time = "2025-04-29T23:28:14.498Z" }, - { url = "https://files.pythonhosted.org/packages/46/bb/6141ec3beac3125c0b07375aee01b5124989907d61c72c7636136e4bd03e/orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469", size = 138290, upload-time = "2025-04-29T23:28:16.211Z" }, - { url = "https://files.pythonhosted.org/packages/77/36/6961eca0b66b7809d33c4ca58c6bd4c23a1b914fb23aba2fa2883f791434/orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f", size = 142828, upload-time = "2025-04-29T23:28:18.065Z" }, - { url = "https://files.pythonhosted.org/packages/8b/2f/0c646d5fd689d3be94f4d83fa9435a6c4322c9b8533edbb3cd4bc8c5f69a/orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68", size = 132806, upload-time = "2025-04-29T23:28:19.782Z" }, - { url = "https://files.pythonhosted.org/packages/ea/af/65907b40c74ef4c3674ef2bcfa311c695eb934710459841b3c2da212215c/orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056", size = 135005, upload-time = "2025-04-29T23:28:21.367Z" }, - { url = "https://files.pythonhosted.org/packages/c7/d1/68bd20ac6a32cd1f1b10d23e7cc58ee1e730e80624e3031d77067d7150fc/orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d", size = 413418, upload-time = "2025-04-29T23:28:23.097Z" }, - { url = "https://files.pythonhosted.org/packages/31/31/c701ec0bcc3e80e5cb6e319c628ef7b768aaa24b0f3b4c599df2eaacfa24/orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8", size = 153288, upload-time = "2025-04-29T23:28:25.02Z" }, - { url = "https://files.pythonhosted.org/packages/d9/31/5e1aa99a10893a43cfc58009f9da840990cc8a9ebb75aa452210ba18587e/orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f", size = 137181, upload-time = "2025-04-29T23:28:26.318Z" }, - { url = "https://files.pythonhosted.org/packages/bf/8c/daba0ac1b8690011d9242a0f37235f7d17df6d0ad941021048523b76674e/orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06", size = 142694, upload-time = "2025-04-29T23:28:28.092Z" }, - { url = "https://files.pythonhosted.org/packages/16/62/8b687724143286b63e1d0fab3ad4214d54566d80b0ba9d67c26aaf28a2f8/orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92", size = 134600, upload-time = "2025-04-29T23:28:29.422Z" }, - { url = "https://files.pythonhosted.org/packages/97/c7/c54a948ce9a4278794f669a353551ce7db4ffb656c69a6e1f2264d563e50/orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8", size = 248929, upload-time = "2025-04-29T23:28:30.716Z" }, - { url = "https://files.pythonhosted.org/packages/9e/60/a9c674ef1dd8ab22b5b10f9300e7e70444d4e3cda4b8258d6c2488c32143/orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d", size = 133364, upload-time = "2025-04-29T23:28:32.392Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4e/f7d1bdd983082216e414e6d7ef897b0c2957f99c545826c06f371d52337e/orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7", size = 136995, upload-time = "2025-04-29T23:28:34.024Z" }, - { url = "https://files.pythonhosted.org/packages/17/89/46b9181ba0ea251c9243b0c8ce29ff7c9796fa943806a9c8b02592fce8ea/orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a", size = 132894, upload-time = "2025-04-29T23:28:35.318Z" }, - { url = "https://files.pythonhosted.org/packages/ca/dd/7bce6fcc5b8c21aef59ba3c67f2166f0a1a9b0317dcca4a9d5bd7934ecfd/orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679", size = 137016, upload-time = "2025-04-29T23:28:36.674Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4a/b8aea1c83af805dcd31c1f03c95aabb3e19a016b2a4645dd822c5686e94d/orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947", size = 138290, upload-time = "2025-04-29T23:28:38.3Z" }, - { url = "https://files.pythonhosted.org/packages/36/d6/7eb05c85d987b688707f45dcf83c91abc2251e0dd9fb4f7be96514f838b1/orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4", size = 142829, upload-time = "2025-04-29T23:28:39.657Z" }, - { url = "https://files.pythonhosted.org/packages/d2/78/ddd3ee7873f2b5f90f016bc04062713d567435c53ecc8783aab3a4d34915/orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334", size = 132805, upload-time = "2025-04-29T23:28:40.969Z" }, - { url = "https://files.pythonhosted.org/packages/8c/09/c8e047f73d2c5d21ead9c180203e111cddeffc0848d5f0f974e346e21c8e/orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17", size = 135008, upload-time = "2025-04-29T23:28:42.284Z" }, - { url = "https://files.pythonhosted.org/packages/0c/4b/dccbf5055ef8fb6eda542ab271955fc1f9bf0b941a058490293f8811122b/orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e", size = 413419, upload-time = "2025-04-29T23:28:43.673Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f3/1eac0c5e2d6d6790bd2025ebfbefcbd37f0d097103d76f9b3f9302af5a17/orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b", size = 153292, upload-time = "2025-04-29T23:28:45.573Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b4/ef0abf64c8f1fabf98791819ab502c2c8c1dc48b786646533a93637d8999/orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7", size = 137182, upload-time = "2025-04-29T23:28:47.229Z" }, - { url = "https://files.pythonhosted.org/packages/a9/a3/6ea878e7b4a0dc5c888d0370d7752dcb23f402747d10e2257478d69b5e63/orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1", size = 142695, upload-time = "2025-04-29T23:28:48.564Z" }, - { url = "https://files.pythonhosted.org/packages/79/2a/4048700a3233d562f0e90d5572a849baa18ae4e5ce4c3ba6247e4ece57b0/orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a", size = 134603, upload-time = "2025-04-29T23:28:50.442Z" }, - { url = "https://files.pythonhosted.org/packages/03/45/10d934535a4993d27e1c84f1810e79ccf8b1b7418cef12151a22fe9bb1e1/orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5", size = 131400, upload-time = "2025-04-29T23:28:51.838Z" }, - { url = "https://files.pythonhosted.org/packages/21/1a/67236da0916c1a192d5f4ccbe10ec495367a726996ceb7614eaa687112f2/orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753", size = 249184, upload-time = "2025-04-29T23:28:53.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/bc/c7f1db3b1d094dc0c6c83ed16b161a16c214aaa77f311118a93f647b32dc/orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17", size = 133279, upload-time = "2025-04-29T23:28:55.055Z" }, - { url = "https://files.pythonhosted.org/packages/af/84/664657cd14cc11f0d81e80e64766c7ba5c9b7fc1ec304117878cc1b4659c/orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d", size = 136799, upload-time = "2025-04-29T23:28:56.828Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bb/f50039c5bb05a7ab024ed43ba25d0319e8722a0ac3babb0807e543349978/orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae", size = 132791, upload-time = "2025-04-29T23:28:58.751Z" }, - { url = "https://files.pythonhosted.org/packages/93/8c/ee74709fc072c3ee219784173ddfe46f699598a1723d9d49cbc78d66df65/orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f", size = 137059, upload-time = "2025-04-29T23:29:00.129Z" }, - { url = "https://files.pythonhosted.org/packages/6a/37/e6d3109ee004296c80426b5a62b47bcadd96a3deab7443e56507823588c5/orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c", size = 138359, upload-time = "2025-04-29T23:29:01.704Z" }, - { url = "https://files.pythonhosted.org/packages/4f/5d/387dafae0e4691857c62bd02839a3bf3fa648eebd26185adfac58d09f207/orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad", size = 142853, upload-time = "2025-04-29T23:29:03.576Z" }, - { url = "https://files.pythonhosted.org/packages/27/6f/875e8e282105350b9a5341c0222a13419758545ae32ad6e0fcf5f64d76aa/orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c", size = 133131, upload-time = "2025-04-29T23:29:05.753Z" }, - { url = "https://files.pythonhosted.org/packages/48/b2/73a1f0b4790dcb1e5a45f058f4f5dcadc8a85d90137b50d6bbc6afd0ae50/orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406", size = 134834, upload-time = "2025-04-29T23:29:07.35Z" }, - { url = "https://files.pythonhosted.org/packages/56/f5/7ed133a5525add9c14dbdf17d011dd82206ca6840811d32ac52a35935d19/orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6", size = 413368, upload-time = "2025-04-29T23:29:09.301Z" }, - { url = "https://files.pythonhosted.org/packages/11/7c/439654221ed9c3324bbac7bdf94cf06a971206b7b62327f11a52544e4982/orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06", size = 153359, upload-time = "2025-04-29T23:29:10.813Z" }, - { url = "https://files.pythonhosted.org/packages/48/e7/d58074fa0cc9dd29a8fa2a6c8d5deebdfd82c6cfef72b0e4277c4017563a/orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5", size = 137466, upload-time = "2025-04-29T23:29:12.26Z" }, - { url = "https://files.pythonhosted.org/packages/57/4d/fe17581cf81fb70dfcef44e966aa4003360e4194d15a3f38cbffe873333a/orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e", size = 142683, upload-time = "2025-04-29T23:29:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/469f62d25ab5f0f3aee256ea732e72dc3aab6d73bac777bd6277955bceef/orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc", size = 134754, upload-time = "2025-04-29T23:29:15.338Z" }, - { url = "https://files.pythonhosted.org/packages/10/b0/1040c447fac5b91bc1e9c004b69ee50abb0c1ffd0d24406e1350c58a7fcb/orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a", size = 131218, upload-time = "2025-04-29T23:29:17.324Z" }, - { url = "https://files.pythonhosted.org/packages/04/f0/8aedb6574b68096f3be8f74c0b56d36fd94bcf47e6c7ed47a7bd1474aaa8/orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147", size = 249087, upload-time = "2025-04-29T23:29:19.083Z" }, - { url = "https://files.pythonhosted.org/packages/bc/f7/7118f965541aeac6844fcb18d6988e111ac0d349c9b80cda53583e758908/orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c", size = 133273, upload-time = "2025-04-29T23:29:20.602Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d9/839637cc06eaf528dd8127b36004247bf56e064501f68df9ee6fd56a88ee/orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103", size = 136779, upload-time = "2025-04-29T23:29:22.062Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/f226ecfef31a1f0e7d6bf9a31a0bbaf384c7cbe3fce49cc9c2acc51f902a/orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595", size = 132811, upload-time = "2025-04-29T23:29:23.602Z" }, - { url = "https://files.pythonhosted.org/packages/73/2d/371513d04143c85b681cf8f3bce743656eb5b640cb1f461dad750ac4b4d4/orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc", size = 137018, upload-time = "2025-04-29T23:29:25.094Z" }, - { url = "https://files.pythonhosted.org/packages/69/cb/a4d37a30507b7a59bdc484e4a3253c8141bf756d4e13fcc1da760a0b00cb/orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc", size = 138368, upload-time = "2025-04-29T23:29:26.609Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ae/cd10883c48d912d216d541eb3db8b2433415fde67f620afe6f311f5cd2ca/orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049", size = 142840, upload-time = "2025-04-29T23:29:28.153Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4c/2bda09855c6b5f2c055034c9eda1529967b042ff8d81a05005115c4e6772/orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58", size = 133135, upload-time = "2025-04-29T23:29:29.726Z" }, - { url = "https://files.pythonhosted.org/packages/13/4a/35971fd809a8896731930a80dfff0b8ff48eeb5d8b57bb4d0d525160017f/orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034", size = 134810, upload-time = "2025-04-29T23:29:31.269Z" }, - { url = "https://files.pythonhosted.org/packages/99/70/0fa9e6310cda98365629182486ff37a1c6578e34c33992df271a476ea1cd/orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1", size = 413491, upload-time = "2025-04-29T23:29:33.315Z" }, - { url = "https://files.pythonhosted.org/packages/32/cb/990a0e88498babddb74fb97855ae4fbd22a82960e9b06eab5775cac435da/orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012", size = 153277, upload-time = "2025-04-29T23:29:34.946Z" }, - { url = "https://files.pythonhosted.org/packages/92/44/473248c3305bf782a384ed50dd8bc2d3cde1543d107138fd99b707480ca1/orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f", size = 137367, upload-time = "2025-04-29T23:29:36.52Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fd/7f1d3edd4ffcd944a6a40e9f88af2197b619c931ac4d3cfba4798d4d3815/orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea", size = 142687, upload-time = "2025-04-29T23:29:38.292Z" }, - { url = "https://files.pythonhosted.org/packages/4b/03/c75c6ad46be41c16f4cfe0352a2d1450546f3c09ad2c9d341110cd87b025/orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52", size = 134794, upload-time = "2025-04-29T23:29:40.349Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/f53038a5a72cc4fd0b56c1eafb4ef64aec9685460d5ac34de98ca78b6e29/orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3", size = 131186, upload-time = "2025-04-29T23:29:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/9b/64/4a3cef001c6cd9c64256348d4c13a7b09b857e3e1cbb5185917df67d8ced/orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7", size = 238600, upload-time = "2025-08-26T17:44:36.875Z" }, + { url = "https://files.pythonhosted.org/packages/10/ce/0c8c87f54f79d051485903dc46226c4d3220b691a151769156054df4562b/orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120", size = 123526, upload-time = "2025-08-26T17:44:39.574Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d0/249497e861f2d438f45b3ab7b7b361484237414945169aa285608f9f7019/orjson-3.11.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58533f9e8266cb0ac298e259ed7b4d42ed3fa0b78ce76860626164de49e0d467", size = 128075, upload-time = "2025-08-26T17:44:40.672Z" }, + { url = "https://files.pythonhosted.org/packages/e5/64/00485702f640a0fd56144042a1ea196469f4a3ae93681871564bf74fa996/orjson-3.11.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c212cfdd90512fe722fa9bd620de4d46cda691415be86b2e02243242ae81873", size = 130483, upload-time = "2025-08-26T17:44:41.788Z" }, + { url = "https://files.pythonhosted.org/packages/64/81/110d68dba3909171bf3f05619ad0cf187b430e64045ae4e0aa7ccfe25b15/orjson-3.11.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff835b5d3e67d9207343effb03760c00335f8b5285bfceefd4dc967b0e48f6a", size = 132539, upload-time = "2025-08-26T17:44:43.12Z" }, + { url = "https://files.pythonhosted.org/packages/79/92/dba25c22b0ddfafa1e6516a780a00abac28d49f49e7202eb433a53c3e94e/orjson-3.11.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5aa4682912a450c2db89cbd92d356fef47e115dffba07992555542f344d301b", size = 135390, upload-time = "2025-08-26T17:44:44.199Z" }, + { url = "https://files.pythonhosted.org/packages/44/1d/ca2230fd55edbd87b58a43a19032d63a4b180389a97520cc62c535b726f9/orjson-3.11.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d18dd34ea2e860553a579df02041845dee0af8985dff7f8661306f95504ddf", size = 132966, upload-time = "2025-08-26T17:44:45.719Z" }, + { url = "https://files.pythonhosted.org/packages/6e/b9/96bbc8ed3e47e52b487d504bd6861798977445fbc410da6e87e302dc632d/orjson-3.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8b11701bc43be92ea42bd454910437b355dfb63696c06fe953ffb40b5f763b4", size = 131349, upload-time = "2025-08-26T17:44:46.862Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3c/418fbd93d94b0df71cddf96b7fe5894d64a5d890b453ac365120daec30f7/orjson-3.11.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90368277087d4af32d38bd55f9da2ff466d25325bf6167c8f382d8ee40cb2bbc", size = 404087, upload-time = "2025-08-26T17:44:48.079Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a9/2bfd58817d736c2f63608dec0c34857339d423eeed30099b126562822191/orjson-3.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd7ff459fb393358d3a155d25b275c60b07a2c83dcd7ea962b1923f5a1134569", size = 146067, upload-time = "2025-08-26T17:44:49.302Z" }, + { url = "https://files.pythonhosted.org/packages/33/ba/29023771f334096f564e48d82ed855a0ed3320389d6748a9c949e25be734/orjson-3.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8d902867b699bcd09c176a280b1acdab57f924489033e53d0afe79817da37e6", size = 135506, upload-time = "2025-08-26T17:44:50.558Z" }, + { url = "https://files.pythonhosted.org/packages/39/62/b5a1eca83f54cb3aa11a9645b8a22f08d97dbd13f27f83aae7c6666a0a05/orjson-3.11.3-cp310-cp310-win32.whl", hash = "sha256:bb93562146120bb51e6b154962d3dadc678ed0fce96513fa6bc06599bb6f6edc", size = 136352, upload-time = "2025-08-26T17:44:51.698Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c0/7ebfaa327d9a9ed982adc0d9420dbce9a3fec45b60ab32c6308f731333fa/orjson-3.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:976c6f1975032cc327161c65d4194c549f2589d88b105a5e3499429a54479770", size = 131539, upload-time = "2025-08-26T17:44:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/360674cd817faef32e49276187922a946468579fcaf37afdfb6c07046e92/orjson-3.11.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d2ae0cc6aeb669633e0124531f342a17d8e97ea999e42f12a5ad4adaa304c5f", size = 238238, upload-time = "2025-08-26T17:44:54.214Z" }, + { url = "https://files.pythonhosted.org/packages/05/3d/5fa9ea4b34c1a13be7d9046ba98d06e6feb1d8853718992954ab59d16625/orjson-3.11.3-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ba21dbb2493e9c653eaffdc38819b004b7b1b246fb77bfc93dc016fe664eac91", size = 127713, upload-time = "2025-08-26T17:44:55.596Z" }, + { url = "https://files.pythonhosted.org/packages/e5/5f/e18367823925e00b1feec867ff5f040055892fc474bf5f7875649ecfa586/orjson-3.11.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00f1a271e56d511d1569937c0447d7dce5a99a33ea0dec76673706360a051904", size = 123241, upload-time = "2025-08-26T17:44:57.185Z" }, + { url = "https://files.pythonhosted.org/packages/0f/bd/3c66b91c4564759cf9f473251ac1650e446c7ba92a7c0f9f56ed54f9f0e6/orjson-3.11.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b67e71e47caa6680d1b6f075a396d04fa6ca8ca09aafb428731da9b3ea32a5a6", size = 127895, upload-time = "2025-08-26T17:44:58.349Z" }, + { url = "https://files.pythonhosted.org/packages/82/b5/dc8dcd609db4766e2967a85f63296c59d4722b39503e5b0bf7fd340d387f/orjson-3.11.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d012ebddffcce8c85734a6d9e5f08180cd3857c5f5a3ac70185b43775d043d", size = 130303, upload-time = "2025-08-26T17:44:59.491Z" }, + { url = "https://files.pythonhosted.org/packages/48/c2/d58ec5fd1270b2aa44c862171891adc2e1241bd7dab26c8f46eb97c6c6f1/orjson-3.11.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd759f75d6b8d1b62012b7f5ef9461d03c804f94d539a5515b454ba3a6588038", size = 132366, upload-time = "2025-08-26T17:45:00.654Z" }, + { url = "https://files.pythonhosted.org/packages/73/87/0ef7e22eb8dd1ef940bfe3b9e441db519e692d62ed1aae365406a16d23d0/orjson-3.11.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6890ace0809627b0dff19cfad92d69d0fa3f089d3e359a2a532507bb6ba34efb", size = 135180, upload-time = "2025-08-26T17:45:02.424Z" }, + { url = "https://files.pythonhosted.org/packages/bb/6a/e5bf7b70883f374710ad74faf99bacfc4b5b5a7797c1d5e130350e0e28a3/orjson-3.11.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d4a5e041ae435b815e568537755773d05dac031fee6a57b4ba70897a44d9d2", size = 132741, upload-time = "2025-08-26T17:45:03.663Z" }, + { url = "https://files.pythonhosted.org/packages/bd/0c/4577fd860b6386ffaa56440e792af01c7882b56d2766f55384b5b0e9d39b/orjson-3.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d68bf97a771836687107abfca089743885fb664b90138d8761cce61d5625d55", size = 131104, upload-time = "2025-08-26T17:45:04.939Z" }, + { url = "https://files.pythonhosted.org/packages/66/4b/83e92b2d67e86d1c33f2ea9411742a714a26de63641b082bdbf3d8e481af/orjson-3.11.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfc27516ec46f4520b18ef645864cee168d2a027dbf32c5537cb1f3e3c22dac1", size = 403887, upload-time = "2025-08-26T17:45:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e5/9eea6a14e9b5ceb4a271a1fd2e1dec5f2f686755c0fab6673dc6ff3433f4/orjson-3.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f66b001332a017d7945e177e282a40b6997056394e3ed7ddb41fb1813b83e824", size = 145855, upload-time = "2025-08-26T17:45:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/45/78/8d4f5ad0c80ba9bf8ac4d0fc71f93a7d0dc0844989e645e2074af376c307/orjson-3.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:212e67806525d2561efbfe9e799633b17eb668b8964abed6b5319b2f1cfbae1f", size = 135361, upload-time = "2025-08-26T17:45:09.625Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5f/16386970370178d7a9b438517ea3d704efcf163d286422bae3b37b88dbb5/orjson-3.11.3-cp311-cp311-win32.whl", hash = "sha256:6e8e0c3b85575a32f2ffa59de455f85ce002b8bdc0662d6b9c2ed6d80ab5d204", size = 136190, upload-time = "2025-08-26T17:45:10.962Z" }, + { url = "https://files.pythonhosted.org/packages/09/60/db16c6f7a41dd8ac9fb651f66701ff2aeb499ad9ebc15853a26c7c152448/orjson-3.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:6be2f1b5d3dc99a5ce5ce162fc741c22ba9f3443d3dd586e6a1211b7bc87bc7b", size = 131389, upload-time = "2025-08-26T17:45:12.285Z" }, + { url = "https://files.pythonhosted.org/packages/3e/2a/bb811ad336667041dea9b8565c7c9faf2f59b47eb5ab680315eea612ef2e/orjson-3.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:fafb1a99d740523d964b15c8db4eabbfc86ff29f84898262bf6e3e4c9e97e43e", size = 126120, upload-time = "2025-08-26T17:45:13.515Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b0/a7edab2a00cdcb2688e1c943401cb3236323e7bfd2839815c6131a3742f4/orjson-3.11.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8c752089db84333e36d754c4baf19c0e1437012242048439c7e80eb0e6426e3b", size = 238259, upload-time = "2025-08-26T17:45:15.093Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c6/ff4865a9cc398a07a83342713b5932e4dc3cb4bf4bc04e8f83dedfc0d736/orjson-3.11.3-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:9b8761b6cf04a856eb544acdd82fc594b978f12ac3602d6374a7edb9d86fd2c2", size = 127633, upload-time = "2025-08-26T17:45:16.417Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e6/e00bea2d9472f44fe8794f523e548ce0ad51eb9693cf538a753a27b8bda4/orjson-3.11.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b13974dc8ac6ba22feaa867fc19135a3e01a134b4f7c9c28162fed4d615008a", size = 123061, upload-time = "2025-08-26T17:45:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/54/31/9fbb78b8e1eb3ac605467cb846e1c08d0588506028b37f4ee21f978a51d4/orjson-3.11.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f83abab5bacb76d9c821fd5c07728ff224ed0e52d7a71b7b3de822f3df04e15c", size = 127956, upload-time = "2025-08-26T17:45:19.172Z" }, + { url = "https://files.pythonhosted.org/packages/36/88/b0604c22af1eed9f98d709a96302006915cfd724a7ebd27d6dd11c22d80b/orjson-3.11.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6fbaf48a744b94091a56c62897b27c31ee2da93d826aa5b207131a1e13d4064", size = 130790, upload-time = "2025-08-26T17:45:20.586Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/1c1238ae9fffbfed51ba1e507731b3faaf6b846126a47e9649222b0fd06f/orjson-3.11.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc779b4f4bba2847d0d2940081a7b6f7b5877e05408ffbb74fa1faf4a136c424", size = 132385, upload-time = "2025-08-26T17:45:22.036Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b5/c06f1b090a1c875f337e21dd71943bc9d84087f7cdf8c6e9086902c34e42/orjson-3.11.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd4b909ce4c50faa2192da6bb684d9848d4510b736b0611b6ab4020ea6fd2d23", size = 135305, upload-time = "2025-08-26T17:45:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/a0/26/5f028c7d81ad2ebbf84414ba6d6c9cac03f22f5cd0d01eb40fb2d6a06b07/orjson-3.11.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524b765ad888dc5518bbce12c77c2e83dee1ed6b0992c1790cc5fb49bb4b6667", size = 132875, upload-time = "2025-08-26T17:45:25.182Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/b8df70d9cfb56e385bf39b4e915298f9ae6c61454c8154a0f5fd7efcd42e/orjson-3.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:84fd82870b97ae3cdcea9d8746e592b6d40e1e4d4527835fc520c588d2ded04f", size = 130940, upload-time = "2025-08-26T17:45:27.209Z" }, + { url = "https://files.pythonhosted.org/packages/da/5e/afe6a052ebc1a4741c792dd96e9f65bf3939d2094e8b356503b68d48f9f5/orjson-3.11.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbecb9709111be913ae6879b07bafd4b0785b44c1eb5cac8ac76da048b3885a1", size = 403852, upload-time = "2025-08-26T17:45:28.478Z" }, + { url = "https://files.pythonhosted.org/packages/f8/90/7bbabafeb2ce65915e9247f14a56b29c9334003536009ef5b122783fe67e/orjson-3.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dba358d55aee552bd868de348f4736ca5a4086d9a62e2bfbbeeb5629fe8b0cc", size = 146293, upload-time = "2025-08-26T17:45:29.86Z" }, + { url = "https://files.pythonhosted.org/packages/27/b3/2d703946447da8b093350570644a663df69448c9d9330e5f1d9cce997f20/orjson-3.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eabcf2e84f1d7105f84580e03012270c7e97ecb1fb1618bda395061b2a84a049", size = 135470, upload-time = "2025-08-26T17:45:31.243Z" }, + { url = "https://files.pythonhosted.org/packages/38/70/b14dcfae7aff0e379b0119c8a812f8396678919c431efccc8e8a0263e4d9/orjson-3.11.3-cp312-cp312-win32.whl", hash = "sha256:3782d2c60b8116772aea8d9b7905221437fdf53e7277282e8d8b07c220f96cca", size = 136248, upload-time = "2025-08-26T17:45:32.567Z" }, + { url = "https://files.pythonhosted.org/packages/35/b8/9e3127d65de7fff243f7f3e53f59a531bf6bb295ebe5db024c2503cc0726/orjson-3.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:79b44319268af2eaa3e315b92298de9a0067ade6e6003ddaef72f8e0bedb94f1", size = 131437, upload-time = "2025-08-26T17:45:34.949Z" }, + { url = "https://files.pythonhosted.org/packages/51/92/a946e737d4d8a7fd84a606aba96220043dcc7d6988b9e7551f7f6d5ba5ad/orjson-3.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:0e92a4e83341ef79d835ca21b8bd13e27c859e4e9e4d7b63defc6e58462a3710", size = 125978, upload-time = "2025-08-26T17:45:36.422Z" }, + { url = "https://files.pythonhosted.org/packages/fc/79/8932b27293ad35919571f77cb3693b5906cf14f206ef17546052a241fdf6/orjson-3.11.3-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:af40c6612fd2a4b00de648aa26d18186cd1322330bd3a3cc52f87c699e995810", size = 238127, upload-time = "2025-08-26T17:45:38.146Z" }, + { url = "https://files.pythonhosted.org/packages/1c/82/cb93cd8cf132cd7643b30b6c5a56a26c4e780c7a145db6f83de977b540ce/orjson-3.11.3-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:9f1587f26c235894c09e8b5b7636a38091a9e6e7fe4531937534749c04face43", size = 127494, upload-time = "2025-08-26T17:45:39.57Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/2d9eb181a9b6bb71463a78882bcac1027fd29cf62c38a40cc02fc11d3495/orjson-3.11.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61dcdad16da5bb486d7227a37a2e789c429397793a6955227cedbd7252eb5a27", size = 123017, upload-time = "2025-08-26T17:45:40.876Z" }, + { url = "https://files.pythonhosted.org/packages/b4/14/a0e971e72d03b509190232356d54c0f34507a05050bd026b8db2bf2c192c/orjson-3.11.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11c6d71478e2cbea0a709e8a06365fa63da81da6498a53e4c4f065881d21ae8f", size = 127898, upload-time = "2025-08-26T17:45:42.188Z" }, + { url = "https://files.pythonhosted.org/packages/8e/af/dc74536722b03d65e17042cc30ae586161093e5b1f29bccda24765a6ae47/orjson-3.11.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff94112e0098470b665cb0ed06efb187154b63649403b8d5e9aedeb482b4548c", size = 130742, upload-time = "2025-08-26T17:45:43.511Z" }, + { url = "https://files.pythonhosted.org/packages/62/e6/7a3b63b6677bce089fe939353cda24a7679825c43a24e49f757805fc0d8a/orjson-3.11.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b756575aaa2a855a75192f356bbda11a89169830e1439cfb1a3e1a6dde7be", size = 132377, upload-time = "2025-08-26T17:45:45.525Z" }, + { url = "https://files.pythonhosted.org/packages/fc/cd/ce2ab93e2e7eaf518f0fd15e3068b8c43216c8a44ed82ac2b79ce5cef72d/orjson-3.11.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9416cc19a349c167ef76135b2fe40d03cea93680428efee8771f3e9fb66079d", size = 135313, upload-time = "2025-08-26T17:45:46.821Z" }, + { url = "https://files.pythonhosted.org/packages/d0/b4/f98355eff0bd1a38454209bbc73372ce351ba29933cb3e2eba16c04b9448/orjson-3.11.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b822caf5b9752bc6f246eb08124c3d12bf2175b66ab74bac2ef3bbf9221ce1b2", size = 132908, upload-time = "2025-08-26T17:45:48.126Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/8f5182d7bc2a1bed46ed960b61a39af8389f0ad476120cd99e67182bfb6d/orjson-3.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:414f71e3bdd5573893bf5ecdf35c32b213ed20aa15536fe2f588f946c318824f", size = 130905, upload-time = "2025-08-26T17:45:49.414Z" }, + { url = "https://files.pythonhosted.org/packages/1a/60/c41ca753ce9ffe3d0f67b9b4c093bdd6e5fdb1bc53064f992f66bb99954d/orjson-3.11.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:828e3149ad8815dc14468f36ab2a4b819237c155ee1370341b91ea4c8672d2ee", size = 403812, upload-time = "2025-08-26T17:45:51.085Z" }, + { url = "https://files.pythonhosted.org/packages/dd/13/e4a4f16d71ce1868860db59092e78782c67082a8f1dc06a3788aef2b41bc/orjson-3.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac9e05f25627ffc714c21f8dfe3a579445a5c392a9c8ae7ba1d0e9fb5333f56e", size = 146277, upload-time = "2025-08-26T17:45:52.851Z" }, + { url = "https://files.pythonhosted.org/packages/8d/8b/bafb7f0afef9344754a3a0597a12442f1b85a048b82108ef2c956f53babd/orjson-3.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e44fbe4000bd321d9f3b648ae46e0196d21577cf66ae684a96ff90b1f7c93633", size = 135418, upload-time = "2025-08-26T17:45:54.806Z" }, + { url = "https://files.pythonhosted.org/packages/60/d4/bae8e4f26afb2c23bea69d2f6d566132584d1c3a5fe89ee8c17b718cab67/orjson-3.11.3-cp313-cp313-win32.whl", hash = "sha256:2039b7847ba3eec1f5886e75e6763a16e18c68a63efc4b029ddf994821e2e66b", size = 136216, upload-time = "2025-08-26T17:45:57.182Z" }, + { url = "https://files.pythonhosted.org/packages/88/76/224985d9f127e121c8cad882cea55f0ebe39f97925de040b75ccd4b33999/orjson-3.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:29be5ac4164aa8bdcba5fa0700a3c9c316b411d8ed9d39ef8a882541bd452fae", size = 131362, upload-time = "2025-08-26T17:45:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cf/0dce7a0be94bd36d1346be5067ed65ded6adb795fdbe3abd234c8d576d01/orjson-3.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:18bd1435cb1f2857ceb59cfb7de6f92593ef7b831ccd1b9bfb28ca530e539dce", size = 125989, upload-time = "2025-08-26T17:45:59.95Z" }, + { url = "https://files.pythonhosted.org/packages/ef/77/d3b1fef1fc6aaeed4cbf3be2b480114035f4df8fa1a99d2dac1d40d6e924/orjson-3.11.3-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cf4b81227ec86935568c7edd78352a92e97af8da7bd70bdfdaa0d2e0011a1ab4", size = 238115, upload-time = "2025-08-26T17:46:01.669Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6d/468d21d49bb12f900052edcfbf52c292022d0a323d7828dc6376e6319703/orjson-3.11.3-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:bc8bc85b81b6ac9fc4dae393a8c159b817f4c2c9dee5d12b773bddb3b95fc07e", size = 127493, upload-time = "2025-08-26T17:46:03.466Z" }, + { url = "https://files.pythonhosted.org/packages/67/46/1e2588700d354aacdf9e12cc2d98131fb8ac6f31ca65997bef3863edb8ff/orjson-3.11.3-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:88dcfc514cfd1b0de038443c7b3e6a9797ffb1b3674ef1fd14f701a13397f82d", size = 122998, upload-time = "2025-08-26T17:46:04.803Z" }, + { url = "https://files.pythonhosted.org/packages/3b/94/11137c9b6adb3779f1b34fd98be51608a14b430dbc02c6d41134fbba484c/orjson-3.11.3-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d61cd543d69715d5fc0a690c7c6f8dcc307bc23abef9738957981885f5f38229", size = 132915, upload-time = "2025-08-26T17:46:06.237Z" }, + { url = "https://files.pythonhosted.org/packages/10/61/dccedcf9e9bcaac09fdabe9eaee0311ca92115699500efbd31950d878833/orjson-3.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2b7b153ed90ababadbef5c3eb39549f9476890d339cf47af563aea7e07db2451", size = 130907, upload-time = "2025-08-26T17:46:07.581Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/0e935539aa7b08b3ca0f817d73034f7eb506792aae5ecc3b7c6e679cdf5f/orjson-3.11.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7909ae2460f5f494fecbcd10613beafe40381fd0316e35d6acb5f3a05bfda167", size = 403852, upload-time = "2025-08-26T17:46:08.982Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2b/50ae1a5505cd1043379132fdb2adb8a05f37b3e1ebffe94a5073321966fd/orjson-3.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2030c01cbf77bc67bee7eef1e7e31ecf28649353987775e3583062c752da0077", size = 146309, upload-time = "2025-08-26T17:46:10.576Z" }, + { url = "https://files.pythonhosted.org/packages/cd/1d/a473c158e380ef6f32753b5f39a69028b25ec5be331c2049a2201bde2e19/orjson-3.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0169ebd1cbd94b26c7a7ad282cf5c2744fce054133f959e02eb5265deae1872", size = 135424, upload-time = "2025-08-26T17:46:12.386Z" }, + { url = "https://files.pythonhosted.org/packages/da/09/17d9d2b60592890ff7382e591aa1d9afb202a266b180c3d4049b1ec70e4a/orjson-3.11.3-cp314-cp314-win32.whl", hash = "sha256:0c6d7328c200c349e3a4c6d8c83e0a5ad029bdc2d417f234152bf34842d0fc8d", size = 136266, upload-time = "2025-08-26T17:46:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/15/58/358f6846410a6b4958b74734727e582ed971e13d335d6c7ce3e47730493e/orjson-3.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:317bbe2c069bbc757b1a2e4105b64aacd3bc78279b66a6b9e51e846e4809f804", size = 131351, upload-time = "2025-08-26T17:46:15.27Z" }, + { url = "https://files.pythonhosted.org/packages/28/01/d6b274a0635be0468d4dbd9cafe80c47105937a0d42434e805e67cd2ed8b/orjson-3.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:e8f6a7a27d7b7bec81bd5924163e9af03d49bbb63013f107b48eb5d16db711bc", size = 125985, upload-time = "2025-08-26T17:46:16.67Z" }, ] [[package]] @@ -1977,7 +2231,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1988,47 +2242,48 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest-asyncio" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload-time = "2025-05-26T04:54:40.484Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload-time = "2025-05-26T04:54:39.035Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, ] [[package]] name = "pytest-cov" -version = "6.2.1" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] [[package]] name = "pytest-mock" -version = "3.14.1" +version = "3.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/28/67172c96ba684058a4d24ffe144d64783d2a270d0af0d9e792737bddc75c/pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e", size = 33241, upload-time = "2025-05-26T13:58:45.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/99/3323ee5c16b3637b4d941c362182d3e749c11e400bea31018c42219f3a98/pytest_mock-3.15.0.tar.gz", hash = "sha256:ab896bd190316b9d5d87b277569dfcdf718b2d049a2ccff5f7aca279c002a1cf", size = 33838, upload-time = "2025-09-04T20:57:48.679Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/05/77b60e520511c53d1c1ca75f1930c7dd8e971d0c4379b7f4b3f9644685ba/pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0", size = 9923, upload-time = "2025-05-26T13:58:43.487Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b3/7fefc43fb706380144bcd293cc6e446e6f637ddfa8b83f48d1734156b529/pytest_mock-3.15.0-py3-none-any.whl", hash = "sha256:ef2219485fb1bd256b00e7ad7466ce26729b30eadfc7cbcdb4fa9a92ca68db6f", size = 10050, upload-time = "2025-09-04T20:57:47.274Z" }, ] [[package]] @@ -2194,7 +2449,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, { name = "opencv-python-headless" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-extensions" }, ] 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" } @@ -2219,16 +2475,15 @@ wheels = [ [[package]] name = "rich" -version = "14.0.0" +version = "14.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, + { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, ] [[package]] @@ -2304,27 +2559,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.2" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239, upload-time = "2025-07-03T16:40:19.566Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/1a/1f4b722862840295bcaba8c9e5261572347509548faaa99b2d57ee7bfe6a/ruff-0.13.0.tar.gz", hash = "sha256:5b4b1ee7eb35afae128ab94459b13b2baaed282b1fb0f472a73c82c996c8ae60", size = 5372863, upload-time = "2025-09-10T16:25:37.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761, upload-time = "2025-07-03T16:39:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659, upload-time = "2025-07-03T16:39:42.294Z" }, - { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769, upload-time = "2025-07-03T16:39:44.75Z" }, - { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602, upload-time = "2025-07-03T16:39:47.652Z" }, - { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772, upload-time = "2025-07-03T16:39:49.641Z" }, - { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173, upload-time = "2025-07-03T16:39:52.069Z" }, - { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002, upload-time = "2025-07-03T16:39:54.551Z" }, - { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330, upload-time = "2025-07-03T16:39:57.55Z" }, - { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717, upload-time = "2025-07-03T16:39:59.78Z" }, - { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659, upload-time = "2025-07-03T16:40:01.934Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012, upload-time = "2025-07-03T16:40:04.363Z" }, - { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799, upload-time = "2025-07-03T16:40:06.514Z" }, - { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507, upload-time = "2025-07-03T16:40:08.708Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609, upload-time = "2025-07-03T16:40:10.836Z" }, - { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823, upload-time = "2025-07-03T16:40:13.203Z" }, - { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831, upload-time = "2025-07-03T16:40:15.478Z" }, - { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334, upload-time = "2025-07-03T16:40:17.677Z" }, + { url = "https://files.pythonhosted.org/packages/ac/fe/6f87b419dbe166fd30a991390221f14c5b68946f389ea07913e1719741e0/ruff-0.13.0-py3-none-linux_armv6l.whl", hash = "sha256:137f3d65d58ee828ae136a12d1dc33d992773d8f7644bc6b82714570f31b2004", size = 12187826, upload-time = "2025-09-10T16:24:39.5Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/c92296b1fc36d2499e12b74a3fdb230f77af7bdf048fad7b0a62e94ed56a/ruff-0.13.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:21ae48151b66e71fd111b7d79f9ad358814ed58c339631450c66a4be33cc28b9", size = 12933428, upload-time = "2025-09-10T16:24:43.866Z" }, + { url = "https://files.pythonhosted.org/packages/44/cf/40bc7221a949470307d9c35b4ef5810c294e6cfa3caafb57d882731a9f42/ruff-0.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:64de45f4ca5441209e41742d527944635a05a6e7c05798904f39c85bafa819e3", size = 12095543, upload-time = "2025-09-10T16:24:46.638Z" }, + { url = "https://files.pythonhosted.org/packages/f1/03/8b5ff2a211efb68c63a1d03d157e924997ada87d01bebffbd13a0f3fcdeb/ruff-0.13.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b2c653ae9b9d46e0ef62fc6fbf5b979bda20a0b1d2b22f8f7eb0cde9f4963b8", size = 12312489, upload-time = "2025-09-10T16:24:49.556Z" }, + { url = "https://files.pythonhosted.org/packages/37/fc/2336ef6d5e9c8d8ea8305c5f91e767d795cd4fc171a6d97ef38a5302dadc/ruff-0.13.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cec632534332062bc9eb5884a267b689085a1afea9801bf94e3ba7498a2d207", size = 11991631, upload-time = "2025-09-10T16:24:53.439Z" }, + { url = "https://files.pythonhosted.org/packages/39/7f/f6d574d100fca83d32637d7f5541bea2f5e473c40020bbc7fc4a4d5b7294/ruff-0.13.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd628101d9f7d122e120ac7c17e0a0f468b19bc925501dbe03c1cb7f5415b24", size = 13720602, upload-time = "2025-09-10T16:24:56.392Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c8/a8a5b81d8729b5d1f663348d11e2a9d65a7a9bd3c399763b1a51c72be1ce/ruff-0.13.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afe37db8e1466acb173bb2a39ca92df00570e0fd7c94c72d87b51b21bb63efea", size = 14697751, upload-time = "2025-09-10T16:24:59.89Z" }, + { url = "https://files.pythonhosted.org/packages/57/f5/183ec292272ce7ec5e882aea74937f7288e88ecb500198b832c24debc6d3/ruff-0.13.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f96a8d90bb258d7d3358b372905fe7333aaacf6c39e2408b9f8ba181f4b6ef2", size = 14095317, upload-time = "2025-09-10T16:25:03.025Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8d/7f9771c971724701af7926c14dab31754e7b303d127b0d3f01116faef456/ruff-0.13.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b5e3d883e4f924c5298e3f2ee0f3085819c14f68d1e5b6715597681433f153", size = 13144418, upload-time = "2025-09-10T16:25:06.272Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a6/7985ad1778e60922d4bef546688cd8a25822c58873e9ff30189cfe5dc4ab/ruff-0.13.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03447f3d18479df3d24917a92d768a89f873a7181a064858ea90a804a7538991", size = 13370843, upload-time = "2025-09-10T16:25:09.965Z" }, + { url = "https://files.pythonhosted.org/packages/64/1c/bafdd5a7a05a50cc51d9f5711da704942d8dd62df3d8c70c311e98ce9f8a/ruff-0.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fbc6b1934eb1c0033da427c805e27d164bb713f8e273a024a7e86176d7f462cf", size = 13321891, upload-time = "2025-09-10T16:25:12.969Z" }, + { url = "https://files.pythonhosted.org/packages/bc/3e/7817f989cb9725ef7e8d2cee74186bf90555279e119de50c750c4b7a72fe/ruff-0.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8ab6a3e03665d39d4a25ee199d207a488724f022db0e1fe4002968abdb8001b", size = 12119119, upload-time = "2025-09-10T16:25:16.621Z" }, + { url = "https://files.pythonhosted.org/packages/58/07/9df080742e8d1080e60c426dce6e96a8faf9a371e2ce22eef662e3839c95/ruff-0.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2a5c62f8ccc6dd2fe259917482de7275cecc86141ee10432727c4816235bc41", size = 11961594, upload-time = "2025-09-10T16:25:19.49Z" }, + { url = "https://files.pythonhosted.org/packages/6a/f4/ae1185349197d26a2316840cb4d6c3fba61d4ac36ed728bf0228b222d71f/ruff-0.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b7b85ca27aeeb1ab421bc787009831cffe6048faae08ad80867edab9f2760945", size = 12933377, upload-time = "2025-09-10T16:25:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/b6/39/e776c10a3b349fc8209a905bfb327831d7516f6058339a613a8d2aaecacd/ruff-0.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:79ea0c44a3032af768cabfd9616e44c24303af49d633b43e3a5096e009ebe823", size = 13418555, upload-time = "2025-09-10T16:25:25.681Z" }, + { url = "https://files.pythonhosted.org/packages/46/09/dca8df3d48e8b3f4202bf20b1658898e74b6442ac835bfe2c1816d926697/ruff-0.13.0-py3-none-win32.whl", hash = "sha256:4e473e8f0e6a04e4113f2e1de12a5039579892329ecc49958424e5568ef4f768", size = 12141613, upload-time = "2025-09-10T16:25:28.664Z" }, + { url = "https://files.pythonhosted.org/packages/61/21/0647eb71ed99b888ad50e44d8ec65d7148babc0e242d531a499a0bbcda5f/ruff-0.13.0-py3-none-win_amd64.whl", hash = "sha256:48e5c25c7a3713eea9ce755995767f4dcd1b0b9599b638b12946e892123d1efb", size = 13258250, upload-time = "2025-09-10T16:25:31.773Z" }, + { url = "https://files.pythonhosted.org/packages/e1/a3/03216a6a86c706df54422612981fb0f9041dbb452c3401501d4a22b942c9/ruff-0.13.0-py3-none-win_arm64.whl", hash = "sha256:ab80525317b1e1d38614addec8ac954f1b3e662de9d59114ecbf771d00cf613e", size = 12312357, upload-time = "2025-09-10T16:25:35.595Z" }, ] [[package]] @@ -2338,7 +2594,8 @@ dependencies = [ { name = "numpy" }, { name = "packaging" }, { name = "pillow" }, - { name = "scipy" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tifffile" }, ] 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" } @@ -2364,11 +2621,16 @@ wheels = [ name = "scikit-learn" version = "1.3.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] dependencies = [ - { name = "joblib" }, - { name = "numpy" }, - { name = "scipy" }, - { name = "threadpoolctl" }, + { name = "joblib", marker = "python_full_version < '3.11'" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.11.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -2389,12 +2651,70 @@ wheels = [ { 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]] +name = "scikit-learn" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "numpy", marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/84/5f4af978fff619706b8961accac84780a6d298d82a8873446f72edb4ead0/scikit_learn-1.7.1.tar.gz", hash = "sha256:24b3f1e976a4665aa74ee0fcaac2b8fccc6ae77c8e07ab25da3ba6d3292b9802", size = 7190445, upload-time = "2025-07-18T08:01:54.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/88/0dd5be14ef19f2d80a77780be35a33aa94e8a3b3223d80bee8892a7832b4/scikit_learn-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:406204dd4004f0517f0b23cf4b28c6245cbd51ab1b6b78153bc784def214946d", size = 9338868, upload-time = "2025-07-18T08:01:00.25Z" }, + { url = "https://files.pythonhosted.org/packages/fd/52/3056b6adb1ac58a0bc335fc2ed2fcf599974d908855e8cb0ca55f797593c/scikit_learn-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:16af2e44164f05d04337fd1fc3ae7c4ea61fd9b0d527e22665346336920fe0e1", size = 8655943, upload-time = "2025-07-18T08:01:02.974Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a4/e488acdece6d413f370a9589a7193dac79cd486b2e418d3276d6ea0b9305/scikit_learn-1.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2f2e78e56a40c7587dea9a28dc4a49500fa2ead366869418c66f0fd75b80885c", size = 9652056, upload-time = "2025-07-18T08:01:04.978Z" }, + { url = "https://files.pythonhosted.org/packages/18/41/bceacec1285b94eb9e4659b24db46c23346d7e22cf258d63419eb5dec6f7/scikit_learn-1.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b62b76ad408a821475b43b7bb90a9b1c9a4d8d125d505c2df0539f06d6e631b1", size = 9473691, upload-time = "2025-07-18T08:01:07.006Z" }, + { url = "https://files.pythonhosted.org/packages/12/7b/e1ae4b7e1dd85c4ca2694ff9cc4a9690970fd6150d81b975e6c5c6f8ee7c/scikit_learn-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:9963b065677a4ce295e8ccdee80a1dd62b37249e667095039adcd5bce6e90deb", size = 8900873, upload-time = "2025-07-18T08:01:09.332Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bd/a23177930abd81b96daffa30ef9c54ddbf544d3226b8788ce4c3ef1067b4/scikit_learn-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90c8494ea23e24c0fb371afc474618c1019dc152ce4a10e4607e62196113851b", size = 9334838, upload-time = "2025-07-18T08:01:11.239Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a1/d3a7628630a711e2ac0d1a482910da174b629f44e7dd8cfcd6924a4ef81a/scikit_learn-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:bb870c0daf3bf3be145ec51df8ac84720d9972170786601039f024bf6d61a518", size = 8651241, upload-time = "2025-07-18T08:01:13.234Z" }, + { url = "https://files.pythonhosted.org/packages/26/92/85ec172418f39474c1cd0221d611345d4f433fc4ee2fc68e01f524ccc4e4/scikit_learn-1.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40daccd1b5623f39e8943ab39735cadf0bdce80e67cdca2adcb5426e987320a8", size = 9718677, upload-time = "2025-07-18T08:01:15.649Z" }, + { url = "https://files.pythonhosted.org/packages/df/ce/abdb1dcbb1d2b66168ec43b23ee0cee356b4cc4100ddee3943934ebf1480/scikit_learn-1.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30d1f413cfc0aa5a99132a554f1d80517563c34a9d3e7c118fde2d273c6fe0f7", size = 9511189, upload-time = "2025-07-18T08:01:18.013Z" }, + { url = "https://files.pythonhosted.org/packages/b2/3b/47b5eaee01ef2b5a80ba3f7f6ecf79587cb458690857d4777bfd77371c6f/scikit_learn-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c711d652829a1805a95d7fe96654604a8f16eab5a9e9ad87b3e60173415cb650", size = 8914794, upload-time = "2025-07-18T08:01:20.357Z" }, + { url = "https://files.pythonhosted.org/packages/cb/16/57f176585b35ed865f51b04117947fe20f130f78940c6477b6d66279c9c2/scikit_learn-1.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3cee419b49b5bbae8796ecd690f97aa412ef1674410c23fc3257c6b8b85b8087", size = 9260431, upload-time = "2025-07-18T08:01:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/67/4e/899317092f5efcab0e9bc929e3391341cec8fb0e816c4789686770024580/scikit_learn-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2fd8b8d35817b0d9ebf0b576f7d5ffbbabdb55536b0655a8aaae629d7ffd2e1f", size = 8637191, upload-time = "2025-07-18T08:01:24.731Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1b/998312db6d361ded1dd56b457ada371a8d8d77ca2195a7d18fd8a1736f21/scikit_learn-1.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:588410fa19a96a69763202f1d6b7b91d5d7a5d73be36e189bc6396bfb355bd87", size = 9486346, upload-time = "2025-07-18T08:01:26.713Z" }, + { url = "https://files.pythonhosted.org/packages/ad/09/a2aa0b4e644e5c4ede7006748f24e72863ba2ae71897fecfd832afea01b4/scikit_learn-1.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3142f0abe1ad1d1c31a2ae987621e41f6b578144a911ff4ac94781a583adad7", size = 9290988, upload-time = "2025-07-18T08:01:28.938Z" }, + { url = "https://files.pythonhosted.org/packages/15/fa/c61a787e35f05f17fc10523f567677ec4eeee5f95aa4798dbbbcd9625617/scikit_learn-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ddd9092c1bd469acab337d87930067c87eac6bd544f8d5027430983f1e1ae88", size = 8735568, upload-time = "2025-07-18T08:01:30.936Z" }, + { url = "https://files.pythonhosted.org/packages/52/f8/e0533303f318a0f37b88300d21f79b6ac067188d4824f1047a37214ab718/scikit_learn-1.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b7839687fa46d02e01035ad775982f2470be2668e13ddd151f0f55a5bf123bae", size = 9213143, upload-time = "2025-07-18T08:01:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/71/f3/f1df377d1bdfc3e3e2adc9c119c238b182293e6740df4cbeac6de2cc3e23/scikit_learn-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a10f276639195a96c86aa572ee0698ad64ee939a7b042060b98bd1930c261d10", size = 8591977, upload-time = "2025-07-18T08:01:34.967Z" }, + { url = "https://files.pythonhosted.org/packages/99/72/c86a4cd867816350fe8dee13f30222340b9cd6b96173955819a5561810c5/scikit_learn-1.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13679981fdaebc10cc4c13c43344416a86fcbc61449cb3e6517e1df9d12c8309", size = 9436142, upload-time = "2025-07-18T08:01:37.397Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/277967b29bd297538dc7a6ecfb1a7dce751beabd0d7f7a2233be7a4f7832/scikit_learn-1.7.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f1262883c6a63f067a980a8cdd2d2e7f2513dddcef6a9eaada6416a7a7cbe43", size = 9282996, upload-time = "2025-07-18T08:01:39.721Z" }, + { url = "https://files.pythonhosted.org/packages/e2/47/9291cfa1db1dae9880420d1e07dbc7e8dd4a7cdbc42eaba22512e6bde958/scikit_learn-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca6d31fb10e04d50bfd2b50d66744729dbb512d4efd0223b864e2fdbfc4cee11", size = 8707418, upload-time = "2025-07-18T08:01:42.124Z" }, + { url = "https://files.pythonhosted.org/packages/61/95/45726819beccdaa34d3362ea9b2ff9f2b5d3b8bf721bd632675870308ceb/scikit_learn-1.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:781674d096303cfe3d351ae6963ff7c958db61cde3421cd490e3a5a58f2a94ae", size = 9561466, upload-time = "2025-07-18T08:01:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1c/6f4b3344805de783d20a51eb24d4c9ad4b11a7f75c1801e6ec6d777361fd/scikit_learn-1.7.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:10679f7f125fe7ecd5fad37dd1aa2daae7e3ad8df7f3eefa08901b8254b3e12c", size = 9040467, upload-time = "2025-07-18T08:01:46.671Z" }, + { url = "https://files.pythonhosted.org/packages/6f/80/abe18fe471af9f1d181904203d62697998b27d9b62124cd281d740ded2f9/scikit_learn-1.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f812729e38c8cb37f760dce71a9b83ccfb04f59b3dca7c6079dcdc60544fa9e", size = 9532052, upload-time = "2025-07-18T08:01:48.676Z" }, + { url = "https://files.pythonhosted.org/packages/14/82/b21aa1e0c4cee7e74864d3a5a721ab8fcae5ca55033cb6263dca297ed35b/scikit_learn-1.7.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88e1a20131cf741b84b89567e1717f27a2ced228e0f29103426102bc2e3b8ef7", size = 9361575, upload-time = "2025-07-18T08:01:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/f4777fcd5627dc6695fa6b92179d0edb7a3ac1b91bcd9a1c7f64fa7ade23/scikit_learn-1.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b1bd1d919210b6a10b7554b717c9000b5485aa95a1d0f177ae0d7ee8ec750da5", size = 9277310, upload-time = "2025-07-18T08:01:52.547Z" }, +] + [[package]] name = "scipy" version = "1.11.4" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "python_full_version < '3.11'" }, ] 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 = [ @@ -2418,6 +2738,85 @@ wheels = [ { 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 = "scipy" +version = "1.16.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/4a/b927028464795439faec8eaf0b03b011005c487bb2d07409f28bf30879c4/scipy-1.16.1.tar.gz", hash = "sha256:44c76f9e8b6e8e488a586190ab38016e4ed2f8a038af7cd3defa903c0a2238b3", size = 30580861, upload-time = "2025-07-27T16:33:30.834Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/91/812adc6f74409b461e3a5fa97f4f74c769016919203138a3bf6fc24ba4c5/scipy-1.16.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:c033fa32bab91dc98ca59d0cf23bb876454e2bb02cbe592d5023138778f70030", size = 36552519, upload-time = "2025-07-27T16:26:29.658Z" }, + { url = "https://files.pythonhosted.org/packages/47/18/8e355edcf3b71418d9e9f9acd2708cc3a6c27e8f98fde0ac34b8a0b45407/scipy-1.16.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6e5c2f74e5df33479b5cd4e97a9104c511518fbd979aa9b8f6aec18b2e9ecae7", size = 28638010, upload-time = "2025-07-27T16:26:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/d9/eb/e931853058607bdfbc11b86df19ae7a08686121c203483f62f1ecae5989c/scipy-1.16.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0a55ffe0ba0f59666e90951971a884d1ff6f4ec3275a48f472cfb64175570f77", size = 20909790, upload-time = "2025-07-27T16:26:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/be83a271d6e96750cd0be2e000f35ff18880a46f05ce8b5d3465dc0f7a2a/scipy-1.16.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f8a5d6cd147acecc2603fbd382fed6c46f474cccfcf69ea32582e033fb54dcfe", size = 23513352, upload-time = "2025-07-27T16:26:50.017Z" }, + { url = "https://files.pythonhosted.org/packages/7c/bf/fe6eb47e74f762f933cca962db7f2c7183acfdc4483bd1c3813cfe83e538/scipy-1.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb18899127278058bcc09e7b9966d41a5a43740b5bb8dcba401bd983f82e885b", size = 33534643, upload-time = "2025-07-27T16:26:57.503Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ba/63f402e74875486b87ec6506a4f93f6d8a0d94d10467280f3d9d7837ce3a/scipy-1.16.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adccd93a2fa937a27aae826d33e3bfa5edf9aa672376a4852d23a7cd67a2e5b7", size = 35376776, upload-time = "2025-07-27T16:27:06.639Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b4/04eb9d39ec26a1b939689102da23d505ea16cdae3dbb18ffc53d1f831044/scipy-1.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:18aca1646a29ee9a0625a1be5637fa798d4d81fdf426481f06d69af828f16958", size = 35698906, upload-time = "2025-07-27T16:27:14.943Z" }, + { url = "https://files.pythonhosted.org/packages/04/d6/bb5468da53321baeb001f6e4e0d9049eadd175a4a497709939128556e3ec/scipy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d85495cef541729a70cdddbbf3e6b903421bc1af3e8e3a9a72a06751f33b7c39", size = 38129275, upload-time = "2025-07-27T16:27:23.873Z" }, + { url = "https://files.pythonhosted.org/packages/c4/94/994369978509f227cba7dfb9e623254d0d5559506fe994aef4bea3ed469c/scipy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:226652fca853008119c03a8ce71ffe1b3f6d2844cc1686e8f9806edafae68596", size = 38644572, upload-time = "2025-07-27T16:27:32.637Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d9/ec4864f5896232133f51382b54a08de91a9d1af7a76dfa372894026dfee2/scipy-1.16.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81b433bbeaf35728dad619afc002db9b189e45eebe2cd676effe1fb93fef2b9c", size = 36575194, upload-time = "2025-07-27T16:27:41.321Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6d/40e81ecfb688e9d25d34a847dca361982a6addf8e31f0957b1a54fbfa994/scipy-1.16.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:886cc81fdb4c6903a3bb0464047c25a6d1016fef77bb97949817d0c0d79f9e04", size = 28594590, upload-time = "2025-07-27T16:27:49.204Z" }, + { url = "https://files.pythonhosted.org/packages/0e/37/9f65178edfcc629377ce9a64fc09baebea18c80a9e57ae09a52edf84880b/scipy-1.16.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:15240c3aac087a522b4eaedb09f0ad061753c5eebf1ea430859e5bf8640d5919", size = 20866458, upload-time = "2025-07-27T16:27:54.98Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7b/749a66766871ea4cb1d1ea10f27004db63023074c22abed51f22f09770e0/scipy-1.16.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f81a25805f3659b48126b5053d9e823d3215e4a63730b5e1671852a1705921", size = 23539318, upload-time = "2025-07-27T16:28:01.604Z" }, + { url = "https://files.pythonhosted.org/packages/c4/db/8d4afec60eb833a666434d4541a3151eedbf2494ea6d4d468cbe877f00cd/scipy-1.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6c62eea7f607f122069b9bad3f99489ddca1a5173bef8a0c75555d7488b6f725", size = 33292899, upload-time = "2025-07-27T16:28:09.147Z" }, + { url = "https://files.pythonhosted.org/packages/51/1e/79023ca3bbb13a015d7d2757ecca3b81293c663694c35d6541b4dca53e98/scipy-1.16.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f965bbf3235b01c776115ab18f092a95aa74c271a52577bcb0563e85738fd618", size = 35162637, upload-time = "2025-07-27T16:28:17.535Z" }, + { url = "https://files.pythonhosted.org/packages/b6/49/0648665f9c29fdaca4c679182eb972935b3b4f5ace41d323c32352f29816/scipy-1.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f006e323874ffd0b0b816d8c6a8e7f9a73d55ab3b8c3f72b752b226d0e3ac83d", size = 35490507, upload-time = "2025-07-27T16:28:25.705Z" }, + { url = "https://files.pythonhosted.org/packages/62/8f/66cbb9d6bbb18d8c658f774904f42a92078707a7c71e5347e8bf2f52bb89/scipy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8fd15fc5085ab4cca74cb91fe0a4263b1f32e4420761ddae531ad60934c2119", size = 37923998, upload-time = "2025-07-27T16:28:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/14/c3/61f273ae550fbf1667675701112e380881905e28448c080b23b5a181df7c/scipy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7b8013c6c066609577d910d1a2a077021727af07b6fab0ee22c2f901f22352a", size = 38508060, upload-time = "2025-07-27T16:28:43.242Z" }, + { url = "https://files.pythonhosted.org/packages/93/0b/b5c99382b839854a71ca9482c684e3472badc62620287cbbdab499b75ce6/scipy-1.16.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5451606823a5e73dfa621a89948096c6528e2896e40b39248295d3a0138d594f", size = 36533717, upload-time = "2025-07-27T16:28:51.706Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e5/69ab2771062c91e23e07c12e7d5033a6b9b80b0903ee709c3c36b3eb520c/scipy-1.16.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:89728678c5ca5abd610aee148c199ac1afb16e19844401ca97d43dc548a354eb", size = 28570009, upload-time = "2025-07-27T16:28:57.017Z" }, + { url = "https://files.pythonhosted.org/packages/f4/69/bd75dbfdd3cf524f4d753484d723594aed62cfaac510123e91a6686d520b/scipy-1.16.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e756d688cb03fd07de0fffad475649b03cb89bee696c98ce508b17c11a03f95c", size = 20841942, upload-time = "2025-07-27T16:29:01.152Z" }, + { url = "https://files.pythonhosted.org/packages/ea/74/add181c87663f178ba7d6144b370243a87af8476664d5435e57d599e6874/scipy-1.16.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5aa2687b9935da3ed89c5dbed5234576589dd28d0bf7cd237501ccfbdf1ad608", size = 23498507, upload-time = "2025-07-27T16:29:05.202Z" }, + { url = "https://files.pythonhosted.org/packages/1d/74/ece2e582a0d9550cee33e2e416cc96737dce423a994d12bbe59716f47ff1/scipy-1.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0851f6a1e537fe9399f35986897e395a1aa61c574b178c0d456be5b1a0f5ca1f", size = 33286040, upload-time = "2025-07-27T16:29:10.201Z" }, + { url = "https://files.pythonhosted.org/packages/e4/82/08e4076df538fb56caa1d489588d880ec7c52d8273a606bb54d660528f7c/scipy-1.16.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fedc2cbd1baed37474b1924c331b97bdff611d762c196fac1a9b71e67b813b1b", size = 35176096, upload-time = "2025-07-27T16:29:17.091Z" }, + { url = "https://files.pythonhosted.org/packages/fa/79/cd710aab8c921375711a8321c6be696e705a120e3011a643efbbcdeeabcc/scipy-1.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2ef500e72f9623a6735769e4b93e9dcb158d40752cdbb077f305487e3e2d1f45", size = 35490328, upload-time = "2025-07-27T16:29:22.928Z" }, + { url = "https://files.pythonhosted.org/packages/71/73/e9cc3d35ee4526d784520d4494a3e1ca969b071fb5ae5910c036a375ceec/scipy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:978d8311674b05a8f7ff2ea6c6bce5d8b45a0cb09d4c5793e0318f448613ea65", size = 37939921, upload-time = "2025-07-27T16:29:29.108Z" }, + { url = "https://files.pythonhosted.org/packages/21/12/c0efd2941f01940119b5305c375ae5c0fcb7ec193f806bd8f158b73a1782/scipy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:81929ed0fa7a5713fcdd8b2e6f73697d3b4c4816d090dd34ff937c20fa90e8ab", size = 38479462, upload-time = "2025-07-27T16:30:24.078Z" }, + { url = "https://files.pythonhosted.org/packages/7a/19/c3d08b675260046a991040e1ea5d65f91f40c7df1045fffff412dcfc6765/scipy-1.16.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:bcc12db731858abda693cecdb3bdc9e6d4bd200213f49d224fe22df82687bdd6", size = 36938832, upload-time = "2025-07-27T16:29:35.057Z" }, + { url = "https://files.pythonhosted.org/packages/81/f2/ce53db652c033a414a5b34598dba6b95f3d38153a2417c5a3883da429029/scipy-1.16.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:744d977daa4becb9fc59135e75c069f8d301a87d64f88f1e602a9ecf51e77b27", size = 29093084, upload-time = "2025-07-27T16:29:40.201Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/7a10ff04a7dc15f9057d05b33737ade244e4bd195caa3f7cc04d77b9e214/scipy-1.16.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:dc54f76ac18073bcecffb98d93f03ed6b81a92ef91b5d3b135dcc81d55a724c7", size = 21365098, upload-time = "2025-07-27T16:29:44.295Z" }, + { url = "https://files.pythonhosted.org/packages/36/ac/029ff710959932ad3c2a98721b20b405f05f752f07344622fd61a47c5197/scipy-1.16.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:367d567ee9fc1e9e2047d31f39d9d6a7a04e0710c86e701e053f237d14a9b4f6", size = 23896858, upload-time = "2025-07-27T16:29:48.784Z" }, + { url = "https://files.pythonhosted.org/packages/71/13/d1ef77b6bd7898720e1f0b6b3743cb945f6c3cafa7718eaac8841035ab60/scipy-1.16.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4cf5785e44e19dcd32a0e4807555e1e9a9b8d475c6afff3d21c3c543a6aa84f4", size = 33438311, upload-time = "2025-07-27T16:29:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e0/e64a6821ffbb00b4c5b05169f1c1fddb4800e9307efe3db3788995a82a2c/scipy-1.16.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3d0b80fb26d3e13a794c71d4b837e2a589d839fd574a6bbb4ee1288c213ad4a3", size = 35279542, upload-time = "2025-07-27T16:30:00.249Z" }, + { url = "https://files.pythonhosted.org/packages/57/59/0dc3c8b43e118f1e4ee2b798dcc96ac21bb20014e5f1f7a8e85cc0653bdb/scipy-1.16.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8503517c44c18d1030d666cb70aaac1cc8913608816e06742498833b128488b7", size = 35667665, upload-time = "2025-07-27T16:30:05.916Z" }, + { url = "https://files.pythonhosted.org/packages/45/5f/844ee26e34e2f3f9f8febb9343748e72daeaec64fe0c70e9bf1ff84ec955/scipy-1.16.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:30cc4bb81c41831ecfd6dc450baf48ffd80ef5aed0f5cf3ea775740e80f16ecc", size = 38045210, upload-time = "2025-07-27T16:30:11.655Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d7/210f2b45290f444f1de64bc7353aa598ece9f0e90c384b4a156f9b1a5063/scipy-1.16.1-cp313-cp313t-win_amd64.whl", hash = "sha256:c24fa02f7ed23ae514460a22c57eca8f530dbfa50b1cfdbf4f37c05b5309cc39", size = 38593661, upload-time = "2025-07-27T16:30:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/81/ea/84d481a5237ed223bd3d32d6e82d7a6a96e34756492666c260cef16011d1/scipy-1.16.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:796a5a9ad36fa3a782375db8f4241ab02a091308eb079746bc0f874c9b998318", size = 36525921, upload-time = "2025-07-27T16:30:30.081Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9f/d9edbdeff9f3a664807ae3aea383e10afaa247e8e6255e6d2aa4515e8863/scipy-1.16.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:3ea0733a2ff73fd6fdc5fecca54ee9b459f4d74f00b99aced7d9a3adb43fb1cc", size = 28564152, upload-time = "2025-07-27T16:30:35.336Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/8125bcb1fe04bc267d103e76516243e8d5e11229e6b306bda1024a5423d1/scipy-1.16.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:85764fb15a2ad994e708258bb4ed8290d1305c62a4e1ef07c414356a24fcfbf8", size = 20836028, upload-time = "2025-07-27T16:30:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/77/9c/bf92e215701fc70bbcd3d14d86337cf56a9b912a804b9c776a269524a9e9/scipy-1.16.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ca66d980469cb623b1759bdd6e9fd97d4e33a9fad5b33771ced24d0cb24df67e", size = 23489666, upload-time = "2025-07-27T16:30:43.663Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/5e941d397d9adac41b02839011594620d54d99488d1be5be755c00cde9ee/scipy-1.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7cc1ffcc230f568549fc56670bcf3df1884c30bd652c5da8138199c8c76dae0", size = 33358318, upload-time = "2025-07-27T16:30:48.982Z" }, + { url = "https://files.pythonhosted.org/packages/0e/87/8db3aa10dde6e3e8e7eb0133f24baa011377d543f5b19c71469cf2648026/scipy-1.16.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ddfb1e8d0b540cb4ee9c53fc3dea3186f97711248fb94b4142a1b27178d8b4b", size = 35185724, upload-time = "2025-07-27T16:30:54.26Z" }, + { url = "https://files.pythonhosted.org/packages/89/b4/6ab9ae443216807622bcff02690262d8184078ea467efee2f8c93288a3b1/scipy-1.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4dc0e7be79e95d8ba3435d193e0d8ce372f47f774cffd882f88ea4e1e1ddc731", size = 35554335, upload-time = "2025-07-27T16:30:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9a/d0e9dc03c5269a1afb60661118296a32ed5d2c24298af61b676c11e05e56/scipy-1.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f23634f9e5adb51b2a77766dac217063e764337fbc816aa8ad9aaebcd4397fd3", size = 37960310, upload-time = "2025-07-27T16:31:06.151Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/c8f3130a50521a7977874817ca89e0599b1b4ee8e938bad8ae798a0e1f0d/scipy-1.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:57d75524cb1c5a374958a2eae3d84e1929bb971204cc9d52213fb8589183fc19", size = 39319239, upload-time = "2025-07-27T16:31:59.942Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f2/1ca3eda54c3a7e4c92f6acef7db7b3a057deb135540d23aa6343ef8ad333/scipy-1.16.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:d8da7c3dd67bcd93f15618938f43ed0995982eb38973023d46d4646c4283ad65", size = 36939460, upload-time = "2025-07-27T16:31:11.865Z" }, + { url = "https://files.pythonhosted.org/packages/80/30/98c2840b293a132400c0940bb9e140171dcb8189588619048f42b2ce7b4f/scipy-1.16.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:cc1d2f2fd48ba1e0620554fe5bc44d3e8f5d4185c8c109c7fbdf5af2792cfad2", size = 29093322, upload-time = "2025-07-27T16:31:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e6/1e6e006e850622cf2a039b62d1a6ddc4497d4851e58b68008526f04a9a00/scipy-1.16.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:21a611ced9275cb861bacadbada0b8c0623bc00b05b09eb97f23b370fc2ae56d", size = 21365329, upload-time = "2025-07-27T16:31:21.188Z" }, + { url = "https://files.pythonhosted.org/packages/8e/02/72a5aa5b820589dda9a25e329ca752842bfbbaf635e36bc7065a9b42216e/scipy-1.16.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dfbb25dffc4c3dd9371d8ab456ca81beeaf6f9e1c2119f179392f0dc1ab7695", size = 23897544, upload-time = "2025-07-27T16:31:25.408Z" }, + { url = "https://files.pythonhosted.org/packages/2b/dc/7122d806a6f9eb8a33532982234bed91f90272e990f414f2830cfe656e0b/scipy-1.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0ebb7204f063fad87fc0a0e4ff4a2ff40b2a226e4ba1b7e34bf4b79bf97cd86", size = 33442112, upload-time = "2025-07-27T16:31:30.62Z" }, + { url = "https://files.pythonhosted.org/packages/24/39/e383af23564daa1021a5b3afbe0d8d6a68ec639b943661841f44ac92de85/scipy-1.16.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f1b9e5962656f2734c2b285a8745358ecb4e4efbadd00208c80a389227ec61ff", size = 35286594, upload-time = "2025-07-27T16:31:36.112Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/1a0b0aff40c3056d955f38b0df5d178350c3d74734ec54f9c68d23910be5/scipy-1.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e1a106f8c023d57a2a903e771228bf5c5b27b5d692088f457acacd3b54511e4", size = 35665080, upload-time = "2025-07-27T16:31:42.025Z" }, + { url = "https://files.pythonhosted.org/packages/64/df/ce88803e9ed6e27fe9b9abefa157cf2c80e4fa527cf17ee14be41f790ad4/scipy-1.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:709559a1db68a9abc3b2c8672c4badf1614f3b440b3ab326d86a5c0491eafae3", size = 38050306, upload-time = "2025-07-27T16:31:48.109Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6c/a76329897a7cae4937d403e623aa6aaea616a0bb5b36588f0b9d1c9a3739/scipy-1.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c0c804d60492a0aad7f5b2bb1862f4548b990049e27e828391ff2bf6f7199998", size = 39427705, upload-time = "2025-07-27T16:31:53.96Z" }, +] + [[package]] name = "setuptools" version = "70.3.0" @@ -2504,27 +2903,27 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.21.2" +version = "0.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545, upload-time = "2025-06-24T10:24:52.449Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/b4/c1ce3699e81977da2ace8b16d2badfd42b060e7d33d75c4ccdbf9dc920fa/tokenizers-0.22.0.tar.gz", hash = "sha256:2e33b98525be8453f355927f3cab312c36cd3e44f4d7e9e97da2fa94d0a49dcb", size = 362771, upload-time = "2025-08-29T10:25:33.914Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206, upload-time = "2025-06-24T10:24:42.755Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655, upload-time = "2025-06-24T10:24:41.56Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202, upload-time = "2025-06-24T10:24:31.791Z" }, - { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539, upload-time = "2025-06-24T10:24:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665, upload-time = "2025-06-24T10:24:39.024Z" }, - { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305, upload-time = "2025-06-24T10:24:36.133Z" }, - { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757, upload-time = "2025-06-24T10:24:37.784Z" }, - { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887, upload-time = "2025-06-24T10:24:40.293Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965, upload-time = "2025-06-24T10:24:44.431Z" }, - { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372, upload-time = "2025-06-24T10:24:46.455Z" }, - { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632, upload-time = "2025-06-24T10:24:48.446Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074, upload-time = "2025-06-24T10:24:50.378Z" }, - { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115, upload-time = "2025-06-24T10:24:55.069Z" }, - { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918, upload-time = "2025-06-24T10:24:53.71Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b1/18c13648edabbe66baa85fe266a478a7931ddc0cd1ba618802eb7b8d9865/tokenizers-0.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484", size = 3081954, upload-time = "2025-08-29T10:25:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/c2/02/c3c454b641bd7c4f79e4464accfae9e7dfc913a777d2e561e168ae060362/tokenizers-0.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79", size = 2945644, upload-time = "2025-08-29T10:25:23.405Z" }, + { url = "https://files.pythonhosted.org/packages/55/02/d10185ba2fd8c2d111e124c9d92de398aee0264b35ce433f79fb8472f5d0/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec5b71f668a8076802b0241a42387d48289f25435b86b769ae1837cad4172a17", size = 3254764, upload-time = "2025-08-29T10:25:12.445Z" }, + { url = "https://files.pythonhosted.org/packages/13/89/17514bd7ef4bf5bfff58e2b131cec0f8d5cea2b1c8ffe1050a2c8de88dbb/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ea8562fa7498850d02a16178105b58803ea825b50dc9094d60549a7ed63654bb", size = 3161654, upload-time = "2025-08-29T10:25:15.493Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d8/bac9f3a7ef6dcceec206e3857c3b61bb16c6b702ed7ae49585f5bd85c0ef/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4136e1558a9ef2e2f1de1555dcd573e1cbc4a320c1a06c4107a3d46dc8ac6e4b", size = 3511484, upload-time = "2025-08-29T10:25:20.477Z" }, + { url = "https://files.pythonhosted.org/packages/aa/27/9c9800eb6763683010a4851db4d1802d8cab9cec114c17056eccb4d4a6e0/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf5954de3962a5fd9781dc12048d24a1a6f1f5df038c6e95db328cd22964206", size = 3712829, upload-time = "2025-08-29T10:25:17.154Z" }, + { url = "https://files.pythonhosted.org/packages/10/e3/b1726dbc1f03f757260fa21752e1921445b5bc350389a8314dd3338836db/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8337ca75d0731fc4860e6204cc24bb36a67d9736142aa06ed320943b50b1e7ed", size = 3408934, upload-time = "2025-08-29T10:25:18.76Z" }, + { url = "https://files.pythonhosted.org/packages/d4/61/aeab3402c26874b74bb67a7f2c4b569dde29b51032c5384db592e7b216f4/tokenizers-0.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a89264e26f63c449d8cded9061adea7b5de53ba2346fc7e87311f7e4117c1cc8", size = 3345585, upload-time = "2025-08-29T10:25:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d3/498b4a8a8764cce0900af1add0f176ff24f475d4413d55b760b8cdf00893/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:790bad50a1b59d4c21592f9c3cf5e5cf9c3c7ce7e1a23a739f13e01fb1be377a", size = 9322986, upload-time = "2025-08-29T10:25:26.607Z" }, + { url = "https://files.pythonhosted.org/packages/a2/62/92378eb1c2c565837ca3cb5f9569860d132ab9d195d7950c1ea2681dffd0/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:76cf6757c73a10ef10bf06fa937c0ec7393d90432f543f49adc8cab3fb6f26cb", size = 9276630, upload-time = "2025-08-29T10:25:28.349Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f0/342d80457aa1cda7654327460f69db0d69405af1e4c453f4dc6ca7c4a76e/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1626cb186e143720c62c6c6b5371e62bbc10af60481388c0da89bc903f37ea0c", size = 9547175, upload-time = "2025-08-29T10:25:29.989Z" }, + { url = "https://files.pythonhosted.org/packages/14/84/8aa9b4adfc4fbd09381e20a5bc6aa27040c9c09caa89988c01544e008d18/tokenizers-0.22.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da589a61cbfea18ae267723d6b029b84598dc8ca78db9951d8f5beff72d8507c", size = 9692735, upload-time = "2025-08-29T10:25:32.089Z" }, + { url = "https://files.pythonhosted.org/packages/bf/24/83ee2b1dc76bfe05c3142e7d0ccdfe69f0ad2f1ebf6c726cea7f0874c0d0/tokenizers-0.22.0-cp39-abi3-win32.whl", hash = "sha256:dbf9d6851bddae3e046fedfb166f47743c1c7bd11c640f0691dd35ef0bcad3be", size = 2471915, upload-time = "2025-08-29T10:25:36.411Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9b/0e0bf82214ee20231845b127aa4a8015936ad5a46779f30865d10e404167/tokenizers-0.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:c78174859eeaee96021f248a56c801e36bfb6bd5b067f2e95aa82445ca324f00", size = 2680494, upload-time = "2025-08-29T10:25:35.14Z" }, ] [[package]] @@ -2550,50 +2949,50 @@ wheels = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20250516" +version = "6.0.12.20250822" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/22/59e2aeb48ceeee1f7cd4537db9568df80d62bdb44a7f9e743502ea8aab9c/types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba", size = 17378, upload-time = "2025-05-16T03:08:04.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/85/90a442e538359ab5c9e30de415006fb22567aa4301c908c09f19e42975c2/types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413", size = 17481, upload-time = "2025-08-22T03:02:16.209Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/5f/e0af6f7f6a260d9af67e1db4f54d732abad514252a7a378a6c4d17dd1036/types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530", size = 20312, upload-time = "2025-05-16T03:08:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/32/8e/8f0aca667c97c0d76024b37cffa39e76e2ce39ca54a38f285a64e6ae33ba/types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098", size = 20314, upload-time = "2025-08-22T03:02:15.002Z" }, ] [[package]] name = "types-requests" -version = "2.32.4.20250611" +version = "2.32.4.20250809" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/7f/73b3a04a53b0fd2a911d4ec517940ecd6600630b559e4505cc7b68beb5a0/types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826", size = 23118, upload-time = "2025-06-11T03:11:41.272Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3d/ea/0be9258c5a4fa1ba2300111aa5a0767ee6d18eb3fd20e91616c12082284d/types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072", size = 20643, upload-time = "2025-06-11T03:11:40.186Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, ] [[package]] name = "types-setuptools" -version = "80.9.0.20250529" +version = "80.9.0.20250822" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/66/1b276526aad4696a9519919e637801f2c103419d2c248a6feb2729e034d1/types_setuptools-80.9.0.20250529.tar.gz", hash = "sha256:79e088ba0cba2186c8d6499cbd3e143abb142d28a44b042c28d3148b1e353c91", size = 41337, upload-time = "2025-05-29T03:07:34.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/bd/1e5f949b7cb740c9f0feaac430e301b8f1c5f11a81e26324299ea671a237/types_setuptools-80.9.0.20250822.tar.gz", hash = "sha256:070ea7716968ec67a84c7f7768d9952ff24d28b65b6594797a464f1b3066f965", size = 41296, upload-time = "2025-08-22T03:02:08.771Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/d8/83790d67ec771bf029a45ff1bd1aedbb738d8aa58c09dd0cc3033eea0e69/types_setuptools-80.9.0.20250529-py3-none-any.whl", hash = "sha256:00dfcedd73e333a430e10db096e4d46af93faf9314f832f13b6bbe3d6757e95f", size = 63263, upload-time = "2025-05-29T03:07:33.064Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/475bf15c1cdc172e7a0d665b6e373ebfb1e9bf734d3f2f543d668b07a142/types_setuptools-80.9.0.20250822-py3-none-any.whl", hash = "sha256:53bf881cb9d7e46ed12c76ef76c0aaf28cfe6211d3fab12e0b83620b1a8642c3", size = 63179, upload-time = "2025-08-22T03:02:07.643Z" }, ] [[package]] name = "types-simplejson" -version = "3.20.0.20250326" +version = "3.20.0.20250822" 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, upload-time = "2025-03-26T02:53:35.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/6b/96d43a90cd202bd552cdd871858a11c138fe5ef11aeb4ed8e8dc51389257/types_simplejson-3.20.0.20250822.tar.gz", hash = "sha256:2b0bfd57a6beed3b932fd2c3c7f8e2f48a7df3978c9bba43023a32b3741a95b0", size = 10608, upload-time = "2025-08-22T03:03:35.36Z" } 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, upload-time = "2025-03-26T02:53:35.036Z" }, + { url = "https://files.pythonhosted.org/packages/3c/9f/8e2c9e6aee9a2ff34f2ffce6ccd9c26edeef6dfd366fde611dc2e2c00ab9/types_simplejson-3.20.0.20250822-py3-none-any.whl", hash = "sha256:b5e63ae220ac7a1b0bb9af43b9cb8652237c947981b2708b0c776d3b5d8fa169", size = 10417, upload-time = "2025-08-22T03:03:34.485Z" }, ] [[package]] name = "types-ujson" -version = "5.10.0.20250326" +version = "5.10.0.20250822" 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, upload-time = "2025-03-26T02:53:39.197Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/bd/d372d44534f84864a96c19a7059d9b4d29db8541828b8b9dc3040f7a46d0/types_ujson-5.10.0.20250822.tar.gz", hash = "sha256:0a795558e1f78532373cf3f03f35b1f08bc60d52d924187b97995ee3597ba006", size = 8437, upload-time = "2025-08-22T03:02:19.433Z" } 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, upload-time = "2025-03-26T02:53:38.2Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f2/d812543c350674d8b3f6e17c8922248ee3bb752c2a76f64beb8c538b40cf/types_ujson-5.10.0.20250822-py3-none-any.whl", hash = "sha256:3e9e73a6dc62ccc03449d9ac2c580cd1b7a8e4873220db498f7dd056754be080", size = 7657, upload-time = "2025-08-22T03:02:18.699Z" }, ] [[package]] diff --git a/misc/release/archive-version.js b/misc/release/archive-version.js index 3ef4f58b1e..1a66963dad 100755 --- a/misc/release/archive-version.js +++ b/misc/release/archive-version.js @@ -10,7 +10,7 @@ if (!nextVersion) { const filename = './docs/static/archived-versions.json'; const oldVersions = JSON.parse(readFileSync(filename)); const newVersions = [ - { label: `v${nextVersion}`, url: `https://v${nextVersion}.archive.immich.app` }, + { label: `v${nextVersion}`, url: `https://docs.v${nextVersion}.archive.immich.app` }, ...oldVersions, ]; diff --git a/misc/release/pump-version.sh b/misc/release/pump-version.sh index e7fbae34dc..65a2e70e50 100755 --- a/misc/release/pump-version.sh +++ b/misc/release/pump-version.sh @@ -61,19 +61,26 @@ fi if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER" - npm --prefix server version "$SERVER_PUMP" - npm --prefix server ci - npm --prefix server run build - make open-api - npm --prefix open-api/typescript-sdk version "$SERVER_PUMP" + jq --arg version "$NEXT_SERVER" '.version = $version' server/package.json > server/package.json.tmp && mv server/package.json.tmp server/package.json + pnpm install --frozen-lockfile --prefix server + pnpm --prefix server run build + + ( cd ./open-api && bash ./bin/generate-open-api.sh ) + + jq --arg version "$NEXT_SERVER" '.version = $version' open-api/typescript-sdk/package.json > open-api/typescript-sdk/package.json.tmp && mv open-api/typescript-sdk/package.json.tmp open-api/typescript-sdk/package.json + # TODO use $SERVER_PUMP once we pass 2.2.x - npm --prefix cli version patch - npm --prefix cli i --package-lock-only - npm --prefix web version "$SERVER_PUMP" - npm --prefix web i --package-lock-only - npm --prefix e2e version "$SERVER_PUMP" - npm --prefix e2e i --package-lock-only - uvx --from=toml-cli toml set --toml-path=pyproject.toml project.version "$SERVER_PUMP" + CURRENT_CLI_VERSION=$(jq -r '.version' cli/package.json) + CLI_PATCH_VERSION=$(echo "$CURRENT_CLI_VERSION" | awk -F. '{print $1"."$2"."($3+1)}') + jq --arg version "$CLI_PATCH_VERSION" '.version = $version' cli/package.json > cli/package.json.tmp && mv cli/package.json.tmp cli/package.json + pnpm install --frozen-lockfile --prefix cli + + jq --arg version "$NEXT_SERVER" '.version = $version' web/package.json > web/package.json.tmp && mv web/package.json.tmp web/package.json + pnpm install --frozen-lockfile --prefix web + + jq --arg version "$NEXT_SERVER" '.version = $version' e2e/package.json > e2e/package.json.tmp && mv e2e/package.json.tmp e2e/package.json + pnpm install --frozen-lockfile --prefix e2e + uvx --from=toml-cli toml set --toml-path=machine-learning/pyproject.toml project.version "$NEXT_SERVER" fi if [ "$CURRENT_MOBILE" != "$NEXT_MOBILE" ]; then diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000000..a1a735ea18 --- /dev/null +++ b/mise.toml @@ -0,0 +1,507 @@ +[tools] +node = "22.20.0" +flutter = "3.35.5" +pnpm = "10.15.1" + +[tools."github:CQLabs/homebrew-dcm"] +version = "1.30.0" +bin = "dcm" +postinstall = "chmod +x $MISE_TOOL_INSTALL_PATH/dcm" + +[settings] +experimental = true +pin = true + +# .github +[tasks."github:install"] +run = "pnpm install --filter github --frozen-lockfile" + +[tasks."github:format"] +env._.path = "./.github/node_modules/.bin" +dir = ".github" +run = "prettier --check ." + +[tasks."github:format-fix"] +env._.path = "./.github/node_modules/.bin" +dir = ".github" +run = "prettier --write ." + +# @immich/cli +[tasks."cli:install"] +run = "pnpm install --filter @immich/cli --frozen-lockfile" + +[tasks."cli:build"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "vite build" + +[tasks."cli:test"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "vite" + +[tasks."cli:lint"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "eslint \"src/**/*.ts\" --max-warnings 0" + +[tasks."cli:lint-fix"] +run = "mise run cli:lint --fix" + +[tasks."cli:format"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "prettier --check ." + +[tasks."cli:format-fix"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "prettier --write ." + +[tasks."cli:check"] +env._.path = "./cli/node_modules/.bin" +dir = "cli" +run = "tsc --noEmit" + +# @immich/sdk +[tasks."sdk:install"] +run = "pnpm install --filter @immich/sdk --frozen-lockfile" + +[tasks."sdk:build"] +env._.path = "./open-api/typescript-sdk/node_modules/.bin" +dir = "./open-api/typescript-sdk" +run = "tsc" + +# docs +[tasks."docs:install"] +run = "pnpm install --filter documentation --frozen-lockfile" + +[tasks."docs:start"] +env._.path = "./docs/node_modules/.bin" +dir = "docs" +run = "docusaurus --port 3005" + +[tasks."docs:build"] +env._.path = "./docs/node_modules/.bin" +dir = "docs" +run = [ + "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0", + "docusaurus build", +] + + +[tasks."docs:preview"] +env._.path = "./docs/node_modules/.bin" +dir = "docs" +run = "docusaurus serve" + + +[tasks."docs:format"] +env._.path = "./docs/node_modules/.bin" +dir = "docs" +run = "prettier --check ." + +[tasks."docs:format-fix"] +env._.path = "./docs/node_modules/.bin" +dir = "docs" +run = "prettier --write ." + + +# e2e +[tasks."e2e:install"] +run = "pnpm install --filter immich-e2e --frozen-lockfile" + +[tasks."e2e:test"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "vitest --run" + +[tasks."e2e:test-web"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "playwright test" + +[tasks."e2e:format"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "prettier --check ." + +[tasks."e2e:format-fix"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "prettier --write ." + +[tasks."e2e:lint"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "eslint \"src/**/*.ts\" --max-warnings 0" + +[tasks."e2e:lint-fix"] +run = "mise run e2e:lint --fix" + +[tasks."e2e:check"] +env._.path = "./e2e/node_modules/.bin" +dir = "e2e" +run = "tsc --noEmit" + +# i18n +[tasks."i18n:format"] +run = "mise run i18n:format-fix" + +[tasks."i18n:format-fix"] +run = "pnpm dlx sort-json ./i18n/*.json" + + +# server +[tasks."server:install"] +run = "pnpm install --filter immich --frozen-lockfile" + +[tasks."server:build"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "nest build" + +[tasks."server:test"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "vitest --config test/vitest.config.mjs" + +[tasks."server:test-medium"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "vitest --config test/vitest.config.medium.mjs" + +[tasks."server:format"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "prettier --check ." + +[tasks."server:format-fix"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "prettier --write ." + +[tasks."server:lint"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "eslint \"src/**/*.ts\" \"test/**/*.ts\" --max-warnings 0" + +[tasks."server:lint-fix"] +run = "mise run server:lint --fix" + +[tasks."server:check"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "tsc --noEmit" + +[tasks."server:sql"] +dir = "server" +run = "node ./dist/bin/sync-open-api.js" + +[tasks."server:open-api"] +dir = "server" +run = "node ./dist/bin/sync-open-api.js" + +[tasks."server:migrations"] +dir = "server" +run = "node ./dist/bin/migrations.js" +description = "Run database migration commands (create, generate, run, debug, or query)" + +[tasks."server:schema-drop"] +run = "mise run server:migrations query 'DROP schema public cascade; CREATE schema public;'" + +[tasks."server:schema-reset"] +run = "mise run server:schema-drop && mise run server:migrations run" + +[tasks."server:email-dev"] +env._.path = "./server/node_modules/.bin" +dir = "server" +run = "email dev -p 3050 --dir src/emails" + +[tasks."server:checklist"] +run = [ + "mise run server:install", + "mise run server:format", + "mise run server:lint", + "mise run server:check", + "mise run server:test-medium --run", + "mise run server:test --run", +] + + +# web +[tasks."web:install"] +run = "pnpm install --filter immich-web --frozen-lockfile" + +[tasks."web:svelte-kit-sync"] +env._.path = "./web/node_modules/.bin" +dir = "web" +run = "svelte-kit sync" + +[tasks."web:build"] +env._.path = "./web/node_modules/.bin" +dir = "web" +run = "vite build" + +[tasks."web:build-stats"] +env.BUILD_STATS = "true" +env._.path = "./web/node_modules/.bin" +dir = "web" +run = "vite build" + +[tasks."web:preview"] +env._.path = "./web/node_modules/.bin" +dir = "web" +run = "vite preview" + +[tasks."web:start"] +env._.path = "web/node_modules/.bin" +dir = "web" +run = "vite dev --host 0.0.0.0 --port 3000" + +[tasks."web:test"] +depends = "web:svelte-kit-sync" +env._.path = "web/node_modules/.bin" +dir = "web" +run = "vitest" + +[tasks."web:format"] +env._.path = "web/node_modules/.bin" +dir = "web" +run = "prettier --check ." + +[tasks."web:format-fix"] +env._.path = "web/node_modules/.bin" +dir = "web" +run = "prettier --write ." + +[tasks."web:lint"] +env._.path = "web/node_modules/.bin" +dir = "web" +run = "eslint . --max-warnings 0 --concurrency 4" + +[tasks."web:lint-fix"] +run = "mise run web:lint --fix" + +[tasks."web:check"] +depends = "web:svelte-kit-sync" +env._.path = "web/node_modules/.bin" +dir = "web" +run = "tsc --noEmit" + +[tasks."web:check-svelte"] +depends = "web:svelte-kit-sync" +env._.path = "web/node_modules/.bin" +dir = "web" +run = "svelte-check --no-tsconfig --fail-on-warnings" + +[tasks."web:checklist"] +run = [ + "mise run web:install", + "mise run web:format", + "mise run web:check", + "mise run web:test --run", + "mise run web:lint", +] + + +# mobile +[tasks."mobile:codegen:dart"] +alias = "mobile:codegen" +description = "Execute build_runner to auto-generate dart code" +dir = "mobile" +sources = [ + "pubspec.yaml", + "build.yaml", + "lib/**/*.dart", + "infrastructure/**/*.drift", +] +outputs = { auto = true } +run = "dart run build_runner build --delete-conflicting-outputs" + +[tasks."mobile:codegen:pigeon"] +alias = "mobile:pigeon" +description = "Generate pigeon platform code" +dir = "mobile" +depends = [ + "mobile:pigeon:native-sync", + "mobile:pigeon:thumbnail", + "mobile:pigeon:background-worker", + "mobile:pigeon:background-worker-lock", + "mobile:pigeon:connectivity", +] + +[tasks."mobile:codegen:translation"] +alias = "mobile:translation" +description = "Generate translations from i18n JSONs" +dir = "mobile" +run = [ + { task = "i18n:format-fix" }, + { tasks = [ + "mobile:i18n:loader", + "mobile:i18n:keys", + ] }, +] + +[tasks."mobile:codegen:app-icon"] +description = "Generate app icons" +dir = "mobile" +run = "flutter pub run flutter_launcher_icons:main" + +[tasks."mobile:codegen:splash"] +description = "Generate splash screen" +dir = "mobile" +run = "flutter pub run flutter_native_splash:create" + +[tasks."mobile:test"] +description = "Run mobile tests" +dir = "mobile" +run = "flutter test" + +[tasks."mobile:lint"] +description = "Analyze Dart code" +dir = "mobile" +depends = ["mobile:analyze:dart", "mobile:analyze:dcm"] + +[tasks."mobile:lint-fix"] +description = "Auto-fix Dart code" +dir = "mobile" +depends = ["mobile:analyze:fix:dart", "mobile:analyze:fix:dcm"] + +[tasks."mobile:format"] +description = "Format Dart code" +dir = "mobile" +run = "dart format --set-exit-if-changed $(find lib -name '*.dart' -not \\( -name '*.g.dart' -o -name '*.drift.dart' -o -name '*.gr.dart' \\))" + +[tasks."mobile:build:android"] +description = "Build Android release" +dir = "mobile" +run = "flutter build appbundle" + +[tasks."mobile:drift:migration"] +alias = "mobile:migration" +description = "Generate database migrations" +dir = "mobile" +run = "dart run drift_dev make-migrations" + + +# mobile internal tasks +[tasks."mobile:pigeon:native-sync"] +description = "Generate native sync API pigeon code" +dir = "mobile" +hide = true +sources = ["pigeon/native_sync_api.dart"] +outputs = [ + "lib/platform/native_sync_api.g.dart", + "ios/Runner/Sync/Messages.g.swift", + "android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt", +] +run = [ + "dart run pigeon --input pigeon/native_sync_api.dart", + "dart format lib/platform/native_sync_api.g.dart", +] + +[tasks."mobile:pigeon:thumbnail"] +description = "Generate thumbnail API pigeon code" +dir = "mobile" +hide = true +sources = ["pigeon/thumbnail_api.dart"] +outputs = [ + "lib/platform/thumbnail_api.g.dart", + "ios/Runner/Images/Thumbnails.g.swift", + "android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt", +] +run = [ + "dart run pigeon --input pigeon/thumbnail_api.dart", + "dart format lib/platform/thumbnail_api.g.dart", +] + +[tasks."mobile:pigeon:background-worker"] +description = "Generate background worker API pigeon code" +dir = "mobile" +hide = true +sources = ["pigeon/background_worker_api.dart"] +outputs = [ + "lib/platform/background_worker_api.g.dart", + "ios/Runner/Background/BackgroundWorker.g.swift", + "android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt", +] +run = [ + "dart run pigeon --input pigeon/background_worker_api.dart", + "dart format lib/platform/background_worker_api.g.dart", +] + +[tasks."mobile:pigeon:background-worker-lock"] +description = "Generate background worker lock API pigeon code" +dir = "mobile" +hide = true +sources = ["pigeon/background_worker_lock_api.dart"] +outputs = [ + "lib/platform/background_worker_lock_api.g.dart", + "android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt", +] +run = [ + "dart run pigeon --input pigeon/background_worker_lock_api.dart", + "dart format lib/platform/background_worker_lock_api.g.dart", +] + +[tasks."mobile:pigeon:connectivity"] +description = "Generate connectivity API pigeon code" +dir = "mobile" +hide = true +sources = ["pigeon/connectivity_api.dart"] +outputs = [ + "lib/platform/connectivity_api.g.dart", + "ios/Runner/Connectivity/Connectivity.g.swift", + "android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt", +] +run = [ + "dart run pigeon --input pigeon/connectivity_api.dart", + "dart format lib/platform/connectivity_api.g.dart", +] + +[tasks."mobile:i18n:loader"] +description = "Generate i18n loader" +dir = "mobile" +hide = true +sources = ["i18n/"] +outputs = "lib/generated/codegen_loader.g.dart" +run = [ + "dart run easy_localization:generate -S ../i18n", + "dart format lib/generated/codegen_loader.g.dart", +] + +[tasks."mobile:i18n:keys"] +description = "Generate i18n keys" +dir = "mobile" +hide = true +sources = ["i18n/en.json"] +outputs = "lib/generated/intl_keys.g.dart" +run = [ + "dart run bin/generate_keys.dart", + "dart format lib/generated/intl_keys.g.dart", +] + +[tasks."mobile:analyze:dart"] +description = "Run Dart analysis" +dir = "mobile" +hide = true +run = "dart analyze --fatal-infos" + +[tasks."mobile:analyze:dcm"] +description = "Run Dart Code Metrics" +dir = "mobile" +hide = true +run = "dcm analyze lib --fatal-style --fatal-warnings" + +[tasks."mobile:analyze:fix:dart"] +description = "Auto-fix Dart analysis" +dir = "mobile" +hide = true +run = "dart fix --apply" + +[tasks."mobile:analyze:fix:dcm"] +description = "Auto-fix Dart Code Metrics" +dir = "mobile" +hide = true +run = "dcm fix lib" diff --git a/mobile/.fvmrc b/mobile/.fvmrc index 7d2a608dfa..a4d5f6d9b7 100644 --- a/mobile/.fvmrc +++ b/mobile/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.32.6" + "flutter": "3.35.4" } \ No newline at end of file diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json index c1b3fc9ce3..9c6057e582 100644 --- a/mobile/.vscode/settings.json +++ b/mobile/.vscode/settings.json @@ -1,5 +1,9 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.32.6", + "dart.flutterSdkPath": ".fvm/versions/3.35.4", + "dart.lineLength": 120, + "[dart]": { + "editor.rulers": [120] + }, "search.exclude": { "**/.fvm": true }, diff --git a/mobile/README.md b/mobile/README.md index 436b0a4c34..59b2d9340c 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -84,4 +84,4 @@ Below is how your code needs to be structured: ## Contributing -Please refer to the [architecture](https://immich.app/docs/developer/architecture/) for contributing to the mobile app! +Please refer to the [architecture](https://docs.immich.app/developer/architecture/) for contributing to the mobile app! diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 7a03b54a95..c04e1dafdc 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -9,6 +9,9 @@ # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +formatter: + page_width: 120 + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` @@ -40,8 +43,9 @@ analyzer: - lib/**/*.g.dart - lib/**/*.drift.dart - plugins: - - custom_lint + # TODO: Re-enable after upgrading custom_lint + # plugins: + # - custom_lint custom_lint: debug: true @@ -78,6 +82,7 @@ custom_lint: # acceptable exceptions for the time being (until Isar is fully replaced) - lib/providers/app_life_cycle.provider.dart - integration_test/test_utils/general_helper.dart + - lib/domain/services/background_worker.service.dart - lib/main.dart - lib/pages/album/album_asset_selection.page.dart - lib/routing/router.dart @@ -130,6 +135,13 @@ custom_lint: dart_code_metrics: rules: + - banned-usage: + entries: + - name: debugPrint + description: Use dPrint instead of debugPrint for proper tree-shaking in release builds. + exclude-paths: + - 'lib/utils/debug_print.dart' + severity: perf # All rules from "recommended" preset # Show potential errors # - avoid-cascade-after-if-null diff --git a/mobile/android/app/CMakeLists.txt b/mobile/android/app/CMakeLists.txt new file mode 100644 index 0000000000..1569f1859e --- /dev/null +++ b/mobile/android/app/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.12) + +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) + +project(native_buffer LANGUAGES C) + +add_library(native_buffer SHARED + src/main/cpp/native_buffer.c +) diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 8b4dc42b7e..3c2125e24e 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -72,20 +72,6 @@ android { } } - flavorDimensions "default" - productFlavors { - production { - dimension "default" - applicationId "app.alextran.immich" - } - - beta { - dimension "default" - applicationId "app.alextran.immich.beta" - versionNameSuffix "-BETA" - } - } - buildTypes { debug { applicationIdSuffix '.debug' @@ -97,6 +83,12 @@ android { } } namespace 'app.alextran.immich' + + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } } flutter { diff --git a/mobile/android/app/src/beta/AndroidManifest.xml b/mobile/android/app/src/beta/AndroidManifest.xml deleted file mode 100644 index d4f7b5bc80..0000000000 --- a/mobile/android/app/src/beta/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index 09276f6d4a..c6e04e5a10 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -27,7 +27,9 @@ + android:largeHeap="true" android:enableOnBackInvokedCallback="false" android:allowBackup="false"> + + +#include + +JNIEXPORT jlong JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_00024Companion_allocateNative( + JNIEnv *env, jclass clazz, jint size) { + void *ptr = malloc(size); + return (jlong) ptr; +} + +JNIEXPORT jlong JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_allocateNative( + JNIEnv *env, jclass clazz, jint size) { + void *ptr = malloc(size); + return (jlong) ptr; +} + +JNIEXPORT void JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_00024Companion_freeNative( + JNIEnv *env, jclass clazz, jlong address) { + free((void *) address); +} + +JNIEXPORT void JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_freeNative( + JNIEnv *env, jclass clazz, jlong address) { + free((void *) address); +} + +JNIEXPORT jobject JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_00024Companion_wrapAsBuffer( + JNIEnv *env, jclass clazz, jlong address, jint capacity) { + return (*env)->NewDirectByteBuffer(env, (void *) address, capacity); +} + +JNIEXPORT jobject JNICALL +Java_app_alextran_immich_images_ThumbnailsImpl_wrapAsBuffer( + JNIEnv *env, jclass clazz, jlong address, jint capacity) { + return (*env)->NewDirectByteBuffer(env, (void *) address, capacity); +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/AppGlideModule.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/AppGlideModule.kt index f969b9576f..7b589cd80f 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/AppGlideModule.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/AppGlideModule.kt @@ -1,7 +1,20 @@ package app.alextran.immich +import android.content.Context +import com.bumptech.glide.GlideBuilder import com.bumptech.glide.annotation.GlideModule +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter +import com.bumptech.glide.load.engine.cache.DiskCacheAdapter +import com.bumptech.glide.load.engine.cache.MemoryCacheAdapter import com.bumptech.glide.module.AppGlideModule @GlideModule -class AppGlideModule : AppGlideModule() \ No newline at end of file +class AppGlideModule : AppGlideModule() { + override fun applyOptions(context: Context, builder: GlideBuilder) { + super.applyOptions(context, builder) + // disable caching as this is already done on the Flutter side + builder.setMemoryCache(MemoryCacheAdapter()) + builder.setDiskCache(DiskCacheAdapter.Factory()) + builder.setBitmapPool(BitmapPoolAdapter()) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt index ff806870f9..4474c63e09 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt @@ -1,19 +1,30 @@ package app.alextran.immich import android.app.Application +import android.os.Handler +import android.os.Looper import androidx.work.Configuration import androidx.work.WorkManager +import app.alextran.immich.background.BackgroundEngineLock +import app.alextran.immich.background.BackgroundWorkerApiImpl class ImmichApp : Application() { - override fun onCreate() { - super.onCreate() - val config = Configuration.Builder().build() - WorkManager.initialize(this, config) - // always start BackupWorker after WorkManager init; this fixes the following bug: - // After the process is killed (by user or system), the first trigger (taking a new picture) is lost. - // Thus, the BackupWorker is not started. If the system kills the process after each initialization - // (because of low memory etc.), the backup is never performed. - // As a workaround, we also run a backup check when initializing the application - ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) - } -} \ No newline at end of file + override fun onCreate() { + super.onCreate() + val config = Configuration.Builder().build() + WorkManager.initialize(this, config) + // always start BackupWorker after WorkManager init; this fixes the following bug: + // After the process is killed (by user or system), the first trigger (taking a new picture) is lost. + // Thus, the BackupWorker is not started. If the system kills the process after each initialization + // (because of low memory etc.), the backup is never performed. + // As a workaround, we also run a backup check when initializing the application + + ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) + Handler(Looper.getMainLooper()).postDelayed({ + // We can only check the engine count and not the status of the lock here, + // as the previous start might have been killed without unlocking. + if (BackgroundEngineLock.connectEngines > 0) return@postDelayed + BackgroundWorkerApiImpl.enqueueBackgroundWorker(this) + }, 5000) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt index f9c4ee2a1f..034f5ee72e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt @@ -1,8 +1,16 @@ package app.alextran.immich +import android.content.Context import android.os.Build import android.os.ext.SdkExtensions -import androidx.annotation.NonNull +import app.alextran.immich.background.BackgroundEngineLock +import app.alextran.immich.background.BackgroundWorkerApiImpl +import app.alextran.immich.background.BackgroundWorkerFgHostApi +import app.alextran.immich.background.BackgroundWorkerLockApi +import app.alextran.immich.connectivity.ConnectivityApi +import app.alextran.immich.connectivity.ConnectivityApiImpl +import app.alextran.immich.images.ThumbnailApi +import app.alextran.immich.images.ThumbnailsImpl import app.alextran.immich.sync.NativeSyncApi import app.alextran.immich.sync.NativeSyncApiImpl26 import app.alextran.immich.sync.NativeSyncApiImpl30 @@ -10,18 +18,30 @@ import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine class MainActivity : FlutterFragmentActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) - flutterEngine.plugins.add(BackgroundServicePlugin()) - flutterEngine.plugins.add(HttpSSLOptionsPlugin()) - // No need to set up method channel here as it's now handled in the plugin + registerPlugins(this, flutterEngine) + } - val nativeSyncApiImpl = - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) { - NativeSyncApiImpl26(this) - } else { - NativeSyncApiImpl30(this) - } - NativeSyncApi.setUp(flutterEngine.dartExecutor.binaryMessenger, nativeSyncApiImpl) + companion object { + fun registerPlugins(ctx: Context, flutterEngine: FlutterEngine) { + val messenger = flutterEngine.dartExecutor.binaryMessenger + val backgroundEngineLockImpl = BackgroundEngineLock(ctx) + BackgroundWorkerLockApi.setUp(messenger, backgroundEngineLockImpl) + val nativeSyncApiImpl = + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) < 1) { + NativeSyncApiImpl26(ctx) + } else { + NativeSyncApiImpl30(ctx) + } + NativeSyncApi.setUp(messenger, nativeSyncApiImpl) + ThumbnailApi.setUp(messenger, ThumbnailsImpl(ctx)) + BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx)) + ConnectivityApi.setUp(messenger, ConnectivityApiImpl(ctx)) + + flutterEngine.plugins.add(BackgroundServicePlugin()) + flutterEngine.plugins.add(HttpSSLOptionsPlugin()) + flutterEngine.plugins.add(backgroundEngineLockImpl) + } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt new file mode 100644 index 0000000000..504267a4e5 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt @@ -0,0 +1,53 @@ +package app.alextran.immich.background + +import android.content.Context +import android.util.Log +import io.flutter.embedding.engine.plugins.FlutterPlugin +import java.util.concurrent.atomic.AtomicInteger + +private const val TAG = "BackgroundEngineLock" + +class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterPlugin { + private val ctx: Context = context.applicationContext + + companion object { + + private var engineCount = AtomicInteger(0) + + val connectEngines: Int + get() = engineCount.get() + + private fun checkAndEnforceBackgroundLock(ctx: Context) { + // work manager task is running while the main app is opened, cancel the worker + if (BackgroundWorkerPreferences(ctx).isLocked() && + connectEngines > 1 && + BackgroundWorkerApiImpl.isBackgroundWorkerRunning() + ) { + Log.i(TAG, "Background worker is locked, cancelling the background worker") + BackgroundWorkerApiImpl.cancelBackgroundWorker(ctx) + } + } + } + + override fun lock() { + BackgroundWorkerPreferences(ctx).setLocked(true) + checkAndEnforceBackgroundLock(ctx) + Log.i(TAG, "Background worker is locked") + } + + override fun unlock() { + BackgroundWorkerPreferences(ctx).setLocked(false) + Log.i(TAG, "Background worker is unlocked") + } + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + checkAndEnforceBackgroundLock(binding.applicationContext) + engineCount.incrementAndGet() + Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + engineCount.decrementAndGet() + Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt new file mode 100644 index 0000000000..5857453ad3 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt @@ -0,0 +1,332 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package app.alextran.immich.background + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object BackgroundWorkerPigeonUtils { + + fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") } + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is List<*> && b is List<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && a.all { + (b as Map).containsKey(it.key) && + deepEquals(it.value, b[it.key]) + } + } + return a == b + } + +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +/** Generated class from Pigeon that represents data sent in messages. */ +data class BackgroundWorkerSettings ( + val requiresCharging: Boolean, + val minimumDelaySeconds: Long +) + { + companion object { + fun fromList(pigeonVar_list: List): BackgroundWorkerSettings { + val requiresCharging = pigeonVar_list[0] as Boolean + val minimumDelaySeconds = pigeonVar_list[1] as Long + return BackgroundWorkerSettings(requiresCharging, minimumDelaySeconds) + } + } + fun toList(): List { + return listOf( + requiresCharging, + minimumDelaySeconds, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is BackgroundWorkerSettings) { + return false + } + if (this === other) { + return true + } + return BackgroundWorkerPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} +private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { + BackgroundWorkerSettings.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is BackgroundWorkerSettings -> { + stream.write(129) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface BackgroundWorkerFgHostApi { + fun enable() + fun saveNotificationMessage(title: String, body: String) + fun configure(settings: BackgroundWorkerSettings) + fun disable() + + companion object { + /** The codec used by BackgroundWorkerFgHostApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + /** Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enable$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.enable() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val titleArg = args[0] as String + val bodyArg = args[1] as String + val wrapped: List = try { + api.saveNotificationMessage(titleArg, bodyArg) + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val settingsArg = args[0] as BackgroundWorkerSettings + val wrapped: List = try { + api.configure(settingsArg) + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disable$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.disable() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface BackgroundWorkerBgHostApi { + fun onInitialized() + fun close() + + companion object { + /** The codec used by BackgroundWorkerBgHostApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + /** Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.onInitialized() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.close() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} +/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +class BackgroundWorkerFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { + companion object { + /** The codec used by BackgroundWorkerFlutterApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerPigeonCodec() + } + } + fun onIosUpload(isRefreshArg: Boolean, maxSecondsArg: Long?, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(isRefreshArg, maxSecondsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } + fun onAndroidUpload(callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(null) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } + fun cancel(callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(null) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName))) + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt new file mode 100644 index 0000000000..e59cee2c16 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt @@ -0,0 +1,225 @@ +package app.alextran.immich.background + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.os.PowerManager +import android.util.Log +import androidx.core.app.NotificationCompat +import androidx.work.ForegroundInfo +import androidx.work.ListenableWorker +import androidx.work.WorkerParameters +import app.alextran.immich.MainActivity +import app.alextran.immich.R +import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.SettableFuture +import io.flutter.FlutterInjector +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.embedding.engine.FlutterEngineCache +import io.flutter.embedding.engine.dart.DartExecutor +import io.flutter.embedding.engine.loader.FlutterLoader +import java.util.concurrent.TimeUnit + +private const val TAG = "BackgroundWorker" + +class BackgroundWorker(context: Context, params: WorkerParameters) : + ListenableWorker(context, params), BackgroundWorkerBgHostApi { + private val ctx: Context = context.applicationContext + + /// The Flutter loader that loads the native Flutter library and resources. + /// This must be initialized before starting the Flutter engine. + private var loader: FlutterLoader = FlutterInjector.instance().flutterLoader() + + /// The Flutter engine created specifically for background execution. + /// This is a separate instance from the main Flutter engine that handles the UI. + /// It operates in its own isolate and doesn't share memory with the main engine. + /// Must be properly started, registered, and torn down during background execution. + private var engine: FlutterEngine? = null + + // Used to call methods on the flutter side + private var flutterApi: BackgroundWorkerFlutterApi? = null + + /// Result returned when the background task completes. This is used to signal + /// to the WorkManager that the task has finished, either successfully or with failure. + private val completionHandler: SettableFuture = SettableFuture.create() + + /// Flag to track whether the background task has completed to prevent duplicate completions + private var isComplete = false + + private val notificationManager = + ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + private var foregroundFuture: ListenableFuture? = null + + companion object { + private const val NOTIFICATION_CHANNEL_ID = "immich::background_worker::notif" + private const val NOTIFICATION_ID = 100 + } + + override fun startWork(): ListenableFuture { + Log.i(TAG, "Starting background upload worker") + + if (!loader.initialized()) { + loader.startInitialization(ctx) + } + + val notificationChannel = NotificationChannel( + NOTIFICATION_CHANNEL_ID, + NOTIFICATION_CHANNEL_ID, + NotificationManager.IMPORTANCE_LOW + ) + notificationManager.createNotificationChannel(notificationChannel) + val notificationConfig = BackgroundWorkerPreferences(ctx).getNotificationConfig() + showNotification(notificationConfig.first, notificationConfig.second) + + loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) { + engine = FlutterEngine(ctx) + FlutterEngineCache.getInstance().put(BackgroundWorkerApiImpl.ENGINE_CACHE_KEY, engine!!) + + // Register custom plugins + MainActivity.registerPlugins(ctx, engine!!) + flutterApi = + BackgroundWorkerFlutterApi(binaryMessenger = engine!!.dartExecutor.binaryMessenger) + BackgroundWorkerBgHostApi.setUp( + binaryMessenger = engine!!.dartExecutor.binaryMessenger, + api = this + ) + + engine!!.dartExecutor.executeDartEntrypoint( + DartExecutor.DartEntrypoint( + loader.findAppBundlePath(), + "package:immich_mobile/domain/services/background_worker.service.dart", + "backgroundSyncNativeEntrypoint" + ) + ) + } + + return completionHandler + } + + /** + * Called by the Flutter side when it has finished initialization and is ready to receive commands. + * Routes the appropriate task type (refresh or processing) to the corresponding Flutter method. + * This method acts as a bridge between the native Android background task system and Flutter. + */ + override fun onInitialized() { + flutterApi?.onAndroidUpload { handleHostResult(it) } + } + + // TODO: Move this to a separate NotificationManager class + private fun showNotification(title: String, content: String) { + val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.notification_icon) + .setOnlyAlertOnce(true) + .setOngoing(true) + .setTicker(title) + .setContentTitle(title) + .setContentText(content) + .build() + + if (isIgnoringBatteryOptimizations()) { + foregroundFuture = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + setForegroundAsync( + ForegroundInfo( + NOTIFICATION_ID, + notification, + FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + ) + } else { + setForegroundAsync(ForegroundInfo(NOTIFICATION_ID, notification)) + } + } else { + notificationManager.notify(NOTIFICATION_ID, notification) + } + } + + override fun close() { + if (isComplete) { + return + } + + Handler(Looper.getMainLooper()).postAtFrontOfQueue { + if (flutterApi != null) { + flutterApi?.cancel { + complete(Result.failure()) + } + } + } + + waitForForegroundPromotion() + + Handler(Looper.getMainLooper()).postDelayed({ + complete(Result.failure()) + }, 5000) + } + + /** + * Called when the system has to stop this worker because constraints are + * no longer met or the system needs resources for more important tasks + * This is also called when the worker has been explicitly cancelled or replaced + */ + override fun onStopped() { + Log.d(TAG, "About to stop BackupWorker") + close() + } + + private fun handleHostResult(result: kotlin.Result) { + if (isComplete) { + return + } + + result.fold( + onSuccess = { _ -> complete(Result.success()) }, + onFailure = { _ -> onStopped() } + ) + } + + /** + * Cleans up resources by destroying the Flutter engine context and invokes the completion handler. + * This method ensures that the background task is marked as complete, releases the Flutter engine, + * and notifies the caller of the task's success or failure. This is the final step in the + * background task lifecycle and should only be called once per task instance. + * + * - Parameter success: Indicates whether the background task completed successfully + */ + private fun complete(success: Result) { + Log.d(TAG, "About to complete BackupWorker with result: $success") + isComplete = true + engine?.destroy() + engine = null + flutterApi = null + notificationManager.cancel(NOTIFICATION_ID) + FlutterEngineCache.getInstance().remove(BackgroundWorkerApiImpl.ENGINE_CACHE_KEY) + waitForForegroundPromotion() + completionHandler.set(success) + } + + /** + * Returns `true` if the app is ignoring battery optimizations + */ + private fun isIgnoringBatteryOptimizations(): Boolean { + val powerManager = ctx.getSystemService(Context.POWER_SERVICE) as PowerManager + return powerManager.isIgnoringBatteryOptimizations(ctx.packageName) + } + + /** + * Calls to setForegroundAsync() that do not complete before completion of a ListenableWorker will signal an IllegalStateException + * https://android-review.googlesource.com/c/platform/frameworks/support/+/1262743 + * Wait for a short period of time for the foreground promotion to complete before completing the worker + */ + private fun waitForForegroundPromotion() { + val foregroundFuture = this.foregroundFuture + if (foregroundFuture != null && !foregroundFuture.isCancelled && !foregroundFuture.isDone) { + try { + foregroundFuture.get(500, TimeUnit.MILLISECONDS) + } catch (e: Exception) { + // ignored, there is nothing to be done + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt new file mode 100644 index 0000000000..a78db3c5ea --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt @@ -0,0 +1,96 @@ +package app.alextran.immich.background + +import android.content.Context +import android.provider.MediaStore +import android.util.Log +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import io.flutter.embedding.engine.FlutterEngineCache +import java.util.concurrent.TimeUnit + +private const val TAG = "BackgroundWorkerApiImpl" + +class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi { + private val ctx: Context = context.applicationContext + + override fun enable() { + enqueueMediaObserver(ctx) + } + + override fun saveNotificationMessage(title: String, body: String) { + BackgroundWorkerPreferences(ctx).updateNotificationConfig(title, body) + } + + override fun configure(settings: BackgroundWorkerSettings) { + BackgroundWorkerPreferences(ctx).updateSettings(settings) + enqueueMediaObserver(ctx) + } + + override fun disable() { + WorkManager.getInstance(ctx).apply { + cancelUniqueWork(OBSERVER_WORKER_NAME) + cancelUniqueWork(BACKGROUND_WORKER_NAME) + } + Log.i(TAG, "Cancelled background upload tasks") + } + + companion object { + private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1" + private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1" + const val ENGINE_CACHE_KEY = "immich::background_worker::engine" + + + fun enqueueMediaObserver(ctx: Context) { + val settings = BackgroundWorkerPreferences(ctx).getSettings() + val constraints = Constraints.Builder().apply { + addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true) + addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true) + addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true) + addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true) + setTriggerContentUpdateDelay(settings.minimumDelaySeconds, TimeUnit.SECONDS) + setTriggerContentMaxDelay(settings.minimumDelaySeconds * 10, TimeUnit.SECONDS) + setRequiresCharging(settings.requiresCharging) + }.build() + + val work = OneTimeWorkRequest.Builder(MediaObserver::class.java) + .setConstraints(constraints) + .build() + WorkManager.getInstance(ctx) + .enqueueUniqueWork(OBSERVER_WORKER_NAME, ExistingWorkPolicy.REPLACE, work) + + Log.i( + TAG, + "Enqueued media observer worker with name: $OBSERVER_WORKER_NAME and settings: $settings" + ) + } + + fun enqueueBackgroundWorker(ctx: Context) { + val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build() + + val work = OneTimeWorkRequest.Builder(BackgroundWorker::class.java) + .setConstraints(constraints) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) + .build() + WorkManager.getInstance(ctx) + .enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.KEEP, work) + + Log.i(TAG, "Enqueued background worker with name: $BACKGROUND_WORKER_NAME") + } + + fun isBackgroundWorkerRunning(): Boolean { + // Easier to check if the engine is cached as we always cache the engine when starting the worker + // and remove it when the worker is finished + return FlutterEngineCache.getInstance().get(ENGINE_CACHE_KEY) != null + } + + fun cancelBackgroundWorker(ctx: Context) { + WorkManager.getInstance(ctx).cancelUniqueWork(BACKGROUND_WORKER_NAME) + FlutterEngineCache.getInstance().remove(ENGINE_CACHE_KEY) + + Log.i(TAG, "Cancelled background upload task") + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt new file mode 100644 index 0000000000..3d00bafba2 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt @@ -0,0 +1,95 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package app.alextran.immich.background + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object BackgroundWorkerLockPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } +} +private open class BackgroundWorkerLockPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface BackgroundWorkerLockApi { + fun lock() + fun unlock() + + companion object { + /** The codec used by BackgroundWorkerLockApi. */ + val codec: MessageCodec by lazy { + BackgroundWorkerLockPigeonCodec() + } + /** Sets up an instance of `BackgroundWorkerLockApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerLockApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.lock$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.lock() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerLockPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.unlock$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.unlock() + listOf(null) + } catch (exception: Throwable) { + BackgroundWorkerLockPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt new file mode 100644 index 0000000000..450113e5b0 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerPreferences.kt @@ -0,0 +1,69 @@ +package app.alextran.immich.background + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit + +class BackgroundWorkerPreferences(private val ctx: Context) { + companion object { + const val SHARED_PREF_NAME = "Immich::BackgroundWorker" + private const val SHARED_PREF_MIN_DELAY_KEY = "BackgroundWorker::minDelaySeconds" + private const val SHARED_PREF_REQUIRE_CHARGING_KEY = "BackgroundWorker::requireCharging" + private const val SHARED_PREF_LOCK_KEY = "BackgroundWorker::isLocked" + private const val SHARED_PREF_NOTIF_TITLE_KEY = "BackgroundWorker::notificationTitle" + private const val SHARED_PREF_NOTIF_MSG_KEY = "BackgroundWorker::notificationMessage" + + private const val DEFAULT_MIN_DELAY_SECONDS = 30L + private const val DEFAULT_REQUIRE_CHARGING = false + private const val DEFAULT_NOTIF_TITLE = "Uploading media" + private const val DEFAULT_NOTIF_MSG = "Checking for new assetsâ€Ļ" + } + + private val sp: SharedPreferences by lazy { + ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) + } + + fun updateSettings(settings: BackgroundWorkerSettings) { + sp.edit { + putLong(SHARED_PREF_MIN_DELAY_KEY, settings.minimumDelaySeconds) + putBoolean(SHARED_PREF_REQUIRE_CHARGING_KEY, settings.requiresCharging) + } + } + + fun getSettings(): BackgroundWorkerSettings { + val delaySeconds = sp.getLong(SHARED_PREF_MIN_DELAY_KEY, DEFAULT_MIN_DELAY_SECONDS) + + return BackgroundWorkerSettings( + minimumDelaySeconds = if (delaySeconds >= 1000) delaySeconds / 1000 else delaySeconds, + requiresCharging = sp.getBoolean( + SHARED_PREF_REQUIRE_CHARGING_KEY, + DEFAULT_REQUIRE_CHARGING + ), + ) + } + + fun updateNotificationConfig(title: String, message: String) { + sp.edit { + putString(SHARED_PREF_NOTIF_TITLE_KEY, title) + putString(SHARED_PREF_NOTIF_MSG_KEY, message) + } + } + + fun getNotificationConfig(): Pair { + val title = + sp.getString(SHARED_PREF_NOTIF_TITLE_KEY, DEFAULT_NOTIF_TITLE) ?: DEFAULT_NOTIF_TITLE + val message = sp.getString(SHARED_PREF_NOTIF_MSG_KEY, DEFAULT_NOTIF_MSG) ?: DEFAULT_NOTIF_MSG + return Pair(title, message) + } + + fun setLocked(paused: Boolean) { + sp.edit { + putBoolean(SHARED_PREF_LOCK_KEY, paused) + } + } + + fun isLocked(): Boolean { + return sp.getBoolean(SHARED_PREF_LOCK_KEY, true) + } +} + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt new file mode 100644 index 0000000000..7283411ac0 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/MediaObserver.kt @@ -0,0 +1,22 @@ +package app.alextran.immich.background + +import android.content.Context +import android.util.Log +import androidx.work.Worker +import androidx.work.WorkerParameters + +class MediaObserver(context: Context, params: WorkerParameters) : Worker(context, params) { + private val ctx: Context = context.applicationContext + + override fun doWork(): Result { + Log.i("MediaObserver", "Content change detected, starting background worker") + // Re-enqueue itself to listen for future changes + BackgroundWorkerApiImpl.enqueueMediaObserver(ctx) + + // Enqueue backup worker only if there are new media changes + if (triggeredContentUris.isNotEmpty()) { + BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx) + } + return Result.success() + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt new file mode 100644 index 0000000000..434ba47ca1 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt @@ -0,0 +1,116 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package app.alextran.immich.connectivity + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object ConnectivityPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class NetworkCapability(val raw: Int) { + CELLULAR(0), + WIFI(1), + VPN(2), + UNMETERED(3); + + companion object { + fun ofRaw(raw: Int): NetworkCapability? { + return values().firstOrNull { it.raw == raw } + } + } +} +private open class ConnectivityPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { + NetworkCapability.ofRaw(it.toInt()) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is NetworkCapability -> { + stream.write(129) + writeValue(stream, value.raw) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ConnectivityApi { + fun getCapabilities(): List + + companion object { + /** The codec used by ConnectivityApi. */ + val codec: MessageCodec by lazy { + ConnectivityPigeonCodec() + } + /** Sets up an instance of `ConnectivityApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ConnectivityApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ConnectivityApi.getCapabilities$separatedMessageChannelSuffix", codec, taskQueue) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getCapabilities()) + } catch (exception: Throwable) { + ConnectivityPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt new file mode 100644 index 0000000000..e8554dd63a --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt @@ -0,0 +1,39 @@ +package app.alextran.immich.connectivity + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.net.wifi.WifiManager + +class ConnectivityApiImpl(context: Context) : ConnectivityApi { + private val connectivityManager = + context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + private val wifiManager = + context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager + + override fun getCapabilities(): List { + val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + ?: return emptyList() + + val hasWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) + val hasCellular = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + val hasVpn = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) + val isUnmetered = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + + return buildList { + if (hasWifi) add(NetworkCapability.WIFI) + if (hasCellular) add(NetworkCapability.CELLULAR) + if (hasVpn) { + add(NetworkCapability.VPN) + if (!hasWifi && !hasCellular) { + if (wifiManager.isWifiEnabled) add(NetworkCapability.WIFI) + // If VPN is active, but neither WIFI nor CELLULAR is reported as active, + // assume CELLULAR if WIFI is not enabled + else add(NetworkCapability.CELLULAR) + } + } + if (isUnmetered) add(NetworkCapability.UNMETERED) + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbHash.java b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbHash.java new file mode 100644 index 0000000000..3af76b5763 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbHash.java @@ -0,0 +1,164 @@ +package app.alextran.immich.images; + +// Copyright (c) 2023 Evan Wallace +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import java.nio.ByteBuffer; + +// modified to use native allocations +public final class ThumbHash { + /** + * Decodes a ThumbHash to an RGBA image. RGB is not be premultiplied by A. + * + * @param hash The bytes of the ThumbHash. + * @return The width, height, and pixels of the rendered placeholder image. + */ + public static Image thumbHashToRGBA(byte[] hash) { + // Read the constants + int header24 = (hash[0] & 255) | ((hash[1] & 255) << 8) | ((hash[2] & 255) << 16); + int header16 = (hash[3] & 255) | ((hash[4] & 255) << 8); + float l_dc = (float) (header24 & 63) / 63.0f; + float p_dc = (float) ((header24 >> 6) & 63) / 31.5f - 1.0f; + float q_dc = (float) ((header24 >> 12) & 63) / 31.5f - 1.0f; + float l_scale = (float) ((header24 >> 18) & 31) / 31.0f; + boolean hasAlpha = (header24 >> 23) != 0; + float p_scale = (float) ((header16 >> 3) & 63) / 63.0f; + float q_scale = (float) ((header16 >> 9) & 63) / 63.0f; + boolean isLandscape = (header16 >> 15) != 0; + int lx = Math.max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7); + int ly = Math.max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7); + float a_dc = hasAlpha ? (float) (hash[5] & 15) / 15.0f : 1.0f; + float a_scale = (float) ((hash[5] >> 4) & 15) / 15.0f; + + // Read the varying factors (boost saturation by 1.25x to compensate for quantization) + int ac_start = hasAlpha ? 6 : 5; + int ac_index = 0; + Channel l_channel = new Channel(lx, ly); + Channel p_channel = new Channel(3, 3); + Channel q_channel = new Channel(3, 3); + Channel a_channel = null; + ac_index = l_channel.decode(hash, ac_start, ac_index, l_scale); + ac_index = p_channel.decode(hash, ac_start, ac_index, p_scale * 1.25f); + ac_index = q_channel.decode(hash, ac_start, ac_index, q_scale * 1.25f); + if (hasAlpha) { + a_channel = new Channel(5, 5); + a_channel.decode(hash, ac_start, ac_index, a_scale); + } + float[] l_ac = l_channel.ac; + float[] p_ac = p_channel.ac; + float[] q_ac = q_channel.ac; + float[] a_ac = hasAlpha ? a_channel.ac : null; + + // Decode using the DCT into RGB + float ratio = thumbHashToApproximateAspectRatio(hash); + int w = Math.round(ratio > 1.0f ? 32.0f : 32.0f * ratio); + int h = Math.round(ratio > 1.0f ? 32.0f / ratio : 32.0f); + int size = w * h * 4; + long pointer = ThumbnailsImpl.allocateNative(size); + ByteBuffer rgba = ThumbnailsImpl.wrapAsBuffer(pointer, size); + int cx_stop = Math.max(lx, hasAlpha ? 5 : 3); + int cy_stop = Math.max(ly, hasAlpha ? 5 : 3); + float[] fx = new float[cx_stop]; + float[] fy = new float[cy_stop]; + for (int y = 0, i = 0; y < h; y++) { + for (int x = 0; x < w; x++, i += 4) { + float l = l_dc, p = p_dc, q = q_dc, a = a_dc; + + // Precompute the coefficients + for (int cx = 0; cx < cx_stop; cx++) + fx[cx] = (float) Math.cos(Math.PI / w * (x + 0.5f) * cx); + for (int cy = 0; cy < cy_stop; cy++) + fy[cy] = (float) Math.cos(Math.PI / h * (y + 0.5f) * cy); + + // Decode L + for (int cy = 0, j = 0; cy < ly; cy++) { + float fy2 = fy[cy] * 2.0f; + for (int cx = cy > 0 ? 0 : 1; cx * ly < lx * (ly - cy); cx++, j++) + l += l_ac[j] * fx[cx] * fy2; + } + + // Decode P and Q + for (int cy = 0, j = 0; cy < 3; cy++) { + float fy2 = fy[cy] * 2.0f; + for (int cx = cy > 0 ? 0 : 1; cx < 3 - cy; cx++, j++) { + float f = fx[cx] * fy2; + p += p_ac[j] * f; + q += q_ac[j] * f; + } + } + + // Decode A + if (hasAlpha) + for (int cy = 0, j = 0; cy < 5; cy++) { + float fy2 = fy[cy] * 2.0f; + for (int cx = cy > 0 ? 0 : 1; cx < 5 - cy; cx++, j++) + a += a_ac[j] * fx[cx] * fy2; + } + + // Convert to RGB + float b = l - 2.0f / 3.0f * p; + float r = (3.0f * l - b + q) / 2.0f; + float g = r - q; + rgba.put(i, (byte) Math.max(0, Math.round(255.0f * Math.min(1, r)))); + rgba.put(i + 1, (byte) Math.max(0, Math.round(255.0f * Math.min(1, g)))); + rgba.put(i + 2, (byte) Math.max(0, Math.round(255.0f * Math.min(1, b)))); + rgba.put(i + 3, (byte) Math.max(0, Math.round(255.0f * Math.min(1, a)))); + } + } + return new Image(w, h, pointer); + } + + /** + * Extracts the approximate aspect ratio of the original image. + * + * @param hash The bytes of the ThumbHash. + * @return The approximate aspect ratio (i.e. width / height). + */ + public static float thumbHashToApproximateAspectRatio(byte[] hash) { + byte header = hash[3]; + boolean hasAlpha = (hash[2] & 0x80) != 0; + boolean isLandscape = (hash[4] & 0x80) != 0; + int lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7; + int ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7; + return (float) lx / (float) ly; + } + + public static final class Image { + public int width; + public int height; + public long pointer; + + public Image(int width, int height, long pointer) { + this.width = width; + this.height = height; + this.pointer = pointer; + } + } + + private static final class Channel { + int nx; + int ny; + float[] ac; + + Channel(int nx, int ny) { + this.nx = nx; + this.ny = ny; + int n = 0; + for (int cy = 0; cy < ny; cy++) + for (int cx = cy > 0 ? 0 : 1; cx * ny < nx * (ny - cy); cx++) + n++; + ac = new float[n]; + } + + int decode(byte[] hash, int start, int index, float scale) { + for (int i = 0; i < ac.length; i++) { + int data = hash[start + (index >> 1)] >> ((index & 1) << 2); + ac[i] = ((float) (data & 15) / 7.5f - 1.0f) * scale; + index++; + } + return index; + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt new file mode 100644 index 0000000000..e9993a3c45 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt @@ -0,0 +1,139 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package app.alextran.immich.images + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object ThumbnailsPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() +private open class ThumbnailsPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ThumbnailApi { + fun requestImage(assetId: String, requestId: Long, width: Long, height: Long, isVideo: Boolean, callback: (Result>) -> Unit) + fun cancelImageRequest(requestId: Long) + fun getThumbhash(thumbhash: String, callback: (Result>) -> Unit) + + companion object { + /** The codec used by ThumbnailApi. */ + val codec: MessageCodec by lazy { + ThumbnailsPigeonCodec() + } + /** Sets up an instance of `ThumbnailApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ThumbnailApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ThumbnailApi.requestImage$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val assetIdArg = args[0] as String + val requestIdArg = args[1] as Long + val widthArg = args[2] as Long + val heightArg = args[3] as Long + val isVideoArg = args[4] as Boolean + api.requestImage(assetIdArg, requestIdArg, widthArg, heightArg, isVideoArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(ThumbnailsPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(ThumbnailsPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ThumbnailApi.cancelImageRequest$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val requestIdArg = args[0] as Long + val wrapped: List = try { + api.cancelImageRequest(requestIdArg) + listOf(null) + } catch (exception: Throwable) { + ThumbnailsPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ThumbnailApi.getThumbhash$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val thumbhashArg = args[0] as String + api.getThumbhash(thumbhashArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(ThumbnailsPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(ThumbnailsPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt new file mode 100644 index 0000000000..a9d602c19c --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/ThumbnailsImpl.kt @@ -0,0 +1,215 @@ +package app.alextran.immich.images + +import android.content.ContentResolver +import android.content.ContentUris +import android.content.Context +import android.graphics.* +import android.net.Uri +import android.os.Build +import android.os.CancellationSignal +import android.os.OperationCanceledException +import android.provider.MediaStore.Images +import android.provider.MediaStore.Video +import android.util.Size +import java.nio.ByteBuffer +import kotlin.math.* +import java.util.concurrent.Executors +import com.bumptech.glide.Glide +import com.bumptech.glide.Priority +import com.bumptech.glide.load.DecodeFormat +import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL +import java.util.Base64 +import java.util.concurrent.CancellationException +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.Future + +data class Request( + val taskFuture: Future<*>, + val cancellationSignal: CancellationSignal, + val callback: (Result>) -> Unit +) + +class ThumbnailsImpl(context: Context) : ThumbnailApi { + private val ctx: Context = context.applicationContext + private val resolver: ContentResolver = ctx.contentResolver + private val requestThread = Executors.newSingleThreadExecutor() + private val threadPool = + Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2 + 1) + private val requestMap = ConcurrentHashMap() + + companion object { + val CANCELLED = Result.success>(mapOf()) + val OPTIONS = BitmapFactory.Options().apply { inPreferredConfig = Bitmap.Config.ARGB_8888 } + + init { + System.loadLibrary("native_buffer") + } + + @JvmStatic + external fun allocateNative(size: Int): Long + + @JvmStatic + external fun freeNative(pointer: Long) + + @JvmStatic + external fun wrapAsBuffer(address: Long, capacity: Int): ByteBuffer + } + + override fun getThumbhash(thumbhash: String, callback: (Result>) -> Unit) { + threadPool.execute { + try { + val bytes = Base64.getDecoder().decode(thumbhash) + val image = ThumbHash.thumbHashToRGBA(bytes) + val res = mapOf( + "pointer" to image.pointer, + "width" to image.width.toLong(), + "height" to image.height.toLong() + ) + callback(Result.success(res)) + } catch (e: Exception) { + callback(Result.failure(e)) + } + } + } + + override fun requestImage( + assetId: String, + requestId: Long, + width: Long, + height: Long, + isVideo: Boolean, + callback: (Result>) -> Unit + ) { + val signal = CancellationSignal() + val task = threadPool.submit { + try { + getThumbnailBufferInternal(assetId, width, height, isVideo, callback, signal) + } catch (e: Exception) { + when (e) { + is OperationCanceledException -> callback(CANCELLED) + is CancellationException -> callback(CANCELLED) + else -> callback(Result.failure(e)) + } + } finally { + requestMap.remove(requestId) + } + } + val request = Request(task, signal, callback) + requestMap[requestId] = request + } + + override fun cancelImageRequest(requestId: Long) { + val request = requestMap.remove(requestId) ?: return + request.taskFuture.cancel(false) + request.cancellationSignal.cancel() + if (request.taskFuture.isCancelled) { + requestThread.execute { + try { + request.callback(CANCELLED) + } catch (_: Exception) { + } + } + } + } + + private fun getThumbnailBufferInternal( + assetId: String, + width: Long, + height: Long, + isVideo: Boolean, + callback: (Result>) -> Unit, + signal: CancellationSignal + ) { + signal.throwIfCanceled() + val size = Size(width.toInt(), height.toInt()) + val id = assetId.toLong() + + signal.throwIfCanceled() + val bitmap = if (isVideo) { + decodeVideoThumbnail(id, size, signal) + } else { + decodeImage(id, size, signal) + } + + processBitmap(bitmap, callback, signal) + } + + private fun processBitmap( + bitmap: Bitmap, callback: (Result>) -> Unit, signal: CancellationSignal + ) { + signal.throwIfCanceled() + val actualWidth = bitmap.width + val actualHeight = bitmap.height + + val size = actualWidth * actualHeight * 4 + val pointer = allocateNative(size) + + try { + signal.throwIfCanceled() + val buffer = wrapAsBuffer(pointer, size) + bitmap.copyPixelsToBuffer(buffer) + bitmap.recycle() + signal.throwIfCanceled() + val res = mapOf( + "pointer" to pointer, "width" to actualWidth.toLong(), "height" to actualHeight.toLong() + ) + callback(Result.success(res)) + } catch (e: Exception) { + freeNative(pointer) + callback(if (e is OperationCanceledException) CANCELLED else Result.failure(e)) + } + } + + private fun decodeImage(id: Long, size: Size, signal: CancellationSignal): Bitmap { + signal.throwIfCanceled() + val uri = ContentUris.withAppendedId(Images.Media.EXTERNAL_CONTENT_URI, id) + if (size.width <= 0 || size.height <= 0 || size.width > 768 || size.height > 768) { + return decodeSource(uri, size, signal) + } + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + resolver.loadThumbnail(uri, size, signal) + } else { + signal.setOnCancelListener { Images.Thumbnails.cancelThumbnailRequest(resolver, id) } + Images.Thumbnails.getThumbnail(resolver, id, Images.Thumbnails.MINI_KIND, OPTIONS) + } + } + + private fun decodeVideoThumbnail(id: Long, target: Size, signal: CancellationSignal): Bitmap { + signal.throwIfCanceled() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val uri = ContentUris.withAppendedId(Video.Media.EXTERNAL_CONTENT_URI, id) + // ensure a valid resolution as the thumbnail is used for videos even when no scaling is needed + val size = if (target.width > 0 && target.height > 0) target else Size(768, 768) + resolver.loadThumbnail(uri, size, signal) + } else { + signal.setOnCancelListener { Video.Thumbnails.cancelThumbnailRequest(resolver, id) } + Video.Thumbnails.getThumbnail(resolver, id, Video.Thumbnails.MINI_KIND, OPTIONS) + } + } + + private fun decodeSource(uri: Uri, target: Size, signal: CancellationSignal): Bitmap { + signal.throwIfCanceled() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val source = ImageDecoder.createSource(resolver, uri) + signal.throwIfCanceled() + ImageDecoder.decodeBitmap(source) { decoder, info, _ -> + if (target.width > 0 && target.height > 0) { + val sample = max(1, min(info.size.width / target.width, info.size.height / target.height)) + decoder.setTargetSampleSize(sample) + } + decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE + decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) + } + } else { + val ref = + Glide.with(ctx).asBitmap().priority(Priority.IMMEDIATE).load(uri).disallowHardwareConfig() + .format(DecodeFormat.PREFER_ARGB_8888).submit( + if (target.width > 0) target.width else SIZE_ORIGINAL, + if (target.height > 0) target.height else SIZE_ORIGINAL, + ) + signal.setOnCancelListener { Glide.with(ctx).clear(ref) } + ref.get() + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index b5ef90310e..28400c803f 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -88,7 +88,8 @@ data class PlatformAsset ( val width: Long? = null, val height: Long? = null, val durationInSeconds: Long, - val orientation: Long + val orientation: Long, + val isFavorite: Boolean ) { companion object { @@ -102,7 +103,8 @@ data class PlatformAsset ( val height = pigeonVar_list[6] as Long? val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation) + val isFavorite = pigeonVar_list[9] as Boolean + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) } } fun toList(): List { @@ -116,6 +118,7 @@ data class PlatformAsset ( height, durationInSeconds, orientation, + isFavorite, ) } override fun equals(other: Any?): Boolean { @@ -206,6 +209,40 @@ data class SyncDelta ( override fun hashCode(): Int = toList().hashCode() } + +/** Generated class from Pigeon that represents data sent in messages. */ +data class HashResult ( + val assetId: String, + val error: String? = null, + val hash: String? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): HashResult { + val assetId = pigeonVar_list[0] as String + val error = pigeonVar_list[1] as String? + val hash = pigeonVar_list[2] as String? + return HashResult(assetId, error, hash) + } + } + fun toList(): List { + return listOf( + assetId, + error, + hash, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is HashResult) { + return false + } + if (this === other) { + return true + } + return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} private open class MessagesPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { @@ -224,6 +261,11 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { SyncDelta.fromList(it) } } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { + HashResult.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -241,11 +283,16 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { stream.write(131) writeValue(stream, value.toList()) } + is HashResult -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface NativeSyncApi { fun shouldFullSync(): Boolean @@ -256,7 +303,8 @@ interface NativeSyncApi { fun getAlbums(): List fun getAssetsCountSince(albumId: String, timestamp: Long): Long fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List - fun hashPaths(paths: List): List + fun hashAssets(assetIds: List, allowNetworkAccess: Boolean, callback: (Result>) -> Unit) + fun cancelHashing() companion object { /** The codec used by NativeSyncApi. */ @@ -399,13 +447,33 @@ interface NativeSyncApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths$separatedMessageChannelSuffix", codec, taskQueue) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashAssets$separatedMessageChannelSuffix", codec, taskQueue) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val pathsArg = args[0] as List + val assetIdsArg = args[0] as List + val allowNetworkAccessArg = args[1] as Boolean + api.hashAssets(assetIdsArg, allowNetworkAccessArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelHashing$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> val wrapped: List = try { - listOf(api.hashPaths(pathsArg)) + api.cancelHashing() + listOf(null) } catch (exception: Throwable) { MessagesPigeonUtils.wrapError(exception) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 02cd54b8c3..868f3c6cdd 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -1,14 +1,25 @@ package app.alextran.immich.sync import android.annotation.SuppressLint +import android.content.ContentUris import android.content.Context import android.database.Cursor import android.provider.MediaStore -import android.util.Log +import android.util.Base64 import androidx.core.database.getStringOrNull +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit import java.io.File -import java.io.FileInputStream import java.security.MessageDigest +import kotlin.coroutines.cancellation.CancellationException +import kotlin.coroutines.coroutineContext sealed class AssetResult { data class ValidAsset(val asset: PlatformAsset, val albumId: String) : AssetResult() @@ -19,8 +30,12 @@ sealed class AssetResult { open class NativeSyncApiImplBase(context: Context) { private val ctx: Context = context.applicationContext + private var hashTask: Job? = null + companion object { - private const val TAG = "NativeSyncApiImplBase" + private const val MAX_CONCURRENT_HASH_OPERATIONS = 16 + private val hashSemaphore = Semaphore(MAX_CONCURRENT_HASH_OPERATIONS) + private const val HASHING_CANCELLED_CODE = "HASH_CANCELLED" const val MEDIA_SELECTION = "(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)" @@ -29,20 +44,24 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString() ) const val BUCKET_SELECTION = "(${MediaStore.Files.FileColumns.BUCKET_ID} = ?)" - val ASSET_PROJECTION = arrayOf( - MediaStore.MediaColumns._ID, - MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, - MediaStore.MediaColumns.DATE_TAKEN, - MediaStore.MediaColumns.DATE_ADDED, - MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.Files.FileColumns.MEDIA_TYPE, - MediaStore.MediaColumns.BUCKET_ID, - MediaStore.MediaColumns.WIDTH, - MediaStore.MediaColumns.HEIGHT, - MediaStore.MediaColumns.DURATION, - MediaStore.MediaColumns.ORIENTATION, - ) + val ASSET_PROJECTION = buildList { + add(MediaStore.MediaColumns._ID) + add(MediaStore.MediaColumns.DATA) + add(MediaStore.MediaColumns.DISPLAY_NAME) + add(MediaStore.MediaColumns.DATE_TAKEN) + add(MediaStore.MediaColumns.DATE_ADDED) + add(MediaStore.MediaColumns.DATE_MODIFIED) + add(MediaStore.Files.FileColumns.MEDIA_TYPE) + add(MediaStore.MediaColumns.BUCKET_ID) + add(MediaStore.MediaColumns.WIDTH) + add(MediaStore.MediaColumns.HEIGHT) + add(MediaStore.MediaColumns.DURATION) + add(MediaStore.MediaColumns.ORIENTATION) + // IS_FAVORITE is only available on Android 11 and above + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + add(MediaStore.MediaColumns.IS_FAVORITE) + } + }.toTypedArray() const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 } @@ -77,6 +96,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) + val favoriteColumn = c.getColumnIndex(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -105,6 +125,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) + val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 val asset = PlatformAsset( id, @@ -116,6 +137,7 @@ open class NativeSyncApiImplBase(context: Context) { height, duration, orientation.toLong(), + isFavorite, ) yield(AssetResult.ValidAsset(asset, bucketId)) } @@ -208,23 +230,74 @@ open class NativeSyncApiImplBase(context: Context) { .toList() } - fun hashPaths(paths: List): List { - val buffer = ByteArray(HASH_BUFFER_SIZE) - val digest = MessageDigest.getInstance("SHA-1") + fun hashAssets( + assetIds: List, + // allowNetworkAccess is only used on the iOS implementation + @Suppress("UNUSED_PARAMETER") allowNetworkAccess: Boolean, + callback: (Result>) -> Unit + ) { + if (assetIds.isEmpty()) { + callback(Result.success(emptyList())) + return + } - return paths.map { path -> + hashTask?.cancel() + hashTask = CoroutineScope(Dispatchers.IO).launch { try { - FileInputStream(path).use { file -> - var bytesRead: Int - while (file.read(buffer).also { bytesRead = it } > 0) { - digest.update(buffer, 0, bytesRead) + val results = assetIds.map { assetId -> + async { + hashSemaphore.withPermit { + ensureActive() + hashAsset(assetId) + } } - } - digest.digest() + }.awaitAll() + + callback(Result.success(results)) + } catch (e: CancellationException) { + callback( + Result.failure( + FlutterError( + HASHING_CANCELLED_CODE, + "Hashing operation was cancelled", + null + ) + ) + ) } catch (e: Exception) { - Log.w(TAG, "Failed to hash file $path: $e") - null + callback(Result.failure(e)) } } } + + private suspend fun hashAsset(assetId: String): HashResult { + return try { + val assetUri = ContentUris.withAppendedId( + MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), + assetId.toLong() + ) + + val digest = MessageDigest.getInstance("SHA-1") + ctx.contentResolver.openInputStream(assetUri)?.use { inputStream -> + var bytesRead: Int + val buffer = ByteArray(HASH_BUFFER_SIZE) + while (inputStream.read(buffer).also { bytesRead = it } > 0) { + coroutineContext.ensureActive() + digest.update(buffer, 0, bytesRead) + } + } ?: return HashResult(assetId, "Cannot open input stream for asset", null) + + val hashString = Base64.encodeToString(digest.digest(), Base64.NO_WRAP) + HashResult(assetId, null, hashString) + } catch (e: SecurityException) { + HashResult(assetId, "Permission denied accessing asset: ${e.message}", null) + } catch (e: Exception) { + HashResult(assetId, "Failed to hash asset: ${e.message}", null) + } + } + + fun cancelHashing() { + hashTask?.cancel() + hashTask = null + } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt index 3915f291f8..25a7ed99f1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -69,7 +69,7 @@ class ImageDownloadWorker( .build() manager.enqueueUniqueWork( - "$uniqueWorkName-$appWidgetId", + "$uniqueWorkName-$appWidgetId-singleShot", ExistingWorkPolicy.REPLACE, workRequest ) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt index 42f5fb4b1b..54ccb1ddfb 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt @@ -24,14 +24,23 @@ class ImmichAPI(cfg: ServerConfig) { val serverURL = prefs.getString("widget_server_url", "") ?: "" val sessionKey = prefs.getString("widget_auth_token", "") ?: "" + val customHeadersJSON = prefs.getString("widget_custom_headers", "") ?: "" if (serverURL.isBlank() || sessionKey.isBlank()) { return null } + var customHeaders: Map = HashMap() + + if (customHeadersJSON.isNotBlank()) { + val stringMapType = object : TypeToken>() {}.type + customHeaders = Gson().fromJson(customHeadersJSON, stringMapType) + } + return ServerConfig( serverURL, - sessionKey + sessionKey, + customHeaders ) } } @@ -50,11 +59,19 @@ class ImmichAPI(cfg: ServerConfig) { return URL(urlString.toString()) } + private fun HttpURLConnection.applyCustomHeaders() { + serverConfig.customHeaders.forEach { (key, value) -> + setRequestProperty(key, value) + } + } + suspend fun fetchSearchResults(filters: SearchFilters): List = withContext(Dispatchers.IO) { val url = buildRequestURL("/search/random") val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "POST" setRequestProperty("Content-Type", "application/json") + applyCustomHeaders() + doOutput = true } @@ -75,6 +92,7 @@ class ImmichAPI(cfg: ServerConfig) { val url = buildRequestURL("/memories", listOf("for" to iso8601)) val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "GET" + applyCustomHeaders() } val response = connection.inputStream.bufferedReader().readText() @@ -94,6 +112,7 @@ class ImmichAPI(cfg: ServerConfig) { val url = buildRequestURL("/albums") val connection = (url.openConnection() as HttpURLConnection).apply { requestMethod = "GET" + applyCustomHeaders() } val response = connection.inputStream.bufferedReader().readText() diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt index 7721af7d6f..63b32eb6f0 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt @@ -28,19 +28,21 @@ class MemoryReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, MemoryReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, MemoryReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + super.onReceive(context, intent) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt index 39afd76c35..a7662181bc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -28,19 +28,21 @@ class RandomReceiver : GlanceAppWidgetReceiver() { override fun onReceive(context: Context, intent: Intent) { val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, RandomReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) // Launch coroutine to setup a single shot if the app requested the update if (fromMainApp) { - CoroutineScope(Dispatchers.Default).launch { - val provider = ComponentName(context, RandomReceiver::class.java) - val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) - - glanceIds.forEach { widgetID -> - ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) - } + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) } } + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + super.onReceive(context, intent) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt index 9595a3b696..545a1edc59 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt @@ -55,7 +55,11 @@ data class WidgetEntry ( val deeplink: String? ) -data class ServerConfig(val serverEndpoint: String, val sessionKey: String) +data class ServerConfig( + val serverEndpoint: String, + val sessionKey: String, + val customHeaders: Map +) // MARK: Widget State Keys val kImageUUID = stringPreferencesKey("uuid") diff --git a/mobile/android/build.gradle b/mobile/android/build.gradle index bcf3daa1c8..719c946bd6 100644 --- a/mobile/android/build.gradle +++ b/mobile/android/build.gradle @@ -1,5 +1,5 @@ allprojects { - ext.kotlin_version = '2.0.20' + ext.kotlin_version = '2.2.20' repositories { google() @@ -16,8 +16,8 @@ subprojects { if (project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.library")) { project.android { - compileSdkVersion 35 - buildToolsVersion "35.0.0" + compileSdkVersion 36 + buildToolsVersion "36.0.0" } } } diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 31203fe55f..cbc2440fa9 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" => 204, - "android.injected.version.name" => "1.135.3", + "android.injected.version.code" => 3021, + "android.injected.version.name" => "2.0.1", } ) upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab') diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties index dedd5d1e69..ed4c299adb 100644 --- a/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/mobile/android/settings.gradle b/mobile/android/settings.gradle index 29c3a7c056..fbed55a3e3 100644 --- a/mobile/android/settings.gradle +++ b/mobile/android/settings.gradle @@ -18,10 +18,10 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.7.2' apply false - id "org.jetbrains.kotlin.android" version "2.0.20" apply false + id "com.android.application" version '8.11.2' apply false + id "org.jetbrains.kotlin.android" version "2.2.20" apply false id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' apply false - id 'com.google.devtools.ksp' version '2.0.20-1.0.24' apply false + id 'com.google.devtools.ksp' version '2.2.20-2.0.3' apply false } include ":app" diff --git a/mobile/build.yaml b/mobile/build.yaml index 76cc0a9988..cb718d9d3e 100644 --- a/mobile/build.yaml +++ b/mobile/build.yaml @@ -19,6 +19,7 @@ targets: - lib/infrastructure/entities/*.dart - lib/infrastructure/entities/*.drift - lib/infrastructure/repositories/db.repository.dart + - lib/infrastructure/repositories/logger_db.repository.dart drift_dev:modular: enabled: true options: *drift_options diff --git a/mobile/drift_schemas/main/drift_schema_v10.json b/mobile/drift_schemas/main/drift_schema_v10.json new file mode 100644 index 0000000000..aba030da04 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v10.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v11.json b/mobile/drift_schemas/main/drift_schema_v11.json new file mode 100644 index 0000000000..1c100ab37f --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v11.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v12.json b/mobile/drift_schemas/main/drift_schema_v12.json new file mode 100644 index 0000000000..1c100ab37f --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v12.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[],"type":"table","data":{"name":"auth_user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"avatar_color","getter_name":"avatarColor","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AvatarColor.values)","dart_type_name":"AvatarColor"}},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"pin_code","getter_name":"pinCode","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":14,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":15,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":16,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":17,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":18,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":19,"references":[1,18],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":20,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[1,20],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":23,"references":[15],"type":"index","data":{"on":15,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json new file mode 100644 index 0000000000..2488319a5e --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":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 (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v5.json b/mobile/drift_schemas/main/drift_schema_v5.json new file mode 100644 index 0000000000..ce29eaabdc --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v5.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":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 (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v6.json b/mobile/drift_schemas/main/drift_schema_v6.json new file mode 100644 index 0000000000..adb2484006 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v6.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":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 (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v7.json b/mobile/drift_schemas/main/drift_schema_v7.json new file mode 100644 index 0000000000..bcd502bdc0 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v7.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":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 (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[13],"type":"index","data":{"on":13,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v8.json b/mobile/drift_schemas/main/drift_schema_v8.json new file mode 100644 index 0000000000..6a4fe73fcb --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v8.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":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 (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[13],"type":"index","data":{"on":13,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v9.json b/mobile/drift_schemas/main/drift_schema_v9.json new file mode 100644 index 0000000000..5b08a752ec --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v9.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"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":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","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":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[4],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"linked_remote_album_id","getter_name":"linkedRemoteAlbumId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":6,"references":[3,5],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":7,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)","unique":false,"columns":[]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)","unique":false,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":11,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":"CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)","unique":false,"columns":[]}},{"id":12,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":13,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":14,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":15,"references":[1,4],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[4,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":21,"references":[],"type":"table","data":{"name":"store_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"string_value","getter_name":"stringValue","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"int_value","getter_name":"intValue","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":22,"references":[14],"type":"index","data":{"on":14,"name":"idx_lat_lng","sql":"CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)","unique":false,"columns":[]}}]} \ No newline at end of file diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index 8e17bae9d3..d6065170ef 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -39,16 +39,17 @@ class ImmichTestHelper { static Future loadApp(WidgetTester tester) async { await EasyLocalization.ensureInitialized(); // Clear all data from Isar (reuse existing instance if available) - final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final (isar, drift, logDb) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDb); await Store.clear(); - await db.writeTxn(() => db.clear()); + await isar.writeTxn(() => isar.clear()); // Load main Widget await tester.pumpWidget( ProviderScope( overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + driftProvider.overrideWith(driftOverride(drift)), ], child: const app.MainWidget(), ), @@ -59,18 +60,11 @@ class ImmichTestHelper { } @isTest -void immichWidgetTest( - String description, - Future Function(WidgetTester, ImmichTestHelper) test, -) { - testWidgets( - description, - (widgetTester) async { - await ImmichTestHelper.loadApp(widgetTester); - await test(widgetTester, ImmichTestHelper(widgetTester)); - }, - semanticsEnabled: false, - ); +void immichWidgetTest(String description, Future Function(WidgetTester, ImmichTestHelper) test) { + testWidgets(description, (widgetTester) async { + await ImmichTestHelper.loadApp(widgetTester); + await test(widgetTester, ImmichTestHelper(widgetTester)); + }, semanticsEnabled: false); } Future pumpUntilFound( @@ -79,8 +73,7 @@ Future pumpUntilFound( Duration timeout = const Duration(seconds: 120), }) async { bool found = false; - final timer = - Timer(timeout, () => throw TimeoutException("Pump until has timed out")); + final timer = Timer(timeout, () => throw TimeoutException("Pump until has timed out")); while (found != true) { await tester.pump(); found = tester.any(finder); diff --git a/mobile/ios/.gitignore b/mobile/ios/.gitignore index f312f249a3..e32cadbf68 100644 --- a/mobile/ios/.gitignore +++ b/mobile/ios/.gitignore @@ -4,7 +4,6 @@ *.moved-aside *.pbxuser *.perspectivev3 -**/*sync/ .sconsign.dblite .tags* **/.vagrant/ diff --git a/mobile/ios/Flutter/AppFrameworkInfo.plist b/mobile/ios/Flutter/AppFrameworkInfo.plist index 7c56964006..1dc6cf7652 100644 --- a/mobile/ios/Flutter/AppFrameworkInfo.plist +++ b/mobile/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 09bd36022b..9bff8cd8e2 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -64,7 +64,7 @@ PODS: - Flutter - integration_test (0.0.1): - Flutter - - isar_flutter_libs (1.0.0): + - isar_community_flutter_libs (1.0.0): - Flutter - local_auth_darwin (0.0.1): - Flutter @@ -149,7 +149,7 @@ DEPENDENCIES: - home_widget (from `.symlinks/plugins/home_widget/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) + - isar_community_flutter_libs (from `.symlinks/plugins/isar_community_flutter_libs/ios`) - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - native_video_player (from `.symlinks/plugins/native_video_player/ios`) @@ -210,8 +210,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: :path: ".symlinks/plugins/integration_test/ios" - isar_flutter_libs: - :path: ".symlinks/plugins/isar_flutter_libs/ios" + isar_community_flutter_libs: + :path: ".symlinks/plugins/isar_community_flutter_libs/ios" local_auth_darwin: :path: ".symlinks/plugins/local_auth_darwin/darwin" maplibre_gl: @@ -253,7 +253,7 @@ SPEC CHECKSUMS: DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 @@ -264,7 +264,7 @@ SPEC CHECKSUMS: home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e - isar_flutter_libs: bc909e72c3d756c2759f14c8776c13b5b0556e26 + isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index fb0908e8b6..6403a0ab4b 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -16,6 +16,11 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */; }; + B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */; }; + B25D377A2E72CA15008B6CA7 /* Connectivity.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */; }; + B25D377C2E72CA26008B6CA7 /* ConnectivityApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25D377B2E72CA20008B6CA7 /* ConnectivityApiImpl.swift */; }; + B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; }; D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; }; F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; }; @@ -24,6 +29,9 @@ FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = FAC6F8902D287C890078CB2F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC6F8B52D287F120078CB2F /* ShareViewController.swift */; }; FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */; }; + FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */; }; + FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */; }; + FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -89,6 +97,11 @@ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = ""; }; + B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorkerApiImpl.swift; sourceTree = ""; }; + B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.swift; sourceTree = ""; }; + B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connectivity.g.swift; sourceTree = ""; }; + B25D377B2E72CA20008B6CA7 /* ConnectivityApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityApiImpl.swift; sourceTree = ""; }; + B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = ""; }; E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; @@ -102,6 +115,9 @@ FAC6F8B42D287F120078CB2F /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = ""; }; FAC6F8B52D287F120078CB2F /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = ""; }; + FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbhash.swift; sourceTree = ""; }; + FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnails.g.swift; sourceTree = ""; }; + FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailsImpl.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -231,6 +247,8 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + B25D37792E72CA15008B6CA7 /* Connectivity */, + B21E34A62E5AF9760031FDB9 /* Background */, B2CF7F8C2DDE4EBB00744BF6 /* Sync */, FA9973382CF6DF4B000EF859 /* Runner.entitlements */, 65DD438629917FAD0047FFA8 /* BackgroundSync */, @@ -243,10 +261,30 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + FED3B1952E253E9B0030FD97 /* Images */, ); path = Runner; sourceTree = ""; }; + B21E34A62E5AF9760031FDB9 /* Background */ = { + isa = PBXGroup; + children = ( + B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */, + B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */, + B21E34A92E5AFD210031FDB9 /* BackgroundWorkerApiImpl.swift */, + ); + path = Background; + sourceTree = ""; + }; + B25D37792E72CA15008B6CA7 /* Connectivity */ = { + isa = PBXGroup; + children = ( + B25D377B2E72CA20008B6CA7 /* ConnectivityApiImpl.swift */, + B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */, + ); + path = Connectivity; + sourceTree = ""; + }; FAC6F8B62D287F120078CB2F /* ShareExtension */ = { isa = PBXGroup; children = ( @@ -258,6 +296,16 @@ path = ShareExtension; sourceTree = ""; }; + FED3B1952E253E9B0030FD97 /* Images */ = { + isa = PBXGroup; + children = ( + FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */, + FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */, + FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */, + ); + path = Images; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -523,7 +571,15 @@ files = ( 65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */, + B25D377A2E72CA15008B6CA7 /* Connectivity.g.swift in Sources */, + FEAFA8732E4D42F4001E47FE /* Thumbhash.swift in Sources */, + B25D377C2E72CA26008B6CA7 /* ConnectivityApiImpl.swift in Sources */, + FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */, + B21E34AA2E5AFD2B0031FDB9 /* BackgroundWorkerApiImpl.swift in Sources */, + FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */, 65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -649,7 +705,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -793,7 +849,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -823,7 +879,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -857,7 +913,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -900,7 +956,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -940,7 +996,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -979,7 +1035,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1023,7 +1079,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1064,7 +1120,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 230; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/AppDelegate.swift b/mobile/ios/Runner/AppDelegate.swift index 55d08adc6a..3476030923 100644 --- a/mobile/ios/Runner/AppDelegate.swift +++ b/mobile/ios/Runner/AppDelegate.swift @@ -19,12 +19,12 @@ import UIKit } GeneratedPluginRegistrant.register(with: self) - BackgroundServicePlugin.registerBackgroundProcessing() - - BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!) - let controller: FlutterViewController = window?.rootViewController as! FlutterViewController - NativeSyncApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: NativeSyncApiImpl()) + AppDelegate.registerPlugins(binaryMessenger: controller.binaryMessenger) + BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!) + + BackgroundServicePlugin.registerBackgroundProcessing() + BackgroundWorkerApiImpl.registerBackgroundWorkers() BackgroundServicePlugin.setPluginRegistrantCallback { registry in if !registry.hasPlugin("org.cocoapods.path-provider-foundation") { @@ -50,4 +50,10 @@ import UIKit return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + public static func registerPlugins(binaryMessenger: FlutterBinaryMessenger) { + NativeSyncApiSetup.setUp(binaryMessenger: binaryMessenger, api: NativeSyncApiImpl()) + ThumbnailApiSetup.setUp(binaryMessenger: binaryMessenger, api: ThumbnailApiImpl()) + BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: BackgroundWorkerApiImpl()) + } } diff --git a/mobile/ios/Runner/Background/BackgroundWorker.g.swift b/mobile/ios/Runner/Background/BackgroundWorker.g.swift new file mode 100644 index 0000000000..e339f150e7 --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorker.g.swift @@ -0,0 +1,365 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +func deepEqualsBackgroundWorker(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case is (Void, Void): + return true + + case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): + return cleanLhsHashable == cleanRhsHashable + + case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): + guard cleanLhsArray.count == cleanRhsArray.count else { return false } + for (index, element) in cleanLhsArray.enumerated() { + if !deepEqualsBackgroundWorker(element, cleanRhsArray[index]) { + return false + } + } + return true + + case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } + for (key, cleanLhsValue) in cleanLhsDictionary { + guard cleanRhsDictionary.index(forKey: key) != nil else { return false } + if !deepEqualsBackgroundWorker(cleanLhsValue, cleanRhsDictionary[key]!) { + return false + } + } + return true + + default: + // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. + return false + } +} + +func deepHashBackgroundWorker(value: Any?, hasher: inout Hasher) { + if let valueList = value as? [AnyHashable] { + for item in valueList { deepHashBackgroundWorker(value: item, hasher: &hasher) } + return + } + + if let valueDict = value as? [AnyHashable: AnyHashable] { + for key in valueDict.keys { + hasher.combine(key) + deepHashBackgroundWorker(value: valueDict[key]!, hasher: &hasher) + } + return + } + + if let hashableValue = value as? AnyHashable { + hasher.combine(hashableValue.hashValue) + } + + return hasher.combine(String(describing: value)) +} + + + +/// Generated class from Pigeon that represents data sent in messages. +struct BackgroundWorkerSettings: Hashable { + var requiresCharging: Bool + var minimumDelaySeconds: Int64 + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> BackgroundWorkerSettings? { + let requiresCharging = pigeonVar_list[0] as! Bool + let minimumDelaySeconds = pigeonVar_list[1] as! Int64 + + return BackgroundWorkerSettings( + requiresCharging: requiresCharging, + minimumDelaySeconds: minimumDelaySeconds + ) + } + func toList() -> [Any?] { + return [ + requiresCharging, + minimumDelaySeconds, + ] + } + static func == (lhs: BackgroundWorkerSettings, rhs: BackgroundWorkerSettings) -> Bool { + return deepEqualsBackgroundWorker(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashBackgroundWorker(value: toList(), hasher: &hasher) + } +} + +private class BackgroundWorkerPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return BackgroundWorkerSettings.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class BackgroundWorkerPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? BackgroundWorkerSettings { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class BackgroundWorkerPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return BackgroundWorkerPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return BackgroundWorkerPigeonCodecWriter(data: data) + } +} + +class BackgroundWorkerPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = BackgroundWorkerPigeonCodec(readerWriter: BackgroundWorkerPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol BackgroundWorkerFgHostApi { + func enable() throws + func saveNotificationMessage(title: String, body: String) throws + func configure(settings: BackgroundWorkerSettings) throws + func disable() throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class BackgroundWorkerFgHostApiSetup { + static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared } + /// Sets up an instance of `BackgroundWorkerFgHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let enableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + enableChannel.setMessageHandler { _, reply in + do { + try api.enable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + enableChannel.setMessageHandler(nil) + } + let saveNotificationMessageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + saveNotificationMessageChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let titleArg = args[0] as! String + let bodyArg = args[1] as! String + do { + try api.saveNotificationMessage(title: titleArg, body: bodyArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + saveNotificationMessageChannel.setMessageHandler(nil) + } + let configureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + configureChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let settingsArg = args[0] as! BackgroundWorkerSettings + do { + try api.configure(settings: settingsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + configureChannel.setMessageHandler(nil) + } + let disableChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disable\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disableChannel.setMessageHandler { _, reply in + do { + try api.disable() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + disableChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol BackgroundWorkerBgHostApi { + func onInitialized() throws + func close() throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class BackgroundWorkerBgHostApiSetup { + static var codec: FlutterStandardMessageCodec { BackgroundWorkerPigeonCodec.shared } + /// Sets up an instance of `BackgroundWorkerBgHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BackgroundWorkerBgHostApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let onInitializedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + onInitializedChannel.setMessageHandler { _, reply in + do { + try api.onInitialized() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + onInitializedChannel.setMessageHandler(nil) + } + let closeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + closeChannel.setMessageHandler { _, reply in + do { + try api.close() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + closeChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol BackgroundWorkerFlutterApiProtocol { + func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) + func onAndroidUpload(completion: @escaping (Result) -> Void) + func cancel(completion: @escaping (Result) -> Void) +} +class BackgroundWorkerFlutterApi: BackgroundWorkerFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: BackgroundWorkerPigeonCodec { + return BackgroundWorkerPigeonCodec.shared + } + func onIosUpload(isRefresh isRefreshArg: Bool, maxSeconds maxSecondsArg: Int64?, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([isRefreshArg, maxSecondsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func onAndroidUpload(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func cancel(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } +} diff --git a/mobile/ios/Runner/Background/BackgroundWorker.swift b/mobile/ios/Runner/Background/BackgroundWorker.swift new file mode 100644 index 0000000000..15df971203 --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorker.swift @@ -0,0 +1,175 @@ +import BackgroundTasks +import Flutter + +enum BackgroundTaskType { case refresh, processing } + +/* + * DEBUG: Testing Background Tasks in Xcode + * + * To test background task functionality during development: + * 1. Pause the application in Xcode debugger + * 2. In the debugger console, enter one of the following commands: + + ## For background refresh (short-running sync): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"] + + ## For background processing (long-running upload): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"] + + * To simulate task expiration (useful for testing expiration handlers): + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.refreshUpload"] + + e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"app.alextran.immich.background.processingUpload"] + + * 3. Resume the application to see the background code execute + * + * NOTE: This must be tested on a physical device, not in the simulator. + * In testing, only the background processing task can be reliably simulated. + * These commands submit the respective task to BGTaskScheduler for immediate processing. + * Use the expiration commands to test how the app handles iOS terminating background tasks. + */ + + +/// The background worker which creates a new Flutter VM, communicates with it +/// to run the backup job, and then finishes execution and calls back to its callback handler. +/// This class manages a separate Flutter engine instance for background execution, +/// independent of the main UI Flutter engine. +class BackgroundWorker: BackgroundWorkerBgHostApi { + private let taskType: BackgroundTaskType + /// The maximum number of seconds to run the task before timing out + private let maxSeconds: Int? + /// Callback function to invoke when the background task completes + private let completionHandler: (_ success: Bool) -> Void + + /// The Flutter engine created specifically for background execution. + /// This is a separate instance from the main Flutter engine that handles the UI. + /// It operates in its own isolate and doesn't share memory with the main engine. + /// Must be properly started, registered, and torn down during background execution. + private let engine = FlutterEngine(name: "BackgroundImmich") + + /// Used to call methods on the flutter side + private var flutterApi: BackgroundWorkerFlutterApi? + + /// Flag to track whether the background task has completed to prevent duplicate completions + private var isComplete = false + + /** + * Initializes a new background worker with the specified task type and execution constraints. + * Creates a new Flutter engine instance for background execution and sets up the necessary + * communication channels between native iOS and Flutter code. + * + * - Parameters: + * - taskType: The type of background task to execute (upload or sync task) + * - maxSeconds: Optional maximum execution time in seconds before the task is cancelled + * - completionHandler: Callback function invoked when the task completes, with success status + */ + init(taskType: BackgroundTaskType, maxSeconds: Int?, completionHandler: @escaping (_ success: Bool) -> Void) { + self.taskType = taskType + self.maxSeconds = maxSeconds + self.completionHandler = completionHandler + // Should be initialized only after the engine starts running + self.flutterApi = nil + } + + /** + * Starts the background Flutter engine and begins execution of the background task. + * Retrieves the callback handle from UserDefaults, looks up the Flutter callback, + * starts the engine, and sets up a timeout timer if specified. + */ + func run() { + // Start the Flutter engine with the specified callback as the entry point + let isRunning = engine.run( + withEntrypoint: "backgroundSyncNativeEntrypoint", + libraryURI: "package:immich_mobile/domain/services/background_worker.service.dart" + ) + + // Verify that the Flutter engine started successfully + if !isRunning { + complete(success: false) + return + } + + // Register plugins in the new engine + GeneratedPluginRegistrant.register(with: engine) + // Register custom plugins + AppDelegate.registerPlugins(binaryMessenger: engine.binaryMessenger) + flutterApi = BackgroundWorkerFlutterApi(binaryMessenger: engine.binaryMessenger) + BackgroundWorkerBgHostApiSetup.setUp(binaryMessenger: engine.binaryMessenger, api: self) + + // Set up a timeout timer if maxSeconds was specified to prevent runaway background tasks + if maxSeconds != nil { + // Schedule a timer to cancel the task after the specified timeout period + Timer.scheduledTimer(withTimeInterval: TimeInterval(maxSeconds!), repeats: false) { _ in + self.close() + } + } + } + + /** + * Called by the Flutter side when it has finished initialization and is ready to receive commands. + * Routes the appropriate task type (refresh or processing) to the corresponding Flutter method. + * This method acts as a bridge between the native iOS background task system and Flutter. + */ + func onInitialized() throws { + flutterApi?.onIosUpload(isRefresh: self.taskType == .refresh, maxSeconds: maxSeconds.map { Int64($0) }, completion: { result in + self.handleHostResult(result: result) + }) + } + + /** + * Cancels the currently running background task, either due to timeout or external request. + * Sends a cancel signal to the Flutter side and sets up a fallback timer to ensure + * the completion handler is eventually called even if Flutter doesn't respond. + */ + func close() { + if isComplete { + return + } + + flutterApi?.cancel { result in + self.complete(success: false) + } + + // Fallback safety mechanism: ensure completion is called within 2 seconds + // This prevents the background task from hanging indefinitely if Flutter doesn't respond + Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in + self.complete(success: false) + } + } + + + /** + * Handles the result from Flutter API calls and determines the success/failure status. + * Converts Flutter's Result type to a simple boolean success indicator for task completion. + * + * - Parameter result: The result returned from a Flutter API call + */ + private func handleHostResult(result: Result) { + switch result { + case .success(): self.complete(success: true) + case .failure(_): self.close() + } + } + + /** + * Cleans up resources by destroying the Flutter engine context and invokes the completion handler. + * This method ensures that the background task is marked as complete, releases the Flutter engine, + * and notifies the caller of the task's success or failure. This is the final step in the + * background task lifecycle and should only be called once per task instance. + * + * - Parameter success: Indicates whether the background task completed successfully + */ + private func complete(success: Bool) { + if(isComplete) { + return + } + + isComplete = true + engine.destroyContext() + flutterApi = nil + completionHandler(success) + } +} diff --git a/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift new file mode 100644 index 0000000000..a7bbc31ceb --- /dev/null +++ b/mobile/ios/Runner/Background/BackgroundWorkerApiImpl.swift @@ -0,0 +1,127 @@ +import BackgroundTasks + +class BackgroundWorkerApiImpl: BackgroundWorkerFgHostApi { + + func enable() throws { + BackgroundWorkerApiImpl.scheduleRefreshWorker() + BackgroundWorkerApiImpl.scheduleProcessingWorker() + print("BackgroundWorkerApiImpl:enable Background worker scheduled") + } + + func configure(settings: BackgroundWorkerSettings) throws { + // Android only + } + + func saveNotificationMessage(title: String, body: String) throws { + // Android only + } + + func disable() throws { + BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.refreshTaskID); + BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: BackgroundWorkerApiImpl.processingTaskID); + print("BackgroundWorkerApiImpl:disableUploadWorker Disabled background workers") + } + + private static let refreshTaskID = "app.alextran.immich.background.refreshUpload" + private static let processingTaskID = "app.alextran.immich.background.processingUpload" + private static let taskSemaphore = DispatchSemaphore(value: 1) + + public static func registerBackgroundWorkers() { + BGTaskScheduler.shared.register( + forTaskWithIdentifier: processingTaskID, using: nil) { task in + if task is BGProcessingTask { + handleBackgroundProcessing(task: task as! BGProcessingTask) + } + } + + BGTaskScheduler.shared.register( + forTaskWithIdentifier: refreshTaskID, using: nil) { task in + if task is BGAppRefreshTask { + handleBackgroundRefresh(task: task as! BGAppRefreshTask) + } + } + } + + private static func scheduleRefreshWorker() { + let backgroundRefresh = BGAppRefreshTaskRequest(identifier: refreshTaskID) + backgroundRefresh.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // 5 mins + + do { + try BGTaskScheduler.shared.submit(backgroundRefresh) + } catch { + print("Could not schedule the refresh upload task \(error.localizedDescription)") + } + } + + private static func scheduleProcessingWorker() { + let backgroundProcessing = BGProcessingTaskRequest(identifier: processingTaskID) + + backgroundProcessing.requiresNetworkConnectivity = true + backgroundProcessing.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 mins + + do { + try BGTaskScheduler.shared.submit(backgroundProcessing) + } catch { + print("Could not schedule the processing upload task \(error.localizedDescription)") + } + } + + private static func handleBackgroundRefresh(task: BGAppRefreshTask) { + scheduleRefreshWorker() + // If another task is running, cede the background time back to the OS + if taskSemaphore.wait(timeout: .now()) == .success { + // Restrict the refresh task to run only for a maximum of (maxSeconds) seconds + runBackgroundWorker(task: task, taskType: .refresh, maxSeconds: 20) + } else { + task.setTaskCompleted(success: false) + } + } + + private static func handleBackgroundProcessing(task: BGProcessingTask) { + scheduleProcessingWorker() + taskSemaphore.wait() + // There are no restrictions for processing tasks. Although, the OS could signal expiration at any time + runBackgroundWorker(task: task, taskType: .processing, maxSeconds: nil) + } + + /** + * Executes the background worker within the context of a background task. + * This method creates a BackgroundWorker, sets up task expiration handling, + * and manages the synchronization between the background task and the Flutter engine. + * + * - Parameters: + * - task: The iOS background task that provides the execution context + * - taskType: The type of background operation to perform (refresh or processing) + * - maxSeconds: Optional timeout for the operation in seconds + */ + private static func runBackgroundWorker(task: BGTask, taskType: BackgroundTaskType, maxSeconds: Int?) { + defer { taskSemaphore.signal() } + let semaphore = DispatchSemaphore(value: 0) + var isSuccess = true + + let backgroundWorker = BackgroundWorker(taskType: taskType, maxSeconds: maxSeconds) { success in + isSuccess = success + semaphore.signal() + } + + task.expirationHandler = { + DispatchQueue.main.async { + backgroundWorker.close() + } + isSuccess = false + + // Schedule a timer to signal the semaphore after 2 seconds + Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in + semaphore.signal() + } + } + + DispatchQueue.main.async { + backgroundWorker.run() + } + + semaphore.wait() + task.setTaskCompleted(success: isSuccess) + print("Background task completed with success: \(isSuccess)") + } +} diff --git a/mobile/ios/Runner/Connectivity/Connectivity.g.swift b/mobile/ios/Runner/Connectivity/Connectivity.g.swift new file mode 100644 index 0000000000..45333f03d8 --- /dev/null +++ b/mobile/ios/Runner/Connectivity/Connectivity.g.swift @@ -0,0 +1,129 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + + +enum NetworkCapability: Int { + case cellular = 0 + case wifi = 1 + case vpn = 2 + case unmetered = 3 +} + +private class ConnectivityPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return NetworkCapability(rawValue: enumResultAsInt) + } + return nil + default: + return super.readValue(ofType: type) + } + } +} + +private class ConnectivityPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? NetworkCapability { + super.writeByte(129) + super.writeValue(value.rawValue) + } else { + super.writeValue(value) + } + } +} + +private class ConnectivityPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return ConnectivityPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return ConnectivityPigeonCodecWriter(data: data) + } +} + +class ConnectivityPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = ConnectivityPigeonCodec(readerWriter: ConnectivityPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol ConnectivityApi { + func getCapabilities() throws -> [NetworkCapability] +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class ConnectivityApiSetup { + static var codec: FlutterStandardMessageCodec { ConnectivityPigeonCodec.shared } + /// Sets up an instance of `ConnectivityApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ConnectivityApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + #if os(iOS) + let taskQueue = binaryMessenger.makeBackgroundTaskQueue?() + #else + let taskQueue: FlutterTaskQueue? = nil + #endif + let getCapabilitiesChannel = taskQueue == nil + ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ConnectivityApi.getCapabilities\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ConnectivityApi.getCapabilities\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + if let api = api { + getCapabilitiesChannel.setMessageHandler { _, reply in + do { + let result = try api.getCapabilities() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getCapabilitiesChannel.setMessageHandler(nil) + } + } +} diff --git a/mobile/ios/Runner/Connectivity/ConnectivityApiImpl.swift b/mobile/ios/Runner/Connectivity/ConnectivityApiImpl.swift new file mode 100644 index 0000000000..0261cb26fb --- /dev/null +++ b/mobile/ios/Runner/Connectivity/ConnectivityApiImpl.swift @@ -0,0 +1,6 @@ + +class ConnectivityApiImpl: ConnectivityApi { + func getCapabilities() throws -> [NetworkCapability] { + [] + } +} diff --git a/mobile/ios/Runner/Images/Thumbhash.swift b/mobile/ios/Runner/Images/Thumbhash.swift new file mode 100644 index 0000000000..b3c5fbab39 --- /dev/null +++ b/mobile/ios/Runner/Images/Thumbhash.swift @@ -0,0 +1,225 @@ +// Copyright (c) 2023 Evan Wallace +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import Foundation + +// NOTE: Swift has an exponential-time type checker and compiling very simple +// expressions can easily take many seconds, especially when expressions involve +// numeric type constructors. +// +// This file deliberately breaks compound expressions up into separate variables +// to improve compile time even though this comes at the expense of readability. +// This is a known workaround for this deficiency in the Swift compiler. +// +// The following command is helpful when debugging Swift compile time issues: +// +// swiftc ThumbHash.swift -Xfrontend -debug-time-function-bodies +// +// These optimizations brought the compile time for this file from around 2.5 +// seconds to around 250ms (10x faster). + +// NOTE: Swift's debug-build performance of for-in loops over numeric ranges is +// really awful. Debug builds compile a very generic indexing iterator thing +// that makes many nested calls for every iteration, which makes debug-build +// performance crawl. +// +// This file deliberately avoids for-in loops that loop for more than a few +// times to improve debug-build run time even though this comes at the expense +// of readability. Similarly unsafe pointers are used instead of array getters +// to avoid unnecessary bounds checks, which have extra overhead in debug builds. +// +// These optimizations brought the run time to encode and decode 10 ThumbHashes +// in debug mode from 700ms to 70ms (10x faster). + +// changed signature and allocation method to avoid automatic GC +func thumbHashToRGBA(hash: Data) -> (Int, Int, UnsafeMutableRawBufferPointer) { + // Read the constants + let h0 = UInt32(hash[0]) + let h1 = UInt32(hash[1]) + let h2 = UInt32(hash[2]) + let h3 = UInt16(hash[3]) + let h4 = UInt16(hash[4]) + let header24 = h0 | (h1 << 8) | (h2 << 16) + let header16 = h3 | (h4 << 8) + let il_dc = header24 & 63 + let ip_dc = (header24 >> 6) & 63 + let iq_dc = (header24 >> 12) & 63 + var l_dc = Float32(il_dc) + var p_dc = Float32(ip_dc) + var q_dc = Float32(iq_dc) + l_dc = l_dc / 63 + p_dc = p_dc / 31.5 - 1 + q_dc = q_dc / 31.5 - 1 + let il_scale = (header24 >> 18) & 31 + var l_scale = Float32(il_scale) + l_scale = l_scale / 31 + let hasAlpha = (header24 >> 23) != 0 + let ip_scale = (header16 >> 3) & 63 + let iq_scale = (header16 >> 9) & 63 + var p_scale = Float32(ip_scale) + var q_scale = Float32(iq_scale) + p_scale = p_scale / 63 + q_scale = q_scale / 63 + let isLandscape = (header16 >> 15) != 0 + let lx16 = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7) + let ly16 = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7) + let lx = Int(lx16) + let ly = Int(ly16) + var a_dc = Float32(1) + var a_scale = Float32(1) + if hasAlpha { + let ia_dc = hash[5] & 15 + let ia_scale = hash[5] >> 4 + a_dc = Float32(ia_dc) + a_scale = Float32(ia_scale) + a_dc /= 15 + a_scale /= 15 + } + + // Read the varying factors (boost saturation by 1.25x to compensate for quantization) + let ac_start = hasAlpha ? 6 : 5 + var ac_index = 0 + let decodeChannel = { (nx: Int, ny: Int, scale: Float32) -> [Float32] in + var ac: [Float32] = [] + for cy in 0 ..< ny { + var cx = cy > 0 ? 0 : 1 + while cx * ny < nx * (ny - cy) { + let iac = (hash[ac_start + (ac_index >> 1)] >> ((ac_index & 1) << 2)) & 15; + var fac = Float32(iac) + fac = (fac / 7.5 - 1) * scale + ac.append(fac) + ac_index += 1 + cx += 1 + } + } + return ac + } + let l_ac = decodeChannel(lx, ly, l_scale) + let p_ac = decodeChannel(3, 3, p_scale * 1.25) + let q_ac = decodeChannel(3, 3, q_scale * 1.25) + let a_ac = hasAlpha ? decodeChannel(5, 5, a_scale) : [] + + // Decode using the DCT into RGB + let ratio = thumbHashToApproximateAspectRatio(hash: hash) + let fw = round(ratio > 1 ? 32 : 32 * ratio) + let fh = round(ratio > 1 ? 32 / ratio : 32) + let w = Int(fw) + let h = Int(fh) + let pointer = UnsafeMutableRawBufferPointer.allocate( + byteCount: w * h * 4, + alignment: MemoryLayout.alignment + ) + var rgba = pointer.baseAddress!.assumingMemoryBound(to: UInt8.self) + let cx_stop = max(lx, hasAlpha ? 5 : 3) + let cy_stop = max(ly, hasAlpha ? 5 : 3) + var fx = [Float32](repeating: 0, count: cx_stop) + var fy = [Float32](repeating: 0, count: cy_stop) + fx.withUnsafeMutableBytes { fx in + let fx = fx.baseAddress!.bindMemory(to: Float32.self, capacity: fx.count) + fy.withUnsafeMutableBytes { fy in + let fy = fy.baseAddress!.bindMemory(to: Float32.self, capacity: fy.count) + var y = 0 + while y < h { + var x = 0 + while x < w { + var l = l_dc + var p = p_dc + var q = q_dc + var a = a_dc + + // Precompute the coefficients + var cx = 0 + while cx < cx_stop { + let fw = Float32(w) + let fxx = Float32(x) + let fcx = Float32(cx) + fx[cx] = cos(Float32.pi / fw * (fxx + 0.5) * fcx) + cx += 1 + } + var cy = 0 + while cy < cy_stop { + let fh = Float32(h) + let fyy = Float32(y) + let fcy = Float32(cy) + fy[cy] = cos(Float32.pi / fh * (fyy + 0.5) * fcy) + cy += 1 + } + + // Decode L + var j = 0 + cy = 0 + while cy < ly { + var cx = cy > 0 ? 0 : 1 + let fy2 = fy[cy] * 2 + while cx * ly < lx * (ly - cy) { + l += l_ac[j] * fx[cx] * fy2 + j += 1 + cx += 1 + } + cy += 1 + } + + // Decode P and Q + j = 0 + cy = 0 + while cy < 3 { + var cx = cy > 0 ? 0 : 1 + let fy2 = fy[cy] * 2 + while cx < 3 - cy { + let f = fx[cx] * fy2 + p += p_ac[j] * f + q += q_ac[j] * f + j += 1 + cx += 1 + } + cy += 1 + } + + // Decode A + if hasAlpha { + j = 0 + cy = 0 + while cy < 5 { + var cx = cy > 0 ? 0 : 1 + let fy2 = fy[cy] * 2 + while cx < 5 - cy { + a += a_ac[j] * fx[cx] * fy2 + j += 1 + cx += 1 + } + cy += 1 + } + } + + // Convert to RGB + var b = l - 2 / 3 * p + var r = (3 * l - b + q) / 2 + var g = r - q + r = max(0, 255 * min(1, r)) + g = max(0, 255 * min(1, g)) + b = max(0, 255 * min(1, b)) + a = max(0, 255 * min(1, a)) + rgba[0] = UInt8(r) + rgba[1] = UInt8(g) + rgba[2] = UInt8(b) + rgba[3] = UInt8(a) + rgba = rgba.advanced(by: 4) + x += 1 + } + y += 1 + } + } + } + return (w, h, pointer) +} + +func thumbHashToApproximateAspectRatio(hash: Data) -> Float32 { + let header = hash[3] + let hasAlpha = (hash[2] & 0x80) != 0 + let isLandscape = (hash[4] & 0x80) != 0 + let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7 + let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7 + return Float32(lx) / Float32(ly) +} diff --git a/mobile/ios/Runner/Images/Thumbnails.g.swift b/mobile/ios/Runner/Images/Thumbnails.g.swift new file mode 100644 index 0000000000..be40a18b41 --- /dev/null +++ b/mobile/ios/Runner/Images/Thumbnails.g.swift @@ -0,0 +1,138 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + + +private class ThumbnailsPigeonCodecReader: FlutterStandardReader { +} + +private class ThumbnailsPigeonCodecWriter: FlutterStandardWriter { +} + +private class ThumbnailsPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return ThumbnailsPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return ThumbnailsPigeonCodecWriter(data: data) + } +} + +class ThumbnailsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = ThumbnailsPigeonCodec(readerWriter: ThumbnailsPigeonCodecReaderWriter()) +} + + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol ThumbnailApi { + func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, completion: @escaping (Result<[String: Int64], Error>) -> Void) + func cancelImageRequest(requestId: Int64) throws + func getThumbhash(thumbhash: String, completion: @escaping (Result<[String: Int64], Error>) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class ThumbnailApiSetup { + static var codec: FlutterStandardMessageCodec { ThumbnailsPigeonCodec.shared } + /// Sets up an instance of `ThumbnailApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ThumbnailApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let requestImageChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ThumbnailApi.requestImage\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + requestImageChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let assetIdArg = args[0] as! String + let requestIdArg = args[1] as! Int64 + let widthArg = args[2] as! Int64 + let heightArg = args[3] as! Int64 + let isVideoArg = args[4] as! Bool + api.requestImage(assetId: assetIdArg, requestId: requestIdArg, width: widthArg, height: heightArg, isVideo: isVideoArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + requestImageChannel.setMessageHandler(nil) + } + let cancelImageRequestChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ThumbnailApi.cancelImageRequest\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + cancelImageRequestChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let requestIdArg = args[0] as! Int64 + do { + try api.cancelImageRequest(requestId: requestIdArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + cancelImageRequestChannel.setMessageHandler(nil) + } + let getThumbhashChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ThumbnailApi.getThumbhash\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getThumbhashChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let thumbhashArg = args[0] as! String + api.getThumbhash(thumbhash: thumbhashArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getThumbhashChannel.setMessageHandler(nil) + } + } +} diff --git a/mobile/ios/Runner/Images/ThumbnailsImpl.swift b/mobile/ios/Runner/Images/ThumbnailsImpl.swift new file mode 100644 index 0000000000..452ca62377 --- /dev/null +++ b/mobile/ios/Runner/Images/ThumbnailsImpl.swift @@ -0,0 +1,211 @@ +import CryptoKit +import Flutter +import MobileCoreServices +import Photos + +class Request { + weak var workItem: DispatchWorkItem? + var isCancelled = false + let callback: (Result<[String: Int64], any Error>) -> Void + + init(callback: @escaping (Result<[String: Int64], any Error>) -> Void) { + self.callback = callback + } +} + +class ThumbnailApiImpl: ThumbnailApi { + private static let imageManager = PHImageManager.default() + private static let fetchOptions = { + let fetchOptions = PHFetchOptions() + fetchOptions.fetchLimit = 1 + fetchOptions.wantsIncrementalChangeDetails = false + return fetchOptions + }() + private static let requestOptions = { + let requestOptions = PHImageRequestOptions() + requestOptions.isNetworkAccessAllowed = true + requestOptions.deliveryMode = .highQualityFormat + requestOptions.resizeMode = .fast + requestOptions.isSynchronous = true + requestOptions.version = .current + return requestOptions + }() + + private static let assetQueue = DispatchQueue(label: "thumbnail.assets", qos: .userInitiated) + private static let requestQueue = DispatchQueue(label: "thumbnail.requests", qos: .userInitiated) + private static let cancelQueue = DispatchQueue(label: "thumbnail.cancellation", qos: .default) + private static let processingQueue = DispatchQueue(label: "thumbnail.processing", qos: .userInteractive, attributes: .concurrent) + + private static let rgbColorSpace = CGColorSpaceCreateDeviceRGB() + private static let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).rawValue + private static var requests = [Int64: Request]() + private static let cancelledResult = Result<[String: Int64], any Error>.success([:]) + private static let concurrencySemaphore = DispatchSemaphore(value: ProcessInfo.processInfo.activeProcessorCount * 2) + private static let assetCache = { + let assetCache = NSCache() + assetCache.countLimit = 10000 + return assetCache + }() + private static let activitySemaphore = DispatchSemaphore(value: 1) + private static let willResignActiveObserver = NotificationCenter.default.addObserver( + forName: UIApplication.willResignActiveNotification, + object: nil, + queue: .main + ) { _ in + processingQueue.suspend() + activitySemaphore.wait() + } + private static let didBecomeActiveObserver = NotificationCenter.default.addObserver( + forName: UIApplication.didBecomeActiveNotification, + object: nil, + queue: .main + ) { _ in + processingQueue.resume() + activitySemaphore.signal() + } + + func getThumbhash(thumbhash: String, completion: @escaping (Result<[String : Int64], any Error>) -> Void) { + Self.processingQueue.async { + guard let data = Data(base64Encoded: thumbhash) + else { return completion(.failure(PigeonError(code: "", message: "Invalid base64 string: \(thumbhash)", details: nil)))} + + let (width, height, pointer) = thumbHashToRGBA(hash: data) + self.waitForActiveState() + completion(.success(["pointer": Int64(Int(bitPattern: pointer.baseAddress)), "width": Int64(width), "height": Int64(height)])) + } + } + + func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, completion: @escaping (Result<[String: Int64], any Error>) -> Void) { + let request = Request(callback: completion) + let item = DispatchWorkItem { + if request.isCancelled { + return completion(Self.cancelledResult) + } + + Self.concurrencySemaphore.wait() + defer { + Self.concurrencySemaphore.signal() + } + + if request.isCancelled { + return completion(Self.cancelledResult) + } + + guard let asset = Self.requestAsset(assetId: assetId) + else { + Self.removeRequest(requestId: requestId) + completion(.failure(PigeonError(code: "", message: "Could not get asset data for \(assetId)", details: nil))) + return + } + + if request.isCancelled { + return completion(Self.cancelledResult) + } + + var image: UIImage? + Self.imageManager.requestImage( + for: asset, + targetSize: width > 0 && height > 0 ? CGSize(width: Double(width), height: Double(height)) : PHImageManagerMaximumSize, + contentMode: .aspectFill, + options: Self.requestOptions, + resultHandler: { (_image, info) -> Void in + image = _image + } + ) + + if request.isCancelled { + return completion(Self.cancelledResult) + } + + guard let image = image, + let cgImage = image.cgImage else { + Self.removeRequest(requestId: requestId) + return completion(.failure(PigeonError(code: "", message: "Could not get pixel data for \(assetId)", details: nil))) + } + + let pointer = UnsafeMutableRawPointer.allocate( + byteCount: Int(cgImage.width) * Int(cgImage.height) * 4, + alignment: MemoryLayout.alignment + ) + + if request.isCancelled { + pointer.deallocate() + return completion(Self.cancelledResult) + } + + guard let context = CGContext( + data: pointer, + width: cgImage.width, + height: cgImage.height, + bitsPerComponent: 8, + bytesPerRow: cgImage.width * 4, + space: Self.rgbColorSpace, + bitmapInfo: Self.bitmapInfo + ) else { + pointer.deallocate() + Self.removeRequest(requestId: requestId) + return completion(.failure(PigeonError(code: "", message: "Could not create context for \(assetId)", details: nil))) + } + + if request.isCancelled { + pointer.deallocate() + return completion(Self.cancelledResult) + } + + context.interpolationQuality = .none + context.draw(cgImage, in: CGRect(x: 0, y: 0, width: cgImage.width, height: cgImage.height)) + + if request.isCancelled { + pointer.deallocate() + return completion(Self.cancelledResult) + } + + self.waitForActiveState() + completion(.success(["pointer": Int64(Int(bitPattern: pointer)), "width": Int64(cgImage.width), "height": Int64(cgImage.height)])) + Self.removeRequest(requestId: requestId) + } + + request.workItem = item + Self.addRequest(requestId: requestId, request: request) + Self.processingQueue.async(execute: item) + } + + func cancelImageRequest(requestId: Int64) { + Self.cancelRequest(requestId: requestId) + } + + private static func addRequest(requestId: Int64, request: Request) -> Void { + requestQueue.sync { requests[requestId] = request } + } + + private static func removeRequest(requestId: Int64) -> Void { + requestQueue.sync { requests[requestId] = nil } + } + + private static func cancelRequest(requestId: Int64) -> Void { + requestQueue.async { + guard let request = requests.removeValue(forKey: requestId) else { return } + request.isCancelled = true + guard let item = request.workItem else { return } + if item.isCancelled { + cancelQueue.async { request.callback(Self.cancelledResult) } + } + } + } + + private static func requestAsset(assetId: String) -> PHAsset? { + var asset: PHAsset? + assetQueue.sync { asset = assetCache.object(forKey: assetId as NSString) } + if asset != nil { return asset } + + guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: Self.fetchOptions).firstObject + else { return nil } + assetQueue.async { assetCache.setObject(asset, forKey: assetId as NSString) } + return asset + } + + func waitForActiveState() { + Self.activitySemaphore.wait() + Self.activitySemaphore.signal() + } +} diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 4237813dfc..fb89490550 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -1,187 +1,189 @@ - - AppGroupId - $(CUSTOM_GROUP_ID) - BGTaskSchedulerPermittedIdentifiers - - app.alextran.immich.backgroundFetch - app.alextran.immich.backgroundProcessing - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleDocumentTypes - - - CFBundleTypeName - ShareHandler - LSHandlerRank - Alternate - LSItemContentTypes - - public.file-url - public.image - public.text - public.movie - public.url - public.data - - - - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - ar - ca - cs - da - de - es - fi - fr - he - hi - hu - it - ja - ko - lv - mn - nb - nl - pl - pt - ro - ru - sk - sl - sr - sv - th - uk - vi - zh - - CFBundleName - immich_mobile - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.135.1 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - Share Extension - CFBundleURLSchemes - - ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) - - - - CFBundleTypeRole - Editor - CFBundleURLName - Deep Link - CFBundleURLSchemes - - immich - - - - CFBundleVersion - 210 - FLTEnableImpeller - - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - https - - LSRequiresIPhoneOS - - LSSupportsOpeningDocumentsInPlace - No - MGLMapboxMetricsEnabledSettingShownInApp - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSBonjourServices - - _googlecast._tcp - _CC1AD845._googlecast._tcp - - NSCameraUsageDescription - We need to access the camera to let you take beautiful video using this app - NSFaceIDUsageDescription - We need to use FaceID to allow access to your locked folder - NSLocationAlwaysAndWhenInUseUsageDescription - We require this permission to access the local WiFi name for background upload mechanism - NSLocationUsageDescription - We require this permission to access the local WiFi name - NSLocationWhenInUseUsageDescription - We require this permission to access the local WiFi name - NSMicrophoneUsageDescription - We need to access the microphone to let you take beautiful video using this app - NSPhotoLibraryAddUsageDescription - We need to manage backup your photos album - NSPhotoLibraryUsageDescription - We need to manage backup your photos album - NSUserActivityTypes - - INSendMessageIntent - - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - fetch - processing - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - io.flutter.embedded_views_preview - - NSLocalNetworkUsageDescription - We need local network permission to connect to the local server using IP address and + + AppGroupId + $(CUSTOM_GROUP_ID) + BGTaskSchedulerPermittedIdentifiers + + app.alextran.immich.background.refreshUpload + app.alextran.immich.background.processingUpload + app.alextran.immich.backgroundFetch + app.alextran.immich.backgroundProcessing + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleDocumentTypes + + + CFBundleTypeName + ShareHandler + LSHandlerRank + Alternate + LSItemContentTypes + + public.file-url + public.image + public.text + public.movie + public.url + public.data + + + + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + ar + ca + cs + da + de + es + fi + fr + he + hi + hu + it + ja + ko + lv + mn + nb + nl + pl + pt + ro + ru + sk + sl + sr + sv + th + uk + vi + zh + + CFBundleName + immich_mobile + CFBundlePackageType + APPL + CFBundleShortVersionString + 2.0.1 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + Share Extension + CFBundleURLSchemes + + ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) + + + + CFBundleTypeRole + Editor + CFBundleURLName + Deep Link + CFBundleURLSchemes + + immich + + + + CFBundleVersion + 230 + FLTEnableImpeller + + ITSAppUsesNonExemptEncryption + + LSApplicationQueriesSchemes + + https + + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + No + MGLMapboxMetricsEnabledSettingShownInApp + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSBonjourServices + + _googlecast._tcp + _CC1AD845._googlecast._tcp + + NSCameraUsageDescription + We need to access the camera to let you take beautiful video using this app + NSFaceIDUsageDescription + We need to use FaceID to allow access to your locked folder + NSLocalNetworkUsageDescription + We need local network permission to connect to the local server using IP address and allow the casting feature to work - - \ No newline at end of file + NSLocationAlwaysAndWhenInUseUsageDescription + We require this permission to access the local WiFi name for background upload mechanism + NSLocationUsageDescription + We require this permission to access the local WiFi name + NSLocationWhenInUseUsageDescription + We require this permission to access the local WiFi name + NSMicrophoneUsageDescription + We need to access the microphone to let you take beautiful video using this app + NSPhotoLibraryAddUsageDescription + We need to manage backup your photos album + NSPhotoLibraryUsageDescription + We need to manage backup your photos album + NSUserActivityTypes + + INSendMessageIntent + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + fetch + processing + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + io.flutter.embedded_views_preview + + + diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index e629604d6a..305aca5266 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -139,6 +139,7 @@ struct PlatformAsset: Hashable { var height: Int64? = nil var durationInSeconds: Int64 var orientation: Int64 + var isFavorite: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -152,6 +153,7 @@ struct PlatformAsset: Hashable { let height: Int64? = nilOrValue(pigeonVar_list[6]) let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 + let isFavorite = pigeonVar_list[9] as! Bool return PlatformAsset( id: id, @@ -162,7 +164,8 @@ struct PlatformAsset: Hashable { width: width, height: height, durationInSeconds: durationInSeconds, - orientation: orientation + orientation: orientation, + isFavorite: isFavorite ) } func toList() -> [Any?] { @@ -176,6 +179,7 @@ struct PlatformAsset: Hashable { height, durationInSeconds, orientation, + isFavorite, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { @@ -263,6 +267,39 @@ struct SyncDelta: Hashable { } } +/// Generated class from Pigeon that represents data sent in messages. +struct HashResult: Hashable { + var assetId: String + var error: String? = nil + var hash: String? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> HashResult? { + let assetId = pigeonVar_list[0] as! String + let error: String? = nilOrValue(pigeonVar_list[1]) + let hash: String? = nilOrValue(pigeonVar_list[2]) + + return HashResult( + assetId: assetId, + error: error, + hash: hash + ) + } + func toList() -> [Any?] { + return [ + assetId, + error, + hash, + ] + } + static func == (lhs: HashResult, rhs: HashResult) -> Bool { + return deepEqualsMessages(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashMessages(value: toList(), hasher: &hasher) + } +} + private class MessagesPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -272,6 +309,8 @@ private class MessagesPigeonCodecReader: FlutterStandardReader { return PlatformAlbum.fromList(self.readValue() as! [Any?]) case 131: return SyncDelta.fromList(self.readValue() as! [Any?]) + case 132: + return HashResult.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -289,6 +328,9 @@ private class MessagesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? SyncDelta { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? HashResult { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -309,6 +351,7 @@ class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter()) } + /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol NativeSyncApi { func shouldFullSync() throws -> Bool @@ -319,7 +362,8 @@ protocol NativeSyncApi { func getAlbums() throws -> [PlatformAlbum] func getAssetsCountSince(albumId: String, timestamp: Int64) throws -> Int64 func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset] - func hashPaths(paths: [String]) throws -> [FlutterStandardTypedData?] + func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void) + func cancelHashing() throws } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -455,22 +499,38 @@ class NativeSyncApiSetup { } else { getAssetsForAlbumChannel.setMessageHandler(nil) } - let hashPathsChannel = taskQueue == nil - ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + let hashAssetsChannel = taskQueue == nil + ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) if let api = api { - hashPathsChannel.setMessageHandler { message, reply in + hashAssetsChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let pathsArg = args[0] as! [String] + let assetIdsArg = args[0] as! [String] + let allowNetworkAccessArg = args[1] as! Bool + api.hashAssets(assetIds: assetIdsArg, allowNetworkAccess: allowNetworkAccessArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + hashAssetsChannel.setMessageHandler(nil) + } + let cancelHashingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelHashing\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + cancelHashingChannel.setMessageHandler { _, reply in do { - let result = try api.hashPaths(paths: pathsArg) - reply(wrapResult(result)) + try api.cancelHashing() + reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - hashPathsChannel.setMessageHandler(nil) + cancelHashingChannel.setMessageHandler(nil) } } } diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index 459e29fa5a..bb23bae6b6 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -17,29 +17,16 @@ struct AssetWrapper: Hashable, Equatable { } } -extension PHAsset { - func toPlatformAsset() -> PlatformAsset { - return PlatformAsset( - id: localIdentifier, - name: title(), - type: Int64(mediaType.rawValue), - createdAt: creationDate.map { Int64($0.timeIntervalSince1970) }, - updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) }, - width: Int64(pixelWidth), - height: Int64(pixelHeight), - durationInSeconds: Int64(duration), - orientation: 0 - ) - } -} - class NativeSyncApiImpl: NativeSyncApi { private let defaults: UserDefaults private let changeTokenKey = "immich:changeToken" private let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum] private let recoveredAlbumSubType = 1000000219 - private let hashBufferSize = 2 * 1024 * 1024 + private var hashTask: Task? + private static let hashCancelledCode = "HASH_CANCELLED" + private static let hashCancelled = Result<[HashResult], Error>.failure(PigeonError(code: hashCancelledCode, message: "Hashing cancelled", details: nil)) + init(with defaults: UserDefaults = .standard) { self.defaults = defaults @@ -95,7 +82,7 @@ class NativeSyncApiImpl: NativeSyncApi { let collections = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil) for i in 0.. %@ OR modificationDate > %@", date, date) } - + let result = PHAsset.fetchAssets(in: album, options: options) if(result.count == 0) { return [] @@ -265,23 +253,114 @@ class NativeSyncApiImpl: NativeSyncApi { return assets } - func hashPaths(paths: [String]) throws -> [FlutterStandardTypedData?] { - return paths.map { path in - guard let file = FileHandle(forReadingAtPath: path) else { - print("Cannot open file: \(path)") - return nil - } - - var hasher = Insecure.SHA1() - while autoreleasepool(invoking: { - let chunk = file.readData(ofLength: hashBufferSize) - guard !chunk.isEmpty else { return false } - hasher.update(data: chunk) - return true - }) { } - - let digest = hasher.finalize() - return FlutterStandardTypedData(bytes: Data(digest)) + func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void) { + if let prevTask = hashTask { + prevTask.cancel() + hashTask = nil + } + hashTask = Task { [weak self] in + var missingAssetIds = Set(assetIds) + var assets = [PHAsset]() + assets.reserveCapacity(assetIds.count) + PHAsset.fetchAssets(withLocalIdentifiers: assetIds, options: nil).enumerateObjects { (asset, _, stop) in + if Task.isCancelled { + stop.pointee = true + return + } + missingAssetIds.remove(asset.localIdentifier) + assets.append(asset) } + + if Task.isCancelled { + return completion(Self.hashCancelled) + } + + await withTaskGroup(of: HashResult?.self) { taskGroup in + var results = [HashResult]() + results.reserveCapacity(assets.count) + for asset in assets { + if Task.isCancelled { + return completion(Self.hashCancelled) + } + taskGroup.addTask { + guard let self = self else { return nil } + return await self.hashAsset(asset, allowNetworkAccess: allowNetworkAccess) + } + } + + for await result in taskGroup { + guard let result = result else { + return completion(Self.hashCancelled) + } + results.append(result) + } + + for missing in missingAssetIds { + results.append(HashResult(assetId: missing, error: "Asset not found in library", hash: nil)) + } + + completion(.success(results)) + } + } + } + + func cancelHashing() { + hashTask?.cancel() + hashTask = nil + } + + private func hashAsset(_ asset: PHAsset, allowNetworkAccess: Bool) async -> HashResult? { + class RequestRef { + var id: PHAssetResourceDataRequestID? + } + let requestRef = RequestRef() + return await withTaskCancellationHandler(operation: { + if Task.isCancelled { + return nil + } + + guard let resource = asset.getResource() else { + return HashResult(assetId: asset.localIdentifier, error: "Cannot get asset resource", hash: nil) + } + + if Task.isCancelled { + return nil + } + + let options = PHAssetResourceRequestOptions() + options.isNetworkAccessAllowed = allowNetworkAccess + + return await withCheckedContinuation { continuation in + var hasher = Insecure.SHA1() + + requestRef.id = PHAssetResourceManager.default().requestData( + for: resource, + options: options, + dataReceivedHandler: { data in + hasher.update(data: data) + }, + completionHandler: { error in + let result: HashResult? = switch (error) { + case let e as PHPhotosError where e.code == .userCancelled: nil + case let .some(e): HashResult( + assetId: asset.localIdentifier, + error: "Failed to hash asset: \(e.localizedDescription)", + hash: nil + ) + case .none: + HashResult( + assetId: asset.localIdentifier, + error: nil, + hash: Data(hasher.finalize()).base64EncodedString() + ) + } + continuation.resume(returning: result) + } + ) + } + }, onCancel: { + guard let requestId = requestRef.id else { return } + PHAssetResourceManager.default().cancelDataRequest(requestId) + }) } } diff --git a/mobile/ios/Runner/Sync/PHAssetExtensions.swift b/mobile/ios/Runner/Sync/PHAssetExtensions.swift new file mode 100644 index 0000000000..2b1ef6ac88 --- /dev/null +++ b/mobile/ios/Runner/Sync/PHAssetExtensions.swift @@ -0,0 +1,77 @@ +import Photos + +extension PHAsset { + func toPlatformAsset() -> PlatformAsset { + return PlatformAsset( + id: localIdentifier, + name: title, + type: Int64(mediaType.rawValue), + createdAt: creationDate.map { Int64($0.timeIntervalSince1970) }, + updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) }, + width: Int64(pixelWidth), + height: Int64(pixelHeight), + durationInSeconds: Int64(duration), + orientation: 0, + isFavorite: isFavorite + ) + } + + var title: String { + return filename ?? originalFilename ?? "" + } + + var filename: String? { + return value(forKey: "filename") as? String + } + + // This method is expected to be slow as it goes through the asset resources to fetch the originalFilename + var originalFilename: String? { + return getResource()?.originalFilename + } + + func getResource() -> PHAssetResource? { + let resources = PHAssetResource.assetResources(for: self) + + let filteredResources = resources.filter { $0.isMediaResource && isValidResourceType($0.type) } + + guard !filteredResources.isEmpty else { + return nil + } + + if filteredResources.count == 1 { + return filteredResources.first + } + + if let currentResource = filteredResources.first(where: { $0.isCurrent }) { + return currentResource + } + + if let fullSizeResource = filteredResources.first(where: { isFullSizeResourceType($0.type) }) { + return fullSizeResource + } + + return nil + } + + private func isValidResourceType(_ type: PHAssetResourceType) -> Bool { + switch mediaType { + case .image: + return [.photo, .alternatePhoto, .fullSizePhoto].contains(type) + case .video: + return [.video, .fullSizeVideo, .fullSizePairedVideo].contains(type) + default: + return false + } + } + + private func isFullSizeResourceType(_ type: PHAssetResourceType) -> Bool { + switch mediaType { + case .image: + return type == .fullSizePhoto + case .video: + return type == .fullSizeVideo + default: + return false + } + } +} diff --git a/mobile/ios/Runner/Sync/PHAssetResourceExtensions.swift b/mobile/ios/Runner/Sync/PHAssetResourceExtensions.swift new file mode 100644 index 0000000000..699d55a98d --- /dev/null +++ b/mobile/ios/Runner/Sync/PHAssetResourceExtensions.swift @@ -0,0 +1,16 @@ + +import Photos + +extension PHAssetResource { + var isCurrent: Bool { + return value(forKey: "isCurrent") as? Bool ?? false + } + + var isMediaResource: Bool { + var isMedia = type != .adjustmentData + if #available(iOS 17, *) { + isMedia = isMedia && type != .photoProxy + } + return isMedia + } +} diff --git a/mobile/ios/WidgetExtension/ImmichAPI.swift b/mobile/ios/WidgetExtension/ImmichAPI.swift index 36758b824c..19ff3d38ba 100644 --- a/mobile/ios/WidgetExtension/ImmichAPI.swift +++ b/mobile/ios/WidgetExtension/ImmichAPI.swift @@ -104,10 +104,13 @@ struct Album: Codable, Equatable { // MARK: API class ImmichAPI { + typealias CustomHeaders = [String:String] struct ServerConfig { let serverEndpoint: String let sessionKey: String + let customHeaders: CustomHeaders } + let serverConfig: ServerConfig init() async throws { @@ -122,10 +125,20 @@ class ImmichAPI { if serverURL == "" || sessionKey == "" { throw WidgetError.noLogin } + + // custom headers come in the form of KV pairs in JSON + var customHeadersJSON = (defaults.string(forKey: "widget_custom_headers") ?? "") + var customHeaders: CustomHeaders = [:] + + if customHeadersJSON != "", + let parsedHeaders = try? JSONDecoder().decode(CustomHeaders.self, from: customHeadersJSON.data(using: .utf8)!) { + customHeaders = parsedHeaders + } serverConfig = ServerConfig( serverEndpoint: serverURL, - sessionKey: sessionKey + sessionKey: sessionKey, + customHeaders: customHeaders ) } @@ -155,6 +168,12 @@ class ImmichAPI { return components?.url } + + func applyCustomHeaders(for request: inout URLRequest) { + for (header, value) in serverConfig.customHeaders { + request.addValue(value, forHTTPHeaderField: header) + } + } func fetchSearchResults(with filters: SearchFilter = Album.NONE.filter) async throws @@ -174,7 +193,8 @@ class ImmichAPI { request.httpMethod = "POST" request.httpBody = try JSONEncoder().encode(filters) request.setValue("application/json", forHTTPHeaderField: "Content-Type") - + applyCustomHeaders(for: &request) + let (data, _) = try await URLSession.shared.data(for: request) // decode data @@ -196,6 +216,7 @@ class ImmichAPI { var request = URLRequest(url: searchURL) request.httpMethod = "GET" + applyCustomHeaders(for: &request) let (data, _) = try await URLSession.shared.data(for: request) @@ -254,7 +275,8 @@ class ImmichAPI { var request = URLRequest(url: searchURL) request.httpMethod = "GET" - + applyCustomHeaders(for: &request) + let (data, _) = try await URLSession.shared.data(for: request) // decode data diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 4c60a2e831..f72597fe33 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.135.3" + version_number: "2.0.1" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/lib/constants/colors.dart b/mobile/lib/constants/colors.dart index ade878d6f6..069ed519cf 100644 --- a/mobile/lib/constants/colors.dart +++ b/mobile/lib/constants/colors.dart @@ -1,17 +1,6 @@ import 'package:flutter/material.dart'; -enum ImmichColorPreset { - indigo, - deepPurple, - pink, - red, - orange, - yellow, - lime, - green, - cyan, - slateGray -} +enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray } const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo; const String defaultColorPresetName = "indigo"; diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 206fbbb28f..7429616f14 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -1,16 +1,18 @@ +import 'dart:io'; + const int noDbId = -9223372036854775808; // from Isar const double downloadCompleted = -1; const double downloadFailed = -2; // Number of log entries to retain on app start -const int kLogTruncateLimit = 250; +const int kLogTruncateLimit = 2000; // Sync const int kSyncEventBatchSize = 5000; const int kFetchLocalAssetsBatchSize = 40000; // Hash batch limits -const int kBatchHashFileLimit = 128; +final int kBatchHashFileLimit = Platform.isIOS ? 32 : 512; const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys @@ -20,16 +22,20 @@ const String kSecuredPinCode = "secured_pin_code"; const String kManualUploadGroup = 'manual_upload_group'; const String kBackupGroup = 'backup_group'; const String kBackupLivePhotoGroup = 'backup_live_photo_group'; +const String kDownloadGroupImage = 'group_image'; +const String kDownloadGroupVideo = 'group_video'; +const String kDownloadGroupLivePhoto = 'group_livephoto'; // Timeline constants const int kTimelineNoneSegmentSize = 120; -const int kTimelineAssetLoadBatchSize = 256; +const int kTimelineAssetLoadBatchSize = 1024; const int kTimelineAssetLoadOppositeSize = 64; // Widget keys +const String appShareGroupId = "group.app.immich.share"; const String kWidgetAuthToken = "widget_auth_token"; const String kWidgetServerEndpoint = "widget_server_url"; -const String appShareGroupId = "group.app.immich.share"; +const String kWidgetCustomHeaders = "widget_custom_headers"; // add widget identifiers here for new widgets // these are used to force a widget refresh @@ -41,3 +47,5 @@ const List<(String, String)> kWidgetNames = [ const double kUploadStatusFailed = -1.0; const double kUploadStatusCanceled = -2.0; + +const int kMinMonthsToEnableScrubberSnap = 12; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index febc71032e..6ec0ce37ea 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -1,13 +1,6 @@ -enum SortOrder { - asc, - desc, -} +enum SortOrder { asc, desc } -enum TextSearchType { - context, - filename, - description, -} +enum TextSearchType { context, filename, description } enum AssetVisibilityEnum { timeline, hidden, archive, locked } diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart index 61597f08d1..e77bcfbf1f 100644 --- a/mobile/lib/constants/filters.dart +++ b/mobile/lib/constants/filters.dart @@ -2,511 +2,49 @@ import 'package:flutter/material.dart'; const List filters = [ //Original - ColorFilter.matrix([ - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), //Vintage - ColorFilter.matrix([ - 0.8, - 0.1, - 0.1, - 0, - 20, - 0.1, - 0.8, - 0.1, - 0, - 20, - 0.1, - 0.1, - 0.8, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), //Mood - ColorFilter.matrix([ - 1.2, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), //Crisp - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Cool - ColorFilter.matrix([ - 0.9, - 0, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Blush - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), //Sunkissed - ColorFilter.matrix([ - 1.3, - 0, - 0.1, - 0, - 15, - 0, - 1.1, - 0.1, - 0, - 10, - 0, - 0, - 0.9, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), //Fresh - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 20, - 0, - 1.2, - 0, - 0, - 20, - 0, - 0, - 1.1, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), //Classic - ColorFilter.matrix([ - 1.1, - 0, - -0.1, - 0, - 10, - -0.1, - 1.1, - 0.1, - 0, - 5, - 0, - -0.1, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Lomo-ish - ColorFilter.matrix([ - 1.5, - 0, - 0.1, - 0, - 0, - 0, - 1.45, - 0, - 0, - 0, - 0.1, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Nashville - ColorFilter.matrix([ - 1.2, - 0.15, - -0.15, - 0, - 15, - 0.1, - 1.1, - 0.1, - 0, - 10, - -0.05, - 0.2, - 1.25, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), //Valencia - ColorFilter.matrix([ - 1.15, - 0.1, - 0.1, - 0, - 20, - 0.1, - 1.1, - 0, - 0, - 10, - 0.1, - 0.1, - 1.2, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), //Clarendon - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 10, - 0, - 1.25, - 0, - 0, - 10, - 0, - 0, - 1.3, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), //Moon - ColorFilter.matrix([ - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), //Willow - ColorFilter.matrix([ - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), //Kodak - ColorFilter.matrix([ - 1.3, - 0.1, - -0.1, - 0, - 10, - 0, - 1.25, - 0.1, - 0, - 10, - 0, - -0.1, - 1.1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), //Frost - ColorFilter.matrix([ - 0.8, - 0.2, - 0.1, - 0, - 0, - 0.2, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), //Night Vision - ColorFilter.matrix([ - 0.1, - 0.95, - 0.2, - 0, - 0, - 0.1, - 1.5, - 0.1, - 0, - 0, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), //Sunset - ColorFilter.matrix([ - 1.5, - 0.2, - 0, - 0, - 0, - 0.1, - 0.9, - 0.1, - 0, - 0, - -0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Noir - ColorFilter.matrix([ - 1.3, - -0.3, - 0.1, - 0, - 0, - -0.1, - 1.2, - -0.1, - 0, - 0, - 0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Dreamy - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 0, - 0.1, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.1, - 0, - 15, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), //Sepia - ColorFilter.matrix([ - 0.393, - 0.769, - 0.189, - 0, - 0, - 0.349, - 0.686, - 0.168, - 0, - 0, - 0.272, - 0.534, - 0.131, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]), //Radium ColorFilter.matrix([ 1.438, @@ -554,212 +92,23 @@ const List filters = [ 0, ]), //Purple Haze - ColorFilter.matrix([ - 1.3, - 0, - 1.2, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0.2, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Lemonade - ColorFilter.matrix([ - 1.2, - 0.1, - 0, - 0, - 0, - 0, - 1.1, - 0.2, - 0, - 0, - 0.1, - 0, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), //Caramel - ColorFilter.matrix([ - 1.6, - 0.2, - 0, - 0, - 0, - 0.1, - 1.3, - 0.1, - 0, - 0, - 0, - 0.1, - 0.9, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), //Peachy - ColorFilter.matrix([ - 1.3, - 0.5, - 0, - 0, - 0, - 0.2, - 1.1, - 0.3, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Neon - ColorFilter.matrix([ - 1, - 0, - 1, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), //Cold Morning - ColorFilter.matrix([ - 0.9, - 0.1, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Lush - ColorFilter.matrix([ - 0.9, - 0.2, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Urban Neon - ColorFilter.matrix([ - 1.1, - 0, - 0.3, - 0, - 0, - 0, - 0.9, - 0.3, - 0, - 0, - 0.3, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Monochrome - ColorFilter.matrix([ - 0.6, - 0.2, - 0.2, - 0, - 0, - 0.2, - 0.6, - 0.2, - 0, - 0, - 0.2, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), ]; const List filterNames = [ diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index b2d8df525b..f3c24384b0 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -7,10 +7,8 @@ const Map locales = { 'Arabic (ar)': Locale('ar'), 'Bulgarian (bg)': Locale('bg'), 'Catalan (ca)': Locale('ca'), - 'Chinese Simplified (zh_CN)': - Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), - 'Chinese Traditional (zh_TW)': - Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), + 'Chinese Simplified (zh_CN)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), + 'Chinese Traditional (zh_TW)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), 'Croatian (hr)': Locale('hr'), 'Czech (cs)': Locale('cs'), 'Danish (da)': Locale('da'), @@ -37,10 +35,8 @@ const Map locales = { 'Portuguese (pt)': Locale('pt'), 'Romanian (ro)': Locale('ro'), 'Russian (ru)': Locale('ru'), - 'Serbian Cyrillic (sr_Cyrl)': - Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl'), - 'Serbian Latin (sr_Latn)': - Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn'), + 'Serbian Cyrillic (sr_Cyrl)': Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl'), + 'Serbian Latin (sr_Latn)': Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn'), 'Slovak (sk)': Locale('sk'), 'Slovenian (sl)': Locale('sl'), 'Spanish (es)': Locale('es'), @@ -55,7 +51,4 @@ const Map locales = { const String translationsPath = 'assets/i18n'; -const List localesNotSupportedByOverpass = [ - Locale('el', 'GR'), - Locale('sr', 'Cyrl'), -]; +const List localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')]; diff --git a/mobile/lib/domain/models/album/album.model.dart b/mobile/lib/domain/models/album/album.model.dart index a199bce129..4558e1d5be 100644 --- a/mobile/lib/domain/models/album/album.model.dart +++ b/mobile/lib/domain/models/album/album.model.dart @@ -23,6 +23,7 @@ class RemoteAlbum { final AlbumAssetOrder order; final int assetCount; final String ownerName; + final bool isShared; const RemoteAlbum({ required this.id, @@ -36,6 +37,7 @@ class RemoteAlbum { required this.order, required this.assetCount, required this.ownerName, + required this.isShared, }); @override @@ -52,6 +54,7 @@ class RemoteAlbum { thumbnailAssetId: ${thumbnailAssetId ?? ""} assetCount: $assetCount ownerName: $ownerName + isShared: $isShared }'''; } @@ -69,7 +72,8 @@ class RemoteAlbum { isActivityEnabled == other.isActivityEnabled && order == other.order && assetCount == other.assetCount && - ownerName == other.ownerName; + ownerName == other.ownerName && + isShared == other.isShared; } @override @@ -84,7 +88,8 @@ class RemoteAlbum { isActivityEnabled.hashCode ^ order.hashCode ^ assetCount.hashCode ^ - ownerName.hashCode; + ownerName.hashCode ^ + isShared.hashCode; } RemoteAlbum copyWith({ @@ -99,6 +104,7 @@ class RemoteAlbum { AlbumAssetOrder? order, int? assetCount, String? ownerName, + bool? isShared, }) { return RemoteAlbum( id: id ?? this.id, @@ -112,6 +118,7 @@ class RemoteAlbum { order: order ?? this.order, assetCount: assetCount ?? this.assetCount, ownerName: ownerName ?? this.ownerName, + isShared: isShared ?? this.isShared, ); } } diff --git a/mobile/lib/domain/models/album/local_album.model.dart b/mobile/lib/domain/models/album/local_album.model.dart index b0b08937ab..ea06118aa1 100644 --- a/mobile/lib/domain/models/album/local_album.model.dart +++ b/mobile/lib/domain/models/album/local_album.model.dart @@ -15,6 +15,7 @@ class LocalAlbum { final int assetCount; final BackupSelection backupSelection; + final String? linkedRemoteAlbumId; const LocalAlbum({ required this.id, @@ -23,6 +24,7 @@ class LocalAlbum { this.assetCount = 0, this.backupSelection = BackupSelection.none, this.isIosSharedAlbum = false, + this.linkedRemoteAlbumId, }); LocalAlbum copyWith({ @@ -32,6 +34,7 @@ class LocalAlbum { int? assetCount, BackupSelection? backupSelection, bool? isIosSharedAlbum, + String? linkedRemoteAlbumId, }) { return LocalAlbum( id: id ?? this.id, @@ -40,6 +43,7 @@ class LocalAlbum { assetCount: assetCount ?? this.assetCount, backupSelection: backupSelection ?? this.backupSelection, isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, ); } @@ -53,7 +57,8 @@ class LocalAlbum { other.updatedAt == updatedAt && other.assetCount == assetCount && other.backupSelection == backupSelection && - other.isIosSharedAlbum == isIosSharedAlbum; + other.isIosSharedAlbum == isIosSharedAlbum && + other.linkedRemoteAlbumId == linkedRemoteAlbumId; } @override @@ -63,7 +68,8 @@ class LocalAlbum { updatedAt.hashCode ^ assetCount.hashCode ^ backupSelection.hashCode ^ - isIosSharedAlbum.hashCode; + isIosSharedAlbum.hashCode ^ + linkedRemoteAlbumId.hashCode; } @override @@ -75,6 +81,7 @@ updatedAt: $updatedAt, assetCount: $assetCount, backupSelection: $backupSelection, isIosSharedAlbum: $isIosSharedAlbum +linkedRemoteAlbumId: $linkedRemoteAlbumId, }'''; } } diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index 356b64cffc..4d40be2d32 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -9,11 +9,7 @@ enum AssetType { audio, } -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } sealed class BaseAsset { final String name; @@ -53,10 +49,8 @@ sealed class BaseAsset { return const Duration(); } - bool get hasRemote => - storage == AssetState.remote || storage == AssetState.merged; - bool get hasLocal => - storage == AssetState.local || storage == AssetState.merged; + bool get hasRemote => storage == AssetState.remote || storage == AssetState.merged; + bool get hasLocal => storage == AssetState.local || storage == AssetState.merged; bool get isLocalOnly => storage == AssetState.local; bool get isRemoteOnly => storage == AssetState.remote; diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 3466a0f25b..9cd20acb0a 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -22,8 +22,7 @@ class LocalAsset extends BaseAsset { }); @override - AssetState get storage => - remoteId == null ? AssetState.local : AssetState.merged; + AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged; @override String get heroTag => '${id}_${remoteId ?? checksum}'; @@ -54,8 +53,7 @@ class LocalAsset extends BaseAsset { } @override - int get hashCode => - super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; + int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; LocalAsset copyWith({ String? id, diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 760a16170b..8648255167 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -1,11 +1,6 @@ part of 'base_asset.model.dart'; -enum AssetVisibility { - timeline, - hidden, - archive, - locked, -} +enum AssetVisibility { timeline, hidden, archive, locked } // Model for an asset stored in the server class RemoteAsset extends BaseAsset { @@ -15,7 +10,6 @@ class RemoteAsset extends BaseAsset { final AssetVisibility visibility; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -34,12 +28,10 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override - AssetState get storage => - localId == null ? AssetState.remote : AssetState.merged; + AssetState get storage => localId == null ? AssetState.remote : AssetState.merged; @override String get heroTag => '${localId ?? checksum}_$id'; @@ -61,7 +53,6 @@ class RemoteAsset extends BaseAsset { thumbHash: ${thumbHash ?? ""}, visibility: $visibility, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -77,8 +68,7 @@ class RemoteAsset extends BaseAsset { ownerId == other.ownerId && thumbHash == other.thumbHash && visibility == other.visibility && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -89,8 +79,7 @@ class RemoteAsset extends BaseAsset { localId.hashCode ^ thumbHash.hashCode ^ visibility.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -109,7 +98,6 @@ class RemoteAsset extends BaseAsset { AssetVisibility? visibility, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -128,7 +116,6 @@ class RemoteAsset extends BaseAsset { visibility: visibility ?? this.visibility, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/domain/models/asset_face.model.dart b/mobile/lib/domain/models/asset_face.model.dart new file mode 100644 index 0000000000..f432b923e3 --- /dev/null +++ b/mobile/lib/domain/models/asset_face.model.dart @@ -0,0 +1,98 @@ +// Model for an asset face stored in the server +class AssetFace { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + + const AssetFace({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + + AssetFace copyWith({ + String? id, + String? assetId, + String? personId, + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) { + return AssetFace( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + String toString() { + return '''AssetFace { + id: $id, + assetId: $assetId, + personId: ${personId ?? ""}, + imageWidth: $imageWidth, + imageHeight: $imageHeight, + boundingBoxX1: $boundingBoxX1, + boundingBoxY1: $boundingBoxY1, + boundingBoxX2: $boundingBoxX2, + boundingBoxY2: $boundingBoxY2, + sourceType: $sourceType, +}'''; + } + + @override + bool operator ==(covariant AssetFace other) { + if (identical(this, other)) return true; + + return other.id == id && + other.assetId == assetId && + other.personId == personId && + other.imageWidth == imageWidth && + other.imageHeight == imageHeight && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY2 == boundingBoxY2 && + other.sourceType == sourceType; + } + + @override + int get hashCode { + return id.hashCode ^ + assetId.hashCode ^ + personId.hashCode ^ + imageWidth.hashCode ^ + imageHeight.hashCode ^ + boundingBoxX1.hashCode ^ + boundingBoxY1.hashCode ^ + boundingBoxX2.hashCode ^ + boundingBoxY2.hashCode ^ + sourceType.hashCode; + } +} diff --git a/mobile/lib/domain/models/device_asset.model.dart b/mobile/lib/domain/models/device_asset.model.dart index 2ec56b0d80..a404f5a9e2 100644 --- a/mobile/lib/domain/models/device_asset.model.dart +++ b/mobile/lib/domain/models/device_asset.model.dart @@ -5,19 +5,13 @@ class DeviceAsset { final Uint8List hash; final DateTime modifiedTime; - const DeviceAsset({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime}); @override bool operator ==(covariant DeviceAsset other) { if (identical(this, other)) return true; - return other.assetId == assetId && - other.hash == hash && - other.modifiedTime == modifiedTime; + return other.assetId == assetId && other.hash == hash && other.modifiedTime == modifiedTime; } @override @@ -30,11 +24,7 @@ class DeviceAsset { return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)'; } - DeviceAsset copyWith({ - String? assetId, - Uint8List? hash, - DateTime? modifiedTime, - }) { + DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) { return DeviceAsset( assetId: assetId ?? this.assetId, hash: hash ?? this.hash, diff --git a/mobile/lib/domain/models/exif.model.dart b/mobile/lib/domain/models/exif.model.dart index b73aa4cae1..6e94c44650 100644 --- a/mobile/lib/domain/models/exif.model.dart +++ b/mobile/lib/domain/models/exif.model.dart @@ -25,8 +25,7 @@ class ExifInfo { final int? iso; final double? exposureSeconds; - bool get hasCoordinates => - latitude != null && longitude != null && latitude != 0 && longitude != 0; + bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0; String get exposureTime { if (exposureSeconds == null) { diff --git a/mobile/lib/domain/models/log.model.dart b/mobile/lib/domain/models/log.model.dart index dffd1cccda..9902ca04ca 100644 --- a/mobile/lib/domain/models/log.model.dart +++ b/mobile/lib/domain/models/log.model.dart @@ -1,16 +1,5 @@ /// Log levels according to dart logging [Level] -enum LogLevel { - all, - finest, - finer, - fine, - config, - info, - warning, - severe, - shout, - off, -} +enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off } class LogMessage { final String message; @@ -43,12 +32,7 @@ class LogMessage { @override int get hashCode { - return message.hashCode ^ - level.hashCode ^ - createdAt.hashCode ^ - logger.hashCode ^ - error.hashCode ^ - stack.hashCode; + return message.hashCode ^ level.hashCode ^ createdAt.hashCode ^ logger.hashCode ^ error.hashCode ^ stack.hashCode; } @override diff --git a/mobile/lib/domain/models/map.model.dart b/mobile/lib/domain/models/map.model.dart new file mode 100644 index 0000000000..ce0834f0cb --- /dev/null +++ b/mobile/lib/domain/models/map.model.dart @@ -0,0 +1,18 @@ +import 'package:maplibre_gl/maplibre_gl.dart'; + +class Marker { + final LatLng location; + final String assetId; + + const Marker({required this.location, required this.assetId}); + + @override + bool operator ==(covariant Marker other) { + if (identical(this, other)) return true; + + return other.location == location && other.assetId == assetId; + } + + @override + int get hashCode => location.hashCode ^ assetId.hashCode; +} diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index 38d1c7ef7d..40117c5ac6 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -13,34 +13,23 @@ enum MemoryTypeEnum { class MemoryData { final int year; - const MemoryData({ - required this.year, - }); + const MemoryData({required this.year}); - MemoryData copyWith({ - int? year, - }) { - return MemoryData( - year: year ?? this.year, - ); + MemoryData copyWith({int? year}) { + return MemoryData(year: year ?? this.year); } Map toMap() { - return { - 'year': year, - }; + return {'year': year}; } factory MemoryData.fromMap(Map map) { - return MemoryData( - year: map['year'] as int, - ); + return MemoryData(year: map['year'] as int); } String toJson() => json.encode(toMap()); - factory MemoryData.fromJson(String source) => - MemoryData.fromMap(json.decode(source) as Map); + factory MemoryData.fromJson(String source) => MemoryData.fromMap(json.decode(source) as Map); @override String toString() => 'MemoryData(year: $year)'; diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index d9eee9ae06..7559720c45 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -55,22 +55,17 @@ class PersonDto { factory PersonDto.fromMap(Map map) { return PersonDto( id: map['id'] as String, - birthDate: map['birthDate'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) - : null, + birthDate: map['birthDate'] != null ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) : null, isHidden: map['isHidden'] as bool, name: map['name'] as String, thumbnailPath: map['thumbnailPath'] as String, - updatedAt: map['updatedAt'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int) - : null, + updatedAt: map['updatedAt'] != null ? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int) : null, ); } String toJson() => json.encode(toMap()); - factory PersonDto.fromJson(String source) => - PersonDto.fromMap(json.decode(source) as Map); + factory PersonDto.fromJson(String source) => PersonDto.fromMap(json.decode(source) as Map); @override bool operator ==(covariant PersonDto other) { @@ -96,54 +91,50 @@ class PersonDto { } // Model for a person stored in the server -class Person { +class DriftPerson { final String id; final DateTime createdAt; final DateTime updatedAt; final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; final DateTime? birthDate; - const Person({ + const DriftPerson({ required this.id, required this.createdAt, required this.updatedAt, required this.ownerId, required this.name, this.faceAssetId, - required this.thumbnailPath, required this.isFavorite, required this.isHidden, required this.color, this.birthDate, }); - Person copyWith({ + DriftPerson copyWith({ String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? name, String? faceAssetId, - String? thumbnailPath, bool? isFavorite, bool? isHidden, String? color, DateTime? birthDate, }) { - return Person( + return DriftPerson( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -160,7 +151,6 @@ class Person { ownerId: $ownerId, name: $name, faceAssetId: ${faceAssetId ?? ""}, - thumbnailPath: $thumbnailPath, isFavorite: $isFavorite, isHidden: $isHidden, color: ${color ?? ""}, @@ -169,7 +159,7 @@ class Person { } @override - bool operator ==(covariant Person other) { + bool operator ==(covariant DriftPerson other) { if (identical(this, other)) return true; return other.id == id && @@ -178,7 +168,6 @@ class Person { other.ownerId == ownerId && other.name == name && other.faceAssetId == faceAssetId && - other.thumbnailPath == thumbnailPath && other.isFavorite == isFavorite && other.isHidden == isHidden && other.color == color && @@ -193,7 +182,6 @@ class Person { ownerId.hashCode ^ name.hashCode ^ faceAssetId.hashCode ^ - thumbnailPath.hashCode ^ isFavorite.hashCode ^ isHidden.hashCode ^ color.hashCode ^ diff --git a/mobile/lib/domain/models/search_result.model.dart b/mobile/lib/domain/models/search_result.model.dart index e8c9429432..947bc6192f 100644 --- a/mobile/lib/domain/models/search_result.model.dart +++ b/mobile/lib/domain/models/search_result.model.dart @@ -3,36 +3,30 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; class SearchResult { final List assets; + final double scrollOffset; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.scrollOffset = 0.0, this.nextPage}); - int get totalAssets => assets.length; - - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { + SearchResult copyWith({List? assets, int? nextPage, double? scrollOffset}) { return SearchResult( assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage, + scrollOffset: scrollOffset ?? this.scrollOffset, ); } @override - String toString() => 'SearchResult(assets: $assets, nextPage: $nextPage)'; + String toString() => 'SearchResult(assets: ${assets.length}, nextPage: $nextPage, scrollOffset: $scrollOffset)'; @override bool operator ==(covariant SearchResult other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.assets, assets) && other.nextPage == nextPage; + return listEquals(other.assets, assets) && other.nextPage == nextPage && other.scrollOffset == scrollOffset; } @override - int get hashCode => assets.hashCode ^ nextPage.hashCode; + int get hashCode => assets.hashCode ^ nextPage.hashCode ^ scrollOffset.hashCode; } diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index 99246c31a1..f427d93285 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -8,7 +8,7 @@ enum Setting { loadOriginalVideo(StoreKey.loadOriginalVideo, false), preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), - ; + enableBackup(StoreKey.enableBackup, false); const Setting(this.storeKey, this.defaultValue); diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index 6a449a1eb8..d5ccf5558d 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -14,13 +14,7 @@ class Stack { required this.primaryAssetId, }); - Stack copyWith({ - String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId, - }) { + Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) { return Stack( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -54,11 +48,7 @@ class Stack { @override int get hashCode { - return id.hashCode ^ - createdAt.hashCode ^ - updatedAt.hashCode ^ - ownerId.hashCode ^ - primaryAssetId.hashCode; + return id.hashCode ^ createdAt.hashCode ^ updatedAt.hashCode ^ ownerId.hashCode ^ primaryAssetId.hashCode; } } @@ -67,19 +57,13 @@ class StackResponse { final String primaryAssetId; final List assetIds; - const StackResponse({ - required this.id, - required this.primaryAssetId, - required this.assetIds, - }); + const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds}); @override bool operator ==(covariant StackResponse other) { if (identical(this, other)) return true; - return other.id == id && - other.primaryAssetId == primaryAssetId && - other.assetIds == assetIds; + return other.id == id && other.primaryAssetId == primaryAssetId && other.assetIds == assetIds; } @override diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 305b3f3387..efccc9bccd 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -67,11 +67,19 @@ enum StoreKey { loadOriginalVideo._(136), manageLocalMediaAndroid._(137), + // Read-only Mode settings + readonlyModeEnabled._(138), + // Experimental stuff photoManagerCustomFilter._(1000), betaPromptShown._(1001), betaTimeline._(1002), - enableBackup._(1003); + enableBackup._(1003), + useWifiForUploadVideos._(1004), + useWifiForUploadPhotos._(1005), + needBetaMigration._(1006), + // TODO: Remove this after patching open-api + shouldResetSync._(1007); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/domain/models/timeline.model.dart b/mobile/lib/domain/models/timeline.model.dart index f3b688b8b8..d4cc5ab5c6 100644 --- a/mobile/lib/domain/models/timeline.model.dart +++ b/mobile/lib/domain/models/timeline.model.dart @@ -1,17 +1,8 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; -enum GroupAssetsBy { - day, - month, - none; -} +enum GroupAssetsBy { day, month, auto, none } -enum HeaderType { - none, - month, - day, - monthAndDay; -} +enum HeaderType { none, month, day, monthAndDay } class Bucket { final int assetCount; @@ -44,3 +35,13 @@ class TimeBucket extends Bucket { class TimelineReloadEvent extends Event { const TimelineReloadEvent(); } + +class ScrollToTopEvent extends Event { + const ScrollToTopEvent(); +} + +class ScrollToDateEvent extends Event { + final DateTime date; + + const ScrollToDateEvent(this.date); +} diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index 6cafd8d149..380295b4b3 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -1,7 +1,36 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; +import 'dart:ui'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +enum AvatarColor { + // do not change this order or reuse indices for other purposes, adding is OK + primary("primary"), + pink("pink"), + red("red"), + yellow("yellow"), + blue("blue"), + green("green"), + purple("purple"), + orange("orange"), + gray("gray"), + amber("amber"); + + final String value; + const AvatarColor(this.value); + + Color toColor({bool isDarkTheme = false}) => switch (this) { + AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; +} // TODO: Rename to User once Isar is removed class UserDto { @@ -9,9 +38,8 @@ class UserDto { final String email; final String name; final bool isAdmin; - final DateTime updatedAt; + final DateTime? updatedAt; - final String? profileImagePath; final AvatarColor avatarColor; final bool memoryEnabled; @@ -25,18 +53,22 @@ class UserDto { bool get hasQuota => quotaSizeInBytes > 0; + final bool hasProfileImage; + final DateTime profileChangedAt; + const UserDto({ required this.id, required this.email, required this.name, - required this.isAdmin, - required this.updatedAt, - this.profileImagePath, + this.isAdmin = false, + this.updatedAt, + required this.profileChangedAt, this.avatarColor = AvatarColor.primary, this.memoryEnabled = true, this.inTimeline = false, this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, + this.hasProfileImage = false, this.quotaUsageInBytes = 0, this.quotaSizeInBytes = 0, }); @@ -49,14 +81,13 @@ email: $email, name: $name, isAdmin: $isAdmin, updatedAt: $updatedAt, -profileImagePath: ${profileImagePath ?? ''}, avatarColor: $avatarColor, memoryEnabled: $memoryEnabled, inTimeline: $inTimeline, isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedWith: $isPartnerSharedWith, -quotaUsageInBytes: $quotaUsageInBytes, -quotaSizeInBytes: $quotaSizeInBytes, +hasProfileImage: $hasProfileImage +profileChangedAt: $profileChangedAt }'''; } @@ -66,48 +97,51 @@ quotaSizeInBytes: $quotaSizeInBytes, String? name, bool? isAdmin, DateTime? updatedAt, - String? profileImagePath, AvatarColor? avatarColor, bool? memoryEnabled, bool? inTimeline, bool? isPartnerSharedBy, bool? isPartnerSharedWith, - int? quotaUsageInBytes, + bool? hasProfileImage, + DateTime? profileChangedAt, int? quotaSizeInBytes, - }) => - UserDto( - id: id ?? this.id, - email: email ?? this.email, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, - avatarColor: avatarColor ?? this.avatarColor, - memoryEnabled: memoryEnabled ?? this.memoryEnabled, - inTimeline: inTimeline ?? this.inTimeline, - isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - ); + int? quotaUsageInBytes, + }) => UserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + updatedAt: updatedAt ?? this.updatedAt, + avatarColor: avatarColor ?? this.avatarColor, + memoryEnabled: memoryEnabled ?? this.memoryEnabled, + inTimeline: inTimeline ?? this.inTimeline, + isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); @override bool operator ==(covariant UserDto other) { if (identical(this, other)) return true; return other.id == id && - other.updatedAt.isAtSameMomentAs(updatedAt) && + ((updatedAt == null && other.updatedAt == null) || + (updatedAt != null && other.updatedAt != null && other.updatedAt!.isAtSameMomentAs(updatedAt!))) && other.avatarColor == avatarColor && other.email == email && other.name == name && other.isPartnerSharedBy == isPartnerSharedBy && other.isPartnerSharedWith == isPartnerSharedWith && - other.profileImagePath == profileImagePath && other.isAdmin == isAdmin && other.memoryEnabled == memoryEnabled && other.inTimeline == inTimeline && - other.quotaUsageInBytes == quotaUsageInBytes && - other.quotaSizeInBytes == quotaSizeInBytes; + other.hasProfileImage == hasProfileImage && + other.profileChangedAt.isAtSameMomentAs(profileChangedAt) && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes; } @override @@ -117,14 +151,15 @@ quotaSizeInBytes: $quotaSizeInBytes, email.hashCode ^ updatedAt.hashCode ^ isAdmin.hashCode ^ - profileImagePath.hashCode ^ avatarColor.hashCode ^ memoryEnabled.hashCode ^ inTimeline.hashCode ^ isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; + hasProfileImage.hashCode ^ + profileChangedAt.hashCode ^ + quotaSizeInBytes.hashCode ^ + quotaUsageInBytes.hashCode; } class PartnerUserDto { @@ -143,13 +178,7 @@ class PartnerUserDto { this.profileImagePath, }); - PartnerUserDto copyWith({ - String? id, - String? email, - String? name, - bool? inTimeline, - String? profileImagePath, - }) { + PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) { return PartnerUserDto( id: id ?? this.id, email: email ?? this.email, @@ -175,16 +204,13 @@ class PartnerUserDto { email: map['email'] as String, name: map['name'] as String, inTimeline: map['inTimeline'] as bool, - profileImagePath: map['profileImagePath'] != null - ? map['profileImagePath'] as String - : null, + profileImagePath: map['profileImagePath'] != null ? map['profileImagePath'] as String : null, ); } String toJson() => json.encode(toMap()); - factory PartnerUserDto.fromJson(String source) => - PartnerUserDto.fromMap(json.decode(source) as Map); + factory PartnerUserDto.fromJson(String source) => PartnerUserDto.fromMap(json.decode(source) as Map); @override String toString() { @@ -204,10 +230,6 @@ class PartnerUserDto { @override int get hashCode { - return id.hashCode ^ - email.hashCode ^ - name.hashCode ^ - inTimeline.hashCode ^ - profileImagePath.hashCode; + return id.hashCode ^ email.hashCode ^ name.hashCode ^ inTimeline.hashCode ^ profileImagePath.hashCode; } } diff --git a/mobile/lib/domain/models/user_metadata.model.dart b/mobile/lib/domain/models/user_metadata.model.dart index c5c63cad5e..af404051a7 100644 --- a/mobile/lib/domain/models/user_metadata.model.dart +++ b/mobile/lib/domain/models/user_metadata.model.dart @@ -1,4 +1,4 @@ -import 'dart:ui'; +import 'package:immich_mobile/domain/models/user.model.dart'; enum UserMetadataKey { // do not change this order! @@ -7,37 +7,6 @@ enum UserMetadataKey { license, } -enum AvatarColor { - // do not change this order or reuse indices for other purposes, adding is OK - primary("primary"), - pink("pink"), - red("red"), - yellow("yellow"), - blue("blue"), - green("green"), - purple("purple"), - orange("orange"), - gray("gray"), - amber("amber"); - - final String value; - const AvatarColor(this.value); - - Color toColor({bool isDarkTheme = false}) => switch (this) { - AvatarColor.primary => - isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), - }; -} - class Onboarding { final bool isOnboarded; @@ -75,7 +44,6 @@ isOnboarded: $isOnboarded, int get hashCode => isOnboarded.hashCode; } -// TODO: wait to be overwritten class Preferences { final bool foldersEnabled; final bool memoriesEnabled; @@ -134,17 +102,17 @@ class Preferences { factory Preferences.fromMap(Map map) { return Preferences( - foldersEnabled: map["folders-Enabled"] as bool? ?? false, - memoriesEnabled: map["memories-Enabled"] as bool? ?? true, - peopleEnabled: map["people-Enabled"] as bool? ?? true, - ratingsEnabled: map["ratings-Enabled"] as bool? ?? false, - sharedLinksEnabled: map["sharedLinks-Enabled"] as bool? ?? true, - tagsEnabled: map["tags-Enabled"] as bool? ?? false, + foldersEnabled: (map["folders"] as Map?)?["enabled"] as bool? ?? false, + memoriesEnabled: (map["memories"] as Map?)?["enabled"] as bool? ?? true, + peopleEnabled: (map["people"] as Map?)?["enabled"] as bool? ?? true, + ratingsEnabled: (map["ratings"] as Map?)?["enabled"] as bool? ?? false, + sharedLinksEnabled: (map["sharedLinks"] as Map?)?["enabled"] as bool? ?? true, + tagsEnabled: (map["tags"] as Map?)?["enabled"] as bool? ?? false, userAvatarColor: AvatarColor.values.firstWhere( - (e) => e.value == map["avatar-Color"] as String?, + (e) => e.value == (map["avatar"] as Map?)?["color"] as String?, orElse: () => AvatarColor.primary, ), - showSupportBadge: map["purchase-ShowSupportBadge"] as bool? ?? true, + showSupportBadge: (map["purchase"] as Map?)?["showSupportBadge"] as bool? ?? true, ); } @@ -194,17 +162,9 @@ class License { final String activationKey; final String licenseKey; - const License({ - required this.activatedAt, - required this.activationKey, - required this.licenseKey, - }); + const License({required this.activatedAt, required this.activationKey, required this.licenseKey}); - License copyWith({ - DateTime? activatedAt, - String? activationKey, - String? licenseKey, - }) { + License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) { return License( activatedAt: activatedAt ?? this.activatedAt, activationKey: activationKey ?? this.activationKey, @@ -222,7 +182,7 @@ class License { factory License.fromMap(Map map) { return License( - activatedAt: map["activatedAt"] as DateTime, + activatedAt: DateTime.parse(map["activatedAt"] as String), activationKey: map["activationKey"] as String, licenseKey: map["licenseKey"] as String, ); @@ -241,14 +201,11 @@ licenseKey: $licenseKey, bool operator ==(covariant License other) { if (identical(this, other)) return true; - return activatedAt == other.activatedAt && - activationKey == other.activationKey && - licenseKey == other.licenseKey; + return activatedAt == other.activatedAt && activationKey == other.activationKey && licenseKey == other.licenseKey; } @override - int get hashCode => - activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode; + int get hashCode => activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode; } // Model for a user metadata stored in the server @@ -259,16 +216,11 @@ class UserMetadata { final Preferences? preferences; final License? license; - const UserMetadata({ - required this.userId, - required this.key, - this.onboarding, - this.preferences, - this.license, - }) : assert( - onboarding != null || preferences != null || license != null, - 'One of onboarding, preferences and license must be provided', - ); + const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license}) + : assert( + onboarding != null || preferences != null || license != null, + 'One of onboarding, preferences and license must be provided', + ); UserMetadata copyWith({ String? userId, @@ -310,10 +262,6 @@ license: ${license ?? ""}, @override int get hashCode { - return userId.hashCode ^ - key.hashCode ^ - onboarding.hashCode ^ - preferences.hashCode ^ - license.hashCode; + return userId.hashCode ^ key.hashCode ^ onboarding.hashCode ^ preferences.hashCode ^ license.hashCode; } } diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 63b1aad8c1..7f8ade313c 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -1,38 +1,51 @@ +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; -import 'package:platform/platform.dart'; class AssetService { final RemoteAssetRepository _remoteAssetRepository; final DriftLocalAssetRepository _localAssetRepository; - final Platform _platform; const AssetService({ required RemoteAssetRepository remoteAssetRepository, required DriftLocalAssetRepository localAssetRepository, - }) : _remoteAssetRepository = remoteAssetRepository, - _localAssetRepository = localAssetRepository, - _platform = const LocalPlatform(); + }) : _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository; + + Future getAsset(BaseAsset asset) { + final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; + return asset is LocalAsset ? _localAssetRepository.get(id) : _remoteAssetRepository.get(id); + } Stream watchAsset(BaseAsset asset) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; - return asset is LocalAsset - ? _localAssetRepository.watchAsset(id) - : _remoteAssetRepository.watchAsset(id); + return asset is LocalAsset ? _localAssetRepository.watch(id) : _remoteAssetRepository.watch(id); + } + + Future> getLocalAssetsByChecksum(String checksum) { + return _localAssetRepository.getByChecksum(checksum); + } + + Future getRemoteAssetByChecksum(String checksum) { + return _remoteAssetRepository.getByChecksum(checksum); + } + + Future getRemoteAsset(String id) { + return _remoteAssetRepository.get(id); } Future> getStack(RemoteAsset asset) async { if (asset.stackId == null) { - return []; + return const []; } - return _remoteAssetRepository.getStackChildren(asset).then((assets) { - // Include the primary asset in the stack as the first item - return [asset, ...assets]; - }); + final stack = await _remoteAssetRepository.getStackChildren(asset); + // Include the primary asset in the stack as the first item + return [asset, ...stack]; } Future getExif(BaseAsset asset) async { @@ -40,8 +53,7 @@ class AssetService { return null; } - final id = - asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id; + final id = asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id; return _remoteAssetRepository.getExif(id); } @@ -56,8 +68,7 @@ class AssetService { width = exif?.width ?? asset.width?.toDouble(); height = exif?.height ?? asset.height?.toDouble(); } else if (asset is LocalAsset) { - isFlipped = _platform.isAndroid && - (asset.orientation == 90 || asset.orientation == 270); + isFlipped = CurrentPlatform.isAndroid && (asset.orientation == 90 || asset.orientation == 270); width = asset.width?.toDouble(); height = asset.height?.toDouble(); } else { @@ -73,7 +84,19 @@ class AssetService { return 1.0; } - Future> getPlaces() { - return _remoteAssetRepository.getPlaces(); + Future> getPlaces(String userId) { + return _remoteAssetRepository.getPlaces(userId); + } + + Future<(int local, int remote)> getAssetCounts() async { + return (await _localAssetRepository.getCount(), await _remoteAssetRepository.getCount()); + } + + Future getLocalHashedCount() { + return _localAssetRepository.getHashedCount(); + } + + Future> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) { + return _localAssetRepository.getSourceAlbums(localAssetId, backupSelection: backupSelection); } } diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart new file mode 100644 index 0000000000..d95b1d4951 --- /dev/null +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -0,0 +1,303 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:cancellation_token_http/http.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/network_capability_extensions.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; +import 'package:immich_mobile/platform/background_worker_api.g.dart'; +import 'package:immich_mobile/platform/background_worker_lock_api.g.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/auth.service.dart'; +import 'package:immich_mobile/services/localization.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:isar/isar.dart'; +import 'package:logging/logging.dart'; +import 'package:worker_manager/worker_manager.dart'; + +class BackgroundWorkerFgService { + final BackgroundWorkerFgHostApi _foregroundHostApi; + + const BackgroundWorkerFgService(this._foregroundHostApi); + + // TODO: Move this call to native side once old timeline is removed + Future enable() => _foregroundHostApi.enable(); + + Future saveNotificationMessage(String title, String body) => + _foregroundHostApi.saveNotificationMessage(title, body); + + Future configure({int? minimumDelaySeconds, bool? requireCharging}) => _foregroundHostApi.configure( + BackgroundWorkerSettings( + minimumDelaySeconds: + minimumDelaySeconds ?? + Store.get(AppSettingsEnum.backupTriggerDelay.storeKey, AppSettingsEnum.backupTriggerDelay.defaultValue), + requiresCharging: + requireCharging ?? + Store.get(AppSettingsEnum.backupRequireCharging.storeKey, AppSettingsEnum.backupRequireCharging.defaultValue), + ), + ); + + Future disable() => _foregroundHostApi.disable(); +} + +class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { + ProviderContainer? _ref; + final Isar _isar; + final Drift _drift; + final DriftLogger _driftLogger; + final BackgroundWorkerBgHostApi _backgroundHostApi; + final CancellationToken _cancellationToken = CancellationToken(); + final Logger _logger = Logger('BackgroundWorkerBgService'); + + bool _isCleanedUp = false; + + BackgroundWorkerBgService({required Isar isar, required Drift drift, required DriftLogger driftLogger}) + : _isar = isar, + _drift = drift, + _driftLogger = driftLogger, + _backgroundHostApi = BackgroundWorkerBgHostApi() { + _ref = ProviderContainer( + overrides: [ + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + driftProvider.overrideWith(driftOverride(drift)), + ], + ); + BackgroundWorkerFlutterApi.setUp(this); + } + + bool get _isBackupEnabled => _ref?.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup) ?? false; + + Future init() async { + try { + HttpSSLOptions.apply(applyNative: false); + + await Future.wait( + [ + loadTranslations(), + workerManager.init(dynamicSpawning: true), + _ref?.read(authServiceProvider).setOpenApiServiceEndpoint(), + // Initialize the file downloader + FileDownloader().configure( + globalConfig: [ + // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 + (Config.holdingQueue, (6, 6, 3)), + // On Android, if files are larger than 256MB, run in foreground service + (Config.runInForegroundIfFileLargerThan, 256), + ], + ), + FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false), + FileDownloader().trackTasks(), + _ref?.read(fileMediaRepositoryProvider).enableBackgroundAccess(), + ].nonNulls, + ); + + configureFileDownloaderNotifications(); + + // Notify the host that the background worker service has been initialized and is ready to use + _backgroundHostApi.onInitialized(); + } catch (error, stack) { + _logger.severe("Failed to initialize background worker", error, stack); + _backgroundHostApi.close(); + } + } + + @override + Future onAndroidUpload() async { + _logger.info('Android background processing started'); + final sw = Stopwatch()..start(); + try { + if (!await _syncAssets(hashTimeout: Duration(minutes: _isBackupEnabled ? 3 : 6))) { + _logger.warning("Remote sync did not complete successfully, skipping backup"); + return; + } + await _handleBackup(); + } catch (error, stack) { + _logger.severe("Failed to complete Android background processing", error, stack); + } finally { + sw.stop(); + _logger.info("Android background processing completed in ${sw.elapsed.inSeconds}s"); + await _cleanup(); + } + } + + @override + Future onIosUpload(bool isRefresh, int? maxSeconds) async { + _logger.info('iOS background upload started with maxSeconds: ${maxSeconds}s'); + final sw = Stopwatch()..start(); + try { + final timeout = isRefresh ? const Duration(seconds: 5) : Duration(minutes: _isBackupEnabled ? 3 : 6); + if (!await _syncAssets(hashTimeout: timeout)) { + _logger.warning("Remote sync did not complete successfully, skipping backup"); + return; + } + + final backupFuture = _handleBackup(); + if (maxSeconds != null) { + await backupFuture.timeout(Duration(seconds: maxSeconds - 1), onTimeout: () {}); + } else { + await backupFuture; + } + } catch (error, stack) { + _logger.severe("Failed to complete iOS background upload", error, stack); + } finally { + sw.stop(); + _logger.info("iOS background upload completed in ${sw.elapsed.inSeconds}s"); + await _cleanup(); + } + } + + @override + Future cancel() async { + _logger.warning("Background worker cancelled"); + try { + await _cleanup(); + } catch (error, stack) { + dPrint(() => 'Failed to cleanup background worker: $error with stack: $stack'); + } + } + + Future _cleanup() async { + // If ref is null, it means the service was never initialized properly + if (_isCleanedUp || _ref == null) { + return; + } + + try { + _isCleanedUp = true; + final backgroundSyncManager = _ref?.read(backgroundSyncProvider); + final nativeSyncApi = _ref?.read(nativeSyncApiProvider); + _ref?.dispose(); + _ref = null; + + _cancellationToken.cancel(); + _logger.info("Cleaning up background worker"); + final cleanupFutures = [ + workerManager.dispose().catchError((_) async { + // Discard any errors on the dispose call + return; + }), + LogService.I.dispose(), + Store.dispose(), + _drift.close(), + _driftLogger.close(), + backgroundSyncManager?.cancel(), + nativeSyncApi?.cancelHashing(), + ]; + + if (_isar.isOpen) { + cleanupFutures.add(_isar.close()); + } + await Future.wait(cleanupFutures.nonNulls); + _logger.info("Background worker resources cleaned up"); + } catch (error, stack) { + dPrint(() => 'Failed to cleanup background worker: $error with stack: $stack'); + } + } + + Future _handleBackup() async { + await runZonedGuarded( + () async { + if (_isCleanedUp) { + return; + } + + if (!_isBackupEnabled) { + _logger.info("Backup is disabled. Skipping backup routine"); + return; + } + + final currentUser = _ref?.read(currentUserProvider); + if (currentUser == null) { + _logger.warning("No current user found. Skipping backup from background"); + return; + } + + if (Platform.isIOS) { + return _ref?.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); + } + + final networkCapabilities = await _ref?.read(connectivityApiProvider).getCapabilities() ?? []; + return _ref + ?.read(uploadServiceProvider) + .startBackupWithHttpClient(currentUser.id, networkCapabilities.hasWifi, _cancellationToken); + }, + (error, stack) { + dPrint(() => "Error in backup zone $error, $stack"); + }, + ); + } + + Future _syncAssets({Duration? hashTimeout}) async { + await _ref?.read(backgroundSyncProvider).syncLocal(); + if (_isCleanedUp) { + return false; + } + + final isSuccess = await _ref?.read(backgroundSyncProvider).syncRemote() ?? false; + if (_isCleanedUp) { + return isSuccess; + } + + var hashFuture = _ref?.read(backgroundSyncProvider).hashAssets(); + if (hashTimeout != null && hashFuture != null) { + hashFuture = hashFuture.timeout( + hashTimeout, + onTimeout: () { + // Consume cancellation errors as we want to continue processing + }, + ); + } + + await hashFuture; + return isSuccess; + } +} + +class BackgroundWorkerLockService { + final BackgroundWorkerLockApi _hostApi; + const BackgroundWorkerLockService(this._hostApi); + + Future lock() async { + if (CurrentPlatform.isAndroid) { + return _hostApi.lock(); + } + } + + Future unlock() async { + if (CurrentPlatform.isAndroid) { + return _hostApi.unlock(); + } + } +} + +/// Native entry invoked from the background worker. If renaming or moving this to a different +/// library, make sure to update the entry points and URI in native workers as well +@pragma('vm:entry-point') +Future backgroundSyncNativeEntrypoint() async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + + final (isar, drift, logDB) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDB, shouldBufferLogs: false, listenStoreUpdates: false); + await BackgroundWorkerBgService(isar: isar, drift: drift, driftLogger: logDB).init(); +} diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index d004041276..90f29b8bc1 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -1,120 +1,126 @@ -import 'dart:convert'; - +import 'package:flutter/services.dart'; import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; +const String _kHashCancelledCode = "HASH_CANCELLED"; + class HashService { - final int batchSizeLimit; - final int batchFileLimit; + final int _batchSize; final DriftLocalAlbumRepository _localAlbumRepository; final DriftLocalAssetRepository _localAssetRepository; - final StorageRepository _storageRepository; final NativeSyncApi _nativeSyncApi; + final bool Function()? _cancelChecker; final _log = Logger('HashService'); HashService({ required DriftLocalAlbumRepository localAlbumRepository, required DriftLocalAssetRepository localAssetRepository, - required StorageRepository storageRepository, required NativeSyncApi nativeSyncApi, - this.batchSizeLimit = kBatchHashSizeLimit, - this.batchFileLimit = kBatchHashFileLimit, - }) : _localAlbumRepository = localAlbumRepository, - _localAssetRepository = localAssetRepository, - _storageRepository = storageRepository, - _nativeSyncApi = nativeSyncApi; + bool Function()? cancelChecker, + int? batchSize, + }) : _localAlbumRepository = localAlbumRepository, + _localAssetRepository = localAssetRepository, + _cancelChecker = cancelChecker, + _nativeSyncApi = nativeSyncApi, + _batchSize = batchSize ?? kBatchHashFileLimit; + + bool get isCancelled => _cancelChecker?.call() ?? false; Future hashAssets() async { + _log.info("Starting hashing of assets"); final Stopwatch stopwatch = Stopwatch()..start(); - // Sorted by backupSelection followed by isCloud - final localAlbums = await _localAlbumRepository.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, - ); + try { + // Sorted by backupSelection followed by isCloud + final localAlbums = await _localAlbumRepository.getBackupAlbums(); - for (final album in localAlbums) { - final assetsToHash = - await _localAlbumRepository.getAssetsToHash(album.id); - if (assetsToHash.isNotEmpty) { - await _hashAssets(assetsToHash); + for (final album in localAlbums) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing albums."); + break; + } + + final assetsToHash = await _localAlbumRepository.getAssetsToHash(album.id); + if (assetsToHash.isNotEmpty) { + await _hashAssets(album, assetsToHash); + } } + } on PlatformException catch (e) { + if (e.code == _kHashCancelledCode) { + _log.warning("Hashing cancelled by platform"); + return; + } + } catch (e, s) { + _log.severe("Error during hashing", e, s); } stopwatch.stop(); _log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Hashing took - ${stopwatch.elapsedMilliseconds}ms"); } /// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB /// with hash for those that were successfully hashed. Hashes are looked up in a table /// [LocalAssetHashEntity] by local id. Only missing entries are newly hashed and added to the DB. - Future _hashAssets(List assetsToHash) async { - int bytesProcessed = 0; - final toHash = <_AssetToPath>[]; + Future _hashAssets(LocalAlbum album, List assetsToHash) async { + final toHash = {}; for (final asset in assetsToHash) { - final file = await _storageRepository.getFileForAsset(asset.id); - if (file == null) { - continue; + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing assets."); + return; } - bytesProcessed += await file.length(); - toHash.add(_AssetToPath(asset: asset, path: file.path)); - - if (toHash.length >= batchFileLimit || bytesProcessed >= batchSizeLimit) { - await _processBatch(toHash); + toHash[asset.id] = asset; + if (toHash.length == _batchSize) { + await _processBatch(album, toHash); toHash.clear(); - bytesProcessed = 0; } } - await _processBatch(toHash); + await _processBatch(album, toHash); } /// Processes a batch of assets. - Future _processBatch(List<_AssetToPath> toHash) async { + Future _processBatch(LocalAlbum album, Map toHash) async { if (toHash.isEmpty) { return; } _log.fine("Hashing ${toHash.length} files"); - final hashed = []; - final hashes = - await _nativeSyncApi.hashPaths(toHash.map((e) => e.path).toList()); + final hashed = {}; + final hashResults = await _nativeSyncApi.hashAssets( + toHash.keys.toList(), + allowNetworkAccess: album.backupSelection == BackupSelection.selected, + ); assert( - hashes.length == toHash.length, - "Hashes length does not match toHash length: ${hashes.length} != ${toHash.length}", + hashResults.length == toHash.length, + "Hashes length does not match toHash length: ${hashResults.length} != ${toHash.length}", ); - for (int i = 0; i < hashes.length; i++) { - final hash = hashes[i]; - final asset = toHash[i].asset; - if (hash?.length == 20) { - hashed.add(asset.copyWith(checksum: base64.encode(hash!))); + for (int i = 0; i < hashResults.length; i++) { + if (isCancelled) { + _log.warning("Hashing cancelled. Stopped processing batch."); + return; + } + + final hashResult = hashResults[i]; + if (hashResult.hash != null) { + hashed[hashResult.assetId] = hashResult.hash!; } else { - _log.warning("Failed to hash file for ${asset.id}"); + final asset = toHash[hashResult.assetId]; + _log.warning( + "Failed to hash asset with id: ${hashResult.assetId}, name: ${asset?.name}, createdAt: ${asset?.createdAt}, from album: ${album.name}. Error: ${hashResult.error ?? "unknown"}", + ); } } _log.fine("Hashed ${hashed.length}/${toHash.length} assets"); - DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); } } - -class _AssetToPath { - final LocalAsset asset; - final String path; - - const _AssetToPath({required this.asset, required this.path}); -} diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 79cc58f3e0..e3d888f063 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -18,4 +18,20 @@ class LocalAlbumService { Future update(LocalAlbum album) { return _repository.upsert(album); } + + Future getCount() { + return _repository.getCount(); + } + + Future unlinkRemoteAlbum(String id) async { + return _repository.unlinkRemoteAlbum(id); + } + + Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async { + return _repository.linkRemoteAlbum(localAlbumId, remoteAlbumId); + } + + Future> getBackupAlbums() { + return _repository.getBackupAlbums(); + } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 8d3b747b37..ca356c80d8 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -1,48 +1,41 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; +import 'package:immich_mobile/utils/datetime_helpers.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; -import 'package:platform/platform.dart'; class LocalSyncService { final DriftLocalAlbumRepository _localAlbumRepository; final NativeSyncApi _nativeSyncApi; - final Platform _platform; final Logger _log = Logger("DeviceSyncService"); - LocalSyncService({ - required DriftLocalAlbumRepository localAlbumRepository, - required NativeSyncApi nativeSyncApi, - Platform? platform, - }) : _localAlbumRepository = localAlbumRepository, - _nativeSyncApi = nativeSyncApi, - _platform = platform ?? const LocalPlatform(); + LocalSyncService({required DriftLocalAlbumRepository localAlbumRepository, required NativeSyncApi nativeSyncApi}) + : _localAlbumRepository = localAlbumRepository, + _nativeSyncApi = nativeSyncApi; Future sync({bool full = false}) async { final Stopwatch stopwatch = Stopwatch()..start(); try { if (full || await _nativeSyncApi.shouldFullSync()) { _log.fine("Full sync request from ${full ? "user" : "native"}"); - DLog.log("Full sync request from ${full ? "user" : "native"}"); return await fullSync(); } final delta = await _nativeSyncApi.getMediaChanges(); if (!delta.hasChanges) { _log.fine("No media changes detected. Skipping sync"); - DLog.log("No media changes detected. Skipping sync"); return; } - DLog.log("Delta updated: ${delta.updates.length}"); - DLog.log("Delta deleted: ${delta.deletes.length}"); + _log.fine("Delta updated: ${delta.updates.length}"); + _log.fine("Delta deleted: ${delta.deletes.length}"); final deviceAlbums = await _nativeSyncApi.getAlbums(); await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums()); @@ -55,25 +48,22 @@ class LocalSyncService { final dbAlbums = await _localAlbumRepository.getAll(); // On Android, we need to sync all albums since it is not possible to // detect album deletions from the native side - if (_platform.isAndroid) { + if (CurrentPlatform.isAndroid) { for (final album in dbAlbums) { final deviceIds = await _nativeSyncApi.getAssetIdsForAlbum(album.id); await _localAlbumRepository.syncDeletes(album.id, deviceIds); } } - if (_platform.isIOS) { + if (CurrentPlatform.isIOS) { // On iOS, we need to full sync albums that are marked as cloud as the delta sync // does not include changes for cloud albums. If ignoreIcloudAssets is enabled, // remove the albums from the local database from the previous sync - final cloudAlbums = - deviceAlbums.where((a) => a.isCloud).toLocalAlbums(); + final cloudAlbums = deviceAlbums.where((a) => a.isCloud).toLocalAlbums(); for (final album in cloudAlbums) { final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id); if (dbAlbum == null) { - _log.warning( - "Cloud album ${album.name} not found in local database. Skipping sync.", - ); + _log.warning("Cloud album ${album.name} not found in local database. Skipping sync."); continue; } await updateAlbum(dbAlbum, album); @@ -86,7 +76,6 @@ class LocalSyncService { } finally { stopwatch.stop(); _log.info("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Device sync took - ${stopwatch.elapsedMilliseconds}ms"); } } @@ -95,8 +84,7 @@ class LocalSyncService { final Stopwatch stopwatch = Stopwatch()..start(); final deviceAlbums = await _nativeSyncApi.getAlbums(); - final dbAlbums = - await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id}); + final dbAlbums = await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id}); await diffSortedLists( dbAlbums, @@ -110,7 +98,6 @@ class LocalSyncService { await _nativeSyncApi.checkpointSync(); stopwatch.stop(); _log.info("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); - DLog.log("Full device sync took - ${stopwatch.elapsedMilliseconds}ms"); } catch (e, s) { _log.severe("Error performing full device sync", e, s); } @@ -120,14 +107,9 @@ class LocalSyncService { try { _log.fine("Adding device album ${album.name}"); - final assets = album.assetCount > 0 - ? await _nativeSyncApi.getAssetsForAlbum(album.id) - : []; + final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : []; - await _localAlbumRepository.upsert( - album, - toUpsert: assets.toLocalAssets(), - ); + await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets()); _log.fine("Successfully added device album ${album.name}"); } catch (e, s) { _log.warning("Error while adding device album", e, s); @@ -150,9 +132,7 @@ class LocalSyncService { _log.fine("Syncing device album ${dbAlbum.name}"); if (_albumsEqual(deviceAlbum, dbAlbum)) { - _log.fine( - "Device album ${dbAlbum.name} has not changed. Skipping sync.", - ); + _log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync."); return false; } @@ -161,7 +141,6 @@ class LocalSyncService { // Faster path - only new assets added if (await checkAddition(dbAlbum, deviceAlbum)) { _log.fine("Fast synced device album ${dbAlbum.name}"); - DLog.log("Fast synced device album ${dbAlbum.name}"); return true; } @@ -176,10 +155,7 @@ class LocalSyncService { @visibleForTesting // The [deviceAlbum] is expected to be refreshed before calling this method // with modified time and asset count - Future checkAddition( - LocalAlbum dbAlbum, - LocalAlbum deviceAlbum, - ) async { + Future checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { _log.fine("Fast syncing device album ${dbAlbum.name}"); // Assets has been modified @@ -188,16 +164,12 @@ class LocalSyncService { return false; } - final updatedTime = - (dbAlbum.updatedAt.millisecondsSinceEpoch ~/ 1000) + 1; - final newAssetsCount = - await _nativeSyncApi.getAssetsCountSince(deviceAlbum.id, updatedTime); + final updatedTime = (dbAlbum.updatedAt.millisecondsSinceEpoch ~/ 1000) + 1; + final newAssetsCount = await _nativeSyncApi.getAssetsCountSince(deviceAlbum.id, updatedTime); // Early return if no new assets were found if (newAssetsCount == 0) { - _log.fine( - "No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}", - ); + _log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}"); return false; } @@ -207,10 +179,7 @@ class LocalSyncService { return false; } - final newAssets = await _nativeSyncApi.getAssetsForAlbum( - deviceAlbum.id, - updatedTimeCond: updatedTime, - ); + final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), @@ -230,18 +199,12 @@ class LocalSyncService { Future fullDiff(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { final assetsInDevice = deviceAlbum.assetCount > 0 - ? await _nativeSyncApi - .getAssetsForAlbum(deviceAlbum.id) - .then((a) => a.toLocalAssets()) - : []; - final assetsInDb = dbAlbum.assetCount > 0 - ? await _localAlbumRepository.getAssets(dbAlbum.id) + ? await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id).then((a) => a.toLocalAssets()) : []; + final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : []; if (deviceAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Removing assets from DB.", - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB."); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), toDelete: assetsInDb.map((a) => a.id), @@ -249,18 +212,11 @@ class LocalSyncService { return true; } - final updatedDeviceAlbum = deviceAlbum.copyWith( - backupSelection: dbAlbum.backupSelection, - ); + final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection); if (dbAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Adding assets to DB.", - ); - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsInDevice, - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB."); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice); return true; } @@ -292,18 +248,12 @@ class LocalSyncService { ); if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { - _log.fine( - "No asset changes detected in album ${deviceAlbum.name}. Updating metadata.", - ); + _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); _localAlbumRepository.upsert(updatedDeviceAlbum); return true; } - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsToUpsert, - toDelete: assetsToDelete, - ); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete); return true; } catch (e, s) { @@ -321,9 +271,7 @@ class LocalSyncService { } bool _albumsEqual(LocalAlbum a, LocalAlbum b) { - return a.name == b.name && - a.assetCount == b.assetCount && - a.updatedAt.isAtSameMomentAs(b.updatedAt); + return a.name == b.name && a.assetCount == b.assetCount && a.updatedAt.isAtSameMomentAs(b.updatedAt); } } @@ -333,9 +281,7 @@ extension on Iterable { (e) => LocalAlbum( id: e.id, name: e.name, - updatedAt: e.updatedAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), + updatedAt: tryFromSecondsSinceEpoch(e.updatedAt, isUtc: true) ?? DateTime.timestamp(), assetCount: e.assetCount, ), ).toList(); @@ -350,16 +296,13 @@ extension on Iterable { name: e.name, checksum: null, type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other, - createdAt: e.createdAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000), - updatedAt: e.updatedAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), + createdAt: tryFromSecondsSinceEpoch(e.createdAt, isUtc: true) ?? DateTime.timestamp(), + updatedAt: tryFromSecondsSinceEpoch(e.updatedAt, isUtc: true) ?? DateTime.timestamp(), width: e.width, height: e.height, durationInSeconds: e.durationInSeconds, orientation: e.orientation, + isFavorite: e.isFavorite, ), ).toList(); } diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index c52665f093..64010b9220 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:logging/logging.dart'; /// Service responsible for handling application logging. @@ -14,8 +14,8 @@ import 'package:logging/logging.dart'; /// writes them to a persistent [ILogRepository], and manages log levels /// via [IStoreRepository] class LogService { - final IsarLogRepository _logRepository; - final IsarStoreRepository _storeRepository; + final LogRepository _logRepository; + final IStoreRepository _storeRepository; final List _msgBuffer = []; @@ -37,8 +37,8 @@ class LogService { } static Future init({ - required IsarLogRepository logRepository, - required IsarStoreRepository storeRepository, + required LogRepository logRepository, + required IStoreRepository storeRepository, bool shouldBuffer = true, }) async { _instance ??= await create( @@ -50,34 +50,28 @@ class LogService { } static Future create({ - required IsarLogRepository logRepository, - required IsarStoreRepository storeRepository, + required LogRepository logRepository, + required IStoreRepository storeRepository, bool shouldBuffer = true, }) async { final instance = LogService._(logRepository, storeRepository, shouldBuffer); await logRepository.truncate(limit: kLogTruncateLimit); - final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ?? - LogLevel.info.index; + final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ?? LogLevel.info.index; Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO; return instance; } - LogService._( - this._logRepository, - this._storeRepository, - this._shouldBuffer, - ) { + LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) { _logSubscription = Logger.root.onRecord.listen(_handleLogRecord); } void _handleLogRecord(LogRecord r) { - if (kDebugMode) { - debugPrint( - '[${r.level.name}] [${r.time}] [${r.loggerName}] ${r.message}' - '${r.error == null ? '' : '\nError: ${r.error}'}' - '${r.stackTrace == null ? '' : '\nStack: ${r.stackTrace}'}', - ); - } + dPrint( + () => + '[${r.level.name}] [${r.time}] [${r.loggerName}] ${r.message}' + '${r.error == null ? '' : '\nError: ${r.error}'}' + '${r.stackTrace == null ? '' : '\nStack: ${r.stackTrace}'}', + ); final record = LogMessage( message: r.message, @@ -90,17 +84,14 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer( - const Duration(seconds: 5), - () => unawaited(flushBuffer()), - ); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(_flushBuffer())); } else { unawaited(_logRepository.insert(record)); } } Future setLogLevel(LogLevel level) async { - await _storeRepository.insert(StoreKey.logLevel, level.index); + await _storeRepository.upsert(StoreKey.logLevel, level.index); Logger.root.level = level.toLevel(); } @@ -116,23 +107,26 @@ class LogService { await _logRepository.deleteAll(); } - void flush() { + Future flush() { _flushTimer?.cancel(); - // TODO: Rename enable this after moving to sqlite - #16504 - // await _flushBufferToDatabase(); + return _flushBuffer(); } Future dispose() { _flushTimer?.cancel(); _logSubscription.cancel(); - return flushBuffer(); + return _flushBuffer(); } - // TOOD: Move this to private once Isar is removed - Future flushBuffer() async { + Future _flushBuffer() async { _flushTimer = null; final buffer = [..._msgBuffer]; _msgBuffer.clear(); + + if (buffer.isEmpty) { + return; + } + await _logRepository.insertAll(buffer); } } @@ -146,9 +140,7 @@ class LoggerUnInitializedException implements Exception { /// Log levels according to dart logging [Level] extension LevelDomainToInfraExtension on Level { - LogLevel toLogLevel() => - LogLevel.values.elementAtOrNull(Level.LEVELS.indexOf(this)) ?? - LogLevel.info; + LogLevel toLogLevel() => LogLevel.values.elementAtOrNull(Level.LEVELS.indexOf(this)) ?? LogLevel.info; } extension on LogLevel { diff --git a/mobile/lib/domain/services/map.service.dart b/mobile/lib/domain/services/map.service.dart new file mode 100644 index 0000000000..8c50a5aaeb --- /dev/null +++ b/mobile/lib/domain/services/map.service.dart @@ -0,0 +1,23 @@ +import 'package:immich_mobile/domain/models/map.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +typedef MapMarkerSource = Future> Function(LatLngBounds? bounds); + +typedef MapQuery = ({MapMarkerSource markerSource}); + +class MapFactory { + final DriftMapRepository _mapRepository; + + const MapFactory({required DriftMapRepository mapRepository}) : _mapRepository = mapRepository; + + MapService remote(String ownerId) => MapService(_mapRepository.remote(ownerId)); +} + +class MapService { + final MapMarkerSource _markerSource; + + MapService(MapQuery query) : _markerSource = query.markerSource; + + Future> Function(LatLngBounds? bounds) get getMarkers => _markerSource; +} diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart index c94b8a9f0a..ead613370f 100644 --- a/mobile/lib/domain/services/memory.service.dart +++ b/mobile/lib/domain/services/memory.service.dart @@ -12,4 +12,12 @@ class DriftMemoryService { Future> getMemoryLane(String ownerId) { return _repository.getAll(ownerId); } + + Future get(String memoryId) { + return _repository.get(memoryId); + } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/partner.service.dart b/mobile/lib/domain/services/partner.service.dart index 065560c4be..ce1bd9557b 100644 --- a/mobile/lib/domain/services/partner.service.dart +++ b/mobile/lib/domain/services/partner.service.dart @@ -1,16 +1,13 @@ -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class DriftPartnerService { final DriftPartnerRepository _driftPartnerRepository; final PartnerApiRepository _partnerApiRepository; - const DriftPartnerService( - this._driftPartnerRepository, - this._partnerApiRepository, - ); + const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository); Future> getSharedWith(String userId) { return _driftPartnerRepository.getSharedWith(userId); @@ -20,13 +17,9 @@ class DriftPartnerService { return _driftPartnerRepository.getSharedBy(userId); } - Future> getAvailablePartners( - String currentUserId, - ) async { - final otherUsers = - await _driftPartnerRepository.getAvailablePartners(currentUserId); - final currentPartners = - await _driftPartnerRepository.getSharedBy(currentUserId); + Future> getAvailablePartners(String currentUserId) async { + final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId); + final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId); final available = otherUsers.where((user) { return !currentPartners.any((partner) => partner.id == user.id); }).toList(); @@ -37,14 +30,11 @@ class DriftPartnerService { Future toggleShowInTimeline(String partnerId, String userId) async { final partner = await _driftPartnerRepository.getPartner(partnerId, userId); if (partner == null) { - debugPrint("Partner not found: $partnerId for user: $userId"); + dPrint(() => "Partner not found: $partnerId for user: $userId"); return; } - await _partnerApiRepository.update( - partnerId, - inTimeline: !partner.inTimeline, - ); + await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline); await _driftPartnerRepository.toggleShowInTimeline(partner, userId); } diff --git a/mobile/lib/domain/services/people.service.dart b/mobile/lib/domain/services/people.service.dart new file mode 100644 index 0000000000..d45f710d7b --- /dev/null +++ b/mobile/lib/domain/services/people.service.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +class DriftPeopleService { + final DriftPeopleRepository _repository; + final PersonApiRepository _personApiRepository; + + const DriftPeopleService(this._repository, this._personApiRepository); + + Future> getAssetPeople(String assetId) { + return _repository.getAssetPeople(assetId); + } + + Future> getAllPeople() { + return _repository.getAllPeople(); + } + + Future updateName(String personId, String name) async { + await _personApiRepository.update(personId, name: name); + return _repository.updateName(personId, name); + } + + Future updateBrithday(String personId, DateTime birthday) async { + await _personApiRepository.update(personId, birthday: birthday); + return _repository.updateBirthday(personId, birthday); + } +} diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index ebb24d5fe5..cc28dfafd5 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -1,12 +1,12 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; -import 'package:immich_mobile/utils/remote_album.utils.dart'; class RemoteAlbumService { final DriftRemoteAlbumRepository _repository; @@ -22,12 +22,29 @@ class RemoteAlbumService { return _repository.getAll(); } - List sortAlbums( + Future get(String albumId) { + return _repository.get(albumId); + } + + Future getByName(String albumName, String ownerId) { + return _repository.getByName(albumName, ownerId); + } + + Future> sortAlbums( List albums, RemoteAlbumSortMode sortMode, { bool isReverse = false, - }) { - return sortMode.sortFn(albums, isReverse); + }) async { + final List sorted = switch (sortMode) { + RemoteAlbumSortMode.created => albums.sortedBy((album) => album.createdAt), + RemoteAlbumSortMode.title => albums.sortedBy((album) => album.name), + RemoteAlbumSortMode.lastModified => albums.sortedBy((album) => album.updatedAt), + RemoteAlbumSortMode.assetCount => albums.sortedBy((album) => album.assetCount), + RemoteAlbumSortMode.mostRecent => await _sortByNewestAsset(albums), + RemoteAlbumSortMode.mostOldest => await _sortByOldestAsset(albums), + }; + + return (isReverse ? sorted.reversed : sorted).toList(); } List searchAlbums( @@ -44,8 +61,7 @@ class RemoteAlbumService { filtered = filtered .where( (album) => - album.name.toLowerCase().contains(lowerQuery) || - album.description.toLowerCase().contains(lowerQuery), + album.name.toLowerCase().contains(lowerQuery) || album.description.toLowerCase().contains(lowerQuery), ) .toList(); } @@ -53,12 +69,10 @@ class RemoteAlbumService { if (userId != null) { switch (filterMode) { case QuickFilterMode.myAlbums: - filtered = - filtered.where((album) => album.ownerId == userId).toList(); + filtered = filtered.where((album) => album.ownerId == userId).toList(); break; case QuickFilterMode.sharedWithMe: - filtered = - filtered.where((album) => album.ownerId != userId).toList(); + filtered = filtered.where((album) => album.ownerId != userId).toList(); break; case QuickFilterMode.all: break; @@ -68,17 +82,8 @@ class RemoteAlbumService { return filtered; } - Future createAlbum({ - required String title, - required List assetIds, - String? description, - }) async { - final album = await _albumApiRepository.createDriftAlbum( - title, - description: description, - assetIds: assetIds, - ); - + Future createAlbum({required String title, required List assetIds, String? description}) async { + final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds); await _repository.create(album, assetIds); return album; @@ -119,14 +124,8 @@ class RemoteAlbumService { return _repository.getAssets(albumId); } - Future addAssets({ - required String albumId, - required List assetIds, - }) async { - final album = await _albumApiRepository.addAssets( - albumId, - assetIds, - ); + Future addAssets({required String albumId, required List assetIds}) async { + final album = await _albumApiRepository.addAssets(albumId, assetIds); await _repository.addAssets(albumId, album.added); @@ -139,12 +138,81 @@ class RemoteAlbumService { await _repository.deleteAlbum(albumId); } - Future addUsers({ - required String albumId, - required List userIds, - }) async { + Future addUsers({required String albumId, required List userIds}) async { await _albumApiRepository.addUsers(albumId, userIds); return _repository.addUsers(albumId, userIds); } + + Future removeUser(String albumId, {required String userId}) async { + await _albumApiRepository.removeUser(albumId, userId: userId); + + return _repository.removeUser(albumId, userId: userId); + } + + Future setActivityStatus(String albumId, bool enabled) async { + await _albumApiRepository.setActivityStatus(albumId, enabled); + + return _repository.setActivityStatus(albumId, enabled); + } + + Future getCount() { + return _repository.getCount(); + } + + Future> _sortByNewestAsset(List albums) async { + // map album IDs to their newest asset dates + final Map> assetTimestampFutures = {}; + for (final album in albums) { + assetTimestampFutures[album.id] = _repository.getNewestAssetTimestamp(album.id); + } + + // await all database queries + final entries = await Future.wait( + assetTimestampFutures.entries.map((entry) async => MapEntry(entry.key, await entry.value)), + ); + final assetTimestamps = Map.fromEntries(entries); + + final sorted = albums.sorted((a, b) { + final aDate = assetTimestamps[a.id] ?? DateTime.fromMillisecondsSinceEpoch(0); + final bDate = assetTimestamps[b.id] ?? DateTime.fromMillisecondsSinceEpoch(0); + return aDate.compareTo(bDate); + }); + + return sorted; + } + + Future> _sortByOldestAsset(List albums) async { + // map album IDs to their oldest asset dates + final Map> assetTimestampFutures = { + for (final album in albums) album.id: _repository.getOldestAssetTimestamp(album.id), + }; + + // await all database queries + final entries = await Future.wait( + assetTimestampFutures.entries.map((entry) async => MapEntry(entry.key, await entry.value)), + ); + final assetTimestamps = Map.fromEntries(entries); + + final sorted = albums.sorted((a, b) { + final aDate = assetTimestamps[a.id] ?? DateTime.fromMillisecondsSinceEpoch(0); + final bDate = assetTimestamps[b.id] ?? DateTime.fromMillisecondsSinceEpoch(0); + return aDate.compareTo(bDate); + }); + + return sorted.reversed.toList(); + } +} + +enum RemoteAlbumSortMode { + title("library_page_sort_title"), + assetCount("library_page_sort_asset_count"), + lastModified("library_page_sort_last_modified"), + created("library_page_sort_created"), + mostRecent("sort_newest"), + mostOldest("sort_oldest"); + + final String key; + + const RemoteAlbumSortMode(this.key); } diff --git a/mobile/lib/domain/services/search.service.dart b/mobile/lib/domain/services/search.service.dart index 052a2ca9da..6ccc5a97bf 100644 --- a/mobile/lib/domain/services/search.service.dart +++ b/mobile/lib/domain/services/search.service.dart @@ -83,10 +83,10 @@ extension on AssetResponseDto { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } diff --git a/mobile/lib/domain/services/setting.service.dart b/mobile/lib/domain/services/setting.service.dart index 8f91e9c66b..99e07a2872 100644 --- a/mobile/lib/domain/services/setting.service.dart +++ b/mobile/lib/domain/services/setting.service.dart @@ -9,16 +9,11 @@ final AppSetting = SettingsService(storeService: StoreService.I); class SettingsService { final StoreService _storeService; - const SettingsService({required StoreService storeService}) - : _storeService = storeService; + const SettingsService({required StoreService storeService}) : _storeService = storeService; - T get(Setting setting) => - _storeService.get(setting.storeKey, setting.defaultValue); + T get(Setting setting) => _storeService.get(setting.storeKey, setting.defaultValue); - Future set(Setting setting, T value) => - _storeService.put(setting.storeKey, value); + Future set(Setting setting, T value) => _storeService.put(setting.storeKey, value); - Stream watch(Setting setting) => _storeService - .watch(setting.storeKey) - .map((v) => v ?? setting.defaultValue); + Stream watch(Setting setting) => _storeService.watch(setting.storeKey).map((v) => v ?? setting.defaultValue); } diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index b1e991b348..f9b4a0aa81 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -6,14 +6,13 @@ import 'package:immich_mobile/infrastructure/repositories/store.repository.dart' /// Provides access to a persistent key-value store with an in-memory cache. /// Listens for repository changes to keep the cache updated. class StoreService { - final IsarStoreRepository _storeRepository; + final IStoreRepository _storeRepository; /// In-memory cache. Keys are [StoreKey.id] final Map _cache = {}; - late final StreamSubscription _storeUpdateSubscription; + StreamSubscription>? _storeUpdateSubscription; - StoreService._({required IsarStoreRepository storeRepository}) - : _storeRepository = storeRepository; + StoreService._({required IStoreRepository isarStoreRepository}) : _storeRepository = isarStoreRepository; // TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider static StoreService? _instance; @@ -25,37 +24,36 @@ class StoreService { } // TODO: Replace the implementation with the one from create after removing the typedef - static Future init({ - required IsarStoreRepository storeRepository, - }) async { - _instance ??= await create(storeRepository: storeRepository); + static Future init({required IStoreRepository storeRepository, bool listenUpdates = true}) async { + _instance ??= await create(storeRepository: storeRepository, listenUpdates: listenUpdates); return _instance!; } - static Future create({ - required IsarStoreRepository storeRepository, - }) async { - final instance = StoreService._(storeRepository: storeRepository); - await instance._populateCache(); - instance._storeUpdateSubscription = instance._listenForChange(); + static Future create({required IStoreRepository storeRepository, bool listenUpdates = true}) async { + final instance = StoreService._(isarStoreRepository: storeRepository); + await instance.populateCache(); + if (listenUpdates) { + instance._storeUpdateSubscription = instance._listenForChange(); + } return instance; } - Future _populateCache() async { + Future populateCache() async { final storeValues = await _storeRepository.getAll(); for (StoreDto storeValue in storeValues) { _cache[storeValue.key.id] = storeValue.value; } } - StreamSubscription _listenForChange() => - _storeRepository.watchAll().listen((event) { - _cache[event.key.id] = event.value; - }); + StreamSubscription> _listenForChange() => _storeRepository.watchAll().listen((events) { + for (final event in events) { + _cache[event.key.id] = event.value; + } + }); /// Disposes the store and cancels the subscription. To reuse the store call init() again - void dispose() async { - await _storeUpdateSubscription.cancel(); + Future dispose() async { + await _storeUpdateSubscription?.cancel(); _cache.clear(); } @@ -75,7 +73,7 @@ class StoreService { /// Stores the [value] for the [key]. Skips write if value hasn't changed. Future put, T>(U key, T value) async { if (_cache[key.id] == value) return; - await _storeRepository.insert(key, value); + await _storeRepository.upsert(key, value); _cache[key.id] = value; } @@ -94,7 +92,7 @@ class StoreService { _cache.clear(); } - bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? false; + bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? true; } class StoreKeyNotFoundException implements Exception { diff --git a/mobile/lib/domain/services/sync_linked_album.service.dart b/mobile/lib/domain/services/sync_linked_album.service.dart new file mode 100644 index 0000000000..b61ca1c965 --- /dev/null +++ b/mobile/lib/domain/services/sync_linked_album.service.dart @@ -0,0 +1,110 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:logging/logging.dart'; + +final syncLinkedAlbumServiceProvider = Provider( + (ref) => SyncLinkedAlbumService( + ref.watch(localAlbumRepository), + ref.watch(remoteAlbumRepository), + ref.watch(driftAlbumApiRepositoryProvider), + ), +); + +class SyncLinkedAlbumService { + final DriftLocalAlbumRepository _localAlbumRepository; + final DriftRemoteAlbumRepository _remoteAlbumRepository; + final DriftAlbumApiRepository _albumApiRepository; + + SyncLinkedAlbumService(this._localAlbumRepository, this._remoteAlbumRepository, this._albumApiRepository); + + final _log = Logger("SyncLinkedAlbumService"); + + Future syncLinkedAlbums(String userId) async { + final selectedAlbums = await _localAlbumRepository.getBackupAlbums(); + + await Future.wait( + selectedAlbums.map((localAlbum) async { + final linkedRemoteAlbumId = localAlbum.linkedRemoteAlbumId; + if (linkedRemoteAlbumId == null) { + _log.warning("No linked remote album ID found for local album: ${localAlbum.name}"); + return; + } + + final remoteAlbum = await _remoteAlbumRepository.get(linkedRemoteAlbumId); + if (remoteAlbum == null) { + _log.warning("Linked remote album not found for ID: $linkedRemoteAlbumId"); + return; + } + + // get assets that are uploaded but not in the remote album + final assetIds = await _remoteAlbumRepository.getLinkedAssetIds(userId, localAlbum.id, linkedRemoteAlbumId); + _log.fine("Syncing ${assetIds.length} assets to remote album: ${remoteAlbum.name}"); + if (assetIds.isNotEmpty) { + final album = await _albumApiRepository.addAssets(remoteAlbum.id, assetIds); + await _remoteAlbumRepository.addAssets(remoteAlbum.id, album.added); + } + }), + ); + } + + Future manageLinkedAlbums(List localAlbums, String ownerId) async { + try { + for (final album in localAlbums) { + await _processLocalAlbum(album, ownerId); + } + } catch (error, stackTrace) { + _log.severe("Error managing linked albums", error, stackTrace); + } + } + + /// Processes a single local album to ensure proper linking with remote albums + Future _processLocalAlbum(LocalAlbum localAlbum, String ownerId) { + final hasLinkedRemoteAlbum = localAlbum.linkedRemoteAlbumId != null; + + if (hasLinkedRemoteAlbum) { + return _handleLinkedAlbum(localAlbum); + } else { + return _handleUnlinkedAlbum(localAlbum, ownerId); + } + } + + /// Handles albums that are already linked to a remote album + Future _handleLinkedAlbum(LocalAlbum localAlbum) async { + final remoteAlbumId = localAlbum.linkedRemoteAlbumId!; + final remoteAlbum = await _remoteAlbumRepository.get(remoteAlbumId); + + final remoteAlbumExists = remoteAlbum != null; + if (!remoteAlbumExists) { + return _localAlbumRepository.unlinkRemoteAlbum(localAlbum.id); + } + } + + /// Handles albums that are not linked to any remote album + Future _handleUnlinkedAlbum(LocalAlbum localAlbum, String ownerId) async { + final existingRemoteAlbum = await _remoteAlbumRepository.getByName(localAlbum.name, ownerId); + + if (existingRemoteAlbum != null) { + return _linkToExistingRemoteAlbum(localAlbum, existingRemoteAlbum); + } else { + return _createAndLinkNewRemoteAlbum(localAlbum); + } + } + + /// Links a local album to an existing remote album + Future _linkToExistingRemoteAlbum(LocalAlbum localAlbum, dynamic existingRemoteAlbum) { + return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, existingRemoteAlbum.id); + } + + /// Creates a new remote album and links it to the local album + Future _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async { + dPrint(() => "Creating new remote album for local album: ${localAlbum.name}"); + final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, assetIds: []); + await _remoteAlbumRepository.create(newRemoteAlbum, []); + return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id); + } +} diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 9a7d91ced9..bec7e6afda 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -18,25 +17,164 @@ class SyncStreamService { required SyncApiRepository syncApiRepository, required SyncStreamRepository syncStreamRepository, bool Function()? cancelChecker, - }) : _syncApiRepository = syncApiRepository, - _syncStreamRepository = syncStreamRepository, - _cancelChecker = cancelChecker; + }) : _syncApiRepository = syncApiRepository, + _syncStreamRepository = syncStreamRepository, + _cancelChecker = cancelChecker; bool get isCancelled => _cancelChecker?.call() ?? false; - Future sync() { - _logger.info("Remote sync request for userr"); - DLog.log("Remote sync request for user"); + Future sync() async { + _logger.info("Remote sync request for user"); // Start the sync stream and handle events - return _syncApiRepository.streamChanges(_handleEvents); + bool shouldReset = false; + await _syncApiRepository.streamChanges(_handleEvents, onReset: () => shouldReset = true); + if (shouldReset) { + _logger.info("Resetting sync state as requested by server"); + await _syncApiRepository.streamChanges(_handleEvents); + } + return true; + } + + Future _handleEvents(List events, Function() abort, Function() reset) async { + List items = []; + for (final event in events) { + if (isCancelled) { + _logger.warning("Sync stream cancelled"); + abort(); + return; + } + + if (event.type != items.firstOrNull?.type) { + await _processBatch(items); + } + + if (event.type == SyncEntityType.syncResetV1) { + reset(); + } + + items.add(event); + } + + await _processBatch(items); + } + + Future _processBatch(List batch) async { + if (batch.isEmpty) { + return; + } + + final type = batch.first.type; + await _handleSyncData(type, batch.map((e) => e.data)); + await _syncApiRepository.ack([batch.last.ack]); + batch.clear(); + } + + Future _handleSyncData(SyncEntityType type, Iterable data) async { + _logger.fine("Processing sync data for $type of length ${data.length}"); + switch (type) { + case SyncEntityType.authUserV1: + return _syncStreamRepository.updateAuthUsersV1(data.cast()); + case SyncEntityType.userV1: + return _syncStreamRepository.updateUsersV1(data.cast()); + case SyncEntityType.userDeleteV1: + return _syncStreamRepository.deleteUsersV1(data.cast()); + case SyncEntityType.partnerV1: + return _syncStreamRepository.updatePartnerV1(data.cast()); + case SyncEntityType.partnerDeleteV1: + return _syncStreamRepository.deletePartnerV1(data.cast()); + case SyncEntityType.assetV1: + return _syncStreamRepository.updateAssetsV1(data.cast()); + case SyncEntityType.assetDeleteV1: + return _syncStreamRepository.deleteAssetsV1(data.cast()); + case SyncEntityType.assetExifV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast()); + case SyncEntityType.partnerAssetV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner'); + case SyncEntityType.partnerAssetBackfillV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill'); + case SyncEntityType.partnerAssetDeleteV1: + return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner"); + case SyncEntityType.partnerAssetExifV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner'); + case SyncEntityType.partnerAssetExifBackfillV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill'); + case SyncEntityType.albumV1: + return _syncStreamRepository.updateAlbumsV1(data.cast()); + case SyncEntityType.albumDeleteV1: + return _syncStreamRepository.deleteAlbumsV1(data.cast()); + case SyncEntityType.albumUserV1: + return _syncStreamRepository.updateAlbumUsersV1(data.cast()); + case SyncEntityType.albumUserBackfillV1: + return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill'); + case SyncEntityType.albumUserDeleteV1: + return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); + case SyncEntityType.albumAssetCreateV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset create'); + case SyncEntityType.albumAssetUpdateV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset update'); + case SyncEntityType.albumAssetBackfillV1: + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album asset backfill'); + case SyncEntityType.albumAssetExifCreateV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif create'); + case SyncEntityType.albumAssetExifUpdateV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif update'); + case SyncEntityType.albumAssetExifBackfillV1: + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album asset exif backfill'); + case SyncEntityType.albumToAssetV1: + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); + case SyncEntityType.albumToAssetBackfillV1: + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill'); + case SyncEntityType.albumToAssetDeleteV1: + return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); + // No-op. SyncAckV1 entities are checkpoints in the sync stream + // to acknowledge that the client has processed all the backfill events + case SyncEntityType.syncAckV1: + return; + // No-op. SyncCompleteV1 is used to signal the completion of the sync process + case SyncEntityType.syncCompleteV1: + return; + // Request to reset the client state. Clear everything related to remote entities + case SyncEntityType.syncResetV1: + return _syncStreamRepository.reset(); + case SyncEntityType.memoryV1: + return _syncStreamRepository.updateMemoriesV1(data.cast()); + case SyncEntityType.memoryDeleteV1: + return _syncStreamRepository.deleteMemoriesV1(data.cast()); + case SyncEntityType.memoryToAssetV1: + return _syncStreamRepository.updateMemoryAssetsV1(data.cast()); + case SyncEntityType.memoryToAssetDeleteV1: + return _syncStreamRepository.deleteMemoryAssetsV1(data.cast()); + case SyncEntityType.stackV1: + return _syncStreamRepository.updateStacksV1(data.cast()); + case SyncEntityType.stackDeleteV1: + return _syncStreamRepository.deleteStacksV1(data.cast()); + case SyncEntityType.partnerStackV1: + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner'); + case SyncEntityType.partnerStackBackfillV1: + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill'); + case SyncEntityType.partnerStackDeleteV1: + return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner'); + case SyncEntityType.userMetadataV1: + return _syncStreamRepository.updateUserMetadatasV1(data.cast()); + case SyncEntityType.userMetadataDeleteV1: + return _syncStreamRepository.deleteUserMetadatasV1(data.cast()); + case SyncEntityType.personV1: + return _syncStreamRepository.updatePeopleV1(data.cast()); + case SyncEntityType.personDeleteV1: + return _syncStreamRepository.deletePeopleV1(data.cast()); + case SyncEntityType.assetFaceV1: + return _syncStreamRepository.updateAssetFacesV1(data.cast()); + case SyncEntityType.assetFaceDeleteV1: + return _syncStreamRepository.deleteAssetFacesV1(data.cast()); + default: + _logger.warning("Unknown sync data type: $type"); + } } Future handleWsAssetUploadReadyV1Batch(List batchData) async { if (batchData.isEmpty) return; - _logger.info( - 'Processing batch of ${batchData.length} AssetUploadReadyV1 events', - ); + _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events'); final List assets = []; final List exifs = []; @@ -65,187 +203,12 @@ class SyncStreamService { } if (assets.isNotEmpty && exifs.isNotEmpty) { - await _syncStreamRepository.updateAssetsV1( - assets, - debugLabel: 'websocket-batch', - ); - await _syncStreamRepository.updateAssetsExifV1( - exifs, - debugLabel: 'websocket-batch', - ); + await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch'); + await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch'); _logger.info('Successfully processed ${assets.length} assets in batch'); } } catch (error, stackTrace) { - _logger.severe( - "Error processing AssetUploadReadyV1 websocket batch events", - error, - stackTrace, - ); - } - } - - Future _handleEvents(List events, Function() abort) async { - List items = []; - for (final event in events) { - if (isCancelled) { - _logger.warning("Sync stream cancelled"); - abort(); - return; - } - - if (event.type != items.firstOrNull?.type) { - await _processBatch(items); - } - - items.add(event); - } - - await _processBatch(items); - } - - Future _processBatch(List batch) async { - if (batch.isEmpty) { - return; - } - - final type = batch.first.type; - await _handleSyncData(type, batch.map((e) => e.data)); - await _syncApiRepository.ack([batch.last.ack]); - batch.clear(); - } - - Future _handleSyncData( - SyncEntityType type, - Iterable data, - ) async { - _logger.fine("Processing sync data for $type of length ${data.length}"); - switch (type) { - case SyncEntityType.userV1: - return _syncStreamRepository.updateUsersV1(data.cast()); - case SyncEntityType.userDeleteV1: - return _syncStreamRepository.deleteUsersV1(data.cast()); - case SyncEntityType.partnerV1: - return _syncStreamRepository.updatePartnerV1(data.cast()); - case SyncEntityType.partnerDeleteV1: - return _syncStreamRepository.deletePartnerV1(data.cast()); - case SyncEntityType.assetV1: - return _syncStreamRepository.updateAssetsV1(data.cast()); - case SyncEntityType.assetDeleteV1: - return _syncStreamRepository.deleteAssetsV1(data.cast()); - case SyncEntityType.assetExifV1: - return _syncStreamRepository.updateAssetsExifV1(data.cast()); - case SyncEntityType.partnerAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner', - ); - case SyncEntityType.partnerAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner backfill', - ); - case SyncEntityType.partnerAssetDeleteV1: - return _syncStreamRepository.deleteAssetsV1( - data.cast(), - debugLabel: "partner", - ); - case SyncEntityType.partnerAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner', - ); - case SyncEntityType.partnerAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner backfill', - ); - case SyncEntityType.albumV1: - return _syncStreamRepository.updateAlbumsV1(data.cast()); - case SyncEntityType.albumDeleteV1: - return _syncStreamRepository.deleteAlbumsV1(data.cast()); - case SyncEntityType.albumUserV1: - return _syncStreamRepository.updateAlbumUsersV1(data.cast()); - case SyncEntityType.albumUserBackfillV1: - return _syncStreamRepository.updateAlbumUsersV1( - data.cast(), - debugLabel: 'backfill', - ); - case SyncEntityType.albumUserDeleteV1: - return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); - case SyncEntityType.albumAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album', - ); - case SyncEntityType.albumAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album backfill', - ); - case SyncEntityType.albumAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album', - ); - case SyncEntityType.albumAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album backfill', - ); - case SyncEntityType.albumToAssetV1: - return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); - case SyncEntityType.albumToAssetBackfillV1: - return _syncStreamRepository.updateAlbumToAssetsV1( - data.cast(), - debugLabel: 'backfill', - ); - case SyncEntityType.albumToAssetDeleteV1: - return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); - // No-op. SyncAckV1 entities are checkpoints in the sync stream - // to acknowledge that the client has processed all the backfill events - case SyncEntityType.syncAckV1: - return; - case SyncEntityType.memoryV1: - return _syncStreamRepository.updateMemoriesV1(data.cast()); - case SyncEntityType.memoryDeleteV1: - return _syncStreamRepository.deleteMemoriesV1(data.cast()); - case SyncEntityType.memoryToAssetV1: - return _syncStreamRepository.updateMemoryAssetsV1(data.cast()); - case SyncEntityType.memoryToAssetDeleteV1: - return _syncStreamRepository.deleteMemoryAssetsV1(data.cast()); - case SyncEntityType.stackV1: - return _syncStreamRepository.updateStacksV1(data.cast()); - case SyncEntityType.stackDeleteV1: - return _syncStreamRepository.deleteStacksV1(data.cast()); - case SyncEntityType.partnerStackV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner', - ); - case SyncEntityType.partnerStackBackfillV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner backfill', - ); - case SyncEntityType.partnerStackDeleteV1: - return _syncStreamRepository.deleteStacksV1( - data.cast(), - debugLabel: 'partner', - ); - case SyncEntityType.userMetadataV1: - return _syncStreamRepository.updateUserMetadatasV1( - data.cast(), - ); - case SyncEntityType.userMetadataDeleteV1: - return _syncStreamRepository.deleteUserMetadatasV1( - data.cast(), - ); - case SyncEntityType.personV1: - return _syncStreamRepository.updatePeopleV1(data.cast()); - case SyncEntityType.personDeleteV1: - return _syncStreamRepository.deletePeopleV1(data.cast()); - default: - _logger.warning("Unknown sync data type: $type"); + _logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace); } } } diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 0d31f06e74..6a7a1a22b2 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -10,34 +10,29 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; -typedef TimelineAssetSource = Future> Function( - int index, - int count, -); +typedef TimelineAssetSource = Future> Function(int index, int count); typedef TimelineBucketSource = Stream> Function(); -typedef TimelineQuery = ({ - TimelineAssetSource assetSource, - TimelineBucketSource bucketSource, -}); +typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource}); class TimelineFactory { final DriftTimelineRepository _timelineRepository; final SettingsService _settingsService; - const TimelineFactory({ - required DriftTimelineRepository timelineRepository, - required SettingsService settingsService, - }) : _timelineRepository = timelineRepository, - _settingsService = settingsService; + const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService}) + : _timelineRepository = timelineRepository, + _settingsService = settingsService; - GroupAssetsBy get groupBy => - GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + GroupAssetsBy get groupBy { + final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + // We do not support auto grouping in the new timeline yet, fallback to day grouping + return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group; + } - TimelineService main(List timelineUsers) => - TimelineService(_timelineRepository.main(timelineUsers, groupBy)); + TimelineService main(List timelineUsers) => TimelineService(_timelineRepository.main(timelineUsers, groupBy)); TimelineService localAlbum({required String albumId}) => TimelineService(_timelineRepository.localAlbum(albumId, groupBy)); @@ -45,29 +40,27 @@ class TimelineFactory { TimelineService remoteAlbum({required String albumId}) => TimelineService(_timelineRepository.remoteAlbum(albumId, groupBy)); - TimelineService remoteAssets(String userId) => - TimelineService(_timelineRepository.remote(userId, groupBy)); + TimelineService remoteAssets(String userId) => TimelineService(_timelineRepository.remote(userId, groupBy)); - TimelineService favorite(String userId) => - TimelineService(_timelineRepository.favorite(userId, groupBy)); + TimelineService favorite(String userId) => TimelineService(_timelineRepository.favorite(userId, groupBy)); - TimelineService trash(String userId) => - TimelineService(_timelineRepository.trash(userId, groupBy)); + TimelineService trash(String userId) => TimelineService(_timelineRepository.trash(userId, groupBy)); - TimelineService archive(String userId) => - TimelineService(_timelineRepository.archived(userId, groupBy)); + TimelineService archive(String userId) => TimelineService(_timelineRepository.archived(userId, groupBy)); - TimelineService lockedFolder(String userId) => - TimelineService(_timelineRepository.locked(userId, groupBy)); + TimelineService lockedFolder(String userId) => TimelineService(_timelineRepository.locked(userId, groupBy)); - TimelineService video(String userId) => - TimelineService(_timelineRepository.video(userId, groupBy)); + TimelineService video(String userId) => TimelineService(_timelineRepository.video(userId, groupBy)); - TimelineService place(String place) => - TimelineService(_timelineRepository.place(place, groupBy)); + TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy)); - TimelineService fromAssets(List assets) => - TimelineService(_timelineRepository.fromAssets(assets)); + TimelineService person(String userId, String personId) => + TimelineService(_timelineRepository.person(userId, personId, groupBy)); + + TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); + + TimelineService map(String userId, LatLngBounds bounds) => + TimelineService(_timelineRepository.map(userId, bounds, groupBy)); } class TimelineService { @@ -81,21 +74,14 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService(TimelineQuery query) - : this._( - assetSource: query.assetSource, - bucketSource: query.bucketSource, - ); + TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource); - TimelineService._({ - required TimelineAssetSource assetSource, - required TimelineBucketSource bucketSource, - }) : _assetSource = assetSource, - _bucketSource = bucketSource { + TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource}) + : _assetSource = assetSource, + _bucketSource = bucketSource { _bucketSubscription = _bucketSource().listen((buckets) { _mutex.run(() async { - final totalAssets = - buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); + final totalAssets = buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); if (totalAssets == 0) { _bufferOffset = 0; @@ -110,10 +96,7 @@ class TimelineService { count = kTimelineAssetLoadBatchSize; } else { offset = _bufferOffset; - count = math.min( - _buffer.length, - totalAssets - _bufferOffset, - ); + count = math.min(_buffer.length, totalAssets - _bufferOffset); } _buffer = await _assetSource(offset, count); _bufferOffset = offset; @@ -128,8 +111,7 @@ class TimelineService { Stream> Function() get watchBuckets => _bucketSource; - Future> loadAssets(int index, int count) => - _mutex.run(() => _loadAssets(index, count)); + Future> loadAssets(int index, int count) => _mutex.run(() => _loadAssets(index, count)); Future> _loadAssets(int index, int count) async { if (hasRange(index, count)) { @@ -142,10 +124,7 @@ class TimelineService { // make sure to load a meaningful amount of data (and not only the requested slice) // otherwise, each call to [loadAssets] would result in DB call trashing performance // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests - final len = math.max( - kTimelineAssetLoadBatchSize, - count + kTimelineAssetLoadOppositeSize, - ); + final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize); // when scrolling forward, start shortly before the requested offset // when scrolling backward, end shortly after the requested offset to guard against the user scrolling // in the other direction a tiny bit resulting in another required load from the DB @@ -178,11 +157,9 @@ class TimelineService { } // Pre-cache assets around the given index for asset viewer - Future preCacheAssets(int index) => - _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index))); + Future preCacheAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index))); - BaseAsset getRandomAsset() => - _buffer.elementAt(math.Random().nextInt(_buffer.length)); + BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length)); BaseAsset getAsset(int index) { if (!hasRange(index, 1)) { @@ -193,10 +170,40 @@ class TimelineService { return _buffer.elementAt(index - _bufferOffset); } + /// Gets an asset at the given index, automatically loading the buffer if needed. + /// This is an async version that can handle out-of-range indices by loading the appropriate buffer. + Future getAssetAsync(int index) async { + if (index < 0 || index >= _totalAssets) { + return null; + } + + if (hasRange(index, 1)) { + return _buffer.elementAt(index - _bufferOffset); + } + + // Load the buffer containing the requested index + try { + final assets = await loadAssets(index, 1); + return assets.isNotEmpty ? assets.first : null; + } catch (e) { + return null; + } + } + + /// Safely gets an asset at the given index without throwing a RangeError. + /// Returns null if the index is out of bounds or not currently in the buffer. + /// For automatic buffer loading, use getAssetAsync instead. + BaseAsset? getAssetSafe(int index) { + if (index < 0 || index >= _totalAssets || !hasRange(index, 1)) { + return null; + } + return _buffer.elementAt(index - _bufferOffset); + } + Future dispose() async { await _bucketSubscription?.cancel(); _bucketSubscription = null; - _buffer.clear(); + _buffer = []; _bufferOffset = 0; } } diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 14fed9fb93..d347d8aa4f 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -18,9 +18,9 @@ class UserService { required IsarUserRepository isarUserRepository, required UserApiRepository userApiRepository, required StoreService storeService, - }) : _isarUserRepository = isarUserRepository, - _userApiRepository = userApiRepository, - _storeService = storeService; + }) : _isarUserRepository = isarUserRepository, + _userApiRepository = userApiRepository, + _storeService = storeService; UserDto getMyUser() { return _storeService.get(StoreKey.currentUser); @@ -44,11 +44,8 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { - final path = await _userApiRepository.createProfileImage( - name: name, - data: image, - ); - final updatedUser = getMyUser().copyWith(profileImagePath: path); + final path = await _userApiRepository.createProfileImage(name: name, data: image); + final updatedUser = getMyUser(); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); return path; diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index 4a44c4d8f2..38e249b9f1 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -1,29 +1,46 @@ import 'dart:async'; +import 'package:immich_mobile/domain/utils/sync_linked_album.dart'; import 'package:immich_mobile/providers/infrastructure/sync.provider.dart'; import 'package:immich_mobile/utils/isolate.dart'; import 'package:worker_manager/worker_manager.dart'; typedef SyncCallback = void Function(); +typedef SyncCallbackWithResult = void Function(T result); typedef SyncErrorCallback = void Function(String error); class BackgroundSyncManager { final SyncCallback? onRemoteSyncStart; - final SyncCallback? onRemoteSyncComplete; + final SyncCallbackWithResult? onRemoteSyncComplete; final SyncErrorCallback? onRemoteSyncError; - Cancelable? _syncTask; + final SyncCallback? onLocalSyncStart; + final SyncCallback? onLocalSyncComplete; + final SyncErrorCallback? onLocalSyncError; + + final SyncCallback? onHashingStart; + final SyncCallback? onHashingComplete; + final SyncErrorCallback? onHashingError; + + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; + Cancelable? _linkedAlbumSyncTask; Cancelable? _hashTask; BackgroundSyncManager({ this.onRemoteSyncStart, this.onRemoteSyncComplete, this.onRemoteSyncError, + this.onLocalSyncStart, + this.onLocalSyncComplete, + this.onLocalSyncError, + this.onHashingStart, + this.onHashingComplete, + this.onHashingError, }); - Future cancel() { + Future cancel() async { final futures = []; if (_syncTask != null) { @@ -38,7 +55,39 @@ class BackgroundSyncManager { _syncWebsocketTask?.cancel(); _syncWebsocketTask = null; - return Future.wait(futures); + if (_linkedAlbumSyncTask != null) { + futures.add(_linkedAlbumSyncTask!.future); + } + _linkedAlbumSyncTask?.cancel(); + _linkedAlbumSyncTask = null; + + try { + await Future.wait(futures); + } on CanceledError { + // Ignore cancellation errors + } + } + + Future cancelLocal() async { + final futures = []; + + if (_hashTask != null) { + futures.add(_hashTask!.future); + } + _hashTask?.cancel(); + _hashTask = null; + + if (_deviceAlbumSyncTask != null) { + futures.add(_deviceAlbumSyncTask!.future); + } + _deviceAlbumSyncTask?.cancel(); + _deviceAlbumSyncTask = null; + + try { + await Future.wait(futures); + } on CanceledError { + // Ignore cancellation errors + } } // No need to cancel the task, as it can also be run when the user logs out @@ -47,54 +96,80 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.future; } + onLocalSyncStart?.call(); + // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full ? runInIsolateGentle( - computation: (ref) => - ref.read(localSyncServiceProvider).sync(full: true), + computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true), + debugLabel: 'local-sync-full-true', ) : runInIsolateGentle( - computation: (ref) => - ref.read(localSyncServiceProvider).sync(full: false), + computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false), + debugLabel: 'local-sync-full-false', ); - return _deviceAlbumSyncTask!.whenComplete(() { - _deviceAlbumSyncTask = null; - }); + return _deviceAlbumSyncTask! + .whenComplete(() { + _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }) + .catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; + }); } -// No need to cancel the task, as it can also be run when the user logs out + // No need to cancel the task, as it can also be run when the user logs out Future hashAssets() { if (_hashTask != null) { return _hashTask!.future; } + onHashingStart?.call(); + _hashTask = runInIsolateGentle( computation: (ref) => ref.read(hashServiceProvider).hashAssets(), + debugLabel: 'hash-assets', ); - return _hashTask!.whenComplete(() { - _hashTask = null; - }); + + return _hashTask! + .whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }) + .catchError((error) { + onHashingError?.call(error.toString()); + _hashTask = null; + }); } - Future syncRemote() { + Future syncRemote() { if (_syncTask != null) { - return _syncTask!.future; + return _syncTask!.future.then((result) => result ?? false).catchError((_) => false); } onRemoteSyncStart?.call(); _syncTask = runInIsolateGentle( computation: (ref) => ref.read(syncStreamServiceProvider).sync(), + debugLabel: 'remote-sync', ); - return _syncTask!.whenComplete(() { - onRemoteSyncComplete?.call(); - _syncTask = null; - }).catchError((error) { - onRemoteSyncError?.call(error.toString()); - _syncTask = null; - }); + return _syncTask! + .then((result) { + final success = result ?? false; + onRemoteSyncComplete?.call(success); + return success; + }) + .catchError((error) { + onRemoteSyncError?.call(error.toString()); + _syncTask = null; + return false; + }) + .whenComplete(() { + _syncTask = null; + }); } Future syncWebsocketBatch(List batchData) { @@ -106,13 +181,20 @@ class BackgroundSyncManager { _syncWebsocketTask = null; }); } + + Future syncLinkedAlbum() { + if (_linkedAlbumSyncTask != null) { + return _linkedAlbumSyncTask!.future; + } + + _linkedAlbumSyncTask = runInIsolateGentle(computation: syncLinkedAlbumsIsolated, debugLabel: 'linked-album-sync'); + return _linkedAlbumSyncTask!.whenComplete(() { + _linkedAlbumSyncTask = null; + }); + } } -Cancelable _handleWsAssetUploadReadyV1Batch( - List batchData, -) => - runInIsolateGentle( - computation: (ref) => ref - .read(syncStreamServiceProvider) - .handleWsAssetUploadReadyV1Batch(batchData), - ); +Cancelable _handleWsAssetUploadReadyV1Batch(List batchData) => runInIsolateGentle( + computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), + debugLabel: 'websocket-batch', +); diff --git a/mobile/lib/domain/utils/event_stream.dart b/mobile/lib/domain/utils/event_stream.dart index e728ece58b..5967fdca50 100644 --- a/mobile/lib/domain/utils/event_stream.dart +++ b/mobile/lib/domain/utils/event_stream.dart @@ -9,8 +9,7 @@ class EventStream { static final EventStream shared = EventStream._(); - final StreamController _controller = - StreamController.broadcast(); + final StreamController _controller = StreamController.broadcast(); void emit(Event event) { _controller.add(event); @@ -29,12 +28,7 @@ class EventStream { void Function()? onDone, bool? cancelOnError, }) { - return where().listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); + return where().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } /// Closes the stream controller diff --git a/mobile/lib/domain/utils/sync_linked_album.dart b/mobile/lib/domain/utils/sync_linked_album.dart new file mode 100644 index 0000000000..7bfadc96e7 --- /dev/null +++ b/mobile/lib/domain/utils/sync_linked_album.dart @@ -0,0 +1,14 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/sync_linked_album.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:logging/logging.dart'; + +Future syncLinkedAlbumsIsolated(ProviderContainer ref) { + final user = Store.tryGet(StoreKey.currentUser); + if (user == null) { + Logger("SyncLinkedAlbum").warning("No user logged in, skipping linked album sync"); + return Future.value(); + } + return ref.read(syncLinkedAlbumServiceProvider).syncLinkedAlbums(user.id); +} diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index f6d5322752..2ca0d50dcc 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -95,13 +95,11 @@ class Album { // accessible in an object freshly created (not loaded from DB) @ignore - Iterable get remoteUsers => sharedUsers.isEmpty - ? (sharedUsers as IsarLinksCommon).addedObjects - : sharedUsers; + Iterable get remoteUsers => + sharedUsers.isEmpty ? (sharedUsers as IsarLinksCommon).addedObjects : sharedUsers; @ignore - Iterable get remoteAssets => - assets.isEmpty ? (assets as IsarLinksCommon).addedObjects : assets; + Iterable get remoteAssets => assets.isEmpty ? (assets as IsarLinksCommon).addedObjects : assets; @override bool operator ==(other) { @@ -115,10 +113,7 @@ class Album { modifiedAt.isAtSameMomentAs(other.modifiedAt) && isAtSameMomentAs(startDate, other.startDate) && isAtSameMomentAs(endDate, other.endDate) && - isAtSameMomentAs( - lastModifiedAssetTimestamp, - other.lastModifiedAssetTimestamp, - ) && + isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) && shared == other.shared && activityEnabled == other.activityEnabled && owner.value == other.owner.value && @@ -164,33 +159,25 @@ class Album { a.remoteAssetCount = dto.assetCount; a.owner.value = await db.users.getById(dto.ownerId); if (dto.order != null) { - a.sortOrder = - dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc; + a.sortOrder = dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc; } if (dto.albumThumbnailAssetId != null) { - a.thumbnail.value = await db.assets - .where() - .remoteIdEqualTo(dto.albumThumbnailAssetId) - .findFirst(); + a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst(); } if (dto.albumUsers.isNotEmpty) { - final users = await db.users.getAllById( - dto.albumUsers.map((e) => e.user.id).toList(growable: false), - ); + final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false)); a.sharedUsers.addAll(users.cast()); } if (dto.assets.isNotEmpty) { - final assets = - await db.assets.getAllByRemoteId(dto.assets.map((e) => e.id)); + final assets = await db.assets.getAllByRemoteId(dto.assets.map((e) => e.id)); a.assets.addAll(assets); } return a; } @override - String toString() => - 'remoteId: $remoteId name: $name description: $description'; + String toString() => 'remoteId: $remoteId name: $name description: $description'; } extension AssetsHelper on IsarCollection { diff --git a/mobile/lib/entities/album.entity.g.dart b/mobile/lib/entities/album.entity.g.dart index 546101baca..ecbbab48c2 100644 --- a/mobile/lib/entities/album.entity.g.dart +++ b/mobile/lib/entities/album.entity.g.dart @@ -42,31 +42,19 @@ const AlbumSchema = CollectionSchema( name: r'lastModifiedAssetTimestamp', type: IsarType.dateTime, ), - r'localId': PropertySchema( - id: 5, - name: r'localId', - type: IsarType.string, - ), + r'localId': PropertySchema(id: 5, name: r'localId', type: IsarType.string), r'modifiedAt': PropertySchema( id: 6, name: r'modifiedAt', type: IsarType.dateTime, ), - r'name': PropertySchema( - id: 7, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 7, name: r'name', type: IsarType.string), r'remoteId': PropertySchema( id: 8, name: r'remoteId', type: IsarType.string, ), - r'shared': PropertySchema( - id: 9, - name: r'shared', - type: IsarType.bool, - ), + r'shared': PropertySchema(id: 9, name: r'shared', type: IsarType.bool), r'sortOrder': PropertySchema( id: 10, name: r'sortOrder', @@ -77,8 +65,9 @@ const AlbumSchema = CollectionSchema( id: 11, name: r'startDate', type: IsarType.dateTime, - ) + ), }, + estimateSize: _albumEstimateSize, serialize: _albumSerialize, deserialize: _albumDeserialize, @@ -95,7 +84,7 @@ const AlbumSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -108,9 +97,9 @@ const AlbumSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: { r'owner': LinkSchema( @@ -136,13 +125,14 @@ const AlbumSchema = CollectionSchema( name: r'assets', target: r'Asset', single: false, - ) + ), }, embeddedSchemas: {}, + getId: _albumGetId, getLinks: _albumGetLinks, attach: _albumAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _albumEstimateSize( @@ -212,7 +202,7 @@ Album _albumDeserialize( shared: reader.readBool(offsets[9]), sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ?? - SortOrder.desc, + SortOrder.desc, startDate: reader.readDateTimeOrNull(offsets[11]), ); object.id = id; @@ -248,7 +238,8 @@ P _albumDeserializeProp

( return (reader.readBool(offset)) as P; case 10: return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ?? - SortOrder.desc) as P; + SortOrder.desc) + as P; case 11: return (reader.readDateTimeOrNull(offset)) as P; default: @@ -256,14 +247,8 @@ P _albumDeserializeProp

( } } -const _AlbumsortOrderEnumValueMap = { - 'asc': 0, - 'desc': 1, -}; -const _AlbumsortOrderValueEnumMap = { - 0: SortOrder.asc, - 1: SortOrder.desc, -}; +const _AlbumsortOrderEnumValueMap = {'asc': 0, 'desc': 1}; +const _AlbumsortOrderValueEnumMap = {0: SortOrder.asc, 1: SortOrder.desc}; Id _albumGetId(Album object) { return object.id; @@ -277,8 +262,12 @@ void _albumAttach(IsarCollection col, Id id, Album object) { object.id = id; object.owner.attach(col, col.isar.collection(), r'owner', id); object.thumbnail.attach(col, col.isar.collection(), r'thumbnail', id); - object.sharedUsers - .attach(col, col.isar.collection(), r'sharedUsers', id); + object.sharedUsers.attach( + col, + col.isar.collection(), + r'sharedUsers', + id, + ); object.assets.attach(col, col.isar.collection(), r'assets', id); } @@ -293,10 +282,7 @@ extension AlbumQueryWhereSort on QueryBuilder { extension AlbumQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -322,8 +308,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -331,8 +319,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -347,141 +337,163 @@ extension AlbumQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } @@ -489,22 +501,22 @@ extension AlbumQueryWhere on QueryBuilder { extension AlbumQueryFilter on QueryBuilder { QueryBuilder activityEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'activityEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'activityEnabled', value: value), + ); }); } QueryBuilder createdAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } @@ -513,11 +525,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -526,11 +540,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -541,29 +557,31 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -572,11 +590,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -586,12 +606,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -601,12 +623,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -618,14 +642,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,11 +660,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -647,79 +675,85 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder endDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'endDate'), + ); }); } QueryBuilder endDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'endDate'), + ); }); } QueryBuilder endDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'endDate', value: value), + ); }); } @@ -728,11 +762,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -741,11 +777,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -756,22 +794,23 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'endDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'endDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -780,11 +819,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -793,11 +834,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -808,103 +851,112 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNull() { + lastModifiedAssetTimestampIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lastModifiedAssetTimestamp'), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNotNull() { + lastModifiedAssetTimestampIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'lastModifiedAssetTimestamp', + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampEqualTo(DateTime? value) { + lastModifiedAssetTimestampEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampGreaterThan( + lastModifiedAssetTimestampGreaterThan( DateTime? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampLessThan( - DateTime? value, { - bool include = false, - }) { + lastModifiedAssetTimestampLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampBetween( + lastModifiedAssetTimestampBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastModifiedAssetTimestamp', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastModifiedAssetTimestamp', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -913,11 +965,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -927,12 +981,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -942,12 +998,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -959,14 +1017,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -975,11 +1035,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -988,63 +1050,69 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder modifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedAt', value: value), + ); }); } @@ -1053,11 +1121,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1066,11 +1136,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1081,13 +1153,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1096,11 +1170,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1110,12 +1186,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1125,12 +1203,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1142,14 +1222,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1158,11 +1240,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1171,67 +1255,75 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1240,11 +1332,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1254,12 +1348,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1269,12 +1365,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1286,14 +1384,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1302,11 +1402,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1315,72 +1417,77 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder sharedEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'shared', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'shared', value: value), + ); }); } QueryBuilder sortOrderEqualTo( - SortOrder value) { + SortOrder value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'sortOrder', value: value), + ); }); } @@ -1389,11 +1496,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1402,11 +1511,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1417,39 +1528,41 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'sortOrder', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'sortOrder', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder startDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'startDate'), + ); }); } QueryBuilder startDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'startDate'), + ); }); } QueryBuilder startDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'startDate', value: value), + ); }); } @@ -1458,11 +1571,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1471,11 +1586,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1486,13 +1603,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'startDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'startDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1513,7 +1632,8 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder thumbnail( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'thumbnail'); }); @@ -1526,14 +1646,16 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder sharedUsers( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'sharedUsers'); }); } QueryBuilder sharedUsersLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, true, length, true); }); @@ -1561,10 +1683,7 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder - sharedUsersLengthGreaterThan( - int length, { - bool include = false, - }) { + sharedUsersLengthGreaterThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, include, 999999, true); }); @@ -1578,19 +1697,26 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'sharedUsers', lower, includeLower, upper, includeUpper); + r'sharedUsers', + lower, + includeLower, + upper, + includeUpper, + ); }); } QueryBuilder assets( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'assets'); }); } QueryBuilder assetsLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'assets', length, true, length, true); }); @@ -1634,7 +1760,12 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'assets', lower, includeLower, upper, includeUpper); + r'assets', + lower, + includeLower, + upper, + includeUpper, + ); }); } } @@ -1695,7 +1826,7 @@ extension AlbumQuerySortBy on QueryBuilder { } QueryBuilder - sortByLastModifiedAssetTimestampDesc() { + sortByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1854,7 +1985,7 @@ extension AlbumQuerySortThenBy on QueryBuilder { } QueryBuilder - thenByLastModifiedAssetTimestampDesc() { + thenByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1958,8 +2089,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -1977,8 +2109,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -1990,15 +2123,17 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -2055,7 +2190,7 @@ extension AlbumQueryProperty on QueryBuilder { } QueryBuilder - lastModifiedAssetTimestampProperty() { + lastModifiedAssetTimestampProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'lastModifiedAssetTimestamp'); }); diff --git a/mobile/lib/entities/android_device_asset.entity.g.dart b/mobile/lib/entities/android_device_asset.entity.g.dart index eaa7658565..f8b1e32c72 100644 --- a/mobile/lib/entities/android_device_asset.entity.g.dart +++ b/mobile/lib/entities/android_device_asset.entity.g.dart @@ -18,12 +18,9 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'AndroidDeviceAsset', id: -6758387181232899335, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), }, + estimateSize: _androidDeviceAssetEstimateSize, serialize: _androidDeviceAssetSerialize, deserialize: _androidDeviceAssetDeserialize, @@ -40,16 +37,17 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _androidDeviceAssetGetId, getLinks: _androidDeviceAssetGetLinks, attach: _androidDeviceAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _androidDeviceAssetEstimateSize( @@ -103,12 +101,16 @@ Id _androidDeviceAssetGetId(AndroidDeviceAsset object) { } List> _androidDeviceAssetGetLinks( - AndroidDeviceAsset object) { + AndroidDeviceAsset object, +) { return []; } void _androidDeviceAssetAttach( - IsarCollection col, Id id, AndroidDeviceAsset object) { + IsarCollection col, + Id id, + AndroidDeviceAsset object, +) { object.id = id; } @@ -124,17 +126,14 @@ extension AndroidDeviceAssetQueryWhereSort extension AndroidDeviceAssetQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -157,7 +156,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -166,7 +165,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -175,63 +174,72 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -240,134 +248,97 @@ extension AndroidDeviceAssetQueryWhere extension AndroidDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -385,58 +356,57 @@ extension AndroidDeviceAssetQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -453,14 +423,14 @@ extension AndroidDeviceAssetQuerySortBy extension AndroidDeviceAssetQuerySortThenBy on QueryBuilder { QueryBuilder - thenById() { + thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); }); } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); @@ -470,7 +440,7 @@ extension AndroidDeviceAssetQuerySortThenBy extension AndroidDeviceAssetQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index d754b11dc5..0d549457a1 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -4,8 +4,7 @@ import 'dart:io'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/hash.dart'; @@ -20,34 +19,30 @@ part 'asset.entity.g.dart'; @Collection(inheritance: false) class Asset { Asset.remote(AssetResponseDto remote) - : remoteId = remote.id, - checksum = remote.checksum, - fileCreatedAt = remote.fileCreatedAt, - fileModifiedAt = remote.fileModifiedAt, - updatedAt = remote.updatedAt, - durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, - type = remote.type.toAssetType(), - fileName = remote.originalFileName, - height = remote.exifInfo?.exifImageHeight?.toInt(), - width = remote.exifInfo?.exifImageWidth?.toInt(), - livePhotoVideoId = remote.livePhotoVideoId, - ownerId = fastHash(remote.ownerId), - exifInfo = remote.exifInfo == null - ? null - : ExifDtoConverter.fromDto(remote.exifInfo!), - isFavorite = remote.isFavorite, - isArchived = remote.isArchived, - isTrashed = remote.isTrashed, - isOffline = remote.isOffline, - // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app - // stack handling to properly handle it - stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id - ? null - : remote.stack?.primaryAssetId, - stackCount = remote.stack?.assetCount ?? 0, - stackId = remote.stack?.id, - thumbhash = remote.thumbhash, - visibility = getVisibility(remote.visibility); + : remoteId = remote.id, + checksum = remote.checksum, + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, + durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, + type = remote.type.toAssetType(), + fileName = remote.originalFileName, + height = remote.exifInfo?.exifImageHeight?.toInt(), + width = remote.exifInfo?.exifImageWidth?.toInt(), + livePhotoVideoId = remote.livePhotoVideoId, + ownerId = fastHash(remote.ownerId), + exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), + isFavorite = remote.isFavorite, + isArchived = remote.isArchived, + isTrashed = remote.isTrashed, + isOffline = remote.isOffline, + // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app + // stack handling to properly handle it + stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, + stackCount = remote.stack?.assetCount ?? 0, + stackId = remote.stack?.id, + thumbhash = remote.thumbhash, + visibility = getVisibility(remote.visibility); Asset({ this.id = Isar.autoIncrement, @@ -108,8 +103,7 @@ class Asset { throw Exception('Asset $fileName has no local data'); } - final updatedLocal = - _didUpdateLocal ? local : await local.obtainForNewProperties(); + final updatedLocal = _didUpdateLocal ? local : await local.obtainForNewProperties(); if (updatedLocal == null) { throw Exception('Could not fetch local data for $fileName'); } @@ -133,11 +127,7 @@ class Asset { @Index(unique: false, replace: false, type: IndexType.hash) String? localId; - @Index( - unique: true, - replace: false, - composite: [CompositeIndex("checksum", type: IndexType.hash)], - ) + @Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)]) int ownerId; DateTime fileCreatedAt; @@ -185,10 +175,7 @@ class Asset { final orientatedWidth = this.orientatedWidth; final orientatedHeight = this.orientatedHeight; - if (orientatedWidth != null && - orientatedHeight != null && - orientatedWidth > 0 && - orientatedHeight > 0) { + if (orientatedWidth != null && orientatedHeight != null && orientatedWidth > 0 && orientatedHeight > 0) { return orientatedWidth.toDouble() / orientatedHeight.toDouble(); } @@ -389,8 +376,7 @@ class Asset { // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app // stack handling to properly handle it stackId: stackId, - stackPrimaryAssetId: - stackPrimaryAssetId == remoteId ? null : stackPrimaryAssetId, + stackPrimaryAssetId: stackPrimaryAssetId == remoteId ? null : stackPrimaryAssetId, stackCount: stackCount, isFavorite: isFavorite, isArchived: isArchived, @@ -410,9 +396,7 @@ class Asset { // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app // stack handling to properly handle it stackId: a.stackId, - stackPrimaryAssetId: a.stackPrimaryAssetId == a.remoteId - ? null - : a.stackPrimaryAssetId, + stackPrimaryAssetId: a.stackPrimaryAssetId == a.remoteId ? null : a.stackPrimaryAssetId, stackCount: a.stackCount, // isFavorite + isArchived are not set by device-only assets isFavorite: a.isFavorite, @@ -428,8 +412,7 @@ class Asset { localId: localId ?? a.localId, width: width ?? a.width, height: height ?? a.height, - exifInfo: exifInfo ?? - a.exifInfo?.copyWith(assetId: id), // updated to use assetId + exifInfo: exifInfo ?? a.exifInfo?.copyWith(assetId: id), // updated to use assetId ); } } @@ -460,49 +443,45 @@ class Asset { int? stackCount, String? thumbhash, AssetVisibilityEnum? visibility, - }) => - Asset( - id: id ?? this.id, - checksum: checksum ?? this.checksum, - remoteId: remoteId ?? this.remoteId, - localId: localId ?? this.localId, - ownerId: ownerId ?? this.ownerId, - fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, - fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, - updatedAt: updatedAt ?? this.updatedAt, - durationInSeconds: durationInSeconds ?? this.durationInSeconds, - type: type ?? this.type, - width: width ?? this.width, - height: height ?? this.height, - fileName: fileName ?? this.fileName, - livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, - isFavorite: isFavorite ?? this.isFavorite, - isArchived: isArchived ?? this.isArchived, - isTrashed: isTrashed ?? this.isTrashed, - isOffline: isOffline ?? this.isOffline, - exifInfo: exifInfo ?? this.exifInfo, - stackId: stackId ?? this.stackId, - stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, - stackCount: stackCount ?? this.stackCount, - thumbhash: thumbhash ?? this.thumbhash, - visibility: visibility ?? this.visibility, - ); + }) => Asset( + id: id ?? this.id, + checksum: checksum ?? this.checksum, + remoteId: remoteId ?? this.remoteId, + localId: localId ?? this.localId, + ownerId: ownerId ?? this.ownerId, + fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, + fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, + updatedAt: updatedAt ?? this.updatedAt, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + type: type ?? this.type, + width: width ?? this.width, + height: height ?? this.height, + fileName: fileName ?? this.fileName, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + isFavorite: isFavorite ?? this.isFavorite, + isArchived: isArchived ?? this.isArchived, + isTrashed: isTrashed ?? this.isTrashed, + isOffline: isOffline ?? this.isOffline, + exifInfo: exifInfo ?? this.exifInfo, + stackId: stackId ?? this.stackId, + stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, + stackCount: stackCount ?? this.stackCount, + thumbhash: thumbhash ?? this.thumbhash, + visibility: visibility ?? this.visibility, + ); Future put(Isar db) async { await db.assets.put(this); if (exifInfo != null) { - await db.exifInfos - .put(entity.ExifInfo.fromDto(exifInfo!.copyWith(assetId: id))); + await db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo!.copyWith(assetId: id))); } } static int compareById(Asset a, Asset b) => a.id.compareTo(b.id); - static int compareByLocalId(Asset a, Asset b) => - compareToNullable(a.localId, b.localId); + static int compareByLocalId(Asset a, Asset b) => compareToNullable(a.localId, b.localId); - static int compareByChecksum(Asset a, Asset b) => - a.checksum.compareTo(b.checksum); + static int compareByChecksum(Asset a, Asset b) => a.checksum.compareTo(b.checksum); static int compareByOwnerChecksum(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); @@ -510,10 +489,7 @@ class Asset { return compareByChecksum(a, b); } - static int compareByOwnerChecksumCreatedModified( - Asset a, - Asset b, - ) { + static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); if (ownerIdOrder != 0) return ownerIdOrder; final int checksumOrder = compareByChecksum(a, b); @@ -555,11 +531,11 @@ class Asset { } static getVisibility(AssetVisibility visibility) => switch (visibility) { - AssetVisibility.archive => AssetVisibilityEnum.archive, - AssetVisibility.hidden => AssetVisibilityEnum.hidden, - AssetVisibility.locked => AssetVisibilityEnum.locked, - AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, - }; + AssetVisibility.archive => AssetVisibilityEnum.archive, + AssetVisibility.hidden => AssetVisibilityEnum.hidden, + AssetVisibility.locked => AssetVisibilityEnum.locked, + AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, + }; } enum AssetType { @@ -572,41 +548,28 @@ enum AssetType { extension AssetTypeEnumHelper on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception(), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception(), + }; } /// Describes where the information of this asset came from: /// only from the local device, only from the remote server or merged from both -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } extension AssetsHelper on IsarCollection { - Future deleteAllByRemoteId(Iterable ids) => - ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); - Future deleteAllByLocalId(Iterable ids) => - ids.isEmpty ? Future.value(0) : local(ids).deleteAll(); - Future> getAllByRemoteId(Iterable ids) => - ids.isEmpty ? Future.value([]) : remote(ids).findAll(); - Future> getAllByLocalId(Iterable ids) => - ids.isEmpty ? Future.value([]) : local(ids).findAll(); - Future getByRemoteId(String id) => - where().remoteIdEqualTo(id).findFirst(); + Future deleteAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); + Future deleteAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value(0) : local(ids).deleteAll(); + Future> getAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value([]) : remote(ids).findAll(); + Future> getAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll(); + Future getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst(); - QueryBuilder remote( - Iterable ids, - ) => + QueryBuilder remote(Iterable ids) => where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e)); - QueryBuilder local( - Iterable ids, - ) { + QueryBuilder local(Iterable ids) { return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e)); } } diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index b558690813..db6bc72331 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -42,11 +42,7 @@ const AssetSchema = CollectionSchema( name: r'fileName', type: IsarType.string, ), - r'height': PropertySchema( - id: 5, - name: r'height', - type: IsarType.int, - ), + r'height': PropertySchema(id: 5, name: r'height', type: IsarType.int), r'isArchived': PropertySchema( id: 6, name: r'isArchived', @@ -72,16 +68,8 @@ const AssetSchema = CollectionSchema( name: r'livePhotoVideoId', type: IsarType.string, ), - r'localId': PropertySchema( - id: 11, - name: r'localId', - type: IsarType.string, - ), - r'ownerId': PropertySchema( - id: 12, - name: r'ownerId', - type: IsarType.long, - ), + r'localId': PropertySchema(id: 11, name: r'localId', type: IsarType.string), + r'ownerId': PropertySchema(id: 12, name: r'ownerId', type: IsarType.long), r'remoteId': PropertySchema( id: 13, name: r'remoteId', @@ -92,11 +80,7 @@ const AssetSchema = CollectionSchema( name: r'stackCount', type: IsarType.long, ), - r'stackId': PropertySchema( - id: 15, - name: r'stackId', - type: IsarType.string, - ), + r'stackId': PropertySchema(id: 15, name: r'stackId', type: IsarType.string), r'stackPrimaryAssetId': PropertySchema( id: 16, name: r'stackPrimaryAssetId', @@ -124,12 +108,9 @@ const AssetSchema = CollectionSchema( type: IsarType.byte, enumMap: _AssetvisibilityEnumValueMap, ), - r'width': PropertySchema( - id: 21, - name: r'width', - type: IsarType.int, - ) + r'width': PropertySchema(id: 21, name: r'width', type: IsarType.int), }, + estimateSize: _assetEstimateSize, serialize: _assetSerialize, deserialize: _assetDeserialize, @@ -146,7 +127,7 @@ const AssetSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -159,7 +140,7 @@ const AssetSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'ownerId_checksum': IndexSchema( @@ -177,16 +158,17 @@ const AssetSchema = CollectionSchema( name: r'checksum', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _assetGetId, getLinks: _assetGetLinks, attach: _assetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _assetEstimateSize( @@ -292,12 +274,13 @@ Asset _assetDeserialize( stackId: reader.readStringOrNull(offsets[15]), stackPrimaryAssetId: reader.readStringOrNull(offsets[16]), thumbhash: reader.readStringOrNull(offsets[17]), - type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? + type: + _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, updatedAt: reader.readDateTime(offsets[19]), visibility: _AssetvisibilityValueEnumMap[reader.readByteOrNull(offsets[20])] ?? - AssetVisibilityEnum.timeline, + AssetVisibilityEnum.timeline, width: reader.readIntOrNull(offsets[21]), ); return object; @@ -348,12 +331,14 @@ P _assetDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 18: return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetType.other) as P; + AssetType.other) + as P; case 19: return (reader.readDateTime(offset)) as P; case 20: return (_AssetvisibilityValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetVisibilityEnum.timeline) as P; + AssetVisibilityEnum.timeline) + as P; case 21: return (reader.readIntOrNull(offset)) as P; default: @@ -361,12 +346,7 @@ P _assetDeserializeProp

( } } -const _AssettypeEnumValueMap = { - 'other': 0, - 'image': 1, - 'video': 2, - 'audio': 3, -}; +const _AssettypeEnumValueMap = {'other': 0, 'image': 1, 'video': 2, 'audio': 3}; const _AssettypeValueEnumMap = { 0: AssetType.other, 1: AssetType.image, @@ -416,10 +396,14 @@ extension AssetByIndex on IsarCollection { } Future> getAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -429,10 +413,14 @@ extension AssetByIndex on IsarCollection { } List getAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -442,10 +430,14 @@ extension AssetByIndex on IsarCollection { } Future deleteAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -455,10 +447,14 @@ extension AssetByIndex on IsarCollection { } int deleteAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -479,10 +475,15 @@ extension AssetByIndex on IsarCollection { return putAllByIndex(r'ownerId_checksum', objects); } - List putAllByOwnerIdChecksumSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'ownerId_checksum', objects, - saveLinks: saveLinks); + List putAllByOwnerIdChecksumSync( + List objects, { + bool saveLinks = true, + }) { + return putAllByIndexSync( + r'ownerId_checksum', + objects, + saveLinks: saveLinks, + ); } } @@ -497,10 +498,7 @@ extension AssetQueryWhereSort on QueryBuilder { extension AssetQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -526,8 +524,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -535,8 +535,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -551,186 +553,220 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } QueryBuilder ownerIdEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId], + ), + ); }); } QueryBuilder ownerIdNotEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ); } }); } @@ -740,12 +776,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: include, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: include, + upper: [], + ), + ); }); } @@ -754,12 +792,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: include, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: include, + ), + ); }); } @@ -770,57 +810,71 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [lowerOwnerId], - includeLower: includeLower, - upper: [upperOwnerId], - includeUpper: includeUpper, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [lowerOwnerId], + includeLower: includeLower, + upper: [upperOwnerId], + includeUpper: includeUpper, + ), + ); }); } QueryBuilder ownerIdChecksumEqualTo( - int ownerId, String checksum) { + int ownerId, + String checksum, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId, checksum], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId, checksum], + ), + ); }); } QueryBuilder - ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { + ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ); } }); } @@ -832,11 +886,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -846,12 +902,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -861,12 +919,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -878,14 +938,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'checksum', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'checksum', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -894,11 +956,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -907,77 +971,82 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'checksum', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'checksum', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'checksum', value: ''), + ); }); } QueryBuilder checksumIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'checksum', value: ''), + ); }); } QueryBuilder durationInSecondsEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'durationInSeconds', value: value), + ); }); } QueryBuilder - durationInSecondsGreaterThan( - int value, { - bool include = false, - }) { + durationInSecondsGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -986,11 +1055,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -1001,23 +1072,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'durationInSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'durationInSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileCreatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileCreatedAt', value: value), + ); }); } @@ -1026,11 +1099,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1039,11 +1114,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1054,23 +1131,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileCreatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileCreatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileModifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileModifiedAt', value: value), + ); }); } @@ -1079,11 +1158,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1092,11 +1173,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1107,13 +1190,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileModifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileModifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1122,11 +1207,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1136,12 +1223,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1151,12 +1240,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1168,14 +1259,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileName', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1184,11 +1277,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1197,78 +1292,83 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'fileName', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'fileName', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileName', value: ''), + ); }); } QueryBuilder fileNameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'fileName', value: ''), + ); }); } QueryBuilder heightIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'height'), + ); }); } QueryBuilder heightIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'height'), + ); }); } QueryBuilder heightEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'height', value: value), + ); }); } @@ -1277,11 +1377,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1290,11 +1392,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1305,22 +1409,23 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'height', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'height', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1329,11 +1434,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1342,11 +1449,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1357,70 +1466,72 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isArchivedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isArchived', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isArchived', value: value), + ); }); } QueryBuilder isFavoriteEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isFavorite', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isFavorite', value: value), + ); }); } QueryBuilder isOfflineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isOffline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isOffline', value: value), + ); }); } QueryBuilder isTrashedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isTrashed', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isTrashed', value: value), + ); }); } QueryBuilder livePhotoVideoIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'livePhotoVideoId'), + ); }); } QueryBuilder - livePhotoVideoIdIsNotNull() { + livePhotoVideoIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'livePhotoVideoId'), + ); }); } @@ -1429,11 +1540,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1443,12 +1556,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1458,12 +1573,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1475,14 +1592,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'livePhotoVideoId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'livePhotoVideoId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1491,11 +1610,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1504,70 +1625,76 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'livePhotoVideoId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'livePhotoVideoId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder - livePhotoVideoIdIsNotEmpty() { + livePhotoVideoIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -1576,11 +1703,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1590,12 +1719,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1605,12 +1736,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1622,14 +1755,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1638,11 +1773,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1651,62 +1788,67 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder ownerIdEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'ownerId', value: value), + ); }); } @@ -1715,11 +1857,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1728,11 +1872,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1743,29 +1889,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'ownerId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'ownerId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1774,11 +1922,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1788,12 +1938,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1803,12 +1955,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1820,14 +1974,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1836,11 +1992,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1849,63 +2007,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder stackCountEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackCount', value: value), + ); }); } @@ -1914,11 +2078,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1927,11 +2093,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1942,29 +2110,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder stackIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackId'), + ); }); } QueryBuilder stackIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackId'), + ); }); } @@ -1973,11 +2143,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1987,12 +2159,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2002,12 +2176,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2019,14 +2195,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2035,11 +2213,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2048,71 +2228,77 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackId', value: ''), + ); }); } QueryBuilder stackIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'stackId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNull() { + stackPrimaryAssetIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackPrimaryAssetId'), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotNull() { + stackPrimaryAssetIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackPrimaryAssetId'), + ); }); } @@ -2121,27 +2307,31 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdGreaterThan( + stackPrimaryAssetIdGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2151,12 +2341,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2168,28 +2360,29 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackPrimaryAssetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackPrimaryAssetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + stackPrimaryAssetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2198,71 +2391,80 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackPrimaryAssetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackPrimaryAssetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdIsEmpty() { + stackPrimaryAssetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackPrimaryAssetId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotEmpty() { + stackPrimaryAssetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + property: r'stackPrimaryAssetId', + value: '', + ), + ); }); } QueryBuilder thumbhashIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'thumbhash'), + ); }); } QueryBuilder thumbhashIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'thumbhash'), + ); }); } @@ -2271,11 +2473,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2285,12 +2489,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,12 +2506,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2317,14 +2525,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'thumbhash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'thumbhash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2333,11 +2543,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2346,63 +2558,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'thumbhash', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'thumbhash', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'thumbhash', value: ''), + ); }); } QueryBuilder thumbhashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'thumbhash', value: ''), + ); }); } QueryBuilder typeEqualTo( - AssetType value) { + AssetType value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'type', value: value), + ); }); } @@ -2411,11 +2629,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2424,11 +2644,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2439,23 +2661,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'type', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -2464,11 +2688,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2477,11 +2703,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2492,23 +2720,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder visibilityEqualTo( - AssetVisibilityEnum value) { + AssetVisibilityEnum value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'visibility', value: value), + ); }); } @@ -2517,11 +2747,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2530,11 +2762,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2545,38 +2779,39 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'visibility', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'visibility', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder widthIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'width'), + ); }); } QueryBuilder widthIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'width'), + ); }); } QueryBuilder widthEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'width', value: value), + ); }); } @@ -2585,11 +2820,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2598,11 +2835,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2613,13 +2852,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'width', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'width', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -3173,8 +3414,9 @@ extension AssetQuerySortThenBy on QueryBuilder { } extension AssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByChecksum( - {bool caseSensitive = true}) { + QueryBuilder distinctByChecksum({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'checksum', caseSensitive: caseSensitive); }); @@ -3198,8 +3440,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByFileName( - {bool caseSensitive = true}) { + QueryBuilder distinctByFileName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'fileName', caseSensitive: caseSensitive); }); @@ -3235,16 +3478,20 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLivePhotoVideoId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLivePhotoVideoId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'livePhotoVideoId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'livePhotoVideoId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -3256,8 +3503,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -3269,23 +3517,28 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByStackId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'stackId', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByStackPrimaryAssetId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackPrimaryAssetId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'stackPrimaryAssetId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'stackPrimaryAssetId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByThumbhash( - {bool caseSensitive = true}) { + QueryBuilder distinctByThumbhash({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'thumbhash', caseSensitive: caseSensitive); }); @@ -3444,7 +3697,7 @@ extension AssetQueryProperty on QueryBuilder { } QueryBuilder - visibilityProperty() { + visibilityProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'visibility'); }); diff --git a/mobile/lib/entities/backup_album.entity.dart b/mobile/lib/entities/backup_album.entity.dart index 1e96c0452e..ad2a5d6718 100644 --- a/mobile/lib/entities/backup_album.entity.dart +++ b/mobile/lib/entities/backup_album.entity.dart @@ -14,21 +14,9 @@ class BackupAlbum { Id get isarId => fastHash(id); - BackupAlbum copyWith({ - String? id, - DateTime? lastBackup, - BackupSelection? selection, - }) { - return BackupAlbum( - id ?? this.id, - lastBackup ?? this.lastBackup, - selection ?? this.selection, - ); + BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) { + return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection); } } -enum BackupSelection { - none, - select, - exclude; -} +enum BackupSelection { none, select, exclude } diff --git a/mobile/lib/entities/backup_album.entity.g.dart b/mobile/lib/entities/backup_album.entity.g.dart index 23d00e43ca..583aa55c4d 100644 --- a/mobile/lib/entities/backup_album.entity.g.dart +++ b/mobile/lib/entities/backup_album.entity.g.dart @@ -17,11 +17,7 @@ const BackupAlbumSchema = CollectionSchema( name: r'BackupAlbum', id: 8308487201128361847, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ), + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), r'lastBackup': PropertySchema( id: 1, name: r'lastBackup', @@ -32,8 +28,9 @@ const BackupAlbumSchema = CollectionSchema( name: r'selection', type: IsarType.byte, enumMap: _BackupAlbumselectionEnumValueMap, - ) + ), }, + estimateSize: _backupAlbumEstimateSize, serialize: _backupAlbumSerialize, deserialize: _backupAlbumDeserialize, @@ -42,10 +39,11 @@ const BackupAlbumSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _backupAlbumGetId, getLinks: _backupAlbumGetLinks, attach: _backupAlbumAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _backupAlbumEstimateSize( @@ -96,9 +94,11 @@ P _backupAlbumDeserializeProp

( case 1: return (reader.readDateTime(offset)) as P; case 2: - return (_BackupAlbumselectionValueEnumMap[ - reader.readByteOrNull(offset)] ?? - BackupSelection.none) as P; + return (_BackupAlbumselectionValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + BackupSelection.none) + as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -124,7 +124,10 @@ List> _backupAlbumGetLinks(BackupAlbum object) { } void _backupAlbumAttach( - IsarCollection col, Id id, BackupAlbum object) {} + IsarCollection col, + Id id, + BackupAlbum object, +) {} extension BackupAlbumQueryWhereSort on QueryBuilder { @@ -138,17 +141,18 @@ extension BackupAlbumQueryWhereSort extension BackupAlbumQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder isarIdNotEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -171,8 +175,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdGreaterThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -181,8 +186,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdLessThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -197,12 +203,14 @@ extension BackupAlbumQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -214,11 +222,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -228,12 +238,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -243,12 +255,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -260,14 +274,16 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -276,11 +292,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -289,77 +307,82 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -368,11 +391,13 @@ extension BackupAlbumQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -383,125 +408,125 @@ extension BackupAlbumQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastBackupEqualTo(DateTime value) { + lastBackupEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lastBackup', value: value), + ); }); } QueryBuilder - lastBackupGreaterThan( - DateTime value, { - bool include = false, - }) { + lastBackupGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupLessThan( - DateTime value, { - bool include = false, - }) { + lastBackupLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupBetween( + lastBackupBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastBackup', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastBackup', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - selectionEqualTo(BackupSelection value) { + selectionEqualTo(BackupSelection value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'selection', value: value), + ); }); } QueryBuilder - selectionGreaterThan( - BackupSelection value, { - bool include = false, - }) { + selectionGreaterThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionLessThan( - BackupSelection value, { - bool include = false, - }) { + selectionLessThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionBetween( + selectionBetween( BackupSelection lower, BackupSelection upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'selection', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'selection', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -604,8 +629,9 @@ extension BackupAlbumQuerySortThenBy extension BackupAlbumQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -645,7 +671,7 @@ extension BackupAlbumQueryProperty } QueryBuilder - selectionProperty() { + selectionProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'selection'); }); diff --git a/mobile/lib/entities/duplicated_asset.entity.g.dart b/mobile/lib/entities/duplicated_asset.entity.g.dart index 8965d47c97..80d2f344e6 100644 --- a/mobile/lib/entities/duplicated_asset.entity.g.dart +++ b/mobile/lib/entities/duplicated_asset.entity.g.dart @@ -17,12 +17,9 @@ const DuplicatedAssetSchema = CollectionSchema( name: r'DuplicatedAsset', id: -2679334728174694496, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ) + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), }, + estimateSize: _duplicatedAssetEstimateSize, serialize: _duplicatedAssetSerialize, deserialize: _duplicatedAssetDeserialize, @@ -31,10 +28,11 @@ const DuplicatedAssetSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _duplicatedAssetGetId, getLinks: _duplicatedAssetGetLinks, attach: _duplicatedAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _duplicatedAssetEstimateSize( @@ -62,9 +60,7 @@ DuplicatedAsset _duplicatedAssetDeserialize( List offsets, Map> allOffsets, ) { - final object = DuplicatedAsset( - reader.readString(offsets[0]), - ); + final object = DuplicatedAsset(reader.readString(offsets[0])); return object; } @@ -91,7 +87,10 @@ List> _duplicatedAssetGetLinks(DuplicatedAsset object) { } void _duplicatedAssetAttach( - IsarCollection col, Id id, DuplicatedAsset object) {} + IsarCollection col, + Id id, + DuplicatedAsset object, +) {} extension DuplicatedAssetQueryWhereSort on QueryBuilder { @@ -105,17 +104,16 @@ extension DuplicatedAssetQueryWhereSort extension DuplicatedAssetQueryWhere on QueryBuilder { QueryBuilder - isarIdEqualTo(Id isarId) { + isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -138,7 +136,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -147,7 +145,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -156,19 +154,21 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdBetween( + isarIdBetween( Id lowerIsarId, Id upperIsarId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -176,53 +176,52 @@ extension DuplicatedAssetQueryWhere extension DuplicatedAssetQueryFilter on QueryBuilder { QueryBuilder - idEqualTo( - String value, { - bool caseSensitive = true, - }) { + idEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idBetween( + idBetween( String lower, String upper, { bool includeLower = true, @@ -230,140 +229,141 @@ extension DuplicatedAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idMatches(String pattern, {bool caseSensitive = true}) { + idMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idIsEmpty() { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - idIsNotEmpty() { + idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdEqualTo(Id value) { + isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + isarIdLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdBetween( + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -410,7 +410,7 @@ extension DuplicatedAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -419,8 +419,9 @@ extension DuplicatedAssetQuerySortThenBy extension DuplicatedAssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/etag.entity.g.dart b/mobile/lib/entities/etag.entity.g.dart index afabca4aea..03b4ea9918 100644 --- a/mobile/lib/entities/etag.entity.g.dart +++ b/mobile/lib/entities/etag.entity.g.dart @@ -22,17 +22,10 @@ const ETagSchema = CollectionSchema( name: r'assetCount', type: IsarType.long, ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ), - r'time': PropertySchema( - id: 2, - name: r'time', - type: IsarType.dateTime, - ) + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), + r'time': PropertySchema(id: 2, name: r'time', type: IsarType.dateTime), }, + estimateSize: _eTagEstimateSize, serialize: _eTagSerialize, deserialize: _eTagDeserialize, @@ -49,16 +42,17 @@ const ETagSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _eTagGetId, getLinks: _eTagGetLinks, attach: _eTagAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _eTagEstimateSize( @@ -189,10 +183,9 @@ extension ETagQueryWhereSort on QueryBuilder { extension ETagQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -218,8 +211,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -227,8 +222,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -243,21 +240,22 @@ extension ETagQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -265,32 +263,40 @@ extension ETagQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -299,27 +305,27 @@ extension ETagQueryWhere on QueryBuilder { extension ETagQueryFilter on QueryBuilder { QueryBuilder assetCountIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetCount', value: value), + ); }); } @@ -328,11 +334,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -341,11 +349,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -356,13 +366,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -371,11 +383,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -385,12 +399,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -400,12 +416,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,14 +435,16 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -433,11 +453,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -446,60 +468,67 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -508,11 +537,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -521,11 +552,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -536,38 +569,39 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder timeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'time'), + ); }); } QueryBuilder timeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'time'), + ); }); } QueryBuilder timeEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'time', value: value), + ); }); } @@ -576,11 +610,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -589,11 +625,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -604,13 +642,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'time', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'time', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -714,8 +754,9 @@ extension ETagQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/ios_device_asset.entity.g.dart b/mobile/lib/entities/ios_device_asset.entity.g.dart index ffed338c91..252fe127bb 100644 --- a/mobile/lib/entities/ios_device_asset.entity.g.dart +++ b/mobile/lib/entities/ios_device_asset.entity.g.dart @@ -17,17 +17,10 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'IOSDeviceAsset', id: -1671546753821948030, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), }, + estimateSize: _iOSDeviceAssetEstimateSize, serialize: _iOSDeviceAssetSerialize, deserialize: _iOSDeviceAssetDeserialize, @@ -44,7 +37,7 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -57,16 +50,17 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _iOSDeviceAssetGetId, getLinks: _iOSDeviceAssetGetLinks, attach: _iOSDeviceAssetAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _iOSDeviceAssetEstimateSize( @@ -128,7 +122,10 @@ List> _iOSDeviceAssetGetLinks(IOSDeviceAsset object) { } void _iOSDeviceAssetAttach( - IsarCollection col, Id id, IOSDeviceAsset object) {} + IsarCollection col, + Id id, + IOSDeviceAsset object, +) {} extension IOSDeviceAssetByIndex on IsarCollection { Future getById(String id) { @@ -179,8 +176,10 @@ extension IOSDeviceAssetByIndex on IsarCollection { return putAllByIndex(r'id', objects); } - List putAllByIdSync(List objects, - {bool saveLinks = true}) { + List putAllByIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'id', objects, saveLinks: saveLinks); } } @@ -197,17 +196,17 @@ extension IOSDeviceAssetQueryWhereSort extension IOSDeviceAssetQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -230,7 +229,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -239,7 +238,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -254,101 +253,120 @@ extension IOSDeviceAssetQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } QueryBuilder idNotEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } QueryBuilder hashEqualTo( - List hash) { + List hash, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -357,134 +375,97 @@ extension IOSDeviceAssetQueryWhere extension IOSDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -506,43 +487,45 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -554,141 +537,143 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - idIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - idIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - isarIdEqualTo(Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); - }); - } - - QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, + String pattern, { + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdBetween( + idIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); + }); + } + + QueryBuilder + isarIdEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); + }); + } + + QueryBuilder + isarIdGreaterThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdLessThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -735,7 +720,7 @@ extension IOSDeviceAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -750,8 +735,9 @@ extension IOSDeviceAssetQueryWhereDistinct }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index c937697149..7b59e119d6 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -13,11 +13,11 @@ class SSLClientCertStoreVal { const SSLClientCertStoreVal(this.data, this.password); - void save() { + Future save() async { final b64Str = base64Encode(data); - Store.put(StoreKey.sslClientCertData, b64Str); + await Store.put(StoreKey.sslClientCertData, b64Str); if (password != null) { - Store.put(StoreKey.sslClientPasswd, password!); + await Store.put(StoreKey.sslClientPasswd, password!); } } @@ -31,8 +31,8 @@ class SSLClientCertStoreVal { return SSLClientCertStoreVal(certData, passwd); } - static void delete() { - Store.delete(StoreKey.sslClientCertData); - Store.delete(StoreKey.sslClientPasswd); + static Future delete() async { + await Store.delete(StoreKey.sslClientCertData); + await Store.delete(StoreKey.sslClientPasswd); } } diff --git a/mobile/lib/extensions/asset_extensions.dart b/mobile/lib/extensions/asset_extensions.dart index a5fa50983a..22d5d5030a 100644 --- a/mobile/lib/extensions/asset_extensions.dart +++ b/mobile/lib/extensions/asset_extensions.dart @@ -15,16 +15,10 @@ extension TZExtension on Asset { final location = getLocation(exifInfo!.timeZone!); dt = TZDateTime.from(dt, location); } on LocationNotFoundException { - RegExp re = RegExp( - r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', - caseSensitive: false, - ); + RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false); final m = re.firstMatch(exifInfo!.timeZone!); if (m != null) { - final duration = Duration( - hours: int.parse(m.group(1) ?? '0'), - minutes: int.parse(m.group(2) ?? '0'), - ); + final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0')); dt = dt.add(duration); return (dt, duration); } diff --git a/mobile/lib/extensions/asyncvalue_extensions.dart b/mobile/lib/extensions/asyncvalue_extensions.dart index 554c3a8a8a..c76d7e48d8 100644 --- a/mobile/lib/extensions/asyncvalue_extensions.dart +++ b/mobile/lib/extensions/asyncvalue_extensions.dart @@ -22,15 +22,13 @@ extension LogOnError on AsyncValue { } if (!skip) { - return onLoading?.call() ?? - const Center(child: ImmichLoadingIndicator()); + return onLoading?.call() ?? const Center(child: ImmichLoadingIndicator()); } } if (hasError && !hasValue) { _asyncErrorLogger.severe('Could not load value', error, stackTrace); - return onError?.call(error, stackTrace) ?? - ScaffoldErrorBody(errorMsg: error?.toString()); + return onError?.call(error, stackTrace) ?? ScaffoldErrorBody(errorMsg: error?.toString()); } return onData(requireValue); diff --git a/mobile/lib/extensions/build_context_extensions.dart b/mobile/lib/extensions/build_context_extensions.dart index 7bb194cdae..0a35bc1a86 100644 --- a/mobile/lib/extensions/build_context_extensions.dart +++ b/mobile/lib/extensions/build_context_extensions.dart @@ -13,6 +13,9 @@ extension ContextHelper on BuildContext { // Returns the current height from MediaQuery double get height => MediaQuery.sizeOf(this).height; + // Returns the current size from MediaQuery + Size get sizeData => MediaQuery.sizeOf(this); + // Returns true if the app is running on a mobile device (!tablets) bool get isMobile => width < 550; @@ -60,6 +63,5 @@ extension ContextHelper on BuildContext { FocusScopeNode get focusScope => FocusScope.of(this); // Show SnackBars from the current context - void showSnackBar(SnackBar snackBar) => - ScaffoldMessenger.of(this).showSnackBar(snackBar); + void showSnackBar(SnackBar snackBar) => ScaffoldMessenger.of(this).showSnackBar(snackBar); } diff --git a/mobile/lib/extensions/codec_extensions.dart b/mobile/lib/extensions/codec_extensions.dart new file mode 100644 index 0000000000..00c1158f0c --- /dev/null +++ b/mobile/lib/extensions/codec_extensions.dart @@ -0,0 +1,10 @@ +import 'dart:ui'; + +import 'package:flutter/painting.dart'; + +extension CodecImageInfoExtension on Codec { + Future getImageInfo({double scale = 1.0}) async { + final frame = await getNextFrame(); + return ImageInfo(image: frame.image, scale: scale); + } +} diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart index 95d2f74df8..541db7ccaf 100644 --- a/mobile/lib/extensions/collection_extensions.dart +++ b/mobile/lib/extensions/collection_extensions.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/hash.dart'; extension ListExtension on List { - List uniqueConsecutive({ - int Function(E a, E b)? compare, - void Function(E a, E b)? onDuplicate, - }) { + List uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) { compare ??= (E a, E b) => a == b ? 0 : 1; int i = 1, j = 1; for (; i < length; i++) { @@ -45,9 +42,7 @@ extension IntListExtension on Iterable { extension AssetListExtension on Iterable { /// Returns the assets that are already available in the Immich server - Iterable remoteOnly({ - void Function()? errorCallback, - }) { + Iterable remoteOnly({void Function()? errorCallback}) { final bool onlyRemote = every((e) => e.isRemote); if (!onlyRemote) { if (errorCallback != null) errorCallback(); @@ -58,10 +53,7 @@ extension AssetListExtension on Iterable { /// Returns the assets that are owned by the user passed to the [owner] param /// If [owner] is null, an empty list is returned - Iterable ownedOnly( - UserDto? owner, { - void Function()? errorCallback, - }) { + Iterable ownedOnly(UserDto? owner, {void Function()? errorCallback}) { if (owner == null) return []; final isarUserId = fastHash(owner.id); final bool onlyOwned = every((e) => e.ownerId == isarUserId); diff --git a/mobile/lib/extensions/datetime_extensions.dart b/mobile/lib/extensions/datetime_extensions.dart index e23bf5210f..0bc95565a6 100644 --- a/mobile/lib/extensions/datetime_extensions.dart +++ b/mobile/lib/extensions/datetime_extensions.dart @@ -47,19 +47,13 @@ extension DateRangeFormatting on DateTime { /// - Date range of this year: "Mar 23-May 31" /// - Date range of other year: "Aug 28 - Sep 30, 2023" /// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022" - static String formatDateRange( - DateTime startDate, - DateTime endDate, - Locale? locale, - ) { + static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) { final now = DateTime.now(); final currentYear = now.year; final localeString = locale?.toString() ?? 'en_US'; // Check if it's a single date (same day) - if (startDate.year == endDate.year && - startDate.month == endDate.month && - startDate.day == endDate.day) { + if (startDate.year == endDate.year && startDate.month == endDate.month && startDate.day == endDate.day) { if (startDate.year == currentYear) { // Single date of this year: "Aug 28" return DateFormat.MMMd(localeString).format(startDate); diff --git a/mobile/lib/extensions/maplibrecontroller_extensions.dart b/mobile/lib/extensions/maplibrecontroller_extensions.dart index 42d5e2c1d0..e1f32a4d8c 100644 --- a/mobile/lib/extensions/maplibrecontroller_extensions.dart +++ b/mobile/lib/extensions/maplibrecontroller_extensions.dart @@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController { Future addGeoJSONSourceForMarkers(List markers) async { return addSource( MapUtils.defaultSourceId, - GeojsonSourceProperties( - data: MapUtils.generateGeoJsonForMarkers(markers.toList()), - ), + GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())), ); } @@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController { try { final ByteData bytes = await rootBundle.load("assets/location-pin.png"); await addImage("mapMarker", bytes.buffer.asUint8List()); - return addSymbol( - SymbolOptions( - geometry: centre, - iconImage: "mapMarker", - iconSize: 0.15, - iconAnchor: "bottom", - ), - ); + return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom")); } finally { // no-op } } - Future getBoundsFromPoint( - Point point, - double distance, - ) async { + Future getBoundsFromPoint(Point point, double distance) async { final southWestPx = Point(point.x - distance, point.y + distance); final northEastPx = Point(point.x + distance, point.y - distance); diff --git a/mobile/lib/extensions/network_capability_extensions.dart b/mobile/lib/extensions/network_capability_extensions.dart new file mode 100644 index 0000000000..aeefc11e39 --- /dev/null +++ b/mobile/lib/extensions/network_capability_extensions.dart @@ -0,0 +1,8 @@ +import 'package:immich_mobile/platform/connectivity_api.g.dart'; + +extension NetworkCapabilitiesGetters on List { + bool get hasCellular => contains(NetworkCapability.cellular); + bool get hasWifi => contains(NetworkCapability.wifi); + bool get hasVpn => contains(NetworkCapability.vpn); + bool get isUnmetered => contains(NetworkCapability.unmetered); +} diff --git a/mobile/lib/extensions/platform_extensions.dart b/mobile/lib/extensions/platform_extensions.dart new file mode 100644 index 0000000000..7353fbc6f6 --- /dev/null +++ b/mobile/lib/extensions/platform_extensions.dart @@ -0,0 +1,9 @@ +import 'package:flutter/foundation.dart'; + +extension CurrentPlatform on TargetPlatform { + @pragma('vm:prefer-inline') + static bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS; + + @pragma('vm:prefer-inline') + static bool get isAndroid => defaultTargetPlatform == TargetPlatform.android; +} diff --git a/mobile/lib/extensions/scroll_extensions.dart b/mobile/lib/extensions/scroll_extensions.dart index 2752d0b77a..169032ff5d 100644 --- a/mobile/lib/extensions/scroll_extensions.dart +++ b/mobile/lib/extensions/scroll_extensions.dart @@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics { } @override - SpringDescription get spring => const SpringDescription( - mass: 1, - stiffness: 402.49984375, - damping: 40, - ); + SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40); } class FastClampingScrollPhysics extends ClampingScrollPhysics { @@ -27,12 +23,12 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics { @override SpringDescription get spring => const SpringDescription( - // When swiping between videos on Android, the placeholder of the first opened video - // can briefly be seen and cause a flicker effect if the video begins to initialize - // before the animation finishes - probably a bug in PhotoViewGallery's animation handling - // Making the animation faster is not just stylistic, but also helps to avoid this flicker - mass: 1, - stiffness: 1601.2499609375, - damping: 80, - ); + // When swiping between videos on Android, the placeholder of the first opened video + // can briefly be seen and cause a flicker effect if the video begins to initialize + // before the animation finishes - probably a bug in PhotoViewGallery's animation handling + // Making the animation faster is not just stylistic, but also helps to avoid this flicker + mass: 1, + stiffness: 1601.2499609375, + damping: 80, + ); } diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 67411013ee..ae31565044 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,10 +1,8 @@ +import 'dart:convert'; + extension StringExtension on String { String capitalize() { - return split(" ") - .map( - (str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1), - ) - .join(" "); + return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" "); } } @@ -12,9 +10,7 @@ extension DurationExtension on String { /// Parses and returns the string of format HH:MM:SS as a duration object else null Duration? toDuration() { try { - final parts = split(':') - .map((e) => double.parse(e).toInt()) - .toList(growable: false); + final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false); return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); } catch (e) { return null; @@ -29,3 +25,11 @@ extension DurationExtension on String { return int.parse(this); } } + +Map? tryJsonDecode(dynamic json) { + try { + return jsonDecode(json) as Map; + } catch (e) { + return null; + } +} diff --git a/mobile/lib/extensions/theme_extensions.dart b/mobile/lib/extensions/theme_extensions.dart index b81e4476e0..332dc58fc0 100644 --- a/mobile/lib/extensions/theme_extensions.dart +++ b/mobile/lib/extensions/theme_extensions.dart @@ -2,23 +2,15 @@ import 'package:flutter/material.dart'; extension ImmichColorSchemeExtensions on ColorScheme { bool get _isDarkMode => brightness == Brightness.dark; - Color get onSurfaceSecondary => _isDarkMode - ? onSurface.darken(amount: .3) - : onSurface.lighten(amount: .3); + Color get onSurfaceSecondary => _isDarkMode ? onSurface.darken(amount: .3) : onSurface.lighten(amount: .3); } extension ColorExtensions on Color { Color lighten({double amount = 0.1}) { - return Color.alphaBlend( - Colors.white.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.white.withValues(alpha: amount), this); } Color darken({double amount = 0.1}) { - return Color.alphaBlend( - Colors.black.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.black.withValues(alpha: amount), this); } } diff --git a/mobile/lib/extensions/translate_extensions.dart b/mobile/lib/extensions/translate_extensions.dart index 122830843d..7677f3cbd8 100644 --- a/mobile/lib/extensions/translate_extensions.dart +++ b/mobile/lib/extensions/translate_extensions.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:intl/message_format.dart'; import 'package:flutter/material.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; extension StringTranslateExtension on String { String t({BuildContext? context, Map? args}) { @@ -29,22 +30,17 @@ extension TextTranslateExtension on Text { } } -String _translateHelper( - BuildContext? context, - String key, [ - Map? args, -]) { +String _translateHelper(BuildContext? context, String key, [Map? args]) { if (key.isEmpty) { return ''; } try { final translatedMessage = key.tr(context: context); return args != null - ? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en') - .format(args) + ? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en').format(args) : translatedMessage; } catch (e) { - debugPrint('Translation failed for key "$key". Error: $e'); + dPrint(() => 'Translation failed for key "$key". Error: $e'); return key; } } diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.dart b/mobile/lib/infrastructure/entities/asset_face.entity.dart new file mode 100644 index 0000000000..5f793030c3 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.dart @@ -0,0 +1,31 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class AssetFaceEntity extends Table with DriftDefaultsMixin { + const AssetFaceEntity(); + + TextColumn get id => text()(); + + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get personId => text().nullable().references(PersonEntity, #id, onDelete: KeyAction.setNull)(); + + IntColumn get imageWidth => integer()(); + + IntColumn get imageHeight => integer()(); + + IntColumn get boundingBoxX1 => integer()(); + + IntColumn get boundingBoxY1 => integer()(); + + IntColumn get boundingBoxX2 => integer()(); + + IntColumn get boundingBoxY2 => integer()(); + + TextColumn get sourceType => text()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart new file mode 100644 index 0000000000..092fcc5859 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -0,0 +1,1209 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:drift/internal/modular.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i5; + +typedef $$AssetFaceEntityTableCreateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, + }); + +final class $$AssetFaceEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData + > { + $$AssetFaceEntityTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .assetId, + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); + + i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { + final $_column = $_itemColumn('asset_id')!; + + final manager = i3 + .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( + $_db, + ).resultSet('remote_asset_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .createAlias( + i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .personId, + i4.ReadDatabaseContainer( + db, + ).resultSet('person_entity').id, + ), + ); + + i5.$$PersonEntityTableProcessedTableManager? get personId { + final $_column = $_itemColumn('person_id'); + if ($_column == null) return null; + final manager = i5 + .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( + $_db, + ).resultSet('person_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_personIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$AssetFaceEntityTableFilterComposer + extends i0.Composer { + $$AssetFaceEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnFilters(column), + ); + + i3.$$RemoteAssetEntityTableFilterComposer get assetId { + final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableFilterComposer get personId { + final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableOrderingComposer + extends i0.Composer { + $$AssetFaceEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column), + ); + + i3.$$RemoteAssetEntityTableOrderingComposer get assetId { + final i3.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableOrderingComposer get personId { + final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableAnnotationComposer + extends i0.Composer { + $$AssetFaceEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => column, + ); + + i0.GeneratedColumn get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => column, + ); + + i0.GeneratedColumn get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => column, + ); + + i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { + final i3.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableAnnotationComposer get personId { + final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + > { + $$AssetFaceEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$AssetFaceEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$AssetFaceEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => i1 + .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: + ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({assetId = false, personId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (personId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$AssetFaceEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + >; + +class $AssetFaceEntityTable extends i2.AssetFaceEntity + with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $AssetFaceEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); + @override + late final i0.GeneratedColumn assetId = i0.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _personIdMeta = const i0.VerificationMeta( + 'personId', + ); + @override + late final i0.GeneratedColumn personId = i0.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + static const i0.VerificationMeta _imageWidthMeta = const i0.VerificationMeta( + 'imageWidth', + ); + @override + late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _imageHeightMeta = const i0.VerificationMeta( + 'imageHeight', + ); + @override + late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxX1Meta = + const i0.VerificationMeta('boundingBoxX1'); + @override + late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxY1Meta = + const i0.VerificationMeta('boundingBoxY1'); + @override + late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxX2Meta = + const i0.VerificationMeta('boundingBoxX2'); + @override + late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxY2Meta = + const i0.VerificationMeta('boundingBoxY2'); + @override + late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _sourceTypeMeta = const i0.VerificationMeta( + 'sourceType', + ); + @override + late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('asset_id')) { + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); + } else if (isInserting) { + context.missing(_assetIdMeta); + } + if (data.containsKey('person_id')) { + context.handle( + _personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta), + ); + } + if (data.containsKey('image_width')) { + context.handle( + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown(data['image_width']!, _imageWidthMeta), + ); + } else if (isInserting) { + context.missing(_imageWidthMeta); + } + if (data.containsKey('image_height')) { + context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, + _imageHeightMeta, + ), + ); + } else if (isInserting) { + context.missing(_imageHeightMeta); + } + if (data.containsKey('bounding_box_x1')) { + context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, + _boundingBoxX1Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxX1Meta); + } + if (data.containsKey('bounding_box_y1')) { + context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, + _boundingBoxY1Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxY1Meta); + } + if (data.containsKey('bounding_box_x2')) { + context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, + _boundingBoxX2Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxX2Meta); + } + if (data.containsKey('bounding_box_y2')) { + context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, + _boundingBoxY2Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxY2Meta); + } + if (data.containsKey('source_type')) { + context.handle( + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown(data['source_type']!, _sourceTypeMeta), + ); + } else if (isInserting) { + context.missing(_sourceTypeMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + $AssetFaceEntityTable createAlias(String alias) { + return $AssetFaceEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['asset_id'] = i0.Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = i0.Variable(personId); + } + map['image_width'] = i0.Variable(imageWidth); + map['image_height'] = i0.Variable(imageHeight); + map['bounding_box_x1'] = i0.Variable(boundingBoxX1); + map['bounding_box_y1'] = i0.Variable(boundingBoxY1); + map['bounding_box_x2'] = i0.Variable(boundingBoxX2); + map['bounding_box_y2'] = i0.Variable(boundingBoxY2); + map['source_type'] = i0.Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + i1.AssetFaceEntityData copyWith({ + String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value assetId; + final i0.Value personId; + final i0.Value imageWidth; + final i0.Value imageHeight; + final i0.Value boundingBoxX1; + final i0.Value boundingBoxY1; + final i0.Value boundingBoxX2; + final i0.Value boundingBoxY2; + final i0.Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const i0.Value.absent(), + this.assetId = const i0.Value.absent(), + this.personId = const i0.Value.absent(), + this.imageWidth = const i0.Value.absent(), + this.imageHeight = const i0.Value.absent(), + this.boundingBoxX1 = const i0.Value.absent(), + this.boundingBoxY1 = const i0.Value.absent(), + this.boundingBoxX2 = const i0.Value.absent(), + this.boundingBoxY2 = const i0.Value.absent(), + this.sourceType = const i0.Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? assetId, + i0.Expression? personId, + i0.Expression? imageWidth, + i0.Expression? imageHeight, + i0.Expression? boundingBoxX1, + i0.Expression? boundingBoxY1, + i0.Expression? boundingBoxX2, + i0.Expression? boundingBoxY2, + i0.Expression? sourceType, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + i1.AssetFaceEntityCompanion copyWith({ + i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType, + }) { + return i1.AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = i0.Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = i0.Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = i0.Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = i0.Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = i0.Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = i0.Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = i0.Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = i0.Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = i0.Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/auth_user.entity.dart b/mobile/lib/infrastructure/entities/auth_user.entity.dart new file mode 100644 index 0000000000..cbbeaf536f --- /dev/null +++ b/mobile/lib/infrastructure/entities/auth_user.entity.dart @@ -0,0 +1,27 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class AuthUserEntity extends Table with DriftDefaultsMixin { + const AuthUserEntity(); + + TextColumn get id => text()(); + TextColumn get name => text()(); + TextColumn get email => text()(); + BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); + + // Profile image + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + IntColumn get avatarColor => intEnum()(); + + // Quota + IntColumn get quotaSizeInBytes => integer().withDefault(const Constant(0))(); + IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); + + // Locked Folder + TextColumn get pinCode => text().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/auth_user.entity.drift.dart b/mobile/lib/infrastructure/entities/auth_user.entity.drift.dart new file mode 100644 index 0000000000..4dba1c42fb --- /dev/null +++ b/mobile/lib/infrastructure/entities/auth_user.entity.drift.dart @@ -0,0 +1,933 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart' + as i1; +import 'package:immich_mobile/domain/models/user.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.dart' + as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; + +typedef $$AuthUserEntityTableCreateCompanionBuilder = + i1.AuthUserEntityCompanion Function({ + required String id, + required String name, + required String email, + i0.Value isAdmin, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + required i2.AvatarColor avatarColor, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + i0.Value pinCode, + }); +typedef $$AuthUserEntityTableUpdateCompanionBuilder = + i1.AuthUserEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value email, + i0.Value isAdmin, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + i0.Value avatarColor, + i0.Value quotaSizeInBytes, + i0.Value quotaUsageInBytes, + i0.Value pinCode, + }); + +class $$AuthUserEntityTableFilterComposer + extends i0.Composer { + $$AuthUserEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get email => $composableBuilder( + column: $table.email, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get isAdmin => $composableBuilder( + column: $table.isAdmin, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnWithTypeConverterFilters + get avatarColor => $composableBuilder( + column: $table.avatarColor, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); + + i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get pinCode => $composableBuilder( + column: $table.pinCode, + builder: (column) => i0.ColumnFilters(column), + ); +} + +class $$AuthUserEntityTableOrderingComposer + extends i0.Composer { + $$AuthUserEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get email => $composableBuilder( + column: $table.email, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get isAdmin => $composableBuilder( + column: $table.isAdmin, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get avatarColor => $composableBuilder( + column: $table.avatarColor, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get pinCode => $composableBuilder( + column: $table.pinCode, + builder: (column) => i0.ColumnOrderings(column), + ); +} + +class $$AuthUserEntityTableAnnotationComposer + extends i0.Composer { + $$AuthUserEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + i0.GeneratedColumn get email => + $composableBuilder(column: $table.email, builder: (column) => column); + + i0.GeneratedColumn get isAdmin => + $composableBuilder(column: $table.isAdmin, builder: (column) => column); + + i0.GeneratedColumn get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => column, + ); + + i0.GeneratedColumn get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => column, + ); + + i0.GeneratedColumnWithTypeConverter get avatarColor => + $composableBuilder( + column: $table.avatarColor, + builder: (column) => column, + ); + + i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( + column: $table.quotaSizeInBytes, + builder: (column) => column, + ); + + i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( + column: $table.quotaUsageInBytes, + builder: (column) => column, + ); + + i0.GeneratedColumn get pinCode => + $composableBuilder(column: $table.pinCode, builder: (column) => column); +} + +class $$AuthUserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AuthUserEntityTable, + i1.AuthUserEntityData, + i1.$$AuthUserEntityTableFilterComposer, + i1.$$AuthUserEntityTableOrderingComposer, + i1.$$AuthUserEntityTableAnnotationComposer, + $$AuthUserEntityTableCreateCompanionBuilder, + $$AuthUserEntityTableUpdateCompanionBuilder, + ( + i1.AuthUserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AuthUserEntityTable, + i1.AuthUserEntityData + >, + ), + i1.AuthUserEntityData, + i0.PrefetchHooks Function() + > { + $$AuthUserEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$AuthUserEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$AuthUserEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$AuthUserEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => i1 + .$$AuthUserEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value isAdmin = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + i0.Value avatarColor = const i0.Value.absent(), + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + i0.Value pinCode = const i0.Value.absent(), + }) => i1.AuthUserEntityCompanion( + id: id, + name: name, + email: email, + isAdmin: isAdmin, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + avatarColor: avatarColor, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + pinCode: pinCode, + ), + createCompanionCallback: + ({ + required String id, + required String name, + required String email, + i0.Value isAdmin = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + required i2.AvatarColor avatarColor, + i0.Value quotaSizeInBytes = const i0.Value.absent(), + i0.Value quotaUsageInBytes = const i0.Value.absent(), + i0.Value pinCode = const i0.Value.absent(), + }) => i1.AuthUserEntityCompanion.insert( + id: id, + name: name, + email: email, + isAdmin: isAdmin, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + avatarColor: avatarColor, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + pinCode: pinCode, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$AuthUserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AuthUserEntityTable, + i1.AuthUserEntityData, + i1.$$AuthUserEntityTableFilterComposer, + i1.$$AuthUserEntityTableOrderingComposer, + i1.$$AuthUserEntityTableAnnotationComposer, + $$AuthUserEntityTableCreateCompanionBuilder, + $$AuthUserEntityTableUpdateCompanionBuilder, + ( + i1.AuthUserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AuthUserEntityTable, + i1.AuthUserEntityData + >, + ), + i1.AuthUserEntityData, + i0.PrefetchHooks Function() + >; + +class $AuthUserEntityTable extends i3.AuthUserEntity + with i0.TableInfo<$AuthUserEntityTable, i1.AuthUserEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $AuthUserEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); + @override + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta( + 'email', + ); + @override + late final i0.GeneratedColumn email = i0.GeneratedColumn( + 'email', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isAdminMeta = const i0.VerificationMeta( + 'isAdmin', + ); + @override + late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _hasProfileImageMeta = + const i0.VerificationMeta('hasProfileImage'); + @override + late final i0.GeneratedColumn hasProfileImage = + i0.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _profileChangedAtMeta = + const i0.VerificationMeta('profileChangedAt'); + @override + late final i0.GeneratedColumn profileChangedAt = + i0.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + @override + late final i0.GeneratedColumnWithTypeConverter + avatarColor = + i0.GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$AuthUserEntityTable.$converteravatarColor, + ); + static const i0.VerificationMeta _quotaSizeInBytesMeta = + const i0.VerificationMeta('quotaSizeInBytes'); + @override + late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); + static const i0.VerificationMeta _quotaUsageInBytesMeta = + const i0.VerificationMeta('quotaUsageInBytes'); + @override + late final i0.GeneratedColumn quotaUsageInBytes = + i0.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); + static const i0.VerificationMeta _pinCodeMeta = const i0.VerificationMeta( + 'pinCode', + ); + @override + late final i0.GeneratedColumn pinCode = i0.GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'auth_user_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('email')) { + context.handle( + _emailMeta, + email.isAcceptableOrUnknown(data['email']!, _emailMeta), + ); + } else if (isInserting) { + context.missing(_emailMeta); + } + if (data.containsKey('is_admin')) { + context.handle( + _isAdminMeta, + isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta), + ); + } + if (data.containsKey('has_profile_image')) { + context.handle( + _hasProfileImageMeta, + hasProfileImage.isAcceptableOrUnknown( + data['has_profile_image']!, + _hasProfileImageMeta, + ), + ); + } + if (data.containsKey('profile_changed_at')) { + context.handle( + _profileChangedAtMeta, + profileChangedAt.isAcceptableOrUnknown( + data['profile_changed_at']!, + _profileChangedAtMeta, + ), + ); + } + if (data.containsKey('quota_size_in_bytes')) { + context.handle( + _quotaSizeInBytesMeta, + quotaSizeInBytes.isAcceptableOrUnknown( + data['quota_size_in_bytes']!, + _quotaSizeInBytesMeta, + ), + ); + } + if (data.containsKey('quota_usage_in_bytes')) { + context.handle( + _quotaUsageInBytesMeta, + quotaUsageInBytes.isAcceptableOrUnknown( + data['quota_usage_in_bytes']!, + _quotaUsageInBytesMeta, + ), + ); + } + if (data.containsKey('pin_code')) { + context.handle( + _pinCodeMeta, + pinCode.isAcceptableOrUnknown(data['pin_code']!, _pinCodeMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.AuthUserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.AuthUserEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: i1.$AuthUserEntityTable.$converteravatarColor.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ), + quotaSizeInBytes: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + )!, + quotaUsageInBytes: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + pinCode: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}pin_code'], + ), + ); + } + + @override + $AuthUserEntityTable createAlias(String alias) { + return $AuthUserEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 $converteravatarColor = + const i0.EnumIndexConverter(i2.AvatarColor.values); + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AuthUserEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String name; + final String email; + final bool isAdmin; + final bool hasProfileImage; + final DateTime profileChangedAt; + final i2.AvatarColor avatarColor; + final int quotaSizeInBytes; + final int quotaUsageInBytes; + final String? pinCode; + const AuthUserEntityData({ + required this.id, + required this.name, + required this.email, + required this.isAdmin, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + this.pinCode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['name'] = i0.Variable(name); + map['email'] = i0.Variable(email); + map['is_admin'] = i0.Variable(isAdmin); + map['has_profile_image'] = i0.Variable(hasProfileImage); + map['profile_changed_at'] = i0.Variable(profileChangedAt); + { + map['avatar_color'] = i0.Variable( + i1.$AuthUserEntityTable.$converteravatarColor.toSql(avatarColor), + ); + } + map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); + map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); + if (!nullToAbsent || pinCode != null) { + map['pin_code'] = i0.Variable(pinCode); + } + return map; + } + + factory AuthUserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return AuthUserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + isAdmin: serializer.fromJson(json['isAdmin']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: i1.$AuthUserEntityTable.$converteravatarColor.fromJson( + serializer.fromJson(json['avatarColor']), + ), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + pinCode: serializer.fromJson(json['pinCode']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'isAdmin': serializer.toJson(isAdmin), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson( + i1.$AuthUserEntityTable.$converteravatarColor.toJson(avatarColor), + ), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'pinCode': serializer.toJson(pinCode), + }; + } + + i1.AuthUserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? isAdmin, + bool? hasProfileImage, + DateTime? profileChangedAt, + i2.AvatarColor? avatarColor, + int? quotaSizeInBytes, + int? quotaUsageInBytes, + i0.Value pinCode = const i0.Value.absent(), + }) => i1.AuthUserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode.present ? pinCode.value : this.pinCode, + ); + AuthUserEntityData copyWithCompanion(i1.AuthUserEntityCompanion data) { + return AuthUserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode, + ); + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.AuthUserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.isAdmin == this.isAdmin && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes && + other.pinCode == this.pinCode); +} + +class AuthUserEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value name; + final i0.Value email; + final i0.Value isAdmin; + final i0.Value hasProfileImage; + final i0.Value profileChangedAt; + final i0.Value avatarColor; + final i0.Value quotaSizeInBytes; + final i0.Value quotaUsageInBytes; + final i0.Value pinCode; + const AuthUserEntityCompanion({ + this.id = const i0.Value.absent(), + this.name = const i0.Value.absent(), + this.email = const i0.Value.absent(), + this.isAdmin = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), + this.avatarColor = const i0.Value.absent(), + this.quotaSizeInBytes = const i0.Value.absent(), + this.quotaUsageInBytes = const i0.Value.absent(), + this.pinCode = const i0.Value.absent(), + }); + AuthUserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.isAdmin = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), + required i2.AvatarColor avatarColor, + this.quotaSizeInBytes = const i0.Value.absent(), + this.quotaUsageInBytes = const i0.Value.absent(), + this.pinCode = const i0.Value.absent(), + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email), + avatarColor = i0.Value(avatarColor); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? name, + i0.Expression? email, + i0.Expression? isAdmin, + i0.Expression? hasProfileImage, + i0.Expression? profileChangedAt, + i0.Expression? avatarColor, + i0.Expression? quotaSizeInBytes, + i0.Expression? quotaUsageInBytes, + i0.Expression? pinCode, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (isAdmin != null) 'is_admin': isAdmin, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (pinCode != null) 'pin_code': pinCode, + }); + } + + i1.AuthUserEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? email, + i0.Value? isAdmin, + i0.Value? hasProfileImage, + i0.Value? profileChangedAt, + i0.Value? avatarColor, + i0.Value? quotaSizeInBytes, + i0.Value? quotaUsageInBytes, + i0.Value? pinCode, + }) { + return i1.AuthUserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode ?? this.pinCode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (name.present) { + map['name'] = i0.Variable(name.value); + } + if (email.present) { + map['email'] = i0.Variable(email.value); + } + if (isAdmin.present) { + map['is_admin'] = i0.Variable(isAdmin.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = i0.Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = i0.Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = i0.Variable( + i1.$AuthUserEntityTable.$converteravatarColor.toSql(avatarColor.value), + ); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); + } + if (pinCode.present) { + map['pin_code'] = i0.Variable(pinCode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.dart b/mobile/lib/infrastructure/entities/device_asset.entity.dart index d8bfb2aa45..e3e4a0d4f4 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.dart @@ -16,21 +16,10 @@ class DeviceAssetEntity { final List hash; final DateTime modifiedTime; - const DeviceAssetEntity({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime}); - DeviceAsset toModel() => DeviceAsset( - assetId: assetId, - hash: Uint8List.fromList(hash), - modifiedTime: modifiedTime, - ); + DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime); - static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity( - assetId: dto.assetId, - hash: dto.hash, - modifiedTime: dto.modifiedTime, - ); + static DeviceAssetEntity fromDto(DeviceAsset dto) => + DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime); } diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart index a66f8288ef..b6c30aca6f 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart @@ -17,22 +17,15 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'DeviceAssetEntity', id: 6967030785073446271, properties: { - r'assetId': PropertySchema( - id: 0, - name: r'assetId', - type: IsarType.string, - ), - r'hash': PropertySchema( - id: 1, - name: r'hash', - type: IsarType.byteList, - ), + r'assetId': PropertySchema(id: 0, name: r'assetId', type: IsarType.string), + r'hash': PropertySchema(id: 1, name: r'hash', type: IsarType.byteList), r'modifiedTime': PropertySchema( id: 2, name: r'modifiedTime', type: IsarType.dateTime, - ) + ), }, + estimateSize: _deviceAssetEntityEstimateSize, serialize: _deviceAssetEntitySerialize, deserialize: _deviceAssetEntityDeserialize, @@ -49,7 +42,7 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'assetId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -62,16 +55,17 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _deviceAssetEntityGetId, getLinks: _deviceAssetEntityGetLinks, attach: _deviceAssetEntityAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _deviceAssetEntityEstimateSize( @@ -133,12 +127,16 @@ Id _deviceAssetEntityGetId(DeviceAssetEntity object) { } List> _deviceAssetEntityGetLinks( - DeviceAssetEntity object) { + DeviceAssetEntity object, +) { return []; } void _deviceAssetEntityAttach( - IsarCollection col, Id id, DeviceAssetEntity object) {} + IsarCollection col, + Id id, + DeviceAssetEntity object, +) {} extension DeviceAssetEntityByIndex on IsarCollection { Future getByAssetId(String assetId) { @@ -189,8 +187,10 @@ extension DeviceAssetEntityByIndex on IsarCollection { return putAllByIndex(r'assetId', objects); } - List putAllByAssetIdSync(List objects, - {bool saveLinks = true}) { + List putAllByAssetIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'assetId', objects, saveLinks: saveLinks); } } @@ -207,17 +207,14 @@ extension DeviceAssetEntityQueryWhereSort extension DeviceAssetEntityQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -240,7 +237,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -249,7 +246,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -258,108 +255,124 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - assetIdEqualTo(String assetId) { + assetIdEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'assetId', - value: [assetId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'assetId', value: [assetId]), + ); }); } QueryBuilder - assetIdNotEqualTo(String assetId) { + assetIdNotEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ); } }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -368,53 +381,56 @@ extension DeviceAssetEntityQueryWhere extension DeviceAssetEntityQueryFilter on QueryBuilder { QueryBuilder - assetIdEqualTo( - String value, { - bool caseSensitive = true, - }) { + assetIdEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdGreaterThan( + assetIdGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdLessThan( + assetIdLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdBetween( + assetIdBetween( String lower, String upper, { bool includeLower = true, @@ -422,216 +438,181 @@ extension DeviceAssetEntityQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdEndsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdContains(String value, {bool caseSensitive = true}) { + assetIdContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdMatches(String pattern, {bool caseSensitive = true}) { + assetIdMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'assetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'assetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdIsEmpty() { + assetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetId', value: ''), + ); }); } QueryBuilder - assetIdIsNotEmpty() { + assetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'assetId', value: ''), + ); }); } QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -649,114 +630,112 @@ extension DeviceAssetEntityQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - modifiedTimeEqualTo(DateTime value) { + modifiedTimeEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedTime', value: value), + ); }); } QueryBuilder - modifiedTimeGreaterThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeLessThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeBetween( + modifiedTimeBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedTime', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -770,28 +749,28 @@ extension DeviceAssetEntityQueryLinks extension DeviceAssetEntityQuerySortBy on QueryBuilder { QueryBuilder - sortByAssetId() { + sortByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - sortByAssetIdDesc() { + sortByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); } QueryBuilder - sortByModifiedTime() { + sortByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - sortByModifiedTimeDesc() { + sortByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -801,14 +780,14 @@ extension DeviceAssetEntityQuerySortBy extension DeviceAssetEntityQuerySortThenBy on QueryBuilder { QueryBuilder - thenByAssetId() { + thenByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - thenByAssetIdDesc() { + thenByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); @@ -821,21 +800,21 @@ extension DeviceAssetEntityQuerySortThenBy } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); } QueryBuilder - thenByModifiedTime() { + thenByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - thenByModifiedTimeDesc() { + thenByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -845,21 +824,21 @@ extension DeviceAssetEntityQuerySortThenBy extension DeviceAssetEntityQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByAssetId({bool caseSensitive = true}) { + distinctByAssetId({bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'assetId', caseSensitive: caseSensitive); }); } QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); } QueryBuilder - distinctByModifiedTime() { + distinctByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'modifiedTime'); }); @@ -887,7 +866,7 @@ extension DeviceAssetEntityQueryProperty } QueryBuilder - modifiedTimeProperty() { + modifiedTimeProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'modifiedTime'); }); diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index 2ec8f0023f..9c7f9e9975 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -52,54 +52,54 @@ class ExifInfo { }); static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo( - id: dto.assetId, - fileSize: dto.fileSize, - dateTimeOriginal: dto.dateTimeOriginal, - timeZone: dto.timeZone, - make: dto.make, - model: dto.model, - lens: dto.lens, - f: dto.f, - mm: dto.mm, - iso: dto.iso?.toInt(), - exposureSeconds: dto.exposureSeconds, - lat: dto.latitude, - long: dto.longitude, - city: dto.city, - state: dto.state, - country: dto.country, - description: dto.description, - orientation: dto.orientation, - ); + id: dto.assetId, + fileSize: dto.fileSize, + dateTimeOriginal: dto.dateTimeOriginal, + timeZone: dto.timeZone, + make: dto.make, + model: dto.model, + lens: dto.lens, + f: dto.f, + mm: dto.mm, + iso: dto.iso?.toInt(), + exposureSeconds: dto.exposureSeconds, + lat: dto.latitude, + long: dto.longitude, + city: dto.city, + state: dto.state, + country: dto.country, + description: dto.description, + orientation: dto.orientation, + ); domain.ExifInfo toDto() => domain.ExifInfo( - assetId: id, - fileSize: fileSize, - description: description, - orientation: orientation, - timeZone: timeZone, - dateTimeOriginal: dateTimeOriginal, - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - latitude: lat, - longitude: long, - city: city, - state: state, - country: country, - make: make, - model: model, - lens: lens, - f: f, - mm: mm, - iso: iso?.toInt(), - exposureSeconds: exposureSeconds, - ); + assetId: id, + fileSize: fileSize, + description: description, + orientation: orientation, + timeZone: timeZone, + dateTimeOriginal: dateTimeOriginal, + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + latitude: lat, + longitude: long, + city: city, + state: state, + country: country, + make: make, + model: model, + lens: lens, + f: f, + mm: mm, + iso: iso?.toInt(), + exposureSeconds: exposureSeconds, + ); } +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)') class RemoteExifEntity extends Table with DriftDefaultsMixin { const RemoteExifEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); TextColumn get city => text().nullable()(); @@ -149,24 +149,24 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { extension RemoteExifEntityDataDomainEx on RemoteExifEntityData { domain.ExifInfo toDto() => domain.ExifInfo( - fileSize: fileSize, - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - make: make, - model: model, - iso: iso, - city: city, - state: state, - country: country, - description: description, - orientation: orientation, - latitude: latitude, - longitude: longitude, - f: fNumber?.toDouble(), - mm: focalLength?.toDouble(), - lens: lens, - width: width?.toDouble(), - height: height?.toDouble(), - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - ); + fileSize: fileSize, + dateTimeOriginal: dateTimeOriginal, + timeZone: timeZone, + make: make, + model: model, + iso: iso, + city: city, + state: state, + country: country, + description: description, + orientation: orientation, + latitude: latitude, + longitude: longitude, + f: fNumber?.toDouble(), + mm: focalLength?.toDouble(), + lens: lens, + width: width?.toDouble(), + height: height?.toDouble(), + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + ); } diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index 10712948ea..8695e2004b 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -8,86 +8,100 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift. as i3; import 'package:drift/internal/modular.dart' as i4; -typedef $$RemoteExifEntityTableCreateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - required String assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); -typedef $$RemoteExifEntityTableUpdateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - i0.Value assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); +typedef $$RemoteExifEntityTableCreateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + required String assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); +typedef $$RemoteExifEntityTableUpdateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + i0.Value assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); -final class $$RemoteExifEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$RemoteExifEntityTable, i1.RemoteExifEntityData> { +final class $$RemoteExifEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData + > { $$RemoteExifEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('remote_exif_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -101,93 +115,134 @@ class $$RemoteExifEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnFilters(column)); + column: $table.city, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnFilters(column)); + column: $table.state, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnFilters(column)); + column: $table.country, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnFilters(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnFilters(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnFilters(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnFilters(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.latitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get longitude => $composableBuilder( - column: $table.longitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.longitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnFilters(column)); + column: $table.iso, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnFilters(column)); + column: $table.make, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnFilters(column)); + column: $table.model, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnFilters(column)); + column: $table.lens, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnFilters(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnFilters(column)); + column: $table.rating, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnFilters(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -202,96 +257,135 @@ class $$RemoteExifEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnOrderings(column)); + column: $table.city, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnOrderings(column)); + column: $table.state, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnOrderings(column)); + column: $table.country, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnOrderings(column)); + column: $table.latitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get longitude => $composableBuilder( - column: $table.longitude, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.longitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnOrderings(column)); + column: $table.iso, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnOrderings(column)); + column: $table.make, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnOrderings(column)); + column: $table.model, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnOrderings(column)); + column: $table.lens, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnOrderings(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnOrderings(column)); + column: $table.rating, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -315,10 +409,14 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.country, builder: (column) => column); i0.GeneratedColumn get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, builder: (column) => column); + column: $table.dateTimeOriginal, + builder: (column) => column, + ); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get height => $composableBuilder(column: $table.height, builder: (column) => column); @@ -327,7 +425,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.width, builder: (column) => column); i0.GeneratedColumn get exposureTime => $composableBuilder( - column: $table.exposureTime, builder: (column) => column); + column: $table.exposureTime, + builder: (column) => column, + ); i0.GeneratedColumn get fNumber => $composableBuilder(column: $table.fNumber, builder: (column) => column); @@ -336,7 +436,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.fileSize, builder: (column) => column); i0.GeneratedColumn get focalLength => $composableBuilder( - column: $table.focalLength, builder: (column) => column); + column: $table.focalLength, + builder: (column) => column, + ); i0.GeneratedColumn get latitude => $composableBuilder(column: $table.latitude, builder: (column) => column); @@ -357,7 +459,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.lens, builder: (column) => column); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); i0.GeneratedColumn get timeZone => $composableBuilder(column: $table.timeZone, builder: (column) => column); @@ -366,48 +470,59 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.rating, builder: (column) => column); i0.GeneratedColumn get projectionType => $composableBuilder( - column: $table.projectionType, builder: (column) => column); + column: $table.projectionType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})> { +class $$RemoteExifEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + > { $$RemoteExifEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteExifEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteExifEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -416,115 +531,120 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< .$$RemoteExifEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteExifEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), - createCompanionCallback: ({ - required String assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion.insert( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), + createCompanionCallback: + ({ + required String assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion.insert( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteExifEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteExifEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -535,41 +655,54 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$RemoteExifEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$RemoteExifEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteExifEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})>; +typedef $$RemoteExifEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + >; +i0.Index get idxLatLng => i0.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', +); class $RemoteExifEntityTable extends i2.RemoteExifEntity with i0.TableInfo<$RemoteExifEntityTable, i1.RemoteExifEntityData> { @@ -577,165 +710,277 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteExifEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _cityMeta = - const i0.VerificationMeta('city'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _cityMeta = const i0.VerificationMeta( + 'city', + ); @override late final i0.GeneratedColumn city = i0.GeneratedColumn( - 'city', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _stateMeta = - const i0.VerificationMeta('state'); + 'city', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stateMeta = const i0.VerificationMeta( + 'state', + ); @override late final i0.GeneratedColumn state = i0.GeneratedColumn( - 'state', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _countryMeta = - const i0.VerificationMeta('country'); + 'state', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _countryMeta = const i0.VerificationMeta( + 'country', + ); @override late final i0.GeneratedColumn country = i0.GeneratedColumn( - 'country', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _dateTimeOriginalMeta = const i0.VerificationMeta('dateTimeOriginal'); @override late final i0.GeneratedColumn dateTimeOriginal = - i0.GeneratedColumn('date_time_original', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + i0.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + i0.GeneratedColumn( + 'description', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _exposureTimeMeta = const i0.VerificationMeta('exposureTime'); @override late final i0.GeneratedColumn exposureTime = - i0.GeneratedColumn('exposure_time', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _fNumberMeta = - const i0.VerificationMeta('fNumber'); + i0.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fNumberMeta = const i0.VerificationMeta( + 'fNumber', + ); @override late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( - 'f_number', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _fileSizeMeta = - const i0.VerificationMeta('fileSize'); + 'f_number', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fileSizeMeta = const i0.VerificationMeta( + 'fileSize', + ); @override late final i0.GeneratedColumn fileSize = i0.GeneratedColumn( - 'file_size', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _focalLengthMeta = - const i0.VerificationMeta('focalLength'); + 'file_size', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _focalLengthMeta = const i0.VerificationMeta( + 'focalLength', + ); @override late final i0.GeneratedColumn focalLength = - i0.GeneratedColumn('focal_length', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _latitudeMeta = - const i0.VerificationMeta('latitude'); + i0.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta( + 'latitude', + ); @override late final i0.GeneratedColumn latitude = i0.GeneratedColumn( - 'latitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _longitudeMeta = - const i0.VerificationMeta('longitude'); + 'latitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta( + 'longitude', + ); @override late final i0.GeneratedColumn longitude = i0.GeneratedColumn( - 'longitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _isoMeta = const i0.VerificationMeta('iso'); @override late final i0.GeneratedColumn iso = i0.GeneratedColumn( - 'iso', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _makeMeta = - const i0.VerificationMeta('make'); + 'iso', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _makeMeta = const i0.VerificationMeta( + 'make', + ); @override late final i0.GeneratedColumn make = i0.GeneratedColumn( - 'make', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _modelMeta = - const i0.VerificationMeta('model'); + 'make', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _modelMeta = const i0.VerificationMeta( + 'model', + ); @override late final i0.GeneratedColumn model = i0.GeneratedColumn( - 'model', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _lensMeta = - const i0.VerificationMeta('lens'); + 'model', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _lensMeta = const i0.VerificationMeta( + 'lens', + ); @override late final i0.GeneratedColumn lens = i0.GeneratedColumn( - 'lens', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'lens', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = - i0.GeneratedColumn('orientation', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _timeZoneMeta = - const i0.VerificationMeta('timeZone'); + i0.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _timeZoneMeta = const i0.VerificationMeta( + 'timeZone', + ); @override late final i0.GeneratedColumn timeZone = i0.GeneratedColumn( - 'time_zone', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _ratingMeta = - const i0.VerificationMeta('rating'); + 'time_zone', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ratingMeta = const i0.VerificationMeta( + 'rating', + ); @override late final i0.GeneratedColumn rating = i0.GeneratedColumn( - 'rating', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _projectionTypeMeta = const i0.VerificationMeta('projectionType'); @override late final i0.GeneratedColumn projectionType = - i0.GeneratedColumn('projection_type', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -743,111 +988,162 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const String $name = 'remote_exif_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('city')) { context.handle( - _cityMeta, city.isAcceptableOrUnknown(data['city']!, _cityMeta)); + _cityMeta, + city.isAcceptableOrUnknown(data['city']!, _cityMeta), + ); } if (data.containsKey('state')) { context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); + _stateMeta, + state.isAcceptableOrUnknown(data['state']!, _stateMeta), + ); } if (data.containsKey('country')) { - context.handle(_countryMeta, - country.isAcceptableOrUnknown(data['country']!, _countryMeta)); + context.handle( + _countryMeta, + country.isAcceptableOrUnknown(data['country']!, _countryMeta), + ); } if (data.containsKey('date_time_original')) { context.handle( + _dateTimeOriginalMeta, + dateTimeOriginal.isAcceptableOrUnknown( + data['date_time_original']!, _dateTimeOriginalMeta, - dateTimeOriginal.isAcceptableOrUnknown( - data['date_time_original']!, _dateTimeOriginalMeta)); + ), + ); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('exposure_time')) { context.handle( + _exposureTimeMeta, + exposureTime.isAcceptableOrUnknown( + data['exposure_time']!, _exposureTimeMeta, - exposureTime.isAcceptableOrUnknown( - data['exposure_time']!, _exposureTimeMeta)); + ), + ); } if (data.containsKey('f_number')) { - context.handle(_fNumberMeta, - fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta)); + context.handle( + _fNumberMeta, + fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta), + ); } if (data.containsKey('file_size')) { - context.handle(_fileSizeMeta, - fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta)); + context.handle( + _fileSizeMeta, + fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta), + ); } if (data.containsKey('focal_length')) { context.handle( + _focalLengthMeta, + focalLength.isAcceptableOrUnknown( + data['focal_length']!, _focalLengthMeta, - focalLength.isAcceptableOrUnknown( - data['focal_length']!, _focalLengthMeta)); + ), + ); } if (data.containsKey('latitude')) { - context.handle(_latitudeMeta, - latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta)); + context.handle( + _latitudeMeta, + latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta), + ); } if (data.containsKey('longitude')) { - context.handle(_longitudeMeta, - longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta)); + context.handle( + _longitudeMeta, + longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta), + ); } if (data.containsKey('iso')) { context.handle( - _isoMeta, iso.isAcceptableOrUnknown(data['iso']!, _isoMeta)); + _isoMeta, + iso.isAcceptableOrUnknown(data['iso']!, _isoMeta), + ); } if (data.containsKey('make')) { context.handle( - _makeMeta, make.isAcceptableOrUnknown(data['make']!, _makeMeta)); + _makeMeta, + make.isAcceptableOrUnknown(data['make']!, _makeMeta), + ); } if (data.containsKey('model')) { context.handle( - _modelMeta, model.isAcceptableOrUnknown(data['model']!, _modelMeta)); + _modelMeta, + model.isAcceptableOrUnknown(data['model']!, _modelMeta), + ); } if (data.containsKey('lens')) { context.handle( - _lensMeta, lens.isAcceptableOrUnknown(data['lens']!, _lensMeta)); + _lensMeta, + lens.isAcceptableOrUnknown(data['lens']!, _lensMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } if (data.containsKey('time_zone')) { - context.handle(_timeZoneMeta, - timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta)); + context.handle( + _timeZoneMeta, + timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta), + ); } if (data.containsKey('rating')) { - context.handle(_ratingMeta, - rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta)); + context.handle( + _ratingMeta, + rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta), + ); } if (data.containsKey('projection_type')) { context.handle( + _projectionTypeMeta, + projectionType.isAcceptableOrUnknown( + data['projection_type']!, _projectionTypeMeta, - projectionType.isAcceptableOrUnknown( - data['projection_type']!, _projectionTypeMeta)); + ), + ); } return context; } @@ -855,55 +1151,100 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity @override Set get $primaryKey => {assetId}; @override - i1.RemoteExifEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteExifEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, - data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), exposureTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}rating']), + i0.DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}rating'], + ), projectionType: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}projection_type']), + i0.DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -942,29 +1283,30 @@ class RemoteExifEntityData extends i0.DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1035,16 +1377,19 @@ class RemoteExifEntityData extends i0.DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -1093,57 +1438,57 @@ class RemoteExifEntityData extends i0.DataClass }; } - i1.RemoteExifEntityData copyWith( - {String? assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent()}) => - i1.RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + i1.RemoteExifEntityData copyWith({ + String? assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(i1.RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -1153,8 +1498,9 @@ class RemoteExifEntityData extends i0.DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -1162,16 +1508,18 @@ class RemoteExifEntityData extends i0.DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -1211,29 +1559,29 @@ class RemoteExifEntityData extends i0.DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -1384,29 +1732,30 @@ class RemoteExifEntityCompanion }); } - i1.RemoteExifEntityCompanion copyWith( - {i0.Value? assetId, - i0.Value? city, - i0.Value? state, - i0.Value? country, - i0.Value? dateTimeOriginal, - i0.Value? description, - i0.Value? height, - i0.Value? width, - i0.Value? exposureTime, - i0.Value? fNumber, - i0.Value? fileSize, - i0.Value? focalLength, - i0.Value? latitude, - i0.Value? longitude, - i0.Value? iso, - i0.Value? make, - i0.Value? model, - i0.Value? lens, - i0.Value? orientation, - i0.Value? timeZone, - i0.Value? rating, - i0.Value? projectionType}) { + i1.RemoteExifEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? city, + i0.Value? state, + i0.Value? country, + i0.Value? dateTimeOriginal, + i0.Value? description, + i0.Value? height, + i0.Value? width, + i0.Value? exposureTime, + i0.Value? fNumber, + i0.Value? fileSize, + i0.Value? focalLength, + i0.Value? latitude, + i0.Value? longitude, + i0.Value? iso, + i0.Value? make, + i0.Value? model, + i0.Value? lens, + i0.Value? orientation, + i0.Value? timeZone, + i0.Value? rating, + i0.Value? projectionType, + }) { return i1.RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, diff --git a/mobile/lib/infrastructure/entities/exif.entity.g.dart b/mobile/lib/infrastructure/entities/exif.entity.g.dart index 989338abff..ffbfd0d8f0 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.g.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.g.dart @@ -17,16 +17,8 @@ const ExifInfoSchema = CollectionSchema( name: r'ExifInfo', id: -2409260054350835217, properties: { - r'city': PropertySchema( - id: 0, - name: r'city', - type: IsarType.string, - ), - r'country': PropertySchema( - id: 1, - name: r'country', - type: IsarType.string, - ), + r'city': PropertySchema(id: 0, name: r'city', type: IsarType.string), + r'country': PropertySchema(id: 1, name: r'country', type: IsarType.string), r'dateTimeOriginal': PropertySchema( id: 2, name: r'dateTimeOriginal', @@ -42,67 +34,28 @@ const ExifInfoSchema = CollectionSchema( name: r'exposureSeconds', type: IsarType.float, ), - r'f': PropertySchema( - id: 5, - name: r'f', - type: IsarType.float, - ), - r'fileSize': PropertySchema( - id: 6, - name: r'fileSize', - type: IsarType.long, - ), - r'iso': PropertySchema( - id: 7, - name: r'iso', - type: IsarType.int, - ), - r'lat': PropertySchema( - id: 8, - name: r'lat', - type: IsarType.float, - ), - r'lens': PropertySchema( - id: 9, - name: r'lens', - type: IsarType.string, - ), - r'long': PropertySchema( - id: 10, - name: r'long', - type: IsarType.float, - ), - r'make': PropertySchema( - id: 11, - name: r'make', - type: IsarType.string, - ), - r'mm': PropertySchema( - id: 12, - name: r'mm', - type: IsarType.float, - ), - r'model': PropertySchema( - id: 13, - name: r'model', - type: IsarType.string, - ), + r'f': PropertySchema(id: 5, name: r'f', type: IsarType.float), + r'fileSize': PropertySchema(id: 6, name: r'fileSize', type: IsarType.long), + r'iso': PropertySchema(id: 7, name: r'iso', type: IsarType.int), + r'lat': PropertySchema(id: 8, name: r'lat', type: IsarType.float), + r'lens': PropertySchema(id: 9, name: r'lens', type: IsarType.string), + r'long': PropertySchema(id: 10, name: r'long', type: IsarType.float), + r'make': PropertySchema(id: 11, name: r'make', type: IsarType.string), + r'mm': PropertySchema(id: 12, name: r'mm', type: IsarType.float), + r'model': PropertySchema(id: 13, name: r'model', type: IsarType.string), r'orientation': PropertySchema( id: 14, name: r'orientation', type: IsarType.string, ), - r'state': PropertySchema( - id: 15, - name: r'state', - type: IsarType.string, - ), + r'state': PropertySchema(id: 15, name: r'state', type: IsarType.string), r'timeZone': PropertySchema( id: 16, name: r'timeZone', type: IsarType.string, - ) + ), }, + estimateSize: _exifInfoEstimateSize, serialize: _exifInfoSerialize, deserialize: _exifInfoDeserialize, @@ -111,10 +64,11 @@ const ExifInfoSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _exifInfoGetId, getLinks: _exifInfoGetLinks, attach: _exifInfoAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _exifInfoEstimateSize( @@ -301,10 +255,7 @@ extension ExifInfoQueryWhereSort on QueryBuilder { extension ExifInfoQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -330,8 +281,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -339,8 +292,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -355,12 +310,14 @@ extension ExifInfoQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -369,17 +326,17 @@ extension ExifInfoQueryFilter on QueryBuilder { QueryBuilder cityIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'city'), + ); }); } QueryBuilder cityIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'city'), + ); }); } @@ -388,11 +345,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -402,12 +361,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,12 +378,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -434,14 +397,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'city', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'city', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -450,11 +415,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -463,69 +430,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'city', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'city', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'city', value: ''), + ); }); } QueryBuilder cityIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'city', value: ''), + ); }); } QueryBuilder countryIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'country'), + ); }); } QueryBuilder countryIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'country'), + ); }); } @@ -534,11 +507,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -548,12 +523,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -563,12 +540,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -580,14 +559,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'country', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'country', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -596,11 +577,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -609,144 +592,149 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'country', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'country', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'country', value: ''), + ); }); } QueryBuilder countryIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'country', value: ''), + ); }); } QueryBuilder - dateTimeOriginalIsNull() { + dateTimeOriginalIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalIsNotNull() { + dateTimeOriginalIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalEqualTo(DateTime? value) { + dateTimeOriginalEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'dateTimeOriginal', value: value), + ); }); } QueryBuilder - dateTimeOriginalGreaterThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalGreaterThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalLessThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalBetween( + dateTimeOriginalBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'dateTimeOriginal', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'dateTimeOriginal', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder - descriptionIsNotNull() { + descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -755,27 +743,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - descriptionGreaterThan( + descriptionGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -785,12 +777,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -802,14 +796,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -818,11 +814,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -831,123 +829,135 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder - descriptionIsNotEmpty() { + descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder - exposureSecondsIsNull() { + exposureSecondsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsIsNotNull() { + exposureSecondsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsEqualTo( - double? value, { - double epsilon = Query.epsilon, - }) { + exposureSecondsEqualTo(double? value, {double epsilon = Query.epsilon}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsGreaterThan( + exposureSecondsGreaterThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsLessThan( + exposureSecondsLessThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsBetween( + exposureSecondsBetween( double? lower, double? upper, { bool includeLower = true, @@ -955,30 +965,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'exposureSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'exposureSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'f'), + ); }); } QueryBuilder fIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'f'), + ); }); } @@ -987,11 +1000,9 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'f', value: value, epsilon: epsilon), + ); }); } @@ -1001,12 +1012,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1016,12 +1030,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1033,40 +1050,43 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'f', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'f', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fileSizeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileSize', value: value), + ); }); } @@ -1075,11 +1095,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1088,11 +1110,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1103,38 +1127,39 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileSize', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileSize', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'id'), + ); }); } QueryBuilder idIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'id'), + ); }); } QueryBuilder idEqualTo(Id? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1143,11 +1168,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1156,11 +1183,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1171,39 +1200,41 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isoIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'iso'), + ); }); } QueryBuilder isoIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'iso'), + ); }); } QueryBuilder isoEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'iso', value: value), + ); }); } @@ -1212,11 +1243,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1225,11 +1258,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1240,29 +1275,31 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'iso', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'iso', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder latIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lat'), + ); }); } QueryBuilder latIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lat'), + ); }); } @@ -1271,11 +1308,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1285,12 +1325,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1300,12 +1343,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1317,30 +1363,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lat', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lat', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder lensIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lens'), + ); }); } QueryBuilder lensIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lens'), + ); }); } @@ -1349,11 +1398,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1363,12 +1414,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1378,12 +1431,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1395,14 +1450,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lens', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lens', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1411,11 +1468,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1424,69 +1483,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'lens', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'lens', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lens', value: ''), + ); }); } QueryBuilder lensIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'lens', value: ''), + ); }); } QueryBuilder longIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'long'), + ); }); } QueryBuilder longIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'long'), + ); }); } @@ -1495,11 +1560,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1509,12 +1577,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1524,12 +1595,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1541,30 +1615,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'long', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'long', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder makeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'make'), + ); }); } QueryBuilder makeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'make'), + ); }); } @@ -1573,11 +1650,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1587,12 +1666,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1602,12 +1683,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1619,14 +1702,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'make', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'make', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1635,11 +1720,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1648,69 +1735,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'make', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'make', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'make', value: ''), + ); }); } QueryBuilder makeIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'make', value: ''), + ); }); } QueryBuilder mmIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'mm'), + ); }); } QueryBuilder mmIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'mm'), + ); }); } @@ -1719,11 +1812,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1733,12 +1829,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1748,12 +1847,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1765,30 +1867,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'mm', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'mm', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder modelIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'model'), + ); }); } QueryBuilder modelIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'model'), + ); }); } @@ -1797,11 +1902,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1811,12 +1918,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1826,12 +1935,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1843,14 +1954,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'model', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'model', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1859,11 +1972,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1872,70 +1987,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'model', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'model', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'model', value: ''), + ); }); } QueryBuilder modelIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'model', value: ''), + ); }); } QueryBuilder orientationIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'orientation'), + ); }); } QueryBuilder - orientationIsNotNull() { + orientationIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'orientation'), + ); }); } @@ -1944,27 +2065,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - orientationGreaterThan( + orientationGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1974,12 +2099,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1991,14 +2118,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'orientation', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'orientation', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2007,11 +2136,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2020,70 +2151,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'orientation', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'orientation', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'orientation', value: ''), + ); }); } QueryBuilder - orientationIsNotEmpty() { + orientationIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'orientation', value: ''), + ); }); } QueryBuilder stateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'state'), + ); }); } QueryBuilder stateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'state'), + ); }); } @@ -2092,11 +2229,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2106,12 +2245,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2121,12 +2262,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2138,14 +2281,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'state', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'state', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2154,11 +2299,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2167,69 +2314,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'state', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'state', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'state', value: ''), + ); }); } QueryBuilder stateIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'state', value: ''), + ); }); } QueryBuilder timeZoneIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'timeZone'), + ); }); } QueryBuilder timeZoneIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'timeZone'), + ); }); } @@ -2238,11 +2391,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2252,12 +2407,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2267,12 +2424,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2284,14 +2443,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'timeZone', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'timeZone', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,11 +2461,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2313,53 +2476,59 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timeZone', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'timeZone', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'timeZone', value: ''), + ); }); } QueryBuilder timeZoneIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'timeZone', value: ''), + ); }); } } @@ -2797,15 +2966,17 @@ extension ExifInfoQuerySortThenBy extension ExifInfoQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByCity( - {bool caseSensitive = true}) { + QueryBuilder distinctByCity({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'city', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByCountry( - {bool caseSensitive = true}) { + QueryBuilder distinctByCountry({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'country', caseSensitive: caseSensitive); }); @@ -2817,8 +2988,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -2854,8 +3026,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByLens( - {bool caseSensitive = true}) { + QueryBuilder distinctByLens({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'lens', caseSensitive: caseSensitive); }); @@ -2867,8 +3040,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByMake( - {bool caseSensitive = true}) { + QueryBuilder distinctByMake({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'make', caseSensitive: caseSensitive); }); @@ -2880,29 +3054,33 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByModel( - {bool caseSensitive = true}) { + QueryBuilder distinctByModel({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'model', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByOrientation( - {bool caseSensitive = true}) { + QueryBuilder distinctByOrientation({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'orientation', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByState( - {bool caseSensitive = true}) { + QueryBuilder distinctByState({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'state', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByTimeZone( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimeZone({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'timeZone', caseSensitive: caseSensitive); }); @@ -2930,7 +3108,7 @@ extension ExifInfoQueryProperty } QueryBuilder - dateTimeOriginalProperty() { + dateTimeOriginalProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'dateTimeOriginal'); }); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.dart b/mobile/lib/infrastructure/entities/local_album.entity.dart index 398c5d4e44..707d3326a4 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.dart @@ -1,5 +1,7 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class LocalAlbumEntity extends Table with DriftDefaultsMixin { @@ -9,8 +11,11 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); IntColumn get backupSelection => intEnum()(); - BoolColumn get isIosSharedAlbum => - boolean().withDefault(const Constant(false))(); + BoolColumn get isIosSharedAlbum => boolean().withDefault(const Constant(false))(); + + // // Linked album for putting assets to the remote album after finished uploading + TextColumn get linkedRemoteAlbumId => + text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.setNull).nullable()(); // Used for mark & sweep BoolColumn get marker_ => boolean().nullable()(); @@ -18,3 +23,16 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin { @override Set get primaryKey => {id}; } + +extension LocalAlbumEntityDataHelper on LocalAlbumEntityData { + LocalAlbum toDto({int assetCount = 0}) { + return LocalAlbum( + id: id, + name: name, + updatedAt: updatedAt, + assetCount: assetCount, + backupSelection: backupSelection, + linkedRemoteAlbumId: linkedRemoteAlbumId, + ); + } +} diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index 06f65e25d8..0703844291 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -7,25 +7,77 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart' as i2; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' + as i5; +import 'package:drift/internal/modular.dart' as i6; -typedef $$LocalAlbumEntityTableCreateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value updatedAt, - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); -typedef $$LocalAlbumEntityTableUpdateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value updatedAt, - i0.Value backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); +typedef $$LocalAlbumEntityTableCreateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value updatedAt, + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum, + i0.Value linkedRemoteAlbumId, + i0.Value marker_, + }); +typedef $$LocalAlbumEntityTableUpdateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value updatedAt, + i0.Value backupSelection, + i0.Value isIosSharedAlbum, + i0.Value linkedRemoteAlbumId, + i0.Value marker_, + }); + +final class $$LocalAlbumEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + > { + $$LocalAlbumEntityTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static i5.$RemoteAlbumEntityTable _linkedRemoteAlbumIdTable( + i0.GeneratedDatabase db, + ) => i6.ReadDatabaseContainer(db) + .resultSet('remote_album_entity') + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('local_album_entity') + .linkedRemoteAlbumId, + i6.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); + + i5.$$RemoteAlbumEntityTableProcessedTableManager? get linkedRemoteAlbumId { + final $_column = $_itemColumn('linked_remote_album_id'); + if ($_column == null) return null; + final manager = i5 + .$$RemoteAlbumEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( + $_db, + ).resultSet('remote_album_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_linkedRemoteAlbumIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} class $$LocalAlbumEntityTableFilterComposer extends i0.Composer { @@ -37,25 +89,62 @@ class $$LocalAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnFilters(column)); + column: $table.marker_, + builder: (column) => i0.ColumnFilters(column), + ); + + i5.$$RemoteAlbumEntityTableFilterComposer get linkedRemoteAlbumId { + final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.linkedRemoteAlbumId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } } class $$LocalAlbumEntityTableOrderingComposer @@ -68,25 +157,62 @@ class $$LocalAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.backupSelection, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnOrderings(column)); + column: $table.marker_, + builder: (column) => i0.ColumnOrderings(column), + ); + + i5.$$RemoteAlbumEntityTableOrderingComposer get linkedRemoteAlbumId { + final i5.$$RemoteAlbumEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.linkedRemoteAlbumId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } } class $$LocalAlbumEntityTableAnnotationComposer @@ -108,35 +234,68 @@ class $$LocalAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumnWithTypeConverter - get backupSelection => $composableBuilder( - column: $table.backupSelection, builder: (column) => column); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => column, + ); i0.GeneratedColumn get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, builder: (column) => column); + column: $table.isIosSharedAlbum, + builder: (column) => column, + ); i0.GeneratedColumn get marker_ => $composableBuilder(column: $table.marker_, builder: (column) => column); + + i5.$$RemoteAlbumEntityTableAnnotationComposer get linkedRemoteAlbumId { + final i5.$$RemoteAlbumEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.linkedRemoteAlbumId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } } -class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData, + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function({bool linkedRemoteAlbumId}) + > { $$LocalAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -145,63 +304,115 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< .$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value backupSelection = - const i0.Value.absent(), - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value updatedAt = const i0.Value.absent(), - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion.insert( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value backupSelection = + const i0.Value.absent(), + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value linkedRemoteAlbumId = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId, + marker_: marker_, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value updatedAt = const i0.Value.absent(), + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value linkedRemoteAlbumId = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion.insert( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId, + marker_: marker_, + ), withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .map( + (e) => ( + e.readTable(table), + i1.$$LocalAlbumEntityTableReferences(db, table, e), + ), + ) .toList(), - prefetchHooksCallback: null, - )); + prefetchHooksCallback: ({linkedRemoteAlbumId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (linkedRemoteAlbumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.linkedRemoteAlbumId, + referencedTable: i1 + .$$LocalAlbumEntityTableReferences + ._linkedRemoteAlbumIdTable(db), + referencedColumn: i1 + .$$LocalAlbumEntityTableReferences + ._linkedRemoteAlbumIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); } -typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()>; + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function({bool linkedRemoteAlbumId}) + >; class $LocalAlbumEntityTable extends i3.LocalAlbumEntity with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> { @@ -212,51 +423,101 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); @override late final i0.GeneratedColumnWithTypeConverter - backupSelection = i0.GeneratedColumn( - 'backup_selection', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAlbumEntityTable.$converterbackupSelection); + backupSelection = + i0.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$LocalAlbumEntityTable.$converterbackupSelection, + ); static const i0.VerificationMeta _isIosSharedAlbumMeta = const i0.VerificationMeta('isIosSharedAlbum'); @override late final i0.GeneratedColumn isIosSharedAlbum = - i0.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _marker_Meta = - const i0.VerificationMeta('marker_'); + i0.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _linkedRemoteAlbumIdMeta = + const i0.VerificationMeta('linkedRemoteAlbumId'); + @override + late final i0.GeneratedColumn linkedRemoteAlbumId = + i0.GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta( + 'marker_', + ); @override late final i0.GeneratedColumn marker_ = i0.GeneratedColumn( - 'marker', aliasedName, true, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -264,8 +525,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const String $name = 'local_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -275,23 +537,41 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('is_ios_shared_album')) { context.handle( + _isIosSharedAlbumMeta, + isIosSharedAlbum.isAcceptableOrUnknown( + data['is_ios_shared_album']!, _isIosSharedAlbumMeta, - isIosSharedAlbum.isAcceptableOrUnknown( - data['is_ios_shared_album']!, _isIosSharedAlbumMeta)); + ), + ); + } + if (data.containsKey('linked_remote_album_id')) { + context.handle( + _linkedRemoteAlbumIdMeta, + linkedRemoteAlbumId.isAcceptableOrUnknown( + data['linked_remote_album_id']!, + _linkedRemoteAlbumIdMeta, + ), + ); } if (data.containsKey('marker')) { - context.handle(_marker_Meta, - marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta)); + context.handle( + _marker_Meta, + marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta), + ); } return context; } @@ -299,23 +579,43 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity @override Set get $primaryKey => {id}; @override - i1.LocalAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection - .fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int, - data['${effectivePrefix}backup_selection'])!), + .fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + ), isIosSharedAlbum: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -325,9 +625,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } static i0.JsonTypeConverter2 - $converterbackupSelection = - const i0.EnumIndexConverter( - i2.BackupSelection.values); + $converterbackupSelection = const i0.EnumIndexConverter( + i2.BackupSelection.values, + ); @override bool get withoutRowId => true; @override @@ -341,14 +641,17 @@ class LocalAlbumEntityData extends i0.DataClass final DateTime updatedAt; final i2.BackupSelection backupSelection; final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -356,19 +659,26 @@ class LocalAlbumEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['updated_at'] = i0.Variable(updatedAt); { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection, + ), + ); } map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = i0.Variable(linkedRemoteAlbumId); + } if (!nullToAbsent || marker_ != null) { map['marker'] = i0.Variable(marker_); } return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -377,6 +687,9 @@ class LocalAlbumEntityData extends i0.DataClass backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection .fromJson(serializer.fromJson(json['backupSelection'])), isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), marker_: serializer.fromJson(json['marker_']), ); } @@ -387,29 +700,36 @@ class LocalAlbumEntityData extends i0.DataClass 'id': serializer.toJson(id), 'name': serializer.toJson(name), 'updatedAt': serializer.toJson(updatedAt), - 'backupSelection': serializer.toJson(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toJson(backupSelection)), + 'backupSelection': serializer.toJson( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toJson( + backupSelection, + ), + ), 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), 'marker_': serializer.toJson(marker_), }; } - i1.LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - i2.BackupSelection? backupSelection, - bool? isIosSharedAlbum, - i0.Value marker_ = const i0.Value.absent()}) => - i1.LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + i1.LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + i2.BackupSelection? backupSelection, + bool? isIosSharedAlbum, + i0.Value linkedRemoteAlbumId = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -421,6 +741,9 @@ class LocalAlbumEntityData extends i0.DataClass isIosSharedAlbum: data.isIosSharedAlbum.present ? data.isIosSharedAlbum.value : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -433,6 +756,7 @@ class LocalAlbumEntityData extends i0.DataClass ..write('updatedAt: $updatedAt, ') ..write('backupSelection: $backupSelection, ') ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') ..write('marker_: $marker_') ..write(')')) .toString(); @@ -440,7 +764,14 @@ class LocalAlbumEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -450,6 +781,7 @@ class LocalAlbumEntityData extends i0.DataClass other.updatedAt == this.updatedAt && other.backupSelection == this.backupSelection && other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && other.marker_ == this.marker_); } @@ -460,6 +792,7 @@ class LocalAlbumEntityCompanion final i0.Value updatedAt; final i0.Value backupSelection; final i0.Value isIosSharedAlbum; + final i0.Value linkedRemoteAlbumId; final i0.Value marker_; const LocalAlbumEntityCompanion({ this.id = const i0.Value.absent(), @@ -467,6 +800,7 @@ class LocalAlbumEntityCompanion this.updatedAt = const i0.Value.absent(), this.backupSelection = const i0.Value.absent(), this.isIosSharedAlbum = const i0.Value.absent(), + this.linkedRemoteAlbumId = const i0.Value.absent(), this.marker_ = const i0.Value.absent(), }); LocalAlbumEntityCompanion.insert({ @@ -475,16 +809,18 @@ class LocalAlbumEntityCompanion this.updatedAt = const i0.Value.absent(), required i2.BackupSelection backupSelection, this.isIosSharedAlbum = const i0.Value.absent(), + this.linkedRemoteAlbumId = const i0.Value.absent(), this.marker_ = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - backupSelection = i0.Value(backupSelection); + }) : id = i0.Value(id), + name = i0.Value(name), + backupSelection = i0.Value(backupSelection); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, i0.Expression? updatedAt, i0.Expression? backupSelection, i0.Expression? isIosSharedAlbum, + i0.Expression? linkedRemoteAlbumId, i0.Expression? marker_, }) { return i0.RawValuesInsertable({ @@ -493,23 +829,28 @@ class LocalAlbumEntityCompanion if (updatedAt != null) 'updated_at': updatedAt, if (backupSelection != null) 'backup_selection': backupSelection, if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, if (marker_ != null) 'marker': marker_, }); } - i1.LocalAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? updatedAt, - i0.Value? backupSelection, - i0.Value? isIosSharedAlbum, - i0.Value? marker_}) { + i1.LocalAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? updatedAt, + i0.Value? backupSelection, + i0.Value? isIosSharedAlbum, + i0.Value? linkedRemoteAlbumId, + i0.Value? marker_, + }) { return i1.LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, updatedAt: updatedAt ?? this.updatedAt, backupSelection: backupSelection ?? this.backupSelection, isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, marker_: marker_ ?? this.marker_, ); } @@ -527,13 +868,20 @@ class LocalAlbumEntityCompanion map['updated_at'] = i0.Variable(updatedAt.value); } if (backupSelection.present) { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection.value)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection.value, + ), + ); } if (isIosSharedAlbum.present) { map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum.value); } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = i0.Variable( + linkedRemoteAlbumId.value, + ); + } if (marker_.present) { map['marker'] = i0.Variable(marker_.value); } @@ -548,6 +896,7 @@ class LocalAlbumEntityCompanion ..write('updatedAt: $updatedAt, ') ..write('backupSelection: $backupSelection, ') ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') ..write('marker_: $marker_') ..write(')')) .toString(); diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.dart index b64b9ec2fb..53f1a10662 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.dart @@ -6,11 +6,12 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin { const LocalAlbumAssetEntity(); - TextColumn get assetId => - text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get albumId => - text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)(); + + // Used for mark & sweep + BoolColumn get marker_ => boolean().nullable()(); @override Set get primaryKey => {assetId, albumId}; diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart index e8f94fa74b..70c298332b 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart @@ -11,76 +11,98 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' as i5; -typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + i0.Value marker_, + }); +typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + i0.Value marker_, + }); -final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData> { +final class $$LocalAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { $$LocalAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('local_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_asset_entity').id, + ), + ); i3.$$LocalAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$LocalAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_asset_entity')) + ).resultSet('local_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('local_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_album_entity').id, + ), + ); i5.$$LocalAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$LocalAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_album_entity')) + ).resultSet('local_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -93,47 +115,62 @@ class $$LocalAlbumAssetEntityTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + i0.ColumnFilters get marker_ => $composableBuilder( + column: $table.marker_, + builder: (column) => i0.ColumnFilters(column), + ); + i3.$$LocalAssetEntityTableFilterComposer get assetId { final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableFilterComposer get albumId { final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -147,51 +184,64 @@ class $$LocalAlbumAssetEntityTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + i0.ColumnOrderings get marker_ => $composableBuilder( + column: $table.marker_, + builder: (column) => i0.ColumnOrderings(column), + ); + i3.$$LocalAssetEntityTableOrderingComposer get assetId { final i3.$$LocalAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableOrderingComposer get albumId { final i5.$$LocalAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -205,109 +255,141 @@ class $$LocalAlbumAssetEntityTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + i0.GeneratedColumn get marker_ => + $composableBuilder(column: $table.marker_, builder: (column) => column); + i3.$$LocalAssetEntityTableAnnotationComposer get assetId { final i3.$$LocalAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableAnnotationComposer get albumId { final i5.$$LocalAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$LocalAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableReferences, + ), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$LocalAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$LocalAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$LocalAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$LocalAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.LocalAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + marker_: marker_, + ), + createCompanionCallback: + ({ + required String assetId, + required String albumId, + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + marker_: marker_, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$LocalAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$LocalAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,85 +400,120 @@ class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$LocalAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableReferences - ), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; +typedef $$LocalAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity with - i0 - .TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> { + i0.TableInfo< + $LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta( + 'marker_', + ); @override - List get $columns => [assetId, albumId]; + late final i0.GeneratedColumn marker_ = i0.GeneratedColumn( + 'marker', + aliasedName, + true, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [assetId, albumId, marker_]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -404,36 +521,57 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity static const String $name = 'local_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } + if (data.containsKey('marker')) { + context.handle( + _marker_Meta, + marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta), + ); + } return context; } @override Set get $primaryKey => {assetId, albumId}; @override - i1.LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + marker_: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -452,22 +590,32 @@ class LocalAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + final bool? marker_; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['asset_id'] = i0.Variable(assetId); map['album_id'] = i0.Variable(albumId); + if (!nullToAbsent || marker_ != null) { + map['marker'] = i0.Variable(marker_); + } return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), albumId: serializer.fromJson(json['albumId']), + marker_: serializer.fromJson(json['marker_']), ); } @override @@ -476,19 +624,26 @@ class LocalAlbumAssetEntityData extends i0.DataClass return { 'assetId': serializer.toJson(assetId), 'albumId': serializer.toJson(albumId), + 'marker_': serializer.toJson(marker_), }; } - i1.LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => - i1.LocalAlbumAssetEntityData( - assetId: assetId ?? this.assetId, - albumId: albumId ?? this.albumId, - ); + i1.LocalAlbumAssetEntityData copyWith({ + String? assetId, + String? albumId, + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumAssetEntityData copyWithCompanion( - i1.LocalAlbumAssetEntityCompanion data) { + i1.LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, ); } @@ -496,49 +651,60 @@ class LocalAlbumAssetEntityData extends i0.DataClass String toString() { return (StringBuffer('LocalAlbumAssetEntityData(') ..write('assetId: $assetId, ') - ..write('albumId: $albumId') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(assetId, albumId); + int get hashCode => Object.hash(assetId, albumId, marker_); @override bool operator ==(Object other) => identical(this, other) || (other is i1.LocalAlbumAssetEntityData && other.assetId == this.assetId && - other.albumId == this.albumId); + other.albumId == this.albumId && + other.marker_ == this.marker_); } class LocalAlbumAssetEntityCompanion extends i0.UpdateCompanion { final i0.Value assetId; final i0.Value albumId; + final i0.Value marker_; const LocalAlbumAssetEntityCompanion({ this.assetId = const i0.Value.absent(), this.albumId = const i0.Value.absent(), + this.marker_ = const i0.Value.absent(), }); LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + this.marker_ = const i0.Value.absent(), + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, + i0.Expression? marker_, }) { return i0.RawValuesInsertable({ if (assetId != null) 'asset_id': assetId, if (albumId != null) 'album_id': albumId, + if (marker_ != null) 'marker': marker_, }); } - i1.LocalAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.LocalAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + i0.Value? marker_, + }) { return i1.LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, + marker_: marker_ ?? this.marker_, ); } @@ -551,6 +717,9 @@ class LocalAlbumAssetEntityCompanion if (albumId.present) { map['album_id'] = i0.Variable(albumId.value); } + if (marker_.present) { + map['marker'] = i0.Variable(marker_.value); + } return map; } @@ -558,7 +727,8 @@ class LocalAlbumAssetEntityCompanion String toString() { return (StringBuffer('LocalAlbumAssetEntityCompanion(') ..write('assetId: $assetId, ') - ..write('albumId: $albumId') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 204d5d6a80..8b253f83a3 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -4,7 +4,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'idx_local_asset_checksum', columns: {#checksum}) +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)') class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const LocalAssetEntity(); @@ -20,19 +20,19 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { Set get primaryKey => {id}; } -extension LocalAssetEntityDataDomainEx on LocalAssetEntityData { - LocalAsset toDto() => LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - remoteId: null, - orientation: orientation, - ); +extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData { + LocalAsset toDto({String? remoteId}) => LocalAsset( + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + remoteId: remoteId, + orientation: orientation, + ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index e9c5961aa5..d0fe742463 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -8,34 +8,34 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAssetEntityTableCreateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); -typedef $$LocalAssetEntityTableUpdateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); +typedef $$LocalAssetEntityTableCreateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); +typedef $$LocalAssetEntityTableUpdateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); class $$LocalAssetEntityTableFilterComposer extends i0.Composer { @@ -47,41 +47,60 @@ class $$LocalAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAssetEntityTableOrderingComposer @@ -94,42 +113,59 @@ class $$LocalAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAssetEntityTableAnnotationComposer @@ -160,7 +196,9 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -169,31 +207,43 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); } -class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData, + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + > { $$LocalAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -202,84 +252,94 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< .$$LocalAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()>; -i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + >; +i0.Index get idxLocalAssetChecksum => i0.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', +); class $LocalAssetEntityTable extends i3.LocalAssetEntity with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> { @@ -287,95 +347,146 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LocalAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - '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)); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + '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), + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = i0.GeneratedColumn( - 'orientation', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i4.Constant(0)); + 'orientation', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -383,37 +494,51 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity static const String $name = 'local_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -421,20 +546,25 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } return context; } @@ -442,33 +572,58 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity @override Set get $primaryKey => {id}; @override - i1.LocalAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$LocalAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}orientation'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -498,25 +653,27 @@ class LocalAssetEntityData extends i0.DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -538,13 +695,16 @@ class LocalAssetEntityData extends i0.DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$LocalAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$LocalAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -561,8 +721,9 @@ class LocalAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$LocalAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$LocalAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -575,33 +736,33 @@ class LocalAssetEntityData extends i0.DataClass }; } - i1.LocalAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - i0.Value checksum = const i0.Value.absent(), - bool? isFavorite, - int? orientation}) => - i1.LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + i1.LocalAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + i0.Value checksum = const i0.Value.absent(), + bool? isFavorite, + int? orientation, + }) => i1.LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -615,10 +776,12 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -641,8 +804,19 @@ class LocalAssetEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -698,9 +872,9 @@ class LocalAssetEntityCompanion this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.orientation = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -729,18 +903,19 @@ class LocalAssetEntityCompanion }); } - i1.LocalAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? orientation}) { + i1.LocalAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? orientation, + }) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -764,7 +939,8 @@ class LocalAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type.value)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); diff --git a/mobile/lib/infrastructure/entities/log.entity.dart b/mobile/lib/infrastructure/entities/log.entity.dart index 6a38924e24..e578459827 100644 --- a/mobile/lib/infrastructure/entities/log.entity.dart +++ b/mobile/lib/infrastructure/entities/log.entity.dart @@ -1,47 +1,29 @@ -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:isar/isar.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/domain/models/log.model.dart' as domain; -part 'log.entity.g.dart'; +class LogMessageEntity extends Table { + const LogMessageEntity(); -@Collection(inheritance: false) -class LoggerMessage { - final Id id = Isar.autoIncrement; - final String message; - final String? details; - @Enumerated(EnumType.ordinal) - final LogLevel level; - final DateTime createdAt; - final String? context1; - final String? context2; + @override + String get tableName => 'logger_messages'; - const LoggerMessage({ - required this.message, - required this.details, - this.level = LogLevel.info, - required this.createdAt, - required this.context1, - required this.context2, - }); - - LogMessage toDto() { - return LogMessage( - message: message, - level: level, - createdAt: createdAt, - logger: context1, - error: details, - stack: context2, - ); - } - - static LoggerMessage fromDto(LogMessage log) { - return LoggerMessage( - message: log.message, - details: log.error, - level: log.level, - createdAt: log.createdAt, - context1: log.logger, - context2: log.stack, - ); - } + IntColumn get id => integer().autoIncrement()(); + TextColumn get message => text()(); + TextColumn get details => text().nullable()(); + IntColumn get level => intEnum()(); + DateTimeColumn get createdAt => dateTime()(); + TextColumn get logger => text().nullable()(); + TextColumn get stack => text().nullable()(); +} + +extension LogMessageEntityDataDomainEx on LogMessageEntityData { + domain.LogMessage toDto() => domain.LogMessage( + message: message, + level: level, + createdAt: createdAt, + logger: logger, + error: details, + stack: stack, + ); } diff --git a/mobile/lib/infrastructure/entities/log.entity.drift.dart b/mobile/lib/infrastructure/entities/log.entity.drift.dart new file mode 100644 index 0000000000..d04cd5b7a2 --- /dev/null +++ b/mobile/lib/infrastructure/entities/log.entity.drift.dart @@ -0,0 +1,697 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart' + as i1; +import 'package:immich_mobile/domain/models/log.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/log.entity.dart' as i3; + +typedef $$LogMessageEntityTableCreateCompanionBuilder = + i1.LogMessageEntityCompanion Function({ + i0.Value id, + required String message, + i0.Value details, + required i2.LogLevel level, + required DateTime createdAt, + i0.Value logger, + i0.Value stack, + }); +typedef $$LogMessageEntityTableUpdateCompanionBuilder = + i1.LogMessageEntityCompanion Function({ + i0.Value id, + i0.Value message, + i0.Value details, + i0.Value level, + i0.Value createdAt, + i0.Value logger, + i0.Value stack, + }); + +class $$LogMessageEntityTableFilterComposer + extends i0.Composer { + $$LogMessageEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get message => $composableBuilder( + column: $table.message, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get details => $composableBuilder( + column: $table.details, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnWithTypeConverterFilters get level => + $composableBuilder( + column: $table.level, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get logger => $composableBuilder( + column: $table.logger, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get stack => $composableBuilder( + column: $table.stack, + builder: (column) => i0.ColumnFilters(column), + ); +} + +class $$LogMessageEntityTableOrderingComposer + extends i0.Composer { + $$LogMessageEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get message => $composableBuilder( + column: $table.message, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get details => $composableBuilder( + column: $table.details, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get level => $composableBuilder( + column: $table.level, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get logger => $composableBuilder( + column: $table.logger, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get stack => $composableBuilder( + column: $table.stack, + builder: (column) => i0.ColumnOrderings(column), + ); +} + +class $$LogMessageEntityTableAnnotationComposer + extends i0.Composer { + $$LogMessageEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get message => + $composableBuilder(column: $table.message, builder: (column) => column); + + i0.GeneratedColumn get details => + $composableBuilder(column: $table.details, builder: (column) => column); + + i0.GeneratedColumnWithTypeConverter get level => + $composableBuilder(column: $table.level, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get logger => + $composableBuilder(column: $table.logger, builder: (column) => column); + + i0.GeneratedColumn get stack => + $composableBuilder(column: $table.stack, builder: (column) => column); +} + +class $$LogMessageEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData, + i1.$$LogMessageEntityTableFilterComposer, + i1.$$LogMessageEntityTableOrderingComposer, + i1.$$LogMessageEntityTableAnnotationComposer, + $$LogMessageEntityTableCreateCompanionBuilder, + $$LogMessageEntityTableUpdateCompanionBuilder, + ( + i1.LogMessageEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData + >, + ), + i1.LogMessageEntityData, + i0.PrefetchHooks Function() + > { + $$LogMessageEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$LogMessageEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$LogMessageEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => i1 + .$$LogMessageEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$LogMessageEntityTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value message = const i0.Value.absent(), + i0.Value details = const i0.Value.absent(), + i0.Value level = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityCompanion( + id: id, + message: message, + details: details, + level: level, + createdAt: createdAt, + logger: logger, + stack: stack, + ), + createCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + required String message, + i0.Value details = const i0.Value.absent(), + required i2.LogLevel level, + required DateTime createdAt, + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityCompanion.insert( + id: id, + message: message, + details: details, + level: level, + createdAt: createdAt, + logger: logger, + stack: stack, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$LogMessageEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData, + i1.$$LogMessageEntityTableFilterComposer, + i1.$$LogMessageEntityTableOrderingComposer, + i1.$$LogMessageEntityTableAnnotationComposer, + $$LogMessageEntityTableCreateCompanionBuilder, + $$LogMessageEntityTableUpdateCompanionBuilder, + ( + i1.LogMessageEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LogMessageEntityTable, + i1.LogMessageEntityData + >, + ), + i1.LogMessageEntityData, + i0.PrefetchHooks Function() + >; + +class $LogMessageEntityTable extends i3.LogMessageEntity + with i0.TableInfo<$LogMessageEntityTable, i1.LogMessageEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $LogMessageEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + static const i0.VerificationMeta _messageMeta = const i0.VerificationMeta( + 'message', + ); + @override + late final i0.GeneratedColumn message = i0.GeneratedColumn( + 'message', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _detailsMeta = const i0.VerificationMeta( + 'details', + ); + @override + late final i0.GeneratedColumn details = i0.GeneratedColumn( + 'details', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + late final i0.GeneratedColumnWithTypeConverter level = + i0.GeneratedColumn( + 'level', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LogMessageEntityTable.$converterlevel); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _loggerMeta = const i0.VerificationMeta( + 'logger', + ); + @override + late final i0.GeneratedColumn logger = i0.GeneratedColumn( + 'logger', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stackMeta = const i0.VerificationMeta( + 'stack', + ); + @override + late final i0.GeneratedColumn stack = i0.GeneratedColumn( + 'stack', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + message, + details, + level, + createdAt, + logger, + stack, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'logger_messages'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('message')) { + context.handle( + _messageMeta, + message.isAcceptableOrUnknown(data['message']!, _messageMeta), + ); + } else if (isInserting) { + context.missing(_messageMeta); + } + if (data.containsKey('details')) { + context.handle( + _detailsMeta, + details.isAcceptableOrUnknown(data['details']!, _detailsMeta), + ); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + if (data.containsKey('logger')) { + context.handle( + _loggerMeta, + logger.isAcceptableOrUnknown(data['logger']!, _loggerMeta), + ); + } + if (data.containsKey('stack')) { + context.handle( + _stackMeta, + stack.isAcceptableOrUnknown(data['stack']!, _stackMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.LogMessageEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.LogMessageEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + message: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}message'], + )!, + details: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}details'], + ), + level: i1.$LogMessageEntityTable.$converterlevel.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}level'], + )!, + ), + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + logger: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}logger'], + ), + stack: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack'], + ), + ); + } + + @override + $LogMessageEntityTable createAlias(String alias) { + return $LogMessageEntityTable(attachedDatabase, alias); + } + + static i0.JsonTypeConverter2 $converterlevel = + const i0.EnumIndexConverter(i2.LogLevel.values); +} + +class LogMessageEntityData extends i0.DataClass + implements i0.Insertable { + final int id; + final String message; + final String? details; + final i2.LogLevel level; + final DateTime createdAt; + final String? logger; + final String? stack; + const LogMessageEntityData({ + required this.id, + required this.message, + this.details, + required this.level, + required this.createdAt, + this.logger, + this.stack, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['message'] = i0.Variable(message); + if (!nullToAbsent || details != null) { + map['details'] = i0.Variable(details); + } + { + map['level'] = i0.Variable( + i1.$LogMessageEntityTable.$converterlevel.toSql(level), + ); + } + map['created_at'] = i0.Variable(createdAt); + if (!nullToAbsent || logger != null) { + map['logger'] = i0.Variable(logger); + } + if (!nullToAbsent || stack != null) { + map['stack'] = i0.Variable(stack); + } + return map; + } + + factory LogMessageEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return LogMessageEntityData( + id: serializer.fromJson(json['id']), + message: serializer.fromJson(json['message']), + details: serializer.fromJson(json['details']), + level: i1.$LogMessageEntityTable.$converterlevel.fromJson( + serializer.fromJson(json['level']), + ), + createdAt: serializer.fromJson(json['createdAt']), + logger: serializer.fromJson(json['logger']), + stack: serializer.fromJson(json['stack']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'message': serializer.toJson(message), + 'details': serializer.toJson(details), + 'level': serializer.toJson( + i1.$LogMessageEntityTable.$converterlevel.toJson(level), + ), + 'createdAt': serializer.toJson(createdAt), + 'logger': serializer.toJson(logger), + 'stack': serializer.toJson(stack), + }; + } + + i1.LogMessageEntityData copyWith({ + int? id, + String? message, + i0.Value details = const i0.Value.absent(), + i2.LogLevel? level, + DateTime? createdAt, + i0.Value logger = const i0.Value.absent(), + i0.Value stack = const i0.Value.absent(), + }) => i1.LogMessageEntityData( + id: id ?? this.id, + message: message ?? this.message, + details: details.present ? details.value : this.details, + level: level ?? this.level, + createdAt: createdAt ?? this.createdAt, + logger: logger.present ? logger.value : this.logger, + stack: stack.present ? stack.value : this.stack, + ); + LogMessageEntityData copyWithCompanion(i1.LogMessageEntityCompanion data) { + return LogMessageEntityData( + id: data.id.present ? data.id.value : this.id, + message: data.message.present ? data.message.value : this.message, + details: data.details.present ? data.details.value : this.details, + level: data.level.present ? data.level.value : this.level, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + logger: data.logger.present ? data.logger.value : this.logger, + stack: data.stack.present ? data.stack.value : this.stack, + ); + } + + @override + String toString() { + return (StringBuffer('LogMessageEntityData(') + ..write('id: $id, ') + ..write('message: $message, ') + ..write('details: $details, ') + ..write('level: $level, ') + ..write('createdAt: $createdAt, ') + ..write('logger: $logger, ') + ..write('stack: $stack') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, message, details, level, createdAt, logger, stack); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.LogMessageEntityData && + other.id == this.id && + other.message == this.message && + other.details == this.details && + other.level == this.level && + other.createdAt == this.createdAt && + other.logger == this.logger && + other.stack == this.stack); +} + +class LogMessageEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value message; + final i0.Value details; + final i0.Value level; + final i0.Value createdAt; + final i0.Value logger; + final i0.Value stack; + const LogMessageEntityCompanion({ + this.id = const i0.Value.absent(), + this.message = const i0.Value.absent(), + this.details = const i0.Value.absent(), + this.level = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.logger = const i0.Value.absent(), + this.stack = const i0.Value.absent(), + }); + LogMessageEntityCompanion.insert({ + this.id = const i0.Value.absent(), + required String message, + this.details = const i0.Value.absent(), + required i2.LogLevel level, + required DateTime createdAt, + this.logger = const i0.Value.absent(), + this.stack = const i0.Value.absent(), + }) : message = i0.Value(message), + level = i0.Value(level), + createdAt = i0.Value(createdAt); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? message, + i0.Expression? details, + i0.Expression? level, + i0.Expression? createdAt, + i0.Expression? logger, + i0.Expression? stack, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (message != null) 'message': message, + if (details != null) 'details': details, + if (level != null) 'level': level, + if (createdAt != null) 'created_at': createdAt, + if (logger != null) 'logger': logger, + if (stack != null) 'stack': stack, + }); + } + + i1.LogMessageEntityCompanion copyWith({ + i0.Value? id, + i0.Value? message, + i0.Value? details, + i0.Value? level, + i0.Value? createdAt, + i0.Value? logger, + i0.Value? stack, + }) { + return i1.LogMessageEntityCompanion( + id: id ?? this.id, + message: message ?? this.message, + details: details ?? this.details, + level: level ?? this.level, + createdAt: createdAt ?? this.createdAt, + logger: logger ?? this.logger, + stack: stack ?? this.stack, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (message.present) { + map['message'] = i0.Variable(message.value); + } + if (details.present) { + map['details'] = i0.Variable(details.value); + } + if (level.present) { + map['level'] = i0.Variable( + i1.$LogMessageEntityTable.$converterlevel.toSql(level.value), + ); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (logger.present) { + map['logger'] = i0.Variable(logger.value); + } + if (stack.present) { + map['stack'] = i0.Variable(stack.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LogMessageEntityCompanion(') + ..write('id: $id, ') + ..write('message: $message, ') + ..write('details: $details, ') + ..write('level: $level, ') + ..write('createdAt: $createdAt, ') + ..write('logger: $logger, ') + ..write('stack: $stack') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart deleted file mode 100644 index 9300cf15c5..0000000000 --- a/mobile/lib/infrastructure/entities/log.entity.g.dart +++ /dev/null @@ -1,1295 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'log.entity.dart'; - -// ************************************************************************** -// IsarCollectionGenerator -// ************************************************************************** - -// coverage:ignore-file -// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types - -extension GetLoggerMessageCollection on Isar { - IsarCollection get loggerMessages => this.collection(); -} - -const LoggerMessageSchema = CollectionSchema( - name: r'LoggerMessage', - id: -1606100856208753787, - properties: { - r'context1': PropertySchema( - id: 0, - name: r'context1', - type: IsarType.string, - ), - r'context2': PropertySchema( - id: 1, - name: r'context2', - type: IsarType.string, - ), - r'createdAt': PropertySchema( - id: 2, - name: r'createdAt', - type: IsarType.dateTime, - ), - r'details': PropertySchema( - id: 3, - name: r'details', - type: IsarType.string, - ), - r'level': PropertySchema( - id: 4, - name: r'level', - type: IsarType.byte, - enumMap: _LoggerMessagelevelEnumValueMap, - ), - r'message': PropertySchema( - id: 5, - name: r'message', - type: IsarType.string, - ) - }, - estimateSize: _loggerMessageEstimateSize, - serialize: _loggerMessageSerialize, - deserialize: _loggerMessageDeserialize, - deserializeProp: _loggerMessageDeserializeProp, - idName: r'id', - indexes: {}, - links: {}, - embeddedSchemas: {}, - getId: _loggerMessageGetId, - getLinks: _loggerMessageGetLinks, - attach: _loggerMessageAttach, - version: '3.1.8', -); - -int _loggerMessageEstimateSize( - LoggerMessage object, - List offsets, - Map> allOffsets, -) { - var bytesCount = offsets.last; - { - final value = object.context1; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.context2; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - { - final value = object.details; - if (value != null) { - bytesCount += 3 + value.length * 3; - } - } - bytesCount += 3 + object.message.length * 3; - return bytesCount; -} - -void _loggerMessageSerialize( - LoggerMessage object, - IsarWriter writer, - List offsets, - Map> allOffsets, -) { - writer.writeString(offsets[0], object.context1); - writer.writeString(offsets[1], object.context2); - writer.writeDateTime(offsets[2], object.createdAt); - writer.writeString(offsets[3], object.details); - writer.writeByte(offsets[4], object.level.index); - writer.writeString(offsets[5], object.message); -} - -LoggerMessage _loggerMessageDeserialize( - Id id, - IsarReader reader, - List offsets, - Map> allOffsets, -) { - final object = LoggerMessage( - context1: reader.readStringOrNull(offsets[0]), - context2: reader.readStringOrNull(offsets[1]), - createdAt: reader.readDateTime(offsets[2]), - details: reader.readStringOrNull(offsets[3]), - level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? - LogLevel.info, - message: reader.readString(offsets[5]), - ); - return object; -} - -P _loggerMessageDeserializeProp

( - IsarReader reader, - int propertyId, - int offset, - Map> allOffsets, -) { - switch (propertyId) { - case 0: - return (reader.readStringOrNull(offset)) as P; - case 1: - return (reader.readStringOrNull(offset)) as P; - case 2: - return (reader.readDateTime(offset)) as P; - case 3: - return (reader.readStringOrNull(offset)) as P; - case 4: - return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? - LogLevel.info) as P; - case 5: - return (reader.readString(offset)) as P; - default: - throw IsarError('Unknown property with id $propertyId'); - } -} - -const _LoggerMessagelevelEnumValueMap = { - 'all': 0, - 'finest': 1, - 'finer': 2, - 'fine': 3, - 'config': 4, - 'info': 5, - 'warning': 6, - 'severe': 7, - 'shout': 8, - 'off': 9, -}; -const _LoggerMessagelevelValueEnumMap = { - 0: LogLevel.all, - 1: LogLevel.finest, - 2: LogLevel.finer, - 3: LogLevel.fine, - 4: LogLevel.config, - 5: LogLevel.info, - 6: LogLevel.warning, - 7: LogLevel.severe, - 8: LogLevel.shout, - 9: LogLevel.off, -}; - -Id _loggerMessageGetId(LoggerMessage object) { - return object.id; -} - -List> _loggerMessageGetLinks(LoggerMessage object) { - return []; -} - -void _loggerMessageAttach( - IsarCollection col, Id id, LoggerMessage object) {} - -extension LoggerMessageQueryWhereSort - on QueryBuilder { - QueryBuilder anyId() { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(const IdWhereClause.any()); - }); - } -} - -extension LoggerMessageQueryWhere - on QueryBuilder { - QueryBuilder idEqualTo( - Id id) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); - }); - } - - QueryBuilder idNotEqualTo( - Id id) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ) - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ); - } else { - return query - .addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: false), - ) - .addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: false), - ); - } - }); - } - - QueryBuilder idGreaterThan( - Id id, - {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.greaterThan(lower: id, includeLower: include), - ); - }); - } - - QueryBuilder idLessThan( - Id id, - {bool include = false}) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause( - IdWhereClause.lessThan(upper: id, includeUpper: include), - ); - }); - } - - QueryBuilder idBetween( - Id lowerId, - Id upperId, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); - }); - } -} - -extension LoggerMessageQueryFilter - on QueryBuilder { - QueryBuilder - context1IsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context1', - )); - }); - } - - QueryBuilder - context1IsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context1', - )); - }); - } - - QueryBuilder - context1EqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1GreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1LessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1Between( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context1', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1StartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1EndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1Contains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1Matches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context1', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context1IsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: '', - )); - }); - } - - QueryBuilder - context1IsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context1', - value: '', - )); - }); - } - - QueryBuilder - context2IsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context2', - )); - }); - } - - QueryBuilder - context2IsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context2', - )); - }); - } - - QueryBuilder - context2EqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2GreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2LessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2Between( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context2', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2StartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2EndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2Contains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2Matches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context2', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - context2IsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: '', - )); - }); - } - - QueryBuilder - context2IsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context2', - value: '', - )); - }); - } - - QueryBuilder - createdAtEqualTo(DateTime value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); - }); - } - - QueryBuilder - createdAtGreaterThan( - DateTime value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); - }); - } - - QueryBuilder - createdAtLessThan( - DateTime value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); - }); - } - - QueryBuilder - createdAtBetween( - DateTime lower, - DateTime upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - detailsIsNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'details', - )); - }); - } - - QueryBuilder - detailsIsNotNull() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'details', - )); - }); - } - - QueryBuilder - detailsEqualTo( - String? value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsGreaterThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsLessThan( - String? value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsBetween( - String? lower, - String? upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'details', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'details', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - detailsIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: '', - )); - }); - } - - QueryBuilder - detailsIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'details', - value: '', - )); - }); - } - - QueryBuilder idEqualTo( - Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); - }); - } - - QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); - }); - } - - QueryBuilder idLessThan( - Id value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); - }); - } - - QueryBuilder idBetween( - Id lower, - Id upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - levelEqualTo(LogLevel value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'level', - value: value, - )); - }); - } - - QueryBuilder - levelGreaterThan( - LogLevel value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'level', - value: value, - )); - }); - } - - QueryBuilder - levelLessThan( - LogLevel value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'level', - value: value, - )); - }); - } - - QueryBuilder - levelBetween( - LogLevel lower, - LogLevel upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'level', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - messageEqualTo( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageGreaterThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageBetween( - String lower, - String upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'message', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'message', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - messageIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: '', - )); - }); - } - - QueryBuilder - messageIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'message', - value: '', - )); - }); - } -} - -extension LoggerMessageQueryObject - on QueryBuilder {} - -extension LoggerMessageQueryLinks - on QueryBuilder {} - -extension LoggerMessageQuerySortBy - on QueryBuilder { - QueryBuilder sortByContext1() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.asc); - }); - } - - QueryBuilder - sortByContext1Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.desc); - }); - } - - QueryBuilder sortByContext2() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.asc); - }); - } - - QueryBuilder - sortByContext2Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.desc); - }); - } - - QueryBuilder sortByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.asc); - }); - } - - QueryBuilder - sortByCreatedAtDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.desc); - }); - } - - QueryBuilder sortByDetails() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.asc); - }); - } - - QueryBuilder sortByDetailsDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.desc); - }); - } - - QueryBuilder sortByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.asc); - }); - } - - QueryBuilder sortByLevelDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.desc); - }); - } - - QueryBuilder sortByMessage() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.asc); - }); - } - - QueryBuilder sortByMessageDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.desc); - }); - } -} - -extension LoggerMessageQuerySortThenBy - on QueryBuilder { - QueryBuilder thenByContext1() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.asc); - }); - } - - QueryBuilder - thenByContext1Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context1', Sort.desc); - }); - } - - QueryBuilder thenByContext2() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.asc); - }); - } - - QueryBuilder - thenByContext2Desc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'context2', Sort.desc); - }); - } - - QueryBuilder thenByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.asc); - }); - } - - QueryBuilder - thenByCreatedAtDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'createdAt', Sort.desc); - }); - } - - QueryBuilder thenByDetails() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.asc); - }); - } - - QueryBuilder thenByDetailsDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'details', Sort.desc); - }); - } - - QueryBuilder thenById() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.asc); - }); - } - - QueryBuilder thenByIdDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'id', Sort.desc); - }); - } - - QueryBuilder thenByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.asc); - }); - } - - QueryBuilder thenByLevelDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'level', Sort.desc); - }); - } - - QueryBuilder thenByMessage() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.asc); - }); - } - - QueryBuilder thenByMessageDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'message', Sort.desc); - }); - } -} - -extension LoggerMessageQueryWhereDistinct - on QueryBuilder { - QueryBuilder distinctByContext1( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'context1', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByContext2( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'context2', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByCreatedAt() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'createdAt'); - }); - } - - QueryBuilder distinctByDetails( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'details', caseSensitive: caseSensitive); - }); - } - - QueryBuilder distinctByLevel() { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'level'); - }); - } - - QueryBuilder distinctByMessage( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'message', caseSensitive: caseSensitive); - }); - } -} - -extension LoggerMessageQueryProperty - on QueryBuilder { - QueryBuilder idProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'id'); - }); - } - - QueryBuilder context1Property() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'context1'); - }); - } - - QueryBuilder context2Property() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'context2'); - }); - } - - QueryBuilder createdAtProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'createdAt'); - }); - } - - QueryBuilder detailsProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'details'); - }); - } - - QueryBuilder levelProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'level'); - }); - } - - QueryBuilder messageProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'message'); - }); - } -} diff --git a/mobile/lib/infrastructure/entities/memory.entity.dart b/mobile/lib/infrastructure/entities/memory.entity.dart index 0e19802103..63dcfef5cc 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.dart @@ -14,8 +14,7 @@ class MemoryEntity extends Table with DriftDefaultsMixin { DateTimeColumn get deletedAt => dateTime().nullable()(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get type => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/memory.entity.drift.dart b/mobile/lib/infrastructure/entities/memory.entity.drift.dart index cb88651ba4..f5f18695a5 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.drift.dart @@ -10,65 +10,76 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$MemoryEntityTableCreateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved, - required DateTime memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); -typedef $$MemoryEntityTableUpdateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - i0.Value ownerId, - i0.Value type, - i0.Value data, - i0.Value isSaved, - i0.Value memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); +typedef $$MemoryEntityTableCreateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved, + required DateTime memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); +typedef $$MemoryEntityTableUpdateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + i0.Value ownerId, + i0.Value type, + i0.Value data, + i0.Value isSaved, + i0.Value memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); -final class $$MemoryEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$MemoryEntityTable, i1.MemoryEntityData> { +final class $$MemoryEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData + > { $$MemoryEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').ownerId, + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -82,59 +93,85 @@ class $$MemoryEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnFilters(column)); + column: $table.data, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnFilters(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.showAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -149,60 +186,84 @@ class $$MemoryEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnOrderings(column)); + column: $table.data, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.showAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -251,42 +312,52 @@ class $$MemoryEntityTableAnnotationComposer i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$MemoryEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$MemoryEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -295,74 +366,77 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< i1.$$MemoryEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value data = const i0.Value.absent(), - i0.Value isSaved = const i0.Value.absent(), - i0.Value memoryAt = const i0.Value.absent(), - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved = const i0.Value.absent(), - required DateTime memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value data = const i0.Value.absent(), + i0.Value isSaved = const i0.Value.absent(), + i0.Value memoryAt = const i0.Value.absent(), + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved = const i0.Value.absent(), + required DateTime memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -373,40 +447,50 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$MemoryEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$MemoryEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$MemoryEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $MemoryEntityTable extends i3.MemoryEntity with i0.TableInfo<$MemoryEntityTable, i1.MemoryEntityData> { @@ -417,100 +501,159 @@ class $MemoryEntityTable extends i3.MemoryEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$MemoryEntityTable.$convertertype); - static const i0.VerificationMeta _dataMeta = - const i0.VerificationMeta('data'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$MemoryEntityTable.$convertertype); + static const i0.VerificationMeta _dataMeta = const i0.VerificationMeta( + 'data', + ); @override late final i0.GeneratedColumn data = i0.GeneratedColumn( - 'data', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isSavedMeta = - const i0.VerificationMeta('isSaved'); + 'data', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isSavedMeta = const i0.VerificationMeta( + 'isSaved', + ); @override late final i0.GeneratedColumn isSaved = i0.GeneratedColumn( - 'is_saved', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _memoryAtMeta = - const i0.VerificationMeta('memoryAt'); + 'is_saved', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _memoryAtMeta = const i0.VerificationMeta( + 'memoryAt', + ); @override late final i0.GeneratedColumn memoryAt = - i0.GeneratedColumn('memory_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: true); - static const i0.VerificationMeta _seenAtMeta = - const i0.VerificationMeta('seenAt'); + i0.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _seenAtMeta = const i0.VerificationMeta( + 'seenAt', + ); @override late final i0.GeneratedColumn seenAt = i0.GeneratedColumn( - 'seen_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _showAtMeta = - const i0.VerificationMeta('showAt'); + 'seen_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _showAtMeta = const i0.VerificationMeta( + 'showAt', + ); @override late final i0.GeneratedColumn showAt = i0.GeneratedColumn( - 'show_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _hideAtMeta = - const i0.VerificationMeta('hideAt'); + 'show_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _hideAtMeta = const i0.VerificationMeta( + 'hideAt', + ); @override late final i0.GeneratedColumn hideAt = i0.GeneratedColumn( - 'hide_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -518,8 +661,9 @@ class $MemoryEntityTable extends i3.MemoryEntity static const String $name = 'memory_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -528,50 +672,70 @@ class $MemoryEntityTable extends i3.MemoryEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('data')) { context.handle( - _dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('is_saved')) { - context.handle(_isSavedMeta, - isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta)); + context.handle( + _isSavedMeta, + isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta), + ); } if (data.containsKey('memory_at')) { - context.handle(_memoryAtMeta, - memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta)); + context.handle( + _memoryAtMeta, + memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta), + ); } else if (isInserting) { context.missing(_memoryAtMeta); } if (data.containsKey('seen_at')) { - context.handle(_seenAtMeta, - seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta)); + context.handle( + _seenAtMeta, + seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta), + ); } if (data.containsKey('show_at')) { - context.handle(_showAtMeta, - showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta)); + context.handle( + _showAtMeta, + showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta), + ); } if (data.containsKey('hide_at')) { - context.handle(_hideAtMeta, - hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta)); + context.handle( + _hideAtMeta, + hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta), + ); } return context; } @@ -582,31 +746,56 @@ class $MemoryEntityTable extends i3.MemoryEntity i1.MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: i1.$MemoryEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), - data: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: i1.$MemoryEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), + data: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -637,19 +826,20 @@ class MemoryEntityData extends i0.DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -661,8 +851,9 @@ class MemoryEntityData extends i0.DataClass } map['owner_id'] = i0.Variable(ownerId); { - map['type'] = - i0.Variable(i1.$MemoryEntityTable.$convertertype.toSql(type)); + map['type'] = i0.Variable( + i1.$MemoryEntityTable.$convertertype.toSql(type), + ); } map['data'] = i0.Variable(data); map['is_saved'] = i0.Variable(isSaved); @@ -679,8 +870,10 @@ class MemoryEntityData extends i0.DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -688,8 +881,9 @@ class MemoryEntityData extends i0.DataClass updatedAt: serializer.fromJson(json['updatedAt']), deletedAt: serializer.fromJson(json['deletedAt']), ownerId: serializer.fromJson(json['ownerId']), - type: i1.$MemoryEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$MemoryEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), data: serializer.fromJson(json['data']), isSaved: serializer.fromJson(json['isSaved']), memoryAt: serializer.fromJson(json['memoryAt']), @@ -707,8 +901,9 @@ class MemoryEntityData extends i0.DataClass 'updatedAt': serializer.toJson(updatedAt), 'deletedAt': serializer.toJson(deletedAt), 'ownerId': serializer.toJson(ownerId), - 'type': serializer - .toJson(i1.$MemoryEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$MemoryEntityTable.$convertertype.toJson(type), + ), 'data': serializer.toJson(data), 'isSaved': serializer.toJson(isSaved), 'memoryAt': serializer.toJson(memoryAt), @@ -718,33 +913,33 @@ class MemoryEntityData extends i0.DataClass }; } - i1.MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value deletedAt = const i0.Value.absent(), - String? ownerId, - i2.MemoryTypeEnum? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent()}) => - i1.MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + i1.MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value deletedAt = const i0.Value.absent(), + String? ownerId, + i2.MemoryTypeEnum? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(i1.MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -782,8 +977,20 @@ class MemoryEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -842,11 +1049,11 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { this.seenAt = const i0.Value.absent(), this.showAt = const i0.Value.absent(), this.hideAt = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - type = i0.Value(type), - data = i0.Value(data), - memoryAt = i0.Value(memoryAt); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + type = i0.Value(type), + data = i0.Value(data), + memoryAt = i0.Value(memoryAt); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -877,19 +1084,20 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { }); } - i1.MemoryEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? deletedAt, - i0.Value? ownerId, - i0.Value? type, - i0.Value? data, - i0.Value? isSaved, - i0.Value? memoryAt, - i0.Value? seenAt, - i0.Value? showAt, - i0.Value? hideAt}) { + i1.MemoryEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? deletedAt, + i0.Value? ownerId, + i0.Value? type, + i0.Value? data, + i0.Value? isSaved, + i0.Value? memoryAt, + i0.Value? seenAt, + i0.Value? showAt, + i0.Value? hideAt, + }) { return i1.MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -926,7 +1134,8 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { } if (type.present) { map['type'] = i0.Variable( - i1.$MemoryEntityTable.$convertertype.toSql(type.value)); + i1.$MemoryEntityTable.$convertertype.toSql(type.value), + ); } if (data.present) { map['data'] = i0.Variable(data.value); diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.dart index c304b03724..5053afdfb3 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.dart @@ -6,11 +6,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class MemoryAssetEntity extends Table with DriftDefaultsMixin { const MemoryAssetEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get memoryId => - text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get memoryId => text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)(); @override Set get primaryKey => {assetId, memoryId}; diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart index 9253e8bc05..eedeb85e5c 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart @@ -11,74 +11,92 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i5; -typedef $$MemoryAssetEntityTableCreateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - required String assetId, - required String memoryId, -}); -typedef $$MemoryAssetEntityTableUpdateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value memoryId, -}); +typedef $$MemoryAssetEntityTableCreateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + required String assetId, + required String memoryId, + }); +typedef $$MemoryAssetEntityTableUpdateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value memoryId, + }); -final class $$MemoryAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData> { +final class $$MemoryAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData + > { $$MemoryAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('memory_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .memoryId, - i4.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').id, + ), + ); i5.$$MemoryEntityTableProcessedTableManager get memoryId { final $_column = $_itemColumn('memory_id')!; final manager = i5 .$$MemoryEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('memory_entity')) + ).resultSet('memory_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_memoryIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -93,45 +111,55 @@ class $$MemoryAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableFilterComposer get memoryId { final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,46 +176,55 @@ class $$MemoryAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableOrderingComposer get memoryId { final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -204,65 +241,79 @@ class $$MemoryAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableAnnotationComposer get memoryId { final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})> { +class $$MemoryAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + > { $$MemoryAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -271,35 +322,38 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< .$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value memoryId = const i0.Value.absent(), - }) => - i1.MemoryAssetEntityCompanion( - assetId: assetId, - memoryId: memoryId, - ), - createCompanionCallback: ({ - required String assetId, - required String memoryId, - }) => - i1.MemoryAssetEntityCompanion.insert( - assetId: assetId, - memoryId: memoryId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value memoryId = const i0.Value.absent(), + }) => i1.MemoryAssetEntityCompanion( + assetId: assetId, + memoryId: memoryId, + ), + createCompanionCallback: + ({required String assetId, required String memoryId}) => + i1.MemoryAssetEntityCompanion.insert( + assetId: assetId, + memoryId: memoryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, memoryId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -310,53 +364,65 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$MemoryAssetEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (memoryId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.memoryId, - referencedTable: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (memoryId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.memoryId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})>; +typedef $$MemoryAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + >; class $MemoryAssetEntityTable extends i2.MemoryAssetEntity with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> { @@ -364,24 +430,34 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $MemoryAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _memoryIdMeta = - const i0.VerificationMeta('memoryId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _memoryIdMeta = const i0.VerificationMeta( + 'memoryId', + ); @override late final i0.GeneratedColumn memoryId = i0.GeneratedColumn( - 'memory_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -391,19 +467,24 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity static const String $name = 'memory_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('memory_id')) { - context.handle(_memoryIdMeta, - memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta)); + context.handle( + _memoryIdMeta, + memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta), + ); } else if (isInserting) { context.missing(_memoryIdMeta); } @@ -413,14 +494,20 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity @override Set get $primaryKey => {assetId, memoryId}; @override - i1.MemoryAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.MemoryAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -448,8 +535,10 @@ class MemoryAssetEntityData extends i0.DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -507,8 +596,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = i0.Value(assetId), - memoryId = i0.Value(memoryId); + }) : assetId = i0.Value(assetId), + memoryId = i0.Value(memoryId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? memoryId, @@ -519,8 +608,10 @@ class MemoryAssetEntityCompanion }); } - i1.MemoryAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? memoryId}) { + i1.MemoryAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? memoryId, + }) { return i1.MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c15..d1377f6685 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,73 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id IN :user_ids + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected +) +AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded ) ORDER BY created_at DESC LIMIT $limit; @@ -85,32 +82,37 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL + WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids + ) + AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected + ) + AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded + ) ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1..5a091c349c 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -3,85 +3,113 @@ import 'package:drift/drift.dart' as i0; import 'package:drift/internal/modular.dart' as i1; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i4; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i7; class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset(List var1, - {required i0.Limit limit}) { + i0.Selectable mergedAsset({ + required List userIds, + required MergedAsset$limit limit, + }) { var $arrayStartIndex = 1; - final expandedvar1 = $expandVar($arrayStartIndex, var1.length); - $arrayStartIndex += var1.length; - final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; + final generatedlimit = $write( + limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex, + ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', - variables: [ - for (var $ in var1) i0.Variable($), - ...generatedlimit.introducedVariables - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - ...generatedlimit.watchedTables, - }).map((i0.QueryRow row) => MergedAssetResult( - remoteId: row.readNullable('remote_id'), - localId: row.readNullable('local_id'), - name: row.read('name'), - type: i3.$RemoteAssetEntityTable.$convertertype - .fromSql(row.read('type')), - createdAt: row.read('created_at'), - updatedAt: row.read('updated_at'), - width: row.readNullable('width'), - height: row.readNullable('height'), - durationInSeconds: row.readNullable('duration_in_seconds'), - isFavorite: row.read('is_favorite'), - thumbHash: row.readNullable('thumb_hash'), - checksum: row.readNullable('checksum'), - ownerId: row.readNullable('owner_id'), - livePhotoVideoId: row.readNullable('live_photo_video_id'), - orientation: row.read('orientation'), - stackId: row.readNullable('stack_id'), - stackCount: row.read('stack_count'), - )); + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', + variables: [ + for (var $ in userIds) i0.Variable($), + ...generatedlimit.introducedVariables, + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + stackEntity, + localAlbumAssetEntity, + localAlbumEntity, + ...generatedlimit.watchedTables, + }, + ).map( + (i0.QueryRow row) => MergedAssetResult( + remoteId: row.readNullable('remote_id'), + localId: row.readNullable('local_id'), + name: row.read('name'), + type: i4.$RemoteAssetEntityTable.$convertertype.fromSql( + row.read('type'), + ), + createdAt: row.read('created_at'), + updatedAt: row.read('updated_at'), + width: row.readNullable('width'), + height: row.readNullable('height'), + durationInSeconds: row.readNullable('duration_in_seconds'), + isFavorite: row.read('is_favorite'), + thumbHash: row.readNullable('thumb_hash'), + checksum: row.readNullable('checksum'), + ownerId: row.readNullable('owner_id'), + livePhotoVideoId: row.readNullable('live_photo_video_id'), + orientation: row.read('orientation'), + stackId: row.readNullable('stack_id'), + ), + ); } - i0.Selectable mergedBucket(List var2, - {required int groupBy}) { + i0.Selectable mergedBucket({ + required int groupBy, + required List userIds, + }) { var $arrayStartIndex = 2; - final expandedvar2 = $expandVar($arrayStartIndex, var2.length); - $arrayStartIndex += var2.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', - variables: [ - i0.Variable(groupBy), - for (var $ in var2) i0.Variable($) - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - }).map((i0.QueryRow row) => MergedBucketResult( - assetCount: row.read('asset_count'), - bucketDate: row.read('bucket_date'), - )); + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2)) GROUP BY bucket_date ORDER BY bucket_date DESC', + variables: [ + i0.Variable(groupBy), + for (var $ in userIds) i0.Variable($), + ], + readsFrom: { + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, + }, + ).map( + (i0.QueryRow row) => MergedBucketResult( + assetCount: row.read('asset_count'), + bucketDate: row.read('bucket_date'), + ), + ); } - i3.$RemoteAssetEntityTable get remoteAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i4.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); - i5.$StackEntityTable get stackEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('stack_entity'); + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('remote_asset_entity'); + i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_asset_entity'); + i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => + i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_entity'); } class MergedAssetResult { @@ -101,7 +129,6 @@ class MergedAssetResult { final String? livePhotoVideoId; final int orientation; final String? stackId; - final int stackCount; MergedAssetResult({ this.remoteId, this.localId, @@ -119,15 +146,13 @@ class MergedAssetResult { this.livePhotoVideoId, required this.orientation, this.stackId, - required this.stackCount, }); } +typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); + class MergedBucketResult { final int assetCount; final String bucketDate; - MergedBucketResult({ - required this.assetCount, - required this.bucketDate, - }); + MergedBucketResult({required this.assetCount, required this.bucketDate}); } diff --git a/mobile/lib/infrastructure/entities/partner.entity.dart b/mobile/lib/infrastructure/entities/partner.entity.dart index 8b51d93e6f..dbc675ee99 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.dart @@ -5,11 +5,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class PartnerEntity extends Table with DriftDefaultsMixin { const PartnerEntity(); - TextColumn get sharedById => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedById => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get sharedWithId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedWithId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); BoolColumn get inTimeline => boolean().withDefault(const Constant(false))(); diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart index 26a5dd2fe0..01ec72fe23 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.drift.dart @@ -10,74 +10,94 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline, -}); -typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - i0.Value sharedById, - i0.Value sharedWithId, - i0.Value inTimeline, -}); +typedef $$PartnerEntityTableCreateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline, + }); +typedef $$PartnerEntityTableUpdateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + i0.Value sharedById, + i0.Value sharedWithId, + i0.Value inTimeline, + }); -final class $$PartnerEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PartnerEntityTable, i1.PartnerEntityData> { +final class $$PartnerEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData + > { $$PartnerEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('partner_entity') - .sharedById, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('partner_entity').sharedById, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedById { final $_column = $_itemColumn('shared_by_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet('partner_entity') .sharedWithId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedWithId { final $_column = $_itemColumn('shared_with_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -91,49 +111,61 @@ class $$PartnerEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get sharedById { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableFilterComposer get sharedWithId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,50 +180,61 @@ class $$PartnerEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get inTimeline => $composableBuilder( - column: $table.inTimeline, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get sharedById { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableOrderingComposer get sharedWithId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -206,68 +249,85 @@ class $$PartnerEntityTableAnnotationComposer super.$removeJoinBuilderFromRootComposer, }); i0.GeneratedColumn get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => column); + column: $table.inTimeline, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get sharedById { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableAnnotationComposer get sharedWithId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PartnerEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})> { +class $$PartnerEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + > { $$PartnerEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PartnerEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PartnerEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -276,38 +336,41 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< i1.$$PartnerEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value sharedById = const i0.Value.absent(), - i0.Value sharedWithId = const i0.Value.absent(), - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), - createCompanionCallback: ({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion.insert( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), + updateCompanionCallback: + ({ + i0.Value sharedById = const i0.Value.absent(), + i0.Value sharedWithId = const i0.Value.absent(), + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + createCompanionCallback: + ({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion.insert( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PartnerEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PartnerEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({sharedById = false, sharedWithId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,52 +381,65 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (sharedById) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedById, - referencedTable: - i1.$$PartnerEntityTableReferences._sharedByIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedByIdTable(db) - .id, - ) as T; - } - if (sharedWithId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedWithId, - referencedTable: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (sharedById) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedById, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db) + .id, + ) + as T; + } + if (sharedWithId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedWithId, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>; +typedef $$PartnerEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + >; class $PartnerEntityTable extends i2.PartnerEntity with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> { @@ -371,37 +447,55 @@ class $PartnerEntityTable extends i2.PartnerEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $PartnerEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _sharedByIdMeta = - const i0.VerificationMeta('sharedById'); + static const i0.VerificationMeta _sharedByIdMeta = const i0.VerificationMeta( + 'sharedById', + ); @override late final i0.GeneratedColumn sharedById = i0.GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _sharedWithIdMeta = const i0.VerificationMeta('sharedWithId'); @override late final i0.GeneratedColumn sharedWithId = - i0.GeneratedColumn('shared_with_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _inTimelineMeta = - const i0.VerificationMeta('inTimeline'); + i0.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _inTimelineMeta = const i0.VerificationMeta( + 'inTimeline', + ); @override late final i0.GeneratedColumn inTimeline = i0.GeneratedColumn( - 'in_timeline', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const i3.Constant(false)); + 'in_timeline', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); @override - List get $columns => - [sharedById, sharedWithId, inTimeline]; + List get $columns => [ + sharedById, + sharedWithId, + inTimeline, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -409,31 +503,38 @@ class $PartnerEntityTable extends i2.PartnerEntity static const String $name = 'partner_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('shared_by_id')) { context.handle( + _sharedByIdMeta, + sharedById.isAcceptableOrUnknown( + data['shared_by_id']!, _sharedByIdMeta, - sharedById.isAcceptableOrUnknown( - data['shared_by_id']!, _sharedByIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedByIdMeta); } if (data.containsKey('shared_with_id')) { context.handle( + _sharedWithIdMeta, + sharedWithId.isAcceptableOrUnknown( + data['shared_with_id']!, _sharedWithIdMeta, - sharedWithId.isAcceptableOrUnknown( - data['shared_with_id']!, _sharedWithIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedWithIdMeta); } if (data.containsKey('in_timeline')) { context.handle( - _inTimelineMeta, - inTimeline.isAcceptableOrUnknown( - data['in_timeline']!, _inTimelineMeta)); + _inTimelineMeta, + inTimeline.isAcceptableOrUnknown(data['in_timeline']!, _inTimelineMeta), + ); } return context; } @@ -445,11 +546,17 @@ class $PartnerEntityTable extends i2.PartnerEntity final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PartnerEntityData( sharedById: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, sharedWithId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -469,10 +576,11 @@ class PartnerEntityData extends i0.DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -482,8 +590,10 @@ class PartnerEntityData extends i0.DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -501,22 +611,26 @@ class PartnerEntityData extends i0.DataClass }; } - i1.PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - i1.PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + i1.PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => i1.PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(i1.PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -554,8 +668,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const i0.Value.absent(), - }) : sharedById = i0.Value(sharedById), - sharedWithId = i0.Value(sharedWithId); + }) : sharedById = i0.Value(sharedById), + sharedWithId = i0.Value(sharedWithId); static i0.Insertable custom({ i0.Expression? sharedById, i0.Expression? sharedWithId, @@ -568,10 +682,11 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { }); } - i1.PartnerEntityCompanion copyWith( - {i0.Value? sharedById, - i0.Value? sharedWithId, - i0.Value? inTimeline}) { + i1.PartnerEntityCompanion copyWith({ + i0.Value? sharedById, + i0.Value? sharedWithId, + i0.Value? inTimeline, + }) { return i1.PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, diff --git a/mobile/lib/infrastructure/entities/person.entity.dart b/mobile/lib/infrastructure/entities/person.entity.dart index 68dd04cb5f..f0878e00f8 100644 --- a/mobile/lib/infrastructure/entities/person.entity.dart +++ b/mobile/lib/infrastructure/entities/person.entity.dart @@ -11,16 +11,12 @@ class PersonEntity extends Table with DriftDefaultsMixin { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); TextColumn get name => text()(); - // TODO: foreign key refering to asset faces TextColumn get faceAssetId => text().nullable()(); - TextColumn get thumbnailPath => text()(); - BoolColumn get isFavorite => boolean()(); BoolColumn get isHidden => boolean()(); diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart index f0ced63f0e..ffbd796f4b 100644 --- a/mobile/lib/infrastructure/entities/person.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -9,63 +9,72 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PersonEntityTableCreateCompanionBuilder = i1.PersonEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String name, - i0.Value faceAssetId, - required String thumbnailPath, - required bool isFavorite, - required bool isHidden, - i0.Value color, - i0.Value birthDate, -}); -typedef $$PersonEntityTableUpdateCompanionBuilder = i1.PersonEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value name, - i0.Value faceAssetId, - i0.Value thumbnailPath, - i0.Value isFavorite, - i0.Value isHidden, - i0.Value color, - i0.Value birthDate, -}); +typedef $$PersonEntityTableCreateCompanionBuilder = + i1.PersonEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String name, + i0.Value faceAssetId, + required bool isFavorite, + required bool isHidden, + i0.Value color, + i0.Value birthDate, + }); +typedef $$PersonEntityTableUpdateCompanionBuilder = + i1.PersonEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value name, + i0.Value faceAssetId, + i0.Value isFavorite, + i0.Value isHidden, + i0.Value color, + i0.Value birthDate, + }); -final class $$PersonEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PersonEntityTable, i1.PersonEntityData> { +final class $$PersonEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData + > { $$PersonEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('person_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('person_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -79,56 +88,74 @@ class $$PersonEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnFilters(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnFilters(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnFilters(column)); + column: $table.color, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get birthDate => $composableBuilder( - column: $table.birthDate, builder: (column) => i0.ColumnFilters(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -143,60 +170,74 @@ class $$PersonEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get faceAssetId => $composableBuilder( - column: $table.faceAssetId, - builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.faceAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isHidden => $composableBuilder( - column: $table.isHidden, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isHidden, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get color => $composableBuilder( - column: $table.color, builder: (column) => i0.ColumnOrderings(column)); + column: $table.color, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get birthDate => $composableBuilder( - column: $table.birthDate, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.birthDate, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -223,13 +264,14 @@ class $$PersonEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get faceAssetId => $composableBuilder( - column: $table.faceAssetId, builder: (column) => column); - - i0.GeneratedColumn get thumbnailPath => $composableBuilder( - column: $table.thumbnailPath, builder: (column) => column); + column: $table.faceAssetId, + builder: (column) => column, + ); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get isHidden => $composableBuilder(column: $table.isHidden, builder: (column) => column); @@ -242,42 +284,52 @@ class $$PersonEntityTableAnnotationComposer i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PersonEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$PersonEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$PersonEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PersonEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PersonEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -286,70 +338,69 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< i1.$$PersonEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PersonEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value faceAssetId = const i0.Value.absent(), - i0.Value thumbnailPath = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value isHidden = const i0.Value.absent(), - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String name, - i0.Value faceAssetId = const i0.Value.absent(), - required String thumbnailPath, - required bool isFavorite, - required bool isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent(), - }) => - i1.PersonEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value faceAssetId = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value isHidden = const i0.Value.absent(), + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + i0.Value faceAssetId = const i0.Value.absent(), + required bool isFavorite, + required bool isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PersonEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PersonEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -360,40 +411,50 @@ class $$PersonEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$PersonEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$PersonEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PersonEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PersonEntityTable, - i1.PersonEntityData, - i1.$$PersonEntityTableFilterComposer, - i1.$$PersonEntityTableOrderingComposer, - i1.$$PersonEntityTableAnnotationComposer, - $$PersonEntityTableCreateCompanionBuilder, - $$PersonEntityTableUpdateCompanionBuilder, - (i1.PersonEntityData, i1.$$PersonEntityTableReferences), - i1.PersonEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$PersonEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $PersonEntityTable extends i2.PersonEntity with i0.TableInfo<$PersonEntityTable, i1.PersonEntityData> { @@ -404,95 +465,139 @@ class $PersonEntityTable extends i2.PersonEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _faceAssetIdMeta = - const i0.VerificationMeta('faceAssetId'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _faceAssetIdMeta = const i0.VerificationMeta( + 'faceAssetId', + ); @override late final i0.GeneratedColumn faceAssetId = - i0.GeneratedColumn('face_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbnailPathMeta = - const i0.VerificationMeta('thumbnailPath'); - @override - late final i0.GeneratedColumn thumbnailPath = - i0.GeneratedColumn('thumbnail_path', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + i0.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); - static const i0.VerificationMeta _isHiddenMeta = - const i0.VerificationMeta('isHidden'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _isHiddenMeta = const i0.VerificationMeta( + 'isHidden', + ); @override late final i0.GeneratedColumn isHidden = i0.GeneratedColumn( - 'is_hidden', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_hidden" IN (0, 1))')); - static const i0.VerificationMeta _colorMeta = - const i0.VerificationMeta('color'); + 'is_hidden', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _colorMeta = const i0.VerificationMeta( + 'color', + ); @override late final i0.GeneratedColumn color = i0.GeneratedColumn( - 'color', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _birthDateMeta = - const i0.VerificationMeta('birthDate'); + 'color', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _birthDateMeta = const i0.VerificationMeta( + 'birthDate', + ); @override late final i0.GeneratedColumn birthDate = - i0.GeneratedColumn('birth_date', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - thumbnailPath, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -500,8 +605,9 @@ class $PersonEntityTable extends i2.PersonEntity static const String $name = 'person_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -510,60 +616,69 @@ class $PersonEntityTable extends i2.PersonEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('face_asset_id')) { context.handle( + _faceAssetIdMeta, + faceAssetId.isAcceptableOrUnknown( + data['face_asset_id']!, _faceAssetIdMeta, - faceAssetId.isAcceptableOrUnknown( - data['face_asset_id']!, _faceAssetIdMeta)); - } - if (data.containsKey('thumbnail_path')) { - context.handle( - _thumbnailPathMeta, - thumbnailPath.isAcceptableOrUnknown( - data['thumbnail_path']!, _thumbnailPathMeta)); - } else if (isInserting) { - context.missing(_thumbnailPathMeta); + ), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } else if (isInserting) { context.missing(_isFavoriteMeta); } if (data.containsKey('is_hidden')) { - context.handle(_isHiddenMeta, - isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta)); + context.handle( + _isHiddenMeta, + isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta), + ); } else if (isInserting) { context.missing(_isHiddenMeta); } if (data.containsKey('color')) { context.handle( - _colorMeta, color.isAcceptableOrUnknown(data['color']!, _colorMeta)); + _colorMeta, + color.isAcceptableOrUnknown(data['color']!, _colorMeta), + ); } if (data.containsKey('birth_date')) { - context.handle(_birthDateMeta, - birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta)); + context.handle( + _birthDateMeta, + birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta), + ); } return context; } @@ -574,28 +689,46 @@ class $PersonEntityTable extends i2.PersonEntity i1.PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PersonEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, faceAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + i0.DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -618,23 +751,22 @@ class PersonEntityData extends i0.DataClass final String ownerId; final String name; final String? faceAssetId; - final String thumbnailPath; final bool isFavorite; final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -646,7 +778,6 @@ class PersonEntityData extends i0.DataClass if (!nullToAbsent || faceAssetId != null) { map['face_asset_id'] = i0.Variable(faceAssetId); } - map['thumbnail_path'] = i0.Variable(thumbnailPath); map['is_favorite'] = i0.Variable(isFavorite); map['is_hidden'] = i0.Variable(isHidden); if (!nullToAbsent || color != null) { @@ -658,8 +789,10 @@ class PersonEntityData extends i0.DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -668,7 +801,6 @@ class PersonEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), name: serializer.fromJson(json['name']), faceAssetId: serializer.fromJson(json['faceAssetId']), - thumbnailPath: serializer.fromJson(json['thumbnailPath']), isFavorite: serializer.fromJson(json['isFavorite']), isHidden: serializer.fromJson(json['isHidden']), color: serializer.fromJson(json['color']), @@ -685,7 +817,6 @@ class PersonEntityData extends i0.DataClass 'ownerId': serializer.toJson(ownerId), 'name': serializer.toJson(name), 'faceAssetId': serializer.toJson(faceAssetId), - 'thumbnailPath': serializer.toJson(thumbnailPath), 'isFavorite': serializer.toJson(isFavorite), 'isHidden': serializer.toJson(isHidden), 'color': serializer.toJson(color), @@ -693,31 +824,29 @@ class PersonEntityData extends i0.DataClass }; } - i1.PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - i0.Value faceAssetId = const i0.Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - i0.Value color = const i0.Value.absent(), - i0.Value birthDate = const i0.Value.absent()}) => - i1.PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + i1.PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + i0.Value faceAssetId = const i0.Value.absent(), + bool? isFavorite, + bool? isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(i1.PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -725,13 +854,12 @@ class PersonEntityData extends i0.DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, - thumbnailPath: data.thumbnailPath.present - ? data.thumbnailPath.value - : this.thumbnailPath, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -747,7 +875,6 @@ class PersonEntityData extends i0.DataClass ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') @@ -757,8 +884,18 @@ class PersonEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -769,7 +906,6 @@ class PersonEntityData extends i0.DataClass other.ownerId == this.ownerId && other.name == this.name && other.faceAssetId == this.faceAssetId && - other.thumbnailPath == this.thumbnailPath && other.isFavorite == this.isFavorite && other.isHidden == this.isHidden && other.color == this.color && @@ -783,7 +919,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { final i0.Value ownerId; final i0.Value name; final i0.Value faceAssetId; - final i0.Value thumbnailPath; final i0.Value isFavorite; final i0.Value isHidden; final i0.Value color; @@ -795,7 +930,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { this.ownerId = const i0.Value.absent(), this.name = const i0.Value.absent(), this.faceAssetId = const i0.Value.absent(), - this.thumbnailPath = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.isHidden = const i0.Value.absent(), this.color = const i0.Value.absent(), @@ -808,17 +942,15 @@ class PersonEntityCompanion extends i0.UpdateCompanion { required String ownerId, required String name, this.faceAssetId = const i0.Value.absent(), - required String thumbnailPath, required bool isFavorite, required bool isHidden, this.color = const i0.Value.absent(), this.birthDate = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - name = i0.Value(name), - thumbnailPath = i0.Value(thumbnailPath), - isFavorite = i0.Value(isFavorite), - isHidden = i0.Value(isHidden); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + name = i0.Value(name), + isFavorite = i0.Value(isFavorite), + isHidden = i0.Value(isHidden); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -826,7 +958,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { i0.Expression? ownerId, i0.Expression? name, i0.Expression? faceAssetId, - i0.Expression? thumbnailPath, i0.Expression? isFavorite, i0.Expression? isHidden, i0.Expression? color, @@ -839,7 +970,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (ownerId != null) 'owner_id': ownerId, if (name != null) 'name': name, if (faceAssetId != null) 'face_asset_id': faceAssetId, - if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, if (isFavorite != null) 'is_favorite': isFavorite, if (isHidden != null) 'is_hidden': isHidden, if (color != null) 'color': color, @@ -847,18 +977,18 @@ class PersonEntityCompanion extends i0.UpdateCompanion { }); } - i1.PersonEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? name, - i0.Value? faceAssetId, - i0.Value? thumbnailPath, - i0.Value? isFavorite, - i0.Value? isHidden, - i0.Value? color, - i0.Value? birthDate}) { + i1.PersonEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? name, + i0.Value? faceAssetId, + i0.Value? isFavorite, + i0.Value? isHidden, + i0.Value? color, + i0.Value? birthDate, + }) { return i1.PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -866,7 +996,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ownerId: ownerId ?? this.ownerId, name: name ?? this.name, faceAssetId: faceAssetId ?? this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, isFavorite: isFavorite ?? this.isFavorite, isHidden: isHidden ?? this.isHidden, color: color ?? this.color, @@ -895,9 +1024,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { if (faceAssetId.present) { map['face_asset_id'] = i0.Variable(faceAssetId.value); } - if (thumbnailPath.present) { - map['thumbnail_path'] = i0.Variable(thumbnailPath.value); - } if (isFavorite.present) { map['is_favorite'] = i0.Variable(isFavorite.value); } @@ -922,7 +1048,6 @@ class PersonEntityCompanion extends i0.UpdateCompanion { ..write('ownerId: $ownerId, ') ..write('name: $name, ') ..write('faceAssetId: $faceAssetId, ') - ..write('thumbnailPath: $thumbnailPath, ') ..write('isFavorite: $isFavorite, ') ..write('isHidden: $isHidden, ') ..write('color: $color, ') diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.dart b/mobile/lib/infrastructure/entities/remote_album.entity.dart index 377d67446f..74b00dd9ee 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.dart @@ -17,15 +17,12 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get thumbnailAssetId => text() - .references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull) - .nullable()(); + TextColumn get thumbnailAssetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()(); - BoolColumn get isActivityEnabled => - boolean().withDefault(const Constant(true))(); + BoolColumn get isActivityEnabled => boolean().withDefault(const Constant(true))(); IntColumn get order => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart index bc13c8cb5c..30a6d0b535 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart @@ -13,89 +13,107 @@ import 'package:drift/internal/modular.dart' as i6; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i7; -typedef $$RemoteAlbumEntityTableCreateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - required i2.AlbumAssetOrder order, -}); -typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - i0.Value order, -}); +typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + required i2.AlbumAssetOrder order, + }); +typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + i0.Value order, + }); -final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData> { +final class $$RemoteAlbumEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData + > { $$RemoteAlbumEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_album_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable( - i0.GeneratedDatabase db) => - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .thumbnailAssetId, - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i0.GeneratedDatabase db, + ) => i6.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('remote_album_entity') + .thumbnailAssetId, + i6.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId { final $_column = $_itemColumn('thumbnail_asset_id'); if ($_column == null) return null; final manager = i7 .$$RemoteAssetEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -109,71 +127,92 @@ class $$RemoteAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get order => $composableBuilder( - column: $table.order, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get order => $composableBuilder( + column: $table.order, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -188,73 +227,92 @@ class $$RemoteAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get order => $composableBuilder( - column: $table.order, builder: (column) => i0.ColumnOrderings(column)); + column: $table.order, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -275,7 +333,9 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -284,73 +344,89 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, builder: (column) => column); + column: $table.isActivityEnabled, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get order => $composableBuilder(column: $table.order, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> { +class $$RemoteAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + > { $$RemoteAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -359,63 +435,68 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< .$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - i0.Value order = const i0.Value.absent(), - }) => - i1.RemoteAlbumEntityCompanion( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - required i2.AlbumAssetOrder order, - }) => - i1.RemoteAlbumEntityCompanion.insert( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + i0.Value order = const i0.Value.absent(), + }) => i1.RemoteAlbumEntityCompanion( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + required i2.AlbumAssetOrder order, + }) => i1.RemoteAlbumEntityCompanion.insert( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -426,53 +507,65 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } - if (thumbnailAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.thumbnailAssetId, - referencedTable: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } + if (thumbnailAssetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.thumbnailAssetId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>; +typedef $$RemoteAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + >; class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> { @@ -483,84 +576,129 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const i4.Constant('')); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'description', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const i4.Constant(''), + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _thumbnailAssetIdMeta = const i0.VerificationMeta('thumbnailAssetId'); @override late final i0.GeneratedColumn thumbnailAssetId = - i0.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i0.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); static const i0.VerificationMeta _isActivityEnabledMeta = const i0.VerificationMeta('isActivityEnabled'); @override late final i0.GeneratedColumn isActivityEnabled = - i0.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const i4.Constant(true)); + i0.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const i4.Constant(true), + ); @override late final i0.GeneratedColumnWithTypeConverter - order = i0.GeneratedColumn('order', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumEntityTable.$converterorder); + order = + i0.GeneratedColumn( + 'order', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumEntityTable.$converterorder, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -568,8 +706,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const String $name = 'remote_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -579,41 +718,58 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('thumbnail_asset_id')) { context.handle( + _thumbnailAssetIdMeta, + thumbnailAssetId.isAcceptableOrUnknown( + data['thumbnail_asset_id']!, _thumbnailAssetIdMeta, - thumbnailAssetId.isAcceptableOrUnknown( - data['thumbnail_asset_id']!, _thumbnailAssetIdMeta)); + ), + ); } if (data.containsKey('is_activity_enabled')) { context.handle( + _isActivityEnabledMeta, + isActivityEnabled.isAcceptableOrUnknown( + data['is_activity_enabled']!, _isActivityEnabledMeta, - isActivityEnabled.isAcceptableOrUnknown( - data['is_activity_enabled']!, _isActivityEnabledMeta)); + ), + ); } return context; } @@ -621,29 +777,50 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ), ); } @@ -654,7 +831,8 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static i0.JsonTypeConverter2 $converterorder = const i0.EnumIndexConverter( - i2.AlbumAssetOrder.values); + i2.AlbumAssetOrder.values, + ); @override bool get withoutRowId => true; @override @@ -672,16 +850,17 @@ class RemoteAlbumEntityData extends i0.DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final i2.AlbumAssetOrder order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -697,13 +876,16 @@ class RemoteAlbumEntityData extends i0.DataClass map['is_activity_enabled'] = i0.Variable(isActivityEnabled); { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order), + ); } return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -714,8 +896,9 @@ class RemoteAlbumEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), - order: i1.$RemoteAlbumEntityTable.$converterorder - .fromJson(serializer.fromJson(json['order'])), + order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson( + serializer.fromJson(json['order']), + ), ); } @override @@ -731,39 +914,41 @@ class RemoteAlbumEntityData extends i0.DataClass 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), 'isActivityEnabled': serializer.toJson(isActivityEnabled), 'order': serializer.toJson( - i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)), + i1.$RemoteAlbumEntityTable.$converterorder.toJson(order), + ), }; } - i1.RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - bool? isActivityEnabled, - i2.AlbumAssetOrder? order}) => - i1.RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + i1.RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + bool? isActivityEnabled, + i2.AlbumAssetOrder? order, + }) => i1.RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -794,8 +979,17 @@ class RemoteAlbumEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -843,10 +1037,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(), required i2.AlbumAssetOrder order, - }) : id = i0.Value(id), - name = i0.Value(name), - ownerId = i0.Value(ownerId), - order = i0.Value(order); + }) : id = i0.Value(id), + name = i0.Value(name), + ownerId = i0.Value(ownerId), + order = i0.Value(order); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -871,16 +1065,17 @@ class RemoteAlbumEntityCompanion }); } - i1.RemoteAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? description, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? thumbnailAssetId, - i0.Value? isActivityEnabled, - i0.Value? order}) { + i1.RemoteAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? description, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? thumbnailAssetId, + i0.Value? isActivityEnabled, + i0.Value? order, + }) { return i1.RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -923,7 +1118,8 @@ class RemoteAlbumEntityCompanion } if (order.present) { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart index 1dcc336ed8..e99f5364a4 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart @@ -6,11 +6,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin { const RemoteAlbumAssetEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get albumId => - text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); @override Set get primaryKey => {assetId, albumId}; diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart index ab50607c96..adf22635c1 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' as i5; -typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { +final class $$RemoteAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { $$RemoteAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$RemoteAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$RemoteAlbumAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableFilterComposer get albumId { final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$RemoteAlbumAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i5.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$RemoteAlbumAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i5.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$RemoteAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$RemoteAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.RemoteAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.RemoteAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.RemoteAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.RemoteAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,107 @@ class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, +typedef $$RemoteAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableReferences - ), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity with - i0.TableInfo<$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { + i0.TableInfo< + $RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +489,24 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity static const String $name = 'remote_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +516,20 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +548,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +560,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +585,8 @@ class RemoteAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - i1.RemoteAlbumAssetEntityCompanion data) { + i1.RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +623,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +635,10 @@ class RemoteAlbumAssetEntityCompanion }); } - i1.RemoteAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.RemoteAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart index 4198fb7e46..9cb277f9d0 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart @@ -7,11 +7,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class RemoteAlbumUserEntity extends Table with DriftDefaultsMixin { const RemoteAlbumUserEntity(); - TextColumn get albumId => - text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get userId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get role => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart index 7ec1151a8a..f8167ddf94 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart @@ -12,78 +12,98 @@ import 'package:drift/internal/modular.dart' as i5; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i6; -typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, -}); -typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - i0.Value albumId, - i0.Value userId, - i0.Value role, -}); +typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }); +typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + i0.Value albumId, + i0.Value userId, + i0.Value role, + }); -final class $$RemoteAlbumUserEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData> { +final class $$RemoteAlbumUserEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { $$RemoteAlbumUserEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .albumId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i4 .$$RemoteAlbumEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .userId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i6.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i6 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -97,51 +117,62 @@ class $$RemoteAlbumUserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get role => $composableBuilder( - column: $table.role, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get role => $composableBuilder( + column: $table.role, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i4.$$RemoteAlbumEntityTableFilterComposer get albumId { final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableFilterComposer get userId { final i6.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -156,51 +187,62 @@ class $$RemoteAlbumUserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get role => $composableBuilder( - column: $table.role, builder: (column) => i0.ColumnOrderings(column)); + column: $table.role, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i4.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableOrderingComposer get userId { final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -220,108 +262,134 @@ class $$RemoteAlbumUserEntityTableAnnotationComposer i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i4.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableAnnotationComposer get userId { final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})> { +class $$RemoteAlbumUserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableReferences, + ), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + > { $$RemoteAlbumUserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumUserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumUserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumUserEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumUserEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumUserEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value albumId = const i0.Value.absent(), - i0.Value userId = const i0.Value.absent(), - i0.Value role = const i0.Value.absent(), - }) => - i1.RemoteAlbumUserEntityCompanion( - albumId: albumId, - userId: userId, - role: role, - ), - createCompanionCallback: ({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, - }) => - i1.RemoteAlbumUserEntityCompanion.insert( - albumId: albumId, - userId: userId, - role: role, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value albumId = const i0.Value.absent(), + i0.Value userId = const i0.Value.absent(), + i0.Value role = const i0.Value.absent(), + }) => i1.RemoteAlbumUserEntityCompanion( + albumId: albumId, + userId: userId, + role: role, + ), + createCompanionCallback: + ({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }) => i1.RemoteAlbumUserEntityCompanion.insert( + albumId: albumId, + userId: userId, + role: role, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumUserEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumUserEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({albumId = false, userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -332,89 +400,115 @@ class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumUserEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableReferences - ), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})>; +typedef $$RemoteAlbumUserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + >; class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity with - i0 - .TableInfo<$RemoteAlbumUserEntityTable, i1.RemoteAlbumUserEntityData> { + i0.TableInfo< + $RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter role = - i0.GeneratedColumn('role', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumUserEntityTable.$converterrole); + i0.GeneratedColumn( + 'role', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumUserEntityTable.$converterrole, + ); @override List get $columns => [albumId, userId, role]; @override @@ -424,19 +518,24 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity static const String $name = 'remote_album_user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -446,17 +545,26 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity @override Set get $primaryKey => {albumId, userId}; @override - i1.RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ), ); } @@ -478,8 +586,11 @@ class RemoteAlbumUserEntityData extends i0.DataClass final String albumId; final String userId; final i2.AlbumUserRole role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -487,19 +598,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass map['user_id'] = i0.Variable(userId); { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role), + ); } return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), userId: serializer.fromJson(json['userId']), - role: i1.$RemoteAlbumUserEntityTable.$converterrole - .fromJson(serializer.fromJson(json['role'])), + role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromJson( + serializer.fromJson(json['role']), + ), ); } @override @@ -509,19 +624,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass 'albumId': serializer.toJson(albumId), 'userId': serializer.toJson(userId), 'role': serializer.toJson( - i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role)), + i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role), + ), }; } - i1.RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, i2.AlbumUserRole? role}) => - i1.RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + i1.RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + i2.AlbumUserRole? role, + }) => i1.RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - i1.RemoteAlbumUserEntityCompanion data) { + i1.RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -564,9 +683,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required i2.AlbumUserRole role, - }) : albumId = i0.Value(albumId), - userId = i0.Value(userId), - role = i0.Value(role); + }) : albumId = i0.Value(albumId), + userId = i0.Value(userId), + role = i0.Value(role); static i0.Insertable custom({ i0.Expression? albumId, i0.Expression? userId, @@ -579,10 +698,11 @@ class RemoteAlbumUserEntityCompanion }); } - i1.RemoteAlbumUserEntityCompanion copyWith( - {i0.Value? albumId, - i0.Value? userId, - i0.Value? role}) { + i1.RemoteAlbumUserEntityCompanion copyWith({ + i0.Value? albumId, + i0.Value? userId, + i0.Value? role, + }) { return i1.RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -601,7 +721,8 @@ class RemoteAlbumUserEntityCompanion } if (role.present) { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 0b2896538e..dcc885a2a9 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,14 +5,21 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex( - name: 'UQ_remote_asset_owner_checksum', - columns: {#checksum, #ownerId}, - unique: true, +@TableIndex.sql( + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', ) -@TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) -class RemoteAssetEntity extends Table - with DriftDefaultsMixin, AssetEntityMixin { +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum +ON remote_asset_entity (owner_id, checksum) +WHERE (library_id IS NULL); +''') +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum +ON remote_asset_entity (owner_id, library_id, checksum) +WHERE (library_id IS NOT NULL); +''') +@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)') +class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); TextColumn get id => text()(); @@ -21,8 +28,7 @@ class RemoteAssetEntity extends Table BoolColumn get isFavorite => boolean().withDefault(const Constant(false))(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); DateTimeColumn get localDateTime => dateTime().nullable()(); @@ -36,27 +42,29 @@ class RemoteAssetEntity extends Table TextColumn get stackId => text().nullable()(); + TextColumn get libraryId => text().nullable()(); + @override Set get primaryKey => {id}; } extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { - RemoteAsset toDto() => RemoteAsset( - id: id, - name: name, - ownerId: ownerId, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - thumbHash: thumbHash, - visibility: visibility, - livePhotoVideoId: livePhotoVideoId, - localId: null, - stackId: stackId, - ); + RemoteAsset toDto({String? localId}) => RemoteAsset( + id: id, + name: name, + ownerId: ownerId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + thumbHash: thumbHash, + visibility: visibility, + livePhotoVideoId: livePhotoVideoId, + localId: localId, + stackId: stackId, + ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 543ed65985..eab7f95f64 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -11,78 +11,92 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$RemoteAssetEntityTableCreateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - required String checksum, - i0.Value isFavorite, - required String ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - required i2.AssetVisibility visibility, - i0.Value stackId, -}); -typedef $$RemoteAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - i0.Value visibility, - i0.Value stackId, -}); +typedef $$RemoteAssetEntityTableCreateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + required String checksum, + i0.Value isFavorite, + required String ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + required i2.AssetVisibility visibility, + i0.Value stackId, + i0.Value libraryId, + }); +typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + i0.Value visibility, + i0.Value stackId, + i0.Value libraryId, + }); -final class $$RemoteAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData> { +final class $$RemoteAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData + > { $$RemoteAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -96,79 +110,116 @@ class $$RemoteAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get thumbHash => $composableBuilder( - column: $table.thumbHash, builder: (column) => i0.ColumnFilters(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get visibility => $composableBuilder( + column: $table.visibility, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnFilters(column)); + column: $table.stackId, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -183,81 +234,114 @@ class $$RemoteAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get thumbHash => $composableBuilder( - column: $table.thumbHash, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.visibility, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnOrderings(column)); + column: $table.stackId, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -290,7 +374,9 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -299,10 +385,14 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get localDateTime => $composableBuilder( - column: $table.localDateTime, builder: (column) => column); + column: $table.localDateTime, + builder: (column) => column, + ); i0.GeneratedColumn get thumbHash => $composableBuilder(column: $table.thumbHash, builder: (column) => column); @@ -311,53 +401,70 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.deletedAt, builder: (column) => column); i0.GeneratedColumn get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, builder: (column) => column); + column: $table.livePhotoVideoId, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get visibility => $composableBuilder( - column: $table.visibility, builder: (column) => column); + column: $table.visibility, + builder: (column) => column, + ); i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); + i0.GeneratedColumn get libraryId => + $composableBuilder(column: $table.libraryId, builder: (column) => column); + i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$RemoteAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$RemoteAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -366,95 +473,105 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< .$$RemoteAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i0.Value visibility = const i0.Value.absent(), - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - required String checksum, - i0.Value isFavorite = const i0.Value.absent(), - required String ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - required i2.AssetVisibility visibility, - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i0.Value visibility = + const i0.Value.absent(), + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + libraryId: libraryId, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + required String checksum, + i0.Value isFavorite = const i0.Value.absent(), + required String ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + required i2.AssetVisibility visibility, + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + libraryId: libraryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -465,45 +582,54 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAssetEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAssetEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})>; -i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); +typedef $$RemoteAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; +i0.Index get idxRemoteAssetOwnerChecksum => i0.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', +); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity with i0.TableInfo<$RemoteAssetEntityTable, i1.RemoteAssetEntityData> { @@ -511,138 +637,234 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$RemoteAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - '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)); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + '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), + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _localDateTimeMeta = const i0.VerificationMeta('localDateTime'); @override late final i0.GeneratedColumn localDateTime = - i0.GeneratedColumn('local_date_time', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbHashMeta = - const i0.VerificationMeta('thumbHash'); + i0.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _thumbHashMeta = const i0.VerificationMeta( + 'thumbHash', + ); @override late final i0.GeneratedColumn thumbHash = i0.GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + 'thumb_hash', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _livePhotoVideoIdMeta = const i0.VerificationMeta('livePhotoVideoId'); @override late final i0.GeneratedColumn livePhotoVideoId = - i0.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override late final i0.GeneratedColumnWithTypeConverter - visibility = i0.GeneratedColumn('visibility', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertervisibility); - static const i0.VerificationMeta _stackIdMeta = - const i0.VerificationMeta('stackId'); + visibility = + i0.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAssetEntityTable.$convertervisibility, + ); + static const i0.VerificationMeta _stackIdMeta = const i0.VerificationMeta( + 'stackId', + ); @override late final i0.GeneratedColumn stackId = i0.GeneratedColumn( - 'stack_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _libraryIdMeta = const i0.VerificationMeta( + 'libraryId', + ); + @override + late final i0.GeneratedColumn libraryId = i0.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -650,37 +872,51 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static const String $name = 'remote_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -688,46 +924,68 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } else if (isInserting) { context.missing(_checksumMeta); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('local_date_time')) { context.handle( + _localDateTimeMeta, + localDateTime.isAcceptableOrUnknown( + data['local_date_time']!, _localDateTimeMeta, - localDateTime.isAcceptableOrUnknown( - data['local_date_time']!, _localDateTimeMeta)); + ), + ); } if (data.containsKey('thumb_hash')) { - context.handle(_thumbHashMeta, - thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta)); + context.handle( + _thumbHashMeta, + thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('live_photo_video_id')) { context.handle( + _livePhotoVideoIdMeta, + livePhotoVideoId.isAcceptableOrUnknown( + data['live_photo_video_id']!, _livePhotoVideoIdMeta, - livePhotoVideoId.isAcceptableOrUnknown( - data['live_photo_video_id']!, _livePhotoVideoIdMeta)); + ), + ); } if (data.containsKey('stack_id')) { - context.handle(_stackIdMeta, - stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta)); + context.handle( + _stackIdMeta, + stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), + ); + } + if (data.containsKey('library_id')) { + context.handle( + _libraryIdMeta, + libraryId.isAcceptableOrUnknown(data['library_id']!, _libraryIdMeta), + ); } return context; } @@ -735,47 +993,88 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$RemoteAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$RemoteAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}live_photo_video_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql( - attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!), - stackId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}stack_id']), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + ), + stackId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), ); } @@ -787,8 +1086,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static i0.JsonTypeConverter2 $convertertype = const i0.EnumIndexConverter(i2.AssetType.values); static i0.JsonTypeConverter2 - $convertervisibility = const i0.EnumIndexConverter( - i2.AssetVisibility.values); + $convertervisibility = const i0.EnumIndexConverter( + i2.AssetVisibility.values, + ); @override bool get withoutRowId => true; @override @@ -814,31 +1114,35 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -869,21 +1173,28 @@ class RemoteAssetEntityData extends i0.DataClass } { map['visibility'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility)); + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility), + ); } if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = i0.Variable(libraryId); + } return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$RemoteAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$RemoteAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -897,9 +1208,11 @@ class RemoteAssetEntityData extends i0.DataClass thumbHash: serializer.fromJson(json['thumbHash']), deletedAt: serializer.fromJson(json['deletedAt']), livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), - visibility: i1.$RemoteAssetEntityTable.$convertervisibility - .fromJson(serializer.fromJson(json['visibility'])), + visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromJson( + serializer.fromJson(json['visibility']), + ), stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), ); } @override @@ -907,8 +1220,9 @@ class RemoteAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$RemoteAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -923,53 +1237,58 @@ class RemoteAssetEntityData extends i0.DataClass 'deletedAt': serializer.toJson(deletedAt), 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), 'visibility': serializer.toJson( - i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)), + i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), + ), 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), }; } - i1.RemoteAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i2.AssetVisibility? visibility, - i0.Value stackId = const i0.Value.absent()}) => - i1.RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + i1.RemoteAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i2.AssetVisibility? visibility, + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -983,8 +1302,9 @@ class RemoteAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -994,9 +1314,11 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, ); } @@ -1019,30 +1341,33 @@ class RemoteAssetEntityData extends i0.DataClass ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1063,7 +1388,8 @@ class RemoteAssetEntityData extends i0.DataClass other.deletedAt == this.deletedAt && other.livePhotoVideoId == this.livePhotoVideoId && other.visibility == this.visibility && - other.stackId == this.stackId); + other.stackId == this.stackId && + other.libraryId == this.libraryId); } class RemoteAssetEntityCompanion @@ -1085,6 +1411,7 @@ class RemoteAssetEntityCompanion final i0.Value livePhotoVideoId; final i0.Value visibility; final i0.Value stackId; + final i0.Value libraryId; const RemoteAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -1103,6 +1430,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), this.visibility = const i0.Value.absent(), this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }); RemoteAssetEntityCompanion.insert({ required String name, @@ -1122,12 +1450,13 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id), - checksum = i0.Value(checksum), - ownerId = i0.Value(ownerId), - visibility = i0.Value(visibility); + this.libraryId = const i0.Value.absent(), + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id), + checksum = i0.Value(checksum), + ownerId = i0.Value(ownerId), + visibility = i0.Value(visibility); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -1146,6 +1475,7 @@ class RemoteAssetEntityCompanion i0.Expression? livePhotoVideoId, i0.Expression? visibility, i0.Expression? stackId, + i0.Expression? libraryId, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -1165,27 +1495,30 @@ class RemoteAssetEntityCompanion if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, if (visibility != null) 'visibility': visibility, if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, }); } - i1.RemoteAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? ownerId, - i0.Value? localDateTime, - i0.Value? thumbHash, - i0.Value? deletedAt, - i0.Value? livePhotoVideoId, - i0.Value? visibility, - i0.Value? stackId}) { + i1.RemoteAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? ownerId, + i0.Value? localDateTime, + i0.Value? thumbHash, + i0.Value? deletedAt, + i0.Value? livePhotoVideoId, + i0.Value? visibility, + i0.Value? stackId, + i0.Value? libraryId, + }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1204,6 +1537,7 @@ class RemoteAssetEntityCompanion livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, ); } @@ -1215,7 +1549,8 @@ class RemoteAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); @@ -1257,13 +1592,16 @@ class RemoteAssetEntityCompanion map['live_photo_video_id'] = i0.Variable(livePhotoVideoId.value); } if (visibility.present) { - map['visibility'] = i0.Variable(i1 - .$RemoteAssetEntityTable.$convertervisibility - .toSql(visibility.value)); + map['visibility'] = i0.Variable( + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility.value), + ); } if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); } + if (libraryId.present) { + map['library_id'] = i0.Variable(libraryId.value); + } return map; } @@ -1286,11 +1624,22 @@ class RemoteAssetEntityCompanion ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } } -i0.Index get idxRemoteAssetChecksum => i0.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); +i0.Index get uQRemoteAssetsOwnerChecksum => i0.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', +); +i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', +); +i0.Index get idxRemoteAssetChecksum => i0.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', +); diff --git a/mobile/lib/infrastructure/entities/stack.entity.dart b/mobile/lib/infrastructure/entities/stack.entity.dart index b5da42832e..be50d7e330 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.dart @@ -11,8 +11,7 @@ class StackEntity extends Table with DriftDefaultsMixin { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); TextColumn get primaryAssetId => text()(); diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart index df0390aea0..ff7a3c3444 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -9,51 +9,62 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String primaryAssetId, -}); -typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value primaryAssetId, -}); +typedef $$StackEntityTableCreateCompanionBuilder = + i1.StackEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String primaryAssetId, + }); +typedef $$StackEntityTableUpdateCompanionBuilder = + i1.StackEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value primaryAssetId, + }); -final class $$StackEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> { +final class $$StackEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData + > { $$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('stack_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -67,37 +78,49 @@ class $$StackEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,39 +135,49 @@ class $$StackEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.primaryAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -168,46 +201,58 @@ class $$StackEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get primaryAssetId => $composableBuilder( - column: $table.primaryAssetId, builder: (column) => column); + column: $table.primaryAssetId, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$StackEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$StackEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$StackEntityTableTableManager( - i0.GeneratedDatabase db, i1.$StackEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$StackEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -216,46 +261,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$StackEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value primaryAssetId = const i0.Value.absent(), - }) => - i1.StackEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String primaryAssetId, - }) => - i1.StackEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value primaryAssetId = const i0.Value.absent(), + }) => i1.StackEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) => i1.StackEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$StackEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$StackEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -266,40 +314,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$StackEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$StackEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1.$$StackEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$StackEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$StackEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $StackEntityTable extends i2.StackEntity with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { @@ -310,42 +367,71 @@ class $StackEntityTable extends i2.StackEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _primaryAssetIdMeta = const i0.VerificationMeta('primaryAssetId'); @override late final i0.GeneratedColumn primaryAssetId = - i0.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + i0.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -353,8 +439,9 @@ class $StackEntityTable extends i2.StackEntity static const String $name = 'stack_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -363,24 +450,33 @@ class $StackEntityTable extends i2.StackEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('primary_asset_id')) { context.handle( + _primaryAssetIdMeta, + primaryAssetId.isAcceptableOrUnknown( + data['primary_asset_id']!, _primaryAssetIdMeta, - primaryAssetId.isAcceptableOrUnknown( - data['primary_asset_id']!, _primaryAssetIdMeta)); + ), + ); } else if (isInserting) { context.missing(_primaryAssetIdMeta); } @@ -393,16 +489,26 @@ class $StackEntityTable extends i2.StackEntity i1.StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.StackEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -424,12 +530,13 @@ class StackEntityData extends i0.DataClass final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -441,8 +548,10 @@ class StackEntityData extends i0.DataClass return map; } - factory StackEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -464,19 +573,19 @@ class StackEntityData extends i0.DataClass }; } - i1.StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - i1.StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + i1.StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => i1.StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(i1.StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -534,9 +643,9 @@ class StackEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - primaryAssetId = i0.Value(primaryAssetId); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + primaryAssetId = i0.Value(primaryAssetId); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -553,12 +662,13 @@ class StackEntityCompanion extends i0.UpdateCompanion { }); } - i1.StackEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? primaryAssetId}) { + i1.StackEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? primaryAssetId, + }) { return i1.StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/store.entity.dart b/mobile/lib/infrastructure/entities/store.entity.dart index 8d6d9a7d16..d4b3eec84f 100644 --- a/mobile/lib/infrastructure/entities/store.entity.dart +++ b/mobile/lib/infrastructure/entities/store.entity.dart @@ -1,3 +1,5 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:isar/isar.dart'; part 'store.entity.g.dart'; @@ -11,3 +13,13 @@ class StoreValue { const StoreValue(this.id, {this.intValue, this.strValue}); } + +class StoreEntity extends Table with DriftDefaultsMixin { + IntColumn get id => integer()(); + + TextColumn get stringValue => text().nullable()(); + IntColumn get intValue => integer().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/store.entity.drift.dart b/mobile/lib/infrastructure/entities/store.entity.drift.dart new file mode 100644 index 0000000000..327b0e95d9 --- /dev/null +++ b/mobile/lib/infrastructure/entities/store.entity.drift.dart @@ -0,0 +1,426 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/store.entity.dart' as i2; + +typedef $$StoreEntityTableCreateCompanionBuilder = + i1.StoreEntityCompanion Function({ + required int id, + i0.Value stringValue, + i0.Value intValue, + }); +typedef $$StoreEntityTableUpdateCompanionBuilder = + i1.StoreEntityCompanion Function({ + i0.Value id, + i0.Value stringValue, + i0.Value intValue, + }); + +class $$StoreEntityTableFilterComposer + extends i0.Composer { + $$StoreEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get stringValue => $composableBuilder( + column: $table.stringValue, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get intValue => $composableBuilder( + column: $table.intValue, + builder: (column) => i0.ColumnFilters(column), + ); +} + +class $$StoreEntityTableOrderingComposer + extends i0.Composer { + $$StoreEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get stringValue => $composableBuilder( + column: $table.stringValue, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get intValue => $composableBuilder( + column: $table.intValue, + builder: (column) => i0.ColumnOrderings(column), + ); +} + +class $$StoreEntityTableAnnotationComposer + extends i0.Composer { + $$StoreEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get stringValue => $composableBuilder( + column: $table.stringValue, + builder: (column) => column, + ); + + i0.GeneratedColumn get intValue => + $composableBuilder(column: $table.intValue, builder: (column) => column); +} + +class $$StoreEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData, + i1.$$StoreEntityTableFilterComposer, + i1.$$StoreEntityTableOrderingComposer, + i1.$$StoreEntityTableAnnotationComposer, + $$StoreEntityTableCreateCompanionBuilder, + $$StoreEntityTableUpdateCompanionBuilder, + ( + i1.StoreEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData + >, + ), + i1.StoreEntityData, + i0.PrefetchHooks Function() + > { + $$StoreEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$StoreEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$StoreEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$StoreEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$StoreEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value stringValue = const i0.Value.absent(), + i0.Value intValue = const i0.Value.absent(), + }) => i1.StoreEntityCompanion( + id: id, + stringValue: stringValue, + intValue: intValue, + ), + createCompanionCallback: + ({ + required int id, + i0.Value stringValue = const i0.Value.absent(), + i0.Value intValue = const i0.Value.absent(), + }) => i1.StoreEntityCompanion.insert( + id: id, + stringValue: stringValue, + intValue: intValue, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $$StoreEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData, + i1.$$StoreEntityTableFilterComposer, + i1.$$StoreEntityTableOrderingComposer, + i1.$$StoreEntityTableAnnotationComposer, + $$StoreEntityTableCreateCompanionBuilder, + $$StoreEntityTableUpdateCompanionBuilder, + ( + i1.StoreEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData + >, + ), + i1.StoreEntityData, + i0.PrefetchHooks Function() + >; + +class $StoreEntityTable extends i2.StoreEntity + with i0.TableInfo<$StoreEntityTable, i1.StoreEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $StoreEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _stringValueMeta = const i0.VerificationMeta( + 'stringValue', + ); + @override + late final i0.GeneratedColumn stringValue = + i0.GeneratedColumn( + 'string_value', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _intValueMeta = const i0.VerificationMeta( + 'intValue', + ); + @override + late final i0.GeneratedColumn intValue = i0.GeneratedColumn( + 'int_value', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('string_value')) { + context.handle( + _stringValueMeta, + stringValue.isAcceptableOrUnknown( + data['string_value']!, + _stringValueMeta, + ), + ); + } + if (data.containsKey('int_value')) { + context.handle( + _intValueMeta, + intValue.isAcceptableOrUnknown(data['int_value']!, _intValueMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.StoreEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + $StoreEntityTable createAlias(String alias) { + return $StoreEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends i0.DataClass + implements i0.Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = i0.Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = i0.Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + i1.StoreEntityData copyWith({ + int? id, + i0.Value stringValue = const i0.Value.absent(), + i0.Value intValue = const i0.Value.absent(), + }) => i1.StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(i1.StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value stringValue; + final i0.Value intValue; + const StoreEntityCompanion({ + this.id = const i0.Value.absent(), + this.stringValue = const i0.Value.absent(), + this.intValue = const i0.Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const i0.Value.absent(), + this.intValue = const i0.Value.absent(), + }) : id = i0.Value(id); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? stringValue, + i0.Expression? intValue, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + i1.StoreEntityCompanion copyWith({ + i0.Value? id, + i0.Value? stringValue, + i0.Value? intValue, + }) { + return i1.StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = i0.Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = i0.Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/store.entity.g.dart index b97b5b0a28..626c3084fe 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/store.entity.g.dart @@ -17,17 +17,14 @@ const StoreValueSchema = CollectionSchema( name: r'StoreValue', id: 902899285492123510, properties: { - r'intValue': PropertySchema( - id: 0, - name: r'intValue', - type: IsarType.long, - ), + r'intValue': PropertySchema(id: 0, name: r'intValue', type: IsarType.long), r'strValue': PropertySchema( id: 1, name: r'strValue', type: IsarType.string, - ) + ), }, + estimateSize: _storeValueEstimateSize, serialize: _storeValueSerialize, deserialize: _storeValueDeserialize, @@ -36,10 +33,11 @@ const StoreValueSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _storeValueGetId, getLinks: _storeValueGetLinks, attach: _storeValueAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _storeValueEstimateSize( @@ -120,10 +118,7 @@ extension StoreValueQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -149,8 +144,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -158,8 +155,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -174,12 +173,14 @@ extension StoreValueQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -187,12 +188,12 @@ extension StoreValueQueryWhere extension StoreValueQueryFilter on QueryBuilder { QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -201,11 +202,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -214,11 +217,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -229,54 +234,55 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder intValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'intValue'), + ); }); } QueryBuilder - intValueIsNotNull() { + intValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'intValue'), + ); }); } QueryBuilder intValueEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'intValue', value: value), + ); }); } QueryBuilder - intValueGreaterThan( - int? value, { - bool include = false, - }) { + intValueGreaterThan(int? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -285,11 +291,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -300,30 +308,32 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'intValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'intValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder strValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'strValue'), + ); }); } QueryBuilder - strValueIsNotNull() { + strValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'strValue'), + ); }); } @@ -332,27 +342,31 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueGreaterThan( + strValueGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -362,12 +376,14 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -379,28 +395,29 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'strValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'strValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueStartsWith( - String value, { - bool caseSensitive = true, - }) { + strValueStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -409,55 +426,61 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'strValue', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'strValue', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueIsEmpty() { + strValueIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'strValue', value: ''), + ); }); } QueryBuilder - strValueIsNotEmpty() { + strValueIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'strValue', value: ''), + ); }); } } @@ -542,8 +565,9 @@ extension StoreValueQueryWhereDistinct }); } - QueryBuilder distinctByStrValue( - {bool caseSensitive = true}) { + QueryBuilder distinctByStrValue({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index b0c1e6e866..667a9d6a59 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -1,6 +1,5 @@ import 'package:drift/drift.dart' hide Index; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:isar/isar.dart'; @@ -43,36 +42,37 @@ class User { }); static User fromDto(UserDto dto) => User( - id: dto.id, - updatedAt: dto.updatedAt, - email: dto.email, - name: dto.name, - isAdmin: dto.isAdmin, - isPartnerSharedBy: dto.isPartnerSharedBy, - isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", - avatarColor: dto.avatarColor, - memoryEnabled: dto.memoryEnabled, - inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, - ); + id: dto.id, + updatedAt: dto.updatedAt ?? DateTime(2025), + email: dto.email, + name: dto.name, + isAdmin: dto.isAdmin, + isPartnerSharedBy: dto.isPartnerSharedBy, + isPartnerSharedWith: dto.isPartnerSharedWith, + profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "", + avatarColor: dto.avatarColor, + memoryEnabled: dto.memoryEnabled, + inTimeline: dto.inTimeline, + quotaUsageInBytes: dto.quotaUsageInBytes, + quotaSizeInBytes: dto.quotaSizeInBytes, + ); UserDto toDto() => UserDto( - id: id, - email: email, - name: name, - isAdmin: isAdmin, - updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, - avatarColor: avatarColor, - memoryEnabled: memoryEnabled, - inTimeline: inTimeline, - isPartnerSharedBy: isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes, - ); + id: id, + email: email, + name: name, + isAdmin: isAdmin, + updatedAt: updatedAt, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + inTimeline: inTimeline, + isPartnerSharedBy: isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith, + hasProfileImage: profileImagePath.isNotEmpty, + profileChangedAt: updatedAt, + quotaUsageInBytes: quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes, + ); } class UserEntity extends Table with DriftDefaultsMixin { @@ -80,13 +80,12 @@ class UserEntity extends Table with DriftDefaultsMixin { TextColumn get id => text()(); TextColumn get name => text()(); - BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); - TextColumn get profileImagePath => text().nullable()(); - DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - // Quota - IntColumn get quotaSizeInBytes => integer().nullable()(); - IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); + + // Profile image + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + IntColumn get avatarColor => intEnum().withDefault(const Constant(0))(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 32be969518..083c14a095 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -3,31 +3,28 @@ import 'package:drift/drift.dart' as i0; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2; -import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; +import 'package:immich_mobile/domain/models/user.model.dart' as i2; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i3; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion - Function({ - required String id, - required String name, - i0.Value isAdmin, - required String email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); -typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion - Function({ - i0.Value id, - i0.Value name, - i0.Value isAdmin, - i0.Value email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); +typedef $$UserEntityTableCreateCompanionBuilder = + i1.UserEntityCompanion Function({ + required String id, + required String name, + required String email, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + i0.Value avatarColor, + }); +typedef $$UserEntityTableUpdateCompanionBuilder = + i1.UserEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value email, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + i0.Value avatarColor, + }); class $$UserEntityTableFilterComposer extends i0.Composer { @@ -39,31 +36,35 @@ class $$UserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnFilters(column)); + column: $table.email, + builder: (column) => i0.ColumnFilters(column), + ); - i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnFilters(column), + ); - i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnFilters(column), + ); - i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column)); + i0.ColumnWithTypeConverterFilters + get avatarColor => $composableBuilder( + column: $table.avatarColor, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); } class $$UserEntityTableOrderingComposer @@ -76,32 +77,34 @@ class $$UserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnOrderings(column)); + column: $table.email, + builder: (column) => i0.ColumnOrderings(column), + ); - i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnOrderings(column), + ); - i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnOrderings(column), + ); - i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get avatarColor => $composableBuilder( + column: $table.avatarColor, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$UserEntityTableAnnotationComposer @@ -119,44 +122,53 @@ class $$UserEntityTableAnnotationComposer i0.GeneratedColumn get name => $composableBuilder(column: $table.name, builder: (column) => column); - i0.GeneratedColumn get isAdmin => - $composableBuilder(column: $table.isAdmin, builder: (column) => column); - i0.GeneratedColumn get email => $composableBuilder(column: $table.email, builder: (column) => column); - i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, builder: (column) => column); + i0.GeneratedColumn get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => column, + ); - i0.GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); + i0.GeneratedColumn get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => column, + ); - i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, builder: (column) => column); - - i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, builder: (column) => column); + i0.GeneratedColumnWithTypeConverter get avatarColor => + $composableBuilder( + column: $table.avatarColor, + builder: (column) => column, + ); } -class $$UserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( - i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()> { +class $$UserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + > { $$UserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -165,71 +177,69 @@ class $$UserEntityTableTableManager extends i0.RootTableManager< i1.$$UserEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$UserEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value isAdmin = const i0.Value.absent(), - i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value isAdmin = const i0.Value.absent(), - required String email, - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion.insert( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + i0.Value avatarColor = const i0.Value.absent(), + }) => i1.UserEntityCompanion( + id: id, + name: name, + email: email, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + avatarColor: avatarColor, + ), + createCompanionCallback: + ({ + required String id, + required String name, + required String email, + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + i0.Value avatarColor = const i0.Value.absent(), + }) => i1.UserEntityCompanion.insert( + id: id, + name: name, + email: email, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + avatarColor: avatarColor, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( +typedef $$UserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()>; + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + >; -class $UserEntityTable extends i2.UserEntity +class $UserEntityTable extends i3.UserEntity with i0.TableInfo<$UserEntityTable, i1.UserEntityData> { @override final i0.GeneratedDatabase attachedDatabase; @@ -238,69 +248,80 @@ class $UserEntityTable extends i2.UserEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isAdminMeta = - const i0.VerificationMeta('isAdmin'); - @override - late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( - 'is_admin', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const i3.Constant(false)); - static const i0.VerificationMeta _emailMeta = - const i0.VerificationMeta('email'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta( + 'email', + ); @override late final i0.GeneratedColumn email = i0.GeneratedColumn( - 'email', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _profileImagePathMeta = - const i0.VerificationMeta('profileImagePath'); + 'email', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _hasProfileImageMeta = + const i0.VerificationMeta('hasProfileImage'); @override - late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn('profile_image_path', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + late final i0.GeneratedColumn hasProfileImage = + i0.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _profileChangedAtMeta = + const i0.VerificationMeta('profileChangedAt'); @override - late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _quotaSizeInBytesMeta = - const i0.VerificationMeta('quotaSizeInBytes'); + late final i0.GeneratedColumn profileChangedAt = + i0.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); @override - late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _quotaUsageInBytesMeta = - const i0.VerificationMeta('quotaUsageInBytes'); - @override - late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0)); + late final i0.GeneratedColumnWithTypeConverter + avatarColor = i0.GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ).withConverter(i1.$UserEntityTable.$converteravatarColor); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -308,8 +329,9 @@ class $UserEntityTable extends i2.UserEntity static const String $name = 'user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -319,41 +341,37 @@ class $UserEntityTable extends i2.UserEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } - if (data.containsKey('is_admin')) { - context.handle(_isAdminMeta, - isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta)); - } if (data.containsKey('email')) { context.handle( - _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta)); + _emailMeta, + email.isAcceptableOrUnknown(data['email']!, _emailMeta), + ); } else if (isInserting) { context.missing(_emailMeta); } - if (data.containsKey('profile_image_path')) { + if (data.containsKey('has_profile_image')) { context.handle( - _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, _profileImagePathMeta)); + _hasProfileImageMeta, + hasProfileImage.isAcceptableOrUnknown( + data['has_profile_image']!, + _hasProfileImageMeta, + ), + ); } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - if (data.containsKey('quota_size_in_bytes')) { + if (data.containsKey('profile_changed_at')) { context.handle( - _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, _quotaSizeInBytesMeta)); - } - if (data.containsKey('quota_usage_in_bytes')) { - context.handle( - _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta)); + _profileChangedAtMeta, + profileChangedAt.isAcceptableOrUnknown( + data['profile_changed_at']!, + _profileChangedAtMeta, + ), + ); } return context; } @@ -364,22 +382,32 @@ class $UserEntityTable extends i2.UserEntity i1.UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: i1.$UserEntityTable.$converteravatarColor.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ), ); } @@ -388,6 +416,8 @@ class $UserEntityTable extends i2.UserEntity return $UserEntityTable(attachedDatabase, alias); } + static i0.JsonTypeConverter2 $converteravatarColor = + const i0.EnumIndexConverter(i2.AvatarColor.values); @override bool get withoutRowId => true; @override @@ -398,51 +428,48 @@ class UserEntityData extends i0.DataClass implements i0.Insertable { final String id; final String name; - final bool isAdmin; final String email; - final String? profileImagePath; - final DateTime updatedAt; - final int? quotaSizeInBytes; - final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + final bool hasProfileImage; + final DateTime profileChangedAt; + final i2.AvatarColor avatarColor; + const UserEntityData({ + required this.id, + required this.name, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = i0.Variable(id); map['name'] = i0.Variable(name); - map['is_admin'] = i0.Variable(isAdmin); map['email'] = i0.Variable(email); - if (!nullToAbsent || profileImagePath != null) { - map['profile_image_path'] = i0.Variable(profileImagePath); + map['has_profile_image'] = i0.Variable(hasProfileImage); + map['profile_changed_at'] = i0.Variable(profileChangedAt); + { + map['avatar_color'] = i0.Variable( + i1.$UserEntityTable.$converteravatarColor.toSql(avatarColor), + ); } - map['updated_at'] = i0.Variable(updatedAt); - if (!nullToAbsent || quotaSizeInBytes != null) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); - } - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); return map; } - factory UserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), - isAdmin: serializer.fromJson(json['isAdmin']), email: serializer.fromJson(json['email']), - profileImagePath: serializer.fromJson(json['profileImagePath']), - updatedAt: serializer.fromJson(json['updatedAt']), - quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), - quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: i1.$UserEntityTable.$converteravatarColor.fromJson( + serializer.fromJson(json['avatarColor']), + ), ); } @override @@ -451,54 +478,44 @@ class UserEntityData extends i0.DataClass return { 'id': serializer.toJson(id), 'name': serializer.toJson(name), - 'isAdmin': serializer.toJson(isAdmin), 'email': serializer.toJson(email), - 'profileImagePath': serializer.toJson(profileImagePath), - 'updatedAt': serializer.toJson(updatedAt), - 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), - 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson( + i1.$UserEntityTable.$converteravatarColor.toJson(avatarColor), + ), }; } - i1.UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - i0.Value profileImagePath = const i0.Value.absent(), - DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes}) => - i1.UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + i1.UserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + i2.AvatarColor? avatarColor, + }) => i1.UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present - ? data.profileImagePath.value - : this.profileImagePath, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present - ? data.quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present - ? data.quotaUsageInBytes.value - : this.quotaUsageInBytes, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, ); } @@ -507,104 +524,93 @@ class UserEntityData extends i0.DataClass return (StringBuffer('UserEntityData(') ..write('id: $id, ') ..write('name: $name, ') - ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ); @override bool operator ==(Object other) => identical(this, other) || (other is i1.UserEntityData && other.id == this.id && other.name == this.name && - other.isAdmin == this.isAdmin && other.email == this.email && - other.profileImagePath == this.profileImagePath && - other.updatedAt == this.updatedAt && - other.quotaSizeInBytes == this.quotaSizeInBytes && - other.quotaUsageInBytes == this.quotaUsageInBytes); + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor); } class UserEntityCompanion extends i0.UpdateCompanion { final i0.Value id; final i0.Value name; - final i0.Value isAdmin; final i0.Value email; - final i0.Value profileImagePath; - final i0.Value updatedAt; - final i0.Value quotaSizeInBytes; - final i0.Value quotaUsageInBytes; + final i0.Value hasProfileImage; + final i0.Value profileChangedAt; + final i0.Value avatarColor; const UserEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), - this.isAdmin = const i0.Value.absent(), this.email = const i0.Value.absent(), - this.profileImagePath = const i0.Value.absent(), - this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), + this.avatarColor = const i0.Value.absent(), }); UserEntityCompanion.insert({ required String id, required String name, - this.isAdmin = const i0.Value.absent(), required String email, - this.profileImagePath = const i0.Value.absent(), - this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - email = i0.Value(email); + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), + this.avatarColor = const i0.Value.absent(), + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, - i0.Expression? isAdmin, i0.Expression? email, - i0.Expression? profileImagePath, - i0.Expression? updatedAt, - i0.Expression? quotaSizeInBytes, - i0.Expression? quotaUsageInBytes, + i0.Expression? hasProfileImage, + i0.Expression? profileChangedAt, + i0.Expression? avatarColor, }) { return i0.RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, - if (isAdmin != null) 'is_admin': isAdmin, if (email != null) 'email': email, - if (profileImagePath != null) 'profile_image_path': profileImagePath, - if (updatedAt != null) 'updated_at': updatedAt, - if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, - if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, }); } - i1.UserEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? isAdmin, - i0.Value? email, - i0.Value? profileImagePath, - i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes}) { + i1.UserEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? email, + i0.Value? hasProfileImage, + i0.Value? profileChangedAt, + i0.Value? avatarColor, + }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath ?? this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, ); } @@ -617,23 +623,19 @@ class UserEntityCompanion extends i0.UpdateCompanion { if (name.present) { map['name'] = i0.Variable(name.value); } - if (isAdmin.present) { - map['is_admin'] = i0.Variable(isAdmin.value); - } if (email.present) { map['email'] = i0.Variable(email.value); } - if (profileImagePath.present) { - map['profile_image_path'] = i0.Variable(profileImagePath.value); + if (hasProfileImage.present) { + map['has_profile_image'] = i0.Variable(hasProfileImage.value); } - if (updatedAt.present) { - map['updated_at'] = i0.Variable(updatedAt.value); + if (profileChangedAt.present) { + map['profile_changed_at'] = i0.Variable(profileChangedAt.value); } - if (quotaSizeInBytes.present) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); - } - if (quotaUsageInBytes.present) { - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); + if (avatarColor.present) { + map['avatar_color'] = i0.Variable( + i1.$UserEntityTable.$converteravatarColor.toSql(avatarColor.value), + ); } return map; } @@ -643,12 +645,10 @@ class UserEntityCompanion extends i0.UpdateCompanion { return (StringBuffer('UserEntityCompanion(') ..write('id: $id, ') ..write('name: $name, ') - ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart index 37a793b2c3..7e0af41b77 100644 --- a/mobile/lib/infrastructure/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -23,26 +23,14 @@ const UserSchema = CollectionSchema( type: IsarType.byte, enumMap: _UseravatarColorEnumValueMap, ), - r'email': PropertySchema( - id: 1, - name: r'email', - type: IsarType.string, - ), - r'id': PropertySchema( - id: 2, - name: r'id', - type: IsarType.string, - ), + r'email': PropertySchema(id: 1, name: r'email', type: IsarType.string), + r'id': PropertySchema(id: 2, name: r'id', type: IsarType.string), r'inTimeline': PropertySchema( id: 3, name: r'inTimeline', type: IsarType.bool, ), - r'isAdmin': PropertySchema( - id: 4, - name: r'isAdmin', - type: IsarType.bool, - ), + r'isAdmin': PropertySchema(id: 4, name: r'isAdmin', type: IsarType.bool), r'isPartnerSharedBy': PropertySchema( id: 5, name: r'isPartnerSharedBy', @@ -58,11 +46,7 @@ const UserSchema = CollectionSchema( name: r'memoryEnabled', type: IsarType.bool, ), - r'name': PropertySchema( - id: 8, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 8, name: r'name', type: IsarType.string), r'profileImagePath': PropertySchema( id: 9, name: r'profileImagePath', @@ -82,8 +66,9 @@ const UserSchema = CollectionSchema( id: 12, name: r'updatedAt', type: IsarType.dateTime, - ) + ), }, + estimateSize: _userEstimateSize, serialize: _userSerialize, deserialize: _userDeserialize, @@ -100,16 +85,17 @@ const UserSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _userGetId, getLinks: _userGetLinks, attach: _userAttach, - version: '3.1.8', + version: '3.3.0-dev.3', ); int _userEstimateSize( @@ -155,7 +141,7 @@ User _userDeserialize( final object = User( avatarColor: _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ?? - AvatarColor.primary, + AvatarColor.primary, email: reader.readString(offsets[1]), id: reader.readString(offsets[2]), inTimeline: reader.readBoolOrNull(offsets[3]) ?? false, @@ -181,7 +167,8 @@ P _userDeserializeProp

( switch (propertyId) { case 0: return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ?? - AvatarColor.primary) as P; + AvatarColor.primary) + as P; case 1: return (reader.readString(offset)) as P; case 2: @@ -311,10 +298,9 @@ extension UserQueryWhereSort on QueryBuilder { extension UserQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -340,8 +326,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -349,8 +337,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -365,21 +355,22 @@ extension UserQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -387,32 +378,40 @@ extension UserQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -420,12 +419,12 @@ extension UserQueryWhere on QueryBuilder { extension UserQueryFilter on QueryBuilder { QueryBuilder avatarColorEqualTo( - AvatarColor value) { + AvatarColor value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'avatarColor', value: value), + ); }); } @@ -434,11 +433,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -447,11 +448,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -462,13 +465,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'avatarColor', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'avatarColor', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -477,11 +482,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -491,12 +498,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -506,12 +515,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -523,14 +534,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'email', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'email', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -539,11 +552,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -552,51 +567,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailContains(String value, - {bool caseSensitive = true}) { + QueryBuilder emailContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder emailMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'email', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'email', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder emailIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'email', value: ''), + ); }); } QueryBuilder emailIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'email', value: ''), + ); }); } @@ -605,11 +628,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -619,12 +644,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,12 +661,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -651,14 +680,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -667,11 +698,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -680,99 +713,105 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder inTimelineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'inTimeline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'inTimeline', value: value), + ); }); } QueryBuilder isAdminEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isAdmin', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isAdmin', value: value), + ); }); } QueryBuilder isPartnerSharedByEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedBy', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedBy', value: value), + ); }); } QueryBuilder isPartnerSharedWithEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedWith', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedWith', value: value), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -781,11 +820,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -794,11 +835,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -809,23 +852,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder memoryEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'memoryEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'memoryEnabled', value: value), + ); }); } @@ -834,11 +879,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -848,12 +895,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -863,12 +912,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -880,14 +931,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -896,11 +949,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -909,51 +964,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } @@ -962,11 +1025,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -976,12 +1041,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -991,12 +1058,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1008,14 +1077,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'profileImagePath', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'profileImagePath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1024,11 +1095,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1037,63 +1110,69 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'profileImagePath', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'profileImagePath', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder profileImagePathIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder quotaSizeInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaSizeInBytes', value: value), + ); }); } @@ -1102,11 +1181,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1115,11 +1196,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1130,23 +1213,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaSizeInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaSizeInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder quotaUsageInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaUsageInBytes', value: value), + ); }); } @@ -1155,11 +1240,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1168,11 +1255,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1183,23 +1272,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaUsageInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaUsageInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -1208,11 +1299,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1221,11 +1314,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1236,13 +1331,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1586,15 +1683,17 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByEmail( - {bool caseSensitive = true}) { + QueryBuilder distinctByEmail({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'email', caseSensitive: caseSensitive); }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -1630,18 +1729,22 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByProfileImagePath( - {bool caseSensitive = true}) { + QueryBuilder distinctByProfileImagePath({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'profileImagePath', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'profileImagePath', + caseSensitive: caseSensitive, + ); }); } diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.dart index f9a411e3de..ede3de3966 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.dart @@ -6,8 +6,7 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class UserMetadataEntity extends Table with DriftDefaultsMixin { const UserMetadataEntity(); - TextColumn get userId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get key => intEnum()(); @@ -17,7 +16,6 @@ class UserMetadataEntity extends Table with DriftDefaultsMixin { Set get primaryKey => {userId, key}; } -final JsonTypeConverter2, Uint8List, Object?> - userMetadataConverter = TypeConverter.jsonb( +final JsonTypeConverter2, Uint8List, Object?> userMetadataConverter = TypeConverter.jsonb( fromJson: (json) => json as Map, ); diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart index a13ea5c04e..1e9dc8a890 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart @@ -11,51 +11,64 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$UserMetadataEntityTableCreateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - required String userId, - required i2.UserMetadataKey key, - required Map value, -}); -typedef $$UserMetadataEntityTableUpdateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - i0.Value userId, - i0.Value key, - i0.Value> value, -}); +typedef $$UserMetadataEntityTableCreateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }); +typedef $$UserMetadataEntityTableUpdateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + i0.Value userId, + i0.Value key, + i0.Value> value, + }); -final class $$UserMetadataEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData> { +final class $$UserMetadataEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData + > { $$UserMetadataEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet( - 'user_metadata_entity') + 'user_metadata_entity', + ) .userId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -69,35 +82,45 @@ class $$UserMetadataEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get key => $composableBuilder( - column: $table.key, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get key => $composableBuilder( + column: $table.key, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); - i0.ColumnWithTypeConverterFilters, Map, - i3.Uint8List> - get value => $composableBuilder( - column: $table.value, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + i0.ColumnWithTypeConverterFilters< + Map, + Map, + i3.Uint8List + > + get value => $composableBuilder( + column: $table.value, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get userId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,30 +135,39 @@ class $$UserMetadataEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get key => $composableBuilder( - column: $table.key, builder: (column) => i0.ColumnOrderings(column)); + column: $table.key, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get value => $composableBuilder( - column: $table.value, builder: (column) => i0.ColumnOrderings(column)); + column: $table.value, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get userId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -153,89 +185,106 @@ class $$UserMetadataEntityTableAnnotationComposer $composableBuilder(column: $table.key, builder: (column) => column); i0.GeneratedColumnWithTypeConverter, i3.Uint8List> - get value => - $composableBuilder(column: $table.value, builder: (column) => column); + get value => + $composableBuilder(column: $table.value, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get userId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})> { +class $$UserMetadataEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + > { $$UserMetadataEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserMetadataEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserMetadataEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1 .$$UserMetadataEntityTableFilterComposer($db: db, $table: table), createOrderingComposer: () => i1.$$UserMetadataEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$UserMetadataEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value userId = const i0.Value.absent(), - i0.Value key = const i0.Value.absent(), - i0.Value> value = const i0.Value.absent(), - }) => - i1.UserMetadataEntityCompanion( - userId: userId, - key: key, - value: value, - ), - createCompanionCallback: ({ - required String userId, - required i2.UserMetadataKey key, - required Map value, - }) => - i1.UserMetadataEntityCompanion.insert( - userId: userId, - key: key, - value: value, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value userId = const i0.Value.absent(), + i0.Value key = const i0.Value.absent(), + i0.Value> value = const i0.Value.absent(), + }) => i1.UserMetadataEntityCompanion( + userId: userId, + key: key, + value: value, + ), + createCompanionCallback: + ({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }) => i1.UserMetadataEntityCompanion.insert( + userId: userId, + key: key, + value: value, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$UserMetadataEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$UserMetadataEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -246,42 +295,50 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: - i1.$$UserMetadataEntityTableReferences._userIdTable(db), - referencedColumn: i1.$$UserMetadataEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$UserMetadataEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})>; +typedef $$UserMetadataEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + >; class $UserMetadataEntityTable extends i4.UserMetadataEntity with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> { @@ -289,28 +346,46 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $UserMetadataEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter key = - i0.GeneratedColumn('key', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$UserMetadataEntityTable.$converterkey); + i0.GeneratedColumn( + 'key', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$UserMetadataEntityTable.$converterkey, + ); @override - late final i0 - .GeneratedColumnWithTypeConverter, i3.Uint8List> - value = i0.GeneratedColumn('value', aliasedName, false, - type: i0.DriftSqlType.blob, requiredDuringInsert: true) - .withConverter>( - i1.$UserMetadataEntityTable.$convertervalue); + late final i0.GeneratedColumnWithTypeConverter< + Map, + i3.Uint8List + > + value = + i0.GeneratedColumn( + 'value', + aliasedName, + false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + ).withConverter>( + i1.$UserMetadataEntityTable.$convertervalue, + ); @override List get $columns => [userId, key, value]; @override @@ -320,13 +395,16 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static const String $name = 'user_metadata_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -336,18 +414,28 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity @override Set get $primaryKey => {userId, key}; @override - i1.UserMetadataEntityData map(Map data, - {String? tablePrefix}) { + i1.UserMetadataEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: i1.$UserMetadataEntityTable.$converterkey.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}key'])!), + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: i1.$UserMetadataEntityTable.$converterkey.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + ), value: i1.$UserMetadataEntityTable.$convertervalue.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.blob, data['${effectivePrefix}value'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ), ); } @@ -358,9 +446,10 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static i0.JsonTypeConverter2 $converterkey = const i0.EnumIndexConverter( - i2.UserMetadataKey.values); + i2.UserMetadataKey.values, + ); static i0.JsonTypeConverter2, i3.Uint8List, Object?> - $convertervalue = i4.userMetadataConverter; + $convertervalue = i4.userMetadataConverter; @override bool get withoutRowId => true; @override @@ -372,32 +461,41 @@ class UserMetadataEntityData extends i0.DataClass final String userId; final i2.UserMetadataKey key; final Map value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['user_id'] = i0.Variable(userId); { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key), + ); } { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value), + ); } return map; } - factory UserMetadataEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), - key: i1.$UserMetadataEntityTable.$converterkey - .fromJson(serializer.fromJson(json['key'])), - value: i1.$UserMetadataEntityTable.$convertervalue - .fromJson(serializer.fromJson(json['value'])), + key: i1.$UserMetadataEntityTable.$converterkey.fromJson( + serializer.fromJson(json['key']), + ), + value: i1.$UserMetadataEntityTable.$convertervalue.fromJson( + serializer.fromJson(json['value']), + ), ); } @override @@ -405,24 +503,27 @@ class UserMetadataEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), - 'key': serializer - .toJson(i1.$UserMetadataEntityTable.$converterkey.toJson(key)), + 'key': serializer.toJson( + i1.$UserMetadataEntityTable.$converterkey.toJson(key), + ), 'value': serializer.toJson( - i1.$UserMetadataEntityTable.$convertervalue.toJson(value)), + i1.$UserMetadataEntityTable.$convertervalue.toJson(value), + ), }; } - i1.UserMetadataEntityData copyWith( - {String? userId, - i2.UserMetadataKey? key, - Map? value}) => - i1.UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + i1.UserMetadataEntityData copyWith({ + String? userId, + i2.UserMetadataKey? key, + Map? value, + }) => i1.UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion( - i1.UserMetadataEntityCompanion data) { + i1.UserMetadataEntityCompanion data, + ) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, key: data.key.present ? data.key.value : this.key, @@ -465,9 +566,9 @@ class UserMetadataEntityCompanion required String userId, required i2.UserMetadataKey key, required Map value, - }) : userId = i0.Value(userId), - key = i0.Value(key), - value = i0.Value(value); + }) : userId = i0.Value(userId), + key = i0.Value(key), + value = i0.Value(value); static i0.Insertable custom({ i0.Expression? userId, i0.Expression? key, @@ -480,10 +581,11 @@ class UserMetadataEntityCompanion }); } - i1.UserMetadataEntityCompanion copyWith( - {i0.Value? userId, - i0.Value? key, - i0.Value>? value}) { + i1.UserMetadataEntityCompanion copyWith({ + i0.Value? userId, + i0.Value? key, + i0.Value>? value, + }) { return i1.UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -499,11 +601,13 @@ class UserMetadataEntityCompanion } if (key.present) { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key.value)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key.value), + ); } if (value.present) { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/loaders/image_request.dart b/mobile/lib/infrastructure/loaders/image_request.dart new file mode 100644 index 0000000000..d839b8bdf6 --- /dev/null +++ b/mobile/lib/infrastructure/loaders/image_request.dart @@ -0,0 +1,86 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ffi/ffi.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:logging/logging.dart'; + +part 'local_image_request.dart'; +part 'thumbhash_image_request.dart'; +part 'remote_image_request.dart'; + +abstract class ImageRequest { + static int _nextRequestId = 0; + + final int requestId = _nextRequestId++; + bool _isCancelled = false; + + get isCancelled => _isCancelled; + + ImageRequest(); + + Future load(ImageDecoderCallback decode, {double scale = 1.0}); + + void cancel() { + if (_isCancelled) { + return; + } + _isCancelled = true; + return _onCancelled(); + } + + void _onCancelled(); + + Future _fromPlatformImage(Map info) async { + final address = info['pointer']; + if (address == null) { + return null; + } + + final pointer = Pointer.fromAddress(address); + if (_isCancelled) { + malloc.free(pointer); + return null; + } + + final int actualWidth; + final int actualHeight; + final int actualSize; + final ui.ImmutableBuffer buffer; + try { + actualWidth = info['width']!; + actualHeight = info['height']!; + actualSize = actualWidth * actualHeight * 4; + buffer = await ImmutableBuffer.fromUint8List(pointer.asTypedList(actualSize)); + } finally { + malloc.free(pointer); + } + + if (_isCancelled) { + buffer.dispose(); + return null; + } + + final descriptor = ui.ImageDescriptor.raw( + buffer, + width: actualWidth, + height: actualHeight, + pixelFormat: ui.PixelFormat.rgba8888, + ); + final codec = await descriptor.instantiateCodec(); + if (_isCancelled) { + buffer.dispose(); + descriptor.dispose(); + codec.dispose(); + return null; + } + + return await codec.getNextFrame(); + } +} diff --git a/mobile/lib/infrastructure/loaders/local_image_request.dart b/mobile/lib/infrastructure/loaders/local_image_request.dart new file mode 100644 index 0000000000..7a1b3d8957 --- /dev/null +++ b/mobile/lib/infrastructure/loaders/local_image_request.dart @@ -0,0 +1,35 @@ +part of 'image_request.dart'; + +class LocalImageRequest extends ImageRequest { + final String localId; + final int width; + final int height; + final AssetType assetType; + + LocalImageRequest({required this.localId, required ui.Size size, required this.assetType}) + : width = size.width.toInt(), + height = size.height.toInt(); + + @override + Future load(ImageDecoderCallback decode, {double scale = 1.0}) async { + if (_isCancelled) { + return null; + } + + final Map info = await thumbnailApi.requestImage( + localId, + requestId: requestId, + width: width, + height: height, + isVideo: assetType == AssetType.video, + ); + + final frame = await _fromPlatformImage(info); + return frame == null ? null : ImageInfo(image: frame.image, scale: scale); + } + + @override + Future _onCancelled() { + return thumbnailApi.cancelImageRequest(requestId); + } +} diff --git a/mobile/lib/infrastructure/loaders/remote_image_request.dart b/mobile/lib/infrastructure/loaders/remote_image_request.dart new file mode 100644 index 0000000000..78f6b9479b --- /dev/null +++ b/mobile/lib/infrastructure/loaders/remote_image_request.dart @@ -0,0 +1,179 @@ +part of 'image_request.dart'; + +class RemoteImageRequest extends ImageRequest { + static final log = Logger('RemoteImageRequest'); + static final client = HttpClient()..maxConnectionsPerHost = 16; + final RemoteCacheManager? cacheManager; + final String uri; + final Map headers; + HttpClientRequest? _request; + + RemoteImageRequest({required this.uri, required this.headers, this.cacheManager}); + + @override + Future load(ImageDecoderCallback decode, {double scale = 1.0}) async { + if (_isCancelled) { + return null; + } + + // TODO: the cache manager makes everything sequential with its DB calls and its operations cannot be cancelled, + // so it ends up being a bottleneck. We only prefer fetching from it when it can skip the DB call. + final cachedFileImage = await _loadCachedFile(uri, decode, scale, inMemoryOnly: true); + if (cachedFileImage != null) { + return cachedFileImage; + } + + try { + final buffer = await _downloadImage(uri); + if (buffer == null) { + return null; + } + + return await _decodeBuffer(buffer, decode, scale); + } catch (e) { + if (_isCancelled) { + return null; + } + + final cachedFileImage = await _loadCachedFile(uri, decode, scale, inMemoryOnly: false); + if (cachedFileImage != null) { + return cachedFileImage; + } + + rethrow; + } finally { + _request = null; + } + } + + Future _downloadImage(String url) async { + if (_isCancelled) { + return null; + } + + final request = _request = await client.getUrl(Uri.parse(url)); + if (_isCancelled) { + request.abort(); + return _request = null; + } + + for (final entry in headers.entries) { + request.headers.set(entry.key, entry.value); + } + final response = await request.close(); + if (_isCancelled) { + return null; + } + + final cacheManager = this.cacheManager; + final streamController = StreamController>(sync: true); + final Stream> stream; + cacheManager?.putStreamedFile(url, streamController.stream); + stream = response.map((chunk) { + if (_isCancelled) { + throw StateError('Cancelled request'); + } + if (cacheManager != null) { + streamController.add(chunk); + } + return chunk; + }); + + try { + final Uint8List bytes = await _downloadBytes(stream, response.contentLength); + streamController.close(); + return await ImmutableBuffer.fromUint8List(bytes); + } catch (e) { + streamController.addError(e); + streamController.close(); + if (_isCancelled) { + return null; + } + rethrow; + } + } + + Future _downloadBytes(Stream> stream, int length) async { + final Uint8List bytes; + int offset = 0; + if (length > 0) { + // Known content length - use pre-allocated buffer + bytes = Uint8List(length); + await stream.listen((chunk) { + bytes.setAll(offset, chunk); + offset += chunk.length; + }, cancelOnError: true).asFuture(); + } else { + // Unknown content length - collect chunks dynamically + final chunks = >[]; + int totalLength = 0; + await stream.listen((chunk) { + chunks.add(chunk); + totalLength += chunk.length; + }, cancelOnError: true).asFuture(); + + bytes = Uint8List(totalLength); + for (final chunk in chunks) { + bytes.setAll(offset, chunk); + offset += chunk.length; + } + } + + return bytes; + } + + Future _loadCachedFile( + String url, + ImageDecoderCallback decode, + double scale, { + required bool inMemoryOnly, + }) async { + final cacheManager = this.cacheManager; + if (_isCancelled || cacheManager == null) { + return null; + } + + final file = await (inMemoryOnly ? cacheManager.getFileFromMemory(url) : cacheManager.getFileFromCache(url)); + if (_isCancelled || file == null) { + return null; + } + + try { + final buffer = await ImmutableBuffer.fromFilePath(file.file.path); + return await _decodeBuffer(buffer, decode, scale); + } catch (e) { + log.severe('Failed to decode cached image', e); + _evictFile(url); + return null; + } + } + + Future _evictFile(String url) async { + try { + await cacheManager?.removeFile(url); + } catch (e) { + log.severe('Failed to remove cached image', e); + } + } + + Future _decodeBuffer(ImmutableBuffer buffer, ImageDecoderCallback decode, scale) async { + if (_isCancelled) { + buffer.dispose(); + return null; + } + final codec = await decode(buffer); + if (_isCancelled) { + buffer.dispose(); + codec.dispose(); + return null; + } + final frame = await codec.getNextFrame(); + return ImageInfo(image: frame.image, scale: scale); + } + + @override + void _onCancelled() { + _request?.abort(); + _request = null; + } +} diff --git a/mobile/lib/infrastructure/loaders/thumbhash_image_request.dart b/mobile/lib/infrastructure/loaders/thumbhash_image_request.dart new file mode 100644 index 0000000000..a876020984 --- /dev/null +++ b/mobile/lib/infrastructure/loaders/thumbhash_image_request.dart @@ -0,0 +1,21 @@ +part of 'image_request.dart'; + +class ThumbhashImageRequest extends ImageRequest { + final String thumbhash; + + ThumbhashImageRequest({required this.thumbhash}); + + @override + Future load(ImageDecoderCallback decode, {double scale = 1.0}) async { + if (_isCancelled) { + return null; + } + + final Map info = await thumbnailApi.getThumbhash(thumbhash); + final frame = await _fromPlatformImage(info); + return frame == null ? null : ImageInfo(image: frame.image, scale: scale); + } + + @override + void _onCancelled() {} +} diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart deleted file mode 100644 index e8bf9ace43..0000000000 --- a/mobile/lib/infrastructure/repositories/asset_media.repository.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:typed_data'; -import 'dart:ui'; - -import 'package:photo_manager/photo_manager.dart'; - -class AssetMediaRepository { - const AssetMediaRepository(); - - Future getThumbnail( - String id, { - int quality = 80, - Size size = const Size.square(256), - }) => - AssetEntity( - id: id, - // The below fields are not used in thumbnailDataWithSize but are required - // to create an AssetEntity instance. It is faster to create a dummy AssetEntity - // instance than to fetch the asset from the device first. - typeInt: AssetType.image.index, - width: size.width.toInt(), - height: size.height.toInt(), - ).thumbnailDataWithSize( - ThumbnailSize(size.width.toInt(), size.height.toInt()), - quality: quality, - ); -} diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 99df206db5..0241711d4b 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -1,10 +1,12 @@ +import 'dart:async'; + import 'package:drift/drift.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import "package:immich_mobile/utils/database.utils.dart"; final backupRepositoryProvider = Provider( (ref) => DriftBackupRepository(ref.watch(driftProvider)), @@ -24,105 +26,65 @@ class DriftBackupRepository extends DriftDatabaseRepository { useColumns: false, ), ]) - ..where( - _db.localAlbumEntity.backupSelection - .equalsValue(BackupSelection.excluded), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded)); } - Future getTotalCount() async { - final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - useColumns: false, - ), - ]) - ..where( - _db.localAlbumEntity.backupSelection - .equalsValue(BackupSelection.selected) & - _db.localAlbumAssetEntity.assetId - .isNotInQuery(_getExcludedSubquery()), - ); + /// Returns all backup-related counts in a single query. + /// + /// - total: number of distinct assets in selected albums, excluding those that are also in excluded albums + /// - backup: number of those assets that already exist on the server for [userId] + /// - remainder: number of those assets that do not yet exist on the server for [userId] + /// (includes processing) + /// - processing: number of those assets that are still preparing/have a null checksum + Future<({int total, int remainder, int processing})> getAllCounts(String userId) async { + const sql = ''' + SELECT + COUNT(*) AS total_count, + COUNT(*) FILTER (WHERE lae.checksum IS NULL) AS processing_count, + COUNT(*) FILTER (WHERE rae.id IS NULL) AS remainder_count + FROM local_asset_entity lae + LEFT JOIN main.remote_asset_entity rae + ON lae.checksum = rae.checksum AND rae.owner_id = ?1 + WHERE EXISTS ( + SELECT 1 + FROM local_album_asset_entity laa + INNER JOIN main.local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id + AND la.backup_selection = ?2 + ) + AND NOT EXISTS ( + SELECT 1 + FROM local_album_asset_entity laa + INNER JOIN main.local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id + AND la.backup_selection = ?3 + ); + '''; - return query.get().then((rows) => rows.length); + final row = await _db + .customSelect( + sql, + variables: [ + Variable.withString(userId), + Variable.withInt(BackupSelection.selected.index), + Variable.withInt(BackupSelection.excluded.index), + ], + readsFrom: {_db.localAlbumAssetEntity, _db.localAlbumEntity, _db.localAssetEntity, _db.remoteAssetEntity}, + ) + .getSingle(); + + final data = row.data; + return ( + total: (data['total_count'] as int?) ?? 0, + remainder: (data['remainder_count'] as int?) ?? 0, + processing: (data['processing_count'] as int?) ?? 0, + ); } - Future getRemainderCount() async { - final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - useColumns: false, - ), - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum - .equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where( - _db.localAlbumEntity.backupSelection - .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNull() & - _db.localAlbumAssetEntity.assetId - .isNotInQuery(_getExcludedSubquery()), - ); - - return query.get().then((rows) => rows.length); - } - - Future getBackupCount() async { - final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) - ..addColumns( - [_db.localAlbumAssetEntity.assetId], - ) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), - useColumns: false, - ), - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - innerJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum - .equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ]) - ..where( - _db.localAlbumEntity.backupSelection - .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNotNull() & - _db.localAlbumAssetEntity.assetId - .isNotInQuery(_getExcludedSubquery()), - ); - - return query.get().then((rows) => rows.length); - } - - Future> getCandidates() async { + Future> getCandidates(String userId, {bool onlyHashed = true}) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) - ..where( - _db.localAlbumEntity.backupSelection - .equalsValue(BackupSelection.selected), - ); + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); final query = _db.localAssetEntity.select() ..where( @@ -131,8 +93,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..where( - _db.localAlbumAssetEntity.albumId - .isInQuery(selectedAlbumIds) & + _db.localAlbumAssetEntity.albumId.isInQuery(selectedAlbumIds) & _db.localAlbumAssetEntity.assetId.equalsExp(lae.id), ), ) & @@ -140,17 +101,16 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.selectOnly() ..addColumns([_db.remoteAssetEntity.checksum]) ..where( - _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & - lae.checksum.isNotNull(), + _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & _db.remoteAssetEntity.ownerId.equals(userId), ), ) & lae.id.isNotInQuery(_getExcludedSubquery()), ) - ..orderBy( - [ - (localAsset) => OrderingTerm.desc(localAsset.createdAt), - ], - ); + ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]); + + if (onlyHashed) { + query.where((lae) => lae.checksum.isNotNull()); + } return query.map((localAsset) => localAsset.toDto()).get(); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 7562cf6ff5..7291c3a97b 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,6 +4,8 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; @@ -17,10 +19,11 @@ import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity. import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/stack.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart'; -import 'package:isar/isar.dart'; +import 'package:isar/isar.dart' hide Index; import 'db.repository.drift.dart'; @@ -41,6 +44,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { @DriftDatabase( tables: [ + AuthUserEntity, UserEntity, UserMetadataEntity, PartnerEntity, @@ -56,62 +60,142 @@ class IsarDatabaseRepository implements IDatabaseRepository { MemoryAssetEntity, StackEntity, PersonEntity, + AssetFaceEntity, + StoreEntity, ], - include: { - 'package:immich_mobile/infrastructure/entities/merged_asset.drift', - }, + include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift implements IDatabaseRepository { Drift([QueryExecutor? executor]) - : super( - executor ?? - driftDatabase( - name: 'immich', - native: const DriftNativeOptions(shareAcrossIsolates: true), - ), - ); + : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); + + Future reset() async { + // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + await exclusively(() async { + // https://stackoverflow.com/a/65743498/25690041 + await customStatement('PRAGMA writable_schema = 1;'); + await customStatement('DELETE FROM sqlite_master;'); + await customStatement('VACUUM;'); + await customStatement('PRAGMA writable_schema = 0;'); + await customStatement('PRAGMA integrity_check'); + + await customStatement('PRAGMA user_version = 0'); + await beforeOpen( + // ignore: invalid_use_of_internal_member + resolvedEngine.executor, + OpeningDetails(null, schemaVersion), + ); + await customStatement('PRAGMA user_version = $schemaVersion'); + + // Refresh all stream queries + notifyUpdates({for (final table in allTables) TableUpdate.onTable(table)}); + }); + } @override - int get schemaVersion => 3; + int get schemaVersion => 12; @override MigrationStrategy get migration => MigrationStrategy( - onUpgrade: (m, from, to) async { - // Run migration steps without foreign keys and re-enable them later - await customStatement('PRAGMA foreign_keys = OFF'); + onUpgrade: (m, from, to) async { + // Run migration steps without foreign keys and re-enable them later + await customStatement('PRAGMA foreign_keys = OFF'); - await m.runMigrationSteps( - from: from, - to: to, - steps: migrationSteps( - from1To2: (m, v2) async { - for (final entity in v2.entities) { - await m.drop(entity); - await m.create(entity); - } - }, - from2To3: (m, v3) async { - // Removed foreign key constraint on stack.primaryAssetId - await m.alterTable(TableMigration(v3.stackEntity)); - }, - ), - ); + await m.runMigrationSteps( + from: from, + to: to, + steps: migrationSteps( + from1To2: (m, v2) async { + for (final entity in v2.entities) { + await m.drop(entity); + await m.create(entity); + } + }, + from2To3: (m, v3) async { + // Removed foreign key constraint on stack.primaryAssetId + await m.alterTable(TableMigration(v3.stackEntity)); + }, + from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity + await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added + await m.create(v4.assetFaceEntity); + }, + from4To5: (m, v5) async { + await m.alterTable( + TableMigration( + v5.userEntity, + newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt], + columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime}, + ), + ); + }, + from5To6: (m, v6) async { + // Drops the (checksum, ownerId) and adds it back as (ownerId, checksum) + await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum'); + await m.drop(v6.idxRemoteAssetOwnerChecksum); + await m.create(v6.idxRemoteAssetOwnerChecksum); + // Adds libraryId to remote_asset_entity + await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId); + await m.drop(v6.uQRemoteAssetsOwnerChecksum); + await m.create(v6.uQRemoteAssetsOwnerChecksum); + await m.drop(v6.uQRemoteAssetsOwnerLibraryChecksum); + await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); + }, + from6To7: (m, v7) async { + await m.createIndex(v7.idxLatLng); + }, + from7To8: (m, v8) async { + await m.create(v8.storeEntity); + }, + from8To9: (m, v9) async { + await m.addColumn(v9.localAlbumEntity, v9.localAlbumEntity.linkedRemoteAlbumId); + }, + from9To10: (m, v10) async { + await m.createTable(v10.authUserEntity); + await m.addColumn(v10.userEntity, v10.userEntity.avatarColor); + await m.alterTable(TableMigration(v10.userEntity)); + }, + from10To11: (m, v11) async { + await m.addColumn(v11.localAlbumAssetEntity, v11.localAlbumAssetEntity.marker_); + }, + from11To12: (m, v12) async { + final localToUTCMapping = { + v12.localAssetEntity: [v12.localAssetEntity.createdAt, v12.localAssetEntity.updatedAt], + v12.localAlbumEntity: [v12.localAlbumEntity.updatedAt], + }; - if (kDebugMode) { - // Fail if the migration broke foreign keys - final wrongFKs = - await customSelect('PRAGMA foreign_key_check').get(); - assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); - } - - await customStatement('PRAGMA foreign_keys = ON;'); - }, - beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON'); - await customStatement('PRAGMA synchronous = NORMAL'); - await customStatement('PRAGMA journal_mode = WAL'); - }, + for (final entry in localToUTCMapping.entries) { + final table = entry.key; + await m.alterTable( + TableMigration( + table, + columnTransformer: { + for (final column in entry.value) + column: column.modify(const DateTimeModifier.utc()).strftime('%Y-%m-%dT%H:%M:%fZ'), + }, + ), + ); + } + }, + ), ); + + if (kDebugMode) { + // Fail if the migration broke foreign keys + final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); + assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); + } + + await customStatement('PRAGMA foreign_keys = ON;'); + }, + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 30000'); + }, + ); } class DriftDatabaseRepository implements IDatabaseRepository { @@ -119,6 +203,5 @@ class DriftDatabaseRepository implements IDatabaseRepository { const DriftDatabaseRepository(this._db); @override - Future transaction(Future Function() callback) => - _db.transaction(callback); + Future transaction(Future Function() callback) => _db.transaction(callback); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 0f822e57eb..e39ed8a560 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -5,230 +5,290 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i4; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i6; -import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i7; -import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' - as i8; -import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' - as i9; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' + as i5; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i7; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart' + as i8; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i9; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' as i10; -import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i11; -import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart' as i12; -import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart' as i13; -import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i14; -import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' +import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' as i15; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i16; -import 'package:drift/internal/modular.dart' as i17; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i17; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart' + as i18; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i19; +import 'package:drift/internal/modular.dart' as i20; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); $DriftManager get managers => $DriftManager(this); late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); - late final i2.$RemoteAssetEntityTable remoteAssetEntity = - i2.$RemoteAssetEntityTable(this); - late final i3.$LocalAssetEntityTable localAssetEntity = - i3.$LocalAssetEntityTable(this); - late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this); - late final i5.$UserMetadataEntityTable userMetadataEntity = - i5.$UserMetadataEntityTable(this); - late final i6.$PartnerEntityTable partnerEntity = - i6.$PartnerEntityTable(this); - late final i7.$LocalAlbumEntityTable localAlbumEntity = - i7.$LocalAlbumEntityTable(this); - late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i8.$LocalAlbumAssetEntityTable(this); - late final i9.$RemoteExifEntityTable remoteExifEntity = - i9.$RemoteExifEntityTable(this); - late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = - i10.$RemoteAlbumEntityTable(this); - late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = - i11.$RemoteAlbumAssetEntityTable(this); - late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = - i12.$RemoteAlbumUserEntityTable(this); - late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this); - late final i14.$MemoryAssetEntityTable memoryAssetEntity = - i14.$MemoryAssetEntityTable(this); - late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); - i16.MergedAssetDrift get mergedAssetDrift => i17.ReadDatabaseContainer(this) - .accessor(i16.MergedAssetDrift.new); + late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2 + .$RemoteAssetEntityTable(this); + late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = i4 + .$LocalAssetEntityTable(this); + late final i5.$RemoteAlbumEntityTable remoteAlbumEntity = i5 + .$RemoteAlbumEntityTable(this); + late final i6.$LocalAlbumEntityTable localAlbumEntity = i6 + .$LocalAlbumEntityTable(this); + late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i7 + .$LocalAlbumAssetEntityTable(this); + late final i8.$AuthUserEntityTable authUserEntity = i8.$AuthUserEntityTable( + this, + ); + late final i9.$UserMetadataEntityTable userMetadataEntity = i9 + .$UserMetadataEntityTable(this); + late final i10.$PartnerEntityTable partnerEntity = i10.$PartnerEntityTable( + this, + ); + late final i11.$RemoteExifEntityTable remoteExifEntity = i11 + .$RemoteExifEntityTable(this); + late final i12.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i12 + .$RemoteAlbumAssetEntityTable(this); + late final i13.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i13 + .$RemoteAlbumUserEntityTable(this); + late final i14.$MemoryEntityTable memoryEntity = i14.$MemoryEntityTable(this); + late final i15.$MemoryAssetEntityTable memoryAssetEntity = i15 + .$MemoryAssetEntityTable(this); + late final i16.$PersonEntityTable personEntity = i16.$PersonEntityTable(this); + late final i17.$AssetFaceEntityTable assetFaceEntity = i17 + .$AssetFaceEntityTable(this); + late final i18.$StoreEntityTable storeEntity = i18.$StoreEntityTable(this); + i19.MergedAssetDrift get mergedAssetDrift => i20.ReadDatabaseContainer( + this, + ).accessor(i19.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - i3.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, - i2.idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, + i2.idxRemoteAssetOwnerChecksum, + i2.uQRemoteAssetsOwnerChecksum, + i2.uQRemoteAssetsOwnerLibraryChecksum, + i2.idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + i11.idxLatLng, + ]; @override - i0.StreamQueryUpdateRules get streamUpdateRules => - const i0.StreamQueryUpdateRules( - [ - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('user_metadata_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('memory_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete), - ], - ), - ], - ); + i0.StreamQueryUpdateRules + get streamUpdateRules => const i0.StreamQueryUpdateRules([ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_entity', kind: i0.UpdateKind.update), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('user_metadata_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'memory_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'person_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)], + ), + ]); @override i0.DriftDatabaseOptions get options => const i0.DriftDatabaseOptions(storeDateTimeAsText: true); @@ -241,31 +301,39 @@ class $DriftManager { i1.$$UserEntityTableTableManager(_db, _db.userEntity); i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); - i3.$$LocalAssetEntityTableTableManager get localAssetEntity => - i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i4.$$StackEntityTableTableManager get stackEntity => - i4.$$StackEntityTableTableManager(_db, _db.stackEntity); - i5.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i6.$$PartnerEntityTableTableManager get partnerEntity => - i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8 + i3.$$StackEntityTableTableManager get stackEntity => + i3.$$StackEntityTableTableManager(_db, _db.stackEntity); + i4.$$LocalAssetEntityTableTableManager get localAssetEntity => + i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i5.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => + i5.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity); + i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); - i9.$$RemoteExifEntityTableTableManager get remoteExifEntity => - i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); - i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => - i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity); - i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => - i11.$$RemoteAlbumAssetEntityTableTableManager( - _db, _db.remoteAlbumAssetEntity); - i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12 + i8.$$AuthUserEntityTableTableManager get authUserEntity => + i8.$$AuthUserEntityTableTableManager(_db, _db.authUserEntity); + i9.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i9.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i10.$$PartnerEntityTableTableManager get partnerEntity => + i10.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); + i11.$$RemoteExifEntityTableTableManager get remoteExifEntity => + i11.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); + i12.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => + i12.$$RemoteAlbumAssetEntityTableTableManager( + _db, + _db.remoteAlbumAssetEntity, + ); + i13.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i13 .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); - i13.$$MemoryEntityTableTableManager get memoryEntity => - i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity); - i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity => - i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); - i15.$$PersonEntityTableTableManager get personEntity => - i15.$$PersonEntityTableTableManager(_db, _db.personEntity); + i14.$$MemoryEntityTableTableManager get memoryEntity => + i14.$$MemoryEntityTableTableManager(_db, _db.memoryEntity); + i15.$$MemoryAssetEntityTableTableManager get memoryAssetEntity => + i15.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); + i16.$$PersonEntityTableTableManager get personEntity => + i16.$$PersonEntityTableTableManager(_db, _db.personEntity); + i17.$$AssetFaceEntityTableTableManager get assetFaceEntity => + i17.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity); + i18.$$StoreEntityTableTableManager get storeEntity => + i18.$$StoreEntityTableTableManager(_db, _db.storeEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index a0703c3714..c973cd6f13 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -29,323 +29,286 @@ final class Schema2 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_24, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_24], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape0 extends i0.VersionedTable { @@ -369,33 +332,67 @@ class Shape0 extends i0.VersionedTable { } i1.GeneratedColumn _column_0(String aliasedName) => - i1.GeneratedColumn('id', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_1(String aliasedName) => - i1.GeneratedColumn('name', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'name', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_2(String aliasedName) => - i1.GeneratedColumn('is_admin', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_3(String aliasedName) => - i1.GeneratedColumn('email', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'email', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_4(String aliasedName) => - i1.GeneratedColumn('profile_image_path', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_5(String aliasedName) => - i1.GeneratedColumn('updated_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_6(String aliasedName) => - i1.GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_7(String aliasedName) => - i1.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape1 extends i0.VersionedTable { Shape1({required super.source, required super.alias}) : super.aliased(); @@ -436,53 +433,111 @@ class Shape1 extends i0.VersionedTable { } i1.GeneratedColumn _column_8(String aliasedName) => - i1.GeneratedColumn('type', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'type', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_9(String aliasedName) => - i1.GeneratedColumn('created_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_10(String aliasedName) => - i1.GeneratedColumn('width', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'width', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_11(String aliasedName) => - i1.GeneratedColumn('height', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'height', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_12(String aliasedName) => - i1.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_13(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'checksum', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_14(String aliasedName) => - i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_15(String aliasedName) => - i1.GeneratedColumn('owner_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_16(String aliasedName) => - i1.GeneratedColumn('local_date_time', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_17(String aliasedName) => - i1.GeneratedColumn('thumb_hash', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_18(String aliasedName) => - i1.GeneratedColumn('deleted_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_19(String aliasedName) => - i1.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_20(String aliasedName) => - i1.GeneratedColumn('visibility', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_21(String aliasedName) => - i1.GeneratedColumn('stack_id', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape2 extends i0.VersionedTable { Shape2({required super.source, required super.alias}) : super.aliased(); @@ -511,11 +566,20 @@ class Shape2 extends i0.VersionedTable { } i1.GeneratedColumn _column_22(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'checksum', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_23(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'orientation', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape3 extends i0.VersionedTable { Shape3({required super.source, required super.alias}) : super.aliased(); @@ -532,10 +596,15 @@ class Shape3 extends i0.VersionedTable { } i1.GeneratedColumn _column_24(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); class Shape4 extends i0.VersionedTable { Shape4({required super.source, required super.alias}) : super.aliased(); @@ -548,16 +617,29 @@ class Shape4 extends i0.VersionedTable { } i1.GeneratedColumn _column_25(String aliasedName) => - i1.GeneratedColumn('user_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'user_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_26(String aliasedName) => - i1.GeneratedColumn('key', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'key', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_27(String aliasedName) => - i1.GeneratedColumn('value', aliasedName, false, - type: i1.DriftSqlType.blob); + i1.GeneratedColumn( + 'value', + aliasedName, + false, + type: i1.DriftSqlType.blob, + ); class Shape5 extends i0.VersionedTable { Shape5({required super.source, required super.alias}) : super.aliased(); @@ -570,21 +652,36 @@ class Shape5 extends i0.VersionedTable { } i1.GeneratedColumn _column_28(String aliasedName) => - i1.GeneratedColumn('shared_by_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_29(String aliasedName) => - i1.GeneratedColumn('shared_with_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_30(String aliasedName) => - i1.GeneratedColumn('in_timeline', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); class Shape6 extends i0.VersionedTable { Shape6({required super.source, required super.alias}) : super.aliased(); @@ -603,19 +700,33 @@ class Shape6 extends i0.VersionedTable { } i1.GeneratedColumn _column_31(String aliasedName) => - i1.GeneratedColumn('backup_selection', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_32(String aliasedName) => - i1.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_33(String aliasedName) => - i1.GeneratedColumn('marker', aliasedName, true, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("marker" IN (0, 1))')); + i1.GeneratedColumn( + 'marker', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); class Shape7 extends i0.VersionedTable { Shape7({required super.source, required super.alias}) : super.aliased(); @@ -626,15 +737,25 @@ class Shape7 extends i0.VersionedTable { } i1.GeneratedColumn _column_34(String aliasedName) => - i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_35(String aliasedName) => - i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape8 extends i0.VersionedTable { Shape8({required super.source, required super.alias}) : super.aliased(); @@ -685,67 +806,148 @@ class Shape8 extends i0.VersionedTable { } i1.GeneratedColumn _column_36(String aliasedName) => - i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_37(String aliasedName) => - i1.GeneratedColumn('city', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'city', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_38(String aliasedName) => - i1.GeneratedColumn('state', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'state', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_39(String aliasedName) => - i1.GeneratedColumn('country', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'country', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_40(String aliasedName) => - i1.GeneratedColumn('date_time_original', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_41(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'description', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_42(String aliasedName) => - i1.GeneratedColumn('exposure_time', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_43(String aliasedName) => - i1.GeneratedColumn('f_number', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'f_number', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_44(String aliasedName) => - i1.GeneratedColumn('file_size', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'file_size', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_45(String aliasedName) => - i1.GeneratedColumn('focal_length', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_46(String aliasedName) => - i1.GeneratedColumn('latitude', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'latitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_47(String aliasedName) => - i1.GeneratedColumn('longitude', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'longitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_48(String aliasedName) => - i1.GeneratedColumn('iso', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'iso', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_49(String aliasedName) => - i1.GeneratedColumn('make', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'make', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_50(String aliasedName) => - i1.GeneratedColumn('model', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'model', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_51(String aliasedName) => - i1.GeneratedColumn('lens', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'lens', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_52(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_53(String aliasedName) => - i1.GeneratedColumn('time_zone', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_54(String aliasedName) => - i1.GeneratedColumn('rating', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'rating', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_55(String aliasedName) => - i1.GeneratedColumn('projection_type', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape9 extends i0.VersionedTable { Shape9({required super.source, required super.alias}) : super.aliased(); @@ -770,28 +972,51 @@ class Shape9 extends i0.VersionedTable { } i1.GeneratedColumn _column_56(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, false, - type: i1.DriftSqlType.string, - defaultValue: const CustomExpression('\'\'')); + i1.GeneratedColumn( + 'description', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultValue: const CustomExpression('\'\''), + ); i1.GeneratedColumn _column_57(String aliasedName) => - i1.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i1.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_58(String aliasedName) => - i1.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + i1.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); i1.GeneratedColumn _column_59(String aliasedName) => - i1.GeneratedColumn('order', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'order', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_60(String aliasedName) => - i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape10 extends i0.VersionedTable { Shape10({required super.source, required super.alias}) : super.aliased(); @@ -804,8 +1029,12 @@ class Shape10 extends i0.VersionedTable { } i1.GeneratedColumn _column_61(String aliasedName) => - i1.GeneratedColumn('role', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'role', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); class Shape11 extends i0.VersionedTable { Shape11({required super.source, required super.alias}) : super.aliased(); @@ -836,26 +1065,51 @@ class Shape11 extends i0.VersionedTable { } i1.GeneratedColumn _column_62(String aliasedName) => - i1.GeneratedColumn('data', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'data', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_63(String aliasedName) => - i1.GeneratedColumn('is_saved', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_64(String aliasedName) => - i1.GeneratedColumn('memory_at', aliasedName, false, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_65(String aliasedName) => - i1.GeneratedColumn('seen_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_66(String aliasedName) => - i1.GeneratedColumn('show_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'show_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_67(String aliasedName) => - i1.GeneratedColumn('hide_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); class Shape12 extends i0.VersionedTable { Shape12({required super.source, required super.alias}) : super.aliased(); @@ -866,10 +1120,15 @@ class Shape12 extends i0.VersionedTable { } i1.GeneratedColumn _column_68(String aliasedName) => - i1.GeneratedColumn('memory_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); class Shape13 extends i0.VersionedTable { Shape13({required super.source, required super.alias}) : super.aliased(); @@ -898,27 +1157,53 @@ class Shape13 extends i0.VersionedTable { } i1.GeneratedColumn _column_69(String aliasedName) => - i1.GeneratedColumn('face_asset_id', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_70(String aliasedName) => - i1.GeneratedColumn('thumbnail_path', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_71(String aliasedName) => - i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); i1.GeneratedColumn _column_72(String aliasedName) => - i1.GeneratedColumn('is_hidden', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_hidden" IN (0, 1))')); + i1.GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); i1.GeneratedColumn _column_73(String aliasedName) => - i1.GeneratedColumn('color', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'color', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_74(String aliasedName) => - i1.GeneratedColumn('birth_date', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); final class Schema3 extends i0.VersionedSchema { Schema3({required super.database}) : super(version: 3); @@ -944,331 +1229,3826 @@ final class Schema3 extends i0.VersionedSchema { personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - source: i0.VersionedTable( - entityName: 'remote_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_13, - _column_14, - _column_15, - _column_16, - _column_17, - _column_18, - _column_19, - _column_20, - _column_21, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape2 localAssetEntity = Shape2( - source: i0.VersionedTable( - entityName: 'local_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_1, - _column_8, - _column_9, - _column_5, - _column_10, - _column_11, - _column_12, - _column_0, - _column_22, - _column_14, - _column_23, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape3 stackEntity = Shape3( - source: i0.VersionedTable( - entityName: 'stack_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_75, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final Shape4 userMetadataEntity = Shape4( - source: i0.VersionedTable( - entityName: 'user_metadata_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(user_id, "key")', - ], - columns: [ - _column_25, - _column_26, - _column_27, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); late final Shape5 partnerEntity = Shape5( - source: i0.VersionedTable( - entityName: 'partner_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(shared_by_id, shared_with_id)', - ], - columns: [ - _column_28, - _column_29, - _column_30, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); late final Shape6 localAlbumEntity = Shape6( - source: i0.VersionedTable( - entityName: 'local_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_5, - _column_31, - _column_32, - _column_33, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 localAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'local_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_34, - _column_35, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); late final Shape8 remoteExifEntity = Shape8( - source: i0.VersionedTable( - entityName: 'remote_exif_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id)', - ], - columns: [ - _column_36, - _column_37, - _column_38, - _column_39, - _column_40, - _column_41, - _column_11, - _column_10, - _column_42, - _column_43, - _column_44, - _column_45, - _column_46, - _column_47, - _column_48, - _column_49, - _column_50, - _column_51, - _column_52, - _column_53, - _column_54, - _column_55, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape9 remoteAlbumEntity = Shape9( - source: i0.VersionedTable( - entityName: 'remote_album_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_56, - _column_9, - _column_5, - _column_15, - _column_57, - _column_58, - _column_59, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape7 remoteAlbumAssetEntity = Shape7( - source: i0.VersionedTable( - entityName: 'remote_album_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, album_id)', - ], - columns: [ - _column_36, - _column_60, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); late final Shape10 remoteAlbumUserEntity = Shape10( - source: i0.VersionedTable( - entityName: 'remote_album_user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(album_id, user_id)', - ], - columns: [ - _column_60, - _column_25, - _column_61, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); late final Shape11 memoryEntity = Shape11( - source: i0.VersionedTable( - entityName: 'memory_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_18, - _column_15, - _column_8, - _column_62, - _column_63, - _column_64, - _column_65, - _column_66, - _column_67, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape12 memoryAssetEntity = Shape12( - source: i0.VersionedTable( - entityName: 'memory_asset_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(asset_id, memory_id)', - ], - columns: [ - _column_36, - _column_68, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); late final Shape13 personEntity = Shape13( - source: i0.VersionedTable( - entityName: 'person_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_9, - _column_5, - _column_15, - _column_1, - _column_69, - _column_70, - _column_71, - _column_72, - _column_73, - _column_74, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } i1.GeneratedColumn _column_75(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); + +final class Schema4 extends i0.VersionedSchema { + Schema4({required super.database}) : super(version: 4); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape14 extends i0.VersionedTable { + Shape14({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +class Shape15 extends i0.VersionedTable { + Shape15({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); +i1.GeneratedColumn _column_77(String aliasedName) => + i1.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_78(String aliasedName) => + i1.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_79(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_80(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_81(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_82(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_83(String aliasedName) => + i1.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); + +final class Schema5 extends i0.VersionedSchema { + Schema5({required super.database}) : super(version: 5); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape16 extends i0.VersionedTable { + Shape16({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_84(String aliasedName) => + i1.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_85(String aliasedName) => + i1.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + +final class Schema6 extends i0.VersionedSchema { + Schema6({required super.database}) : super(version: 6); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +class Shape17 extends i0.VersionedTable { + Shape17({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get livePhotoVideoId => + columnsByName['live_photo_video_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get libraryId => + columnsByName['library_id']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_86(String aliasedName) => + i1.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); + +final class Schema7 extends i0.VersionedSchema { + Schema7({required super.database}) : super(version: 7); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + idxLatLng, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + +final class Schema8 extends i0.VersionedSchema { + Schema8({required super.database}) : super(version: 8); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape6 localAlbumEntity = Shape6( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + +class Shape18 extends i0.VersionedTable { + Shape18({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get stringValue => + columnsByName['string_value']! as i1.GeneratedColumn; + i1.GeneratedColumn get intValue => + columnsByName['int_value']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_87(String aliasedName) => + i1.GeneratedColumn( + 'id', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_88(String aliasedName) => + i1.GeneratedColumn( + 'string_value', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_89(String aliasedName) => + i1.GeneratedColumn( + 'int_value', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); + +final class Schema9 extends i0.VersionedSchema { + Schema9({required super.database}) : super(version: 9); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape16 userEntity = Shape16( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_84, + _column_85, + _column_5, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape19 localAlbumEntity = Shape19( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_90, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + +class Shape19 extends i0.VersionedTable { + Shape19({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get backupSelection => + columnsByName['backup_selection']! as i1.GeneratedColumn; + i1.GeneratedColumn get isIosSharedAlbum => + columnsByName['is_ios_shared_album']! as i1.GeneratedColumn; + i1.GeneratedColumn get linkedRemoteAlbumId => + columnsByName['linked_remote_album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get marker_ => + columnsByName['marker']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_90(String aliasedName) => + i1.GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + +final class Schema10 extends i0.VersionedSchema { + Schema10({required super.database}) : super(version: 10); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape20 userEntity = Shape20( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_84, + _column_85, + _column_91, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape19 localAlbumEntity = Shape19( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_90, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 localAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape21 authUserEntity = Shape21( + source: i0.VersionedTable( + entityName: 'auth_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_2, + _column_84, + _column_85, + _column_92, + _column_93, + _column_7, + _column_94, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + +class Shape20 extends i0.VersionedTable { + Shape20({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get avatarColor => + columnsByName['avatar_color']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_91(String aliasedName) => + i1.GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); + +class Shape21 extends i0.VersionedTable { + Shape21({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get avatarColor => + columnsByName['avatar_color']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaSizeInBytes => + columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get quotaUsageInBytes => + columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn; + i1.GeneratedColumn get pinCode => + columnsByName['pin_code']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_92(String aliasedName) => + i1.GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_93(String aliasedName) => + i1.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_94(String aliasedName) => + i1.GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); + +final class Schema11 extends i0.VersionedSchema { + Schema11({required super.database}) : super(version: 11); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape20 userEntity = Shape20( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_84, + _column_85, + _column_91, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape19 localAlbumEntity = Shape19( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_90, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape22 localAlbumAssetEntity = Shape22( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35, _column_33], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape21 authUserEntity = Shape21( + source: i0.VersionedTable( + entityName: 'auth_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_2, + _column_84, + _column_85, + _column_92, + _column_93, + _column_7, + _column_94, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + +class Shape22 extends i0.VersionedTable { + Shape22({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get albumId => + columnsByName['album_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get marker_ => + columnsByName['marker']! as i1.GeneratedColumn; +} + +final class Schema12 extends i0.VersionedSchema { + Schema12({required super.database}) : super(version: 12); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + late final Shape20 userEntity = Shape20( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_84, + _column_85, + _column_91, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape17 remoteAssetEntity = Shape17( + source: i0.VersionedTable( + entityName: 'remote_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_13, + _column_14, + _column_15, + _column_16, + _column_17, + _column_18, + _column_19, + _column_20, + _column_21, + _column_86, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape3 stackEntity = Shape3( + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_75], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape2 localAssetEntity = Shape2( + source: i0.VersionedTable( + entityName: 'local_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_1, + _column_8, + _column_9, + _column_5, + _column_10, + _column_11, + _column_12, + _column_0, + _column_22, + _column_14, + _column_23, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape9 remoteAlbumEntity = Shape9( + source: i0.VersionedTable( + entityName: 'remote_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_56, + _column_9, + _column_5, + _column_15, + _column_57, + _column_58, + _column_59, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape19 localAlbumEntity = Shape19( + source: i0.VersionedTable( + entityName: 'local_album_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_5, + _column_31, + _column_32, + _column_90, + _column_33, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape22 localAlbumAssetEntity = Shape22( + source: i0.VersionedTable( + entityName: 'local_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_34, _column_35, _column_33], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final Shape21 authUserEntity = Shape21( + source: i0.VersionedTable( + entityName: 'auth_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_3, + _column_2, + _column_84, + _column_85, + _column_92, + _column_93, + _column_7, + _column_94, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape4 userMetadataEntity = Shape4( + source: i0.VersionedTable( + entityName: 'user_metadata_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(user_id, "key")'], + columns: [_column_25, _column_26, _column_27], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape5 partnerEntity = Shape5( + source: i0.VersionedTable( + entityName: 'partner_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'], + columns: [_column_28, _column_29, _column_30], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape8 remoteExifEntity = Shape8( + source: i0.VersionedTable( + entityName: 'remote_exif_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id)'], + columns: [ + _column_36, + _column_37, + _column_38, + _column_39, + _column_40, + _column_41, + _column_11, + _column_10, + _column_42, + _column_43, + _column_44, + _column_45, + _column_46, + _column_47, + _column_48, + _column_49, + _column_50, + _column_51, + _column_52, + _column_53, + _column_54, + _column_55, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape7 remoteAlbumAssetEntity = Shape7( + source: i0.VersionedTable( + entityName: 'remote_album_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, album_id)'], + columns: [_column_36, _column_60], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape10 remoteAlbumUserEntity = Shape10( + source: i0.VersionedTable( + entityName: 'remote_album_user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(album_id, user_id)'], + columns: [_column_60, _column_25, _column_61], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape11 memoryEntity = Shape11( + source: i0.VersionedTable( + entityName: 'memory_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_18, + _column_15, + _column_8, + _column_62, + _column_63, + _column_64, + _column_65, + _column_66, + _column_67, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape12 memoryAssetEntity = Shape12( + source: i0.VersionedTable( + entityName: 'memory_asset_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'], + columns: [_column_36, _column_68], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape14 personEntity = Shape14( + source: i0.VersionedTable( + entityName: 'person_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_9, + _column_5, + _column_15, + _column_1, + _column_69, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape15 assetFaceEntity = Shape15( + source: i0.VersionedTable( + entityName: 'asset_face_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_36, + _column_76, + _column_77, + _column_78, + _column_79, + _column_80, + _column_81, + _column_82, + _column_83, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape18 storeEntity = Shape18( + source: i0.VersionedTable( + entityName: 'store_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_87, _column_88, _column_89], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLatLng = i1.Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); +} + i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, + required Future Function(i1.Migrator m, Schema7 schema) from6To7, + required Future Function(i1.Migrator m, Schema8 schema) from7To8, + required Future Function(i1.Migrator m, Schema9 schema) from8To9, + required Future Function(i1.Migrator m, Schema10 schema) from9To10, + required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -1282,6 +5062,51 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from2To3(migrator, schema); return 3; + case 3: + final schema = Schema4(database: database); + final migrator = i1.Migrator(database, schema); + await from3To4(migrator, schema); + return 4; + case 4: + final schema = Schema5(database: database); + final migrator = i1.Migrator(database, schema); + await from4To5(migrator, schema); + return 5; + case 5: + final schema = Schema6(database: database); + final migrator = i1.Migrator(database, schema); + await from5To6(migrator, schema); + return 6; + case 6: + final schema = Schema7(database: database); + final migrator = i1.Migrator(database, schema); + await from6To7(migrator, schema); + return 7; + case 7: + final schema = Schema8(database: database); + final migrator = i1.Migrator(database, schema); + await from7To8(migrator, schema); + return 8; + case 8: + final schema = Schema9(database: database); + final migrator = i1.Migrator(database, schema); + await from8To9(migrator, schema); + return 9; + case 9: + final schema = Schema10(database: database); + final migrator = i1.Migrator(database, schema); + await from9To10(migrator, schema); + return 10; + case 10: + final schema = Schema11(database: database); + final migrator = i1.Migrator(database, schema); + await from10To11(migrator, schema); + return 11; + case 11: + final schema = Schema12(database: database); + final migrator = i1.Migrator(database, schema); + await from11To12(migrator, schema); + return 12; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -1291,9 +5116,27 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, required Future Function(i1.Migrator m, Schema3 schema) from2To3, -}) => - i0.VersionedSchema.stepByStepHelper( - step: migrationSteps( - from1To2: from1To2, - from2To3: from2To3, - )); + required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, + required Future Function(i1.Migrator m, Schema7 schema) from6To7, + required Future Function(i1.Migrator m, Schema8 schema) from7To8, + required Future Function(i1.Migrator m, Schema9 schema) from8To9, + required Future Function(i1.Migrator m, Schema10 schema) from9To10, + required Future Function(i1.Migrator m, Schema11 schema) from10To11, + required Future Function(i1.Migrator m, Schema12 schema) from11To12, +}) => i0.VersionedSchema.stepByStepHelper( + step: migrationSteps( + from1To2: from1To2, + from2To3: from2To3, + from3To4: from3To4, + from4To5: from4To5, + from5To6: from5To6, + from6To7: from6To7, + from7To8: from7To8, + from8To9: from8To9, + from9To10: from9To10, + from10To11: from10To11, + from11To12: from11To12, + ), +); diff --git a/mobile/lib/infrastructure/repositories/device_asset.repository.dart b/mobile/lib/infrastructure/repositories/device_asset.repository.dart index 4e72d4a7f6..73ee148ab3 100644 --- a/mobile/lib/infrastructure/repositories/device_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/device_asset.repository.dart @@ -24,8 +24,7 @@ class IsarDeviceAssetRepository extends IsarDatabaseRepository { Future updateAll(List assetHash) { return transaction(() async { - await _db.deviceAssetEntitys - .putAll(assetHash.map(DeviceAssetEntity.fromDto).toList()); + await _db.deviceAssetEntitys.putAll(assetHash.map(DeviceAssetEntity.fromDto).toList()); return true; }); } diff --git a/mobile/lib/infrastructure/repositories/exif.repository.dart b/mobile/lib/infrastructure/repositories/exif.repository.dart index 0012e329ca..0ede30680e 100644 --- a/mobile/lib/infrastructure/repositories/exif.repository.dart +++ b/mobile/lib/infrastructure/repositories/exif.repository.dart @@ -1,6 +1,5 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; @@ -34,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository { Future> updateAll(List exifInfos) { return transaction(() async { - await _db.exifInfos.putAll( - exifInfos.map(entity.ExifInfo.fromDto).toList(), - ); + await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList()); return exifInfos; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 5f192a20cf..e4bff24879 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -1,27 +1,20 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:immich_mobile/utils/database.utils.dart'; -import 'package:platform/platform.dart'; -enum SortLocalAlbumsBy { - id, - backupSelection, - isIosSharedAlbum, - name, - assetCount -} +enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum, name, assetCount, newestAsset } class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; - final Platform _platform; - const DriftLocalAlbumRepository(this._db, {Platform? platform}) - : _platform = platform ?? const LocalPlatform(), - super(_db); + + const DriftLocalAlbumRepository(this._db) : super(_db); Future> getAll({Set sortBy = const {}}) { final assetCount = _db.localAlbumAssetEntity.assetId.count(); @@ -40,72 +33,72 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), - SortLocalAlbumsBy.backupSelection => - OrderingTerm.asc(_db.localAlbumEntity.backupSelection), - SortLocalAlbumsBy.isIosSharedAlbum => - OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), - SortLocalAlbumsBy.name => - OrderingTerm.asc(_db.localAlbumEntity.name), - SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), - }, - ); + orderings.add(switch (sort) { + SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), + SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), + SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), + SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), + SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), + SortLocalAlbumsBy.newestAsset => OrderingTerm.desc(_db.localAlbumEntity.updatedAt), + }); } query.orderBy(orderings); } - return query - .map( - (row) => row - .readTable(_db.localAlbumEntity) - .toDto(assetCount: row.read(assetCount) ?? 0), - ) - .get(); + return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get(); + } + + Future> getBackupAlbums() async { + final query = _db.localAlbumEntity.select() + ..where((row) => row.backupSelection.equalsValue(BackupSelection.selected)); + + return query.map((row) => row.toDto()).get(); } Future delete(String albumId) => transaction(() async { - // Remove all assets that are only in this particular album - // We cannot remove all assets in the album because they might be in other albums in iOS - // That is not the case on Android since asset <-> album has one:one mapping - final assetsToDelete = _platform.isIOS - ? await _getUniqueAssetsInAlbum(albumId) - : await getAssetIds(albumId); - await _deleteAssets(assetsToDelete); + // Remove all assets that are only in this particular album + // We cannot remove all assets in the album because they might be in other albums in iOS + // That is not the case on Android since asset <-> album has one:one mapping + final assetsToDelete = CurrentPlatform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); + await _deleteAssets(assetsToDelete); - // All the other assets that are still associated will be unlinked automatically on-cascade - await _db.managers.localAlbumEntity - .filter((a) => a.id.equals(albumId)) - .delete(); - }); + await _db.managers.localAlbumEntity + .filter((a) => a.id.equals(albumId) & a.backupSelection.equals(BackupSelection.none)) + .delete(); + }); - Future syncDeletes( - String albumId, - Iterable assetIdsToKeep, - ) async { + Future syncDeletes(String albumId, Iterable assetIdsToKeep) async { if (assetIdsToKeep.isEmpty) { return Future.value(); } - final deleteSmt = _db.localAssetEntity.delete(); - deleteSmt.where((localAsset) { - final subQuery = _db.localAlbumAssetEntity.selectOnly() - ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId - .equalsExp(_db.localAlbumEntity.id), + return _db.transaction(() async { + await _db.managers.localAlbumAssetEntity + .filter((row) => row.albumId.id.equals(albumId)) + .update((album) => album(marker_: const Value(true))); + + await _db.batch((batch) { + for (final assetId in assetIdsToKeep) { + batch.update( + _db.localAlbumAssetEntity, + const LocalAlbumAssetEntityCompanion(marker_: Value(null)), + where: (row) => row.assetId.equals(assetId) & row.albumId.equals(albumId), + ); + } + }); + + final query = _db.localAssetEntity.delete() + ..where( + (row) => row.id.isInQuery( + _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..where( + _db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAlbumAssetEntity.marker_.isNotNull(), + ), ), - ]); - subQuery.where( - _db.localAlbumEntity.id.equals(albumId) & - _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), - ); - return localAsset.id.isInQuery(subQuery); + ); + await query.go(); }); - await deleteSmt.go(); } Future upsert( @@ -122,17 +115,11 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { ); return _db.transaction(() async { - await _db.localAlbumEntity - .insertOne(companion, onConflict: DoUpdate((_) => companion)); + await _db.localAlbumEntity.insertOne(companion, onConflict: DoUpdate((_) => companion)); if (toUpsert.isNotEmpty) { await _upsertAssets(toUpsert); await _db.localAlbumAssetEntity.insertAll( - toUpsert.map( - (a) => LocalAlbumAssetEntityCompanion.insert( - assetId: a.id, - albumId: localAlbum.id, - ), - ), + toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)), mode: InsertMode.insertOrIgnore, ); } @@ -142,9 +129,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { Future updateAll(Iterable albums) { return _db.transaction(() async { - await _db.localAlbumEntity - .update() - .write(const LocalAlbumEntityCompanion(marker_: Value(true))); + await _db.localAlbumEntity.update().write(const LocalAlbumEntityCompanion(marker_: Value(true))); await _db.batch((batch) { for (final album in albums) { @@ -173,7 +158,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } }); - if (_platform.isAndroid) { + if (CurrentPlatform.isAndroid) { // On Android, an asset can only be in one album // So, get the albums that are marked for deletion // and delete all the assets that are in those albums @@ -182,11 +167,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId - .equalsExp(_db.localAlbumEntity.id), - ), + innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)), ]); subQuery.where(_db.localAlbumEntity.marker_.isNotNull()); return localAsset.id.isInQuery(subQuery); @@ -194,33 +175,28 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { await deleteSmt.go(); } - await _db.localAlbumEntity.deleteWhere((f) => f.marker_.isNotNull()); + // Only remove albums that are not explicitly selected or excluded from backups + await _db.localAlbumEntity.deleteWhere( + (f) => f.marker_.isNotNull() & f.backupSelection.equalsValue(BackupSelection.none), + ); }); } Future> getAssets(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); - return query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } Future> getAssetIds(String albumId) { final query = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)); - return query - .map((row) => row.read(_db.localAlbumAssetEntity.assetId)!) - .get(); + return query.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!).get(); } Future processDelta({ @@ -238,12 +214,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { // List await _db.batch((batch) async { assetAlbums.cast>().forEach((assetId, albumIds) { - batch.deleteWhere( - _db.localAlbumAssetEntity, - (f) => - f.albumId.isNotIn(albumIds.cast().nonNulls) & - f.assetId.equals(assetId), - ); + for (final albumId in albumIds.cast().nonNulls) { + batch.deleteWhere(_db.localAlbumAssetEntity, (f) => f.albumId.equals(albumId) & f.assetId.equals(assetId)); + } }); }); await _db.batch((batch) async { @@ -251,11 +224,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insertAll( _db.localAlbumAssetEntity, albumIds.cast().nonNulls.map( - (albumId) => LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), - ), + (albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId), + ), onConflict: DoNothing(), ); }); @@ -264,23 +234,14 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssetsToHash(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where( - _db.localAlbumAssetEntity.albumId.equals(albumId) & - _db.localAssetEntity.checksum.isNull(), - ) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull()) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); - return query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } Future _upsertAssets(Iterable localAssets) { @@ -301,14 +262,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { id: asset.id, orientation: Value(asset.orientation), checksum: const Value(null), + isFavorite: Value(asset.isFavorite), ); batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, companion, - onConflict: DoUpdate( - (_) => companion, - where: (old) => old.updatedAt.isNotValue(asset.updatedAt), - ), + onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)), ); } }); @@ -319,7 +278,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return Future.value(); } - if (_platform.isAndroid) { + if (CurrentPlatform.isAndroid) { return _deleteAssets(assetIds); } @@ -344,12 +303,14 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { return transaction(() async { if (assetsToUnLink.isNotEmpty) { - await _db.batch( - (batch) => batch.deleteWhere( - _db.localAlbumAssetEntity, - (f) => f.assetId.isIn(assetsToUnLink) & f.albumId.equals(albumId), - ), - ); + await _db.batch((batch) { + for (final assetId in assetsToUnLink) { + batch.deleteWhere( + _db.localAlbumAssetEntity, + (row) => row.assetId.equals(assetId) & row.albumId.equals(albumId), + ); + } + }); } await _deleteAssets(assetsToDelete); @@ -364,8 +325,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { ..addColumns([assetId]) ..groupBy( [assetId], - having: _db.localAlbumAssetEntity.albumId.count().equals(1) & - _db.localAlbumAssetEntity.albumId.equals(albumId), + having: _db.localAlbumAssetEntity.albumId.count().equals(1) & _db.localAlbumAssetEntity.albumId.equals(albumId), ); return query.map((row) => row.read(assetId)!).get(); @@ -377,25 +337,39 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } return _db.batch((batch) { - batch.deleteWhere(_db.localAssetEntity, (f) => f.id.isIn(ids)); + for (final id in ids) { + batch.deleteWhere(_db.localAssetEntity, (row) => row.id.equals(id)); + } }); } Future getThumbnail(String albumId) async { - final query = _db.localAlbumAssetEntity.select().join([ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) - ..limit(1); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(1); - final results = await query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); return results.isNotEmpty ? results.first : null; } + + Future getCount() { + return _db.managers.localAlbumEntity.count(); + } + + Future unlinkRemoteAlbum(String id) async { + return _db.localAlbumEntity.update() + ..where((row) => row.id.equals(id)) + ..write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null))); + } + + Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async { + return _db.localAlbumEntity.update() + ..where((row) => row.id.equals(localAlbumId)) + ..write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId))); + } } diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 8d21c858a2..2b76472c9e 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -1,6 +1,7 @@ -import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; @@ -9,37 +10,42 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { final Drift _db; const DriftLocalAssetRepository(this._db) : super(_db); - Stream watchAsset(String id) { - final query = _db.localAssetEntity - .select() - .addColumns([_db.remoteAssetEntity.id]).join([ + SingleOrNullSelectable _assetSelectable(String id) { + final query = _db.localAssetEntity.select().addColumns([_db.remoteAssetEntity.id]).join([ leftOuterJoin( _db.remoteAssetEntity, _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), useColumns: false, ), - ]) - ..where(_db.localAssetEntity.id.equals(id)); + ])..where(_db.localAssetEntity.id.equals(id)); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); - }).watchSingleOrNull(); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); + }); } - Future updateHashes(Iterable hashes) { + Future get(String id) => _assetSelectable(id).getSingleOrNull(); + + Future> getByChecksum(String checksum) { + final query = _db.localAssetEntity.select()..where((lae) => lae.checksum.equals(checksum)); + + return query.map((row) => row.toDto()).get(); + } + + Stream watch(String id) => _assetSelectable(id).watchSingleOrNull(); + + Future updateHashes(Map hashes) { if (hashes.isEmpty) { return Future.value(); } return _db.batch((batch) async { - for (final asset in hashes) { + for (final entry in hashes.entries) { batch.update( _db.localAssetEntity, - LocalAssetEntityCompanion(checksum: Value(asset.checksum)), - where: (e) => e.id.equals(asset.id), + LocalAssetEntityCompanion(checksum: Value(entry.value)), + where: (e) => e.id.equals(entry.key), ); } }); @@ -51,16 +57,42 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { } return _db.batch((batch) { - for (final slice in ids.slices(32000)) { - batch.deleteWhere(_db.localAssetEntity, (e) => e.id.isIn(slice)); + for (final id in ids) { + batch.deleteWhere(_db.localAssetEntity, (e) => e.id.equals(id)); } }); } Future getById(String id) { - final query = _db.localAssetEntity.select() - ..where((lae) => lae.id.equals(id)); + final query = _db.localAssetEntity.select()..where((lae) => lae.id.equals(id)); return query.map((row) => row.toDto()).getSingleOrNull(); } + + Future getCount() { + return _db.managers.localAssetEntity.count(); + } + + Future getHashedCount() { + return _db.managers.localAssetEntity.filter((e) => e.checksum.isNull().not()).count(); + } + + Future> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) { + final query = _db.localAlbumEntity.select() + ..where( + (lae) => existsQuery( + _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.albumId]) + ..where( + _db.localAlbumAssetEntity.albumId.equalsExp(lae.id) & + _db.localAlbumAssetEntity.assetId.equals(localAssetId), + ), + ), + ) + ..orderBy([(lae) => OrderingTerm.asc(lae.name)]); + if (backupSelection != null) { + query.where((lae) => lae.backupSelection.equalsValue(backupSelection)); + } + return query.map((localAlbum) => localAlbum.toDto()).get(); + } } diff --git a/mobile/lib/infrastructure/repositories/log.repository.dart b/mobile/lib/infrastructure/repositories/log.repository.dart index 717900910a..9b3985ba48 100644 --- a/mobile/lib/infrastructure/repositories/log.repository.dart +++ b/mobile/lib/infrastructure/repositories/log.repository.dart @@ -1,46 +1,75 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:isar/isar.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; -class IsarLogRepository extends IsarDatabaseRepository { - final Isar _db; - const IsarLogRepository(super.db) : _db = db; +class LogRepository { + final DriftLogger _db; + const LogRepository(this._db); Future deleteAll() async { - await transaction(() async => await _db.loggerMessages.clear()); + await _db.logMessageEntity.deleteAll(); return true; } - Future> getAll() async { - final logs = - await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); - return logs.map((l) => l.toDto()).toList(); + Future> getAll({int limit = 250}) async { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(limit); + + return query.map((log) => log.toDto()).get(); + } + + LogMessageEntityCompanion _toEntityCompanion(LogMessage log) { + return LogMessageEntityCompanion.insert( + message: log.message, + level: log.level, + createdAt: log.createdAt, + logger: Value(log.logger), + details: Value(log.error), + stack: Value(log.stack), + ); } Future insert(LogMessage log) async { - final logEntity = LoggerMessage.fromDto(log); - await transaction(() async { - await _db.loggerMessages.put(logEntity); - }); + final logEntity = _toEntityCompanion(log); + + try { + await _db.logMessageEntity.insertOne(logEntity); + } catch (e) { + return false; + } + return true; } Future insertAll(Iterable logs) async { - await transaction(() async { - final logEntities = - logs.map((log) => LoggerMessage.fromDto(log)).toList(); - await _db.loggerMessages.putAll(logEntities); - }); + final logEntities = logs.map(_toEntityCompanion).toList(); + await _db.logMessageEntity.insertAll(logEntities); + return true; } - Future truncate({int limit = 250}) async { - await transaction(() async { - final count = await _db.loggerMessages.count(); - if (count <= limit) return; - final toRemove = count - limit; - await _db.loggerMessages.where().limit(toRemove).deleteAll(); - }); + Future deleteByLogger(String logger) async { + await _db.logMessageEntity.deleteWhere((row) => row.logger.equals(logger)); + } + + Stream> watchMessages(String logger) { + final query = _db.logMessageEntity.select() + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..where((row) => row.logger.equals(logger)); + + return query.watch().map((rows) => rows.map((row) => row.toDto()).toList()); + } + + Future truncate({int limit = kLogTruncateLimit}) async { + final totalCount = await _db.managers.logMessageEntity.count(); + if (totalCount > limit) { + final rowsToDelete = totalCount - limit; + + await _db.managers.logMessageEntity.orderBy((o) => o.createdAt.asc()).limit(rowsToDelete).delete(); + } } } diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.dart new file mode 100644 index 0000000000..583fc42813 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.dart @@ -0,0 +1,27 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; +import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; + +import 'logger_db.repository.drift.dart'; + +@DriftDatabase(tables: [LogMessageEntity]) +class DriftLogger extends $DriftLogger implements IDatabaseRepository { + DriftLogger([QueryExecutor? executor]) + : super( + executor ?? driftDatabase(name: 'immich_logs', native: const DriftNativeOptions(shareAcrossIsolates: true)), + ); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration => MigrationStrategy( + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); + }, + ); +} diff --git a/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart new file mode 100644 index 0000000000..8389d3a827 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/logger_db.repository.drift.dart @@ -0,0 +1,27 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/log.entity.drift.dart' + as i1; + +abstract class $DriftLogger extends i0.GeneratedDatabase { + $DriftLogger(i0.QueryExecutor e) : super(e); + $DriftLoggerManager get managers => $DriftLoggerManager(this); + late final i1.$LogMessageEntityTable logMessageEntity = i1 + .$LogMessageEntityTable(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [logMessageEntity]; + @override + i0.DriftDatabaseOptions get options => + const i0.DriftDatabaseOptions(storeDateTimeAsText: true); +} + +class $DriftLoggerManager { + final $DriftLogger _db; + $DriftLoggerManager(this._db); + i1.$$LogMessageEntityTableTableManager get logMessageEntity => + i1.$$LogMessageEntityTableTableManager(_db, _db.logMessageEntity); +} diff --git a/mobile/lib/infrastructure/repositories/map.repository.dart b/mobile/lib/infrastructure/repositories/map.repository.dart new file mode 100644 index 0000000000..9b8cdcc19d --- /dev/null +++ b/mobile/lib/infrastructure/repositories/map.repository.dart @@ -0,0 +1,66 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/map.model.dart'; +import 'package:immich_mobile/domain/services/map.service.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class DriftMapRepository extends DriftDatabaseRepository { + final Drift _db; + + const DriftMapRepository(super._db) : _db = _db; + + MapQuery remote(String ownerId) => _mapQueryBuilder( + assetFilter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + ); + + MapQuery _mapQueryBuilder({Expression Function($RemoteAssetEntityTable row)? assetFilter}) { + return (markerSource: (bounds) => _watchMapMarker(assetFilter: assetFilter, bounds: bounds)); + } + + Future> _watchMapMarker({ + Expression Function($RemoteAssetEntityTable row)? assetFilter, + LatLngBounds? bounds, + }) async { + final assetId = _db.remoteExifEntity.assetId; + final latitude = _db.remoteExifEntity.latitude; + final longitude = _db.remoteExifEntity.longitude; + + final query = _db.remoteExifEntity.selectOnly() + ..addColumns([assetId, latitude, longitude]) + ..join([innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(assetId), useColumns: false)]) + ..limit(10000); + + if (assetFilter != null) { + query.where(assetFilter(_db.remoteAssetEntity)); + } + + if (bounds != null) { + query.where(_db.remoteExifEntity.inBounds(bounds)); + } else { + query.where(latitude.isNotNull() & longitude.isNotNull()); + } + + final rows = await query.get(); + return List.generate(rows.length, (i) { + final row = rows[i]; + return Marker(assetId: row.read(assetId)!, location: LatLng(row.read(latitude)!, row.read(longitude)!)); + }, growable: false); + } +} + +extension MapBounds on $RemoteExifEntityTable { + Expression inBounds(LatLngBounds bounds) { + final southwest = bounds.southwest; + final northeast = bounds.northeast; + + final latInBounds = latitude.isBetweenValues(southwest.latitude, northeast.latitude); + final longInBounds = southwest.longitude <= northeast.longitude + ? longitude.isBetweenValues(southwest.longitude, northeast.longitude) + : (longitude.isBiggerOrEqualValue(southwest.longitude) | longitude.isSmallerOrEqualValue(northeast.longitude)); + return latInBounds & longInBounds; + } +} diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index ff5f75c2ac..0dcf7200cc 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -13,33 +13,26 @@ class DriftMemoryRepository extends DriftDatabaseRepository { final now = DateTime.now(); final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.ownerId.equals(ownerId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..where( - _db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), - ) - ..where( - _db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc), - ) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + innerJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + innerJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.ownerId.equals(ownerId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc)) + ..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc)) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); + if (rows.isEmpty) { + return const []; + } final Map memoriesMap = {}; @@ -56,7 +49,43 @@ class DriftMemoryRepository extends DriftDatabaseRepository { } } - return memoriesMap.values.toList(); + return memoriesMap.values.toList(growable: false); + } + + Future get(String memoryId) async { + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.id.equals(memoryId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); + + final rows = await query.get(); + + if (rows.isEmpty) { + return null; + } + + final memory = rows.first.readTable(_db.memoryEntity); + final assets = []; + + for (final row in rows) { + final asset = row.readTable(_db.remoteAssetEntity); + assets.add(asset.toDto()); + } + + return memory.toDto().copyWith(assets: assets); + } + + Future getCount() { + return _db.managers.memoryEntity.count(); } } diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart index b3b057b035..b12061ad24 100644 --- a/mobile/lib/infrastructure/repositories/partner.repository.dart +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -9,141 +9,85 @@ class DriftPartnerRepository extends DriftDatabaseRepository { Future> getPartners(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } // Get users who we can share our library with Future> getAvailablePartners(String currentUserId) { - final query = _db.select(_db.userEntity) - ..where((row) => row.id.equals(currentUserId).not()); + final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not()); return query.map((user) { - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: false, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false); }).get(); } // Get users who are sharing their photos WITH the current user Future> getSharedWith(String partnerId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedWithId.equals(partnerId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(partnerId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } // Get users who the current user is sharing their photos TO Future> getSharedBy(String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)), + ])..where(_db.partnerEntity.sharedById.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).get(); } Future> getAllPartnerIds(String userId) async { // Get users who are sharing with me (sharedWithId = userId) - final sharingWithMeQuery = _db.select(_db.partnerEntity) - ..where((tbl) => tbl.sharedWithId.equals(userId)); - final sharingWithMe = - await sharingWithMeQuery.map((row) => row.sharedById).get(); + final sharingWithMeQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedWithId.equals(userId)); + final sharingWithMe = await sharingWithMeQuery.map((row) => row.sharedById).get(); // Get users who I am sharing with (sharedById = userId) - final sharingWithThemQuery = _db.select(_db.partnerEntity) - ..where((tbl) => tbl.sharedById.equals(userId)); - final sharingWithThem = - await sharingWithThemQuery.map((row) => row.sharedWithId).get(); + final sharingWithThemQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedById.equals(userId)); + final sharingWithThem = await sharingWithThemQuery.map((row) => row.sharedWithId).get(); // Combine both lists and remove duplicates - final allPartnerIds = - {...sharingWithMe, ...sharingWithThem}.toList(); + final allPartnerIds = {...sharingWithMe, ...sharingWithThem}.toList(); return allPartnerIds; } Future getPartner(String partnerId, String userId) { final query = _db.select(_db.partnerEntity).join([ - innerJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById), - ), - ]) - ..where( - _db.partnerEntity.sharedById.equals(partnerId) & - _db.partnerEntity.sharedWithId.equals(userId), - ); + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId)); return query.map((row) { final user = row.readTable(_db.userEntity); final partner = row.readTable(_db.partnerEntity); - return PartnerUserDto( - id: user.id, - email: user.email, - name: user.name, - inTimeline: partner.inTimeline, - ); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); }).getSingleOrNull(); } Future toggleShowInTimeline(PartnerUserDto partner, String userId) { return _db.partnerEntity.update().replace( - PartnerEntityCompanion( - sharedById: Value(partner.id), - sharedWithId: Value(userId), - inTimeline: Value(!partner.inTimeline), - ), - ); + PartnerEntityCompanion( + sharedById: Value(partner.id), + sharedWithId: Value(userId), + inTimeline: Value(!partner.inTimeline), + ), + ); } Future create(String partnerId, String userId) { @@ -157,8 +101,6 @@ class DriftPartnerRepository extends DriftDatabaseRepository { } Future delete(String partnerId, String userId) { - return _db.partnerEntity.deleteWhere( - (t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId), - ); + return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId)); } } diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart new file mode 100644 index 0000000000..e2b8646dba --- /dev/null +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPeopleRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPeopleRepository(this._db) : super(_db); + + Future> getAssetPeople(String assetId) async { + final query = _db.select(_db.assetFaceEntity).join([ + innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)), + ])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false)); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future> getAllPeople() async { + final query = + _db.select(_db.personEntity).join([ + leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)), + ]) + ..where(_db.personEntity.isHidden.equals(false)) + ..groupBy([_db.personEntity.id], having: _db.assetFaceEntity.id.count().isBiggerOrEqualValue(3)) + ..orderBy([ + OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc), + OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc), + ]); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future updateName(String personId, String name) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now()))); + } + + Future updateBirthday(String personId, DateTime birthday) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now()))); + } +} + +extension on PersonEntityData { + DriftPerson toDto() { + return DriftPerson( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/person.repository.dart b/mobile/lib/infrastructure/repositories/person.repository.dart deleted file mode 100644 index 859765d63b..0000000000 --- a/mobile/lib/infrastructure/repositories/person.repository.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:immich_mobile/domain/models/person.model.dart'; -import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; - -class DriftPersonRepository extends DriftDatabaseRepository { - final Drift _db; - const DriftPersonRepository(this._db) : super(_db); - - Future> getAll(String userId) { - final query = _db.personEntity.select() - ..where((e) => e.ownerId.equals(userId)); - - return query.map((person) { - return person.toDto(); - }).get(); - } -} - -extension on PersonEntityData { - Person toDto() { - return Person( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - thumbnailPath: thumbnailPath, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ); - } -} diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index c3c4570559..22d4715c1e 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -16,10 +16,8 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { final Drift _db; const DriftRemoteAlbumRepository(this._db) : super(_db); - Future> getAll({ - Set sortBy = const {SortRemoteAlbumsBy.updatedAt}, - }) { - final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); + Future> getAll({Set sortBy = const {SortRemoteAlbumsBy.updatedAt}}) { + final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true); final query = _db.remoteAlbumEntity.select().join([ leftOuterJoin( @@ -32,9 +30,10 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), useColumns: false, ), + leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false), leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + _db.remoteAlbumUserEntity, + _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), useColumns: false, ), ]); @@ -42,36 +41,88 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { ..where(_db.remoteAssetEntity.deletedAt.isNull()) ..addColumns([assetCount]) ..addColumns([_db.userEntity.name]) + ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) ..groupBy([_db.remoteAlbumEntity.id]); if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), - SortRemoteAlbumsBy.updatedAt => - OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), - }, - ); + orderings.add(switch (sort) { + SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), + SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), + }); } query.orderBy(orderings); } return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto( assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, ), ) .get(); } - Future create( - RemoteAlbum album, - List assetIds, - ) async { + Future get(String albumId) { + final assetCount = _db.remoteAlbumAssetEntity.assetId.count(distinct: true); + + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAlbumUserEntity, + _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) + ..addColumns([assetCount]) + ..addColumns([_db.userEntity.name]) + ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) + ..groupBy([_db.remoteAlbumEntity.id]); + + return query + .map( + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto( + assetCount: row.read(assetCount) ?? 0, + ownerName: row.read(_db.userEntity.name)!, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, + ), + ) + .getSingleOrNull(); + } + + Future getByName(String albumName, String ownerId) { + final query = _db.remoteAlbumEntity.select() + ..where((row) => row.name.equals(albumName) & row.ownerId.equals(ownerId)) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(1); + + return query.map((row) => row.toDto(ownerName: '', isShared: false)).getSingleOrNull(); + } + + Future create(RemoteAlbum album, List assetIds) async { await _db.transaction(() async { final entity = RemoteAlbumEntityCompanion( id: Value(album.id), @@ -89,17 +140,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (assetIds.isNotEmpty) { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(album.id), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); } }); @@ -107,39 +152,37 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future update(RemoteAlbum album) async { await _db.remoteAlbumEntity.update().replace( - RemoteAlbumEntityCompanion( - id: Value(album.id), - name: Value(album.name), - ownerId: Value(album.ownerId), - createdAt: Value(album.createdAt), - updatedAt: Value(album.updatedAt), - description: Value(album.description), - thumbnailAssetId: Value(album.thumbnailAssetId), - isActivityEnabled: Value(album.isActivityEnabled), - order: Value(album.order), - ), - ); + RemoteAlbumEntityCompanion( + id: Value(album.id), + name: Value(album.name), + ownerId: Value(album.ownerId), + createdAt: Value(album.createdAt), + updatedAt: Value(album.updatedAt), + description: Value(album.description), + thumbnailAssetId: Value(album.thumbnailAssetId), + isActivityEnabled: Value(album.isActivityEnabled), + order: Value(album.order), + ), + ); } - Future removeAssets(String albumId, List assetIds) { - return _db.remoteAlbumAssetEntity.deleteWhere( - (tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds), - ); + Future removeAssets(String albumId, List assetIds) { + return _db.batch((batch) { + for (final assetId in assetIds) { + batch.deleteWhere( + _db.remoteAlbumAssetEntity, + (row) => row.albumId.equals(albumId) & row.assetId.equals(assetId), + ); + } + }); } FutureOr<(DateTime, DateTime)> getDateRange(String albumId) { final query = _db.remoteAlbumAssetEntity.selectOnly() ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) - ..addColumns([ - _db.remoteAssetEntity.createdAt.min(), - _db.remoteAssetEntity.createdAt.max(), - ]) + ..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()]) ..join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id - .equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), ]); return query.map((row) { @@ -150,9 +193,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { } Future> getSharedUsers(String albumId) async { - final albumUserRows = await (_db.select(_db.remoteAlbumUserEntity) - ..where((row) => row.albumId.equals(albumId))) - .get(); + final albumUserRows = await (_db.select( + _db.remoteAlbumUserEntity, + )..where((row) => row.albumId.equals(albumId))).get(); if (albumUserRows.isEmpty) { return []; @@ -166,17 +209,13 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { id: user.id, email: user.email, name: user.name, - profileImagePath: user.profileImagePath?.isEmpty == true - ? null - : user.profileImagePath, - isAdmin: user.isAdmin, - updatedAt: user.updatedAt, - quotaSizeInBytes: user.quotaSizeInBytes ?? 0, - quotaUsageInBytes: user.quotaUsageInBytes, memoryEnabled: true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, + profileChangedAt: user.profileChangedAt, + hasProfileImage: user.hasProfileImage, + avatarColor: user.avatarColor, ), ) .get(); @@ -184,31 +223,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future> getAssets(String albumId) { final query = _db.remoteAlbumAssetEntity.select().join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), - ]) - ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); - return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) - .get(); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } Future addAssets(String albumId, List assetIds) async { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(albumId), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); return assetIds.length; @@ -224,54 +251,132 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { ); return _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumUserEntity, - albumUsers, - ); + batch.insertAll(_db.remoteAlbumUserEntity, albumUsers); }); } + Future removeUser(String albumId, {required String userId}) { + return _db.remoteAlbumUserEntity.deleteWhere((row) => row.albumId.equals(albumId) & row.userId.equals(userId)); + } + Future deleteAlbum(String albumId) async { return _db.transaction(() async { - await _db.remoteAlbumEntity.deleteWhere( - (table) => table.id.equals(albumId), - ); + await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId)); }); } + Future setActivityStatus(String albumId, bool isEnabled) async { + final query = _db.update(_db.remoteAlbumEntity)..where((row) => row.id.equals(albumId)); + + await query.write(RemoteAlbumEntityCompanion(isActivityEnabled: Value(isEnabled))); + } + Stream watchAlbum(String albumId) { - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId)) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAlbumUserEntity, + _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId)) + ..addColumns([_db.userEntity.name]) + ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) + ..groupBy([_db.remoteAlbumEntity.id]); return query.map((row) { - final album = row.readTable(_db.remoteAlbumEntity).toDto( + final album = row + .readTable(_db.remoteAlbumEntity) + .toDto( ownerName: row.read(_db.userEntity.name)!, + isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, ); + return album; }).watchSingleOrNull(); } + + Future getNewestAssetTimestamp(String albumId) { + final query = _db.remoteAlbumAssetEntity.selectOnly() + ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..addColumns([_db.remoteAssetEntity.localDateTime.max()]) + ..join([ + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ]); + + return query.map((row) => row.read(_db.remoteAssetEntity.localDateTime.max())).getSingleOrNull(); + } + + Future getOldestAssetTimestamp(String albumId) { + final query = _db.remoteAlbumAssetEntity.selectOnly() + ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..addColumns([_db.remoteAssetEntity.localDateTime.min()]) + ..join([ + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ]); + + return query.map((row) => row.read(_db.remoteAssetEntity.localDateTime.min())).getSingleOrNull(); + } + + Future getCount() { + return _db.managers.remoteAlbumEntity.count(); + } + + Future> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async { + // Find remote asset ids that: + // 1. Belong to the provided local album (via local_album_asset_entity) + // 2. Have been uploaded (i.e. a matching remote asset exists for the same checksum & owner) + // 3. Are NOT already in the remote album (remote_album_asset_entity) + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.id]) + ..join([ + innerJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + // Left join remote album assets to exclude those already in the remote album + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id) & + _db.remoteAlbumAssetEntity.albumId.equals(remoteAlbumId), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.localAlbumAssetEntity.albumId.equals(localAlbumId) & + _db.remoteAlbumAssetEntity.assetId.isNull(), // only those not yet linked + ); + + return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get(); + } } extension on RemoteAlbumEntityData { - RemoteAlbum toDto({int assetCount = 0, required String ownerName}) { + RemoteAlbum toDto({int assetCount = 0, required String ownerName, required bool isShared}) { return RemoteAlbum( id: id, name: name, @@ -284,6 +389,7 @@ extension on RemoteAlbumEntityData { order: order, assetCount: assetCount, ownerName: ownerName, + isShared: isShared, ); } } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 52cfe2e7c2..be55c21afc 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -2,8 +2,7 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/models/stack.model.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - hide ExifInfo; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' hide ExifInfo; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; @@ -22,8 +21,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { (row) => _db.remoteAssetEntity.ownerId.equals(userId) & _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), ) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) ..limit(10); @@ -31,62 +29,46 @@ class RemoteAssetRepository extends DriftDatabaseRepository { return query.map((row) => row.toDto()).get(); } - Stream watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]); + SingleOrNullSelectable _assetSelectable(String id) { + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); + }); + } - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); - }).watchSingleOrNull(); + Stream watch(String id) { + return _assetSelectable(id).watchSingleOrNull(); + } + + Future get(String id) { + return _assetSelectable(id).getSingleOrNull(); + } + + Future getByChecksum(String checksum) { + final query = _db.remoteAssetEntity.select()..where((row) => row.checksum.equals(checksum)); + + return query.map((row) => row.toDto()).getSingleOrNull(); } Future> getStackChildren(RemoteAsset asset) { - if (asset.stackId == null) { - return Future.value([]); + final stackId = asset.stackId; + if (stackId == null) { + return Future.value(const []); } final query = _db.remoteAssetEntity.select() - ..where( - (row) => - row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(), - ) + ..where((row) => row.stackId.equals(stackId) & row.id.equals(asset.id).not()) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); return query.map((row) => row.toDto()).get(); @@ -99,34 +81,30 @@ class RemoteAssetRepository extends DriftDatabaseRepository { .getSingleOrNull(); } - Future> getPlaces() { + Future> getPlaces(String userId) { final asset = Subquery( _db.remoteAssetEntity.select() + ..where((row) => row.ownerId.equals(userId)) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), "asset", ); - final query = asset.selectOnly().join([ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId - .equalsExp(asset.ref(_db.remoteAssetEntity.id)), - useColumns: false, - ), - ]) - ..addColumns([ - _db.remoteExifEntity.city, - _db.remoteExifEntity.assetId, - ]) - ..where( - _db.remoteExifEntity.city.isNotNull() & - asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & - asset - .ref(_db.remoteAssetEntity.visibility) - .equals(AssetVisibility.timeline.index), - ) - ..groupBy([_db.remoteExifEntity.city]) - ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); + final query = + asset.selectOnly().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId]) + ..where( + _db.remoteExifEntity.city.isNotNull() & + asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & + asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), + ) + ..groupBy([_db.remoteExifEntity.city]) + ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); return query.map((row) { final assetId = row.read(_db.remoteExifEntity.assetId); @@ -171,8 +149,24 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future restoreTrash(List ids) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + const RemoteAssetEntityCompanion(deletedAt: Value(null)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future delete(List ids) { - return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); + return _db.batch((batch) { + for (final id in ids) { + batch.deleteWhere(_db.remoteAssetEntity, (row) => row.id.equals(id)); + } + }); } Future updateLocation(List ids, LatLng location) { @@ -180,16 +174,30 @@ class RemoteAssetRepository extends DriftDatabaseRepository { for (final id in ids) { batch.update( _db.remoteExifEntity, - RemoteExifEntityCompanion( - latitude: Value(location.latitude), - longitude: Value(location.longitude), - ), + RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)), where: (e) => e.assetId.equals(id), ); } }); } + Future updateDateTime(List ids, DateTime dateTime) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteExifEntity, + RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)), + where: (e) => e.assetId.equals(id), + ); + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(createdAt: Value(dateTime)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity @@ -197,26 +205,21 @@ class RemoteAssetRepository extends DriftDatabaseRepository { .map((row) => row.id) .get(); - await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); + await _db.batch((batch) { + for (final stackId in stackIds) { + batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stackId)); + } + }); await _db.batch((batch) { - final companion = StackEntityCompanion( - ownerId: Value(userId), - primaryAssetId: Value(stack.primaryAssetId), - ); + final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId)); - batch.insert( - _db.stackEntity, - companion.copyWith(id: Value(stack.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion)); for (final assetId in stack.assetIds) { batch.update( _db.remoteAssetEntity, - RemoteAssetEntityCompanion( - stackId: Value(stack.id), - ), + RemoteAssetEntityCompanion(stackId: Value(stack.id)), where: (e) => e.id.equals(assetId), ); } @@ -226,16 +229,32 @@ class RemoteAssetRepository extends DriftDatabaseRepository { Future unStack(List stackIds) { return _db.transaction(() async { - await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); + await _db.batch((batch) { + for (final stackId in stackIds) { + batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stackId)); + } + }); // TODO: delete this after adding foreign key on stackId await _db.batch((batch) { - batch.update( - _db.remoteAssetEntity, - const RemoteAssetEntityCompanion(stackId: Value(null)), - where: (e) => e.stackId.isIn(stackIds), - ); + for (final stackId in stackIds) { + batch.update( + _db.remoteAssetEntity, + const RemoteAssetEntityCompanion(stackId: Value(null)), + where: (e) => e.stackId.equals(stackId), + ); + } }); }); } + + Future updateDescription(String assetId, String description) async { + await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write( + RemoteExifEntityCompanion(description: Value(description)), + ); + } + + Future getCount() { + return _db.managers.remoteAssetEntity.count(); + } } diff --git a/mobile/lib/infrastructure/repositories/search_api.repository.dart b/mobile/lib/infrastructure/repositories/search_api.repository.dart index 55604b885c..dd72333398 100644 --- a/mobile/lib/infrastructure/repositories/search_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/search_api.repository.dart @@ -1,5 +1,4 @@ -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' - hide AssetVisibility; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' hide AssetVisibility; import 'package:immich_mobile/infrastructure/repositories/api.repository.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:openapi/api.dart'; @@ -28,38 +27,29 @@ class SearchApiRepository extends ApiRepository { model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - visibility: filter.display.isArchive - ? AssetVisibility.archive - : AssetVisibility.timeline, + visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), type: type, page: page, - size: 1000, + size: 100, ), ); } return _api.searchAssets( MetadataSearchDto( - originalFileName: filter.filename != null && filter.filename!.isNotEmpty - ? filter.filename - : null, + originalFileName: filter.filename != null && filter.filename!.isNotEmpty ? filter.filename : null, country: filter.location.country, - description: - filter.description != null && filter.description!.isNotEmpty - ? filter.description - : null, + description: filter.description != null && filter.description!.isNotEmpty ? filter.description : null, state: filter.location.state, city: filter.location.city, make: filter.camera.make, model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - visibility: filter.display.isArchive - ? AssetVisibility.archive - : AssetVisibility.timeline, + visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), @@ -76,12 +66,5 @@ class SearchApiRepository extends ApiRepository { String? state, String? make, String? model, - }) => - _api.getSearchSuggestions( - type, - country: country, - state: state, - make: make, - model: model, - ); + }) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model); } diff --git a/mobile/lib/infrastructure/repositories/stack.repository.dart b/mobile/lib/infrastructure/repositories/stack.repository.dart index 7f97f3d9ae..28f7496f97 100644 --- a/mobile/lib/infrastructure/repositories/stack.repository.dart +++ b/mobile/lib/infrastructure/repositories/stack.repository.dart @@ -8,8 +8,7 @@ class DriftStackRepository extends DriftDatabaseRepository { const DriftStackRepository(this._db) : super(_db); Future> getAll(String userId) { - final query = _db.stackEntity.select() - ..where((e) => e.ownerId.equals(userId)); + final query = _db.stackEntity.select()..where((e) => e.ownerId.equals(userId)); return query.map((stack) { return stack.toDto(); @@ -19,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository { extension on StackEntityData { Stack toDto() { - return Stack( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ); + return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId); } } diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 0cf4f20ba8..164fa04529 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -16,6 +16,13 @@ class StorageRepository { file = await entity?.originFile; if (file == null) { log.warning("Cannot get file for asset $assetId"); + return null; + } + + final exists = await file.exists(); + if (!exists) { + log.warning("File for asset $assetId does not exist"); + return null; } } catch (error, stackTrace) { log.warning("Error getting file for asset $assetId", error, stackTrace); @@ -34,6 +41,13 @@ class StorageRepository { log.warning( "Cannot get motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", ); + return null; + } + + final exists = await file.exists(); + if (!exists) { + log.warning("Motion file for asset ${asset.id} does not exist"); + return null; } } catch (error, stackTrace) { log.warning( @@ -66,4 +80,14 @@ class StorageRepository { } return entity; } + + Future clearCache() async { + final log = Logger('StorageRepository'); + + try { + await PhotoManager.clearFileCache(); + } catch (error, stackTrace) { + log.warning("Error clearing cache", error, stackTrace); + } + } } diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 400c8ccf7d..d4e34a02f5 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,16 +1,30 @@ +import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:isar/isar.dart'; -class IsarStoreRepository extends IsarDatabaseRepository { +// Temporary interface until Isar is removed to make the service work with both Isar and Sqlite +abstract class IStoreRepository { + Future deleteAll(); + Stream>> watchAll(); + Future delete(StoreKey key); + Future upsert(StoreKey key, T value); + Future tryGet(StoreKey key); + Stream watch(StoreKey key); + Future>> getAll(); +} + +class IsarStoreRepository extends IsarDatabaseRepository implements IStoreRepository { final Isar _db; final validStoreKeys = StoreKey.values.map((e) => e.id).toSet(); IsarStoreRepository(super.db) : _db = db; + @override Future deleteAll() async { return await transaction(() async { await _db.storeValues.clear(); @@ -18,29 +32,29 @@ class IsarStoreRepository extends IsarDatabaseRepository { }); } - Stream> watchAll() { + @override + Stream>> watchAll() { return _db.storeValues .filter() .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .watch(fireImmediately: true) - .asyncExpand( - (entities) => Stream.fromFutures( - entities.map((e) async => _toUpdateEvent(e)), - ), - ); + .asyncMap((entities) => Future.wait(entities.map((entity) => _toUpdateEvent(entity)))); } + @override Future delete(StoreKey key) async { return await transaction(() async => await _db.storeValues.delete(key.id)); } - Future insert(StoreKey key, T value) async { + @override + Future upsert(StoreKey key, T value) async { return await transaction(() async { await _db.storeValues.put(await _fromValue(key, value)); return true; }); } + @override Future tryGet(StoreKey key) async { final entity = (await _db.storeValues.get(key.id)); if (entity == null) { @@ -49,13 +63,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { return await _toValue(key, entity); } - Future update(StoreKey key, T value) async { - return await transaction(() async { - await _db.storeValues.put(await _fromValue(key, value)); - return true; - }); - } - + @override Stream watch(StoreKey key) async* { yield* _db.storeValues .watchObject(key.id, fireImmediately: true) @@ -63,25 +71,22 @@ class IsarStoreRepository extends IsarDatabaseRepository { } Future> _toUpdateEvent(StoreValue entity) async { - final key = StoreKey.values.firstWhere((e) => e.id == entity.id) - as StoreKey; + final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey; final value = await _toValue(key, entity); return StoreDto(key, value); } Future _toValue(StoreKey key, StoreValue entity) async => switch (key.type) { - const (int) => entity.intValue, - const (String) => entity.strValue, - const (bool) => entity.intValue == 1, - const (DateTime) => entity.intValue == null - ? null - : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), - const (UserDto) => entity.strValue == null - ? null - : await IsarUserRepository(_db).getByUserId(entity.strValue!), - _ => null, - } as T?; + const (int) => entity.intValue, + const (String) => entity.strValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => + entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), + _ => null, + } + as T?; Future _fromValue(StoreKey key, T value) async { final (int? intValue, String? strValue) = switch (key.type) { @@ -89,22 +94,99 @@ class IsarStoreRepository extends IsarDatabaseRepository { const (String) => (null, value as String), const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), - const (UserDto) => ( - null, - (await IsarUserRepository(_db).update(value as UserDto)).id, - ), - _ => throw UnsupportedError( - "Unsupported primitive type: ${key.type} for key: ${key.name}", - ), + const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id), + _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreValue(key.id, intValue: intValue, strValue: strValue); } + @override Future>> getAll() async { - final entities = await _db.storeValues - .filter() - .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) - .findAll(); + final entities = await _db.storeValues.filter().anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)).findAll(); return Future.wait(entities.map((e) => _toUpdateEvent(e)).toList()); } } + +class DriftStoreRepository extends DriftDatabaseRepository implements IStoreRepository { + final Drift _db; + final validStoreKeys = StoreKey.values.map((e) => e.id).toSet(); + + DriftStoreRepository(super.db) : _db = db; + + @override + Future deleteAll() async { + await _db.storeEntity.deleteAll(); + return true; + } + + @override + Future>> getAll() async { + final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys)); + return query.asyncMap((entity) => _toUpdateEvent(entity)).get(); + } + + @override + Stream>> watchAll() { + final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys)); + + return query.asyncMap((entity) => _toUpdateEvent(entity)).watch(); + } + + @override + Future delete(StoreKey key) async { + await _db.storeEntity.deleteWhere((entity) => entity.id.equals(key.id)); + return; + } + + @override + Future upsert(StoreKey key, T value) async { + await _db.storeEntity.insertOnConflictUpdate(await _fromValue(key, value)); + return true; + } + + @override + Future tryGet(StoreKey key) async { + final entity = await _db.managers.storeEntity.filter((entity) => entity.id.equals(key.id)).getSingleOrNull(); + if (entity == null) { + return null; + } + return await _toValue(key, entity); + } + + @override + Stream watch(StoreKey key) async* { + final query = _db.storeEntity.select()..where((entity) => entity.id.equals(key.id)); + + yield* query.watchSingleOrNull().asyncMap((e) async => e == null ? null : await _toValue(key, e)); + } + + Future> _toUpdateEvent(StoreEntityData entity) async { + final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey; + final value = await _toValue(key, entity); + return StoreDto(key, value); + } + + Future _toValue(StoreKey key, StoreEntityData entity) async => + switch (key.type) { + const (int) => entity.intValue, + const (String) => entity.stringValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => + entity.stringValue == null ? null : await DriftAuthUserRepository(_db).get(entity.stringValue!), + _ => null, + } + as T?; + + Future _fromValue(StoreKey key, T value) async { + final (int? intValue, String? strValue) = switch (key.type) { + const (int) => (value as int, null), + const (String) => (null, value as String), + const (bool) => ((value as bool) ? 1 : 0, null), + const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), + const (UserDto) => (null, (await DriftAuthUserRepository(_db).upsert(value as UserDto)).id), + _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), + }; + return StoreEntityCompanion(id: Value(key.id), intValue: Value(intValue), stringValue: Value(strValue)); + } +} diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index 11d58663e0..8bf2e80579 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -3,8 +3,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/sync_event.model.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -19,7 +20,8 @@ class SyncApiRepository { } Future streamChanges( - Function(List, Function() abort) onData, { + Future Function(List, Function() abort, Function() reset) onData, { + Function()? onReset, int batchSize = kSyncEventBatchSize, http.Client? httpClient, }) async { @@ -27,20 +29,19 @@ class SyncApiRepository { final client = httpClient ?? http.Client(); final endpoint = "${_api.apiClient.basePath}/sync/stream"; - final headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/jsonlines+json', - }; + final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'}; final headerParams = {}; await _api.applyToParams([], headerParams); headers.addAll(headerParams); + final shouldReset = Store.get(StoreKey.shouldResetSync, false); final request = http.Request('POST', Uri.parse(endpoint)); request.headers.addAll(headers); request.body = jsonEncode( SyncStreamDto( types: [ + SyncRequestType.authUsersV1, SyncRequestType.usersV1, SyncRequestType.assetsV1, SyncRequestType.assetExifsV1, @@ -58,7 +59,9 @@ class SyncApiRepository { SyncRequestType.partnerStacksV1, SyncRequestType.userMetadataV1, SyncRequestType.peopleV1, + SyncRequestType.assetFacesV1, ], + reset: shouldReset, ).toJson(), ); @@ -72,17 +75,19 @@ class SyncApiRepository { shouldAbort = true; } + final reset = onReset ?? () {}; + try { final response = await client.send(request); if (response.statusCode != 200) { final errorBody = await response.stream.bytesToString(); - throw ApiException( - response.statusCode, - 'Failed to get sync stream: $errorBody', - ); + throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody'); } + // Reset after successful stream start + await Store.put(StoreKey.shouldResetSync, false); + await for (final chunk in response.stream.transform(utf8.decoder)) { if (shouldAbort) { break; @@ -97,23 +102,20 @@ class SyncApiRepository { continue; } - await onData(_parseLines(lines), abort); + await onData(_parseLines(lines), abort, reset); lines.clear(); } if (lines.isNotEmpty && !shouldAbort) { - await onData(_parseLines(lines), abort); + await onData(_parseLines(lines), abort, reset); } } catch (error, stack) { - _logger.severe("Error processing stream", error, stack); return Future.error(error, stack); } finally { client.close(); } stopwatch.stop(); - _logger - .info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); - DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); + _logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); } List _parseLines(List lines) { @@ -138,6 +140,7 @@ class SyncApiRepository { } const _kResponseMap = { + SyncEntityType.authUserV1: SyncAuthUserV1.fromJson, SyncEntityType.userV1: SyncUserV1.fromJson, SyncEntityType.userDeleteV1: SyncUserDeleteV1.fromJson, SyncEntityType.partnerV1: SyncPartnerV1.fromJson, @@ -155,14 +158,17 @@ const _kResponseMap = { SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson, - SyncEntityType.albumAssetV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetCreateV1: SyncAssetV1.fromJson, + SyncEntityType.albumAssetUpdateV1: SyncAssetV1.fromJson, SyncEntityType.albumAssetBackfillV1: SyncAssetV1.fromJson, - SyncEntityType.albumAssetExifV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumAssetExifCreateV1: SyncAssetExifV1.fromJson, + SyncEntityType.albumAssetExifUpdateV1: SyncAssetExifV1.fromJson, SyncEntityType.albumAssetExifBackfillV1: SyncAssetExifV1.fromJson, SyncEntityType.albumToAssetV1: SyncAlbumToAssetV1.fromJson, SyncEntityType.albumToAssetBackfillV1: SyncAlbumToAssetV1.fromJson, SyncEntityType.albumToAssetDeleteV1: SyncAlbumToAssetDeleteV1.fromJson, - SyncEntityType.syncAckV1: _SyncAckV1.fromJson, + SyncEntityType.syncAckV1: _SyncEmptyDto.fromJson, + SyncEntityType.syncResetV1: _SyncEmptyDto.fromJson, SyncEntityType.memoryV1: SyncMemoryV1.fromJson, SyncEntityType.memoryDeleteV1: SyncMemoryDeleteV1.fromJson, SyncEntityType.memoryToAssetV1: SyncMemoryAssetV1.fromJson, @@ -176,8 +182,11 @@ const _kResponseMap = { SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson, SyncEntityType.personV1: SyncPersonV1.fromJson, SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson, + SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson, + SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson, + SyncEntityType.syncCompleteV1: _SyncEmptyDto.fromJson, }; -class _SyncAckV1 { - static _SyncAckV1? fromJson(dynamic _) => _SyncAckV1(); +class _SyncEmptyDto { + static _SyncEmptyDto? fromJson(dynamic _) => _SyncEmptyDto(); } diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index e141c387be..3f74fe25d1 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -1,10 +1,14 @@ import 'dart:convert'; +import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; @@ -19,8 +23,8 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:logging/logging.dart'; -import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole; -import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole; +import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey; +import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole, UserMetadataKey; class SyncStreamRepository extends DriftDatabaseRepository { final Logger _logger = Logger('DriftSyncStreamRepository'); @@ -28,10 +32,73 @@ class SyncStreamRepository extends DriftDatabaseRepository { SyncStreamRepository(super.db) : _db = db; + Future reset() async { + _logger.fine("SyncResetV1 received. Resetting remote entities"); + try { + await _db.exclusively(() async { + // foreign_keys PRAGMA is no-op within transactions + // https://www.sqlite.org/pragma.html#pragma_foreign_keys + await _db.customStatement('PRAGMA foreign_keys = OFF'); + await transaction(() async { + await _db.assetFaceEntity.deleteAll(); + await _db.memoryAssetEntity.deleteAll(); + await _db.memoryEntity.deleteAll(); + await _db.partnerEntity.deleteAll(); + await _db.personEntity.deleteAll(); + await _db.remoteAlbumAssetEntity.deleteAll(); + await _db.remoteAlbumEntity.deleteAll(); + await _db.remoteAlbumUserEntity.deleteAll(); + await _db.remoteAssetEntity.deleteAll(); + await _db.remoteExifEntity.deleteAll(); + await _db.stackEntity.deleteAll(); + await _db.authUserEntity.deleteAll(); + await _db.userEntity.deleteAll(); + await _db.userMetadataEntity.deleteAll(); + }); + await _db.customStatement('PRAGMA foreign_keys = ON'); + }); + } catch (error, stack) { + _logger.severe('Error: SyncResetV1', error, stack); + rethrow; + } + } + + Future updateAuthUsersV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final user in data) { + final companion = AuthUserEntityCompanion( + name: Value(user.name), + email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), + avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary), + isAdmin: Value(user.isAdmin), + pinCode: Value(user.pinCode), + quotaSizeInBytes: Value(user.quotaSizeInBytes ?? 0), + quotaUsageInBytes: Value(user.quotaUsageInBytes), + ); + + batch.insert( + _db.authUserEntity, + companion.copyWith(id: Value(user.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: SyncAuthUserV1', error, stack); + rethrow; + } + } + Future deleteUsersV1(Iterable data) async { try { - await _db.userEntity - .deleteWhere((row) => row.id.isIn(data.map((e) => e.userId))); + await _db.batch((batch) { + for (final user in data) { + batch.deleteWhere(_db.userEntity, (row) => row.id.equals(user.userId)); + } + }); } catch (error, stack) { _logger.severe('Error: SyncUserDeleteV1', error, stack); rethrow; @@ -45,13 +112,12 @@ class SyncStreamRepository extends DriftDatabaseRepository { final companion = UserEntityCompanion( name: Value(user.name), email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), + avatarColor: Value(user.avatarColor?.toAvatarColor() ?? AvatarColor.primary), ); - batch.insert( - _db.userEntity, - companion.copyWith(id: Value(user.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } }); } catch (error, stack) { @@ -66,10 +132,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final partner in data) { batch.delete( _db.partnerEntity, - PartnerEntityCompanion( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), ); } }); @@ -83,15 +146,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final partner in data) { - final companion = - PartnerEntityCompanion(inTimeline: Value(partner.inTimeline)); + final companion = PartnerEntityCompanion(inTimeline: Value(partner.inTimeline)); batch.insert( _db.partnerEntity, - companion.copyWith( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), onConflict: DoUpdate((_) => companion), ); } @@ -102,24 +161,20 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.remoteAssetEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.assetId)), - ); + await _db.batch((batch) { + for (final asset in data) { + batch.deleteWhere(_db.remoteAssetEntity, (row) => row.id.equals(asset.assetId)); + } + }); } catch (error, stack) { _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); rethrow; } } - Future updateAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final asset in data) { @@ -128,8 +183,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { type: Value(asset.type.toAssetType()), createdAt: Value.absentIfNull(asset.fileCreatedAt), updatedAt: Value.absentIfNull(asset.fileModifiedAt), - durationInSeconds: - Value(asset.duration?.toDuration()?.inSeconds ?? 0), + durationInSeconds: Value(asset.duration?.toDuration()?.inSeconds ?? 0), checksum: Value(asset.checksum), isFavorite: Value(asset.isFavorite), ownerId: Value(asset.ownerId), @@ -139,6 +193,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { visibility: Value(asset.visibility.toAssetVisibility()), livePhotoVideoId: Value(asset.livePhotoVideoId), stackId: Value(asset.stackId), + libraryId: Value(asset.libraryId), ); batch.insert( @@ -154,10 +209,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAssetsExifV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsExifV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final exif in data) { @@ -193,20 +245,18 @@ class SyncStreamRepository extends DriftDatabaseRepository { } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAssetsExifV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack); rethrow; } } Future deleteAlbumsV1(Iterable data) async { try { - await _db.remoteAlbumEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.albumId)), - ); + await _db.batch((batch) { + for (final album in data) { + batch.deleteWhere(_db.remoteAlbumEntity, (row) => row.id.equals(album.albumId)); + } + }); } catch (error, stack) { _logger.severe('Error: deleteAlbumsV1', error, stack); rethrow; @@ -247,10 +297,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final album in data) { batch.delete( _db.remoteAlbumUserEntity, - RemoteAlbumUserEntityCompanion( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)), ); } }); @@ -260,49 +307,32 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumUsersV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumUsersV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { - final companion = RemoteAlbumUserEntityCompanion( - role: Value(album.role.toAlbumUserRole()), - ); + final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole())); batch.insert( _db.remoteAlbumUserEntity, - companion.copyWith( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)), onConflict: DoUpdate((_) => companion), ); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumUsersV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack); rethrow; } } - Future deleteAlbumToAssetsV1( - Iterable data, - ) async { + Future deleteAlbumToAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final album in data) { batch.delete( _db.remoteAlbumAssetEntity, - RemoteAlbumAssetEntityCompanion( - albumId: Value(album.albumId), - assetId: Value(album.assetId), - ), + RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)), ); } }); @@ -312,10 +342,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumToAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumToAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { @@ -324,19 +351,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { assetId: Value(album.assetId), ); - batch.insert( - _db.remoteAlbumAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumToAssetsV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack); rethrow; } } @@ -373,9 +392,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { Future deleteMemoriesV1(Iterable data) async { try { - await _db.memoryEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.memoryId)), - ); + await _db.batch((batch) { + for (final memory in data) { + batch.deleteWhere(_db.memoryEntity, (row) => row.id.equals(memory.memoryId)); + } + }); } catch (error, stack) { _logger.severe('Error: deleteMemoriesV1', error, stack); rethrow; @@ -386,16 +407,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final asset in data) { - final companion = MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ); + final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)); - batch.insert( - _db.memoryAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { @@ -404,18 +418,13 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteMemoryAssetsV1( - Iterable data, - ) async { + Future deleteMemoryAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final asset in data) { batch.delete( _db.memoryAssetEntity, - MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ), + MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)), ); } }); @@ -425,10 +434,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final stack in data) { @@ -452,36 +458,28 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.stackEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.stackId)), - ); + await _db.batch((batch) { + for (final stack in data) { + batch.deleteWhere(_db.stackEntity, (row) => row.id.equals(stack.stackId)); + } + }); } catch (error, stack) { _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); rethrow; } } - Future updateUserMetadatasV1( - Iterable data, - ) async { + Future updateUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { - final companion = UserMetadataEntityCompanion( - value: Value(userMetadata.value as Map), - ); + final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map)); batch.insert( _db.userMetadataEntity, - companion.copyWith( - userId: Value(userMetadata.userId), - key: Value(userMetadata.key.toUserMetadataKey()), - ), + companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())), onConflict: DoUpdate((_) => companion), ); } @@ -492,9 +490,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteUserMetadatasV1( - Iterable data, - ) async { + Future deleteUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { @@ -542,77 +538,118 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deletePeopleV1( - Iterable data, - ) async { + Future deletePeopleV1(Iterable data) async { try { - await _db.personEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.personId)), - ); + await _db.batch((batch) { + for (final person in data) { + batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId)); + } + }); } catch (error, stack) { _logger.severe('Error: deletePeopleV1', error, stack); + rethrow; + } + } + + Future updateAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + final companion = AssetFaceEntityCompanion( + assetId: Value(assetFace.assetId), + personId: Value(assetFace.personId), + imageWidth: Value(assetFace.imageWidth), + imageHeight: Value(assetFace.imageHeight), + boundingBoxX1: Value(assetFace.boundingBoxX1), + boundingBoxY1: Value(assetFace.boundingBoxY1), + boundingBoxX2: Value(assetFace.boundingBoxX2), + boundingBoxY2: Value(assetFace.boundingBoxY2), + sourceType: Value(assetFace.sourceType), + ); + + batch.insert( + _db.assetFaceEntity, + companion.copyWith(id: Value(assetFace.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateAssetFacesV1', error, stack); + rethrow; + } + } + + Future deleteAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId)); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAssetFacesV1', error, stack); + rethrow; } } } extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } extension on AssetOrder { AlbumAssetOrder toAlbumAssetOrder() => switch (this) { - AssetOrder.asc => AlbumAssetOrder.asc, - AssetOrder.desc => AlbumAssetOrder.desc, - _ => throw Exception('Unknown AssetOrder value: $this'), - }; + AssetOrder.asc => AlbumAssetOrder.asc, + AssetOrder.desc => AlbumAssetOrder.desc, + _ => throw Exception('Unknown AssetOrder value: $this'), + }; } extension on MemoryType { MemoryTypeEnum toMemoryType() => switch (this) { - MemoryType.onThisDay => MemoryTypeEnum.onThisDay, - _ => throw Exception('Unknown MemoryType value: $this'), - }; + MemoryType.onThisDay => MemoryTypeEnum.onThisDay, + _ => throw Exception('Unknown MemoryType value: $this'), + }; } extension on api.AlbumUserRole { AlbumUserRole toAlbumUserRole() => switch (this) { - api.AlbumUserRole.editor => AlbumUserRole.editor, - api.AlbumUserRole.viewer => AlbumUserRole.viewer, - _ => throw Exception('Unknown AlbumUserRole value: $this'), - }; + api.AlbumUserRole.editor => AlbumUserRole.editor, + api.AlbumUserRole.viewer => AlbumUserRole.viewer, + _ => throw Exception('Unknown AlbumUserRole value: $this'), + }; } extension on api.AssetVisibility { AssetVisibility toAssetVisibility() => switch (this) { - api.AssetVisibility.timeline => AssetVisibility.timeline, - api.AssetVisibility.hidden => AssetVisibility.hidden, - api.AssetVisibility.archive => AssetVisibility.archive, - api.AssetVisibility.locked => AssetVisibility.locked, - _ => throw Exception('Unknown AssetVisibility value: $this'), - }; + api.AssetVisibility.timeline => AssetVisibility.timeline, + api.AssetVisibility.hidden => AssetVisibility.hidden, + api.AssetVisibility.archive => AssetVisibility.archive, + api.AssetVisibility.locked => AssetVisibility.locked, + _ => throw Exception('Unknown AssetVisibility value: $this'), + }; } -extension on String { +extension on api.UserMetadataKey { UserMetadataKey toUserMetadataKey() => switch (this) { - "onboarding" => UserMetadataKey.onboarding, - "preferences" => UserMetadataKey.preferences, - "license" => UserMetadataKey.license, - _ => throw Exception('Unknown UserMetadataKey value: $this'), - }; + api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, + api.UserMetadataKey.preferences => UserMetadataKey.preferences, + api.UserMetadataKey.license => UserMetadataKey.license, + _ => throw Exception('Unknown UserMetadataKey value: $this'), + }; } extension on String { Duration? toDuration() { try { - final parts = split(':') - .map((e) => double.parse(e).toInt()) - .toList(growable: false); + final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false); return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); } catch (_) { @@ -620,3 +657,7 @@ extension on String { } } } + +extension on UserAvatarColor { + AvatarColor? toAvatarColor() => AvatarColor.values.firstWhereOrNull((c) => c.name == value); +} diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 0c3eee59af..05928d938f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -11,6 +11,8 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:stream_transform/stream_transform.dart'; class DriftTimelineRepository extends DriftDatabaseRepository { @@ -21,10 +23,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Stream> watchTimelineUserIds(String userId) { final query = _db.partnerEntity.selectOnly() ..addColumns([_db.partnerEntity.sharedById]) - ..where( - _db.partnerEntity.inTimeline.equals(true) & - _db.partnerEntity.sharedWithId.equals(userId), - ); + ..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId)); return query .map((row) => row.read(_db.partnerEntity.sharedById)!) @@ -34,44 +33,24 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchMainBucket( - userIds, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getMainBucketAssets( - userIds, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy), + assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count), + ); - Stream> _watchMainBucket( - List userIds, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchMainBucket(List userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchMainBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket"); } - return _db.mergedAssetDrift - .mergedBucket(userIds, groupBy: groupBy.index) - .map((row) { - final date = row.bucketDate.dateFmt(groupBy); - return TimeBucket(date: date, assetCount: row.assetCount); - }) - .watch() - .throttle(const Duration(seconds: 3), trailing: true); + return _db.mergedAssetDrift.mergedBucket(userIds: userIds, groupBy: groupBy.index).map((row) { + final date = row.bucketDate.truncateDate(groupBy); + return TimeBucket(date: date, assetCount: row.assetCount); + }).watch(); } - Future> _getMainBucketAssets( - List userIds, { - required int offset, - required int count, - }) { + Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +69,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, @@ -111,21 +89,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchLocalAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getLocalAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchLocalAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.localAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -136,160 +104,132 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final assetCountExp = _db.localAssetEntity.id.count(); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.localAssetEntity.selectOnly().join([ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - ]) - ..addColumns([assetCountExp, dateExp]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); + final query = + _db.localAssetEntity.selectOnly().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([assetCountExp, dateExp]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); } - Future> _getLocalAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) { - final query = _db.localAssetEntity.select().join( - [ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum - .equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ], - ) - ..addColumns([_db.remoteAssetEntity.id]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) { + final query = + _db.localAssetEntity.select().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteAssetEntity.id]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(count, offset: offset); - return query.map((row) { - final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); - }).get(); + return query + .map((row) => row.readTable(_db.localAssetEntity).toDto(remoteId: row.read(_db.remoteAssetEntity.id))) + .get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchRemoteAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getRemoteAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchRemoteAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.remoteAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) .map(_generateBuckets) .watch() - .map((results) => results.isNotEmpty ? results.first : []) - .handleError((error) { - return []; - }); + .map((results) => results.isNotEmpty ? results.first : const []) + .handleError((error) => const []); } - return (_db.remoteAlbumEntity.select() - ..where((row) => row.id.equals(albumId))) + return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) .watch() .switchMap((albums) { - if (albums.isEmpty) { - return Stream.value([]); - } + if (albums.isEmpty) { + return Stream.value(const []); + } - final album = albums.first; - final isAscending = album.order == AlbumAssetOrder.asc; - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + final album = albums.first; + final isAscending = album.order == AlbumAssetOrder.asc; + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..join([ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId - .equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ) - ..groupBy([dateExp]); + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]); - if (isAscending) { - query.orderBy([OrderingTerm.asc(dateExp)]); - } else { - query.orderBy([OrderingTerm.desc(dateExp)]); - } + if (isAscending) { + query.orderBy([OrderingTerm.asc(dateExp)]); + } else { + query.orderBy([OrderingTerm.desc(dateExp)]); + } - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - }).handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + return query.map((row) { + final timeline = row.read(dateExp)!.truncateDate(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + }) + // If there's an error (e.g., album was deleted), return empty buckets + .handleError((error) => const []); } - Future> _getRemoteAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) async { - final albumData = await (_db.remoteAlbumEntity.select() - ..where((row) => row.id.equals(albumId))) - .getSingleOrNull(); + Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { + final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull(); // If album doesn't exist (was deleted), return empty list if (albumData == null) { - return []; + return const []; } final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId - .equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - )..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ); + final query = _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); if (isAscending) { query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); @@ -300,89 +240,71 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.limit(count, offset: offset); return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) .get(); } TimelineQuery fromAssets(List assets) => ( - bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => - Future.value(assets.skip(offset).take(count).toList()), - ); + bucketSource: () => Stream.value(_generateBuckets(assets.length)), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList(growable: false)), + ); - TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(ownerId), - groupBy: groupBy, - ); + TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + groupBy: groupBy, + ); - TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.isFavorite.equals(true) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & + row.isFavorite.equals(true) & + row.ownerId.equals(userId) & + row.visibility.equalsValue(AssetVisibility.timeline), + groupBy: groupBy, + ); - TimelineQuery trash(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + groupBy: groupBy, + joinLocal: true, + ); - TimelineQuery archived(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.ownerId.equals(userId) & - row.visibility.equalsValue(AssetVisibility.archive), - groupBy: groupBy, - ); + TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), + groupBy: groupBy, + ); - TimelineQuery locked(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.locked) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), + groupBy: groupBy, + ); - TimelineQuery video(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.type.equalsValue(AssetType.video) & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery place(String place, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchPlaceBucket( - place, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getPlaceBucketAssets( - place, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy), + assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), + ); - Stream> _watchPlaceBucket( - String place, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), + assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count), + ); + + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchPlaceBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket"); } final assetCountExp = _db.remoteAssetEntity.id.count(); @@ -400,54 +322,193 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..where( _db.remoteExifEntity.city.equals(place) & _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), ) ..groupBy([dateExp]) ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); } - Future> _getPlaceBucketAssets( - String place, { + Future> _getPlaceBucketAssets(String place, {required int offset, required int count}) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteExifEntity.city.equals(place), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + + Stream> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + if (groupBy == GroupAssetsBy.none) { + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.id.count()]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ); + + return query.map((row) { + final count = row.read(_db.remoteAssetEntity.id.count())!; + return _generateBuckets(count); + }).watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.truncateDate(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getPersonBucketAssets( + String userId, + String personId, { required int offset, required int count, }) { - final query = _db.remoteAssetEntity.select().join( - [ + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + + TimelineQuery map(String userId, LatLngBounds bounds, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchMapBucket(userId, bounds, groupBy: groupBy), + assetSource: (offset, count) => _getMapBucketAssets(userId, bounds, offset: offset, count: count), + ); + + Stream> _watchMapBucket( + String userId, + LatLngBounds bounds, { + GroupAssetsBy groupBy = GroupAssetsBy.day, + }) { + if (groupBy == GroupAssetsBy.none) { + // TODO: Support GroupAssetsBy.none + throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket"); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ innerJoin( _db.remoteExifEntity, _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), useColumns: false, ), - ], - ) + ]) ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline) & - _db.remoteExifEntity.city.equals(place), + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteAssetEntity.deletedAt.isNull(), ) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); - return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) - .get(); + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.truncateDate(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); } + Future> _getMapBucketAssets( + String userId, + LatLngBounds bounds, { + required int offset, + required int count, + }) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteExifEntity.inBounds(bounds) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteAssetEntity.deletedAt.isNull(), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + + @pragma('vm:prefer-inline') TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, + bool joinLocal = false, }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), assetSource: (offset, count) => - _getRemoteAssets(filter: filter, offset: offset, count: count), + _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal), ); } @@ -470,33 +531,54 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); + final timeline = row.read(dateExp)!.truncateDate(groupBy); final assetCount = row.read(assetCountExp)!; return TimeBucket(date: timeline, assetCount: assetCount); }).watch(); } + @pragma('vm:prefer-inline') Future> _getRemoteAssets({ required Expression Function($RemoteAssetEntityTable row) filter, required int offset, required int count, + bool joinLocal = false, }) { - final query = _db.remoteAssetEntity.select() - ..where(filter) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + if (joinLocal) { + final query = + _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); - return query.map((row) => row.toDto()).get(); + return query + .map((row) => row.readTable(_db.remoteAssetEntity).toDto(localId: row.read(_db.localAssetEntity.id))) + .get(); + } else { + final query = _db.remoteAssetEntity.select() + ..where(filter) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } } } List _generateBuckets(int count) { - final buckets = List.generate( - (count / kTimelineNoneSegmentSize).floor(), - (_) => const Bucket(assetCount: kTimelineNoneSegmentSize), + final buckets = List.filled( + (count / kTimelineNoneSegmentSize).ceil(), + const Bucket(assetCount: kTimelineNoneSegmentSize), ); if (count % kTimelineNoneSegmentSize != 0) { - buckets.add(Bucket(assetCount: count % kTimelineNoneSegmentSize)); + buckets[buckets.length - 1] = Bucket(assetCount: count % kTimelineNoneSegmentSize); } return buckets; } @@ -507,28 +589,20 @@ extension on Expression { // to create the correct time bucket final localTimeExp = modify(const DateTimeModifier.localTime()); return switch (groupBy) { - GroupAssetsBy.day => localTimeExp.date, + GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date, GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; } } extension on String { - DateTime dateFmt(GroupAssetsBy groupBy) { + DateTime truncateDate(GroupAssetsBy groupBy) { final format = switch (groupBy) { - GroupAssetsBy.day => "y-M-d", + GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.month => "y-M", - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; - try { - return DateFormat(format).parse(this); - } catch (e) { - throw FormatException("Invalid date format: $this", e); - } + return DateFormat(format, 'en').parse(this); } } diff --git a/mobile/lib/infrastructure/repositories/user.repository.dart b/mobile/lib/infrastructure/repositories/user.repository.dart index 4ccfccbdcc..d4eb1ceed6 100644 --- a/mobile/lib/infrastructure/repositories/user.repository.dart +++ b/mobile/lib/infrastructure/repositories/user.repository.dart @@ -1,8 +1,11 @@ +import 'package:drift/drift.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/user_metadata.repository.dart'; import 'package:isar/isar.dart'; class IsarUserRepository extends IsarDatabaseRepository { @@ -64,3 +67,63 @@ class IsarUserRepository extends IsarDatabaseRepository { return true; } } + +class DriftAuthUserRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftAuthUserRepository(super.db) : _db = db; + + Future get(String id) async { + final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull(); + + if (user == null) return null; + + final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id)); + final metadata = await query.map((row) => row.toDto()).get(); + + return user.toDto(metadata); + } + + Future upsert(UserDto user) async { + await _db.authUserEntity.insertOnConflictUpdate( + AuthUserEntityCompanion( + id: Value(user.id), + name: Value(user.name), + email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), + isAdmin: Value(user.isAdmin), + quotaSizeInBytes: Value(user.quotaSizeInBytes), + quotaUsageInBytes: Value(user.quotaUsageInBytes), + avatarColor: Value(user.avatarColor), + ), + ); + return user; + } +} + +extension on AuthUserEntityData { + UserDto toDto([List? metadata]) { + bool memoryEnabled = true; + + if (metadata != null) { + for (final meta in metadata) { + if (meta.key == UserMetadataKey.preferences && meta.preferences != null) { + memoryEnabled = meta.preferences?.memoriesEnabled ?? true; + } + } + } + + return UserDto( + id: id, + email: email, + name: name, + profileChangedAt: profileChangedAt, + hasProfileImage: hasProfileImage, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + isAdmin: isAdmin, + quotaSizeInBytes: quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/user_api.repository.dart b/mobile/lib/infrastructure/repositories/user_api.repository.dart index e990b9d8d1..d21a1b71a6 100644 --- a/mobile/lib/infrastructure/repositories/user_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_api.repository.dart @@ -11,22 +11,14 @@ class UserApiRepository extends ApiRepository { const UserApiRepository(this._api); Future getMyUser() async { - final (adminDto, preferenceDto) = - await (_api.getMyUser(), _api.getMyPreferences()).wait; + final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait; if (adminDto == null) return null; return UserConverter.fromAdminDto(adminDto, preferenceDto); } - Future createProfileImage({ - required String name, - required Uint8List data, - }) async { - final res = await checkNull( - _api.createProfileImage( - MultipartFile.fromBytes('file', data, filename: name), - ), - ); + Future createProfileImage({required String name, required Uint8List data}) async { + final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name))); return res.profileImagePath; } diff --git a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart index 19364afb43..173ec10b97 100644 --- a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart @@ -8,8 +8,7 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { const DriftUserMetadataRepository(this._db) : super(_db); Future> getUserMetadata(String userId) { - final query = _db.userMetadataEntity.select() - ..where((e) => e.userId.equals(userId)); + final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(userId)); return query.map((userMetadata) { return userMetadata.toDto(); @@ -17,22 +16,10 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { } } -extension on UserMetadataEntityData { +extension UserMetadataDataExtension on UserMetadataEntityData { UserMetadata toDto() => switch (key) { - UserMetadataKey.onboarding => UserMetadata( - userId: userId, - key: key, - onboarding: Onboarding.fromMap(value), - ), - UserMetadataKey.preferences => UserMetadata( - userId: userId, - key: key, - preferences: Preferences.fromMap(value), - ), - UserMetadataKey.license => UserMetadata( - userId: userId, - key: key, - license: License.fromMap(value), - ), - }; + UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)), + UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)), + UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)), + }; } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index eb7b24737e..826649b247 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -1,68 +1,64 @@ import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:openapi/api.dart'; // TODO: Move to repository once all classes are refactored abstract final class UserConverter { /// Base user dto used where the complete user object is not required static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + hasProfileImage: dto.profileImagePath.isNotEmpty, + profileChangedAt: dto.profileChangedAt, + avatarColor: dto.avatarColor.toAvatarColor(), + ); - static UserDto fromAdminDto( - UserAdminResponseDto adminDto, [ - UserPreferencesResponseDto? preferenceDto, - ]) => - UserDto( - id: adminDto.id, - email: adminDto.email, - name: adminDto.name, - isAdmin: adminDto.isAdmin, - updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, - avatarColor: adminDto.avatarColor.toAvatarColor(), - memoryEnabled: preferenceDto?.memories.enabled ?? true, - inTimeline: false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, - ); + static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto( + id: adminDto.id, + email: adminDto.email, + name: adminDto.name, + isAdmin: adminDto.isAdmin, + updatedAt: adminDto.updatedAt, + avatarColor: adminDto.avatarColor.toAvatarColor(), + memoryEnabled: preferenceDto?.memories.enabled ?? true, + inTimeline: false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + profileChangedAt: adminDto.profileChangedAt, + hasProfileImage: adminDto.profileImagePath.isNotEmpty, + quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, + quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, + ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - memoryEnabled: false, - inTimeline: dto.inTimeline ?? false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + avatarColor: dto.avatarColor.toAvatarColor(), + memoryEnabled: false, + inTimeline: dto.inTimeline ?? false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + profileChangedAt: dto.profileChangedAt, + hasProfileImage: dto.profileImagePath.isNotEmpty, + ); } extension on UserAvatarColor { AvatarColor toAvatarColor() => switch (this) { - UserAvatarColor.red => AvatarColor.red, - UserAvatarColor.green => AvatarColor.green, - UserAvatarColor.blue => AvatarColor.blue, - UserAvatarColor.purple => AvatarColor.purple, - UserAvatarColor.orange => AvatarColor.orange, - UserAvatarColor.pink => AvatarColor.pink, - UserAvatarColor.amber => AvatarColor.amber, - UserAvatarColor.yellow => AvatarColor.yellow, - UserAvatarColor.gray => AvatarColor.gray, - UserAvatarColor.primary || _ => AvatarColor.primary, - }; + UserAvatarColor.red => AvatarColor.red, + UserAvatarColor.green => AvatarColor.green, + UserAvatarColor.blue => AvatarColor.blue, + UserAvatarColor.purple => AvatarColor.purple, + UserAvatarColor.orange => AvatarColor.orange, + UserAvatarColor.pink => AvatarColor.pink, + UserAvatarColor.amber => AvatarColor.amber, + UserAvatarColor.yellow => AvatarColor.yellow, + UserAvatarColor.gray => AvatarColor.gray, + UserAvatarColor.primary || _ => AvatarColor.primary, + }; } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f036fd9bc3..263a5ef769 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -10,13 +10,20 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; +import 'package:immich_mobile/domain/services/background_worker.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; +import 'package:immich_mobile/generated/intl_keys.g.dart'; +import 'package:immich_mobile/platform/background_worker_lock_api.g.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; @@ -29,7 +36,7 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; -import 'package:immich_mobile/utils/download.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/licenses.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -40,19 +47,21 @@ import 'package:worker_manager/worker_manager.dart'; void main() async { ImmichWidgetsBinding(); - final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + unawaited(BackgroundWorkerLockService(BackgroundWorkerLockApi()).lock()); + final (isar, drift, logDb) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDb); await initApp(); // Warm-up isolate pool for worker manager await workerManager.init(dynamicSpawning: true); - await migrateDatabaseIfNeeded(db); + await migrateDatabaseIfNeeded(isar, drift); HttpSSLOptions.apply(); runApp( ProviderScope( overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + driftProvider.overrideWith(driftOverride(drift)), ], child: const MainWidget(), ), @@ -66,9 +75,9 @@ Future initApp() async { if (kReleaseMode && Platform.isAndroid) { try { await FlutterDisplayMode.setHighRefreshRate(); - debugPrint("Enabled high refresh mode"); + dPrint(() => "Enabled high refresh mode"); } catch (e) { - debugPrint("Error setting high refresh rate: $e"); + dPrint(() => "Error setting high refresh rate: $e"); } } @@ -86,7 +95,6 @@ Future initApp() async { }; PlatformDispatcher.instance.onError = (error, stack) { - debugPrint("FlutterError - Catch all: $error \n $stack"); log.severe('PlatformDispatcher - Catch all', error, stack); return true; }; @@ -94,29 +102,22 @@ Future initApp() async { initializeTimeZones(); // Initialize the file downloader - await FileDownloader().configure( // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 - globalConfig: (Config.holdingQueue, (6, 6, 3)), + + // On Android, if files are larger than 256MB, run in foreground service + globalConfig: [(Config.holdingQueue, (6, 6, 3)), (Config.runInForegroundIfFileLargerThan, 256)], ); - await FileDownloader().trackTasksInGroup( - downloadGroupLivePhoto, - markDownloadedComplete: false, - ); + await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); await FileDownloader().trackTasks(); - LicenseRegistry.addLicense( - () async* { - for (final license in nonPubLicenses.entries) { - yield LicenseEntryWithLineBreaks( - [license.key], - license.value, - ); - } - }, - ); + LicenseRegistry.addLicense(() async* { + for (final license in nonPubLicenses.entries) { + yield LicenseEntryWithLineBreaks([license.key], license.value); + } + }); } class ImmichApp extends ConsumerStatefulWidget { @@ -126,29 +127,28 @@ class ImmichApp extends ConsumerStatefulWidget { ImmichAppState createState() => ImmichAppState(); } -class ImmichAppState extends ConsumerState - with WidgetsBindingObserver { +class ImmichAppState extends ConsumerState with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { case AppLifecycleState.resumed: - debugPrint("[APP STATE] resumed"); + dPrint(() => "[APP STATE] resumed"); ref.read(appStateProvider.notifier).handleAppResume(); break; case AppLifecycleState.inactive: - debugPrint("[APP STATE] inactive"); + dPrint(() => "[APP STATE] inactive"); ref.read(appStateProvider.notifier).handleAppInactivity(); break; case AppLifecycleState.paused: - debugPrint("[APP STATE] paused"); + dPrint(() => "[APP STATE] paused"); ref.read(appStateProvider.notifier).handleAppPause(); break; case AppLifecycleState.detached: - debugPrint("[APP STATE] detached"); + dPrint(() => "[APP STATE] detached"); ref.read(appStateProvider.notifier).handleAppDetached(); break; case AppLifecycleState.hidden: - debugPrint("[APP STATE] hidden"); + dPrint(() => "[APP STATE] hidden"); ref.read(appStateProvider.notifier).handleAppHidden(); break; } @@ -161,71 +161,32 @@ class ImmichAppState extends ConsumerState SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // Sets the navigation bar color - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - ); + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); if (Platform.isAndroid) { // Android 8 does not support transparent app bars final info = await DeviceInfoPlugin().androidInfo; if (info.version.sdkInt <= 26) { - overlayStyle = context.isDarkTheme - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light; + overlayStyle = context.isDarkTheme ? SystemUiOverlayStyle.dark : SystemUiOverlayStyle.light; } } SystemChrome.setSystemUIOverlayStyle(overlayStyle); await ref.read(localNotificationService).setup(); } - void _configureFileDownloaderNotifications() { - FileDownloader().configureNotificationForGroup( - downloadGroupImage, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), - progressBar: true, - ); - - FileDownloader().configureNotificationForGroup( - downloadGroupVideo, - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), - progressBar: true, - ); - } - Future _deepLinkBuilder(PlatformDeepLink deepLink) async { final deepLinkHandler = ref.read(deepLinkServiceProvider); final currentRouteName = ref.read(currentRouteNameProvider.notifier).state; - final isColdStart = - currentRouteName == null || currentRouteName == SplashScreenRoute.name; + final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name; if (deepLink.uri.scheme == "immich") { - final proposedRoute = await deepLinkHandler.handleScheme( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleScheme(deepLink, ref, isColdStart); return proposedRoute; } if (deepLink.uri.host == "my.immich.app") { - final proposedRoute = await deepLinkHandler.handleMyImmichApp( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, ref, isColdStart); return proposedRoute; } @@ -238,17 +199,31 @@ class ImmichAppState extends ConsumerState super.didChangeDependencies(); Intl.defaultLocale = context.locale.toLanguageTag(); WidgetsBinding.instance.addPostFrameCallback((_) { - _configureFileDownloaderNotifications(); + configureFileDownloaderNotifications(); }); } @override initState() { super.initState(); - initApp().then((_) => debugPrint("App Init Completed")); + initApp().then((_) => dPrint(() => "App Init Completed")); WidgetsBinding.instance.addPostFrameCallback((_) { // needs to be delayed so that EasyLocalization is working - ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + if (Store.isBetaTimelineEnabled) { + ref.read(backgroundServiceProvider).disableService(); + ref.read(backgroundWorkerFgServiceProvider).enable(); + if (Platform.isAndroid) { + ref + .read(backgroundWorkerFgServiceProvider) + .saveNotificationMessage( + IntlKeys.uploading_media.t(), + IntlKeys.backup_background_service_default_notification.t(), + ); + } + } else { + ref.read(backgroundWorkerFgServiceProvider).disable(); + ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + } }); ref.read(shareIntentUploadProvider.notifier).init(); @@ -266,9 +241,7 @@ class ImmichAppState extends ConsumerState final immichTheme = ref.watch(immichThemeProvider); return ProviderScope( - overrides: [ - localeProvider.overrideWithValue(context.locale), - ], + overrides: [localeProvider.overrideWithValue(context.locale)], child: MaterialApp.router( title: 'Immich', debugShowCheckedModeBanner: true, @@ -276,18 +249,11 @@ class ImmichAppState extends ConsumerState supportedLocales: context.supportedLocales, locale: context.locale, themeMode: ref.watch(immichThemeModeProvider), - darkTheme: getThemeData( - colorScheme: immichTheme.dark, - locale: context.locale, - ), - theme: getThemeData( - colorScheme: immichTheme.light, - locale: context.locale, - ), + darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale), + theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale), routerConfig: router.config( deepLinkBuilder: _deepLinkBuilder, - navigatorObservers: () => - [AppNavigationObserver(ref: ref), HeroController()], + navigatorObservers: () => [AppNavigationObserver(ref: ref)], ), ), ); diff --git a/mobile/lib/models/activities/activity.model.dart b/mobile/lib/models/activities/activity.model.dart index 17f70d5d62..38c2bef77a 100644 --- a/mobile/lib/models/activities/activity.model.dart +++ b/mobile/lib/models/activities/activity.model.dart @@ -56,12 +56,7 @@ class Activity { @override int get hashCode { - return id.hashCode ^ - assetId.hashCode ^ - comment.hashCode ^ - createdAt.hashCode ^ - type.hashCode ^ - user.hashCode; + return id.hashCode ^ assetId.hashCode ^ comment.hashCode ^ createdAt.hashCode ^ type.hashCode ^ user.hashCode; } } diff --git a/mobile/lib/models/albums/album_add_asset_response.model.dart b/mobile/lib/models/albums/album_add_asset_response.model.dart index 26168c957c..38dd989af5 100644 --- a/mobile/lib/models/albums/album_add_asset_response.model.dart +++ b/mobile/lib/models/albums/album_add_asset_response.model.dart @@ -7,15 +7,9 @@ class AlbumAddAssetsResponse { List alreadyInAlbum; int successfullyAdded; - AlbumAddAssetsResponse({ - required this.alreadyInAlbum, - required this.successfullyAdded, - }); + AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded}); - AlbumAddAssetsResponse copyWith({ - List? alreadyInAlbum, - int? successfullyAdded, - }) { + AlbumAddAssetsResponse copyWith({List? alreadyInAlbum, int? successfullyAdded}) { return AlbumAddAssetsResponse( alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum, successfullyAdded: successfullyAdded ?? this.successfullyAdded, @@ -23,25 +17,20 @@ class AlbumAddAssetsResponse { } Map toMap() { - return { - 'alreadyInAlbum': alreadyInAlbum, - 'successfullyAdded': successfullyAdded, - }; + return {'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded}; } String toJson() => json.encode(toMap()); @override - String toString() => - 'AddAssetsResponse(alreadyInAlbum: $alreadyInAlbum, successfullyAdded: $successfullyAdded)'; + String toString() => 'AddAssetsResponse(alreadyInAlbum: $alreadyInAlbum, successfullyAdded: $successfullyAdded)'; @override bool operator ==(covariant AlbumAddAssetsResponse other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.alreadyInAlbum, alreadyInAlbum) && - other.successfullyAdded == successfullyAdded; + return listEquals(other.alreadyInAlbum, alreadyInAlbum) && other.successfullyAdded == successfullyAdded; } @override diff --git a/mobile/lib/models/albums/album_search.model.dart b/mobile/lib/models/albums/album_search.model.dart index ac4eedbff1..4f69e7e2e1 100644 --- a/mobile/lib/models/albums/album_search.model.dart +++ b/mobile/lib/models/albums/album_search.model.dart @@ -1,5 +1 @@ -enum QuickFilterMode { - all, - sharedWithMe, - myAlbums, -} +enum QuickFilterMode { all, sharedWithMe, myAlbums } diff --git a/mobile/lib/models/albums/album_viewer_page_state.model.dart b/mobile/lib/models/albums/album_viewer_page_state.model.dart index 9fd5da1b28..70427899ae 100644 --- a/mobile/lib/models/albums/album_viewer_page_state.model.dart +++ b/mobile/lib/models/albums/album_viewer_page_state.model.dart @@ -11,11 +11,7 @@ class AlbumViewerPageState { required this.editDescriptionText, }); - AlbumViewerPageState copyWith({ - bool? isEditAlbum, - String? editTitleText, - String? editDescriptionText, - }) { + AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) { return AlbumViewerPageState( isEditAlbum: isEditAlbum ?? this.isEditAlbum, editTitleText: editTitleText ?? this.editTitleText, @@ -43,8 +39,7 @@ class AlbumViewerPageState { String toJson() => json.encode(toMap()); - factory AlbumViewerPageState.fromJson(String source) => - AlbumViewerPageState.fromMap(json.decode(source)); + factory AlbumViewerPageState.fromJson(String source) => AlbumViewerPageState.fromMap(json.decode(source)); @override String toString() => @@ -61,8 +56,5 @@ class AlbumViewerPageState { } @override - int get hashCode => - isEditAlbum.hashCode ^ - editTitleText.hashCode ^ - editDescriptionText.hashCode; + int get hashCode => isEditAlbum.hashCode ^ editTitleText.hashCode ^ editDescriptionText.hashCode; } diff --git a/mobile/lib/models/albums/asset_selection_page_result.model.dart b/mobile/lib/models/albums/asset_selection_page_result.model.dart index 2a4daba64a..cc750f397f 100644 --- a/mobile/lib/models/albums/asset_selection_page_result.model.dart +++ b/mobile/lib/models/albums/asset_selection_page_result.model.dart @@ -4,16 +4,13 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class AssetSelectionPageResult { final Set selectedAssets; - const AssetSelectionPageResult({ - required this.selectedAssets, - }); + const AssetSelectionPageResult({required this.selectedAssets}); @override bool operator ==(Object other) { if (identical(this, other)) return true; final setEquals = const DeepCollectionEquality().equals; - return other is AssetSelectionPageResult && - setEquals(other.selectedAssets, selectedAssets); + return other is AssetSelectionPageResult && setEquals(other.selectedAssets, selectedAssets); } @override diff --git a/mobile/lib/models/asset_selection_state.dart b/mobile/lib/models/asset_selection_state.dart index b080dca003..aded3064ce 100644 --- a/mobile/lib/models/asset_selection_state.dart +++ b/mobile/lib/models/asset_selection_state.dart @@ -13,12 +13,7 @@ class AssetSelectionState { this.selectedCount = 0, }); - AssetSelectionState copyWith({ - bool? hasRemote, - bool? hasLocal, - bool? hasMerged, - int? selectedCount, - }) { + AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) { return AssetSelectionState( hasRemote: hasRemote ?? this.hasRemote, hasLocal: hasLocal ?? this.hasLocal, @@ -28,10 +23,10 @@ class AssetSelectionState { } AssetSelectionState.fromSelection(Set selection) - : hasLocal = selection.any((e) => e.storage == AssetState.local), - hasMerged = selection.any((e) => e.storage == AssetState.merged), - hasRemote = selection.any((e) => e.storage == AssetState.remote), - selectedCount = selection.length; + : hasLocal = selection.any((e) => e.storage == AssetState.local), + hasMerged = selection.any((e) => e.storage == AssetState.merged), + hasRemote = selection.any((e) => e.storage == AssetState.remote), + selectedCount = selection.length; @override String toString() => @@ -48,9 +43,5 @@ class AssetSelectionState { } @override - int get hashCode => - hasRemote.hashCode ^ - hasLocal.hashCode ^ - hasMerged.hashCode ^ - selectedCount.hashCode; + int get hashCode => hasRemote.hashCode ^ hasLocal.hashCode ^ hasMerged.hashCode ^ selectedCount.hashCode; } diff --git a/mobile/lib/models/auth/auxilary_endpoint.model.dart b/mobile/lib/models/auth/auxilary_endpoint.model.dart index f49d0f692c..c7f472e111 100644 --- a/mobile/lib/models/auth/auxilary_endpoint.model.dart +++ b/mobile/lib/models/auth/auxilary_endpoint.model.dart @@ -5,19 +5,10 @@ class AuxilaryEndpoint { final String url; final AuxCheckStatus status; - const AuxilaryEndpoint({ - required this.url, - required this.status, - }); + const AuxilaryEndpoint({required this.url, required this.status}); - AuxilaryEndpoint copyWith({ - String? url, - AuxCheckStatus? status, - }) { - return AuxilaryEndpoint( - url: url ?? this.url, - status: status ?? this.status, - ); + AuxilaryEndpoint copyWith({String? url, AuxCheckStatus? status}) { + return AuxilaryEndpoint(url: url ?? this.url, status: status ?? this.status); } @override @@ -34,10 +25,7 @@ class AuxilaryEndpoint { int get hashCode => url.hashCode ^ status.hashCode; Map toMap() { - return { - 'url': url, - 'status': status.toMap(), - }; + return {'url': url, 'status': status.toMap()}; } factory AuxilaryEndpoint.fromMap(Map map) { @@ -55,9 +43,7 @@ class AuxilaryEndpoint { class AuxCheckStatus { final String name; - const AuxCheckStatus({ - required this.name, - }); + const AuxCheckStatus({required this.name}); const AuxCheckStatus._(this.name); static const loading = AuxCheckStatus._('loading'); @@ -75,30 +61,21 @@ class AuxCheckStatus { @override int get hashCode => name.hashCode; - AuxCheckStatus copyWith({ - String? name, - }) { - return AuxCheckStatus( - name: name ?? this.name, - ); + AuxCheckStatus copyWith({String? name}) { + return AuxCheckStatus(name: name ?? this.name); } Map toMap() { - return { - 'name': name, - }; + return {'name': name}; } factory AuxCheckStatus.fromMap(Map map) { - return AuxCheckStatus( - name: map['name'] as String, - ); + return AuxCheckStatus(name: map['name'] as String); } String toJson() => json.encode(toMap()); - factory AuxCheckStatus.fromJson(String source) => - AuxCheckStatus.fromMap(json.decode(source) as Map); + factory AuxCheckStatus.fromJson(String source) => AuxCheckStatus.fromMap(json.decode(source) as Map); @override String toString() => 'AuxCheckStatus(name: $name)'; diff --git a/mobile/lib/models/auth/biometric_status.model.dart b/mobile/lib/models/auth/biometric_status.model.dart index 3057f06e9c..ad2b06be04 100644 --- a/mobile/lib/models/auth/biometric_status.model.dart +++ b/mobile/lib/models/auth/biometric_status.model.dart @@ -5,19 +5,12 @@ class BiometricStatus { final List availableBiometrics; final bool canAuthenticate; - const BiometricStatus({ - required this.availableBiometrics, - required this.canAuthenticate, - }); + const BiometricStatus({required this.availableBiometrics, required this.canAuthenticate}); @override - String toString() => - 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; + String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; - BiometricStatus copyWith({ - List? availableBiometrics, - bool? canAuthenticate, - }) { + BiometricStatus copyWith({List? availableBiometrics, bool? canAuthenticate}) { return BiometricStatus( availableBiometrics: availableBiometrics ?? this.availableBiometrics, canAuthenticate: canAuthenticate ?? this.canAuthenticate, @@ -29,8 +22,7 @@ class BiometricStatus { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.availableBiometrics, availableBiometrics) && - other.canAuthenticate == canAuthenticate; + return listEquals(other.availableBiometrics, availableBiometrics) && other.canAuthenticate == canAuthenticate; } @override diff --git a/mobile/lib/models/backup/available_album.model.dart b/mobile/lib/models/backup/available_album.model.dart index e34d2b1abe..502d0b66be 100644 --- a/mobile/lib/models/backup/available_album.model.dart +++ b/mobile/lib/models/backup/available_album.model.dart @@ -4,17 +4,9 @@ class AvailableAlbum { final Album album; final int assetCount; final DateTime? lastBackup; - const AvailableAlbum({ - required this.album, - required this.assetCount, - this.lastBackup, - }); + const AvailableAlbum({required this.album, required this.assetCount, this.lastBackup}); - AvailableAlbum copyWith({ - Album? album, - int? assetCount, - DateTime? lastBackup, - }) { + AvailableAlbum copyWith({Album? album, int? assetCount, DateTime? lastBackup}) { return AvailableAlbum( album: album ?? this.album, assetCount: assetCount ?? this.assetCount, @@ -29,8 +21,7 @@ class AvailableAlbum { bool get isAll => album.isAll; @override - String toString() => - 'AvailableAlbum(albumEntity: $album, lastBackup: $lastBackup)'; + String toString() => 'AvailableAlbum(albumEntity: $album, lastBackup: $lastBackup)'; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/backup/backup_state.model.dart b/mobile/lib/models/backup/backup_state.model.dart index d829f411fc..635d925c3f 100644 --- a/mobile/lib/models/backup/backup_state.model.dart +++ b/mobile/lib/models/backup/backup_state.model.dart @@ -8,13 +8,7 @@ import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; -enum BackUpProgressEnum { - idle, - inProgress, - manualInProgress, - inBackground, - done -} +enum BackUpProgressEnum { idle, inProgress, manualInProgress, inBackground, done } class BackUpState { // enum @@ -105,26 +99,21 @@ class BackUpState { progressInFileSize: progressInFileSize ?? this.progressInFileSize, progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, - progressInFileSpeedUpdateTime: - progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, - progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? - this.progressInFileSpeedUpdateSentBytes, - iCloudDownloadProgress: - iCloudDownloadProgress ?? this.iCloudDownloadProgress, + progressInFileSpeedUpdateTime: progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? this.progressInFileSpeedUpdateSentBytes, + iCloudDownloadProgress: iCloudDownloadProgress ?? this.iCloudDownloadProgress, cancelToken: cancelToken ?? this.cancelToken, serverInfo: serverInfo ?? this.serverInfo, autoBackup: autoBackup ?? this.autoBackup, backgroundBackup: backgroundBackup ?? this.backgroundBackup, backupRequireWifi: backupRequireWifi ?? this.backupRequireWifi, - backupRequireCharging: - backupRequireCharging ?? this.backupRequireCharging, + backupRequireCharging: backupRequireCharging ?? this.backupRequireCharging, backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay, availableAlbums: availableAlbums ?? this.availableAlbums, selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums, excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums, allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets, - selectedAlbumsBackupAssetsIds: - selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds, + selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds, currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset, ); } @@ -146,8 +135,7 @@ class BackUpState { other.progressInFileSpeed == progressInFileSpeed && collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && - other.progressInFileSpeedUpdateSentBytes == - progressInFileSpeedUpdateSentBytes && + other.progressInFileSpeedUpdateSentBytes == progressInFileSpeedUpdateSentBytes && other.iCloudDownloadProgress == iCloudDownloadProgress && other.cancelToken == cancelToken && other.serverInfo == serverInfo && @@ -160,10 +148,7 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals( - other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds, - ) && + collectionEquals(other.selectedAlbumsBackupAssetsIds, selectedAlbumsBackupAssetsIds) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/models/backup/current_upload_asset.model.dart b/mobile/lib/models/backup/current_upload_asset.model.dart index edaac06987..2214897357 100644 --- a/mobile/lib/models/backup/current_upload_asset.model.dart +++ b/mobile/lib/models/backup/current_upload_asset.model.dart @@ -53,13 +53,11 @@ class CurrentUploadAsset { factory CurrentUploadAsset.fromMap(Map map) { return CurrentUploadAsset( id: map['id'] as String, - fileCreatedAt: - DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt'] as int), + fileCreatedAt: DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt'] as int), fileName: map['fileName'] as String, fileType: map['fileType'] as String, fileSize: map['fileSize'] as int, - iCloudAsset: - map['iCloudAsset'] != null ? map['iCloudAsset'] as bool : null, + iCloudAsset: map['iCloudAsset'] != null ? map['iCloudAsset'] as bool : null, ); } diff --git a/mobile/lib/models/backup/manual_upload_state.model.dart b/mobile/lib/models/backup/manual_upload_state.model.dart index a2d84fbef3..7f797334de 100644 --- a/mobile/lib/models/backup/manual_upload_state.model.dart +++ b/mobile/lib/models/backup/manual_upload_state.model.dart @@ -56,17 +56,14 @@ class ManualUploadState { progressInFileSize: progressInFileSize ?? this.progressInFileSize, progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, - progressInFileSpeedUpdateTime: - progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, - progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? - this.progressInFileSpeedUpdateSentBytes, + progressInFileSpeedUpdateTime: progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? this.progressInFileSpeedUpdateSentBytes, cancelToken: cancelToken ?? this.cancelToken, currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset, totalAssetsToUpload: totalAssetsToUpload ?? this.totalAssetsToUpload, currentAssetIndex: currentAssetIndex ?? this.currentAssetIndex, successfulUploads: successfulUploads ?? this.successfulUploads, - showDetailedNotification: - showDetailedNotification ?? this.showDetailedNotification, + showDetailedNotification: showDetailedNotification ?? this.showDetailedNotification, ); } @@ -86,8 +83,7 @@ class ManualUploadState { other.progressInFileSpeed == progressInFileSpeed && collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && - other.progressInFileSpeedUpdateSentBytes == - progressInFileSpeedUpdateSentBytes && + other.progressInFileSpeedUpdateSentBytes == progressInFileSpeedUpdateSentBytes && other.cancelToken == cancelToken && other.currentUploadAsset == currentUploadAsset && other.totalAssetsToUpload == totalAssetsToUpload && diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart index 2b39dde5ea..da1e104ba3 100644 --- a/mobile/lib/models/backup/success_upload_asset.model.dart +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -5,17 +5,9 @@ class SuccessUploadAsset { final String remoteAssetId; final bool isDuplicate; - const SuccessUploadAsset({ - required this.candidate, - required this.remoteAssetId, - required this.isDuplicate, - }); + const SuccessUploadAsset({required this.candidate, required this.remoteAssetId, required this.isDuplicate}); - SuccessUploadAsset copyWith({ - BackupCandidate? candidate, - String? remoteAssetId, - bool? isDuplicate, - }) { + SuccessUploadAsset copyWith({BackupCandidate? candidate, String? remoteAssetId, bool? isDuplicate}) { return SuccessUploadAsset( candidate: candidate ?? this.candidate, remoteAssetId: remoteAssetId ?? this.remoteAssetId, @@ -31,12 +23,9 @@ class SuccessUploadAsset { bool operator ==(covariant SuccessUploadAsset other) { if (identical(this, other)) return true; - return other.candidate == candidate && - other.remoteAssetId == remoteAssetId && - other.isDuplicate == isDuplicate; + return other.candidate == candidate && other.remoteAssetId == remoteAssetId && other.isDuplicate == isDuplicate; } @override - int get hashCode => - candidate.hashCode ^ remoteAssetId.hashCode ^ isDuplicate.hashCode; + int get hashCode => candidate.hashCode ^ remoteAssetId.hashCode ^ isDuplicate.hashCode; } diff --git a/mobile/lib/models/cast/cast_manager_state.dart b/mobile/lib/models/cast/cast_manager_state.dart index 703ceb4c47..c948921792 100644 --- a/mobile/lib/models/cast/cast_manager_state.dart +++ b/mobile/lib/models/cast/cast_manager_state.dart @@ -59,8 +59,7 @@ class CastManagerState { String toJson() => json.encode(toMap()); - factory CastManagerState.fromJson(String source) => - CastManagerState.fromMap(json.decode(source)); + factory CastManagerState.fromJson(String source) => CastManagerState.fromMap(json.decode(source)); @override String toString() => @@ -80,9 +79,5 @@ class CastManagerState { @override int get hashCode => - isCasting.hashCode ^ - receiverName.hashCode ^ - castState.hashCode ^ - currentTime.hashCode ^ - duration.hashCode; + isCasting.hashCode ^ receiverName.hashCode ^ castState.hashCode ^ currentTime.hashCode ^ duration.hashCode; } diff --git a/mobile/lib/models/download/download_state.model.dart b/mobile/lib/models/download/download_state.model.dart index c032c73dcd..82d4e31253 100644 --- a/mobile/lib/models/download/download_state.model.dart +++ b/mobile/lib/models/download/download_state.model.dart @@ -10,17 +10,9 @@ class DownloadInfo { // enum final TaskStatus status; - const DownloadInfo({ - required this.fileName, - required this.progress, - required this.status, - }); + const DownloadInfo({required this.fileName, required this.progress, required this.status}); - DownloadInfo copyWith({ - String? fileName, - double? progress, - TaskStatus? status, - }) { + DownloadInfo copyWith({String? fileName, double? progress, TaskStatus? status}) { return DownloadInfo( fileName: fileName ?? this.fileName, progress: progress ?? this.progress, @@ -29,11 +21,7 @@ class DownloadInfo { } Map toMap() { - return { - 'fileName': fileName, - 'progress': progress, - 'status': status.index, - }; + return {'fileName': fileName, 'progress': progress, 'status': status.index}; } factory DownloadInfo.fromMap(Map map) { @@ -46,20 +34,16 @@ class DownloadInfo { String toJson() => json.encode(toMap()); - factory DownloadInfo.fromJson(String source) => - DownloadInfo.fromMap(json.decode(source) as Map); + factory DownloadInfo.fromJson(String source) => DownloadInfo.fromMap(json.decode(source) as Map); @override - String toString() => - 'DownloadInfo(fileName: $fileName, progress: $progress, status: $status)'; + String toString() => 'DownloadInfo(fileName: $fileName, progress: $progress, status: $status)'; @override bool operator ==(covariant DownloadInfo other) { if (identical(this, other)) return true; - return other.fileName == fileName && - other.progress == progress && - other.status == status; + return other.fileName == fileName && other.progress == progress && other.status == status; } @override @@ -71,17 +55,9 @@ class DownloadState { final TaskStatus downloadStatus; final Map taskProgress; final bool showProgress; - const DownloadState({ - required this.downloadStatus, - required this.taskProgress, - required this.showProgress, - }); + const DownloadState({required this.downloadStatus, required this.taskProgress, required this.showProgress}); - DownloadState copyWith({ - TaskStatus? downloadStatus, - Map? taskProgress, - bool? showProgress, - }) { + DownloadState copyWith({TaskStatus? downloadStatus, Map? taskProgress, bool? showProgress}) { return DownloadState( downloadStatus: downloadStatus ?? this.downloadStatus, taskProgress: taskProgress ?? this.taskProgress, @@ -104,6 +80,5 @@ class DownloadState { } @override - int get hashCode => - downloadStatus.hashCode ^ taskProgress.hashCode ^ showProgress.hashCode; + int get hashCode => downloadStatus.hashCode ^ taskProgress.hashCode ^ showProgress.hashCode; } diff --git a/mobile/lib/models/download/livephotos_medatada.model.dart b/mobile/lib/models/download/livephotos_medatada.model.dart index 9c0c7ae4e9..f77a1514ac 100644 --- a/mobile/lib/models/download/livephotos_medatada.model.dart +++ b/mobile/lib/models/download/livephotos_medatada.model.dart @@ -1,43 +1,25 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; -enum LivePhotosPart { - video, - image, -} +enum LivePhotosPart { video, image } class LivePhotosMetadata { // enum LivePhotosPart part; String id; - LivePhotosMetadata({ - required this.part, - required this.id, - }); + LivePhotosMetadata({required this.part, required this.id}); - LivePhotosMetadata copyWith({ - LivePhotosPart? part, - String? id, - }) { - return LivePhotosMetadata( - part: part ?? this.part, - id: id ?? this.id, - ); + LivePhotosMetadata copyWith({LivePhotosPart? part, String? id}) { + return LivePhotosMetadata(part: part ?? this.part, id: id ?? this.id); } Map toMap() { - return { - 'part': part.index, - 'id': id, - }; + return {'part': part.index, 'id': id}; } factory LivePhotosMetadata.fromMap(Map map) { - return LivePhotosMetadata( - part: LivePhotosPart.values[map['part'] as int], - id: map['id'] as String, - ); + return LivePhotosMetadata(part: LivePhotosPart.values[map['part'] as int], id: map['id'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/folder/recursive_folder.model.dart b/mobile/lib/models/folder/recursive_folder.model.dart index 62ec670fed..33ac0f4cb4 100644 --- a/mobile/lib/models/folder/recursive_folder.model.dart +++ b/mobile/lib/models/folder/recursive_folder.model.dart @@ -3,9 +3,5 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; class RecursiveFolder extends RootFolder { final String name; - const RecursiveFolder({ - required this.name, - required super.path, - required super.subfolders, - }); + const RecursiveFolder({required this.name, required super.path, required super.subfolders}); } diff --git a/mobile/lib/models/folder/root_folder.model.dart b/mobile/lib/models/folder/root_folder.model.dart index 567093ecd5..d4b791b915 100644 --- a/mobile/lib/models/folder/root_folder.model.dart +++ b/mobile/lib/models/folder/root_folder.model.dart @@ -4,8 +4,5 @@ class RootFolder { final List subfolders; final String path; - const RootFolder({ - required this.subfolders, - required this.path, - }); + const RootFolder({required this.subfolders, required this.path}); } diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart index 7ab925464a..0f425306ff 100644 --- a/mobile/lib/models/map/map_marker.model.dart +++ b/mobile/lib/models/map/map_marker.model.dart @@ -4,28 +4,16 @@ import 'package:openapi/api.dart'; class MapMarker { final LatLng latLng; final String assetRemoteId; - const MapMarker({ - required this.latLng, - required this.assetRemoteId, - }); + const MapMarker({required this.latLng, required this.assetRemoteId}); - MapMarker copyWith({ - LatLng? latLng, - String? assetRemoteId, - }) { - return MapMarker( - latLng: latLng ?? this.latLng, - assetRemoteId: assetRemoteId ?? this.assetRemoteId, - ); + MapMarker copyWith({LatLng? latLng, String? assetRemoteId}) { + return MapMarker(latLng: latLng ?? this.latLng, assetRemoteId: assetRemoteId ?? this.assetRemoteId); } - MapMarker.fromDto(MapMarkerResponseDto dto) - : latLng = LatLng(dto.lat, dto.lon), - assetRemoteId = dto.id; + MapMarker.fromDto(MapMarkerResponseDto dto) : latLng = LatLng(dto.lat, dto.lon), assetRemoteId = dto.id; @override - String toString() => - 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; + String toString() => 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; @override bool operator ==(covariant MapMarker other) { diff --git a/mobile/lib/models/memories/memory.model.dart b/mobile/lib/models/memories/memory.model.dart index c8451dda64..8a9db5d51b 100644 --- a/mobile/lib/models/memories/memory.model.dart +++ b/mobile/lib/models/memories/memory.model.dart @@ -7,19 +7,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class Memory { final String title; final List assets; - const Memory({ - required this.title, - required this.assets, - }); + const Memory({required this.title, required this.assets}); - Memory copyWith({ - String? title, - List? assets, - }) { - return Memory( - title: title ?? this.title, - assets: assets ?? this.assets, - ); + Memory copyWith({String? title, List? assets}) { + return Memory(title: title ?? this.title, assets: assets ?? this.assets); } @override @@ -30,9 +21,7 @@ class Memory { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return other is Memory && - other.title == title && - listEquals(other.assets, assets); + return other is Memory && other.title == title && listEquals(other.assets, assets); } @override diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart index aff61097e2..6e4a083876 100644 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ b/mobile/lib/models/search/search_curated_content.model.dart @@ -14,30 +14,14 @@ class SearchCuratedContent { /// The id to lookup the asset from the server final String id; - const SearchCuratedContent({ - required this.label, - required this.id, - this.subtitle, - }); + const SearchCuratedContent({required this.label, required this.id, this.subtitle}); - SearchCuratedContent copyWith({ - String? label, - String? subtitle, - String? id, - }) { - return SearchCuratedContent( - label: label ?? this.label, - subtitle: subtitle ?? this.subtitle, - id: id ?? this.id, - ); + SearchCuratedContent copyWith({String? label, String? subtitle, String? id}) { + return SearchCuratedContent(label: label ?? this.label, subtitle: subtitle ?? this.subtitle, id: id ?? this.id); } Map toMap() { - return { - 'label': label, - 'subtitle': subtitle, - 'id': id, - }; + return {'label': label, 'subtitle': subtitle, 'id': id}; } factory SearchCuratedContent.fromMap(Map map) { @@ -54,8 +38,7 @@ class SearchCuratedContent { SearchCuratedContent.fromMap(json.decode(source) as Map); @override - String toString() => - 'CuratedContent(label: $label, subtitle: $subtitle, id: $id)'; + String toString() => 'CuratedContent(label: $label, subtitle: $subtitle, id: $id)'; @override bool operator ==(covariant SearchCuratedContent other) { diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index efe6f923ad..7f27b4d333 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -8,30 +8,14 @@ class SearchLocationFilter { String? country; String? state; String? city; - SearchLocationFilter({ - this.country, - this.state, - this.city, - }); + SearchLocationFilter({this.country, this.state, this.city}); - SearchLocationFilter copyWith({ - String? country, - String? state, - String? city, - }) { - return SearchLocationFilter( - country: country ?? this.country, - state: state ?? this.state, - city: city ?? this.city, - ); + SearchLocationFilter copyWith({String? country, String? state, String? city}) { + return SearchLocationFilter(country: country ?? this.country, state: state ?? this.state, city: city ?? this.city); } Map toMap() { - return { - 'country': country, - 'state': state, - 'city': city, - }; + return {'country': country, 'state': state, 'city': city}; } factory SearchLocationFilter.fromMap(Map map) { @@ -48,16 +32,13 @@ class SearchLocationFilter { SearchLocationFilter.fromMap(json.decode(source) as Map); @override - String toString() => - 'SearchLocationFilter(country: $country, state: $state, city: $city)'; + String toString() => 'SearchLocationFilter(country: $country, state: $state, city: $city)'; @override bool operator ==(covariant SearchLocationFilter other) { if (identical(this, other)) return true; - return other.country == country && - other.state == state && - other.city == city; + return other.country == country && other.state == state && other.city == city; } @override @@ -67,26 +48,14 @@ class SearchLocationFilter { class SearchCameraFilter { String? make; String? model; - SearchCameraFilter({ - this.make, - this.model, - }); + SearchCameraFilter({this.make, this.model}); - SearchCameraFilter copyWith({ - String? make, - String? model, - }) { - return SearchCameraFilter( - make: make ?? this.make, - model: model ?? this.model, - ); + SearchCameraFilter copyWith({String? make, String? model}) { + return SearchCameraFilter(make: make ?? this.make, model: model ?? this.model); } Map toMap() { - return { - 'make': make, - 'model': model, - }; + return {'make': make, 'model': model}; } factory SearchCameraFilter.fromMap(Map map) { @@ -118,19 +87,10 @@ class SearchCameraFilter { class SearchDateFilter { DateTime? takenBefore; DateTime? takenAfter; - SearchDateFilter({ - this.takenBefore, - this.takenAfter, - }); + SearchDateFilter({this.takenBefore, this.takenAfter}); - SearchDateFilter copyWith({ - DateTime? takenBefore, - DateTime? takenAfter, - }) { - return SearchDateFilter( - takenBefore: takenBefore ?? this.takenBefore, - takenAfter: takenAfter ?? this.takenAfter, - ); + SearchDateFilter copyWith({DateTime? takenBefore, DateTime? takenAfter}) { + return SearchDateFilter(takenBefore: takenBefore ?? this.takenBefore, takenAfter: takenAfter ?? this.takenAfter); } Map toMap() { @@ -142,12 +102,8 @@ class SearchDateFilter { factory SearchDateFilter.fromMap(Map map) { return SearchDateFilter( - takenBefore: map['takenBefore'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) - : null, - takenAfter: map['takenAfter'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) - : null, + takenBefore: map['takenBefore'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) : null, + takenAfter: map['takenAfter'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) : null, ); } @@ -157,8 +113,7 @@ class SearchDateFilter { SearchDateFilter.fromMap(json.decode(source) as Map); @override - String toString() => - 'SearchDateFilter(takenBefore: $takenBefore, takenAfter: $takenAfter)'; + String toString() => 'SearchDateFilter(takenBefore: $takenBefore, takenAfter: $takenAfter)'; @override bool operator ==(covariant SearchDateFilter other) { @@ -175,17 +130,9 @@ class SearchDisplayFilters { bool isNotInAlbum = false; bool isArchive = false; bool isFavorite = false; - SearchDisplayFilters({ - required this.isNotInAlbum, - required this.isArchive, - required this.isFavorite, - }); + SearchDisplayFilters({required this.isNotInAlbum, required this.isArchive, required this.isFavorite}); - SearchDisplayFilters copyWith({ - bool? isNotInAlbum, - bool? isArchive, - bool? isFavorite, - }) { + SearchDisplayFilters copyWith({bool? isNotInAlbum, bool? isArchive, bool? isFavorite}) { return SearchDisplayFilters( isNotInAlbum: isNotInAlbum ?? this.isNotInAlbum, isArchive: isArchive ?? this.isArchive, @@ -194,11 +141,7 @@ class SearchDisplayFilters { } Map toMap() { - return { - 'isNotInAlbum': isNotInAlbum, - 'isArchive': isArchive, - 'isFavorite': isFavorite, - }; + return {'isNotInAlbum': isNotInAlbum, 'isArchive': isArchive, 'isFavorite': isFavorite}; } factory SearchDisplayFilters.fromMap(Map map) { @@ -222,14 +165,11 @@ class SearchDisplayFilters { bool operator ==(covariant SearchDisplayFilters other) { if (identical(this, other)) return true; - return other.isNotInAlbum == isNotInAlbum && - other.isArchive == isArchive && - other.isFavorite == isFavorite; + return other.isNotInAlbum == isNotInAlbum && other.isArchive == isArchive && other.isFavorite == isFavorite; } @override - int get hashCode => - isNotInAlbum.hashCode ^ isArchive.hashCode ^ isFavorite.hashCode; + int get hashCode => isNotInAlbum.hashCode ^ isArchive.hashCode ^ isFavorite.hashCode; } class SearchFilter { diff --git a/mobile/lib/models/search/search_result.model.dart b/mobile/lib/models/search/search_result.model.dart index 458a9b4abc..02553869bf 100644 --- a/mobile/lib/models/search/search_result.model.dart +++ b/mobile/lib/models/search/search_result.model.dart @@ -6,19 +6,10 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/models/search/search_result_page_state.model.dart b/mobile/lib/models/search/search_result_page_state.model.dart index 2fd4dcbcd3..7c8a27b50c 100644 --- a/mobile/lib/models/search/search_result_page_state.model.dart +++ b/mobile/lib/models/search/search_result_page_state.model.dart @@ -52,10 +52,6 @@ class SearchResultPageState { @override int get hashCode { - return isLoading.hashCode ^ - isSuccess.hashCode ^ - isError.hashCode ^ - isSmart.hashCode ^ - searchResult.hashCode; + return isLoading.hashCode ^ isSuccess.hashCode ^ isError.hashCode ^ isSmart.hashCode ^ searchResult.hashCode; } } diff --git a/mobile/lib/models/server_info/server_config.model.dart b/mobile/lib/models/server_info/server_config.model.dart index f07ffde522..37b98afadb 100644 --- a/mobile/lib/models/server_info/server_config.model.dart +++ b/mobile/lib/models/server_info/server_config.model.dart @@ -15,11 +15,7 @@ class ServerConfig { required this.mapLightStyleUrl, }); - ServerConfig copyWith({ - int? trashDays, - String? oauthButtonText, - String? externalDomain, - }) { + ServerConfig copyWith({int? trashDays, String? oauthButtonText, String? externalDomain}) { return ServerConfig( trashDays: trashDays ?? this.trashDays, oauthButtonText: oauthButtonText ?? this.oauthButtonText, @@ -34,11 +30,11 @@ class ServerConfig { 'ServerConfig(trashDays: $trashDays, oauthButtonText: $oauthButtonText, externalDomain: $externalDomain)'; ServerConfig.fromDto(ServerConfigDto dto) - : trashDays = dto.trashDays, - oauthButtonText = dto.oauthButtonText, - externalDomain = dto.externalDomain, - mapDarkStyleUrl = dto.mapDarkStyleUrl, - mapLightStyleUrl = dto.mapLightStyleUrl; + : trashDays = dto.trashDays, + oauthButtonText = dto.oauthButtonText, + externalDomain = dto.externalDomain, + mapDarkStyleUrl = dto.mapDarkStyleUrl, + mapLightStyleUrl = dto.mapLightStyleUrl; @override bool operator ==(covariant ServerConfig other) { @@ -50,6 +46,5 @@ class ServerConfig { } @override - int get hashCode => - trashDays.hashCode ^ oauthButtonText.hashCode ^ externalDomain.hashCode; + int get hashCode => trashDays.hashCode ^ oauthButtonText.hashCode ^ externalDomain.hashCode; } diff --git a/mobile/lib/models/server_info/server_disk_info.model.dart b/mobile/lib/models/server_info/server_disk_info.model.dart index 01ce49beec..01042b9f6d 100644 --- a/mobile/lib/models/server_info/server_disk_info.model.dart +++ b/mobile/lib/models/server_info/server_disk_info.model.dart @@ -13,12 +13,7 @@ class ServerDiskInfo { required this.diskUsagePercentage, }); - ServerDiskInfo copyWith({ - String? diskAvailable, - String? diskSize, - String? diskUse, - double? diskUsagePercentage, - }) { + ServerDiskInfo copyWith({String? diskAvailable, String? diskSize, String? diskUse, double? diskUsagePercentage}) { return ServerDiskInfo( diskAvailable: diskAvailable ?? this.diskAvailable, diskSize: diskSize ?? this.diskSize, @@ -33,10 +28,10 @@ class ServerDiskInfo { } ServerDiskInfo.fromDto(ServerStorageResponseDto dto) - : diskAvailable = dto.diskAvailable, - diskSize = dto.diskSize, - diskUse = dto.diskUse, - diskUsagePercentage = dto.diskUsagePercentage; + : diskAvailable = dto.diskAvailable, + diskSize = dto.diskSize, + diskUse = dto.diskUse, + diskUsagePercentage = dto.diskUsagePercentage; @override bool operator ==(Object other) { @@ -51,9 +46,6 @@ class ServerDiskInfo { @override int get hashCode { - return diskAvailable.hashCode ^ - diskSize.hashCode ^ - diskUse.hashCode ^ - diskUsagePercentage.hashCode; + return diskAvailable.hashCode ^ diskSize.hashCode ^ diskUse.hashCode ^ diskUsagePercentage.hashCode; } } diff --git a/mobile/lib/models/server_info/server_features.model.dart b/mobile/lib/models/server_info/server_features.model.dart index fee88869ed..20b9f29619 100644 --- a/mobile/lib/models/server_info/server_features.model.dart +++ b/mobile/lib/models/server_info/server_features.model.dart @@ -13,12 +13,7 @@ class ServerFeatures { required this.passwordLogin, }); - ServerFeatures copyWith({ - bool? trash, - bool? map, - bool? oauthEnabled, - bool? passwordLogin, - }) { + ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin}) { return ServerFeatures( trash: trash ?? this.trash, map: map ?? this.map, @@ -33,10 +28,10 @@ class ServerFeatures { } ServerFeatures.fromDto(ServerFeaturesDto dto) - : trash = dto.trash, - map = dto.map, - oauthEnabled = dto.oauth, - passwordLogin = dto.passwordLogin; + : trash = dto.trash, + map = dto.map, + oauthEnabled = dto.oauth, + passwordLogin = dto.passwordLogin; @override bool operator ==(covariant ServerFeatures other) { @@ -50,9 +45,6 @@ class ServerFeatures { @override int get hashCode { - return trash.hashCode ^ - map.hashCode ^ - oauthEnabled.hashCode ^ - passwordLogin.hashCode; + return trash.hashCode ^ map.hashCode ^ oauthEnabled.hashCode ^ passwordLogin.hashCode; } } diff --git a/mobile/lib/models/server_info/server_info.model.dart b/mobile/lib/models/server_info/server_info.model.dart index aafab56e4c..0fa80d45d8 100644 --- a/mobile/lib/models/server_info/server_info.model.dart +++ b/mobile/lib/models/server_info/server_info.model.dart @@ -41,10 +41,8 @@ class ServerInfo { serverConfig: serverConfig ?? this.serverConfig, serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, - isNewReleaseAvailable: - isNewReleaseAvailable ?? this.isNewReleaseAvailable, - versionMismatchErrorMessage: - versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, + isNewReleaseAvailable: isNewReleaseAvailable ?? this.isNewReleaseAvailable, + versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, ); } diff --git a/mobile/lib/models/server_info/server_version.model.dart b/mobile/lib/models/server_info/server_version.model.dart index 1995edb98d..2cb41b0415 100644 --- a/mobile/lib/models/server_info/server_version.model.dart +++ b/mobile/lib/models/server_info/server_version.model.dart @@ -5,22 +5,10 @@ class ServerVersion { final int minor; final int patch; - const ServerVersion({ - required this.major, - required this.minor, - required this.patch, - }); + const ServerVersion({required this.major, required this.minor, required this.patch}); - ServerVersion copyWith({ - int? major, - int? minor, - int? patch, - }) { - return ServerVersion( - major: major ?? this.major, - minor: minor ?? this.minor, - patch: patch ?? this.patch, - ); + ServerVersion copyWith({int? major, int? minor, int? patch}) { + return ServerVersion(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch); } @override @@ -28,19 +16,13 @@ class ServerVersion { return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; } - ServerVersion.fromDto(ServerVersionResponseDto dto) - : major = dto.major, - minor = dto.minor, - patch = dto.patch_; + ServerVersion.fromDto(ServerVersionResponseDto dto) : major = dto.major, minor = dto.minor, patch = dto.patch_; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is ServerVersion && - other.major == major && - other.minor == minor && - other.patch == patch; + return other is ServerVersion && other.major == major && other.minor == minor && other.patch == patch; } @override diff --git a/mobile/lib/models/shared_link/shared_link.model.dart b/mobile/lib/models/shared_link/shared_link.model.dart index a107dd892a..57a1f441eb 100644 --- a/mobile/lib/models/shared_link/shared_link.model.dart +++ b/mobile/lib/models/shared_link/shared_link.model.dart @@ -58,25 +58,23 @@ class SharedLink { } SharedLink.fromDto(SharedLinkResponseDto dto) - : id = dto.id, - allowDownload = dto.allowDownload, - allowUpload = dto.allowUpload, - description = dto.description, - password = dto.password, - expiresAt = dto.expiresAt, - key = dto.key, - showMetadata = dto.showMetadata, - type = dto.type == SharedLinkType.ALBUM - ? SharedLinkSource.album - : SharedLinkSource.individual, - title = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" - : "INDIVIDUAL SHARE", - thumbAssetId = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumThumbnailAssetId - : dto.assets.isNotEmpty - ? dto.assets[0].id - : null; + : id = dto.id, + allowDownload = dto.allowDownload, + allowUpload = dto.allowUpload, + description = dto.description, + password = dto.password, + expiresAt = dto.expiresAt, + key = dto.key, + showMetadata = dto.showMetadata, + type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, + title = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" + : "INDIVIDUAL SHARE", + thumbAssetId = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumThumbnailAssetId + : dto.assets.isNotEmpty + ? dto.assets[0].id + : null; @override String toString() => diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 7e57cf94d2..ae05e4c492 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -5,21 +5,9 @@ import 'dart:io'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart'; -enum ShareIntentAttachmentType { - image, - video, -} +enum ShareIntentAttachmentType { image, video } -enum UploadStatus { - enqueued, - running, - complete, - notFound, - failed, - canceled, - waitingToRetry, - paused, -} +enum UploadStatus { enqueued, running, complete, notFound, failed, canceled, waitingToRetry, paused } class ShareIntentAttachment { final String path; @@ -91,9 +79,7 @@ class ShareIntentAttachment { String toJson() => json.encode(toMap()); factory ShareIntentAttachment.fromJson(String source) => - ShareIntentAttachment.fromMap( - json.decode(source) as Map, - ); + ShareIntentAttachment.fromMap(json.decode(source) as Map); @override String toString() { diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart index 62406d2e3a..f40ac9ccae 100644 --- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart @@ -14,15 +14,11 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { final Album album; - const AlbumAdditionalSharedUserSelectionPage({ - super.key, - required this.album, - }); + const AlbumAdditionalSharedUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final AsyncValue> suggestedShareUsers = - ref.watch(otherUsersProvider); + final AsyncValue> suggestedShareUsers = ref.watch(otherUsersProvider); final sharedUsersList = useState>({}); addNewUsersHandler() { @@ -31,17 +27,9 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -54,31 +42,19 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -88,31 +64,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -125,9 +85,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -138,21 +96,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: - sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { for (var sharedUsers in album.sharedUsers) { - users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == album.ownerId, - ); + users.removeWhere((u) => u.id == sharedUsers.id || u.id == album.ownerId); } return buildUserList(users); diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index 7b0ce8cdc4..ccc4c44d43 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -13,11 +13,7 @@ import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { - const AlbumAssetSelectionPage({ - super.key, - required this.existingAssets, - this.canDeselect = false, - }); + const AlbumAssetSelectionPage({super.key, required this.existingAssets, this.canDeselect = false}); final Set existingAssets; final bool canDeselect; @@ -52,10 +48,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, ), title: selected.value.isEmpty - ? const Text( - 'add_photos', - style: TextStyle(fontSize: 18), - ).tr() + ? const Text('add_photos', style: TextStyle(fontSize: 18)).tr() : const Text( 'share_assets_selected', style: TextStyle(fontSize: 18), @@ -65,24 +58,17 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { if (selected.value.isNotEmpty || canDeselect) TextButton( onPressed: () { - var payload = - AssetSelectionPageResult(selectedAssets: selected.value); - AutoRouter.of(context) - .popForced(payload); + var payload = AssetSelectionPageResult(selectedAssets: selected.value); + AutoRouter.of(context).popForced(payload); }, child: Text( canDeselect ? "done" : "add", - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), ), ], ), - body: assetSelectionRenderList.widgetWhen( - onData: (data) => buildBody(data), - ), + body: assetSelectionRenderList.widgetWhen(onData: (data) => buildBody(data)), ); } } diff --git a/mobile/lib/pages/album/album_control_button.dart b/mobile/lib/pages/album/album_control_button.dart index c453ace618..578eb839a0 100644 --- a/mobile/lib/pages/album/album_control_button.dart +++ b/mobile/lib/pages/album/album_control_button.dart @@ -7,11 +7,7 @@ class AlbumControlButton extends ConsumerWidget { final void Function()? onAddPhotosPressed; final void Function()? onAddUsersPressed; - const AlbumControlButton({ - super.key, - this.onAddPhotosPressed, - this.onAddUsersPressed, - }); + const AlbumControlButton({super.key, this.onAddPhotosPressed, this.onAddUsersPressed}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/pages/album/album_date_range.dart b/mobile/lib/pages/album/album_date_range.dart index 591be260f6..dbfd9214f1 100644 --- a/mobile/lib/pages/album/album_date_range.dart +++ b/mobile/lib/pages/album/album_date_range.dart @@ -33,25 +33,20 @@ class AlbumDateRange extends ConsumerWidget { padding: const EdgeInsets.only(left: 16.0), child: Text( _getDateRangeText(startDate, endDate), - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceVariant, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant), ), ); } @pragma('vm:prefer-inline') String _getDateRangeText(DateTime startDate, DateTime endDate) { - if (startDate.day == endDate.day && - startDate.month == endDate.month && - startDate.year == endDate.year) { + if (startDate.day == endDate.day && startDate.month == endDate.month && startDate.year == endDate.year) { return DateFormat.yMMMd().format(startDate); } - final String startDateText = (startDate.year == endDate.year - ? DateFormat.MMMd() - : DateFormat.yMMMd()) - .format(startDate); + final String startDateText = (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format( + startDate, + ); final String endDateText = DateFormat.yMMMd().format(endDate); return "$startDateText - $endDateText"; } diff --git a/mobile/lib/pages/album/album_description.dart b/mobile/lib/pages/album/album_description.dart index 37c5beb2c2..383367e8b7 100644 --- a/mobile/lib/pages/album/album_description.dart +++ b/mobile/lib/pages/album/album_description.dart @@ -36,10 +36,7 @@ class AlbumDescription extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumDescription ?? 'add_a_description'.tr(), - style: context.textTheme.bodyLarge, - ), + child: Text(albumDescription ?? 'add_a_description'.tr(), style: context.textTheme.bodyLarge), ); } } diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index f177686128..20d4dbd325 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -7,8 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; @@ -28,8 +27,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return const SizedBox(); } - final sharedUsers = - useState(album.sharedUsers.map((u) => u.toDto()).toList()); + final sharedUsers = useState(album.sharedUsers.map((u) => u.toDto()).toList()); final owner = album.owner.value; final userId = ref.watch(authProvider).userId; final activityEnabled = useState(album.activityEnabled); @@ -50,13 +48,10 @@ class AlbumOptionsPage extends HookConsumerWidget { isProcessing.value = true; try { - final isSuccess = - await ref.read(albumProvider.notifier).leaveAlbum(album); + final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo( - const TabControllerRoute(children: [AlbumsRoute()]), - ); + context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); } else { showErrorMessage(); } @@ -99,8 +94,7 @@ class AlbumOptionsPage extends HookConsumerWidget { actions = [ ListTile( leading: const Icon(Icons.person_remove_rounded), - title: const Text("shared_album_section_people_action_remove_user") - .tr(), + title: const Text("shared_album_section_people_action_remove_user").tr(), onTap: () => removeUserFromAlbum(user), ), ]; @@ -114,10 +108,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return SafeArea( child: Padding( padding: const EdgeInsets.only(top: 24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [...actions], - ), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), ), ); }, @@ -126,23 +117,10 @@ class AlbumOptionsPage extends HookConsumerWidget { buildOwnerInfo() { return ListTile( - leading: owner != null - ? UserCircleAvatar(user: owner.toDto()) - : const SizedBox(), - title: Text( - album.owner.value?.name ?? "", - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - album.owner.value?.email ?? "", - style: TextStyle(color: context.colorScheme.onSurfaceSecondary), - ), - trailing: Text( - "owner", - style: context.textTheme.labelLarge, - ).tr(), + leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(), + title: Text(album.owner.value?.name ?? "", style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(album.owner.value?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).tr(), ); } @@ -154,28 +132,11 @@ class AlbumOptionsPage extends HookConsumerWidget { itemBuilder: (context, index) { final user = sharedUsers.value[index]; return ListTile( - leading: UserCircleAvatar( - user: user, - radius: 22, - ), - title: Text( - user.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - user.email, - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - trailing: userId == user.id || isOwner - ? const Icon(Icons.more_horiz_rounded) - : const SizedBox(), - onTap: userId == user.id || isOwner - ? () => handleUserClick(user) - : null, + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), + onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, ); }, ); @@ -204,26 +165,19 @@ class AlbumOptionsPage extends HookConsumerWidget { value: activityEnabled.value, onChanged: (bool value) async { activityEnabled.value = value; - if (await ref - .read(albumProvider.notifier) - .setActivitystatus(album, value)) { + if (await ref.read(albumProvider.notifier).setActivitystatus(album, value)) { album.activityEnabled = value; } }, - activeColor: activityEnabled.value - ? context.primaryColor - : context.themeData.disabledColor, + activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor, dense: true, title: Text( "comments_and_likes", - style: context.textTheme.titleMedium - ?.copyWith(fontWeight: FontWeight.w500), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), ).tr(), subtitle: Text( "let_others_respond", - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), ), buildSectionTitle("shared_album_section_people_title".tr()), diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart index 723bb1e252..fe1823ec61 100644 --- a/mobile/lib/pages/album/album_shared_user_icons.dart +++ b/mobile/lib/pages/album/album_shared_user_icons.dart @@ -41,11 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), - child: UserCircleAvatar( - user: sharedUsers.value[index], - radius: 18, - size: 36, - ), + child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36), ); }), itemCount: sharedUsers.value.length, diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index d5d963b206..562f02a2ab 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -25,10 +25,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { final suggestedShareUsers = ref.watch(otherUsersProvider); createSharedAlbum() async { - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.watch(albumTitleProvider), - assets, - ); + var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(ref.watch(albumTitleProvider), assets); if (newAlbum != null) { ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); @@ -40,9 +37,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { child: SnackBar( content: Text( 'select_user_for_sharing_page_err_album', - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), ), ); @@ -50,17 +45,9 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -75,11 +62,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { backgroundColor: context.primaryColor.withValues(alpha: 0.15), label: Text( user.email, - style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold), ), ), ), @@ -87,18 +70,12 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: const Text( 'suggestions', - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), ListView.builder( @@ -107,25 +84,14 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: buildTileIcon(users[index]), - title: Text( - users[index].email, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(users[index].email, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -138,10 +104,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - 'invite_to_album', - style: TextStyle(color: context.primaryColor), - ).tr(), + title: Text('invite_to_album', style: TextStyle(color: context.primaryColor)).tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -152,9 +115,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - style: TextButton.styleFrom( - foregroundColor: context.primaryColor, - ), + style: TextButton.styleFrom(foregroundColor: context.primaryColor), onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum, child: const Text( "create_album", diff --git a/mobile/lib/pages/album/album_title.dart b/mobile/lib/pages/album/album_title.dart index ccea200f3a..6c7fc3faaa 100644 --- a/mobile/lib/pages/album/album_title.dart +++ b/mobile/lib/pages/album/album_title.dart @@ -19,32 +19,20 @@ class AlbumTitle extends ConsumerWidget { return const (false, false, ''); } - return ( - album.ownerId == userId, - album.isRemote, - album.name, - ); + return (album.ownerId == userId, album.isRemote, album.name); }), ); if (isOwner && isRemote) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8), - child: AlbumViewerEditableTitle( - albumName: albumName, - titleFocusNode: titleFocusNode, - ), + child: AlbumViewerEditableTitle(albumName: albumName, titleFocusNode: titleFocusNode), ); } return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumName, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), + child: Text(albumName, style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700)), ); } } diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index 2edf6082ac..97853fb96a 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -48,8 +48,7 @@ class AlbumViewer extends HookConsumerWidget { ); Future onRemoveFromAlbumPressed(Iterable assets) async { - final bool isSuccess = - await ref.read(albumProvider.notifier).removeAsset(album, assets); + final bool isSuccess = await ref.read(albumProvider.notifier).removeAsset(album, assets); if (!isSuccess) { ImmichToast.show( @@ -65,21 +64,15 @@ class AlbumViewer extends HookConsumerWidget { /// Find out if the assets in album exist on the device /// If they exist, add to selected asset state to show they are already selected. void onAddPhotosPressed() async { - AssetSelectionPageResult? returnPayload = - await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: album.assets, - canDeselect: false, - ), + AssetSelectionPageResult? returnPayload = await context.pushRoute( + AlbumAssetSelectionRoute(existingAssets: album.assets, canDeselect: false), ); if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) { // Check if there is new assets add isProcessing.value = true; - await ref - .watch(albumProvider.notifier) - .addAssets(album, returnPayload.selectedAssets); + await ref.watch(albumProvider.notifier).addAssets(album, returnPayload.selectedAssets); isProcessing.value = false; } @@ -102,9 +95,7 @@ class AlbumViewer extends HookConsumerWidget { onActivitiesPressed() { if (album.remoteId != null) { ref.read(currentAssetProvider.notifier).set(null); - context.pushRoute( - const ActivitiesRoute(), - ); + context.pushRoute(const ActivitiesRoute()); } } @@ -133,14 +124,8 @@ class AlbumViewer extends HookConsumerWidget { children: [ const SizedBox(height: 32), const AlbumDateRange(), - AlbumTitle( - key: const ValueKey("albumTitle"), - titleFocusNode: titleFocusNode, - ), - AlbumDescription( - key: const ValueKey("albumDescription"), - descriptionFocusNode: descriptionFocusNode, - ), + AlbumTitle(key: const ValueKey("albumTitle"), titleFocusNode: titleFocusNode), + AlbumDescription(key: const ValueKey("albumDescription"), descriptionFocusNode: descriptionFocusNode), const AlbumSharedUserIcons(), if (album.isRemote) Padding( diff --git a/mobile/lib/pages/album/album_viewer.page.dart b/mobile/lib/pages/album/album_viewer.page.dart index 146a93a0a6..c99dacd9b7 100644 --- a/mobile/lib/pages/album/album_viewer.page.dart +++ b/mobile/lib/pages/album/album_viewer.page.dart @@ -21,9 +21,7 @@ class AlbumViewerPage extends HookConsumerWidget { ref.listen(assetSelectionTimelineProvider, (_, __) {}); ref.listen(albumWatcher(albumId), (_, albumFuture) { - albumFuture.whenData( - (value) => ref.read(currentAlbumProvider.notifier).set(value), - ); + albumFuture.whenData((value) => ref.read(currentAlbumProvider.notifier).set(value)); }); return const Scaffold(body: AlbumViewer()); diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index 3fc628afd3..5f155c2f0d 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -26,8 +26,7 @@ class AlbumsPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final albums = - ref.watch(albumProvider).where((album) => album.isRemote).toList(); + final albums = ref.watch(albumProvider).where((album) => album.isRemote).toList(); final albumSortOption = ref.watch(albumSortByOptionsProvider); final albumSortIsReverse = ref.watch(albumSortOrderProvider); final sorted = albumSortOption.sortFn(albums, albumSortIsReverse); @@ -53,21 +52,18 @@ class AlbumsPage extends HookConsumerWidget { filterMode.value = mode; } - useEffect( - () { - searchController.addListener(() { + useEffect(() { + searchController.addListener(() { + onSearch(searchController.text, filterMode.value); + }); + + return () { + searchController.removeListener(() { onSearch(searchController.text, filterMode.value); }); - - return () { - searchController.removeListener(() { - onSearch(searchController.text, filterMode.value); - }); - debounceTimer.value?.cancel(); - }; - }, - [], - ); + debounceTimer.value?.cancel(); + }; + }, []); clearSearch() { filterMode.value = QuickFilterMode.all; @@ -80,13 +76,8 @@ class AlbumsPage extends HookConsumerWidget { showUploadButton: false, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - CreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(CreateAlbumRoute()), ), ], ), @@ -101,13 +92,8 @@ class AlbumsPage extends HookConsumerWidget { children: [ Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -125,14 +111,10 @@ class AlbumsPage extends HookConsumerWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: clearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: clearSearch) : null, controller: searchController, - onChanged: (_) => - onSearch(searchController.text, filterMode.value), + onChanged: (_) => onSearch(searchController.text, filterMode.value), focusNode: searchFocusNode, onTapOutside: (_) => searchFocusNode.unfocus(), ), @@ -155,10 +137,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.sharedWithMe, onTap: () { changeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), QuickFilterButton( @@ -166,10 +145,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.myAlbums, onTap: () { changeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -179,12 +155,7 @@ class AlbumsPage extends HookConsumerWidget { children: [ const SortButton(), IconButton( - icon: Icon( - isGrid.value - ? Icons.view_list_outlined - : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: toggleViewMode, ), ], @@ -196,8 +167,7 @@ class AlbumsPage extends HookConsumerWidget { ? GridView.builder( shrinkWrap: true, physics: const ClampingScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithMaxCrossAxisExtent( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 250, mainAxisSpacing: 12, crossAxisSpacing: 12, @@ -206,9 +176,7 @@ class AlbumsPage extends HookConsumerWidget { itemBuilder: (context, index) { return AlbumThumbnailCard( album: sorted[index], - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), showOwner: true, ); }, @@ -226,46 +194,22 @@ class AlbumsPage extends HookConsumerWidget { sorted[index].name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: sorted[index].ownerId != null ? Text( - '${'items_count'.t( - context: context, - args: { - 'count': sorted[index].assetCount, - }, - )} â€ĸ ${sorted[index].ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': sorted[index].ownerName!, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': sorted[index].assetCount})} â€ĸ ${sorted[index].ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': sorted[index].ownerName!}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, - style: - context.textTheme.bodyMedium?.copyWith( - color: context - .colorScheme.onSurfaceSecondary, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, ), ) : null, - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: ImmichThumbnail( - asset: sorted[index].thumbnail.value, - width: 80, - height: 80, - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: ImmichThumbnail(asset: sorted[index].thumbnail.value, width: 80, height: 80), ), // minVerticalPadding: 1, ), @@ -282,12 +226,7 @@ class AlbumsPage extends HookConsumerWidget { } class QuickFilterButton extends StatelessWidget { - const QuickFilterButton({ - super.key, - required this.isSelected, - required this.onTap, - required this.label, - }); + const QuickFilterButton({super.key, required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -298,27 +237,18 @@ class QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), child: Text( label, style: TextStyle( - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, fontSize: 14, ), ), @@ -338,15 +268,9 @@ class SortButton extends ConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: AlbumSortMode.values @@ -354,47 +278,35 @@ class SortButton extends ConsumerWidget { (mode) => MenuItemButton( leadingIcon: albumSortOption == mode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == mode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == mode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () { final selected = albumSortOption == mode; // Switch direction if (selected) { - ref - .read(albumSortOrderProvider.notifier) - .changeSortDirection(!albumSortIsReverse); + ref.read(albumSortOrderProvider.notifier).changeSortDirection(!albumSortIsReverse); } else { - ref - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(mode); + ref.read(albumSortByOptionsProvider.notifier).changeSortMode(mode); } }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( - albumSortOption == mode - ? context.colorScheme.primary - : Colors.transparent, + albumSortOption == mode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( diff --git a/mobile/lib/pages/backup/album_preview.page.dart b/mobile/lib/pages/backup/album_preview.page.dart index b9fed41305..def31afcd4 100644 --- a/mobile/lib/pages/backup/album_preview.page.dart +++ b/mobile/lib/pages/backup/album_preview.page.dart @@ -19,28 +19,20 @@ class AlbumPreviewPage extends HookConsumerWidget { final assets = useState>([]); getAssetsInAlbum() async { - assets.value = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.localId!); + assets.value = await ref.read(albumMediaRepositoryProvider).getAssets(album.localId!); } - useEffect( - () { - getAssetsInAlbum(); - return null; - }, - [], - ); + useEffect(() { + getAssetsInAlbum(); + return null; + }, []); return Scaffold( appBar: AppBar( elevation: 0, title: Column( children: [ - Text( - album.name, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( @@ -54,10 +46,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), ], ), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_new_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_new_rounded)), ), body: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -67,11 +56,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), itemCount: assets.value.length, itemBuilder: (context, index) { - return ImmichThumbnail( - asset: assets.value[index], - width: 100, - height: 100, - ); + return ImmichThumbnail(asset: assets.value[index], width: 100, height: 100); }, ), ); diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index e51d259969..d222211577 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -19,50 +19,33 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; - final enableSyncUploadAlbum = - useAppSettingsState(AppSettingsEnum.syncAlbums); + final enableSyncUploadAlbum = useAppSettingsState(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; final albums = ref.watch(backupProvider).availableAlbums; - useEffect( - () { - ref.watch(backupProvider.notifier).getBackupInfo(); - return null; - }, - [], - ); + useEffect(() { + ref.watch(backupProvider.notifier).getBackupInfo(); + return null; + }, []); buildAlbumSelectionList() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return AlbumInfoListTile( - album: albums[index], - ); - }), - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return AlbumInfoListTile(album: albums[index]); + }), childCount: albums.length), ), ); } buildAlbumSelectionGrid() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -75,9 +58,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), itemCount: albums.length, itemBuilder: ((context, index) { - return AlbumInfoCard( - album: albums[index], - ); + return AlbumInfoCard(album: albums[index]); }), ), ); @@ -85,8 +66,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { buildSelectedAlbumNameChip() { return selectedBackupAlbums.map((album) { - void removeSelection() => - ref.read(backupProvider.notifier).removeAlbumForBackup(album); + void removeSelection() => ref.read(backupProvider.notifier).removeAlbumForBackup(album); return Padding( padding: const EdgeInsets.only(right: 8.0), @@ -103,10 +83,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -117,9 +94,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { buildExcludedAlbumNameChip() { return excludedBackupAlbums.map((album) { void removeSelection() { - ref - .watch(backupProvider.notifier) - .removeExcludedAlbumForBackup(album); + ref.watch(backupProvider.notifier).removeExcludedAlbumForBackup(album); } return GestureDetector( @@ -129,18 +104,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -159,13 +127,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: const Text( - "backup_album_selection_page_select_albums", - ).tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + title: const Text("backup_album_selection_page_select_albums").tr(), elevation: 0, ), body: CustomScrollView( @@ -176,25 +139,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).tr(), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text("backup_album_selection_page_selection_info", style: context.textTheme.titleSmall).tr(), ), - // Selected Album Chips + // Selected Album Chips Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - ...buildSelectedAlbumNameChip(), - ...buildExcludedAlbumNameChip(), - ], - ), + child: Wrap(children: [...buildSelectedAlbumNameChip(), ...buildExcludedAlbumNameChip()]), ), SettingsSwitchListTile( @@ -202,25 +154,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: "sync_albums".tr(), subtitle: "sync_upload_album_setting_subtitle".tr(), contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), + titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), + subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), onChanged: handleSyncAlbumToggle, ), ListTile( title: Text( "backup_album_selection_page_albums_device".tr( - namedArgs: { - 'count': ref - .watch(backupProvider) - .availableAlbums - .length - .toString(), - }, + namedArgs: {'count': ref.watch(backupProvider).availableAlbums.length.toString()}, ), style: context.textTheme.titleSmall, ), @@ -228,46 +170,30 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), trailing: IconButton( splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), + icon: Icon(Icons.info, size: 20, color: context.primaryColor), onPressed: () { // show the dialog showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), content: SingleChildScrollView( child: ListBody( children: [ const Text( 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), + style: TextStyle(fontSize: 14), ).tr(), ], ), diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index ca094437ac..4f55d00ea0 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -30,59 +30,45 @@ class BackupControllerPage extends HookConsumerWidget { final hasAnyAlbum = backupState.selectedBackupAlbums.isNotEmpty; final didGetBackupInfo = useState(false); - bool hasExclusiveAccess = - backupState.backupProgress != BackUpProgressEnum.inBackground; - bool shouldBackup = backupState.allUniqueAssets.length - - backupState.selectedAlbumsBackupAssetsIds.length == - 0 || + bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground; + bool shouldBackup = + backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || !hasExclusiveAccess ? false : true; - useEffect( - () { - // Update the background settings information just to make sure we - // have the latest, since the platform channel will not update - // automatically - if (Platform.isIOS) { - ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); - } + useEffect(() { + // Update the background settings information just to make sure we + // have the latest, since the platform channel will not update + // automatically + if (Platform.isIOS) { + ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); + } - ref - .watch(websocketProvider.notifier) - .stopListenToEvent('on_upload_success'); + ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); - return () { - WakelockPlus.disable(); - }; - }, - [], - ); + return () { + WakelockPlus.disable(); + }; + }, []); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.idle && - !didGetBackupInfo.value) { - ref.watch(backupProvider.notifier).getBackupInfo(); - didGetBackupInfo.value = true; - } - return null; - }, - [backupState.backupProgress], - ); + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { + ref.watch(backupProvider.notifier).getBackupInfo(); + didGetBackupInfo.value = true; + } + return null; + }, [backupState.backupProgress]); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - WakelockPlus.enable(); - } else { - WakelockPlus.disable(); - } + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.inProgress) { + WakelockPlus.enable(); + } else { + WakelockPlus.disable(); + } - return null; - }, - [backupState.backupProgress], - ); + return null; + }, [backupState.backupProgress]); Widget buildSelectedAlbumName() { var text = "backup_controller_page_backup_selected".tr(); @@ -101,9 +87,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -111,9 +95,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -132,9 +114,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -147,22 +127,14 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Card( shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -170,9 +142,7 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -183,18 +153,11 @@ class BackupControllerPage extends HookConsumerWidget { onPressed: () async { await context.pushRoute(const BackupAlbumSelectionRoute()); // waited until returning from selection - await ref - .read(backupProvider.notifier) - .backupAlbumSelectionDone(); + await ref.read(backupProvider.notifier).backupAlbumSelectionDone(); // waited until backup albums are stored in DB ref.read(albumProvider.notifier).refreshDeviceAlbums(); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -203,21 +166,18 @@ class BackupControllerPage extends HookConsumerWidget { void startBackup() { ref.watch(errorBackupListProvider.notifier).empty(); - if (ref.watch(backupProvider).backupProgress != - BackUpProgressEnum.inBackground) { + if (ref.watch(backupProvider).backupProgress != BackUpProgressEnum.inBackground) { ref.watch(backupProvider.notifier).startBackupProcess(); } } Widget buildBackupButton() { return Padding( - padding: const EdgeInsets.only( - top: 24, - ), + padding: const EdgeInsets.only(top: 24), child: Container( - child: backupState.backupProgress == BackUpProgressEnum.inProgress || - backupState.backupProgress == - BackUpProgressEnum.manualInProgress + child: + backupState.backupProgress == BackUpProgressEnum.inProgress || + backupState.backupProgress == BackUpProgressEnum.manualInProgress ? ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Colors.grey[50], @@ -225,29 +185,19 @@ class BackupControllerPage extends HookConsumerWidget { // padding: const EdgeInsets.all(14), ), onPressed: () { - if (backupState.backupProgress == - BackUpProgressEnum.manualInProgress) { + if (backupState.backupProgress == BackUpProgressEnum.manualInProgress) { ref.read(manualUploadProvider.notifier).cancelBackup(); } else { ref.read(backupProvider.notifier).cancelBackup(); } }, - child: const Text( - "cancel", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("cancel", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ) : ElevatedButton( onPressed: shouldBackup ? startBackup : null, child: const Text( "backup_controller_page_start_backup", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ).tr(), ), ), @@ -255,38 +205,30 @@ class BackupControllerPage extends HookConsumerWidget { } buildBackgroundBackupInfo() { - return const ListTile( - leading: Icon(Icons.info_outline_rounded), - title: Text( - "Background backup is currently running, cannot start manual backup", - ), + return ListTile( + leading: const Icon(Icons.info_outline_rounded), + title: Text('background_backup_running_error'.tr()), ); } buildLoadingIndicator() { return const Padding( padding: EdgeInsets.only(top: 42.0), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( elevation: 0, - title: const Text( - "backup_controller_page_backup", - ).tr(), + title: const Text("backup_controller_page_backup").tr(), leading: IconButton( onPressed: () { ref.watch(websocketProvider.notifier).listenUploadEvent(); context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), actions: [ Padding( @@ -294,9 +236,7 @@ class BackupControllerPage extends HookConsumerWidget { child: IconButton( onPressed: () => context.pushRoute(const BackupOptionsRoute()), splashRadius: 24, - icon: const Icon( - Icons.settings_outlined, - ), + icon: const Icon(Icons.settings_outlined), ), ), ], @@ -336,10 +276,7 @@ class BackupControllerPage extends HookConsumerWidget { if (!hasExclusiveAccess) buildBackgroundBackupInfo(), buildBackupButton(), ] - : [ - buildFolderSelectionTile(), - if (!didGetBackupInfo.value) buildLoadingIndicator(), - ], + : [buildFolderSelectionTile(), if (!didGetBackupInfo.value) buildLoadingIndicator()], ), ), ], diff --git a/mobile/lib/pages/backup/backup_options.page.dart b/mobile/lib/pages/backup/backup_options.page.dart index 29822cab15..846a32a742 100644 --- a/mobile/lib/pages/backup/backup_options.page.dart +++ b/mobile/lib/pages/backup/backup_options.page.dart @@ -15,9 +15,7 @@ class BackupOptionsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BackupSettings(), diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 1b9ec8ad07..2e7c3e946c 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -1,16 +1,26 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/generated/intl_keys.g.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; +import 'package:logging/logging.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; @RoutePage() class DriftBackupPage extends ConsumerStatefulWidget { @@ -21,57 +31,100 @@ class DriftBackupPage extends ConsumerStatefulWidget { } class _DriftBackupPageState extends ConsumerState { + bool? syncSuccess; + @override void initState() { super.initState(); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + + WakelockPlus.enable(); + + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + + ref.read(driftBackupProvider.notifier).updateSyncing(true); + syncSuccess = await ref.read(backgroundSyncProvider).syncRemote(); + ref.read(driftBackupProvider.notifier).updateSyncing(false); + + if (mounted) { + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + } + }); } - Future startBackup() async { - await ref.read(driftBackupProvider.notifier).getBackupStatus(); - await ref.read(driftBackupProvider.notifier).backup(); - } - - Future stopBackup() async { - await ref.read(driftBackupProvider.notifier).cancel(); + @override + dispose() { + super.dispose(); + WakelockPlus.disable(); } @override Widget build(BuildContext context) { final selectedAlbum = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + + final error = ref.watch(driftBackupProvider.select((p) => p.error)); + + final backupNotifier = ref.read(driftBackupProvider.notifier); + final backupSyncManager = ref.read(backgroundSyncProvider); + + Future startBackup() async { + final currentUser = Store.tryGet(StoreKey.currentUser); + if (currentUser == null) { + return; + } + + if (syncSuccess == null) { + ref.read(driftBackupProvider.notifier).updateSyncing(true); + syncSuccess = await backupSyncManager.syncRemote(); + ref.read(driftBackupProvider.notifier).updateSyncing(false); + } + + await backupNotifier.getBackupStatus(currentUser.id); + + if (syncSuccess == false) { + Logger("DriftBackupPage").warning("Remote sync did not complete successfully, skipping backup"); + return; + } + await backupNotifier.startBackup(currentUser.id); + } + + Future stopBackup() async { + await backupNotifier.cancel(); + } return Scaffold( appBar: AppBar( elevation: 0, - title: Text( - "backup_controller_page_backup".t(), - ), + title: Text("backup_controller_page_backup".t()), leading: IconButton( onPressed: () { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), + actions: [ + IconButton( + onPressed: () { + context.pushRoute(const DriftBackupOptionsRoute()); + }, + icon: const Icon(Icons.settings_outlined), + tooltip: "backup_options".t(context: context), + ), + ], ), body: Stack( children: [ Padding( - padding: const EdgeInsets.only( - left: 16.0, - right: 16, - bottom: 32, - ), + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), child: ListView( children: [ const SizedBox(height: 8), @@ -83,16 +136,36 @@ class _DriftBackupPageState extends ConsumerState { const Divider(), BackupToggleButton( onStart: () async => await startBackup(), - onStop: () async => await stopBackup(), + onStop: () async { + syncSuccess = null; + await stopBackup(); + }, ), - if (uploadItems.isNotEmpty) - TextButton.icon( - icon: const Icon(Icons.info_outline_rounded), - onPressed: () => context.pushRoute( - const DriftUploadDetailRoute(), + switch (error) { + BackupError.none => const SizedBox.shrink(), + BackupError.syncFailed => Padding( + padding: const EdgeInsets.only(top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Icon(Icons.warning_rounded, color: context.colorScheme.error, fill: 1), + const SizedBox(width: 8), + Text( + IntlKeys.backup_error_sync_failed.t(), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.error), + textAlign: TextAlign.center, + ), + ], ), - label: Text("view_details".t(context: context)), ), + }, + TextButton.icon( + icon: const Icon(Icons.info_outline_rounded), + onPressed: () => context.pushRoute(const DriftUploadDetailRoute()), + label: Text("view_details".t(context: context)), + ), ], ], ), @@ -112,9 +185,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_backup_selected".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.selected, - ) + .where((album) => album.backupSelection == BackupSelection.selected) .toList(); if (albums.isNotEmpty) { @@ -130,9 +201,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -140,9 +209,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -152,9 +219,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { String text = "backup_controller_page_excluded".tr(); final albums = ref .watch(backupAlbumProvider) - .where( - (album) => album.backupSelection == BackupSelection.excluded, - ) + .where((album) => album.backupSelection == BackupSelection.excluded) .toList(); if (albums.isNotEmpty) { @@ -166,9 +231,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -179,19 +242,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { return Card( shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(20)), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -199,9 +256,7 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -211,14 +266,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { trailing: ElevatedButton( onPressed: () async { await context.pushRoute(const DriftBackupAlbumSelectionRoute()); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ); @@ -230,8 +284,7 @@ class _TotalCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final totalCount = - ref.watch(driftBackupProvider.select((p) => p.totalCount)); + final totalCount = ref.watch(driftBackupProvider.select((p) => p.totalCount)); return BackupInfoCard( title: "total".tr(), @@ -246,13 +299,14 @@ class _BackupCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final backupCount = - ref.watch(driftBackupProvider.select((p) => p.backupCount)); + final backupCount = ref.watch(driftBackupProvider.select((p) => p.backupCount)); + final syncStatus = ref.watch(syncStatusProvider); return BackupInfoCard( title: "backup_controller_page_backup".tr(), subtitle: "backup_controller_page_backup_sub".tr(), info: backupCount.toString(), + isLoading: syncStatus.isRemoteSyncing, ); } } @@ -262,12 +316,208 @@ class _RemainderCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final remainderCount = - ref.watch(driftBackupProvider.select((p) => p.remainderCount)); - return BackupInfoCard( - title: "backup_controller_page_remainder".tr(), - subtitle: "backup_controller_page_remainder_sub".tr(), - info: remainderCount.toString(), + final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount)); + final syncStatus = ref.watch(syncStatusProvider); + + return Card( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), + ), + elevation: 0, + borderOnForeground: false, + child: Column( + children: [ + ListTile( + minVerticalPadding: 18, + isThreeLine: true, + title: Text("backup_controller_page_remainder".t(context: context), style: context.textTheme.titleMedium), + subtitle: Padding( + padding: const EdgeInsets.only(top: 4.0, right: 18.0), + child: Text( + "backup_controller_page_remainder_sub".t(context: context), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + ), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Stack( + children: [ + Text( + remainderCount.toString(), + style: context.textTheme.titleLarge?.copyWith( + color: context.colorScheme.onSurface.withAlpha(syncStatus.isRemoteSyncing ? 50 : 255), + ), + ), + if (syncStatus.isRemoteSyncing) + Positioned.fill( + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + color: context.colorScheme.onSurface.withAlpha(150), + ), + ), + ), + ), + ], + ), + Text( + "backup_info_card_assets", + style: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.onSurface.withAlpha(syncStatus.isRemoteSyncing ? 50 : 255), + ), + ).tr(), + ], + ), + ), + const Divider(height: 0), + const _PreparingStatus(), + const Divider(height: 0), + + ListTile( + enableFeedback: true, + visualDensity: VisualDensity.compact, + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + ), + onTap: () => context.pushRoute(const DriftBackupAssetDetailRoute()), + title: Text( + "view_details".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + ), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: context.colorScheme.onSurfaceVariant), + ), + ], + ), + ); + } +} + +class _PreparingStatus extends ConsumerStatefulWidget { + const _PreparingStatus(); + + @override + _PreparingStatusState createState() => _PreparingStatusState(); +} + +class _PreparingStatusState extends ConsumerState { + Timer? _pollingTimer; + + @override + void dispose() { + _pollingTimer?.cancel(); + super.dispose(); + } + + void _startPollingIfNeeded() { + if (_pollingTimer != null) return; + + _pollingTimer = Timer.periodic(const Duration(seconds: 3), (timer) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser != null && mounted) { + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + + // Stop polling if processing count reaches 0 + final updatedProcessingCount = ref.read(driftBackupProvider.select((p) => p.processingCount)); + if (updatedProcessingCount == 0) { + timer.cancel(); + _pollingTimer = null; + } + } else { + timer.cancel(); + _pollingTimer = null; + } + }); + } + + @override + Widget build(BuildContext context) { + final syncStatus = ref.watch(syncStatusProvider); + final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount)); + final processingCount = ref.watch(driftBackupProvider.select((p) => p.processingCount)); + final readyForUploadCount = remainderCount - processingCount; + + ref.listen(driftBackupProvider.select((p) => p.processingCount), (previous, next) { + if (next > 0 && _pollingTimer == null) { + _startPollingIfNeeded(); + } else if (next == 0 && _pollingTimer != null) { + _pollingTimer?.cancel(); + _pollingTimer = null; + } + }); + + if (!syncStatus.isHashing) { + return const SizedBox.shrink(); + } + + return Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 1.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainerHigh.withValues(alpha: 0.5), + shape: BoxShape.rectangle, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + "preparing".t(context: context), + style: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + const SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 1.5)), + ], + ), + const SizedBox(height: 2), + Text( + processingCount.toString(), + style: context.textTheme.titleMedium?.copyWith( + color: context.colorScheme.primary, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + decoration: BoxDecoration(color: context.colorScheme.primary.withValues(alpha: 0.1)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "ready_for_upload".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + ), + const SizedBox(height: 2), + Text( + readyForUploadCount.toString(), + style: context.textTheme.titleMedium?.copyWith( + color: context.primaryColor, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ], ); } } diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index fd39f0a579..cae9f0a408 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -1,36 +1,41 @@ +import 'dart:async'; import 'dart:io'; import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - +import 'package:immich_mobile/domain/services/sync_linked_album.service.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:logging/logging.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { const DriftBackupAlbumSelectionPage({super.key}); @override - ConsumerState createState() => - _DriftBackupAlbumSelectionPageState(); + ConsumerState createState() => _DriftBackupAlbumSelectionPageState(); } -class _DriftBackupAlbumSelectionPageState - extends ConsumerState { +class _DriftBackupAlbumSelectionPageState extends ConsumerState { String _searchQuery = ''; bool _isSearchMode = false; + int _initialTotalAssetCount = 0; late ValueNotifier _enableSyncUploadAlbum; late TextEditingController _searchController; late FocusNode _searchFocusNode; + Future? _handleLinkedAlbumFuture; @override void initState() { @@ -39,10 +44,30 @@ class _DriftBackupAlbumSelectionPageState _searchController = TextEditingController(); _searchFocusNode = FocusNode(); - _enableSyncUploadAlbum.value = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); + _enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); ref.read(backupAlbumProvider.notifier).getAll(); + + _initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); + } + + Future _handlePagePopped() async { + final user = ref.read(currentUserProvider); + if (user == null) { + return; + } + + final enableSyncUploadAlbum = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); + final selectedAlbums = ref + .read(backupAlbumProvider) + .where((a) => a.backupSelection == BackupSelection.selected) + .toList(); + + if (enableSyncUploadAlbum && selectedAlbums.isNotEmpty) { + setState(() { + _handleLinkedAlbumFuture = ref.read(syncLinkedAlbumServiceProvider).manageLinkedAlbums(selectedAlbums, user.id); + }); + await _handleLinkedAlbumFuture; + } } @override @@ -63,195 +88,203 @@ class _DriftBackupAlbumSelectionPageState return album.name.toLowerCase().contains(_searchQuery.toLowerCase()); }).toList(); - final selectedBackupAlbums = albums - .where((album) => album.backupSelection == BackupSelection.selected) - .toList(); - final excludedBackupAlbums = albums - .where((album) => album.backupSelection == BackupSelection.excluded) - .toList(); + final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).toList(); + final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList(); - handleSyncAlbumToggle(bool isEnable) async { - if (isEnable) { - await ref.read(albumProvider.notifier).refreshRemoteAlbums(); - for (final album in selectedBackupAlbums) { - await ref.read(albumProvider.notifier).createSyncAlbum(album.name); - } - } - } + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, _) async { + if (!didPop) { + await _handlePagePopped(); - return Scaffold( - appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: _isSearchMode - ? SearchField( - hintText: 'search_albums'.t(context: context), - autofocus: true, - controller: _searchController, - focusNode: _searchFocusNode, - onChanged: (value) => - setState(() => _searchQuery = value.trim()), - ) - : const Text( - "backup_album_selection_page_select_albums", - ).t(context: context), - actions: [ - if (!_isSearchMode) - IconButton( - icon: const Icon(Icons.search), - onPressed: () => setState(() { - _isSearchMode = true; - _searchQuery = ''; - }), - ) - else - IconButton( - icon: const Icon(Icons.close), - onPressed: () => setState(() { - _isSearchMode = false; - _searchQuery = ''; - _searchController.clear(); - }), - ), - ], - elevation: 0, - ), - body: CustomScrollView( - physics: const ClampingScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).t(context: context), + final user = ref.read(currentUserProvider); + if (user == null) { + return; + } + + final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id); + final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); + final totalChanged = currentTotalAssetCount != _initialTotalAssetCount; + final backupNotifier = ref.read(driftBackupProvider.notifier); + final backgroundSync = ref.read(backgroundSyncProvider); + final nativeSync = ref.read(nativeSyncApiProvider); + if (totalChanged) { + // Waits for hashing to be cancelled before starting a new one + unawaited(nativeSync.cancelHashing().whenComplete(() => backgroundSync.hashAssets())); + if (isBackupEnabled) { + unawaited( + backupNotifier.cancel().whenComplete( + () => backgroundSync.syncRemote().then((success) { + if (success) { + return backupNotifier.startBackup(user.id); + } else { + Logger('DriftBackupAlbumSelectionPage').warning('Background sync failed, not starting backup'); + } + }), ), - // Selected Album Chips + ); + } + } - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( + Navigator.of(context).pop(); + } + }, + child: Scaffold( + appBar: AppBar( + leading: IconButton( + onPressed: () async => await context.maybePop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + title: _isSearchMode + ? SearchField( + hintText: 'search_albums'.t(context: context), + autofocus: true, + controller: _searchController, + focusNode: _searchFocusNode, + onChanged: (value) => setState(() => _searchQuery = value.trim()), + ) + : const Text("backup_album_selection_page_select_albums").t(context: context), + actions: [ + if (!_isSearchMode) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() { + _isSearchMode = true; + _searchQuery = ''; + }), + ) + else + IconButton( + icon: const Icon(Icons.close), + onPressed: () => setState(() { + _isSearchMode = false; + _searchQuery = ''; + _searchController.clear(); + }), + ), + ], + elevation: 0, + ), + body: Stack( + children: [ + CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - _SelectedAlbumNameChips( - selectedBackupAlbums: selectedBackupAlbums, + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text( + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, + ).t(context: context), ), - _ExcludedAlbumNameChips( - excludedBackupAlbums: excludedBackupAlbums, + + // Selected Album Chips + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + _SelectedAlbumNameChips(selectedBackupAlbums: selectedBackupAlbums), + _ExcludedAlbumNameChips(excludedBackupAlbums: excludedBackupAlbums), + ], + ), ), + ListTile( + title: Text( + "albums_on_device_count".t(context: context, args: {'count': albumCount.toString()}), + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + ).t(context: context), + ), + trailing: IconButton( + splashRadius: 16, + icon: Icon(Icons.info, size: 20, color: context.primaryColor), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: context.primaryColor, + ), + ).t(context: context), + content: SingleChildScrollView( + child: ListBody( + children: [ + const Text( + 'backup_album_selection_page_assets_scatter', + style: TextStyle(fontSize: 14), + ).t(context: context), + ], + ), + ), + ); + }, + ); + }, + ), + ), + + if (Platform.isAndroid) + _SelectAllButton(filteredAlbums: filteredAlbums, selectedBackupAlbums: selectedBackupAlbums), ], ), ), - - SettingsSwitchListTile( - valueNotifier: _enableSyncUploadAlbum, - title: "sync_albums".t(context: context), - subtitle: - "sync_upload_album_setting_subtitle".t(context: context), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), - onChanged: handleSyncAlbumToggle, + SliverLayoutBuilder( + builder: (context, constraints) { + if (constraints.crossAxisExtent > 600) { + return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); + } else { + return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery); + } + }, ), - - ListTile( - title: Text( - "albums_on_device_count".t( - context: context, - args: {'count': albumCount.toString()}, - ), - style: context.textTheme.titleSmall, - ), - subtitle: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), - ).t(context: context), - ), - trailing: IconButton( - splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), - onPressed: () { - // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(10)), - ), - elevation: 5, - title: Text( - 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), - ).t(context: context), - content: SingleChildScrollView( - child: ListBody( - children: [ - const Text( - 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), - ).t(context: context), - ], - ), - ), - ); - }, - ); - }, - ), - ), - - if (Platform.isAndroid) - _SelectAllButton( - filteredAlbums: filteredAlbums, - selectedBackupAlbums: selectedBackupAlbums, - ), ], ), - ), - SliverLayoutBuilder( - builder: (context, constraints) { - if (constraints.crossAxisExtent > 600) { - return _AlbumSelectionGrid( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } else { - return _AlbumSelectionList( - filteredAlbums: filteredAlbums, - searchQuery: _searchQuery, - ); - } - }, - ), - ], + if (_handleLinkedAlbumFuture != null) + FutureBuilder( + future: _handleLinkedAlbumFuture, + builder: (context, snapshot) { + return SizedBox( + height: double.infinity, + width: double.infinity, + child: Container( + color: context.scaffoldBackgroundColor.withValues(alpha: 0.8), + child: Center( + child: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + const CircularProgressIndicator(strokeWidth: 4), + Text('creating_linked_albums'.tr(), style: context.textTheme.labelLarge), + ], + ), + ), + ), + ); + }, + ), + ], + ), ), ); } @@ -261,10 +294,7 @@ class _AlbumSelectionList extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionList({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -280,24 +310,15 @@ class _AlbumSelectionList extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); - }), - childCount: filteredAlbums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return DriftAlbumInfoListTile(album: filteredAlbums[index]); + }), childCount: filteredAlbums.length), ), ); } @@ -307,10 +328,7 @@ class _AlbumSelectionGrid extends StatelessWidget { final List filteredAlbums; final String searchQuery; - const _AlbumSelectionGrid({ - required this.filteredAlbums, - required this.searchQuery, - }); + const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery}); @override Widget build(BuildContext context) { @@ -326,11 +344,7 @@ class _AlbumSelectionGrid extends StatelessWidget { } if (filteredAlbums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -343,9 +357,7 @@ class _AlbumSelectionGrid extends StatelessWidget { ), itemCount: filteredAlbums.length, itemBuilder: ((context, index) { - return DriftAlbumInfoListTile( - album: filteredAlbums[index], - ); + return DriftAlbumInfoListTile(album: filteredAlbums[index]); }), ), ); @@ -355,9 +367,7 @@ class _AlbumSelectionGrid extends StatelessWidget { class _SelectedAlbumNameChips extends ConsumerWidget { final List selectedBackupAlbums; - const _SelectedAlbumNameChips({ - required this.selectedBackupAlbums, - }); + const _SelectedAlbumNameChips({required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -386,12 +396,8 @@ class _SelectedAlbumNameChips extends ConsumerWidget { ), ), backgroundColor: context.primaryColor, - deleteIconColor: - context.isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white, + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -405,9 +411,7 @@ class _SelectedAlbumNameChips extends ConsumerWidget { class _ExcludedAlbumNameChips extends ConsumerWidget { final List excludedBackupAlbums; - const _ExcludedAlbumNameChips({ - required this.excludedBackupAlbums, - }); + const _ExcludedAlbumNameChips({required this.excludedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -429,18 +433,11 @@ class _ExcludedAlbumNameChips extends ConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -455,16 +452,11 @@ class _SelectAllButton extends ConsumerWidget { final List filteredAlbums; final List selectedBackupAlbums; - const _SelectAllButton({ - required this.filteredAlbums, - required this.selectedBackupAlbums, - }); + const _SelectAllButton({required this.filteredAlbums, required this.selectedBackupAlbums}); @override Widget build(BuildContext context, WidgetRef ref) { - final canSelectAll = filteredAlbums - .where((album) => album.backupSelection != BackupSelection.selected) - .isNotEmpty; + final canSelectAll = filteredAlbums.where((album) => album.backupSelection != BackupSelection.selected).isNotEmpty; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), @@ -476,9 +468,7 @@ class _SelectAllButton extends ConsumerWidget { ? () { for (final album in filteredAlbums) { if (album.backupSelection != BackupSelection.selected) { - ref - .read(backupAlbumProvider.notifier) - .selectAlbum(album); + ref.read(backupAlbumProvider.notifier).selectAlbum(album); } } } @@ -486,13 +476,9 @@ class _SelectAllButton extends ConsumerWidget { icon: const Icon(Icons.select_all), label: AnimatedSwitcher( duration: const Duration(milliseconds: 200), - child: Text( - "select_all".t(context: context), - ), - ), - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text("select_all".t(context: context)), ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), const SizedBox(width: 8.0), @@ -502,18 +488,14 @@ class _SelectAllButton extends ConsumerWidget { ? () { for (final album in filteredAlbums) { if (album.backupSelection == BackupSelection.selected) { - ref - .read(backupAlbumProvider.notifier) - .deselectAlbum(album); + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); } } } : null, icon: const Icon(Icons.deselect), label: Text('deselect_all'.t(context: context)), - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12.0), - ), + style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), ), ), ], diff --git a/mobile/lib/pages/backup/drift_backup_asset_detail.page.dart b/mobile/lib/pages/backup/drift_backup_asset_detail.page.dart new file mode 100644 index 0000000000..3cc675c4ad --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_asset_detail.page.dart @@ -0,0 +1,97 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +@RoutePage() +class DriftBackupAssetDetailPage extends ConsumerWidget { + const DriftBackupAssetDetailPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + AsyncValue> result = ref.watch(driftBackupCandidateProvider); + return Scaffold( + appBar: AppBar(title: Text('backup_controller_page_remainder'.t(context: context))), + body: result.when( + data: (List candidates) { + return ListView.separated( + padding: const EdgeInsets.only(top: 16.0), + separatorBuilder: (context, index) => Divider(color: context.colorScheme.outlineVariant), + itemCount: candidates.length, + itemBuilder: (context, index) { + final asset = candidates[index]; + final albumsAsyncValue = ref.watch(driftCandidateBackupAlbumInfoProvider(asset.id)); + return LargeLeadingTile( + title: Text( + asset.name, + style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500, fontSize: 16), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + asset.createdAt.toString(), + style: TextStyle(fontSize: 13.0, color: context.colorScheme.onSurfaceSecondary), + ), + Text( + asset.checksum ?? "N/A", + style: TextStyle(fontSize: 13.0, color: context.colorScheme.onSurfaceSecondary), + overflow: TextOverflow.ellipsis, + ), + albumsAsyncValue.when( + data: (albums) { + if (albums.isEmpty) { + return const SizedBox.shrink(); + } + return Text( + albums.map((a) => a.name).join(', '), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + overflow: TextOverflow.ellipsis, + ); + }, + error: (error, stackTrace) => Text( + 'error_saving_image'.tr(args: [error.toString()]), + style: TextStyle(color: context.colorScheme.error), + ), + loading: () => const SizedBox(height: 16, width: 16, child: CircularProgressIndicator.adaptive()), + ), + ], + ), + leading: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: SizedBox( + width: 64, + height: 64, + child: Thumbnail.fromAsset(asset: asset, size: const Size(64, 64), fit: BoxFit.cover), + ), + ), + trailing: const Padding(padding: EdgeInsets.only(right: 24, left: 8), child: Icon(Icons.image_search)), + onTap: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(asset.createdAt)); + }, + ); + }, + ); + }, + error: (Object error, StackTrace stackTrace) { + return Center(child: Text('error_saving_image'.tr(args: [error.toString()]))); + }, + loading: () { + return const SizedBox(height: 48, width: 48, child: Center(child: CircularProgressIndicator.adaptive())); + }, + ), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart new file mode 100644 index 0000000000..1e5c326478 --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; +import 'package:logging/logging.dart'; + +@RoutePage() +class DriftBackupOptionsPage extends ConsumerWidget { + const DriftBackupOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasPopped = false; + final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + + final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + + if (currentWifiReqForVideos == previousWifiReqForVideos && + currentWifiReqForPhotos == previousWifiReqForPhotos) { + return; + } + + if (didPop && !hasPopped) { + hasPopped = true; + + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + if (!isBackupEnabled) { + return; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("network_requirements_updated".t(context: context)), + duration: const Duration(seconds: 4), + ), + ); + + final backupNotifier = ref.read(driftBackupProvider.notifier); + final backgroundSync = ref.read(backgroundSyncProvider); + unawaited( + backupNotifier.cancel().whenComplete( + () => backgroundSync.syncRemote().then((success) { + if (success) { + return backupNotifier.startBackup(currentUser.id); + } else { + Logger('DriftBackupOptionsPage').warning('Background sync failed, not starting backup'); + } + }), + ), + ); + } + }, + child: Scaffold( + appBar: AppBar(title: Text("backup_options".t(context: context))), + body: const DriftBackupSettings(), + ), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart index 66803265e6..80956b708f 100644 --- a/mobile/lib/pages/backup/drift_upload_detail.page.dart +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -1,12 +1,12 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart' as path; @@ -16,9 +16,7 @@ class DriftUploadDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final uploadItems = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); return Scaffold( appBar: AppBar( @@ -27,9 +25,7 @@ class DriftUploadDetailPage extends ConsumerWidget { elevation: 0, scrolledUnderElevation: 1, ), - body: uploadItems.isEmpty - ? _buildEmptyState(context) - : _buildUploadList(uploadItems), + body: uploadItems.isEmpty ? _buildEmptyState(context) : _buildUploadList(uploadItems), ); } @@ -38,26 +34,18 @@ class DriftUploadDetailPage extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.cloud_upload_outlined, - size: 80, - color: context.colorScheme.onSurface.withValues(alpha: 0.3), - ), + Icon(Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3)), const SizedBox(height: 16), Text( "no_uploads_in_progress".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.6), - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.6)), ), ], ), ); } - Widget _buildUploadList( - Map uploadItems, - ) { + Widget _buildUploadList(Map uploadItems) { return ListView.separated( addAutomaticKeepAlives: true, padding: const EdgeInsets.all(16), @@ -70,30 +58,20 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Widget _buildUploadCard( - BuildContext context, - DriftUploadStatus item, - ) { + Widget _buildUploadCard(BuildContext context, DriftUploadStatus item) { final isCompleted = item.progress >= 1.0; final double progressPercentage = (item.progress * 100).clamp(0, 100); return Card( elevation: 0, - color: context.colorScheme.surfaceContainer, + color: item.isFailed != null ? context.colorScheme.errorContainer : context.colorScheme.surfaceContainer, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), child: InkWell( onTap: () => _showFileDetailDialog(context, item), - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), child: Padding( padding: const EdgeInsets.all(16), child: Column( @@ -104,21 +82,25 @@ class DriftUploadDetailPage extends ConsumerWidget { Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, children: [ Text( path.basename(item.filename), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis, ), - const SizedBox(height: 4), + if (item.error != null) + Text( + item.error!, + style: context.textTheme.bodySmall?.copyWith( + color: context.colorScheme.onErrorContainer.withValues(alpha: 0.6), + ), + ), Text( 'Tap for more details', style: context.textTheme.bodySmall?.copyWith( - color: context.colorScheme.onSurface - .withValues(alpha: 0.6), + color: context.colorScheme.onSurface.withValues(alpha: 0.6), ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -161,29 +143,19 @@ class DriftUploadDetailPage extends ConsumerWidget { tween: Tween(begin: 0.0, end: progress), duration: const Duration(milliseconds: 300), builder: (context, value, _) => CircularProgressIndicator( - backgroundColor: - context.colorScheme.outline.withValues(alpha: 0.2), + backgroundColor: context.colorScheme.outline.withValues(alpha: 0.2), strokeWidth: 3, value: value, - color: isCompleted - ? context.colorScheme.primary - : context.colorScheme.secondary, + color: isCompleted ? context.colorScheme.primary : context.colorScheme.secondary, ), ), ), if (isCompleted) - Icon( - Icons.check_circle_rounded, - size: 28, - color: context.colorScheme.primary, - ) + Icon(Icons.check_circle_rounded, size: 28, color: context.colorScheme.primary) else Text( percentage.toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - fontSize: 10, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold, fontSize: 10), ), ], ), @@ -198,10 +170,7 @@ class DriftUploadDetailPage extends ConsumerWidget { ); } - Future _showFileDetailDialog( - BuildContext context, - DriftUploadStatus item, - ) async { + Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { showDialog( context: context, builder: (context) => FileDetailDialog(uploadStatus: item), @@ -212,10 +181,7 @@ class DriftUploadDetailPage extends ConsumerWidget { class FileDetailDialog extends ConsumerWidget { final DriftUploadStatus uploadStatus; - const FileDetailDialog({ - super.key, - required this.uploadStatus, - }); + const FileDetailDialog({super.key, required this.uploadStatus}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -223,29 +189,17 @@ class FileDetailDialog extends ConsumerWidget { insetPadding: const EdgeInsets.all(20), backgroundColor: context.colorScheme.surfaceContainerLow, shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.2), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), ), title: Row( children: [ - Icon( - Icons.info_outline, - color: context.primaryColor, - size: 24, - ), + Icon(Icons.info_outline, color: context.primaryColor, size: 24), const SizedBox(width: 8), Expanded( child: Text( "details".t(context: context), - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], @@ -256,10 +210,7 @@ class FileDetailDialog extends ConsumerWidget { future: _getAssetDetails(ref, uploadStatus.taskId), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return const SizedBox( - height: 200, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); } final asset = snapshot.data; @@ -276,20 +227,11 @@ class FileDetailDialog extends ConsumerWidget { width: 128, height: 128, decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.outline - .withValues(alpha: 0.2), - width: 1, - ), - borderRadius: - const BorderRadius.all(Radius.circular(12)), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: asset != null - ? Thumbnail( - asset: asset, - size: const Size(512, 512), - fit: BoxFit.cover, - ) + ? Thumbnail.fromAsset(asset: asset, size: const Size(128, 128), fit: BoxFit.cover) : null, ), ), @@ -297,45 +239,14 @@ class FileDetailDialog extends ConsumerWidget { const SizedBox(height: 24), if (asset != null) ...[ _buildInfoSection(context, [ - _buildInfoRow( - context, - "Filename", - path.basename(uploadStatus.filename), - ), - _buildInfoRow( - context, - "Local ID", - asset.id, - ), - _buildInfoRow( - context, - "File Size", - formatHumanReadableBytes(uploadStatus.fileSize, 2), - ), - if (asset.width != null) - _buildInfoRow(context, "Width", "${asset.width}px"), - if (asset.height != null) - _buildInfoRow( - context, - "Height", - "${asset.height}px", - ), - _buildInfoRow( - context, - "Created At", - asset.createdAt.toString(), - ), - _buildInfoRow( - context, - "Updated At", - asset.updatedAt.toString(), - ), - if (asset.checksum != null) - _buildInfoRow( - context, - "Checksum", - asset.checksum!, - ), + _buildInfoRow(context, "Filename", path.basename(uploadStatus.filename)), + _buildInfoRow(context, "Local ID", asset.id), + _buildInfoRow(context, "File Size", formatHumanReadableBytes(uploadStatus.fileSize, 2)), + if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"), + if (asset.height != null) _buildInfoRow(context, "Height", "${asset.height}px"), + _buildInfoRow(context, "Created At", asset.createdAt.toString()), + _buildInfoRow(context, "Updated At", asset.updatedAt.toString()), + if (asset.checksum != null) _buildInfoRow(context, "Checksum", asset.checksum!), ]), ], ], @@ -349,39 +260,23 @@ class FileDetailDialog extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(), child: Text( "close".t(), - style: TextStyle( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor), ), ), ], ); } - Widget _buildInfoSection( - BuildContext context, - List children, - ) { + Widget _buildInfoSection(BuildContext context, List children) { return Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, - borderRadius: const BorderRadius.all( - Radius.circular(12), - ), - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: 0.1), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...children, - ], + borderRadius: const BorderRadius.all(Radius.circular(12)), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [...children]), ); } @@ -414,10 +309,7 @@ class FileDetailDialog extends ConsumerWidget { ); } - Future _getAssetDetails( - WidgetRef ref, - String localAssetId, - ) async { + Future _getAssetDetails(WidgetRef ref, String localAssetId) async { try { final repository = ref.read(localAssetRepository); return await repository.getById(localAssetId); diff --git a/mobile/lib/pages/backup/failed_backup_status.page.dart b/mobile/lib/pages/backup/failed_backup_status.page.dart index 167b0f2ebd..b533895cd7 100644 --- a/mobile/lib/pages/backup/failed_backup_status.page.dart +++ b/mobile/lib/pages/backup/failed_backup_status.page.dart @@ -25,9 +25,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: ListView.builder( @@ -37,19 +35,13 @@ class FailedBackupStatusPage extends HookConsumerWidget { var errorAsset = errorBackupList.elementAt(index); return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15), // if you need this ), - side: BorderSide( - color: Colors.black12, - width: 1, - ), + side: BorderSide(color: Colors.black12, width: 1), ), elevation: 0, child: Row( @@ -57,12 +49,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 100, - minHeight: 100, - maxWidth: 100, - maxHeight: 150, - ), + constraints: const BoxConstraints(minWidth: 100, minHeight: 100, maxWidth: 100, maxHeight: 150), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(15), @@ -71,11 +58,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { clipBehavior: Clip.hardEdge, child: Image( fit: BoxFit.cover, - image: ImmichLocalThumbnailProvider( - asset: errorAsset.asset, - height: 512, - width: 512, - ), + image: ImmichLocalThumbnailProvider(asset: errorAsset.asset, height: 512, width: 512), ), ), ), @@ -91,22 +74,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { children: [ Text( DateFormat.yMMMMd().format( - DateTime.parse( - errorAsset.fileCreatedAt.toString(), - ).toLocal(), + DateTime.parse(errorAsset.fileCreatedAt.toString()).toLocal(), ), style: TextStyle( fontWeight: FontWeight.w600, - color: context.isDarkTheme - ? Colors.white70 - : Colors.grey[800], + color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), - Icon( - Icons.error, - color: Colors.red.withAlpha(200), - size: 18, - ), + Icon(Icons.error, color: Colors.red.withAlpha(200), size: 18), ], ), Padding( @@ -115,19 +90,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { errorAsset.fileName, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), Text( errorAsset.errorMessage, style: TextStyle( fontWeight: FontWeight.w500, - color: context.isDarkTheme - ? Colors.white70 - : Colors.grey[800], + color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), ], diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 776ee9977b..1a1955af40 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -16,9 +16,7 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; @RoutePage() class ActivitiesPage extends HookConsumerWidget { - const ActivitiesPage({ - super.key, - }); + const ActivitiesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,10 +25,8 @@ class ActivitiesPage extends HookConsumerWidget { final asset = ref.watch(currentAssetProvider); final user = ref.watch(currentUserProvider); - final activityNotifier = ref - .read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); - final activities = - ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId)); + final activityNotifier = ref.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); + final activities = ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId)); final listViewScrollController = useScrollController(); @@ -49,10 +45,7 @@ class ActivitiesPage extends HookConsumerWidget { body: activities.widgetWhen( onData: (data) { final liked = data.firstWhereOrNull( - (a) => - a.type == ActivityType.like && - a.user.id == user?.id && - a.assetId == asset?.remoteId, + (a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.remoteId, ); return SafeArea( @@ -65,14 +58,11 @@ class ActivitiesPage extends HookConsumerWidget { itemBuilder: (context, index) { // Additional vertical gap after the last element if (index == data.length) { - return const SizedBox( - height: 80, - ); + return const SizedBox(height: 80); } final activity = data[index]; - final canDelete = activity.user.id == user?.id || - album.ownerId == user?.id; + final canDelete = activity.user.id == user?.id || album.ownerId == user?.id; return Padding( padding: const EdgeInsets.all(5), @@ -80,8 +70,7 @@ class ActivitiesPage extends HookConsumerWidget { activity.id, ActivityTile(activity), onDismiss: canDelete - ? (activityId) async => await activityNotifier - .removeActivity(activity.id) + ? (activityId) async => await activityNotifier.removeActivity(activity.id) : null, ), ); diff --git a/mobile/lib/pages/common/app_log.page.dart b/mobile/lib/pages/common/app_log.page.dart index 359a541de0..37aec2f13c 100644 --- a/mobile/lib/pages/common/app_log.page.dart +++ b/mobile/lib/pages/common/app_log.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -8,21 +9,16 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/immich_logger.service.dart'; -import 'package:intl/intl.dart'; @RoutePage() class AppLogPage extends HookConsumerWidget { - const AppLogPage({ - super.key, - }); + const AppLogPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final immichLogger = LogService.I; final shouldReload = useState(false); - final logMessages = useFuture( - useMemoized(() => immichLogger.getMessages(), [shouldReload.value]), - ); + final logMessages = useFuture(useMemoized(() => immichLogger.getMessages(), [shouldReload.value])); Widget colorStatusIndicator(Color color) { return Column( @@ -31,38 +27,29 @@ class AppLogPage extends HookConsumerWidget { Container( width: 10, height: 10, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), ], ); } Widget buildLeadingIcon(LogLevel level) => switch (level) { - LogLevel.info => colorStatusIndicator(context.primaryColor), - LogLevel.severe => colorStatusIndicator(Colors.redAccent), - LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), - _ => colorStatusIndicator(Colors.grey), - }; + LogLevel.info => colorStatusIndicator(context.primaryColor), + LogLevel.severe => colorStatusIndicator(Colors.redAccent), + LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), + _ => colorStatusIndicator(Colors.grey), + }; Color getTileColor(LogLevel level) => switch (level) { - LogLevel.info => Colors.transparent, - LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), - LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), - _ => context.primaryColor.withValues(alpha: 0.1), - }; + LogLevel.info => Colors.transparent, + LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), + LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), + _ => context.primaryColor.withValues(alpha: 0.1), + }; return Scaffold( appBar: AppBar( - title: const Text( - "Logs", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16.0, - ), - ), + title: Text('logs'.tr(), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), scrolledUnderElevation: 1, elevation: 2, actions: [ @@ -81,12 +68,7 @@ class AppLogPage extends HookConsumerWidget { Builder( builder: (BuildContext iconContext) { return IconButton( - icon: Icon( - Icons.share_rounded, - color: context.primaryColor, - semanticLabel: "Share logs", - size: 20.0, - ), + icon: Icon(Icons.share_rounded, color: context.primaryColor, semanticLabel: "Share logs", size: 20.0), onPressed: () { ImmichLogger.shareLogs(iconContext); }, @@ -98,10 +80,7 @@ class AppLogPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: const Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - ), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20.0), ), centerTitle: true, ), @@ -113,11 +92,7 @@ class AppLogPage extends HookConsumerWidget { itemBuilder: (context, index) { var logMessage = logMessages.data![index]; return ListTile( - onTap: () => context.pushRoute( - AppLogDetailRoute( - logMessage: logMessage, - ), - ), + onTap: () => context.pushRoute(AppLogDetailRoute(logMessage: logMessage)), trailing: const Icon(Icons.arrow_forward_ios_rounded), visualDensity: VisualDensity.compact, dense: true, @@ -125,18 +100,11 @@ class AppLogPage extends HookConsumerWidget { minLeadingWidth: 10, title: Text( truncateLogMessage(logMessage.message, 4), - style: TextStyle( - fontSize: 14.0, - color: context.colorScheme.onSurface, - fontFamily: "Inconsolata", - ), + style: TextStyle(fontSize: 14.0, color: context.colorScheme.onSurface, fontFamily: "Inconsolata"), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.logger}", - style: TextStyle( - fontSize: 12.0, - color: context.colorScheme.onSurfaceSecondary, - ), + style: TextStyle(fontSize: 12.0, color: context.colorScheme.onSurfaceSecondary), ), leading: buildLeadingIcon(logMessage.level), ); diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index b88d6aeb3a..de9604b7ad 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -27,11 +28,7 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( header, - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), IconButton( @@ -40,39 +37,27 @@ class AppLogDetailPage extends HookConsumerWidget { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( - "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + "copied_to_clipboard".tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ], ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( text, - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -81,7 +66,7 @@ class AppLogDetailPage extends HookConsumerWidget { ); } - buildLogContext1(String context1) { + buildLogContext(String logger) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -91,29 +76,19 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "FROM", - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( - context1.toString(), - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + logger.toString(), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -123,22 +98,14 @@ class AppLogDetailPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("Log Detail"), - ), + appBar: AppBar(title: Text("log_detail_title".tr())), body: SafeArea( child: ListView( children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), - if (logMessage.error != null) - buildTextWithCopyButton("DETAILS", logMessage.error.toString()), - if (logMessage.logger != null) - buildLogContext1(logMessage.logger.toString()), - if (logMessage.stack != null) - buildTextWithCopyButton( - "STACK TRACE", - logMessage.stack.toString(), - ), + if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), + if (logMessage.logger != null) buildLogContext(logMessage.logger.toString()), + if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), ), diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index a8569b25a0..2cc3dede1e 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -1,16 +1,27 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; -import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/background.service.dart'; import 'package:immich_mobile/utils/migration.dart'; +import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; @RoutePage() @@ -24,7 +35,7 @@ class ChangeExperiencePage extends ConsumerStatefulWidget { } class _ChangeExperiencePageState extends ConsumerState { - bool hasMigrated = false; + AsyncValue hasMigrated = const AsyncValue.loading(); @override void initState() { @@ -33,6 +44,32 @@ class _ChangeExperiencePageState extends ConsumerState { } Future _handleMigration() async { + try { + await _performMigrationLogic().timeout( + const Duration(minutes: 3), + onTimeout: () async { + await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); + await DriftStoreRepository(ref.read(driftProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); + }, + ); + + if (mounted) { + setState(() { + HapticFeedback.heavyImpact(); + hasMigrated = const AsyncValue.data(true); + }); + } + } catch (e, s) { + Logger("ChangeExperiencePage").severe("Error during migration", e, s); + if (mounted) { + setState(() { + hasMigrated = AsyncValue.error(e, s); + }); + } + } + } + + Future _performMigrationLogic() async { if (widget.switchingToBeta) { final assetNotifier = ref.read(assetProvider.notifier); if (assetNotifier.mounted) { @@ -43,36 +80,45 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } + // Cancel uploads + await Store.put(StoreKey.backgroundBackup, false); + ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onBatteryInfo: () {}, onError: (_) {}); + ref.read(backupProvider.notifier).setAutoBackup(false); + ref.read(backupProvider.notifier).cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); + // Start listening to new websocket events ref.read(websocketProvider.notifier).stopListenToOldEvents(); ref.read(websocketProvider.notifier).startListeningToBetaEvents(); - final permission = await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); + await ref.read(driftProvider).reset(); + await Store.put(StoreKey.shouldResetSync, true); + final delay = Store.get(StoreKey.backupTriggerDelay, AppSettingsEnum.backupTriggerDelay.defaultValue); + if (delay >= 1000) { + await Store.put(StoreKey.backupTriggerDelay, (delay / 1000).toInt()); + } + final permission = await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); if (permission.isGranted) { await ref.read(backgroundSyncProvider).syncLocal(full: true); - await migrateDeviceAssetToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); - await migrateBackupAlbumsToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); + await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await migrateStoreToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await ref.read(backgroundServiceProvider).disableService(); } } else { await ref.read(backgroundSyncProvider).cancel(); ref.read(websocketProvider.notifier).stopListeningToBetaEvents(); ref.read(websocketProvider.notifier).startListeningToOldEvents(); + ref.read(readonlyModeProvider.notifier).setReadonlyMode(false); + await migrateStoreToIsar(ref.read(isarProvider), ref.read(driftProvider)); + await ref.read(backgroundServiceProvider).resumeServiceIfEnabled(); + await ref.read(backgroundWorkerFgServiceProvider).disable(); } - if (mounted) { - setState(() { - HapticFeedback.heavyImpact(); - hasMigrated = true; - }); - } + await IsarStoreRepository(ref.read(isarProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); + await DriftStoreRepository(ref.read(driftProvider)).upsert(StoreKey.betaTimeline, widget.switchingToBeta); } @override @@ -84,54 +130,34 @@ class _ChangeExperiencePageState extends ConsumerState { children: [ AnimatedSwitcher( duration: Durations.long4, - child: hasMigrated - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - size: 48.0, - ) - : const SizedBox( - width: 50.0, - height: 50.0, - child: CircularProgressIndicator(), - ), + child: hasMigrated.when( + data: (data) => const Icon(Icons.check_circle_rounded, color: Colors.green, size: 48.0), + error: (error, stackTrace) => const Icon(Icons.error, color: Colors.red, size: 48.0), + loading: () => const SizedBox(width: 50.0, height: 50.0, child: CircularProgressIndicator()), + ), ), const SizedBox(height: 16.0), - Center( - child: Column( - children: [ - SizedBox( - width: 300.0, - child: AnimatedSwitcher( - duration: Durations.long4, - child: hasMigrated - ? Text( - "Migration success!", - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ) - : Text( - "Data migration in progress...\nPlease wait and don't close this page", - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ), - ), + SizedBox( + width: 300.0, + child: AnimatedSwitcher( + duration: Durations.long4, + child: hasMigrated.when( + data: (data) => Text( + "Migration success!\nPlease close and reopen the app to apply changes", + style: context.textTheme.titleMedium, + textAlign: TextAlign.center, ), - if (hasMigrated) - Padding( - padding: const EdgeInsets.only(top: 16.0), - child: ElevatedButton( - onPressed: () { - context.replaceRoute( - widget.switchingToBeta - ? const TabShellRoute() - : const TabControllerRoute(), - ); - }, - child: const Text("Continue"), - ), - ), - ], + error: (error, stackTrace) => Text( + "Migration failed!\nError: $error", + style: context.textTheme.titleMedium, + textAlign: TextAlign.center, + ), + loading: () => Text( + "Data migration in progress...\nPlease wait and don't close this page", + style: context.textTheme.titleMedium, + textAlign: TextAlign.center, + ), + ), ), ), ], diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index faf1671a9e..5a0d4154f8 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -20,22 +20,16 @@ import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart'; class CreateAlbumPage extends HookConsumerWidget { final List? assets; - const CreateAlbumPage({ - super.key, - this.assets, - }); + const CreateAlbumPage({super.key, this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { - final albumTitleController = - useTextEditingController.fromValue(TextEditingValue.empty); + final albumTitleController = useTextEditingController.fromValue(TextEditingValue.empty); final albumTitleTextFieldFocusNode = useFocusNode(); final albumDescriptionTextFieldFocusNode = useFocusNode(); final isAlbumTitleTextFieldFocus = useState(false); final isAlbumTitleEmpty = useState(true); - final selectedAssets = useState>( - assets != null ? Set.from(assets!) : const {}, - ); + final selectedAssets = useState>(assets != null ? Set.from(assets!) : const {}); void onBackgroundTapped() { albumTitleTextFieldFocusNode.unfocus(); @@ -45,19 +39,13 @@ class CreateAlbumPage extends HookConsumerWidget { if (albumTitleController.text.isEmpty) { albumTitleController.text = 'create_album_page_untitled'.tr(); isAlbumTitleEmpty.value = false; - ref - .watch(albumTitleProvider.notifier) - .setAlbumTitle('create_album_page_untitled'.tr()); + ref.watch(albumTitleProvider.notifier).setAlbumTitle('create_album_page_untitled'.tr()); } } onSelectPhotosButtonPressed() async { - AssetSelectionPageResult? selectedAsset = - await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: selectedAssets.value, - canDeselect: true, - ), + AssetSelectionPageResult? selectedAsset = await context.pushRoute( + AlbumAssetSelectionRoute(existingAssets: selectedAssets.value, canDeselect: true), ); if (selectedAsset == null) { selectedAssets.value = const {}; @@ -68,10 +56,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumTitleTextField( isAlbumTitleEmpty: isAlbumTitleEmpty, albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, @@ -83,10 +68,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumViewerEditableDescription( albumDescription: '', descriptionFocusNode: albumDescriptionTextFieldFocusNode, @@ -99,10 +81,7 @@ class CreateAlbumPage extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 200, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).tr(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).tr(), ), ); } @@ -118,20 +97,12 @@ class CreateAlbumPage extends HookConsumerWidget { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotosButtonPressed, - icon: Icon( - Icons.add_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( @@ -179,17 +150,12 @@ class CreateAlbumPage extends HookConsumerWidget { crossAxisSpacing: 5.0, mainAxisSpacing: 5, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return GestureDetector( - onTap: onBackgroundTapped, - child: SharedAlbumThumbnailImage( - asset: selectedAssets.value.elementAt(index), - ), - ); - }, - childCount: selectedAssets.value.length, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + return GestureDetector( + onTap: onBackgroundTapped, + child: SharedAlbumThumbnailImage(asset: selectedAssets.value.elementAt(index)), + ); + }, childCount: selectedAssets.value.length), ), ); } @@ -199,10 +165,9 @@ class CreateAlbumPage extends HookConsumerWidget { Future createAlbum() async { onBackgroundTapped(); - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.read(albumTitleProvider), - selectedAssets.value, - ); + var newAlbum = await ref + .watch(albumProvider.notifier) + .createAlbum(ref.read(albumTitleProvider), selectedAssets.value); if (newAlbum != null) { ref.read(albumProvider.notifier).refreshRemoteAlbums(); @@ -225,20 +190,15 @@ class CreateAlbumPage extends HookConsumerWidget { }, icon: const Icon(Icons.close_rounded), ), - title: const Text( - 'create_album', - ).tr(), + title: const Text('create_album').tr(), actions: [ TextButton( - onPressed: - albumTitleController.text.isNotEmpty ? createAlbum : null, + onPressed: albumTitleController.text.isNotEmpty ? createAlbum : null, child: Text( 'create'.tr(), style: TextStyle( fontWeight: FontWeight.bold, - color: albumTitleController.text.isNotEmpty - ? context.primaryColor - : context.themeData.disabledColor, + color: albumTitleController.text.isNotEmpty ? context.primaryColor : context.themeData.disabledColor, ), ), ), diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_panel.dart index cc543c9e4e..0775f5b4e4 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_panel.dart @@ -6,22 +6,13 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; class DownloadPanel extends ConsumerWidget { - const DownloadPanel({ - super.key, - }); + const DownloadPanel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showProgress = ref.watch( - downloadStateProvider.select((state) => state.showProgress), - ); + final showProgress = ref.watch(downloadStateProvider.select((state) => state.showProgress)); - final tasks = ref - .watch( - downloadStateProvider.select((state) => state.taskProgress), - ) - .entries - .toList(); + final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); onCancelDownload(String id) { ref.watch(downloadStateProvider.notifier).cancelDownload(id); @@ -34,8 +25,7 @@ class DownloadPanel extends ConsumerWidget { duration: const Duration(milliseconds: 300), child: showProgress ? ConstrainedBox( - constraints: - BoxConstraints.loose(Size(context.width - 32, 300)), + constraints: BoxConstraints.loose(Size(context.width - 32, 300)), child: ListView.builder( shrinkWrap: true, itemCount: tasks.length, @@ -75,62 +65,46 @@ class DownloadTaskTile extends StatelessWidget { final progressPercent = (progress * 100).round(); String getStatusText() => switch (status) { - TaskStatus.running => 'downloading'.tr(), - TaskStatus.complete => 'download_complete'.tr(), - TaskStatus.failed => 'download_failed'.tr(), - TaskStatus.canceled => 'download_canceled'.tr(), - TaskStatus.paused => 'download_paused'.tr(), - TaskStatus.enqueued => 'download_enqueue'.tr(), - TaskStatus.notFound => 'download_notfound'.tr(), - TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), - }; + TaskStatus.running => 'downloading'.tr(), + TaskStatus.complete => 'download_complete'.tr(), + TaskStatus.failed => 'download_failed'.tr(), + TaskStatus.canceled => 'download_canceled'.tr(), + TaskStatus.paused => 'download_paused'.tr(), + TaskStatus.enqueued => 'download_enqueue'.tr(), + TaskStatus.notFound => 'download_notfound'.tr(), + TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), + }; return SizedBox( key: const ValueKey('download_progress'), width: context.width - 32, child: Card( clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: ListTile( minVerticalPadding: 18, leading: const Icon(Icons.video_file_outlined), - title: Text( - getStatusText(), - style: context.textTheme.labelLarge, - ), + title: Text(getStatusText(), style: context.textTheme.labelLarge), trailing: IconButton( icon: Icon(Icons.close, color: context.colorScheme.onError), onPressed: onCancelDownload, - style: ElevatedButton.styleFrom( - backgroundColor: context.colorScheme.error.withAlpha(200), - ), + style: ElevatedButton.styleFrom(backgroundColor: context.colorScheme.error.withAlpha(200)), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - fileName, - style: context.textTheme.labelMedium, - ), + Text(fileName, style: context.textTheme.labelMedium), Row( children: [ Expanded( child: LinearProgressIndicator( minHeight: 8.0, value: progress, - borderRadius: - const BorderRadius.all(Radius.circular(10.0)), + borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), const SizedBox(width: 8), - Text( - '$progressPercent%', - style: context.textTheme.labelSmall, - ), + Text('$progressPercent%', style: context.textTheme.labelSmall), ], ), ], diff --git a/mobile/lib/pages/common/gallery_stacked_children.dart b/mobile/lib/pages/common/gallery_stacked_children.dart index eafc325049..7145bc2553 100644 --- a/mobile/lib/pages/common/gallery_stacked_children.dart +++ b/mobile/lib/pages/common/gallery_stacked_children.dart @@ -36,11 +36,7 @@ class GalleryStackedChildren extends HookConsumerWidget { shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: stackElements.length, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemBuilder: (context, index) { final currentAsset = stackElements.elementAt(index); final assetId = currentAsset.remoteId; @@ -63,9 +59,7 @@ class GalleryStackedChildren extends HookConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 539406365a..3c279dfcd2 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -87,11 +87,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (index < totalAssets.value && index >= 0) { final asset = loadAsset(index); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), context, onError: onError, ); @@ -103,23 +99,20 @@ class GalleryViewerPage extends HookConsumerWidget { } } - useEffect( - () { - if (ref.read(showControlsProvider)) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); - } + useEffect(() { + if (ref.read(showControlsProvider)) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + } - // Delay this a bit so we can finish loading the page - Timer(const Duration(milliseconds: 400), () { - precacheNextImage(currentIndex.value + 1); - }); + // Delay this a bit so we can finish loading the page + Timer(const Duration(milliseconds: 400), () { + precacheNextImage(currentIndex.value + 1); + }); - return null; - }, - const [], - ); + return null; + }, const []); useEffect(() { final asset = loadAsset(currentIndex.value); @@ -136,9 +129,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 1), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -147,9 +138,7 @@ class GalleryViewerPage extends HookConsumerWidget { } } return null; - }, [ - ref.watch(castProvider).isCasting, - ]); + }, [ref.watch(castProvider).isCasting]); void showInfo() { final asset = ref.read(currentAssetProvider); @@ -157,9 +146,7 @@ class GalleryViewerPage extends HookConsumerWidget { return; } showModalBottomSheet( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), barrierColor: Colors.transparent, isScrollControlled: true, showDragHandle: true, @@ -174,20 +161,10 @@ class GalleryViewerPage extends HookConsumerWidget { expand: false, builder: (context, scrollController) { return Padding( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), - child: ref.watch(appSettingsServiceProvider).getSetting( - AppSettingsEnum.advancedTroubleshooting, - ) - ? AdvancedBottomSheet( - assetDetail: asset, - scrollController: scrollController, - ) - : DetailPanel( - asset: asset, - scrollController: scrollController, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), + child: ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.advancedTroubleshooting) + ? AdvancedBottomSheet(assetDetail: asset, scrollController: scrollController) + : DetailPanel(asset: asset, scrollController: scrollController), ); }, ); @@ -258,17 +235,13 @@ class GalleryViewerPage extends HookConsumerWidget { tightMode: true, initialScale: PhotoViewComputedScale.contained * 0.99, minScale: PhotoViewComputedScale.contained * 0.99, - errorBuilder: (context, error, stackTrace) => ImmichImage( - asset, - fit: BoxFit.contain, - ), + errorBuilder: (context, error, stackTrace) => ImmichImage(asset, fit: BoxFit.contain), ); } PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) { return PhotoViewGalleryPageOptions.customChild( - onDragStart: (_, details, __, ___) => - localPosition.value = details.localPosition, + onDragStart: (_, details, __, ___) => localPosition.value = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), heroAttributes: _getHeroAttributes(asset), filterQuality: FilterQuality.high, @@ -284,11 +257,7 @@ class GalleryViewerPage extends HookConsumerWidget { asset: asset, image: Image( key: ValueKey(asset), - image: ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + image: ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), fit: BoxFit.contain, height: context.height, width: context.width, @@ -304,8 +273,7 @@ class GalleryViewerPage extends HookConsumerWidget { final stackId = newAsset.stackId; if (stackId != null && currentIndex.value == index) { - final stackElements = - ref.read(assetStackStateProvider(newAsset.stackId!)); + final stackElements = ref.read(assetStackStateProvider(newAsset.stackId!)); if (stackIndex.value < stackElements.length) { newAsset = stackElements.elementAt(stackIndex.value); } @@ -319,8 +287,7 @@ class GalleryViewerPage extends HookConsumerWidget { return PopScope( // Change immersive mode back to normal "edgeToEdge" mode - onPopInvokedWithResult: (didPop, _) => - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge), + onPopInvokedWithResult: (didPop, _) => SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge), child: Scaffold( backgroundColor: Colors.black, body: Stack( @@ -335,8 +302,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (asset.isImage && !ref.read(isPlayingMotionVideoProvider)) { isZoomed.value = state != PhotoViewScaleState.initial; - ref.read(showControlsProvider.notifier).show = - !isZoomed.value; + ref.read(showControlsProvider.notifier).show = !isZoomed.value; } }, gaplessPlayback: true, @@ -346,17 +312,8 @@ class GalleryViewerPage extends HookConsumerWidget { child: Stack( fit: StackFit.expand, children: [ - BackdropFilter( - filter: ui.ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - ), - ImmichThumbnail( - key: ValueKey(asset), - asset: asset, - fit: BoxFit.contain, - ), + BackdropFilter(filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)), + ImmichThumbnail(key: ValueKey(asset), asset: asset, fit: BoxFit.contain), ], ), ); @@ -365,9 +322,9 @@ class GalleryViewerPage extends HookConsumerWidget { scrollPhysics: isZoomed.value ? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in : (Platform.isIOS - ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - ), + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics() // Use heavy physics for Android + ), itemCount: totalAssets.value, scrollDirection: Axis.horizontal, onPageChanged: (value, _) { @@ -405,9 +362,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -420,10 +375,7 @@ class GalleryViewerPage extends HookConsumerWidget { top: 0, left: 0, right: 0, - child: GalleryAppBar( - key: const ValueKey('app-bar'), - showInfo: showInfo, - ), + child: GalleryAppBar(key: const ValueKey('app-bar'), showInfo: showInfo), ), Positioned( bottom: 0, @@ -454,9 +406,7 @@ class GalleryViewerPage extends HookConsumerWidget { @pragma('vm:prefer-inline') PhotoViewHeroAttributes _getHeroAttributes(Asset asset) { return PhotoViewHeroAttributes( - tag: asset.isInDb - ? asset.id + heroOffset - : '${asset.remoteId}-$heroOffset', + tag: asset.isInDb ? asset.id + heroOffset : '${asset.remoteId}-$heroOffset', transitionOnUserGestures: true, ); } diff --git a/mobile/lib/pages/common/headers_settings.page.dart b/mobile/lib/pages/common/headers_settings.page.dart index 0f4ab882c8..4cf683b4d9 100644 --- a/mobile/lib/pages/common/headers_settings.page.dart +++ b/mobile/lib/pages/common/headers_settings.page.dart @@ -79,10 +79,8 @@ class HeaderSettingsPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), itemCount: list.length, itemBuilder: (ctx, index) => list[index], - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), - child: Divider(), - ), + separatorBuilder: (context, index) => + const Padding(padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), child: Divider()), ), ), ); @@ -109,12 +107,9 @@ class HeaderKeyValueSettings extends StatelessWidget { final SettingsHeader header; final Function() onRemove; - HeaderKeyValueSettings({ - super.key, - required this.header, - required this.onRemove, - }) : keyController = TextEditingController(text: header.key), - valueController = TextEditingController(text: header.value); + HeaderKeyValueSettings({super.key, required this.header, required this.onRemove}) + : keyController = TextEditingController(text: header.key), + valueController = TextEditingController(text: header.value); String? emptyFieldValidator(String? value) { if (value == null || value.isEmpty) { @@ -150,9 +145,7 @@ class HeaderKeyValueSettings extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 8), child: IconButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), color: Colors.red[400], onPressed: onRemove, icon: const Icon(Icons.delete_outline), diff --git a/mobile/lib/pages/common/large_leading_tile.dart b/mobile/lib/pages/common/large_leading_tile.dart index 4f22a5f2b2..4563834473 100644 --- a/mobile/lib/pages/common/large_leading_tile.dart +++ b/mobile/lib/pages/common/large_leading_tile.dart @@ -8,10 +8,7 @@ class LargeLeadingTile extends StatelessWidget { required this.onTap, required this.title, this.subtitle, - this.leadingPadding = const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16.0, - ), + this.leadingPadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16.0), this.borderRadius = 20.0, this.trailing, this.selected = false, @@ -40,26 +37,19 @@ class LargeLeadingTile extends StatelessWidget { child: Container( decoration: BoxDecoration( color: selected - ? selectedTileColor ?? - Theme.of(context).primaryColor.withAlpha(30) + ? selectedTileColor ?? Theme.of(context).primaryColor.withAlpha(30) : tileColor ?? Colors.transparent, borderRadius: BorderRadius.circular(borderRadius), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: leadingPadding, - child: leading, - ), + Padding(padding: leadingPadding, child: leading), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - width: context.width * 0.6, - child: title, - ), + SizedBox(width: context.width * 0.6, child: title), subtitle ?? const SizedBox.shrink(), ], ), diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index 8afa6ab4e3..d8b6db2276 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -63,6 +63,8 @@ class NativeVideoViewerPage extends HookConsumerWidget { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + final isVideoReady = useState(false); + Future createSource() async { if (!context.mounted) { return null; @@ -76,10 +78,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } @@ -88,8 +87,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { final isOriginalVideo = ref .read(appSettingsServiceProvider) .getSetting(AppSettingsEnum.loadOriginalVideo); - final String postfixUrl = - isOriginalVideo ? 'original' : 'video/playback'; + final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = asset.livePhotoVideoId != null ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' : '$serverEndpoint/assets/${asset.remoteId}/$postfixUrl'; @@ -101,31 +99,24 @@ class NativeVideoViewerPage extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.fileName}: $error', - ); + log.severe('Error creating video source for asset ${asset.fileName}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(asset.aspectRatio); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = - await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.fileName}: $error', - ); - } - }, - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.fileName}: $error'); + } + }); void checkIfBuffering() { if (!context.mounted) { @@ -133,11 +124,11 @@ class NativeVideoViewerPage extends HookConsumerWidget { } final videoPlayback = ref.read(videoPlaybackValueProvider); - if ((isBuffering.value || - videoPlayback.state == VideoPlaybackState.initializing) && + if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -193,10 +184,11 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback; + isVideoReady.value = true; + try { await videoController.play(); await videoController.setVolume(0.9); @@ -211,8 +203,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); if (videoPlayback.state == VideoPlaybackState.playing) { // Sync with the controls playing WakelockPlus.enable(); @@ -221,8 +212,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { WakelockPlus.disable(); } - ref.read(videoPlaybackValueProvider.notifier).status = - videoPlayback.state; + ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state; } void onPlaybackPositionChanged() { @@ -241,8 +231,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - ref.read(videoPlaybackValueProvider.notifier).position = - Duration(seconds: playbackInfo.position); + ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position); // Check if the video is buffering if (playbackInfo.status == PlaybackStatus.playing) { @@ -261,18 +250,14 @@ class NativeVideoViewerPage extends HookConsumerWidget { } if (videoController.playbackInfo?.status == PlaybackStatus.stopped && - !ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo)) { + !ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo)) { ref.read(isPlayingMotionVideoProvider.notifier).playing = false; } } void removeListeners(NativeVideoPlayerController controller) { - controller.onPlaybackPositionChanged - .removeListener(onPlaybackPositionChanged); - controller.onPlaybackStatusChanged - .removeListener(onPlaybackStatusChanged); + controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged); + controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged); controller.onPlaybackReady.removeListener(onPlaybackReady); controller.onPlaybackEnded.removeListener(onPlaybackEnded); } @@ -297,9 +282,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { nc.loadVideoSource(source).catchError((error) { log.severe('Error loading video source: $error'); }); - final loopVideo = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo); + final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); nc.setLoop(loopVideo); controller.value = nc; @@ -332,48 +315,42 @@ class NativeVideoViewerPage extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -393,7 +370,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { children: [ // This remains under the video to avoid flickering // For motion videos, this is the image portion of the asset - Center(key: ValueKey(asset.id), child: image), + if (!isVideoReady.value || asset.isMotionPhoto) Center(key: ValueKey(asset.id), child: image), if (aspectRatio.value != null && !isCasting) Visibility.maintain( key: ValueKey(asset), @@ -403,12 +380,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index e45001270c..0fe2ccec09 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -1,75 +1,50 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; -import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; +import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - advanced( - 'advanced', - Icons.build_outlined, - "advanced_settings_tile_subtitle", - ), - assetViewer( - 'asset_viewer_settings_title', - Icons.image_outlined, - "asset_viewer_settings_subtitle", - ), - backup( - 'backup_controller_page_backup', - Icons.cloud_upload_outlined, - "backup_setting_subtitle", - ), - languages( - 'language', - Icons.language, - "setting_languages_subtitle", - ), - networking( - 'networking_settings', - Icons.wifi, - "networking_subtitle", - ), - notifications( - 'notifications', - Icons.notifications_none_rounded, - "setting_notifications_subtitle", - ), - preferences( - 'preferences_settings_title', - Icons.interests_outlined, - "preferences_settings_subtitle", - ), - timeline( - 'asset_list_settings_title', - Icons.auto_awesome_mosaic_outlined, - "asset_list_settings_subtitle", - ); + advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), + assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), + languages('language', Icons.language, "setting_languages_subtitle"), + networking('networking_settings', Icons.wifi, "networking_subtitle"), + notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), + preferences('preferences_settings_title', Icons.interests_outlined, "preferences_settings_subtitle"), + timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"), + beta('sync_status', Icons.sync_outlined, "sync_status_subtitle"); final String title; final String subtitle; final IconData icon; Widget get widget => switch (this) { - SettingSection.advanced => const AdvancedSettings(), - SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), - SettingSection.languages => const LanguageSettings(), - SettingSection.networking => const NetworkingSettings(), - SettingSection.notifications => const NotificationSetting(), - SettingSection.preferences => const PreferenceSetting(), - SettingSection.timeline => const AssetListSettings(), - }; + SettingSection.advanced => const AdvancedSettings(), + SettingSection.assetViewer => const AssetViewerSettings(), + SettingSection.backup => + Store.tryGet(StoreKey.betaTimeline) ?? false ? const DriftBackupSettings() : const BackupSettings(), + SettingSection.languages => const LanguageSettings(), + SettingSection.networking => const NetworkingSettings(), + SettingSection.notifications => const NotificationSetting(), + SettingSection.preferences => const PreferenceSetting(), + SettingSection.timeline => const AssetListSettings(), + SettingSection.beta => const SyncStatusAndActions(), + }; const SettingSection(this.title, this.icon, this.subtitle); } @@ -82,11 +57,8 @@ class SettingsPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: const Text('settings').tr(), - ), - body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), + appBar: AppBar(centerTitle: false, title: const Text('settings').tr()), + body: context.isMobile ? const SafeArea(child: _MobileLayout()) : const SafeArea(child: _TabletLayout()), ); } } @@ -96,58 +68,31 @@ class _MobileLayout extends StatelessWidget { @override Widget build(BuildContext context) { final List settings = SettingSection.values - .map( - (setting) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Card( - elevation: 0, - clipBehavior: Clip.antiAlias, - color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - margin: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - leading: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(16)), - color: context.isDarkTheme - ? Colors.black26 - : Colors.white.withAlpha(100), + .expand( + (setting) => setting == SettingSection.beta + ? [ + if (Store.isBetaTimelineEnabled) + SettingsCard( + icon: Icons.sync_outlined, + title: 'sync_status'.tr(), + subtitle: 'sync_status_subtitle'.tr(), + settingRoute: const SyncStatusRoute(), + ), + ] + : [ + SettingsCard( + title: setting.title.tr(), + subtitle: setting.subtitle.tr(), + icon: setting.icon, + settingRoute: SettingsSubRoute(section: setting), ), - padding: const EdgeInsets.all(16.0), - child: Icon(setting.icon, color: context.primaryColor), - ), - title: Text( - setting.title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ).tr(), - subtitle: Text( - setting.subtitle, - style: context.textTheme.labelLarge, - ).tr(), - onTap: () => - context.pushRoute(SettingsSubRoute(section: setting)), - ), - ), - ), + ], ) .toList(); return ListView( physics: const ClampingScrollPhysics(), - padding: const EdgeInsets.only(top: 10.0, bottom: 56), - children: [ - const BetaTimelineListTile(), - ...settings, - ], + padding: const EdgeInsets.only(top: 10.0, bottom: 16), + children: [...settings], ); } } @@ -156,8 +101,7 @@ class _TabletLayout extends HookWidget { const _TabletLayout(); @override Widget build(BuildContext context) { - final selectedSection = - useState(SettingSection.values.first); + final selectedSection = useState(SettingSection.values.first); return Row( mainAxisAlignment: MainAxisAlignment.start, @@ -165,27 +109,24 @@ class _TabletLayout extends HookWidget { Expanded( flex: 2, child: CustomScrollView( - slivers: SettingSection.values - .map( - (s) => SliverToBoxAdapter( - child: ListTile( - title: Text(s.title).tr(), - leading: Icon(s.icon), - selected: s.index == selectedSection.value.index, - selectedColor: context.primaryColor, - selectedTileColor: context.themeData.highlightColor, - onTap: () => selectedSection.value = s, - ), + slivers: [ + ...SettingSection.values.map( + (s) => SliverToBoxAdapter( + child: ListTile( + title: Text(s.title).tr(), + leading: Icon(s.icon), + selected: s.index == selectedSection.value.index, + selectedColor: context.primaryColor, + selectedTileColor: context.themeData.highlightColor, + onTap: () => selectedSection.value = s, ), - ) - .toList(), + ), + ), + ], ), ), const VerticalDivider(width: 1), - Expanded( - flex: 4, - child: selectedSection.value.widget, - ), + Expanded(flex: 4, child: selectedSection.value.widget), ], ); } @@ -201,10 +142,7 @@ class SettingsSubPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: Text(section.title).tr(), - ), + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), body: section.widget, ); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 598e920651..29b3dcd3be 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -1,11 +1,17 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:logging/logging.dart'; @@ -19,6 +25,7 @@ class SplashScreenPage extends StatefulHookConsumerWidget { class SplashScreenPageState extends ConsumerState { final log = Logger("SplashScreenPage"); + @override void initState() { super.initState(); @@ -42,65 +49,97 @@ class SplashScreenPageState extends ConsumerState { final endpoint = Store.tryGet(StoreKey.serverEndpoint); final accessToken = Store.tryGet(StoreKey.accessToken); - bool isAuthSuccess = false; - if (accessToken != null && serverUrl != null && endpoint != null) { - try { - isAuthSuccess = await ref.read(authProvider.notifier).saveAuthInfo( - accessToken: accessToken, - ); - } catch (error, stackTrace) { - log.severe( - 'Cannot set success login info', - error, - stackTrace, - ); - } - } else { - isAuthSuccess = false; - log.severe( - 'Missing authentication, server, or endpoint info from the local store', - ); - } + final infoProvider = ref.read(serverInfoProvider.notifier); + final wsProvider = ref.read(websocketProvider.notifier); + final backgroundManager = ref.read(backgroundSyncProvider); + final backupProvider = ref.read(driftBackupProvider.notifier); - if (!isAuthSuccess) { - log.severe( - 'Unable to login using offline or online methods - Logging out completely', + ref.read(authProvider.notifier).saveAuthInfo(accessToken: accessToken).then( + (_) async { + try { + wsProvider.connect(); + infoProvider.getServerInfo(); + + if (Store.isBetaTimelineEnabled) { + bool syncSuccess = false; + await Future.wait([ + backgroundManager.syncLocal(), + backgroundManager.syncRemote().then((success) => syncSuccess = success), + ]); + + if (syncSuccess) { + await Future.wait([ + backgroundManager.hashAssets().then((_) { + _resumeBackup(backupProvider); + }), + _resumeBackup(backupProvider), + ]); + } else { + await backgroundManager.hashAssets(); + } + + if (Store.get(StoreKey.syncAlbums, false)) { + await backgroundManager.syncLinkedAlbum(); + } + } + } catch (e) { + log.severe('Failed establishing connection to the server: $e'); + } + }, + onError: (exception) => { + log.severe('Failed to update auth info with access token: $accessToken'), + ref.read(authProvider.notifier).logout(), + context.replaceRoute(const LoginRoute()), + }, ); + } else { + log.severe('Missing crucial offline login info - Logging out completely'); ref.read(authProvider.notifier).logout(); context.replaceRoute(const LoginRoute()); return; } + // clean install - change the default of the flag + // current install not using beta timeline if (context.router.current.name == SplashScreenRoute.name) { - context.replaceRoute( - Store.isBetaTimelineEnabled - ? const TabShellRoute() - : const TabControllerRoute(), - ); + final needBetaMigration = Store.get(StoreKey.needBetaMigration, false); + if (needBetaMigration) { + await Store.put(StoreKey.needBetaMigration, false); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: true)]); + return; + } + + context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); } if (Store.isBetaTimelineEnabled) { return; } - final hasPermission = - await ref.read(galleryPermissionNotifier.notifier).hasPermission; + final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission; if (hasPermission) { // Resume backup (if enable) then navigate ref.watch(backupProvider.notifier).resumeBackup(); } } + Future _resumeBackup(DriftBackupNotifier notifier) async { + final isEnableBackup = Store.get(StoreKey.enableBackup, false); + + if (isEnableBackup) { + final currentUser = Store.tryGet(StoreKey.currentUser); + if (currentUser != null) { + notifier.handleBackupResume(currentUser.id); + } + } + } + @override Widget build(BuildContext context) { return const Scaffold( body: Center( - child: Image( - image: AssetImage('assets/immich-logo.png'), - width: 80, - filterQuality: FilterQuality.high, - ), + child: Image(image: AssetImage('assets/immich-logo.png'), width: 80, filterQuality: FilterQuality.high), ), ); } diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index e713b3f8da..ef637ba1c8 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -20,8 +20,7 @@ class TabControllerPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isRefreshingAssets = ref.watch(assetProvider); final isRefreshingRemoteAlbums = ref.watch(isRefreshingRemoteAlbumProvider); - final isScreenLandscape = - MediaQuery.orientationOf(context) == Orientation.landscape; + final isScreenLandscape = MediaQuery.orientationOf(context) == Orientation.landscape; Widget buildIcon({required Widget icon, required bool isProcessing}) { if (!isProcessing) return icon; @@ -37,9 +36,7 @@ class TabControllerPage extends HookConsumerWidget { width: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), + valueColor: AlwaysStoppedAnimation(context.primaryColor), ), ), ), @@ -66,51 +63,31 @@ class TabControllerPage extends HookConsumerWidget { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), + icon: const Icon(Icons.photo_library_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_library, color: context.primaryColor), ), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), + icon: const Icon(Icons.photo_album_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingRemoteAlbums, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), + icon: const Icon(Icons.space_dashboard_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ), ]; @@ -118,8 +95,7 @@ class TabControllerPage extends HookConsumerWidget { Widget bottomNavigationBar(TabsRouter tabsRouter) { return NavigationBar( selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => - onNavigationSelected(tabsRouter, index), + onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), destinations: navigationDestinations, ); } @@ -127,16 +103,9 @@ class TabControllerPage extends HookConsumerWidget { Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), - onDestinationSelected: (index) => - onNavigationSelected(tabsRouter, index), + onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), selectedIndex: tabsRouter.activeIndex, labelType: NavigationRailLabelType.all, groupAlignment: 0.0, @@ -145,23 +114,14 @@ class TabControllerPage extends HookConsumerWidget { final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( - routes: [ - const PhotosRoute(), - SearchRoute(), - const AlbumsRoute(), - const LibraryRoute(), - ], + routes: [const PhotosRoute(), SearchRoute(), const AlbumsRoute(), const LibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( canPop: tabsRouter.activeIndex == 0, - onPopInvokedWithResult: (didPop, _) => - !didPop ? tabsRouter.setActiveIndex(0) : null, + onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, child: Scaffold( resizeToAvoidBottomInset: false, body: isScreenLandscape @@ -173,9 +133,7 @@ class TabControllerPage extends HookConsumerWidget { ], ) : child, - bottomNavigationBar: multiselectEnabled || isScreenLandscape - ? null - : bottomNavigationBar(tabsRouter), + bottomNavigationBar: multiselectEnabled || isScreenLandscape ? null : bottomNavigationBar(tabsRouter), ), ); }, diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index b0be136a15..b60fe1ddc1 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -1,20 +1,21 @@ +import 'dart:async'; + import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart'; -import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; -import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/utils/migration.dart'; @RoutePage() class TabShellPage extends ConsumerStatefulWidget { @@ -25,69 +26,34 @@ class TabShellPage extends ConsumerStatefulWidget { } class _TabShellPageState extends ConsumerState { - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((_) async { - ref.read(websocketProvider.notifier).connect(); - - final isEnableBackup = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableBackup); - - await runNewSync(ref, full: true).then((_) async { - if (isEnableBackup) { - await ref.read(driftBackupProvider.notifier).handleBackupResume(); - } - }); - }); - } - @override Widget build(BuildContext context) { final isScreenLandscape = context.orientation == Orientation.landscape; + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), - selectedIcon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_library_outlined), + selectedIcon: Icon(Icons.photo_library, color: context.primaryColor), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), - selectedIcon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.photo_album_outlined), + selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), - selectedIcon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: const Icon(Icons.space_dashboard_outlined), + selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), + enabled: !isReadonlyModeEnabled, ), ]; @@ -99,11 +65,11 @@ class _TabShellPageState extends ConsumerState { icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon, + disabled: !e.enabled, ), ) .toList(), - onDestinationSelected: (index) => - _onNavigationSelected(tabsRouter, index, ref), + onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), selectedIndex: tabsRouter.activeIndex, labelType: NavigationRailLabelType.all, groupAlignment: 0.0, @@ -111,23 +77,14 @@ class _TabShellPageState extends ConsumerState { } return AutoTabsRouter( - routes: [ - const MainTimelineRoute(), - DriftSearchRoute(), - const DriftAlbumsRoute(), - const DriftLibraryRoute(), - ], + routes: [const MainTimelineRoute(), DriftSearchRoute(), const DriftAlbumsRoute(), const DriftLibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( canPop: tabsRouter.activeIndex == 0, - onPopInvokedWithResult: (didPop, _) => - !didPop ? tabsRouter.setActiveIndex(0) : null, + onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, child: Scaffold( resizeToAvoidBottomInset: false, body: isScreenLandscape @@ -139,10 +96,7 @@ class _TabShellPageState extends ConsumerState { ], ) : child, - bottomNavigationBar: _BottomNavigationBar( - tabsRouter: tabsRouter, - destinations: navigationDestinations, - ), + bottomNavigationBar: _BottomNavigationBar(tabsRouter: tabsRouter, destinations: navigationDestinations), ), ); }, @@ -153,7 +107,11 @@ class _TabShellPageState extends ConsumerState { void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { // On Photos page menu tapped if (router.activeIndex == 0 && index == 0) { - scrollToTopNotifierProvider.scrollToTop(); + EventStream.shared.emit(const ScrollToTopEvent()); + } + + if (index == 0) { + ref.invalidate(driftMemoryFutureProvider); } // On Search page tapped @@ -166,35 +124,61 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { ref.read(remoteAlbumProvider.notifier).refresh(); } + // Library page + if (index == 3) { + ref.invalidate(localAlbumProvider); + ref.invalidate(driftGetAllPeopleProvider); + } + ref.read(hapticFeedbackProvider.notifier).selectionClick(); router.setActiveIndex(index); ref.read(tabProvider.notifier).state = TabEnum.values[index]; } -class _BottomNavigationBar extends ConsumerWidget { - const _BottomNavigationBar({ - required this.tabsRouter, - required this.destinations, - }); +class _BottomNavigationBar extends ConsumerStatefulWidget { + const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; @override - Widget build(BuildContext context, WidgetRef ref) { - final isScreenLandscape = context.orientation == Orientation.landscape; - final isMultiselectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + ConsumerState createState() => _BottomNavigationBarState(); +} - if (isScreenLandscape || isMultiselectEnabled) { +class _BottomNavigationBarState extends ConsumerState<_BottomNavigationBar> { + bool hideNavigationBar = false; + StreamSubscription? _eventSubscription; + + @override + void initState() { + super.initState(); + _eventSubscription = EventStream.shared.listen(_onEvent); + } + + void _onEvent(MultiSelectToggleEvent event) { + setState(() { + hideNavigationBar = event.isEnabled; + }); + } + + @override + void dispose() { + _eventSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isScreenLandscape = context.orientation == Orientation.landscape; + + if (isScreenLandscape || hideNavigationBar) { return const SizedBox.shrink(); } return NavigationBar( - selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => - _onNavigationSelected(tabsRouter, index, ref), - destinations: destinations, + selectedIndex: widget.tabsRouter.activeIndex, + onDestinationSelected: (index) => _onNavigationSelected(widget.tabsRouter, index, ref), + destinations: widget.destinations, ); } } diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index f7f459c770..35fd615800 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -32,20 +32,10 @@ class CropImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute( - EditImageRoute( - asset: asset, - image: croppedImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); }, ), ], @@ -60,11 +50,7 @@ class CropImagePage extends HookWidget { padding: const EdgeInsets.only(top: 20), width: constraints.maxWidth * 0.9, height: constraints.maxHeight * 0.6, - child: CropImage( - controller: cropController, - image: image, - gridColor: Colors.white, - ), + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), ), Expanded( child: Container( @@ -81,28 +67,18 @@ class CropImagePage extends HookWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - bottom: 10, - ), + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( - icon: Icon( - Icons.rotate_left, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateLeft(); }, ), IconButton( - icon: Icon( - Icons.rotate_right, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateRight(); }, @@ -178,19 +154,14 @@ class _AspectRatioButton extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: Icon( - switch (label) { - 'Free' => Icons.crop_free_rounded, - '1:1' => Icons.crop_square_rounded, - '16:9' => Icons.crop_16_9_rounded, - '3:2' => Icons.crop_3_2_rounded, - '7:5' => Icons.crop_7_5_rounded, - _ => Icons.crop_free_rounded, - }, - color: aspectRatio.value == ratio - ? context.primaryColor - : context.themeData.iconTheme.color, - ), + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), onPressed: () { cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); aspectRatio.value = ratio; diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index d37941f4fe..c9ab014456 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -29,54 +29,34 @@ class EditImagePage extends ConsumerWidget { final Image image; final bool isEdited; - const EditImagePage({ - super.key, - required this.asset, - required this.image, - required this.isEdited, - }); + const EditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); Future _imageToUint8List(Image image) async { final Completer completer = Completer(); - image.image.resolve(const ImageConfiguration()).addListener( - ImageStreamListener( - (ImageInfo info, bool _) { - info.image - .toByteData(format: ImageByteFormat.png) - .then((byteData) { - if (byteData != null) { - completer.complete(byteData.buffer.asUint8List()); - } else { - completer.completeError('Failed to convert image to bytes'); - } - }); - }, - onError: (exception, stackTrace) => - completer.completeError(exception), - ), + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), ); return completer.future; } - Future _saveEditedImage( - BuildContext context, - Asset asset, - Image image, - WidgetRef ref, - ) async { + Future _saveEditedImage(BuildContext context, Asset asset, Image image, WidgetRef ref) async { try { final Uint8List imageData = await _imageToUint8List(image); - await ref.read(fileMediaRepositoryProvider).saveImage( - imageData, - title: "${p.withoutExtension(asset.fileName)}_edited.jpg", - ); + await ref + .read(fileMediaRepositoryProvider) + .saveImage(imageData, title: "${p.withoutExtension(asset.fileName)}_edited.jpg"); await ref.read(albumProvider.notifier).refreshDeviceAlbums(); context.navigator.popUntil((route) => route.isFirst); - ImmichToast.show( - durationInSecond: 3, - context: context, - msg: 'Image Saved!', - gravity: ToastGravity.CENTER, - ); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!', gravity: ToastGravity.CENTER); } catch (e) { ImmichToast.show( durationInSecond: 6, @@ -94,39 +74,23 @@ class EditImagePage extends ConsumerWidget { title: Text("edit".tr()), backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( - icon: Icon( - Icons.close_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), onPressed: () => context.navigator.popUntil((route) => route.isFirst), ), actions: [ TextButton( - onPressed: isEdited - ? () => _saveEditedImage(context, asset, image, ref) - : null, - child: Text( - "save_to_gallery".tr(), - style: TextStyle( - color: isEdited ? context.primaryColor : Colors.grey, - ), - ), + onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), ), ], ), backgroundColor: context.scaffoldBackgroundColor, body: Center( child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: context.height * 0.7, - maxWidth: context.width * 0.9, - ), + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), child: Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.2), @@ -137,13 +101,8 @@ class EditImagePage extends ConsumerWidget { ], ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), - child: Image( - image: image.image, - fit: BoxFit.contain, - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), ), ), ), @@ -153,9 +112,7 @@ class EditImagePage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( color: context.scaffoldBackgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(30), - ), + borderRadius: const BorderRadius.all(Radius.circular(30)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -164,15 +121,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.crop_rotate_rounded, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - CropImageRoute(asset: asset, image: image), - ); + context.pushRoute(CropImageRoute(asset: asset, image: image)); }, ), Text("crop".tr(), style: context.textTheme.displayMedium), @@ -182,18 +133,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.filter, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - FilterImageRoute( - asset: asset, - image: image, - ), - ); + context.pushRoute(FilterImageRoute(asset: asset, image: image)); }, ), Text("filter".tr(), style: context.textTheme.displayMedium), diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index 9af065489d..6d41b4c5b8 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -18,34 +18,23 @@ class FilterImagePage extends HookWidget { final Image image; final Asset asset; - const FilterImagePage({ - super.key, - required this.image, - required this.asset, - }); + const FilterImagePage({super.key, required this.image, required this.asset}); @override Widget build(BuildContext context) { final colorFilter = useState(filters[0]); final selectedFilterIndex = useState(0); - Future createFilteredImage( - ui.Image inputImage, - ColorFilter filter, - ) { + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { final completer = Completer(); - final size = - Size(inputImage.width.toDouble(), inputImage.height.toDouble()); + final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); final paint = Paint()..colorFilter = filter; canvas.drawImage(inputImage, Offset.zero, paint); - recorder - .endRecording() - .toImage(size.width.round(), size.height.round()) - .then((image) { + recorder.endRecording().toImage(size.width.round(), size.height.round()).then((image) { completer.complete(image); }); @@ -59,16 +48,17 @@ class FilterImagePage extends HookWidget { Future applyFilterAndConvert(ColorFilter filter) async { final completer = Completer(); - image.image.resolve(ImageConfiguration.empty).addListener( - ImageStreamListener((ImageInfo info, bool _) { - completer.complete(info.image); - }), - ); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); final uiImage = await completer.future; final filteredUiImage = await createFilteredImage(uiImage, filter); - final byteData = - await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); + final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); final pngBytes = byteData!.buffer.asUint8List(); return Image.memory(pngBytes, fit: BoxFit.contain); @@ -81,21 +71,10 @@ class FilterImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { - final filteredImage = - await applyFilterAndConvert(colorFilter.value); - context.pushRoute( - EditImageRoute( - asset: asset, - image: filteredImage, - isEdited: true, - ), - ); + final filteredImage = await applyFilterAndConvert(colorFilter.value); + context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true)); }, ), ], @@ -106,10 +85,7 @@ class FilterImagePage extends HookWidget { SizedBox( height: context.height * 0.7, child: Center( - child: ColorFiltered( - colorFilter: colorFilter.value, - child: image, - ), + child: ColorFiltered(colorFilter: colorFilter.value, child: image), ), ), SizedBox( @@ -162,23 +138,14 @@ class _FilterButton extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), - border: isSelected - ? Border.all(color: context.primaryColor, width: 3) - : null, + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), child: ColorFiltered( colorFilter: filter, - child: FittedBox( - fit: BoxFit.cover, - child: image, - ), + child: FittedBox(fit: BoxFit.cover, child: image), ), ), ), diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index 2b4aa64f3b..8ca1bb9752 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -16,15 +16,10 @@ class ArchivePage extends HookConsumerWidget { final archiveRenderList = ref.watch(archiveTimelineProvider); final count = archiveRenderList.value?.totalAssets.toString() ?? "?"; return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'archive_page_title', - ).tr(namedArgs: {'count': count}), + title: const Text('archive_page_title').tr(namedArgs: {'count': count}), ); } diff --git a/mobile/lib/pages/library/favorite.page.dart b/mobile/lib/pages/library/favorite.page.dart index 070693fe4a..649d7727d5 100644 --- a/mobile/lib/pages/library/favorite.page.dart +++ b/mobile/lib/pages/library/favorite.page.dart @@ -14,15 +14,10 @@ class FavoritesPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { AppBar buildAppBar() { return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'favorites', - ).tr(), + title: const Text('favorites').tr(), ); } diff --git a/mobile/lib/pages/library/folder/folder.page.dart b/mobile/lib/pages/library/folder/folder.page.dart index 6ac7d60f9b..2968bca18e 100644 --- a/mobile/lib/pages/library/folder/folder.page.dart +++ b/mobile/lib/pages/library/folder/folder.page.dart @@ -16,14 +16,9 @@ import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -RecursiveFolder? _findFolderInStructure( - RootFolder rootFolder, - RecursiveFolder targetFolder, -) { +RecursiveFolder? _findFolderInStructure(RootFolder rootFolder, RecursiveFolder targetFolder) { for (final folder in rootFolder.subfolders) { - if (targetFolder.path == '/' && - folder.path.isEmpty && - folder.name == targetFolder.name) { + if (targetFolder.path == '/' && folder.path.isEmpty && folder.name == targetFolder.name) { return folder; } @@ -51,36 +46,26 @@ class FolderPage extends HookConsumerWidget { final currentFolder = useState(folder); final sortOrder = useState(SortOrder.asc); - useEffect( - () { - if (folder == null) { - ref - .read(folderStructureProvider.notifier) - .fetchFolders(sortOrder.value); - } - return null; - }, - [], - ); + useEffect(() { + if (folder == null) { + ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); + } + return null; + }, []); // Update current folder when root structure changes - useEffect( - () { - if (folder != null && folderState.hasValue) { - final updatedFolder = - _findFolderInStructure(folderState.value!, folder!); - if (updatedFolder != null) { - currentFolder.value = updatedFolder; - } + useEffect(() { + if (folder != null && folderState.hasValue) { + final updatedFolder = _findFolderInStructure(folderState.value!, folder!); + if (updatedFolder != null) { + currentFolder.value = updatedFolder; } - return null; - }, - [folderState], - ); + } + return null; + }, [folderState]); void onToggleSortOrder() { - final newOrder = - sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; + final newOrder = sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; ref.read(folderStructureProvider.notifier).fetchFolders(newOrder); @@ -92,38 +77,19 @@ class FolderPage extends HookConsumerWidget { title: Text(currentFolder.value?.name ?? tr("folders")), elevation: 0, centerTitle: false, - actions: [ - IconButton( - icon: const Icon(Icons.swap_vert), - onPressed: onToggleSortOrder, - ), - ], + actions: [IconButton(icon: const Icon(Icons.swap_vert), onPressed: onToggleSortOrder)], ), body: folderState.when( data: (rootFolder) { if (folder == null) { - return FolderContent( - folder: rootFolder, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: rootFolder, root: rootFolder, sortOrder: sortOrder.value); } else { - return FolderContent( - folder: currentFolder.value!, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: currentFolder.value!, root: rootFolder, sortOrder: sortOrder.value); } }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_folder".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_folder".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_folder").tr()); }, ), @@ -136,28 +102,18 @@ class FolderContent extends HookConsumerWidget { final RootFolder root; final SortOrder sortOrder; - const FolderContent({ - super.key, - this.folder, - required this.root, - this.sortOrder = SortOrder.asc, - }); + const FolderContent({super.key, this.folder, required this.root, this.sortOrder = SortOrder.asc}); @override Widget build(BuildContext context, WidgetRef ref) { final folderRenderlist = ref.watch(folderRenderListProvider(folder!)); // Initial asset fetch - useEffect( - () { - if (folder == null) return; - ref - .read(folderRenderListProvider(folder!).notifier) - .fetchAssets(sortOrder); - return null; - }, - [folder], - ); + useEffect(() { + if (folder == null) return; + ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); + return null; + }, [folder]); if (folder == null) { return Center(child: const Text("folder_not_found").tr()); @@ -190,18 +146,12 @@ class FolderContent extends HookConsumerWidget { if (folder!.subfolders.isNotEmpty) ...folder!.subfolders.map( (subfolder) => LargeLeadingTile( - leading: Icon( - Icons.folder, - color: context.primaryColor, - size: 48, - ), + leading: Icon(Icons.folder, color: context.primaryColor, size: 48), title: Text( subfolder.name, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: subfolder.subfolders.isNotEmpty ? Text( @@ -211,35 +161,24 @@ class FolderContent extends HookConsumerWidget { ), ) : null, - onTap: () => - context.pushRoute(FolderRoute(folder: subfolder)), + onTap: () => context.pushRoute(FolderRoute(folder: subfolder)), ), ), - if (!list.isEmpty && - list.allAssets != null && - list.allAssets!.isNotEmpty) + if (!list.isEmpty && list.allAssets != null && list.allAssets!.isNotEmpty) ...list.allAssets!.map( (asset) => LargeLeadingTile( onTap: () { ref.read(currentAssetProvider.notifier).set(asset); context.pushRoute( - GalleryViewerRoute( - renderList: list, - initialIndex: list.allAssets!.indexOf(asset), - ), + GalleryViewerRoute(renderList: list, initialIndex: list.allAssets!.indexOf(asset)), ); }, leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), child: SizedBox( width: 80, height: 80, - child: ThumbnailImage( - asset: asset, - showStorageIndicator: false, - ), + child: ThumbnailImage(asset: asset, showStorageIndicator: false), ), ), title: Text( @@ -247,30 +186,20 @@ class FolderContent extends HookConsumerWidget { maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( "${asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo?.fileSize ?? 0) : ""} â€ĸ ${DateFormat.yMMMd().format(asset.fileCreatedAt)}", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), ], ); }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_assets".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_assets".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_assets").tr()); }, ), @@ -284,11 +213,7 @@ class FolderPath extends StatelessWidget { final RootFolder currentFolder; final RootFolder root; - const FolderPath({ - super.key, - required this.currentFolder, - required this.root, - }); + const FolderPath({super.key, required this.currentFolder, required this.root}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index a9ef479e2c..483427d2de 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -25,8 +25,7 @@ class LibraryPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { context.locale; - final trashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final trashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); return Scaffold( appBar: const ImmichAppBar(), @@ -75,17 +74,11 @@ class LibraryPage extends ConsumerWidget { const Wrap( spacing: 8, runSpacing: 8, - children: [ - PeopleCollectionCard(), - PlacesCollectionCard(), - LocalAlbumsCollectionCard(), - ], + children: [PeopleCollectionCard(), PlacesCollectionCard(), LocalAlbumsCollectionCard()], ), const SizedBox(height: 12), const QuickAccessButtons(), - const SizedBox( - height: 32, - ), + const SizedBox(height: 32), ], ), ), @@ -101,13 +94,8 @@ class QuickAccessButtons extends ConsumerWidget { return Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -131,41 +119,26 @@ class QuickAccessButtons extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( IntlKeys.folders.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( IntlKeys.locked_folder.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const LockedRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( IntlKeys.partners.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const PartnerRoute()), ), @@ -197,24 +170,13 @@ class PartnerList extends ConsumerWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), leading: userAvatar(context, partner, radius: 16), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), - ).tr( - namedArgs: { - 'user': partner.name, - }, - ), - onTap: () => context.pushRoute( - (PartnerDetailRoute(partner: partner)), - ), + style: TextStyle(fontWeight: FontWeight.w500), + ).tr(namedArgs: {'user': partner.name}), + onTap: () => context.pushRoute((PartnerDetailRoute(partner: partner))), ); }, ); @@ -242,22 +204,15 @@ class PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -309,9 +264,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - const LocalAlbumsRoute(), - ), + onTap: () => context.pushRoute(const LocalAlbumsRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -322,10 +275,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -337,10 +287,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { mainAxisSpacing: 8, physics: const NeverScrollableScrollPhysics(), children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); + return AlbumThumbnailCard(album: album, showTitle: false); }).toList(), ), ), @@ -374,11 +321,7 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - PlacesCollectionRoute( - currentLocation: null, - ), - ), + onTap: () => context.pushRoute(PlacesCollectionRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -388,20 +331,14 @@ class PlacesCollectionCard extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), - color: - context.colorScheme.secondaryContainer.withAlpha(100), + color: context.colorScheme.secondaryContainer.withAlpha(100), ), child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: context.isDarkTheme - ? ThemeMode.dark - : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -429,12 +366,7 @@ class ActionButton extends StatelessWidget { final IconData icon; final String label; - const ActionButton({ - super.key, - required this.onPressed, - required this.icon, - required this.label, - }); + const ActionButton({super.key, required this.onPressed, required this.icon, required this.label}); @override Widget build(BuildContext context) { @@ -443,13 +375,7 @@ class ActionButton extends StatelessWidget { onPressed: onPressed, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -458,16 +384,10 @@ class ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 9eceaca205..e52a8326df 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -18,9 +18,7 @@ class LocalAlbumsPage extends HookConsumerWidget { final albums = ref.watch(localAlbumsProvider); return Scaffold( - appBar: AppBar( - title: Text('on_this_device'.tr()), - ), + appBar: AppBar(title: Text('on_this_device'.tr())), body: ListView.builder( padding: const EdgeInsets.all(18.0), itemCount: albums.length, @@ -28,34 +26,20 @@ class LocalAlbumsPage extends HookConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(15)), - child: ImmichThumbnail( - asset: albums[index].thumbnail.value, - width: 80, - height: 80, - ), + child: ImmichThumbnail(asset: albums[index].thumbnail.value, width: 80, height: 80), ), title: Text( albums[index].name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': albums[index].assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': albums[index].assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), - onTap: () => context - .pushRoute(AlbumViewerRoute(albumId: albums[index].id)), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: albums[index].id)), ), ); }, diff --git a/mobile/lib/pages/library/locked/locked.page.dart b/mobile/lib/pages/library/locked/locked.page.dart index eef12a7107..aea62e0051 100644 --- a/mobile/lib/pages/library/locked/locked.page.dart +++ b/mobile/lib/pages/library/locked/locked.page.dart @@ -19,29 +19,23 @@ class LockedPage extends HookConsumerWidget { final showOverlay = useState(false); final authProviderNotifier = ref.read(authProvider.notifier); // lock the page when it is destroyed - useEffect( - () { - return () { - authProviderNotifier.lockPinCode(); - }; - }, - [], - ); + useEffect(() { + return () { + authProviderNotifier.lockPinCode(); + }; + }, []); - useEffect( - () { - if (context.mounted) { - if (appLifeCycle == AppLifecycleState.resumed) { - showOverlay.value = false; - } else { - showOverlay.value = true; - } + useEffect(() { + if (context.mounted) { + if (appLifeCycle == AppLifecycleState.resumed) { + showOverlay.value = false; + } else { + showOverlay.value = true; } + } - return null; - }, - [appLifeCycle], - ); + return null; + }, [appLifeCycle]); return Scaffold( appBar: ref.watch(multiselectProvider) ? null : const LockPageAppBar(), @@ -51,12 +45,7 @@ class LockedPage extends HookConsumerWidget { renderListProvider: lockedTimelineProvider, topWidget: Padding( padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - 'no_locked_photos_message'.tr(), - style: context.textTheme.labelLarge, - ), - ), + child: Center(child: Text('no_locked_photos_message'.tr(), style: context.textTheme.labelLarge)), ), editEnabled: false, favoriteEnabled: false, @@ -84,9 +73,7 @@ class LockPageAppBar extends ConsumerWidget implements PreferredSizeWidget { ), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'locked_folder', - ).tr(), + title: const Text('locked_folder').tr(), ); } diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index 9bfd96ed74..36befa0016 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -23,19 +23,12 @@ class PinAuthPage extends HookConsumerWidget { final isBetaTimeline = Store.isBetaTimelineEnabled; Future registerBiometric(String pinCode) async { - final isRegistered = - await ref.read(localAuthProvider.notifier).registerBiometric( - context, - pinCode, - ); + final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric(context, pinCode); if (isRegistered) { context.showSnackBar( SnackBar( - content: Text( - 'biometric_auth_enabled'.tr(), - style: context.textTheme.labelLarge, - ), + content: Text('biometric_auth_enabled'.tr(), style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.primaryContainer, ), @@ -80,20 +73,14 @@ class PinAuthPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: Text('locked_folder'.tr()), - ), + appBar: AppBar(title: Text('locked_folder'.tr())), body: ListView( shrinkWrap: true, children: [ Padding( padding: const EdgeInsets.only(top: 36.0), child: showPinRegistrationForm.value - ? Center( - child: PinRegistrationForm( - onDone: () => showPinRegistrationForm.value = false, - ), - ) + ? Center(child: PinRegistrationForm(onDone: () => showPinRegistrationForm.value = false)) : Column( children: [ Center( @@ -101,8 +88,7 @@ class PinAuthPage extends HookConsumerWidget { autoFocus: true, onSuccess: (_) { if (isBetaTimeline) { - context - .replaceRoute(const DriftLockedFolderRoute()); + context.replaceRoute(const DriftLockedFolderRoute()); } else { context.replaceRoute(const LockedRoute()); } @@ -114,17 +100,11 @@ class PinAuthPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(right: 16.0), child: TextButton.icon( - icon: const Icon( - Icons.fingerprint, - size: 28, - ), + icon: const Icon(Icons.fingerprint, size: 28), onPressed: enableBiometricAuth, label: Text( 'use_biometric'.tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - fontSize: 18, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor, fontSize: 18), ), ), ), diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart index 04efbe066c..d81cc44c76 100644 --- a/mobile/lib/pages/library/partner/drift_partner.page.dart +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; @@ -22,10 +22,7 @@ class DriftPartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final potentialPartners = potentialPartnersAsync.value; if (potentialPartners == null || potentialPartners.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -63,10 +60,8 @@ class DriftPartnerPage extends HookConsumerWidget { builder: (BuildContext context) { return ConfirmDialog( title: "stop_photo_sharing", - content: "partner_page_stop_sharing_content" - .tr(namedArgs: {'partner': partner.name}), - onOk: () => - ref.read(partnerUsersProvider.notifier).removePartner(partner), + content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': partner.name}), + onOk: () => ref.read(partnerUsersProvider.notifier).removePartner(partner), ); }, ); @@ -79,18 +74,13 @@ class DriftPartnerPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - onPressed: potentialPartnersAsync.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: potentialPartnersAsync.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), tooltip: "add_partner".tr(), ), ], ), - body: _SharedToPartnerList( - onAddPartner: addNewUsersHandler, - onDeletePartner: onDeleteUser, - ), + body: _SharedToPartnerList(onAddPartner: addNewUsersHandler, onDeletePartner: onDeleteUser), ); } } @@ -99,10 +89,7 @@ class _SharedToPartnerList extends ConsumerWidget { final VoidCallback onAddPartner; final Function(PartnerUserDto partner) onDeletePartner; - const _SharedToPartnerList({ - required this.onAddPartner, - required this.onDeletePartner, - }); + const _SharedToPartnerList({required this.onAddPartner, required this.onDeletePartner}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -118,10 +105,7 @@ class _SharedToPartnerList extends ConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, @@ -144,18 +128,13 @@ class _SharedToPartnerList extends ConsumerWidget { leading: PartnerUserAvatar(partner: partner), title: Text(partner.name), subtitle: Text(partner.email), - trailing: IconButton( - icon: const Icon(Icons.person_remove), - onPressed: () => onDeletePartner(partner), - ), + trailing: IconButton(icon: const Icon(Icons.person_remove), onPressed: () => onDeletePartner(partner)), ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center( - child: Text("Error loading partners: $error"), - ), + error: (error, stack) => Center(child: Text('error_loading_partners'.tr(args: [error.toString()]))), ); } } diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index 91b661e7ce..eae4228a2d 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -22,10 +22,7 @@ class PartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final users = availableUsers.value; if (users == null || users.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -40,10 +37,7 @@ class PartnerPage extends HookConsumerWidget { onPressed: () => context.pop(u), child: Row( children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: userAvatar(context, u), - ), + Padding(padding: const EdgeInsets.only(right: 8), child: userAvatar(context, u)), Text(u.name), ], ), @@ -53,16 +47,11 @@ class PartnerPage extends HookConsumerWidget { }, ); if (selectedUser != null) { - final ok = - await ref.read(partnerServiceProvider).addPartner(selectedUser); + final ok = await ref.read(partnerServiceProvider).addPartner(selectedUser); if (ok) { ref.invalidate(partnerSharedByProvider); } else { - ImmichToast.show( - context: context, - msg: "partner_page_partner_add_failed".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "partner_page_partner_add_failed".tr(), toastType: ToastType.error); } } } @@ -73,8 +62,7 @@ class PartnerPage extends HookConsumerWidget { builder: (BuildContext context) { return ConfirmDialog( title: "stop_photo_sharing", - content: "partner_page_stop_sharing_content" - .tr(namedArgs: {'partner': u.name}), + content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': u.name}), onOk: () => ref.read(partnerServiceProvider).removePartner(u), ); }, @@ -89,9 +77,7 @@ class PartnerPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: Text( "partner_page_shared_to_title", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ).tr(), ), if (users.isNotEmpty) @@ -101,10 +87,7 @@ class PartnerPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: userAvatar(context, users[index]), - title: Text( - users[index].email, - style: context.textTheme.bodyLarge, - ), + title: Text(users[index].email, style: context.textTheme.bodyLarge), trailing: IconButton( icon: const Icon(Icons.person_remove), onPressed: () => onDeleteUser(users[index]), @@ -120,17 +103,12 @@ class PartnerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, child: ElevatedButton.icon( - onPressed: availableUsers.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), label: const Text("add_partner").tr(), ), @@ -149,8 +127,7 @@ class PartnerPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - onPressed: - availableUsers.whenOrNull(data: (data) => addNewUsersHandler), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), tooltip: "add_partner".tr(), ), diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 94e098b973..1f15dab6a3 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -22,17 +22,10 @@ class PartnerDetailPage extends HookConsumerWidget { final inTimeline = useState(partner.inTimeline); bool toggleInProcess = false; - useEffect( - () { - Future.microtask( - () async => { - await ref.read(assetProvider.notifier).getAllAsset(), - }, - ); - return null; - }, - [], - ); + useEffect(() { + Future.microtask(() async => {await ref.read(assetProvider.notifier).getAllAsset()}); + return null; + }, []); void toggleInTimeline() async { if (toggleInProcess) return; @@ -66,28 +59,16 @@ class PartnerDetailPage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null - : AppBar( - title: Text(partner.name), - elevation: 0, - centerTitle: false, - ), + : AppBar(title: Text(partner.name), elevation: 0, centerTitle: false), body: MultiselectGrid( topWidget: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -97,18 +78,13 @@ class PartnerDetailPage extends HookConsumerWidget { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: inTimeline.value, - onChanged: (_) => toggleInTimeline(), - ), + trailing: Switch(value: inTimeline.value, onChanged: (_) => toggleInTimeline()), ), ), ), diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index b98e46aabe..375d4d2a96 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -21,10 +21,7 @@ class PeopleCollectionPage extends HookConsumerWidget { final formFocus = useFocusNode(); final ValueNotifier search = useState(null); - showNameEditModel( - String personId, - String personName, - ) { + showNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -66,9 +63,7 @@ class PeopleCollectionPage extends HookConsumerWidget { data: (people) { if (search.value != null) { people = people.where((person) { - return person.name - .toLowerCase() - .contains(search.value!.toLowerCase()); + return person.name.toLowerCase().contains(search.value!.toLowerCase()); }).toList(); } return GridView.builder( @@ -86,29 +81,20 @@ class PeopleCollectionPage extends HookConsumerWidget { children: [ GestureDetector( onTap: () { - context.pushRoute( - PersonResultRoute( - personId: person.id, - personName: person.name, - ), - ); + context.pushRoute(PersonResultRoute(personId: person.id, personName: person.name)); }, child: Material( shape: const CircleBorder(side: BorderSide.none), elevation: 3, child: CircleAvatar( maxRadius: isTablet ? 120 / 2 : 96 / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), const SizedBox(height: 12), GestureDetector( - onTap: () => - showNameEditModel(person.id, person.name), + onTap: () => showNameEditModel(person.id, person.name), child: person.name.isEmpty ? Text( 'add_a_name'.tr(), @@ -118,16 +104,11 @@ class PeopleCollectionPage extends HookConsumerWidget { ), ) : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( person.name, overflow: TextOverflow.ellipsis, - style: - context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), ), ), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index b98537d515..f376709316 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -59,17 +59,11 @@ class PlacesCollectionPage extends HookConsumerWidget { height: 200, width: context.width, child: MapThumbnail( - onTap: (_, __) => context - .pushRoute(MapRoute(initialLocation: currentLocation)), + onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -77,9 +71,7 @@ class PlacesCollectionPage extends HookConsumerWidget { data: (places) { if (search.value != null) { places = places.where((place) { - return place.label - .toLowerCase() - .contains(search.value!.toLowerCase()); + return place.label.toLowerCase().contains(search.value!.toLowerCase()); }).toList(); } return ListView.builder( @@ -93,7 +85,7 @@ class PlacesCollectionPage extends HookConsumerWidget { }, ); }, - error: (error, stask) => const Text('Error getting places'), + error: (error, stask) => Text('error_getting_places'.tr()), loading: () => const Center(child: CircularProgressIndicator()), ), ], @@ -110,24 +102,17 @@ class PlaceTile extends StatelessWidget { @override Widget build(BuildContext context) { - final thumbnailUrl = - '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail'; + final thumbnailUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail'; void navigateToPlace() { context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: name, - ), + location: SearchLocationFilter(city: name), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), @@ -136,24 +121,16 @@ class PlaceTile extends StatelessWidget { return LargeLeadingTile( onTap: () => navigateToPlace(), - title: Text( - name, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(name, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: CachedNetworkImage( width: 80, height: 80, fit: BoxFit.cover, imageUrl: thumbnailUrl, httpHeaders: ApiService.getRequestHeaders(), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ), ); diff --git a/mobile/lib/pages/library/shared_link/shared_link.page.dart b/mobile/lib/pages/library/shared_link/shared_link.page.dart index 8873e9b443..66a77fb761 100644 --- a/mobile/lib/pages/library/shared_link/shared_link.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link.page.dart @@ -17,16 +17,13 @@ class SharedLinkPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final sharedLinks = ref.watch(sharedLinksStateProvider); - useEffect( - () { - ref.read(sharedLinksStateProvider.notifier).fetchLinks(); - return () { - if (!context.mounted) return; - ref.invalidate(sharedLinksStateProvider); - }; - }, - [], - ); + useEffect(() { + ref.read(sharedLinksStateProvider.notifier).fetchLinks(); + return () { + if (!context.mounted) return; + ref.invalidate(sharedLinksStateProvider); + }; + }, []); Widget buildNoShares() { return Column( @@ -36,31 +33,19 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: const Text( "shared_link_manage_links", - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), - child: const Text( - "you_dont_have_any_shared_links", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("you_dont_have_any_shared_links", style: TextStyle(fontSize: 14)).tr(), ), ), Expanded( child: Center( - child: Icon( - Icons.link_off, - size: 100, - color: - context.themeData.iconTheme.color?.withValues(alpha: 0.5), - ), + child: Icon(Icons.link_off, size: 100, color: context.themeData.iconTheme.color?.withValues(alpha: 0.5)), ), ), ], @@ -75,9 +60,7 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 30.0), child: Text( "shared_link_manage_links", - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), - ), + style: context.textTheme.labelLarge?.copyWith(color: context.textTheme.labelLarge?.color?.withAlpha(200)), ).tr(), ), Expanded( @@ -86,8 +69,7 @@ class SharedLinkPage extends HookConsumerWidget { if (constraints.maxWidth > 600) { // Two column return GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisExtent: 180, ), @@ -113,16 +95,11 @@ class SharedLinkPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("shared_link_app_bar_title").tr(), - elevation: 0, - centerTitle: false, - ), + appBar: AppBar(title: const Text("shared_link_app_bar_title").tr(), elevation: 0, centerTitle: false), body: SafeArea( child: sharedLinks.widgetWhen( onError: (error, stackTrace) => buildNoShares(), - onData: (links) => - links.isNotEmpty ? buildSharesList(links) : buildNoShares(), + onData: (links) => links.isNotEmpty ? buildSharesList(links) : buildNoShares(), ), ), ); diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index 6c18841089..8b66bb231f 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -19,23 +19,16 @@ class SharedLinkEditPage extends HookConsumerWidget { final List? assetsList; final String? albumId; - const SharedLinkEditPage({ - super.key, - this.existingLink, - this.assetsList, - this.albumId, - }); + const SharedLinkEditPage({super.key, this.existingLink, this.assetsList, this.albumId}); @override Widget build(BuildContext context, WidgetRef ref) { const padding = 20.0; final themeData = context.themeData; final colorScheme = context.colorScheme; - final descriptionController = - useTextEditingController(text: existingLink?.description ?? ""); + final descriptionController = useTextEditingController(text: existingLink?.description ?? ""); final descriptionFocusNode = useFocusNode(); - final passwordController = - useTextEditingController(text: existingLink?.password ?? ""); + final passwordController = useTextEditingController(text: existingLink?.password ?? ""); final showMetadata = useState(existingLink?.showMetadata ?? true); final allowDownload = useState(existingLink?.allowDownload ?? true); final allowUpload = useState(existingLink?.allowUpload ?? false); @@ -48,20 +41,11 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.album) { return Row( children: [ - const Text( - 'public_album', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('public_album', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Text( existingLink!.title, - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), ), ], ); @@ -70,21 +54,12 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.individual) { return Row( children: [ - const Text( - 'shared_link_individual_shared', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('shared_link_individual_shared', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: Text( existingLink!.description ?? "--", - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ), @@ -93,10 +68,7 @@ class SharedLinkEditPage extends HookConsumerWidget { } } - return const Text( - "create_link_to_share_description", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(); + return const Text("create_link_to_share_description", style: TextStyle(fontWeight: FontWeight.bold)).tr(); } Widget buildDescriptionField() { @@ -108,20 +80,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'description'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_description_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), onTapOutside: (_) => descriptionFocusNode.unfocus(), ); @@ -134,20 +98,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'password'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), ); } @@ -155,31 +111,22 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildShowMetaButton() { return SwitchListTile.adaptive( value: showMetadata.value, - onChanged: newShareLink.value.isEmpty - ? (value) => showMetadata.value = value - : null, - activeColor: colorScheme.primary, + onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null, + activeThumbColor: colorScheme.primary, dense: true, - title: Text( - "show_metadata", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text("show_metadata", style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), ); } Widget buildAllowDownloadButton() { return SwitchListTile.adaptive( value: allowDownload.value, - onChanged: newShareLink.value.isEmpty - ? (value) => allowDownload.value = value - : null, - activeColor: colorScheme.primary, + onChanged: newShareLink.value.isEmpty ? (value) => allowDownload.value = value : null, + activeThumbColor: colorScheme.primary, dense: true, title: Text( "allow_public_user_to_download", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -187,15 +134,12 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildAllowUploadButton() { return SwitchListTile.adaptive( value: allowUpload.value, - onChanged: newShareLink.value.isEmpty - ? (value) => allowUpload.value = value - : null, - activeColor: colorScheme.primary, + onChanged: newShareLink.value.isEmpty ? (value) => allowUpload.value = value : null, + activeThumbColor: colorScheme.primary, dense: true, title: Text( "allow_public_user_to_upload", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -203,15 +147,12 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildEditExpiryButton() { return SwitchListTile.adaptive( value: editExpiry.value, - onChanged: newShareLink.value.isEmpty - ? (value) => editExpiry.value = value - : null, - activeColor: colorScheme.primary, + onChanged: newShareLink.value.isEmpty ? (value) => editExpiry.value = value : null, + activeThumbColor: colorScheme.primary, dense: true, title: Text( "change_expiration_time", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -220,62 +161,43 @@ class SharedLinkEditPage extends HookConsumerWidget { return DropdownMenu( label: Text( "expire_after", - style: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), ).tr(), enableSearch: false, enableFilter: false, width: context.width - 40, initialSelection: expiryAfter.value, - enabled: newShareLink.value.isEmpty && - (existingLink == null || editExpiry.value), + enabled: newShareLink.value.isEmpty && (existingLink == null || editExpiry.value), onSelected: (value) { expiryAfter.value = value!; }, dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "never".tr(), - ), + DropdownMenuEntry(value: 0, label: "never".tr()), DropdownMenuEntry( value: 30, - label: "shared_link_edit_expire_after_option_minutes" - .tr(namedArgs: {'count': "30"}), - ), - DropdownMenuEntry( - value: 60, - label: "shared_link_edit_expire_after_option_hour".tr(), + label: "shared_link_edit_expire_after_option_minutes".tr(namedArgs: {'count': "30"}), ), + DropdownMenuEntry(value: 60, label: "shared_link_edit_expire_after_option_hour".tr()), DropdownMenuEntry( value: 60 * 6, - label: "shared_link_edit_expire_after_option_hours" - .tr(namedArgs: {'count': "6"}), - ), - DropdownMenuEntry( - value: 60 * 24, - label: "shared_link_edit_expire_after_option_day".tr(), + label: "shared_link_edit_expire_after_option_hours".tr(namedArgs: {'count': "6"}), ), + DropdownMenuEntry(value: 60 * 24, label: "shared_link_edit_expire_after_option_day".tr()), DropdownMenuEntry( value: 60 * 24 * 7, - label: "shared_link_edit_expire_after_option_days" - .tr(namedArgs: {'count': "7"}), + label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "7"}), ), DropdownMenuEntry( value: 60 * 24 * 30, - label: "shared_link_edit_expire_after_option_days" - .tr(namedArgs: {'count': "30"}), + label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "30"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 3, - label: "shared_link_edit_expire_after_option_months" - .tr(namedArgs: {'count': "3"}), + label: "shared_link_edit_expire_after_option_months".tr(namedArgs: {'count': "3"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 12, - label: "shared_link_edit_expire_after_option_year" - .tr(namedArgs: {'count': "1"}), + label: "shared_link_edit_expire_after_option_year".tr(namedArgs: {'count': "1"}), ), ], ); @@ -287,9 +209,7 @@ class SharedLinkEditPage extends HookConsumerWidget { SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -300,23 +220,14 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildNewLinkField() { return Column( children: [ - const Padding( - padding: EdgeInsets.only( - top: 20, - bottom: 20, - ), - child: Divider(), - ), + const Padding(padding: EdgeInsets.only(top: 20, bottom: 20), child: Divider()), TextFormField( readOnly: true, initialValue: newShareLink.value, decoration: InputDecoration( border: const OutlineInputBorder(), enabledBorder: themeData.inputDecorationTheme.focusedBorder, - suffixIcon: IconButton( - onPressed: copyLinkToClipboard, - icon: const Icon(Icons.copy), - ), + suffixIcon: IconButton(onPressed: copyLinkToClipboard, icon: const Icon(Icons.copy)), ), ), Padding( @@ -327,13 +238,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - child: const Text( - "done", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("done", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -346,30 +251,28 @@ class SharedLinkEditPage extends HookConsumerWidget { } Future handleNewLink() async { - final newLink = - await ref.read(sharedLinkServiceProvider).createSharedLink( - albumId: albumId, - assetIds: assetsList, - showMeta: showMetadata.value, - allowDownload: allowDownload.value, - allowUpload: allowUpload.value, - description: descriptionController.text.isEmpty - ? null - : descriptionController.text, - password: passwordController.text.isEmpty - ? null - : passwordController.text, - expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), - ); + final newLink = await ref + .read(sharedLinkServiceProvider) + .createSharedLink( + albumId: albumId, + assetIds: assetsList, + showMeta: showMetadata.value, + allowDownload: allowDownload.value, + allowUpload: allowUpload.value, + description: descriptionController.text.isEmpty ? null : descriptionController.text, + password: passwordController.text.isEmpty ? null : passwordController.text, + expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), + ); ref.invalidate(sharedLinksStateProvider); - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); - var serverUrl = - externalDomain.isNotEmpty ? externalDomain : getServerUrl(); + + await ref.read(serverInfoProvider.notifier).getServerConfig(); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); + + var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; } + if (newLink != null && serverUrl != null) { newShareLink.value = "${serverUrl}share/${newLink.key}"; copyLinkToClipboard(); @@ -417,7 +320,9 @@ class SharedLinkEditPage extends HookConsumerWidget { changeExpiry = true; } - await ref.read(sharedLinkServiceProvider).updateSharedLink( + await ref + .read(sharedLinkServiceProvider) + .updateSharedLink( existingLink!.id, showMeta: meta, allowDownload: download, @@ -433,9 +338,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - existingLink == null ? "create_link_to_share" : "edit_link", - ).tr(), + title: Text(existingLink == null ? "create_link_to_share" : "edit_link").tr(), elevation: 0, leading: const CloseButton(), centerTitle: false, @@ -443,86 +346,47 @@ class SharedLinkEditPage extends HookConsumerWidget { body: SafeArea( child: ListView( children: [ + Padding(padding: const EdgeInsets.all(padding), child: buildLinkTitle()), + Padding(padding: const EdgeInsets.all(padding), child: buildDescriptionField()), + Padding(padding: const EdgeInsets.all(padding), child: buildPasswordField()), Padding( - padding: const EdgeInsets.all(padding), - child: buildLinkTitle(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildDescriptionField(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildPasswordField(), - ), - Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildShowMetaButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildAllowDownloadButton(), ), Padding( - padding: - const EdgeInsets.only(left: padding, right: 20, bottom: 20), + padding: const EdgeInsets.only(left: padding, right: 20, bottom: 20), child: buildAllowUploadButton(), ), if (existingLink != null) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildEditExpiryButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildExpiryAfterButton(), ), if (newShareLink.value.isEmpty) Align( alignment: Alignment.bottomRight, child: Padding( - padding: const EdgeInsets.only( - right: padding + 10, - bottom: padding, - ), + padding: const EdgeInsets.only(right: padding + 10, bottom: padding), child: ElevatedButton( - onPressed: - existingLink != null ? handleEditLink : handleNewLink, + onPressed: existingLink != null ? handleEditLink : handleNewLink, child: Text( - existingLink != null - ? "shared_link_edit_submit_button" - : "create_link", - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + existingLink != null ? "shared_link_edit_submit_button" : "create_link", + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ).tr(), ), ), ), if (newShareLink.value.isNotEmpty) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildNewLinkField(), ), ], diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index c645719974..2279998c2d 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -24,16 +24,12 @@ class TrashPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final trashRenderList = ref.watch(trashTimelineProvider); - final trashDays = - ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); final selectionEnabledHook = useState(false); final selection = useState({}); final processing = useProcessingOverlay(); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; } @@ -44,11 +40,7 @@ class TrashPage extends HookConsumerWidget { processing.value = false; selectionEnabledHook.value = false; if (context.mounted) { - ImmichToast.show( - context: context, - msg: 'trash_emptied'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'trash_emptied'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -68,16 +60,13 @@ class TrashPage extends HookConsumerWidget { processing.value = true; try { if (selection.value.isNotEmpty) { - final isRemoved = await ref - .read(assetProvider.notifier) - .deleteAssets(selection.value, force: true); + final isRemoved = await ref.read(assetProvider.notifier).deleteAssets(selection.value, force: true); if (isRemoved) { if (context.mounted) { ImmichToast.show( context: context, - msg: 'assets_deleted_permanently' - .tr(namedArgs: {'count': "${selection.value.length}"}), + msg: 'assets_deleted_permanently'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -92,10 +81,7 @@ class TrashPage extends HookConsumerWidget { handlePermanentDelete() async { await showDialog( context: context, - builder: (context) => DeleteDialog( - alert: "delete_dialog_alert_remote", - onDelete: () => onPermanentlyDelete(), - ), + builder: (context) => DeleteDialog(alert: "delete_dialog_alert_remote", onDelete: () => onPermanentlyDelete()), ); } @@ -110,15 +96,12 @@ class TrashPage extends HookConsumerWidget { processing.value = true; try { if (selection.value.isNotEmpty) { - final result = await ref - .read(trashProvider.notifier) - .restoreAssets(selection.value); + final result = await ref.read(trashProvider.notifier).restoreAssets(selection.value); if (result && context.mounted) { ImmichToast.show( context: context, - msg: 'assets_restored_successfully' - .tr(namedArgs: {'count': "${selection.value.length}"}), + msg: 'assets_restored_successfully'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -131,9 +114,7 @@ class TrashPage extends HookConsumerWidget { String getAppBarTitle(String count) { if (selectionEnabledHook.value) { - return selection.value.isNotEmpty - ? "${selection.value.length}" - : "trash_page_select_assets_btn".tr(); + return selection.value.isNotEmpty ? "${selection.value.length}" : "trash_page_select_assets_btn".tr(); } return 'trash_page_title'.tr(namedArgs: {'count': count}); } @@ -159,14 +140,8 @@ class TrashPage extends HookConsumerWidget { PopupMenuButton( itemBuilder: (context) { return [ - PopupMenuItem( - value: () => selectionEnabledHook.value = true, - child: const Text('select').tr(), - ), - PopupMenuItem( - value: handleEmptyTrash, - child: const Text('empty_trash').tr(), - ), + PopupMenuItem(value: () => selectionEnabledHook.value = true, child: const Text('select').tr()), + PopupMenuItem(value: handleEmptyTrash, child: const Text('empty_trash').tr()), ]; }, onSelected: (fn) => fn(), @@ -187,44 +162,28 @@ class TrashPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( - selection.value.isEmpty - ? 'trash_page_delete_all'.tr() - : 'delete'.tr(), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + selection.value.isEmpty ? 'trash_page_delete_all'.tr() : 'delete'.tr(), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleEmptyTrash - : handlePermanentDelete, + ? handleEmptyTrash + : handlePermanentDelete, ), TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), + icon: const Icon(Icons.history_rounded), label: Text( - selection.value.isEmpty - ? 'trash_page_restore_all'.tr() - : 'restore'.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + selection.value.isEmpty ? 'trash_page_restore_all'.tr() : 'restore'.tr(), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleRestoreAll - : handleRestore, + ? handleRestoreAll + : handleRestore, ), ], ), @@ -241,9 +200,7 @@ class TrashPage extends HookConsumerWidget { ), body: trashRenderList.widgetWhen( onData: (data) => data.isEmpty - ? Center( - child: Text('trash_page_no_assets'.tr()), - ) + ? Center(child: Text('trash_page_no_assets'.tr())) : Stack( children: [ SafeArea( @@ -254,13 +211,8 @@ class TrashPage extends HookConsumerWidget { showMultiSelectIndicator: false, showStack: true, topWidget: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 24, - ), - child: const Text( - "trash_page_info", - ).tr(namedArgs: {"days": "$trashDays"}), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24), + child: const Text("trash_page_info").tr(namedArgs: {"days": "$trashDays"}), ), ), ), diff --git a/mobile/lib/pages/login/change_password.page.dart b/mobile/lib/pages/login/change_password.page.dart index b05397ee38..248526df1b 100644 --- a/mobile/lib/pages/login/change_password.page.dart +++ b/mobile/lib/pages/login/change_password.page.dart @@ -9,8 +9,6 @@ class ChangePasswordPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return const Scaffold( - body: ChangePasswordForm(), - ); + return const Scaffold(body: ChangePasswordForm()); } } diff --git a/mobile/lib/pages/login/login.page.dart b/mobile/lib/pages/login/login.page.dart index 8045ae649f..e1d551900f 100644 --- a/mobile/lib/pages/login/login.page.dart +++ b/mobile/lib/pages/login/login.page.dart @@ -21,12 +21,10 @@ class LoginPage extends HookConsumerWidget { appVersion.value = packageInfo.version; } - useEffect( - () { - getAppInfo(); - return null; - }, - ); + useEffect(() { + getAppInfo(); + return null; + }); return Scaffold( body: LoginForm(), diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index b0a1b34b06..52d4ac0125 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -26,26 +26,18 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'permission_onboarding_request', - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ).tr(), + Text('permission_onboarding_request', style: context.textTheme.titleMedium, textAlign: TextAlign.center).tr(), const SizedBox(height: 18), ElevatedButton( - onPressed: () => ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission() - .then((permission) async { - if (permission.isGranted) { - // If permission is limited, we will show the limited - // permission page - goToBackup(); - } - }), - child: const Text( - 'continue', - ).tr(), + onPressed: () => + ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission().then((permission) async { + if (permission.isGranted) { + // If permission is limited, we will show the limited + // permission page + goToBackup(); + } + }), + child: const Text('continue').tr(), ), ], ); @@ -64,10 +56,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { textAlign: TextAlign.center, ).tr(), const SizedBox(height: 18), - ElevatedButton( - onPressed: () => goToBackup(), - child: const Text('permission_onboarding_get_started').tr(), - ), + ElevatedButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_get_started').tr()), ], ); } @@ -80,11 +69,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.yellow, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.yellow, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_limited', @@ -94,17 +79,10 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), const SizedBox(height: 8.0), - TextButton( - onPressed: () => goToBackup(), - child: const Text( - 'permission_onboarding_continue_anyway', - ).tr(), - ), + TextButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_continue_anyway').tr()), ], ); } @@ -114,11 +92,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.red, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.red, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_denied', @@ -128,9 +102,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), ], ); @@ -139,12 +111,8 @@ class PermissionOnboardingPage extends HookConsumerWidget { final Widget child = switch (permission) { PermissionStatus.limited => buildPermissionLimited(), PermissionStatus.denied => buildRequestPermission(), - PermissionStatus.granted || - PermissionStatus.provisional => - buildPermissionGranted(), - PermissionStatus.restricted || - PermissionStatus.permanentlyDenied => - buildPermissionDenied() + PermissionStatus.granted || PermissionStatus.provisional => buildPermissionGranted(), + PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied(), }; return Scaffold( @@ -156,21 +124,13 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const ImmichLogo( - heroTag: 'logo', - ), + const ImmichLogo(heroTag: 'logo'), const ImmichTitleText(), AnimatedSwitcher( duration: const Duration(milliseconds: 500), - child: Padding( - padding: const EdgeInsets.all(18.0), - child: child, - ), - ), - TextButton( - child: const Text('back').tr(), - onPressed: () => context.maybePop(), + child: Padding(padding: const EdgeInsets.all(18.0), child: child), ), + TextButton(child: const Text('back').tr(), onPressed: () => context.maybePop()), ], ), ), diff --git a/mobile/lib/pages/photos/memory.page.dart b/mobile/lib/pages/photos/memory.page.dart index 211472f27a..20bd32a171 100644 --- a/mobile/lib/pages/photos/memory.page.dart +++ b/mobile/lib/pages/photos/memory.page.dart @@ -16,32 +16,24 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; @RoutePage() - /// Expects [currentAssetProvider] to be set before navigating to this page class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const MemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const MemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); /// The list of all of the asset page controllers - final memoryAssetPageControllers = - List.generate(memories.length, (i) => usePageController()); + final memoryAssetPageControllers = List.generate(memories.length, (i) => usePageController()); /// The main vertically scrolling page controller with each list of memories final memoryPageController = usePageController(initialPage: memoryIndex); @@ -56,36 +48,27 @@ class MemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { final previousIndex = currentMemoryIndex.value - 1; - final previousMemoryController = - memoryAssetPageControllers[previousIndex]; + final previousMemoryController = memoryAssetPageControllers[previousIndex]; // Ensure the controller is attached if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } else { // Wait for the next frame until it is attached SchedulerBinding.instance.addPostFrameCallback((_) { if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } }); } @@ -96,13 +79,9 @@ class MemoryPage extends HookConsumerWidget { toNextAsset(int currentAssetIndex) { if (currentAssetIndex + 1 < currentMemory.value.assets.length) { // Go to the next asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -112,13 +91,9 @@ class MemoryPage extends HookConsumerWidget { toPreviousAsset(int currentAssetIndex) { if (currentAssetIndex > 0) { // Go to the previous asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -126,8 +101,7 @@ class MemoryPage extends HookConsumerWidget { } updateProgressText() { - assetProgress.value = - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; + assetProgress.value = "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; } /// Downloads and caches the image for the asset at this [currentMemory]'s index @@ -168,11 +142,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: size.width, - height: size.height, - ), + ImmichImage.imageProvider(asset: asset, width: size.width, height: size.height), context, size: size, ); @@ -180,8 +150,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the next page right away if we are on the first page if (currentAssetPage.value == 0) { - Future.delayed(const Duration(milliseconds: 200)) - .then((_) => precacheAsset(1)); + Future.delayed(const Duration(milliseconds: 200)).then((_) => precacheAsset(1)); } Future onAssetChanged(int otherIndex) async { @@ -212,12 +181,10 @@ class MemoryPage extends HookConsumerWidget { // maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1 // or sum of vertical pixels of all memories for depth = 0 if (notification is ScrollUpdateNotification) { - final isEpiloguePage = - (memoryPageController.page?.floor() ?? 0) >= memories.length; + final isEpiloguePage = (memoryPageController.page?.floor() ?? 0) >= memories.length; final offset = notification.metrics.pixels; - if (isEpiloguePage && - (offset > notification.metrics.maxScrollExtent + 150)) { + if (isEpiloguePage && (offset > notification.metrics.maxScrollExtent + 150)) { context.maybePop(); return true; } @@ -229,9 +196,7 @@ class MemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -262,12 +227,7 @@ class MemoryPage extends HookConsumerWidget { return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -287,9 +247,7 @@ class MemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -300,11 +258,7 @@ class MemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: MemoryCard( - asset: asset, - title: memories[mIndex].title, - showTitle: index == 0, - ), + child: MemoryCard(asset: asset, title: memories[mIndex].title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -345,28 +299,19 @@ class MemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), - if (currentAsset.value != null && - currentAsset.value!.isVideo) + if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 62ac96c8aa..957c32b224 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -29,17 +29,14 @@ class PhotosPage extends HookConsumerWidget { final tipOneOpacity = useState(0.0); final refreshCount = useState(0); - useEffect( - () { - ref.read(websocketProvider.notifier).connect(); - Future(() => ref.read(assetProvider.notifier).getAllAsset()); - Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); - ref.read(serverInfoProvider.notifier).getServerInfo(); + useEffect(() { + ref.read(websocketProvider.notifier).connect(); + Future(() => ref.read(assetProvider.notifier).getAllAsset()); + Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); + ref.read(serverInfoProvider.notifier).getServerInfo(); - return; - }, - [], - ); + return; + }, []); Widget buildLoadingIndicator() { Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1); @@ -53,9 +50,7 @@ class PhotosPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16.0), child: Text( 'home_page_building_timeline', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), ).tr(), ), const SizedBox(height: 8), @@ -106,9 +101,7 @@ class PhotosPage extends HookConsumerWidget { return Stack( children: [ MultiselectGrid( - topWidget: (currentUser != null && currentUser.memoryEnabled) - ? const MemoryLane() - : const SizedBox(), + topWidget: (currentUser != null && currentUser.memoryEnabled) ? const MemoryLane() : const SizedBox(), renderListProvider: timelineUsers.length > 1 ? multiUsersTimelineProvider(timelineUsers) : singleUserTimelineProvider(currentUser?.id), @@ -120,9 +113,7 @@ class PhotosPage extends HookConsumerWidget { ), AnimatedPositioned( duration: const Duration(milliseconds: 300), - top: ref.watch(multiselectProvider) - ? -(kToolbarHeight + context.padding.top) - : 0, + top: ref.watch(multiselectProvider) ? -(kToolbarHeight + context.padding.top) : 0, left: 0, right: 0, child: Container( diff --git a/mobile/lib/pages/search/all_motion_videos.page.dart b/mobile/lib/pages/search/all_motion_videos.page.dart index 5d1081db33..60bb8a6cff 100644 --- a/mobile/lib/pages/search/all_motion_videos.page.dart +++ b/mobile/lib/pages/search/all_motion_videos.page.dart @@ -17,16 +17,9 @@ class AllMotionPhotosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('search_page_motion_photos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: motionPhotos.widgetWhen( - onData: (assets) => ImmichAssetGrid( - assets: assets, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: motionPhotos.widgetWhen(onData: (assets) => ImmichAssetGrid(assets: assets)), ); } } diff --git a/mobile/lib/pages/search/all_people.page.dart b/mobile/lib/pages/search/all_people.page.dart index 7e2a136721..b2814e6c13 100644 --- a/mobile/lib/pages/search/all_people.page.dart +++ b/mobile/lib/pages/search/all_people.page.dart @@ -17,20 +17,13 @@ class AllPeoplePage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'people', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + title: const Text('people').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: curatedPeople.widgetWhen( onData: (people) => ExploreGrid( isPeople: true, - curatedContent: people - .map((e) => SearchCuratedContent(label: e.name, id: e.id)) - .toList(), + curatedContent: people.map((e) => SearchCuratedContent(label: e.name, id: e.id)).toList(), ), ), ); diff --git a/mobile/lib/pages/search/all_places.page.dart b/mobile/lib/pages/search/all_places.page.dart index 92521d13cf..c92f87d3ac 100644 --- a/mobile/lib/pages/search/all_places.page.dart +++ b/mobile/lib/pages/search/all_places.page.dart @@ -13,24 +13,14 @@ class AllPlacesPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - AsyncValue> places = - ref.watch(getAllPlacesProvider); + AsyncValue> places = ref.watch(getAllPlacesProvider); return Scaffold( appBar: AppBar( - title: const Text( - 'places', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: places.widgetWhen( - onData: (data) => ExploreGrid( - curatedContent: data, - ), + title: const Text('places').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: places.widgetWhen(onData: (data) => ExploreGrid(curatedContent: data)), ); } } diff --git a/mobile/lib/pages/search/all_videos.page.dart b/mobile/lib/pages/search/all_videos.page.dart index 6d123d9d7f..acad043a58 100644 --- a/mobile/lib/pages/search/all_videos.page.dart +++ b/mobile/lib/pages/search/all_videos.page.dart @@ -14,10 +14,7 @@ class AllVideosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('videos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: MultiselectGrid(renderListProvider: allVideosTimelineProvider), ); diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 9f32b1877b..34522f0f04 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -48,8 +48,7 @@ class MapPage extends HookConsumerWidget { final layerDebouncer = useDebouncer(interval: const Duration(seconds: 1)); final isLoading = useProcessingOverlay(); final scrollController = useScrollController(); - final markerDebouncer = - useDebouncer(interval: const Duration(milliseconds: 800)); + final markerDebouncer = useDebouncer(interval: const Duration(milliseconds: 800)); final selectedAssets = useValueNotifier>({}); const mapZoomToAssetLevel = 12.0; @@ -63,18 +62,11 @@ class MapPage extends HookConsumerWidget { final bounds = await mapController.value!.getVisibleRegion(); final inBounds = markers.value - .where( - (m) => - bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), - ) + .where((m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude))) .toList(); // Notify bottom sheet to update asset grid only when there are new assets if (markersInBounds.value.length != inBounds.length) { - bottomSheetStreamController.add( - MapAssetsInBoundsUpdated( - inBounds.map((e) => e.assetRemoteId).toList(), - ), - ); + bottomSheetStreamController.add(MapAssetsInBoundsUpdated(inBounds.map((e) => e.assetRemoteId).toList())); } markersInBounds.value = inBounds; } @@ -82,9 +74,7 @@ class MapPage extends HookConsumerWidget { // removes all sources and layers and re-adds them with the updated markers Future reloadLayers() async { if (mapController.value != null) { - layerDebouncer.run( - () => mapController.value!.reloadAllLayersForMarkers(markers.value), - ); + layerDebouncer.run(() => mapController.value!.reloadAllLayersForMarkers(markers.value)); } } @@ -99,16 +89,12 @@ class MapPage extends HookConsumerWidget { } } - useEffect( - () { - final currentAssetLink = - ref.read(currentAssetProvider.notifier).ref.keepAlive(); + useEffect(() { + final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); - loadMarkers(); - return currentAssetLink.close; - }, - [], - ); + loadMarkers(); + return currentAssetLink.close; + }, []); // Refetch markers when map state is changed ref.listen(mapStateNotifierProvider, (_, current) { @@ -124,17 +110,9 @@ class MapPage extends HookConsumerWidget { }); // updates the selected markers position based on the current map camera - Future updateAssetMarkerPosition( - MapMarker marker, { - bool shouldAnimate = true, - }) async { - final assetPoint = - await mapController.value!.toScreenLocation(marker.latLng); - selectedMarker.value = _AssetMarkerMeta( - point: assetPoint, - marker: marker, - shouldAnimate: shouldAnimate, - ); + Future updateAssetMarkerPosition(MapMarker marker, {bool shouldAnimate = true}) async { + final assetPoint = await mapController.value!.toScreenLocation(marker.latLng); + selectedMarker.value = _AssetMarkerMeta(point: assetPoint, marker: marker, shouldAnimate: shouldAnimate); (assetPoint, marker, shouldAnimate); } @@ -144,11 +122,9 @@ class MapPage extends HookConsumerWidget { if (mapController.value == null) { return; } - final latlngBound = - await mapController.value!.getBoundsFromPoint(point, 50); + final latlngBound = await mapController.value!.getBoundsFromPoint(point, 50); final marker = markersInBounds.value.firstWhereOrNull( - (m) => - latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), + (m) => latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), ); if (marker != null) { @@ -166,10 +142,7 @@ class MapPage extends HookConsumerWidget { mapController.value = controller; controller.addListener(() { if (controller.isCameraMoving && selectedMarker.value != null) { - updateAssetMarkerPosition( - selectedMarker.value!.marker, - shouldAnimate: false, - ); + updateAssetMarkerPosition(selectedMarker.value!.marker, shouldAnimate: false); } }); } @@ -186,22 +159,13 @@ class MapPage extends HookConsumerWidget { } // Since we only have a single asset, we can just show GroupAssetBy.none - final renderList = await RenderList.fromAssets( - [asset], - GroupAssetsBy.none, - ); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.none); ref.read(currentAssetProvider.notifier).set(asset); if (asset.isVideo) { ref.read(showControlsProvider.notifier).show = false; } - context.pushRoute( - GalleryViewerRoute( - initialIndex: 0, - heroOffset: 0, - renderList: renderList, - ), - ); + context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList)); } /// BOTTOM SHEET CALLBACKS @@ -211,23 +175,18 @@ class MapPage extends HookConsumerWidget { } void onBottomSheetScrolled(String assetRemoteId) { - final assetMarker = markersInBounds.value - .firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); + final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); if (assetMarker != null) { updateAssetMarkerPosition(assetMarker); } } void onZoomToAsset(String assetRemoteId) { - final assetMarker = markersInBounds.value - .firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); + final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); if (mapController.value != null && assetMarker != null) { // Offset the latitude a little to show the marker just above the viewports center final offset = context.isMobile ? 0.02 : 0; - final latlng = LatLng( - assetMarker.latLng.latitude - offset, - assetMarker.latLng.longitude, - ); + final latlng = LatLng(assetMarker.latLng.latitude - offset, assetMarker.latLng.longitude); mapController.value!.animateCamera( CameraUpdate.newLatLngZoom(latlng, mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), @@ -236,8 +195,7 @@ class MapPage extends HookConsumerWidget { } void onZoomToLocation() async { - final (location, error) = - await MapUtils.checkPermAndGetLocation(context: context); + final (location, error) = await MapUtils.checkPermAndGetLocation(context: context); if (error != null) { if (error == LocationPermission.unableToDetermine && context.mounted) { ImmichToast.show( @@ -252,10 +210,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && location != null) { mapController.value!.animateCamera( - CameraUpdate.newLatLngZoom( - LatLng(location.latitude, location.longitude), - mapZoomToAssetLevel, - ), + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); } @@ -320,9 +275,7 @@ class MapPage extends HookConsumerWidget { bottom: context.padding.bottom + 16, child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), @@ -353,15 +306,10 @@ class _AssetMarkerMeta { final MapMarker marker; final bool shouldAnimate; - const _AssetMarkerMeta({ - required this.point, - required this.marker, - required this.shouldAnimate, - }); + const _AssetMarkerMeta({required this.point, required this.marker, required this.shouldAnimate}); @override - String toString() => - '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; + String toString() => '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; } class _MapWithMarker extends StatelessWidget { 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 f27deae052..0fe8b769f5 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/map_utils.dart'; class MapLocationPickerPage extends HookConsumerWidget { final LatLng initialLatLng; - const MapLocationPickerPage({ - super.key, - this.initialLatLng = const LatLng(0, 0), - }); + const MapLocationPickerPage({super.key, this.initialLatLng = const LatLng(0, 0)}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,8 +32,7 @@ class MapLocationPickerPage extends HookConsumerWidget { selectedLatLng.value = centre; controller.value?.animateCamera(CameraUpdate.newLatLng(centre)); if (marker.value != null) { - await controller.value - ?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); + await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); } } @@ -45,15 +41,13 @@ class MapLocationPickerPage extends HookConsumerWidget { } Future getCurrentLocation() async { - var (currentLocation, _) = - await MapUtils.checkPermAndGetLocation(context: context); + var (currentLocation, _) = await MapUtils.checkPermAndGetLocation(context: context); if (currentLocation == null) { return; } - var currentLatLng = - LatLng(currentLocation.latitude, currentLocation.longitude); + var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude); selectedLatLng.value = currentLatLng; controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng)); } @@ -69,17 +63,12 @@ class MapLocationPickerPage extends HookConsumerWidget { onData: (style) => Container( clipBehavior: Clip.antiAliasWithSaveLayer, decoration: const BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(40), - bottomRight: Radius.circular(40), - ), + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40)), ), child: MapLibreMap( - initialCameraPosition: - CameraPosition(target: initialLatLng, zoom: 12), + initialCameraPosition: CameraPosition(target: initialLatLng, zoom: 12), styleString: style, - onMapCreated: (mapController) => - controller.value = mapController, + onMapCreated: (mapController) => controller.value = mapController, onStyleLoadedCallback: onStyleLoaded, onMapClick: onMapClick, dragEnabled: false, @@ -113,9 +102,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget { alignment: Alignment.centerLeft, child: ElevatedButton( onPressed: onClose, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ), @@ -131,11 +118,7 @@ class _BottomBar extends StatelessWidget { final Function() onUseLocation; final Function() onGetCurrentLocation; - const _BottomBar({ - required this.selectedLatLng, - required this.onUseLocation, - required this.onGetCurrentLocation, - }); + const _BottomBar({required this.selectedLatLng, required this.onUseLocation, required this.onGetCurrentLocation}); @override Widget build(BuildContext context) { @@ -154,9 +137,8 @@ class _BottomBar extends StatelessWidget { const SizedBox(width: 15), ValueListenableBuilder( valueListenable: selectedLatLng, - builder: (_, value, __) => Text( - "${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}", - ), + builder: (_, value, __) => + Text("${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}"), ), ], ), @@ -165,13 +147,9 @@ class _BottomBar extends StatelessWidget { children: [ ElevatedButton( onPressed: onUseLocation, - child: - const Text("map_location_picker_page_use_location").tr(), - ), - ElevatedButton( - onPressed: onGetCurrentLocation, - child: const Icon(Icons.my_location), + child: const Text("map_location_picker_page_use_location").tr(), ), + ElevatedButton(onPressed: onGetCurrentLocation, child: const Icon(Icons.my_location)), ], ), ], diff --git a/mobile/lib/pages/search/person_result.page.dart b/mobile/lib/pages/search/person_result.page.dart index 859c7e8a89..7d2e612d25 100644 --- a/mobile/lib/pages/search/person_result.page.dart +++ b/mobile/lib/pages/search/person_result.page.dart @@ -15,11 +15,7 @@ class PersonResultPage extends HookConsumerWidget { final String personId; final String personName; - const PersonResultPage({ - super.key, - required this.personId, - required this.personName, - }); + const PersonResultPage({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,10 +26,7 @@ class PersonResultPage extends HookConsumerWidget { context: context, useRootNavigator: false, builder: (BuildContext context) { - return PersonNameEditForm( - personId: personId, - personName: name.value, - ); + return PersonNameEditForm(personId: personId, personName: name.value); }, ).then((result) { if (result != null && result.success) { @@ -55,10 +48,7 @@ class PersonResultPage extends HookConsumerWidget { children: [ ListTile( leading: const Icon(Icons.edit_outlined), - title: const Text( - 'edit_name', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text('edit_name', style: TextStyle(fontWeight: FontWeight.bold)).tr(), onTap: showEditNameDialog, ), ], @@ -75,27 +65,13 @@ class PersonResultPage extends HookConsumerWidget { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'add_a_name', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), - ).tr(), - Text( - 'find_them_fast', - style: context.textTheme.labelLarge, - ).tr(), + Text('add_a_name', style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor)).tr(), + Text('find_them_fast', style: context.textTheme.labelLarge).tr(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.value, - style: context.textTheme.titleLarge, - overflow: TextOverflow.ellipsis, - ), - ], + children: [Text(name.value, style: context.textTheme.titleLarge, overflow: TextOverflow.ellipsis)], ), ); } @@ -103,16 +79,8 @@ class PersonResultPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: Text(name.value), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - actions: [ - IconButton( - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_vert_rounded), - ), - ], + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + actions: [IconButton(onPressed: buildBottomSheet, icon: const Icon(Icons.more_vert_rounded))], ), body: MultiselectGrid( renderListProvider: personAssetsProvider(personId), @@ -122,16 +90,10 @@ class PersonResultPage extends HookConsumerWidget { children: [ CircleAvatar( radius: 36, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(personId), - headers: ApiService.getRequestHeaders(), - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(personId), headers: ApiService.getRequestHeaders()), ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: buildTitleBlock(), - ), + child: Padding(padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: buildTitleBlock()), ), ], ), diff --git a/mobile/lib/pages/search/recently_taken.page.dart b/mobile/lib/pages/search/recently_taken.page.dart index cc1eb7086e..988af2faf0 100644 --- a/mobile/lib/pages/search/recently_taken.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -17,16 +17,9 @@ class RecentlyTakenPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('recently_taken_page_title').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: recents.widgetWhen( - onData: (searchResponse) => ImmichAssetGrid( - assets: searchResponse, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: recents.widgetWhen(onData: (searchResponse) => ImmichAssetGrid(assets: searchResponse)), ); } } diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index 3e5c153f88..acfa2fd59f 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -41,15 +41,9 @@ class SearchPage extends HookConsumerWidget { location: prefilter?.location ?? SearchLocationFilter(), camera: prefilter?.camera ?? SearchCameraFilter(), date: prefilter?.date ?? SearchDateFilter(), - display: prefilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: prefilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: prefilter?.mediaType ?? AssetType.other, - language: - "${context.locale.languageCode}-${context.locale.countryCode}", + language: "${context.locale.languageCode}-${context.locale.countryCode}", ), ); @@ -66,10 +60,7 @@ class SearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -87,14 +78,10 @@ class SearchPage extends HookConsumerWidget { isSearching.value = true; ref.watch(paginatedSearchProvider.notifier).clear(); - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.tr())); } previousFilter.value = filter.value; @@ -103,14 +90,10 @@ class SearchPage extends HookConsumerWidget { loadMoreSearchResult() async { isSearching.value = true; - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.tr())); } isSearching.value = false; @@ -118,39 +101,26 @@ class SearchPage extends HookConsumerWidget { searchPrefilter() { if (prefilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (prefilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - prefilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (prefilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(prefilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPrefilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPrefilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.tr()).join(', '), @@ -159,9 +129,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -177,10 +145,7 @@ class SearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -189,11 +154,7 @@ class SearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -209,16 +170,11 @@ class SearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -235,15 +191,10 @@ class SearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -254,10 +205,7 @@ class SearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -267,9 +215,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -285,10 +231,7 @@ class SearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -320,9 +263,7 @@ class SearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -332,13 +273,7 @@ class SearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -366,24 +301,20 @@ class SearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.tr() : assetType == AssetType.video - ? 'video'.tr() - : 'all'.tr(), + ? 'video'.tr() + : 'all'.tr(), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -395,10 +326,7 @@ class SearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.tr(), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -410,32 +338,19 @@ class SearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText - .add('search_filter_display_option_not_in_album'.tr()); + filterText.add('search_filter_display_option_not_in_album'.tr()); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.tr()); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.tr()); } @@ -448,19 +363,12 @@ class SearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -473,10 +381,7 @@ class SearchPage extends HookConsumerWidget { title: 'display_options'.tr(), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -484,27 +389,15 @@ class SearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -512,10 +405,10 @@ class SearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -528,21 +421,11 @@ class SearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -552,7 +435,7 @@ class SearchPage extends HookConsumerWidget { } }, icon: const Icon(Icons.more_vert_rounded), - tooltip: 'Show text search menu', + tooltip: 'show_text_search_menu'.tr(), ); }, menuChildren: [ @@ -563,9 +446,7 @@ class SearchPage extends HookConsumerWidget { 'search_by_context'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.context - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -583,9 +464,7 @@ class SearchPage extends HookConsumerWidget { 'search_filter_filename'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.filename - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -603,15 +482,11 @@ class SearchPage extends HookConsumerWidget { 'search_by_description'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: - textSearchType.value == TextSearchType.description - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, - selected: - textSearchType.value == TextSearchType.description, + selected: textSearchType.value == TextSearchType.description, ), onPressed: () { textSearchType.value = TextSearchType.description; @@ -624,13 +499,8 @@ class SearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -645,15 +515,8 @@ class SearchPage extends HookConsumerWidget { hintText: searchHintText.value, key: const Key('search_text_field'), controller: textSearchController, - contentPadding: prefilter != null - ? const EdgeInsets.only(left: 24) - : const EdgeInsets.all(8), - prefixIcon: prefilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + contentPadding: prefilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), + prefixIcon: prefilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -713,14 +576,9 @@ class SearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const Expanded( - child: Center(child: CircularProgressIndicator()), - ) + const Expanded(child: Center(child: CircularProgressIndicator())) else - SearchResultGrid( - onScrollEnd: loadMoreSearchResult, - isSearching: isSearching.value, - ), + SearchResultGrid(onScrollEnd: loadMoreSearchResult, isSearching: isSearching.value), ], ), ); @@ -731,11 +589,7 @@ class SearchResultGrid extends StatelessWidget { final VoidCallback onScrollEnd; final bool isSearching; - const SearchResultGrid({ - super.key, - required this.onScrollEnd, - this.isSearching = false, - }); + const SearchResultGrid({super.key, required this.onScrollEnd, this.isSearching = false}); @override Widget build(BuildContext context) { @@ -744,17 +598,13 @@ class SearchResultGrid extends StatelessWidget { padding: const EdgeInsets.only(top: 8.0), child: NotificationListener( onNotification: (notification) { - final isBottomSheetNotification = notification.context - ?.findAncestorWidgetOfExactType< - DraggableScrollableSheet>() != - null; + final isBottomSheetNotification = + notification.context?.findAncestorWidgetOfExactType() != null; final metrics = notification.metrics; final isVerticalScroll = metrics.axis == Axis.vertical; - if (metrics.pixels >= metrics.maxScrollExtent && - isVerticalScroll && - !isBottomSheetNotification) { + if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { onScrollEnd(); } @@ -770,9 +620,7 @@ class SearchResultGrid extends StatelessWidget { dragScrollLabelEnabled: false, emptyIndicator: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: !isSearching - ? const SearchEmptyContent() - : const SizedBox.shrink(), + child: !isSearching ? const SearchEmptyContent() : const SizedBox.shrink(), ), ), ), @@ -794,19 +642,12 @@ class SearchEmptyContent extends StatelessWidget { const SizedBox(height: 40), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/polaroid-dark.png' - : 'assets/polaroid-light.png', + context.isDarkTheme ? 'assets/polaroid-dark.png' : 'assets/polaroid-light.png', height: 125, ), ), const SizedBox(height: 16), - Center( - child: Text( - 'search_page_search_photos_videos'.tr(), - style: context.textTheme.labelLarge, - ), - ), + Center(child: Text('search_page_search_photos_videos'.tr(), style: context.textTheme.labelLarge)), const SizedBox(height: 32), const QuickLinkList(), ], @@ -822,13 +663,8 @@ class QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -892,19 +728,9 @@ class QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/pages/settings/sync_status.page.dart b/mobile/lib/pages/settings/sync_status.page.dart new file mode 100644 index 0000000000..d54ba89e5d --- /dev/null +++ b/mobile/lib/pages/settings/sync_status.page.dart @@ -0,0 +1,25 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart'; + +@RoutePage() +class SyncStatusPage extends StatelessWidget { + const SyncStatusPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("sync_status").t(context: context), + leading: IconButton( + onPressed: () => context.maybePop(true), + splashRadius: 24, + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + ), + body: const SyncStatusAndActions(), + ); + } +} diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index 299ffe5497..9d2dbe80c2 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -38,9 +38,7 @@ class ShareIntentPage extends HookConsumerWidget { void upload() async { for (final attachment in candidates) { - await ref - .read(shareIntentUploadProvider.notifier) - .upload(attachment.file); + await ref.read(shareIntentUploadProvider.notifier).upload(attachment.file); } isUploaded.value = true; @@ -62,24 +60,16 @@ class ShareIntentPage extends HookConsumerWidget { appBar: AppBar( title: Column( children: [ - const Text('upload_to_immich').tr( - namedArgs: {'count': candidates.length.toString()}, - ), + const Text('upload_to_immich').tr(namedArgs: {'count': candidates.length.toString()}), Text( currentEndpoint, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), ], ), leading: IconButton( onPressed: () { - context.navigateTo( - Store.isBetaTimelineEnabled - ? const TabShellRoute() - : const TabControllerRoute(), - ); + context.navigateTo(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); }, icon: const Icon(Icons.arrow_back), ), @@ -88,16 +78,10 @@ class ShareIntentPage extends HookConsumerWidget { itemCount: attachments.length, itemBuilder: (context, index) { final attachment = attachments[index]; - final target = candidates.firstWhere( - (element) => element.id == attachment.id, - orElse: () => attachment, - ); + final target = candidates.firstWhere((element) => element.id == attachment.id, orElse: () => attachment); return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16), child: LargeLeadingTile( onTap: () => toggleSelection(attachment), disabled: isUploaded.value, @@ -107,21 +91,11 @@ class ShareIntentPage extends HookConsumerWidget { ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: attachment.isImage - ? Image.file( - attachment.file, - width: 64, - height: 64, - fit: BoxFit.cover, - ) + ? Image.file(attachment.file, width: 64, height: 64, fit: BoxFit.cover) : const SizedBox( width: 64, height: 64, - child: Center( - child: Icon( - Icons.videocam, - color: Colors.white, - ), - ), + child: Center(child: Icon(Icons.videocam, color: Colors.white)), ), ), if (attachment.isImage) @@ -132,25 +106,13 @@ class ShareIntentPage extends HookConsumerWidget { Icons.image, color: Colors.white, size: 20, - shadows: [ - Shadow( - offset: Offset(0, 0), - blurRadius: 8.0, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 0), blurRadius: 8.0, color: Colors.black45)], ), ), ], ), - title: Text( - attachment.fileName, - style: context.textTheme.titleSmall, - ), - subtitle: Text( - attachment.fileSize, - style: context.textTheme.labelLarge, - ), + title: Text(attachment.fileName, style: context.textTheme.titleSmall), + subtitle: Text(attachment.fileSize, style: context.textTheme.labelLarge), trailing: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: UploadStatusIcon( @@ -170,9 +132,7 @@ class ShareIntentPage extends HookConsumerWidget { height: 48, child: ElevatedButton( onPressed: isUploaded.value ? null : upload, - child: isUploaded.value - ? UploadingText(candidates: candidates) - : const Text('upload').tr(), + child: isUploaded.value ? UploadingText(candidates: candidates) : const Text('upload').tr(), ), ), ), @@ -191,22 +151,14 @@ class UploadingText extends StatelessWidget { return element.status == UploadStatus.complete; }).length; - return const Text("shared_intent_upload_button_progress_text").tr( - namedArgs: { - 'current': uploadedCount.toString(), - 'total': candidates.length.toString(), - }, - ); + return const Text( + "shared_intent_upload_button_progress_text", + ).tr(namedArgs: {'current': uploadedCount.toString(), 'total': candidates.length.toString()}); } } class UploadStatusIcon extends StatelessWidget { - const UploadStatusIcon({ - super.key, - required this.status, - required this.selected, - this.progress = 0, - }); + const UploadStatusIcon({super.key, required this.status, required this.selected, this.progress = 0}); final UploadStatus status; final double progress; @@ -224,55 +176,42 @@ class UploadStatusIcon extends StatelessWidget { final statusIcon = switch (status) { UploadStatus.enqueued => Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - semanticLabel: 'enqueued'.tr(), - ), + Icons.check_circle_rounded, + color: context.primaryColor, + semanticLabel: 'enqueued'.tr(), + ), UploadStatus.running => Stack( - alignment: AlignmentDirectional.center, - children: [ - SizedBox( - width: 40, - height: 40, - child: TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: progress), - duration: const Duration(milliseconds: 500), - builder: (context, value, _) => CircularProgressIndicator( - backgroundColor: context.colorScheme.surfaceContainerLow, - strokeWidth: 3, - value: value, - semanticsLabel: 'uploading'.tr(), - ), + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 40, + height: 40, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 500), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: context.colorScheme.surfaceContainerLow, + strokeWidth: 3, + value: value, + semanticsLabel: 'uploading'.tr(), ), ), - Text( - (progress * 100).toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ], - ), - UploadStatus.complete => Icon( - Icons.check_circle_rounded, - color: Colors.green, - semanticLabel: 'completed'.tr(), - ), - UploadStatus.notFound || UploadStatus.failed => Icon( - Icons.error_rounded, - color: Colors.red, - semanticLabel: 'failed'.tr(), - ), - UploadStatus.canceled => Icon( - Icons.cancel_rounded, - color: Colors.red, - semanticLabel: 'canceled'.tr(), - ), + ), + Text( + (progress * 100).toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ], + ), + UploadStatus.complete => Icon(Icons.check_circle_rounded, color: Colors.green, semanticLabel: 'completed'.tr()), + UploadStatus.notFound || + UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: 'failed'.tr()), + UploadStatus.canceled => Icon(Icons.cancel_rounded, color: Colors.red, semanticLabel: 'canceled'.tr()), UploadStatus.waitingToRetry || UploadStatus.paused => Icon( - Icons.pause_circle_rounded, - color: context.primaryColor, - semanticLabel: 'paused'.tr(), - ), + Icons.pause_circle_rounded, + color: context.primaryColor, + semanticLabel: 'paused'.tr(), + ), }; return statusIcon; diff --git a/mobile/lib/platform/background_worker_api.g.dart b/mobile/lib/platform/background_worker_api.g.dart new file mode 100644 index 0000000000..22325603c0 --- /dev/null +++ b/mobile/lib/platform/background_worker_api.g.dart @@ -0,0 +1,367 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && a.indexed.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]), + ); + } + return a == b; +} + +class BackgroundWorkerSettings { + BackgroundWorkerSettings({required this.requiresCharging, required this.minimumDelaySeconds}); + + bool requiresCharging; + + int minimumDelaySeconds; + + List _toList() { + return [requiresCharging, minimumDelaySeconds]; + } + + Object encode() { + return _toList(); + } + + static BackgroundWorkerSettings decode(Object result) { + result as List; + return BackgroundWorkerSettings(requiresCharging: result[0]! as bool, minimumDelaySeconds: result[1]! as int); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! BackgroundWorkerSettings || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is BackgroundWorkerSettings) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return BackgroundWorkerSettings.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class BackgroundWorkerFgHostApi { + /// Constructor for [BackgroundWorkerFgHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + BackgroundWorkerFgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future enable() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enable$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future saveNotificationMessage(String title, String body) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.saveNotificationMessage$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([title, body]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future configure(BackgroundWorkerSettings settings) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.configure$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([settings]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future disable() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disable$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} + +class BackgroundWorkerBgHostApi { + /// Constructor for [BackgroundWorkerBgHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + BackgroundWorkerBgHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future onInitialized() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.onInitialized$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future close() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerBgHostApi.close$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} + +abstract class BackgroundWorkerFlutterApi { + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + Future onIosUpload(bool isRefresh, int? maxSeconds); + + Future onAndroidUpload(); + + Future cancel(); + + static void setUp( + BackgroundWorkerFlutterApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null.', + ); + final List args = (message as List?)!; + final bool? arg_isRefresh = (args[0] as bool?); + assert( + arg_isRefresh != null, + 'Argument for dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onIosUpload was null, expected non-null bool.', + ); + final int? arg_maxSeconds = (args[1] as int?); + try { + await api.onIosUpload(arg_isRefresh!, arg_maxSeconds); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onAndroidUpload$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + try { + await api.onAndroidUpload(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.cancel$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + try { + await api.cancel(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString()), + ); + } + }); + } + } + } +} diff --git a/mobile/lib/platform/background_worker_lock_api.g.dart b/mobile/lib/platform/background_worker_lock_api.g.dart new file mode 100644 index 0000000000..9f00017dc8 --- /dev/null +++ b/mobile/lib/platform/background_worker_lock_api.g.dart @@ -0,0 +1,97 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class BackgroundWorkerLockApi { + /// Constructor for [BackgroundWorkerLockApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + BackgroundWorkerLockApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future lock() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.lock$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future unlock() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.unlock$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} diff --git a/mobile/lib/platform/connectivity_api.g.dart b/mobile/lib/platform/connectivity_api.g.dart new file mode 100644 index 0000000000..c348356f81 --- /dev/null +++ b/mobile/lib/platform/connectivity_api.g.dart @@ -0,0 +1,87 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +enum NetworkCapability { cellular, wifi, vpn, unmetered } + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is NetworkCapability) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : NetworkCapability.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ConnectivityApi { + /// Constructor for [ConnectivityApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ConnectivityApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future> getCapabilities() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ConnectivityApi.getCapabilities$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } +} diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 46599eb016..01237f8c19 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -17,15 +17,14 @@ PlatformException _createConnectionError(String channelName) { bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { - return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + return a.length == b.length && a.indexed.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } @@ -41,6 +40,7 @@ class PlatformAsset { this.height, required this.durationInSeconds, required this.orientation, + required this.isFavorite, }); String id; @@ -61,18 +61,10 @@ class PlatformAsset { int orientation; + bool isFavorite; + List _toList() { - return [ - id, - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - orientation, - ]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite]; } Object encode() { @@ -91,6 +83,7 @@ class PlatformAsset { height: result[6] as int?, durationInSeconds: result[7]! as int, orientation: result[8]! as int, + isFavorite: result[9]! as bool, ); } @@ -131,13 +124,7 @@ class PlatformAlbum { int assetCount; List _toList() { - return [ - id, - name, - updatedAt, - isCloud, - assetCount, - ]; + return [id, name, updatedAt, isCloud, assetCount]; } Object encode() { @@ -173,12 +160,7 @@ class PlatformAlbum { } class SyncDelta { - SyncDelta({ - required this.hasChanges, - required this.updates, - required this.deletes, - required this.assetAlbums, - }); + SyncDelta({required this.hasChanges, required this.updates, required this.deletes, required this.assetAlbums}); bool hasChanges; @@ -189,12 +171,7 @@ class SyncDelta { Map> assetAlbums; List _toList() { - return [ - hasChanges, - updates, - deletes, - assetAlbums, - ]; + return [hasChanges, updates, deletes, assetAlbums]; } Object encode() { @@ -207,8 +184,7 @@ class SyncDelta { hasChanges: result[0]! as bool, updates: (result[1] as List?)!.cast(), deletes: (result[2] as List?)!.cast(), - assetAlbums: - (result[3] as Map?)!.cast>(), + assetAlbums: (result[3] as Map?)!.cast>(), ); } @@ -229,6 +205,45 @@ class SyncDelta { int get hashCode => Object.hashAll(_toList()); } +class HashResult { + HashResult({required this.assetId, this.error, this.hash}); + + String assetId; + + String? error; + + String? hash; + + List _toList() { + return [assetId, error, hash]; + } + + Object encode() { + return _toList(); + } + + static HashResult decode(Object result) { + result as List; + return HashResult(assetId: result[0]! as String, error: result[1] as String?, hash: result[2] as String?); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! HashResult || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -245,6 +260,9 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is SyncDelta) { buffer.putUint8(131); writeValue(buffer, value.encode()); + } else if (value is HashResult) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -259,6 +277,8 @@ class _PigeonCodec extends StandardMessageCodec { return PlatformAlbum.decode(readValue(buffer)!); case 131: return SyncDelta.decode(readValue(buffer)!); + case 132: + return HashResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -269,11 +289,9 @@ class NativeSyncApi { /// Constructor for [NativeSyncApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - NativeSyncApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + NativeSyncApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -283,15 +301,13 @@ class NativeSyncApi { Future shouldFullSync() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -313,15 +329,13 @@ class NativeSyncApi { Future getMediaChanges() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -343,15 +357,13 @@ class NativeSyncApi { Future checkpointSync() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.checkpointSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -368,15 +380,13 @@ class NativeSyncApi { Future clearSyncCheckpoint() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.clearSyncCheckpoint$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -393,16 +403,13 @@ class NativeSyncApi { Future> getAssetIdsForAlbum(String albumId) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -424,15 +431,13 @@ class NativeSyncApi { Future> getAlbums() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -454,16 +459,13 @@ class NativeSyncApi { Future getAssetsCountSince(String albumId, int timestamp) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsCountSince$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId, timestamp]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId, timestamp]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -482,20 +484,16 @@ class NativeSyncApi { } } - Future> getAssetsForAlbum(String albumId, - {int? updatedTimeCond}) async { + Future> getAssetsForAlbum(String albumId, {int? updatedTimeCond}) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId, updatedTimeCond]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId, updatedTimeCond]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -514,19 +512,16 @@ class NativeSyncApi { } } - Future> hashPaths(List paths) async { + Future> hashAssets(List assetIds, {bool allowNetworkAccess = false}) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashAssets$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([paths]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([assetIds, allowNetworkAccess]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -541,7 +536,30 @@ class NativeSyncApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as List?)!.cast(); + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } + + Future cancelHashing() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.cancelHashing$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; } } } diff --git a/mobile/lib/platform/thumbnail_api.g.dart b/mobile/lib/platform/thumbnail_api.g.dart new file mode 100644 index 0000000000..2b4add7482 --- /dev/null +++ b/mobile/lib/platform/thumbnail_api.g.dart @@ -0,0 +1,142 @@ +// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ThumbnailApi { + /// Constructor for [ThumbnailApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ThumbnailApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future> requestImage( + String assetId, { + required int requestId, + required int width, + required int height, + required bool isVideo, + }) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ThumbnailApi.requestImage$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([ + assetId, + requestId, + width, + height, + isVideo, + ]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)!.cast(); + } + } + + Future cancelImageRequest(int requestId) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ThumbnailApi.cancelImageRequest$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([requestId]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future> getThumbhash(String thumbhash) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ThumbnailApi.getThumbhash$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([thumbhash]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)!.cast(); + } + } +} diff --git a/mobile/lib/presentation/pages/dev/dev_logger.dart b/mobile/lib/presentation/pages/dev/dev_logger.dart deleted file mode 100644 index ab9849f87c..0000000000 --- a/mobile/lib/presentation/pages/dev/dev_logger.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:immich_mobile/domain/models/log.model.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; -// ignore: import_rule_isar -import 'package:isar/isar.dart'; - -const kDevLoggerTag = 'DEV'; - -abstract final class DLog { - const DLog(); - - static Stream> watchLog() { - final db = Isar.getInstance(); - if (db == null) { - return const Stream.empty(); - } - - return db.loggerMessages - .filter() - .context1EqualTo(kDevLoggerTag) - .sortByCreatedAtDesc() - .watch(fireImmediately: true) - .map((logs) => logs.map((log) => log.toDto()).toList()); - } - - static void clearLog() { - final db = Isar.getInstance(); - if (db == null) { - return; - } - - db.writeTxnSync(() { - db.loggerMessages.filter().context1EqualTo(kDevLoggerTag).deleteAllSync(); - }); - } - - static void log(String message, [Object? error, StackTrace? stackTrace]) { - if (!Platform.environment.containsKey('FLUTTER_TEST')) { - debugPrint('[$kDevLoggerTag] [${DateTime.now()}] $message'); - } - if (error != null) { - debugPrint('Error: $error'); - } - if (stackTrace != null) { - debugPrint('StackTrace: $stackTrace'); - } - - final isar = Isar.getInstance(); - if (isar == null) { - return; - } - - final record = LogMessage( - message: message, - level: LogLevel.info, - createdAt: DateTime.now(), - logger: kDevLoggerTag, - error: error?.toString(), - stack: stackTrace?.toString(), - ); - - unawaited(IsarLogRepository(isar).insert(record)); - } -} diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 7ee151f94d..491c38e7a8 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -6,15 +6,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:logging/logging.dart'; final _features = [ _Feature( @@ -31,27 +29,18 @@ final _features = [ return Future.value(); } - final assets = - await ref.read(remoteAssetRepositoryProvider).getSome(user.id); + final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id); final selectedAssets = await ctx.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: assets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log( - "Selected ${selectedAssets?.length ?? 0} assets", - ); + Logger("FeaturesInDevelopment").fine("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, @@ -75,15 +64,9 @@ final _features = [ _Feature( name: 'WAL Checkpoint', icon: Icons.save_rounded, - onTap: (_, ref) => ref - .read(driftProvider) - .customStatement("pragma wal_checkpoint(truncate)"), - ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), + onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"), ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Clear Delta Checkpoint', icon: Icons.delete_rounded, @@ -91,10 +74,7 @@ final _features = [ ), _Feature( name: 'Clear Local Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_forever_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -105,10 +85,7 @@ final _features = [ ), _Feature( name: 'Clear Remote Data', - style: const TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -121,33 +98,25 @@ final _features = [ await db.memoryAssetEntity.deleteAll(); await db.stackEntity.deleteAll(); await db.personEntity.deleteAll(); + await db.assetFaceEntity.deleteAll(); }, ), _Feature( name: 'Local Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), _Feature( name: 'Remote Media Summary', - style: const TextStyle( - color: Colors.indigo, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.summarize_rounded, onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), ), _Feature( name: 'Reset Sqlite', icon: Icons.table_view_rounded, - style: const TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold), onTap: (_, ref) async { final drift = ref.read(driftProvider); // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member @@ -167,10 +136,7 @@ class FeatInDevPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Features in Development'), - centerTitle: true, - ), + appBar: AppBar(title: Text('features_in_development'.tr()), centerTitle: true), body: Column( children: [ Flexible( @@ -180,10 +146,7 @@ class FeatInDevPage extends StatelessWidget { final feat = _features[index]; return Consumer( builder: (ctx, ref, _) => ListTile( - title: Text( - feat.name, - style: feat.style, - ), + title: Text(feat.name, style: feat.style), trailing: Icon(feat.icon), visualDensity: VisualDensity.compact, onTap: () => unawaited(feat.onTap(ctx, ref)), @@ -194,7 +157,6 @@ class FeatInDevPage extends StatelessWidget { ), ), const Divider(height: 0), - const Flexible(child: _DevLogs()), ], ), ); @@ -202,76 +164,10 @@ class FeatInDevPage extends StatelessWidget { } class _Feature { - const _Feature({ - required this.name, - required this.icon, - required this.onTap, - this.style, - }); + const _Feature({required this.name, required this.icon, required this.onTap, this.style}); final String name; final IconData icon; final TextStyle? style; final Future Function(BuildContext, WidgetRef _) onTap; } - -class _DevLogs extends StatelessWidget { - const _DevLogs(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - actions: [ - IconButton( - onPressed: DLog.clearLog, - icon: Icon( - Icons.delete_outline_rounded, - size: 20.0, - color: context.primaryColor, - semanticLabel: "Clear logs", - ), - ), - ], - centerTitle: true, - ), - body: StreamBuilder( - initialData: [], - stream: DLog.watchLog(), - builder: (_, logMessages) { - return ListView.separated( - itemBuilder: (ctx, index) { - final logMessage = logMessages.data![index]; - return ListTile( - title: Text( - logMessage.message, - style: TextStyle( - color: ctx.colorScheme.onSurface, - fontSize: 14.0, - overflow: TextOverflow.ellipsis, - ), - ), - subtitle: Text( - "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle( - color: ctx.colorScheme.onSurfaceSecondary, - fontSize: 12.0, - ), - ), - dense: true, - visualDensity: VisualDensity.compact, - tileColor: Colors.transparent, - minLeadingWidth: 10, - ); - }, - separatorBuilder: (_, index) { - return const Divider(height: 0); - }, - itemCount: logMessages.data?.length ?? 0, - ); - }, - ), - ); - } -} diff --git a/mobile/lib/presentation/pages/dev/main_timeline.page.dart b/mobile/lib/presentation/pages/dev/main_timeline.page.dart index 0582399eaf..5ec946858d 100644 --- a/mobile/lib/presentation/pages/dev/main_timeline.page.dart +++ b/mobile/lib/presentation/pages/dev/main_timeline.page.dart @@ -11,22 +11,11 @@ class MainTimelinePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); - - return memoryLaneProvider.maybeWhen( - data: (memories) { - return memories.isEmpty - ? const Timeline(showStorageIndicator: true) - : Timeline( - topSliverWidget: SliverToBoxAdapter( - key: Key('memory-lane-${memories.first.assets.first.id}'), - child: DriftMemoryLane(memories: memories), - ), - topSliverWidgetHeight: 200, - showStorageIndicator: true, - ); - }, - orElse: () => const Timeline(showStorageIndicator: true), + final hasMemories = ref.watch(driftMemoryFutureProvider.select((state) => state.value?.isNotEmpty ?? false)); + return Timeline( + topSliverWidget: const SliverToBoxAdapter(child: DriftMemoryLane()), + topSliverWidgetHeight: hasMemories ? 200 : 0, + showStorageIndicator: true, ); } } diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index acd7b219b3..4c18a09200 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -21,12 +22,7 @@ class _Summary extends StatelessWidget { final Future countFuture; final void Function()? onTap; - const _Summary({ - required this.name, - required this.countFuture, - this.leading, - this.onTap, - }); + const _Summary({required this.name, required this.countFuture, this.leading, this.onTap}); @override Widget build(BuildContext context) { @@ -40,31 +36,17 @@ class _Summary extends StatelessWidget { } else if (snapshot.hasError) { subtitle = const Icon(Icons.error_rounded); } else { - subtitle = Text( - '${snapshot.data ?? 0}', - style: ctx.textTheme.bodyLarge, - ); + subtitle = Text('${snapshot.data ?? 0}', style: ctx.textTheme.bodyLarge); } - return ListTile( - leading: leading, - title: Text(name), - trailing: subtitle, - onTap: onTap, - ); + return ListTile(leading: leading, title: Text(name), trailing: subtitle, onTap: onTap); }, ); } } final _localStats = [ - _Stat( - name: 'Local Assets', - load: (db) => db.managers.localAssetEntity.count(), - ), - _Stat( - name: 'Local Albums', - load: (db) => db.managers.localAlbumEntity.count(), - ), + _Stat(name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count()), + _Stat(name: 'Local Albums', load: (db) => db.managers.localAlbumEntity.count()), ]; @RoutePage() @@ -74,7 +56,7 @@ class LocalMediaSummaryPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Local Media Summary')), + appBar: AppBar(title: Text('local_media_summary'.tr())), body: Consumer( builder: (ctx, ref, __) { final db = ref.watch(driftProvider); @@ -97,10 +79,7 @@ class LocalMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), ), ], ), @@ -124,9 +103,7 @@ class LocalMediaSummaryPage extends StatelessWidget { leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - LocalTimelineRoute(album: album), - ), + onTap: () => context.router.push(LocalTimelineRoute(album: album)), ); }, itemCount: albums.length, @@ -142,34 +119,14 @@ class LocalMediaSummaryPage extends StatelessWidget { } final _remoteStats = [ - _Stat( - name: 'Remote Assets', - load: (db) => db.managers.remoteAssetEntity.count(), - ), - _Stat( - name: 'Exif Entities', - load: (db) => db.managers.remoteExifEntity.count(), - ), - _Stat( - name: 'Remote Albums', - load: (db) => db.managers.remoteAlbumEntity.count(), - ), - _Stat( - name: 'Memories', - load: (db) => db.managers.memoryEntity.count(), - ), - _Stat( - name: 'Memories Assets', - load: (db) => db.managers.memoryAssetEntity.count(), - ), - _Stat( - name: 'Stacks', - load: (db) => db.managers.stackEntity.count(), - ), - _Stat( - name: 'People', - load: (db) => db.managers.personEntity.count(), - ), + _Stat(name: 'Remote Assets', load: (db) => db.managers.remoteAssetEntity.count()), + _Stat(name: 'Exif Entities', load: (db) => db.managers.remoteExifEntity.count()), + _Stat(name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count()), + _Stat(name: 'Memories', load: (db) => db.managers.memoryEntity.count()), + _Stat(name: 'Memories Assets', load: (db) => db.managers.memoryAssetEntity.count()), + _Stat(name: 'Stacks', load: (db) => db.managers.stackEntity.count()), + _Stat(name: 'People', load: (db) => db.managers.personEntity.count()), + _Stat(name: 'AssetFaces', load: (db) => db.managers.assetFaceEntity.count()), ]; @RoutePage() @@ -179,7 +136,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Remote Media Summary')), + appBar: AppBar(title: Text('remote_media_summary'.tr())), body: Consumer( builder: (ctx, ref, __) { final db = ref.watch(driftProvider); @@ -202,10 +159,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("album_summary".tr(), style: ctx.textTheme.titleMedium), ), ], ), @@ -229,9 +183,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - RemoteAlbumRoute(album: album), - ), + onTap: () => context.router.push(RemoteAlbumRoute(album: album)), ); }, itemCount: albums.length, diff --git a/mobile/lib/presentation/pages/download_info.page.dart b/mobile/lib/presentation/pages/download_info.page.dart new file mode 100644 index 0000000000..e805458e76 --- /dev/null +++ b/mobile/lib/presentation/pages/download_info.page.dart @@ -0,0 +1,57 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/pages/common/download_panel.dart'; +import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; + +@RoutePage() +class DownloadInfoPage extends ConsumerWidget { + const DownloadInfoPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); + + onCancelDownload(String id) { + ref.watch(downloadStateProvider.notifier).cancelDownload(id); + } + + return Scaffold( + appBar: AppBar( + title: Text("download".t(context: context)), + actions: [], + ), + body: ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemCount: tasks.length, + itemBuilder: (context, index) { + final task = tasks[index]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + child: DownloadTaskTile( + progress: task.value.progress, + fileName: task.value.fileName, + status: task.value.status, + onCancelDownload: () => onCancelDownload(task.key), + ), + ); + }, + ), + persistentFooterButtons: [ + OutlinedButton( + onPressed: () { + tasks.map((e) => e.key).forEach(onCancelDownload); + }, + style: OutlinedButton.styleFrom(side: BorderSide(color: context.colorScheme.primary)), + child: Text( + 'clear_all'.t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), + ), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_activities.page.dart b/mobile/lib/presentation/pages/drift_activities.page.dart new file mode 100644 index 0000000000..8e67d85884 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_activities.page.dart @@ -0,0 +1,104 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/models/activities/activity.model.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/like_activity_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/drift_activity_text_field.dart'; +import 'package:immich_mobile/providers/activity.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/activities/activity_tile.dart'; +import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; + +@RoutePage() +class DriftActivitiesPage extends HookConsumerWidget { + const DriftActivitiesPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final album = ref.watch(currentRemoteAlbumProvider)!; + final asset = ref.watch(currentAssetNotifier) as RemoteAsset?; + final user = ref.watch(currentUserProvider); + + final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier); + final activities = ref.watch(albumActivityProvider(album.id, asset?.id)); + final listViewScrollController = useScrollController(); + + void scrollToBottom() { + listViewScrollController.animateTo( + listViewScrollController.position.maxScrollExtent + 80, + duration: const Duration(milliseconds: 600), + curve: Curves.fastOutSlowIn, + ); + } + + Future onAddComment(String comment) async { + await activityNotifier.addComment(comment); + scrollToBottom(); + } + + return Scaffold( + appBar: AppBar( + title: asset == null ? Text(album.name) : null, + actions: [const LikeActivityActionButton(menuItem: true)], + actionsPadding: const EdgeInsets.only(right: 8), + ), + body: activities.widgetWhen( + onData: (data) { + final liked = data.firstWhereOrNull( + (a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.id, + ); + + return SafeArea( + child: Stack( + children: [ + ListView.builder( + controller: listViewScrollController, + itemCount: data.length + 1, + itemBuilder: (context, index) { + if (index == data.length) { + return const SizedBox(height: 80); + } + final activity = data[index]; + final canDelete = activity.user.id == user?.id || album.ownerId == user?.id; + return Padding( + padding: const EdgeInsets.all(5), + child: DismissibleActivity( + activity.id, + ActivityTile(activity), + onDismiss: canDelete + ? (activityId) async => await activityNotifier.removeActivity(activity.id) + : null, + ), + ); + }, + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + border: Border(top: BorderSide(color: context.colorScheme.secondaryContainer, width: 1)), + ), + child: DriftActivityTextField( + isEnabled: album.isActivityEnabled, + likeId: liked?.id, + onSubmit: onAddComment, + ), + ), + ), + ], + ), + ); + }, + ), + resizeToAvoidBottomInset: true, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index c7dffbeaef..0835c741ad 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -1,24 +1,13 @@ import 'dart:async'; -import 'dart:math'; import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/album/album.model.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/models/albums/album_search.model.dart'; -import 'package:immich_mobile/pages/common/large_leading_tile.dart'; -import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/utils/remote_album.utils.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; -import 'package:immich_mobile/widgets/common/search_field.dart'; @RoutePage() class DriftAlbumsPage extends ConsumerStatefulWidget { @@ -29,70 +18,12 @@ class DriftAlbumsPage extends ConsumerStatefulWidget { } class _DriftAlbumsPageState extends ConsumerState { - bool isGrid = false; - final searchController = TextEditingController(); - QuickFilterMode filterMode = QuickFilterMode.all; - final searchFocusNode = FocusNode(); - - @override - void initState() { - super.initState(); - - // Load albums when component mounts - WidgetsBinding.instance.addPostFrameCallback((_) { - ref.read(remoteAlbumProvider.notifier).refresh(); - }); - - searchController.addListener(() { - onSearch(searchController.text, filterMode); - }); - } - - void onSearch(String searchTerm, QuickFilterMode sortMode) { - final userId = ref.watch(currentUserProvider)?.id; - ref - .read(remoteAlbumProvider.notifier) - .searchAlbums(searchTerm, userId, sortMode); - } - Future onRefresh() async { await ref.read(remoteAlbumProvider.notifier).refresh(); } - void toggleViewMode() { - setState(() { - isGrid = !isGrid; - }); - } - - void changeFilter(QuickFilterMode sortMode) { - setState(() { - filterMode = sortMode; - }); - } - - void clearSearch() { - setState(() { - filterMode = QuickFilterMode.all; - searchController.clear(); - ref.read(remoteAlbumProvider.notifier).clearSearch(); - }); - } - - @override - void dispose() { - searchController.dispose(); - searchFocusNode.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - final albums = - ref.watch(remoteAlbumProvider.select((s) => s.filteredAlbums)); - - final userId = ref.watch(currentUserProvider)?.id; - return RefreshIndicator( onRefresh: onRefresh, edgeOffset: 100, @@ -104,632 +35,20 @@ class _DriftAlbumsPageState extends ConsumerState { pinned: true, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - const DriftCreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()), ), ], showUploadButton: false, ), - _SearchBar( - searchController: searchController, - searchFocusNode: searchFocusNode, - onSearch: onSearch, - filterMode: filterMode, - onClearSearch: clearSearch, + AlbumSelector( + onAlbumSelected: (album) { + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); + context.router.push(RemoteAlbumRoute(album: album)); + }, ), - _QuickFilterButtonRow( - filterMode: filterMode, - onChangeFilter: changeFilter, - onSearch: onSearch, - searchController: searchController, - ), - _QuickSortAndViewMode( - isGrid: isGrid, - onToggleViewMode: toggleViewMode, - ), - isGrid - ? _AlbumGrid( - albums: albums, - userId: userId, - ) - : _AlbumList( - albums: albums, - userId: userId, - ), ], ), ); } } - -class _SortButton extends ConsumerStatefulWidget { - const _SortButton(); - - @override - ConsumerState<_SortButton> createState() => _SortButtonState(); -} - -class _SortButtonState extends ConsumerState<_SortButton> { - RemoteAlbumSortMode albumSortOption = RemoteAlbumSortMode.lastModified; - bool albumSortIsReverse = true; - - void onMenuTapped(RemoteAlbumSortMode sortMode) { - final selected = albumSortOption == sortMode; - // Switch direction - if (selected) { - setState(() { - albumSortIsReverse = !albumSortIsReverse; - }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); - } else { - setState(() { - albumSortOption = sortMode; - }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); - } - } - - @override - Widget build(BuildContext context) { - return MenuAnchor( - style: MenuStyle( - elevation: const WidgetStatePropertyAll(1), - shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), - ), - ), - consumeOutsideTap: true, - menuChildren: RemoteAlbumSortMode.values - .map( - (sortMode) => MenuItemButton( - leadingIcon: albumSortOption == sortMode - ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : const Icon(Icons.abc, color: Colors.transparent), - onPressed: () => onMenuTapped(sortMode), - style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), - backgroundColor: WidgetStateProperty.all( - albumSortOption == sortMode - ? context.colorScheme.primary - : Colors.transparent, - ), - shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - ), - child: Text( - sortMode.key.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface.withAlpha(185), - ), - ), - ), - ) - .toList(), - builder: (context, controller, child) { - return GestureDetector( - onTap: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 5), - child: albumSortIsReverse - ? const Icon( - Icons.keyboard_arrow_down, - ) - : const Icon( - Icons.keyboard_arrow_up_rounded, - ), - ), - Text( - albumSortOption.key.t(context: context), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.onSurface.withAlpha(225), - ), - ), - ], - ), - ); - }, - ); - } -} - -class _SearchBar extends StatelessWidget { - const _SearchBar({ - required this.searchController, - required this.searchFocusNode, - required this.onSearch, - required this.filterMode, - required this.onClearSearch, - }); - - final TextEditingController searchController; - final FocusNode searchFocusNode; - final void Function(String, QuickFilterMode) onSearch; - final QuickFilterMode filterMode; - final VoidCallback onClearSearch; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - sliver: SliverToBoxAdapter( - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withValues(alpha: 0.075), - context.colorScheme.primary.withValues(alpha: 0.09), - context.colorScheme.primary.withValues(alpha: 0.075), - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - transform: const GradientRotation(0.5 * pi), - ), - ), - child: SearchField( - autofocus: false, - contentPadding: const EdgeInsets.all(16), - hintText: 'search_albums'.tr(), - prefixIcon: const Icon(Icons.search_rounded), - suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClearSearch, - ) - : null, - controller: searchController, - onChanged: (_) => onSearch(searchController.text, filterMode), - focusNode: searchFocusNode, - onTapOutside: (_) => searchFocusNode.unfocus(), - ), - ), - ), - ); - } -} - -class _QuickFilterButtonRow extends StatelessWidget { - const _QuickFilterButtonRow({ - required this.filterMode, - required this.onChangeFilter, - required this.onSearch, - required this.searchController, - }); - - final QuickFilterMode filterMode; - final void Function(QuickFilterMode) onChangeFilter; - final void Function(String, QuickFilterMode) onSearch; - final TextEditingController searchController; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( - child: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - _QuickFilterButton( - label: 'all'.tr(), - isSelected: filterMode == QuickFilterMode.all, - onTap: () { - onChangeFilter(QuickFilterMode.all); - onSearch(searchController.text, QuickFilterMode.all); - }, - ), - _QuickFilterButton( - label: 'shared_with_me'.tr(), - isSelected: filterMode == QuickFilterMode.sharedWithMe, - onTap: () { - onChangeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); - }, - ), - _QuickFilterButton( - label: 'my_albums'.tr(), - isSelected: filterMode == QuickFilterMode.myAlbums, - onTap: () { - onChangeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); - }, - ), - ], - ), - ), - ); - } -} - -class _QuickFilterButton extends StatelessWidget { - const _QuickFilterButton({ - required this.isSelected, - required this.onTap, - required this.label, - }); - - final bool isSelected; - final VoidCallback onTap; - final String label; - - @override - Widget build(BuildContext context) { - return TextButton( - onPressed: onTap, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), - ), - ), - ), - child: Text( - label, - style: TextStyle( - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - fontSize: 14, - ), - ), - ); - } -} - -class _QuickSortAndViewMode extends StatelessWidget { - const _QuickSortAndViewMode({ - required this.isGrid, - required this.onToggleViewMode, - }); - - final bool isGrid; - final VoidCallback onToggleViewMode; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const _SortButton(), - IconButton( - icon: Icon( - isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), - onPressed: onToggleViewMode, - ), - ], - ), - ), - ); - } -} - -class _AlbumList extends ConsumerWidget { - const _AlbumList({ - required this.albums, - required this.userId, - }); - - final List albums; - final String? userId; - - @override - Widget build(BuildContext context, WidgetRef ref) { - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - sliver: SliverList.builder( - itemBuilder: (_, index) { - final album = albums[index]; - - return Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - ), - child: LargeLeadingTile( - title: Text( - album.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - subtitle: Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', - overflow: TextOverflow.ellipsis, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - onTap: () { - ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); - }, - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: album.thumbnailAssetId != null - ? ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: SizedBox( - width: 80, - height: 80, - child: Thumbnail( - remoteId: album.thumbnailAssetId, - ), - ), - ) - : SizedBox( - width: 80, - height: 80, - child: Container( - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainer, - borderRadius: - const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: const Icon( - Icons.photo_album_rounded, - size: 24, - color: Colors.grey, - ), - ), - ), - ), - ); - }, - itemCount: albums.length, - ), - ); - } -} - -class _AlbumGrid extends StatelessWidget { - const _AlbumGrid({ - required this.albums, - required this.userId, - }); - - final List albums; - final String? userId; - - @override - Widget build(BuildContext context) { - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 250, - mainAxisSpacing: 4, - crossAxisSpacing: 4, - childAspectRatio: .7, - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final album = albums[index]; - return _GridAlbumCard( - album: album, - userId: userId, - ); - }, - childCount: albums.length, - ), - ), - ); - } -} - -class _GridAlbumCard extends ConsumerWidget { - const _GridAlbumCard({ - required this.album, - required this.userId, - }); - - final RemoteAlbum album; - final String? userId; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return GestureDetector( - onTap: () { - ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); - }, - child: Card( - elevation: 0, - color: context.colorScheme.surfaceBright, - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: ClipRRect( - borderRadius: const BorderRadius.vertical( - top: Radius.circular(15), - ), - child: SizedBox( - width: double.infinity, - child: album.thumbnailAssetId != null - ? Thumbnail( - remoteId: album.thumbnailAssetId, - ) - : Container( - color: context.colorScheme.surfaceContainerHighest, - child: const Icon( - Icons.photo_album_rounded, - size: 40, - color: Colors.grey, - ), - ), - ), - ), - ), - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - album.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/mobile/lib/presentation/pages/drift_album_options.page.dart b/mobile/lib/presentation/pages/drift_album_options.page.dart new file mode 100644 index 0000000000..7f49a1ff79 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_album_options.page.dart @@ -0,0 +1,237 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; + +@RoutePage() +class DriftAlbumOptionsPage extends HookConsumerWidget { + const DriftAlbumOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final album = ref.watch(currentRemoteAlbumProvider); + if (album == null) { + return const SizedBox(); + } + + final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(album.id)); + final userId = ref.watch(authProvider).userId; + final activityEnabled = useState(album.isActivityEnabled); + final isOwner = album.ownerId == userId; + + void showErrorMessage() { + context.pop(); + ImmichToast.show( + context: context, + msg: "shared_album_section_people_action_error".t(context: context), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + + void leaveAlbum() async { + try { + await ref.read(remoteAlbumProvider.notifier).leaveAlbum(album.id, userId: userId); + context.navigateTo(const DriftAlbumsRoute()); + } catch (_) { + showErrorMessage(); + } + } + + void removeUserFromAlbum(UserDto user) async { + try { + await ref.read(remoteAlbumProvider.notifier).removeUser(album.id, user.id); + ref.invalidate(remoteAlbumSharedUsersProvider(album.id)); + } catch (_) { + showErrorMessage(); + } + + context.pop(); + } + + Future addUsers() async { + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: album)); + + if (newUsers == null || newUsers.isEmpty) { + return; + } + + try { + await ref.read(remoteAlbumProvider.notifier).addUsers(album.id, newUsers); + + if (newUsers.isNotEmpty) { + ImmichToast.show( + context: context, + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), + toastType: ToastType.success, + ); + } + + ref.invalidate(remoteAlbumSharedUsersProvider(album.id)); + } catch (e) { + ImmichToast.show( + context: context, + msg: "Failed to add users to album: ${e.toString()}", + toastType: ToastType.error, + ); + } + } + + void handleUserClick(UserDto user) { + var actions = []; + + if (user.id == userId) { + actions = [ + ListTile( + leading: const Icon(Icons.exit_to_app_rounded), + title: const Text("leave_album").t(context: context), + onTap: leaveAlbum, + ), + ]; + } + + if (isOwner) { + actions = [ + ListTile( + leading: const Icon(Icons.person_remove_rounded), + title: const Text("remove_user").t(context: context), + onTap: () => removeUserFromAlbum(user), + ), + ]; + } + + showModalBottomSheet( + backgroundColor: context.colorScheme.surfaceContainer, + isScrollControlled: false, + context: context, + builder: (context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.only(top: 24.0), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), + ), + ); + }, + ); + } + + buildOwnerInfo() { + if (isOwner) { + final owner = ref.watch(currentUserProvider); + return ListTile( + leading: owner != null ? UserCircleAvatar(user: owner) : const SizedBox(), + title: Text(album.ownerName, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(owner?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).t(context: context), + ); + } else { + final usersProvider = ref.watch(driftUsersProvider); + return usersProvider.maybeWhen( + data: (users) { + final user = users.firstWhereOrNull((u) => u.id == album.ownerId); + + if (user == null) { + return const SizedBox(); + } + + return ListTile( + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).t(context: context), + ); + }, + orElse: () => const SizedBox(), + ); + } + } + + buildSharedUsersList() { + return sharedUsersAsync.maybeWhen( + data: (sharedUsers) => ListView.builder( + primary: false, + shrinkWrap: true, + itemCount: sharedUsers.length, + itemBuilder: (context, index) { + final user = sharedUsers[index]; + return ListTile( + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), + onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, + ); + }, + ), + orElse: () => const Center(child: CircularProgressIndicator()), + ); + } + + buildSectionTitle(String text) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Text(text, style: context.textTheme.bodySmall), + ); + } + + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () => context.maybePop(null), + ), + centerTitle: true, + title: Text("options".t(context: context)), + ), + body: ListView( + children: [ + const SizedBox(height: 8), + if (isOwner) + SwitchListTile.adaptive( + value: activityEnabled.value, + onChanged: (bool value) async { + activityEnabled.value = value; + await ref.read(remoteAlbumProvider.notifier).setActivityStatus(album.id, value); + }, + activeThumbColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor, + dense: true, + title: Text( + "comments_and_likes", + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), + ).t(context: context), + subtitle: Text( + "let_others_respond", + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ).t(context: context), + ), + buildSectionTitle("shared_album_section_people_title".t(context: context)), + if (isOwner) ...[ + ListTile( + leading: const Icon(Icons.person_add_rounded), + title: Text("invite_people".t(context: context)), + onTap: () async => addUsers(), + ), + const Divider(indent: 16), + ], + buildOwnerInfo(), + buildSharedUsersList(), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_archive.page.dart b/mobile/lib/presentation/pages/drift_archive.page.dart index 90b8dcb646..ee8417bcad 100644 --- a/mobile/lib/presentation/pages/drift_archive.page.dart +++ b/mobile/lib/presentation/pages/drift_archive.page.dart @@ -16,19 +16,16 @@ class DriftArchivePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access archive'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } - final timelineService = - ref.watch(timelineFactoryProvider).archive(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart index 7ac378e4f5..19f813cdb5 100644 --- a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class DriftAssetSelectionTimelinePage extends ConsumerWidget { final Set lockedSelectionAssets; - const DriftAssetSelectionTimelinePage({ - super.key, - this.lockedSelectionAssets = const {}, - }); + const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,28 +18,19 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget { overrides: [ multiSelectProvider.overrideWith( () => MultiSelectNotifier( - MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: lockedSelectionAssets, - forceEnable: true, - ), + MultiSelectState(selectedAssets: {}, lockedSelectionAssets: lockedSelectionAssets, forceEnable: true), ), ), - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access asset selection timeline', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access asset selection timeline'); + } - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: const Timeline(), ); diff --git a/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart new file mode 100644 index 0000000000..7a899f4e72 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_asset_troubleshoot.page.dart @@ -0,0 +1,352 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; + +@RoutePage() +class AssetTroubleshootPage extends ConsumerWidget { + final BaseAsset asset; + + const AssetTroubleshootPage({super.key, required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar(title: Text('asset_troubleshoot'.tr())), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: _AssetDetailsView(asset: asset), + ), + ), + ); + } +} + +class _AssetDetailsView extends ConsumerWidget { + final BaseAsset asset; + + const _AssetDetailsView({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _AssetPropertiesSection(asset: asset), + const SizedBox(height: 16), + Text( + 'matching_assets'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), + ), + if (asset.checksum != null) ...[ + _LocalAssetsSection(asset: asset), + const SizedBox(height: 16), + _RemoteAssetSection(asset: asset), + ] else ...[ + _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'no_checksum_local'.tr())], + ), + const SizedBox(height: 16), + _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'no_checksum_remote'.tr())], + ), + ], + ], + ); + } +} + +class _AssetPropertiesSection extends ConsumerStatefulWidget { + final BaseAsset asset; + + const _AssetPropertiesSection({required this.asset}); + + @override + ConsumerState createState() => _AssetPropertiesSectionState(); +} + +class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection> { + List<_PropertyItem> properties = []; + + @override + void initState() { + super.initState(); + _buildAssetProperties(widget.asset).whenComplete(() { + if (mounted) { + setState(() {}); + } + }); + } + + @override + Widget build(BuildContext context) { + final title = _getAssetTypeTitle(widget.asset); + + return _PropertySectionCard(title: title, properties: properties); + } + + Future _buildAssetProperties(BaseAsset asset) async { + _addCommonProperties(); + + if (asset is LocalAsset) { + await _addLocalAssetProperties(asset); + } else if (asset is RemoteAsset) { + await _addRemoteAssetProperties(asset); + } + } + + void _addCommonProperties() { + final asset = widget.asset; + properties.addAll([ + _PropertyItem(label: 'Name', value: asset.name), + _PropertyItem(label: 'Checksum', value: asset.checksum), + _PropertyItem(label: 'Type', value: asset.type.toString()), + _PropertyItem(label: 'Created At', value: asset.createdAt.toString()), + _PropertyItem(label: 'Updated At', value: asset.updatedAt.toString()), + _PropertyItem(label: 'Width', value: asset.width?.toString()), + _PropertyItem(label: 'Height', value: asset.height?.toString()), + _PropertyItem( + label: 'Duration', + value: asset.durationInSeconds != null ? '${asset.durationInSeconds} seconds' : null, + ), + _PropertyItem(label: 'Is Favorite', value: asset.isFavorite.toString()), + _PropertyItem(label: 'Live Photo Video ID', value: asset.livePhotoVideoId), + ]); + } + + Future _addLocalAssetProperties(LocalAsset asset) async { + properties.insertAll(0, [ + _PropertyItem(label: 'Local ID', value: asset.id), + _PropertyItem(label: 'Remote ID', value: asset.remoteId), + ]); + + properties.insert(4, _PropertyItem(label: 'Orientation', value: asset.orientation.toString())); + final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id); + properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', '))); + } + + Future _addRemoteAssetProperties(RemoteAsset asset) async { + properties.insertAll(0, [ + _PropertyItem(label: 'Remote ID', value: asset.id), + _PropertyItem(label: 'Local ID', value: asset.localId), + _PropertyItem(label: 'Owner ID', value: asset.ownerId), + ]); + + final additionalProps = <_PropertyItem>[ + _PropertyItem(label: 'Thumb Hash', value: asset.thumbHash), + _PropertyItem(label: 'Visibility', value: asset.visibility.toString()), + _PropertyItem(label: 'Stack ID', value: asset.stackId), + ]; + + properties.insertAll(4, additionalProps); + + final exif = await ref.read(assetServiceProvider).getExif(asset); + if (exif != null) { + _addExifProperties(exif); + } else { + properties.add(const _PropertyItem(label: 'EXIF', value: null)); + } + } + + void _addExifProperties(ExifInfo exif) { + properties.addAll([ + _PropertyItem( + label: 'File Size', + value: exif.fileSize != null ? '${(exif.fileSize! / 1024 / 1024).toStringAsFixed(2)} MB' : null, + ), + _PropertyItem(label: 'Description', value: exif.description), + _PropertyItem(label: 'EXIF Width', value: exif.width?.toString()), + _PropertyItem(label: 'EXIF Height', value: exif.height?.toString()), + _PropertyItem(label: 'Date Taken', value: exif.dateTimeOriginal?.toString()), + _PropertyItem(label: 'Time Zone', value: exif.timeZone), + _PropertyItem(label: 'Camera Make', value: exif.make), + _PropertyItem(label: 'Camera Model', value: exif.model), + _PropertyItem(label: 'Lens', value: exif.lens), + _PropertyItem(label: 'F-Number', value: exif.f != null ? 'f/${exif.fNumber}' : null), + _PropertyItem(label: 'Focal Length', value: exif.mm != null ? '${exif.focalLength}mm' : null), + _PropertyItem(label: 'ISO', value: exif.iso?.toString()), + _PropertyItem(label: 'Exposure Time', value: exif.exposureTime.isNotEmpty ? exif.exposureTime : null), + _PropertyItem( + label: 'GPS Coordinates', + value: exif.hasCoordinates ? '${exif.latitude}, ${exif.longitude}' : null, + ), + _PropertyItem( + label: 'Location', + value: [exif.city, exif.state, exif.country].where((e) => e != null && e.isNotEmpty).join(', '), + ), + ]); + } + + String _getAssetTypeTitle(BaseAsset asset) { + if (asset is LocalAsset) return 'Local Asset'; + if (asset is RemoteAsset) return 'Remote Asset'; + return 'Base Asset'; + } +} + +class _LocalAssetsSection extends ConsumerWidget { + final BaseAsset asset; + + const _LocalAssetsSection({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + + return FutureBuilder>( + future: assetService.getLocalAssetsByChecksum(asset.checksum!), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'Loading...')], + ); + } + + if (snapshot.hasError) { + return _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())], + ); + } + + final localAssets = snapshot.data?.cast() ?? []; + if (asset is LocalAsset) { + localAssets.removeWhere((a) => a.id == (asset as LocalAsset).id); + + if (localAssets.isEmpty) { + return const SizedBox.shrink(); + } + } + + if (localAssets.isEmpty) { + return _PropertySectionCard( + title: 'Local Assets', + properties: [_PropertyItem(label: 'Status', value: 'no_local_assets_found'.tr())], + ); + } + + return Column( + children: [ + if (localAssets.length > 1) + _PropertySectionCard( + title: 'Local Assets Summary', + properties: [_PropertyItem(label: 'Total Count', value: localAssets.length.toString())], + ), + ...localAssets.map((localAsset) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: _AssetPropertiesSection(asset: localAsset), + ); + }), + ], + ); + }, + ); + } +} + +class _RemoteAssetSection extends ConsumerWidget { + final BaseAsset asset; + + const _RemoteAssetSection({required this.asset}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + + if (asset is RemoteAsset) { + return const SizedBox.shrink(); + } + + return FutureBuilder( + future: assetService.getRemoteAssetByChecksum(asset.checksum!), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'Loading...')], + ); + } + + if (snapshot.hasError) { + return _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Error', value: snapshot.error.toString())], + ); + } + + final remoteAsset = snapshot.data; + + if (remoteAsset == null) { + return _PropertySectionCard( + title: 'Remote Assets', + properties: [_PropertyItem(label: 'Status', value: 'no_remote_assets_found'.tr())], + ); + } + + return _AssetPropertiesSection(asset: remoteAsset); + }, + ); + } +} + +class _PropertySectionCard extends StatelessWidget { + final String title; + final List<_PropertyItem> properties; + + const _PropertySectionCard({required this.title, required this.properties}); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 8), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + ...properties, + ], + ), + ), + ); + } +} + +class _PropertyItem extends StatelessWidget { + final String label; + final String? value; + + const _PropertyItem({required this.label, this.value}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 120, + child: Text('$label:', style: const TextStyle(fontWeight: FontWeight.w500)), + ), + Expanded( + child: Text( + value ?? 'not_available'.tr(), + style: TextStyle(color: Theme.of(context).colorScheme.secondary), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_create_album.page.dart b/mobile/lib/presentation/pages/drift_create_album.page.dart index f6ba98f61c..c70c4a0bd7 100644 --- a/mobile/lib/presentation/pages/drift_create_album.page.dart +++ b/mobile/lib/presentation/pages/drift_create_album.page.dart @@ -15,8 +15,7 @@ class DriftCreateAlbumPage extends ConsumerStatefulWidget { const DriftCreateAlbumPage({super.key}); @override - ConsumerState createState() => - _DriftCreateAlbumPageState(); + ConsumerState createState() => _DriftCreateAlbumPageState(); } class _DriftCreateAlbumPageState extends ConsumerState { @@ -71,12 +70,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildContent() { if (selectedAssets.isEmpty) { - return SliverList( - delegate: SliverChildListDelegate([ - _buildEmptyState(), - _buildSelectPhotosButton(), - ]), - ); + return SliverList(delegate: SliverChildListDelegate([_buildEmptyState(), _buildSelectPhotosButton()])); } else { return _buildSelectedImageGrid(); } @@ -85,10 +79,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildEmptyState() { return Padding( padding: const EdgeInsets.only(top: 0, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).t(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).t(), ); } @@ -98,27 +89,17 @@ class _DriftCreateAlbumPageState extends ConsumerState { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric( - vertical: 24.0, - horizontal: 16.0, - ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)), - ), + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotos, icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( - padding: const EdgeInsets.only( - left: 8.0, - ), + padding: const EdgeInsets.only(left: 8.0), child: Text( 'create_shared_album_page_share_select_photos', - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).t(), ), ), @@ -134,16 +115,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { crossAxisSpacing: 1.0, mainAxisSpacing: 1.0, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final asset = selectedAssets.elementAt(index); - return GestureDetector( - onTap: onBackgroundTapped, - child: Thumbnail(asset: asset), - ); - }, - childCount: selectedAssets.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final asset = selectedAssets.elementAt(index); + return GestureDetector( + onTap: onBackgroundTapped, + child: Thumbnail.fromAsset(asset: asset), + ); + }, childCount: selectedAssets.length), ), ); } @@ -163,9 +141,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Future onSelectPhotos() async { final assets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: selectedAssets, - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets), ); if (assets == null || assets.isEmpty) { @@ -184,16 +160,15 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (title.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('create_album_title_required'.t()), - backgroundColor: context.colorScheme.error, - ), + SnackBar(content: Text('create_album_title_required'.t()), backgroundColor: context.colorScheme.error), ); } return; } - final album = await ref.watch(remoteAlbumProvider.notifier).createAlbum( + final album = await ref + .watch(remoteAlbumProvider.notifier) + .createAlbum( title: title, description: albumDescriptionController.text.trim(), assetIds: selectedAssets.map((asset) { @@ -204,18 +179,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (album != null) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.replaceRoute( - RemoteAlbumRoute(album: album), - ); + context.replaceRoute(RemoteAlbumRoute(album: album)); } } Widget buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0), child: _AlbumTitleTextField( focusNode: albumTitleTextFieldFocusNode, textController: albumTitleController, @@ -231,11 +201,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - top: 8, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0, top: 8), child: _AlbumViewerEditableDescription( textController: albumDescriptionController, focusNode: albumDescriptionTextFieldFocusNode, @@ -245,11 +211,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildControlButton() { return Padding( - padding: const EdgeInsets.only( - left: 12.0, - top: 8.0, - bottom: 8.0, - ), + padding: const EdgeInsets.only(left: 12.0, top: 8.0, bottom: 8.0), child: SizedBox( height: 42.0, child: ListView( @@ -273,10 +235,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { elevation: 0, centerTitle: false, backgroundColor: context.scaffoldBackgroundColor, - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.close_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.close_rounded)), title: const Text('create_album').t(), actions: [ TextButton( @@ -285,9 +244,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { 'create'.t(), style: TextStyle( fontWeight: FontWeight.bold, - color: _canCreateAlbum - ? context.primaryColor - : context.themeData.disabledColor, + color: _canCreateAlbum ? context.primaryColor : context.themeData.disabledColor, ), ), ), @@ -295,12 +252,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { ), body: GestureDetector( onTap: onBackgroundTapped, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildContent(), - ], - ), + child: CustomScrollView(slivers: [_buildSliverAppBar(), _buildContent()]), ), ); } @@ -344,48 +296,31 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { Widget build(BuildContext context) { return TextField( focusNode: widget.focusNode, - style: TextStyle( - fontSize: 28.0, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28.0, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: widget.textController, onTap: () { - if (widget.textController.text == - 'create_album_page_untitled'.t(context: context)) { + if (widget.textController.text == 'create_album_page_untitled'.t(context: context)) { widget.textController.clear(); } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), suffixIcon: widget.textController.text.isNotEmpty && widget.isFocus ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(16.0), - ), + borderRadius: BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintText: 'add_a_title'.t(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( @@ -402,21 +337,16 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } class _AlbumViewerEditableDescription extends StatefulWidget { - const _AlbumViewerEditableDescription({ - required this.textController, - required this.focusNode, - }); + const _AlbumViewerEditableDescription({required this.textController, required this.focusNode}); final TextEditingController textController; final FocusNode focusNode; @override - State<_AlbumViewerEditableDescription> createState() => - _AlbumViewerEditableDescriptionState(); + State<_AlbumViewerEditableDescription> createState() => _AlbumViewerEditableDescriptionState(); } -class _AlbumViewerEditableDescriptionState - extends State<_AlbumViewerEditableDescription> { +class _AlbumViewerEditableDescriptionState extends State<_AlbumViewerEditableDescription> { @override void initState() { super.initState(); @@ -454,38 +384,23 @@ class _AlbumViewerEditableDescriptionState minLines: 1, controller: widget.textController, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 16.0, - ), - suffixIcon: - widget.focusNode.hasFocus && widget.textController.text.isNotEmpty - ? IconButton( - onPressed: () { - widget.textController.clear(); - }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), - splashRadius: 10.0, - ) - : null, + contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0), + suffixIcon: widget.focusNode.hasFocus && widget.textController.text.isNotEmpty + ? IconButton( + onPressed: () { + widget.textController.clear(); + }, + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), + splashRadius: 10.0, + ) + : null, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( fontSize: 16.0, diff --git a/mobile/lib/presentation/pages/drift_favorite.page.dart b/mobile/lib/presentation/pages/drift_favorite.page.dart index 90a273f93b..2bc3f26363 100644 --- a/mobile/lib/presentation/pages/drift_favorite.page.dart +++ b/mobile/lib/presentation/pages/drift_favorite.page.dart @@ -16,19 +16,16 @@ class DriftFavoritePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access favorite'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access favorite'); + } - final timelineService = - ref.watch(timelineFactoryProvider).favorite(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index eba0a5ea81..d1d663e4f4 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -6,10 +7,10 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; -import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; -import 'package:immich_mobile/providers/search/people.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -27,12 +28,7 @@ class DriftLibraryPage extends ConsumerWidget { return const Scaffold( body: CustomScrollView( slivers: [ - ImmichSliverAppBar( - snap: false, - floating: false, - pinned: true, - showUploadButton: false, - ), + ImmichSliverAppBar(snap: false, floating: false, pinned: true, showUploadButton: false), _ActionButtonGrid(), _CollectionCards(), _QuickAccessButtonList(), @@ -47,9 +43,7 @@ class _ActionButtonGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12), @@ -79,9 +73,7 @@ class _ActionButtonGrid extends ConsumerWidget { onTap: () => context.pushRoute(const SharedLinkRoute()), label: 'shared_links'.t(context: context), ), - isTrashEnable - ? const SizedBox(width: 8) - : const SizedBox.shrink(), + isTrashEnable ? const SizedBox(width: 8) : const SizedBox.shrink(), isTrashEnable ? _ActionButton( icon: Icons.delete_outline_rounded, @@ -99,11 +91,7 @@ class _ActionButtonGrid extends ConsumerWidget { } class _ActionButton extends StatelessWidget { - const _ActionButton({ - required this.icon, - required this.onTap, - required this.label, - }); + const _ActionButton({required this.icon, required this.onTap, required this.label}); final IconData icon; final VoidCallback onTap; @@ -116,13 +104,7 @@ class _ActionButton extends StatelessWidget { onPressed: onTap, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -131,16 +113,10 @@ class _ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } @@ -157,11 +133,7 @@ class _CollectionCards extends StatelessWidget { child: Wrap( spacing: 8, runSpacing: 8, - children: [ - _PeopleCollectionCard(), - _PlacesCollectionCard(), - _LocalAlbumsCollectionCard(), - ], + children: [_PeopleCollectionCard(), _PlacesCollectionCard(), _LocalAlbumsCollectionCard()], ), ), ); @@ -173,7 +145,7 @@ class _PeopleCollectionCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final people = ref.watch(getAllPeopleProvider); + final people = ref.watch(driftGetAllPeopleProvider); return LayoutBuilder( builder: (context, constraints) { @@ -182,7 +154,7 @@ class _PeopleCollectionCard extends ConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PeopleCollectionRoute()), + onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -190,22 +162,15 @@ class _PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -255,9 +220,7 @@ class _PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - DriftPlaceRoute(currentLocation: null), - ), + onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -267,20 +230,14 @@ class _PlacesCollectionCard extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), - color: - context.colorScheme.secondaryContainer.withAlpha(100), + color: context.colorScheme.secondaryContainer.withAlpha(100), ), child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: context.isDarkTheme - ? ThemeMode.dark - : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -328,10 +285,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -345,24 +299,16 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { children: albums.when( data: (data) { return data.take(4).map((album) { - return LocalAlbumThumbnail( - albumId: album.id, - ); + return LocalAlbumThumbnail(albumId: album.id); }).toList(); }, error: (error, _) { return [ - Center( - child: Text('Error: $error'), - ), + Center(child: Text('error_saving_image'.tr(args: [error.toString()]))), ]; }, loading: () { - return [ - const Center( - child: CircularProgressIndicator(), - ), - ]; + return [const Center(child: CircularProgressIndicator())]; }, ), ), @@ -399,13 +345,8 @@ class _QuickAccessButtonList extends ConsumerWidget { sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -430,41 +371,26 @@ class _QuickAccessButtonList extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( 'folders'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( 'locked_folder'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftLockedFolderRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( 'partners'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const DriftPartnerRoute()), ), @@ -499,22 +425,13 @@ class _PartnerList extends StatelessWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), - leading: PartnerUserAvatar( - partner: partner, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), + leading: PartnerUserAvatar(partner: partner), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontWeight: FontWeight.w500), ).t(context: context, args: {'user': partner.name}), - onTap: () => context.pushRoute( - DriftPartnerDetailRoute(partner: partner), - ), + onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)), ); }, ); diff --git a/mobile/lib/presentation/pages/drift_local_album.page.dart b/mobile/lib/presentation/pages/drift_local_album.page.dart index 0ad9abd2fa..6b133f7a6f 100644 --- a/mobile/lib/presentation/pages/drift_local_album.page.dart +++ b/mobile/lib/presentation/pages/drift_local_album.page.dart @@ -1,4 +1,5 @@ import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -16,14 +17,7 @@ class DriftLocalAlbumsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: CustomScrollView( - slivers: [ - LocalAlbumsSliverAppBar(), - _AlbumList(), - ], - ), - ); + return const Scaffold(body: CustomScrollView(slivers: [LocalAlbumsSliverAppBar(), _AlbumList()])); } } @@ -37,10 +31,7 @@ class _AlbumList extends ConsumerWidget { return albums.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -49,21 +40,16 @@ class _AlbumList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading albums: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), ), data: (albums) { if (albums.isEmpty) { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('no_albums_yet'.tr())), ), ); } @@ -77,33 +63,14 @@ class _AlbumList extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: SizedBox( - width: 80, - height: 80, - child: LocalAlbumThumbnail( - albumId: album.id, - ), - ), - title: Text( - album.name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), + leadingPadding: const EdgeInsets.only(right: 16), + leading: SizedBox(width: 80, height: 80, child: LocalAlbumThumbnail(albumId: album.id)), + title: Text(album.name, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': album.assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), - onTap: () => - context.pushRoute(LocalTimelineRoute(album: album)), + onTap: () => context.pushRoute(LocalTimelineRoute(album: album)), ), ); }, diff --git a/mobile/lib/presentation/pages/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/drift_locked_folder.page.dart index e134b418e9..b19e8468ca 100644 --- a/mobile/lib/presentation/pages/drift_locked_folder.page.dart +++ b/mobile/lib/presentation/pages/drift_locked_folder.page.dart @@ -14,12 +14,10 @@ class DriftLockedFolderPage extends ConsumerStatefulWidget { const DriftLockedFolderPage({super.key}); @override - ConsumerState createState() => - _DriftLockedFolderPageState(); + ConsumerState createState() => _DriftLockedFolderPageState(); } -class _DriftLockedFolderPageState extends ConsumerState - with WidgetsBindingObserver { +class _DriftLockedFolderPageState extends ConsumerState with WidgetsBindingObserver { bool _showOverlay = false; @override @@ -47,29 +45,23 @@ class _DriftLockedFolderPageState extends ConsumerState Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access locked folder'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access locked folder'); + } - final timelineService = - ref.watch(timelineFactoryProvider).lockedFolder(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: _showOverlay ? const SizedBox() : PopScope( - onPopInvokedWithResult: (didPop, _) => - didPop ? ref.read(authProvider.notifier).lockPinCode() : null, + onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null, child: Timeline( - appBar: MesmerizingSliverAppBar( - title: 'locked_folder'.t(context: context), - ), + appBar: MesmerizingSliverAppBar(title: 'locked_folder'.t(context: context)), bottomSheet: const LockedFolderBottomSheet(), ), ), diff --git a/mobile/lib/presentation/pages/drift_map.page.dart b/mobile/lib/presentation/pages/drift_map.page.dart new file mode 100644 index 0000000000..de8dde7714 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_map.page.dart @@ -0,0 +1,39 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.widget.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +@RoutePage() +class DriftMapPage extends StatelessWidget { + final LatLng? initialLocation; + + const DriftMapPage({super.key, this.initialLocation}); + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + body: Stack( + children: [ + DriftMap(initialLocation: initialLocation), + Positioned( + left: 16, + top: 60, + child: IconButton.filled( + color: Colors.white, + onPressed: () => context.pop(), + icon: const Icon(Icons.arrow_back_ios_new_rounded), + style: IconButton.styleFrom( + padding: const EdgeInsets.all(8), + backgroundColor: Colors.indigo, + shadowColor: Colors.black26, + elevation: 4, + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_memory.page.dart b/mobile/lib/presentation/pages/drift_memory.page.dart index 7da2d1a4c7..55e5d24ecb 100644 --- a/mobile/lib/presentation/pages/drift_memory.page.dart +++ b/mobile/lib/presentation/pages/drift_memory.page.dart @@ -22,26 +22,19 @@ class DriftMemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const DriftMemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); /// The list of all of the asset page controllers - final memoryAssetPageControllers = - List.generate(memories.length, (i) => usePageController()); + final memoryAssetPageControllers = List.generate(memories.length, (i) => usePageController()); /// The main vertically scrolling page controller with each list of memories final memoryPageController = usePageController(initialPage: memoryIndex); @@ -56,36 +49,27 @@ class DriftMemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { final previousIndex = currentMemoryIndex.value - 1; - final previousMemoryController = - memoryAssetPageControllers[previousIndex]; + final previousMemoryController = memoryAssetPageControllers[previousIndex]; // Ensure the controller is attached if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } else { // Wait for the next frame until it is attached SchedulerBinding.instance.addPostFrameCallback((_) { if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } }); } @@ -96,13 +80,9 @@ class DriftMemoryPage extends HookConsumerWidget { toNextAsset(int currentAssetIndex) { if (currentAssetIndex + 1 < currentMemory.value.assets.length) { // Go to the next asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -112,13 +92,9 @@ class DriftMemoryPage extends HookConsumerWidget { toPreviousAsset(int currentAssetIndex) { if (currentAssetIndex > 0) { // Go to the previous asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -126,8 +102,7 @@ class DriftMemoryPage extends HookConsumerWidget { } updateProgressText() { - assetProgress.value = - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; + assetProgress.value = "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; } /// Downloads and caches the image for the asset at this [currentMemory]'s index @@ -167,20 +142,12 @@ class DriftMemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); - await precacheImage( - getFullImageProvider( - asset, - size: Size(size.width, size.height), - ), - context, - size: size, - ); + await precacheImage(getFullImageProvider(asset, size: Size(size.width, size.height)), context, size: size); } // Precache the next page right away if we are on the first page if (currentAssetPage.value == 0) { - Future.delayed(const Duration(milliseconds: 200)) - .then((_) => precacheAsset(1)); + Future.delayed(const Duration(milliseconds: 200)).then((_) => precacheAsset(1)); } Future onAssetChanged(int otherIndex) async { @@ -212,12 +179,10 @@ class DriftMemoryPage extends HookConsumerWidget { // maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1 // or sum of vertical pixels of all memories for depth = 0 if (notification is ScrollUpdateNotification) { - final isEpiloguePage = - (memoryPageController.page?.floor() ?? 0) >= memories.length; + final isEpiloguePage = (memoryPageController.page?.floor() ?? 0) >= memories.length; final offset = notification.metrics.pixels; - if (isEpiloguePage && - (offset > notification.metrics.maxScrollExtent + 150)) { + if (isEpiloguePage && (offset > notification.metrics.maxScrollExtent + 150)) { context.maybePop(); return true; } @@ -229,9 +194,7 @@ class DriftMemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -259,23 +222,13 @@ class DriftMemoryPage extends HookConsumerWidget { } final yearsAgo = DateTime.now().year - memories[mIndex].data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); // Build horizontal page final assetController = memoryAssetPageControllers[mIndex]; return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -295,9 +248,7 @@ class DriftMemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -308,11 +259,7 @@ class DriftMemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: DriftMemoryCard( - asset: asset, - title: title, - showTitle: index == 0, - ), + child: DriftMemoryCard(asset: asset, title: title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -353,36 +300,24 @@ class DriftMemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), - if (currentAsset.value != null && - currentAsset.value!.isVideo) + if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), ), - DriftMemoryBottomInfo( - memory: memories[mIndex], - title: title, - ), + DriftMemoryBottomInfo(memory: memories[mIndex], title: title), ], ); }, diff --git a/mobile/lib/presentation/pages/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/drift_partner_detail.page.dart index 6c77a480ea..f8a19b6b70 100644 --- a/mobile/lib/presentation/pages/drift_partner_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_partner_detail.page.dart @@ -10,34 +10,26 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; @RoutePage() class DriftPartnerDetailPage extends StatelessWidget { final PartnerUserDto partner; - const DriftPartnerDetailPage({ - super.key, - required this.partner, - }); + const DriftPartnerDetailPage({super.key, required this.partner}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(partner.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: partner.name, - icon: Icons.person_outline, - ), + appBar: MesmerizingSliverAppBar(title: partner.name, icon: Icons.person_outline), topSliverWidget: _InfoBox(partner: partner), topSliverWidgetHeight: 110, bottomSheet: const PartnerDetailBottomSheet(), @@ -49,9 +41,7 @@ class DriftPartnerDetailPage extends StatelessWidget { class _InfoBox extends ConsumerStatefulWidget { final PartnerUserDto partner; - const _InfoBox({ - required this.partner, - }); + const _InfoBox({required this.partner}); @override ConsumerState<_InfoBox> createState() => _InfoBoxState(); @@ -73,16 +63,13 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { } try { - await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline( - widget.partner.id, - user.id, - ); + await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(widget.partner.id, user.id); setState(() { _inTimeline = !_inTimeline; }); } catch (error, stack) { - debugPrint("Failed to toggle in timeline: $error $stack"); + dPrint(() => "Failed to toggle in timeline: $error $stack"); ImmichToast.show( context: context, toastType: ToastType.error, @@ -102,18 +89,10 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -123,18 +102,13 @@ class _InfoBoxState extends ConsumerState<_InfoBox> { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: _inTimeline, - onChanged: (_) => _toggleInTimeline(), - ), + trailing: Switch(value: _inTimeline, onChanged: (_) => _toggleInTimeline()), ), ), ), diff --git a/mobile/lib/presentation/pages/drift_people_collection.page.dart b/mobile/lib/presentation/pages/drift_people_collection.page.dart new file mode 100644 index 0000000000..ca4e20aad0 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_people_collection.page.dart @@ -0,0 +1,130 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftPeopleCollectionPage extends ConsumerStatefulWidget { + const DriftPeopleCollectionPage({super.key}); + + @override + ConsumerState createState() => _DriftPeopleCollectionPageState(); +} + +class _DriftPeopleCollectionPageState extends ConsumerState { + final FocusNode _formFocus = FocusNode(); + String? _search; + + @override + void dispose() { + _formFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final people = ref.watch(driftGetAllPeopleProvider); + final headers = ApiService.getRequestHeaders(); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final isPortrait = context.orientation == Orientation.portrait; + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: _search == null, + title: _search != null + ? SearchField( + focusNode: _formFocus, + onTapOutside: (_) => _formFocus.unfocus(), + onChanged: (value) => setState(() => _search = value), + filled: true, + hintText: 'filter_people'.tr(), + autofocus: true, + ) + : Text('people'.tr()), + actions: [ + IconButton( + icon: Icon(_search != null ? Icons.close : Icons.search), + onPressed: () { + setState(() => _search = _search == null ? '' : null); + }, + ), + ], + ), + body: SafeArea( + child: people.when( + data: (people) { + if (_search != null) { + people = people.where((person) { + return person.name.toLowerCase().contains(_search!.toLowerCase()); + }).toList(); + } + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isTablet ? 6 : 3, + childAspectRatio: 0.85, + mainAxisSpacing: isPortrait && isTablet ? 36 : 0, + ), + padding: const EdgeInsets.symmetric(vertical: 32), + itemCount: people.length, + itemBuilder: (context, index) { + final person = people[index]; + + return Column( + children: [ + GestureDetector( + onTap: () { + context.pushRoute(DriftPersonRoute(person: person)); + }, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: isTablet ? 100 / 2 : 96 / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () => showNameEditModal(context, person), + child: person.name.isEmpty + ? Text( + 'add_a_name'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.primary, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + person.name, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), + ), + ), + ), + ], + ); + }, + ); + }, + error: (error, stack) => const Text("error"), + loading: () => const Center(child: CircularProgressIndicator()), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_person.page.dart b/mobile/lib/presentation/pages/drift_person.page.dart new file mode 100644 index 0000000000..ebd4954a51 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_person.page.dart @@ -0,0 +1,98 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_option_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart'; + +@RoutePage() +class DriftPersonPage extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonPage({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonPageState(); +} + +class _DriftPersonPageState extends ConsumerState { + late DriftPerson _person; + + @override + initState() { + super.initState(); + _person = widget.person; + } + + Future handleEditName(BuildContext context) async { + final newName = await showNameEditModal(context, _person); + + if (newName != null && newName.isNotEmpty) { + setState(() { + _person = _person.copyWith(name: newName); + }); + } + } + + Future handleEditBirthday(BuildContext context) async { + final birthday = await showBirthdayEditModal(context, _person); + + if (birthday != null) { + setState(() { + _person = _person.copyWith(birthDate: birthday); + }); + } + } + + void showOptionSheet(BuildContext context) { + showModalBottomSheet( + context: context, + backgroundColor: context.colorScheme.surface, + isScrollControlled: false, + builder: (context) { + return PersonOptionSheet( + onEditName: () async { + await handleEditName(context); + context.pop(); + }, + onEditBirthday: () async { + await handleEditBirthday(context); + context.pop(); + }, + birthdayExists: _person.birthDate != null, + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to view person timeline'); + } + + final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: Timeline( + appBar: PersonSliverAppBar( + person: _person, + onNameTap: () => handleEditName(context), + onBirthdayTap: () => handleEditBirthday(context), + onShowOptions: () => showOptionSheet(context), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_place.page.dart b/mobile/lib/presentation/pages/drift_place.page.dart index 5e5b932f60..d042f52673 100644 --- a/mobile/lib/presentation/pages/drift_place.page.dart +++ b/mobile/lib/presentation/pages/drift_place.page.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; @@ -22,34 +23,37 @@ class DriftPlacePage extends StatelessWidget { final ValueNotifier search = ValueNotifier(null); return Scaffold( - body: CustomScrollView( - slivers: [ - _PlaceSliverAppBar(search: search), - _Map(search: search, currentLocation: currentLocation), - _PlaceList(search: search), - ], + body: ValueListenableBuilder( + valueListenable: search, + builder: (context, searchValue, child) { + return CustomScrollView( + slivers: [ + _PlaceSliverAppBar(search: search), + _Map(search: search, currentLocation: currentLocation), + _PlaceList(search: search), + ], + ); + }, ), ); } } -class _PlaceSliverAppBar extends StatelessWidget { +class _PlaceSliverAppBar extends HookWidget { const _PlaceSliverAppBar({required this.search}); final ValueNotifier search; @override Widget build(BuildContext context) { - final searchFocusNode = FocusNode(); + final searchFocusNode = useFocusNode(); return SliverAppBar( floating: true, pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: search.value == null, centerTitle: true, title: search.value != null @@ -89,24 +93,17 @@ class _Map extends StatelessWidget { child: SizedBox( height: 200, width: context.width, - // TODO: migrate to DriftMapRoute after merging #19898 child: MapThumbnail( - onTap: (_, __) => context - .pushRoute(MapRoute(initialLocation: currentLocation)), + onTap: (_, __) => context.pushRoute(DriftMapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), ) - : const SizedBox.shrink(); + : const SliverToBoxAdapter(child: SizedBox.shrink()); } } @@ -122,10 +119,7 @@ class _PlaceList extends ConsumerWidget { return places.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -134,9 +128,7 @@ class _PlaceList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading places: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -169,20 +161,13 @@ class _PlaceTile extends StatelessWidget { Widget build(BuildContext context) { return LargeLeadingTile( onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)), - title: Text( - place.$1, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: Thumbnail( - size: const Size(80, 80), - fit: BoxFit.cover, - remoteId: place.$2, + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: SizedBox( + width: 80, + height: 80, + child: Thumbnail.remote(remoteId: place.$2, fit: BoxFit.cover), ), ), ); diff --git a/mobile/lib/presentation/pages/drift_place_detail.page.dart b/mobile/lib/presentation/pages/drift_place_detail.page.dart index 9999d35297..0f50227945 100644 --- a/mobile/lib/presentation/pages/drift_place_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_place_detail.page.dart @@ -9,29 +9,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPlaceDetailPage extends StatelessWidget { final String place; - const DriftPlaceDetailPage({ - super.key, - required this.place, - }); + const DriftPlaceDetailPage({super.key, required this.place}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = - ref.watch(timelineFactoryProvider).place(place); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).place(place); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: place, - icon: Icons.location_on, - ), + appBar: MesmerizingSliverAppBar(title: place, icon: Icons.location_on), ), ); } diff --git a/mobile/lib/presentation/pages/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/drift_recently_taken.page.dart index df303e7b31..ceb121b124 100644 --- a/mobile/lib/presentation/pages/drift_recently_taken.page.dart +++ b/mobile/lib/presentation/pages/drift_recently_taken.page.dart @@ -15,25 +15,18 @@ class DriftRecentlyTakenPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access recently taken', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access recently taken'); + } - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())), ); } } diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 6b68bfcd4e..34d8919674 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -11,6 +10,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/remote_album_bot import 'package:immich_mobile/presentation/widgets/remote_album/drift_album_option.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -22,37 +22,35 @@ import 'package:immich_mobile/widgets/common/remote_album_sliver_app_bar.dart'; class RemoteAlbumPage extends ConsumerStatefulWidget { final RemoteAlbum album; - const RemoteAlbumPage({ - super.key, - required this.album, - }); + const RemoteAlbumPage({super.key, required this.album}); @override ConsumerState createState() => _RemoteAlbumPageState(); } class _RemoteAlbumPageState extends ConsumerState { + late RemoteAlbum _album; @override void initState() { super.initState(); + _album = widget.album; } Future addAssets(BuildContext context) async { - final albumAssets = - await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); + final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(_album.id); final newAssets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: albumAssets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), ); if (newAssets == null || newAssets.isEmpty) { return; } - final added = await ref.read(remoteAlbumProvider.notifier).addAssets( - widget.album.id, + final added = await ref + .read(remoteAlbumProvider.notifier) + .addAssets( + _album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; return remoteAsset.id; @@ -62,45 +60,31 @@ class _RemoteAlbumPageState extends ConsumerState { if (added > 0) { ImmichToast.show( context: context, - msg: "assets_added_to_album_count".t( - context: context, - args: { - 'count': added.toString(), - }, - ), + msg: "assets_added_to_album_count".t(context: context, args: {'count': added.toString()}), toastType: ToastType.success, ); } } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>( - DriftUserSelectionRoute(album: widget.album), - ); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: _album)); if (newUsers == null || newUsers.isEmpty) { return; } try { - await ref - .read(remoteAlbumProvider.notifier) - .addUsers(widget.album.id, newUsers); + await ref.read(remoteAlbumProvider.notifier).addUsers(_album.id, newUsers); if (newUsers.isNotEmpty) { ImmichToast.show( context: context, - msg: "users_added_to_album_count".t( - context: context, - args: { - 'count': newUsers.length, - }, - ), + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), toastType: ToastType.success, ); } - ref.invalidate(remoteAlbumSharedUsersProvider(widget.album.id)); + ref.invalidate(remoteAlbumSharedUsersProvider(_album.id)); } catch (e) { ImmichToast.show( context: context, @@ -111,9 +95,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder( - widget.album.id, - ); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(_album.id); ref.invalidate(timelineServiceProvider); } @@ -127,16 +109,9 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - 'album_delete_confirmation'.t( - context: context, - args: {'album': widget.album.name}, - ), - ), + Text('album_delete_confirmation'.t(context: context, args: {'album': _album.name})), const SizedBox(height: 8), - Text( - 'album_delete_confirmation_description'.t(context: context), - ), + Text('album_delete_confirmation_description'.t(context: context)), ], ), actions: [ @@ -146,9 +121,7 @@ class _RemoteAlbumPageState extends ConsumerState { ), TextButton( onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - ), + style: TextButton.styleFrom(foregroundColor: Theme.of(context).colorScheme.error), child: Text('delete_album'.t(context: context)), ), ], @@ -158,14 +131,11 @@ class _RemoteAlbumPageState extends ConsumerState { if (confirmed == true) { try { - await ref - .read(remoteAlbumProvider.notifier) - .deleteAlbum(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(_album.id); ImmichToast.show( context: context, - msg: 'library_deleted' - .t(context: context), // Using existing success message + msg: 'album_deleted'.t(context: context), toastType: ToastType.success, ); @@ -184,17 +154,24 @@ class _RemoteAlbumPageState extends ConsumerState { final result = await showDialog<_EditAlbumData?>( context: context, barrierDismissible: true, - builder: (context) => _EditAlbumDialog(album: widget.album), + builder: (context) => _EditAlbumDialog(album: _album), ); if (result != null && context.mounted) { + setState(() { + _album = _album.copyWith(name: result.name, description: result.description ?? ''); + }); HapticFeedback.mediumImpact(); } } + Future showActivity(BuildContext context) async { + context.pushRoute(const DriftActivitiesRoute()); + } + void showOptionSheet(BuildContext context) { final user = ref.watch(currentUserProvider); - final isOwner = user != null ? user.id == widget.album.ownerId : false; + final isOwner = user != null ? user.id == _album.ownerId : false; showModalBottomSheet( context: context, @@ -228,6 +205,14 @@ class _RemoteAlbumPageState extends ConsumerState { context.pop(); await showEditTitleAndDescription(context); }, + onCreateSharedLink: () async { + context.pop(); + context.pushRoute(SharedLinkEditRoute(albumId: _album.id)); + }, + onShowOptions: () { + context.pop(); + context.pushRoute(const DriftAlbumOptionsRoute()); + }, ); }, ); @@ -235,27 +220,34 @@ class _RemoteAlbumPageState extends ConsumerState { @override Widget build(BuildContext context) { - return ProviderScope( - overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .remoteAlbum(albumId: widget.album.id); + return PopScope( + onPopInvokedWithResult: (didPop, _) { + if (didPop) { + Future.microtask(() { + if (mounted) { + ref.read(currentRemoteAlbumProvider.notifier).dispose(); + ref.read(remoteAlbumProvider.notifier).refresh(); + } + }); + } + }, + child: ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); ref.onDispose(timelineService.dispose); return timelineService; - }, - ), - ], - child: Timeline( - appBar: RemoteAlbumSliverAppBar( - icon: Icons.photo_album_outlined, - onShowOptions: () => showOptionSheet(context), - onToggleAlbumOrder: () => toggleAlbumOrder(), - onEditTitle: () => showEditTitleAndDescription(context), - ), - bottomSheet: RemoteAlbumBottomSheet( - album: widget.album, + }), + ], + child: Timeline( + appBar: RemoteAlbumSliverAppBar( + icon: Icons.photo_album_outlined, + onShowOptions: () => showOptionSheet(context), + onToggleAlbumOrder: () => toggleAlbumOrder(), + onEditTitle: () => showEditTitleAndDescription(context), + onActivity: () => showActivity(context), + ), + bottomSheet: RemoteAlbumBottomSheet(album: _album), ), ), ); @@ -266,18 +258,13 @@ class _EditAlbumData { final String name; final String? description; - const _EditAlbumData({ - required this.name, - this.description, - }); + const _EditAlbumData({required this.name, this.description}); } class _EditAlbumDialog extends ConsumerStatefulWidget { final RemoteAlbum album; - const _EditAlbumDialog({ - required this.album, - }); + const _EditAlbumDialog({required this.album}); @override ConsumerState<_EditAlbumDialog> createState() => _EditAlbumDialogState(); @@ -311,19 +298,14 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { final newTitle = titleController.text.trim(); final newDescription = descriptionController.text.trim(); - await ref.read(remoteAlbumProvider.notifier).updateAlbum( - widget.album.id, - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ); + await ref + .read(remoteAlbumProvider.notifier) + .updateAlbum(widget.album.id, name: newTitle, description: newDescription); if (mounted) { - Navigator.of(context).pop( - _EditAlbumData( - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ), - ); + Navigator.of( + context, + ).pop(_EditAlbumData(name: newTitle, description: newDescription.isEmpty ? null : newDescription)); } } catch (e) { if (mounted) { @@ -340,11 +322,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { Widget build(BuildContext context) { return Dialog( insetPadding: const EdgeInsets.all(24), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16), @@ -357,16 +335,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { children: [ Row( children: [ - Icon( - Icons.edit_outlined, - color: context.colorScheme.primary, - size: 24, - ), + Icon(Icons.edit_outlined, color: context.colorScheme.primary, size: 24), const SizedBox(width: 12), - Text( - 'edit_album'.t(context: context), - style: context.textTheme.titleMedium, - ), + Text('edit_album'.t(context: context), style: context.textTheme.titleMedium), ], ), const SizedBox(height: 24), @@ -374,9 +345,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Album Name Text( 'album_name'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -384,9 +353,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 1, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), @@ -403,9 +370,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Description Text( 'description'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -413,11 +378,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 4, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(12), - ), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 9cd2fac760..8713166027 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -2,8 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() @@ -14,19 +16,16 @@ class DriftTrashPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access trash'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access trash'); + } - final timelineService = - ref.watch(timelineFactoryProvider).trash(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: SliverAppBar( @@ -37,6 +36,20 @@ class DriftTrashPage extends StatelessWidget { centerTitle: true, elevation: 0, ), + topSliverWidgetHeight: 24, + topSliverWidget: Consumer( + builder: (context, ref, child) { + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); + + return SliverPadding( + padding: const EdgeInsets.all(16.0), + sliver: SliverToBoxAdapter( + child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), + ), + ); + }, + ), + bottomSheet: const TrashBottomBar(), ), ); } diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index 6f5c4c3e2b..b73913fd02 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -3,9 +3,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -14,8 +13,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; // TODO: Refactor this provider when we have user provider/service/repository pattern in place -final driftUsersProvider = - FutureProvider.autoDispose>((ref) async { +final driftUsersProvider = FutureProvider.autoDispose>((ref) async { final drift = ref.watch(driftProvider); final currentUser = ref.watch(currentUserProvider); @@ -27,16 +25,13 @@ final driftUsersProvider = id: entity.id, name: entity.name, email: entity.email, - isAdmin: entity.isAdmin, - profileImagePath: entity.profileImagePath, - updatedAt: entity.updatedAt, - quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, - quotaUsageInBytes: entity.quotaUsageInBytes, isPartnerSharedBy: false, isPartnerSharedWith: false, - avatarColor: AvatarColor.primary, + avatarColor: entity.avatarColor, memoryEnabled: true, inTimeline: true, + profileChangedAt: entity.profileChangedAt, + hasProfileImage: entity.hasProfileImage, ), ) .toList(); @@ -50,15 +45,11 @@ final driftUsersProvider = class DriftUserSelectionPage extends HookConsumerWidget { final RemoteAlbum album; - const DriftUserSelectionPage({ - super.key, - required this.album, - }); + const DriftUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final AsyncValue> suggestedShareUsers = - ref.watch(driftUsersProvider); + final AsyncValue> suggestedShareUsers = ref.watch(driftUsersProvider); final sharedUsersList = useState>({}); addNewUsersHandler() { @@ -67,17 +58,9 @@ class DriftUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -90,31 +73,19 @@ class DriftUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -124,31 +95,15 @@ class DriftUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -161,9 +116,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -174,28 +127,21 @@ class DriftUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: - sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { // Get shared users for this album from the database - final sharedUsers = - ref.watch(remoteAlbumSharedUsersProvider(album.id)); + final sharedUsers = ref.watch(remoteAlbumSharedUsersProvider(album.id)); return sharedUsers.when( data: (albumSharedUsers) { // Filter out users that are already shared with this album and the owner final filteredUsers = users.where((user) { - return !albumSharedUsers - .any((sharedUser) => sharedUser.id == user.id) && - user.id != album.ownerId; + return !albumSharedUsers.any((sharedUser) => sharedUser.id == user.id) && user.id != album.ownerId; }).toList(); return buildUserList(filteredUsers); @@ -203,8 +149,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { // If we can't load shared users, just filter out the owner - final filteredUsers = - users.where((user) => user.id != album.ownerId).toList(); + final filteredUsers = users.where((user) => user.id != album.ownerId).toList(); return buildUserList(filteredUsers); }, ); diff --git a/mobile/lib/presentation/pages/drift_video.page.dart b/mobile/lib/presentation/pages/drift_video.page.dart index 8c0e8e6911..eef05acdce 100644 --- a/mobile/lib/presentation/pages/drift_video.page.dart +++ b/mobile/lib/presentation/pages/drift_video.page.dart @@ -15,23 +15,18 @@ class DriftVideoPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to video'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to video'); + } - final timelineService = - ref.watch(timelineFactoryProvider).video(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).video(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar(title: 'videos'.t()), - ), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())), ); } } diff --git a/mobile/lib/presentation/pages/editing/drift_crop.page.dart b/mobile/lib/presentation/pages/editing/drift_crop.page.dart new file mode 100644 index 0000000000..5b14292aa2 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_crop.page.dart @@ -0,0 +1,174 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:crop_image/crop_image.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart'; + +/// A widget for cropping an image. +/// This widget uses [HookWidget] to manage its lifecycle and state. It allows +/// users to crop an image and then navigate to the [EditImagePage] with the +/// cropped image. + +@RoutePage() +class DriftCropImagePage extends HookWidget { + final Image image; + final BaseAsset asset; + const DriftCropImagePage({super.key, required this.image, required this.asset}); + + @override + Widget build(BuildContext context) { + final cropController = useCropController(); + final aspectRatio = useState(null); + + return Scaffold( + appBar: AppBar( + backgroundColor: context.scaffoldBackgroundColor, + title: Text("crop".tr()), + leading: CloseButton(color: context.primaryColor), + actions: [ + IconButton( + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), + onPressed: () async { + final croppedImage = await cropController.croppedImage(); + context.pushRoute(DriftEditImageRoute(asset: asset, image: croppedImage, isEdited: true)); + }, + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: SafeArea( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20), + width: constraints.maxWidth * 0.9, + height: constraints.maxHeight * 0.6, + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), + ), + Expanded( + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), + onPressed: () { + cropController.rotateLeft(); + }, + ), + IconButton( + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), + onPressed: () { + cropController.rotateRight(); + }, + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: null, + label: 'Free', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 1.0, + label: '1:1', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 16.0 / 9.0, + label: '16:9', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 3.0 / 2.0, + label: '3:2', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 7.0 / 5.0, + label: '7:5', + ), + ], + ), + ], + ), + ), + ), + ), + ], + ); + }, + ), + ), + ); + } +} + +class _AspectRatioButton extends StatelessWidget { + final CropController cropController; + final ValueNotifier aspectRatio; + final double? ratio; + final String label; + + const _AspectRatioButton({ + required this.cropController, + required this.aspectRatio, + required this.ratio, + required this.label, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), + onPressed: () { + cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); + aspectRatio.value = ratio; + cropController.aspectRatio = ratio; + }, + ), + Text(label, style: context.textTheme.displayMedium), + ], + ); + } +} diff --git a/mobile/lib/presentation/pages/editing/drift_edit.page.dart b/mobile/lib/presentation/pages/editing/drift_edit.page.dart new file mode 100644 index 0000000000..e24a1967f2 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_edit.page.dart @@ -0,0 +1,170 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/repositories/file_media.repository.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/upload.service.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +/// A stateless widget that provides functionality for editing an image. +/// +/// This widget allows users to edit an image provided either as an [Asset] or +/// directly as an [Image]. It ensures that exactly one of these is provided. +/// +/// It also includes a conversion method to convert an [Image] to a [Uint8List] to save the image on the user's phone +/// They automatically navigate to the [HomePage] with the edited image saved and they eventually get backed up to the server. +@immutable +@RoutePage() +class DriftEditImagePage extends ConsumerWidget { + final BaseAsset asset; + final Image image; + final bool isEdited; + + const DriftEditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); + Future _imageToUint8List(Image image) async { + final Completer completer = Completer(); + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), + ); + return completer.future; + } + + void _exitEditing(BuildContext context) { + // this assumes that the only way to get to this page is from the AssetViewerRoute + context.navigator.popUntil((route) => route.data?.name == AssetViewerRoute.name); + } + + Future _saveEditedImage(BuildContext context, BaseAsset asset, Image image, WidgetRef ref) async { + try { + final Uint8List imageData = await _imageToUint8List(image); + LocalAsset? localAsset; + + try { + localAsset = await ref + .read(fileMediaRepositoryProvider) + .saveLocalAsset(imageData, title: "${p.withoutExtension(asset.name)}_edited.jpg"); + } on PlatformException catch (e) { + // OS might not return the saved image back, so we handle that gracefully + // This can happen if app does not have full library access + Logger("SaveEditedImage").warning("Failed to retrieve the saved image back from OS", e); + } + + ref.read(backgroundSyncProvider).syncLocal(full: true); + _exitEditing(context); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!'); + + if (localAsset == null) { + return; + } + + await ref.read(uploadServiceProvider).manualBackup([localAsset]); + } catch (e) { + ImmichToast.show( + durationInSecond: 6, + context: context, + msg: "error_saving_image".tr(namedArgs: {'error': e.toString()}), + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar( + title: Text("edit".tr()), + backgroundColor: context.scaffoldBackgroundColor, + leading: IconButton( + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), + onPressed: () => _exitEditing(context), + ), + actions: [ + TextButton( + onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(7)), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.2), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), + ), + ), + ), + ), + bottomNavigationBar: Container( + height: 70, + margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(30)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), + onPressed: () { + context.pushRoute(DriftCropImageRoute(asset: asset, image: image)); + }, + ), + Text("crop".tr(), style: context.textTheme.displayMedium), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), + onPressed: () { + context.pushRoute(DriftFilterImageRoute(asset: asset, image: image)); + }, + ), + Text("filter".tr(), style: context.textTheme.displayMedium), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/editing/drift_filter.page.dart b/mobile/lib/presentation/pages/editing/drift_filter.page.dart new file mode 100644 index 0000000000..75c3f81de2 --- /dev/null +++ b/mobile/lib/presentation/pages/editing/drift_filter.page.dart @@ -0,0 +1,159 @@ +import 'dart:async'; +import 'dart:ui' as ui; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:immich_mobile/constants/filters.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/routing/router.dart'; + +/// A widget for filtering an image. +/// This widget uses [HookWidget] to manage its lifecycle and state. It allows +/// users to add filters to an image and then navigate to the [EditImagePage] with the +/// final composition.' +@RoutePage() +class DriftFilterImagePage extends HookWidget { + final Image image; + final BaseAsset asset; + + const DriftFilterImagePage({super.key, required this.image, required this.asset}); + + @override + Widget build(BuildContext context) { + final colorFilter = useState(filters[0]); + final selectedFilterIndex = useState(0); + + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { + final completer = Completer(); + final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + + final paint = Paint()..colorFilter = filter; + canvas.drawImage(inputImage, Offset.zero, paint); + + recorder.endRecording().toImage(size.width.round(), size.height.round()).then((image) { + completer.complete(image); + }); + + return completer.future; + } + + void applyFilter(ColorFilter filter, int index) { + colorFilter.value = filter; + selectedFilterIndex.value = index; + } + + Future applyFilterAndConvert(ColorFilter filter) async { + final completer = Completer(); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); + final uiImage = await completer.future; + + final filteredUiImage = await createFilteredImage(uiImage, filter); + final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); + final pngBytes = byteData!.buffer.asUint8List(); + + return Image.memory(pngBytes, fit: BoxFit.contain); + } + + return Scaffold( + appBar: AppBar( + backgroundColor: context.scaffoldBackgroundColor, + title: Text("filter".tr()), + leading: CloseButton(color: context.primaryColor), + actions: [ + IconButton( + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), + onPressed: () async { + final filteredImage = await applyFilterAndConvert(colorFilter.value); + context.pushRoute(DriftEditImageRoute(asset: asset, image: filteredImage, isEdited: true)); + }, + ), + ], + ), + backgroundColor: context.scaffoldBackgroundColor, + body: Column( + children: [ + SizedBox( + height: context.height * 0.7, + child: Center( + child: ColorFiltered(colorFilter: colorFilter.value, child: image), + ), + ), + SizedBox( + height: 120, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: filters.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: _FilterButton( + image: image, + label: filterNames[index], + filter: filters[index], + isSelected: selectedFilterIndex.value == index, + onTap: () => applyFilter(filters[index], index), + ), + ); + }, + ), + ), + ], + ), + ); + } +} + +class _FilterButton extends StatelessWidget { + final Image image; + final String label; + final ColorFilter filter; + final bool isSelected; + final VoidCallback onTap; + + const _FilterButton({ + required this.image, + required this.label, + required this.filter, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + GestureDetector( + onTap: onTap, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(10)), + child: ColorFiltered( + colorFilter: filter, + child: FittedBox(fit: BoxFit.cover, child: image), + ), + ), + ), + ), + const SizedBox(height: 10), + Text(label, style: context.themeData.textTheme.bodyMedium), + ], + ); + } +} diff --git a/mobile/lib/presentation/pages/local_timeline.page.dart b/mobile/lib/presentation/pages/local_timeline.page.dart index fd4e44616b..67bc17cb37 100644 --- a/mobile/lib/presentation/pages/local_timeline.page.dart +++ b/mobile/lib/presentation/pages/local_timeline.page.dart @@ -17,15 +17,11 @@ class LocalTimelinePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .localAlbum(albumId: album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar(title: album.name), diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 7101a42b01..92716144f3 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/presentation/pages/search/paginated_search.provider.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; @@ -36,8 +37,7 @@ class DriftSearchPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final textSearchType = useState(TextSearchType.context); - final searchHintText = - useState('sunrise_on_the_beach'.t(context: context)); + final searchHintText = useState('sunrise_on_the_beach'.t(context: context)); final textSearchController = useTextEditingController(); final filter = useState( SearchFilter( @@ -45,15 +45,9 @@ class DriftSearchPage extends HookConsumerWidget { location: preFilter?.location ?? SearchLocationFilter(), camera: preFilter?.camera ?? SearchCameraFilter(), date: preFilter?.date ?? SearchDateFilter(), - display: preFilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: preFilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: preFilter?.mediaType ?? AssetType.other, - language: - "${context.locale.languageCode}-${context.locale.countryCode}", + language: "${context.locale.languageCode}-${context.locale.countryCode}", ), ); @@ -70,10 +64,7 @@ class DriftSearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -91,14 +82,10 @@ class DriftSearchPage extends HookConsumerWidget { isSearching.value = true; ref.watch(paginatedSearchProvider.notifier).clear(); - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context))); } previousFilter.value = filter.value; @@ -107,14 +94,10 @@ class DriftSearchPage extends HookConsumerWidget { loadMoreSearchResult() async { isSearching.value = true; - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.t(context: context))); } isSearching.value = false; @@ -122,52 +105,35 @@ class DriftSearchPage extends HookConsumerWidget { searchPreFilter() { if (preFilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (preFilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - preFilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (preFilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(preFilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPreFilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPreFilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( - value - .map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)) - .join(', '), + value.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', '), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -183,10 +149,7 @@ class DriftSearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -195,11 +158,7 @@ class DriftSearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -215,16 +174,11 @@ class DriftSearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -241,15 +195,10 @@ class DriftSearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -260,10 +209,7 @@ class DriftSearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -273,9 +219,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -291,10 +235,7 @@ class DriftSearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -326,9 +267,7 @@ class DriftSearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -338,13 +277,7 @@ class DriftSearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -373,24 +306,20 @@ class DriftSearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.t(context: context) : assetType == AssetType.video - ? 'video'.t(context: context) - : 'all'.t(context: context), + ? 'video'.t(context: context) + : 'all'.t(context: context), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -402,10 +331,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.t(context: context), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -417,34 +343,19 @@ class DriftSearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText.add( - 'search_filter_display_option_not_in_album' - .t(context: context), - ); + filterText.add('search_filter_display_option_not_in_album'.t(context: context)); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.t(context: context)); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.t(context: context)); } @@ -457,19 +368,12 @@ class DriftSearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -482,10 +386,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'display_options'.t(context: context), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -493,27 +394,15 @@ class DriftSearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -521,10 +410,10 @@ class DriftSearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -537,21 +426,11 @@ class DriftSearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -561,7 +440,7 @@ class DriftSearchPage extends HookConsumerWidget { } }, icon: const Icon(Icons.more_vert_rounded), - tooltip: 'Show text search menu', + tooltip: 'show_text_search_menu'.tr(), ); }, menuChildren: [ @@ -572,9 +451,7 @@ class DriftSearchPage extends HookConsumerWidget { 'search_by_context'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.context - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -582,8 +459,7 @@ class DriftSearchPage extends HookConsumerWidget { ), onPressed: () { textSearchType.value = TextSearchType.context; - searchHintText.value = - 'sunrise_on_the_beach'.t(context: context); + searchHintText.value = 'sunrise_on_the_beach'.t(context: context); }, ), MenuItemButton( @@ -593,9 +469,7 @@ class DriftSearchPage extends HookConsumerWidget { 'search_filter_filename'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.filename - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -603,8 +477,7 @@ class DriftSearchPage extends HookConsumerWidget { ), onPressed: () { textSearchType.value = TextSearchType.filename; - searchHintText.value = - 'file_name_or_extension'.t(context: context); + searchHintText.value = 'file_name_or_extension'.t(context: context); }, ), MenuItemButton( @@ -614,20 +487,15 @@ class DriftSearchPage extends HookConsumerWidget { 'search_by_description'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: - textSearchType.value == TextSearchType.description - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, - selected: - textSearchType.value == TextSearchType.description, + selected: textSearchType.value == TextSearchType.description, ), onPressed: () { textSearchType.value = TextSearchType.description; - searchHintText.value = - 'search_by_description_example'.t(context: context); + searchHintText.value = 'search_by_description_example'.t(context: context); }, ), ], @@ -636,13 +504,8 @@ class DriftSearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -657,15 +520,8 @@ class DriftSearchPage extends HookConsumerWidget { hintText: searchHintText.value, key: const Key('search_text_field'), controller: textSearchController, - contentPadding: preFilter != null - ? const EdgeInsets.only(left: 24) - : const EdgeInsets.all(8), - prefixIcon: preFilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + contentPadding: preFilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), + prefixIcon: preFilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -674,7 +530,7 @@ class DriftSearchPage extends HookConsumerWidget { body: CustomScrollView( slivers: [ SliverPadding( - padding: const EdgeInsets.only(top: 12.0), + padding: const EdgeInsets.only(top: 12.0, bottom: 4.0), sliver: SliverToBoxAdapter( child: SizedBox( height: 50, @@ -718,8 +574,7 @@ class DriftSearchPage extends HookConsumerWidget { SearchFilterChip( icon: Icons.display_settings_outlined, onTap: showDisplayOptionPicker, - label: - 'search_filter_display_options'.t(context: context), + label: 'search_filter_display_options'.t(context: context), currentFilter: displayOptionCurrentFilterWidget.value, ), ], @@ -728,10 +583,7 @@ class DriftSearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const SliverFillRemaining( - hasScrollBody: false, - child: Center(child: CircularProgressIndicator()), - ) + const SliverFillRemaining(hasScrollBody: false, child: Center(child: CircularProgressIndicator())) else _SearchResultGrid(onScrollEnd: loadMoreSearchResult), ], @@ -747,25 +599,23 @@ class _SearchResultGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final searchResult = ref.watch(paginatedSearchProvider); + final assets = ref.watch(paginatedSearchProvider.select((s) => s.assets)); - if (searchResult.totalAssets == 0) { + if (assets.isEmpty) { return const _SearchEmptyContent(); } return NotificationListener( onNotification: (notification) { - final isBottomSheetNotification = notification.context - ?.findAncestorWidgetOfExactType() != - null; + final isBottomSheetNotification = + notification.context?.findAncestorWidgetOfExactType() != null; final metrics = notification.metrics; final isVerticalScroll = metrics.axis == Axis.vertical; - if (metrics.pixels >= metrics.maxScrollExtent && - isVerticalScroll && - !isBottomSheetNotification) { + if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { onScrollEnd(); + ref.read(paginatedSearchProvider.notifier).setScrollOffset(metrics.maxScrollExtent); } return true; @@ -773,20 +623,19 @@ class _SearchResultGrid extends ConsumerWidget { child: SliverFillRemaining( child: ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .fromAssets(searchResult.assets); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).fromAssets(assets); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - key: ValueKey(searchResult.totalAssets), - appBar: null, + key: ValueKey(assets.length), groupBy: GroupAssetsBy.none, + appBar: null, + bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), + snapToMonth: false, + initialScrollOffset: ref.read(paginatedSearchProvider.select((s) => s.scrollOffset)), ), ), ), @@ -806,24 +655,16 @@ class _SearchEmptyContent extends StatelessWidget { const SizedBox(height: 40), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/polaroid-dark.png' - : 'assets/polaroid-light.png', + context.isDarkTheme ? 'assets/polaroid-dark.png' : 'assets/polaroid-light.png', height: 125, ), ), const SizedBox(height: 16), Center( - child: Text( - 'search_page_search_photos_videos'.t(context: context), - style: context.textTheme.labelLarge, - ), + child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge), ), const SizedBox(height: 32), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: _QuickLinkList(), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()), ], ), ); @@ -837,13 +678,8 @@ class _QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -906,19 +742,9 @@ class _QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/presentation/pages/search/paginated_search.provider.dart b/mobile/lib/presentation/pages/search/paginated_search.provider.dart index 84635fd0b9..c0c822198d 100644 --- a/mobile/lib/presentation/pages/search/paginated_search.provider.dart +++ b/mobile/lib/presentation/pages/search/paginated_search.provider.dart @@ -4,16 +4,14 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/providers/infrastructure/search.provider.dart'; -final paginatedSearchProvider = - StateNotifierProvider( +final paginatedSearchProvider = StateNotifierProvider( (ref) => PaginatedSearchNotifier(ref.watch(searchServiceProvider)), ); class PaginatedSearchNotifier extends StateNotifier { final SearchService _searchService; - PaginatedSearchNotifier(this._searchService) - : super(const SearchResult(assets: [], nextPage: 1)); + PaginatedSearchNotifier(this._searchService) : super(const SearchResult(assets: [], nextPage: 1)); Future search(SearchFilter filter) async { if (state.nextPage == null) { @@ -29,12 +27,17 @@ class PaginatedSearchNotifier extends StateNotifier { state = SearchResult( assets: [...state.assets, ...result.assets], nextPage: result.nextPage, + scrollOffset: state.scrollOffset, ); return true; } + void setScrollOffset(double offset) { + state = state.copyWith(scrollOffset: offset); + } + clear() { - state = const SearchResult(assets: [], nextPage: 1); + state = const SearchResult(assets: [], nextPage: 1, scrollOffset: 0.0); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart new file mode 100644 index 0000000000..170f827fdb --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; + +class AdvancedInfoActionButton extends ConsumerWidget { + final ActionSource source; + + const AdvancedInfoActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + ref.read(actionProvider.notifier).troubleshoot(source, context); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 115.0, + iconData: Icons.help_outline_rounded, + label: "troubleshoot".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index 86537816e3..d30ba07d0c 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,17 +23,16 @@ class ArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).archive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'archive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'archive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -42,7 +43,7 @@ class ArchiveActionButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.archive_outlined, - label: "archive".t(context: context), + label: "to_archive".t(context: context), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart index 2ad285326c..5ec6c8bc54 100644 --- a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart @@ -25,12 +25,10 @@ class BaseActionButton extends StatelessWidget { @override Widget build(BuildContext context) { - final miniWidth = - minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0); + final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0); final iconTheme = IconTheme.of(context); final iconSize = iconTheme.size ?? 24.0; - final iconColor = - this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color; + final iconColor = this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color; final textColor = context.themeData.textTheme.labelLarge?.color; if (menuItem) { @@ -41,14 +39,10 @@ class BaseActionButton extends StatelessWidget { } return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), child: MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), textColor: textColor, onPressed: onPressed, onLongPress: onLongPressed, @@ -61,12 +55,10 @@ class BaseActionButton extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - ), + style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400), maxLines: 3, textAlign: TextAlign.center, + softWrap: true, ), ], ), diff --git a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart index 2900d55834..26b8ba6f47 100644 --- a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart @@ -17,14 +17,10 @@ class CastActionButton extends ConsumerWidget { return BaseActionButton( iconData: isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - iconColor: - isCasting ? context.primaryColor : null, // null = default color + iconColor: isCasting ? context.primaryColor : null, // null = default color label: "cast".t(context: context), onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, menuItem: menuItem, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart index d81f998a7b..723700af55 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart @@ -2,36 +2,66 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class DeletePermanentActionButton extends ConsumerWidget { +/// This delete action has the following behavior: +/// - Set the deletedAt information, put the asset in the trash in the server +/// which will be permanently deleted after the number of days configure by the admin +/// - Prompt to delete the asset locally +class DeleteActionButton extends ConsumerWidget { final ActionSource source; - - const DeletePermanentActionButton({super.key, required this.source}); + final bool showConfirmation; + const DeleteActionButton({super.key, required this.source, this.showConfirmation = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { return; } - final result = await ref.read(actionProvider.notifier).delete(source); + if (showConfirmation) { + final confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('delete'.t(context: context)), + content: Text('delete_action_confirmation_message'.t(context: context)), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('cancel'.t(context: context)), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text( + 'confirm'.t(context: context), + style: TextStyle(color: context.colorScheme.error), + ), + ), + ], + ), + ); + if (confirm != true) return; + } + + final result = await ref.read(actionProvider.notifier).trashRemoteAndDeleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'delete_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'delete_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -42,8 +72,8 @@ class DeletePermanentActionButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( maxWidth: 110.0, - iconData: Icons.delete_forever, - label: "delete_dialog_title".t(context: context), + iconData: Icons.delete_sweep_outlined, + label: "delete".t(context: context), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 90534ca68c..cccdee9b3a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -2,12 +2,16 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; +/// This delete action has the following behavior: +/// - Prompt to delete the asset locally class DeleteLocalActionButton extends ConsumerWidget { final ActionSource source; @@ -21,17 +25,20 @@ class DeleteLocalActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).deleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'delete_local_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + if (result.count == 0) { + return; + } + + final successMessage = 'delete_local_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart new file mode 100644 index 0000000000..4979df904c --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +/// This delete action has the following behavior: +/// - Delete permanently on the server +/// - Prompt to delete the asset locally +class DeletePermanentActionButton extends ConsumerWidget { + final ActionSource source; + + const DeletePermanentActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'delete_permanently_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 110.0, + iconData: Icons.delete_forever, + label: "delete_permanently".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart new file mode 100644 index 0000000000..cb0e7091c8 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +/// This delete action has the following behavior: +/// - Delete permanently on the server +/// - Prompt to delete the asset locally +/// +/// This action is used when the asset is selected in multi-selection mode in the trash page +class DeleteTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const DeleteTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'assets_permanently_deleted_count'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: Icon(Icons.delete_forever, color: Colors.red[400]), + label: Text( + "delete".t(context: context), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart index c6eda703a5..cb898f069a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart @@ -1,54 +1,45 @@ -import 'package:fluttertoast/fluttertoast.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; -import 'package:immich_mobile/widgets/common/immich_toast.dart'; class DownloadActionButton extends ConsumerWidget { final ActionSource source; + final bool menuItem; + const DownloadActionButton({super.key, required this.source, this.menuItem = false}); - const DownloadActionButton({super.key, required this.source}); - - void _onTap(BuildContext context, WidgetRef ref) async { + void _onTap(BuildContext context, WidgetRef ref, BackgroundSyncManager backgroundSyncManager) async { if (!context.mounted) { return; } - final result = await ref.read(actionProvider.notifier).downloadAll(source); - ref.read(multiSelectProvider.notifier).reset(); + try { + await ref.read(actionProvider.notifier).downloadAll(source); - if (!context.mounted) { - return; - } - - if (!result.success) { - ImmichToast.show( - context: context, - msg: 'scaffold_body_error_occurred'.t(context: context), - gravity: ToastGravity.BOTTOM, - toastType: ToastType.error, - ); - } else if (result.count > 0) { - ImmichToast.show( - context: context, - msg: 'download_action_prompt' - .t(context: context, args: {'count': result.count.toString()}), - gravity: ToastGravity.BOTTOM, - toastType: ToastType.success, - ); + Future.delayed(const Duration(seconds: 1), () async { + await backgroundSyncManager.syncLocal(); + await backgroundSyncManager.hashAssets(); + }); + } finally { + ref.read(multiSelectProvider.notifier).reset(); } } @override Widget build(BuildContext context, WidgetRef ref) { + final backgroundManager = ref.watch(backgroundSyncProvider); + return BaseActionButton( iconData: Icons.download, + maxWidth: 95, label: "download".t(context: context), - onPressed: () => _onTap(context, ref), + menuItem: menuItem, + onPressed: () => _onTap(context, ref, backgroundManager), ); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/download_status_floating_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_status_floating_button.widget.dart new file mode 100644 index 0000000000..efa7f5c6d0 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/download_status_floating_button.widget.dart @@ -0,0 +1,64 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +class DownloadStatusFloatingButton extends ConsumerWidget { + const DownloadStatusFloatingButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final shouldShow = ref.watch(downloadStateProvider.select((state) => state.showProgress)); + final itemCount = ref.watch(downloadStateProvider.select((state) => state.taskProgress.length)); + final isDownloading = ref + .watch(downloadStateProvider.select((state) => state.taskProgress)) + .values + .where((element) => element.progress != 1) + .isNotEmpty; + + return shouldShow + ? Badge.count( + count: itemCount, + textColor: context.colorScheme.onPrimary, + backgroundColor: context.colorScheme.primary, + child: FloatingActionButton( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), + ), + backgroundColor: context.isDarkTheme + ? context.colorScheme.surfaceContainer + : context.colorScheme.surfaceBright, + elevation: 2, + onPressed: () { + context.pushRoute(const DownloadInfoRoute()); + }, + child: Stack( + alignment: AlignmentDirectional.center, + children: [ + isDownloading + ? Icon(Icons.downloading_rounded, color: context.colorScheme.primary, size: 28) + : Icon( + Icons.download_done, + color: context.isDarkTheme ? Colors.green[200] : Colors.green[400], + size: 28, + ), + if (isDownloading) + const SizedBox( + height: 31, + width: 31, + child: CircularProgressIndicator( + strokeWidth: 2, + backgroundColor: Colors.transparent, + value: null, // Indeterminate progress + ), + ), + ], + ), + ), + ) + : const SizedBox.shrink(); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart index 3db3dde44d..6eeec0658b 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart @@ -1,10 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class EditDateTimeActionButton extends ConsumerWidget { - const EditDateTimeActionButton({super.key}); + final ActionSource source; + + const EditDateTimeActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).editDateTime(source, context); + if (result == null) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'edit_date_and_time_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { @@ -12,6 +46,7 @@ class EditDateTimeActionButton extends ConsumerWidget { maxWidth: 95.0, iconData: Icons.edit_calendar_outlined, label: "control_bottom_app_bar_edit_time".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart new file mode 100644 index 0000000000..4c7b6ffbdc --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/edit_image_action_button.widget.dart @@ -0,0 +1,32 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; + +class EditImageActionButton extends ConsumerWidget { + const EditImageActionButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentAsset = ref.watch(currentAssetNotifier); + + onPress() { + if (currentAsset == null) { + return; + } + + final image = Image(image: getFullImageProvider(currentAsset)); + context.pushRoute(DriftEditImageRoute(asset: currentAsset, image: image, isEdited: false)); + } + + return BaseActionButton( + iconData: Icons.tune, + label: "edit".t(context: context), + onPressed: onPress, + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart index c7279995ff..1a8a1a5c39 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart @@ -18,25 +18,19 @@ class EditLocationActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).editLocation(source, context); + final result = await ref.read(actionProvider.notifier).editLocation(source, context); if (result == null) { return; } ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'edit_location_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'edit_location_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart index 39d059c2d1..0aca5158ef 100644 --- a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart @@ -12,11 +12,7 @@ class FavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const FavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const FavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,17 +27,12 @@ class FavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'favorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'favorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart new file mode 100644 index 0000000000..33794eae11 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/like_activity_action_button.widget.dart @@ -0,0 +1,65 @@ +import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/models/activities/activity.model.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/activity.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +class LikeActivityActionButton extends ConsumerWidget { + const LikeActivityActionButton({super.key, this.menuItem = false}); + + final bool menuItem; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final album = ref.watch(currentRemoteAlbumProvider); + final asset = ref.watch(currentAssetNotifier) as RemoteAsset?; + final user = ref.watch(currentUserProvider); + + final activities = ref.watch(albumActivityProvider(album?.id ?? "", asset?.id)); + + onTap(Activity? liked) async { + if (user == null) { + return; + } + + if (liked != null) { + await ref.read(albumActivityProvider(album?.id ?? "", asset?.id).notifier).removeActivity(liked.id); + } else { + await ref.read(albumActivityProvider(album?.id ?? "", asset?.id).notifier).addLike(); + } + + ref.invalidate(albumActivityProvider(album?.id ?? "", asset?.id)); + } + + return activities.when( + data: (data) { + final liked = data.firstWhereOrNull( + (a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.id, + ); + + return BaseActionButton( + maxWidth: 60, + iconData: liked != null ? Icons.favorite : Icons.favorite_border, + label: "like".t(context: context), + onPressed: () => onTap(liked), + menuItem: menuItem, + ); + }, + + // default to empty heart during loading + loading: () => BaseActionButton( + iconData: Icons.favorite_border, + label: "like".t(context: context), + menuItem: menuItem, + ), + error: (error, stack) => Text('error_saving_image'.tr(args: [error.toString()])), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart index 47f797c166..696b9ff367 100644 --- a/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart @@ -14,9 +14,7 @@ class MotionPhotoActionButton extends ConsumerWidget { final isPlaying = ref.watch(isPlayingMotionVideoProvider); return BaseActionButton( - iconData: isPlaying - ? Icons.motion_photos_pause_outlined - : Icons.play_circle_outline_rounded, + iconData: isPlaying ? Icons.motion_photos_pause_outlined : Icons.play_circle_outline_rounded, label: "play_motion_photo".t(context: context), onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle, menuItem: menuItem, diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index 7546f07961..78b9e3cde6 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -18,10 +20,13 @@ class MoveToLockFolderActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).moveToLockFolder(source); + final result = await ref.read(actionProvider.notifier).moveToLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'move_to_lock_folder_action_prompt'.t( context: context, args: {'count': result.count.toString()}, @@ -30,9 +35,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -42,7 +45,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - maxWidth: 100.0, + maxWidth: 115.0, iconData: Icons.lock_outline_rounded, label: "move_to_locked_folder".t(context: context), onPressed: () => _onTap(context, ref), diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart index 8228e27cc1..2ce8b345a1 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -12,20 +12,14 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { final String albumId; final ActionSource source; - const RemoveFromAlbumActionButton({ - super.key, - required this.albumId, - required this.source, - }); + const RemoveFromAlbumActionButton({super.key, required this.albumId, required this.source}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { return; } - final result = await ref - .read(actionProvider.notifier) - .removeFromAlbum(source, albumId); + final result = await ref.read(actionProvider.notifier).removeFromAlbum(source, albumId); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'remove_from_album_action_prompt'.t( @@ -36,9 +30,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -51,6 +43,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { iconData: Icons.remove_circle_outline, label: "remove_from_album".t(context: context), onPressed: () => _onTap(context, ref), + maxWidth: 100, ); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart index 20fb62013f..028abf5596 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart @@ -18,8 +18,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).removeFromLockFolder(source); + final result = await ref.read(actionProvider.notifier).removeFromLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'remove_from_lock_folder_action_prompt'.t( @@ -30,9 +29,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart new file mode 100644 index 0000000000..e7928bd325 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class RestoreTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const RestoreTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).restoreTrash(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'assets_restored_count'.t(context: context, args: {'count': result.count.toString()}); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: const Icon(Icons.history_rounded), + label: Text('restore'.t(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart index 1b1553dabc..740ac528b0 100644 --- a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart @@ -1,15 +1,34 @@ import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; +class _SharePreparingDialog extends StatelessWidget { + const _SharePreparingDialog(); + + @override + Widget build(BuildContext context) { + return AlertDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CircularProgressIndicator(), + Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()), + ], + ), + ); + } +} + class ShareActionButton extends ConsumerWidget { final ActionSource source; @@ -20,36 +39,40 @@ class ShareActionButton extends ConsumerWidget { return; } - final result = await ref.read(actionProvider.notifier).shareAssets(source); - ref.read(multiSelectProvider.notifier).reset(); + showDialog( + context: context, + builder: (BuildContext buildContext) { + ref.read(actionProvider.notifier).shareAssets(source, context).then((ActionResult result) { + ref.read(multiSelectProvider.notifier).reset(); - if (!context.mounted) { - return; - } + if (!context.mounted) { + return; + } - if (!result.success) { - ImmichToast.show( - context: context, - msg: 'scaffold_body_error_occurred'.t(context: context), - gravity: ToastGravity.BOTTOM, - toastType: ToastType.error, - ); - } else if (result.count > 0) { - ImmichToast.show( - context: context, - msg: 'share_action_prompt' - .t(context: context, args: {'count': result.count.toString()}), - gravity: ToastGravity.BOTTOM, - toastType: ToastType.success, - ); - } + if (!result.success) { + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + + buildContext.pop(); + }); + + // show a loading spinner with a "Preparing" message + return const _SharePreparingDialog(); + }, + barrierDismissible: false, + useRootNavigator: false, + ); } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - iconData: - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, + iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, label: 'share'.t(context: context), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart index 13782c0098..22fccf5473 100644 --- a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart @@ -24,21 +24,15 @@ class StackActionButton extends ConsumerWidget { throw Exception('User must be logged in to access stack action'); } - final result = - await ref.read(actionProvider.notifier).stack(user.id, source); + final result = await ref.read(actionProvider.notifier).stack(user.id, source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'stack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'stack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index 449b688550..df8f544601 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -2,12 +2,17 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; +/// This delete action has the following behavior: +/// - Set the deletedAt information, put the asset in the trash in the server +/// which will be permanently deleted after the number of days configure by the admin class TrashActionButton extends ConsumerWidget { final ActionSource source; @@ -21,17 +26,16 @@ class TrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).trash(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'trash_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'trash_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart index e44500144c..b457a1b4ca 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart @@ -21,17 +21,12 @@ class UnArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unArchive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unarchive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unarchive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart index b465643796..7fdc5e81e8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart @@ -12,11 +12,7 @@ class UnFavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const UnFavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const UnFavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,17 +27,12 @@ class UnFavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unfavorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unfavorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart index c2757043a3..a07803ace5 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart @@ -21,17 +21,12 @@ class UnStackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unStack(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unstack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unstack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -41,7 +36,7 @@ class UnStackActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - iconData: Icons.filter_none_rounded, + iconData: Icons.layers_clear_outlined, label: "unstack".t(context: context), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 66467e44a3..f037d365d8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -1,16 +1,45 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class UploadActionButton extends ConsumerWidget { - const UploadActionButton({super.key}); + final ActionSource source; + + const UploadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).upload(source); + + final successMessage = 'upload_action_prompt'.t(context: context, args: {'count': result.count.toString()}); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + + ref.read(multiSelectProvider.notifier).reset(); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.backup_outlined, label: "upload".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart new file mode 100644 index 0000000000..bffe3d3941 --- /dev/null +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -0,0 +1,742 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; +import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/album_filter.utils.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +typedef AlbumSelectorCallback = void Function(RemoteAlbum album); + +class AlbumSelector extends ConsumerStatefulWidget { + final AlbumSelectorCallback onAlbumSelected; + final Function? onKeyboardExpanded; + + const AlbumSelector({super.key, required this.onAlbumSelected, this.onKeyboardExpanded}); + + @override + ConsumerState createState() => _AlbumSelectorState(); +} + +class _AlbumSelectorState extends ConsumerState { + bool isGrid = false; + final searchController = TextEditingController(); + final menuController = MenuController(); + final searchFocusNode = FocusNode(); + List sortedAlbums = []; + List shownAlbums = []; + + AlbumFilter filter = AlbumFilter(query: "", mode: QuickFilterMode.all); + AlbumSort sort = AlbumSort(mode: RemoteAlbumSortMode.lastModified, isReverse: true); + + @override + void initState() { + super.initState(); + + // Load albums when component mounts + WidgetsBinding.instance.addPostFrameCallback((_) { + ref.read(remoteAlbumProvider.notifier).refresh(); + }); + + searchController.addListener(() { + onSearch(searchController.text, filter.mode); + }); + + searchFocusNode.addListener(() { + if (searchFocusNode.hasFocus) { + widget.onKeyboardExpanded?.call(); + } + }); + } + + void onSearch(String searchTerm, QuickFilterMode filterMode) { + final userId = ref.watch(currentUserProvider)?.id; + filter = filter.copyWith(query: searchTerm, userId: userId, mode: filterMode); + + filterAlbums(); + } + + Future onRefresh() async { + await ref.read(remoteAlbumProvider.notifier).refresh(); + } + + void toggleViewMode() { + setState(() { + isGrid = !isGrid; + }); + } + + void changeFilter(QuickFilterMode mode) { + setState(() { + filter = filter.copyWith(mode: mode); + }); + + filterAlbums(); + } + + Future changeSort(AlbumSort sort) async { + setState(() { + this.sort = sort; + }); + + await sortAlbums(); + } + + void clearSearch() { + setState(() { + filter = filter.copyWith(mode: QuickFilterMode.all, query: null); + searchController.clear(); + }); + + filterAlbums(); + } + + Future sortAlbums() async { + final sorted = await ref + .read(remoteAlbumProvider.notifier) + .sortAlbums(ref.read(remoteAlbumProvider).albums, sort.mode, isReverse: sort.isReverse); + + setState(() { + sortedAlbums = sorted; + }); + + // we need to re-filter the albums after sorting + // so shownAlbums gets updated + filterAlbums(); + } + + Future filterAlbums() async { + if (filter.query == null) { + setState(() { + shownAlbums = sortedAlbums; + }); + + return; + } + + final filteredAlbums = ref + .read(remoteAlbumProvider.notifier) + .searchAlbums(sortedAlbums, filter.query!, filter.userId, filter.mode); + + setState(() { + shownAlbums = filteredAlbums; + }); + } + + @override + void dispose() { + searchController.dispose(); + searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final userId = ref.watch(currentUserProvider)?.id; + + // refilter and sort when albums change + ref.listen(remoteAlbumProvider.select((state) => state.albums), (_, _) async { + await sortAlbums(); + }); + + return PopScope( + onPopInvokedWithResult: (didPop, _) { + menuController.close(); + }, + child: MultiSliver( + children: [ + _SearchBar( + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearch: onSearch, + filterMode: filter.mode, + onClearSearch: clearSearch, + ), + _QuickFilterButtonRow( + filterMode: filter.mode, + onChangeFilter: changeFilter, + onSearch: onSearch, + searchController: searchController, + ), + _QuickSortAndViewMode( + isGrid: isGrid, + onToggleViewMode: toggleViewMode, + onSortChanged: changeSort, + controller: menuController, + ), + isGrid + ? _AlbumGrid(albums: shownAlbums, userId: userId, onAlbumSelected: widget.onAlbumSelected) + : _AlbumList(albums: shownAlbums, userId: userId, onAlbumSelected: widget.onAlbumSelected), + ], + ), + ); + } +} + +class _SortButton extends ConsumerStatefulWidget { + const _SortButton(this.onSortChanged, {this.controller}); + + final Future Function(AlbumSort) onSortChanged; + final MenuController? controller; + + @override + ConsumerState<_SortButton> createState() => _SortButtonState(); +} + +class _SortButtonState extends ConsumerState<_SortButton> { + RemoteAlbumSortMode albumSortOption = RemoteAlbumSortMode.lastModified; + bool albumSortIsReverse = true; + bool isSorting = false; + + Future onMenuTapped(RemoteAlbumSortMode sortMode) async { + final selected = albumSortOption == sortMode; + // Switch direction + if (selected) { + setState(() { + albumSortIsReverse = !albumSortIsReverse; + isSorting = true; + }); + } else { + setState(() { + albumSortOption = sortMode; + isSorting = true; + }); + } + + await widget.onSortChanged.call(AlbumSort(mode: albumSortOption, isReverse: albumSortIsReverse)); + + setState(() { + isSorting = false; + }); + } + + @override + Widget build(BuildContext context) { + return MenuAnchor( + controller: widget.controller, + style: MenuStyle( + elevation: const WidgetStatePropertyAll(1), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), + ), + consumeOutsideTap: true, + menuChildren: RemoteAlbumSortMode.values + .map( + (sortMode) => MenuItemButton( + leadingIcon: albumSortOption == sortMode + ? albumSortIsReverse + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : const Icon(Icons.abc, color: Colors.transparent), + onPressed: () => onMenuTapped(sortMode), + style: ButtonStyle( + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), + backgroundColor: WidgetStateProperty.all( + albumSortOption == sortMode ? context.colorScheme.primary : Colors.transparent, + ), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + ), + ), + child: Text( + sortMode.key.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface.withAlpha(185), + ), + ), + ), + ) + .toList(), + builder: (context, controller, child) { + return GestureDetector( + onTap: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 5), + child: albumSortIsReverse + ? const Icon(Icons.keyboard_arrow_down) + : const Icon(Icons.keyboard_arrow_up_rounded), + ), + Text( + albumSortOption.key.t(context: context), + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(225), + ), + ), + isSorting + ? SizedBox( + width: 22, + height: 22, + child: CircularProgressIndicator( + strokeWidth: 2, + color: context.colorScheme.onSurface.withAlpha(225), + ), + ) + : const SizedBox.shrink(), + ], + ), + ); + }, + ); + } +} + +class _SearchBar extends StatelessWidget { + const _SearchBar({ + required this.searchController, + required this.searchFocusNode, + required this.onSearch, + required this.filterMode, + required this.onClearSearch, + }); + + final TextEditingController searchController; + final FocusNode searchFocusNode; + final void Function(String, QuickFilterMode) onSearch; + final QuickFilterMode filterMode; + final VoidCallback onClearSearch; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + sliver: SliverToBoxAdapter( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withValues(alpha: 0.075), + context.colorScheme.primary.withValues(alpha: 0.09), + context.colorScheme.primary.withValues(alpha: 0.075), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + transform: const GradientRotation(0.5 * pi), + ), + ), + child: SearchField( + autofocus: false, + contentPadding: const EdgeInsets.all(16), + hintText: 'search_albums'.tr(), + prefixIcon: const Icon(Icons.search_rounded), + suffixIcon: searchController.text.isNotEmpty + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClearSearch) + : null, + controller: searchController, + onChanged: (_) => onSearch(searchController.text, filterMode), + focusNode: searchFocusNode, + onTapOutside: (_) => searchFocusNode.unfocus(), + ), + ), + ), + ); + } +} + +class _QuickFilterButtonRow extends StatelessWidget { + const _QuickFilterButtonRow({ + required this.filterMode, + required this.onChangeFilter, + required this.onSearch, + required this.searchController, + }); + + final QuickFilterMode filterMode; + final void Function(QuickFilterMode) onChangeFilter; + final void Function(String, QuickFilterMode) onSearch; + final TextEditingController searchController; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Wrap( + spacing: 4, + runSpacing: 4, + children: [ + _QuickFilterButton( + label: 'all'.tr(), + isSelected: filterMode == QuickFilterMode.all, + onTap: () { + onChangeFilter(QuickFilterMode.all); + onSearch(searchController.text, QuickFilterMode.all); + }, + ), + _QuickFilterButton( + label: 'shared_with_me'.tr(), + isSelected: filterMode == QuickFilterMode.sharedWithMe, + onTap: () { + onChangeFilter(QuickFilterMode.sharedWithMe); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); + }, + ), + _QuickFilterButton( + label: 'my_albums'.tr(), + isSelected: filterMode == QuickFilterMode.myAlbums, + onTap: () { + onChangeFilter(QuickFilterMode.myAlbums); + onSearch(searchController.text, QuickFilterMode.myAlbums); + }, + ), + ], + ), + ), + ); + } +} + +class _QuickFilterButton extends StatelessWidget { + const _QuickFilterButton({required this.isSelected, required this.onTap, required this.label}); + + final bool isSelected; + final VoidCallback onTap; + final String label; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: onTap, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), + ), + ), + ), + child: Text( + label, + style: TextStyle( + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, + fontSize: 14, + ), + ), + ); + } +} + +class _QuickSortAndViewMode extends StatelessWidget { + const _QuickSortAndViewMode({ + required this.isGrid, + required this.onToggleViewMode, + required this.onSortChanged, + this.controller, + }); + + final bool isGrid; + final VoidCallback onToggleViewMode; + final MenuController? controller; + final Future Function(AlbumSort) onSortChanged; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _SortButton(onSortChanged, controller: controller), + IconButton( + icon: Icon(isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), + onPressed: onToggleViewMode, + ), + ], + ), + ), + ); + } +} + +class _AlbumList extends ConsumerWidget { + const _AlbumList({required this.albums, required this.userId, required this.onAlbumSelected}); + + final List albums; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (albums.isEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('album_search_not_found'.tr())), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 64), + sliver: SliverList.builder( + itemBuilder: (_, index) { + final album = albums[index]; + final albumTile = LargeLeadingTile( + title: Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + subtitle: Text( + '${'items_count'.t(context: context, args: {'count': album.assetCount})} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', + overflow: TextOverflow.ellipsis, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + onTap: () => onAlbumSelected(album), + leadingPadding: const EdgeInsets.only(right: 16), + leading: album.thumbnailAssetId != null + ? ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: SizedBox(width: 80, height: 80, child: Thumbnail.remote(remoteId: album.thumbnailAssetId!)), + ) + : SizedBox( + width: 80, + height: 80, + child: Container( + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), + ), + child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), + ), + ), + ); + final isOwner = album.ownerId == userId; + + if (isOwner) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Dismissible( + key: ValueKey(album.id), + background: Container( + color: context.colorScheme.error, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 16), + child: Icon(Icons.delete, color: context.colorScheme.onError), + ), + direction: DismissDirection.endToStart, + confirmDismiss: (direction) { + return showDialog( + context: context, + builder: (context) => ConfirmDialog( + onOk: () => true, + title: "delete_album".t(context: context), + content: "album_delete_confirmation".t(context: context, args: {'album': album.name}), + ok: "delete".t(context: context), + ), + ); + }, + onDismissed: (direction) async { + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(album.id); + }, + child: albumTile, + ), + ); + } else { + return Padding(padding: const EdgeInsets.only(bottom: 8.0), child: albumTile); + } + }, + itemCount: albums.length, + ), + ); + } +} + +class _AlbumGrid extends StatelessWidget { + const _AlbumGrid({required this.albums, required this.userId, required this.onAlbumSelected}); + + final List albums; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context) { + if (albums.isEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding(padding: const EdgeInsets.all(20.0), child: Text('album_search_not_found'.tr())), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + sliver: SliverGrid( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 250, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + childAspectRatio: .7, + ), + delegate: SliverChildBuilderDelegate((context, index) { + final album = albums[index]; + return _GridAlbumCard(album: album, userId: userId, onAlbumSelected: onAlbumSelected); + }, childCount: albums.length), + ), + ); + } +} + +class _GridAlbumCard extends ConsumerWidget { + const _GridAlbumCard({required this.album, required this.userId, required this.onAlbumSelected}); + + final RemoteAlbum album; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return GestureDetector( + onTap: () => onAlbumSelected(album), + child: Card( + elevation: 0, + color: context.colorScheme.surfaceBright, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: ClipRRect( + borderRadius: const BorderRadius.vertical(top: Radius.circular(15)), + child: SizedBox( + width: double.infinity, + child: album.thumbnailAssetId != null + ? Thumbnail.remote(remoteId: album.thumbnailAssetId!) + : Container( + color: context.colorScheme.surfaceContainerHighest, + child: const Icon(Icons.photo_album_rounded, size: 40, color: Colors.grey), + ), + ), + ), + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + Text( + '${'items_count'.t(context: context, args: {'count': album.assetCount})} â€ĸ ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + +class AddToAlbumHeader extends ConsumerWidget { + const AddToAlbumHeader({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Future onCreateAlbum() async { + final newAlbum = await ref + .read(remoteAlbumProvider.notifier) + .createAlbum( + title: "Untitled Album", + assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(), + ); + + if (newAlbum == null) { + ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr()); + return; + } + + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); + ref.read(multiSelectProvider.notifier).reset(); + context.pushRoute(RemoteAlbumRoute(album: newAlbum)); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("add_to_album", style: context.textTheme.titleSmall).tr(), + TextButton.icon( + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), // remove internal padding + minimumSize: const Size(0, 0), // allow shrinking + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // remove extra height + ), + onPressed: onCreateAlbum, + icon: Icon(Icons.add, color: context.primaryColor), + label: Text( + "common_create_new_album", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), + ).tr(), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/album/drift_activity_text_field.dart b/mobile/lib/presentation/widgets/album/drift_activity_text_field.dart new file mode 100644 index 0000000000..a49ac9551a --- /dev/null +++ b/mobile/lib/presentation/widgets/album/drift_activity_text_field.dart @@ -0,0 +1,109 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; + +class DriftActivityTextField extends ConsumerStatefulWidget { + final bool isEnabled; + final String? likeId; + final Function(String) onSubmit; + final Function()? onKeyboardFocus; + + const DriftActivityTextField({ + required this.onSubmit, + this.isEnabled = true, + this.likeId, + this.onKeyboardFocus, + super.key, + }); + + @override + ConsumerState createState() => _DriftActivityTextFieldState(); +} + +class _DriftActivityTextFieldState extends ConsumerState { + late FocusNode inputFocusNode; + late TextEditingController inputController; + bool sendEnabled = false; + + @override + void initState() { + super.initState(); + inputController = TextEditingController(); + inputFocusNode = FocusNode(); + + inputFocusNode.requestFocus(); + + inputFocusNode.addListener(() { + if (inputFocusNode.hasFocus) { + widget.onKeyboardFocus?.call(); + } + }); + + inputController.addListener(() { + setState(() { + sendEnabled = inputController.text.trim().isNotEmpty; + }); + }); + } + + @override + void dispose() { + inputController.dispose(); + inputFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final user = ref.watch(currentUserProvider); + + // Pass text to callback and reset controller + void onEditingComplete() { + if (inputController.text.trim().isEmpty) { + return; + } + + widget.onSubmit(inputController.text); + inputController.clear(); + inputFocusNode.unfocus(); + } + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: TextField( + controller: inputController, + enabled: widget.isEnabled, + focusNode: inputFocusNode, + textInputAction: TextInputAction.send, + autofocus: false, + decoration: InputDecoration( + isDense: true, + contentPadding: const EdgeInsets.symmetric(vertical: 12), // Adjust as needed + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + prefixIcon: user != null + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: UserCircleAvatar(user: user, size: 30, radius: 15), + ) + : null, + suffixIcon: IconButton( + onPressed: sendEnabled ? onEditingComplete : null, + icon: const Icon(Icons.send), + iconSize: 24, + color: context.primaryColor, + disabledColor: context.colorScheme.secondaryContainer, + ), + hintText: !widget.isEnabled ? 'shared_album_activities_input_disable'.tr() : 'say_something'.tr(), + hintStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 14, color: Colors.grey[600]), + ), + onEditingComplete: onEditingComplete, + onTapOutside: (_) => inputFocusNode.unfocus(), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56c..dae6db568c 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -2,16 +2,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -class StackChildrenNotifier - extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { +class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset> { @override - Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { - return const []; + Future> build(BaseAsset asset) { + if (asset is! RemoteAsset || asset.stackId == null) { + return Future.value(const []); } return ref.watch(assetServiceProvider).getStack(asset); @@ -19,6 +14,4 @@ class StackChildrenNotifier } final stackChildrenNotifier = AsyncNotifierProvider.autoDispose - .family, BaseAsset?>( - StackChildrenNotifier.new, -); + .family, BaseAsset>(StackChildrenNotifier.new); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart index 8b3d0c6575..0978b3c9af 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart @@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; -import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; class AssetStackRow extends ConsumerWidget { @@ -11,31 +11,25 @@ class AssetStackRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); - - if (!showControls) { - opacity = 0; + final asset = ref.watch(assetViewerProvider.select((state) => state.currentAsset)); + if (asset == null) { + return const SizedBox.shrink(); } - final asset = ref.watch(assetViewerProvider.select((s) => s.currentAsset)); + final stackChildren = ref.watch(stackChildrenNotifier(asset)).valueOrNull; + if (stackChildren == null || stackChildren.isEmpty) { + return const SizedBox.shrink(); + } + + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final opacity = showControls ? ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)) : 0; return IgnorePointer( ignoring: opacity < 255, child: AnimatedOpacity( opacity: opacity / 255, duration: Durations.short2, - child: ref.watch(stackChildrenNotifier(asset)).when( - data: (state) => SizedBox.square( - dimension: 80, - child: _StackList(stack: state), - ), - error: (_, __) => const SizedBox.shrink(), - loading: () => const SizedBox.shrink(), - ), + child: _StackList(stack: stackChildren), ), ); } @@ -48,72 +42,77 @@ class _StackList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return ListView.builder( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), - itemCount: stack.length, - itemBuilder: (ctx, index) { - final asset = stack[index]; - return Padding( - padding: const EdgeInsets.only(right: 5), - child: GestureDetector( - onTap: () { - ref.read(assetViewerProvider.notifier).setStackIndex(index); - ref.read(currentAssetNotifier.notifier).setAsset(asset); - }, - child: Container( - height: 60, - width: 60, - decoration: index == - ref.watch(assetViewerProvider.select((s) => s.stackIndex)) - ? const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), - ) - : const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(6)), - border: null, - ), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(4)), - child: Stack( - fit: StackFit.expand, - children: [ - Image( - fit: BoxFit.cover, - image: getThumbnailImageProvider( - remoteId: asset.id, - size: const Size.square(60), - ), - ), - if (asset.isVideo) - const Icon( - Icons.play_circle_outline_rounded, - color: Colors.white, - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), - ], - ), - ), - ), + return Center( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 20.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 5.0, + children: List.generate(stack.length, (i) { + final asset = stack[i]; + return _StackItem(key: ValueKey(asset.heroTag), asset: asset, index: i); + }), ), - ); - }, + ), + ), + ); + } +} + +class _StackItem extends ConsumerStatefulWidget { + final RemoteAsset asset; + final int index; + + const _StackItem({super.key, required this.asset, required this.index}); + + @override + ConsumerState<_StackItem> createState() => _StackItemState(); +} + +class _StackItemState extends ConsumerState<_StackItem> { + void _onTap() { + ref.read(currentAssetNotifier.notifier).setAsset(widget.asset); + ref.read(assetViewerProvider.notifier).setStackIndex(widget.index); + } + + @override + Widget build(BuildContext context) { + const playIcon = Center( + child: Icon( + Icons.play_circle_outline_rounded, + color: Colors.white, + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], + ), + ); + const selectedDecoration = BoxDecoration( + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), + borderRadius: BorderRadius.all(Radius.circular(10)), + ); + const unselectedDecoration = BoxDecoration( + border: Border.fromBorderSide(BorderSide(color: Colors.grey, width: 0.5)), + borderRadius: BorderRadius.all(Radius.circular(10)), + ); + + Widget thumbnail = Thumbnail.fromAsset(asset: widget.asset, size: const Size(60, 40)); + if (widget.asset.isVideo) { + thumbnail = Stack(children: [thumbnail, playIcon]); + } + thumbnail = ClipRRect(borderRadius: const BorderRadius.all(Radius.circular(10)), child: thumbnail); + final isSelected = ref.watch(assetViewerProvider.select((s) => s.stackIndex == widget.index)); + return SizedBox( + width: 60, + height: 40, + child: GestureDetector( + onTap: _onTap, + child: DecoratedBox( + decoration: isSelected ? selectedDecoration : unselectedDecoration, + position: DecorationPosition.foreground, + child: thumbnail, + ), + ), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 9356c2f43e..7ac8cf34d5 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -3,13 +3,16 @@ import 'dart:async'; import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/extensions/scroll_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_status_floating_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; @@ -24,22 +27,19 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provi import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; -import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart'; -import 'package:platform/platform.dart'; @RoutePage() class AssetViewerPage extends StatelessWidget { final int initialIndex; final TimelineService timelineService; + final int? heroOffset; - const AssetViewerPage({ - super.key, - required this.initialIndex, - required this.timelineService, - }); + const AssetViewerPage({super.key, required this.initialIndex, required this.timelineService, this.heroOffset}); @override Widget build(BuildContext context) { @@ -47,29 +47,47 @@ class AssetViewerPage extends StatelessWidget { // since the Timeline and AssetViewer are on different routes / Widget subtrees. return ProviderScope( overrides: [timelineServiceProvider.overrideWithValue(timelineService)], - child: AssetViewer(initialIndex: initialIndex), + child: AssetViewer(initialIndex: initialIndex, heroOffset: heroOffset), ); } } class AssetViewer extends ConsumerStatefulWidget { final int initialIndex; - final Platform? platform; + final int? heroOffset; - const AssetViewer({ - super.key, - required this.initialIndex, - this.platform, - }); + const AssetViewer({super.key, required this.initialIndex, this.heroOffset}); @override ConsumerState createState() => _AssetViewerState(); + + static void setAsset(WidgetRef ref, BaseAsset asset) { + ref.read(assetViewerProvider.notifier).reset(); + _setAsset(ref, asset); + } + + void changeAsset(WidgetRef ref, BaseAsset asset) { + _setAsset(ref, asset); + } + + static void _setAsset(WidgetRef ref, BaseAsset asset) { + // Always holds the current asset from the timeline + ref.read(assetViewerProvider.notifier).setAsset(asset); + // The currentAssetNotifier actually holds the current asset that is displayed + // which could be stack children as well + ref.read(currentAssetNotifier.notifier).setAsset(asset); + if (asset.isVideo || asset.isMotionPhoto) { + ref.read(videoPlaybackValueProvider.notifier).reset(); + ref.read(videoPlayerControlsProvider.notifier).pause(); + } + } } const double _kBottomSheetMinimumExtent = 0.4; const double _kBottomSheetSnapExtent = 0.7; class _AssetViewerState extends ConsumerState { + static final _dummyListener = ImageStreamListener((image, _) => image.dispose()); late PageController pageController; late DraggableScrollableController bottomSheetController; PersistentBottomSheetController? sheetCloseController; @@ -77,7 +95,6 @@ class _AssetViewerState extends ConsumerState { PhotoViewControllerBase? viewController; StreamSubscription? reloadSubscription; - late Platform platform; late final int heroOffset; late PhotoViewControllerValue initialPhotoViewState; bool? hasDraggedDown; @@ -85,6 +102,7 @@ class _AssetViewerState extends ConsumerState { bool blockGestures = false; bool dragInProgress = false; bool shouldPopOnDrag = false; + bool assetReloadRequested = false; double? initialScale; double previousExtent = _kBottomSheetMinimumExtent; Offset dragDownPosition = Offset.zero; @@ -96,18 +114,25 @@ class _AssetViewerState extends ConsumerState { // Delayed operations that should be cancelled on disposal final List _delayedOperations = []; + ImageStream? _prevPreCacheStream; + ImageStream? _nextPreCacheStream; + + KeepAliveLink? _stackChildrenKeepAlive; + @override void initState() { super.initState(); + assert(ref.read(currentAssetNotifier) != null, "Current asset should not be null when opening the AssetViewer"); pageController = PageController(initialPage: widget.initialIndex); - platform = widget.platform ?? const LocalPlatform(); totalAssets = ref.read(timelineServiceProvider).totalAssets; bottomSheetController = DraggableScrollableController(); - WidgetsBinding.instance.addPostFrameCallback((_) { - _onAssetChanged(widget.initialIndex); - }); + WidgetsBinding.instance.addPostFrameCallback(_onAssetInit); reloadSubscription = EventStream.shared.listen(_onEvent); - heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + heroOffset = widget.heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + final asset = ref.read(currentAssetNotifier); + if (asset != null) { + _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive(); + } } @override @@ -116,15 +141,17 @@ class _AssetViewerState extends ConsumerState { bottomSheetController.dispose(); _cancelTimers(); reloadSubscription?.cancel(); + _prevPreCacheStream?.removeListener(_dummyListener); + _nextPreCacheStream?.removeListener(_dummyListener); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + _stackChildrenKeepAlive?.close(); super.dispose(); } - bool get showingBottomSheet => - ref.read(assetViewerProvider.select((s) => s.showingBottomSheet)); + bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet)); Color get backgroundColor { - final opacity = - ref.read(assetViewerProvider.select((s) => s.backgroundOpacity)); + final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity)); return Colors.black.withAlpha(opacity); } @@ -135,72 +162,60 @@ class _AssetViewerState extends ConsumerState { _delayedOperations.clear(); } - // This is used to calculate the scale of the asset when the bottom sheet is showing. - // It is a small increment to ensure that the asset is slightly zoomed in when the - // bottom sheet is showing, which emulates the zoom effect. - double get _getScaleForBottomSheet => - (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + - 0.01; - double _getVerticalOffsetForBottomSheet(double extent) => (context.height * extent) - (context.height * _kBottomSheetMinimumExtent); - Future _precacheImage(int index) async { - if (!mounted || index < 0 || index >= totalAssets) { - return; - } - - final asset = ref.read(timelineServiceProvider).getAsset(index); - final screenSize = Size(context.width, context.height); - - // Precache both thumbnail and full image for smooth transitions - unawaited( - Future.wait([ - precacheImage( - getThumbnailImageProvider(asset: asset, size: screenSize), - context, - onError: (_, __) {}, - ), - precacheImage( - getFullImageProvider(asset, size: screenSize), - context, - onError: (_, __) {}, - ), - ]), - ); + ImageStream _precacheImage(BaseAsset asset) { + final provider = getFullImageProvider(asset, size: context.sizeData); + return provider.resolve(ImageConfiguration.empty)..addListener(_dummyListener); } - void _onAssetChanged(int index) { - final asset = ref.read(timelineServiceProvider).getAsset(index); - // Always holds the current asset from the timeline - ref.read(assetViewerProvider.notifier).setAsset(asset); - // The currentAssetNotifier actually holds the current asset that is displayed - // which could be stack children as well - ref.read(currentAssetNotifier.notifier).setAsset(asset); - if (asset.isVideo || asset.isMotionPhoto) { - ref.read(videoPlaybackValueProvider.notifier).reset(); - ref.read(videoPlayerControlsProvider.notifier).pause(); - } - - unawaited(ref.read(timelineServiceProvider).preCacheAssets(index)); + void _precacheAssets(int index) { + final timelineService = ref.read(timelineServiceProvider); + unawaited(timelineService.preCacheAssets(index)); _cancelTimers(); // This will trigger the pre-caching of adjacent assets ensuring // that they are ready when the user navigates to them. - final timer = Timer(Durations.medium4, () { + final timer = Timer(Durations.medium4, () async { // Check if widget is still mounted before proceeding if (!mounted) return; - for (final offset in [-1, 1]) { - unawaited(_precacheImage(index + offset)); - } + final (prevAsset, nextAsset) = await ( + timelineService.getAssetAsync(index - 1), + timelineService.getAssetAsync(index + 1), + ).wait; + if (!mounted) return; + _prevPreCacheStream?.removeListener(_dummyListener); + _nextPreCacheStream?.removeListener(_dummyListener); + _prevPreCacheStream = prevAsset != null ? _precacheImage(prevAsset) : null; + _nextPreCacheStream = nextAsset != null ? _precacheImage(nextAsset) : null; }); _delayedOperations.add(timer); - - _handleCasting(asset); } - void _handleCasting(BaseAsset asset) { + void _onAssetInit(Duration _) { + _precacheAssets(widget.initialIndex); + _handleCasting(); + } + + void _onAssetChanged(int index) async { + final timelineService = ref.read(timelineServiceProvider); + final asset = await timelineService.getAssetAsync(index); + if (asset == null) { + return; + } + + widget.changeAsset(ref, asset); + _precacheAssets(index); + _handleCasting(); + _stackChildrenKeepAlive?.close(); + _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive(); + } + + void _handleCasting() { if (!ref.read(castProvider).isCasting) return; + final asset = ref.read(currentAssetNotifier); + if (asset == null) return; // hide any casting snackbars if they exist context.scaffoldMessenger.hideCurrentSnackBar(); @@ -221,9 +236,7 @@ class _AssetViewerState extends ConsumerState { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -233,23 +246,19 @@ class _AssetViewerState extends ConsumerState { void _onPageBuild(PhotoViewControllerBase controller) { viewController ??= controller; - if (showingBottomSheet) { - final verticalOffset = (context.height * bottomSheetController.size) - - (context.height * _kBottomSheetMinimumExtent); + if (showingBottomSheet && bottomSheetController.isAttached) { + final verticalOffset = + (context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent); controller.position = Offset(0, -verticalOffset); + // Apply the zoom effect when the bottom sheet is showing + initialScale = controller.scale; + controller.scale = (controller.scale ?? 1.0) + 0.01; } } void _onPageChanged(int index, PhotoViewControllerBase? controller) { _onAssetChanged(index); viewController = controller; - - // If the bottom sheet is showing, we need to adjust scale the asset to - // emulate the zoom effect - if (showingBottomSheet) { - initialScale = controller?.scale; - controller?.scale = _getScaleForBottomSheet; - } } void _onDragStart( @@ -263,7 +272,7 @@ class _AssetViewerState extends ConsumerState { initialPhotoViewState = controller.value; final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || - scaleStateController.scaleState == PhotoViewScaleState.covering; + scaleStateController.scaleState == PhotoViewScaleState.covering; if (!showingBottomSheet && isZoomed) { blockGestures = true; } @@ -329,7 +338,7 @@ class _AssetViewerState extends ConsumerState { bottomSheetController.jumpTo((centre + distanceToOrigin) / ctx.height); } - if (distanceToOrigin > openThreshold && !showingBottomSheet) { + if (distanceToOrigin > openThreshold && !showingBottomSheet && !ref.read(readonlyModeProvider)) { _openBottomSheet(ctx); } } @@ -348,13 +357,9 @@ class _AssetViewerState extends ConsumerState { updatedScale = initialPhotoViewState.scale! * (1.0 - scaleReduction); } - final backgroundOpacity = - (255 * (1.0 - (scaleReduction / dragRatio))).round(); + final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round(); - viewController?.updateMultiple( - position: initialPhotoViewState.position + delta, - scale: updatedScale, - ); + viewController?.updateMultiple(position: initialPhotoViewState.position + delta, scale: updatedScale); ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity); } @@ -404,7 +409,12 @@ class _AssetViewerState extends ConsumerState { void _onEvent(Event event) { if (event is TimelineReloadEvent) { - _onTimelineReload(event); + _onTimelineReloadEvent(); + return; + } + + if (event is ViewerReloadAssetEvent) { + assetReloadRequested = true; return; } @@ -417,53 +427,56 @@ class _AssetViewerState extends ConsumerState { } } - void _onTimelineReload(_) { + void _onTimelineReloadEvent() { + totalAssets = ref.read(timelineServiceProvider).totalAssets; + if (totalAssets == 0) { + context.maybePop(); + return; + } + + if (assetReloadRequested) { + assetReloadRequested = false; + _onAssetReloadEvent(); + return; + } + } + + void _onAssetReloadEvent() async { + final index = pageController.page?.round() ?? 0; + final timelineService = ref.read(timelineServiceProvider); + final newAsset = await timelineService.getAssetAsync(index); + + if (newAsset == null) { + return; + } + + final currentAsset = ref.read(currentAssetNotifier); + // Do not reload / close the bottom sheet if the asset has not changed + if (newAsset.heroTag == currentAsset?.heroTag) { + return; + } + setState(() { - totalAssets = ref.read(timelineServiceProvider).totalAssets; - if (totalAssets == 0) { - context.maybePop(); - return; - } - - final index = pageController.page?.round() ?? 0; - final newAsset = ref.read(timelineServiceProvider).getAsset(index); - final currentAsset = ref.read(currentAssetNotifier); - // Do not reload / close the bottom sheet if the asset has not changed - if (newAsset.heroTag == currentAsset?.heroTag) { - return; - } - _onAssetChanged(pageController.page!.round()); sheetCloseController?.close(); }); } - void _openBottomSheet( - BuildContext ctx, { - double extent = _kBottomSheetMinimumExtent, - }) { + void _openBottomSheet(BuildContext ctx, {double extent = _kBottomSheetMinimumExtent}) { ref.read(assetViewerProvider.notifier).setBottomSheet(true); initialScale = viewController?.scale; - viewController?.updateMultiple(scale: _getScaleForBottomSheet); + // viewController?.updateMultiple(scale: (viewController?.scale ?? 1.0) + 0.01); previousExtent = _kBottomSheetMinimumExtent; sheetCloseController = showBottomSheet( context: ctx, - sheetAnimationStyle: const AnimationStyle( - duration: Durations.short4, - reverseDuration: Durations.short2, - ), + sheetAnimationStyle: const AnimationStyle(duration: Durations.short4, reverseDuration: Durations.short2), constraints: const BoxConstraints(maxWidth: double.infinity), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), backgroundColor: ctx.colorScheme.surfaceContainerLowest, builder: (_) { return NotificationListener( onNotification: _onNotification, - child: AssetDetailBottomSheet( - controller: bottomSheetController, - initialChildSize: extent, - ), + child: AssetDetailBottomSheet(controller: bottomSheetController, initialChildSize: extent), ); }, ); @@ -480,42 +493,17 @@ class _AssetViewerState extends ConsumerState { } void _snapBottomSheet() { - if (bottomSheetController.size > _kBottomSheetSnapExtent || + if (!bottomSheetController.isAttached || + bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) { return; } isSnapping = true; - bottomSheetController.animateTo( - _kBottomSheetSnapExtent, - duration: Durations.short3, - curve: Curves.easeOut, - ); + bottomSheetController.animateTo(_kBottomSheetSnapExtent, duration: Durations.short3, curve: Curves.easeOut); } - Widget _placeholderBuilder( - BuildContext ctx, - ImageChunkEvent? progress, - int index, - ) { - BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); - final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; - if (stackChildren != null && stackChildren.isNotEmpty) { - asset = stackChildren - .elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); - } - return Container( - width: double.infinity, - height: double.infinity, - color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: Size( - ctx.width, - ctx.height, - ), - ), - ); + Widget _placeholderBuilder(BuildContext ctx, ImageChunkEvent? progress, int index) { + return const Center(child: ImmichLoadingIndicator()); } void _onScaleStateChanged(PhotoViewScaleState scaleState) { @@ -530,32 +518,44 @@ class _AssetViewerState extends ConsumerState { PhotoViewGalleryPageOptions _assetBuilder(BuildContext ctx, int index) { scaffoldContext ??= ctx; - BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); + final timelineService = ref.read(timelineServiceProvider); + final asset = timelineService.getAssetSafe(index); + + // If asset is not available in buffer, return a placeholder + if (asset == null) { + return PhotoViewGalleryPageOptions.customChild( + heroAttributes: PhotoViewHeroAttributes(tag: 'loading_$index'), + child: Container( + width: ctx.width, + height: ctx.height, + color: backgroundColor, + child: const Center(child: CircularProgressIndicator()), + ), + ); + } + + BaseAsset displayAsset = asset; final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; if (stackChildren != null && stackChildren.isNotEmpty) { - asset = stackChildren - .elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); + displayAsset = stackChildren.elementAt(ref.read(assetViewerProvider).stackIndex); } final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider); - if (asset.isImage && !isPlayingMotionVideo) { - return _imageBuilder(ctx, asset); + if (displayAsset.isImage && !isPlayingMotionVideo) { + return _imageBuilder(ctx, displayAsset); } - return _videoBuilder(ctx, asset); + return _videoBuilder(ctx, displayAsset); } PhotoViewGalleryPageOptions _imageBuilder(BuildContext ctx, BaseAsset asset) { - final size = Size(ctx.width, ctx.height); + final size = ctx.sizeData; return PhotoViewGalleryPageOptions( key: ValueKey(asset.heroTag), imageProvider: getFullImageProvider(asset, size: size), - heroAttributes: - PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), + heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), filterQuality: FilterQuality.high, tightMode: true, - initialScale: PhotoViewComputedScale.contained * 0.999, - minScale: PhotoViewComputedScale.contained * 0.999, disableScaleGestures: showingBottomSheet, onDragStart: _onDragStart, onDragUpdate: _onDragUpdate, @@ -563,14 +563,10 @@ class _AssetViewerState extends ConsumerState { onTapDown: _onTapDown, onLongPressStart: asset.isMotionPhoto ? _onLongPress : null, errorBuilder: (_, __, ___) => Container( - width: ctx.width, - height: ctx.height, + width: size.width, + height: size.height, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: size, - ), + child: Thumbnail.fromAsset(asset: asset, fit: BoxFit.contain), ), ); } @@ -586,12 +582,9 @@ class _AssetViewerState extends ConsumerState { onDragUpdate: _onDragUpdate, onDragEnd: _onDragEnd, onTapDown: _onTapDown, - heroAttributes: - PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), + heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), filterQuality: FilterQuality.high, - initialScale: PhotoViewComputedScale.contained * 0.99, maxScale: 1.0, - minScale: PhotoViewComputedScale.contained * 0.99, basePosition: Alignment.center, child: SizedBox( width: ctx.width, @@ -601,8 +594,7 @@ class _AssetViewerState extends ConsumerState { asset: asset, image: Image( key: ValueKey(asset), - image: - getFullImageProvider(asset, size: Size(ctx.width, ctx.height)), + image: getFullImageProvider(asset, size: ctx.sizeData), fit: BoxFit.contain, height: ctx.height, width: ctx.width, @@ -622,24 +614,31 @@ class _AssetViewerState extends ConsumerState { // Rebuild the widget when the asset viewer state changes // Using multiple selectors to avoid unnecessary rebuilds for other state changes ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + ref.watch(assetViewerProvider.select((s) => s.showingControls)); ref.watch(assetViewerProvider.select((s) => s.backgroundOpacity)); ref.watch(assetViewerProvider.select((s) => s.stackIndex)); ref.watch(isPlayingMotionVideoProvider); // Listen for casting changes and send initial asset to the cast provider - ref.listen(castProvider.select((value) => value.isCasting), - (_, isCasting) async { + ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) async { if (!isCasting) return; final asset = ref.read(currentAssetNotifier); if (asset == null) return; WidgetsBinding.instance.addPostFrameCallback((_) { - _handleCasting(asset); + _handleCasting(); }); }); - final isInLockedView = ref.watch(inLockedViewProvider); + // Listen for control visibility changes and change system UI mode accordingly + ref.listen(assetViewerProvider.select((value) => value.showingControls), (_, showingControls) async { + if (showingControls) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + } + }); // Currently it is not possible to scroll the asset when the bottom sheet is open all the way. // Issue: https://github.com/flutter/flutter/issues/109037 @@ -651,32 +650,33 @@ class _AssetViewerState extends ConsumerState { appBar: const ViewerTopAppBar(), extendBody: true, extendBodyBehindAppBar: true, - body: PhotoViewGallery.builder( - gaplessPlayback: true, - loadingBuilder: _placeholderBuilder, - pageController: pageController, - scrollPhysics: platform.isIOS - ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - , - itemCount: totalAssets, - onPageChanged: _onPageChanged, - onPageBuild: _onPageBuild, - scaleStateChangedCallback: _onScaleStateChanged, - builder: _assetBuilder, - backgroundDecoration: BoxDecoration(color: backgroundColor), - enablePanAlways: true, + floatingActionButton: const DownloadStatusFloatingButton(), + body: Stack( + children: [ + PhotoViewGallery.builder( + gaplessPlayback: true, + loadingBuilder: _placeholderBuilder, + pageController: pageController, + scrollPhysics: CurrentPlatform.isIOS + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics(), // Use heavy physics for Android + itemCount: totalAssets, + onPageChanged: _onPageChanged, + onPageBuild: _onPageBuild, + scaleStateChangedCallback: _onScaleStateChanged, + builder: _assetBuilder, + backgroundDecoration: BoxDecoration(color: backgroundColor), + enablePanAlways: true, + ), + ], ), bottomNavigationBar: showingBottomSheet ? const SizedBox.shrink() - : Column( + : const Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const AssetStackRow(), - if (!isInLockedView) const ViewerBottomBar(), - ], + children: [AssetStackRow(), ViewerBottomBar()], ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 825b637e8d..354902c9d7 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -7,6 +7,10 @@ class ViewerOpenBottomSheetEvent extends Event { const ViewerOpenBottomSheetEvent(); } +class ViewerReloadAssetEvent extends Event { + const ViewerReloadAssetEvent(); +} + class AssetViewerState { final int backgroundOpacity; final bool showingBottomSheet; @@ -64,34 +68,44 @@ class AssetViewerState { stackIndex.hashCode; } -class AssetViewerStateNotifier extends AutoDisposeNotifier { +class AssetViewerStateNotifier extends Notifier { @override AssetViewerState build() { return const AssetViewerState(); } + void reset() { + state = const AssetViewerState(); + } + void setAsset(BaseAsset? asset) { + if (asset == state.currentAsset) { + return; + } state = state.copyWith(currentAsset: asset, stackIndex: 0); } void setOpacity(int opacity) { - state = state.copyWith( - backgroundOpacity: opacity, - showingControls: opacity == 255 ? true : state.showingControls, - ); + if (opacity == state.backgroundOpacity) { + return; + } + state = state.copyWith(backgroundOpacity: opacity, showingControls: opacity == 255 ? true : state.showingControls); } void setBottomSheet(bool showing) { - state = state.copyWith( - showingBottomSheet: showing, - showingControls: showing ? true : state.showingControls, - ); + if (showing == state.showingBottomSheet) { + return; + } + state = state.copyWith(showingBottomSheet: showing, showingControls: showing ? true : state.showingControls); if (showing) { ref.read(videoPlayerControlsProvider.notifier).pause(); } } void setControls(bool isShowing) { + if (isShowing == state.showingControls) { + return; + } state = state.copyWith(showingControls: isShowing); } @@ -100,11 +114,11 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { } void setStackIndex(int index) { + if (index == state.stackIndex) { + return; + } state = state.copyWith(stackIndex: index); } } -final assetViewerProvider = - AutoDisposeNotifierProvider( - AssetViewerStateNotifier.new, -); +final assetViewerProvider = NotifierProvider(AssetViewerStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index d35a315f48..3111512823 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -4,9 +4,16 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/edit_image_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart'; @@ -20,16 +27,14 @@ class ViewerBottomBar extends ConsumerWidget { return const SizedBox.shrink(); } + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; - final isSheetOpen = ref.watch( - assetViewerProvider.select((s) => s.showingBottomSheet), - ); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final isInLockedView = ref.watch(inLockedViewProvider); + final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive; if (!showControls) { opacity = 0; @@ -37,8 +42,17 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), - if (asset.hasRemote && isOwner) - const ArchiveActionButton(source: ActionSource.viewer), + if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), + if (asset.type == AssetType.image) const EditImageActionButton(), + if (isOwner) ...[ + if (asset.hasRemote && isOwner && isArchived) + const UnArchiveActionButton(source: ActionSource.viewer) + else + const ArchiveActionButton(source: ActionSource.viewer), + asset.isLocalOnly + ? const DeleteLocalActionButton(source: ActionSource.viewer) + : const DeleteActionButton(source: ActionSource.viewer, showConfirmation: true), + ], ]; return IgnorePointer( @@ -50,31 +64,23 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short4, child: isSheetOpen ? const SizedBox.shrink() - : SafeArea( - child: Theme( - data: context.themeData.copyWith( - iconTheme: - const IconThemeData(size: 22, color: Colors.white), - textTheme: context.themeData.textTheme.copyWith( - labelLarge: - context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), - ), + : Theme( + data: context.themeData.copyWith( + iconTheme: const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: context.themeData.textTheme.labelLarge?.copyWith(color: Colors.white), ), - child: Container( - height: asset.isVideo ? 160 : 80, - color: Colors.black.withAlpha(125), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), - ], - ), + ), + child: Container( + color: Colors.black.withAlpha(125), + padding: EdgeInsets.only(bottom: context.padding.bottom, top: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + if (!isInLockedView && !isReadonlyModeEnabled) + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), + ], ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 89822fef91..bdd7fb9b48 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -1,26 +1,27 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/utils/action_button.utils.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; const _kSeparator = ' â€ĸ '; @@ -28,11 +29,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { final DraggableScrollableController? controller; final double initialChildSize; - const AssetDetailBottomSheet({ - this.controller, - this.initialChildSize = 0.35, - super.key, - }); + const AssetDetailBottomSheet({this.controller, this.initialChildSize = 0.35, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -41,36 +38,29 @@ class AssetDetailBottomSheet extends ConsumerWidget { return const SizedBox.shrink(); } - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + final isOwner = asset is RemoteAsset && asset.ownerId == ref.watch(currentUserProvider)?.id; + final isInLockedView = ref.watch(inLockedViewProvider); + final currentAlbum = ref.watch(currentRemoteAlbumProvider); + final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive; + final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting); + + final buttonContext = ActionButtonContext( + asset: asset, + isOwner: isOwner, + isArchived: isArchived, + isTrashEnabled: isTrashEnable, + isInLockedView: isInLockedView, + isStacked: asset is RemoteAsset && asset.stackId != null, + currentAlbum: currentAlbum, + advancedTroubleshooting: advancedTroubleshooting, + source: ActionSource.viewer, ); - final isInLockedView = ref.watch(inLockedViewProvider); - - final actions = [ - const ShareActionButton(source: ActionSource.viewer), - if (asset.hasRemote) ...[ - const ShareLinkActionButton(source: ActionSource.viewer), - const ArchiveActionButton(source: ActionSource.viewer), - if (!asset.hasLocal) - const DownloadActionButton(source: ActionSource.viewer), - isTrashEnable - ? const TrashActionButton(source: ActionSource.viewer) - : const DeletePermanentActionButton(source: ActionSource.viewer), - const MoveToLockFolderActionButton( - source: ActionSource.viewer, - ), - ], - if (asset.storage == AssetState.local) ...[ - const DeleteLocalActionButton(source: ActionSource.viewer), - const UploadActionButton(), - ], - ]; - - final lockedViewActions = []; + final actions = ActionButtonBuilder.build(buttonContext); return BaseBottomSheet( - actions: isInLockedView ? lockedViewActions : actions, + actions: actions, slivers: const [_AssetDetailBottomSheet()], controller: controller, initialChildSize: initialChildSize, @@ -100,18 +90,14 @@ class _AssetDetailBottomSheet extends ConsumerWidget { String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) { final height = asset.height ?? exifInfo?.height; final width = asset.width ?? exifInfo?.width; - final resolution = (width != null && height != null) - ? "${width.toInt()} x ${height.toInt()}" - : null; - final fileSize = - exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; + final resolution = (width != null && height != null) ? "${width.toInt()} x ${height.toInt()}" : null; + final fileSize = exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; return switch ((fileSize, resolution)) { (null, null) => '', (String fileSize, null) => fileSize, (null, String resolution) => resolution, - (String fileSize, String resolution) => - '$fileSize$_kSeparator$resolution', + (String fileSize, String resolution) => '$fileSize$_kSeparator$resolution', }; } @@ -133,17 +119,16 @@ class _AssetDetailBottomSheet extends ConsumerWidget { return null; } - final fNumber = - exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null; - final exposureTime = - exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null; - final focalLength = - exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null; + final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null; + final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null; + final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null; final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null; - return [fNumber, exposureTime, focalLength, iso] - .where((spec) => spec != null && spec.isNotEmpty) - .join(_kSeparator); + return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator); + } + + Future _editDateTime(BuildContext context, WidgetRef ref) async { + await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); } @override @@ -161,10 +146,12 @@ class _AssetDetailBottomSheet extends ConsumerWidget { // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), - titleStyle: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), + trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote ? () async => await _editDateTime(context, ref) : null, ), + if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + const SheetPeopleDetails(), const SheetLocationDetails(), // Details header _SheetTile( @@ -193,27 +180,26 @@ class _AssetDetailBottomSheet extends ConsumerWidget { _SheetTile( title: cameraTitle, titleStyle: context.textTheme.labelLarge, - leading: Icon( - Icons.camera_outlined, - size: 24, - color: context.textTheme.labelLarge?.color, - ), + leading: Icon(Icons.camera_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getCameraInfoSubtitle(exifInfo), subtitleStyle: context.textTheme.bodyMedium?.copyWith( color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), + const SizedBox(height: 64), ], ); } } -class _SheetTile extends StatelessWidget { +class _SheetTile extends ConsumerWidget { final String title; final Widget? leading; + final Widget? trailing; final String? subtitle; final TextStyle? titleStyle; final TextStyle? subtitleStyle; + final VoidCallback? onTap; const _SheetTile({ required this.title, @@ -221,10 +207,22 @@ class _SheetTile extends StatelessWidget { this.leading, this.subtitle, this.subtitleStyle, + this.trailing, + this.onTap, }); + void copyTitle(BuildContext context, WidgetRef ref) { + Clipboard.setData(ClipboardData(text: title)); + ImmichToast.show( + context: context, + msg: 'copied_to_clipboard'.t(context: context), + toastType: ToastType.info, + ); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + } + @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final Widget titleWidget; if (leading == null) { titleWidget = LimitedBox( @@ -254,11 +252,88 @@ class _SheetTile extends StatelessWidget { return ListTile( dense: true, visualDensity: VisualDensity.compact, - title: titleWidget, + title: GestureDetector(onLongPress: () => copyTitle(context, ref), child: titleWidget), titleAlignment: ListTileTitleAlignment.center, leading: leading, + trailing: trailing, contentPadding: leading == null ? null : const EdgeInsets.only(left: 25), subtitle: subtitleWidget, + onTap: onTap, + ); + } +} + +class _SheetAssetDescription extends ConsumerStatefulWidget { + final ExifInfo exif; + + const _SheetAssetDescription({required this.exif}); + + @override + ConsumerState<_SheetAssetDescription> createState() => _SheetAssetDescriptionState(); +} + +class _SheetAssetDescriptionState extends ConsumerState<_SheetAssetDescription> { + late TextEditingController _controller; + final _descriptionFocus = FocusNode(); + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.exif.description ?? ''); + } + + Future saveDescription(String? previousDescription) async { + final newDescription = _controller.text.trim(); + + if (newDescription == previousDescription) { + _descriptionFocus.unfocus(); + return; + } + + final editAction = await ref.read(actionProvider.notifier).updateDescription(ActionSource.viewer, newDescription); + + if (!editAction.success) { + _controller.text = previousDescription ?? ''; + + ImmichToast.show( + context: context, + msg: 'exif_bottom_sheet_description_error'.t(context: context), + toastType: ToastType.error, + ); + } + + _descriptionFocus.unfocus(); + } + + @override + Widget build(BuildContext context) { + // Watch the current asset EXIF provider to get updates + final currentExifInfo = ref.watch(currentAssetExifProvider).valueOrNull; + + // Update controller text when EXIF data changes + final currentDescription = currentExifInfo?.description ?? ''; + if (_controller.text != currentDescription && !_descriptionFocus.hasFocus) { + _controller.text = currentDescription; + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + child: TextField( + controller: _controller, + keyboardType: TextInputType.multiline, + focusNode: _descriptionFocus, + maxLines: null, // makes it grow as text is added + decoration: InputDecoration( + hintText: 'exif_bottom_sheet_description'.t(context: context), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + onTapOutside: (_) => saveDescription(currentExifInfo?.description), + ), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart similarity index 68% rename from mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart rename to mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart index 855328ebde..ab57ea4d8b 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart @@ -38,20 +38,13 @@ class _SheetLocationDetailsState extends ConsumerState { _mapController = controller; } - void _onExifChanged( - AsyncValue? previous, - AsyncValue current, - ) { + void _onExifChanged(AsyncValue? previous, AsyncValue current) { asset = ref.read(currentAssetNotifier); setState(() { exifInfo = current.valueOrNull; final hasCoordinates = exifInfo?.hasCoordinates ?? false; if (exifInfo != null && hasCoordinates) { - _mapController?.moveCamera( - CameraUpdate.newLatLng( - LatLng(exifInfo!.latitude!, exifInfo!.longitude!), - ), - ); + _mapController?.moveCamera(CameraUpdate.newLatLng(LatLng(exifInfo!.latitude!, exifInfo!.longitude!))); } }); } @@ -59,11 +52,7 @@ class _SheetLocationDetailsState extends ConsumerState { @override void initState() { super.initState(); - ref.listenManual( - currentAssetExifProvider, - _onExifChanged, - fireImmediately: true, - ); + ref.listenManual(currentAssetExifProvider, _onExifChanged, fireImmediately: true); } @override @@ -71,23 +60,16 @@ class _SheetLocationDetailsState extends ConsumerState { final hasCoordinates = exifInfo?.hasCoordinates ?? false; // Guard no lat/lng - if (!hasCoordinates || - (asset != null && asset is LocalAsset && asset!.hasRemote)) { + if (!hasCoordinates || (asset != null && asset is LocalAsset && asset!.hasRemote)) { return const SizedBox.shrink(); } - final remoteId = asset is LocalAsset - ? (asset as LocalAsset).remoteId - : (asset as RemoteAsset).id; + final remoteId = asset is LocalAsset ? (asset as LocalAsset).remoteId : (asset as RemoteAsset).id; final locationName = _getLocationName(exifInfo); - final coordinates = - "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; + final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; return Padding( - padding: EdgeInsets.symmetric( - vertical: 16.0, - horizontal: context.isMobile ? 16.0 : 56.0, - ), + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: context.isMobile ? 16.0 : 56.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -101,25 +83,16 @@ class _SheetLocationDetailsState extends ConsumerState { ), ), ), - ExifMap( - exifInfo: exifInfo!, - markerId: remoteId, - onMapCreated: _onMapCreated, - ), + ExifMap(exifInfo: exifInfo!, markerId: remoteId, onMapCreated: _onMapCreated), const SizedBox(height: 15), if (locationName != null) Padding( padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - locationName, - style: context.textTheme.labelLarge, - ), + child: Text(locationName, style: context.textTheme.labelLarge), ), Text( coordinates, - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart new file mode 100644 index 0000000000..64f22eca92 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -0,0 +1,178 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; + +class SheetPeopleDetails extends ConsumerStatefulWidget { + const SheetPeopleDetails({super.key}); + + @override + ConsumerState createState() => _SheetPeopleDetailsState(); +} + +class _SheetPeopleDetailsState extends ConsumerState { + @override + Widget build(BuildContext context) { + final asset = ref.watch(currentAssetNotifier); + if (asset is! RemoteAsset) { + return const SizedBox.shrink(); + } + + final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id)); + + Future showNameEditModal(DriftPerson person) async { + await showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); + + ref.invalidate(driftPeopleAssetProvider(asset.id)); + } + + return peopleFuture.when( + data: (people) { + return AnimatedCrossFade( + firstChild: const SizedBox.shrink(), + secondChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: Text( + "people".t(context: context).toUpperCase(), + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 160, + child: ListView( + padding: const EdgeInsets.only(left: 16.0), + scrollDirection: Axis.horizontal, + children: [ + for (final person in people) + _PeopleAvatar( + person: person, + assetFileCreatedAt: asset.createdAt, + onTap: () { + final previousRouteData = ref.read(previousRouteDataProvider); + final previousRouteArgs = previousRouteData?.arguments; + + // Prevent circular navigation + if (previousRouteArgs is DriftPersonRouteArgs && previousRouteArgs.person.id == person.id) { + context.back(); + return; + } + context.pop(); + context.pushRoute(DriftPersonRoute(person: person)); + }, + onNameTap: () => showNameEditModal(person), + ), + ], + ), + ), + ], + ), + crossFadeState: people.isEmpty ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: Durations.short4, + ); + }, + error: (error, stack) => Text("error_loading_people".t(context: context), style: context.textTheme.bodyMedium), + loading: () => const SizedBox.shrink(), + ); + } +} + +class _PeopleAvatar extends StatelessWidget { + final DriftPerson person; + final DateTime assetFileCreatedAt; + final VoidCallback? onTap; + final VoidCallback? onNameTap; + final double imageSize = 96; + + const _PeopleAvatar({required this.person, required this.assetFileCreatedAt, this.onTap, this.onNameTap}); + + @override + Widget build(BuildContext context) { + final headers = ApiService.getRequestHeaders(); + + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 96), + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: onTap, + child: SizedBox( + height: imageSize, + child: Material( + shape: CircleBorder(side: BorderSide(color: context.primaryColor.withAlpha(50), width: 1.0)), + shadowColor: context.colorScheme.shadow, + elevation: 3, + child: CircleAvatar( + maxRadius: imageSize / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + ), + const SizedBox(height: 4), + if (person.name.isEmpty) + GestureDetector( + onTap: () => onNameTap?.call(), + child: Text( + "add_a_name".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ) + else + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + person.name, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelLarge, + maxLines: 1, + ), + if (person.birthDate != null) + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + formatAge(person.birthDate!, assetFileCreatedAt), + textAlign: TextAlign.center, + style: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(175), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index 4cdf9f2287..149252ab17 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -3,18 +3,24 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/cast_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/favorite_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { const ViewerTopAppBar({super.key}); @@ -26,49 +32,61 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { return const SizedBox.shrink(); } + final album = ref.watch(currentRemoteAlbumProvider); + final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; final isInLockedView = ref.watch(inLockedViewProvider); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); - final isShowingSheet = ref - .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final previousRouteName = ref.watch(previousRouteNameProvider); + final tabRoute = ref.watch(tabProvider); + final showViewInTimelineButton = + (previousRouteName != TabShellRoute.name || tabRoute == TabEnum.search) && + previousRouteName != AssetViewerRoute.name && + previousRouteName != null && + previousRouteName != LocalTimelineRoute.name; + + final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; } - final isCasting = ref.watch( - castProvider.select((c) => c.isCasting), - ); - final websocketConnected = - ref.watch(websocketProvider.select((c) => c.isConnected)); + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); final actions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, + if (asset.isRemoteOnly) const DownloadActionButton(source: ActionSource.viewer, menuItem: true), + if (isCasting || (asset.hasRemote)) const CastActionButton(menuItem: true), + if (album != null && album.isActivityEnabled && album.isShared) + IconButton( + icon: const Icon(Icons.chat_outlined), + onPressed: () { + context.navigateTo(const DriftActivitiesRoute()); + }, + ), + if (showViewInTimelineButton) + IconButton( + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(asset.createdAt)); + }, + icon: const Icon(Icons.image_search), + tooltip: 'view_in_timeline'.t(context: context), ), if (asset.hasRemote && isOwner && !asset.isFavorite) const FavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.hasRemote && isOwner && asset.isFavorite) - const UnFavoriteActionButton( - source: ActionSource.viewer, - menuItem: true, - ), + const UnFavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true), const _KebabMenu(), ]; final lockedViewActions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, - ), + if (isCasting || (asset.hasRemote)) const CastActionButton(menuItem: true), const _KebabMenu(), ]; @@ -78,17 +96,16 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { opacity: opacity / 255, duration: Durations.short2, child: AppBar( - backgroundColor: - isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125), + backgroundColor: isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125), leading: const _AppBarBackButton(), iconTheme: const IconThemeData(size: 22, color: Colors.white), actionsIconTheme: const IconThemeData(size: 22, color: Colors.white), shape: const Border(), - actions: isShowingSheet + actions: isShowingSheet || isReadonlyModeEnabled ? null : isInLockedView - ? lockedViewActions - : actions, + ? lockedViewActions + : actions, ), ), ); @@ -117,12 +134,9 @@ class _AppBarBackButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isShowingSheet = ref - .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - final backgroundColor = - isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black; - final foregroundColor = - isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white; + final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + final backgroundColor = isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black; + final foregroundColor = isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white; return Padding( padding: const EdgeInsets.only(left: 12.0), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 17880da3e7..2bab507e3f 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,6 +27,23 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +bool _isCurrentAsset(BaseAsset asset, BaseAsset? currentAsset) { + if (asset is RemoteAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.id == asset.id, + LocalAsset localAsset => localAsset.remoteId == asset.id, + _ => false, + }; + } else if (asset is LocalAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.localId == asset.id, + LocalAsset localAsset => localAsset.id == asset.id, + _ => false, + }; + } + return false; +} + class NativeVideoViewer extends HookConsumerWidget { final BaseAsset asset; final bool showControls; @@ -56,7 +73,7 @@ class NativeVideoViewer extends HookConsumerWidget { // If the swipe is completed, `isCurrent` will be true for video B after a delay. // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. final currentAsset = useState(ref.read(currentAssetNotifier)); - final isCurrent = currentAsset.value == asset; + final isCurrent = _isCurrentAsset(asset, currentAsset.value); // Used to show the placeholder during hero animations for remote videos to avoid a stutter final isVisible = useState(Platform.isIOS && asset.hasLocal); @@ -70,33 +87,35 @@ class NativeVideoViewer extends HookConsumerWidget { return null; } + final videoAsset = await ref.read(assetServiceProvider).getAsset(asset) ?? asset; + if (!context.mounted) { + return null; + } + try { - if (asset.hasLocal && asset.livePhotoVideoId == null) { - final id = asset is LocalAsset - ? (asset as LocalAsset).id - : (asset as RemoteAsset).localId!; + if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) { + final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!; final file = await const StorageRepository().getFileForAsset(id); + if (!context.mounted) { + return null; + } + if (file == null) { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } - final remoteId = (asset as RemoteAsset).id; + final remoteId = (videoAsset as RemoteAsset).id; // Use a network URL for the video player controller final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final isOriginalVideo = - ref.read(settingsProvider).get(Setting.loadOriginalVideo); - final String postfixUrl = - isOriginalVideo ? 'original' : 'video/playback'; - final String videoUrl = asset.livePhotoVideoId != null - ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' + final isOriginalVideo = ref.read(settingsProvider).get(Setting.loadOriginalVideo); + final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; + final String videoUrl = videoAsset.livePhotoVideoId != null + ? '$serverEndpoint/assets/${videoAsset.livePhotoVideoId}/$postfixUrl' : '$serverEndpoint/assets/$remoteId/$postfixUrl'; final source = await VideoSource.init( @@ -106,32 +125,24 @@ class NativeVideoViewer extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.name}: $error', - ); + log.severe('Error creating video source for asset ${videoAsset.name}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(null); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = - await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.name}: $error', - ); - } - }, - [asset.heroTag], - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.name}: $error'); + } + }, [asset.heroTag]); void checkIfBuffering() { if (!context.mounted) { @@ -139,11 +150,11 @@ class NativeVideoViewer extends HookConsumerWidget { } final videoPlayback = ref.read(videoPlaybackValueProvider); - if ((isBuffering.value || - videoPlayback.state == VideoPlaybackState.initializing) && + if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -199,8 +210,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback; if (ref.read(assetViewerProvider.select((s) => s.showingBottomSheet))) { @@ -221,8 +231,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); if (videoPlayback.state == VideoPlaybackState.playing) { // Sync with the controls playing WakelockPlus.enable(); @@ -231,8 +240,7 @@ class NativeVideoViewer extends HookConsumerWidget { WakelockPlus.disable(); } - ref.read(videoPlaybackValueProvider.notifier).status = - videoPlayback.state; + ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state; } void onPlaybackPositionChanged() { @@ -251,8 +259,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - ref.read(videoPlaybackValueProvider.notifier).position = - Duration(seconds: playbackInfo.position); + ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position); // Check if the video is buffering if (playbackInfo.status == PlaybackStatus.playing) { @@ -276,10 +283,8 @@ class NativeVideoViewer extends HookConsumerWidget { } void removeListeners(NativeVideoPlayerController controller) { - controller.onPlaybackPositionChanged - .removeListener(onPlaybackPositionChanged); - controller.onPlaybackStatusChanged - .removeListener(onPlaybackStatusChanged); + controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged); + controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged); controller.onPlaybackReady.removeListener(onPlaybackReady); controller.onPlaybackEnded.removeListener(onPlaybackEnded); } @@ -292,7 +297,7 @@ class NativeVideoViewer extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); final source = await videoSource; - if (source == null) { + if (source == null || !context.mounted) { return; } @@ -304,9 +309,7 @@ class NativeVideoViewer extends HookConsumerWidget { nc.loadVideoSource(source).catchError((error) { log.severe('Error loading video source: $error'); }); - final loopVideo = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo); + final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); nc.setLoop(!asset.isMotionPhoto && loopVideo); controller.value = nc; @@ -319,6 +322,9 @@ class NativeVideoViewer extends HookConsumerWidget { removeListeners(playerController); } + if (value != null) { + isVisible.value = _isCurrentAsset(value, asset); + } final curAsset = currentAsset.value; if (curAsset == asset) { return; @@ -339,48 +345,42 @@ class NativeVideoViewer extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -410,12 +410,7 @@ class NativeVideoViewer extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart index 883169ae83..c1324b8ac0 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart @@ -13,47 +13,33 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class VideoViewerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const VideoViewerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const VideoViewerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetNotifier.select((asset) => asset != null && asset.isVideo), - ); - bool showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); - final showBottomSheet = - ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + final assetIsVideo = ref.watch(currentAssetNotifier.select((asset) => asset != null && asset.isVideo)); + bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); if (showBottomSheet) { showControls = false; } - final VideoPlaybackState state = - ref.watch(videoPlaybackValueProvider.select((value) => value.state)); + final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && - state != VideoPlaybackState.completed && - assetIsVideo) { - ref.read(assetViewerProvider.notifier).setControls(false); - } - }, - ); - final showBuffering = - state == VideoPlaybackState.buffering && !cast.isCasting; + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(assetViewerProvider.notifier).setControls(false); + } + }); + final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them void showControlsAndStartHideTimer() { @@ -62,8 +48,7 @@ class VideoViewerControls extends HookConsumerWidget { } // When we change position, show or hide timer - ref.listen(videoPlayerControlsProvider.select((v) => v.position), - (previous, next) { + ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) { showControlsAndStartHideTimer(); }); @@ -104,21 +89,16 @@ class VideoViewerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( - onTap: () => - ref.read(assetViewerProvider.notifier).setControls(false), + onTap: () => ref.read(assetViewerProvider.notifier).setControls(false), child: CenterPlayButton( backgroundColor: Colors.black54, iconColor: Colors.white, isFinished: state == VideoPlaybackState.completed, - isPlaying: state == VideoPlaybackState.playing || - (cast.isCasting && cast.castState == CastState.playing), + isPlaying: + state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing), show: assetIsVideo && showControls, onPressed: togglePlay, ), diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart index c817b9b4b6..8d374f74ff 100644 --- a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -11,18 +11,13 @@ class BackupToggleButton extends ConsumerStatefulWidget { final VoidCallback onStart; final VoidCallback onStop; - const BackupToggleButton({ - super.key, - required this.onStart, - required this.onStop, - }); + const BackupToggleButton({super.key, required this.onStart, required this.onStop}); @override ConsumerState createState() => BackupToggleButtonState(); } -class BackupToggleButtonState extends ConsumerState - with SingleTickerProviderStateMixin { +class BackupToggleButtonState extends ConsumerState with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _gradientAnimation; bool _isEnabled = false; @@ -30,21 +25,14 @@ class BackupToggleButtonState extends ConsumerState @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 8), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 8), vsync: this); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - _isEnabled = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableBackup); + _isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); } @override @@ -54,9 +42,7 @@ class BackupToggleButtonState extends ConsumerState } Future _onToggle(bool value) async { - await ref - .read(appSettingsServiceProvider) - .setSetting(AppSettingsEnum.enableBackup, value); + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value); setState(() { _isEnabled = value; @@ -71,23 +57,17 @@ class BackupToggleButtonState extends ConsumerState @override Widget build(BuildContext context) { - final enqueueCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueCount), - ); + final enqueueCount = ref.watch(driftBackupProvider.select((state) => state.enqueueCount)); - final enqueueTotalCount = ref.watch( - driftBackupProvider.select((state) => state.enqueueTotalCount), - ); + final enqueueTotalCount = ref.watch(driftBackupProvider.select((state) => state.enqueueTotalCount)); - final isCanceling = ref.watch( - driftBackupProvider.select((state) => state.isCanceling), - ); + final isCanceling = ref.watch(driftBackupProvider.select((state) => state.isCanceling)); - final uploadTasks = ref.watch( - driftBackupProvider.select((state) => state.uploadItems), - ); + final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); - final isUploading = uploadTasks.isNotEmpty; + final isSyncing = ref.watch(driftBackupProvider.select((state) => state.isSyncing)); + + final isProcessing = uploadTasks.isNotEmpty || isSyncing; return AnimatedBuilder( animation: _animationController, @@ -121,11 +101,7 @@ class BackupToggleButtonState extends ConsumerState end: Alignment.bottomRight, ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 12, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 2)), ], ), child: Container( @@ -141,8 +117,7 @@ class BackupToggleButtonState extends ConsumerState borderRadius: const BorderRadius.all(Radius.circular(20.5)), onTap: () => isCanceling ? null : _onToggle(!_isEnabled), child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Row( children: [ Container( @@ -156,19 +131,9 @@ class BackupToggleButtonState extends ConsumerState ], ), ), - child: isUploading - ? const SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) - : Icon( - Icons.cloud_upload_outlined, - color: context.primaryColor, - size: 24, - ), + child: isProcessing + ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) + : Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24), ), const SizedBox(width: 16), Expanded( @@ -180,8 +145,7 @@ class BackupToggleButtonState extends ConsumerState children: [ Text( "enable_backup".t(context: context), - style: - context.textTheme.titleMedium?.copyWith( + style: context.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: context.primaryColor, ), @@ -192,10 +156,7 @@ class BackupToggleButtonState extends ConsumerState Text( "queue_status".t( context: context, - args: { - 'count': enqueueCount.toString(), - 'total': enqueueTotalCount.toString(), - }, + args: {'count': enqueueCount.toString(), 'total': enqueueTotalCount.toString()}, ), style: context.textTheme.labelLarge?.copyWith( color: context.colorScheme.onSurfaceSecondary, @@ -204,19 +165,14 @@ class BackupToggleButtonState extends ConsumerState if (isCanceling) Row( children: [ - Text( - "canceling".t(), - style: context.textTheme.labelLarge, - ), + Text("canceling".t(), style: context.textTheme.labelLarge), const SizedBox(width: 4), SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, - backgroundColor: context - .colorScheme.onSurface - .withValues(alpha: 0.2), + backgroundColor: context.colorScheme.onSurface.withValues(alpha: 0.2), ), ), ], @@ -224,11 +180,7 @@ class BackupToggleButtonState extends ConsumerState ], ), ), - Switch.adaptive( - value: _isEnabled, - onChanged: (value) => - isCanceling ? null : _onToggle(value), - ), + Switch.adaptive(value: _isEnabled, onChanged: (value) => isCanceling ? null : _onToggle(value)), ], ), ), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index 9ed35da4cd..f40e189e18 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -13,6 +13,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -24,9 +25,7 @@ class ArchiveBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,19 +40,16 @@ class ArchiveBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), - const StackActionButton(source: ActionSource.timeline), + const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index e172eec03b..0549bceb9c 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -22,21 +22,19 @@ class BaseBottomSheet extends ConsumerStatefulWidget { this.slivers, this.controller, this.initialChildSize = 0.35, - this.minChildSize = 0.15, + double? minChildSize, this.maxChildSize = 0.65, this.expand = true, this.shouldCloseOnMinExtent = true, this.resizeOnScroll = true, this.backgroundColor, - }); + }) : minChildSize = minChildSize ?? 0.15; @override - ConsumerState createState() => - _BaseDraggableScrollableSheetState(); + ConsumerState createState() => _BaseDraggableScrollableSheetState(); } -class _BaseDraggableScrollableSheetState - extends ConsumerState { +class _BaseDraggableScrollableSheetState extends ConsumerState { late DraggableScrollableController _controller; @override @@ -71,41 +69,28 @@ class _BaseDraggableScrollableSheetState shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent, builder: (BuildContext context, ScrollController scrollController) { return Card( - color: widget.backgroundColor ?? - context.colorScheme.surfaceContainerHigh, - borderOnForeground: false, - clipBehavior: Clip.antiAlias, - elevation: 6.0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - ), + color: widget.backgroundColor ?? context.colorScheme.surfaceContainer, + elevation: 3.0, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(18))), margin: const EdgeInsets.symmetric(horizontal: 0), child: CustomScrollView( controller: scrollController, slivers: [ - SliverToBoxAdapter( - child: Column( - children: [ - const SizedBox(height: 10), - const _DragHandle(), - const SizedBox(height: 14), - if (widget.actions.isNotEmpty) + const SliverPersistentHeader(delegate: _DragHandleDelegate(), pinned: true), + if (widget.actions.isNotEmpty) + SliverToBoxAdapter( + child: Column( + children: [ SizedBox( height: 115, - child: ListView( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: widget.actions, - ), + child: ListView(shrinkWrap: true, scrollDirection: Axis.horizontal, children: widget.actions), ), - if (widget.actions.isNotEmpty) ...[ const Divider(indent: 16, endIndent: 16), const SizedBox(height: 16), ], - ], + ), ), - ), - ...(widget.slivers ?? []), + if (widget.slivers != null) ...widget.slivers!, ], ), ); @@ -114,17 +99,42 @@ class _BaseDraggableScrollableSheetState } } +class _DragHandleDelegate extends SliverPersistentHeaderDelegate { + const _DragHandleDelegate(); + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + return const _DragHandle(); + } + + @override + bool shouldRebuild(_DragHandleDelegate oldDelegate) => false; + + @override + double get minExtent => 50.0; + + @override + double get maxExtent => 50.0; +} + class _DragHandle extends StatelessWidget { const _DragHandle(); @override Widget build(BuildContext context) { - return Container( - height: 6, - width: 32, - decoration: BoxDecoration( - color: context.themeData.dividerColor.lighten(amount: 0.6), - borderRadius: const BorderRadius.all(Radius.circular(20)), + return SizedBox( + height: 50, + child: Center( + child: SizedBox( + width: 32, + height: 6, + child: DecoratedBox( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + color: context.themeData.dividerColor.lighten(amount: 0.6), + ), + ), + ), ), ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index a1e1255a9f..b2502127d4 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -13,10 +16,14 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class FavoriteBottomSheet extends ConsumerWidget { const FavoriteBottomSheet({super.key}); @@ -24,13 +31,44 @@ class FavoriteBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + + Future addAssetsToAlbum(RemoteAlbum album) async { + final selectedAssets = multiselect.selectedAssets; + if (selectedAssets.isEmpty) { + return; + } + + final remoteAssets = selectedAssets.whereType(); + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, remoteAssets.map((e) => e.id).toList()); + + if (selectedAssets.length != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context), + ); + } + + if (addedCount != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_already_exists'.t(args: {"album": album.name}), + ); + } else { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_added'.t(args: {"album": album.name}), + ); + } + + ref.read(multiSelectProvider.notifier).reset(); + } return BaseBottomSheet( - initialChildSize: 0.25, - maxChildSize: 0.4, + initialChildSize: 0.4, + maxChildSize: 0.7, shouldCloseOnMinExtent: false, actions: [ const ShareActionButton(source: ActionSource.timeline), @@ -41,21 +79,21 @@ class FavoriteBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), - const StackActionButton(source: ActionSource.timeline), + const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], + slivers: multiselect.hasRemote + ? [const AddToAlbumHeader(), AlbumSelector(onAlbumSelected: addAssetsToAlbum)] + : [], ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index 373d264d82..9436707c84 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -1,9 +1,16 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -13,49 +20,116 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class GeneralBottomSheet extends ConsumerWidget { - const GeneralBottomSheet({super.key}); +class GeneralBottomSheet extends ConsumerStatefulWidget { + final double? minChildSize; + const GeneralBottomSheet({super.key, this.minChildSize}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _GeneralBottomSheetState(); +} + +class _GeneralBottomSheetState extends ConsumerState { + late DraggableScrollableController sheetController; + @override + void initState() { + super.initState(); + sheetController = DraggableScrollableController(); + } + + @override + void dispose() { + sheetController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting); + + Future addAssetsToAlbum(RemoteAlbum album) async { + final selectedAssets = multiselect.selectedAssets; + if (selectedAssets.isEmpty) { + return; + } + + final remoteAssets = selectedAssets.whereType(); + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, remoteAssets.map((e) => e.id).toList()); + + if (selectedAssets.length != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_some_local_assets'.t(context: context), + ); + } + + if (addedCount != remoteAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), + ); + } else { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), + ); + } + + ref.read(multiSelectProvider.notifier).reset(); + } + + Future onKeyboardExpand() { + return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); + } return BaseBottomSheet( - initialChildSize: 0.25, - maxChildSize: 0.4, + controller: sheetController, + initialChildSize: widget.minChildSize ?? 0.15, + minChildSize: widget.minChildSize, + maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ + if (multiselect.selectedAssets.length == 1 && advancedTroubleshooting) ...[ + const AdvancedInfoActionButton(source: ActionSource.timeline), + ], const ShareActionButton(source: ActionSource.timeline), if (multiselect.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.timeline), - const ArchiveActionButton(source: ActionSource.timeline), - const FavoriteActionButton(source: ActionSource.timeline), const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const FavoriteActionButton(source: ActionSource.timeline), + const ArchiveActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), - const StackActionButton(source: ActionSource.timeline), - ], - if (multiselect.hasLocal) ...[ - const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), + const DeleteActionButton(source: ActionSource.timeline), ], + if (multiselect.hasLocal || multiselect.hasMerged) const DeleteLocalActionButton(source: ActionSource.timeline), + if (multiselect.hasLocal) const UploadActionButton(source: ActionSource.timeline), ], + slivers: multiselect.hasRemote + ? [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), + ] + : [], ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart index 2ad0fe7485..b1e87dfaea 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { actions: [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), - UploadActionButton(), + UploadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart index 7f82f750f7..a644e6a035 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; diff --git a/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart new file mode 100644 index 0000000000..ac3772a02b --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +class MapBottomSheet extends StatelessWidget { + const MapBottomSheet({super.key}); + + @override + Widget build(BuildContext context) { + return BaseBottomSheet( + initialChildSize: 0.25, + maxChildSize: 0.9, + shouldCloseOnMinExtent: false, + resizeOnScroll: false, + actions: [], + backgroundColor: context.themeData.colorScheme.surface, + slivers: [const SliverFillRemaining(hasScrollBody: false, child: _ScopedMapTimeline())], + ); + } +} + +class _ScopedMapTimeline extends StatelessWidget { + const _ScopedMapTimeline(); + + @override + Widget build(BuildContext context) { + // TODO: this causes the timeline to switch to flicker to "loading" state and back. This is both janky and inefficient. + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } + + final bounds = ref.watch(mapStateProvider).bounds; + final timelineService = ref.watch(timelineFactoryProvider).map(user.id, bounds); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: const Timeline(appBar: null, bottomSheet: null, withScrubber: false), + ); + } +} diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index cfb6fe4f1a..0ab419a56b 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_location_action_button.widget.dart'; @@ -15,25 +17,77 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class RemoteAlbumBottomSheet extends ConsumerWidget { +class RemoteAlbumBottomSheet extends ConsumerStatefulWidget { final RemoteAlbum album; const RemoteAlbumBottomSheet({super.key, required this.album}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _RemoteAlbumBottomSheetState(); +} + +class _RemoteAlbumBottomSheetState extends ConsumerState { + late DraggableScrollableController sheetController; + + @override + void initState() { + super.initState(); + sheetController = DraggableScrollableController(); + } + + @override + void dispose() { + sheetController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + + Future addAssetsToAlbum(RemoteAlbum album) async { + final selectedAssets = multiselect.selectedAssets; + if (selectedAssets.isEmpty) { + return; + } + + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); + + if (addedCount != selectedAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_already_exists'.t(context: context, args: {"album": album.name}), + ); + } else { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_added'.t(context: context, args: {"album": album.name}), + ); + } + + ref.read(multiSelectProvider.notifier).reset(); + } + + Future onKeyboardExpand() { + return sheetController.animateTo(0.85, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut); + } return BaseBottomSheet( - initialChildSize: 0.25, - maxChildSize: 0.4, + controller: sheetController, + initialChildSize: 0.22, + minChildSize: 0.22, + maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ const ShareActionButton(source: ActionSource.timeline), @@ -44,24 +98,22 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), - const StackActionButton(source: ActionSource.timeline), + const MoveToLockFolderActionButton(source: ActionSource.timeline), + if (multiselect.selectedAssets.length > 1) const StackActionButton(source: ActionSource.timeline), + if (multiselect.hasStacked) const UnStackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton( - source: ActionSource.timeline, - albumId: album.id, - ), + RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: widget.album.id), + ], + slivers: [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum, onKeyboardExpanded: onKeyboardExpand), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart new file mode 100644 index 0000000000..9f8216c4ed --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart'; + +class TrashBottomBar extends ConsumerWidget { + const TrashBottomBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SafeArea( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + height: 64, + child: Container( + color: context.themeData.canvasColor, + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DeleteTrashActionButton(source: ActionSource.timeline), + RestoreTrashActionButton(source: ActionSource.timeline), + ], + ), + ), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index 970bb581cf..810340aeb8 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -1,24 +1,113 @@ +import 'package:async/async.dart'; import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/setting.service.dart'; +import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:logging/logging.dart'; -ImageProvider getFullImageProvider( - BaseAsset asset, { - Size size = const Size(1080, 1920), -}) { +abstract class CancellableImageProvider extends ImageProvider { + void cancel(); +} + +mixin CancellableImageProviderMixin on CancellableImageProvider { + static final _log = Logger('CancellableImageProviderMixin'); + + bool isCancelled = false; + ImageRequest? request; + CancelableOperation? cachedOperation; + + ImageInfo? getInitialImage(CancellableImageProvider provider) { + final completer = CancelableCompleter(onCancel: provider.cancel); + final cachedStream = provider.resolve(const ImageConfiguration()); + ImageInfo? cachedImage; + final listener = ImageStreamListener((image, synchronousCall) { + if (synchronousCall) { + cachedImage = image; + } + + if (!completer.isCompleted) { + completer.complete(image); + } + }, onError: completer.completeError); + + cachedStream.addListener(listener); + if (cachedImage != null) { + cachedStream.removeListener(listener); + return cachedImage; + } + + completer.operation.valueOrCancellation().whenComplete(() { + cachedStream.removeListener(listener); + cachedOperation = null; + }); + cachedOperation = completer.operation; + return null; + } + + Stream loadRequest(ImageRequest request, ImageDecoderCallback decode) async* { + if (isCancelled) { + this.request = null; + evict(); + return; + } + + try { + final image = await request.load(decode); + if (image == null || isCancelled) { + evict(); + return; + } + yield image; + } finally { + this.request = null; + } + } + + Stream initialImageStream() async* { + final cachedOperation = this.cachedOperation; + if (cachedOperation == null) { + return; + } + + try { + final cachedImage = await cachedOperation.valueOrCancellation(); + if (cachedImage != null && !isCancelled) { + yield cachedImage; + } + } catch (e, stack) { + _log.severe('Error loading initial image', e, stack); + } finally { + this.cachedOperation = null; + } + } + + @override + void cancel() { + isCancelled = true; + final request = this.request; + if (request != null) { + this.request = null; + request.cancel(); + } + + final operation = cachedOperation; + if (operation != null) { + cachedOperation = null; + operation.cancel(); + } + } +} + +ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) { // Create new provider and cache it final ImageProvider provider; if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - provider = LocalFullImageProvider( - id: id, - name: asset.name, - size: size, - type: asset.type, - ); + provider = LocalFullImageProvider(id: id, size: size, assetType: asset.type); } else { final String assetId; if (asset is LocalAsset && asset.hasRemote) { @@ -34,42 +123,15 @@ ImageProvider getFullImageProvider( return provider; } -ImageProvider getThumbnailImageProvider({ - BaseAsset? asset, - String? remoteId, - Size size = const Size.square(256), -}) { - assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); - - if (remoteId != null) { - return RemoteThumbProvider(assetId: remoteId); - } - - if (_shouldUseLocalAsset(asset!)) { +ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnailResolution}) { + if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - return LocalThumbProvider( - id: id, - updatedAt: asset.updatedAt, - name: asset.name, - size: size, - ); + return LocalThumbProvider(id: id, size: size, assetType: asset.type); } - final String assetId; - if (asset is LocalAsset && asset.hasRemote) { - assetId = asset.remoteId!; - } else if (asset is RemoteAsset) { - assetId = asset.id; - } else { - throw ArgumentError("Unsupported asset type: ${asset.runtimeType}"); - } - - return RemoteThumbProvider(assetId: assetId); + final assetId = asset is RemoteAsset ? asset.id : (asset as LocalAsset).remoteId; + return assetId != null ? RemoteThumbProvider(assetId: assetId) : null; } bool _shouldUseLocalAsset(BaseAsset asset) => - asset.hasLocal && - (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); + asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); diff --git a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart index dcf0f28527..b519da33c3 100644 --- a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; class LocalAlbumThumbnail extends ConsumerWidget { - const LocalAlbumThumbnail({ - super.key, - required this.albumId, - }); + const LocalAlbumThumbnail({super.key, required this.albumId}); final String albumId; @override @@ -21,34 +18,21 @@ class LocalAlbumThumbnail extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: Icon( - Icons.collections, - size: 24, - color: context.primaryColor, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: Icon(Icons.collections, size: 24, color: context.primaryColor), ); } return ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Thumbnail( - asset: data, - ), + child: Thumbnail.fromAsset(asset: data), ); }, error: (error, stack) { return const Icon(Icons.error, size: 24); }, - loading: () => const SizedBox( - width: 24, - height: 24, - child: Center(child: CircularProgressIndicator()), - ), + loading: () => const SizedBox(width: 24, height: 24, child: Center(child: CircularProgressIndicator())), ); } } diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 65311de48a..f90961ea5a 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -1,38 +1,23 @@ import 'dart:async'; -import 'dart:io'; import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/setting.model.dart'; -import 'package:immich_mobile/domain/services/setting.service.dart'; -import 'package:immich_mobile/infrastructure/repositories/asset_media.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; -import 'package:immich_mobile/providers/image/cache/thumbnail_image_cache_manager.dart'; -import 'package:immich_mobile/providers/image/exceptions/image_loading_exception.dart'; -import 'package:logging/logging.dart'; - -class LocalThumbProvider extends ImageProvider { - final AssetMediaRepository _assetMediaRepository = - const AssetMediaRepository(); - final CacheManager? cacheManager; +class LocalThumbProvider extends CancellableImageProvider + with CancellableImageProviderMixin { final String id; - final DateTime updatedAt; - final String name; final Size size; + final AssetType assetType; - const LocalThumbProvider({ - required this.id, - required this.updatedAt, - required this.name, - this.size = const Size.square(kTimelineFixedTileExtent), - this.cacheManager, - }); + LocalThumbProvider({required this.id, required this.assetType, this.size = kThumbnailResolution}); @override Future obtainKey(ImageConfiguration configuration) { @@ -40,84 +25,42 @@ class LocalThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalThumbProvider key, - ImageDecoderCallback decode, - ) { - final cache = cacheManager ?? ThumbnailImageCacheManager(); - return MultiFrameImageStreamCompleter( - codec: _codec(key, cache, decode), - scale: 1.0, + ImageStreamCompleter loadImage(LocalThumbProvider key, ImageDecoderCallback decode) { + return OneFramePlaceholderImageStreamCompleter( + _codec(key, decode), informationCollector: () => [ - DiagnosticsProperty('Image provider', this), DiagnosticsProperty('Id', key.id), - DiagnosticsProperty('Updated at', key.updatedAt), - DiagnosticsProperty('Name', key.name), DiagnosticsProperty('Size', key.size), ], + onDispose: cancel, ); } - Future _codec( - LocalThumbProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async { - final cacheKey = - '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}'; - - final fileFromCache = await cache.getFileFromCache(cacheKey); - if (fileFromCache != null) { - try { - final buffer = - await ImmutableBuffer.fromFilePath(fileFromCache.file.path); - return decode(buffer); - } catch (_) {} - } - - final thumbnailBytes = - await _assetMediaRepository.getThumbnail(key.id, size: key.size); - if (thumbnailBytes == null) { - PaintingBinding.instance.imageCache.evict(key); - throw StateError( - "Loading thumb for local photo ${key.name} failed", - ); - } - - final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); - unawaited(cache.putFile(cacheKey, thumbnailBytes)); - return decode(buffer); + Stream _codec(LocalThumbProvider key, ImageDecoderCallback decode) { + final request = this.request = LocalImageRequest(localId: key.id, size: key.size, assetType: key.assetType); + return loadRequest(request, decode); } @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is LocalThumbProvider) { - return id == other.id && updatedAt == other.updatedAt; + return id == other.id; } return false; } @override - int get hashCode => id.hashCode ^ updatedAt.hashCode; + int get hashCode => id.hashCode; } -class LocalFullImageProvider extends ImageProvider { - final AssetMediaRepository _assetMediaRepository = - const AssetMediaRepository(); - final StorageRepository _storageRepository = const StorageRepository(); - +class LocalFullImageProvider extends CancellableImageProvider + with CancellableImageProviderMixin { final String id; - final String name; final Size size; - final AssetType type; + final AssetType assetType; - const LocalFullImageProvider({ - required this.id, - required this.name, - required this.size, - required this.type, - }); + LocalFullImageProvider({required this.id, required this.assetType, required this.size}); @override Future obtainKey(ImageConfiguration configuration) { @@ -125,128 +68,59 @@ class LocalFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) { - return MultiImageStreamCompleter( - codec: _codec(key, decode), - scale: 1.0, - informationCollector: () sync* { - yield ErrorDescription(name); - }, + ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) { + return OneFramePlaceholderImageStreamCompleter( + _codec(key, decode), + initialImage: getInitialImage(LocalThumbProvider(id: key.id, assetType: key.assetType)), + informationCollector: () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Id', key.id), + DiagnosticsProperty('Size', key.size), + ], + onDispose: cancel, ); } - // Streams in each stage of the image as we ask for it - Stream _codec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { - try { - switch (key.type) { - case AssetType.image: - yield* _decodeProgressive(key, decode); - break; - case AssetType.video: - final codec = await _getThumbnailCodec(key, decode); - if (codec == null) { - throw StateError("Failed to load preview for ${key.name}"); - } - yield codec; - break; - case AssetType.other: - case AssetType.audio: - throw StateError('Unsupported asset type ${key.type}'); - } - } catch (error, stack) { - Logger('ImmichLocalImageProvider') - .severe('Error loading local image ${key.name}', error, stack); - throw const ImageLoadingException( - 'Could not load image from local storage', - ); - } - } + Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { + yield* initialImageStream(); - Future _getThumbnailCodec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async { - final thumbBytes = - await _assetMediaRepository.getThumbnail(key.id, size: key.size); - if (thumbBytes == null) { - return null; - } - final buffer = await ImmutableBuffer.fromUint8List(thumbBytes); - return decode(buffer); - } - - Stream _decodeProgressive( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { - final file = await _storageRepository.getFileForAsset(key.id); - if (file == null) { - throw StateError("Opening file for asset ${key.name} failed"); - } - - final fileSize = await file.length(); - final devicePixelRatio = - PlatformDispatcher.instance.views.first.devicePixelRatio; - final isLargeFile = fileSize > 20 * 1024 * 1024; // 20MB - final isHEIC = file.path.toLowerCase().contains(RegExp(r'\.(heic|heif)$')); - final isProgressive = isLargeFile || (isHEIC && !Platform.isIOS); - - if (isProgressive) { - try { - final progressiveMultiplier = devicePixelRatio >= 3.0 ? 1.3 : 1.2; - final size = Size( - (key.size.width * progressiveMultiplier).clamp(256, 1024), - (key.size.height * progressiveMultiplier).clamp(256, 1024), - ); - final mediumThumb = - await _assetMediaRepository.getThumbnail(key.id, size: size); - if (mediumThumb != null) { - final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb); - yield await decode(mediumBuffer); - } - } catch (_) {} - } - - // Load original only when the file is smaller or if the user wants to load original images - // Or load a slightly larger image for progressive loading - if (isProgressive && !(AppSetting.get(Setting.loadOriginal))) { - final progressiveMultiplier = devicePixelRatio >= 3.0 ? 2.0 : 1.6; - final size = Size( - (key.size.width * progressiveMultiplier).clamp(512, 2048), - (key.size.height * progressiveMultiplier).clamp(512, 2048), - ); - final highThumb = - await _assetMediaRepository.getThumbnail(key.id, size: size); - if (highThumb != null) { - final highBuffer = await ImmutableBuffer.fromUint8List(highThumb); - yield await decode(highBuffer); - } + if (isCancelled) { + evict(); return; } - final buffer = await ImmutableBuffer.fromFilePath(file.path); - yield await decode(buffer); + final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio; + var request = this.request = LocalImageRequest( + localId: key.id, + size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio), + assetType: key.assetType, + ); + + yield* loadRequest(request, decode); + + if (!Store.get(StoreKey.loadOriginal, false)) { + return; + } + + if (isCancelled) { + evict(); + return; + } + + request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero); + + yield* loadRequest(request, decode); } @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is LocalFullImageProvider) { - return id == other.id && - size == other.size && - type == other.type && - name == other.name; + return id == other.id && size == other.size; } return false; } @override - int get hashCode => - id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode; + int get hashCode => id.hashCode ^ size.hashCode; } diff --git a/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart b/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart new file mode 100644 index 0000000000..6d549d4fda --- /dev/null +++ b/mobile/lib/presentation/widgets/images/one_frame_multi_image_stream_completer.dart @@ -0,0 +1,51 @@ +// The below code is adapted from cached_network_image package's +// MultiImageStreamCompleter to better suit one-frame image loading. +// In particular, it allows providing an initial image to emit synchronously. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; + +/// An ImageStreamCompleter with support for loading multiple images. +class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter { + void Function()? _onDispose; + + /// The constructor to create an OneFramePlaceholderImageStreamCompleter. The [images] + /// should be the primary images to display (typically asynchronously as they load). + /// The [initialImage] is an optional image that will be emitted synchronously + /// until the first stream image is completed, useful as a thumbnail or placeholder. + OneFramePlaceholderImageStreamCompleter( + Stream images, { + ImageInfo? initialImage, + InformationCollector? informationCollector, + void Function()? onDispose, + }) { + if (initialImage != null) { + setImage(initialImage); + } + _onDispose = onDispose; + images.listen( + setImage, + onError: (Object error, StackTrace stack) { + reportError( + context: ErrorDescription('resolving a single-frame image stream'), + exception: error, + stack: stack, + informationCollector: informationCollector, + silent: true, + ); + }, + ); + } + + @override + void onDisposed() { + final onDispose = _onDispose; + if (onDispose != null) { + _onDispose = null; + onDispose(); + } + super.onDisposed(); + } +} diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart index 14d13a08d8..75fd186c8a 100644 --- a/mobile/lib/presentation/widgets/images/remote_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -1,24 +1,22 @@ import 'dart:async'; -import 'dart:ui'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/services/setting.service.dart'; -import 'package:immich_mobile/providers/image/cache/image_loader.dart'; +import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart'; +import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; -class RemoteThumbProvider extends ImageProvider { +class RemoteThumbProvider extends CancellableImageProvider + with CancellableImageProviderMixin { + static final cacheManager = RemoteThumbnailCacheManager(); final String assetId; - final CacheManager? cacheManager; - const RemoteThumbProvider({ - required this.assetId, - this.cacheManager, - }); + RemoteThumbProvider({required this.assetId}); @override Future obtainKey(ImageConfiguration configuration) { @@ -26,39 +24,24 @@ class RemoteThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteThumbProvider key, - ImageDecoderCallback decode, - ) { - final cache = cacheManager ?? RemoteImageCacheManager(); - final chunkController = StreamController(); - return MultiFrameImageStreamCompleter( - codec: _codec(key, cache, decode, chunkController), - scale: 1.0, - chunkEvents: chunkController.stream, + ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) { + return OneFramePlaceholderImageStreamCompleter( + _codec(key, decode), informationCollector: () => [ DiagnosticsProperty('Image provider', this), DiagnosticsProperty('Asset Id', key.assetId), ], + onDispose: cancel, ); } - Future _codec( - RemoteThumbProvider key, - CacheManager cache, - ImageDecoderCallback decode, - StreamController chunkController, - ) async { - final preview = getThumbnailUrlForRemoteId( - key.assetId, + Stream _codec(RemoteThumbProvider key, ImageDecoderCallback decode) { + final request = this.request = RemoteImageRequest( + uri: getThumbnailUrlForRemoteId(key.assetId), + headers: ApiService.getRequestHeaders(), + cacheManager: cacheManager, ); - - return ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - chunkEvents: chunkController, - ).whenComplete(chunkController.close); + return loadRequest(request, decode); } @override @@ -75,14 +58,12 @@ class RemoteThumbProvider extends ImageProvider { int get hashCode => assetId.hashCode; } -class RemoteFullImageProvider extends ImageProvider { +class RemoteFullImageProvider extends CancellableImageProvider + with CancellableImageProviderMixin { + static final cacheManager = RemoteThumbnailCacheManager(); final String assetId; - final CacheManager? cacheManager; - const RemoteFullImageProvider({ - required this.assetId, - this.cacheManager, - }); + RemoteFullImageProvider({required this.assetId}); @override Future obtainKey(ImageConfiguration configuration) { @@ -90,41 +71,43 @@ class RemoteFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteFullImageProvider key, - ImageDecoderCallback decode, - ) { - final cache = cacheManager ?? RemoteImageCacheManager(); - final chunkEvents = StreamController(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode, chunkEvents), - scale: 1.0, - chunkEvents: chunkEvents.stream, + ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) { + return OneFramePlaceholderImageStreamCompleter( + _codec(key, decode), + initialImage: getInitialImage(RemoteThumbProvider(assetId: key.assetId)), + informationCollector: () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Asset Id', key.assetId), + ], + onDispose: cancel, ); } - Stream _codec( - RemoteFullImageProvider key, - CacheManager cache, - ImageDecoderCallback decode, - StreamController chunkController, - ) async* { - yield await ImageLoader.loadImageFromCache( - getPreviewUrlForRemoteId(key.assetId), - cache: cache, - decode: decode, - chunkEvents: chunkController, + Stream _codec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* { + yield* initialImageStream(); + + if (isCancelled) { + evict(); + return; + } + + final headers = ApiService.getRequestHeaders(); + final request = this.request = RemoteImageRequest( + uri: getPreviewUrlForRemoteId(key.assetId), + headers: headers, + cacheManager: cacheManager, ); + yield* loadRequest(request, decode); + + if (isCancelled) { + evict(); + return; + } if (AppSetting.get(Setting.loadOriginal)) { - yield await ImageLoader.loadImageFromCache( - getOriginalUrlForRemoteId(key.assetId), - cache: cache, - decode: decode, - chunkEvents: chunkController, - ); + final request = this.request = RemoteImageRequest(uri: getOriginalUrlForRemoteId(key.assetId), headers: headers); + yield* loadRequest(request, decode); } - await chunkController.close(); } @override diff --git a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart index cd286a4cdf..fcd2fca72f 100644 --- a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart +++ b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart @@ -1,16 +1,14 @@ -import 'dart:convert' hide Codec; -import 'dart:ui'; - import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; -import 'package:thumbhash/thumbhash.dart'; +import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; -class ThumbHashProvider extends ImageProvider { +class ThumbHashProvider extends CancellableImageProvider + with CancellableImageProviderMixin { final String thumbHash; - const ThumbHashProvider({ - required this.thumbHash, - }); + ThumbHashProvider({required this.thumbHash}); @override Future obtainKey(ImageConfiguration configuration) { @@ -18,22 +16,13 @@ class ThumbHashProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadCodec(key, decode), - scale: 1.0, - ); + ImageStreamCompleter loadImage(ThumbHashProvider key, ImageDecoderCallback decode) { + return OneFramePlaceholderImageStreamCompleter(_loadCodec(key, decode), onDispose: cancel); } - Future _loadCodec( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) async { - final image = thumbHashToRGBA(base64Decode(key.thumbHash)); - return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image))); + Stream _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) { + final request = this.request = ThumbhashImageRequest(thumbhash: key.thumbHash); + return loadRequest(request, decode); } @override diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index f54c32dac1..92b1bb2544 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -1,88 +1,379 @@ +import 'dart:ui' as ui; + import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart'; -import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart'; -import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; import 'package:logging/logging.dart'; -import 'package:octo_image/octo_image.dart'; -class Thumbnail extends StatelessWidget { - const Thumbnail({ - this.asset, - this.remoteId, - this.size = const Size.square(256), - this.fit = BoxFit.cover, - super.key, - }) : assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); +final log = Logger('ThumbnailWidget'); - final BaseAsset? asset; - final String? remoteId; - final Size size; +enum ThumbhashMode { enabled, disabled, only } + +class Thumbnail extends StatefulWidget { + final ImageProvider? imageProvider; + final ImageProvider? thumbhashProvider; final BoxFit fit; + const Thumbnail({this.imageProvider, this.fit = BoxFit.cover, this.thumbhashProvider, super.key}); + + Thumbnail.remote({required String remoteId, this.fit = BoxFit.cover, Size size = kThumbnailResolution, super.key}) + : imageProvider = RemoteThumbProvider(assetId: remoteId), + thumbhashProvider = null; + + Thumbnail.fromAsset({ + required BaseAsset? asset, + this.fit = BoxFit.cover, + + /// The logical UI size of the thumbnail. This is only used to determine the ideal image resolution and does not affect the widget size. + Size size = kThumbnailResolution, + super.key, + }) : thumbhashProvider = switch (asset) { + RemoteAsset() when asset.thumbHash != null && asset.localId == null => ThumbHashProvider( + thumbHash: asset.thumbHash!, + ), + _ => null, + }, + imageProvider = asset == null ? null : getThumbnailImageProvider(asset, size: size); + + @override + State createState() => _ThumbnailState(); +} + +class _ThumbnailState extends State with SingleTickerProviderStateMixin { + ui.Image? _providerImage; + ui.Image? _previousImage; + + late AnimationController _fadeController; + late Animation _fadeAnimation; + + ImageStream? _imageStream; + ImageStreamListener? _imageStreamListener; + ImageStream? _thumbhashStream; + ImageStreamListener? _thumbhashStreamListener; + + static final _gradientCache = {}; + + @override + void initState() { + super.initState(); + _fadeController = AnimationController(duration: const Duration(milliseconds: 100), vsync: this); + _fadeAnimation = CurvedAnimation(parent: _fadeController, curve: Curves.easeOut); + _fadeController.addStatusListener(_onAnimationStatusChanged); + _loadImage(); + } + + void _onAnimationStatusChanged(AnimationStatus status) { + if (status == AnimationStatus.completed) { + _previousImage?.dispose(); + _previousImage = null; + } + } + + void _loadFromThumbhashProvider() { + _stopListeningToThumbhashStream(); + final thumbhashProvider = widget.thumbhashProvider; + if (thumbhashProvider == null || _providerImage != null) return; + + final thumbhashStream = _thumbhashStream = thumbhashProvider.resolve(ImageConfiguration.empty); + final thumbhashStreamListener = _thumbhashStreamListener = ImageStreamListener( + (ImageInfo imageInfo, bool synchronousCall) { + _stopListeningToThumbhashStream(); + if (!mounted || _providerImage != null) { + imageInfo.dispose(); + return; + } + _fadeController.value = 1.0; + setState(() { + _providerImage = imageInfo.image; + }); + }, + onError: (exception, stackTrace) { + log.severe('Error loading thumbhash', exception, stackTrace); + _stopListeningToThumbhashStream(); + }, + ); + thumbhashStream.addListener(thumbhashStreamListener); + } + + void _loadFromImageProvider() { + _stopListeningToImageStream(); + final imageProvider = widget.imageProvider; + if (imageProvider == null) return; + + final imageStream = _imageStream = imageProvider.resolve(ImageConfiguration.empty); + final imageStreamListener = _imageStreamListener = ImageStreamListener( + (ImageInfo imageInfo, bool synchronousCall) { + _stopListeningToThumbhashStream(); + if (!mounted) { + imageInfo.dispose(); + return; + } + + if (_providerImage == imageInfo.image) { + return; + } + + if ((synchronousCall && _providerImage == null) || !_isVisible()) { + _fadeController.value = 1.0; + } else if (_fadeController.isAnimating) { + _fadeController.forward(); + } else { + _fadeController.forward(from: 0.0); + } + + setState(() { + _previousImage?.dispose(); + if (_providerImage != null) { + _previousImage = _providerImage; + } else { + _previousImage = null; + } + _providerImage = imageInfo.image; + }); + }, + onError: (exception, stackTrace) { + log.severe('Error loading image: $exception', exception, stackTrace); + _stopListeningToImageStream(); + }, + ); + imageStream.addListener(imageStreamListener); + } + + void _stopListeningToImageStream() { + if (_imageStreamListener != null && _imageStream != null) { + _imageStream!.removeListener(_imageStreamListener!); + } + _imageStream = null; + _imageStreamListener = null; + } + + void _stopListeningToThumbhashStream() { + if (_thumbhashStreamListener != null && _thumbhashStream != null) { + _thumbhashStream!.removeListener(_thumbhashStreamListener!); + } + _thumbhashStream = null; + _thumbhashStreamListener = null; + } + + void _stopListeningToStream() { + _stopListeningToImageStream(); + _stopListeningToThumbhashStream(); + } + + @override + void didUpdateWidget(Thumbnail oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.imageProvider != oldWidget.imageProvider) { + if (_fadeController.isAnimating) { + _fadeController.stop(); + _previousImage?.dispose(); + _previousImage = null; + } + _loadFromImageProvider(); + } + + if (_providerImage == null && oldWidget.thumbhashProvider != widget.thumbhashProvider) { + _loadFromThumbhashProvider(); + } + } + + @override + void reassemble() { + super.reassemble(); + _loadImage(); + } + + void _loadImage() { + _loadFromImageProvider(); + _loadFromThumbhashProvider(); + } + + bool _isVisible() { + final renderObject = context.findRenderObject() as RenderBox?; + if (renderObject == null || !renderObject.attached) return false; + + final topLeft = renderObject.localToGlobal(Offset.zero); + final bottomRight = renderObject.localToGlobal(Offset(renderObject.size.width, renderObject.size.height)); + return topLeft.dy < context.height && bottomRight.dy > 0; + } + @override Widget build(BuildContext context) { - final thumbHash = - asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null; - final provider = - getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size); - - return OctoImage.fromSet( - image: provider, - octoSet: OctoSet( - placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit), - errorBuilder: _blurHashErrorBuilder( - thumbHash, - provider: provider, - fit: fit, - asset: asset, - ), - ), - fadeOutDuration: const Duration(milliseconds: 100), - fadeInDuration: Duration.zero, - width: size.width, - height: size.height, - fit: fit, - placeholderFadeInDuration: Duration.zero, + final colorScheme = context.colorScheme; + final gradient = _gradientCache[colorScheme] ??= LinearGradient( + colors: [colorScheme.surfaceContainer, colorScheme.surfaceContainer.darken(amount: .1)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, ); + + return AnimatedBuilder( + animation: _fadeAnimation, + builder: (context, child) { + return _ThumbnailLeaf( + image: _providerImage, + previousImage: _previousImage, + fadeValue: _fadeAnimation.value, + fit: widget.fit, + placeholderGradient: gradient, + ); + }, + ); + } + + @override + void dispose() { + final imageProvider = widget.imageProvider; + if (imageProvider is CancellableImageProvider) { + imageProvider.cancel(); + } + + final thumbhashProvider = widget.thumbhashProvider; + if (thumbhashProvider is CancellableImageProvider) { + thumbhashProvider.cancel(); + } + + _fadeController.removeStatusListener(_onAnimationStatusChanged); + _fadeController.dispose(); + _stopListeningToStream(); + _providerImage?.dispose(); + _previousImage?.dispose(); + super.dispose(); } } -OctoPlaceholderBuilder _blurHashPlaceholderBuilder( - String? thumbHash, { - BoxFit? fit, -}) { - return (context) => thumbHash == null - ? const ThumbnailPlaceholder() - : FadeInPlaceholderImage( - placeholder: const ThumbnailPlaceholder(), - image: ThumbHashProvider(thumbHash: thumbHash), - fit: fit ?? BoxFit.cover, - ); +class _ThumbnailLeaf extends LeafRenderObjectWidget { + final ui.Image? image; + final ui.Image? previousImage; + final double fadeValue; + final BoxFit fit; + final Gradient placeholderGradient; + + const _ThumbnailLeaf({ + required this.image, + required this.previousImage, + required this.fadeValue, + required this.fit, + required this.placeholderGradient, + }); + + @override + RenderObject createRenderObject(BuildContext context) { + return _ThumbnailRenderBox( + image: image, + previousImage: previousImage, + fadeValue: fadeValue, + fit: fit, + placeholderGradient: placeholderGradient, + ); + } + + @override + void updateRenderObject(BuildContext context, _ThumbnailRenderBox renderObject) { + renderObject + ..image = image + ..previousImage = previousImage + ..fadeValue = fadeValue + ..fit = fit + ..placeholderGradient = placeholderGradient; + } } -OctoErrorBuilder _blurHashErrorBuilder( - String? blurhash, { - BaseAsset? asset, - ImageProvider? provider, - BoxFit? fit, -}) => - (context, e, s) { - Logger("ImThumbnail") - .warning("Error loading thumbnail for ${asset?.name}", e, s); - provider?.evict(); - return Stack( - alignment: Alignment.center, - children: [ - _blurHashPlaceholderBuilder(blurhash, fit: fit)(context), - const Opacity( - opacity: 0.75, - child: Icon(Icons.error_outline_rounded), - ), - ], +class _ThumbnailRenderBox extends RenderBox { + ui.Image? _image; + ui.Image? _previousImage; + double _fadeValue; + BoxFit _fit; + Gradient _placeholderGradient; + + @override + bool isRepaintBoundary = true; + + _ThumbnailRenderBox({ + required ui.Image? image, + required ui.Image? previousImage, + required double fadeValue, + required BoxFit fit, + required Gradient placeholderGradient, + }) : _image = image, + _previousImage = previousImage, + _fadeValue = fadeValue, + _fit = fit, + _placeholderGradient = placeholderGradient; + + @override + void paint(PaintingContext context, Offset offset) { + final rect = offset & size; + final canvas = context.canvas; + + if (_previousImage != null && _fadeValue < 1.0) { + paintImage( + canvas: canvas, + rect: rect, + image: _previousImage!, + fit: _fit, + filterQuality: FilterQuality.low, + opacity: 1.0, ); - }; + } else if (_image == null || _fadeValue < 1.0) { + final paint = Paint()..shader = _placeholderGradient.createShader(rect); + canvas.drawRect(rect, paint); + } + + if (_image != null) { + paintImage( + canvas: canvas, + rect: rect, + image: _image!, + fit: _fit, + filterQuality: FilterQuality.low, + opacity: _fadeValue, + ); + } + } + + @override + void performLayout() { + size = constraints.biggest; + } + + set image(ui.Image? value) { + if (_image != value) { + _image = value; + markNeedsPaint(); + } + } + + set previousImage(ui.Image? value) { + if (_previousImage != value) { + _previousImage = value; + markNeedsPaint(); + } + } + + set fadeValue(double value) { + if (_fadeValue != value) { + _fadeValue = value; + markNeedsPaint(); + } + } + + set fit(BoxFit value) { + if (_fit != value) { + _fit = value; + markNeedsPaint(); + } + } + + set placeholderGradient(Gradient value) { + if (_placeholderGradient != value) { + _placeholderGradient = value; + markNeedsPaint(); + } + } +} diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629f..5359391261 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -2,59 +2,60 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/duration_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class ThumbnailTile extends ConsumerWidget { const ThumbnailTile( this.asset, { - this.size = const Size.square(256), + this.size = kThumbnailResolution, this.fit = BoxFit.cover, - this.showStorageIndicator = true, + this.showStorageIndicator = false, this.lockSelection = false, + this.heroOffset, super.key, }); - final BaseAsset asset; + final BaseAsset? asset; final Size size; final BoxFit fit; final bool showStorageIndicator; final bool lockSelection; + final int? heroOffset; @override Widget build(BuildContext context, WidgetRef ref) { - final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + final asset = this.asset; + final heroIndex = heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; final assetContainerColor = context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75); final isSelected = ref.watch( - multiSelectProvider.select( - (multiselect) => multiselect.selectedAssets.contains(asset), - ), + multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); final borderStyle = lockSelection ? BoxDecoration( color: context.colorScheme.surfaceContainerHighest, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 6, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6), ) : isSelected - ? BoxDecoration( - color: assetContainerColor, - border: Border.all(color: assetContainerColor, width: 6), - ) - : const BoxDecoration(); + ? BoxDecoration( + color: assetContainerColor, + border: Border.all(color: assetContainerColor, width: 6), + ) + : const BoxDecoration(); - final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + final bool storageIndicator = + ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && showStorageIndicator; return Stack( children: [ @@ -70,60 +71,40 @@ class ThumbnailTile extends ConsumerWidget { children: [ Positioned.fill( child: Hero( - tag: '${asset.heroTag}_$heroOffset', - child: Thumbnail( - asset: asset, - fit: fit, - size: size, - ), + tag: '${asset?.heroTag ?? ''}_$heroIndex', + child: Thumbnail.fromAsset(asset: asset, size: size), ), ), - if (hasStack) + if (asset != null) Align( alignment: Alignment.topRight, - child: Padding( - padding: EdgeInsets.only( - right: 10.0, - top: asset.isVideo ? 24.0 : 6.0, - ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), - ), + child: _AssetTypeIcons(asset: asset), ), - if (asset.isVideo) - Align( - alignment: Alignment.topRight, - child: Padding( - padding: const EdgeInsets.only(right: 10.0, top: 6.0), - child: _VideoIndicator(asset.duration), - ), - ), - if (showStorageIndicator) + if (storageIndicator && asset != null) switch (asset.storage) { AssetState.local => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_off_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_off_outlined), ), + ), AssetState.remote => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_outlined), ), + ), AssetState.merged => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_done_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_done_outlined), ), + ), }, - if (asset.isFavorite) + if (asset != null && asset.isFavorite) const Align( alignment: Alignment.bottomLeft, child: Padding( @@ -143,9 +124,7 @@ class ThumbnailTile extends ConsumerWidget { child: _SelectionIndicator( isSelected: isSelected, isLocked: lockSelection, - color: lockSelection - ? context.colorScheme.surfaceContainerHighest - : assetContainerColor, + color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor, ), ), ), @@ -159,79 +138,26 @@ class _SelectionIndicator extends StatelessWidget { final bool isLocked; final Color? color; - const _SelectionIndicator({ - required this.isSelected, - required this.isLocked, - this.color, - }); + const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); @override Widget build(BuildContext context) { if (isLocked) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: const Icon( - Icons.check_circle_rounded, - color: Colors.grey, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: const Icon(Icons.check_circle_rounded, color: Colors.grey), ); } else if (isSelected) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } else { - return const Icon( - Icons.circle_outlined, - color: Colors.white, - ); + return const Icon(Icons.circle_outlined, color: Colors.white); } } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); @@ -251,12 +177,7 @@ class _VideoIndicator extends StatelessWidget { color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6))], ), ), const _TileOverlayIcon(Icons.play_circle_outline_rounded), @@ -276,12 +197,37 @@ class _TileOverlayIcon extends StatelessWidget { icon, color: Colors.white, size: 16, - shadows: [ - const Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), + shadows: [const Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], + ); + } +} + +class _AssetTypeIcons extends StatelessWidget { + final BaseAsset asset; + + const _AssetTypeIcons({required this.asset}); + + @override + Widget build(BuildContext context) { + final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; + final isLivePhoto = asset is RemoteAsset && asset.livePhotoVideoId != null; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (asset.isVideo) + Padding(padding: const EdgeInsets.only(right: 10.0, top: 6.0), child: _VideoIndicator(asset.duration)), + if (hasStack) + const Padding( + padding: EdgeInsets.only(right: 10.0, top: 6.0), + child: _TileOverlayIcon(Icons.burst_mode_rounded), + ), + if (isLivePhoto) + const Padding( + padding: EdgeInsets.only(right: 10.0, top: 6.0), + child: _TileOverlayIcon(Icons.motion_photos_on_rounded), + ), ], ); } diff --git a/mobile/lib/presentation/widgets/map/map.state.dart b/mobile/lib/presentation/widgets/map/map.state.dart new file mode 100644 index 0000000000..b849f954ae --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map.state.dart @@ -0,0 +1,61 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/infrastructure/map.provider.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class MapState { + final LatLngBounds bounds; + + const MapState({required this.bounds}); + + @override + bool operator ==(covariant MapState other) { + return bounds == other.bounds; + } + + @override + int get hashCode => bounds.hashCode; + + MapState copyWith({LatLngBounds? bounds}) { + return MapState(bounds: bounds ?? this.bounds); + } +} + +class MapStateNotifier extends Notifier { + MapStateNotifier(); + + bool setBounds(LatLngBounds bounds) { + if (state.bounds == bounds) { + return false; + } + state = state.copyWith(bounds: bounds); + return true; + } + + @override + MapState build() => MapState( + // TODO: set default bounds + bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)), + ); +} + +// This provider watches the markers from the map service and serves the markers. +// It should be used only after the map service provider is overridden +final mapMarkerProvider = FutureProvider.family, LatLngBounds?>((ref, bounds) async { + final mapService = ref.watch(mapServiceProvider); + final markers = await mapService.getMarkers(bounds); + final features = List.filled(markers.length, const {}); + for (int i = 0; i < markers.length; i++) { + final marker = markers[i]; + features[i] = { + 'type': 'Feature', + 'id': marker.assetId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.location.longitude, marker.location.latitude], + }, + }; + } + return {'type': 'FeatureCollection', 'features': features}; +}, dependencies: [mapServiceProvider]); + +final mapStateProvider = NotifierProvider(MapStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/map/map.widget.dart b/mobile/lib/presentation/widgets/map/map.widget.dart new file mode 100644 index 0000000000..c1d5bf6464 --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map.widget.dart @@ -0,0 +1,250 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/map/map.state.dart'; +import 'package:immich_mobile/presentation/widgets/map/map_utils.dart'; +import 'package:immich_mobile/utils/async_mutex.dart'; +import 'package:immich_mobile/utils/debounce.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/map/map_theme_override.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class CustomSourceProperties implements SourceProperties { + final Map data; + const CustomSourceProperties({required this.data}); + + @override + Map toJson() { + return { + "type": "geojson", + "data": data, + // "cluster": true, + // "clusterRadius": 1, + // "clusterMinPoints": 5, + // "tolerance": 0.1, + }; + } +} + +class DriftMap extends ConsumerStatefulWidget { + final LatLng? initialLocation; + + const DriftMap({super.key, this.initialLocation}); + + @override + ConsumerState createState() => _DriftMapState(); +} + +class _DriftMapState extends ConsumerState { + MapLibreMapController? mapController; + final _reloadMutex = AsyncMutex(); + final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2)); + final ValueNotifier bottomSheetOffset = ValueNotifier(0.25); + + @override + void dispose() { + _debouncer.dispose(); + bottomSheetOffset.dispose(); + super.dispose(); + } + + void onMapCreated(MapLibreMapController controller) { + mapController = controller; + } + + Future onMapReady() async { + final controller = mapController; + if (controller == null) { + return; + } + + await controller.addSource( + MapUtils.defaultSourceId, + const CustomSourceProperties(data: {'type': 'FeatureCollection', 'features': []}), + ); + + if (Platform.isAndroid) { + await controller.addCircleLayer( + MapUtils.defaultSourceId, + MapUtils.defaultHeatMapLayerId, + const CircleLayerProperties( + circleRadius: 10, + circleColor: "rgba(150,86,34,0.7)", + circleBlur: 1.0, + circleOpacity: 0.7, + circleStrokeWidth: 0.1, + circleStrokeColor: "rgba(203,46,19,0.5)", + circleStrokeOpacity: 0.7, + ), + ); + } + + if (Platform.isIOS) { + await controller.addHeatmapLayer( + MapUtils.defaultSourceId, + MapUtils.defaultHeatMapLayerId, + MapUtils.defaultHeatmapLayerProperties, + ); + } + + _debouncer.run(setBounds); + controller.addListener(onMapMoved); + } + + void onMapMoved() { + if (mapController!.isCameraMoving || !mounted) { + return; + } + + _debouncer.run(setBounds); + } + + Future setBounds() async { + final controller = mapController; + if (controller == null || !mounted) { + return; + } + + final bounds = await controller.getVisibleRegion(); + _reloadMutex.run(() async { + if (mounted && ref.read(mapStateProvider.notifier).setBounds(bounds)) { + final markers = await ref.read(mapMarkerProvider(bounds).future); + await reloadMarkers(markers); + } + }); + } + + Future reloadMarkers(Map markers) async { + final controller = mapController; + if (controller == null || !mounted) { + return; + } + + await controller.setGeoJsonSource(MapUtils.defaultSourceId, markers); + } + + Future onZoomToLocation() async { + final (location, error) = await MapUtils.checkPermAndGetLocation(context: context); + if (error != null) { + if (error == LocationPermission.unableToDetermine && context.mounted) { + ImmichToast.show( + context: context, + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + msg: "map_cannot_get_user_location".t(context: context), + ); + } + return; + } + + final controller = mapController; + if (controller != null && location != null) { + controller.animateCamera( + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), MapUtils.mapZoomToAssetLevel), + duration: const Duration(milliseconds: 800), + ); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady), + _DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset), + _DynamicMyLocationButton(onZoomToLocation: onZoomToLocation, bottomSheetOffset: bottomSheetOffset), + ], + ); + } +} + +class _Map extends StatelessWidget { + final LatLng? initialLocation; + + const _Map({this.initialLocation, required this.onMapCreated, required this.onMapReady}); + + final MapCreatedCallback onMapCreated; + + final VoidCallback onMapReady; + + @override + Widget build(BuildContext context) { + final initialLocation = this.initialLocation; + return MapThemeOverride( + mapBuilder: (style) => style.widgetWhen( + onData: (style) => MapLibreMap( + initialCameraPosition: initialLocation == null + ? const CameraPosition(target: LatLng(0, 0), zoom: 0) + : CameraPosition(target: initialLocation, zoom: MapUtils.mapZoomToAssetLevel), + compassEnabled: false, + rotateGesturesEnabled: false, + styleString: style, + onMapCreated: onMapCreated, + onStyleLoadedCallback: onMapReady, + attributionButtonPosition: AttributionButtonPosition.topRight, + attributionButtonMargins: Platform.isIOS ? const Point(40, 12) : const Point(40, 72), + ), + ), + ); + } +} + +class _DynamicBottomSheet extends StatefulWidget { + final ValueNotifier bottomSheetOffset; + + const _DynamicBottomSheet({required this.bottomSheetOffset}); + + @override + State<_DynamicBottomSheet> createState() => _DynamicBottomSheetState(); +} + +class _DynamicBottomSheetState extends State<_DynamicBottomSheet> { + @override + Widget build(BuildContext context) { + return NotificationListener( + onNotification: (notification) { + widget.bottomSheetOffset.value = notification.extent; + return true; + }, + child: const MapBottomSheet(), + ); + } +} + +class _DynamicMyLocationButton extends StatelessWidget { + const _DynamicMyLocationButton({required this.onZoomToLocation, required this.bottomSheetOffset}); + + final VoidCallback onZoomToLocation; + final ValueNotifier bottomSheetOffset; + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: bottomSheetOffset, + builder: (context, offset, child) { + return Positioned( + right: 16, + bottom: context.height * (offset - 0.02) + context.padding.bottom, + child: AnimatedOpacity( + opacity: offset < 0.8 ? 1 : 0, + duration: const Duration(milliseconds: 150), + child: ElevatedButton( + onPressed: onZoomToLocation, + style: ElevatedButton.styleFrom(shape: const CircleBorder()), + child: const Icon(Icons.my_location), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/widgets/map/map_utils.dart b/mobile/lib/presentation/widgets/map/map_utils.dart new file mode 100644 index 0000000000..1c18fc48d6 --- /dev/null +++ b/mobile/lib/presentation/widgets/map/map_utils.dart @@ -0,0 +1,138 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:logging/logging.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; + +class MapUtils { + static final Logger _logger = Logger("MapUtils"); + + static const mapZoomToAssetLevel = 12.0; + static const defaultSourceId = 'asset-map-markers'; + static const defaultHeatMapLayerId = 'asset-heatmap-layer'; + static var markerCompleter = Completer()..complete(); + + static const defaultCircleLayerLayerProperties = CircleLayerProperties( + circleRadius: 10, + circleColor: "rgba(150,86,34,0.7)", + circleBlur: 1.0, + circleOpacity: 0.7, + circleStrokeWidth: 0.1, + circleStrokeColor: "rgba(203,46,19,0.5)", + circleStrokeOpacity: 0.7, + ); + + static const defaultHeatmapLayerProperties = HeatmapLayerProperties( + heatmapColor: [ + Expressions.interpolate, + ["linear"], + ["heatmap-density"], + 0.0, + "rgba(103,58,183,0.0)", + 0.3, + "rgb(103,58,183)", + 0.5, + "rgb(33,149,243)", + 0.7, + "rgb(76,175,79)", + 0.95, + "rgb(255,235,59)", + 1.0, + "rgb(255,86,34)", + ], + heatmapIntensity: [ + Expressions.interpolate, + ["linear"], + [Expressions.zoom], + 0, + 0.5, + 9, + 2, + ], + heatmapRadius: [ + Expressions.interpolate, + ["linear"], + [Expressions.zoom], + 0, + 4, + 4, + 8, + 9, + 16, + ], + heatmapOpacity: 0.7, + ); + + static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ + required BuildContext context, + bool silent = false, + }) async { + try { + bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled && !silent) { + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog(context)); + return (null, LocationPermission.deniedForever); + } + + LocationPermission permission = await Geolocator.checkPermission(); + bool shouldRequestPermission = false; + + if (permission == LocationPermission.denied && !silent) { + shouldRequestPermission = await showDialog( + context: context, + builder: (context) => _LocationPermissionDisabledDialog(context), + ); + if (shouldRequestPermission) { + permission = await Geolocator.requestPermission(); + } + } + + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { + // Open app settings only if you did not request for permission before + if (permission == LocationPermission.deniedForever && !shouldRequestPermission && !silent) { + await Geolocator.openAppSettings(); + } + return (null, LocationPermission.deniedForever); + } + + Position currentUserLocation = await Geolocator.getCurrentPosition( + locationSettings: const LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 0, + timeLimit: Duration(seconds: 5), + ), + ); + return (currentUserLocation, null); + } catch (error, stack) { + _logger.severe("Cannot get user's current location", error, stack); + return (null, LocationPermission.unableToDetermine); + } + } +} + +class _LocationServiceDisabledDialog extends ConfirmDialog { + _LocationServiceDisabledDialog(BuildContext context) + : super( + title: 'map_location_service_disabled_title'.t(context: context), + content: 'map_location_service_disabled_content'.t(context: context), + cancel: 'cancel'.t(context: context), + ok: 'yes'.t(context: context), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); +} + +class _LocationPermissionDisabledDialog extends ConfirmDialog { + _LocationPermissionDisabledDialog(BuildContext context) + : super( + title: 'map_no_location_permission_title'.t(context: context), + content: 'map_no_location_permission_content'.t(context: context), + cancel: 'cancel'.t(context: context), + ok: 'yes'.t(context: context), + onOk: () {}, + ); +} diff --git a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart index 79e6288a72..f067bc6bf3 100644 --- a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart @@ -4,17 +4,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; - -import 'package:immich_mobile/providers/asset_viewer/scroll_to_date_notifier.provider.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/routing/router.dart'; class DriftMemoryBottomInfo extends StatelessWidget { final DriftMemory memory; final String title; - const DriftMemoryBottomInfo({ - super.key, - required this.memory, - required this.title, - }); + const DriftMemoryBottomInfo({super.key, required this.memory, required this.title}); @override Widget build(BuildContext context) { @@ -22,43 +19,39 @@ class DriftMemoryBottomInfo extends StatelessWidget { final fileCreatedDate = memory.assets.first.createdAt; return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format(fileCreatedDate), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, + Text( + df.format(fileCreatedDate), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider.scrollToDate(fileCreatedDate); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + Tooltip( + message: 'view_in_timeline'.tr(), + child: MaterialButton( + minWidth: 0, + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ), + ], + ), ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index 268bbc30c0..eaed60b204 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -29,39 +29,28 @@ class DriftMemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio BoxFit fit = BoxFit.contain; if (asset.width != null && asset.height != null) { final aspectRatio = asset.width! / asset.height!; - final phoneAspectRatio = - constraints.maxWidth / constraints.maxHeight; + final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight; // Look for a 25% difference in either direction - if (phoneAspectRatio * .75 < aspectRatio && - phoneAspectRatio * 1.25 > aspectRatio) { + if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) { // Cover to look nice if we have nearly the same aspect ratio fit = BoxFit.cover; } } if (asset.isImage) { - return FullImage( - asset, - fit: fit, - size: const Size(double.infinity, double.infinity), - ); + return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity)); } else { return SizedBox( width: context.width, @@ -71,11 +60,7 @@ class DriftMemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: FullImage( - asset, - size: Size(context.width, context.height), - fit: BoxFit.contain, - ), + image: FullImage(asset, size: Size(context.width, context.height), fit: BoxFit.contain), ), ); } @@ -87,10 +72,7 @@ class DriftMemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -111,16 +93,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -131,16 +106,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: getFullImageProvider( - asset, - size: Size(context.width, context.height), - ), + image: getFullImageProvider(asset, size: Size(context.width, context.height)), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index 403d8de061..b2c61c7488 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -7,27 +7,28 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; import 'package:immich_mobile/routing/router.dart'; class DriftMemoryLane extends ConsumerWidget { - final List memories; - - const DriftMemoryLane({super.key, required this.memories}); + const DriftMemoryLane({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + final memoryLaneProvider = ref.watch(driftMemoryFutureProvider); + final memories = memoryLaneProvider.value ?? const []; + if (memories.isEmpty) { + return const SizedBox.shrink(); + } + return ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (index) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -40,68 +41,44 @@ class DriftMemoryLane extends ConsumerWidget { } } - context.pushRoute( - DriftMemoryRoute( - memories: memories, - memoryIndex: index, - ), - ); + context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)); }, - children: - memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), + children: memories + .map((memory) => DriftMemoryCard(key: Key(memory.id), memory: memory)) + .toList(growable: false), ), ); } } class DriftMemoryCard extends ConsumerWidget { - const DriftMemoryCard({ - super.key, - required this.memory, - }); + const DriftMemoryCard({super.key, required this.memory}); final DriftMemory memory; @override Widget build(BuildContext context, WidgetRef ref) { final yearsAgo = DateTime.now().year - memory.data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); return Center( child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: SizedBox( width: 205, height: 200, - child: Thumbnail( - remoteId: memory.assets[0].id, - fit: BoxFit.cover, - ), + child: Thumbnail.remote(remoteId: memory.assets[0].id, fit: BoxFit.cover), ), ), Positioned( bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart similarity index 92% rename from mobile/lib/presentation/widgets/partner_user_avatar.widget.dart rename to mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart index 9be55cae67..8cdf1ed286 100644 --- a/mobile/lib/presentation/widgets/partner_user_avatar.widget.dart +++ b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart @@ -13,8 +13,7 @@ class PartnerUserAvatar extends StatelessWidget { @override Widget build(BuildContext context) { - final url = - "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image"; + final url = "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image"; final nameFirstLetter = partner.name.isNotEmpty ? partner.name[0] : ""; return CircleAvatar( radius: 16, diff --git a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart new file mode 100644 index 0000000000..dd6390406b --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart @@ -0,0 +1,119 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:scroll_date_picker/scroll_date_picker.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; + +class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonBirthdayEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late DateTime _selectedDate; + + @override + void initState() { + super.initState(); + _selectedDate = widget.person.birthDate ?? DateTime.now(); + } + + void saveBirthday() async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateBrithday(widget.person.id, _selectedDate); + + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(_selectedDate); + } + } catch (error) { + dPrint(() => 'Error updating birthday: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + "edit_birthday".t(context: context), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + content: SizedBox( + width: double.maxFinite, + height: 300, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + child: ScrollDatePicker( + options: DatePickerOptions( + backgroundColor: context.colorScheme.surfaceContainerHigh, + itemExtent: 50, + diameterRatio: 5, + ), + scrollViewOptions: DatePickerScrollViewOptions( + day: ScrollViewDetailOptions( + isLoop: false, + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + month: ScrollViewDetailOptions( + isLoop: false, + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + year: ScrollViewDetailOptions( + isLoop: false, + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + selectedDate: _selectedDate, + locale: context.locale, + minimumDate: DateTime(1800, 1, 1), + onDateTimeChanged: (DateTime value) { + setState(() { + _selectedDate = value; + }); + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => saveBirthday(), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart new file mode 100644 index 0000000000..6de19000e0 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart @@ -0,0 +1,83 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; + +class DriftPersonNameEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonNameEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late TextEditingController _formController; + + @override + void initState() { + super.initState(); + _formController = TextEditingController(text: widget.person.name); + } + + void onEdit(String personId, String newName) async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateName(personId, newName); + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(newName); + } + } catch (error) { + dPrint(() => 'Error updating name: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("edit_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + content: SingleChildScrollView( + child: TextFormField( + controller: _formController, + textCapitalization: TextCapitalization.words, + autofocus: true, + decoration: InputDecoration(hintText: 'name'.tr(), border: const OutlineInputBorder()), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => onEdit(widget.person.id, _formController.text), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart new file mode 100644 index 0000000000..b374d48417 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class PersonOptionSheet extends ConsumerWidget { + const PersonOptionSheet({super.key, this.onEditName, this.onEditBirthday, this.birthdayExists = false}); + + final VoidCallback? onEditName; + final VoidCallback? onEditBirthday; + final bool birthdayExists; + + @override + Widget build(BuildContext context, WidgetRef ref) { + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); + + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: ListView( + shrinkWrap: true, + children: [ + ListTile( + leading: const Icon(Icons.edit), + title: Text('edit_name'.t(context: context), style: textStyle), + onTap: onEditName, + ), + ListTile( + leading: const Icon(Icons.cake), + title: Text((birthdayExists ? 'edit_birthday' : "add_birthday").t(context: context), style: textStyle), + onTap: onEditBirthday, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart index d32608a8b4..b82d951b68 100644 --- a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart +++ b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart @@ -13,6 +13,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { this.onCreateSharedLink, this.onToggleAlbumOrder, this.onEditAlbum, + this.onShowOptions, }); final VoidCallback? onAddPhotos; @@ -22,12 +23,11 @@ class DriftRemoteAlbumOption extends ConsumerWidget { final VoidCallback? onCreateSharedLink; final VoidCallback? onToggleAlbumOrder; final VoidCallback? onEditAlbum; + final VoidCallback? onShowOptions; @override Widget build(BuildContext context, WidgetRef ref) { - TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: FontWeight.w600, - ); + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); return SafeArea( child: Padding( @@ -38,74 +38,52 @@ class DriftRemoteAlbumOption extends ConsumerWidget { if (onEditAlbum != null) ListTile( leading: const Icon(Icons.edit), - title: Text( - 'edit_album'.t(context: context), - style: textStyle, - ), + title: Text('edit_album'.t(context: context), style: textStyle), onTap: onEditAlbum, ), if (onAddPhotos != null) ListTile( leading: const Icon(Icons.add_a_photo), - title: Text( - 'add_photos'.t(context: context), - style: textStyle, - ), + title: Text('add_photos'.t(context: context), style: textStyle), onTap: onAddPhotos, ), if (onAddUsers != null) ListTile( leading: const Icon(Icons.group_add), - title: Text( - 'album_viewer_page_share_add_users'.t(context: context), - style: textStyle, - ), + title: Text('album_viewer_page_share_add_users'.t(context: context), style: textStyle), onTap: onAddUsers, ), if (onLeaveAlbum != null) ListTile( leading: const Icon(Icons.person_remove_rounded), - title: Text( - 'leave_album'.t(context: context), - style: textStyle, - ), + title: Text('leave_album'.t(context: context), style: textStyle), onTap: onLeaveAlbum, ), if (onToggleAlbumOrder != null) ListTile( leading: const Icon(Icons.swap_vert_rounded), - title: Text( - 'change_display_order'.t(context: context), - style: textStyle, - ), + title: Text('change_display_order'.t(context: context), style: textStyle), onTap: onToggleAlbumOrder, ), if (onCreateSharedLink != null) ListTile( leading: const Icon(Icons.link), - title: Text( - 'create_shared_link'.t(context: context), - style: textStyle, - ), + title: Text('create_shared_link'.t(context: context), style: textStyle), onTap: onCreateSharedLink, ), - if (onDeleteAlbum != null) ...[ - const Divider( - indent: 16, - endIndent: 16, - ), + if (onShowOptions != null) ListTile( - leading: Icon( - Icons.delete, - color: - context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + leading: const Icon(Icons.settings), + title: Text('options'.t(context: context), style: textStyle), + onTap: onShowOptions, + ), + if (onDeleteAlbum != null) ...[ + const Divider(indent: 16, endIndent: 16), + ListTile( + leading: Icon(Icons.delete, color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), title: Text( 'delete_album'.t(context: context), - style: textStyle.copyWith( - color: - context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + style: textStyle.copyWith(color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), ), onTap: onDeleteAlbum, ), diff --git a/mobile/lib/presentation/widgets/timeline/constants.dart b/mobile/lib/presentation/widgets/timeline/constants.dart index fb9034f179..cfe96b1c81 100644 --- a/mobile/lib/presentation/widgets/timeline/constants.dart +++ b/mobile/lib/presentation/widgets/timeline/constants.dart @@ -1,5 +1,8 @@ +import 'dart:ui'; + const double kTimelineHeaderExtent = 80.0; -const double kTimelineFixedTileExtent = 256; +const Size kTimelineFixedTileExtent = Size.square(256); +const Size kThumbnailResolution = Size.square(320); // TODO: make the resolution vary based on actual tile size const double kTimelineSpacing = 2.0; const int kTimelineColumnCount = 3; diff --git a/mobile/lib/presentation/widgets/timeline/fixed/row.dart b/mobile/lib/presentation/widgets/timeline/fixed/row.dart index 1062c00740..3fe3cea3c9 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/row.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/row.dart @@ -16,11 +16,7 @@ class FixedTimelineRow extends MultiChildRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { - return RenderFixedRow( - dimension: dimension, - spacing: spacing, - textDirection: textDirection, - ); + return RenderFixedRow(dimension: dimension, spacing: spacing, textDirection: textDirection); } @override @@ -50,9 +46,9 @@ class RenderFixedRow extends RenderBox required double dimension, required double spacing, required TextDirection textDirection, - }) : _dimension = dimension, - _spacing = spacing, - _textDirection = textDirection { + }) : _dimension = dimension, + _spacing = spacing, + _textDirection = textDirection { addAll(children); } @@ -90,8 +86,7 @@ class RenderFixedRow extends RenderBox } } - double get intrinsicWidth => - dimension * childCount + spacing * (childCount - 1); + double get intrinsicWidth => dimension * childCount + spacing * (childCount - 1); @override double computeMinIntrinsicWidth(double height) => intrinsicWidth; diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 7fff6d7d2d..8121bb76d3 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -1,18 +1,22 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -33,15 +37,13 @@ class FixedSegment extends Segment { required super.headerExtent, required super.spacing, required super.header, - }) : assert(tileHeight != 0), - mainAxisExtend = tileHeight + spacing; + }) : assert(tileHeight != 0), + mainAxisExtend = tileHeight + spacing; @override double indexToLayoutOffset(int index) { final relativeIndex = index - gridIndex; - return relativeIndex < 0 - ? startOffset - : gridOffset + (mainAxisExtend * relativeIndex); + return relativeIndex < 0 ? startOffset : gridOffset + (mainAxisExtend * relativeIndex); } @override @@ -66,12 +68,7 @@ class FixedSegment extends Segment { final numberOfAssets = math.min(columnCount, assetCount - assetIndex); if (index == firstIndex) { - return TimelineHeader( - bucket: bucket, - header: header, - height: headerExtent, - assetOffset: firstAssetIndex, - ); + return TimelineHeader(bucket: bucket, header: header, height: headerExtent, assetOffset: firstAssetIndex); } return _FixedSegmentRow( @@ -98,19 +95,14 @@ class _FixedSegmentRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isScrubbing = - ref.watch(timelineStateProvider.select((s) => s.isScrubbing)); + final isScrubbing = ref.watch(timelineStateProvider.select((s) => s.isScrubbing)); final timelineService = ref.read(timelineServiceProvider); if (isScrubbing) { return _buildPlaceholder(context); } - if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow( - context, - timelineService.getAssets(assetIndex, assetCount), - ); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount), timelineService); } return FutureBuilder>( @@ -119,31 +111,30 @@ class _FixedSegmentRow extends ConsumerWidget { if (snapshot.connectionState != ConnectionState.done) { return _buildPlaceholder(context); } - return _buildAssetRow(context, snapshot.requireData); + return _buildAssetRow(context, snapshot.requireData, timelineService); }, ); } Widget _buildPlaceholder(BuildContext context) { - return SegmentBuilder.buildPlaceholder( - context, - assetCount, - size: Size.square(tileHeight), - spacing: spacing, - ); + return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } - Widget _buildAssetRow(BuildContext context, List assets) { + Widget _buildAssetRow(BuildContext context, List assets, TimelineService timelineService) { return FixedTimelineRow( dimension: tileHeight, spacing: spacing, textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(assets[i].heroTag), - asset: assets[i], + TimelineAssetIndexWrapper( assetIndex: assetIndex + i, + segmentIndex: 0, // For simplicity, using 0 for now + child: _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ), ], ); @@ -154,18 +145,9 @@ class _AssetTileWidget extends ConsumerWidget { final BaseAsset asset; final int assetIndex; - const _AssetTileWidget({ - super.key, - required this.asset, - required this.assetIndex, - }); + const _AssetTileWidget({super.key, required this.asset, required this.assetIndex}); - Future _handleOnTap( - BuildContext ctx, - WidgetRef ref, - int assetIndex, - BaseAsset asset, - ) async { + Future _handleOnTap(BuildContext ctx, WidgetRef ref, int assetIndex, BaseAsset asset, int? heroOffset) async { final multiSelectState = ref.read(multiSelectProvider); if (multiSelectState.forceEnable || multiSelectState.isEnabled) { @@ -173,10 +155,12 @@ class _AssetTileWidget extends ConsumerWidget { } else { await ref.read(timelineServiceProvider).loadAssets(assetIndex, 1); ref.read(isPlayingMotionVideoProvider.notifier).playing = false; + AssetViewer.setAsset(ref, asset); ctx.pushRoute( AssetViewerRoute( initialIndex: assetIndex, timelineService: ref.read(timelineServiceProvider), + heroOffset: heroOffset, ), ); } @@ -193,11 +177,7 @@ class _AssetTileWidget extends ConsumerWidget { } bool _getLockSelectionStatus(WidgetRef ref) { - final lockSelectionAssets = ref.read( - multiSelectProvider.select( - (state) => state.lockedSelectionAssets, - ), - ); + final lockSelectionAssets = ref.read(multiSelectProvider.select((state) => state.lockedSelectionAssets)); if (lockSelectionAssets.isEmpty) { return false; @@ -208,22 +188,21 @@ class _AssetTileWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + final lockSelection = _getLockSelectionStatus(ref); - final showStorageIndicator = ref.watch( - timelineArgsProvider.select((args) => args.showStorageIndicator), - ); + final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); return RepaintBoundary( child: GestureDetector( - onTap: () => lockSelection - ? null - : _handleOnTap(context, ref, assetIndex, asset), - onLongPress: () => - lockSelection ? null : _handleOnLongPress(ref, asset), + onTap: () => lockSelection ? null : _handleOnTap(context, ref, assetIndex, asset, heroOffset), + onLongPress: () => lockSelection || isReadonlyModeEnabled ? null : _handleOnLongPress(ref, asset), child: ThumbnailTile( asset, lockSelection: lockSelection, showStorageIndicator: showStorageIndicator, + heroOffset: heroOffset, ), ), ); diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart index 327e690267..b65582f976 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart @@ -35,18 +35,14 @@ class FixedSegmentBuilder extends SegmentBuilder { final timelineHeader = switch (groupBy) { GroupAssetsBy.month => HeaderType.month, - GroupAssetsBy.day => - bucket is TimeBucket && bucket.date.month != previousDate?.month - ? HeaderType.monthAndDay - : HeaderType.day, + GroupAssetsBy.day || GroupAssetsBy.auto => + bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day, GroupAssetsBy.none => HeaderType.none, }; final headerExtent = SegmentBuilder.headerExtent(timelineHeader); final segmentStartOffset = startOffset; - startOffset += headerExtent + - (tileHeight * numberOfRows) + - spacing * (numberOfRows - 1); + startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1); final segmentEndOffset = startOffset; segments.add( diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index c6c10c26c9..3eff305251 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -7,9 +7,10 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; -class TimelineHeader extends StatelessWidget { +class TimelineHeader extends HookConsumerWidget { final Bucket bucket; final HeaderType header; final double height; @@ -36,24 +37,17 @@ class TimelineHeader extends StatelessWidget { } @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { if (bucket is! TimeBucket || header == HeaderType.none) { return const SizedBox.shrink(); } final date = (bucket as TimeBucket).date; - - final isMonthHeader = - header == HeaderType.month || header == HeaderType.monthAndDay; - final isDayHeader = - header == HeaderType.day || header == HeaderType.monthAndDay; + final isMonthHeader = header == HeaderType.month || header == HeaderType.monthAndDay; + final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay; return Padding( - padding: EdgeInsets.only( - top: isMonthHeader ? 8.0 : 0.0, - left: 12.0, - right: 12.0, - ), + padding: EdgeInsets.only(top: isMonthHeader ? 8.0 : 0.0, left: 12.0, right: 12.0), child: SizedBox( height: height, child: Column( @@ -64,31 +58,22 @@ class TimelineHeader extends StatelessWidget { Row( children: [ Text( - _formatMonth(context, date), + toBeginningOfSentenceCase(_formatMonth(context, date)), style: context.textTheme.labelLarge?.copyWith(fontSize: 24), ), const Spacer(), - if (header != HeaderType.monthAndDay) - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + if (header != HeaderType.monthAndDay) _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), if (isDayHeader) Row( children: [ Text( - _formatDay(context, date), - style: context.textTheme.labelLarge?.copyWith( - fontSize: 15, - ), + toBeginningOfSentenceCase(_formatDay(context, date)), + style: context.textTheme.labelLarge?.copyWith(fontSize: 15), ), const Spacer(), - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), ], @@ -102,43 +87,30 @@ class _BulkSelectIconButton extends ConsumerWidget { final Bucket bucket; final int assetOffset; - const _BulkSelectIconButton({ - required this.bucket, - required this.assetOffset, - }); + const _BulkSelectIconButton({required this.bucket, required this.assetOffset}); @override Widget build(BuildContext context, WidgetRef ref) { List bucketAssets; try { - bucketAssets = ref - .watch(timelineServiceProvider) - .getAssets(assetOffset, bucket.assetCount); + bucketAssets = ref.watch(timelineServiceProvider).getAssets(assetOffset, bucket.assetCount); } catch (e) { bucketAssets = []; } + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final isAllSelected = ref.watch(bucketSelectionProvider(bucketAssets)); - return IconButton( - onPressed: () { - ref.read(multiSelectProvider.notifier).toggleBucketSelection( - assetOffset, - bucket.assetCount, - ); - ref.read(hapticFeedbackProvider.notifier).heavyImpact(); - }, - icon: isAllSelected - ? Icon( - Icons.check_circle_rounded, - size: 26, - color: context.primaryColor, - ) - : Icon( - Icons.check_circle_outline_rounded, - size: 26, - color: context.colorScheme.onSurfaceSecondary, - ), - ); + return isReadonlyModeEnabled + ? const SizedBox.shrink() + : IconButton( + onPressed: () { + ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); + }, + icon: isAllSelected + ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) + : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), + ); } } diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 248fc6b579..58d7f933e9 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -3,12 +3,15 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; +import 'package:immich_mobile/utils/debounce.dart'; import 'package:intl/intl.dart' hide TextDirection; /// A widget that will display a BoxScrollView with a ScrollThumb that can be dragged @@ -28,6 +31,11 @@ class Scrubber extends ConsumerStatefulWidget { final double? monthSegmentSnappingOffset; + final bool snapToMonth; + + /// Whether an app bar is present, affects coordinate calculations + final bool hasAppBar; + Scrubber({ super.key, Key? scrollThumbKey, @@ -36,6 +44,8 @@ class Scrubber extends ConsumerStatefulWidget { this.topPadding = 0, this.bottomPadding = 0, this.monthSegmentSnappingOffset, + this.snapToMonth = true, + this.hasAppBar = true, required this.child, }) : assert(child.scrollDirection == Axis.vertical); @@ -43,11 +53,8 @@ class Scrubber extends ConsumerStatefulWidget { ConsumerState createState() => ScrubberState(); } -List<_Segment> _buildSegments({ - required List layoutSegments, - required double timelineHeight, -}) { - const double offsetThreshold = 20.0; +List<_Segment> _buildSegments({required List layoutSegments, required double timelineHeight}) { + const double offsetThreshold = 40.0; final segments = <_Segment>[]; if (layoutSegments.isEmpty || layoutSegments.first.bucket is! TimeBucket) { @@ -58,24 +65,15 @@ List<_Segment> _buildSegments({ DateTime? lastDate; double lastOffset = -offsetThreshold; for (final layoutSegment in layoutSegments) { - final scrollPercentage = - layoutSegment.startOffset / layoutSegments.last.endOffset; + final scrollPercentage = layoutSegment.startOffset / layoutSegments.last.endOffset; final startOffset = scrollPercentage * timelineHeight; final date = (layoutSegment.bucket as TimeBucket).date; final label = formatter.format(date); - final showSegment = lastOffset + offsetThreshold <= startOffset && - (lastDate == null || date.year != lastDate.year); + final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year); - segments.add( - _Segment( - date: date, - startOffset: startOffset, - scrollLabel: label, - showSegment: showSegment, - ), - ); + segments.add(_Segment(date: date, startOffset: startOffset, scrollLabel: label, showSegment: showSegment)); lastDate = date; if (showSegment) { lastOffset = startOffset; @@ -85,11 +83,14 @@ List<_Segment> _buildSegments({ return segments; } -class ScrubberState extends ConsumerState - with TickerProviderStateMixin { +class ScrubberState extends ConsumerState with TickerProviderStateMixin { + String? _lastLabel; double _thumbTopOffset = 0.0; bool _isDragging = false; List<_Segment> _segments = []; + int _monthCount = 0; + DateTime? _currentScrubberDate; + Debouncer? _scrubberDebouncer; late AnimationController _thumbAnimationController; Timer? _fadeOutTimer; @@ -98,44 +99,27 @@ class ScrubberState extends ConsumerState late AnimationController _labelAnimationController; late Animation _labelAnimation; - double get _scrubberHeight => - widget.timelineHeight - widget.topPadding - widget.bottomPadding; + double get _scrubberHeight => widget.timelineHeight - widget.topPadding - widget.bottomPadding; late ScrollController _scrollController; double get _currentOffset { if (_scrollController.hasClients != true) return 0.0; - return _scrollController.offset * - _scrubberHeight / - _scrollController.position.maxScrollExtent; + return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent; } @override void initState() { super.initState(); _isDragging = false; - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); - _thumbAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastEaseInToSlowEaseOut, - ); - _labelAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); + _thumbAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastEaseInToSlowEaseOut); + _labelAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); + _monthCount = getMonthCount(); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -148,12 +132,9 @@ class ScrubberState extends ConsumerState void didUpdateWidget(covariant Scrubber oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.layoutSegments.lastOrNull?.endOffset != - widget.layoutSegments.lastOrNull?.endOffset) { - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); + if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) { + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); + _monthCount = getMonthCount(); } } @@ -162,6 +143,7 @@ class ScrubberState extends ConsumerState _thumbAnimationController.dispose(); _labelAnimationController.dispose(); _fadeOutTimer?.cancel(); + _scrubberDebouncer?.dispose(); super.dispose(); } @@ -173,14 +155,17 @@ class ScrubberState extends ConsumerState }); } + int getMonthCount() { + return _segments.map((e) => "${e.date.month}_${e.date.year}").toSet().length; + } + bool _onScrollNotification(ScrollNotification notification) { if (_isDragging) { // If the user is dragging the thumb, we don't want to update the position return false; } - if (notification is ScrollStartNotification || - notification is ScrollUpdateNotification) { + if (notification is ScrollStartNotification || notification is ScrollUpdateNotification) { ref.read(timelineStateProvider.notifier).setScrolling(true); } else if (notification is ScrollEndNotification) { ref.read(timelineStateProvider.notifier).setScrolling(false); @@ -202,12 +187,30 @@ class ScrubberState extends ConsumerState return false; } + void _onScrubberDateChanged(DateTime date) { + if (_currentScrubberDate != date) { + // Date changed, immediately set scrubbing to true + _currentScrubberDate = date; + ref.read(timelineStateProvider.notifier).setScrubbing(true); + + // Initialize debouncer if needed + _scrubberDebouncer ??= Debouncer(interval: const Duration(milliseconds: 50)); + + // Debounce setting scrubbing to false + _scrubberDebouncer!.run(() { + if (_currentScrubberDate == date) { + ref.read(timelineStateProvider.notifier).setScrubbing(false); + } + }); + } + } + void _onDragStart(DragStartDetails _) { - ref.read(timelineStateProvider.notifier).setScrubbing(true); setState(() { _isDragging = true; _labelAnimationController.forward(); _fadeOutTimer?.cancel(); + _lastLabel = null; }); } @@ -224,6 +227,25 @@ class ScrubberState extends ConsumerState final nearestMonthSegment = _findNearestMonthSegment(dragPosition); if (nearestMonthSegment != null) { + final label = nearestMonthSegment.scrollLabel; + if (_lastLabel != label) { + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + _lastLabel = label; + + // Notify timeline state of the new scrubber date position + if (_monthCount >= kMinMonthsToEnableScrubberSnap) { + _onScrubberDateChanged(nearestMonthSegment.date); + } + } + } + + if (_monthCount < kMinMonthsToEnableScrubberSnap || !widget.snapToMonth) { + // If there are less than kMinMonthsToEnableScrubberSnap months, we don't need to snap to segments + setState(() { + _thumbTopOffset = dragPosition; + _scrollController.jumpTo((dragPosition / _scrubberHeight) * _scrollController.position.maxScrollExtent); + }); + } else if (nearestMonthSegment != null) { _snapToSegment(nearestMonthSegment); } } @@ -244,14 +266,28 @@ class ScrubberState extends ConsumerState /// - If user drags to global Y position that's 100 pixels from the top /// - The relative position would be 100 - 50 = 50 (50 pixels into the scrubber area) double _calculateDragPosition(DragUpdateDetails details) { + if (widget.hasAppBar) { + final dragAreaTop = widget.topPadding; + final dragAreaBottom = widget.timelineHeight - widget.bottomPadding; + final dragAreaHeight = dragAreaBottom - dragAreaTop; + + final relativePosition = details.globalPosition.dy - dragAreaTop; + + // Make sure the position stays within the scrubber's bounds + return relativePosition.clamp(0.0, dragAreaHeight); + } + + // Get the local position relative to the gesture detector + final RenderBox? renderBox = context.findRenderObject() as RenderBox?; + if (renderBox != null) { + final localPosition = renderBox.globalToLocal(details.globalPosition); + return localPosition.dy.clamp(0.0, _scrubberHeight); + } + + // Fallback to current logic if render box is not available final dragAreaTop = widget.topPadding; - final dragAreaBottom = widget.timelineHeight - widget.bottomPadding; - final dragAreaHeight = dragAreaBottom - dragAreaTop; - final relativePosition = details.globalPosition.dy - dragAreaTop; - - // Make sure the position stays within the scrubber's bounds - return relativePosition.clamp(0.0, dragAreaHeight); + return relativePosition.clamp(0.0, _scrubberHeight); } /// Find the segment closest to the given position @@ -284,13 +320,10 @@ class ScrubberState extends ConsumerState } int _findLayoutSegmentIndex(_Segment segment) { - return widget.layoutSegments.indexWhere( - (layoutSegment) { - final bucket = layoutSegment.bucket as TimeBucket; - return bucket.date.year == segment.date.year && - bucket.date.month == segment.date.month; - }, - ); + return widget.layoutSegments.indexWhere((layoutSegment) { + final bucket = layoutSegment.bucket as TimeBucket; + return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; + }); } void _scrollToLayoutSegment(int layoutSegmentIndex) { @@ -299,21 +332,24 @@ class ScrubberState extends ConsumerState final viewportHeight = _scrollController.position.viewportDimension; final targetScrollOffset = layoutSegment.startOffset; - final centeredOffset = targetScrollOffset - - (viewportHeight / 4) + - 100 + - (widget.monthSegmentSnappingOffset ?? 0.0); + final centeredOffset = targetScrollOffset - (viewportHeight / 4) + 100 + (widget.monthSegmentSnappingOffset ?? 0.0); _scrollController.jumpTo(centeredOffset.clamp(0.0, maxScrollExtent)); } void _onDragEnd(DragEndDetails _) { - ref.read(timelineStateProvider.notifier).setScrubbing(false); _labelAnimationController.reverse(); setState(() { _isDragging = false; }); + ref.read(timelineStateProvider.notifier).setScrubbing(false); + + // Reset scrubber tracking when drag ends + _currentScrubberDate = null; + _scrubberDebouncer?.dispose(); + _scrubberDebouncer = null; + _resetThumbTimer(); } @@ -323,19 +359,13 @@ class ScrubberState extends ConsumerState if (_scrollController.hasClients == true) { // Cache to avoid multiple calls to [_currentOffset] final scrollOffset = _currentOffset; - final labelText = _segments - .lastWhereOrNull( - (segment) => segment.startOffset <= scrollOffset, - ) - ?.scrollLabel ?? + final labelText = + _segments.lastWhereOrNull((segment) => segment.startOffset <= scrollOffset)?.scrollLabel ?? _segments.firstOrNull?.scrollLabel; label = labelText != null ? Text( labelText, - style: ctx.textTheme.bodyLarge?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: ctx.textTheme.bodyLarge?.copyWith(color: Colors.white, fontWeight: FontWeight.bold), ) : null; } @@ -354,8 +384,7 @@ class ScrubberState extends ConsumerState isDragging: _isDragging, ), ), - if (_scrollController.hasClients && - _scrollController.position.maxScrollExtent > 0) + if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0) PositionedDirectional( top: _thumbTopOffset + widget.topPadding, end: 0, @@ -364,11 +393,7 @@ class ScrubberState extends ConsumerState onVerticalDragStart: _onDragStart, onVerticalDragUpdate: _onDragUpdate, onVerticalDragEnd: _onDragEnd, - child: _Scrubber( - thumbAnimation: _thumbAnimation, - labelAnimation: _labelAnimation, - label: label, - ), + child: _Scrubber(thumbAnimation: _thumbAnimation, labelAnimation: _labelAnimation, label: label), ), ), ), @@ -383,12 +408,7 @@ class _SegmentsLayer extends StatelessWidget { final double topPadding; final bool isDragging; - const _SegmentsLayer({ - super.key, - required this.segments, - required this.topPadding, - required this.isDragging, - }); + const _SegmentsLayer({super.key, required this.segments, required this.topPadding, required this.isDragging}); @override Widget build(BuildContext context) { @@ -402,9 +422,7 @@ class _SegmentsLayer extends StatelessWidget { key: ValueKey('segment_${segment.date.millisecondsSinceEpoch}'), top: topPadding + segment.startOffset, end: 100, - child: RepaintBoundary( - child: _SegmentWidget(segment), - ), + child: RepaintBoundary(child: _SegmentWidget(segment)), ), ) .toList(), @@ -422,7 +440,7 @@ class _SegmentWidget extends StatelessWidget { Widget build(BuildContext context) { return IgnorePointer( child: Container( - margin: const EdgeInsets.only(right: 12.0), + margin: const EdgeInsets.only(right: 36.0), child: Material( color: context.colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(16.0)), @@ -432,10 +450,7 @@ class _SegmentWidget extends StatelessWidget { alignment: Alignment.center, child: Text( _segment.date.year.toString(), - style: context.textTheme.labelMedium?.copyWith( - fontFamily: "OverpassMono", - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelMedium?.copyWith(fontFamily: "OverpassMono", fontWeight: FontWeight.w600), ), ), ), @@ -449,11 +464,7 @@ class _ScrollLabel extends StatelessWidget { final Color backgroundColor; final Animation animation; - const _ScrollLabel({ - required this.label, - required this.backgroundColor, - required this.animation, - }); + const _ScrollLabel({required this.label, required this.backgroundColor, required this.animation}); @override Widget build(BuildContext context) { @@ -484,11 +495,7 @@ class _Scrubber extends StatelessWidget { final Animation thumbAnimation; final Animation labelAnimation; - const _Scrubber({ - this.label, - required this.thumbAnimation, - required this.labelAnimation, - }); + const _Scrubber({this.label, required this.thumbAnimation, required this.labelAnimation}); @override Widget build(BuildContext context) { @@ -502,12 +509,7 @@ class _Scrubber extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - if (label != null) - _ScrollLabel( - label: label!, - backgroundColor: backgroundColor, - animation: labelAnimation, - ), + if (label != null) _ScrollLabel(label: label!, backgroundColor: backgroundColor, animation: labelAnimation), _CircularThumb(backgroundColor), ], ), @@ -533,9 +535,7 @@ class _CircularThumb extends StatelessWidget { topRight: Radius.circular(4.0), bottomRight: Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0)), - ), + child: Container(constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0))), ), ); } @@ -557,14 +557,8 @@ class _ArrowPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -580,27 +574,18 @@ class _SlideFadeTransition extends StatelessWidget { final Animation _animation; final Widget _child; - const _SlideFadeTransition({ - required Animation animation, - required Widget child, - }) : _animation = animation, - _child = child; + const _SlideFadeTransition({required Animation animation, required Widget child}) + : _animation = animation, + _child = child; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, - builder: (context, child) => - _animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(_animation), - child: FadeTransition( - opacity: _animation, - child: _child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(_animation), + child: FadeTransition(opacity: _animation, child: _child), ), ); } @@ -612,19 +597,9 @@ class _Segment { final String scrollLabel; final bool showSegment; - const _Segment({ - required this.date, - required this.startOffset, - required this.scrollLabel, - this.showSegment = false, - }); + const _Segment({required this.date, required this.startOffset, required this.scrollLabel, this.showSegment = false}); - _Segment copyWith({ - DateTime? date, - double? startOffset, - String? scrollLabel, - bool? showSegment, - }) { + _Segment copyWith({DateTime? date, double? startOffset, String? scrollLabel, bool? showSegment}) { return _Segment( date: date ?? this.date, startOffset: startOffset ?? this.startOffset, diff --git a/mobile/lib/presentation/widgets/timeline/segment.model.dart b/mobile/lib/presentation/widgets/timeline/segment.model.dart index 09d892f69a..bc5f974874 100644 --- a/mobile/lib/presentation/widgets/timeline/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/segment.model.dart @@ -37,13 +37,12 @@ abstract class Segment { required this.headerExtent, required this.spacing, required this.header, - }) : gridIndex = firstIndex + 1, - gridOffset = startOffset + headerExtent + spacing; + }) : gridIndex = firstIndex + 1, + gridOffset = startOffset + headerExtent + spacing; bool containsIndex(int index) => firstIndex <= index && index <= lastIndex; - bool isWithinOffset(double offset) => - startOffset <= offset && offset <= endOffset; + bool isWithinOffset(double offset) => startOffset <= offset && offset <= endOffset; int getMinChildIndexForScrollOffset(double scrollOffset); int getMaxChildIndexForScrollOffset(double scrollOffset); @@ -88,13 +87,9 @@ abstract class Segment { } extension SegmentListExtension on List { - bool equals(List other) => - length == other.length && - lastOrNull?.endOffset == other.lastOrNull?.endOffset; + bool equals(List other) => length == other.length && lastOrNull?.endOffset == other.lastOrNull?.endOffset; - Segment? findByIndex(int index) => - firstWhereOrNull((s) => s.containsIndex(index)); + Segment? findByIndex(int index) => firstWhereOrNull((s) => s.containsIndex(index)); - Segment? findByOffset(double offset) => - firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull; + Segment? findByOffset(double offset) => firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull; } diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart index a746eab243..79ffb47e95 100644 --- a/mobile/lib/presentation/widgets/timeline/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -9,34 +9,26 @@ abstract class SegmentBuilder { final double spacing; final GroupAssetsBy groupBy; - const SegmentBuilder({ - required this.buckets, - this.spacing = kTimelineSpacing, - this.groupBy = GroupAssetsBy.day, - }); + const SegmentBuilder({required this.buckets, this.spacing = kTimelineSpacing, this.groupBy = GroupAssetsBy.day}); static double headerExtent(HeaderType header) => switch (header) { - HeaderType.month => kTimelineHeaderExtent, - HeaderType.day => kTimelineHeaderExtent * 0.90, - HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, - HeaderType.none => 0.0, - }; + HeaderType.month => kTimelineHeaderExtent, + HeaderType.day => kTimelineHeaderExtent * 0.90, + HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, + HeaderType.none => 0.0, + }; static Widget buildPlaceholder( BuildContext context, int count, { - Size size = const Size.square(kTimelineFixedTileExtent), + Size size = kTimelineFixedTileExtent, double spacing = kTimelineSpacing, - }) => - RepaintBoundary( - child: FixedTimelineRow( - dimension: size.height, - spacing: spacing, - textDirection: Directionality.of(context), - children: List.generate( - count, - (_) => ThumbnailPlaceholder(width: size.width, height: size.height), - ), - ), - ); + }) => RepaintBoundary( + child: FixedTimelineRow( + dimension: size.height, + spacing: spacing, + textDirection: Directionality.of(context), + children: List.generate(count, (_) => ThumbnailPlaceholder(width: size.width, height: size.height)), + ), + ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index 6faa4da9f7..1e1d4130f7 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -54,10 +54,7 @@ class TimelineState { final bool isScrubbing; final bool isScrolling; - const TimelineState({ - this.isScrubbing = false, - this.isScrolling = false, - }); + const TimelineState({this.isScrubbing = false, this.isScrolling = false}); bool get isInteracting => isScrubbing || isScrolling; @@ -70,16 +67,11 @@ class TimelineState { int get hashCode => isScrubbing.hashCode ^ isScrolling.hashCode; TimelineState copyWith({bool? isScrubbing, bool? isScrolling}) { - return TimelineState( - isScrubbing: isScrubbing ?? this.isScrubbing, - isScrolling: isScrolling ?? this.isScrolling, - ); + return TimelineState(isScrubbing: isScrubbing ?? this.isScrubbing, isScrolling: isScrolling ?? this.isScrolling); } } class TimelineStateNotifier extends Notifier { - TimelineStateNotifier(); - void setScrubbing(bool isScrubbing) { state = state.copyWith(isScrubbing: isScrubbing); } @@ -89,41 +81,30 @@ class TimelineStateNotifier extends Notifier { } @override - TimelineState build() => const TimelineState( - isScrubbing: false, - isScrolling: false, - ); + TimelineState build() => const TimelineState(isScrubbing: false, isScrolling: false); } // This provider watches the buckets from the timeline service & args and serves the segments. // It should be used only after the timeline service and timeline args provider is overridden -final timelineSegmentProvider = StreamProvider.autoDispose>( - (ref) async* { - final args = ref.watch(timelineArgsProvider); - final columnCount = args.columnCount; - final spacing = args.spacing; - final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); - final tileExtent = math.max(0, availableTileWidth) / columnCount; +final timelineSegmentProvider = StreamProvider.autoDispose>((ref) async* { + final args = ref.watch(timelineArgsProvider); + final columnCount = args.columnCount; + final spacing = args.spacing; + final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); + final tileExtent = math.max(0, availableTileWidth) / columnCount; - final groupBy = args.groupBy ?? - GroupAssetsBy - .values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; - final timelineService = ref.watch(timelineServiceProvider); - yield* timelineService.watchBuckets().map((buckets) { - return FixedSegmentBuilder( - buckets: buckets, - tileHeight: tileExtent, - columnCount: columnCount, - spacing: spacing, - groupBy: groupBy, - ).generate(); - }); - }, - dependencies: [timelineServiceProvider, timelineArgsProvider], -); + final timelineService = ref.watch(timelineServiceProvider); + yield* timelineService.watchBuckets().map((buckets) { + return FixedSegmentBuilder( + buckets: buckets, + tileHeight: tileExtent, + columnCount: columnCount, + spacing: spacing, + groupBy: groupBy, + ).generate(); + }); +}, dependencies: [timelineServiceProvider, timelineArgsProvider]); -final timelineStateProvider = - NotifierProvider( - TimelineStateNotifier.new, -); +final timelineStateProvider = NotifierProvider(TimelineStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 873908832b..5f1e7f27b0 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -1,20 +1,26 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_status_floating_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -29,13 +35,12 @@ class Timeline extends StatelessWidget { this.topSliverWidgetHeight, this.showStorageIndicator = false, this.withStack = false, - this.appBar = const ImmichSliverAppBar( - floating: true, - pinned: false, - snap: false, - ), - this.bottomSheet = const GeneralBottomSheet(), + this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), + this.bottomSheet = const GeneralBottomSheet(minChildSize: 0.23), this.groupBy, + this.withScrubber = true, + this.snapToMonth = true, + this.initialScrollOffset, }); final Widget? topSliverWidget; @@ -45,10 +50,15 @@ class Timeline extends StatelessWidget { final Widget? bottomSheet; final bool withStack; final GroupAssetsBy? groupBy; + final bool withScrubber; + final bool snapToMonth; + final double? initialScrollOffset; @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, + floatingActionButton: const DownloadStatusFloatingButton(), body: LayoutBuilder( builder: (_, constraints) => ProviderScope( overrides: [ @@ -56,9 +66,7 @@ class Timeline extends StatelessWidget { (ref) => TimelineArgs( maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight, - columnCount: ref.watch( - settingsProvider.select((s) => s.get(Setting.tilesPerRow)), - ), + columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), showStorageIndicator: showStorageIndicator, withStack: withStack, groupBy: groupBy, @@ -70,6 +78,9 @@ class Timeline extends StatelessWidget { topSliverWidgetHeight: topSliverWidgetHeight, appBar: appBar, bottomSheet: bottomSheet, + withScrubber: withScrubber, + snapToMonth: snapToMonth, + initialScrollOffset: initialScrollOffset, ), ), ), @@ -83,143 +94,352 @@ class _SliverTimeline extends ConsumerStatefulWidget { this.topSliverWidgetHeight, this.appBar, this.bottomSheet, + this.withScrubber = true, + this.snapToMonth = true, + this.initialScrollOffset, }); final Widget? topSliverWidget; final double? topSliverWidgetHeight; final Widget? appBar; final Widget? bottomSheet; + final bool withScrubber; + final bool snapToMonth; + final double? initialScrollOffset; @override ConsumerState createState() => _SliverTimelineState(); } class _SliverTimelineState extends ConsumerState<_SliverTimeline> { - final _scrollController = ScrollController(); - StreamSubscription? _reloadSubscription; + late final ScrollController _scrollController; + StreamSubscription? _eventSubscription; + + // Drag selection state + bool _dragging = false; + TimelineAssetIndex? _dragAnchorIndex; + final Set _draggedAssets = HashSet(); + ScrollPhysics? _scrollPhysics; + + int _perRow = 4; + double _scaleFactor = 3.0; + double _baseScaleFactor = 3.0; + int? _scaleRestoreAssetIndex; @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _scrollController = ScrollController( + initialScrollOffset: widget.initialScrollOffset ?? 0.0, + onAttach: _restoreScalePosition, + ); + _eventSubscription = EventStream.shared.listen(_onEvent); + + final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + _perRow = currentTilesPerRow; + _scaleFactor = 7.0 - _perRow; + _baseScaleFactor = _scaleFactor; + + ref.listenManual(multiSelectProvider.select((s) => s.isEnabled), _onMultiSelectionToggled); + } + + void _onEvent(Event event) { + switch (event) { + case ScrollToTopEvent(): + ref.read(timelineStateProvider.notifier).setScrubbing(true); + _scrollController + .animateTo(0, duration: const Duration(milliseconds: 250), curve: Curves.easeInOut) + .whenComplete(() => ref.read(timelineStateProvider.notifier).setScrubbing(false)); + + case ScrollToDateEvent scrollToDateEvent: + _scrollToDate(scrollToDateEvent.date); + case TimelineReloadEvent(): + setState(() {}); + default: + break; + } + } + + void _onMultiSelectionToggled(_, bool isEnabled) { + EventStream.shared.emit(MultiSelectToggleEvent(isEnabled)); + } + + void _restoreScalePosition(_) { + if (_scaleRestoreAssetIndex == null) return; + + final asyncSegments = ref.read(timelineSegmentProvider); + asyncSegments.whenData((segments) { + final targetSegment = segments.lastWhereOrNull((segment) => segment.firstAssetIndex <= _scaleRestoreAssetIndex!); + if (targetSegment != null) { + final assetIndexInSegment = _scaleRestoreAssetIndex! - targetSegment.firstAssetIndex; + final newColumnCount = ref.read(timelineArgsProvider).columnCount; + final rowIndexInSegment = (assetIndexInSegment / newColumnCount).floor(); + final targetRowIndex = targetSegment.firstIndex + 1 + rowIndexInSegment; + final targetOffset = targetSegment.indexToLayoutOffset(targetRowIndex); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _scrollController.jumpTo(targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent)); + } + }); + } + }); + _scaleRestoreAssetIndex = null; } @override void dispose() { _scrollController.dispose(); - _reloadSubscription?.cancel(); + _eventSubscription?.cancel(); super.dispose(); } + void _scrollToDate(DateTime date) { + final asyncSegments = ref.read(timelineSegmentProvider); + asyncSegments.whenData((segments) { + // Find the segment that contains assets from the target date + final targetSegment = segments.firstWhereOrNull((segment) { + if (segment.bucket is TimeBucket) { + final segmentDate = (segment.bucket as TimeBucket).date; + // Check if the segment date matches the target date (year, month, day) + return segmentDate.year == date.year && segmentDate.month == date.month && segmentDate.day == date.day; + } + return false; + }); + + // If exact date not found, try to find the closest month + final fallbackSegment = + targetSegment ?? + segments.firstWhereOrNull((segment) { + if (segment.bucket is TimeBucket) { + final segmentDate = (segment.bucket as TimeBucket).date; + return segmentDate.year == date.year && segmentDate.month == date.month; + } + return false; + }); + + if (fallbackSegment != null) { + // Scroll to the segment with a small offset to show the header + final targetOffset = fallbackSegment.startOffset - 50; + ref.read(timelineStateProvider.notifier).setScrubbing(true); + _scrollController + .animateTo( + targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent), + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ) + .whenComplete(() => ref.read(timelineStateProvider.notifier).setScrubbing(false)); + } + }); + } + + // Drag selection methods + void _setDragStartIndex(TimelineAssetIndex index) { + setState(() { + _scrollPhysics = const ClampingScrollPhysics(); + _dragAnchorIndex = index; + _dragging = true; + }); + } + + void _stopDrag() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Update the physics post frame to prevent sudden change in physics on iOS. + setState(() { + _scrollPhysics = null; + }); + }); + setState(() { + _dragging = false; + _draggedAssets.clear(); + }); + // Reset the scrolling state after a small delay to allow bottom sheet to expand again + Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) { + ref.read(timelineStateProvider.notifier).setScrolling(false); + } + }); + } + + void _dragScroll(ScrollDirection direction) { + _scrollController.animateTo( + _scrollController.offset + (direction == ScrollDirection.forward ? 175 : -175), + duration: const Duration(milliseconds: 125), + curve: Curves.easeOut, + ); + } + + void _handleDragAssetEnter(TimelineAssetIndex index) { + if (_dragAnchorIndex == null || !_dragging) return; + + final timelineService = ref.read(timelineServiceProvider); + final dragAnchorIndex = _dragAnchorIndex!; + + // Calculate the range of assets to select + final startIndex = math.min(dragAnchorIndex.assetIndex, index.assetIndex); + final endIndex = math.max(dragAnchorIndex.assetIndex, index.assetIndex); + final count = endIndex - startIndex + 1; + + // Load the assets in the range + if (timelineService.hasRange(startIndex, count)) { + final selectedAssets = timelineService.getAssets(startIndex, count); + + // Clear previous drag selection and add new range + final multiSelectNotifier = ref.read(multiSelectProvider.notifier); + for (final asset in _draggedAssets) { + multiSelectNotifier.deselectAsset(asset); + } + _draggedAssets.clear(); + + for (final asset in selectedAssets) { + multiSelectNotifier.selectAsset(asset); + _draggedAssets.add(asset); + } + } + } + @override Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); - final maxHeight = - ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); - final isSelectionMode = ref.watch( - multiSelectProvider.select((s) => s.forceEnable), - ); + final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); + final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); - return asyncSegments.widgetWhen( - onData: (segments) { - final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; - final double appBarExpandedHeight = - widget.appBar != null && widget.appBar is MesmerizingSliverAppBar - ? 200 - : 0; - final topPadding = context.padding.top + - (widget.appBar == null ? 0 : kToolbarHeight) + - 10; + return PopScope( + canPop: !isMultiSelectEnabled, + onPopInvokedWithResult: (_, __) { + if (isMultiSelectEnabled) { + ref.read(multiSelectProvider.notifier).reset(); + } + }, + child: asyncSegments.widgetWhen( + onData: (segments) { + final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; + final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar + ? 200 + : 0; + final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; - const scrubberBottomPadding = 100.0; - final bottomPadding = context.padding.bottom + - (widget.appBar == null ? 0 : scrubberBottomPadding); + const scrubberBottomPadding = 100.0; + final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); - return PrimaryScrollController( - controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: - widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) - const SelectionSliverAppBar() - else if (widget.appBar != null) - widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? - const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, + final grid = CustomScrollView( + primary: true, + physics: _scrollPhysics, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) const SelectionSliverAppBar() else if (widget.appBar != null) widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], + ); + + final Widget timeline; + if (widget.withScrubber) { + timeline = Scrubber( + snapToMonth: widget.snapToMonth, + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + hasAppBar: widget.appBar != null, + child: grid, + ); + } else { + timeline = grid; + } + + return PrimaryScrollController( + controller: _scrollController, + child: RawGestureDetector( + gestures: { + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + _baseScaleFactor = _scaleFactor; + }; + + scale.onUpdate = (details) { + final newScaleFactor = math.max(math.min(5.0, _baseScaleFactor * details.scale), 1.0); + final newPerRow = 7 - newScaleFactor.toInt(); + + if (newPerRow != _perRow) { + final currentOffset = _scrollController.offset.clamp( + 0.0, + _scrollController.position.maxScrollExtent, + ); + final segment = segments.findByOffset(currentOffset) ?? segments.lastOrNull; + int? targetAssetIndex; + if (segment != null) { + final rowIndex = segment.getMinChildIndexForScrollOffset(currentOffset); + if (rowIndex > segment.firstIndex) { + final rowIndexInSegment = rowIndex - (segment.firstIndex + 1); + final assetsPerRow = ref.read(timelineArgsProvider).columnCount; + final assetIndexInSegment = rowIndexInSegment * assetsPerRow; + targetAssetIndex = segment.firstAssetIndex + assetIndexInSegment; + } else { + targetAssetIndex = segment.firstAssetIndex; + } + } + + setState(() { + _scaleFactor = newScaleFactor; + _perRow = newPerRow; + _scaleRestoreAssetIndex = targetAssetIndex; + }); + + ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); + } + }; + }, + ), + }, + child: TimelineDragRegion( + onStart: !isReadonlyModeEnabled ? _setDragStartIndex : null, + onAssetEnter: _handleDragAssetEnter, + onEnd: !isReadonlyModeEnabled ? _stopDrag : null, + onScroll: _dragScroll, + onScrollStart: () { + // Minimize the bottom sheet when drag selection starts + ref.read(timelineStateProvider.notifier).setScrolling(true); + }, + child: Stack( + children: [ + timeline, + if (!isSelectionMode && isMultiSelectEnabled) ...[ + Positioned( + top: MediaQuery.paddingOf(context).top, + left: 25, + child: const SizedBox( + height: kToolbarHeight, + child: Center(child: _MultiSelectStatusButton()), + ), ), - ), - const SliverPadding( - padding: EdgeInsets.only( - bottom: scrubberBottomPadding, - ), - ), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], ), ), - if (!isSelectionMode) ...[ - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: const Positioned( - top: 60, - left: 25, - child: _MultiSelectStatusButton(), - ), - ), - if (widget.bottomSheet != null) - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: widget.bottomSheet, - ), - ], - ], - ), - ); - }, + ), + ); + }, + ), ); } } @@ -227,23 +447,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget { final List _segments; - const _SliverSegmentedList({ - required List segments, - required super.delegate, - }) : _segments = segments; + const _SliverSegmentedList({required List segments, required super.delegate}) : _segments = segments; @override _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => - _RenderSliverTimelineBoxAdaptor( - childManager: context as SliverMultiBoxAdaptorElement, - segments: _segments, - ); + _RenderSliverTimelineBoxAdaptor(childManager: context as SliverMultiBoxAdaptorElement, segments: _segments); @override - void updateRenderObject( - BuildContext context, - _RenderSliverTimelineBoxAdaptor renderObject, - ) { + void updateRenderObject(BuildContext context, _RenderSliverTimelineBoxAdaptor renderObject) { renderObject.segments = _segments; } } @@ -260,23 +471,17 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { markNeedsLayout(); } - _RenderSliverTimelineBoxAdaptor({ - required super.childManager, - required List segments, - }) : _segments = segments; + _RenderSliverTimelineBoxAdaptor({required super.childManager, required List segments}) + : _segments = segments; int getMinChildIndexForScrollOffset(double offset) => - _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? - 0; + _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0; int getMaxChildIndexForScrollOffset(double offset) => - _segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? - 0; + _segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? 0; double indexToLayoutOffset(int index) => - (_segments.findByIndex(index) ?? _segments.lastOrNull) - ?.indexToLayoutOffset(index) ?? - 0; + (_segments.findByIndex(index) ?? _segments.lastOrNull)?.indexToLayoutOffset(index) ?? 0; double estimateMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0; @@ -288,8 +493,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Assume initially that we have enough children to fill the viewport/cache area. childManager.setDidUnderflow(false); - final double scrollOffset = - constraints.scrollOffset + constraints.cacheOrigin; + final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin; assert(scrollOffset >= 0.0); final double remainingExtent = constraints.remainingCacheExtent; @@ -298,8 +502,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { final double targetScrollOffset = scrollOffset + remainingExtent; // Find the index of the first child that should be visible or in the leading cache area. - final int firstRequiredChildIndex = - getMinChildIndexForScrollOffset(scrollOffset); + final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset); // Find the index of the last child that should be visible or in the trailing cache area. final int? lastRequiredChildIndex = targetScrollOffset.isFinite @@ -310,8 +513,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { if (firstChild == null) { collectGarbage(0, 0); } else { - final int leadingChildrenToRemove = - calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); + final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); final int trailingChildrenToRemove = lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); @@ -321,17 +523,12 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // If there are currently no children laid out (e.g., initial load), // try to add the first child needed for the current scroll offset. if (firstChild == null) { - final double firstChildLayoutOffset = - indexToLayoutOffset(firstRequiredChildIndex); - final bool childAdded = addInitialChild( - index: firstRequiredChildIndex, - layoutOffset: firstChildLayoutOffset, - ); + final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex); + final bool childAdded = addInitialChild(index: firstRequiredChildIndex, layoutOffset: firstChildLayoutOffset); if (!childAdded) { // There are either no children, or we are past the end of all our children. - final double max = - firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset(); + final double max = firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset(); geometry = SliverGeometry(scrollExtent: max, maxPaintExtent: max); childManager.didFinishLayout(); return; @@ -342,26 +539,20 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { RenderBox? highestLaidOutChild; final childConstraints = constraints.asBoxConstraints(); - for (int currentIndex = indexOf(firstChild!) - 1; - currentIndex >= firstRequiredChildIndex; - --currentIndex) { - final RenderBox? newLeadingChild = - insertAndLayoutLeadingChild(childConstraints); + for (int currentIndex = indexOf(firstChild!) - 1; currentIndex >= firstRequiredChildIndex; --currentIndex) { + final RenderBox? newLeadingChild = insertAndLayoutLeadingChild(childConstraints); if (newLeadingChild == null) { // If a child is missing where we expect one, it indicates // an inconsistency in offset that needs correction. - final Segment? segment = - _segments.findByIndex(currentIndex) ?? _segments.firstOrNull; + final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.firstOrNull; geometry = SliverGeometry( // Request a scroll correction based on where the missing child should have been. - scrollOffsetCorrection: - segment?.indexToLayoutOffset(currentIndex) ?? 0.0, + scrollOffsetCorrection: segment?.indexToLayoutOffset(currentIndex) ?? 0.0, ); // Parent will re-layout everything. return; } - final childParentData = - newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData; + final childParentData = newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData; childParentData.layoutOffset = indexToLayoutOffset(currentIndex); assert(childParentData.index == currentIndex); highestLaidOutChild ??= newLeadingChild; @@ -375,10 +566,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // The [firstChild] that existed at the start of performLayout is still the first one we need. if (highestLaidOutChild == null) { firstChild!.layout(childConstraints); - final childParentData = - firstChild!.parentData! as SliverMultiBoxAdaptorParentData; - childParentData.layoutOffset = - indexToLayoutOffset(firstRequiredChildIndex); + final childParentData = firstChild!.parentData! as SliverMultiBoxAdaptorParentData; + childParentData.layoutOffset = indexToLayoutOffset(firstRequiredChildIndex); highestLaidOutChild = firstChild; } @@ -388,23 +577,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // until we reach the [lastRequiredChildIndex] or run out of children. double calculatedMaxScrollOffset = double.infinity; - for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; - lastRequiredChildIndex == null || - currentIndex <= lastRequiredChildIndex; - ++currentIndex) { + for ( + int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; + lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; + ++currentIndex + ) { RenderBox? child = childAfter(mostRecentlyLaidOutChild!); if (child == null || indexOf(child) != currentIndex) { - child = insertAndLayoutChild( - childConstraints, - after: mostRecentlyLaidOutChild, - ); + child = insertAndLayoutChild(childConstraints, after: mostRecentlyLaidOutChild); if (child == null) { - final Segment? segment = - _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; - calculatedMaxScrollOffset = - segment?.indexToLayoutOffset(currentIndex) ?? - computeMaxScrollOffset(); + final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; + calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset(); break; } } else { @@ -412,49 +596,30 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { } mostRecentlyLaidOutChild = child; - final childParentData = mostRecentlyLaidOutChild.parentData! - as SliverMultiBoxAdaptorParentData; + final childParentData = mostRecentlyLaidOutChild.parentData! as SliverMultiBoxAdaptorParentData; assert(childParentData.index == currentIndex); childParentData.layoutOffset = indexToLayoutOffset(currentIndex); } final int lastLaidOutChildIndex = indexOf(lastChild!); - final double leadingScrollOffset = - indexToLayoutOffset(firstRequiredChildIndex); - final double trailingScrollOffset = - indexToLayoutOffset(lastLaidOutChildIndex + 1); + final double leadingScrollOffset = indexToLayoutOffset(firstRequiredChildIndex); + final double trailingScrollOffset = indexToLayoutOffset(lastLaidOutChildIndex + 1); assert( firstRequiredChildIndex == 0 || - (childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= - precisionErrorTolerance, + (childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= precisionErrorTolerance, ); assert(debugAssertChildListIsNonEmptyAndContiguous()); assert(indexOf(firstChild!) == firstRequiredChildIndex); - assert( - lastRequiredChildIndex == null || - lastLaidOutChildIndex <= lastRequiredChildIndex, - ); + assert(lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex); - calculatedMaxScrollOffset = math.min( - calculatedMaxScrollOffset, - estimateMaxScrollOffset(), - ); + calculatedMaxScrollOffset = math.min(calculatedMaxScrollOffset, estimateMaxScrollOffset()); - final double paintExtent = calculatePaintOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double paintExtent = calculatePaintOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double cacheExtent = calculateCacheOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double cacheExtent = calculateCacheOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double targetEndScrollOffsetForPaint = - constraints.scrollOffset + constraints.remainingPaintExtent; + final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent; final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null; @@ -468,8 +633,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Indicates if there's content scrolled off-screen. // This is true if the last child needed for painting is actually laid out, // or if the first child is partially visible. - hasVisualOverflow: (targetLastIndexForPaint != null && - lastLaidOutChildIndex >= targetLastIndexForPaint) || + hasVisualOverflow: + (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || constraints.scrollOffset > 0.0, cacheExtent: cacheExtent, ); @@ -489,21 +654,22 @@ class _MultiSelectStatusButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final selectCount = - ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); + final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); return ElevatedButton.icon( onPressed: () => ref.read(multiSelectProvider.notifier).reset(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( selectCount.toString(), - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ); } } + +/// accepts a gesture even though it should reject it (because child won) +class CustomScaleGestureRecognizer extends ScaleGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart new file mode 100644 index 0000000000..88d46b143f --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart @@ -0,0 +1,212 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class TimelineDragRegion extends StatefulWidget { + final Widget child; + + final void Function(TimelineAssetIndex valueKey)? onStart; + final void Function(TimelineAssetIndex valueKey)? onAssetEnter; + final void Function()? onEnd; + final void Function()? onScrollStart; + final void Function(ScrollDirection direction)? onScroll; + + const TimelineDragRegion({ + super.key, + required this.child, + this.onStart, + this.onAssetEnter, + this.onEnd, + this.onScrollStart, + this.onScroll, + }); + + @override + State createState() => _TimelineDragRegionState(); +} + +class _TimelineDragRegionState extends State { + late TimelineAssetIndex? assetUnderPointer; + late TimelineAssetIndex? anchorAsset; + + // Scroll related state + static const double scrollOffset = 0.10; + double? topScrollOffset; + double? bottomScrollOffset; + Timer? scrollTimer; + late bool scrollNotified; + + @override + void initState() { + super.initState(); + assetUnderPointer = null; + anchorAsset = null; + scrollNotified = false; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + topScrollOffset = null; + bottomScrollOffset = null; + } + + @override + void dispose() { + scrollTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RawGestureDetector( + gestures: { + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( + () => _CustomLongPressGestureRecognizer(), + _registerCallbacks, + ), + }, + child: widget.child, + ); + } + + void _registerCallbacks(_CustomLongPressGestureRecognizer recognizer) { + recognizer.onLongPressMoveUpdate = (details) => _onLongPressMove(details); + recognizer.onLongPressStart = (details) => _onLongPressStart(details); + recognizer.onLongPressUp = _onLongPressEnd; + } + + TimelineAssetIndex? _getValueKeyAtPosition(Offset position) { + final box = context.findAncestorRenderObjectOfType(); + if (box == null) return null; + + final hitTestResult = BoxHitTestResult(); + final local = box.globalToLocal(position); + if (!box.hitTest(hitTestResult, position: local)) return null; + + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target + as _TimelineAssetIndexProxy?) + ?.index; + } + + void _onLongPressStart(LongPressStartDetails event) { + /// Calculate widget height and scroll offset when long press starting instead of in [initState] + /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size + final height = context.size?.height; + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { + topScrollOffset = height * scrollOffset; + bottomScrollOffset = height - topScrollOffset!; + } + + final initialHit = _getValueKeyAtPosition(event.globalPosition); + anchorAsset = initialHit; + if (initialHit == null) return; + + if (anchorAsset != null) { + widget.onStart?.call(anchorAsset!); + } + } + + void _onLongPressEnd() { + scrollNotified = false; + scrollTimer?.cancel(); + widget.onEnd?.call(); + } + + void _onLongPressMove(LongPressMoveUpdateDetails event) { + if (anchorAsset == null) return; + if (topScrollOffset == null || bottomScrollOffset == null) return; + + final currentDy = event.localPosition.dy; + + if (currentDy > bottomScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.forward), + ); + } else if (currentDy < topScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.reverse), + ); + } else { + scrollTimer?.cancel(); + scrollTimer = null; + } + + final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition); + if (currentlyTouchingAsset == null) return; + + if (assetUnderPointer != currentlyTouchingAsset) { + if (!scrollNotified) { + scrollNotified = true; + widget.onScrollStart?.call(); + } + + widget.onAssetEnter?.call(currentlyTouchingAsset); + assetUnderPointer = currentlyTouchingAsset; + } + } +} + +class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} + +class TimelineAssetIndexWrapper extends SingleChildRenderObjectWidget { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndexWrapper({ + required Widget super.child, + required this.assetIndex, + required this.segmentIndex, + super.key, + }); + + @override + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy createRenderObject(BuildContext context) { + return _TimelineAssetIndexProxy( + index: TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex), + ); + } + + @override + void updateRenderObject( + BuildContext context, + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy renderObject, + ) { + renderObject.index = TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex); + } +} + +class _TimelineAssetIndexProxy extends RenderProxyBox { + TimelineAssetIndex index; + + _TimelineAssetIndexProxy({required this.index}); +} + +class TimelineAssetIndex { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndex({required this.assetIndex, required this.segmentIndex}); + + @override + bool operator ==(covariant TimelineAssetIndex other) { + if (identical(this, other)) return true; + + return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex; + } + + @override + int get hashCode => assetIndex.hashCode ^ segmentIndex.hashCode; +} diff --git a/mobile/lib/providers/activity.provider.dart b/mobile/lib/providers/activity.provider.dart index 0dcc99320b..a867a5a281 100644 --- a/mobile/lib/providers/activity.provider.dart +++ b/mobile/lib/providers/activity.provider.dart @@ -11,9 +11,7 @@ part 'activity.provider.g.dart'; class AlbumActivity extends _$AlbumActivity { @override Future> build(String albumId, [String? assetId]) async { - return ref - .watch(activityServiceProvider) - .getAllActivities(albumId, assetId: assetId); + return ref.watch(activityServiceProvider).getAllActivities(albumId, assetId: assetId); } Future removeActivity(String id) async { @@ -24,17 +22,13 @@ class AlbumActivity extends _$AlbumActivity { state = AsyncData(activities); // Decrement activity count only for comments if (removedActivity.type == ActivityType.comment) { - ref - .watch(activityStatisticsProvider(albumId, assetId).notifier) - .removeActivity(); + ref.watch(activityStatisticsProvider(albumId, assetId).notifier).removeActivity(); } } } Future addLike() async { - final activity = await ref - .watch(activityServiceProvider) - .addActivity(albumId, ActivityType.like, assetId: assetId); + final activity = await ref.watch(activityServiceProvider).addActivity(albumId, ActivityType.like, assetId: assetId); if (activity.hasValue) { final activities = state.asData?.value ?? []; state = AsyncData([...activities, activity.requireValue]); @@ -42,19 +36,14 @@ class AlbumActivity extends _$AlbumActivity { } Future addComment(String comment) async { - final activity = await ref.watch(activityServiceProvider).addActivity( - albumId, - ActivityType.comment, - assetId: assetId, - comment: comment, - ); + final activity = await ref + .watch(activityServiceProvider) + .addActivity(albumId, ActivityType.comment, assetId: assetId, comment: comment); if (activity.hasValue) { final activities = state.valueOrNull ?? []; state = AsyncData([...activities, activity.requireValue]); - ref - .watch(activityStatisticsProvider(albumId, assetId).notifier) - .addActivity(); + ref.watch(activityStatisticsProvider(albumId, assetId).notifier).addActivity(); // The previous addActivity call would increase the count of an asset if assetId != null // To also increase the activity count of the album, calling it once again with assetId set to null if (assetId != null) { diff --git a/mobile/lib/providers/activity.provider.g.dart b/mobile/lib/providers/activity.provider.g.dart index af574b991a..dc927795f8 100644 --- a/mobile/lib/providers/activity.provider.g.dart +++ b/mobile/lib/providers/activity.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$AlbumActivity late final String albumId; late final String? assetId; - FutureOr> build( - String albumId, [ - String? assetId, - ]); + FutureOr> build(String albumId, [String? assetId]); } /// Maintains the current list of all activities for @@ -58,24 +55,15 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider call( - String albumId, [ - String? assetId, - ]) { - return AlbumActivityProvider( - albumId, - assetId, - ); + AlbumActivityProvider call(String albumId, [String? assetId]) { + return AlbumActivityProvider(albumId, assetId); } @override AlbumActivityProvider getProviderOverride( covariant AlbumActivityProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -96,30 +84,28 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. -class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< - AlbumActivity, List> { +class AlbumActivityProvider + extends + AutoDisposeAsyncNotifierProviderImpl> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => AlbumActivity() - ..albumId = albumId - ..assetId = assetId, - from: albumActivityProvider, - name: r'albumActivityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumActivityHash, - dependencies: AlbumActivityFamily._dependencies, - allTransitiveDependencies: - AlbumActivityFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + AlbumActivityProvider(String albumId, [String? assetId]) + : this._internal( + () => AlbumActivity() + ..albumId = albumId + ..assetId = assetId, + from: albumActivityProvider, + name: r'albumActivityProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumActivityHash, + dependencies: AlbumActivityFamily._dependencies, + allTransitiveDependencies: + AlbumActivityFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); AlbumActivityProvider._internal( super._createNotifier, { @@ -136,13 +122,8 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< final String? assetId; @override - FutureOr> runNotifierBuild( - covariant AlbumActivity notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + FutureOr> runNotifierBuild(covariant AlbumActivity notifier) { + return notifier.build(albumId, assetId); } @override @@ -166,7 +147,7 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< @override AutoDisposeAsyncNotifierProviderElement> - createElement() { + createElement() { return _AlbumActivityProviderElement(this); } @@ -198,8 +179,9 @@ mixin AlbumActivityRef on AutoDisposeAsyncNotifierProviderRef> { } class _AlbumActivityProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AlbumActivityRef { + extends + AutoDisposeAsyncNotifierProviderElement> + with AlbumActivityRef { _AlbumActivityProviderElement(super.provider); @override @@ -207,5 +189,6 @@ class _AlbumActivityProviderElement @override String? get assetId => (origin as AlbumActivityProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/activity_service.provider.dart b/mobile/lib/providers/activity_service.provider.dart index 2d63e55354..a7fd0715f8 100644 --- a/mobile/lib/providers/activity_service.provider.dart +++ b/mobile/lib/providers/activity_service.provider.dart @@ -6,5 +6,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'activity_service.provider.g.dart'; @riverpod -ActivityService activityService(Ref ref) => - ActivityService(ref.watch(activityApiRepositoryProvider)); +ActivityService activityService(Ref ref) => ActivityService(ref.watch(activityApiRepositoryProvider)); diff --git a/mobile/lib/providers/activity_statistics.provider.dart b/mobile/lib/providers/activity_statistics.provider.dart index c260a7a547..96d2633d1b 100644 --- a/mobile/lib/providers/activity_statistics.provider.dart +++ b/mobile/lib/providers/activity_statistics.provider.dart @@ -9,10 +9,7 @@ part 'activity_statistics.provider.g.dart'; class ActivityStatistics extends _$ActivityStatistics { @override int build(String albumId, [String? assetId]) { - ref - .watch(activityServiceProvider) - .getStatistics(albumId, assetId: assetId) - .then((stats) => state = stats.comments); + ref.watch(activityServiceProvider).getStatistics(albumId, assetId: assetId).then((stats) => state = stats.comments); return 0; } diff --git a/mobile/lib/providers/activity_statistics.provider.g.dart b/mobile/lib/providers/activity_statistics.provider.g.dart index d2de32c0aa..83d887f6dc 100644 --- a/mobile/lib/providers/activity_statistics.provider.g.dart +++ b/mobile/lib/providers/activity_statistics.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$ActivityStatistics extends BuildlessAutoDisposeNotifier { late final String albumId; late final String? assetId; - int build( - String albumId, [ - String? assetId, - ]); + int build(String albumId, [String? assetId]); } /// Maintains the current number of comments by @@ -58,24 +55,15 @@ class ActivityStatisticsFamily extends Family { /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider call( - String albumId, [ - String? assetId, - ]) { - return ActivityStatisticsProvider( - albumId, - assetId, - ); + ActivityStatisticsProvider call(String albumId, [String? assetId]) { + return ActivityStatisticsProvider(albumId, assetId); } @override ActivityStatisticsProvider getProviderOverride( covariant ActivityStatisticsProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -101,25 +89,22 @@ class ActivityStatisticsProvider /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => ActivityStatistics() - ..albumId = albumId - ..assetId = assetId, - from: activityStatisticsProvider, - name: r'activityStatisticsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityStatisticsHash, - dependencies: ActivityStatisticsFamily._dependencies, - allTransitiveDependencies: - ActivityStatisticsFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + ActivityStatisticsProvider(String albumId, [String? assetId]) + : this._internal( + () => ActivityStatistics() + ..albumId = albumId + ..assetId = assetId, + from: activityStatisticsProvider, + name: r'activityStatisticsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activityStatisticsHash, + dependencies: ActivityStatisticsFamily._dependencies, + allTransitiveDependencies: + ActivityStatisticsFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); ActivityStatisticsProvider._internal( super._createNotifier, { @@ -136,13 +121,8 @@ class ActivityStatisticsProvider final String? assetId; @override - int runNotifierBuild( - covariant ActivityStatistics notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + int runNotifierBuild(covariant ActivityStatistics notifier) { + return notifier.build(albumId, assetId); } @override @@ -206,5 +186,6 @@ class _ActivityStatisticsProviderElement @override String? get assetId => (origin as ActivityStatisticsProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index 39f5af7344..35634d77c8 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -18,8 +18,7 @@ class AlbumNotifier extends StateNotifier> { } }); - _streamSub = - albumService.watchRemoteAlbums().listen((data) => state = data); + _streamSub = albumService.watchRemoteAlbums().listen((data) => state = data); } final AlbumService albumService; @@ -36,31 +35,15 @@ class AlbumNotifier extends StateNotifier> { Future deleteAlbum(Album album) => albumService.deleteAlbum(album); - Future createAlbum( - String albumTitle, - Set assets, - ) => - albumService.createAlbum(albumTitle, assets, []); + Future createAlbum(String albumTitle, Set assets) => albumService.createAlbum(albumTitle, assets, []); - Future getAlbumByName( - String albumName, { - bool? remote, - bool? shared, - bool? owner, - }) => - albumService.getAlbumByName( - albumName, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String albumName, {bool? remote, bool? shared, bool? owner}) => + albumService.getAlbumByName(albumName, remote: remote, shared: shared, owner: owner); /// Create an album on the server with the same name as the selected album for backup /// First this will check if the album already exists on the server with name /// If it does not exist, it will create the album on the server - Future createSyncAlbum( - String albumName, - ) async { + Future createSyncAlbum(String albumName) async { final album = await getAlbumByName(albumName, remote: true, owner: true); if (album != null) { return; @@ -106,16 +89,12 @@ class AlbumNotifier extends StateNotifier> { return await albumService.removeAsset(album, assets); } - Future setActivitystatus( - Album album, - bool enabled, - ) { + Future setActivitystatus(Album album, bool enabled) { return albumService.setActivityStatus(album, enabled); } Future toggleSortOrder(Album album) { - final order = - album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc; + final order = album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc; return albumService.updateSortOrder(album, order); } @@ -127,16 +106,11 @@ class AlbumNotifier extends StateNotifier> { } } -final albumProvider = - StateNotifierProvider.autoDispose>((ref) { - return AlbumNotifier( - ref.watch(albumServiceProvider), - ref, - ); +final albumProvider = StateNotifierProvider.autoDispose>((ref) { + return AlbumNotifier(ref.watch(albumServiceProvider), ref); }); -final albumWatcher = - StreamProvider.autoDispose.family((ref, id) async* { +final albumWatcher = StreamProvider.autoDispose.family((ref, id) async* { final albumService = ref.watch(albumServiceProvider); final album = await albumService.getAlbumById(id); @@ -172,7 +146,6 @@ class LocalAlbumsNotifier extends StateNotifier> { } } -final localAlbumsProvider = - StateNotifierProvider.autoDispose>((ref) { +final localAlbumsProvider = StateNotifierProvider.autoDispose>((ref) { return LocalAlbumsNotifier(ref.watch(albumServiceProvider)); }); diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.dart b/mobile/lib/providers/album/album_sort_by_options.provider.dart index c89cd43132..3dd09f1282 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.dart @@ -31,8 +31,7 @@ class _AlbumSortHandlers { static const AlbumSortFn assetCount = _sortByAssetCount; static List _sortByAssetCount(List albums, bool isReverse) { - final sorted = - albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); + final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); return (isReverse ? sorted.reversed : sorted).toList(); } @@ -76,22 +75,10 @@ class _AlbumSortHandlers { enum AlbumSortMode { title(1, "library_page_sort_title", _AlbumSortHandlers.title), assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount), - lastModified( - 3, - "library_page_sort_last_modified", - _AlbumSortHandlers.lastModified, - ), + lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified), created(0, "library_page_sort_created", _AlbumSortHandlers.created), - mostRecent( - 2, - "sort_recent", - _AlbumSortHandlers.mostRecent, - ), - mostOldest( - 5, - "sort_oldest", - _AlbumSortHandlers.mostOldest, - ); + mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent), + mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest); final int storeIndex; final String label; @@ -104,21 +91,13 @@ enum AlbumSortMode { class AlbumSortByOptions extends _$AlbumSortByOptions { @override AlbumSortMode build() { - final sortOpt = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.selectedAlbumSortOrder); - return AlbumSortMode.values.firstWhere( - (e) => e.storeIndex == sortOpt, - orElse: () => AlbumSortMode.title, - ); + final sortOpt = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortOrder); + return AlbumSortMode.values.firstWhere((e) => e.storeIndex == sortOpt, orElse: () => AlbumSortMode.title); } void changeSortMode(AlbumSortMode sortOption) { state = sortOption; - ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - sortOption.storeIndex, - ); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortOrder, sortOption.storeIndex); } } @@ -126,15 +105,11 @@ class AlbumSortByOptions extends _$AlbumSortByOptions { class AlbumSortOrder extends _$AlbumSortOrder { @override bool build() { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.selectedAlbumSortReverse); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortReverse); } void changeSortDirection(bool isReverse) { state = isReverse; - ref - .watch(appSettingsServiceProvider) - .setSetting(AppSettingsEnum.selectedAlbumSortReverse, isReverse); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortReverse, isReverse); } } diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart index ba20e7eb66..750329c9d5 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart @@ -13,14 +13,14 @@ String _$albumSortByOptionsHash() => @ProviderFor(AlbumSortByOptions) final albumSortByOptionsProvider = AutoDisposeNotifierProvider.internal( - AlbumSortByOptions.new, - name: r'albumSortByOptionsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortByOptionsHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortByOptions.new, + name: r'albumSortByOptionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortByOptionsHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortByOptions = AutoDisposeNotifier; String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @@ -29,14 +29,14 @@ String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @ProviderFor(AlbumSortOrder) final albumSortOrderProvider = AutoDisposeNotifierProvider.internal( - AlbumSortOrder.new, - name: r'albumSortOrderProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortOrderHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortOrder.new, + name: r'albumSortOrderProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortOrderHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortOrder = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/album_title.provider.dart b/mobile/lib/providers/album/album_title.provider.dart index 126b3499a3..bf812a01d8 100644 --- a/mobile/lib/providers/album/album_title.provider.dart +++ b/mobile/lib/providers/album/album_title.provider.dart @@ -12,6 +12,4 @@ class AlbumTitleNotifier extends StateNotifier { } } -final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier(), -); +final albumTitleProvider = StateNotifierProvider((ref) => AlbumTitleNotifier()); diff --git a/mobile/lib/providers/album/album_viewer.provider.dart b/mobile/lib/providers/album/album_viewer.provider.dart index d1290df298..f4ce047464 100644 --- a/mobile/lib/providers/album/album_viewer.provider.dart +++ b/mobile/lib/providers/album/album_viewer.provider.dart @@ -5,13 +5,7 @@ import 'package:immich_mobile/services/album.service.dart'; class AlbumViewerNotifier extends StateNotifier { AlbumViewerNotifier(this.ref) - : super( - const AlbumViewerPageState( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ), - ); + : super(const AlbumViewerPageState(editTitleText: "", isEditAlbum: false, editDescriptionText: "")); final Ref ref; @@ -40,17 +34,10 @@ class AlbumViewerNotifier extends StateNotifier { } void resetState() { - state = state.copyWith( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ); + state = state.copyWith(editTitleText: "", isEditAlbum: false, editDescriptionText: ""); } - Future changeAlbumTitle( - Album album, - String newAlbumTitle, - ) async { + Future changeAlbumTitle(Album album, String newAlbumTitle) async { AlbumService service = ref.watch(albumServiceProvider); bool isSuccess = await service.changeTitleAlbum(album, newAlbumTitle); @@ -65,16 +52,10 @@ class AlbumViewerNotifier extends StateNotifier { return false; } - Future changeAlbumDescription( - Album album, - String newAlbumDescription, - ) async { + Future changeAlbumDescription(Album album, String newAlbumDescription) async { AlbumService service = ref.watch(albumServiceProvider); - bool isSuccess = await service.changeDescriptionAlbum( - album, - newAlbumDescription, - ); + bool isSuccess = await service.changeDescriptionAlbum(album, newAlbumDescription); if (isSuccess) { state = state.copyWith(editDescriptionText: "", isEditAlbum: false); @@ -88,7 +69,6 @@ class AlbumViewerNotifier extends StateNotifier { } } -final albumViewerProvider = - StateNotifierProvider((ref) { +final albumViewerProvider = StateNotifierProvider((ref) { return AlbumViewerNotifier(ref); }); diff --git a/mobile/lib/providers/album/current_album.provider.g.dart b/mobile/lib/providers/album/current_album.provider.g.dart index 60ebe3e333..b6d079231f 100644 --- a/mobile/lib/providers/album/current_album.provider.g.dart +++ b/mobile/lib/providers/album/current_album.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAlbumHash() => r'61f00273d6b69da45add1532cc3d3a076ee55110'; @ProviderFor(CurrentAlbum) final currentAlbumProvider = AutoDisposeNotifierProvider.internal( - CurrentAlbum.new, - name: r'currentAlbumProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAlbumHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAlbum.new, + name: r'currentAlbumProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAlbumHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAlbum = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/suggested_shared_users.provider.dart b/mobile/lib/providers/album/suggested_shared_users.provider.dart index 3c8dcb6733..51146748c7 100644 --- a/mobile/lib/providers/album/suggested_shared_users.provider.dart +++ b/mobile/lib/providers/album/suggested_shared_users.provider.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; -final otherUsersProvider = - FutureProvider.autoDispose>((ref) async { +final otherUsersProvider = FutureProvider.autoDispose>((ref) async { UserService userService = ref.watch(userServiceProvider); final currentUser = ref.watch(currentUserProvider); diff --git a/mobile/lib/providers/api.provider.g.dart b/mobile/lib/providers/api.provider.g.dart index 8a6f514ce6..ee1781c24c 100644 --- a/mobile/lib/providers/api.provider.g.dart +++ b/mobile/lib/providers/api.provider.g.dart @@ -13,8 +13,9 @@ String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c'; final apiServiceProvider = Provider.internal( apiService, name: r'apiServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$apiServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$apiServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3be46d2fbd..3b51874ab5 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; @@ -15,6 +15,7 @@ import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; @@ -26,19 +27,18 @@ import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; -enum AppLifeCycleEnum { - active, - inactive, - paused, - resumed, - detached, - hidden, -} +enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden } class AppLifeCycleNotifier extends StateNotifier { final Ref _ref; bool _wasPaused = false; + // Add operation coordination + Completer? _resumeOperation; + Completer? _pauseOperation; + + final _log = Logger("AppLifeCycleNotifier"); + AppLifeCycleNotifier(this._ref) : super(AppLifeCycleEnum.active); AppLifeCycleEnum getAppState() { @@ -48,6 +48,32 @@ class AppLifeCycleNotifier extends StateNotifier { void handleAppResume() async { state = AppLifeCycleEnum.resumed; + // Prevent overlapping resume operations + if (_resumeOperation != null && !_resumeOperation!.isCompleted) { + await _resumeOperation!.future; + return; + } + + // Cancel any ongoing pause operation + if (_pauseOperation != null && !_pauseOperation!.isCompleted) { + _pauseOperation!.complete(); + } + + _resumeOperation = Completer(); + + try { + await _performResume(); + } catch (e, stackTrace) { + _log.severe("Error during app resume", e, stackTrace); + } finally { + if (!_resumeOperation!.isCompleted) { + _resumeOperation!.complete(); + } + _resumeOperation = null; + } + } + + Future _performResume() async { // no need to resume because app was never really paused if (!_wasPaused) return; _wasPaused = false; @@ -57,11 +83,8 @@ class AppLifeCycleNotifier extends StateNotifier { // Needs to be logged in if (isAuthenticated) { // switch endpoint if needed - final endpoint = - await _ref.read(authProvider.notifier).setOpenApiServiceEndpoint(); - if (kDebugMode) { - debugPrint("Using server URL: $endpoint"); - } + final endpoint = await _ref.read(authProvider.notifier).setOpenApiServiceEndpoint(); + _log.info("Using server URL: $endpoint"); if (!Store.isBetaTimelineEnabled) { final permission = _ref.watch(galleryPermissionNotifier); @@ -87,50 +110,13 @@ class AppLifeCycleNotifier extends StateNotifier { break; } } else { - _ref.read(backupProvider.notifier).cancelBackup(); - - final backgroundManager = _ref.read(backgroundSyncProvider); - // Ensure proper cleanup before starting new background tasks - try { - await Future.wait([ - backgroundManager.syncLocal().then( - (_) { - Logger("AppLifeCycleNotifier") - .fine("Hashing assets after syncLocal"); - // Check if app is still active before hashing - if (state == AppLifeCycleEnum.resumed) { - backgroundManager.hashAssets(); - } - }, - ), - backgroundManager.syncRemote(), - ]).then((_) async { - final isEnableBackup = _ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableBackup); - - if (isEnableBackup) { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(); - } - }); - } catch (e, stackTrace) { - Logger("AppLifeCycleNotifier").severe( - "Error during background sync", - e, - stackTrace, - ); - } + _ref.read(websocketProvider.notifier).connect(); + await _handleBetaTimelineResume(); } - _ref.read(websocketProvider.notifier).connect(); + await _ref.read(notificationPermissionProvider.notifier).getNotificationPermission(); - await _ref - .read(notificationPermissionProvider.notifier) - .getNotificationPermission(); - - await _ref - .read(galleryPermissionNotifier.notifier) - .getGalleryPermissionStatus(); + await _ref.read(galleryPermissionNotifier.notifier).getGalleryPermissionStatus(); if (!Store.isBetaTimelineEnabled) { await _ref.read(iOSBackgroundSettingsProvider.notifier).refresh(); @@ -139,20 +125,115 @@ class AppLifeCycleNotifier extends StateNotifier { } } + Future _safeRun(Future action, String debugName) async { + if (!_shouldContinueOperation()) { + return; + } + + try { + await action; + } catch (e, stackTrace) { + _log.warning("Error during $debugName operation", e, stackTrace); + } + } + + Future _handleBetaTimelineResume() async { + _ref.read(backupProvider.notifier).cancelBackup(); + unawaited(_ref.read(backgroundWorkerLockServiceProvider).lock()); + + // Give isolates time to complete any ongoing database transactions + await Future.delayed(const Duration(milliseconds: 500)); + + final backgroundManager = _ref.read(backgroundSyncProvider); + final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); + + try { + bool syncSuccess = false; + await Future.wait([ + _safeRun(backgroundManager.syncLocal(), "syncLocal"), + _safeRun(backgroundManager.syncRemote().then((success) => syncSuccess = success), "syncRemote"), + ]); + if (syncSuccess) { + await Future.wait([ + _safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) { + _resumeBackup(); + }), + _resumeBackup(), + ]); + } else { + await _safeRun(backgroundManager.hashAssets(), "hashAssets"); + } + + if (isAlbumLinkedSyncEnable) { + await _safeRun(backgroundManager.syncLinkedAlbum(), "syncLinkedAlbum"); + } + } catch (e, stackTrace) { + _log.severe("Error during background sync", e, stackTrace); + } + } + + Future _resumeBackup() async { + final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + + if (isEnableBackup) { + final currentUser = Store.tryGet(StoreKey.currentUser); + if (currentUser != null) { + await _safeRun( + _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id), + "handleBackupResume", + ); + } + } + } + + // Helper method to check if operations should continue + bool _shouldContinueOperation() { + return [AppLifeCycleEnum.resumed, AppLifeCycleEnum.active].contains(state) && + (_resumeOperation?.isCompleted == false || _resumeOperation == null); + } + void handleAppInactivity() { state = AppLifeCycleEnum.inactive; // do not stop/clean up anything on inactivity: issued on every orientation change } - void handleAppPause() { + Future handleAppPause() async { state = AppLifeCycleEnum.paused; _wasPaused = true; + // Prevent overlapping pause operations + if (_pauseOperation != null && !_pauseOperation!.isCompleted) { + await _pauseOperation!.future; + return; + } + + // Cancel any ongoing resume operation + if (_resumeOperation != null && !_resumeOperation!.isCompleted) { + _resumeOperation!.complete(); + } + + _pauseOperation = Completer(); + + try { + if (Store.isBetaTimelineEnabled) { + unawaited(_ref.read(backgroundWorkerLockServiceProvider).unlock()); + } + await _performPause(); + } catch (e, stackTrace) { + _log.severe("Error during app pause", e, stackTrace); + } finally { + if (!_pauseOperation!.isCompleted) { + _pauseOperation!.complete(); + } + _pauseOperation = null; + } + } + + Future _performPause() async { if (_ref.read(authProvider).isAuthenticated) { if (!Store.isBetaTimelineEnabled) { // Do not cancel backup if manual upload is in progress - if (_ref.read(backupProvider.notifier).backupProgress != - BackUpProgressEnum.manualInProgress) { + if (_ref.read(backupProvider.notifier).backupProgress != BackUpProgressEnum.manualInProgress) { _ref.read(backupProvider.notifier).cancelBackup(); } } @@ -162,20 +243,20 @@ class AppLifeCycleNotifier extends StateNotifier { try { LogService.I.flush(); - } catch (e) { - // Ignore flush errors during pause - } + } catch (_) {} } Future handleAppDetached() async { state = AppLifeCycleEnum.detached; + if (Store.isBetaTimelineEnabled) { + unawaited(_ref.read(backgroundWorkerLockServiceProvider).unlock()); + } + // Flush logs before closing database try { LogService.I.flush(); - } catch (e) { - // Ignore flush errors during shutdown - } + } catch (_) {} // Close Isar database safely try { @@ -183,9 +264,7 @@ class AppLifeCycleNotifier extends StateNotifier { if (isar != null && isar.isOpen) { await isar.close(); } - } catch (e) { - // Ignore close errors during shutdown - } + } catch (_) {} if (Store.isBetaTimelineEnabled) { return; @@ -194,9 +273,7 @@ class AppLifeCycleNotifier extends StateNotifier { // no guarantee this is called at all try { _ref.read(manualUploadProvider.notifier).cancelBackup(); - } catch (e) { - // Ignore errors during shutdown - } + } catch (_) {} } void handleAppHidden() { @@ -205,7 +282,6 @@ class AppLifeCycleNotifier extends StateNotifier { } } -final appStateProvider = - StateNotifierProvider((ref) { +final appStateProvider = StateNotifierProvider((ref) { return AppLifeCycleNotifier(ref); }); diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 7fbacc3afb..75635950ff 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; @@ -13,6 +12,7 @@ import 'package:immich_mobile/services/etag.service.dart'; import 'package:immich_mobile/services/exif.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:logging/logging.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final assetProvider = StateNotifierProvider((ref) { return AssetNotifier( @@ -68,9 +68,7 @@ class AssetNotifier extends StateNotifier { } final bool newRemote = await _assetService.refreshRemoteAssets(); final bool newLocal = await _albumService.refreshDeviceAlbums(); - debugPrint( - "changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal", - ); + dPrint(() => "changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal"); if (newRemote) { _ref.invalidate(memoryFutureProvider); } @@ -122,17 +120,11 @@ class AssetNotifier extends StateNotifier { /// Delete remote asset only /// /// Default behavior is trashing the asset - Future deleteRemoteAssets( - Iterable deleteAssets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable deleteAssets, {bool shouldDeletePermanently = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteRemoteAssets( - deleteAssets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await _assetService.deleteRemoteAssets(deleteAssets, shouldDeletePermanently: shouldDeletePermanently); return true; } catch (error) { log.severe("Failed to delete remote assets", error); @@ -143,17 +135,11 @@ class AssetNotifier extends StateNotifier { } } - Future deleteAssets( - Iterable deleteAssets, { - bool force = false, - }) async { + Future deleteAssets(Iterable deleteAssets, {bool force = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteAssets( - deleteAssets, - shouldDeletePermanently: force, - ); + await _assetService.deleteAssets(deleteAssets, shouldDeletePermanently: force); return true; } catch (error) { log.severe("Failed to delete assets", error); @@ -174,16 +160,12 @@ class AssetNotifier extends StateNotifier { return _assetService.changeArchiveStatus(assets, status); } - Future setLockedView( - List selection, - AssetVisibilityEnum visibility, - ) { + Future setLockedView(List selection, AssetVisibilityEnum visibility) { return _assetService.setVisibility(selection, visibility); } } -final assetDetailProvider = - StreamProvider.autoDispose.family((ref, asset) async* { +final assetDetailProvider = StreamProvider.autoDispose.family((ref, asset) async* { final assetService = ref.watch(assetServiceProvider); yield await assetService.loadExif(asset); @@ -194,8 +176,7 @@ final assetDetailProvider = } }); -final assetWatcher = - StreamProvider.autoDispose.family((ref, asset) { +final assetWatcher = StreamProvider.autoDispose.family((ref, asset) { final assetService = ref.watch(assetServiceProvider); return assetService.watchAsset(asset.id, fireImmediately: true); }); diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.dart index b334ef193a..e2227920c7 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.dart @@ -17,9 +17,7 @@ class AssetPeopleNotifier extends _$AssetPeopleNotifier { return []; } - final list = await ref - .watch(assetServiceProvider) - .getRemotePeopleOfAsset(asset.remoteId!); + final list = await ref.watch(assetServiceProvider).getRemotePeopleOfAsset(asset.remoteId!); if (list == null) { return []; } diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart index ebe8a14186..031a70e0d9 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart @@ -30,13 +30,12 @@ class _SystemHash { } } -abstract class _$AssetPeopleNotifier extends BuildlessAutoDisposeAsyncNotifier< - List> { +abstract class _$AssetPeopleNotifier + extends + BuildlessAutoDisposeAsyncNotifier> { late final Asset asset; - FutureOr> build( - Asset asset, - ); + FutureOr> build(Asset asset); } /// Maintains the list of people for an asset. @@ -58,21 +57,15 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider call( - Asset asset, - ) { - return AssetPeopleNotifierProvider( - asset, - ); + AssetPeopleNotifierProvider call(Asset asset) { + return AssetPeopleNotifierProvider(asset); } @override AssetPeopleNotifierProvider getProviderOverride( covariant AssetPeopleNotifierProvider provider, ) { - return call( - provider.asset, - ); + return call(provider.asset); } static const Iterable? _dependencies = null; @@ -93,26 +86,28 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. -class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< - AssetPeopleNotifier, List> { +class AssetPeopleNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + AssetPeopleNotifier, + List + > { /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider( - Asset asset, - ) : this._internal( - () => AssetPeopleNotifier()..asset = asset, - from: assetPeopleNotifierProvider, - name: r'assetPeopleNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$assetPeopleNotifierHash, - dependencies: AssetPeopleNotifierFamily._dependencies, - allTransitiveDependencies: - AssetPeopleNotifierFamily._allTransitiveDependencies, - asset: asset, - ); + AssetPeopleNotifierProvider(Asset asset) + : this._internal( + () => AssetPeopleNotifier()..asset = asset, + from: assetPeopleNotifierProvider, + name: r'assetPeopleNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$assetPeopleNotifierHash, + dependencies: AssetPeopleNotifierFamily._dependencies, + allTransitiveDependencies: + AssetPeopleNotifierFamily._allTransitiveDependencies, + asset: asset, + ); AssetPeopleNotifierProvider._internal( super._createNotifier, { @@ -130,9 +125,7 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< FutureOr> runNotifierBuild( covariant AssetPeopleNotifier notifier, ) { - return notifier.build( - asset, - ); + return notifier.build(asset); } @override @@ -152,8 +145,11 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< } @override - AutoDisposeAsyncNotifierProviderElement> createElement() { + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + createElement() { return _AssetPeopleNotifierProviderElement(this); } @@ -180,12 +176,17 @@ mixin AssetPeopleNotifierRef } class _AssetPeopleNotifierProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AssetPeopleNotifierRef { + extends + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + with AssetPeopleNotifierRef { _AssetPeopleNotifierProviderElement(super.provider); @override Asset get asset => (origin as AssetPeopleNotifierProvider).asset; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/asset_viewer/asset_stack.provider.dart b/mobile/lib/providers/asset_viewer/asset_stack.provider.dart index 9bbbfb49aa..8772e3d0cb 100644 --- a/mobile/lib/providers/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/providers/asset_viewer/asset_stack.provider.dart @@ -32,10 +32,8 @@ class AssetStackNotifier extends StateNotifier> { } } -final assetStackStateProvider = StateNotifierProvider.autoDispose - .family, String>( - (ref, stackId) => - AssetStackNotifier(ref.watch(assetServiceProvider), stackId), +final assetStackStateProvider = StateNotifierProvider.autoDispose.family, String>( + (ref, stackId) => AssetStackNotifier(ref.watch(assetServiceProvider), stackId), ); @riverpod diff --git a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart index 53b02c2ace..e0d8d47d3a 100644 --- a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAssetHash() => r'2def10ea594152c984ae2974d687ab6856d7bdd0'; @ProviderFor(CurrentAsset) final currentAssetProvider = AutoDisposeNotifierProvider.internal( - CurrentAsset.new, - name: r'currentAssetProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAssetHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAsset.new, + name: r'currentAssetProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAssetHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAsset = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index 7f0d913a02..36b935abe7 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -18,17 +18,14 @@ class DownloadStateNotifier extends StateNotifier { final ShareService _shareService; final AlbumService _albumService; - DownloadStateNotifier( - this._downloadService, - this._shareService, - this._albumService, - ) : super( - const DownloadState( - downloadStatus: TaskStatus.complete, - showProgress: false, - taskProgress: {}, - ), - ) { + DownloadStateNotifier(this._downloadService, this._shareService, this._albumService) + : super( + const DownloadState( + downloadStatus: TaskStatus.complete, + showProgress: false, + taskProgress: {}, + ), + ) { _downloadService.onImageDownloadStatus = _downloadImageCallback; _downloadService.onVideoDownloadStatus = _downloadVideoCallback; _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; @@ -62,8 +59,7 @@ class DownloadStateNotifier extends StateNotifier { if (update.task.metaData.isEmpty) { return; } - final livePhotosId = - LivePhotosMetadata.fromJson(update.task.metaData).id; + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; _downloadService.saveLivePhotos(update.task, livePhotosId); _onDownloadComplete(update.task.taskId); break; @@ -132,9 +128,7 @@ class DownloadStateNotifier extends StateNotifier { ); if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } _albumService.refreshDeviceAlbums(); }); @@ -160,9 +154,7 @@ class DownloadStateNotifier extends StateNotifier { } if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } } @@ -170,19 +162,17 @@ class DownloadStateNotifier extends StateNotifier { showDialog( context: context, builder: (BuildContext buildContext) { - _shareService.shareAsset(asset, context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + _shareService.shareAsset(asset, context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -191,11 +181,10 @@ class DownloadStateNotifier extends StateNotifier { } } -final downloadStateProvider = - StateNotifierProvider( +final downloadStateProvider = StateNotifierProvider( ((ref) => DownloadStateNotifier( - ref.watch(downloadServiceProvider), - ref.watch(shareServiceProvider), - ref.watch(albumServiceProvider), - )), + ref.watch(downloadServiceProvider), + ref.watch(shareServiceProvider), + ref.watch(albumServiceProvider), + )), ); diff --git a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart index 4af061f954..08722dc896 100644 --- a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart +++ b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart @@ -1,8 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; /// Whether to display the video part of a motion photo -final isPlayingMotionVideoProvider = - StateNotifierProvider((ref) { +final isPlayingMotionVideoProvider = StateNotifierProvider((ref) { return IsPlayingMotionVideo(ref); }); diff --git a/mobile/lib/providers/asset_viewer/render_list_status_provider.dart b/mobile/lib/providers/asset_viewer/render_list_status_provider.dart index 903007031e..189ac85452 100644 --- a/mobile/lib/providers/asset_viewer/render_list_status_provider.dart +++ b/mobile/lib/providers/asset_viewer/render_list_status_provider.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum RenderListStatusEnum { complete, empty, error, loading } -final renderListStatusProvider = - StateNotifierProvider((ref) { +final renderListStatusProvider = StateNotifierProvider((ref) { return RenderListStatus(ref); }); diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index 3c448b112f..7b2ab5b27a 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -3,34 +3,32 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/share_intent_service.dart'; import 'package:immich_mobile/services/upload.service.dart'; +import 'package:path/path.dart'; -final shareIntentUploadProvider = StateNotifierProvider< - ShareIntentUploadStateNotifier, List>( +final shareIntentUploadProvider = StateNotifierProvider>( ((ref) => ShareIntentUploadStateNotifier( - ref.watch(appRouterProvider), - ref.watch(uploadServiceProvider), - ref.watch(shareIntentServiceProvider), - )), + ref.watch(appRouterProvider), + ref.watch(uploadServiceProvider), + ref.watch(shareIntentServiceProvider), + )), ); -class ShareIntentUploadStateNotifier - extends StateNotifier> { +class ShareIntentUploadStateNotifier extends StateNotifier> { final AppRouter router; final UploadService _uploadService; final ShareIntentService _shareIntentService; - ShareIntentUploadStateNotifier( - this.router, - this._uploadService, - this._shareIntentService, - ) : super([]) { - _uploadService.onUploadStatus = _updateUploadStatus; - _uploadService.onTaskProgress = _taskProgressCallback; + ShareIntentUploadStateNotifier(this.router, this._uploadService, this._shareIntentService) : super([]) { + _uploadService.taskStatusStream.listen(_updateUploadStatus); + _uploadService.taskProgressStream.listen(_taskProgressCallback); } void init() { @@ -53,8 +51,7 @@ class ShareIntentUploadStateNotifier } void removeAttachment(ShareIntentAttachment attachment) { - final updatedState = - state.where((element) => element != attachment).toList(); + final updatedState = state.where((element) => element != attachment).toList(); if (updatedState.length != state.length) { state = updatedState; } @@ -82,36 +79,68 @@ class ShareIntentUploadStateNotifier TaskStatus.running => UploadStatus.running, TaskStatus.paused => UploadStatus.paused, TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingToRetry + TaskStatus.waitingToRetry => UploadStatus.waitingToRetry, }; state = [ for (final attachment in state) - if (attachment.id == taskId.toInt()) - attachment.copyWith(status: uploadStatus) - else - attachment, + if (attachment.id == taskId.toInt()) attachment.copyWith(status: uploadStatus) else attachment, ]; } void _taskProgressCallback(TaskProgressUpdate update) { // Ignore if the task is canceled or completed - if (update.progress == downloadFailed || - update.progress == downloadCompleted) { + if (update.progress == downloadFailed || update.progress == downloadCompleted) { return; } final taskId = update.task.taskId; state = [ for (final attachment in state) - if (attachment.id == taskId.toInt()) - attachment.copyWith(uploadProgress: update.progress) - else - attachment, + if (attachment.id == taskId.toInt()) attachment.copyWith(uploadProgress: update.progress) else attachment, ]; } - Future upload(File file) { - return _uploadService.buildUploadTask(file, group: kManualUploadGroup); + Future upload(File file) async { + final task = await _buildUploadTask(hash(file.path).toString(), file); + + _uploadService.enqueueTasks([task]); + } + + Future _buildUploadTask(String id, File file, {Map? fields}) async { + final serverEndpoint = Store.get(StoreKey.serverEndpoint); + final url = Uri.parse('$serverEndpoint/assets').toString(); + final headers = ApiService.getRequestHeaders(); + final deviceId = Store.get(StoreKey.deviceId); + + final (baseDirectory, directory, filename) = await Task.split(filePath: file.path); + final stats = await file.stat(); + final fileCreatedAt = stats.changed; + final fileModifiedAt = stats.modified; + + final fieldsMap = { + 'filename': filename, + 'deviceAssetId': id, + 'deviceId': deviceId, + 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), + 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), + 'isFavorite': 'false', + 'duration': '0', + if (fields != null) ...fields, + }; + + return UploadTask( + taskId: id, + httpRequestMethod: 'POST', + url: url, + headers: headers, + filename: filename, + fields: fieldsMap, + baseDirectory: baseDirectory, + directory: directory, + fileField: 'assetData', + group: kManualUploadGroup, + updates: Updates.statusAndProgress, + ); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart index 69be91480f..3cfc2e2f6f 100644 --- a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart @@ -2,24 +2,18 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; class VideoPlaybackControls { - const VideoPlaybackControls({ - required this.position, - required this.pause, - this.restarted = false, - }); + const VideoPlaybackControls({required this.position, required this.pause, this.restarted = false}); final double position; final bool pause; final bool restarted; } -final videoPlayerControlsProvider = - StateNotifierProvider((ref) { +final videoPlayerControlsProvider = StateNotifierProvider((ref) { return VideoPlayerControls(ref); }); -const videoPlayerControlsDefault = - VideoPlaybackControls(position: 0, pause: false); +const videoPlayerControlsDefault = VideoPlaybackControls(position: 0, pause: false); class VideoPlayerControls extends StateNotifier { VideoPlayerControls(this.ref) : super(videoPlayerControlsDefault); @@ -64,17 +58,14 @@ class VideoPlayerControls extends StateNotifier { } void togglePlay() { - state = - VideoPlaybackControls(position: state.position, pause: !state.pause); + state = VideoPlaybackControls(position: state.position, pause: !state.pause); } void restart() { - state = - const VideoPlaybackControls(position: 0, pause: false, restarted: true); - ref.read(videoPlaybackValueProvider.notifier).value = - ref.read(videoPlaybackValueProvider.notifier).value.copyWith( - state: VideoPlaybackState.playing, - position: Duration.zero, - ); + state = const VideoPlaybackControls(position: 0, pause: false, restarted: true); + ref.read(videoPlaybackValueProvider.notifier).value = ref + .read(videoPlaybackValueProvider.notifier) + .value + .copyWith(state: VideoPlaybackState.playing, position: Duration.zero); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart index 1a3c54e9e9..c478ddd6f5 100644 --- a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart @@ -1,13 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:native_video_player/native_video_player.dart'; -enum VideoPlaybackState { - initializing, - paused, - playing, - buffering, - completed, -} +enum VideoPlaybackState { initializing, paused, playing, buffering, completed } class VideoPlaybackValue { /// The current position of the video @@ -22,16 +16,9 @@ class VideoPlaybackValue { /// The volume of the video final double volume; - const VideoPlaybackValue({ - required this.position, - required this.duration, - required this.state, - required this.volume, - }); + const VideoPlaybackValue({required this.position, required this.duration, required this.state, required this.volume}); - factory VideoPlaybackValue.fromNativeController( - NativeVideoPlayerController controller, - ) { + factory VideoPlaybackValue.fromNativeController(NativeVideoPlayerController controller) { final playbackInfo = controller.playbackInfo; final videoInfo = controller.videoInfo; @@ -53,12 +40,7 @@ class VideoPlaybackValue { ); } - VideoPlaybackValue copyWith({ - Duration? position, - Duration? duration, - VideoPlaybackState? state, - double? volume, - }) { + VideoPlaybackValue copyWith({Duration? position, Duration? duration, VideoPlaybackState? state, double? volume}) { return VideoPlaybackValue( position: position ?? this.position, duration: duration ?? this.duration, @@ -75,8 +57,7 @@ const VideoPlaybackValue videoPlaybackValueDefault = VideoPlaybackValue( volume: 0.0, ); -final videoPlaybackValueProvider = - StateNotifierProvider((ref) { +final videoPlaybackValueProvider = StateNotifierProvider((ref) { return VideoPlaybackValueState(ref); }); @@ -93,22 +74,12 @@ class VideoPlaybackValueState extends StateNotifier { set position(Duration value) { if (state.position == value) return; - state = VideoPlaybackValue( - position: value, - duration: state.duration, - state: state.state, - volume: state.volume, - ); + state = VideoPlaybackValue(position: value, duration: state.duration, state: state.state, volume: state.volume); } set status(VideoPlaybackState value) { if (state.state == value) return; - state = VideoPlaybackValue( - position: state.position, - duration: state.duration, - state: value, - volume: state.volume, - ); + state = VideoPlaybackValue(position: state.position, duration: state.duration, state: value, volume: state.volume); } void reset() { diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 2d3de11257..9a15598998 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter_udid/flutter_udid.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; @@ -13,16 +12,19 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:immich_mobile/services/widget.service.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final authProvider = StateNotifierProvider((ref) { return AuthNotifier( ref.watch(authServiceProvider), ref.watch(apiServiceProvider), ref.watch(userServiceProvider), + ref.watch(uploadServiceProvider), ref.watch(secureStorageServiceProvider), ref.watch(widgetServiceProvider), ); @@ -32,6 +34,7 @@ class AuthNotifier extends StateNotifier { final AuthService _authService; final ApiService _apiService; final UserService _userService; + final UploadService _uploadService; final SecureStorageService _secureStorageService; final WidgetService _widgetService; final _log = Logger("AuthenticationNotifier"); @@ -42,19 +45,20 @@ class AuthNotifier extends StateNotifier { this._authService, this._apiService, this._userService, + this._uploadService, this._secureStorageService, this._widgetService, ) : super( - const AuthState( - deviceId: "", - userId: "", - userEmail: "", - name: '', - profileImagePath: '', - isAdmin: false, - isAuthenticated: false, - ), - ); + const AuthState( + deviceId: "", + userId: "", + userEmail: "", + name: '', + profileImagePath: '', + isAdmin: false, + isAuthenticated: false, + ), + ); Future validateServerUrl(String url) { return _authService.validateServerUrl(url); @@ -83,6 +87,7 @@ class AuthNotifier extends StateNotifier { await _widgetService.clearCredentials(); await _authService.logout(); + await _uploadService.cancelBackup(); } finally { await _cleanUp(); } @@ -113,25 +118,20 @@ class AuthNotifier extends StateNotifier { } } - Future saveAuthInfo({ - required String accessToken, - }) async { + Future saveAuthInfo({required String accessToken}) async { await _apiService.setAccessToken(accessToken); - await _widgetService.writeCredentials( - Store.get(StoreKey.serverEndpoint), - accessToken, - ); + final serverEndpoint = Store.get(StoreKey.serverEndpoint); + final customHeaders = Store.tryGet(StoreKey.customHeaders); + await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders); // Get the deviceid from the store if it exists, otherwise generate a new one - String deviceId = - Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; + String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; UserDto? user = _userService.tryGetMyUser(); try { - final serverUser = - await _userService.refreshMyUser().timeout(_timeoutDuration); + final serverUser = await _userService.refreshMyUser().timeout(_timeoutDuration); if (serverUser == null) { _log.severe("Unable to get user information from the server."); } else { @@ -147,22 +147,10 @@ class AuthNotifier extends StateNotifier { _log.severe("Unauthorized access, token likely expired. Logging out."); return false; } - _log.severe( - "Error getting user information from the server [API EXCEPTION]", - stackTrace, - ); + _log.severe("Error getting user information from the server [API EXCEPTION]", stackTrace); } catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [CATCH ALL]", - error, - stackTrace, - ); - - if (kDebugMode) { - debugPrint( - "Error getting user information from the server [CATCH ALL] $error $stackTrace", - ); - } + _log.severe("Error getting user information from the server [CATCH ALL]", error, stackTrace); + dPrint(() => "Error getting user information from the server [CATCH ALL] $error $stackTrace"); } // If the user is null, the login was not successful @@ -178,7 +166,6 @@ class AuthNotifier extends StateNotifier { isAuthenticated: true, name: user.name, isAdmin: user.isAdmin, - profileImagePath: user.profileImagePath, ); return true; diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index dc9cc0d59f..a61cd93022 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -1,13 +1,28 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; final backgroundSyncProvider = Provider((ref) { final syncStatusNotifier = ref.read(syncStatusProvider.notifier); + final backupProvider = ref.read(driftBackupProvider.notifier); + final manager = BackgroundSyncManager( - onRemoteSyncStart: syncStatusNotifier.startRemoteSync, - onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, + onRemoteSyncStart: () { + syncStatusNotifier.startRemoteSync(); + backupProvider.updateError(BackupError.none); + }, + onRemoteSyncComplete: (isSuccess) { + syncStatusNotifier.completeRemoteSync(); + backupProvider.updateError(isSuccess == true ? BackupError.none : BackupError.syncFailed); + }, onRemoteSyncError: syncStatusNotifier.errorRemoteSync, + onLocalSyncStart: syncStatusNotifier.startLocalSync, + onLocalSyncComplete: syncStatusNotifier.completeLocalSync, + onLocalSyncError: syncStatusNotifier.errorLocalSync, + onHashingStart: syncStatusNotifier.startHashJob, + onHashingComplete: syncStatusNotifier.completeHashJob, + onHashingError: syncStatusNotifier.errorHashJob, ); ref.onDispose(manager.cancel); return manager; diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 69246398ed..03666466ff 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -2,8 +2,6 @@ import 'dart:io'; import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -33,9 +31,9 @@ import 'package:immich_mobile/utils/diff.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; +import 'package:immich_mobile/utils/debug_print.dart'; -final backupProvider = - StateNotifierProvider((ref) { +final backupProvider = StateNotifierProvider((ref) { return BackupNotifier( ref.watch(backupServiceProvider), ref.watch(serverInfoServiceProvider), @@ -61,44 +59,38 @@ class BackupNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - BackUpState( - backupProgress: BackUpProgressEnum.idle, - allAssetsInDatabase: const [], - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - autoBackup: Store.get(StoreKey.autoBackup, false), - backgroundBackup: Store.get(StoreKey.backgroundBackup, false), - backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), - backupRequireCharging: - Store.get(StoreKey.backupRequireCharging, false), - backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), - serverInfo: const ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - availableAlbums: const [], - selectedBackupAlbums: const {}, - excludedBackupAlbums: const {}, - allUniqueAssets: const {}, - selectedAlbumsBackupAssetsIds: const {}, - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - fileSize: 0, - iCloudAsset: false, - ), - iCloudDownloadProgress: 0.0, + BackUpState( + backupProgress: BackUpProgressEnum.idle, + allAssetsInDatabase: const [], + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + autoBackup: Store.get(StoreKey.autoBackup, false), + backgroundBackup: Store.get(StoreKey.backgroundBackup, false), + backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), + backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), + backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), + serverInfo: const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + availableAlbums: const [], + selectedBackupAlbums: const {}, + excludedBackupAlbums: const {}, + allUniqueAssets: const {}, + selectedAlbumsBackupAssetsIds: const {}, + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', + fileSize: 0, + iCloudAsset: false, ), - ); + iCloudDownloadProgress: 0.0, + ), + ); final log = Logger('BackupNotifier'); final BackupService _backupService; @@ -124,16 +116,14 @@ class BackupNotifier extends StateNotifier { removeExcludedAlbumForBackup(album); } - state = state - .copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); + state = state.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); } void addExcludedAlbumForBackup(AvailableAlbum album) { if (state.selectedBackupAlbums.contains(album)) { removeAlbumForBackup(album); } - state = state - .copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); + state = state.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); } void removeAlbumForBackup(AvailableAlbum album) { @@ -157,11 +147,7 @@ class BackupNotifier extends StateNotifier { // disable any backup cancelBackup(); setAutoBackup(false); - configureBackgroundBackup( - enabled: false, - onError: (msg) {}, - onBatteryInfo: () {}, - ); + configureBackgroundBackup(enabled: false, onError: (msg) {}, onBatteryInfo: () {}); } return _updateBackupAssetCount(); } @@ -179,12 +165,7 @@ class BackupNotifier extends StateNotifier { required void Function(String msg) onError, required void Function() onBatteryInfo, }) async { - assert( - enabled != null || - requireWifi != null || - requireCharging != null || - triggerDelay != null, - ); + assert(enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null); final bool wasEnabled = state.backgroundBackup; final bool wasWifi = state.backupRequireWifi; final bool wasCharging = state.backupRequireCharging; @@ -204,7 +185,8 @@ class BackupNotifier extends StateNotifier { } success &= await _backgroundService.enableService(immediate: true); } - success &= success && + success &= + success && await _backgroundService.configureService( requireUnmetered: state.backupRequireWifi, requireCharging: state.backupRequireCharging, @@ -213,10 +195,7 @@ class BackupNotifier extends StateNotifier { ); if (success) { await Store.put(StoreKey.backupRequireWifi, state.backupRequireWifi); - await Store.put( - StoreKey.backupRequireCharging, - state.backupRequireCharging, - ); + await Store.put(StoreKey.backupRequireCharging, state.backupRequireCharging); await Store.put(StoreKey.backupTriggerDelay, state.backupTriggerDelay); await Store.put(StoreKey.backgroundBackup, state.backgroundBackup); } else { @@ -257,9 +236,7 @@ class BackupNotifier extends StateNotifier { for (Album album in albums) { AvailableAlbum availableAlbum = AvailableAlbum( album: album, - assetCount: await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.localId!), + assetCount: await ref.read(albumMediaRepositoryProvider).getAssetCount(album.localId!), ); availableAlbums.add(availableAlbum); @@ -268,10 +245,8 @@ class BackupNotifier extends StateNotifier { } state = state.copyWith(availableAlbums: availableAlbums); - final List excludedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.exclude); - final List selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); + final List excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); + final List selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); final Set selectedAlbums = {}; for (final BackupAlbum ba in selectedBackupAlbums) { @@ -281,8 +256,7 @@ class BackupNotifier extends StateNotifier { selectedAlbums.add( AvailableAlbum( album: albumAsset, - assetCount: - await _albumMediaRepository.getAssetCount(albumAsset.localId!), + assetCount: await _albumMediaRepository.getAssetCount(albumAsset.localId!), lastBackup: ba.lastBackup, ), ); @@ -299,9 +273,7 @@ class BackupNotifier extends StateNotifier { excludedAlbums.add( AvailableAlbum( album: albumAsset, - assetCount: await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(albumAsset.localId!), + assetCount: await ref.read(albumMediaRepositoryProvider).getAssetCount(albumAsset.localId!), lastBackup: ba.lastBackup, ), ); @@ -310,15 +282,10 @@ class BackupNotifier extends StateNotifier { } } - state = state.copyWith( - selectedBackupAlbums: selectedAlbums, - excludedBackupAlbums: excludedAlbums, - ); + state = state.copyWith(selectedBackupAlbums: selectedAlbums, excludedBackupAlbums: excludedAlbums); - log.info( - "_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums", - ); - debugPrint("_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms"); + log.info("_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums"); + dPrint(() => "_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms"); } /// @@ -335,62 +302,44 @@ class BackupNotifier extends StateNotifier { final Set assetsFromExcludedAlbums = {}; for (final album in state.selectedBackupAlbums) { - final assetCount = await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.album.localId!); + final assetCount = await ref.read(albumMediaRepositoryProvider).getAssetCount(album.album.localId!); if (assetCount == 0) { continue; } - final assets = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.album.localId!); + final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); // Add album's name to the asset info for (final asset in assets) { List albumNames = [album.name]; - final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull( - (a) => a.asset.localId == asset.localId, - ); + final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull((a) => a.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); assetsFromSelectedAlbums.remove(existingAsset); } - assetsFromSelectedAlbums.add( - BackupCandidate( - asset: asset, - albumNames: albumNames, - ), - ); + assetsFromSelectedAlbums.add(BackupCandidate(asset: asset, albumNames: albumNames)); } } for (final album in state.excludedBackupAlbums) { - final assetCount = await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.album.localId!); + final assetCount = await ref.read(albumMediaRepositoryProvider).getAssetCount(album.album.localId!); if (assetCount == 0) { continue; } - final assets = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.album.localId!); + final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); for (final asset in assets) { - assetsFromExcludedAlbums.add( - BackupCandidate(asset: asset, albumNames: [album.name]), - ); + assetsFromExcludedAlbums.add(BackupCandidate(asset: asset, albumNames: [album.name])); } } - final Set allUniqueAssets = - assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); + final Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); final allAssetsInDatabase = await _backupService.getDeviceBackupAsset(); @@ -399,16 +348,12 @@ class BackupNotifier extends StateNotifier { } // Find asset that were backup from selected albums - final Set selectedAlbumsBackupAssets = - Set.from(allUniqueAssets.map((e) => e.asset.localId)); + final Set selectedAlbumsBackupAssets = Set.from(allUniqueAssets.map((e) => e.asset.localId)); - selectedAlbumsBackupAssets - .removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); + selectedAlbumsBackupAssets.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); // Remove duplicated asset from all unique assets - allUniqueAssets.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + allUniqueAssets.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (allUniqueAssets.isEmpty) { log.info("No assets are selected for back up"); @@ -459,8 +404,7 @@ class BackupNotifier extends StateNotifier { final candidates = selected.followedBy(excluded).toList(); candidates.sortBy((e) => e.id); - final savedBackupAlbums = - await _backupAlbumService.getAll(sort: BackupAlbumSort.id); + final savedBackupAlbums = await _backupAlbumService.getAll(sort: BackupAlbumSort.id); final List toDelete = []; final List toUpsert = []; @@ -469,8 +413,7 @@ class BackupNotifier extends StateNotifier { candidates, compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id), both: (BackupAlbum a, BackupAlbum b) { - b.lastBackup = - a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; + b.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; toUpsert.add(b); return true; }, @@ -484,7 +427,7 @@ class BackupNotifier extends StateNotifier { /// Invoke backup process Future startBackupProcess() async { - debugPrint("Start backup process"); + dPrint(() => "Start backup process"); assert(state.backupProgress == BackUpProgressEnum.idle); state = state.copyWith(backupProgress: BackUpProgressEnum.inProgress); @@ -536,9 +479,7 @@ class BackupNotifier extends StateNotifier { } void setAvailableAlbums(availableAlbums) { - state = state.copyWith( - availableAlbums: availableAlbums, - ); + state = state.copyWith(availableAlbums: availableAlbums); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { @@ -568,40 +509,23 @@ class BackupNotifier extends StateNotifier { if (result.isDuplicate) { state = state.copyWith( allUniqueAssets: state.allUniqueAssets - .where( - (candidate) => - candidate.asset.localId != result.candidate.asset.localId, - ) + .where((candidate) => candidate.asset.localId != result.candidate.asset.localId) .toSet(), ); } else { state = state.copyWith( - selectedAlbumsBackupAssetsIds: { - ...state.selectedAlbumsBackupAssetsIds, - result.candidate.asset.localId!, - }, - allAssetsInDatabase: [ - ...state.allAssetsInDatabase, - result.candidate.asset.localId!, - ], + selectedAlbumsBackupAssetsIds: {...state.selectedAlbumsBackupAssetsIds, result.candidate.asset.localId!}, + allAssetsInDatabase: [...state.allAssetsInDatabase, result.candidate.asset.localId!], ); } - if (state.allUniqueAssets.length - - state.selectedAlbumsBackupAssetsIds.length == - 0) { + if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { final latestAssetBackup = state.allUniqueAssets .map((candidate) => candidate.asset.fileModifiedAt) - .reduce( - (v, e) => e.isAfter(v) ? e : v, - ); + .reduce((v, e) => e.isAfter(v) ? e : v); state = state.copyWith( - selectedBackupAlbums: state.selectedBackupAlbums - .map((e) => e.copyWith(lastBackup: latestAssetBackup)) - .toSet(), - excludedBackupAlbums: state.excludedBackupAlbums - .map((e) => e.copyWith(lastBackup: latestAssetBackup)) - .toSet(), + selectedBackupAlbums: state.selectedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), + excludedBackupAlbums: state.excludedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0, progressInFileSize: "0 B / 0 B", @@ -630,9 +554,7 @@ class BackupNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -654,9 +576,7 @@ class BackupNotifier extends StateNotifier { // Update server info if (diskInfo != null) { - state = state.copyWith( - serverInfo: diskInfo, - ); + state = state.copyWith(serverInfo: diskInfo); } } @@ -696,24 +616,16 @@ class BackupNotifier extends StateNotifier { } Future resumeBackup() async { - final List selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); - final List excludedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.exclude); + final List selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); + final List excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); Set selectedAlbums = state.selectedBackupAlbums; Set excludedAlbums = state.excludedBackupAlbums; if (selectedAlbums.isNotEmpty) { - selectedAlbums = _updateAlbumsBackupTime( - selectedAlbums, - selectedBackupAlbums, - ); + selectedAlbums = _updateAlbumsBackupTime(selectedAlbums, selectedBackupAlbums); } if (excludedAlbums.isNotEmpty) { - excludedAlbums = _updateAlbumsBackupTime( - excludedAlbums, - excludedBackupAlbums, - ); + excludedAlbums = _updateAlbumsBackupTime(excludedAlbums, excludedBackupAlbums); } final BackUpProgressEnum previous = state.backupProgress; state = state.copyWith( @@ -730,32 +642,21 @@ class BackupNotifier extends StateNotifier { return _resumeBackup(); } - Set _updateAlbumsBackupTime( - Set albums, - List backupAlbums, - ) { + Set _updateAlbumsBackupTime(Set albums, List backupAlbums) { Set result = {}; for (BackupAlbum ba in backupAlbums) { try { AvailableAlbum a = albums.firstWhere((e) => e.id == ba.id); result.add(a.copyWith(lastBackup: ba.lastBackup)); } on StateError { - log.severe( - "[_updateAlbumBackupTime] failed to find album in state", - "State Error", - StackTrace.current, - ); + log.severe("[_updateAlbumBackupTime] failed to find album in state", "State Error", StackTrace.current); } } return result; } Future notifyBackgroundServiceCanRun() async { - const allowedStates = [ - AppLifeCycleEnum.inactive, - AppLifeCycleEnum.paused, - AppLifeCycleEnum.detached, - ]; + const allowedStates = [AppLifeCycleEnum.inactive, AppLifeCycleEnum.paused, AppLifeCycleEnum.detached]; if (allowedStates.contains(ref.read(appStateProvider.notifier).state)) { _backgroundService.releaseLock(); } diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart index 2915c7c216..f81f905c2f 100644 --- a/mobile/lib/providers/backup/backup_album.provider.dart +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -4,11 +4,8 @@ import 'package:immich_mobile/domain/services/local_album.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -final backupAlbumProvider = - StateNotifierProvider>( - (ref) => BackupAlbumNotifier( - ref.watch(localAlbumServiceProvider), - ), +final backupAlbumProvider = StateNotifierProvider>( + (ref) => BackupAlbumNotifier(ref.watch(localAlbumServiceProvider)), ); class BackupAlbumNotifier extends StateNotifier> { @@ -19,8 +16,7 @@ class BackupAlbumNotifier extends StateNotifier> { final LocalAlbumService _localAlbumService; Future getAll() async { - state = - await _localAlbumService.getAll(sortBy: {SortLocalAlbumsBy.assetCount}); + state = await _localAlbumService.getAll(sortBy: {SortLocalAlbumsBy.assetCount}); } Future selectAlbum(LocalAlbum album) async { @@ -42,9 +38,8 @@ class BackupAlbumNotifier extends StateNotifier> { state = state .map( - (currentAlbum) => currentAlbum.id == album.id - ? currentAlbum.copyWith(backupSelection: BackupSelection.none) - : currentAlbum, + (currentAlbum) => + currentAlbum.id == album.id ? currentAlbum.copyWith(backupSelection: BackupSelection.none) : currentAlbum, ) .toList(); } diff --git a/mobile/lib/providers/backup/backup_verification.provider.dart b/mobile/lib/providers/backup/backup_verification.provider.dart index 5881814320..da4253576b 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.dart @@ -23,8 +23,7 @@ class BackupVerification extends _$BackupVerification { state = true; final backupState = ref.read(backupProvider); - if (backupState.allUniqueAssets.length > - backupState.selectedAlbumsBackupAssetsIds.length) { + if (backupState.allUniqueAssets.length > backupState.selectedAlbumsBackupAssetsIds.length) { if (context.mounted) { ImmichToast.show( context: context, @@ -48,9 +47,7 @@ class BackupVerification extends _$BackupVerification { WakelockPlus.enable(); const limit = 100; - final toDelete = await ref - .read(backupVerificationServiceProvider) - .findWronglyBackedUpAssets(limit: limit); + final toDelete = await ref.read(backupVerificationServiceProvider).findWronglyBackedUpAssets(limit: limit); if (toDelete.isEmpty) { if (context.mounted) { ImmichToast.show( @@ -81,23 +78,18 @@ class BackupVerification extends _$BackupVerification { } } - Future _performDeletion( - BuildContext context, - List assets, - ) async { + Future _performDeletion(BuildContext context, List assets) async { try { state = true; if (context.mounted) { - ImmichToast.show( - context: context, - msg: "Deleting ${assets.length} assets on the server...", - ); + ImmichToast.show(context: context, msg: "Deleting ${assets.length} assets on the server..."); } await ref.read(assetProvider.notifier).deleteAssets(assets, force: true); if (context.mounted) { ImmichToast.show( context: context, - msg: "Deleted ${assets.length} assets on the server. " + msg: + "Deleted ${assets.length} assets on the server. " "You can now start a manual backup", toastType: ToastType.success, ); diff --git a/mobile/lib/providers/backup/backup_verification.provider.g.dart b/mobile/lib/providers/backup/backup_verification.provider.g.dart index bae3ec366b..727e06a12c 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.g.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.g.dart @@ -13,14 +13,14 @@ String _$backupVerificationHash() => @ProviderFor(BackupVerification) final backupVerificationProvider = AutoDisposeNotifierProvider.internal( - BackupVerification.new, - name: r'backupVerificationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$backupVerificationHash, - dependencies: null, - allTransitiveDependencies: null, -); + BackupVerification.new, + name: r'backupVerificationProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$backupVerificationHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$BackupVerification = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index c51c40775e..f52fc654f2 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -1,38 +1,32 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:async'; -import 'dart:convert'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/services/drift_backup.service.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/string_extensions.dart'; +import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/upload.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:logging/logging.dart'; class EnqueueStatus { final int enqueueCount; final int totalCount; - const EnqueueStatus({ - required this.enqueueCount, - required this.totalCount, - }); + const EnqueueStatus({required this.enqueueCount, required this.totalCount}); - EnqueueStatus copyWith({ - int? enqueueCount, - int? totalCount, - }) { - return EnqueueStatus( - enqueueCount: enqueueCount ?? this.enqueueCount, - totalCount: totalCount ?? this.totalCount, - ); + EnqueueStatus copyWith({int? enqueueCount, int? totalCount}) { + return EnqueueStatus(enqueueCount: enqueueCount ?? this.enqueueCount, totalCount: totalCount ?? this.totalCount); } @override - String toString() => - 'EnqueueStatus(enqueueCount: $enqueueCount, totalCount: $totalCount)'; + String toString() => 'EnqueueStatus(enqueueCount: $enqueueCount, totalCount: $totalCount)'; } class DriftUploadStatus { @@ -41,6 +35,8 @@ class DriftUploadStatus { final double progress; final int fileSize; final String networkSpeedAsString; + final bool? isFailed; + final String? error; const DriftUploadStatus({ required this.taskId, @@ -48,6 +44,8 @@ class DriftUploadStatus { required this.progress, required this.fileSize, required this.networkSpeedAsString, + this.isFailed, + this.error, }); DriftUploadStatus copyWith({ @@ -56,6 +54,8 @@ class DriftUploadStatus { double? progress, int? fileSize, String? networkSpeedAsString, + bool? isFailed, + String? error, }) { return DriftUploadStatus( taskId: taskId ?? this.taskId, @@ -63,12 +63,14 @@ class DriftUploadStatus { progress: progress ?? this.progress, fileSize: fileSize ?? this.fileSize, networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, + isFailed: isFailed ?? this.isFailed, + error: error ?? this.error, ); } @override String toString() { - return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString)'; + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed, error: $error)'; } @override @@ -79,7 +81,9 @@ class DriftUploadStatus { other.filename == filename && other.progress == progress && other.fileSize == fileSize && - other.networkSpeedAsString == networkSpeedAsString; + other.networkSpeedAsString == networkSpeedAsString && + other.isFailed == isFailed && + other.error == error; } @override @@ -88,44 +92,26 @@ class DriftUploadStatus { filename.hashCode ^ progress.hashCode ^ fileSize.hashCode ^ - networkSpeedAsString.hashCode; + networkSpeedAsString.hashCode ^ + isFailed.hashCode ^ + error.hashCode; } - - Map toMap() { - return { - 'taskId': taskId, - 'filename': filename, - 'progress': progress, - 'fileSize': fileSize, - 'networkSpeedAsString': networkSpeedAsString, - }; - } - - factory DriftUploadStatus.fromMap(Map map) { - return DriftUploadStatus( - taskId: map['taskId'] as String, - filename: map['filename'] as String, - progress: map['progress'] as double, - fileSize: map['fileSize'] as int, - networkSpeedAsString: map['networkSpeedAsString'] as String, - ); - } - - String toJson() => json.encode(toMap()); - - factory DriftUploadStatus.fromJson(String source) => - DriftUploadStatus.fromMap(json.decode(source) as Map); } +enum BackupError { none, syncFailed } + class DriftBackupState { final int totalCount; final int backupCount; final int remainderCount; + final int processingCount; final int enqueueCount; final int enqueueTotalCount; + final bool isSyncing; final bool isCanceling; + final BackupError error; final Map uploadItems; @@ -133,35 +119,44 @@ class DriftBackupState { required this.totalCount, required this.backupCount, required this.remainderCount, + required this.processingCount, required this.enqueueCount, required this.enqueueTotalCount, required this.isCanceling, + required this.isSyncing, required this.uploadItems, + this.error = BackupError.none, }); DriftBackupState copyWith({ int? totalCount, int? backupCount, int? remainderCount, + int? processingCount, int? enqueueCount, int? enqueueTotalCount, bool? isCanceling, + bool? isSyncing, Map? uploadItems, + BackupError? error, }) { return DriftBackupState( totalCount: totalCount ?? this.totalCount, backupCount: backupCount ?? this.backupCount, remainderCount: remainderCount ?? this.remainderCount, + processingCount: processingCount ?? this.processingCount, enqueueCount: enqueueCount ?? this.enqueueCount, enqueueTotalCount: enqueueTotalCount ?? this.enqueueTotalCount, isCanceling: isCanceling ?? this.isCanceling, + isSyncing: isSyncing ?? this.isSyncing, uploadItems: uploadItems ?? this.uploadItems, + error: error ?? this.error, ); } @override String toString() { - return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, uploadItems: $uploadItems)'; + return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, processingCount: $processingCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, isSyncing: $isSyncing, uploadItems: $uploadItems, error: $error)'; } @override @@ -172,10 +167,13 @@ class DriftBackupState { return other.totalCount == totalCount && other.backupCount == backupCount && other.remainderCount == remainderCount && + other.processingCount == processingCount && other.enqueueCount == enqueueCount && other.enqueueTotalCount == enqueueTotalCount && other.isCanceling == isCanceling && - mapEquals(other.uploadItems, uploadItems); + other.isSyncing == isSyncing && + mapEquals(other.uploadItems, uploadItems) && + other.error == error; } @override @@ -183,76 +181,104 @@ class DriftBackupState { return totalCount.hashCode ^ backupCount.hashCode ^ remainderCount.hashCode ^ + processingCount.hashCode ^ enqueueCount.hashCode ^ enqueueTotalCount.hashCode ^ isCanceling.hashCode ^ - uploadItems.hashCode; + isSyncing.hashCode ^ + uploadItems.hashCode ^ + error.hashCode; } } -final driftBackupProvider = - StateNotifierProvider((ref) { - return ExpBackupNotifier( - ref.watch(driftBackupServiceProvider), - ref.watch(uploadServiceProvider), - ); +final driftBackupProvider = StateNotifierProvider((ref) { + return DriftBackupNotifier(ref.watch(uploadServiceProvider)); }); -class ExpBackupNotifier extends StateNotifier { - ExpBackupNotifier( - this._backupService, - this._uploadService, - ) : super( - const DriftBackupState( - totalCount: 0, - backupCount: 0, - remainderCount: 0, - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: false, - uploadItems: {}, - ), - ) { +class DriftBackupNotifier extends StateNotifier { + DriftBackupNotifier(this._uploadService) + : super( + const DriftBackupState( + totalCount: 0, + backupCount: 0, + remainderCount: 0, + processingCount: 0, + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: false, + isSyncing: false, + uploadItems: {}, + error: BackupError.none, + ), + ) { { _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); _uploadService.taskProgressStream.listen(_handleTaskProgressUpdate); } } - final DriftBackupService _backupService; final UploadService _uploadService; StreamSubscription? _statusSubscription; StreamSubscription? _progressSubscription; + final _logger = Logger("DriftBackupNotifier"); /// Remove upload item from state void _removeUploadItem(String taskId) { if (state.uploadItems.containsKey(taskId)) { - final updatedItems = - Map.from(state.uploadItems); + final updatedItems = Map.from(state.uploadItems); updatedItems.remove(taskId); state = state.copyWith(uploadItems: updatedItems); } } void _handleTaskStatusUpdate(TaskStatusUpdate update) { + final taskId = update.task.taskId; + switch (update.status) { case TaskStatus.complete: if (update.task.group == kBackupGroup) { - state = state.copyWith( - backupCount: state.backupCount + 1, - remainderCount: state.remainderCount - 1, - ); + if (update.responseStatusCode == 201) { + state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1); + } } // Remove the completed task from the upload items - final taskId = update.task.taskId; if (state.uploadItems.containsKey(taskId)) { - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 1000), () { _removeUploadItem(taskId); }); } case TaskStatus.failed: + // Ignore retry errors to avoid confusing users + if (update.exception?.description == 'Delayed or retried enqueue failed') { + _removeUploadItem(taskId); + return; + } + + final currentItem = state.uploadItems[taskId]; + if (currentItem == null) { + return; + } + + String? error; + final exception = update.exception; + if (exception != null && exception is TaskHttpException) { + final message = tryJsonDecode(exception.description)?['message'] as String?; + if (message != null) { + final responseCode = exception.httpResponseCode; + error = "${exception.exceptionType}, response code $responseCode: $message"; + } + } + error ??= update.exception?.toString(); + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: currentItem.copyWith(isFailed: true, error: error), + }, + ); + _logger.fine("Upload failed for taskId: $taskId, exception: ${update.exception}"); break; case TaskStatus.canceled: @@ -284,9 +310,7 @@ class ExpBackupNotifier extends StateNotifier { fileSize: update.expectedFileSize, networkSpeedAsString: update.networkSpeedAsString, ) - : currentItem.copyWith( - progress: progress, - ), + : currentItem.copyWith(progress: progress), }, ); @@ -307,65 +331,64 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future getBackupStatus() async { - final [totalCount, backupCount, remainderCount] = await Future.wait([ - _backupService.getTotalCount(), - _backupService.getBackupCount(), - _backupService.getRemainderCount(), - ]); + Future getBackupStatus(String userId) async { + final counts = await _uploadService.getBackupCounts(userId); state = state.copyWith( - totalCount: totalCount, - backupCount: backupCount, - remainderCount: remainderCount, + totalCount: counts.total, + backupCount: counts.total - counts.remainder, + remainderCount: counts.remainder, + processingCount: counts.processing, ); } - Future backup() { - return _backupService.backup(_updateEnqueueCount); + void updateError(BackupError error) async { + state = state.copyWith(error: error); + } + + void updateSyncing(bool isSyncing) async { + state = state.copyWith(isSyncing: isSyncing); + } + + Future startBackup(String userId) { + state = state.copyWith(error: BackupError.none); + return _uploadService.startBackup(userId, _updateEnqueueCount); } void _updateEnqueueCount(EnqueueStatus status) { - state = state.copyWith( - enqueueCount: status.enqueueCount, - enqueueTotalCount: status.totalCount, - ); + state = state.copyWith(enqueueCount: status.enqueueCount, enqueueTotalCount: status.totalCount); } Future cancel() async { - state = state.copyWith( - enqueueCount: 0, - enqueueTotalCount: 0, - isCanceling: true, - ); + dPrint(() => "Canceling backup tasks..."); + state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true, error: BackupError.none); - await _backupService.cancel(); + final activeTaskCount = await _uploadService.cancelBackup(); - // Check if there are any tasks left in the queue - final tasks = await FileDownloader().allTasks(group: kBackupGroup); - - debugPrint("Tasks left to cancel: ${tasks.length}"); - - if (tasks.isNotEmpty) { + if (activeTaskCount > 0) { + dPrint(() => "$activeTaskCount tasks left, continuing to cancel..."); await cancel(); } else { + dPrint(() => "All tasks canceled successfully."); // Clear all upload items when cancellation is complete - state = state.copyWith( - isCanceling: false, - uploadItems: {}, - ); + state = state.copyWith(isCanceling: false, uploadItems: {}); } } - Future handleBackupResume() async { - final tasks = await FileDownloader().allTasks(group: kBackupGroup); + Future handleBackupResume(String userId) async { + _logger.info("Resuming backup tasks..."); + state = state.copyWith(error: BackupError.none); + final tasks = await _uploadService.getActiveTasks(kBackupGroup); + _logger.info("Found ${tasks.length} tasks"); + if (tasks.isEmpty) { // Start a new backup queue - await backup(); + _logger.info("Start a new backup queue"); + return startBackup(userId); } - debugPrint("Tasks to resume: ${tasks.length}"); - await FileDownloader().start(); + _logger.info("Tasks to resume: ${tasks.length}"); + return _uploadService.resumeBackup(); } @override @@ -375,3 +398,19 @@ class ExpBackupNotifier extends StateNotifier { super.dispose(); } } + +final driftBackupCandidateProvider = FutureProvider.autoDispose>((ref) async { + final user = ref.watch(currentUserProvider); + if (user == null) { + return []; + } + + return ref.read(backupRepositoryProvider).getCandidates(user.id, onlyHashed: false); +}); + +final driftCandidateBackupAlbumInfoProvider = FutureProvider.autoDispose.family, String>(( + ref, + assetId, +) { + return ref.read(localAssetRepository).getSourceAlbums(assetId, backupSelection: BackupSelection.selected); +}); diff --git a/mobile/lib/providers/backup/error_backup_list.provider.dart b/mobile/lib/providers/backup/error_backup_list.provider.dart index 22ff995905..db116e4bb9 100644 --- a/mobile/lib/providers/backup/error_backup_list.provider.dart +++ b/mobile/lib/providers/backup/error_backup_list.provider.dart @@ -17,7 +17,6 @@ class ErrorBackupListNotifier extends StateNotifier> { } } -final errorBackupListProvider = - StateNotifierProvider>( +final errorBackupListProvider = StateNotifierProvider>( (ref) => ErrorBackupListNotifier(), ); diff --git a/mobile/lib/providers/backup/ios_background_settings.provider.dart b/mobile/lib/providers/backup/ios_background_settings.provider.dart index d70e674845..98d55882cc 100644 --- a/mobile/lib/providers/backup/ios_background_settings.provider.dart +++ b/mobile/lib/providers/backup/ios_background_settings.provider.dart @@ -15,21 +15,17 @@ class IOSBackgroundSettings { }); } -class IOSBackgroundSettingsNotifier - extends StateNotifier { +class IOSBackgroundSettingsNotifier extends StateNotifier { final BackgroundService _service; IOSBackgroundSettingsNotifier(this._service) : super(null); IOSBackgroundSettings? get settings => state; Future refresh() async { - final lastFetchTime = - await _service.getIOSBackupLastRun(IosBackgroundTask.fetch); - final lastProcessingTime = - await _service.getIOSBackupLastRun(IosBackgroundTask.processing); + final lastFetchTime = await _service.getIOSBackupLastRun(IosBackgroundTask.fetch); + final lastProcessingTime = await _service.getIOSBackupLastRun(IosBackgroundTask.processing); int numberOfProcesses = await _service.getIOSBackupNumberOfProcesses(); - final appRefreshEnabled = - await _service.getIOSBackgroundAppRefreshEnabled(); + final appRefreshEnabled = await _service.getIOSBackgroundAppRefreshEnabled(); // If this is enabled and there are no background processes, // the user just enabled app refresh in Settings. @@ -53,7 +49,6 @@ class IOSBackgroundSettingsNotifier } } -final iOSBackgroundSettingsProvider = StateNotifierProvider< - IOSBackgroundSettingsNotifier, IOSBackgroundSettings?>( +final iOSBackgroundSettingsProvider = StateNotifierProvider( (ref) => IOSBackgroundSettingsNotifier(ref.watch(backgroundServiceProvider)), ); diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index 646a03cebc..bfc079bfa3 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -30,9 +30,9 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; +import 'package:immich_mobile/utils/debug_print.dart'; -final manualUploadProvider = - StateNotifierProvider((ref) { +final manualUploadProvider = StateNotifierProvider((ref) { return ManualUploadNotifier( ref.watch(localNotificationService), ref.watch(backupProvider.notifier), @@ -57,45 +57,43 @@ class ManualUploadNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - ManualUploadState( - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - ), - totalAssetsToUpload: 0, - successfulUploads: 0, - currentAssetIndex: 0, - showDetailedNotification: false, + ManualUploadState( + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', ), - ); + totalAssetsToUpload: 0, + successfulUploads: 0, + currentAssetIndex: 0, + showDetailedNotification: false, + ), + ); String _lastPrintedDetailContent = ''; String? _lastPrintedDetailTitle; static const notifyInterval = Duration(milliseconds: 500); - late final ThrottleProgressUpdate _throttledNotifiy = - ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); void _updateProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { _localNotificationService.showOrUpdateManualUploadStatus( "backup_background_service_in_progress_notification".tr(), - formatAssetBackupProgress( - state.currentAssetIndex, - state.totalAssetsToUpload, - ), + formatAssetBackupProgress(state.currentAssetIndex, state.totalAssetsToUpload), maxProgress: state.totalAssetsToUpload, progress: state.currentAssetIndex, showActions: true, @@ -106,11 +104,9 @@ class ManualUploadNotifier extends StateNotifier { void _updateDetailProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { - final String msg = - total > 0 ? humanReadableBytesProgress(progress, total) : ""; + final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : ""; // only update if message actually differs (to stop many useless notification updates on large assets or slow connections) - if (msg != _lastPrintedDetailContent || - title != _lastPrintedDetailTitle) { + if (msg != _lastPrintedDetailContent || title != _lastPrintedDetailTitle) { _lastPrintedDetailContent = msg; _lastPrintedDetailTitle = title; _localNotificationService.showOrUpdateManualUploadStatus( @@ -150,9 +146,7 @@ class ManualUploadNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -169,24 +163,22 @@ class ManualUploadNotifier extends StateNotifier { ); if (state.showDetailedNotification) { - final title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': state.currentUploadAsset.fileName}); + final title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': state.currentUploadAsset.fileName}, + ); _throttledDetailNotify(title: title, progress: sent, total: total); } } void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset) { - state = state.copyWith( - currentUploadAsset: currentUploadAsset, - currentAssetIndex: state.currentAssetIndex + 1, - ); + state = state.copyWith(currentUploadAsset: currentUploadAsset, currentAssetIndex: state.currentAssetIndex + 1); if (state.totalAssetsToUpload > 1) { _throttledNotifiy(); } if (state.showDetailedNotification) { - _throttledDetailNotify.title = - "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } @@ -200,8 +192,7 @@ class ManualUploadNotifier extends StateNotifier { if (ref.read(galleryPermissionNotifier.notifier).hasPermission) { await ref.read(fileMediaRepositoryProvider).clearFileCache(); - final allAssetsFromDevice = - allManualUploads.where((e) => e.isLocal && !e.isRemote).toList(); + final allAssetsFromDevice = allManualUploads.where((e) => e.isLocal && !e.isRemote).toList(); if (allAssetsFromDevice.length != allManualUploads.length) { _log.warning( @@ -209,14 +200,11 @@ class ManualUploadNotifier extends StateNotifier { ); } - final selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); - final excludedBackupAlbums = await _backupAlbumService - .getAllBySelection(BackupSelection.exclude); + final selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); + final excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); // Get candidates from selected albums and excluded albums - Set candidates = - await _backupService.buildUploadCandidates( + Set candidates = await _backupService.buildUploadCandidates( selectedBackupAlbums, excludedBackupAlbums, useTimeFilter: false, @@ -225,14 +213,11 @@ class ManualUploadNotifier extends StateNotifier { // Extrack candidate from allAssetsFromDevice final uploadAssets = candidates.where( (candidate) => - allAssetsFromDevice.firstWhereOrNull( - (asset) => asset.localId == candidate.asset.localId, - ) != - null, + allAssetsFromDevice.firstWhereOrNull((asset) => asset.localId == candidate.asset.localId) != null, ); if (uploadAssets.isEmpty) { - debugPrint("[_startUpload] No Assets to upload - Abort Process"); + dPrint(() => "[_startUpload] No Assets to upload - Abort Process"); _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); return false; } @@ -261,15 +246,14 @@ class ManualUploadNotifier extends StateNotifier { // Show detailed asset if enabled in settings or if a single asset is uploaded bool showDetailedNotification = - ref.read(appSettingsServiceProvider).getSetting( - AppSettingsEnum.backgroundBackupSingleProgress, - ) || - state.totalAssetsToUpload == 1; - state = - state.copyWith(showDetailedNotification: showDetailedNotification); + ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.backgroundBackupSingleProgress) || + state.totalAssetsToUpload == 1; + state = state.copyWith(showDetailedNotification: showDetailedNotification); final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; - final bool ok = await ref.read(backupServiceProvider).backupAsset( + final bool ok = await ref + .read(backupServiceProvider) + .backupAsset( uploadAssets, state.cancelToken, pmProgressHandler: pmProgressHandler, @@ -280,9 +264,7 @@ class ManualUploadNotifier extends StateNotifier { ); // Close detailed notification - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); _log.info( '[_startUpload] Manual Upload Completed - success: ${state.successfulUploads},' @@ -297,8 +279,7 @@ class ManualUploadNotifier extends StateNotifier { presentBanner: true, ); hasErrors = true; - } else if (state.successfulUploads == 0 || - (!ok && !state.cancelToken.isCancelled)) { + } else if (state.successfulUploads == 0 || (!ok && !state.cancelToken.isCancelled)) { await _localNotificationService.showOrUpdateManualUploadStatus( "backup_manual_title".tr(), "failed".tr(), @@ -314,17 +295,15 @@ class ManualUploadNotifier extends StateNotifier { } } else { openAppSettings(); - debugPrint("[_startUpload] Do not have permission to the gallery"); + dPrint(() => "[_startUpload] Do not have permission to the gallery"); } } catch (e) { - debugPrint("ERROR _startUpload: ${e.toString()}"); + dPrint(() => "ERROR _startUpload: ${e.toString()}"); hasErrors = true; } finally { _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); _handleAppInActivity(); - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); await _backupProvider.notifyBackgroundServiceCanRun(); } return !hasErrors; @@ -334,8 +313,7 @@ class ManualUploadNotifier extends StateNotifier { final appState = ref.read(appStateProvider.notifier).getAppState(); // The app is currently in background. Perform the necessary cleanups which // are on-hold for upload completion - if (appState != AppLifeCycleEnum.active && - appState != AppLifeCycleEnum.resumed) { + if (appState != AppLifeCycleEnum.active && appState != AppLifeCycleEnum.resumed) { ref.read(backupProvider.notifier).cancelBackup(); } } @@ -358,16 +336,12 @@ class ManualUploadNotifier extends StateNotifier { ); } - Future uploadAssets( - BuildContext context, - Iterable allManualUploads, - ) async { + Future uploadAssets(BuildContext context, Iterable allManualUploads) async { // assumes the background service is currently running and // waits until it has stopped to start the backup. - final bool hasLock = - await ref.read(backgroundServiceProvider).acquireLock(); + final bool hasLock = await ref.read(backgroundServiceProvider).acquireLock(); if (!hasLock) { - debugPrint("[uploadAssets] could not acquire lock, exiting"); + dPrint(() => "[uploadAssets] could not acquire lock, exiting"); ImmichToast.show( context: context, msg: "failed".tr(), @@ -382,18 +356,18 @@ class ManualUploadNotifier extends StateNotifier { // check if backup is already in process - then return if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { - debugPrint("[uploadAssets] Manual upload is already running - abort"); + dPrint(() => "[uploadAssets] Manual upload is already running - abort"); showInProgress = true; } if (_backupProvider.backupProgress == BackUpProgressEnum.inProgress) { - debugPrint("[uploadAssets] Auto Backup is already in progress - abort"); + dPrint(() => "[uploadAssets] Auto Backup is already in progress - abort"); showInProgress = true; return false; } if (_backupProvider.backupProgress == BackUpProgressEnum.inBackground) { - debugPrint("[uploadAssets] Background backup is running - abort"); + dPrint(() => "[uploadAssets] Background backup is running - abort"); showInProgress = true; } diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index 11cdcd54c5..75a2a35fb6 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -15,15 +15,15 @@ class CastNotifier extends StateNotifier { List<(String, CastDestinationType, dynamic)> discovered = List.empty(); CastNotifier(this._gCastService) - : super( - const CastManagerState( - isCasting: false, - currentTime: Duration.zero, - duration: Duration.zero, - receiverName: '', - castState: CastState.idle, - ), - ) { + : super( + const CastManagerState( + isCasting: false, + currentTime: Duration.zero, + duration: Duration.zero, + receiverName: '', + castState: CastState.idle, + ), + ) { _gCastService.onConnectionState = _onConnectionState; _gCastService.onCurrentTime = _onCurrentTime; _gCastService.onDuration = _onDuration; @@ -65,8 +65,8 @@ class CastNotifier extends StateNotifier { type: asset.type == old_asset_entity.AssetType.image ? AssetType.image : asset.type == old_asset_entity.AssetType.video - ? AssetType.video - : AssetType.other, + ? AssetType.video + : AssetType.other, createdAt: asset.fileCreatedAt, updatedAt: asset.updatedAt, ); diff --git a/mobile/lib/providers/folder.provider.dart b/mobile/lib/providers/folder.provider.dart index 810c2cea73..696d7e19fd 100644 --- a/mobile/lib/providers/folder.provider.dart +++ b/mobile/lib/providers/folder.provider.dart @@ -22,12 +22,8 @@ class FolderStructureNotifier extends StateNotifier> { } } -final folderStructureProvider = - StateNotifierProvider>( - (ref) { - return FolderStructureNotifier( - ref.watch(folderServiceProvider), - ); +final folderStructureProvider = StateNotifierProvider>((ref) { + return FolderStructureNotifier(ref.watch(folderServiceProvider)); }); class FolderRenderListNotifier extends StateNotifier> { @@ -35,14 +31,12 @@ class FolderRenderListNotifier extends StateNotifier> { final RootFolder _folder; final Logger _log = Logger("FolderAssetsNotifier"); - FolderRenderListNotifier(this._folderService, this._folder) - : super(const AsyncLoading()); + FolderRenderListNotifier(this._folderService, this._folder) : super(const AsyncLoading()); Future fetchAssets(SortOrder order) async { try { final assets = await _folderService.getFolderAssets(_folder, order); - final renderList = - await RenderList.fromAssets(assets, GroupAssetsBy.none); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.none); state = AsyncData(renderList); } catch (e, stack) { _log.severe("Failed to fetch folder assets", e, stack); @@ -51,12 +45,7 @@ class FolderRenderListNotifier extends StateNotifier> { } } -final folderRenderListProvider = StateNotifierProvider.family< - FolderRenderListNotifier, - AsyncValue, - RootFolder>((ref, folder) { - return FolderRenderListNotifier( - ref.watch(folderServiceProvider), - folder, - ); -}); +final folderRenderListProvider = + StateNotifierProvider.family, RootFolder>((ref, folder) { + return FolderRenderListNotifier(ref.watch(folderServiceProvider), folder); + }); diff --git a/mobile/lib/providers/gallery_permission.provider.dart b/mobile/lib/providers/gallery_permission.provider.dart index 07d9cca591..6e4fc69926 100644 --- a/mobile/lib/providers/gallery_permission.provider.dart +++ b/mobile/lib/providers/gallery_permission.provider.dart @@ -6,8 +6,8 @@ import 'package:permission_handler/permission_handler.dart'; class GalleryPermissionNotifier extends StateNotifier { GalleryPermissionNotifier() - : super(PermissionStatus.denied) // Denied is the initial state - { + : super(PermissionStatus.denied) // Denied is the initial state + { // Sets the initial state getGalleryPermissionStatus(); } @@ -36,8 +36,7 @@ class GalleryPermissionNotifier extends StateNotifier { // Return the joint result of those two permissions final PermissionStatus status; - if ((photos.isGranted && videos.isGranted) || - (photos.isLimited && videos.isLimited)) { + if ((photos.isGranted && videos.isGranted) || (photos.isLimited && videos.isLimited)) { status = PermissionStatus.granted; } else if (photos.isDenied || videos.isDenied) { status = PermissionStatus.denied; @@ -49,8 +48,7 @@ class GalleryPermissionNotifier extends StateNotifier { result = status; } - if (result == PermissionStatus.granted && - androidInfo.version.sdkInt >= 29) { + if (result == PermissionStatus.granted && androidInfo.version.sdkInt >= 29) { result = await Permission.accessMediaLocation.request(); } } else { @@ -80,8 +78,7 @@ class GalleryPermissionNotifier extends StateNotifier { // Return the joint result of those two permissions final PermissionStatus status; - if ((photos.isGranted && videos.isGranted) || - (photos.isLimited && videos.isLimited)) { + if ((photos.isGranted && videos.isGranted) || (photos.isLimited && videos.isLimited)) { status = PermissionStatus.granted; } else if (photos.isDenied || videos.isDenied) { status = PermissionStatus.denied; @@ -93,8 +90,7 @@ class GalleryPermissionNotifier extends StateNotifier { result = status; } - if (state == PermissionStatus.granted && - androidInfo.version.sdkInt >= 29) { + if (state == PermissionStatus.granted && androidInfo.version.sdkInt >= 29) { result = await Permission.accessMediaLocation.status; } } else { @@ -107,7 +103,6 @@ class GalleryPermissionNotifier extends StateNotifier { } } -final galleryPermissionNotifier = - StateNotifierProvider( +final galleryPermissionNotifier = StateNotifierProvider( (ref) => GalleryPermissionNotifier(), ); diff --git a/mobile/lib/providers/haptic_feedback.provider.dart b/mobile/lib/providers/haptic_feedback.provider.dart index ce8997c85c..711c6fa4e2 100644 --- a/mobile/lib/providers/haptic_feedback.provider.dart +++ b/mobile/lib/providers/haptic_feedback.provider.dart @@ -3,8 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -final hapticFeedbackProvider = - StateNotifierProvider((ref) { +final hapticFeedbackProvider = StateNotifierProvider((ref) { return HapticNotifier(ref); }); @@ -15,41 +14,31 @@ class HapticNotifier extends StateNotifier { HapticNotifier(this._ref) : super(null); selectionClick() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.selectionClick(); } } lightImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.lightImpact(); } } mediumImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.mediumImpact(); } } heavyImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.heavyImpact(); } } vibrate() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.vibrate(); } } diff --git a/mobile/lib/providers/image/cache/image_loader.dart b/mobile/lib/providers/image/cache/image_loader.dart index f88d54e4f1..50530f7cdf 100644 --- a/mobile/lib/providers/image/cache/image_loader.dart +++ b/mobile/lib/providers/image/cache/image_loader.dart @@ -19,20 +19,13 @@ class ImageLoader { }) async { final headers = ApiService.getRequestHeaders(); - final stream = cache.getFileStream( - uri, - withProgress: chunkEvents != null, - headers: headers, - ); + final stream = cache.getFileStream(uri, withProgress: chunkEvents != null, headers: headers); await for (final result in stream) { if (result is DownloadProgress) { // We are downloading the file, so update the [chunkEvents] chunkEvents?.add( - ImageChunkEvent( - cumulativeBytesLoaded: result.downloaded, - expectedTotalBytes: result.totalSize, - ), + ImageChunkEvent(cumulativeBytesLoaded: result.downloaded, expectedTotalBytes: result.totalSize), ); } else if (result is FileInfo) { // We have the file diff --git a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart index da20f46c62..41c541ccdb 100644 --- a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart @@ -1,20 +1,148 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +// ignore: implementation_imports +import 'package:flutter_cache_manager/src/cache_store.dart'; +import 'package:logging/logging.dart'; +import 'package:uuid/uuid.dart'; -/// The cache manager for full size images [ImmichRemoteImageProvider] -class RemoteImageCacheManager extends CacheManager { +abstract class RemoteCacheManager extends CacheManager { + static final _log = Logger('RemoteCacheManager'); + + RemoteCacheManager.custom(super.config, CacheStore store) + // Unfortunately, CacheStore is not a public API + // ignore: invalid_use_of_visible_for_testing_member + : super.custom(cacheStore: store); + + Future putStreamedFile( + String url, + Stream> source, { + String? key, + String? eTag, + Duration maxAge = const Duration(days: 30), + String fileExtension = 'file', + }); + + // Unlike `putFileStream`, this method handles request cancellation, + // does not make a (slow) DB call checking if the file is already cached, + // does not synchronously check if a file exists, + // and deletes the file on cancellation without making these checks again. + Future putStreamedFileToStore( + CacheStore store, + String url, + Stream> source, { + String? key, + String? eTag, + Duration maxAge = const Duration(days: 30), + String fileExtension = 'file', + }) async { + final path = '${const Uuid().v1()}.$fileExtension'; + final file = await store.fileSystem.createFile(path); + final sink = file.openWrite(); + try { + await source.listen(sink.add, cancelOnError: true).asFuture(); + } catch (e) { + try { + await sink.close(); + await file.delete(); + } catch (e) { + _log.severe('Failed to delete incomplete cache file: $e'); + } + return; + } + + try { + await sink.flush(); + await sink.close(); + } catch (e) { + try { + await file.delete(); + } catch (e) { + _log.severe('Failed to delete incomplete cache file: $e'); + } + return; + } + + final cacheObject = CacheObject( + url, + key: key, + relativePath: path, + validTill: DateTime.now().add(maxAge), + eTag: eTag, + ); + try { + await store.putFile(cacheObject); + } catch (e) { + try { + await file.delete(); + } catch (e) { + _log.severe('Failed to delete untracked cache file: $e'); + } + } + } +} + +class RemoteImageCacheManager extends RemoteCacheManager { static const key = 'remoteImageCacheKey'; static final RemoteImageCacheManager _instance = RemoteImageCacheManager._(); + static final _config = Config(key, maxNrOfCacheObjects: 500, stalePeriod: const Duration(days: 30)); + static final _store = CacheStore(_config); factory RemoteImageCacheManager() { return _instance; } - RemoteImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 500, - stalePeriod: const Duration(days: 30), - ), - ); + RemoteImageCacheManager._() : super.custom(_config, _store); + + @override + Future putStreamedFile( + String url, + Stream> source, { + String? key, + String? eTag, + Duration maxAge = const Duration(days: 30), + String fileExtension = 'file', + }) { + return putStreamedFileToStore( + _store, + url, + source, + key: key, + eTag: eTag, + maxAge: maxAge, + fileExtension: fileExtension, + ); + } +} + +/// The cache manager for full size images [ImmichRemoteImageProvider] +class RemoteThumbnailCacheManager extends RemoteCacheManager { + static const key = 'remoteThumbnailCacheKey'; + static final RemoteThumbnailCacheManager _instance = RemoteThumbnailCacheManager._(); + static final _config = Config(key, maxNrOfCacheObjects: 5000, stalePeriod: const Duration(days: 30)); + static final _store = CacheStore(_config); + + factory RemoteThumbnailCacheManager() { + return _instance; + } + + RemoteThumbnailCacheManager._() : super.custom(_config, _store); + + @override + Future putStreamedFile( + String url, + Stream> source, { + String? key, + String? eTag, + Duration maxAge = const Duration(days: 30), + String fileExtension = 'file', + }) { + return putStreamedFileToStore( + _store, + url, + source, + key: key, + eTag: eTag, + maxAge: maxAge, + fileExtension: fileExtension, + ); + } } diff --git a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart index dd7ad35277..bfea36eef6 100644 --- a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart @@ -3,19 +3,11 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; /// The cache manager for thumbnail images [ImmichRemoteThumbnailProvider] class ThumbnailImageCacheManager extends CacheManager { static const key = 'thumbnailImageCacheKey'; - static final ThumbnailImageCacheManager _instance = - ThumbnailImageCacheManager._(); + static final ThumbnailImageCacheManager _instance = ThumbnailImageCacheManager._(); factory ThumbnailImageCacheManager() { return _instance; } - ThumbnailImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 5000, - stalePeriod: const Duration(days: 30), - ), - ); + ThumbnailImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 5000, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 4c77ee4b56..8c46c52906 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -18,11 +18,8 @@ class ImmichLocalImageProvider extends ImageProvider { final double height; final Logger log = Logger('ImmichLocalImageProvider'); - ImmichLocalImageProvider({ - required this.asset, - required this.width, - required this.height, - }) : assert(asset.local != null, 'Only usable when asset.local is set'); + ImmichLocalImageProvider({required this.asset, required this.width, required this.height}) + : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @@ -32,10 +29,7 @@ class ImmichLocalImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ImmichLocalImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalImageProvider key, ImageDecoderCallback decode) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( codec: _codec(key.asset, decode, chunkEvents), diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index edcf8a9458..5edb0fc79e 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -13,8 +13,7 @@ import 'package:logging/logging.dart'; /// The local image provider for an asset /// Only viable -class ImmichLocalThumbnailProvider - extends ImageProvider { +class ImmichLocalThumbnailProvider extends ImageProvider { final Asset asset; final int height; final int width; @@ -33,17 +32,12 @@ class ImmichLocalThumbnailProvider /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichLocalThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( codec: _codec(key.asset, cache, decode), @@ -55,18 +49,12 @@ class ImmichLocalThumbnailProvider } // Streams in each stage of the image as we ask for it - Stream _codec( - Asset assetData, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { - final cacheKey = - '$userId${assetData.localId}${assetData.checksum}$width$height'; + Stream _codec(Asset assetData, CacheManager cache, ImageDecoderCallback decode) async* { + 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 buffer = await ui.ImmutableBuffer.fromFilePath(fileFromCache.file.path); final codec = await decode(buffer); yield codec; return; @@ -75,14 +63,9 @@ class ImmichLocalThumbnailProvider } } - final thumbnailBytes = await assetData.local?.thumbnailDataWithSize( - ThumbnailSize(width, height), - quality: 80, - ); + final thumbnailBytes = await assetData.local?.thumbnailDataWithSize(ThumbnailSize(width, height), quality: 80); if (thumbnailBytes == null) { - throw StateError( - "Loading thumb for local photo ${assetData.fileName} failed", - ); + throw StateError("Loading thumb for local photo ${assetData.fileName} failed"); } final buffer = await ui.ImmutableBuffer.fromUint8List(thumbnailBytes); diff --git a/mobile/lib/providers/image/immich_remote_image_provider.dart b/mobile/lib/providers/image/immich_remote_image_provider.dart index fe0811583c..16d5312e4c 100644 --- a/mobile/lib/providers/image/immich_remote_image_provider.dart +++ b/mobile/lib/providers/image/immich_remote_image_provider.dart @@ -15,33 +15,24 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; /// The remote image provider for full size remote images -class ImmichRemoteImageProvider - extends ImageProvider { +class ImmichRemoteImageProvider extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteImageProvider({ - required this.assetId, - this.cacheManager, - }); + const ImmichRemoteImageProvider({required this.assetId, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( @@ -52,10 +43,7 @@ class ImmichRemoteImageProvider } /// Whether to show the original file or load a compressed version - bool get _useOriginal => Store.get( - AppSettingsEnum.loadOriginal.storeKey, - AppSettingsEnum.loadOriginal.defaultValue, - ); + bool get _useOriginal => Store.get(AppSettingsEnum.loadOriginal.storeKey, AppSettingsEnum.loadOriginal.defaultValue); // Streams in each stage of the image as we ask for it Stream _codec( @@ -65,28 +53,15 @@ class ImmichRemoteImageProvider StreamController chunkEvents, ) async* { // Load the higher resolution version of the image - final url = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.preview, - ); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final url = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.preview); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; // Load the final remote image if (_useOriginal) { // Load the original image final url = getOriginalUrlForRemoteId(key.assetId); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; } await chunkEvents.close(); diff --git a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart index 8533cb6f08..08ee4325e8 100644 --- a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart @@ -13,8 +13,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; /// The remote image provider -class ImmichRemoteThumbnailProvider - extends ImageProvider { +class ImmichRemoteThumbnailProvider extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; @@ -24,51 +23,27 @@ class ImmichRemoteThumbnailProvider /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteThumbnailProvider({ - required this.assetId, - this.height, - this.width, - this.cacheManager, - }); + const ImmichRemoteThumbnailProvider({required this.assetId, this.height, this.width, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode), - scale: 1.0, - ); + return MultiImageStreamCompleter(codec: _codec(key, cache, decode), scale: 1.0); } // Streams in each stage of the image as we ask for it - Stream _codec( - ImmichRemoteThumbnailProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(ImmichRemoteThumbnailProvider key, CacheManager cache, ImageDecoderCallback decode) async* { // Load a preview to the chunk events - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.thumbnail); - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - ); + yield await ImageLoader.loadImageFromCache(preview, cache: cache, decode: decode); } @override diff --git a/mobile/lib/providers/immich_logo_provider.g.dart b/mobile/lib/providers/immich_logo_provider.g.dart index 90b117d574..f1af433c1b 100644 --- a/mobile/lib/providers/immich_logo_provider.g.dart +++ b/mobile/lib/providers/immich_logo_provider.g.dart @@ -13,8 +13,9 @@ String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea'; final immichLogoProvider = AutoDisposeFutureProvider.internal( immichLogo, name: r'immichLogoProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$immichLogoHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$immichLogoHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index cb025ef941..21d76201c1 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,20 +1,26 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/asset.service.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; final actionProvider = NotifierProvider( ActionNotifier.new, - dependencies: [ - multiSelectProvider, - timelineServiceProvider, - ], + dependencies: [multiSelectProvider, timelineServiceProvider], ); class ActionResult { @@ -25,33 +31,60 @@ class ActionResult { const ActionResult({required this.count, required this.success, this.error}); @override - String toString() => - 'ActionResult(count: $count, success: $success, error: $error)'; + String toString() => 'ActionResult(count: $count, success: $success, error: $error)'; } class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; + late UploadService _uploadService; + late DownloadService _downloadService; + late AssetService _assetService; ActionNotifier() : super(); @override void build() { + _uploadService = ref.watch(uploadServiceProvider); _service = ref.watch(actionServiceProvider); + _assetService = ref.watch(assetServiceProvider); + _downloadService = ref.watch(downloadServiceProvider); + _downloadService.onImageDownloadStatus = _downloadImageCallback; + _downloadService.onVideoDownloadStatus = _downloadVideoCallback; + _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; + } + + void _downloadImageCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveImageWithPath(update.task); + } + } + + void _downloadVideoCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveVideo(update.task); + } + } + + void _downloadLivePhotoCallback(TaskStatusUpdate update) async { + if (update.status == TaskStatus.complete) { + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; + _downloadService.saveLivePhotos(update.task, livePhotosId); + } } List _getRemoteIdsForSource(ActionSource source) { - return _getAssets(source) - .whereType() - .toIds() - .toList(growable: false); + return _getAssets(source).whereType().toIds().toList(growable: false); } - List _getLocalIdsForSource(ActionSource source) { + List _getLocalIdsForSource(ActionSource source, {bool ignoreLocalOnly = false}) { final Set assets = _getAssets(source); final List localIds = []; for (final asset in assets) { + if (ignoreLocalOnly && asset.storage != AssetState.merged) { + continue; + } if (asset is LocalAsset) { localIds.add(asset.id); } else if (asset is RemoteAsset && asset.localId != null) { @@ -64,11 +97,7 @@ class ActionNotifier extends Notifier { List _getOwnedRemoteIdsForSource(ActionSource source) { final ownerId = ref.read(currentUserProvider)?.id; - return _getAssets(source) - .whereType() - .ownedAssets(ownerId) - .toIds() - .toList(growable: false); + return _getAssets(source).whereType().ownedAssets(ownerId).toIds().toList(growable: false); } List _getOwnedRemoteAssetsForSource(ActionSource source) { @@ -79,37 +108,41 @@ class ActionNotifier extends Notifier { Iterable _getIdsForSource(ActionSource source) { final Set assets = _getAssets(source); return switch (T) { - const (RemoteAsset) => assets.whereType(), - const (LocalAsset) => assets.whereType(), - _ => const [], - } as Iterable; + const (RemoteAsset) => assets.whereType(), + const (LocalAsset) => assets.whereType(), + _ => const [], + } + as Iterable; } Set _getAssets(ActionSource source) { return switch (source) { ActionSource.timeline => ref.read(multiSelectProvider).selectedAssets, ActionSource.viewer => switch (ref.read(currentAssetNotifier)) { - BaseAsset asset => {asset}, - null => const {}, - }, + BaseAsset asset => {asset}, + null => const {}, + }, }; } - Future shareLink( - ActionSource source, - BuildContext context, - ) async { + Future troubleshoot(ActionSource source, BuildContext context) async { + final assets = _getAssets(source); + if (assets.length > 1) { + return ActionResult(count: assets.length, success: false, error: 'Cannot troubleshoot multiple assets'); + } + context.pushRoute(AssetTroubleshootRoute(asset: assets.first)); + + return ActionResult(count: assets.length, success: true); + } + + Future shareLink(ActionSource source, BuildContext context) async { final ids = _getRemoteIdsForSource(source); try { await _service.shareLink(ids, context); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to create shared link for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -120,11 +153,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to favorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -135,11 +164,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unfavorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -150,11 +175,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to archive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -165,27 +186,19 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unarchive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future moveToLockFolder(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); - final localIds = _getLocalIdsForSource(source); + final localIds = _getLocalIdsForSource(source, ignoreLocalOnly: true); try { await _service.moveToLockFolder(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to move assets to lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -196,63 +209,69 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future trash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + try { await _service.trash(ids); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future delete(ActionSource source) async { + Future restoreTrash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { - await _service.delete(ids); + await _service.restoreTrash(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to restore trash assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future trashRemoteAndDeleteLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.trashRemoteAndDeleteLocal(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future deleteRemoteAndLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.deleteRemoteAndLocal(ids, localIds); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to delete assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future deleteLocal(ActionSource source) async { final ids = _getLocalIdsForSource(source); try { - await _service.deleteLocal(ids); - return ActionResult(count: ids.length, success: true); + final deletedCount = await _service.deleteLocal(ids); + return ActionResult(count: deletedCount, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future editLocation( - ActionSource source, - BuildContext context, - ) async { + Future editLocation(ActionSource source, BuildContext context) async { final ids = _getOwnedRemoteIdsForSource(source); try { final isEdited = await _service.editLocation(ids, context); @@ -263,29 +282,49 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to edit location for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future removeFromAlbum( - ActionSource source, - String albumId, - ) async { + Future editDateTime(ActionSource source, BuildContext context) async { + final ids = _getOwnedRemoteIdsForSource(source); + try { + final isEdited = await _service.editDateTime(ids, context); + if (!isEdited) { + return null; + } + + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to edit date and time for assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { final removedCount = await _service.removeFromAlbum(ids, albumId); return ActionResult(count: removedCount, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from album', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future updateDescription(ActionSource source, String description) async { + final ids = _getRemoteIdsForSource(source); + if (ids.length != 1) { + _logger.warning('updateDescription called with multiple assets, expected single asset'); + return ActionResult(count: ids.length, success: false, error: 'Expected single asset for description update'); + } + + try { + final isUpdated = await _service.updateDescription(ids.first, description); + return ActionResult(count: 1, success: isUpdated); + } catch (error, stack) { + _logger.severe('Failed to update description for asset', error, stack); + return ActionResult(count: 1, success: false, error: error.toString()); } } @@ -296,11 +335,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to stack assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -308,47 +343,53 @@ class ActionNotifier extends Notifier { final assets = _getOwnedRemoteAssetsForSource(source); try { await _service.unStack(assets.map((e) => e.stackId).nonNulls.toList()); + if (source == ActionSource.viewer) { + final updatedParent = await _assetService.getRemoteAsset(assets.first.id); + if (updatedParent != null) { + ref.read(currentAssetNotifier.notifier).setAsset(updatedParent); + ref.read(assetViewerProvider.notifier).setAsset(updatedParent); + } + } + return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed to unstack assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - ); + return ActionResult(count: assets.length, success: false); } } - Future shareAssets(ActionSource source) async { + Future shareAssets(ActionSource source, BuildContext context) async { final ids = _getAssets(source).toList(growable: false); try { - final count = await _service.shareAssets(ids); - return ActionResult(count: count, success: true); + await _service.shareAssets(ids, context); + return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to share assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future downloadAll(ActionSource source) async { - final assets = - _getAssets(source).whereType().toList(growable: false); - + final assets = _getAssets(source).whereType().toList(growable: false); try { final didEnqueue = await _service.downloadAll(assets); final enqueueCount = didEnqueue.where((e) => e).length; return ActionResult(count: enqueueCount, success: true); } catch (error, stack) { _logger.severe('Failed to download assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: assets.length, success: false, error: error.toString()); + } + } + + Future upload(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(); + try { + await _uploadService.manualBackup(assets); + return ActionResult(count: assets.length, success: true); + } catch (error, stack) { + _logger.severe('Failed manually upload assets', error, stack); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } } diff --git a/mobile/lib/providers/infrastructure/album.provider.dart b/mobile/lib/providers/infrastructure/album.provider.dart index 4ec3453d16..8388480974 100644 --- a/mobile/lib/providers/infrastructure/album.provider.dart +++ b/mobile/lib/providers/infrastructure/album.provider.dart @@ -18,12 +18,13 @@ final localAlbumServiceProvider = Provider( ); final localAlbumProvider = FutureProvider>( - (ref) => LocalAlbumService(ref.watch(localAlbumRepository)).getAll(), + (ref) => LocalAlbumService(ref.watch(localAlbumRepository)) + .getAll(sortBy: {SortLocalAlbumsBy.newestAsset}) + .then((albums) => albums.where((album) => album.assetCount > 0).toList()), ); final localAlbumThumbnailProvider = FutureProvider.family( - (ref, albumId) => - LocalAlbumService(ref.watch(localAlbumRepository)).getThumbnail(albumId), + (ref, albumId) => LocalAlbumService(ref.watch(localAlbumRepository)).getThumbnail(albumId), ); final remoteAlbumRepository = Provider( @@ -31,15 +32,11 @@ final remoteAlbumRepository = Provider( ); final remoteAlbumServiceProvider = Provider( - (ref) => RemoteAlbumService( - ref.watch(remoteAlbumRepository), - ref.watch(driftAlbumApiRepositoryProvider), - ), + (ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)), dependencies: [remoteAlbumRepository], ); -final remoteAlbumProvider = - NotifierProvider( +final remoteAlbumProvider = NotifierProvider( RemoteAlbumNotifier.new, dependencies: [remoteAlbumServiceProvider], ); diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index 102e6aa60c..4b51ce33bd 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; final localAssetRepository = Provider( (ref) => DriftLocalAssetRepository(ref.watch(driftProvider)), @@ -19,9 +20,13 @@ final assetServiceProvider = Provider( ), ); -final placesProvider = FutureProvider>( - (ref) => AssetService( - remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider), - localAssetRepository: ref.watch(localAssetRepository), - ).getPlaces(), -); +final placesProvider = FutureProvider>((ref) { + final assetService = ref.watch(assetServiceProvider); + final auth = ref.watch(currentUserProvider); + + if (auth == null) { + return Future.value(const []); + } + + return assetService.getPlaces(auth.id); +}); diff --git a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart index 996d5d816f..1956170c1e 100644 --- a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart @@ -4,10 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -final currentAssetNotifier = - AutoDisposeNotifierProvider( - CurrentAssetNotifier.new, -); +final currentAssetNotifier = AutoDisposeNotifierProvider(CurrentAssetNotifier.new); class CurrentAssetNotifier extends AutoDisposeNotifier { KeepAliveLink? _keepAliveLink; @@ -20,10 +17,7 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { _keepAliveLink?.close(); _assetSubscription?.cancel(); state = asset; - _assetSubscription = ref - .watch(assetServiceProvider) - .watchAsset(asset) - .listen((updatedAsset) { + _assetSubscription = ref.watch(assetServiceProvider).watchAsset(asset).listen((updatedAsset) { if (updatedAsset != null) { state = updatedAsset; } @@ -37,12 +31,10 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { } } -final currentAssetExifProvider = FutureProvider.autoDispose( - (ref) { - final currentAsset = ref.watch(currentAssetNotifier); - if (currentAsset == null) { - return null; - } - return ref.watch(assetServiceProvider).getExif(currentAsset); - }, -); +final currentAssetExifProvider = FutureProvider.autoDispose((ref) { + final currentAsset = ref.watch(currentAssetNotifier); + if (currentAsset == null) { + return null; + } + return ref.watch(assetServiceProvider).getExif(currentAsset); +}); diff --git a/mobile/lib/providers/infrastructure/current_album.provider.dart b/mobile/lib/providers/infrastructure/current_album.provider.dart index ece188ee15..0d95674ec7 100644 --- a/mobile/lib/providers/infrastructure/current_album.provider.dart +++ b/mobile/lib/providers/infrastructure/current_album.provider.dart @@ -4,8 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -final currentRemoteAlbumProvider = - AutoDisposeNotifierProvider( +final currentRemoteAlbumProvider = AutoDisposeNotifierProvider( CurrentAlbumNotifier.new, ); @@ -21,10 +20,7 @@ class CurrentAlbumNotifier extends AutoDisposeNotifier { _assetSubscription?.cancel(); state = album; - _assetSubscription = ref - .watch(remoteAlbumServiceProvider) - .watchAlbum(album.id) - .listen((updatedAlbum) { + _assetSubscription = ref.watch(remoteAlbumServiceProvider).watchAlbum(album.id).listen((updatedAlbum) { if (updatedAlbum != null) { state = updatedAlbum; } @@ -35,5 +31,6 @@ class CurrentAlbumNotifier extends AutoDisposeNotifier { void dispose() { _keepAliveLink?.close(); _assetSubscription?.cancel(); + state = null; } } diff --git a/mobile/lib/providers/infrastructure/db.provider.dart b/mobile/lib/providers/infrastructure/db.provider.dart index cdf934e508..d38bcbfb55 100644 --- a/mobile/lib/providers/infrastructure/db.provider.dart +++ b/mobile/lib/providers/infrastructure/db.provider.dart @@ -10,9 +10,12 @@ part 'db.provider.g.dart'; @Riverpod(keepAlive: true) Isar isar(Ref ref) => throw UnimplementedError('isar'); -final driftProvider = Provider((ref) { - final drift = Drift(); +Drift Function(Ref ref) driftOverride(Drift drift) => (ref) { ref.onDispose(() => unawaited(drift.close())); ref.keepAlive(); return drift; -}); +}; + +final driftProvider = Provider( + (ref) => throw UnimplementedError("driftProvider must be overridden in the isolate's ProviderContainer before use"), +); diff --git a/mobile/lib/providers/infrastructure/db.provider.g.dart b/mobile/lib/providers/infrastructure/db.provider.g.dart index 33b330192f..46abfb66a9 100644 --- a/mobile/lib/providers/infrastructure/db.provider.g.dart +++ b/mobile/lib/providers/infrastructure/db.provider.g.dart @@ -13,8 +13,9 @@ String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb'; final isarProvider = Provider.internal( isar, name: r'isarProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$isarHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$isarHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/exif.provider.dart b/mobile/lib/providers/infrastructure/exif.provider.dart index 59ad632927..c126f6cac0 100644 --- a/mobile/lib/providers/infrastructure/exif.provider.dart +++ b/mobile/lib/providers/infrastructure/exif.provider.dart @@ -6,5 +6,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'exif.provider.g.dart'; @Riverpod(keepAlive: true) -IsarExifRepository exifRepository(Ref ref) => - IsarExifRepository(ref.watch(isarProvider)); +IsarExifRepository exifRepository(Ref ref) => IsarExifRepository(ref.watch(isarProvider)); diff --git a/mobile/lib/providers/infrastructure/map.provider.dart b/mobile/lib/providers/infrastructure/map.provider.dart new file mode 100644 index 0000000000..e774cec756 --- /dev/null +++ b/mobile/lib/providers/infrastructure/map.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/infrastructure/repositories/map.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/domain/services/map.service.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; + +final mapRepositoryProvider = Provider((ref) => DriftMapRepository(ref.watch(driftProvider))); + +final mapServiceProvider = Provider( + (ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access map'); + } + + final mapService = ref.watch(mapFactoryProvider).remote(user.id); + return mapService; + }, + // Empty dependencies to inform the framework that this provider + // might be used in a ProviderScope + dependencies: const [], +); + +final mapFactoryProvider = Provider((ref) => MapFactory(mapRepository: ref.watch(mapRepositoryProvider))); diff --git a/mobile/lib/providers/infrastructure/memory.provider.dart b/mobile/lib/providers/infrastructure/memory.provider.dart index 0e58943f55..0965f4349b 100644 --- a/mobile/lib/providers/infrastructure/memory.provider.dart +++ b/mobile/lib/providers/infrastructure/memory.provider.dart @@ -14,14 +14,12 @@ final driftMemoryServiceProvider = Provider( (ref) => DriftMemoryService(ref.watch(driftMemoryRepositoryProvider)), ); -final driftMemoryFutureProvider = - FutureProvider.autoDispose>((ref) async { - final user = ref.watch(currentUserProvider); - if (user == null) { - return []; +final driftMemoryFutureProvider = FutureProvider.autoDispose>((ref) { + final (userId, enabled) = ref.watch(currentUserProvider.select((user) => (user?.id, user?.memoryEnabled ?? true))); + if (userId == null || !enabled) { + return const []; } final service = ref.watch(driftMemoryServiceProvider); - - return service.getMemoryLane(user.id); + return service.getMemoryLane(userId); }); diff --git a/mobile/lib/providers/infrastructure/partner.provider.dart b/mobile/lib/providers/infrastructure/partner.provider.dart index e1c7ebf960..f4ba4cc73a 100644 --- a/mobile/lib/providers/infrastructure/partner.provider.dart +++ b/mobile/lib/providers/infrastructure/partner.provider.dart @@ -59,20 +59,16 @@ class PartnerNotifier extends Notifier> { } } -final driftAvailablePartnerProvider = - FutureProvider.autoDispose>((ref) { +final driftAvailablePartnerProvider = FutureProvider.autoDispose>((ref) { final currentUser = ref.watch(currentUserProvider); if (currentUser == null) { return []; } - return ref - .watch(driftPartnerServiceProvider) - .getAvailablePartners(currentUser.id); + return ref.watch(driftPartnerServiceProvider).getAvailablePartners(currentUser.id); }); -final driftSharedByPartnerProvider = - FutureProvider.autoDispose>((ref) { +final driftSharedByPartnerProvider = FutureProvider.autoDispose>((ref) { final currentUser = ref.watch(currentUserProvider); if (currentUser == null) { return []; @@ -81,8 +77,7 @@ final driftSharedByPartnerProvider = return ref.watch(driftPartnerServiceProvider).getSharedBy(currentUser.id); }); -final driftSharedWithPartnerProvider = - FutureProvider.autoDispose>((ref) { +final driftSharedWithPartnerProvider = FutureProvider.autoDispose>((ref) { final currentUser = ref.watch(currentUserProvider); if (currentUser == null) { return []; diff --git a/mobile/lib/providers/infrastructure/people.provider.dart b/mobile/lib/providers/infrastructure/people.provider.dart new file mode 100644 index 0000000000..94a1b2447f --- /dev/null +++ b/mobile/lib/providers/infrastructure/people.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/services/people.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +final driftPeopleRepositoryProvider = Provider( + (ref) => DriftPeopleRepository(ref.watch(driftProvider)), +); + +final driftPeopleServiceProvider = Provider( + (ref) => DriftPeopleService(ref.watch(driftPeopleRepositoryProvider), ref.watch(personApiRepositoryProvider)), +); + +final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAssetPeople(assetId); +}); + +final driftGetAllPeopleProvider = FutureProvider>((ref) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAllPeople(); +}); diff --git a/mobile/lib/providers/infrastructure/person.provider.dart b/mobile/lib/providers/infrastructure/person.provider.dart deleted file mode 100644 index a733104b33..0000000000 --- a/mobile/lib/providers/infrastructure/person.provider.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/infrastructure/repositories/person.repository.dart'; -import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; - -final driftPersonProvider = Provider( - (ref) => DriftPersonRepository(ref.watch(driftProvider)), -); diff --git a/mobile/lib/providers/infrastructure/platform.provider.dart b/mobile/lib/providers/infrastructure/platform.provider.dart index 477046d0bf..11c5280c02 100644 --- a/mobile/lib/providers/infrastructure/platform.provider.dart +++ b/mobile/lib/providers/infrastructure/platform.provider.dart @@ -1,4 +1,19 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/background_worker.service.dart'; +import 'package:immich_mobile/platform/background_worker_api.g.dart'; +import 'package:immich_mobile/platform/background_worker_lock_api.g.dart'; +import 'package:immich_mobile/platform/connectivity_api.g.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; +import 'package:immich_mobile/platform/thumbnail_api.g.dart'; + +final backgroundWorkerFgServiceProvider = Provider((_) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi())); + +final backgroundWorkerLockServiceProvider = Provider( + (_) => BackgroundWorkerLockService(BackgroundWorkerLockApi()), +); final nativeSyncApiProvider = Provider((_) => NativeSyncApi()); + +final connectivityApiProvider = Provider((_) => ConnectivityApi()); + +final thumbnailApi = ThumbnailApi(); diff --git a/mobile/lib/providers/infrastructure/readonly_mode.provider.dart b/mobile/lib/providers/infrastructure/readonly_mode.provider.dart new file mode 100644 index 0000000000..9e96c3cfc4 --- /dev/null +++ b/mobile/lib/providers/infrastructure/readonly_mode.provider.dart @@ -0,0 +1,36 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; + +class ReadOnlyModeNotifier extends Notifier { + late AppSettingsService _appSettingService; + + @override + bool build() { + _appSettingService = ref.read(appSettingsServiceProvider); + final readonlyMode = _appSettingService.getSetting(AppSettingsEnum.readonlyModeEnabled); + return readonlyMode; + } + + void setMode(bool value) { + _appSettingService.setSetting(AppSettingsEnum.readonlyModeEnabled, value); + state = value; + + if (value) { + ref.read(appRouterProvider).navigate(const MainTimelineRoute()); + } + } + + void setReadonlyMode(bool isEnabled) { + state = isEnabled; + setMode(state); + } + + void toggleReadonlyMode() { + state = !state; + setMode(state); + } +} + +final readonlyModeProvider = NotifierProvider(() => ReadOnlyModeNotifier()); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index 2ce10d7cbd..38ba52dc56 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -5,7 +5,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; -import 'package:immich_mobile/utils/remote_album.utils.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -13,56 +12,42 @@ import 'album.provider.dart'; class RemoteAlbumState { final List albums; - final List filteredAlbums; - const RemoteAlbumState({ - required this.albums, - List? filteredAlbums, - }) : filteredAlbums = filteredAlbums ?? albums; + const RemoteAlbumState({required this.albums}); - RemoteAlbumState copyWith({ - List? albums, - List? filteredAlbums, - }) { - return RemoteAlbumState( - albums: albums ?? this.albums, - filteredAlbums: filteredAlbums ?? this.filteredAlbums, - ); + RemoteAlbumState copyWith({List? albums}) { + return RemoteAlbumState(albums: albums ?? this.albums); } @override - String toString() => - 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length})'; + String toString() => 'RemoteAlbumState(albums: ${albums.length})'; @override bool operator ==(covariant RemoteAlbumState other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.albums, albums) && - listEquals(other.filteredAlbums, filteredAlbums); + return listEquals(other.albums, albums); } @override - int get hashCode => albums.hashCode ^ filteredAlbums.hashCode; + int get hashCode => albums.hashCode; } class RemoteAlbumNotifier extends Notifier { late RemoteAlbumService _remoteAlbumService; final _logger = Logger('RemoteAlbumNotifier'); + @override RemoteAlbumState build() { _remoteAlbumService = ref.read(remoteAlbumServiceProvider); - return const RemoteAlbumState(albums: [], filteredAlbums: []); + return const RemoteAlbumState(albums: []); } Future> _getAll() async { try { final albums = await _remoteAlbumService.getAll(); - state = state.copyWith( - albums: albums, - filteredAlbums: albums, - ); + state = state.copyWith(albums: albums); return albums; } catch (error, stack) { _logger.severe('Failed to fetch albums', error, stack); @@ -74,36 +59,21 @@ class RemoteAlbumNotifier extends Notifier { await _getAll(); } - void searchAlbums( + List searchAlbums( + List albums, String query, String? userId, [ QuickFilterMode filterMode = QuickFilterMode.all, ]) { - final filtered = _remoteAlbumService.searchAlbums( - state.albums, - query, - userId, - filterMode, - ); - - state = state.copyWith( - filteredAlbums: filtered, - ); + return _remoteAlbumService.searchAlbums(albums, query, userId, filterMode); } - void clearSearch() { - state = state.copyWith( - filteredAlbums: state.albums, - ); - } - - void sortFilteredAlbums( + Future> sortAlbums( + List albums, RemoteAlbumSortMode sortMode, { bool isReverse = false, - }) { - final sortedAlbums = _remoteAlbumService - .sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); - state = state.copyWith(filteredAlbums: sortedAlbums); + }) async { + return await _remoteAlbumService.sortAlbums(albums, sortMode, isReverse: isReverse); } Future createAlbum({ @@ -112,16 +82,9 @@ class RemoteAlbumNotifier extends Notifier { List assetIds = const [], }) async { try { - final album = await _remoteAlbumService.createAlbum( - title: title, - description: description, - assetIds: assetIds, - ); + final album = await _remoteAlbumService.createAlbum(title: title, description: description, assetIds: assetIds); - state = state.copyWith( - albums: [...state.albums, album], - filteredAlbums: [...state.filteredAlbums, album], - ); + state = state.copyWith(albums: [...state.albums, album]); return album; } catch (error, stack) { @@ -152,14 +115,7 @@ class RemoteAlbumNotifier extends Notifier { return album.id == albumId ? updatedAlbum : album; }).toList(); - final updatedFilteredAlbums = state.filteredAlbums.map((album) { - return album.id == albumId ? updatedAlbum : album; - }).toList(); - - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums); return updatedAlbum; } catch (error, stack) { @@ -169,12 +125,9 @@ class RemoteAlbumNotifier extends Notifier { } Future toggleAlbumOrder(String albumId) async { - final currentAlbum = - state.albums.firstWhere((album) => album.id == albumId); + final currentAlbum = state.albums.firstWhere((album) => album.id == albumId); - final newOrder = currentAlbum.order == AlbumAssetOrder.asc - ? AlbumAssetOrder.desc - : AlbumAssetOrder.asc; + final newOrder = currentAlbum.order == AlbumAssetOrder.asc ? AlbumAssetOrder.desc : AlbumAssetOrder.asc; return updateAlbum(albumId, order: newOrder); } @@ -182,15 +135,8 @@ class RemoteAlbumNotifier extends Notifier { Future deleteAlbum(String albumId) async { await _remoteAlbumService.deleteAlbum(albumId); - final updatedAlbums = - state.albums.where((album) => album.id != albumId).toList(); - final updatedFilteredAlbums = - state.filteredAlbums.where((album) => album.id != albumId).toList(); - - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); + state = state.copyWith(albums: updatedAlbums); } Future> getAssets(String albumId) { @@ -198,34 +144,37 @@ class RemoteAlbumNotifier extends Notifier { } Future addAssets(String albumId, List assetIds) { - return _remoteAlbumService.addAssets( - albumId: albumId, - assetIds: assetIds, - ); + return _remoteAlbumService.addAssets(albumId: albumId, assetIds: assetIds); } Future addUsers(String albumId, List userIds) { - return _remoteAlbumService.addUsers( - albumId: albumId, - userIds: userIds, - ); + return _remoteAlbumService.addUsers(albumId: albumId, userIds: userIds); + } + + Future removeUser(String albumId, String userId) { + return _remoteAlbumService.removeUser(albumId, userId: userId); + } + + Future leaveAlbum(String albumId, {required String userId}) async { + await _remoteAlbumService.removeUser(albumId, userId: userId); + + final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); + state = state.copyWith(albums: updatedAlbums); + } + + Future setActivityStatus(String albumId, bool enabled) { + return _remoteAlbumService.setActivityStatus(albumId, enabled); } } -final remoteAlbumDateRangeProvider = - FutureProvider.family<(DateTime, DateTime), String>( - (ref, albumId) async { - final service = ref.watch(remoteAlbumServiceProvider); - return service.getDateRange(albumId); - }, -); +final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>((ref, albumId) async { + final service = ref.watch(remoteAlbumServiceProvider); + return service.getDateRange(albumId); +}); -final remoteAlbumSharedUsersProvider = - FutureProvider.autoDispose.family, String>( - (ref, albumId) async { - final link = ref.keepAlive(); - ref.onDispose(() => link.close()); - final service = ref.watch(remoteAlbumServiceProvider); - return service.getSharedUsers(albumId); - }, -); +final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>((ref, albumId) async { + final link = ref.keepAlive(); + ref.onDispose(() => link.close()); + final service = ref.watch(remoteAlbumServiceProvider); + return service.getSharedUsers(albumId); +}); diff --git a/mobile/lib/providers/infrastructure/search.provider.dart b/mobile/lib/providers/infrastructure/search.provider.dart index cdcd3ee43b..7d992f9d5f 100644 --- a/mobile/lib/providers/infrastructure/search.provider.dart +++ b/mobile/lib/providers/infrastructure/search.provider.dart @@ -3,10 +3,6 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final searchApiRepositoryProvider = Provider( - (ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi), -); +final searchApiRepositoryProvider = Provider((ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi)); -final searchServiceProvider = Provider( - (ref) => SearchService(ref.watch(searchApiRepositoryProvider)), -); +final searchServiceProvider = Provider((ref) => SearchService(ref.watch(searchApiRepositoryProvider))); diff --git a/mobile/lib/providers/infrastructure/setting.provider.dart b/mobile/lib/providers/infrastructure/setting.provider.dart index ad0af8282e..7d8be72cd0 100644 --- a/mobile/lib/providers/infrastructure/setting.provider.dart +++ b/mobile/lib/providers/infrastructure/setting.provider.dart @@ -5,8 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; class SettingsNotifier extends Notifier { @override - SettingsService build() => - SettingsService(storeService: ref.read(storeServiceProvider)); + SettingsService build() => SettingsService(storeService: ref.read(storeServiceProvider)); T get(Setting setting) => state.get(setting); @@ -18,5 +17,4 @@ class SettingsNotifier extends Notifier { Stream watch(Setting setting) => state.watch(setting); } -final settingsProvider = - NotifierProvider(SettingsNotifier.new); +final settingsProvider = NotifierProvider(SettingsNotifier.new); diff --git a/mobile/lib/providers/infrastructure/stack.provider.dart b/mobile/lib/providers/infrastructure/stack.provider.dart index 71abd1e87a..0528fd0c91 100644 --- a/mobile/lib/providers/infrastructure/stack.provider.dart +++ b/mobile/lib/providers/infrastructure/stack.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftStackProvider = Provider( - (ref) => DriftStackRepository(ref.watch(driftProvider)), -); +final driftStackProvider = Provider((ref) => DriftStackRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/storage.provider.dart b/mobile/lib/providers/infrastructure/storage.provider.dart index 5bbbe51497..ccca964027 100644 --- a/mobile/lib/providers/infrastructure/storage.provider.dart +++ b/mobile/lib/providers/infrastructure/storage.provider.dart @@ -1,6 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -final storageRepositoryProvider = Provider( - (ref) => const StorageRepository(), -); +final storageRepositoryProvider = Provider((ref) => const StorageRepository()); diff --git a/mobile/lib/providers/infrastructure/store.provider.dart b/mobile/lib/providers/infrastructure/store.provider.dart index 6ae7ff987b..0bf42f3e8b 100644 --- a/mobile/lib/providers/infrastructure/store.provider.dart +++ b/mobile/lib/providers/infrastructure/store.provider.dart @@ -7,8 +7,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'store.provider.g.dart'; @Riverpod(keepAlive: true) -IsarStoreRepository storeRepository(Ref ref) => - IsarStoreRepository(ref.watch(isarProvider)); +IsarStoreRepository storeRepository(Ref ref) => IsarStoreRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) StoreService storeService(Ref _) => StoreService.I; diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 22b783013a..98c978cb60 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -30,8 +30,9 @@ String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0'; final storeServiceProvider = Provider.internal( storeService, name: r'storeServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$storeServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 2406c37fa8..f03754505c 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -10,7 +10,6 @@ import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; final syncStreamServiceProvider = Provider( (ref) => SyncStreamService( @@ -20,13 +19,9 @@ final syncStreamServiceProvider = Provider( ), ); -final syncApiRepositoryProvider = Provider( - (ref) => SyncApiRepository(ref.watch(apiServiceProvider)), -); +final syncApiRepositoryProvider = Provider((ref) => SyncApiRepository(ref.watch(apiServiceProvider))); -final syncStreamRepositoryProvider = Provider( - (ref) => SyncStreamRepository(ref.watch(driftProvider)), -); +final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.watch(driftProvider))); final localSyncServiceProvider = Provider( (ref) => LocalSyncService( @@ -39,7 +34,6 @@ final hashServiceProvider = Provider( (ref) => HashService( localAlbumRepository: ref.watch(localAlbumRepository), localAssetRepository: ref.watch(localAssetRepository), - storageRepository: ref.watch(storageRepositoryProvider), nativeSyncApi: ref.watch(nativeSyncApiProvider), ), ); diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index bbc04349de..06ec0242b2 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -11,15 +11,13 @@ final timelineRepositoryProvider = Provider( ); final timelineArgsProvider = Provider.autoDispose( - (ref) => - throw UnimplementedError('Will be overridden through a ProviderScope.'), + (ref) => throw UnimplementedError('Will be overridden through a ProviderScope.'), ); final timelineServiceProvider = Provider( (ref) { final timelineUsers = ref.watch(timelineUsersProvider).valueOrNull ?? []; - final timelineService = - ref.watch(timelineFactoryProvider).main(timelineUsers); + final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers); ref.onDispose(timelineService.dispose); return timelineService; }, @@ -35,15 +33,11 @@ final timelineFactoryProvider = Provider( ), ); -final timelineUsersProvider = StreamProvider>( - (ref) { - final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); - if (currentUserId == null) { - return Stream.value([]); - } +final timelineUsersProvider = StreamProvider>((ref) { + final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); + if (currentUserId == null) { + return Stream.value([]); + } - return ref - .watch(timelineRepositoryProvider) - .watchTimelineUserIds(currentUserId); - }, -); + return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); +}); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index d328f97600..922b9866bb 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -15,19 +15,17 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'user.provider.g.dart'; @Riverpod(keepAlive: true) -IsarUserRepository userRepository(Ref ref) => - IsarUserRepository(ref.watch(isarProvider)); +IsarUserRepository userRepository(Ref ref) => IsarUserRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) -UserApiRepository userApiRepository(Ref ref) => - UserApiRepository(ref.watch(apiServiceProvider).usersApi); +UserApiRepository userApiRepository(Ref ref) => UserApiRepository(ref.watch(apiServiceProvider).usersApi); @Riverpod(keepAlive: true) UserService userService(Ref ref) => UserService( - isarUserRepository: ref.watch(userRepositoryProvider), - userApiRepository: ref.watch(userApiRepositoryProvider), - storeService: ref.watch(storeServiceProvider), - ); + isarUserRepository: ref.watch(userRepositoryProvider), + userApiRepository: ref.watch(userApiRepositoryProvider), + storeService: ref.watch(storeServiceProvider), +); /// Drifts final driftPartnerRepositoryProvider = Provider( @@ -35,13 +33,7 @@ final driftPartnerRepositoryProvider = Provider( ); final driftPartnerServiceProvider = Provider( - (ref) => DriftPartnerService( - ref.watch(driftPartnerRepositoryProvider), - ref.watch(partnerApiRepositoryProvider), - ), + (ref) => DriftPartnerService(ref.watch(driftPartnerRepositoryProvider), ref.watch(partnerApiRepositoryProvider)), ); -final partnerUsersProvider = - NotifierProvider>( - PartnerNotifier.new, -); +final partnerUsersProvider = NotifierProvider>(PartnerNotifier.new); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart index 7664b15fd5..f9148bf3a7 100644 --- a/mobile/lib/providers/infrastructure/user.provider.g.dart +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -47,8 +47,9 @@ String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1'; final userServiceProvider = Provider.internal( userService, name: r'userServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$userServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$userServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/local_auth.provider.dart b/mobile/lib/providers/local_auth.provider.dart index 6f7ca5eb71..44fc5ad80c 100644 --- a/mobile/lib/providers/local_auth.provider.dart +++ b/mobile/lib/providers/local_auth.provider.dart @@ -9,12 +9,8 @@ import 'package:immich_mobile/services/local_auth.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:logging/logging.dart'; -final localAuthProvider = - StateNotifierProvider((ref) { - return LocalAuthNotifier( - ref.watch(localAuthServiceProvider), - ref.watch(secureStorageServiceProvider), - ); +final localAuthProvider = StateNotifierProvider((ref) { + return LocalAuthNotifier(ref.watch(localAuthServiceProvider), ref.watch(secureStorageServiceProvider)); }); class LocalAuthNotifier extends StateNotifier { @@ -24,23 +20,14 @@ class LocalAuthNotifier extends StateNotifier { final _log = Logger("LocalAuthNotifier"); LocalAuthNotifier(this._localAuthService, this._secureStorageService) - : super( - const BiometricStatus( - availableBiometrics: [], - canAuthenticate: false, - ), - ) { + : super(const BiometricStatus(availableBiometrics: [], canAuthenticate: false)) { _localAuthService.getStatus().then((value) { - state = state.copyWith( - canAuthenticate: value.canAuthenticate, - availableBiometrics: value.availableBiometrics, - ); + state = state.copyWith(canAuthenticate: value.canAuthenticate, availableBiometrics: value.availableBiometrics); }); } Future registerBiometric(BuildContext context, String pinCode) async { - final isAuthenticated = - await authenticate(context, 'Authenticate to enable biometrics'); + final isAuthenticated = await authenticate(context, 'Authenticate to enable biometrics'); if (!isAuthenticated) { return false; @@ -81,10 +68,7 @@ class LocalAuthNotifier extends StateNotifier { if (errorMessage.isNotEmpty) { context.showSnackBar( SnackBar( - content: Text( - errorMessage, - style: context.textTheme.labelLarge, - ), + content: Text(errorMessage, style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.errorContainer, ), diff --git a/mobile/lib/providers/map/map_marker.provider.dart b/mobile/lib/providers/map/map_marker.provider.dart index 23342b77b3..e107dd3602 100644 --- a/mobile/lib/providers/map/map_marker.provider.dart +++ b/mobile/lib/providers/map/map_marker.provider.dart @@ -12,26 +12,17 @@ Future> mapMarkers(Ref ref) async { final mapState = ref.read(mapStateNotifierProvider); DateTime? fileCreatedAfter; bool? isFavorite; - bool? isIncludeArchived; - bool? isWithPartners; + bool isIncludeArchived = mapState.includeArchived; + bool isWithPartners = mapState.withPartners; if (mapState.relativeTime != 0) { - fileCreatedAfter = - DateTime.now().subtract(Duration(days: mapState.relativeTime)); + fileCreatedAfter = DateTime.now().subtract(Duration(days: mapState.relativeTime)); } if (mapState.showFavoriteOnly) { isFavorite = true; } - if (!mapState.includeArchived) { - isIncludeArchived = false; - } - - if (mapState.withPartners) { - isWithPartners = true; - } - final markers = await service.getMapMarkers( isFavorite: isFavorite, withArchived: isIncludeArchived, diff --git a/mobile/lib/providers/map/map_marker.provider.g.dart b/mobile/lib/providers/map/map_marker.provider.g.dart index 76cc44a103..80a21a39b2 100644 --- a/mobile/lib/providers/map/map_marker.provider.g.dart +++ b/mobile/lib/providers/map/map_marker.provider.g.dart @@ -6,15 +6,16 @@ part of 'map_marker.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$mapMarkersHash() => r'f33ac4baa3251b3f06423aece89673315966f885'; +String _$mapMarkersHash() => r'a0c129fcddbf1b9bce4aafcd2e47a858ab6ef1c9'; /// See also [mapMarkers]. @ProviderFor(mapMarkers) final mapMarkersProvider = AutoDisposeFutureProvider>.internal( mapMarkers, name: r'mapMarkersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapMarkersHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapMarkersHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_service.provider.g.dart b/mobile/lib/providers/map/map_service.provider.g.dart index 0bb5094c61..e8eb1cd1ee 100644 --- a/mobile/lib/providers/map/map_service.provider.g.dart +++ b/mobile/lib/providers/map/map_service.provider.g.dart @@ -13,8 +13,9 @@ String _$mapServiceHash() => r'ffc8f38b726083452b9df236ed58903879348987'; final mapServiceProvider = AutoDisposeProvider.internal( mapService, name: r'mapServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index 189a23cd0a..31f2849df6 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -13,44 +13,28 @@ class MapStateNotifier extends _$MapStateNotifier { MapState build() { final appSettingsProvider = ref.read(appSettingsServiceProvider); - final lightStyleUrl = - ref.read(serverInfoProvider).serverConfig.mapLightStyleUrl; - final darkStyleUrl = - ref.read(serverInfoProvider).serverConfig.mapDarkStyleUrl; + final lightStyleUrl = ref.read(serverInfoProvider).serverConfig.mapLightStyleUrl; + final darkStyleUrl = ref.read(serverInfoProvider).serverConfig.mapDarkStyleUrl; return MapState( - themeMode: ThemeMode.values[ - appSettingsProvider.getSetting(AppSettingsEnum.mapThemeMode)], - showFavoriteOnly: appSettingsProvider - .getSetting(AppSettingsEnum.mapShowFavoriteOnly), - includeArchived: appSettingsProvider - .getSetting(AppSettingsEnum.mapIncludeArchived), - withPartners: - appSettingsProvider.getSetting(AppSettingsEnum.mapwithPartners), - relativeTime: - appSettingsProvider.getSetting(AppSettingsEnum.mapRelativeDate), + themeMode: ThemeMode.values[appSettingsProvider.getSetting(AppSettingsEnum.mapThemeMode)], + showFavoriteOnly: appSettingsProvider.getSetting(AppSettingsEnum.mapShowFavoriteOnly), + includeArchived: appSettingsProvider.getSetting(AppSettingsEnum.mapIncludeArchived), + withPartners: appSettingsProvider.getSetting(AppSettingsEnum.mapwithPartners), + relativeTime: appSettingsProvider.getSetting(AppSettingsEnum.mapRelativeDate), lightStyleFetched: AsyncData(lightStyleUrl), darkStyleFetched: AsyncData(darkStyleUrl), ); } void switchTheme(ThemeMode mode) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapThemeMode, - mode.index, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapThemeMode, mode.index); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapShowFavoriteOnly, - isFavoriteOnly, - ); - state = state.copyWith( - showFavoriteOnly: isFavoriteOnly, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } void setRefetchMarkers(bool shouldRefetch) { @@ -58,35 +42,17 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapIncludeArchived, - isIncludeArchived, - ); - state = state.copyWith( - includeArchived: isIncludeArchived, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapwithPartners, - isWithPartners, - ); - state = state.copyWith( - withPartners: isWithPartners, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapRelativeDate, - relativeTime, - ); - state = state.copyWith( - relativeTime: relativeTime, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeTime); + state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/providers/map/map_state.provider.g.dart b/mobile/lib/providers/map/map_state.provider.g.dart index 85a237099c..94d0ff8698 100644 --- a/mobile/lib/providers/map/map_state.provider.g.dart +++ b/mobile/lib/providers/map/map_state.provider.g.dart @@ -12,14 +12,14 @@ String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66'; @ProviderFor(MapStateNotifier) final mapStateNotifierProvider = NotifierProvider.internal( - MapStateNotifier.new, - name: r'mapStateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$mapStateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); + MapStateNotifier.new, + name: r'mapStateNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapStateNotifierHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$MapStateNotifier = Notifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/memory.provider.dart b/mobile/lib/providers/memory.provider.dart index aed546002d..7fef3060cc 100644 --- a/mobile/lib/providers/memory.provider.dart +++ b/mobile/lib/providers/memory.provider.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; import 'package:immich_mobile/services/memory.service.dart'; -final memoryFutureProvider = - FutureProvider.autoDispose?>((ref) async { +final memoryFutureProvider = FutureProvider.autoDispose?>((ref) async { final service = ref.watch(memoryServiceProvider); return await service.getMemoryLane(); diff --git a/mobile/lib/providers/network.provider.dart b/mobile/lib/providers/network.provider.dart index 5cb2fae4b1..cd91ff6d56 100644 --- a/mobile/lib/providers/network.provider.dart +++ b/mobile/lib/providers/network.provider.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/network.service.dart'; final networkProvider = StateNotifierProvider((ref) { - return NetworkNotifier( - ref.watch(networkServiceProvider), - ); + return NetworkNotifier(ref.watch(networkServiceProvider)); }); class NetworkNotifier extends StateNotifier { diff --git a/mobile/lib/providers/notification_permission.provider.dart b/mobile/lib/providers/notification_permission.provider.dart index 608f35d63f..da0badd4ec 100644 --- a/mobile/lib/providers/notification_permission.provider.dart +++ b/mobile/lib/providers/notification_permission.provider.dart @@ -5,11 +5,7 @@ import 'package:permission_handler/permission_handler.dart'; class NotificationPermissionNotifier extends StateNotifier { NotificationPermissionNotifier() - : super( - Platform.isAndroid - ? PermissionStatus.granted - : PermissionStatus.restricted, - ) { + : super(Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted) { // Sets the initial state getNotificationPermission().then((p) => state = p); } @@ -40,7 +36,6 @@ class NotificationPermissionNotifier extends StateNotifier { } } -final notificationPermissionProvider = - StateNotifierProvider( +final notificationPermissionProvider = StateNotifierProvider( (ref) => NotificationPermissionNotifier(), ); diff --git a/mobile/lib/providers/oauth.provider.dart b/mobile/lib/providers/oauth.provider.dart index d8d66122f7..14b3353943 100644 --- a/mobile/lib/providers/oauth.provider.dart +++ b/mobile/lib/providers/oauth.provider.dart @@ -2,5 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/oauth.service.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final oAuthServiceProvider = - Provider((ref) => OAuthService(ref.watch(apiServiceProvider))); +final oAuthServiceProvider = Provider((ref) => OAuthService(ref.watch(apiServiceProvider))); diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index f210c7fe3f..5a85cea1d4 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -12,17 +12,20 @@ class PartnerSharedWithNotifier extends StateNotifier> { PartnerSharedWithNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedWith().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedWith().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedWith() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedWith().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } Future updatePartner(UserDto partner, {required bool inTimeline}) { @@ -38,11 +41,8 @@ class PartnerSharedWithNotifier extends StateNotifier> { } } -final partnerSharedWithProvider = - StateNotifierProvider>((ref) { - return PartnerSharedWithNotifier( - ref.watch(partnerServiceProvider), - ); +final partnerSharedWithProvider = StateNotifierProvider>((ref) { + return PartnerSharedWithNotifier(ref.watch(partnerServiceProvider)); }); class PartnerSharedByNotifier extends StateNotifier> { @@ -51,17 +51,20 @@ class PartnerSharedByNotifier extends StateNotifier> { PartnerSharedByNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedBy().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedBy().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedBy() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedBy().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } @override @@ -73,13 +76,11 @@ class PartnerSharedByNotifier extends StateNotifier> { } } -final partnerSharedByProvider = - StateNotifierProvider>((ref) { +final partnerSharedByProvider = StateNotifierProvider>((ref) { return PartnerSharedByNotifier(ref.watch(partnerServiceProvider)); }); -final partnerAvailableProvider = - FutureProvider.autoDispose>((ref) async { +final partnerAvailableProvider = FutureProvider.autoDispose>((ref) async { final otherUsers = await ref.watch(otherUsersProvider.future); final currentPartners = ref.watch(partnerSharedByProvider); final available = Set.of(otherUsers); diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart index 74d86f4767..c51f67bc0e 100644 --- a/mobile/lib/providers/routes.provider.dart +++ b/mobile/lib/providers/routes.provider.dart @@ -1,4 +1,7 @@ +import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; final inLockedViewProvider = StateProvider((ref) => false); final currentRouteNameProvider = StateProvider((ref) => null); +final previousRouteNameProvider = StateProvider((ref) => null); +final previousRouteDataProvider = StateProvider((ref) => null); diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index 65afb39b7c..9a37d83320 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -8,16 +8,14 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'paginated_search.provider.g.dart'; -final paginatedSearchProvider = - StateNotifierProvider( +final paginatedSearchProvider = StateNotifierProvider( (ref) => PaginatedSearchNotifier(ref.watch(searchServiceProvider)), ); class PaginatedSearchNotifier extends StateNotifier { final SearchService _searchService; - PaginatedSearchNotifier(this._searchService) - : super(const SearchResult(assets: [], nextPage: 1)); + PaginatedSearchNotifier(this._searchService) : super(const SearchResult(assets: [], nextPage: 1)); Future search(SearchFilter filter) async { if (state.nextPage == null) { @@ -30,10 +28,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } @@ -44,13 +39,8 @@ class PaginatedSearchNotifier extends StateNotifier { } @riverpod -Future paginatedSearchRenderList( - Ref ref, -) { +Future paginatedSearchRenderList(Ref ref) { final result = ref.watch(paginatedSearchProvider); final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - result.assets, - GroupAssetsBy.none, - ); + return timelineService.getTimelineFromAssets(result.assets, GroupAssetsBy.none); } diff --git a/mobile/lib/providers/search/paginated_search.provider.g.dart b/mobile/lib/providers/search/paginated_search.provider.g.dart index 650cf130fc..e984997967 100644 --- a/mobile/lib/providers/search/paginated_search.provider.g.dart +++ b/mobile/lib/providers/search/paginated_search.provider.g.dart @@ -13,14 +13,14 @@ String _$paginatedSearchRenderListHash() => @ProviderFor(paginatedSearchRenderList) final paginatedSearchRenderListProvider = AutoDisposeFutureProvider.internal( - paginatedSearchRenderList, - name: r'paginatedSearchRenderListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$paginatedSearchRenderListHash, - dependencies: null, - allTransitiveDependencies: null, -); + paginatedSearchRenderList, + name: r'paginatedSearchRenderListProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchRenderListHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index f6ac9d1125..3ff8d67983 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -9,9 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'people.provider.g.dart'; @riverpod -Future> getAllPeople( - Ref ref, -) async { +Future> getAllPeople(Ref ref) async { final PersonService personService = ref.read(personServiceProvider); final people = await personService.getAllPeople(); @@ -25,17 +23,12 @@ Future personAssets(Ref ref, String personId) async { final assets = await personService.getPersonAssets(personId); final settings = ref.read(appSettingsServiceProvider); - final groupBy = - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; + final groupBy = GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; return await RenderList.fromAssets(assets, groupBy); } @riverpod -Future updatePersonName( - Ref ref, - String personId, - String updatedName, -) async { +Future updatePersonName(Ref ref, String personId, String updatedName) async { final PersonService personService = ref.read(personServiceProvider); final person = await personService.updateName(personId, updatedName); diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart index 4625891abb..9595c36eec 100644 --- a/mobile/lib/providers/search/people.provider.g.dart +++ b/mobile/lib/providers/search/people.provider.g.dart @@ -12,13 +12,14 @@ String _$getAllPeopleHash() => r'2c5e6a207683f15ab209650615fdf9cb7f76c736'; @ProviderFor(getAllPeople) final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( - getAllPeople, - name: r'getAllPeopleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$getAllPeopleHash, - dependencies: null, - allTransitiveDependencies: null, -); + getAllPeople, + name: r'getAllPeopleProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getAllPeopleHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element @@ -56,21 +57,15 @@ class PersonAssetsFamily extends Family> { const PersonAssetsFamily(); /// See also [personAssets]. - PersonAssetsProvider call( - String personId, - ) { - return PersonAssetsProvider( - personId, - ); + PersonAssetsProvider call(String personId) { + return PersonAssetsProvider(personId); } @override PersonAssetsProvider getProviderOverride( covariant PersonAssetsProvider provider, ) { - return call( - provider.personId, - ); + return call(provider.personId); } static const Iterable? _dependencies = null; @@ -91,24 +86,19 @@ class PersonAssetsFamily extends Family> { /// See also [personAssets]. class PersonAssetsProvider extends AutoDisposeFutureProvider { /// See also [personAssets]. - PersonAssetsProvider( - String personId, - ) : this._internal( - (ref) => personAssets( - ref as PersonAssetsRef, - personId, - ), - from: personAssetsProvider, - name: r'personAssetsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$personAssetsHash, - dependencies: PersonAssetsFamily._dependencies, - allTransitiveDependencies: - PersonAssetsFamily._allTransitiveDependencies, - personId: personId, - ); + PersonAssetsProvider(String personId) + : this._internal( + (ref) => personAssets(ref as PersonAssetsRef, personId), + from: personAssetsProvider, + name: r'personAssetsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$personAssetsHash, + dependencies: PersonAssetsFamily._dependencies, + allTransitiveDependencies: + PersonAssetsFamily._allTransitiveDependencies, + personId: personId, + ); PersonAssetsProvider._internal( super._createNotifier, { @@ -167,7 +157,8 @@ mixin PersonAssetsRef on AutoDisposeFutureProviderRef { } class _PersonAssetsProviderElement - extends AutoDisposeFutureProviderElement with PersonAssetsRef { + extends AutoDisposeFutureProviderElement + with PersonAssetsRef { _PersonAssetsProviderElement(super.provider); @override @@ -186,24 +177,15 @@ class UpdatePersonNameFamily extends Family> { const UpdatePersonNameFamily(); /// See also [updatePersonName]. - UpdatePersonNameProvider call( - String personId, - String updatedName, - ) { - return UpdatePersonNameProvider( - personId, - updatedName, - ); + UpdatePersonNameProvider call(String personId, String updatedName) { + return UpdatePersonNameProvider(personId, updatedName); } @override UpdatePersonNameProvider getProviderOverride( covariant UpdatePersonNameProvider provider, ) { - return call( - provider.personId, - provider.updatedName, - ); + return call(provider.personId, provider.updatedName); } static const Iterable? _dependencies = null; @@ -224,27 +206,21 @@ class UpdatePersonNameFamily extends Family> { /// See also [updatePersonName]. class UpdatePersonNameProvider extends AutoDisposeFutureProvider { /// See also [updatePersonName]. - UpdatePersonNameProvider( - String personId, - String updatedName, - ) : this._internal( - (ref) => updatePersonName( - ref as UpdatePersonNameRef, - personId, - updatedName, - ), - from: updatePersonNameProvider, - name: r'updatePersonNameProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$updatePersonNameHash, - dependencies: UpdatePersonNameFamily._dependencies, - allTransitiveDependencies: - UpdatePersonNameFamily._allTransitiveDependencies, - personId: personId, - updatedName: updatedName, - ); + UpdatePersonNameProvider(String personId, String updatedName) + : this._internal( + (ref) => + updatePersonName(ref as UpdatePersonNameRef, personId, updatedName), + from: updatePersonNameProvider, + name: r'updatePersonNameProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$updatePersonNameHash, + dependencies: UpdatePersonNameFamily._dependencies, + allTransitiveDependencies: + UpdatePersonNameFamily._allTransitiveDependencies, + personId: personId, + updatedName: updatedName, + ); UpdatePersonNameProvider._internal( super._createNotifier, { @@ -312,7 +288,8 @@ mixin UpdatePersonNameRef on AutoDisposeFutureProviderRef { } class _UpdatePersonNameProviderElement - extends AutoDisposeFutureProviderElement with UpdatePersonNameRef { + extends AutoDisposeFutureProviderElement + with UpdatePersonNameRef { _UpdatePersonNameProviderElement(super.provider); @override @@ -320,5 +297,6 @@ class _UpdatePersonNameProviderElement @override String get updatedName => (origin as UpdatePersonNameProvider).updatedName; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_filter.provider.g.dart b/mobile/lib/providers/search/search_filter.provider.g.dart index 03f88b0332..5a322ca285 100644 --- a/mobile/lib/providers/search/search_filter.provider.g.dart +++ b/mobile/lib/providers/search/search_filter.provider.g.dart @@ -95,29 +95,28 @@ class GetSearchSuggestionsProvider String? make, String? model, }) : this._internal( - (ref) => getSearchSuggestions( - ref as GetSearchSuggestionsRef, - type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ), - from: getSearchSuggestionsProvider, - name: r'getSearchSuggestionsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$getSearchSuggestionsHash, - dependencies: GetSearchSuggestionsFamily._dependencies, - allTransitiveDependencies: - GetSearchSuggestionsFamily._allTransitiveDependencies, - type: type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ); + (ref) => getSearchSuggestions( + ref as GetSearchSuggestionsRef, + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + from: getSearchSuggestionsProvider, + name: r'getSearchSuggestionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getSearchSuggestionsHash, + dependencies: GetSearchSuggestionsFamily._dependencies, + allTransitiveDependencies: + GetSearchSuggestionsFamily._allTransitiveDependencies, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); GetSearchSuggestionsProvider._internal( super._createNotifier, { @@ -227,5 +226,6 @@ class _GetSearchSuggestionsProviderElement @override String? get model => (origin as GetSearchSuggestionsProvider).model; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart index d0e3720c0f..23d5606922 100644 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ b/mobile/lib/providers/search/search_page_state.provider.dart @@ -3,8 +3,7 @@ import 'package:immich_mobile/models/search/search_curated_content.model.dart'; import 'package:immich_mobile/services/search.service.dart'; -final getPreviewPlacesProvider = - FutureProvider.autoDispose>((ref) async { +final getPreviewPlacesProvider = FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); final exploreData = await searchService.getExploreData(); @@ -13,23 +12,14 @@ final getPreviewPlacesProvider = return []; } - final locations = - exploreData.firstWhere((data) => data.fieldName == "exifInfo.city").items; + final locations = exploreData.firstWhere((data) => data.fieldName == "exifInfo.city").items; - final curatedContent = locations - .map( - (l) => SearchCuratedContent( - label: l.value, - id: l.data.id, - ), - ) - .toList(); + final curatedContent = locations.map((l) => SearchCuratedContent(label: l.value, id: l.data.id)).toList(); return curatedContent; }); -final getAllPlacesProvider = - FutureProvider.autoDispose>((ref) async { +final getAllPlacesProvider = FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); final assetPlaces = await searchService.getAllPlaces(); @@ -39,12 +29,7 @@ final getAllPlacesProvider = } final curatedContent = assetPlaces - .map( - (data) => SearchCuratedContent( - label: data.exifInfo!.city!, - id: data.id, - ), - ) + .map((data) => SearchCuratedContent(label: data.exifInfo!.city!, id: data.id)) .toList(); return curatedContent; diff --git a/mobile/lib/providers/secure_storage.provider.dart b/mobile/lib/providers/secure_storage.provider.dart index 0194e527e9..39813d1027 100644 --- a/mobile/lib/providers/secure_storage.provider.dart +++ b/mobile/lib/providers/secure_storage.provider.dart @@ -1,7 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -final secureStorageProvider = - StateNotifierProvider((ref) { +final secureStorageProvider = StateNotifierProvider((ref) { return SecureStorageProvider(); }); diff --git a/mobile/lib/providers/server_info.provider.dart b/mobile/lib/providers/server_info.provider.dart index aabf6a5390..25b1002b7a 100644 --- a/mobile/lib/providers/server_info.provider.dart +++ b/mobile/lib/providers/server_info.provider.dart @@ -11,43 +11,24 @@ import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) - : super( - const ServerInfo( - serverVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - latestVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - serverFeatures: ServerFeatures( - map: true, - trash: true, - oauthEnabled: false, - passwordLogin: true, - ), - serverConfig: ServerConfig( - trashDays: 30, - oauthButtonText: '', - externalDomain: '', - mapLightStyleUrl: - 'https://tiles.immich.cloud/v1/style/light.json', - mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', - ), - serverDiskInfo: ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - isVersionMismatch: false, - isNewReleaseAvailable: false, - versionMismatchErrorMessage: "", + : super( + const ServerInfo( + serverVersion: ServerVersion(major: 0, minor: 0, patch: 0), + latestVersion: ServerVersion(major: 0, minor: 0, patch: 0), + serverFeatures: ServerFeatures(map: true, trash: true, oauthEnabled: false, passwordLogin: true), + serverConfig: ServerConfig( + trashDays: 30, + oauthButtonText: '', + externalDomain: '', + mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', + mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', ), - ); + serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + isVersionMismatch: false, + isNewReleaseAvailable: false, + versionMismatchErrorMessage: "", + ), + ); final ServerInfoService _serverInfoService; final _log = Logger("ServerInfoNotifier"); @@ -63,19 +44,14 @@ class ServerInfoNotifier extends StateNotifier { final serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { - state = state.copyWith( - isVersionMismatch: true, - versionMismatchErrorMessage: "common_server_error".tr(), - ); + state = state.copyWith(isVersionMismatch: true, versionMismatchErrorMessage: "common_server_error".tr()); return; } await _checkServerVersionMismatch(serverVersion); } catch (e, stackTrace) { _log.severe("Failed to get server version", e, stackTrace); - state = state.copyWith( - isVersionMismatch: true, - ); + state = state.copyWith(isVersionMismatch: true); return; } } @@ -90,8 +66,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["major"]! > serverVersion.major) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_server_out_of_date_major".tr(), + versionMismatchErrorMessage: "profile_drawer_server_out_of_date_major".tr(), ); return; } @@ -99,8 +74,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["major"]! < serverVersion.major) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_client_out_of_date_major".tr(), + versionMismatchErrorMessage: "profile_drawer_client_out_of_date_major".tr(), ); return; } @@ -108,8 +82,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["minor"]! > serverVersion.minor) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_server_out_of_date_minor".tr(), + versionMismatchErrorMessage: "profile_drawer_server_out_of_date_minor".tr(), ); return; } @@ -117,35 +90,26 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["minor"]! < serverVersion.minor) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_client_out_of_date_minor".tr(), + versionMismatchErrorMessage: "profile_drawer_client_out_of_date_minor".tr(), ); return; } - state = state.copyWith( - isVersionMismatch: false, - versionMismatchErrorMessage: "", - ); + state = state.copyWith(isVersionMismatch: false, versionMismatchErrorMessage: ""); } - handleNewRelease( - ServerVersion serverVersion, - ServerVersion latestVersion, - ) { + handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) { // Update local server version _checkServerVersionMismatch(serverVersion); final majorEqual = latestVersion.major == serverVersion.major; final minorEqual = majorEqual && latestVersion.minor == serverVersion.minor; - final newVersionAvailable = latestVersion.major > serverVersion.major || + final newVersionAvailable = + latestVersion.major > serverVersion.major || (majorEqual && latestVersion.minor > serverVersion.minor) || (minorEqual && latestVersion.patch > serverVersion.patch); - state = state.copyWith( - latestVersion: latestVersion, - isNewReleaseAvailable: newVersionAvailable, - ); + state = state.copyWith(latestVersion: latestVersion, isNewReleaseAvailable: newVersionAvailable); } getServerFeatures() async { @@ -171,15 +135,10 @@ class ServerInfoNotifier extends StateNotifier { var minor = detail[1]; var patch = detail[2]; - return { - "major": int.parse(major), - "minor": int.parse(minor), - "patch": int.parse(patch.replaceAll("-DEBUG", "")), - }; + return {"major": int.parse(major), "minor": int.parse(minor), "patch": int.parse(patch.replaceAll("-DEBUG", ""))}; } } -final serverInfoProvider = - StateNotifierProvider((ref) { +final serverInfoProvider = StateNotifierProvider((ref) { return ServerInfoNotifier(ref.read(serverInfoServiceProvider)); }); diff --git a/mobile/lib/providers/shared_link.provider.dart b/mobile/lib/providers/shared_link.provider.dart index 29b628c765..f574554bcb 100644 --- a/mobile/lib/providers/shared_link.provider.dart +++ b/mobile/lib/providers/shared_link.provider.dart @@ -20,10 +20,6 @@ class SharedLinksNotifier extends StateNotifier>> { } } -final sharedLinksStateProvider = - StateNotifierProvider>>( - (ref) { - return SharedLinksNotifier( - ref.watch(sharedLinkServiceProvider), - ); +final sharedLinksStateProvider = StateNotifierProvider>>((ref) { + return SharedLinksNotifier(ref.watch(sharedLinkServiceProvider)); }); diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart index 18d851aa19..8e24bbf4d0 100644 --- a/mobile/lib/providers/sync_status.provider.dart +++ b/mobile/lib/providers/sync_status.provider.dart @@ -1,43 +1,66 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum SyncStatus { idle, syncing, success, - error, + error; + + localized() { + return switch (this) { + SyncStatus.idle => "idle".tr(), + SyncStatus.syncing => "running".tr(), + SyncStatus.success => "success".tr(), + SyncStatus.error => "error".tr(), + }; + } } class SyncStatusState { final SyncStatus remoteSyncStatus; + final SyncStatus localSyncStatus; + final SyncStatus hashJobStatus; + final String? errorMessage; const SyncStatusState({ this.remoteSyncStatus = SyncStatus.idle, + this.localSyncStatus = SyncStatus.idle, + this.hashJobStatus = SyncStatus.idle, this.errorMessage, }); SyncStatusState copyWith({ SyncStatus? remoteSyncStatus, + SyncStatus? localSyncStatus, + SyncStatus? hashJobStatus, String? errorMessage, }) { return SyncStatusState( remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus, + localSyncStatus: localSyncStatus ?? this.localSyncStatus, + hashJobStatus: hashJobStatus ?? this.hashJobStatus, errorMessage: errorMessage ?? this.errorMessage, ); } bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; + bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing; + bool get isHashing => hashJobStatus == SyncStatus.syncing; @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is SyncStatusState && other.remoteSyncStatus == remoteSyncStatus && + other.localSyncStatus == localSyncStatus && + other.hashJobStatus == hashJobStatus && other.errorMessage == errorMessage; } @override - int get hashCode => Object.hash(remoteSyncStatus, errorMessage); + int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage); } class SyncStatusNotifier extends Notifier { @@ -46,23 +69,46 @@ class SyncStatusNotifier extends Notifier { return const SyncStatusState( errorMessage: null, remoteSyncStatus: SyncStatus.idle, + localSyncStatus: SyncStatus.idle, + hashJobStatus: SyncStatus.idle, ); } + /// + /// Remote Sync + /// + void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { - state = state.copyWith( - remoteSyncStatus: status, - errorMessage: status == SyncStatus.error ? errorMessage : null, - ); + state = state.copyWith(remoteSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); } void startRemoteSync() => setRemoteSyncStatus(SyncStatus.syncing); void completeRemoteSync() => setRemoteSyncStatus(SyncStatus.success); - void errorRemoteSync(String error) => - setRemoteSyncStatus(SyncStatus.error, error); + void errorRemoteSync(String error) => setRemoteSyncStatus(SyncStatus.error, error); + + /// + /// Local Sync + /// + + void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith(localSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); + } + + void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); + void completeLocalSync() => setLocalSyncStatus(SyncStatus.success); + void errorLocalSync(String error) => setLocalSyncStatus(SyncStatus.error, error); + + /// + /// Hash Job + /// + + void setHashJobStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith(hashJobStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); + } + + void startHashJob() => setHashJobStatus(SyncStatus.syncing); + void completeHashJob() => setHashJobStatus(SyncStatus.success); + void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); } -final syncStatusProvider = - NotifierProvider( - SyncStatusNotifier.new, -); +final syncStatusProvider = NotifierProvider(SyncStatusNotifier.new); diff --git a/mobile/lib/providers/tab.provider.dart b/mobile/lib/providers/tab.provider.dart index a4875115ce..d523e72c38 100644 --- a/mobile/lib/providers/tab.provider.dart +++ b/mobile/lib/providers/tab.provider.dart @@ -3,6 +3,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum TabEnum { home, search, albums, library } /// Provides the currently active tab -final tabProvider = StateProvider( - (ref) => TabEnum.home, -); +final tabProvider = StateProvider((ref) => TabEnum.home); diff --git a/mobile/lib/providers/theme.provider.dart b/mobile/lib/providers/theme.provider.dart index 73623bd026..1d5511f1ff 100644 --- a/mobile/lib/providers/theme.provider.dart +++ b/mobile/lib/providers/theme.provider.dart @@ -7,13 +7,12 @@ import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final immichThemeModeProvider = StateProvider((ref) { - final themeMode = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.themeMode); + final themeMode = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.themeMode); - debugPrint("Current themeMode $themeMode"); + dPrint(() => "Current themeMode $themeMode"); if (themeMode == ThemeMode.light.name) { return ThemeMode.light; @@ -26,36 +25,25 @@ final immichThemeModeProvider = StateProvider((ref) { final immichThemePresetProvider = StateProvider((ref) { final appSettingsProvider = ref.watch(appSettingsServiceProvider); - final primaryColorPreset = - appSettingsProvider.getSetting(AppSettingsEnum.primaryColor); + final primaryColorPreset = appSettingsProvider.getSetting(AppSettingsEnum.primaryColor); - debugPrint("Current theme preset $primaryColorPreset"); + dPrint(() => "Current theme preset $primaryColorPreset"); try { - return ImmichColorPreset.values - .firstWhere((e) => e.name == primaryColorPreset); + return ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorPreset); } catch (e) { - debugPrint( - "Theme preset $primaryColorPreset not found. Applying default preset.", - ); - appSettingsProvider.setSetting( - AppSettingsEnum.primaryColor, - defaultColorPresetName, - ); + dPrint(() => "Theme preset $primaryColorPreset not found. Applying default preset."); + appSettingsProvider.setSetting(AppSettingsEnum.primaryColor, defaultColorPresetName); return defaultColorPreset; } }); final dynamicThemeSettingProvider = StateProvider((ref) { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.dynamicTheme); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.dynamicTheme); }); final colorfulInterfaceSettingProvider = StateProvider((ref) { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.colorfulInterface); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.colorfulInterface); }); // Provider for current selected theme @@ -64,11 +52,7 @@ final immichThemeProvider = StateProvider((ref) { final useSystemColor = ref.watch(dynamicThemeSettingProvider); final useColorfulInterface = ref.watch(colorfulInterfaceSettingProvider); final ImmichTheme? dynamicTheme = DynamicTheme.theme; - final currentTheme = (useSystemColor && dynamicTheme != null) - ? dynamicTheme - : primaryColorPreset.themeOfPreset; + final currentTheme = (useSystemColor && dynamicTheme != null) ? dynamicTheme : primaryColorPreset.themeOfPreset; - return useColorfulInterface - ? currentTheme - : decolorizeSurfaces(theme: currentTheme); + return useColorfulInterface ? currentTheme : decolorizeSurfaces(theme: currentTheme); }); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index b2c763cdfa..71ea308dbf 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -5,31 +5,23 @@ import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -final singleUserTimelineProvider = StreamProvider.family( - (ref, userId) { - if (userId == null) { - return const Stream.empty(); - } +final singleUserTimelineProvider = StreamProvider.family((ref, userId) { + if (userId == null) { + return const Stream.empty(); + } - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchHomeTimeline(userId); - }, - dependencies: [localeProvider], -); + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchHomeTimeline(userId); +}, dependencies: [localeProvider]); -final multiUsersTimelineProvider = - StreamProvider.family>( - (ref, userIds) { - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchMultiUsersTimeline(userIds); - }, - dependencies: [localeProvider], -); +final multiUsersTimelineProvider = StreamProvider.family>((ref, userIds) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchMultiUsersTimeline(userIds); +}, dependencies: [localeProvider]); -final albumTimelineProvider = - StreamProvider.autoDispose.family((ref, id) { +final albumTimelineProvider = StreamProvider.autoDispose.family((ref, id) { final album = ref.watch(albumWatcher(id)).value; final timelineService = ref.watch(timelineServiceProvider); @@ -65,13 +57,9 @@ final assetSelectionTimelineProvider = StreamProvider((ref) { return timelineService.watchAssetSelectionTimeline(); }); -final assetsTimelineProvider = - FutureProvider.family>((ref, assets) { +final assetsTimelineProvider = FutureProvider.family>((ref, assets) { final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - assets, - null, - ); + return timelineService.getTimelineFromAssets(assets, null); }); final lockedTimelineProvider = StreamProvider((ref) { diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index b1a926545d..6949413cd9 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -1,36 +1,38 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; -final multiSelectProvider = - NotifierProvider( +final multiSelectProvider = NotifierProvider( MultiSelectNotifier.new, dependencies: [timelineServiceProvider], ); +class MultiSelectToggleEvent extends Event { + final bool isEnabled; + const MultiSelectToggleEvent(this.isEnabled); +} + class MultiSelectState { final Set selectedAssets; final Set lockedSelectionAssets; final bool forceEnable; - const MultiSelectState({ - required this.selectedAssets, - required this.lockedSelectionAssets, - this.forceEnable = false, - }); + const MultiSelectState({required this.selectedAssets, required this.lockedSelectionAssets, this.forceEnable = false}); bool get isEnabled => selectedAssets.isNotEmpty; - bool get hasRemote => selectedAssets.any( - (asset) => - asset.storage == AssetState.remote || - asset.storage == AssetState.merged, - ); - bool get hasLocal => selectedAssets.any( - (asset) => asset.storage == AssetState.local, - ); + + /// Cloud only + bool get hasRemote => + selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); + + bool get hasStacked => selectedAssets.any((asset) => asset is RemoteAsset && asset.stackId != null); + + bool get hasLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); + + bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); MultiSelectState copyWith({ Set? selectedAssets, @@ -39,8 +41,7 @@ class MultiSelectState { }) { return MultiSelectState( selectedAssets: selectedAssets ?? this.selectedAssets, - lockedSelectionAssets: - lockedSelectionAssets ?? this.lockedSelectionAssets, + lockedSelectionAssets: lockedSelectionAssets ?? this.lockedSelectionAssets, forceEnable: forceEnable ?? this.forceEnable, ); } @@ -60,10 +61,7 @@ class MultiSelectState { } @override - int get hashCode => - selectedAssets.hashCode ^ - lockedSelectionAssets.hashCode ^ - forceEnable.hashCode; + int get hashCode => selectedAssets.hashCode ^ lockedSelectionAssets.hashCode ^ forceEnable.hashCode; } class MultiSelectNotifier extends Notifier { @@ -74,12 +72,7 @@ class MultiSelectNotifier extends Notifier { @override MultiSelectState build() { - return _defaultState ?? - const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + return _defaultState ?? const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } void selectAsset(BaseAsset asset) { @@ -87,9 +80,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: {...state.selectedAssets, asset}, - ); + state = state.copyWith(selectedAssets: {...state.selectedAssets, asset}); } void deselectAsset(BaseAsset asset) { @@ -97,9 +88,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: state.selectedAssets.where((a) => a != asset).toSet(), - ); + state = state.copyWith(selectedAssets: state.selectedAssets.where((a) => a != asset).toSet()); } void toggleAssetSelection(BaseAsset asset) { @@ -111,11 +100,7 @@ class MultiSelectNotifier extends Notifier { } void reset() { - state = const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + state = const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } /// Bucket bulk operations @@ -125,9 +110,7 @@ class MultiSelectNotifier extends Notifier { selectedAssets.addAll(assets); - state = state.copyWith( - selectedAssets: selectedAssets, - ); + state = state.copyWith(selectedAssets: selectedAssets); } void deselectBucket(int offset, int bucketCount) async { @@ -148,8 +131,7 @@ class MultiSelectNotifier extends Notifier { if (bucketAssets.isEmpty) return; // Check if all assets in this bucket are currently selected - final allSelected = - bucketAssets.every((asset) => state.selectedAssets.contains(asset)); + final allSelected = bucketAssets.every((asset) => state.selectedAssets.contains(asset)); final selectedAssets = state.selectedAssets.toSet(); @@ -165,21 +147,15 @@ class MultiSelectNotifier extends Notifier { } void setLockedSelectionAssets(Set assets) { - state = state.copyWith( - lockedSelectionAssets: assets, - ); + state = state.copyWith(lockedSelectionAssets: assets); } } -final bucketSelectionProvider = Provider.family>( - (ref, bucketAssets) { - final selectedAssets = - ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); +final bucketSelectionProvider = Provider.family>((ref, bucketAssets) { + final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - if (bucketAssets.isEmpty) return false; + if (bucketAssets.isEmpty) return false; - // Check if all assets in the bucket are selected - return bucketAssets.every((asset) => selectedAssets.contains(asset)); - }, - dependencies: [multiSelectProvider, timelineServiceProvider], -); + // Check if all assets in the bucket are selected + return bucketAssets.every((asset) => selectedAssets.contains(asset)); +}, dependencies: [multiSelectProvider, timelineServiceProvider]); diff --git a/mobile/lib/providers/trash.provider.dart b/mobile/lib/providers/trash.provider.dart index c78cccff8a..adf3b1027b 100644 --- a/mobile/lib/providers/trash.provider.dart +++ b/mobile/lib/providers/trash.provider.dart @@ -7,9 +7,7 @@ class TrashNotifier extends StateNotifier { final TrashService _trashService; final _log = Logger('TrashNotifier'); - TrashNotifier( - this._trashService, - ) : super(false); + TrashNotifier(this._trashService) : super(false); Future emptyTrash() async { try { @@ -43,7 +41,5 @@ class TrashNotifier extends StateNotifier { } final trashProvider = StateNotifierProvider((ref) { - return TrashNotifier( - ref.watch(trashServiceProvider), - ); + return TrashNotifier(ref.watch(trashServiceProvider)); }); diff --git a/mobile/lib/providers/upload_profile_image.provider.dart b/mobile/lib/providers/upload_profile_image.provider.dart index fe13944ef6..5aa924ed1c 100644 --- a/mobile/lib/providers/upload_profile_image.provider.dart +++ b/mobile/lib/providers/upload_profile_image.provider.dart @@ -1,31 +1,20 @@ import 'dart:convert'; -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:image_picker/image_picker.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; -enum UploadProfileStatus { - idle, - loading, - success, - failure, -} +enum UploadProfileStatus { idle, loading, success, failure } class UploadProfileImageState { // enum final UploadProfileStatus status; final String profileImagePath; - const UploadProfileImageState({ - required this.status, - required this.profileImagePath, - }); + const UploadProfileImageState({required this.status, required this.profileImagePath}); - UploadProfileImageState copyWith({ - UploadProfileStatus? status, - String? profileImagePath, - }) { + UploadProfileImageState copyWith({UploadProfileStatus? status, String? profileImagePath}) { return UploadProfileImageState( status: status ?? this.status, profileImagePath: profileImagePath ?? this.profileImagePath, @@ -50,52 +39,36 @@ class UploadProfileImageState { String toJson() => json.encode(toMap()); - factory UploadProfileImageState.fromJson(String source) => - UploadProfileImageState.fromMap(json.decode(source)); + factory UploadProfileImageState.fromJson(String source) => UploadProfileImageState.fromMap(json.decode(source)); @override - String toString() => - 'UploadProfileImageState(status: $status, profileImagePath: $profileImagePath)'; + String toString() => 'UploadProfileImageState(status: $status, profileImagePath: $profileImagePath)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is UploadProfileImageState && - other.status == status && - other.profileImagePath == profileImagePath; + return other is UploadProfileImageState && other.status == status && other.profileImagePath == profileImagePath; } @override int get hashCode => status.hashCode ^ profileImagePath.hashCode; } -class UploadProfileImageNotifier - extends StateNotifier { +class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userService) - : super( - const UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - ), - ); + : super(const UploadProfileImageState(profileImagePath: '', status: UploadProfileStatus.idle)); final UserService _userService; Future upload(XFile file) async { state = state.copyWith(status: UploadProfileStatus.loading); - var profileImagePath = await _userService.createProfileImage( - file.name, - await file.readAsBytes(), - ); + var profileImagePath = await _userService.createProfileImage(file.name, await file.readAsBytes()); if (profileImagePath != null) { - debugPrint("Successfully upload profile image"); - state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: profileImagePath, - ); + dPrint(() => "Successfully upload profile image"); + state = state.copyWith(status: UploadProfileStatus.success, profileImagePath: profileImagePath); return true; } @@ -104,7 +77,6 @@ class UploadProfileImageNotifier } } -final uploadProfileImageProvider = - StateNotifierProvider( +final uploadProfileImageProvider = StateNotifierProvider( ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))), ); diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart index 1a1c21554c..10dcb2aff5 100644 --- a/mobile/lib/providers/user.provider.dart +++ b/mobile/lib/providers/user.provider.dart @@ -10,8 +10,7 @@ import 'package:immich_mobile/services/timeline.service.dart'; class CurrentUserProvider extends StateNotifier { CurrentUserProvider(this._userService) : super(null) { state = _userService.tryGetMyUser(); - streamSub = - _userService.watchMyUser().listen((user) => state = user ?? state); + streamSub = _userService.watchMyUser().listen((user) => state = user ?? state); } final UserService _userService; @@ -30,8 +29,7 @@ class CurrentUserProvider extends StateNotifier { } } -final currentUserProvider = - StateNotifierProvider((ref) { +final currentUserProvider = StateNotifierProvider((ref) { return CurrentUserProvider(ref.watch(userServiceProvider)); }); @@ -56,7 +54,6 @@ class TimelineUserIdsProvider extends StateNotifier> { } } -final timelineUsersIdsProvider = - StateNotifierProvider>((ref) { +final timelineUsersIdsProvider = StateNotifierProvider>((ref) { return TimelineUserIdsProvider(ref.watch(timelineServiceProvider)); }); diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index 2718738286..136c6049a7 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -12,7 +10,6 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; -// import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -21,24 +18,16 @@ import 'package:immich_mobile/utils/debounce.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; -enum PendingAction { - assetDelete, - assetUploaded, - assetHidden, - assetTrash, -} +enum PendingAction { assetDelete, assetUploaded, assetHidden, assetTrash } class PendingChange { final String id; final PendingAction action; final dynamic value; - const PendingChange( - this.id, - this.action, - this.value, - ); + const PendingChange(this.id, this.action, this.value); @override String toString() => 'PendingChange(id: $id, action: $action, value: $value)'; @@ -59,17 +48,9 @@ class WebsocketState { final bool isConnected; final List pendingChanges; - const WebsocketState({ - this.socket, - required this.isConnected, - required this.pendingChanges, - }); + const WebsocketState({this.socket, required this.isConnected, required this.pendingChanges}); - WebsocketState copyWith({ - Socket? socket, - bool? isConnected, - List? pendingChanges, - }) { + WebsocketState copyWith({Socket? socket, bool? isConnected, List? pendingChanges}) { return WebsocketState( socket: socket ?? this.socket, isConnected: isConnected ?? this.isConnected, @@ -78,16 +59,13 @@ class WebsocketState { } @override - String toString() => - 'WebsocketState(socket: $socket, isConnected: $isConnected)'; + String toString() => 'WebsocketState(socket: $socket, isConnected: $isConnected)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is WebsocketState && - other.socket == socket && - other.isConnected == isConnected; + return other is WebsocketState && other.socket == socket && other.isConnected == isConnected; } @override @@ -95,19 +73,11 @@ class WebsocketState { } class WebsocketNotifier extends StateNotifier { - WebsocketNotifier(this._ref) - : super( - const WebsocketState( - socket: null, - isConnected: false, - pendingChanges: [], - ), - ); + WebsocketNotifier(this._ref) : super(const WebsocketState(socket: null, isConnected: false, pendingChanges: [])); final _log = Logger('WebsocketNotifier'); final Ref _ref; - final Debouncer _debounce = - Debouncer(interval: const Duration(milliseconds: 500)); + final Debouncer _debounce = Debouncer(interval: const Duration(milliseconds: 500)); final Debouncer _batchDebouncer = Debouncer( interval: const Duration(seconds: 5), @@ -131,11 +101,10 @@ class WebsocketNotifier extends StateNotifier { final endpoint = Uri.parse(Store.get(StoreKey.serverEndpoint)); final headers = ApiService.getRequestHeaders(); if (endpoint.userInfo.isNotEmpty) { - headers["Authorization"] = - "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; + headers["Authorization"] = "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; } - debugPrint("Attempting to connect to websocket"); + dPrint(() => "Attempting to connect to websocket"); // Configure socket transports must be specified Socket socket = io( endpoint.origin, @@ -151,30 +120,18 @@ class WebsocketNotifier extends StateNotifier { ); socket.onConnect((_) { - debugPrint("Established Websocket Connection"); - state = WebsocketState( - isConnected: true, - socket: socket, - pendingChanges: state.pendingChanges, - ); + dPrint(() => "Established Websocket Connection"); + state = WebsocketState(isConnected: true, socket: socket, pendingChanges: state.pendingChanges); }); socket.onDisconnect((_) { - debugPrint("Disconnect to Websocket Connection"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + dPrint(() => "Disconnect to Websocket Connection"); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); socket.on('error', (errorMessage) { _log.severe("Websocket Error - $errorMessage"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); if (!Store.isBetaTimelineEnabled) { @@ -192,24 +149,20 @@ class WebsocketNotifier extends StateNotifier { socket.on('on_config_update', _handleOnConfigUpdate); socket.on('on_new_release', _handleReleaseUpdates); } catch (e) { - debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); + dPrint(() => "[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); } } } void disconnect() { - debugPrint("Attempting to disconnect from websocket"); + dPrint(() => "Attempting to disconnect from websocket"); _batchedAssetUploadReady.clear(); var socket = state.socket?.disconnect(); if (socket?.disconnected == true) { - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); } } @@ -246,65 +199,43 @@ class WebsocketNotifier extends StateNotifier { } void listenUploadEvent() { - debugPrint("Start listening to event on_upload_success"); + dPrint(() => "Start listening to event on_upload_success"); state.socket?.on('on_upload_success', _handleOnUploadSuccess); } void addPendingChange(PendingAction action, dynamic value) { final now = DateTime.now(); state = state.copyWith( - pendingChanges: [ - ...state.pendingChanges, - PendingChange(now.millisecondsSinceEpoch.toString(), action, value), - ], + pendingChanges: [...state.pendingChanges, PendingChange(now.millisecondsSinceEpoch.toString(), action, value)], ); _debounce.run(handlePendingChanges); } Future _handlePendingTrashes() async { - final trashChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetTrash) - .toList(); + final trashChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetTrash).toList(); if (trashChanges.isNotEmpty) { - List remoteIds = trashChanges - .expand((a) => (a.value as List).map((e) => e.toString())) - .toList(); + List remoteIds = trashChanges.expand((a) => (a.value as List).map((e) => e.toString())).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); await _ref.read(assetProvider.notifier).getAllAsset(); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => trashChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList()); } } Future _handlePendingDeletes() async { - final deleteChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetDelete) - .toList(); + final deleteChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetDelete).toList(); if (deleteChanges.isNotEmpty) { - List remoteIds = - deleteChanges.map((a) => a.value.toString()).toList(); + List remoteIds = deleteChanges.map((a) => a.value.toString()).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => deleteChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList()); } } Future _handlePendingUploaded() async { - final uploadedChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetUploaded) - .toList(); + final uploadedChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetUploaded).toList(); if (uploadedChanges.isNotEmpty) { - List remoteAssets = uploadedChanges - .map((a) => AssetResponseDto.fromJson(a.value)) - .toList(); + List remoteAssets = uploadedChanges.map((a) => AssetResponseDto.fromJson(a.value)).toList(); for (final dto in remoteAssets) { if (dto != null) { final newAsset = Asset.remote(dto); @@ -312,28 +243,19 @@ class WebsocketNotifier extends StateNotifier { } } state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => uploadedChanges.contains(c)) - .toList(), + pendingChanges: state.pendingChanges.whereNot((c) => uploadedChanges.contains(c)).toList(), ); } } Future _handlingPendingHidden() async { - final hiddenChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetHidden) - .toList(); + final hiddenChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetHidden).toList(); if (hiddenChanges.isNotEmpty) { - List remoteIds = - hiddenChanges.map((a) => a.value.toString()).toList(); + List remoteIds = hiddenChanges.map((a) => a.value.toString()).toList(); final db = _ref.watch(dbProvider); await db.writeTxn(() => db.assets.deleteAllByRemoteId(remoteIds)); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => hiddenChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList()); } } @@ -354,18 +276,15 @@ class WebsocketNotifier extends StateNotifier { _ref.read(assetProvider.notifier).getAllAsset(); } - void _handleOnUploadSuccess(dynamic data) => - addPendingChange(PendingAction.assetUploaded, data); + void _handleOnUploadSuccess(dynamic data) => addPendingChange(PendingAction.assetUploaded, data); - void _handleOnAssetDelete(dynamic data) => - addPendingChange(PendingAction.assetDelete, data); + void _handleOnAssetDelete(dynamic data) => addPendingChange(PendingAction.assetDelete, data); void _handleOnAssetTrash(dynamic data) { addPendingChange(PendingAction.assetTrash, data); } - void _handleOnAssetHidden(dynamic data) => - addPendingChange(PendingAction.assetHidden, data); + void _handleOnAssetHidden(dynamic data) => addPendingChange(PendingAction.assetHidden, data); _handleReleaseUpdates(dynamic data) { // Json guard @@ -374,27 +293,21 @@ class WebsocketNotifier extends StateNotifier { } final json = data.cast(); - final serverVersionJson = - json.containsKey('serverVersion') ? json['serverVersion'] : null; - final releaseVersionJson = - json.containsKey('releaseVersion') ? json['releaseVersion'] : null; + final serverVersionJson = json.containsKey('serverVersion') ? json['serverVersion'] : null; + final releaseVersionJson = json.containsKey('releaseVersion') ? json['releaseVersion'] : null; if (serverVersionJson == null || releaseVersionJson == null) { return; } - final serverVersionDto = - ServerVersionResponseDto.fromJson(serverVersionJson); - final releaseVersionDto = - ServerVersionResponseDto.fromJson(releaseVersionJson); + final serverVersionDto = ServerVersionResponseDto.fromJson(serverVersionJson); + final releaseVersionDto = ServerVersionResponseDto.fromJson(releaseVersionJson); if (serverVersionDto == null || releaseVersionDto == null) { return; } final serverVersion = ServerVersion.fromDto(serverVersionDto); final releaseVersion = ServerVersion.fromDto(releaseVersionDto); - _ref - .read(serverInfoProvider.notifier) - .handleNewRelease(serverVersion, releaseVersion); + _ref.read(serverInfoProvider.notifier).handleNewRelease(serverVersion, releaseVersion); } void _handleSyncAssetUploadReady(dynamic data) { @@ -407,11 +320,14 @@ class WebsocketNotifier extends StateNotifier { return; } + final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false); try { unawaited( - _ref - .read(backgroundSyncProvider) - .syncWebsocketBatch(_batchedAssetUploadReady.toList()), + _ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()).then((_) { + if (isSyncAlbumEnabled) { + _ref.read(backgroundSyncProvider).syncLinkedAlbum(); + } + }), ); } catch (error) { _log.severe("Error processing batched AssetUploadReadyV1 events: $error"); @@ -421,7 +337,6 @@ class WebsocketNotifier extends StateNotifier { } } -final websocketProvider = - StateNotifierProvider((ref) { +final websocketProvider = StateNotifierProvider((ref) { return WebsocketNotifier(ref); }); diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 1ee92b2e2f..e8f9abc8c8 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -15,22 +15,14 @@ class ActivityApiRepository extends ApiRepository { ActivityApiRepository(this._api); Future> getAll(String albumId, {String? assetId}) async { - final response = - await checkNull(_api.getActivities(albumId, assetId: assetId)); + final response = await checkNull(_api.getActivities(albumId, assetId: assetId)); return response.map(_toActivity).toList(); } - Future create( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + Future create(String albumId, ActivityType type, {String? assetId, String? comment}) async { final dto = ActivityCreateDto( albumId: albumId, - type: type == ActivityType.comment - ? ReactionType.comment - : ReactionType.like, + type: type == ActivityType.comment ? ReactionType.comment : ReactionType.like, assetId: assetId, comment: comment, ); @@ -43,19 +35,16 @@ class ActivityApiRepository extends ApiRepository { } Future getStats(String albumId, {String? assetId}) async { - final response = - await checkNull(_api.getActivityStatistics(albumId, assetId: assetId)); + final response = await checkNull(_api.getActivityStatistics(albumId, assetId: assetId)); return ActivityStats(comments: response.comments); } static Activity _toActivity(ActivityResponseDto dto) => Activity( - id: dto.id, - createdAt: dto.createdAt, - type: dto.type == ReactionType.comment - ? ActivityType.comment - : ActivityType.like, - user: UserConverter.fromSimpleUserDto(dto.user), - assetId: dto.assetId, - comment: dto.comment, - ); + id: dto.id, + createdAt: dto.createdAt, + type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, + user: UserConverter.fromSimpleUserDto(dto.user), + assetId: dto.assetId, + comment: dto.comment, + ); } diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index 1d0349bb2a..2d24004944 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; @@ -14,8 +13,7 @@ import 'package:isar/isar.dart'; enum AlbumSort { remoteId, localId } -final albumRepositoryProvider = - Provider((ref) => AlbumRepository(ref.watch(dbProvider))); +final albumRepositoryProvider = Provider((ref) => AlbumRepository(ref.watch(dbProvider))); class AlbumRepository extends DatabaseRepository { const AlbumRepository(super.db); @@ -32,12 +30,7 @@ class AlbumRepository extends DatabaseRepository { Future create(Album album) => txn(() => db.albums.store(album)); - Future getByName( - String name, { - bool? shared, - bool? remote, - bool? owner, - }) { + Future getByName(String name, {bool? shared, bool? remote, bool? owner}) { var query = db.albums.filter().nameEqualTo(name); if (shared != null) { query = query.sharedEqualTo(shared); @@ -60,12 +53,7 @@ class AlbumRepository extends DatabaseRepository { Future delete(int albumId) => txn(() => db.albums.delete(albumId)); - Future> getAll({ - bool? shared, - bool? remote, - int? ownerId, - AlbumSort? sortBy, - }) { + Future> getAll({bool? shared, bool? remote, int? ownerId, AlbumSort? sortBy}) { final baseQuery = db.albums.where(); final QueryBuilder afterWhere; if (remote == null) { @@ -75,8 +63,7 @@ class AlbumRepository extends DatabaseRepository { } else { afterWhere = baseQuery.localIdIsNotNull(); } - QueryBuilder filterQuery = - afterWhere.filter().noOp(); + QueryBuilder filterQuery = afterWhere.filter().noOp(); if (shared != null) { filterQuery = filterQuery.sharedEqualTo(true); } @@ -97,38 +84,27 @@ class AlbumRepository extends DatabaseRepository { return db.albums.filter().remoteIdEqualTo(remoteId).findFirst(); } - Future removeUsers(Album album, List users) => txn( - () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), - ); + Future removeUsers(Album album, List users) => + txn(() => album.sharedUsers.update(unlink: users.map(entity.User.fromDto))); - Future addAssets(Album album, List assets) => - txn(() => album.assets.update(link: assets)); + Future addAssets(Album album, List assets) => txn(() => album.assets.update(link: assets)); - Future removeAssets(Album album, List assets) => - txn(() => album.assets.update(unlink: assets)); + Future removeAssets(Album album, List assets) => txn(() => album.assets.update(unlink: assets)); Future recalculateMetadata(Album album) async { album.startDate = await album.assets.filter().fileCreatedAtProperty().min(); album.endDate = await album.assets.filter().fileCreatedAtProperty().max(); - album.lastModifiedAssetTimestamp = - await album.assets.filter().updatedAtProperty().max(); + album.lastModifiedAssetTimestamp = await album.assets.filter().updatedAtProperty().max(); return album; } Future addUsers(Album album, List users) => txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto))); - Future deleteAllLocal() => - txn(() => db.albums.where().localIdIsNotNull().deleteAll()); + Future deleteAllLocal() => txn(() => db.albums.where().localIdIsNotNull().deleteAll()); - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { - var query = db.albums - .filter() - .nameContains(searchTerm, caseSensitive: false) - .remoteIdIsNotNull(); + Future> search(String searchTerm, QuickFilterMode filterMode) async { + var query = db.albums.filter().nameContains(searchTerm, caseSensitive: false).remoteIdIsNotNull(); final isarUserId = fastHash(Store.get(StoreKey.currentUser).id); switch (filterMode) { diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index 20365534c2..525f0906ba 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -1,19 +1,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/domain/models/album/album.model.dart' - show AlbumAssetOrder, RemoteAlbum; +import 'package:immich_mobile/domain/models/album/album.model.dart' show AlbumAssetOrder, RemoteAlbum; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final albumApiRepositoryProvider = Provider( - (ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi), -); +final albumApiRepositoryProvider = Provider((ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi)); class AlbumApiRepository extends ApiRepository { final AlbumsApi _api; @@ -36,9 +32,7 @@ class AlbumApiRepository extends ApiRepository { Iterable sharedUserIds = const [], String? description, }) async { - final users = sharedUserIds.map( - (id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor), - ); + final users = sharedUserIds.map((id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor)); final responseDto = await checkNull( _api.createAlbum( CreateAlbumDto( @@ -53,19 +47,9 @@ class AlbumApiRepository extends ApiRepository { } // TODO: Change name after removing old method - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return _toRemoteAlbum(responseDto); @@ -104,16 +88,8 @@ class AlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future<({List added, List duplicates})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List duplicates})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = []; final List duplicates = []; @@ -128,16 +104,8 @@ class AlbumApiRepository extends ApiRepository { return (added: added, duplicates: duplicates); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -150,14 +118,8 @@ class AlbumApiRepository extends ApiRepository { } Future addUsers(String albumId, Iterable userIds) async { - final albumUsers = - userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return _toAlbum(response); } @@ -180,11 +142,9 @@ class AlbumApiRepository extends ApiRepository { sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc, ); album.remoteAssetCount = dto.assetCount; - album.owner.value = - entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); + album.owner.value = entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); album.remoteThumbnailAssetId = dto.albumThumbnailAssetId; - final users = dto.albumUsers - .map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); + final users = dto.albumUsers.map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); album.sharedUsers.addAll(users.map(entity.User.fromDto)); final assets = dto.assets.map(Asset.remote).toList(); album.assets.addAll(assets); @@ -202,11 +162,10 @@ class AlbumApiRepository extends ApiRepository { updatedAt: dto.updatedAt, thumbnailAssetId: dto.albumThumbnailAssetId, isActivityEnabled: dto.isActivityEnabled, - order: dto.order == AssetOrder.asc - ? AlbumAssetOrder.asc - : AlbumAssetOrder.desc, + order: dto.order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, assetCount: dto.assetCount, ownerName: dto.owner.name, + isShared: dto.albumUsers.length > 2, ); } } diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index 94f1d0581e..89860f4e75 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -7,60 +7,52 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final albumMediaRepositoryProvider = - Provider((ref) => const AlbumMediaRepository()); +final albumMediaRepositoryProvider = Provider((ref) => const AlbumMediaRepository()); class AlbumMediaRepository { const AlbumMediaRepository(); - bool get useCustomFilter => - Store.get(StoreKey.photoManagerCustomFilter, true); + bool get useCustomFilter => Store.get(StoreKey.photoManagerCustomFilter, true); FilterOptionGroup? _getAlbumFilter({ DateTimeCond? updateTimeCond, bool? containsPathModified, List? orderBy, - }) => - useCustomFilter - ? FilterOptionGroup( - imageOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - ), - videoOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - durationConstraint: DurationConstraint(allowNullable: true), - ), - containsPathModified: containsPathModified ?? false, - createTimeCond: DateTimeCond.def().copyWith(ignore: true), - updateTimeCond: - updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), - orders: orderBy ?? [], - ) - : null; + }) => useCustomFilter + ? FilterOptionGroup( + imageOption: const FilterOption(needTitle: true, sizeConstraint: SizeConstraint(ignoreSize: true)), + videoOption: const FilterOption( + needTitle: true, + sizeConstraint: SizeConstraint(ignoreSize: true), + durationConstraint: DurationConstraint(allowNullable: true), + ), + containsPathModified: containsPathModified ?? false, + createTimeCond: DateTimeCond.def().copyWith(ignore: true), + updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), + orders: orderBy ?? [], + ) + : null; Future> getAll() async { final filter = useCustomFilter ? CustomFilter.sql(where: '${CustomColumns.base.width} > 0') : FilterOptionGroup(containsPathModified: true); - final List assetPathEntities = - await PhotoManager.getAssetPathList(hasAll: true, filterOption: filter); + final List assetPathEntities = await PhotoManager.getAssetPathList( + hasAll: true, + filterOption: filter, + ); return assetPathEntities.map(_toAlbum).toList(); } Future> getAssetIds(String albumId) async { - final album = - await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); - final List assets = - await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff); + final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); + final List assets = await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff); return assets.map((e) => e.id).toList(); } Future getAssetCount(String albumId) async { - final album = - await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); + final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); return album.assetCountAsync; } @@ -77,36 +69,25 @@ class AlbumMediaRepository { filterOption: _getAlbumFilter( updateTimeCond: modifiedFrom == null && modifiedUntil == null ? null - : DateTimeCond( - min: modifiedFrom ?? DateTime.utc(-271820), - max: modifiedUntil ?? DateTime.utc(275760), - ), - orderBy: orderByModificationDate - ? [const OrderOption(type: OrderOptionType.updateDate)] - : [], + : DateTimeCond(min: modifiedFrom ?? DateTime.utc(-271820), max: modifiedUntil ?? DateTime.utc(275760)), + orderBy: orderByModificationDate ? [const OrderOption(type: OrderOptionType.updateDate)] : [], ), ); - final List assets = - await onDevice.getAssetListRange(start: start, end: end); + final List assets = await onDevice.getAssetListRange(start: start, end: end); return assets.map(AssetMediaRepository.toAsset).toList().cast(); } Future get(String id) async { - final assetPathEntity = await AssetPathEntity.fromId( - id, - filterOption: _getAlbumFilter(containsPathModified: true), - ); + final assetPathEntity = await AssetPathEntity.fromId(id, filterOption: _getAlbumFilter(containsPathModified: true)); return _toAlbum(assetPathEntity); } static Album _toAlbum(AssetPathEntity assetPathEntity) { final Album album = Album( name: assetPathEntity.name, - createdAt: - assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), - modifiedAt: - assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), + createdAt: assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), + modifiedAt: assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), shared: false, activityEnabled: false, ); diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 1e3defae08..79af8b4921 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -11,8 +11,7 @@ import 'package:isar/isar.dart'; enum AssetSort { checksum, ownerIdChecksum } -final assetRepositoryProvider = - Provider((ref) => AssetRepository(ref.watch(dbProvider))); +final assetRepositoryProvider = Provider((ref) => AssetRepository(ref.watch(dbProvider))); class AssetRepository extends DatabaseRepository { const AssetRepository(super.db); @@ -29,8 +28,7 @@ class AssetRepository extends DatabaseRepository { if (notOwnedBy.length == 1) { query = query.not().ownerIdEqualTo(isarUserIds.first); } else if (notOwnedBy.isNotEmpty) { - query = - query.not().anyOf(isarUserIds, (q, int id) => q.ownerIdEqualTo(id)); + query = query.not().anyOf(isarUserIds, (q, int id) => q.ownerIdEqualTo(id)); } if (ownerId != null) { query = query.ownerIdEqualTo(fastHash(ownerId)); @@ -44,8 +42,7 @@ class AssetRepository extends DatabaseRepository { }; } - final QueryBuilder sortedQuery = - switch (sortBy) { + final QueryBuilder sortedQuery = switch (sortBy) { null => query.noOp(), AssetSort.checksum => query.sortByChecksum(), AssetSort.ownerIdChecksum => query.sortByOwnerId().thenByChecksum(), @@ -55,16 +52,13 @@ class AssetRepository extends DatabaseRepository { } Future deleteByIds(List ids) => txn(() async { - await db.assets.deleteAll(ids); - await db.exifInfos.deleteAll(ids); - }); + await db.assets.deleteAll(ids); + await db.exifInfos.deleteAll(ids); + }); Future getByRemoteId(String id) => db.assets.getByRemoteId(id); - Future> getAllByRemoteId( - Iterable ids, { - AssetState? state, - }) async { + Future> getAllByRemoteId(Iterable ids, {AssetState? state}) async { if (ids.isEmpty) { return []; } @@ -72,10 +66,7 @@ class AssetRepository extends DatabaseRepository { return _getAllByRemoteIdImpl(ids, state).findAll(); } - QueryBuilder _getAllByRemoteIdImpl( - Iterable ids, - AssetState? state, - ) { + QueryBuilder _getAllByRemoteIdImpl(Iterable ids, AssetState? state) { final query = db.assets.remote(ids).filter(); return switch (state) { null => query.noOp(), @@ -85,39 +76,21 @@ class AssetRepository extends DatabaseRepository { }; } - Future> getAll({ - required String ownerId, - AssetState? state, - AssetSort? sortBy, - int? limit, - }) { + Future> getAll({required String ownerId, AssetState? state, AssetSort? sortBy, int? limit}) { final baseQuery = db.assets.where(); final isarUserIds = fastHash(ownerId); - final QueryBuilder filteredQuery = - switch (state) { + final QueryBuilder filteredQuery = switch (state) { null => baseQuery.ownerIdEqualToAnyChecksum(isarUserIds).noOp(), - AssetState.local => baseQuery - .remoteIdIsNull() - .filter() - .localIdIsNotNull() - .ownerIdEqualTo(isarUserIds), - AssetState.remote => baseQuery - .localIdIsNull() - .filter() - .remoteIdIsNotNull() - .ownerIdEqualTo(isarUserIds), - AssetState.merged => baseQuery - .ownerIdEqualToAnyChecksum(isarUserIds) - .filter() - .remoteIdIsNotNull() - .localIdIsNotNull(), + AssetState.local => baseQuery.remoteIdIsNull().filter().localIdIsNotNull().ownerIdEqualTo(isarUserIds), + AssetState.remote => baseQuery.localIdIsNull().filter().remoteIdIsNotNull().ownerIdEqualTo(isarUserIds), + AssetState.merged => + baseQuery.ownerIdEqualToAnyChecksum(isarUserIds).filter().remoteIdIsNotNull().localIdIsNotNull(), }; final QueryBuilder query = switch (sortBy) { null => filteredQuery.noOp(), AssetSort.checksum => filteredQuery.sortByChecksum(), - AssetSort.ownerIdChecksum => - filteredQuery.sortByOwnerId().thenByChecksum(), + AssetSort.ownerIdChecksum => filteredQuery.sortByOwnerId().thenByChecksum(), }; return limit == null ? query.findAll() : query.limit(limit).findAll(); @@ -135,15 +108,11 @@ class AssetRepository extends DatabaseRepository { int limit = 100, }) { final baseQuery = db.assets.where(); - final QueryBuilder query = - switch (state) { + final QueryBuilder query = switch (state) { null => baseQuery.noOp(), - AssetState.local => - baseQuery.remoteIdIsNull().filter().localIdIsNotNull(), - AssetState.remote => - baseQuery.localIdIsNull().filter().remoteIdIsNotNull(), - AssetState.merged => - baseQuery.localIdIsNotNull().filter().remoteIdIsNotNull(), + AssetState.local => baseQuery.remoteIdIsNull().filter().localIdIsNotNull(), + AssetState.remote => baseQuery.localIdIsNull().filter().remoteIdIsNotNull(), + AssetState.merged => baseQuery.localIdIsNotNull().filter().remoteIdIsNotNull(), }; return _getMatchesImpl(query, fastHash(ownerId), assets, limit); } @@ -153,25 +122,18 @@ class AssetRepository extends DatabaseRepository { return asset; } - Future upsertDuplicatedAssets(Iterable duplicatedAssets) => txn( - () => db.duplicatedAssets - .putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()), - ); + Future upsertDuplicatedAssets(Iterable duplicatedAssets) => + txn(() => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList())); - Future> getAllDuplicatedAssetIds() => - db.duplicatedAssets.where().idProperty().findAll(); + Future> getAllDuplicatedAssetIds() => db.duplicatedAssets.where().idProperty().findAll(); Future getByOwnerIdChecksum(int ownerId, String checksum) => db.assets.getByOwnerIdChecksum(ownerId, checksum); - Future> getAllByOwnerIdChecksum( - List ownerIds, - List checksums, - ) => + Future> getAllByOwnerIdChecksum(List ownerIds, List checksums) => db.assets.getAllByOwnerIdChecksum(ownerIds, checksums); - Future> getAllLocal() => - db.assets.where().localIdIsNotNull().findAll(); + Future> getAllLocal() => db.assets.where().localIdIsNotNull().findAll(); Future deleteAllByRemoteId(List ids, {AssetState? state}) => txn(() => _getAllByRemoteIdImpl(ids, state).deleteAll()); @@ -234,26 +196,25 @@ Future> _getMatchesImpl( int ownerId, List assets, int limit, -) => - query - .ownerIdEqualTo(ownerId) - .anyOf( - assets, - (q, Asset a) => q - .fileNameEqualTo(a.fileName) - .and() - .durationInSecondsEqualTo(a.durationInSeconds) - .and() - .fileCreatedAtBetween( - a.fileCreatedAt.subtract(const Duration(hours: 12)), - a.fileCreatedAt.add(const Duration(hours: 12)), - ) - .and() - .not() - .checksumEqualTo(a.checksum), - ) - .sortByFileName() - .thenByFileCreatedAt() - .thenByFileModifiedAt() - .limit(limit) - .findAll(); +) => query + .ownerIdEqualTo(ownerId) + .anyOf( + assets, + (q, Asset a) => q + .fileNameEqualTo(a.fileName) + .and() + .durationInSecondsEqualTo(a.durationInSeconds) + .and() + .fileCreatedAtBetween( + a.fileCreatedAt.subtract(const Duration(hours: 12)), + a.fileCreatedAt.add(const Duration(hours: 12)), + ) + .and() + .not() + .checksumEqualTo(a.checksum), + ) + .sortByFileName() + .thenByFileCreatedAt() + .thenByFileModifiedAt() + .limit(limit) + .findAll(); diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 4c854973b1..07639fbb3a 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -13,6 +13,7 @@ final assetApiRepositoryProvider = Provider( ref.watch(apiServiceProvider).assetsApi, ref.watch(apiServiceProvider).searchApi, ref.watch(apiServiceProvider).stacksApi, + ref.watch(apiServiceProvider).trashApi, ), ); @@ -20,13 +21,12 @@ class AssetApiRepository extends ApiRepository { final AssetsApi _api; final SearchApi _searchApi; final StacksApi _stacksApi; + final TrashApi _trashApi; - AssetApiRepository(this._api, this._searchApi, this._stacksApi); + AssetApiRepository(this._api, this._searchApi, this._stacksApi, this._trashApi); Future update(String id, {String? description}) async { - final response = await checkNull( - _api.updateAsset(id, UpdateAssetDto(description: description)), - ); + final response = await checkNull(_api.updateAsset(id, UpdateAssetDto(description: description))); return Asset.remote(response); } @@ -37,13 +37,7 @@ class AssetApiRepository extends ApiRepository { int currentPage = 1; while (hasNext) { final response = await checkNull( - _searchApi.searchAssets( - MetadataSearchDto( - personIds: personIds, - page: currentPage, - size: 1000, - ), - ), + _searchApi.searchAssets(MetadataSearchDto(personIds: personIds, page: currentPage, size: 1000)), ); result.addAll(response.assets.items.map(Asset.remote)); hasNext = response.assets.nextPage != null; @@ -56,40 +50,28 @@ class AssetApiRepository extends ApiRepository { return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force)); } - Future updateVisibility( - List ids, - AssetVisibilityEnum visibility, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)), - ); + Future restoreTrash(List ids) async { + await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); } - Future updateFavorite( - List ids, - bool isFavorite, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite), - ); + Future updateVisibility(List ids, AssetVisibilityEnum visibility) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility))); } - Future updateLocation( - List ids, - LatLng location, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto( - ids: ids, - latitude: location.latitude, - longitude: location.longitude, - ), - ); + Future updateFavorite(List ids, bool isFavorite) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite)); + } + + Future updateLocation(List ids, LatLng location) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); + } + + Future updateDateTime(List ids, DateTime dateTime) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, dateTimeOriginal: dateTime.toIso8601String())); } Future stack(List ids) async { - final responseDto = - await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); + final responseDto = await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); return responseDto.toStack(); } @@ -103,11 +85,11 @@ class AssetApiRepository extends ApiRepository { } _mapVisibility(AssetVisibilityEnum visibility) => switch (visibility) { - AssetVisibilityEnum.timeline => AssetVisibility.timeline, - AssetVisibilityEnum.hidden => AssetVisibility.hidden, - AssetVisibilityEnum.locked => AssetVisibility.locked, - AssetVisibilityEnum.archive => AssetVisibility.archive, - }; + AssetVisibilityEnum.timeline => AssetVisibility.timeline, + AssetVisibilityEnum.hidden => AssetVisibility.hidden, + AssetVisibilityEnum.locked => AssetVisibility.locked, + AssetVisibilityEnum.archive => AssetVisibility.archive, + }; Future getAssetMIMEType(String assetId) async { final response = await checkNull(_api.getAssetInfo(assetId)); @@ -115,14 +97,14 @@ class AssetApiRepository extends ApiRepository { // we need to get the MIME of the thumbnail once that gets added to the API return response.originalMimeType; } + + Future updateDescription(String assetId, String description) { + return _api.updateAsset(assetId, UpdateAssetDto(description: description)); + } } extension on StackResponseDto { StackResponse toStack() { - return StackResponse( - id: id, - primaryAssetId: primaryAssetId, - assetIds: assets.map((asset) => asset.id).toList(), - ); + return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList()); } } diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 8708ce9cfd..8336d2341d 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -1,22 +1,24 @@ import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; +import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:photo_manager/photo_manager.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:share_plus/share_plus.dart'; -final assetMediaRepositoryProvider = Provider( - (ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)), -); +final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider))); class AssetMediaRepository { final AssetApiRepository _assetApiRepository; @@ -24,8 +26,28 @@ class AssetMediaRepository { const AssetMediaRepository(this._assetApiRepository); - Future> deleteAll(List ids) => - PhotoManager.editor.deleteWithIds(ids); + Future _androidSupportsTrash() async { + if (Platform.isAndroid) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + int sdkVersion = androidInfo.version.sdkInt; + return sdkVersion >= 31; + } + return false; + } + + Future> deleteAll(List ids) async { + if (CurrentPlatform.isAndroid) { + if (await _androidSupportsTrash()) { + return PhotoManager.editor.android.moveToTrash( + ids.map((e) => AssetEntity(id: e, width: 1, height: 1, typeInt: 0)).toList(), + ); + } else { + return PhotoManager.editor.deleteWithIds(ids); + } + } + return PhotoManager.editor.deleteWithIds(ids); + } Future get(String id) async { final entity = await AssetEntity.fromId(id); @@ -52,8 +74,7 @@ class AssetMediaRepository { asset.fileCreatedAt = asset.fileModifiedAt; } if (local.latitude != null) { - asset.exifInfo = - ExifInfo(latitude: local.latitude, longitude: local.longitude); + asset.exifInfo = ExifInfo(latitude: local.latitude, longitude: local.longitude); } asset.local = local; return asset; @@ -72,20 +93,22 @@ class AssetMediaRepository { } // TODO: make this more efficient - Future shareAssets(List assets) async { + Future shareAssets(List assets, BuildContext context) async { final downloadedXFiles = []; + final tempFiles = []; for (var asset in assets) { final localId = (asset is LocalAsset) ? asset.id : asset is RemoteAsset - ? asset.localId - : null; + ? asset.localId + : null; if (localId != null) { - File? f = - await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0) - .originFile; + File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile; downloadedXFiles.add(XFile(f!.path)); + if (CurrentPlatform.isIOS) { + tempFiles.add(f); + } } else if (asset is RemoteAsset) { final tempDir = await getTemporaryDirectory(); final name = asset.name; @@ -99,6 +122,7 @@ class AssetMediaRepository { await tempFile.writeAsBytes(res.bodyBytes); downloadedXFiles.add(XFile(tempFile.path)); + tempFiles.add(tempFile); } else { _log.warning("Asset type not supported for sharing: $asset"); continue; @@ -110,17 +134,22 @@ class AssetMediaRepository { return 0; } - final result = await Share.shareXFiles(downloadedXFiles); - - for (var file in downloadedXFiles) { - try { - await File(file.path).delete(); - } catch (e) { - _log.warning("Failed to delete temporary file: ${file.path}", e); + // we dont want to await the share result since the + // "preparing" dialog will not disappear until + final size = context.sizeData; + Share.shareXFiles( + downloadedXFiles, + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + ).then((result) async { + for (var file in tempFiles) { + try { + await file.delete(); + } catch (e) { + _log.warning("Failed to delete temporary file: ${file.path}", e); + } } - } - return result.status == ShareResultStatus.success - ? downloadedXFiles.length - : 0; + }); + + return downloadedXFiles.length; } } diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index 5cf357d5a4..ba978b0df0 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:drift/drift.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -10,6 +9,7 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -25,24 +25,7 @@ class AuthRepository extends DatabaseRepository { const AuthRepository(super.db, this._drift); Future clearLocalData() async { - // Drift deletions - child entities first (those with foreign keys) - await Future.wait([ - _drift.memoryAssetEntity.deleteAll(), - _drift.remoteAlbumAssetEntity.deleteAll(), - _drift.remoteAlbumUserEntity.deleteAll(), - _drift.remoteExifEntity.deleteAll(), - _drift.userMetadataEntity.deleteAll(), - _drift.partnerEntity.deleteAll(), - _drift.stackEntity.deleteAll(), - _drift.personEntity.deleteAll(), - ]); - // Drift deletions - parent entities - await Future.wait([ - _drift.memoryEntity.deleteAll(), - _drift.remoteAlbumEntity.deleteAll(), - _drift.remoteAssetEntity.deleteAll(), - _drift.userEntity.deleteAll(), - ]); + await SyncStreamRepository(_drift).reset(); return db.writeTxn(() { return Future.wait([ @@ -79,8 +62,7 @@ class AuthRepository extends DatabaseRepository { } final List jsonList = jsonDecode(jsonString); - final endpointList = - jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + final endpointList = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); return endpointList; } diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index 4b68867506..4b0880ddcf 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -5,8 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:openapi/api.dart'; -final authApiRepositoryProvider = - Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider))); +final authApiRepositoryProvider = Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider))); class AuthApiRepository extends ApiRepository { final ApiService _apiService; @@ -14,30 +13,21 @@ class AuthApiRepository extends ApiRepository { AuthApiRepository(this._apiService); Future changePassword(String newPassword) async { - await _apiService.usersApi.updateMyUser( - UserUpdateMeDto( - password: newPassword, - ), - ); + await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: newPassword)); } Future login(String email, String password) async { final loginResponseDto = await checkNull( - _apiService.authenticationApi.login( - LoginCredentialDto( - email: email, - password: password, - ), - ), + _apiService.authenticationApi.login(LoginCredentialDto(email: email, password: password)), ); return _mapLoginReponse(loginResponseDto); } Future logout() async { - await _apiService.authenticationApi - .logout() - .timeout(const Duration(seconds: 7)); + if (_apiService.apiClient.basePath.isEmpty) return; + + await _apiService.authenticationApi.logout().timeout(const Duration(seconds: 7)); } _mapLoginReponse(LoginResponseDto dto) { @@ -54,8 +44,7 @@ class AuthApiRepository extends ApiRepository { Future unlockPinCode(String pinCode) async { try { - await _apiService.authenticationApi - .unlockAuthSession(SessionUnlockDto(pinCode: pinCode)); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: pinCode)); return true; } catch (_) { return false; @@ -63,8 +52,7 @@ class AuthApiRepository extends ApiRepository { } Future setupPinCode(String pinCode) { - return _apiService.authenticationApi - .setupPinCode(PinCodeSetupDto(pinCode: pinCode)); + return _apiService.authenticationApi.setupPinCode(PinCodeSetupDto(pinCode: pinCode)); } Future lockPinCode() { diff --git a/mobile/lib/repositories/backup.repository.dart b/mobile/lib/repositories/backup.repository.dart index 05d2047add..6cee6a4427 100644 --- a/mobile/lib/repositories/backup.repository.dart +++ b/mobile/lib/repositories/backup.repository.dart @@ -6,16 +6,14 @@ import 'package:isar/isar.dart'; enum BackupAlbumSort { id } -final backupAlbumRepositoryProvider = - Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider))); +final backupAlbumRepositoryProvider = Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider))); class BackupAlbumRepository extends DatabaseRepository { const BackupAlbumRepository(super.db); Future> getAll({BackupAlbumSort? sort}) { final baseQuery = db.backupAlbums.where(); - final QueryBuilder query = - switch (sort) { + final QueryBuilder query = switch (sort) { null => baseQuery.noOp(), BackupAlbumSort.id => baseQuery.sortById(), }; @@ -28,9 +26,7 @@ class BackupAlbumRepository extends DatabaseRepository { Future> getAllBySelection(BackupSelection backup) => db.backupAlbums.filter().selectionEqualTo(backup).findAll(); - Future deleteAll(List ids) => - txn(() => db.backupAlbums.deleteAll(ids)); + Future deleteAll(List ids) => txn(() => db.backupAlbums.deleteAll(ids)); - Future updateAll(List backupAlbums) => - txn(() => db.backupAlbums.putAll(backupAlbums)); + Future updateAll(List backupAlbums) => txn(() => db.backupAlbums.putAll(backupAlbums)); } diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart index 94540c8249..8bd5a85542 100644 --- a/mobile/lib/repositories/biometric.repository.dart +++ b/mobile/lib/repositories/biometric.repository.dart @@ -3,8 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:local_auth/local_auth.dart'; -final biometricRepositoryProvider = - Provider((ref) => BiometricRepository(LocalAuthentication())); +final biometricRepositoryProvider = Provider((ref) => BiometricRepository(LocalAuthentication())); class BiometricRepository { final LocalAuthentication _localAuth; @@ -12,21 +11,14 @@ class BiometricRepository { const BiometricRepository(this._localAuth); Future getStatus() async { - final bool canAuthenticateWithBiometrics = - await _localAuth.canCheckBiometrics; - final bool canAuthenticate = - canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); + final bool canAuthenticateWithBiometrics = await _localAuth.canCheckBiometrics; + final bool canAuthenticate = canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); final availableBiometric = await _localAuth.getAvailableBiometrics(); - return BiometricStatus( - canAuthenticate: canAuthenticate, - availableBiometrics: availableBiometric, - ); + return BiometricStatus(canAuthenticate: canAuthenticate, availableBiometrics: availableBiometric); } Future authenticate(String? message) async { - return _localAuth.authenticate( - localizedReason: message ?? 'please_auth_to_access'.tr(), - ); + return _localAuth.authenticate(localizedReason: message ?? 'please_auth_to_access'.tr()); } } diff --git a/mobile/lib/repositories/database.repository.dart b/mobile/lib/repositories/database.repository.dart index c644dae668..71c15e1c40 100644 --- a/mobile/lib/repositories/database.repository.dart +++ b/mobile/lib/repositories/database.repository.dart @@ -11,12 +11,10 @@ abstract class DatabaseRepository implements IDatabaseRepository { bool get inTxn => Zone.current[_zoneTxn] != null; - Future txn(Future Function() callback) => - inTxn ? callback() : transaction(callback); + Future txn(Future Function() callback) => inTxn ? callback() : transaction(callback); @override - Future transaction(Future Function() callback) => - db.writeTxn(callback); + Future transaction(Future Function() callback) => db.writeTxn(callback); } extension Asd on QueryBuilder { diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index f1dae3c251..c578746a4c 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -4,10 +4,10 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); @@ -21,7 +21,7 @@ class DownloadRepository { group: '', updates: Updates.statusAndProgress, ); - static final _dummyMetadata = {'part': LivePhotosPart.image, 'id': ''}; + static final _dummyMetadata = {'part': LivePhotosPart.image.index, 'id': ''}; void Function(TaskStatusUpdate)? onImageDownloadStatus; @@ -33,19 +33,19 @@ class DownloadRepository { DownloadRepository() { _downloader.registerCallbacks( - group: downloadGroupImage, + group: kDownloadGroupImage, taskStatusCallback: (update) => onImageDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupVideo, + group: kDownloadGroupVideo, taskStatusCallback: (update) => onVideoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); _downloader.registerCallbacks( - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, taskStatusCallback: (update) => onLivePhotoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); @@ -64,10 +64,7 @@ class DownloadRepository { } Future> getLiveVideoTasks() { - return _downloader.database.allRecordsWithStatus( - TaskStatus.complete, - group: downloadGroupLivePhoto, - ); + return _downloader.database.allRecordsWithStatus(TaskStatus.complete, group: kDownloadGroupLivePhoto); } Future deleteRecordsWithIds(List ids) { @@ -93,19 +90,23 @@ class DownloadRepository { final isVideo = asset.isVideo; final url = getOriginalUrlForRemoteId(id); - if (Platform.isAndroid || livePhotoVideoId == null || isVideo) { + // on iOS it cannot link the image, check if the filename has .MP extension + // to avoid downloading the video part + final isAndroidMotionPhoto = asset.name.contains(".MP"); + + if (Platform.isAndroid || livePhotoVideoId == null || isVideo || isAndroidMotionPhoto) { tasks[taskIndex++] = DownloadTask( taskId: id, url: url, headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: isVideo ? downloadGroupVideo : downloadGroupImage, + group: isVideo ? kDownloadGroupVideo : kDownloadGroupImage, ); continue; } - _dummyMetadata['part'] = LivePhotosPart.image; + _dummyMetadata['part'] = LivePhotosPart.image.index; _dummyMetadata['id'] = id; tasks[taskIndex++] = DownloadTask( taskId: id, @@ -113,20 +114,18 @@ class DownloadRepository { headers: headers, filename: asset.name, updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); - _dummyMetadata['part'] = LivePhotosPart.video; + _dummyMetadata['part'] = LivePhotosPart.video.index; tasks[taskIndex++] = DownloadTask( taskId: livePhotoVideoId, - url: url, + url: getOriginalUrlForRemoteId(livePhotoVideoId), headers: headers, - filename: asset.name - .toUpperCase() - .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), + filename: asset.name.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), updates: Updates.statusAndProgress, - group: downloadGroupLivePhoto, + group: kDownloadGroupLivePhoto, metaData: json.encode(_dummyMetadata), ); } diff --git a/mobile/lib/repositories/drift_album_api_repository.dart b/mobile/lib/repositories/drift_album_api_repository.dart index 26b55fbef6..557050323a 100644 --- a/mobile/lib/repositories/drift_album_api_repository.dart +++ b/mobile/lib/repositories/drift_album_api_repository.dart @@ -14,34 +14,16 @@ class DriftAlbumApiRepository extends ApiRepository { DriftAlbumApiRepository(this._api); - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return responseDto.toRemoteAlbum(); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -53,16 +35,8 @@ class DriftAlbumApiRepository extends ApiRepository { return (removed: removed, failed: failed); } - Future<({List added, List failed})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List failed})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = [], failed = []; for (final dto in response) { if (dto.success) { @@ -85,8 +59,7 @@ class DriftAlbumApiRepository extends ApiRepository { }) async { AssetOrder? apiOrder; if (order != null) { - apiOrder = - order == AlbumAssetOrder.asc ? AssetOrder.asc : AssetOrder.desc; + apiOrder = order == AlbumAssetOrder.asc ? AssetOrder.asc : AssetOrder.desc; } final responseDto = await checkNull( @@ -109,20 +82,20 @@ class DriftAlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future addUsers( - String albumId, - Iterable userIds, - ) async { - final albumUsers = - userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + Future addUsers(String albumId, Iterable userIds) async { + final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return response.toRemoteAlbum(); } + + Future removeUser(String albumId, {required String userId}) async { + await _api.removeUserFromAlbum(albumId, userId); + } + + Future setActivityStatus(String albumId, bool isEnabled) async { + final response = await checkNull(_api.updateAlbumInfo(albumId, UpdateAlbumDto(isActivityEnabled: isEnabled))); + return response.isActivityEnabled; + } } extension on AlbumResponseDto { @@ -136,10 +109,10 @@ extension on AlbumResponseDto { updatedAt: updatedAt, thumbnailAssetId: albumThumbnailAssetId, isActivityEnabled: isActivityEnabled, - order: - order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, + order: order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, assetCount: assetCount, ownerName: owner.name, + isShared: albumUsers.length > 2, ); } } diff --git a/mobile/lib/repositories/etag.repository.dart b/mobile/lib/repositories/etag.repository.dart index b8382efdfc..768d95b95c 100644 --- a/mobile/lib/repositories/etag.repository.dart +++ b/mobile/lib/repositories/etag.repository.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final etagRepositoryProvider = - Provider((ref) => ETagRepository(ref.watch(dbProvider))); +final etagRepositoryProvider = Provider((ref) => ETagRepository(ref.watch(dbProvider))); class ETagRepository extends DatabaseRepository { const ETagRepository(super.db); @@ -16,8 +15,7 @@ class ETagRepository extends DatabaseRepository { Future upsertAll(List etags) => txn(() => db.eTags.putAll(etags)); - Future deleteByIds(List ids) => - txn(() => db.eTags.deleteAllById(ids)); + Future deleteByIds(List ids) => txn(() => db.eTags.deleteAllById(ids)); Future getById(String id) => db.eTags.getById(id); diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 2bccb05210..654be78fb4 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -2,73 +2,50 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/entities/asset.entity.dart' hide AssetType; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final fileMediaRepositoryProvider = - Provider((ref) => const FileMediaRepository()); +final fileMediaRepositoryProvider = Provider((ref) => const FileMediaRepository()); class FileMediaRepository { const FileMediaRepository(); - Future saveImage( - Uint8List data, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImage( - data, - filename: title, - title: title, - relativePath: relativePath, - ); + Future saveImage(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveImageWithFile( - String filePath, { - String? title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImageWithPath( - filePath, - title: title, - relativePath: relativePath, + Future saveLocalAsset(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); + + return LocalAsset( + id: entity.id, + name: title, + type: AssetType.image, + createdAt: entity.createDateTime, + updatedAt: entity.modifiedDateTime, ); + } + + Future saveImageWithFile(String filePath, {String? title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImageWithPath(filePath, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveLivePhoto({ - required File image, - required File video, - required String title, - }) async { - final entity = await PhotoManager.editor.darwin.saveLivePhoto( - imageFile: image, - videoFile: video, - title: title, - ); + Future saveLivePhoto({required File image, required File video, required String title}) async { + final entity = await PhotoManager.editor.darwin.saveLivePhoto(imageFile: image, videoFile: video, title: title); return AssetMediaRepository.toAsset(entity); } - Future saveVideo( - File file, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + Future saveVideo(File file, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveVideo(file, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } Future clearFileCache() => PhotoManager.clearFileCache(); - Future enableBackgroundAccess() => - PhotoManager.setIgnorePermissionCheck(true); + Future enableBackgroundAccess() => PhotoManager.setIgnorePermissionCheck(true); - Future requestExtendedPermissions() => - PhotoManager.requestPermissionExtend(); + Future requestExtendedPermissions() => PhotoManager.requestPermissionExtend(); } diff --git a/mobile/lib/repositories/folder_api.repository.dart b/mobile/lib/repositories/folder_api.repository.dart index 9fcb57ae21..dfda19e45e 100644 --- a/mobile/lib/repositories/folder_api.repository.dart +++ b/mobile/lib/repositories/folder_api.repository.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final folderApiRepositoryProvider = Provider( - (ref) => FolderApiRepository( - ref.watch(apiServiceProvider).viewApi, - ), -); +final folderApiRepositoryProvider = Provider((ref) => FolderApiRepository(ref.watch(apiServiceProvider).viewApi)); class FolderApiRepository extends ApiRepository { final ViewApi _api; diff --git a/mobile/lib/repositories/gcast.repository.dart b/mobile/lib/repositories/gcast.repository.dart index 11c149ab37..db3e0f45d0 100644 --- a/mobile/lib/repositories/gcast.repository.dart +++ b/mobile/lib/repositories/gcast.repository.dart @@ -33,19 +33,13 @@ class GCastRepository { }); // open the default receiver - sendMessage(CastSession.kNamespaceReceiver, { - 'type': 'LAUNCH', - 'appId': 'CC1AD845', - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': 'LAUNCH', 'appId': 'CC1AD845'}); } Future disconnect() async { final sessionID = getSessionId(); - sendMessage(CastSession.kNamespaceReceiver, { - 'type': "STOP", - "sessionId": sessionID, - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': "STOP", "sessionId": sessionID}); // wait 500ms to ensure the stop command is processed await Future.delayed(const Duration(milliseconds: 500)); @@ -69,7 +63,6 @@ class GCastRepository { } Future> listDestinations() async { - return await CastDiscoveryService() - .search(timeout: const Duration(seconds: 3)); + return await CastDiscoveryService().search(timeout: const Duration(seconds: 3)); } } diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index 35c59acc2f..519d79b49b 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/local_files_manager.service.dart'; final localFilesManagerRepositoryProvider = Provider( - (ref) => - LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)), + (ref) => LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)), ); class LocalFilesManagerRepository { diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index eb41391ba2..7f5ce62e0c 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -1,49 +1,34 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final partnerRepositoryProvider = Provider( - (ref) => PartnerRepository(ref.watch(dbProvider)), -); +final partnerRepositoryProvider = Provider((ref) => PartnerRepository(ref.watch(dbProvider))); class PartnerRepository extends DatabaseRepository { const PartnerRepository(super.db); Future> getSharedBy() async { - return (await db.users - .filter() - .isPartnerSharedByEqualTo(true) - .sortById() - .findAll()) - .map((u) => u.toDto()) - .toList(); + return (await db.users.filter().isPartnerSharedByEqualTo(true).sortById().findAll()).map((u) => u.toDto()).toList(); } Future> getSharedWith() async { - return (await db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .findAll()) + return (await db.users.filter().isPartnerSharedWithEqualTo(true).sortById().findAll()) .map((u) => u.toDto()) .toList(); } Stream> watchSharedBy() { - return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } Stream> watchSharedWith() { - return (db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 836a708e3a..d497da4d4c 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -5,16 +5,9 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -enum Direction { - sharedWithMe, - sharedByMe, -} +enum Direction { sharedWithMe, sharedByMe } -final partnerApiRepositoryProvider = Provider( - (ref) => PartnerApiRepository( - ref.watch(apiServiceProvider).partnersApi, - ), -); +final partnerApiRepositoryProvider = Provider((ref) => PartnerApiRepository(ref.watch(apiServiceProvider).partnersApi)); class PartnerApiRepository extends ApiRepository { final PartnersApi _api; @@ -23,29 +16,20 @@ class PartnerApiRepository extends ApiRepository { Future> getAll(Direction direction) async { final response = await checkNull( - _api.getPartners( - direction == Direction.sharedByMe - ? PartnerDirection.by - : PartnerDirection.with_, - ), + _api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_), ); return response.map(UserConverter.fromPartnerDto).toList(); } Future create(String id) async { - final dto = await checkNull(_api.createPartner(id)); + final dto = await checkNull(_api.createPartnerDeprecated(id)); return UserConverter.fromPartnerDto(dto); } Future delete(String id) => _api.removePartner(id); Future update(String id, {required bool inTimeline}) async { - final dto = await checkNull( - _api.updatePartner( - id, - UpdatePartnerDto(inTimeline: inTimeline), - ), - ); + final dto = await checkNull(_api.updatePartner(id, PartnerUpdateDto(inTimeline: inTimeline))); return UserConverter.fromPartnerDto(dto); } } diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 26f11dd51d..04e4bd2a2c 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -4,9 +4,7 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final personApiRepositoryProvider = Provider( - (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi), -); +final personApiRepositoryProvider = Provider((ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi)); class PersonApiRepository extends ApiRepository { final PeopleApi _api; @@ -18,18 +16,16 @@ class PersonApiRepository extends ApiRepository { return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name}) async { - final dto = await checkNull( - _api.updatePerson(id, PersonUpdateDto(name: name)), - ); + Future update(String id, {String? name, DateTime? birthday}) async { + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name, birthDate: birthday))); return _toPerson(dto); } static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( - birthDate: dto.birthDate, - id: dto.id, - isHidden: dto.isHidden, - name: dto.name, - thumbnailPath: dto.thumbnailPath, - ); + birthDate: dto.birthDate, + id: dto.id, + isHidden: dto.isHidden, + name: dto.name, + thumbnailPath: dto.thumbnailPath, + ); } diff --git a/mobile/lib/repositories/secure_storage.repository.dart b/mobile/lib/repositories/secure_storage.repository.dart index 5660258a2d..9ae643a587 100644 --- a/mobile/lib/repositories/secure_storage.repository.dart +++ b/mobile/lib/repositories/secure_storage.repository.dart @@ -1,8 +1,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -final secureStorageRepositoryProvider = - Provider((ref) => const SecureStorageRepository(FlutterSecureStorage())); +final secureStorageRepositoryProvider = Provider((ref) => const SecureStorageRepository(FlutterSecureStorage())); class SecureStorageRepository { final FlutterSecureStorage _secureStorage; diff --git a/mobile/lib/repositories/sessions_api.repository.dart b/mobile/lib/repositories/sessions_api.repository.dart index 8ef43d54f2..f25e724f19 100644 --- a/mobile/lib/repositories/sessions_api.repository.dart +++ b/mobile/lib/repositories/sessions_api.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; final sessionsAPIRepositoryProvider = Provider( - (ref) => SessionsAPIRepository( - ref.watch(apiServiceProvider).sessionsApi, - ), + (ref) => SessionsAPIRepository(ref.watch(apiServiceProvider).sessionsApi), ); class SessionsAPIRepository extends ApiRepository { @@ -15,19 +13,9 @@ class SessionsAPIRepository extends ApiRepository { SessionsAPIRepository(this._api); - Future createSession( - String deviceType, - String deviceOS, { - int? duration, - }) async { + Future createSession(String deviceType, String deviceOS, {int? duration}) async { final dto = await checkNull( - _api.createSession( - SessionCreateDto( - deviceType: deviceType, - deviceOS: deviceOS, - duration: duration, - ), - ), + _api.createSession(SessionCreateDto(deviceType: deviceType, deviceOS: deviceOS, duration: duration)), ); return SessionCreateResponse( diff --git a/mobile/lib/repositories/share_handler.repository.dart b/mobile/lib/repositories/share_handler.repository.dart index e739ebe6ae..4650b04a79 100644 --- a/mobile/lib/repositories/share_handler.repository.dart +++ b/mobile/lib/repositories/share_handler.repository.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:share_handler/share_handler.dart'; -final shareHandlerRepositoryProvider = Provider( - (ref) => ShareHandlerRepository(), -); +final shareHandlerRepositoryProvider = Provider((ref) => ShareHandlerRepository()); class ShareHandlerRepository { ShareHandlerRepository(); @@ -28,9 +26,7 @@ class ShareHandlerRepository { }); } - List _buildPayload( - List attachments, - ) { + List _buildPayload(List attachments) { final payload = []; for (final attachment in attachments) { diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index ea4cb63753..c8c173b6f6 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -9,30 +9,17 @@ import 'package:immich_mobile/utils/hash.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; -final timelineRepositoryProvider = - Provider((ref) => TimelineRepository(ref.watch(dbProvider))); +final timelineRepositoryProvider = Provider((ref) => TimelineRepository(ref.watch(dbProvider))); class TimelineRepository extends DatabaseRepository { const TimelineRepository(super.db); Future> getTimelineUserIds(String id) { - return db.users - .filter() - .inTimelineEqualTo(true) - .or() - .idEqualTo(id) - .idProperty() - .findAll(); + return db.users.filter().inTimelineEqualTo(true).or().idEqualTo(id).idProperty().findAll(); } Stream> watchTimelineUsers(String id) { - return db.users - .filter() - .inTimelineEqualTo(true) - .or() - .idEqualTo(id) - .idProperty() - .watch(); + return db.users.filter().inTimelineEqualTo(true).or().idEqualTo(id).idProperty().watch(); } Stream watchArchiveTimeline(String userId) { @@ -61,15 +48,8 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchAlbumTimeline( - Album album, - GroupAssetsBy groupAssetByOption, - ) { - final query = album.assets - .filter() - .isTrashedEqualTo(false) - .not() - .visibilityEqualTo(AssetVisibilityEnum.locked); + Stream watchAlbumTimeline(Album album, GroupAssetsBy groupAssetByOption) { + final query = album.assets.filter().isTrashedEqualTo(false).not().visibilityEqualTo(AssetVisibilityEnum.locked); final withSortedOption = switch (album.sortOrder) { SortOrder.asc => query.sortByFileCreatedAt(), @@ -80,11 +60,7 @@ class TimelineRepository extends DatabaseRepository { } Stream watchTrashTimeline(String userId) { - final query = db.assets - .filter() - .ownerIdEqualTo(fastHash(userId)) - .isTrashedEqualTo(true) - .sortByFileCreatedAtDesc(); + final query = db.assets.filter().ownerIdEqualTo(fastHash(userId)).isTrashedEqualTo(true).sortByFileCreatedAtDesc(); return _watchRenderList(query, GroupAssetsBy.none); } @@ -102,10 +78,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchHomeTimeline( - String userId, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchHomeTimeline(String userId, GroupAssetsBy groupAssetByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) @@ -118,10 +91,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Stream watchMultiUsersTimeline( - List userIds, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchMultiUsersTimeline(List userIds, GroupAssetsBy groupAssetByOption) { final isarUserIds = userIds.map(fastHash).toList(); final query = db.assets .where() @@ -134,10 +104,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy getGroupByOption, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy getGroupByOption) { return RenderList.fromAssets(assets, getGroupByOption); } @@ -155,10 +122,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchLockedTimeline( - String userId, - GroupAssetsBy getGroupByOption, - ) { + Stream watchLockedTimeline(String userId, GroupAssetsBy getGroupByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index b98eece656..38f2c22cf2 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -1,12 +1,26 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:background_downloader/background_downloader.dart'; +import 'package:cancellation_token_http/http.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:logging/logging.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; + +class UploadTaskWithFile { + final File file; + final UploadTask task; + + const UploadTaskWithFile({required this.file, required this.task}); +} final uploadRepositoryProvider = Provider((ref) => UploadRepository()); class UploadRepository { void Function(TaskStatusUpdate)? onUploadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; UploadRepository() { @@ -20,13 +34,22 @@ class UploadRepository { taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); + FileDownloader().registerCallbacks( + group: kManualUploadGroup, + taskStatusCallback: (update) => onUploadStatus?.call(update), + taskProgressCallback: (update) => onTaskProgress?.call(update), + ); } - void enqueueAll(List tasks) { - FileDownloader().enqueueAll(tasks); + Future enqueueBackground(UploadTask task) { + return FileDownloader().enqueue(task); } - Future deleteAllTrackingRecords(String group) { + Future> enqueueBackgroundAll(List tasks) { + return FileDownloader().enqueueAll(tasks); + } + + Future deleteDatabaseRecords(String group) { return FileDownloader().database.deleteAllRecords(group: group); } @@ -37,4 +60,84 @@ class UploadRepository { Future reset(String group) { return FileDownloader().reset(group: group); } + + /// Get a list of tasks that are ENQUEUED or RUNNING + Future> getActiveTasks(String group) { + return FileDownloader().allTasks(group: group); + } + + Future start() { + return FileDownloader().start(); + } + + Future getUploadInfo() async { + final [enqueuedTasks, runningTasks, canceledTasks, waitingTasks, pausedTasks] = await Future.wait([ + FileDownloader().database.allRecordsWithStatus(TaskStatus.enqueued, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.running, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.canceled, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.waitingToRetry, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.paused, group: kBackupGroup), + ]); + + dPrint( + () => + """ + Upload Info: + Enqueued: ${enqueuedTasks.length} + Running: ${runningTasks.length} + Canceled: ${canceledTasks.length} + Waiting: ${waitingTasks.length} + Paused: ${pausedTasks.length} + """, + ); + } + + Future backupWithDartClient(Iterable tasks, CancellationToken cancelToken) async { + final httpClient = Client(); + final String savedEndpoint = Store.get(StoreKey.serverEndpoint); + + Logger logger = Logger('UploadRepository'); + for (final candidate in tasks) { + if (cancelToken.isCancelled) { + logger.warning("Backup was cancelled by the user"); + break; + } + + try { + final fileStream = candidate.file.openRead(); + final assetRawUploadData = MultipartFile( + "assetData", + fileStream, + candidate.file.lengthSync(), + filename: candidate.task.filename, + ); + + final baseRequest = MultipartRequest('POST', Uri.parse('$savedEndpoint/assets')); + + baseRequest.headers.addAll(candidate.task.headers); + baseRequest.fields.addAll(candidate.task.fields); + baseRequest.files.add(assetRawUploadData); + + final response = await httpClient.send(baseRequest, cancellationToken: cancelToken); + + final responseBody = jsonDecode(await response.stream.bytesToString()); + + if (![200, 201].contains(response.statusCode)) { + final error = responseBody; + + logger.warning( + "Error(${error['statusCode']}) uploading ${candidate.task.filename} | Created on ${candidate.task.fields["fileCreatedAt"]} | ${error['error']}", + ); + + continue; + } + } on CancelledException { + logger.warning("Backup was cancelled by the user"); + break; + } catch (error, stackTrace) { + logger.warning("Error backup asset: ${error.toString()}: $stackTrace"); + continue; + } + } + } } diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart index 09532f4b78..f21d31e1ec 100644 --- a/mobile/lib/repositories/widget.repository.dart +++ b/mobile/lib/repositories/widget.repository.dart @@ -11,10 +11,7 @@ class WidgetRepository { } Future refresh(String iosName, String androidName) async { - await HomeWidget.updateWidget( - iOSName: iosName, - qualifiedAndroidName: androidName, - ); + await HomeWidget.updateWidget(iOSName: iosName, qualifiedAndroidName: androidName); } Future setAppGroupId(String appGroupId) async { diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 98560018ee..26ec017b9a 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -8,73 +8,53 @@ class AppNavigationObserver extends AutoRouterObserver { /// Riverpod Instance final WidgetRef ref; - AppNavigationObserver({ - required this.ref, - }); + AppNavigationObserver({required this.ref}); @override - Future didChangeTabRoute( - TabPageRoute route, - TabPageRoute previousRoute, - ) async { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } @override void didPush(Route route, Route? previousRoute) { _handleLockedViewState(route, previousRoute); _handleDriftLockedFolderState(route, previousRoute); - Future( - () => ref.read(currentRouteNameProvider.notifier).state = - route.settings.name, - ); + Future(() { + ref.read(currentRouteNameProvider.notifier).state = route.settings.name; + ref.read(previousRouteNameProvider.notifier).state = previousRoute?.settings.name; + ref.read(previousRouteDataProvider.notifier).state = previousRoute?.settings; + }); } _handleLockedViewState(Route route, Route? previousRoute) { final isInLockedView = ref.read(inLockedViewProvider); final isFromLockedViewToDetailView = - route.settings.name == GalleryViewerRoute.name && - previousRoute?.settings.name == LockedRoute.name; + route.settings.name == GalleryViewerRoute.name && previousRoute?.settings.name == LockedRoute.name; - final isFromDetailViewToInfoPanelView = route.settings.name == null && - previousRoute?.settings.name == GalleryViewerRoute.name && - isInLockedView; + final isFromDetailViewToInfoPanelView = + route.settings.name == null && previousRoute?.settings.name == GalleryViewerRoute.name && isInLockedView; - if (route.settings.name == LockedRoute.name || - isFromLockedViewToDetailView || - isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + if (route.settings.name == LockedRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } _handleDriftLockedFolderState(Route route, Route? previousRoute) { final isInLockedView = ref.read(inLockedViewProvider); final isFromLockedViewToDetailView = - route.settings.name == AssetViewerRoute.name && - previousRoute?.settings.name == DriftLockedFolderRoute.name; + route.settings.name == AssetViewerRoute.name && previousRoute?.settings.name == DriftLockedFolderRoute.name; - final isFromDetailViewToInfoPanelView = route.settings.name == null && - previousRoute?.settings.name == AssetViewerRoute.name && - isInLockedView; + final isFromDetailViewToInfoPanelView = + route.settings.name == null && previousRoute?.settings.name == AssetViewerRoute.name && isInLockedView; if (route.settings.name == DriftLockedFolderRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } } diff --git a/mobile/lib/routing/custom_transition_builders.dart b/mobile/lib/routing/custom_transition_builders.dart index 610edd8185..d8412eb7cf 100644 --- a/mobile/lib/routing/custom_transition_builders.dart +++ b/mobile/lib/routing/custom_transition_builders.dart @@ -3,8 +3,7 @@ import 'package:flutter/material.dart'; class CustomTransitionsBuilders { const CustomTransitionsBuilders._(); - static const ZoomPageTransitionsBuilder zoomPageTransitionsBuilder = - ZoomPageTransitionsBuilder(); + static const ZoomPageTransitionsBuilder zoomPageTransitionsBuilder = ZoomPageTransitionsBuilder(); static const RouteTransitionsBuilder zoomedPage = _zoomedPage; @@ -19,8 +18,7 @@ class CustomTransitionsBuilders { PageRouteBuilder( allowSnapshotting: true, fullscreenDialog: false, - pageBuilder: (context, animation, secondaryAnimation) => - const SizedBox.shrink(), + pageBuilder: (context, animation, secondaryAnimation) => const SizedBox.shrink(), ), context, animation, diff --git a/mobile/lib/routing/duplicate_guard.dart b/mobile/lib/routing/duplicate_guard.dart index efc649bc41..c55c7318d0 100644 --- a/mobile/lib/routing/duplicate_guard.dart +++ b/mobile/lib/routing/duplicate_guard.dart @@ -1,5 +1,5 @@ import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; /// Guards against duplicate navigation to this route class DuplicateGuard extends AutoRouteGuard { @@ -8,9 +8,7 @@ class DuplicateGuard extends AutoRouteGuard { void onNavigation(NavigationResolver resolver, StackRouter router) async { // Duplicate navigation if (resolver.route.name == router.current.name) { - debugPrint( - 'DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}', - ); + dPrint(() => 'DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}'); resolver.next(false); } else { resolver.next(true); diff --git a/mobile/lib/routing/gallery_guard.dart b/mobile/lib/routing/gallery_guard.dart index fcac142271..eace8257b6 100644 --- a/mobile/lib/routing/gallery_guard.dart +++ b/mobile/lib/routing/gallery_guard.dart @@ -7,8 +7,7 @@ class GalleryGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) async { final newRouteName = resolver.route.name; - final currentTopRouteName = - router.stack.isNotEmpty ? router.stack.last.name : null; + final currentTopRouteName = router.stack.isNotEmpty ? router.stack.last.name : null; if (currentTopRouteName == newRouteName) { // Replace instead of pushing duplicate diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart index d731c7942c..851407ff16 100644 --- a/mobile/lib/routing/locked_guard.dart +++ b/mobile/lib/routing/locked_guard.dart @@ -17,11 +17,7 @@ class LockedGuard extends AutoRouteGuard { final LocalAuthService _localAuth; final _log = Logger("AuthGuard"); - LockedGuard( - this._apiService, - this._secureStorageService, - this._localAuth, - ); + LockedGuard(this._apiService, this._secureStorageService, this._localAuth); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { @@ -58,9 +54,7 @@ class LockedGuard extends AutoRouteGuard { return; } - await _apiService.authenticationApi.unlockAuthSession( - SessionUnlockDto(pinCode: securePinCode), - ); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: securePinCode)); resolver.next(true); } on PlatformException catch (error) { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index ba31ccef2b..7554c7b1cf 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -22,11 +23,13 @@ import 'package:immich_mobile/pages/album/album_shared_user_selection.page.dart' import 'package:immich_mobile/pages/album/album_viewer.page.dart'; import 'package:immich_mobile/pages/albums/albums.page.dart'; import 'package:immich_mobile/pages/backup/album_preview.page.dart'; -import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; -import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_asset_detail.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart'; import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; @@ -73,27 +76,38 @@ 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_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; +import 'package:immich_mobile/pages/settings/sync_status.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart'; +import 'package:immich_mobile/presentation/pages/download_info.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_activities.page.dart'; import 'package:immich_mobile/presentation/pages/drift_album.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_album_options.page.dart'; import 'package:immich_mobile/presentation/pages/drift_archive.page.dart'; import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_asset_troubleshoot.page.dart'; import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart'; import 'package:immich_mobile/presentation/pages/drift_library.page.dart'; import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_map.page.dart'; import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_people_collection.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_person.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart'; -import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_trash.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'; import 'package:immich_mobile/presentation/pages/drift_video.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_crop.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_edit.page.dart'; +import 'package:immich_mobile/presentation/pages/editing/drift_filter.page.dart'; import 'package:immich_mobile/presentation/pages/local_timeline.page.dart'; import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; @@ -138,8 +152,7 @@ class AppRouter extends RootStackRouter { ) { _authGuard = AuthGuard(apiService); _duplicateGuard = const DuplicateGuard(); - _lockedGuard = - LockedGuard(apiService, secureStorageService, localAuthService); + _lockedGuard = LockedGuard(apiService, secureStorageService, localAuthService); _backupPermissionGuard = BackupPermissionGuard(galleryPermissionNotifier); _galleryGuard = const GalleryGuard(); } @@ -150,38 +163,18 @@ class AppRouter extends RootStackRouter { @override late final List routes = [ AutoRoute(page: SplashScreenRoute.page, initial: true), - AutoRoute( - page: PermissionOnboardingRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PermissionOnboardingRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: LoginRoute.page, guards: [_duplicateGuard]), AutoRoute(page: ChangePasswordRoute.page), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), CustomRoute( page: TabControllerRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: PhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: LibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -189,23 +182,10 @@ class AppRouter extends RootStackRouter { page: TabShellRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftSearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftSearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -214,18 +194,9 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _galleryGuard], transitionsBuilder: CustomTransitionsBuilders.zoomedPage, ), - AutoRoute( - page: BackupControllerRoute.page, - guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], - ), - AutoRoute( - page: AllPlacesRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: CreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupControllerRoute.page, guards: [_authGuard, _duplicateGuard, _backupPermissionGuard]), + AutoRoute(page: AllPlacesRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: CreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: EditImageRoute.page), AutoRoute(page: CropImageRoute.page), AutoRoute(page: FilterImageRoute.page), @@ -235,14 +206,8 @@ class AppRouter extends RootStackRouter { transitionsBuilder: TransitionsBuilders.slideLeft, ), AutoRoute(page: AllVideosRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AllMotionPhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AllMotionPhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAssetSelectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -253,23 +218,14 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: AlbumViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumViewerRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAdditionalSharedUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: BackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumPreviewRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumPreviewRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: FailedBackupStatusRoute.page, guards: [_authGuard, _duplicateGuard], @@ -289,26 +245,13 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - CustomRoute( - page: FolderRoute.page, - guards: [_authGuard], - transitionsBuilder: TransitionsBuilders.fadeIn, - ), - AutoRoute( - page: PartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: PersonResultRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + CustomRoute(page: FolderRoute.page, guards: [_authGuard], transitionsBuilder: TransitionsBuilders.fadeIn), + AutoRoute(page: PartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: PersonResultRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: AllPeopleRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AlbumOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: TrashRoute.page, guards: [_authGuard, _duplicateGuard], @@ -319,28 +262,16 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: SharedLinkEditRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: SharedLinkEditRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: ActivitiesRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, durationInMilliseconds: 200, ), - CustomRoute( - page: MapLocationPickerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BackupOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: HeaderSettingsRoute.page, - guards: [_duplicateGuard], - ), + CustomRoute(page: MapLocationPickerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: HeaderSettingsRoute.page, guards: [_duplicateGuard]), CustomRoute( page: PeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -361,54 +292,18 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: NativeVideoViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ShareIntentRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LockedRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: PinAuthRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: FeatInDevRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftBackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: NativeVideoViewerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute( page: AssetViewerRoute.page, guards: [_authGuard, _duplicateGuard], @@ -421,80 +316,37 @@ class AppRouter extends RootStackRouter { ), ), ), - AutoRoute( - page: DriftMemoryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftFavoriteRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftTrashRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftArchiveRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLockedFolderRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftVideoRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAssetSelectionTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftRecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLocalAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftCreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUserSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - - AutoRoute( - page: ChangeExperienceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - - AutoRoute( - page: DriftPartnerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUploadDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftMemoryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftFavoriteRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftTrashRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftArchiveRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLockedFolderRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: DriftVideoRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAssetSelectionTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftRecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLocalAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftCreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SyncStatusRoute.page, guards: [_duplicateGuard]), + AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), + AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftMapRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftEditImageRoute.page), + AutoRoute(page: DriftCropImageRoute.page), + AutoRoute(page: DriftFilterImageRoute.page), + AutoRoute(page: DriftActivitiesRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupAssetDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AssetTroubleshootRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DownloadInfoRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 0e24f776d8..4e488a30c7 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -14,7 +14,7 @@ part of 'router.dart'; /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { const ActivitiesRoute({List? children}) - : super(ActivitiesRoute.name, initialChildren: children); + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -35,13 +35,13 @@ class AlbumAdditionalSharedUserSelectionRoute required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -83,14 +83,14 @@ class AlbumAssetSelectionRoute bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -130,7 +130,7 @@ class AlbumAssetSelectionRouteArgs { /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { const AlbumOptionsRoute({List? children}) - : super(AlbumOptionsRoute.name, initialChildren: children); + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -150,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -188,10 +188,10 @@ class AlbumSharedUserSelectionRoute required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -225,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -258,7 +258,7 @@ class AlbumViewerRouteArgs { /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { const AlbumsRoute({List? children}) - : super(AlbumsRoute.name, initialChildren: children); + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -274,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -290,7 +290,7 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { const AllPeopleRoute({List? children}) - : super(AllPeopleRoute.name, initialChildren: children); + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -306,7 +306,7 @@ class AllPeopleRoute extends PageRouteInfo { /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { const AllPlacesRoute({List? children}) - : super(AllPlacesRoute.name, initialChildren: children); + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -322,7 +322,7 @@ class AllPlacesRoute extends PageRouteInfo { /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { const AllVideosRoute({List? children}) - : super(AllVideosRoute.name, initialChildren: children); + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -342,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -375,7 +375,7 @@ class AppLogDetailRouteArgs { /// [AppLogPage] class AppLogRoute extends PageRouteInfo { const AppLogRoute({List? children}) - : super(AppLogRoute.name, initialChildren: children); + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -391,7 +391,7 @@ class AppLogRoute extends PageRouteInfo { /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { const ArchiveRoute({List? children}) - : super(ArchiveRoute.name, initialChildren: children); + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -403,6 +403,43 @@ class ArchiveRoute extends PageRouteInfo { ); } +/// generated route for +/// [AssetTroubleshootPage] +class AssetTroubleshootRoute extends PageRouteInfo { + AssetTroubleshootRoute({ + Key? key, + required BaseAsset asset, + List? children, + }) : super( + AssetTroubleshootRoute.name, + args: AssetTroubleshootRouteArgs(key: key, asset: asset), + initialChildren: children, + ); + + static const String name = 'AssetTroubleshootRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return AssetTroubleshootPage(key: args.key, asset: args.asset); + }, + ); +} + +class AssetTroubleshootRouteArgs { + const AssetTroubleshootRouteArgs({this.key, required this.asset}); + + final Key? key; + + final BaseAsset asset; + + @override + String toString() { + return 'AssetTroubleshootRouteArgs{key: $key, asset: $asset}'; + } +} + /// generated route for /// [AssetViewerPage] class AssetViewerRoute extends PageRouteInfo { @@ -410,16 +447,18 @@ class AssetViewerRoute extends PageRouteInfo { Key? key, required int initialIndex, required TimelineService timelineService, + int? heroOffset, List? children, }) : super( - AssetViewerRoute.name, - args: AssetViewerRouteArgs( - key: key, - initialIndex: initialIndex, - timelineService: timelineService, - ), - initialChildren: children, - ); + AssetViewerRoute.name, + args: AssetViewerRouteArgs( + key: key, + initialIndex: initialIndex, + timelineService: timelineService, + heroOffset: heroOffset, + ), + initialChildren: children, + ); static const String name = 'AssetViewerRoute'; @@ -431,6 +470,7 @@ class AssetViewerRoute extends PageRouteInfo { key: args.key, initialIndex: args.initialIndex, timelineService: args.timelineService, + heroOffset: args.heroOffset, ); }, ); @@ -441,6 +481,7 @@ class AssetViewerRouteArgs { this.key, required this.initialIndex, required this.timelineService, + this.heroOffset, }); final Key? key; @@ -449,9 +490,11 @@ class AssetViewerRouteArgs { final TimelineService timelineService; + final int? heroOffset; + @override String toString() { - return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService}'; + return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService, heroOffset: $heroOffset}'; } } @@ -459,7 +502,7 @@ class AssetViewerRouteArgs { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -475,7 +518,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -491,7 +534,7 @@ class BackupControllerRoute extends PageRouteInfo { /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { const BackupOptionsRoute({List? children}) - : super(BackupOptionsRoute.name, initialChildren: children); + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -511,13 +554,13 @@ class ChangeExperienceRoute extends PageRouteInfo { required bool switchingToBeta, List? children, }) : super( - ChangeExperienceRoute.name, - args: ChangeExperienceRouteArgs( - key: key, - switchingToBeta: switchingToBeta, - ), - initialChildren: children, - ); + ChangeExperienceRoute.name, + args: ChangeExperienceRouteArgs( + key: key, + switchingToBeta: switchingToBeta, + ), + initialChildren: children, + ); static const String name = 'ChangeExperienceRoute'; @@ -550,7 +593,7 @@ class ChangeExperienceRouteArgs { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -570,10 +613,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -610,10 +653,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -645,11 +688,59 @@ class CropImageRouteArgs { } } +/// generated route for +/// [DownloadInfoPage] +class DownloadInfoRoute extends PageRouteInfo { + const DownloadInfoRoute({List? children}) + : super(DownloadInfoRoute.name, initialChildren: children); + + static const String name = 'DownloadInfoRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DownloadInfoPage(); + }, + ); +} + +/// generated route for +/// [DriftActivitiesPage] +class DriftActivitiesRoute extends PageRouteInfo { + const DriftActivitiesRoute({List? children}) + : super(DriftActivitiesRoute.name, initialChildren: children); + + static const String name = 'DriftActivitiesRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftActivitiesPage(); + }, + ); +} + +/// generated route for +/// [DriftAlbumOptionsPage] +class DriftAlbumOptionsRoute extends PageRouteInfo { + const DriftAlbumOptionsRoute({List? children}) + : super(DriftAlbumOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftAlbumOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftAlbumOptionsPage(); + }, + ); +} + /// generated route for /// [DriftAlbumsPage] class DriftAlbumsRoute extends PageRouteInfo { const DriftAlbumsRoute({List? children}) - : super(DriftAlbumsRoute.name, initialChildren: children); + : super(DriftAlbumsRoute.name, initialChildren: children); static const String name = 'DriftAlbumsRoute'; @@ -665,7 +756,7 @@ class DriftAlbumsRoute extends PageRouteInfo { /// [DriftArchivePage] class DriftArchiveRoute extends PageRouteInfo { const DriftArchiveRoute({List? children}) - : super(DriftArchiveRoute.name, initialChildren: children); + : super(DriftArchiveRoute.name, initialChildren: children); static const String name = 'DriftArchiveRoute'; @@ -686,13 +777,13 @@ class DriftAssetSelectionTimelineRoute Set lockedSelectionAssets = const {}, List? children, }) : super( - DriftAssetSelectionTimelineRoute.name, - args: DriftAssetSelectionTimelineRouteArgs( - key: key, - lockedSelectionAssets: lockedSelectionAssets, - ), - initialChildren: children, - ); + DriftAssetSelectionTimelineRoute.name, + args: DriftAssetSelectionTimelineRouteArgs( + key: key, + lockedSelectionAssets: lockedSelectionAssets, + ), + initialChildren: children, + ); static const String name = 'DriftAssetSelectionTimelineRoute'; @@ -730,7 +821,7 @@ class DriftAssetSelectionTimelineRouteArgs { /// [DriftBackupAlbumSelectionPage] class DriftBackupAlbumSelectionRoute extends PageRouteInfo { const DriftBackupAlbumSelectionRoute({List? children}) - : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); + : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'DriftBackupAlbumSelectionRoute'; @@ -742,11 +833,43 @@ class DriftBackupAlbumSelectionRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftBackupAssetDetailPage] +class DriftBackupAssetDetailRoute extends PageRouteInfo { + const DriftBackupAssetDetailRoute({List? children}) + : super(DriftBackupAssetDetailRoute.name, initialChildren: children); + + static const String name = 'DriftBackupAssetDetailRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupAssetDetailPage(); + }, + ); +} + +/// generated route for +/// [DriftBackupOptionsPage] +class DriftBackupOptionsRoute extends PageRouteInfo { + const DriftBackupOptionsRoute({List? children}) + : super(DriftBackupOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftBackupOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupOptionsPage(); + }, + ); +} + /// generated route for /// [DriftBackupPage] class DriftBackupRoute extends PageRouteInfo { const DriftBackupRoute({List? children}) - : super(DriftBackupRoute.name, initialChildren: children); + : super(DriftBackupRoute.name, initialChildren: children); static const String name = 'DriftBackupRoute'; @@ -762,7 +885,7 @@ class DriftBackupRoute extends PageRouteInfo { /// [DriftCreateAlbumPage] class DriftCreateAlbumRoute extends PageRouteInfo { const DriftCreateAlbumRoute({List? children}) - : super(DriftCreateAlbumRoute.name, initialChildren: children); + : super(DriftCreateAlbumRoute.name, initialChildren: children); static const String name = 'DriftCreateAlbumRoute'; @@ -774,11 +897,117 @@ class DriftCreateAlbumRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftCropImagePage] +class DriftCropImageRoute extends PageRouteInfo { + DriftCropImageRoute({ + Key? key, + required Image image, + required BaseAsset asset, + List? children, + }) : super( + DriftCropImageRoute.name, + args: DriftCropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); + + static const String name = 'DriftCropImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftCropImagePage( + key: args.key, + image: args.image, + asset: args.asset, + ); + }, + ); +} + +class DriftCropImageRouteArgs { + const DriftCropImageRouteArgs({ + this.key, + required this.image, + required this.asset, + }); + + final Key? key; + + final Image image; + + final BaseAsset asset; + + @override + String toString() { + return 'DriftCropImageRouteArgs{key: $key, image: $image, asset: $asset}'; + } +} + +/// generated route for +/// [DriftEditImagePage] +class DriftEditImageRoute extends PageRouteInfo { + DriftEditImageRoute({ + Key? key, + required BaseAsset asset, + required Image image, + required bool isEdited, + List? children, + }) : super( + DriftEditImageRoute.name, + args: DriftEditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); + + static const String name = 'DriftEditImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftEditImagePage( + key: args.key, + asset: args.asset, + image: args.image, + isEdited: args.isEdited, + ); + }, + ); +} + +class DriftEditImageRouteArgs { + const DriftEditImageRouteArgs({ + this.key, + required this.asset, + required this.image, + required this.isEdited, + }); + + final Key? key; + + final BaseAsset asset; + + final Image image; + + final bool isEdited; + + @override + String toString() { + return 'DriftEditImageRouteArgs{key: $key, asset: $asset, image: $image, isEdited: $isEdited}'; + } +} + /// generated route for /// [DriftFavoritePage] class DriftFavoriteRoute extends PageRouteInfo { const DriftFavoriteRoute({List? children}) - : super(DriftFavoriteRoute.name, initialChildren: children); + : super(DriftFavoriteRoute.name, initialChildren: children); static const String name = 'DriftFavoriteRoute'; @@ -790,11 +1019,59 @@ class DriftFavoriteRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftFilterImagePage] +class DriftFilterImageRoute extends PageRouteInfo { + DriftFilterImageRoute({ + Key? key, + required Image image, + required BaseAsset asset, + List? children, + }) : super( + DriftFilterImageRoute.name, + args: DriftFilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); + + static const String name = 'DriftFilterImageRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftFilterImagePage( + key: args.key, + image: args.image, + asset: args.asset, + ); + }, + ); +} + +class DriftFilterImageRouteArgs { + const DriftFilterImageRouteArgs({ + this.key, + required this.image, + required this.asset, + }); + + final Key? key; + + final Image image; + + final BaseAsset asset; + + @override + String toString() { + return 'DriftFilterImageRouteArgs{key: $key, image: $image, asset: $asset}'; + } +} + /// generated route for /// [DriftLibraryPage] class DriftLibraryRoute extends PageRouteInfo { const DriftLibraryRoute({List? children}) - : super(DriftLibraryRoute.name, initialChildren: children); + : super(DriftLibraryRoute.name, initialChildren: children); static const String name = 'DriftLibraryRoute'; @@ -810,7 +1087,7 @@ class DriftLibraryRoute extends PageRouteInfo { /// [DriftLocalAlbumsPage] class DriftLocalAlbumsRoute extends PageRouteInfo { const DriftLocalAlbumsRoute({List? children}) - : super(DriftLocalAlbumsRoute.name, initialChildren: children); + : super(DriftLocalAlbumsRoute.name, initialChildren: children); static const String name = 'DriftLocalAlbumsRoute'; @@ -826,7 +1103,7 @@ class DriftLocalAlbumsRoute extends PageRouteInfo { /// [DriftLockedFolderPage] class DriftLockedFolderRoute extends PageRouteInfo { const DriftLockedFolderRoute({List? children}) - : super(DriftLockedFolderRoute.name, initialChildren: children); + : super(DriftLockedFolderRoute.name, initialChildren: children); static const String name = 'DriftLockedFolderRoute'; @@ -838,6 +1115,45 @@ class DriftLockedFolderRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftMapPage] +class DriftMapRoute extends PageRouteInfo { + DriftMapRoute({ + Key? key, + LatLng? initialLocation, + List? children, + }) : super( + DriftMapRoute.name, + args: DriftMapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); + + static const String name = 'DriftMapRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs( + orElse: () => const DriftMapRouteArgs(), + ); + return DriftMapPage(key: args.key, initialLocation: args.initialLocation); + }, + ); +} + +class DriftMapRouteArgs { + const DriftMapRouteArgs({this.key, this.initialLocation}); + + final Key? key; + + final LatLng? initialLocation; + + @override + String toString() { + return 'DriftMapRouteArgs{key: $key, initialLocation: $initialLocation}'; + } +} + /// generated route for /// [DriftMemoryPage] class DriftMemoryRoute extends PageRouteInfo { @@ -847,14 +1163,14 @@ class DriftMemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - DriftMemoryRoute.name, - args: DriftMemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + DriftMemoryRoute.name, + args: DriftMemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'DriftMemoryRoute'; @@ -899,10 +1215,10 @@ class DriftPartnerDetailRoute required PartnerUserDto partner, List? children, }) : super( - DriftPartnerDetailRoute.name, - args: DriftPartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + DriftPartnerDetailRoute.name, + args: DriftPartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'DriftPartnerDetailRoute'; @@ -932,7 +1248,7 @@ class DriftPartnerDetailRouteArgs { /// [DriftPartnerPage] class DriftPartnerRoute extends PageRouteInfo { const DriftPartnerRoute({List? children}) - : super(DriftPartnerRoute.name, initialChildren: children); + : super(DriftPartnerRoute.name, initialChildren: children); static const String name = 'DriftPartnerRoute'; @@ -944,6 +1260,59 @@ class DriftPartnerRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftPeopleCollectionPage] +class DriftPeopleCollectionRoute extends PageRouteInfo { + const DriftPeopleCollectionRoute({List? children}) + : super(DriftPeopleCollectionRoute.name, initialChildren: children); + + static const String name = 'DriftPeopleCollectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPeopleCollectionPage(); + }, + ); +} + +/// generated route for +/// [DriftPersonPage] +class DriftPersonRoute extends PageRouteInfo { + DriftPersonRoute({ + Key? key, + required DriftPerson person, + List? children, + }) : super( + DriftPersonRoute.name, + args: DriftPersonRouteArgs(key: key, person: person), + initialChildren: children, + ); + + static const String name = 'DriftPersonRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftPersonPage(key: args.key, person: args.person); + }, + ); +} + +class DriftPersonRouteArgs { + const DriftPersonRouteArgs({this.key, required this.person}); + + final Key? key; + + final DriftPerson person; + + @override + String toString() { + return 'DriftPersonRouteArgs{key: $key, person: $person}'; + } +} + /// generated route for /// [DriftPlaceDetailPage] class DriftPlaceDetailRoute extends PageRouteInfo { @@ -952,10 +1321,10 @@ class DriftPlaceDetailRoute extends PageRouteInfo { required String place, List? children, }) : super( - DriftPlaceDetailRoute.name, - args: DriftPlaceDetailRouteArgs(key: key, place: place), - initialChildren: children, - ); + DriftPlaceDetailRoute.name, + args: DriftPlaceDetailRouteArgs(key: key, place: place), + initialChildren: children, + ); static const String name = 'DriftPlaceDetailRoute'; @@ -989,10 +1358,10 @@ class DriftPlaceRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - DriftPlaceRoute.name, - args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), - initialChildren: children, - ); + DriftPlaceRoute.name, + args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), + initialChildren: children, + ); static const String name = 'DriftPlaceRoute'; @@ -1027,7 +1396,7 @@ class DriftPlaceRouteArgs { /// [DriftRecentlyTakenPage] class DriftRecentlyTakenRoute extends PageRouteInfo { const DriftRecentlyTakenRoute({List? children}) - : super(DriftRecentlyTakenRoute.name, initialChildren: children); + : super(DriftRecentlyTakenRoute.name, initialChildren: children); static const String name = 'DriftRecentlyTakenRoute'; @@ -1047,10 +1416,10 @@ class DriftSearchRoute extends PageRouteInfo { SearchFilter? preFilter, List? children, }) : super( - DriftSearchRoute.name, - args: DriftSearchRouteArgs(key: key, preFilter: preFilter), - initialChildren: children, - ); + DriftSearchRoute.name, + args: DriftSearchRouteArgs(key: key, preFilter: preFilter), + initialChildren: children, + ); static const String name = 'DriftSearchRoute'; @@ -1082,7 +1451,7 @@ class DriftSearchRouteArgs { /// [DriftTrashPage] class DriftTrashRoute extends PageRouteInfo { const DriftTrashRoute({List? children}) - : super(DriftTrashRoute.name, initialChildren: children); + : super(DriftTrashRoute.name, initialChildren: children); static const String name = 'DriftTrashRoute'; @@ -1098,7 +1467,7 @@ class DriftTrashRoute extends PageRouteInfo { /// [DriftUploadDetailPage] class DriftUploadDetailRoute extends PageRouteInfo { const DriftUploadDetailRoute({List? children}) - : super(DriftUploadDetailRoute.name, initialChildren: children); + : super(DriftUploadDetailRoute.name, initialChildren: children); static const String name = 'DriftUploadDetailRoute'; @@ -1119,10 +1488,10 @@ class DriftUserSelectionRoute required RemoteAlbum album, List? children, }) : super( - DriftUserSelectionRoute.name, - args: DriftUserSelectionRouteArgs(key: key, album: album), - initialChildren: children, - ); + DriftUserSelectionRoute.name, + args: DriftUserSelectionRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'DriftUserSelectionRoute'; @@ -1152,7 +1521,7 @@ class DriftUserSelectionRouteArgs { /// [DriftVideoPage] class DriftVideoRoute extends PageRouteInfo { const DriftVideoRoute({List? children}) - : super(DriftVideoRoute.name, initialChildren: children); + : super(DriftVideoRoute.name, initialChildren: children); static const String name = 'DriftVideoRoute'; @@ -1174,15 +1543,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -1226,7 +1595,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -1242,7 +1611,7 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { const FavoritesRoute({List? children}) - : super(FavoritesRoute.name, initialChildren: children); + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -1258,7 +1627,7 @@ class FavoritesRoute extends PageRouteInfo { /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { const FeatInDevRoute({List? children}) - : super(FeatInDevRoute.name, initialChildren: children); + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -1279,10 +1648,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -1326,10 +1695,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -1368,16 +1737,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -1425,7 +1794,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -1441,7 +1810,7 @@ class HeaderSettingsRoute extends PageRouteInfo { /// [LibraryPage] class LibraryRoute extends PageRouteInfo { const LibraryRoute({List? children}) - : super(LibraryRoute.name, initialChildren: children); + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -1457,7 +1826,7 @@ class LibraryRoute extends PageRouteInfo { /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { const LocalAlbumsRoute({List? children}) - : super(LocalAlbumsRoute.name, initialChildren: children); + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -1473,7 +1842,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -1493,10 +1862,10 @@ class LocalTimelineRoute extends PageRouteInfo { required LocalAlbum album, List? children, }) : super( - LocalTimelineRoute.name, - args: LocalTimelineRouteArgs(key: key, album: album), - initialChildren: children, - ); + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'LocalTimelineRoute'; @@ -1526,7 +1895,7 @@ class LocalTimelineRouteArgs { /// [LockedPage] class LockedRoute extends PageRouteInfo { const LockedRoute({List? children}) - : super(LockedRoute.name, initialChildren: children); + : super(LockedRoute.name, initialChildren: children); static const String name = 'LockedRoute'; @@ -1542,7 +1911,7 @@ class LockedRoute extends PageRouteInfo { /// [LoginPage] class LoginRoute extends PageRouteInfo { const LoginRoute({List? children}) - : super(LoginRoute.name, initialChildren: children); + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -1558,7 +1927,7 @@ class LoginRoute extends PageRouteInfo { /// [MainTimelinePage] class MainTimelineRoute extends PageRouteInfo { const MainTimelineRoute({List? children}) - : super(MainTimelineRoute.name, initialChildren: children); + : super(MainTimelineRoute.name, initialChildren: children); static const String name = 'MainTimelineRoute'; @@ -1578,13 +1947,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -1622,11 +1991,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -1663,14 +2032,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1717,16 +2086,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1778,10 +2147,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1811,7 +2180,7 @@ class PartnerDetailRouteArgs { /// [PartnerPage] class PartnerRoute extends PageRouteInfo { const PartnerRoute({List? children}) - : super(PartnerRoute.name, initialChildren: children); + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1827,7 +2196,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1843,7 +2212,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1864,14 +2233,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1911,7 +2280,7 @@ class PersonResultRouteArgs { /// [PhotosPage] class PhotosRoute extends PageRouteInfo { const PhotosRoute({List? children}) - : super(PhotosRoute.name, initialChildren: children); + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1931,10 +2300,10 @@ class PinAuthRoute extends PageRouteInfo { bool createPinCode = false, List? children, }) : super( - PinAuthRoute.name, - args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), - initialChildren: children, - ); + PinAuthRoute.name, + args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), + initialChildren: children, + ); static const String name = 'PinAuthRoute'; @@ -1970,13 +2339,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -2011,7 +2380,7 @@ class PlacesCollectionRouteArgs { /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { const RecentlyTakenRoute({List? children}) - : super(RecentlyTakenRoute.name, initialChildren: children); + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -2031,10 +2400,10 @@ class RemoteAlbumRoute extends PageRouteInfo { required RemoteAlbum album, List? children, }) : super( - RemoteAlbumRoute.name, - args: RemoteAlbumRouteArgs(key: key, album: album), - initialChildren: children, - ); + RemoteAlbumRoute.name, + args: RemoteAlbumRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'RemoteAlbumRoute'; @@ -2064,7 +2433,7 @@ class RemoteAlbumRouteArgs { /// [RemoteMediaSummaryPage] class RemoteMediaSummaryRoute extends PageRouteInfo { const RemoteMediaSummaryRoute({List? children}) - : super(RemoteMediaSummaryRoute.name, initialChildren: children); + : super(RemoteMediaSummaryRoute.name, initialChildren: children); static const String name = 'RemoteMediaSummaryRoute'; @@ -2084,10 +2453,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -2119,7 +2488,7 @@ class SearchRouteArgs { /// [SettingsPage] class SettingsRoute extends PageRouteInfo { const SettingsRoute({List? children}) - : super(SettingsRoute.name, initialChildren: children); + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -2139,10 +2508,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -2176,10 +2545,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -2215,15 +2584,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -2269,7 +2638,7 @@ class SharedLinkEditRouteArgs { /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { const SharedLinkRoute({List? children}) - : super(SharedLinkRoute.name, initialChildren: children); + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -2285,7 +2654,7 @@ class SharedLinkRoute extends PageRouteInfo { /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { const SplashScreenRoute({List? children}) - : super(SplashScreenRoute.name, initialChildren: children); + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -2297,11 +2666,27 @@ class SplashScreenRoute extends PageRouteInfo { ); } +/// generated route for +/// [SyncStatusPage] +class SyncStatusRoute extends PageRouteInfo { + const SyncStatusRoute({List? children}) + : super(SyncStatusRoute.name, initialChildren: children); + + static const String name = 'SyncStatusRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const SyncStatusPage(); + }, + ); +} + /// generated route for /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { const TabControllerRoute({List? children}) - : super(TabControllerRoute.name, initialChildren: children); + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -2317,7 +2702,7 @@ class TabControllerRoute extends PageRouteInfo { /// [TabShellPage] class TabShellRoute extends PageRouteInfo { const TabShellRoute({List? children}) - : super(TabShellRoute.name, initialChildren: children); + : super(TabShellRoute.name, initialChildren: children); static const String name = 'TabShellRoute'; @@ -2333,7 +2718,7 @@ class TabShellRoute extends PageRouteInfo { /// [TrashPage] class TrashRoute extends PageRouteInfo { const TrashRoute({List? children}) - : super(TrashRoute.name, initialChildren: children); + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index 7b0d74e420..9c3768080b 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -1,7 +1,6 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; @@ -11,8 +10,10 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; +import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:maplibre_gl/maplibre_gl.dart' as maplibre; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -49,11 +50,7 @@ class ActionService { ); Future shareLink(List remoteIds, BuildContext context) async { - context.pushRoute( - SharedLinkEditRoute( - assetsList: remoteIds, - ), - ); + context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); } Future favorite(List remoteIds) async { @@ -67,39 +64,18 @@ class ActionService { } Future archive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.archive, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.archive, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.archive); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.archive); } Future unArchive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } - Future moveToLockFolder( - List remoteIds, - List localIds, - ) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.locked, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.locked, - ); + Future moveToLockFolder(List remoteIds, List localIds) async { + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.locked); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.locked); // Ask user if they want to delete local copies if (localIds.isNotEmpty) { @@ -112,14 +88,8 @@ class ActionService { } Future removeFromLockFolder(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } Future trash(List remoteIds) async { @@ -127,20 +97,48 @@ class ActionService { await _remoteAssetRepository.trash(remoteIds); } - Future delete(List remoteIds) async { + Future restoreTrash(List ids) async { + await _assetApiRepository.restoreTrash(ids); + await _remoteAssetRepository.restoreTrash(ids); + } + + Future trashRemoteAndDeleteLocal(List remoteIds, List localIds) async { + await _assetApiRepository.delete(remoteIds, false); + await _remoteAssetRepository.trash(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } + } + + Future deleteRemoteAndLocal(List remoteIds, List localIds) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } - Future deleteLocal(List localIds) async { - await _assetMediaRepository.deleteAll(localIds); - await _localAssetRepository.delete(localIds); + Future deleteLocal(List localIds) async { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + return deletedIds.length; + } + + return 0; } - Future editLocation( - List remoteIds, - BuildContext context, - ) async { + Future editLocation(List remoteIds, BuildContext context) async { maplibre.LatLng? initialLatLng; if (remoteIds.length == 1) { final exif = await _remoteAssetRepository.getExif(remoteIds[0]); @@ -150,37 +148,70 @@ class ActionService { } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return false; } - await _assetApiRepository.updateLocation( - remoteIds, - location, - ); - await _remoteAssetRepository.updateLocation( - remoteIds, - location, + await _assetApiRepository.updateLocation(remoteIds, location); + await _remoteAssetRepository.updateLocation(remoteIds, location); + + return true; + } + + Future editDateTime(List remoteIds, BuildContext context) async { + DateTime? initialDate; + String? timeZone; + Duration? offset; + + if (remoteIds.length == 1) { + final assetId = remoteIds.first; + final asset = await _remoteAssetRepository.get(assetId); + if (asset == null) { + return false; + } + + final exifData = await _remoteAssetRepository.getExif(assetId); + initialDate = asset.createdAt.toLocal(); + offset = initialDate.timeZoneOffset; + timeZone = exifData?.timeZone; + } + + final dateTime = await showDateTimePicker( + context: context, + initialDateTime: initialDate, + initialTZ: timeZone, + initialTZOffset: offset, ); + if (dateTime == null) { + return false; + } + + // convert dateTime to DateTime object + final parsedDateTime = DateTime.parse(dateTime); + + await _assetApiRepository.updateDateTime(remoteIds, parsedDateTime); + await _remoteAssetRepository.updateDateTime(remoteIds, parsedDateTime); + return true; } Future removeFromAlbum(List remoteIds, String albumId) async { - int removedCount = 0; final result = await _albumApiRepository.removeAssets(albumId, remoteIds); - if (result.removed.isNotEmpty) { - removedCount = - await _remoteAlbumRepository.removeAssets(albumId, result.removed); + await _remoteAlbumRepository.removeAssets(albumId, result.removed); } + return result.removed.length; + } - return removedCount; + Future updateDescription(String assetId, String description) async { + // update remote first, then local to ensure consistency + await _assetApiRepository.updateDescription(assetId, description); + await _remoteAssetRepository.updateDescription(assetId, description); + + return true; } Future stack(String userId, List remoteIds) async { @@ -193,8 +224,8 @@ class ActionService { await _assetApiRepository.unStack(stackIds); } - Future shareAssets(List assets) { - return _assetMediaRepository.shareAssets(assets); + Future shareAssets(List assets, BuildContext context) { + return _assetMediaRepository.shareAssets(assets, context); } Future> downloadAll(List assets) { diff --git a/mobile/lib/services/activity.service.dart b/mobile/lib/services/activity.service.dart index 611d985afe..1f09309947 100644 --- a/mobile/lib/services/activity.service.dart +++ b/mobile/lib/services/activity.service.dart @@ -1,3 +1,4 @@ +import 'package:immich_mobile/constants/errors.dart'; import 'package:immich_mobile/mixins/error_logger.mixin.dart'; import 'package:immich_mobile/models/activities/activity.model.dart'; import 'package:immich_mobile/repositories/activity_api.repository.dart'; @@ -11,10 +12,7 @@ class ActivityService with ErrorLoggerMixin { ActivityService(this._activityApiRepository); - Future> getAllActivities( - String albumId, { - String? assetId, - }) async { + Future> getAllActivities(String albumId, {String? assetId}) async { return logError( () => _activityApiRepository.getAll(albumId, assetId: assetId), defaultValue: [], @@ -33,7 +31,11 @@ class ActivityService with ErrorLoggerMixin { Future removeActivity(String id) async { return logError( () async { - await _activityApiRepository.delete(id); + try { + await _activityApiRepository.delete(id); + } on NoResponseDtoError { + return true; + } return true; }, defaultValue: false, @@ -41,19 +43,9 @@ class ActivityService with ErrorLoggerMixin { ); } - AsyncFuture addActivity( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + AsyncFuture addActivity(String albumId, ActivityType type, {String? assetId, String? comment}) async { return guardError( - () => _activityApiRepository.create( - albumId, - type, - assetId: assetId, - comment: comment, - ), + () => _activityApiRepository.create(albumId, type, assetId: assetId, comment: comment), errorMessage: "Failed to create $type for album $albumId", ); } diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index 6733ec41b2..a9eee0528e 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'dart:io'; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -11,8 +10,7 @@ import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; @@ -25,6 +23,7 @@ import 'package:immich_mobile/services/entity.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final albumServiceProvider = Provider( (ref) => AlbumService( @@ -76,13 +75,9 @@ class AlbumService { bool changes = false; try { final (selectedIds, excludedIds, onDevice) = await ( - _backupAlbumRepository - .getIdsBySelection(BackupSelection.select) - .then((value) => value.toSet()), - _backupAlbumRepository - .getIdsBySelection(BackupSelection.exclude) - .then((value) => value.toSet()), - _albumMediaRepository.getAll() + _backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()), + _backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()), + _albumMediaRepository.getAll(), ).wait; _log.info("Found ${onDevice.length} device albums"); if (selectedIds.isEmpty) { @@ -107,9 +102,7 @@ class AlbumService { } // remove all excluded albums onDevice.removeWhere((e) => excludedIds.contains(e.localId)); - _log.info( - "Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums", - ); + _log.info("Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums"); } final allAlbum = onDevice.firstWhereOrNull((album) => album.isAll); @@ -126,30 +119,20 @@ class AlbumService { onDevice.removeWhere((album) => !selectedIds.contains(album.localId)); _log.info("'Recents' is not selected, keeping only selected albums"); } - changes = - await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets); + changes = await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets); _log.info("Syncing completed. Changes: $changes"); } finally { _localCompleter.complete(changes); } - debugPrint("refreshDeviceAlbums took ${sw.elapsedMilliseconds}ms"); + dPrint(() => "refreshDeviceAlbums took ${sw.elapsedMilliseconds}ms"); return changes; } - Future> _loadExcludedAssetIds( - List albums, - Set excludedAlbumIds, - ) async { + Future> _loadExcludedAssetIds(List albums, Set excludedAlbumIds) async { final Set result = HashSet(); - for (final batchAlbums in albums - .where((album) => excludedAlbumIds.contains(album.localId)) - .slices(5)) { + for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) { await batchAlbums - .map( - (album) => _albumMediaRepository - .getAssetIds(album.localId!) - .then((assetIds) => result.addAll(assetIds)), - ) + .map((album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds))) .wait; } return result; @@ -177,13 +160,10 @@ class AlbumService { _albumApiRepository.getAll(shared: true), // Passing null (or nothing) for `shared` returns only albums that // explicitly belong to us - _albumApiRepository.getAll(shared: null) + _albumApiRepository.getAll(shared: null), ).wait; - final albums = HashSet( - equals: (a, b) => a.remoteId == b.remoteId, - hashCode: (a) => a.remoteId.hashCode, - ); + final albums = HashSet(equals: (a, b) => a.remoteId == b.remoteId, hashCode: (a) => a.remoteId.hashCode); albums.addAll(sharedAlbum); albums.addAll(ownedAlbum); @@ -192,7 +172,7 @@ class AlbumService { } finally { _remoteCompleter.complete(changes); } - debugPrint("refreshRemoteAlbums took ${sw.elapsedMilliseconds}ms"); + dPrint(() => "refreshRemoteAlbums took ${sw.elapsedMilliseconds}ms"); return changes; } @@ -215,7 +195,7 @@ class AlbumService { */ Future _getNextAlbumName() async { const baseName = "Untitled"; - for (int round = 0;; round++) { + for (int round = 0; ; round++) { final proposedName = "$baseName${round == 0 ? "" : " ($round)"}"; if (null == await _albumRepository.getByName(proposedName, owner: true)) { @@ -224,25 +204,13 @@ class AlbumService { } } - Future createAlbumWithGeneratedName( - Iterable assets, - ) async { - return createAlbum( - await _getNextAlbumName(), - assets, - [], - ); + Future createAlbumWithGeneratedName(Iterable assets) async { + return createAlbum(await _getNextAlbumName(), assets, []); } - Future addAssets( - Album album, - Iterable assets, - ) async { + Future addAssets(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.addAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.addAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); final List addedAssets = result.added .map((id) => assets.firstWhere((asset) => asset.remoteId == id)) @@ -250,21 +218,14 @@ class AlbumService { await _updateAssets(album.id, add: addedAssets); - return AlbumAddAssetsResponse( - alreadyInAlbum: result.duplicates, - successfullyAdded: addedAssets.length, - ); + return AlbumAddAssetsResponse(alreadyInAlbum: result.duplicates, successfullyAdded: addedAssets.length); } catch (e) { - debugPrint("Error addAssets ${e.toString()}"); + dPrint(() => "Error addAssets ${e.toString()}"); } return null; } - Future _updateAssets( - int albumId, { - List add = const [], - List remove = const [], - }) => + Future _updateAssets(int albumId, {List add = const [], List remove = const []}) => _albumRepository.transaction(() async { final album = await _albumRepository.get(albumId); if (album == null) return; @@ -276,15 +237,12 @@ class AlbumService { Future setActivityStatus(Album album, bool enabled) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - activityEnabled: enabled, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, activityEnabled: enabled); album.activityEnabled = updatedAlbum.activityEnabled; await _albumRepository.update(album); return true; } catch (e) { - debugPrint("Error setActivityEnabled ${e.toString()}"); + dPrint(() => "Error setActivityEnabled ${e.toString()}"); } return false; } @@ -296,19 +254,15 @@ class AlbumService { await _albumApiRepository.delete(album.remoteId!); } if (album.shared) { - final foreignAssets = - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); + final foreignAssets = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); await _albumRepository.delete(album.id); final List albums = await _albumRepository.getAll(shared: true); final List existing = []; for (Album album in albums) { - existing.addAll( - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]), - ); + existing.addAll(await _assetRepository.getByAlbum(album, notOwnedBy: [userId])); } - final List idsToRemove = - _syncService.sharedAssetsToRemove(foreignAssets, existing); + final List idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing); if (idsToRemove.isNotEmpty) { await _assetRepository.deleteByIds(idsToRemove); } @@ -317,7 +271,7 @@ class AlbumService { } return true; } catch (e) { - debugPrint("Error deleteAlbum ${e.toString()}"); + dPrint(() => "Error deleteAlbum ${e.toString()}"); } return false; } @@ -327,39 +281,26 @@ class AlbumService { await _albumApiRepository.removeUser(album.remoteId!, userId: "me"); return true; } catch (e) { - debugPrint("Error leaveAlbum ${e.toString()}"); + dPrint(() => "Error leaveAlbum ${e.toString()}"); return false; } } - Future removeAsset( - Album album, - Iterable assets, - ) async { + Future removeAsset(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.removeAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); - final toRemove = result.removed - .map((id) => assets.firstWhere((asset) => asset.remoteId == id)); + final result = await _albumApiRepository.removeAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); + final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id)); await _updateAssets(album.id, remove: toRemove.toList()); return true; } catch (e) { - debugPrint("Error removeAssetFromAlbum ${e.toString()}"); + dPrint(() => "Error removeAssetFromAlbum ${e.toString()}"); } return false; } - Future removeUser( - Album album, - UserDto user, - ) async { + Future removeUser(Album album, UserDto user) async { try { - await _albumApiRepository.removeUser( - album.remoteId!, - userId: user.id, - ); + await _albumApiRepository.removeUser(album.remoteId!, userId: user.id); album.sharedUsers.remove(entity.User.fromDto(user)); await _albumRepository.removeUsers(album, [user]); @@ -369,93 +310,61 @@ class AlbumService { return true; } catch (error) { - debugPrint("Error removeUser ${error.toString()}"); + dPrint(() => "Error removeUser ${error.toString()}"); return false; } } - Future addUsers( - Album album, - List userIds, - ) async { + Future addUsers(Album album, List userIds) async { try { - final updatedAlbum = - await _albumApiRepository.addUsers(album.remoteId!, userIds); + final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds); album.sharedUsers.addAll(updatedAlbum.remoteUsers); album.shared = true; - await _albumRepository.addUsers( - album, - album.sharedUsers.map((u) => u.toDto()).toList(), - ); + await _albumRepository.addUsers(album, album.sharedUsers.map((u) => u.toDto()).toList()); await _albumRepository.update(album); return true; } catch (error) { - debugPrint("Error addUsers ${error.toString()}"); + dPrint(() => "Error addUsers ${error.toString()}"); } return false; } - Future changeTitleAlbum( - Album album, - String newAlbumTitle, - ) async { + Future changeTitleAlbum(Album album, String newAlbumTitle) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - name: newAlbumTitle, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, name: newAlbumTitle); album.name = updatedAlbum.name; await _albumRepository.update(album); return true; } catch (e) { - debugPrint("Error changeTitleAlbum ${e.toString()}"); + dPrint(() => "Error changeTitleAlbum ${e.toString()}"); return false; } } - Future changeDescriptionAlbum( - Album album, - String newAlbumDescription, - ) async { + Future changeDescriptionAlbum(Album album, String newAlbumDescription) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - description: newAlbumDescription, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, description: newAlbumDescription); album.description = updatedAlbum.description; await _albumRepository.update(album); return true; } catch (e) { - debugPrint("Error changeDescriptionAlbum ${e.toString()}"); + dPrint(() => "Error changeDescriptionAlbum ${e.toString()}"); return false; } } - Future getAlbumByName( - String name, { - bool? remote, - bool? shared, - bool? owner, - }) => - _albumRepository.getByName( - name, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String name, {bool? remote, bool? shared, bool? owner}) => + _albumRepository.getByName(name, remote: remote, shared: shared, owner: owner); /// /// Add the uploaded asset to the selected albums /// - Future syncUploadAlbums( - List albumNames, - List assetIds, - ) async { + Future syncUploadAlbums(List albumNames, List assetIds) async { for (final albumName in albumNames) { Album? album = await getAlbumByName(albumName, remote: true, owner: true); album ??= await createAlbum(albumName, []); @@ -494,17 +403,13 @@ class AlbumService { return _albumRepository.watchAlbum(id); } - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { return _albumRepository.search(searchTerm, filterMode); } Future updateSortOrder(Album album, SortOrder order) async { try { - final updateAlbum = - await _albumApiRepository.update(album.remoteId!, sortOrder: order); + final updateAlbum = await _albumApiRepository.update(album.remoteId!, sortOrder: order); album.sortOrder = updateAlbum.sortOrder; return _albumRepository.update(album); diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index fe007a2aab..4033ffb184 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/material.dart'; import 'package:http/http.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -11,6 +10,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:immich_mobile/utils/user_agent.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class ApiService implements Authentication { late ApiClient _apiClient; @@ -127,11 +127,7 @@ class ApiService implements Authentication { } on SocketException catch (_) { return false; } catch (error, stackTrace) { - _log.severe( - "Error while checking server availability", - error, - stackTrace, - ); + _log.severe("Error while checking server availability", error, stackTrace); return false; } return true; @@ -145,10 +141,7 @@ class ApiService implements Authentication { headers.addAll(getRequestHeaders()); final res = await client - .get( - Uri.parse("$baseUrl/.well-known/immich"), - headers: headers, - ) + .get(Uri.parse("$baseUrl/.well-known/immich"), headers: headers) .timeout(const Duration(seconds: 5)); if (res.statusCode == 200) { @@ -162,7 +155,7 @@ class ApiService implements Authentication { return endpoint; } } catch (e) { - debugPrint("Could not locate /.well-known/immich at $baseUrl"); + dPrint(() => "Could not locate /.well-known/immich at $baseUrl"); } return ""; @@ -178,13 +171,11 @@ class ApiService implements Authentication { if (Platform.isIOS) { final iosInfo = await deviceInfoPlugin.iosInfo; - authenticationApi.apiClient - .addDefaultHeader('deviceModel', iosInfo.utsname.machine); + authenticationApi.apiClient.addDefaultHeader('deviceModel', iosInfo.utsname.machine); authenticationApi.apiClient.addDefaultHeader('deviceType', 'iOS'); } else if (Platform.isAndroid) { final androidInfo = await deviceInfoPlugin.androidInfo; - authenticationApi.apiClient - .addDefaultHeader('deviceModel', androidInfo.model); + authenticationApi.apiClient.addDefaultHeader('deviceModel', androidInfo.model); authenticationApi.apiClient.addDefaultHeader('deviceType', 'Android'); } else { authenticationApi.apiClient.addDefaultHeader('deviceModel', 'Unknown'); @@ -213,10 +204,7 @@ class ApiService implements Authentication { } @override - Future applyToParams( - List queryParams, - Map headerParams, - ) { + Future applyToParams(List queryParams, Map headerParams) { return Future(() { var headers = ApiService.getRequestHeaders(); headerParams.addAll(headers); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index cefc52385a..03d91328d1 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -5,26 +5,10 @@ import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { loadPreview(StoreKey.loadPreview, "loadPreview", true), loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), - themeMode( - StoreKey.themeMode, - "themeMode", - "system", - ), // "light","dark","system" - primaryColor( - StoreKey.primaryColor, - "primaryColor", - defaultColorPresetName, - ), - dynamicTheme( - StoreKey.dynamicTheme, - "dynamicTheme", - false, - ), - colorfulInterface( - StoreKey.colorfulInterface, - "colorfulInterface", - true, - ), + themeMode(StoreKey.themeMode, "themeMode", "system"), // "light","dark","system" + primaryColor(StoreKey.primaryColor, "primaryColor", defaultColorPresetName), + dynamicTheme(StoreKey.dynamicTheme, "dynamicTheme", false), + colorfulInterface(StoreKey.colorfulInterface, "colorfulInterface", true), tilesPerRow(StoreKey.tilesPerRow, "tilesPerRow", 4), dynamicLayout(StoreKey.dynamicLayout, "dynamicLayout", false), groupAssetsBy(StoreKey.groupAssetsBy, "groupBy", 0), @@ -33,43 +17,23 @@ enum AppSettingsEnum { "uploadErrorNotificationGracePeriod", 2, ), - backgroundBackupTotalProgress( - StoreKey.backgroundBackupTotalProgress, - "backgroundBackupTotalProgress", - true, - ), + backgroundBackupTotalProgress(StoreKey.backgroundBackupTotalProgress, "backgroundBackupTotalProgress", true), backgroundBackupSingleProgress( StoreKey.backgroundBackupSingleProgress, "backgroundBackupSingleProgress", false, ), storageIndicator(StoreKey.storageIndicator, "storageIndicator", true), - thumbnailCacheSize( - StoreKey.thumbnailCacheSize, - "thumbnailCacheSize", - 10000, - ), + thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), - albumThumbnailCacheSize( - StoreKey.albumThumbnailCacheSize, - "albumThumbnailCacheSize", - 200, - ), - selectedAlbumSortOrder( - StoreKey.selectedAlbumSortOrder, - "selectedAlbumSortOrder", - 0, - ), + albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), + selectedAlbumSortOrder(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 0), advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), logLevel(StoreKey.logLevel, null, 5), // Level.INFO = 5 preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), - loadOriginalVideo( - StoreKey.loadOriginalVideo, - "loadOriginalVideo", - false, - ), + loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), mapThemeMode(StoreKey.mapThemeMode, null, 0), mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), @@ -77,22 +41,18 @@ enum AppSettingsEnum { mapRelativeDate(StoreKey.mapRelativeDate, null, 0), allowSelfSignedSSLCert(StoreKey.selfSignedCert, null, false), ignoreIcloudAssets(StoreKey.ignoreIcloudAssets, null, false), - selectedAlbumSortReverse( - StoreKey.selectedAlbumSortReverse, - null, - false, - ), + selectedAlbumSortReverse(StoreKey.selectedAlbumSortReverse, null, false), enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), syncAlbums(StoreKey.syncAlbums, null, false), autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), - photoManagerCustomFilter( - StoreKey.photoManagerCustomFilter, - null, - true, - ), - betaTimeline(StoreKey.betaTimeline, null, false), + photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), + betaTimeline(StoreKey.betaTimeline, null, true), enableBackup(StoreKey.enableBackup, null, false), - ; + useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), + useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false), + backupRequireCharging(StoreKey.backupRequireCharging, null, false), + backupTriggerDelay(StoreKey.backupTriggerDelay, null, 30), + readonlyModeEnabled(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index cb7f59e3a9..b9fab35442 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -26,6 +25,7 @@ import 'package:immich_mobile/services/sync.service.dart'; import 'package:logging/logging.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final assetServiceProvider = Provider( (ref) => AssetService( @@ -80,26 +80,23 @@ class AssetService { final syncedUserIds = await _etagRepository.getAllIds(); final List syncedUsers = syncedUserIds.isEmpty ? [] - : (await _isarUserRepository.getByUserIds(syncedUserIds)) - .nonNulls - .toList(); + : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, getChangedAssets: _getRemoteAssetChanges, loadAssets: _getRemoteAssets, ); - debugPrint("refreshRemoteAssets full took ${sw.elapsedMilliseconds}ms"); + dPrint(() => "refreshRemoteAssets full took ${sw.elapsedMilliseconds}ms"); return changes; } /// Returns `(null, null)` if changes are invalid -> requires full sync - Future<(List? toUpsert, List? toDelete)> - _getRemoteAssetChanges(List users, DateTime since) async { - final dto = AssetDeltaSyncDto( - updatedAfter: since, - userIds: users.map((e) => e.id).toList(), - ); + Future<(List? toUpsert, List? toDelete)> _getRemoteAssetChanges( + List users, + DateTime since, + ) async { + final dto = AssetDeltaSyncDto(updatedAfter: since, userIds: users.map((e) => e.id).toList()); final changes = await _apiService.syncApi.getDeltaSync(dto); return changes == null || changes.needsFullSync ? (null, null) @@ -108,20 +105,13 @@ class AssetService { /// Returns the list of people of the given asset id. // If the server is not reachable `null` is returned. - Future?> getRemotePeopleOfAsset( - String remoteId, - ) async { + Future?> getRemotePeopleOfAsset(String remoteId) async { try { - final AssetResponseDto? dto = - await _apiService.assetsApi.getAssetInfo(remoteId); + final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId); return dto?.people; } catch (error, stack) { - log.severe( - 'Error while getting remote asset info: ${error.toString()}', - error, - stack, - ); + log.severe('Error while getting remote asset info: ${error.toString()}', error, stack); return null; } @@ -135,19 +125,11 @@ class AssetService { String? lastId; // will break on error or once all assets are loaded while (true) { - final dto = AssetFullSyncDto( - limit: chunkSize, - updatedUntil: until, - lastId: lastId, - userId: user.id, - ); + final dto = AssetFullSyncDto(limit: chunkSize, updatedUntil: until, lastId: lastId, userId: user.id); log.fine("Requesting $chunkSize assets from $lastId"); - final List? assets = - await _apiService.syncApi.getFullSyncForUser(dto); + final List? assets = await _apiService.syncApi.getFullSyncForUser(dto); if (assets == null) return null; - log.fine( - "Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}", - ); + log.fine("Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}"); allAssets.addAll(assets.map(Asset.remote)); if (assets.length != chunkSize) break; lastId = assets.last.id; @@ -172,10 +154,9 @@ class AssetService { a.exifInfo = newExif; if (newExif != a.exifInfo) { if (a.isInDb) { - await _assetRepository - .transaction(() => _assetRepository.update(a)); + await _assetRepository.transaction(() => _assetRepository.update(a)); } else { - debugPrint("[loadExif] parameter Asset is not from DB!"); + dPrint(() => "[loadExif] parameter Asset is not from DB!"); } } } @@ -186,10 +167,7 @@ class AssetService { return a; } - Future updateAssets( - List assets, - UpdateAssetDto updateAssetDto, - ) async { + Future updateAssets(List assets, UpdateAssetDto updateAssetDto) async { return await _apiService.assetsApi.updateAssets( AssetBulkUpdateDto( ids: assets.map((e) => e.remoteId!).toList(), @@ -202,10 +180,7 @@ class AssetService { ); } - Future> changeFavoriteStatus( - List assets, - bool isFavorite, - ) async { + Future> changeFavoriteStatus(List assets, bool isFavorite) async { try { await updateAssets(assets, UpdateAssetDto(isFavorite: isFavorite)); @@ -222,24 +197,16 @@ class AssetService { } } - Future> changeArchiveStatus( - List assets, - bool isArchived, - ) async { + Future> changeArchiveStatus(List assets, bool isArchived) async { try { await updateAssets( assets, - UpdateAssetDto( - visibility: - isArchived ? AssetVisibility.archive : AssetVisibility.timeline, - ), + UpdateAssetDto(visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline), ); for (var element in assets) { element.isArchived = isArchived; - element.visibility = isArchived - ? AssetVisibilityEnum.archive - : AssetVisibilityEnum.timeline; + element.visibility = isArchived ? AssetVisibilityEnum.archive : AssetVisibilityEnum.timeline; } await _syncService.upsertAssetsWithExif(assets); @@ -251,20 +218,13 @@ class AssetService { } } - Future?> changeDateTime( - List assets, - String updatedDt, - ) async { + Future?> changeDateTime(List assets, String updatedDt) async { try { - await updateAssets( - assets, - UpdateAssetDto(dateTimeOriginal: updatedDt), - ); + await updateAssets(assets, UpdateAssetDto(dateTimeOriginal: updatedDt)); for (var element in assets) { element.fileCreatedAt = DateTime.parse(updatedDt); - element.exifInfo = element.exifInfo - ?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt)); + element.exifInfo = element.exifInfo?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt)); } await _syncService.upsertAssetsWithExif(assets); @@ -276,24 +236,12 @@ class AssetService { } } - Future?> changeLocation( - List assets, - LatLng location, - ) async { + Future?> changeLocation(List assets, LatLng location) async { try { - await updateAssets( - assets, - UpdateAssetDto( - latitude: location.latitude, - longitude: location.longitude, - ), - ); + await updateAssets(assets, UpdateAssetDto(latitude: location.latitude, longitude: location.longitude)); for (var element in assets) { - element.exifInfo = element.exifInfo?.copyWith( - latitude: location.latitude, - longitude: location.longitude, - ); + element.exifInfo = element.exifInfo?.copyWith(latitude: location.latitude, longitude: location.longitude); } await _syncService.upsertAssetsWithExif(assets); @@ -307,10 +255,8 @@ class AssetService { Future syncUploadedAssetToAlbums() async { try { - final selectedAlbums = - await _backupRepository.getAllBySelection(BackupSelection.select); - final excludedAlbums = - await _backupRepository.getAllBySelection(BackupSelection.exclude); + final selectedAlbums = await _backupRepository.getAllBySelection(BackupSelection.select); + final excludedAlbums = await _backupRepository.getAllBySelection(BackupSelection.exclude); final candidates = await _backupService.buildUploadCandidates( selectedAlbums, @@ -320,18 +266,13 @@ class AssetService { await refreshRemoteAssets(); final owner = _userService.getMyUser(); - final remoteAssets = await _assetRepository.getAll( - ownerId: owner.id, - state: AssetState.merged, - ); + final remoteAssets = await _assetRepository.getAll(ownerId: owner.id, state: AssetState.merged); /// Map Map> assetToAlbums = {}; for (BackupCandidate candidate in candidates) { - final asset = remoteAssets.firstWhereOrNull( - (a) => a.localId == candidate.asset.localId, - ); + final asset = remoteAssets.firstWhereOrNull((a) => a.localId == candidate.asset.localId); if (asset != null) { for (final albumName in candidate.albumNames) { @@ -352,10 +293,7 @@ class AssetService { } } - Future setDescription( - Asset asset, - String newDescription, - ) async { + Future setDescription(Asset asset, String newDescription) async { final remoteAssetId = asset.remoteId; final localExifId = asset.exifInfo?.assetId; @@ -364,10 +302,7 @@ class AssetService { return; } - final result = await _assetApiRepository.update( - remoteAssetId, - description: newDescription, - ); + final result = await _assetApiRepository.update(remoteAssetId, description: newDescription); final description = result.exifInfo?.description; @@ -375,8 +310,7 @@ class AssetService { var exifInfo = await _exifInfoRepository.get(localExifId); if (exifInfo != null) { - await _exifInfoRepository - .update(exifInfo.copyWith(description: description)); + await _exifInfoRepository.update(exifInfo.copyWith(description: description)); } } } @@ -429,22 +363,16 @@ class AssetService { // Delete files from local gallery final candidates = assets.where((asset) => asset.isLocal); - final deletedIds = await _assetMediaRepository - .deleteAll(candidates.map((asset) => asset.localId!).toList()); + final deletedIds = await _assetMediaRepository.deleteAll(candidates.map((asset) => asset.localId!).toList()); // Modify local database by removing the reference to the local assets if (deletedIds.isNotEmpty) { // Delete records from local database - final isarIds = assets - .where((asset) => asset.storage == AssetState.local) - .map((asset) => asset.id) - .toList(); + final isarIds = assets.where((asset) => asset.storage == AssetState.local).map((asset) => asset.id).toList(); await _assetRepository.deleteByIds(isarIds); // Modify Merged asset to be remote only - final updatedAssets = assets - .where((asset) => asset.storage == AssetState.merged) - .map((asset) { + final updatedAssets = assets.where((asset) => asset.storage == AssetState.merged).map((asset) { asset.localId = null; return asset; }).toList(); @@ -454,10 +382,7 @@ class AssetService { } /// Delete assets from the server and unreference from the database - Future deleteRemoteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final candidates = assets.where((a) => a.isRemote); if (candidates.isEmpty) { @@ -465,17 +390,12 @@ class AssetService { } await _apiService.assetsApi.deleteAssets( - AssetBulkDeleteDto( - ids: candidates.map((a) => a.remoteId!).toList(), - force: shouldDeletePermanently, - ), + AssetBulkDeleteDto(ids: candidates.map((a) => a.remoteId!).toList(), force: shouldDeletePermanently), ); /// Update asset info bassed on the deletion type. final payload = shouldDeletePermanently - ? assets - .where((asset) => asset.storage == AssetState.merged) - .map((asset) { + ? assets.where((asset) => asset.storage == AssetState.merged).map((asset) { asset.remoteId = null; asset.visibility = AssetVisibilityEnum.timeline; return asset; @@ -500,10 +420,7 @@ class AssetService { /// Delete assets on both local file system and the server. /// Unreference from the database. - Future deleteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final hasLocal = assets.any((asset) => asset.isLocal); final hasRemote = assets.any((asset) => asset.isRemote); @@ -512,10 +429,7 @@ class AssetService { } if (hasRemote) { - await deleteRemoteAssets( - assets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await deleteRemoteAssets(assets, shouldDeletePermanently: shouldDeletePermanently); } } @@ -533,14 +447,8 @@ class AssetService { return _assetRepository.getMotionAssets(me.id); } - Future setVisibility( - List assets, - AssetVisibilityEnum visibility, - ) async { - await _assetApiRepository.updateVisibility( - assets.map((asset) => asset.remoteId!).toList(), - visibility, - ); + Future setVisibility(List assets, AssetVisibilityEnum visibility) async { + await _assetApiRepository.updateVisibility(assets.map((asset) => asset.remoteId!).toList(), visibility); final updatedAssets = assets.map((asset) { asset.visibility = visibility; diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 0eec253ee1..91c23cac1c 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -8,10 +8,12 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/repositories/auth.repository.dart'; import 'package:immich_mobile/repositories/auth_api.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/network.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -23,6 +25,7 @@ final authServiceProvider = Provider( ref.watch(apiServiceProvider), ref.watch(networkServiceProvider), ref.watch(backgroundSyncProvider), + ref.watch(appSettingsServiceProvider), ), ); @@ -32,7 +35,7 @@ class AuthService { final ApiService _apiService; final NetworkService _networkService; final BackgroundSyncManager _backgroundSyncManager; - + final AppSettingsService _appSettingsService; final _log = Logger("AuthService"); AuthService( @@ -41,6 +44,7 @@ class AuthService { this._apiService, this._networkService, this._backgroundSyncManager, + this._appSettingsService, ); /// Validates the provided server URL by resolving and setting the endpoint. @@ -106,6 +110,8 @@ class AuthService { await clearLocalData().catchError((error, stackTrace) { _log.severe("Error clearing local data", error, stackTrace); }); + + await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false); } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 97acf2941f..33a8e810f1 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -7,7 +7,6 @@ import 'dart:ui' show DartPluginRegistrant, IsolateNameServer, PluginUtilities; import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -29,6 +28,7 @@ import 'package:immich_mobile/services/backup.service.dart'; import 'package:immich_mobile/services/localization.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:path_provider_foundation/path_provider_foundation.dart'; @@ -39,10 +39,8 @@ final backgroundServiceProvider = Provider((ref) => BackgroundService()); /// Background backup service class BackgroundService { static const String _portNameLock = "immichLock"; - static const MethodChannel _foregroundChannel = - MethodChannel('immich/foregroundChannel'); - static const MethodChannel _backgroundChannel = - MethodChannel('immich/backgroundChannel'); + static const MethodChannel _foregroundChannel = MethodChannel('immich/foregroundChannel'); + static const MethodChannel _backgroundChannel = MethodChannel('immich/backgroundChannel'); static const notifyInterval = Duration(milliseconds: 400); bool _isBackgroundInitialized = false; CancellationToken? _cancellationToken; @@ -56,10 +54,11 @@ class BackgroundService { int _assetsToUploadCount = 0; String _lastPrintedDetailContent = ""; String? _lastPrintedDetailTitle; - late final ThrottleProgressUpdate _throttledNotifiy = - ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); bool get isBackgroundInitialized { return _isBackgroundInitialized; @@ -74,10 +73,8 @@ class BackgroundService { Future enableService({bool immediate = false}) async { try { final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!; - final String title = - "backup_background_service_default_notification".tr(); - final bool ok = await _foregroundChannel - .invokeMethod('enable', [callback.toRawHandle(), title, immediate]); + final String title = "backup_background_service_default_notification".tr(); + final bool ok = await _foregroundChannel.invokeMethod('enable', [callback.toRawHandle(), title, immediate]); return ok; } catch (error) { return false; @@ -92,15 +89,12 @@ class BackgroundService { int triggerMaxDelay = 50000, }) async { try { - final bool ok = await _foregroundChannel.invokeMethod( - 'configure', - [ - requireUnmetered, - requireCharging, - triggerUpdateDelay, - triggerMaxDelay, - ], - ); + final bool ok = await _foregroundChannel.invokeMethod('configure', [ + requireUnmetered, + requireCharging, + triggerUpdateDelay, + triggerMaxDelay, + ]); return ok; } catch (error) { return false; @@ -133,8 +127,7 @@ class BackgroundService { return true; } try { - return await _foregroundChannel - .invokeMethod('isIgnoringBatteryOptimizations'); + return await _foregroundChannel.invokeMethod('isIgnoringBatteryOptimizations'); } catch (error) { return false; } @@ -146,10 +139,7 @@ class BackgroundService { } Future?> digestFiles(List paths) { - return _foregroundChannel.invokeListMethod( - "digestFiles", - paths, - ); + return _foregroundChannel.invokeListMethod("digestFiles", paths); } /// Updates the notification shown by the background service @@ -164,30 +154,30 @@ class BackgroundService { }) async { try { if (_isBackgroundInitialized) { - return _backgroundChannel.invokeMethod( - 'updateNotification', - [title, content, progress, max, indeterminate, isDetail, onlyIfFG], - ); + return _backgroundChannel.invokeMethod('updateNotification', [ + title, + content, + progress, + max, + indeterminate, + isDetail, + onlyIfFG, + ]); } } catch (error) { - debugPrint("[_updateNotification] failed to communicate with plugin"); + dPrint(() => "[_updateNotification] failed to communicate with plugin"); } return false; } /// Shows a new priority notification - Future _showErrorNotification({ - required String title, - String? content, - String? individualTag, - }) async { + Future _showErrorNotification({required String title, String? content, String? individualTag}) async { try { if (_isBackgroundInitialized && _errorGracePeriodExceeded) { - return await _backgroundChannel - .invokeMethod('showError', [title, content, individualTag]); + return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]); } } catch (error) { - debugPrint("[_showErrorNotification] failed to communicate with plugin"); + dPrint(() => "[_showErrorNotification] failed to communicate with plugin"); } return false; } @@ -198,9 +188,7 @@ class BackgroundService { return await _backgroundChannel.invokeMethod('clearErrorNotifications'); } } catch (error) { - debugPrint( - "[_clearErrorNotifications] failed to communicate with plugin", - ); + dPrint(() => "[_clearErrorNotifications] failed to communicate with plugin"); } return false; } @@ -208,7 +196,7 @@ class BackgroundService { /// await to ensure this thread (foreground or background) has exclusive access Future acquireLock() async { if (_hasLock) { - debugPrint("WARNING: [acquireLock] called more than once"); + dPrint(() => "WARNING: [acquireLock] called more than once"); return true; } final int lockTime = Timeline.now; @@ -240,8 +228,7 @@ class BackgroundService { final bs = tempRp.asBroadcastStream(); while (_wantsLockTime == lockTime) { other.send(tempSp); - final dynamic answer = await bs.first - .timeout(const Duration(seconds: 3), onTimeout: () => null); + final dynamic answer = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => null); if (_wantsLockTime != lockTime) { break; } @@ -257,8 +244,7 @@ class BackgroundService { } else if (answer == false) { // other isolate is still active } - final dynamic isFinished = await bs.first - .timeout(const Duration(seconds: 3), onTimeout: () => false); + final dynamic isFinished = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => false); if (isFinished == true) { break; } @@ -311,27 +297,24 @@ class BackgroundService { // indefinitely and can run later // Android is fine to wait here until the lock releases final waitForLock = Platform.isIOS - ? acquireLock().timeout( - const Duration(seconds: 5), - onTimeout: () => false, - ) + ? acquireLock().timeout(const Duration(seconds: 5), onTimeout: () => false) : acquireLock(); final bool hasAccess = await waitForLock; if (!hasAccess) { - debugPrint("[_callHandler] could not acquire lock, exiting"); + dPrint(() => "[_callHandler] could not acquire lock, exiting"); return false; } final translationsOk = await loadTranslations(); if (!translationsOk) { - debugPrint("[_callHandler] could not load translations"); + dPrint(() => "[_callHandler] could not load translations"); } final bool ok = await _onAssetsChanged(); return ok; } catch (error) { - debugPrint(error.toString()); + dPrint(() => error.toString()); return false; } finally { releaseLock(); @@ -341,39 +324,30 @@ class BackgroundService { _cancellationToken?.cancel(); return true; default: - debugPrint("Unknown method ${call.method}"); + dPrint(() => "Unknown method ${call.method}"); return false; } } Future _onAssetsChanged() async { - final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final (isar, drift, logDb) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false, listenStoreUpdates: false); final ref = ProviderContainer( overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + driftProvider.overrideWith(driftOverride(drift)), ], ); HttpSSLOptions.apply(); - ref - .read(apiServiceProvider) - .setAccessToken(Store.get(StoreKey.accessToken)); + ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); - if (kDebugMode) { - debugPrint( - "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}", - ); - } + dPrint(() => "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); - final selectedAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAllBySelection(BackupSelection.select); - final excludedAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAllBySelection(BackupSelection.exclude); + final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select); + final excludedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.exclude); if (selectedAlbums.isEmpty) { return true; } @@ -392,9 +366,7 @@ class BackgroundService { final backupAlbums = [...selectedAlbums, ...excludedAlbums]; backupAlbums.sortBy((e) => e.id); - final dbAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAll(sort: BackupAlbumSort.id); + final dbAlbums = await ref.read(backupAlbumRepositoryProvider).getAll(sort: BackupAlbumSort.id); final List toDelete = []; final List toUpsert = []; // stores the most recent `lastBackup` per album but always keeps the `selection` from the most recent DB state @@ -403,9 +375,7 @@ class BackgroundService { backupAlbums, compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id), both: (BackupAlbum a, BackupAlbum b) { - a.lastBackup = a.lastBackup.isAfter(b.lastBackup) - ? a.lastBackup - : b.lastBackup; + a.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; toUpsert.add(a); return true; }, @@ -419,9 +389,7 @@ class BackgroundService { return false; } // Android should check for new assets added while performing backup - } while (Platform.isAndroid && - true == - await _backgroundChannel.invokeMethod("hasContentChanged")); + } while (Platform.isAndroid && true == await _backgroundChannel.invokeMethod("hasContentChanged")); return true; } @@ -432,19 +400,14 @@ class BackgroundService { List excludedAlbums, ) async { _errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService); - final bool notifyTotalProgress = settingsService - .getSetting(AppSettingsEnum.backgroundBackupTotalProgress); - final bool notifySingleProgress = settingsService - .getSetting(AppSettingsEnum.backgroundBackupSingleProgress); + final bool notifyTotalProgress = settingsService.getSetting(AppSettingsEnum.backgroundBackupTotalProgress); + final bool notifySingleProgress = settingsService.getSetting(AppSettingsEnum.backgroundBackupSingleProgress); if (_canceledBySystem) { return false; } - Set toUpload = await backupService.buildUploadCandidates( - selectedAlbums, - excludedAlbums, - ); + Set toUpload = await backupService.buildUploadCandidates(selectedAlbums, excludedAlbums); try { toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); @@ -467,12 +430,7 @@ class BackgroundService { _uploadedAssetsCount = 0; _updateNotification( title: "backup_background_service_in_progress_notification".tr(), - content: notifyTotalProgress - ? formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ) - : null, + content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, progress: 0, max: notifyTotalProgress ? _assetsToUploadCount : 0, indeterminate: !notifyTotalProgress, @@ -486,13 +444,9 @@ class BackgroundService { toUpload, _cancellationToken!, pmProgressHandler: pmProgressHandler, - onSuccess: (result) => _onAssetUploaded( - shouldNotify: notifyTotalProgress, - ), - onProgress: (bytes, totalBytes) => - _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), - onCurrentAsset: (asset) => - _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), + onSuccess: (result) => _onAssetUploaded(shouldNotify: notifyTotalProgress), + onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), + onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), onError: _onBackupError, isBackground: true, ); @@ -507,9 +461,7 @@ class BackgroundService { return ok; } - void _onAssetUploaded({ - bool shouldNotify = false, - }) async { + void _onAssetUploaded({bool shouldNotify = false}) async { if (!shouldNotify) { return; } @@ -527,8 +479,7 @@ class BackgroundService { } void _updateDetailProgress(String? title, int progress, int total) { - final String msg = - total > 0 ? humanReadableBytesProgress(progress, total) : ""; + final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : ""; // only update if message actually differs (to stop many useless notification updates on large assets or slow connections) if (msg != _lastPrintedDetailContent || _lastPrintedDetailTitle != title) { _lastPrintedDetailContent = msg; @@ -548,39 +499,33 @@ class BackgroundService { progress: _uploadedAssetsCount, max: _assetsToUploadCount, title: title, - content: formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ), + content: formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount), ); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { _showErrorNotification( - title: "backup_background_service_upload_failure_notification" - .tr(namedArgs: {'filename': errorAssetInfo.fileName}), + title: "backup_background_service_upload_failure_notification".tr( + namedArgs: {'filename': errorAssetInfo.fileName}, + ), individualTag: errorAssetInfo.id, ); } - void _onSetCurrentBackupAsset( - CurrentUploadAsset currentUploadAsset, { - bool shouldNotify = false, - }) { + void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset, {bool shouldNotify = false}) { if (!shouldNotify) { return; } - _throttledDetailNotify.title = - "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } bool _isErrorGracePeriodExceeded(AppSettingsService appSettingsService) { - final int value = appSettingsService - .getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod); + final int value = appSettingsService.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod); if (value == 0) { return true; } else if (value == 5) { diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 370b64398d..539fd1fbd9 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:cancellation_token_http/http.dart' as http; import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -29,6 +28,7 @@ import 'package:openapi/api.dart'; import 'package:path/path.dart' as p; import 'package:permission_handler/permission_handler.dart' as pm; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; +import 'package:immich_mobile/utils/debug_print.dart'; final backupServiceProvider = Provider( (ref) => BackupService( @@ -69,15 +69,13 @@ class BackupService { try { return await _apiService.assetsApi.getAllUserAssetsByDeviceId(deviceId); } catch (e) { - debugPrint('Error [getDeviceBackupAsset] ${e.toString()}'); + dPrint(() => 'Error [getDeviceBackupAsset] ${e.toString()}'); return null; } } Future _saveDuplicatedAssetIds(List deviceAssetIds) => - _assetRepository.transaction( - () => _assetRepository.upsertDuplicatedAssets(deviceAssetIds), - ); + _assetRepository.transaction(() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds)); /// Get duplicated asset id from database Future> getDuplicatedAssetIds() async { @@ -127,8 +125,7 @@ class BackupService { continue; } - if (useTimeFilter && - localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) { + if (useTimeFilter && localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) { continue; } final List assets; @@ -137,8 +134,8 @@ class BackupService { backupAlbum.id, modifiedFrom: useTimeFilter ? - // subtract 2 seconds to prevent missing assets due to rounding issues - backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) + // subtract 2 seconds to prevent missing assets due to rounding issues + backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) : null, modifiedUntil: useTimeFilter ? now : null, ); @@ -151,9 +148,7 @@ class BackupService { for (final asset in assets) { List albumNames = [localAlbum.name]; - final existingAsset = candidates.firstWhereOrNull( - (candidate) => candidate.asset.localId == asset.localId, - ); + final existingAsset = candidates.firstWhereOrNull((candidate) => candidate.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); @@ -170,17 +165,13 @@ class BackupService { } /// Returns a new list of assets not yet uploaded - Future> removeAlreadyUploadedAssets( - Set candidates, - ) async { + Future> removeAlreadyUploadedAssets(Set candidates) async { if (candidates.isEmpty) { return candidates; } final Set duplicatedAssetIds = await getDuplicatedAssetIds(); - candidates.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + candidates.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (candidates.isEmpty) { return candidates; @@ -189,12 +180,8 @@ class BackupService { final Set existing = {}; try { final String deviceId = Store.get(StoreKey.deviceId); - final CheckExistingAssetsResponseDto? duplicates = - await _apiService.assetsApi.checkExistingAssets( - CheckExistingAssetsDto( - deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), - deviceId: deviceId, - ), + final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets( + CheckExistingAssetsDto(deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), deviceId: deviceId), ); if (duplicates != null) { existing.addAll(duplicates.existingIds); @@ -215,12 +202,13 @@ class BackupService { } Future _checkPermissions() async { - if (Platform.isAndroid && - !(await pm.Permission.accessMediaLocation.status).isGranted) { + if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) { // double check that permission is granted here, to guard against // uploading corrupt assets without EXIF information - _log.warning("Media location permission is not granted. " - "Cannot access original assets for backup."); + _log.warning( + "Media location permission is not granted. " + "Cannot access original assets for backup.", + ); return false; } @@ -236,13 +224,11 @@ class BackupService { /// Upload images before video assets for background tasks /// these are further sorted by using their creation date List _sortPhotosFirst(List candidates) { - return candidates.sorted( - (a, b) { - final cmp = a.asset.type.index - b.asset.type.index; - if (cmp != 0) return cmp; - return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); - }, - ); + return candidates.sorted((a, b) { + final cmp = a.asset.type.index - b.asset.type.index; + if (cmp != 0) return cmp; + return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); + }); } Future backupAsset( @@ -255,8 +241,7 @@ class BackupService { required void Function(CurrentUploadAsset asset) onCurrentAsset, required void Function(ErrorUploadAsset error) onError, }) async { - final bool isIgnoreIcloudAssets = - _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); + final bool isIgnoreIcloudAssets = _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); final shouldSyncAlbums = _appSetting.getSetting(AppSettingsEnum.syncAlbums); final String deviceId = Store.get(StoreKey.deviceId); final String savedEndpoint = Store.get(StoreKey.serverEndpoint); @@ -279,8 +264,7 @@ class BackupService { File? livePhotoFile; try { - final isAvailableLocally = - await asset.local!.isLocallyAvailable(isOrigin: true); + final isAvailableLocally = await asset.local!.isLocallyAvailable(isOrigin: true); // Handle getting files from iCloud if (!isAvailableLocally && Platform.isIOS) { @@ -292,43 +276,32 @@ class BackupService { onCurrentAsset( CurrentUploadAsset( id: asset.localId!, - fileCreatedAt: asset.fileCreatedAt.year == 1970 - ? asset.fileModifiedAt - : asset.fileCreatedAt, + fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt, fileName: asset.fileName, fileType: _getAssetType(asset.type), iCloudAsset: true, ), ); - file = - await asset.local!.loadFile(progressHandler: pmProgressHandler); + file = await asset.local!.loadFile(progressHandler: pmProgressHandler); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.loadFile( - withSubtype: true, - progressHandler: pmProgressHandler, - ); + livePhotoFile = await asset.local!.loadFile(withSubtype: true, progressHandler: pmProgressHandler); } } else { - file = - await asset.local!.originFile.timeout(const Duration(seconds: 5)); + file = await asset.local!.originFile.timeout(const Duration(seconds: 5)); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.originFileWithSubtype - .timeout(const Duration(seconds: 5)); + livePhotoFile = await asset.local!.originFileWithSubtype.timeout(const Duration(seconds: 5)); } } if (file != null) { - String? originalFileName = - await _assetMediaRepository.getOriginalFilename(asset.localId!); + String? originalFileName = await _assetMediaRepository.getOriginalFilename(asset.localId!); originalFileName ??= asset.fileName; if (asset.local!.isLivePhoto) { if (livePhotoFile == null) { - _log.warning( - "Failed to obtain motion part of the livePhoto - $originalFileName", - ); + _log.warning("Failed to obtain motion part of the livePhoto - $originalFileName"); } } @@ -349,10 +322,8 @@ class BackupService { baseRequest.headers.addAll(ApiService.getRequestHeaders()); baseRequest.fields['deviceAssetId'] = asset.localId!; baseRequest.fields['deviceId'] = deviceId; - baseRequest.fields['fileCreatedAt'] = - asset.fileCreatedAt.toUtc().toIso8601String(); - baseRequest.fields['fileModifiedAt'] = - asset.fileModifiedAt.toUtc().toIso8601String(); + baseRequest.fields['fileCreatedAt'] = asset.fileCreatedAt.toUtc().toIso8601String(); + baseRequest.fields['fileModifiedAt'] = asset.fileModifiedAt.toUtc().toIso8601String(); baseRequest.fields['isFavorite'] = asset.isFavorite.toString(); baseRequest.fields['duration'] = asset.duration.toString(); baseRequest.files.add(assetRawUploadData); @@ -360,9 +331,7 @@ class BackupService { onCurrentAsset( CurrentUploadAsset( id: asset.localId!, - fileCreatedAt: asset.fileCreatedAt.year == 1970 - ? asset.fileModifiedAt - : asset.fileCreatedAt, + fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt, fileName: originalFileName, fileType: _getAssetType(asset.type), fileSize: file.lengthSync(), @@ -372,32 +341,24 @@ class BackupService { String? livePhotoVideoId; if (asset.local!.isLivePhoto && livePhotoFile != null) { - livePhotoVideoId = await uploadLivePhotoVideo( - originalFileName, - livePhotoFile, - baseRequest, - cancelToken, - ); + livePhotoVideoId = await uploadLivePhotoVideo(originalFileName, livePhotoFile, baseRequest, cancelToken); } if (livePhotoVideoId != null) { baseRequest.fields['livePhotoVideoId'] = livePhotoVideoId; } - final response = await httpClient.send( - baseRequest, - cancellationToken: cancelToken, - ); + final response = await httpClient.send(baseRequest, cancellationToken: cancelToken); - final responseBody = - jsonDecode(await response.stream.bytesToString()); + final responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { final error = responseBody; final errorMessage = error['message'] ?? error['error']; - debugPrint( - "Error(${error['statusCode']}) uploading ${asset.localId} | $originalFileName | Created on ${asset.fileCreatedAt} | ${error['error']}", + dPrint( + () => + "Error(${error['statusCode']}) uploading ${asset.localId} | $originalFileName | Created on ${asset.fileCreatedAt} | ${error['error']}", ); onError( @@ -434,18 +395,15 @@ class BackupService { ); if (shouldSyncAlbums) { - await _albumService.syncUploadAlbums( - candidate.albumNames, - [responseBody['id'] as String], - ); + await _albumService.syncUploadAlbums(candidate.albumNames, [responseBody['id'] as String]); } } } on http.CancelledException { - debugPrint("Backup was cancelled by the user"); + dPrint(() => "Backup was cancelled by the user"); anyErrors = true; break; } catch (error, stackTrace) { - debugPrint("Error backup asset: ${error.toString()}: $stackTrace"); + dPrint(() => "Error backup asset: ${error.toString()}: $stackTrace"); anyErrors = true; continue; } finally { @@ -454,7 +412,7 @@ class BackupService { await file?.delete(); await livePhotoFile?.delete(); } catch (e) { - debugPrint("ERROR deleting file: ${e.toString()}"); + dPrint(() => "ERROR deleting file: ${e.toString()}"); } } } @@ -476,10 +434,7 @@ class BackupService { if (livePhotoVideoFile == null) { return null; } - final livePhotoTitle = p.setExtension( - originalFileName, - p.extension(livePhotoVideoFile.path), - ); + final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoVideoFile.path)); final fileStream = livePhotoVideoFile.openRead(); final livePhotoRawUploadData = http.MultipartFile( "assetData", @@ -487,28 +442,21 @@ class BackupService { livePhotoVideoFile.lengthSync(), filename: livePhotoTitle, ); - final livePhotoReq = MultipartRequest( - baseRequest.method, - baseRequest.url, - onProgress: baseRequest.onProgress, - ) + final livePhotoReq = MultipartRequest(baseRequest.method, baseRequest.url, onProgress: baseRequest.onProgress) ..headers.addAll(baseRequest.headers) ..fields.addAll(baseRequest.fields); livePhotoReq.files.add(livePhotoRawUploadData); - var response = await httpClient.send( - livePhotoReq, - cancellationToken: cancelToken, - ); + var response = await httpClient.send(livePhotoReq, cancellationToken: cancelToken); var responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { var error = responseBody; - debugPrint( - "Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}", + dPrint( + () => "Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}", ); } @@ -516,20 +464,16 @@ class BackupService { } String _getAssetType(AssetType assetType) => switch (assetType) { - AssetType.audio => "AUDIO", - AssetType.image => "IMAGE", - AssetType.video => "VIDEO", - AssetType.other => "OTHER", - }; + AssetType.audio => "AUDIO", + AssetType.image => "IMAGE", + AssetType.video => "VIDEO", + AssetType.other => "OTHER", + }; } class MultipartRequest extends http.MultipartRequest { /// Creates a new [MultipartRequest]. - MultipartRequest( - super.method, - super.url, { - required this.onProgress, - }); + MultipartRequest(super.method, super.url, {required this.onProgress}); final void Function(int bytes, int totalBytes) onProgress; diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 6d6884eb00..94c4721cca 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -37,11 +37,7 @@ class BackupVerificationService { /// Returns at most [limit] assets that were backed up without exif Future> findWronglyBackedUpAssets({int limit = 100}) async { final owner = _userService.getMyUser().id; - final List onlyLocal = await _assetRepository.getAll( - ownerId: owner, - state: AssetState.local, - limit: limit, - ); + final List onlyLocal = await _assetRepository.getAll(ownerId: owner, state: AssetState.local, limit: limit); final List remoteMatches = await _assetRepository.getMatches( assets: onlyLocal, ownerId: owner, @@ -75,41 +71,32 @@ class BackupVerificationService { if (deleteCandidates.length > 10) { // performs 2 checks in parallel for a nice speedup final half = deleteCandidates.length ~/ 2; - final lower = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(0, half), - originals: originals.slice(0, half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); - final upper = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(half), - originals: originals.slice(half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + final lower = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(0, half), + originals: originals.slice(0, half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); + final upper = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(half), + originals: originals.slice(half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); toDelete = await lower + await upper; } else { - toDelete = await compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates, - originals: originals, - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + toDelete = await compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates, + originals: originals, + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); } return toDelete; } @@ -122,34 +109,27 @@ class BackupVerificationService { String endpoint, RootIsolateToken rootIsolateToken, FileMediaRepository fileMediaRepository, - }) tuple, + }) + tuple, ) async { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken); - final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db); + final (isar, drift, logDb) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDb); await tuple.fileMediaRepository.enableBackgroundAccess(); final ApiService apiService = ApiService(); apiService.setEndpoint(tuple.endpoint); apiService.setAccessToken(tuple.auth); for (int i = 0; i < tuple.deleteCandidates.length; i++) { - if (await _compareAssets( - tuple.deleteCandidates[i], - tuple.originals[i], - apiService, - )) { + if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { result.add(tuple.deleteCandidates[i]); } } return result; } - static Future _compareAssets( - Asset remote, - Asset local, - ApiService apiService, - ) async { + static Future _compareAssets(Asset remote, Asset local, ApiService apiService) async { if (remote.checksum == local.checksum) return false; ExifInfo? exif = remote.exifInfo; if (exif != null && exif.latitude != null) return false; @@ -169,10 +149,7 @@ class BackupVerificationService { latLng.latitude != null && (remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) || remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) || - _sameExceptTimeZone( - remote.fileCreatedAt, - local.fileCreatedAt, - ))) { + _sameExceptTimeZone(remote.fileCreatedAt, local.fileCreatedAt))) { if (remote.type == AssetType.video) { // it's very unlikely that a video of same length, filesize, name // and date is wrong match. Cannot easily compare videos anyway @@ -181,10 +158,8 @@ class BackupVerificationService { // for images: make sure they are pixel-wise identical // (skip first few KBs containing metadata) - final Uint64List localImage = - _fakeDecodeImg(await file.readAsBytes()); - final res = await apiService.assetsApi - .downloadAssetWithHttpInfo(remote.remoteId!); + final Uint64List localImage = _fakeDecodeImg(await file.readAsBytes()); + final res = await apiService.assetsApi.downloadAssetWithHttpInfo(remote.remoteId!); final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes); final eq = const ListEquality().equals(remoteImage, localImage); @@ -198,9 +173,7 @@ class BackupVerificationService { static Uint64List _fakeDecodeImg(Uint8List bytes) { const headerLength = 131072; // assume header is at most 128 KB - final start = bytes.length < headerLength * 2 - ? (bytes.length ~/ (4 * 8)) * 8 - : headerLength; + final start = bytes.length < headerLength * 2 ? (bytes.length ~/ (4 * 8)) * 8 : headerLength; return bytes.buffer.asUint64List(start); } diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index e97a370967..6226781919 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -1,12 +1,23 @@ import 'package:auto_route/auto_route.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/asset.service.dart' as beta_asset_service; +import 'package:immich_mobile/domain/services/memory.service.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/asset.service.dart'; import 'package:immich_mobile/services/memory.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; final deepLinkServiceProvider = Provider( (ref) => DeepLinkService( @@ -15,32 +26,60 @@ final deepLinkServiceProvider = Provider( ref.watch(albumServiceProvider), ref.watch(currentAssetProvider.notifier), ref.watch(currentAlbumProvider.notifier), + // Below is used for beta timeline + ref.watch(timelineFactoryProvider), + ref.watch(beta_asset_provider.assetServiceProvider), + ref.watch(currentRemoteAlbumProvider.notifier), + ref.watch(remoteAlbumServiceProvider), + ref.watch(driftMemoryServiceProvider), ), ); class DeepLinkService { + /// TODO: Remove this when beta is default final MemoryService _memoryService; final AssetService _assetService; final AlbumService _albumService; final CurrentAsset _currentAsset; final CurrentAlbum _currentAlbum; + /// Used for beta timeline + final TimelineFactory _betaTimelineFactory; + final beta_asset_service.AssetService _betaAssetService; + final CurrentAlbumNotifier _betaCurrentAlbumNotifier; + final RemoteAlbumService _betaRemoteAlbumService; + final DriftMemoryService _betaMemoryServiceProvider; + const DeepLinkService( this._memoryService, this._assetService, this._albumService, this._currentAsset, this._currentAlbum, + this._betaTimelineFactory, + this._betaAssetService, + this._betaCurrentAlbumNotifier, + this._betaRemoteAlbumService, + this._betaMemoryServiceProvider, ); - Future handleScheme(PlatformDeepLink link, bool isColdStart) async { + DeepLink _handleColdStart(PageRouteInfo route, bool isColdStart) { + return DeepLink([ + // we need something to segue back to if the app was cold started + // TODO: use MainTimelineRoute this when beta is default + if (isColdStart) (Store.isBetaTimelineEnabled) ? const TabShellRoute() : const PhotosRoute(), + route, + ]); + } + + Future handleScheme(PlatformDeepLink link, WidgetRef ref, bool isColdStart) async { // get everything after the scheme, since Uri cannot parse path final intent = link.uri.host; final queryParams = link.uri.queryParameters; PageRouteInfo? deepLinkRoute = switch (intent) { "memory" => await _buildMemoryDeepLink(queryParams['id'] ?? ''), - "asset" => await _buildAssetDeepLink(queryParams['id'] ?? ''), + "asset" => await _buildAssetDeepLink(queryParams['id'] ?? '', ref), "album" => await _buildAlbumDeepLink(queryParams['id'] ?? ''), _ => null, }; @@ -54,28 +93,20 @@ class DeepLinkService { return DeepLink.none; } - return DeepLink([ - // we need something to segue back to if the app was cold started - if (isColdStart) const PhotosRoute(), - deepLinkRoute, - ]); + return _handleColdStart(deepLinkRoute, isColdStart); } - Future handleMyImmichApp( - PlatformDeepLink link, - bool isColdStart, - ) async { + Future handleMyImmichApp(PlatformDeepLink link, WidgetRef ref, bool isColdStart) async { final path = link.uri.path; - const uuidRegex = - r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; final assetRegex = RegExp('/photos/($uuidRegex)'); final albumRegex = RegExp('/albums/($uuidRegex)'); PageRouteInfo? deepLinkRoute; if (assetRegex.hasMatch(path)) { final assetId = assetRegex.firstMatch(path)?.group(1) ?? ''; - deepLinkRoute = await _buildAssetDeepLink(assetId); + deepLinkRoute = await _buildAssetDeepLink(assetId, ref); } else if (albumRegex.hasMatch(path)) { final albumId = albumRegex.firstMatch(path)?.group(1) ?? ''; deepLinkRoute = await _buildAlbumDeepLink(albumId); @@ -87,49 +118,73 @@ class DeepLinkService { return DeepLink.none; } - return DeepLink([ - // we need something to segue back to if the app was cold started - if (isColdStart) const PhotosRoute(), - deepLinkRoute, - ]); + return _handleColdStart(deepLinkRoute, isColdStart); } Future _buildMemoryDeepLink(String memoryId) async { - final memory = await _memoryService.getMemoryById(memoryId); + if (Store.isBetaTimelineEnabled) { + final memory = await _betaMemoryServiceProvider.get(memoryId); - if (memory == null) { - return null; + if (memory == null) { + return null; + } + + return DriftMemoryRoute(memories: [memory], memoryIndex: 0); + } else { + // TODO: Remove this when beta is default + final memory = await _memoryService.getMemoryById(memoryId); + + if (memory == null) { + return null; + } + + return MemoryRoute(memories: [memory], memoryIndex: 0); } - - return MemoryRoute(memories: [memory], memoryIndex: 0); } - Future _buildAssetDeepLink(String assetId) async { - final asset = await _assetService.getAssetByRemoteId(assetId); - if (asset == null) { - return null; + Future _buildAssetDeepLink(String assetId, WidgetRef ref) async { + if (Store.isBetaTimelineEnabled) { + final asset = await _betaAssetService.getRemoteAsset(assetId); + if (asset == null) { + return null; + } + + AssetViewer.setAsset(ref, asset); + return AssetViewerRoute(initialIndex: 0, timelineService: _betaTimelineFactory.fromAssets([asset])); + } else { + // TODO: Remove this when beta is default + final asset = await _assetService.getAssetByRemoteId(assetId); + if (asset == null) { + return null; + } + + _currentAsset.set(asset); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); + + return GalleryViewerRoute(renderList: renderList, initialIndex: 0, heroOffset: 0, showStack: true); } - - _currentAsset.set(asset); - final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); - - return GalleryViewerRoute( - renderList: renderList, - initialIndex: 0, - heroOffset: 0, - showStack: true, - ); } Future _buildAlbumDeepLink(String albumId) async { - final album = await _albumService.getAlbumByRemoteId(albumId); + if (Store.isBetaTimelineEnabled) { + final album = await _betaRemoteAlbumService.get(albumId); - if (album == null) { - return null; + if (album == null) { + return null; + } + + _betaCurrentAlbumNotifier.setAlbum(album); + return RemoteAlbumRoute(album: album); + } else { + // TODO: Remove this when beta is default + final album = await _albumService.getAlbumByRemoteId(albumId); + + if (album == null) { + return null; + } + + _currentAlbum.set(album); + return AlbumViewerRoute(albumId: album.id); } - - _currentAlbum.set(album); - - return AlbumViewerRoute(albumId: album.id); } } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index 98f1765d04..7d2cf01b7c 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -10,14 +11,10 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( - (ref) => DownloadService( - ref.watch(fileMediaRepositoryProvider), - ref.watch(downloadRepositoryProvider), - ), + (ref) => DownloadService(ref.watch(fileMediaRepositoryProvider), ref.watch(downloadRepositoryProvider)), ); class DownloadService { @@ -29,14 +26,10 @@ class DownloadService { void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; void Function(TaskProgressUpdate)? onTaskProgress; - DownloadService( - this._fileMediaRepository, - this._downloadRepository, - ) { + DownloadService(this._fileMediaRepository, this._downloadRepository) { _downloadRepository.onImageDownloadStatus = _onImageDownloadCallback; _downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback; - _downloadRepository.onLivePhotoDownloadStatus = - _onLivePhotoDownloadCallback; + _downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback; _downloadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -83,11 +76,7 @@ class DownloadService { final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; final file = File(filePath); try { - final Asset? resultAsset = await _fileMediaRepository.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + final Asset? resultAsset = await _fileMediaRepository.saveVideo(file, title: title, relativePath: relativePath); return resultAsset != null; } catch (error, stack) { _log.severe("Error saving video", error, stack); @@ -99,19 +88,14 @@ class DownloadService { } } - Future saveLivePhotos( - Task task, - String livePhotosId, - ) async { + Future saveLivePhotos(Task task, String livePhotosId) async { final records = await _downloadRepository.getLiveVideoTasks(); if (records.length < 2) { return false; } - final imageRecord = - _findTaskRecord(records, livePhotosId, LivePhotosPart.image); - final videoRecord = - _findTaskRecord(records, livePhotosId, LivePhotosPart.video); + final imageRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.image); + final videoRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.video); final imageFilePath = await imageRecord.task.filePath(); final videoFilePath = await videoRecord.task.filePath(); @@ -126,8 +110,7 @@ class DownloadService { } on PlatformException catch (error, stack) { // Handle saving MotionPhotos on iOS if (error.code == 'PHPhotosErrorDomain (-1)') { - final result = await _fileMediaRepository - .saveImageWithFile(imageFilePath, title: task.filename); + final result = await _fileMediaRepository.saveImageWithFile(imageFilePath, title: task.filename); return result != null; } _log.severe("Error saving live photo", error, stack); @@ -146,10 +129,7 @@ class DownloadService { await videoFile.delete(); } - await _downloadRepository.deleteRecordsWithIds([ - imageRecord.task.taskId, - videoRecord.task.taskId, - ]); + await _downloadRepository.deleteRecordsWithIds([imageRecord.task.taskId, videoRecord.task.taskId]); } } @@ -158,8 +138,7 @@ class DownloadService { } Future> downloadAll(List assets) async { - return await _downloadRepository - .downloadAll(assets.expand(_createDownloadTasks).toList()); + return await _downloadRepository.downloadAll(assets.expand(_createDownloadTasks).toList()); } Future download(Asset asset) async { @@ -173,22 +152,14 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: downloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.image, - id: asset.remoteId!, - ).toJson(), + group: kDownloadGroupLivePhoto, + metadata: LivePhotosMetadata(part: LivePhotosPart.image, id: asset.remoteId!).toJson(), ), _buildDownloadTask( asset.livePhotoVideoId!, - asset.fileName - .toUpperCase() - .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), - group: downloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.video, - id: asset.remoteId!, - ).toJson(), + asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), + group: kDownloadGroupLivePhoto, + metadata: LivePhotosMetadata(part: LivePhotosPart.video, id: asset.remoteId!).toJson(), ), ]; } @@ -201,17 +172,12 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: asset.isImage ? downloadGroupImage : downloadGroupVideo, + group: asset.isImage ? kDownloadGroupImage : kDownloadGroupVideo, ), ]; } - DownloadTask _buildDownloadTask( - String id, - String filename, { - String? group, - String? metadata, - }) { + DownloadTask _buildDownloadTask(String id, String filename, {String? group, String? metadata}) { final path = r'/assets/{id}/original'.replaceAll('{id}', id); final serverEndpoint = Store.get(StoreKey.serverEndpoint); final headers = ApiService.getRequestHeaders(); @@ -228,11 +194,7 @@ class DownloadService { } } -TaskRecord _findTaskRecord( - List records, - String livePhotosId, - LivePhotosPart part, -) { +TaskRecord _findTaskRecord(List records, String livePhotosId, LivePhotosPart part) { return records.firstWhere((record) { final metadata = LivePhotosMetadata.fromJson(record.task.metaData); return metadata.id == livePhotosId && metadata.part == part; diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart deleted file mode 100644 index 2f51c261fb..0000000000 --- a/mobile/lib/services/drift_backup.service.dart +++ /dev/null @@ -1,293 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:background_downloader/background_downloader.dart'; -import 'package:flutter/material.dart'; -import 'package:immich_mobile/constants/constants.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; -import 'package:immich_mobile/services/upload.service.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as p; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -final driftBackupServiceProvider = Provider( - (ref) => DriftBackupService( - ref.watch(backupRepositoryProvider), - ref.watch(storageRepositoryProvider), - ref.watch(uploadServiceProvider), - ref.watch(localAssetRepository), - ), -); - -class DriftBackupService { - DriftBackupService( - this._backupRepository, - this._storageRepository, - this._uploadService, - this._localAssetRepository, - ) { - _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); - } - - final DriftBackupRepository _backupRepository; - final StorageRepository _storageRepository; - final DriftLocalAssetRepository _localAssetRepository; - final UploadService _uploadService; - final _log = Logger("DriftBackupService"); - - bool shouldCancel = false; - - Future getTotalCount() { - return _backupRepository.getTotalCount(); - } - - Future getRemainderCount() { - return _backupRepository.getRemainderCount(); - } - - Future getBackupCount() { - return _backupRepository.getBackupCount(); - } - - Future backup( - void Function(EnqueueStatus status) onEnqueueTasks, - ) async { - shouldCancel = false; - - final candidates = await _backupRepository.getCandidates(); - if (candidates.isEmpty) { - return; - } - - const batchSize = 100; - int count = 0; - for (int i = 0; i < candidates.length; i += batchSize) { - if (shouldCancel) { - break; - } - - final batch = candidates.skip(i).take(batchSize).toList(); - - List tasks = []; - for (final asset in batch) { - final task = await _getUploadTask(asset); - if (task != null) { - tasks.add(task); - } - } - - if (tasks.isNotEmpty && !shouldCancel) { - count += tasks.length; - _uploadService.enqueueTasks(tasks); - - onEnqueueTasks( - EnqueueStatus( - enqueueCount: count, - totalCount: candidates.length, - ), - ); - } - } - } - - void _handleTaskStatusUpdate(TaskStatusUpdate update) { - switch (update.status) { - case TaskStatus.complete: - _handleLivePhoto(update); - break; - - default: - break; - } - } - - Future _handleLivePhoto(TaskStatusUpdate update) async { - try { - if (update.task.metaData.isEmpty || update.task.metaData == '') { - return; - } - - final metadata = UploadTaskMetadata.fromJson(update.task.metaData); - if (!metadata.isLivePhotos) { - return; - } - - if (update.responseBody == null || update.responseBody!.isEmpty) { - return; - } - final response = jsonDecode(update.responseBody!); - - final localAsset = - await _localAssetRepository.getById(metadata.localAssetId); - if (localAsset == null) { - return; - } - - final uploadTask = await _getLivePhotoUploadTask( - localAsset, - response['id'] as String, - ); - - if (uploadTask == null) { - return; - } - - _uploadService.enqueueTasks([uploadTask]); - } catch (error, stackTrace) { - _log.severe("Error handling live photo upload task", error, stackTrace); - debugPrint("Error handling live photo upload task: $error $stackTrace"); - } - } - - Future _getUploadTask(LocalAsset asset) async { - final entity = await _storageRepository.getAssetEntityForAsset(asset); - if (entity == null) { - return null; - } - - File? file; - - /// iOS LivePhoto has two files: a photo and a video. - /// They are uploaded separately, with video file being upload first, then returned with the assetId - /// The assetId is then used as a metadata for the photo file upload task. - /// - /// We implement two separate upload groups for this, the normal one for the video file - /// and the higher priority group for the photo file because the video file is already uploaded. - /// - /// The cancel operation will only cancel the video group (normal group), the photo group will not - /// be touched, as the video file is already uploaded. - - if (entity.isLivePhoto) { - file = await _storageRepository.getMotionFileForAsset(asset); - } else { - file = await _storageRepository.getFileForAsset(asset.id); - } - - if (file == null) { - return null; - } - - final originalFileName = entity.isLivePhoto - ? p.setExtension( - asset.name, - p.extension(file.path), - ) - : asset.name; - - String metadata = UploadTaskMetadata( - localAssetId: asset.id, - isLivePhotos: entity.isLivePhoto, - livePhotoVideoId: '', - ).toJson(); - - return _uploadService.buildUploadTask( - file, - originalFileName: originalFileName, - deviceAssetId: asset.id, - metadata: metadata, - group: kBackupGroup, - ); - } - - Future _getLivePhotoUploadTask( - LocalAsset asset, - String livePhotoVideoId, - ) async { - final entity = await _storageRepository.getAssetEntityForAsset(asset); - if (entity == null) { - return null; - } - - final file = await _storageRepository.getFileForAsset(asset.id); - if (file == null) { - return null; - } - - final fields = { - 'livePhotoVideoId': livePhotoVideoId, - }; - - return _uploadService.buildUploadTask( - file, - originalFileName: asset.name, - deviceAssetId: asset.id, - fields: fields, - group: kBackupLivePhotoGroup, - priority: 0, // Highest priority to get upload immediately - ); - } - - Future cancel() async { - shouldCancel = true; - await _uploadService.cancelAllForGroup(kBackupGroup); - } -} - -class UploadTaskMetadata { - final String localAssetId; - final bool isLivePhotos; - final String livePhotoVideoId; - - const UploadTaskMetadata({ - required this.localAssetId, - required this.isLivePhotos, - required this.livePhotoVideoId, - }); - - UploadTaskMetadata copyWith({ - String? localAssetId, - bool? isLivePhotos, - String? livePhotoVideoId, - }) { - return UploadTaskMetadata( - localAssetId: localAssetId ?? this.localAssetId, - isLivePhotos: isLivePhotos ?? this.isLivePhotos, - livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, - ); - } - - Map toMap() { - return { - 'localAssetId': localAssetId, - 'isLivePhotos': isLivePhotos, - 'livePhotoVideoId': livePhotoVideoId, - }; - } - - factory UploadTaskMetadata.fromMap(Map map) { - return UploadTaskMetadata( - localAssetId: map['localAssetId'] as String, - isLivePhotos: map['isLivePhotos'] as bool, - livePhotoVideoId: map['livePhotoVideoId'] as String, - ); - } - - String toJson() => json.encode(toMap()); - - factory UploadTaskMetadata.fromJson(String source) => - UploadTaskMetadata.fromMap(json.decode(source) as Map); - - @override - String toString() => - 'UploadTaskMetadata(localAssetId: $localAssetId, isLivePhotos: $isLivePhotos, livePhotoVideoId: $livePhotoVideoId)'; - - @override - bool operator ==(covariant UploadTaskMetadata other) { - if (identical(this, other)) return true; - - return other.localAssetId == localAssetId && - other.isLivePhotos == isLivePhotos && - other.livePhotoVideoId == livePhotoVideoId; - } - - @override - int get hashCode => - localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode; -} diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index 8ffead40fa..fe7358fce6 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -8,10 +8,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; class EntityService { final AssetRepository _assetRepository; final IsarUserRepository _isarUserRepository; - const EntityService( - this._assetRepository, - this._isarUserRepository, - ); + const EntityService(this._assetRepository, this._isarUserRepository); Future fillAlbumWithDatabaseEntities(Album album) async { final ownerId = album.ownerId; @@ -20,25 +17,21 @@ class EntityService { final user = await _isarUserRepository.getByUserId(ownerId); album.owner.value = user == null ? null : User.fromDto(user); } - final thumbnailAssetId = - album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId; + final thumbnailAssetId = album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId; if (thumbnailAssetId != null) { // set thumbnail with asset from database - album.thumbnail.value = - await _assetRepository.getByRemoteId(thumbnailAssetId); + album.thumbnail.value = await _assetRepository.getByRemoteId(thumbnailAssetId); } if (album.remoteUsers.isNotEmpty) { // replace all users with users from database - final users = await _isarUserRepository - .getByUserIds(album.remoteUsers.map((user) => user.id).toList()); + final users = await _isarUserRepository.getByUserIds(album.remoteUsers.map((user) => user.id).toList()); album.sharedUsers.clear(); album.sharedUsers.addAll(users.nonNulls.map(User.fromDto)); album.shared = true; } if (album.remoteAssets.isNotEmpty) { // replace all assets with assets from database - final assets = await _assetRepository - .getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!)); + final assets = await _assetRepository.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!)); album.assets.clear(); album.assets.addAll(assets); } @@ -47,8 +40,5 @@ class EntityService { } final entityServiceProvider = Provider( - (ref) => EntityService( - ref.watch(assetRepositoryProvider), - ref.watch(userRepositoryProvider), - ), + (ref) => EntityService(ref.watch(assetRepositoryProvider), ref.watch(userRepositoryProvider)), ); diff --git a/mobile/lib/services/etag.service.dart b/mobile/lib/services/etag.service.dart index 4b6f2279ed..00eb83fcea 100644 --- a/mobile/lib/services/etag.service.dart +++ b/mobile/lib/services/etag.service.dart @@ -1,8 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/etag.repository.dart'; -final etagServiceProvider = - Provider((ref) => ETagService(ref.watch(etagRepositoryProvider))); +final etagServiceProvider = Provider((ref) => ETagService(ref.watch(etagRepositoryProvider))); class ETagService { final ETagRepository _eTagRepository; diff --git a/mobile/lib/services/exif.service.dart b/mobile/lib/services/exif.service.dart index 4d21614ec5..57f793b21e 100644 --- a/mobile/lib/services/exif.service.dart +++ b/mobile/lib/services/exif.service.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; -final exifServiceProvider = - Provider((ref) => ExifService(ref.watch(exifRepositoryProvider))); +final exifServiceProvider = Provider((ref) => ExifService(ref.watch(exifRepositoryProvider))); class ExifService { final IsarExifRepository _exifInfoRepository; diff --git a/mobile/lib/services/folder.service.dart b/mobile/lib/services/folder.service.dart index 5b97b475b2..91fb455110 100644 --- a/mobile/lib/services/folder.service.dart +++ b/mobile/lib/services/folder.service.dart @@ -6,9 +6,7 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; import 'package:immich_mobile/repositories/folder_api.repository.dart'; import 'package:logging/logging.dart'; -final folderServiceProvider = Provider( - (ref) => FolderService(ref.watch(folderApiRepositoryProvider)), -); +final folderServiceProvider = Provider((ref) => FolderService(ref.watch(folderApiRepositoryProvider))); class FolderService { final FolderApiRepository _folderApiRepository; @@ -30,15 +28,13 @@ class FolderService { fullPath = '/$fullPath'; } - List segments = fullPath.split('/') - ..removeWhere((s) => s.isEmpty); + List segments = fullPath.split('/')..removeWhere((s) => s.isEmpty); String currentPath = ''; for (int i = 0; i < segments.length; i++) { String parentPath = currentPath.isEmpty ? '_root_' : currentPath; - currentPath = - i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}'; + currentPath = i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}'; if (!folderMap.containsKey(parentPath)) { folderMap[parentPath] = []; @@ -46,35 +42,23 @@ class FolderService { if (!folderMap[parentPath]!.any((f) => f.name == segments[i])) { folderMap[parentPath]!.add( - RecursiveFolder( - path: parentPath == '_root_' ? '' : parentPath, - name: segments[i], - subfolders: [], - ), + RecursiveFolder(path: parentPath == '_root_' ? '' : parentPath, name: segments[i], subfolders: []), ); // Sort folders based on order parameter folderMap[parentPath]!.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), + (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), ); } } } void attachSubfolders(RecursiveFolder folder) { - String fullPath = folder.path.isEmpty - ? '/${folder.name}' - : '${folder.path}/${folder.name}'; + String fullPath = folder.path.isEmpty ? '/${folder.name}' : '${folder.path}/${folder.name}'; if (folderMap.containsKey(fullPath)) { folder.subfolders.addAll(folderMap[fullPath]!); // Sort subfolders based on order parameter - folder.subfolders.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), - ); + folder.subfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var subfolder in folder.subfolders) { attachSubfolders(subfolder); } @@ -83,30 +67,19 @@ class FolderService { List rootSubfolders = folderMap['_root_'] ?? []; // Sort root subfolders based on order parameter - rootSubfolders.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), - ); + rootSubfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var folder in rootSubfolders) { attachSubfolders(folder); } - return RootFolder( - subfolders: rootSubfolders, - path: '/', - ); + return RootFolder(subfolders: rootSubfolders, path: '/'); } - Future> getFolderAssets( - RootFolder folder, - SortOrder order, - ) async { + Future> getFolderAssets(RootFolder folder, SortOrder order) async { try { if (folder is RecursiveFolder) { - String fullPath = - folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; + String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; fullPath = fullPath[0] == '/' ? fullPath.substring(1) : fullPath; var result = await _folderApiRepository.getAssetsForPath(fullPath); @@ -121,11 +94,7 @@ class FolderService { final result = await _folderApiRepository.getAssetsForPath('/'); return result; } catch (e, stack) { - _log.severe( - "Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", - e, - stack, - ); + _log.severe("Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", e, stack); return []; } } diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index 6d6646fe50..dcf7685237 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -41,11 +41,7 @@ class GCastService { void Function(CastState)? onCastState; - GCastService( - this._gCastRepository, - this._sessionsApiService, - this._assetApiRepository, - ) { + GCastService(this._gCastRepository, this._sessionsApiService, this._assetApiRepository) { _gCastRepository.onCastStatus = _onCastStatusCallback; _gCastRepository.onCastMessage = _onCastMessageCallback; } @@ -71,8 +67,7 @@ class GCastService { } void _handleMediaStatus(Map message) { - final statusList = - (message['status'] as List).whereType>().toList(); + final statusList = (message['status'] as List).whereType>().toList(); if (statusList.isEmpty) { return; @@ -101,9 +96,7 @@ class GCastService { } if (status["media"] != null && status["media"]["duration"] != null) { - final duration = Duration( - milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt(), - ); + final duration = Duration(milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt()); onDuration?.call(duration); } @@ -112,8 +105,7 @@ class GCastService { } if (status["currentTime"] != null) { - final currentTime = - Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt()); + final currentTime = Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt()); onCurrentTime?.call(currentTime); } } @@ -150,8 +142,7 @@ class GCastService { // we want to make sure we have at least 10 seconds remaining in the session // this is to account for network latency and other delays when sending the request - final bufferedExpiration = - tokenExpiration.subtract(const Duration(seconds: 10)); + final bufferedExpiration = tokenExpiration.subtract(const Duration(seconds: 10)); return bufferedExpiration.isAfter(DateTime.now()); } @@ -173,16 +164,10 @@ class GCastService { } final unauthenticatedUrl = asset.isVideo - ? getPlaybackUrlForRemoteId( - asset.id, - ) - : getThumbnailUrlForRemoteId( - asset.id, - type: AssetMediaSize.fullsize, - ); + ? getPlaybackUrlForRemoteId(asset.id) + : getThumbnailUrlForRemoteId(asset.id, type: AssetMediaSize.fullsize); - final authenticatedURL = - "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; + final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; // get image mime type final mimeType = await _assetApiRepository.getAssetMIMEType(asset.id); @@ -210,8 +195,7 @@ class GCastService { _mediaStatusPollingTimer?.cancel(); if (asset.isVideo) { - _mediaStatusPollingTimer = - Timer.periodic(const Duration(milliseconds: 500), (timer) { + _mediaStatusPollingTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { if (isConnected) { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "GET_STATUS", @@ -225,17 +209,11 @@ class GCastService { } void play() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PLAY", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PLAY", "mediaSessionId": _sessionId}); } void pause() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PAUSE", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PAUSE", "mediaSessionId": _sessionId}); } void seekTo(Duration position) { @@ -247,10 +225,7 @@ class GCastService { } void stop() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "STOP", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "STOP", "mediaSessionId": _sessionId}); _mediaStatusPollingTimer?.cancel(); currentAssetId = null; @@ -263,18 +238,13 @@ class GCastService { final dests = await _gCastRepository.listDestinations(); return dests - .map( - (device) => ( - device.extras["fn"] ?? "Google Cast", - CastDestinationType.googleCast, - device - ), - ) + .map((device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device)) .where((device) { - final caString = device.$3.extras["ca"]; - final caNumber = int.tryParse(caString ?? "0") ?? 0; + final caString = device.$3.extras["ca"]; + final caNumber = int.tryParse(caString ?? "0") ?? 0; - return isDisplay(caNumber); - }).toList(growable: false); + return isDisplay(caNumber); + }) + .toList(growable: false); } } diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart index f0554bf00b..9d1f4e51e8 100644 --- a/mobile/lib/services/hash.service.dart +++ b/mobile/lib/services/hash.service.dart @@ -16,9 +16,10 @@ class HashService { required IsarDeviceAssetRepository deviceAssetRepository, required BackgroundService backgroundService, this.batchSizeLimit = kBatchHashSizeLimit, - this.batchFileLimit = kBatchHashFileLimit, - }) : _deviceAssetRepository = deviceAssetRepository, - _backgroundService = backgroundService; + int? batchFileLimit, + }) : _deviceAssetRepository = deviceAssetRepository, + _backgroundService = backgroundService, + batchFileLimit = batchFileLimit ?? kBatchHashFileLimit; final IsarDeviceAssetRepository _deviceAssetRepository; final BackgroundService _backgroundService; @@ -33,9 +34,7 @@ class HashService { assets.sort(Asset.compareByLocalId); // Get and sort DB entries - guaranteed to be a subset of assets - final hashesInDB = await _deviceAssetRepository.getByIds( - assets.map((a) => a.localId!).toList(), - ); + final hashesInDB = await _deviceAssetRepository.getByIds(assets.map((a) => a.localId!).toList()); hashesInDB.sort((a, b) => a.assetId.compareTo(b.assetId)); int dbIndex = 0; @@ -60,9 +59,7 @@ class HashService { matchingDbEntry.hash.isNotEmpty && matchingDbEntry.modifiedTime.isAtSameMomentAs(asset.fileModifiedAt)) { // Reuse the existing hash - hashedAssets.add( - asset.copyWith(checksum: base64.encode(matchingDbEntry.hash)), - ); + hashedAssets.add(asset.copyWith(checksum: base64.encode(matchingDbEntry.hash))); continue; } @@ -125,10 +122,7 @@ class HashService { /// Processes a batch of files and returns a list of successfully hashed assets after saving /// them in [DeviceAssetToHash] for future retrieval - Future> _processBatch( - List<_AssetPath> toBeHashed, - List toBeDeleted, - ) async { + Future> _processBatch(List<_AssetPath> toBeHashed, List toBeDeleted) async { _log.info("Hashing ${toBeHashed.length} files"); final hashes = await _hashFiles(toBeHashed.map((e) => e.path).toList()); assert( @@ -143,13 +137,7 @@ class HashService { final asset = toBeHashed.elementAtOrNull(index)?.asset; if (asset != null && hash?.length == 20) { hashedAssets.add(asset.copyWith(checksum: base64.encode(hash!))); - toBeAdded.add( - DeviceAsset( - assetId: asset.localId!, - hash: hash, - modifiedTime: asset.fileModifiedAt, - ), - ); + toBeAdded.add(DeviceAsset(assetId: asset.localId!, hash: hash, modifiedTime: asset.fileModifiedAt)); } else { _log.warning("Failed to hash file ${asset?.localId ?? ''}"); if (asset != null) { diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart index 12da5f256b..4721911e8d 100644 --- a/mobile/lib/services/local_auth.service.dart +++ b/mobile/lib/services/local_auth.service.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/repositories/biometric.repository.dart'; -final localAuthServiceProvider = Provider( - (ref) => LocalAuthService( - ref.watch(biometricRepositoryProvider), - ), -); +final localAuthServiceProvider = Provider((ref) => LocalAuthService(ref.watch(biometricRepositoryProvider))); class LocalAuthService { final BiometricRepository _biometricRepository; diff --git a/mobile/lib/services/local_files_manager.service.dart b/mobile/lib/services/local_files_manager.service.dart index 6ba8b43fbf..7cb3067342 100644 --- a/mobile/lib/services/local_files_manager.service.dart +++ b/mobile/lib/services/local_files_manager.service.dart @@ -2,9 +2,7 @@ import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; -final localFileManagerServiceProvider = Provider( - (ref) => const LocalFilesManagerService(), -); +final localFileManagerServiceProvider = Provider((ref) => const LocalFilesManagerService()); class LocalFilesManagerService { const LocalFilesManagerService(); @@ -13,8 +11,7 @@ class LocalFilesManagerService { Future moveToTrash(List mediaUrls) async { try { - return await _channel - .invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); + return await _channel.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); } catch (e, s) { _logger.warning('Error moving file to trash', e, s); return false; @@ -23,10 +20,7 @@ class LocalFilesManagerService { Future restoreFromTrash(String fileName, int type) async { try { - return await _channel.invokeMethod( - 'restoreFromTrash', - {'fileName': fileName, 'type': type}, - ); + return await _channel.invokeMethod('restoreFromTrash', {'fileName': fileName, 'type': type}); } catch (e, s) { _logger.warning('Error restore file from trash', e, s); return false; diff --git a/mobile/lib/services/local_notification.service.dart b/mobile/lib/services/local_notification.service.dart index b47ee280b8..bf85f4a9a9 100644 --- a/mobile/lib/services/local_notification.service.dart +++ b/mobile/lib/services/local_notification.service.dart @@ -1,20 +1,16 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final localNotificationService = Provider( - (ref) => LocalNotificationService( - ref.watch(notificationPermissionProvider), - ref, - ), + (ref) => LocalNotificationService(ref.watch(notificationPermissionProvider), ref), ); class LocalNotificationService { - final FlutterLocalNotificationsPlugin _localNotificationPlugin = - FlutterLocalNotificationsPlugin(); + final FlutterLocalNotificationsPlugin _localNotificationPlugin = FlutterLocalNotificationsPlugin(); final PermissionStatus _permissionStatus; final Ref ref; @@ -29,17 +25,14 @@ class LocalNotificationService { static const cancelUploadActionID = 'cancel_upload'; Future setup() async { - const androidSetting = - AndroidInitializationSettings('@drawable/notification_icon'); + const androidSetting = AndroidInitializationSettings('@drawable/notification_icon'); const iosSetting = DarwinInitializationSettings(); - const initSettings = - InitializationSettings(android: androidSetting, iOS: iosSetting); + const initSettings = InitializationSettings(android: androidSetting, iOS: iosSetting); await _localNotificationPlugin.initialize( initSettings, - onDidReceiveNotificationResponse: - _onDidReceiveForegroundNotificationResponse, + onDidReceiveNotificationResponse: _onDidReceiveForegroundNotificationResponse, ); } @@ -50,10 +43,7 @@ class LocalNotificationService { AndroidNotificationDetails androidNotificationDetails, DarwinNotificationDetails iosNotificationDetails, ) async { - final notificationDetails = NotificationDetails( - android: androidNotificationDetails, - iOS: iosNotificationDetails, - ); + final notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails); if (_permissionStatus == PermissionStatus.granted) { await _localNotificationPlugin.show(id, title, body, notificationDetails); @@ -99,20 +89,12 @@ class LocalNotificationService { ongoing: true, actions: (showActions ?? false) ? [ - const AndroidNotificationAction( - cancelUploadActionID, - 'Cancel', - showsUserInterface: true, - ), + const AndroidNotificationAction(cancelUploadActionID, 'Cancel', showsUserInterface: true), ] : null, ) // Non-progress notification - : AndroidNotificationDetails( - androidChannelID, - androidChannelName, - playSound: false, - ); + : AndroidNotificationDetails(androidChannelID, androidChannelName, playSound: false); final iosNotificationDetails = DarwinNotificationDetails( presentBadge: true, @@ -120,23 +102,15 @@ class LocalNotificationService { presentBanner: presentBanner, ); - return _showOrUpdateNotification( - notificationlId, - title, - body, - androidNotificationDetails, - iosNotificationDetails, - ); + return _showOrUpdateNotification(notificationlId, title, body, androidNotificationDetails, iosNotificationDetails); } - void _onDidReceiveForegroundNotificationResponse( - NotificationResponse notificationResponse, - ) { + void _onDidReceiveForegroundNotificationResponse(NotificationResponse notificationResponse) { // Handle notification actions switch (notificationResponse.actionId) { case cancelUploadActionID: { - debugPrint("User cancelled manual upload operation"); + dPrint(() => "User cancelled manual upload operation"); ref.read(manualUploadProvider.notifier).cancelBackup(); } } diff --git a/mobile/lib/services/localization.service.dart b/mobile/lib/services/localization.service.dart index 8bee710544..af63894249 100644 --- a/mobile/lib/services/localization.service.dart +++ b/mobile/lib/services/localization.service.dart @@ -2,9 +2,9 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; import 'package:easy_localization/src/localization.dart'; -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; /// Workaround to manually load translations in another Isolate Future loadTranslations() async { @@ -17,7 +17,7 @@ Future loadTranslations() async { assetLoader: const CodegenLoader(), path: translationsPath, useOnlyLangCode: false, - onLoadError: (e) => debugPrint(e.toString()), + onLoadError: (e) => dPrint(() => e.toString()), fallbackLocale: locales.values.first, ); diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index a44a5ad1bd..e485bb0957 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final memoryServiceProvider = StateProvider((ref) { - return MemoryService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ); + return MemoryService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)); }); class MemoryService { @@ -35,21 +32,11 @@ class MemoryService { List memories = []; for (final memory in data) { - final dbAssets = await _assetRepository - .getAllByRemoteId(memory.assets.map((e) => e.id)); + final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); - memories.add( - Memory( - title: title, - assets: dbAssets, - ), - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); + memories.add(Memory(title: title, assets: dbAssets)); } } @@ -67,23 +54,15 @@ class MemoryService { if (memoryResponse == null) { return null; } - final dbAssets = await _assetRepository - .getAllByRemoteId(memoryResponse.assets.map((e) => e.id)); + final dbAssets = await _assetRepository.getAllByRemoteId(memoryResponse.assets.map((e) => e.id)); if (dbAssets.isEmpty) { log.warning("No assets found for memory with ID: $id"); return null; } final yearsAgo = DateTime.now().year - memoryResponse.data.year; - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); - return Memory( - title: title, - assets: dbAssets, - ); + return Memory(title: title, assets: dbAssets); } catch (error, stack) { log.severe("Cannot get memory with ID: $id", error, stack); return null; diff --git a/mobile/lib/services/network.service.dart b/mobile/lib/services/network.service.dart index de55da8d7c..8622400e7a 100644 --- a/mobile/lib/services/network.service.dart +++ b/mobile/lib/services/network.service.dart @@ -3,10 +3,7 @@ import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; final networkServiceProvider = Provider((ref) { - return NetworkService( - ref.watch(networkRepositoryProvider), - ref.watch(permissionRepositoryProvider), - ); + return NetworkService(ref.watch(networkRepositoryProvider), ref.watch(permissionRepositoryProvider)); }); class NetworkService { diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index 9a54a8d7c9..99ceca3229 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -11,24 +11,14 @@ class OAuthService { final log = Logger('OAuthService'); OAuthService(this._apiService); - Future getOAuthServerUrl( - String serverUrl, - String state, - String codeChallenge, - ) async { + Future getOAuthServerUrl(String serverUrl, String state, String codeChallenge) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); final redirectUri = '$callbackUrlScheme:///oauth-callback'; - log.info( - "Starting OAuth flow with redirect URI: $redirectUri", - ); + log.info("Starting OAuth flow with redirect URI: $redirectUri"); final dto = await _apiService.oAuthApi.startOAuth( - OAuthConfigDto( - redirectUri: redirectUri, - state: state, - codeChallenge: codeChallenge, - ), + OAuthConfigDto(redirectUri: redirectUri, state: state, codeChallenge: codeChallenge), ); final authUrl = dto?.url; @@ -37,31 +27,17 @@ class OAuthService { return authUrl; } - Future oAuthLogin( - String oauthUrl, - String state, - String codeVerifier, - ) async { - String result = await FlutterWebAuth2.authenticate( - url: oauthUrl, - callbackUrlScheme: callbackUrlScheme, - ); + Future oAuthLogin(String oauthUrl, String state, String codeVerifier) async { + String result = await FlutterWebAuth2.authenticate(url: oauthUrl, callbackUrlScheme: callbackUrlScheme); log.info('Received OAuth callback: $result'); if (result.startsWith('app.immich:/oauth-callback')) { - result = result.replaceAll( - 'app.immich:/oauth-callback', - 'app.immich:///oauth-callback', - ); + result = result.replaceAll('app.immich:/oauth-callback', 'app.immich:///oauth-callback'); } return await _apiService.oAuthApi.finishOAuth( - OAuthCallbackDto( - url: result, - state: state, - codeVerifier: codeVerifier, - ), + OAuthCallbackDto(url: result, state: state, codeVerifier: codeVerifier), ); } } diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index ec210fd587..b8e5ae9a4d 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -20,11 +20,7 @@ class PartnerService { final IsarUserRepository _isarUserRepository; final Logger _log = Logger("PartnerService"); - PartnerService( - this._partnerApiRepository, - this._isarUserRepository, - this._partnerRepository, - ); + PartnerService(this._partnerApiRepository, this._isarUserRepository, this._partnerRepository); Future> getSharedWith() async { return _partnerRepository.getSharedWith(); @@ -45,8 +41,7 @@ class PartnerService { Future removePartner(UserDto partner) async { try { await _partnerApiRepository.delete(partner.id); - await _isarUserRepository - .update(partner.copyWith(isPartnerSharedBy: false)); + await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: false)); } catch (e) { _log.warning("Failed to remove partner ${partner.id}", e); return false; @@ -57,8 +52,7 @@ class PartnerService { Future addPartner(UserDto partner) async { try { await _partnerApiRepository.create(partner.id); - await _isarUserRepository - .update(partner.copyWith(isPartnerSharedBy: true)); + await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: true)); return true; } catch (e) { _log.warning("Failed to add partner ${partner.id}", e); @@ -66,17 +60,10 @@ class PartnerService { return false; } - Future updatePartner( - UserDto partner, { - required bool inTimeline, - }) async { + Future updatePartner(UserDto partner, {required bool inTimeline}) async { try { - final dto = await _partnerApiRepository.update( - partner.id, - inTimeline: inTimeline, - ); - await _isarUserRepository - .update(partner.copyWith(inTimeline: dto.inTimeline)); + final dto = await _partnerApiRepository.update(partner.id, inTimeline: inTimeline); + await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { _log.warning("Failed to update partner ${partner.id}", e); diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index 08b18dfd10..37b16a8d29 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -11,10 +11,10 @@ part 'person.service.g.dart'; @riverpod PersonService personService(Ref ref) => PersonService( - ref.watch(personApiRepositoryProvider), - ref.watch(assetApiRepositoryProvider), - ref.read(assetRepositoryProvider), - ); + ref.watch(personApiRepositoryProvider), + ref.watch(assetApiRepositoryProvider), + ref.read(assetRepositoryProvider), +); class PersonService { final Logger _log = Logger("PersonService"); @@ -22,11 +22,7 @@ class PersonService { final AssetApiRepository _assetApiRepository; final AssetRepository _assetRepository; - PersonService( - this._personApiRepository, - this._assetApiRepository, - this._assetRepository, - ); + PersonService(this._personApiRepository, this._assetApiRepository, this._assetRepository); Future> getAllPeople() async { try { @@ -40,8 +36,7 @@ class PersonService { Future> getPersonAssets(String id) async { try { final assets = await _assetApiRepository.search(personIds: [id]); - return await _assetRepository - .getAllByRemoteId(assets.map((a) => a.remoteId!)); + return await _assetRepository.getAllByRemoteId(assets.map((a) => a.remoteId!)); } catch (error, stack) { _log.severe("Error while fetching person assets", error, stack); } diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index aa72a7908b..f33adf80f9 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart'; @@ -10,6 +9,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; final searchServiceProvider = Provider( (ref) => SearchService( @@ -25,11 +25,7 @@ class SearchService { final SearchApiRepository _searchApiRepository; final _log = Logger("SearchService"); - SearchService( - this._apiService, - this._assetRepository, - this._searchApiRepository, - ); + SearchService(this._apiService, this._assetRepository, this._searchApiRepository); Future?> getSearchSuggestions( SearchSuggestionType type, { @@ -47,7 +43,7 @@ class SearchService { model: model, ); } catch (e) { - debugPrint("[ERROR] [getSearchSuggestions] ${e.toString()}"); + dPrint(() => "[ERROR] [getSearchSuggestions] ${e.toString()}"); return []; } } @@ -61,8 +57,7 @@ class SearchService { } return SearchResult( - assets: await _assetRepository - .getAllByRemoteId(response.assets.items.map((e) => e.id)), + assets: await _assetRepository.getAllByRemoteId(response.assets.items.map((e) => e.id)), nextPage: response.assets.nextPage?.toInt(), ); } catch (error, stackTrace) { diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart index 38e6deb0d4..95d2e7a2c8 100644 --- a/mobile/lib/services/secure_storage.service.dart +++ b/mobile/lib/services/secure_storage.service.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/secure_storage.repository.dart'; final secureStorageServiceProvider = Provider( - (ref) => SecureStorageService( - ref.watch(secureStorageRepositoryProvider), - ), + (ref) => SecureStorageService(ref.watch(secureStorageRepositoryProvider)), ); class SecureStorageService { diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index 75ce68a73c..460e135421 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/server_info/server_config.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; @@ -6,12 +5,9 @@ import 'package:immich_mobile/models/server_info/server_features.model.dart'; import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; -final serverInfoServiceProvider = Provider( - (ref) => ServerInfoService( - ref.watch(apiServiceProvider), - ), -); +final serverInfoServiceProvider = Provider((ref) => ServerInfoService(ref.watch(apiServiceProvider))); class ServerInfoService { final ApiService _apiService; @@ -25,7 +21,7 @@ class ServerInfoService { return ServerDiskInfo.fromDto(dto); } } catch (e) { - debugPrint("Error [getDiskInfo] ${e.toString()}"); + dPrint(() => "Error [getDiskInfo] ${e.toString()}"); } return null; } @@ -37,7 +33,7 @@ class ServerInfoService { return ServerVersion.fromDto(dto); } } catch (e) { - debugPrint("Error [getServerVersion] ${e.toString()}"); + dPrint(() => "Error [getServerVersion] ${e.toString()}"); } return null; } @@ -49,7 +45,7 @@ class ServerInfoService { return ServerFeatures.fromDto(dto); } } catch (e) { - debugPrint("Error [getServerFeatures] ${e.toString()}"); + dPrint(() => "Error [getServerFeatures] ${e.toString()}"); } return null; } @@ -61,7 +57,7 @@ class ServerInfoService { return ServerConfig.fromDto(dto); } } catch (e) { - debugPrint("Error [getServerConfig] ${e.toString()}"); + dPrint(() => "Error [getServerConfig] ${e.toString()}"); } return null; } diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index 77afa10fb6..7ba385d71c 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -10,8 +10,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; import 'api.service.dart'; -final shareServiceProvider = - Provider((ref) => ShareService(ref.watch(apiServiceProvider))); +final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider))); class ShareService { final ApiService _apiService; @@ -37,14 +36,10 @@ class ShareService { final tempDir = await getTemporaryDirectory(); final fileName = asset.fileName; final tempFile = await File('${tempDir.path}/$fileName').create(); - final res = await _apiService.assetsApi - .downloadAssetWithHttpInfo(asset.remoteId!); + final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!); if (res.statusCode != 200) { - _log.severe( - "Asset download for ${asset.fileName} failed", - res.toLoggerString(), - ); + _log.severe("Asset download for ${asset.fileName} failed", res.toLoggerString()); continue; } @@ -59,18 +54,13 @@ class ShareService { } if (downloadedXFiles.length != assets.length) { - _log.warning( - "Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}", - ); + _log.warning("Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}"); } final size = MediaQuery.of(context).size; Share.shareXFiles( downloadedXFiles, - sharePositionOrigin: Rect.fromPoints( - Offset.zero, - Offset(size.width / 3, size.height), - ), + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), ); return true; } catch (error) { diff --git a/mobile/lib/services/share_intent_service.dart b/mobile/lib/services/share_intent_service.dart index e514e5bbdc..fca5c4a188 100644 --- a/mobile/lib/services/share_intent_service.dart +++ b/mobile/lib/services/share_intent_service.dart @@ -2,19 +2,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/repositories/share_handler.repository.dart'; -final shareIntentServiceProvider = Provider( - (ref) => ShareIntentService( - ref.watch(shareHandlerRepositoryProvider), - ), -); +final shareIntentServiceProvider = Provider((ref) => ShareIntentService(ref.watch(shareHandlerRepositoryProvider))); class ShareIntentService { final ShareHandlerRepository shareHandlerRepository; void Function(List attachments)? onSharedMedia; - ShareIntentService( - this.shareHandlerRepository, - ); + ShareIntentService(this.shareHandlerRepository); void init() { shareHandlerRepository.onSharedMedia = onSharedMedia; diff --git a/mobile/lib/services/shared_link.service.dart b/mobile/lib/services/shared_link.service.dart index a2b5ed9062..25151c234f 100644 --- a/mobile/lib/services/shared_link.service.dart +++ b/mobile/lib/services/shared_link.service.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final sharedLinkServiceProvider = Provider( - (ref) => SharedLinkService(ref.watch(apiServiceProvider)), -); +final sharedLinkServiceProvider = Provider((ref) => SharedLinkService(ref.watch(apiServiceProvider))); class SharedLinkService { final ApiService _apiService; @@ -18,9 +16,7 @@ class SharedLinkService { Future>> getAllSharedLinks() async { try { final list = await _apiService.sharedLinksApi.getAllSharedLinks(); - return list != null - ? AsyncData(list.map(SharedLink.fromDto).toList()) - : const AsyncData([]); + return list != null ? AsyncData(list.map(SharedLink.fromDto).toList()) : const AsyncData([]); } catch (e, stack) { _log.severe("Failed to fetch shared links", e, stack); return AsyncError(e, stack); @@ -46,8 +42,7 @@ class SharedLinkService { DateTime? expiresAt, }) async { try { - final type = - albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL; + final type = albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL; SharedLinkCreateDto? dto; if (type == SharedLinkType.ALBUM) { dto = SharedLinkCreateDto( @@ -74,8 +69,7 @@ class SharedLinkService { } if (dto != null) { - final responseDto = - await _apiService.sharedLinksApi.createSharedLink(dto); + final responseDto = await _apiService.sharedLinksApi.createSharedLink(dto); if (responseDto != null) { return SharedLink.fromDto(responseDto); } diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart index e2ab6c804b..88189c6bcd 100644 --- a/mobile/lib/services/stack.service.dart +++ b/mobile/lib/services/stack.service.dart @@ -1,10 +1,10 @@ -import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:openapi/api.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class StackService { const StackService(this._api, this._assetRepository); @@ -16,33 +16,25 @@ class StackService { try { return _api.stacksApi.getStack(stackId); } catch (error) { - debugPrint("Error while fetching stack: $error"); + dPrint(() => "Error while fetching stack: $error"); } return null; } Future createStack(List assetIds) async { try { - return _api.stacksApi.createStack( - StackCreateDto(assetIds: assetIds), - ); + return _api.stacksApi.createStack(StackCreateDto(assetIds: assetIds)); } catch (error) { - debugPrint("Error while creating stack: $error"); + dPrint(() => "Error while creating stack: $error"); } return null; } - Future updateStack( - String stackId, - String primaryAssetId, - ) async { + Future updateStack(String stackId, String primaryAssetId) async { try { - return await _api.stacksApi.updateStack( - stackId, - StackUpdateDto(primaryAssetId: primaryAssetId), - ); + return await _api.stacksApi.updateStack(stackId, StackUpdateDto(primaryAssetId: primaryAssetId)); } catch (error) { - debugPrint("Error while updating stack children: $error"); + dPrint(() => "Error while updating stack children: $error"); } return null; } @@ -60,17 +52,13 @@ class StackService { removeAssets.add(asset); } - await _assetRepository - .transaction(() => _assetRepository.updateAll(removeAssets)); + await _assetRepository.transaction(() => _assetRepository.updateAll(removeAssets)); } catch (error) { - debugPrint("Error while deleting stack: $error"); + dPrint(() => "Error while deleting stack: $error"); } } } final stackServiceProvider = Provider( - (ref) => StackService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ), + (ref) => StackService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)), ); diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 5a95be2237..1a5cb2a116 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -94,63 +94,44 @@ class SyncService { /// Syncs users from the server to the local database /// Returns `true`if there were any changes - Future syncUsersFromServer(List users) => - _lock.run(() => _syncUsersFromServer(users)); + Future syncUsersFromServer(List users) => _lock.run(() => _syncUsersFromServer(users)); /// Syncs remote assets owned by the logged-in user to the DB /// Returns `true` if there were any changes Future syncRemoteAssetsToDb({ required List users, - required Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, - required FutureOr?> Function(UserDto user, DateTime until) - loadAssets, - }) => - _lock.run( - () async => - await _syncRemoteAssetChanges(users, getChangedAssets) ?? - await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), - ); + required Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, + required FutureOr?> Function(UserDto user, DateTime until) loadAssets, + }) => _lock.run( + () async => + await _syncRemoteAssetChanges(users, getChangedAssets) ?? + await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), + ); /// Syncs remote albums to the database /// returns `true` if there were any changes - Future syncRemoteAlbumsToDb( - List remote, - ) => - _lock.run(() => _syncRemoteAlbumsToDb(remote)); + Future syncRemoteAlbumsToDb(List remote) => _lock.run(() => _syncRemoteAlbumsToDb(remote)); /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) => + Future syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) => _lock.run(() => _syncLocalAlbumAssetsToDb(onDevice, excludedAssets)); /// returns all Asset IDs that are not contained in the existing list - List sharedAssetsToRemove( - List deleteCandidates, - List existing, - ) { + List sharedAssetsToRemove(List deleteCandidates, List existing) { if (deleteCandidates.isEmpty) { return []; } deleteCandidates.sort(Asset.compareById); existing.sort(Asset.compareById); - return _diffAssets(existing, deleteCandidates, compare: Asset.compareById) - .$3 - .map((e) => e.id) - .toList(); + return _diffAssets(existing, deleteCandidates, compare: Asset.compareById).$3.map((e) => e.id).toList(); } /// Syncs a new asset to the db. Returns `true` if successful - Future syncNewAssetToDb(Asset newAsset) => - _lock.run(() => _syncNewAssetToDb(newAsset)); + Future syncNewAssetToDb(Asset newAsset) => _lock.run(() => _syncNewAssetToDb(newAsset)); - Future removeAllLocalAlbumsAndAssets() => - _lock.run(_removeAllLocalAlbumsAndAssets); + Future removeAllLocalAlbumsAndAssets() => _lock.run(_removeAllLocalAlbumsAndAssets); // private methods: @@ -166,7 +147,9 @@ class SyncService { dbUsers, compare: (UserDto a, UserDto b) => a.id.compareTo(b.id), both: (UserDto a, UserDto b) { - if (!a.updatedAt.isAtSameMomentAs(b.updatedAt) || + if ((a.updatedAt == null && b.updatedAt != null) || + (a.updatedAt != null && b.updatedAt == null) || + (a.updatedAt != null && b.updatedAt != null && !a.updatedAt!.isAtSameMomentAs(b.updatedAt!)) || a.isPartnerSharedBy != b.isPartnerSharedBy || a.isPartnerSharedWith != b.isPartnerSharedWith || a.inTimeline != b.inTimeline) { @@ -189,8 +172,7 @@ class SyncService { /// Syncs a new asset to the db. Returns `true` if successful Future _syncNewAssetToDb(Asset a) async { - final Asset? inDb = - await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum); + final Asset? inDb = await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum); if (inDb != null) { // unify local/remote assets by replacing the // local-only asset in the DB with a local&remote asset @@ -208,14 +190,11 @@ class SyncService { /// Efficiently syncs assets via changes. Returns `null` when a full sync is required. Future _syncRemoteAssetChanges( List users, - Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, ) async { final currentUser = _userService.getMyUser(); - final DateTime? since = - (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); + final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); if (since == null) return null; final DateTime now = DateTime.now(); final (toUpsert, toDelete) = await getChangedAssets(users, since); @@ -244,14 +223,9 @@ class SyncService { Future _moveToTrashMatchedAssets(Iterable idsToDelete) async { final List localAssets = await _assetRepository.getAllLocal(); - final List matchedAssets = localAssets - .where((asset) => idsToDelete.contains(asset.remoteId)) - .toList(); + final List matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList(); - final mediaUrls = await Future.wait( - matchedAssets - .map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), - ); + final mediaUrls = await Future.wait(matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null))); await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } @@ -259,18 +233,9 @@ class SyncService { /// Deletes remote-only assets, updates merged assets to be local-only Future handleRemoteAssetRemoval(List idsToDelete) async { return _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - idsToDelete, - state: AssetState.remote, - ); - final merged = await _assetRepository.getAllByRemoteId( - idsToDelete, - state: AssetState.merged, - ); - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + await _assetRepository.deleteAllByRemoteId(idsToDelete, state: AssetState.remote); + final merged = await _assetRepository.getAllByRemoteId(idsToDelete, state: AssetState.merged); + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { await _moveToTrashMatchedAssets(idsToDelete); } if (merged.isEmpty) return; @@ -316,10 +281,7 @@ class SyncService { if (remote == null) { return false; } - final List inDb = await _assetRepository.getAll( - ownerId: user.id, - sortBy: AssetSort.checksum, - ); + final List inDb = await _assetRepository.getAll(ownerId: user.id, sortBy: AssetSort.checksum); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); remote.sort(Asset.compareByChecksum); @@ -355,15 +317,10 @@ class SyncService { /// Syncs remote albums to the database /// returns `true` if there were any changes - Future _syncRemoteAlbumsToDb( - List remoteAlbums, - ) async { + Future _syncRemoteAlbumsToDb(List remoteAlbums) async { remoteAlbums.sortBy((e) => e.remoteId!); - final List dbAlbums = await _albumRepository.getAll( - remote: true, - sortBy: AlbumSort.remoteId, - ); + final List dbAlbums = await _albumRepository.getAll(remote: true, sortBy: AlbumSort.remoteId); final List toDelete = []; final List existing = []; @@ -371,10 +328,8 @@ class SyncService { final bool changes = await diffSortedLists( remoteAlbums, dbAlbums, - compare: (remoteAlbum, dbAlbum) => - remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!), - both: (remoteAlbum, dbAlbum) => - _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing), + compare: (remoteAlbum, dbAlbum) => remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!), + both: (remoteAlbum, dbAlbum) => _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing), onlyFirst: (remoteAlbum) => _addAlbumFromServer(remoteAlbum, existing), onlySecond: (dbAlbum) => _removeAlbumFromDb(dbAlbum, toDelete), ); @@ -393,12 +348,7 @@ class SyncService { /// syncs albums from the server to the local database (does not support /// syncing changes from local back to server) /// accumulates - Future _syncRemoteAlbum( - Album dto, - Album album, - List deleteCandidates, - List existing, - ) async { + Future _syncRemoteAlbum(Album dto, Album album, List deleteCandidates, List existing) async { if (!_hasRemoteAlbumChanged(dto, album)) { return false; } @@ -407,25 +357,16 @@ class SyncService { final originalDto = dto; dto = await _albumApiRepository.get(dto.remoteId!); - final assetsInDb = await _assetRepository.getByAlbum( - album, - sortBy: AssetSort.ownerIdChecksum, - ); + final assetsInDb = await _assetRepository.getByAlbum(album, sortBy: AssetSort.ownerIdChecksum); assert(assetsInDb.isSorted(Asset.compareByOwnerChecksum), "inDb unsorted!"); final List assetsOnRemote = dto.remoteAssets.toList(); assetsOnRemote.sort(Asset.compareByOwnerChecksum); - final (toAdd, toUpdate, toUnlink) = _diffAssets( - assetsOnRemote, - assetsInDb, - compare: Asset.compareByOwnerChecksum, - ); + final (toAdd, toUpdate, toUnlink) = _diffAssets(assetsOnRemote, assetsInDb, compare: Asset.compareByOwnerChecksum); // update shared users - final List sharedUsers = - album.sharedUsers.map((u) => u.toDto()).toList(growable: false); + final List sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false); sharedUsers.sort((a, b) => a.id.compareTo(b.id)); - final List users = dto.remoteUsers.map((u) => u.toDto()).toList() - ..sort((a, b) => a.id.compareTo(b.id)); + final List users = dto.remoteUsers.map((u) => u.toDto()).toList()..sort((a, b) => a.id.compareTo(b.id)); final List userIdsToAdd = []; final List usersToUnlink = []; diffSortedListsSync( @@ -456,10 +397,8 @@ class SyncService { album.sortOrder = dto.sortOrder; final remoteThumbnailAssetId = dto.remoteThumbnailAssetId; - if (remoteThumbnailAssetId != null && - album.thumbnail.value?.remoteId != remoteThumbnailAssetId) { - album.thumbnail.value = - await _assetRepository.getByRemoteId(remoteThumbnailAssetId); + if (remoteThumbnailAssetId != null && album.thumbnail.value?.remoteId != remoteThumbnailAssetId) { + album.thumbnail.value = await _assetRepository.getByRemoteId(remoteThumbnailAssetId); } // write & commit all changes to DB @@ -480,8 +419,7 @@ class SyncService { if (album.shared || dto.shared) { final userId = (_userService.getMyUser()).id; - final foreign = - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); + final foreign = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); existing.addAll(foreign); // delete assets in DB unless they belong to this user or part of some other shared album @@ -495,18 +433,14 @@ class SyncService { /// Adds a remote album to the database while making sure to add any foreign /// (shared) assets to the database beforehand /// accumulates assets already existing in the database - Future _addAlbumFromServer( - Album album, - List existing, - ) async { + Future _addAlbumFromServer(Album album, List existing) async { if (album.remoteAssetCount != album.remoteAssets.length) { album = await _albumApiRepository.get(album.remoteId!); } if (album.remoteAssetCount == album.remoteAssets.length) { // in case an album contains assets not yet present in local DB: // put missing album assets into local DB - final (existingInDb, updated) = - await _linkWithExistingFromDb(album.remoteAssets.toList()); + final (existingInDb, updated) = await _linkWithExistingFromDb(album.remoteAssets.toList()); existing.addAll(existingInDb); await upsertAssetsWithExif(updated); @@ -514,28 +448,23 @@ class SyncService { await _albumRepository.create(album); } else { _log.warning( - "Failed to add album from server: assetCount ${album.remoteAssetCount} != " - "asset array length ${album.remoteAssets.length} for album ${album.name}"); + "Failed to add album from server: assetCount ${album.remoteAssetCount} != " + "asset array length ${album.remoteAssets.length} for album ${album.name}", + ); } } /// Accumulates all suitable album assets to the `deleteCandidates` and /// removes the album from the database. - Future _removeAlbumFromDb( - Album album, - List deleteCandidates, - ) async { + Future _removeAlbumFromDb(Album album, List deleteCandidates) async { if (album.isLocal) { _log.info("Removing local album $album from DB"); // delete assets in DB unless they are remote or part of some other album - deleteCandidates.addAll( - await _assetRepository.getByAlbum(album, state: AssetState.local), - ); + deleteCandidates.addAll(await _assetRepository.getByAlbum(album, state: AssetState.local)); } else if (album.shared) { // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner final userIds = (await _getAllAccessibleUsers()).map((user) => user.id); - final orphanedAssets = - await _assetRepository.getByAlbum(album, notOwnedBy: userIds); + final orphanedAssets = await _assetRepository.getByAlbum(album, notOwnedBy: userIds); deleteCandidates.addAll(orphanedAssets); } try { @@ -548,45 +477,28 @@ class SyncService { /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future _syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) async { + Future _syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) async { onDevice.sort((a, b) => a.localId!.compareTo(b.localId!)); - final inDb = - await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); + final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); final List deleteCandidates = []; final List existing = []; final bool anyChanges = await diffSortedLists( onDevice, inDb, compare: (Album a, Album b) => a.localId!.compareTo(b.localId!), - both: (Album a, Album b) => _syncAlbumInDbAndOnDevice( - a, - b, - deleteCandidates, - existing, - excludedAssets, - ), + both: (Album a, Album b) => _syncAlbumInDbAndOnDevice(a, b, deleteCandidates, existing, excludedAssets), onlyFirst: (Album a) => _addAlbumFromDevice(a, existing, excludedAssets), onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates), ); - _log.fine( - "Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete", - ); - final (toDelete, toUpdate) = - _handleAssetRemoval(deleteCandidates, existing, remote: false); - _log.fine( - "${toDelete.length} assets to delete, ${toUpdate.length} to update", - ); + _log.fine("Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete"); + final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false); + _log.fine("${toDelete.length} assets to delete, ${toUpdate.length} to update"); if (toDelete.isNotEmpty || toUpdate.isNotEmpty) { await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); }); - _log.info( - "Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB", - ); + _log.info("Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB"); } return anyChanges; } @@ -604,15 +516,11 @@ class SyncService { ]) async { _log.info("Syncing a local album to DB: ${deviceAlbum.name}"); if (!forceRefresh && !await _hasAlbumChangeOnDevice(deviceAlbum, dbAlbum)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } _log.info("Local album ${deviceAlbum.name} has changed. Syncing..."); - if (!forceRefresh && - excludedAssets == null && - await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) { + if (!forceRefresh && excludedAssets == null && await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) { _log.info("Fast synced local album ${deviceAlbum.name} to DB"); return true; } @@ -624,12 +532,8 @@ class SyncService { ); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); - final int assetCountOnDevice = - await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final List onDevice = await _getHashedAssets( - deviceAlbum, - excludedAssets: excludedAssets, - ); + final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); + final List onDevice = await _getHashedAssets(deviceAlbum, excludedAssets: excludedAssets); _removeDuplicates(onDevice); // _removeDuplicates sorts `onDevice` by checksum final (toAdd, toUpdate, toDelete) = _diffAssets(onDevice, inDb); @@ -640,18 +544,9 @@ class SyncService { dbAlbum.description == deviceAlbum.description && dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) { // changes only affeted excluded albums - _log.info( - "Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.", - ); - if (assetCountOnDevice != - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount) { - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + _log.info("Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync."); + if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) { + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); } return false; } @@ -667,8 +562,7 @@ class SyncService { dbAlbum.name = deviceAlbum.name; dbAlbum.description = deviceAlbum.description; dbAlbum.modifiedAt = deviceAlbum.modifiedAt; - if (dbAlbum.thumbnail.value != null && - toDelete.contains(dbAlbum.thumbnail.value)) { + if (dbAlbum.thumbnail.value != null && toDelete.contains(dbAlbum.thumbnail.value)) { dbAlbum.thumbnail.value = null; } try { @@ -678,12 +572,7 @@ class SyncService { await _albumRepository.removeAssets(dbAlbum, toDelete); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); }); _log.info("Synced changes of local album ${deviceAlbum.name} to DB"); } catch (e) { @@ -697,21 +586,13 @@ class SyncService { /// returns `true` if successful, else `false` Future _syncDeviceAlbumFast(Album deviceAlbum, Album dbAlbum) async { if (!deviceAlbum.modifiedAt.isAfter(dbAlbum.modifiedAt)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } - final int totalOnDevice = - await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final int lastKnownTotal = - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount ?? - 0; + final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); + final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0; if (totalOnDevice <= lastKnownTotal) { - _log.info( - "Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync."); return false; } final List newAssets = await _getHashedAssets( @@ -735,16 +616,11 @@ class SyncService { await _albumRepository.addAssets(dbAlbum, existingInDb + updated); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll( - [ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)], - ); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)]); }); _log.info("Fast synced local album ${deviceAlbum.name} to DB"); } catch (e) { - _log.severe( - "Failed to fast sync local album ${deviceAlbum.name} to DB", - e, - ); + _log.severe("Failed to fast sync local album ${deviceAlbum.name} to DB", e); return false; } @@ -753,21 +629,12 @@ class SyncService { /// Adds a new album from the device to the database and Accumulates all /// assets already existing in the database to the list of `existing` assets - Future _addAlbumFromDevice( - Album album, - List existing, [ - Set? excludedAssets, - ]) async { + Future _addAlbumFromDevice(Album album, List existing, [Set? excludedAssets]) async { _log.info("Adding a new local album to DB: ${album.name}"); - final assets = await _getHashedAssets( - album, - excludedAssets: excludedAssets, - ); + final assets = await _getHashedAssets(album, excludedAssets: excludedAssets); _removeDuplicates(assets); final (existingInDb, updated) = await _linkWithExistingFromDb(assets); - _log.info( - "${existingInDb.length} assets already existed in DB, to upsert ${updated.length}", - ); + _log.info("${existingInDb.length} assets already existed in DB, to upsert ${updated.length}"); await upsertAssetsWithExif(updated); existing.addAll(existingInDb); album.assets.addAll(existingInDb); @@ -776,11 +643,8 @@ class SyncService { album.thumbnail.value = thumb; try { await _albumRepository.create(album); - final int assetCount = - await _albumMediaRepository.getAssetCount(album.localId!); - await _eTagRepository.upsertAll([ - ETag(id: album.eTagKeyAssetCount, assetCount: assetCount), - ]); + final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!); + await _eTagRepository.upsertAll([ETag(id: album.eTagKeyAssetCount, assetCount: assetCount)]); _log.info("Added a new local album to DB: ${album.name}"); } catch (e) { _log.severe("Failed to add new local album ${album.name} to DB", e); @@ -788,9 +652,7 @@ class SyncService { } /// Returns a tuple (existing, updated) - Future<(List existing, List updated)> _linkWithExistingFromDb( - List assets, - ) async { + Future<(List existing, List updated)> _linkWithExistingFromDb(List assets) async { if (assets.isEmpty) return ([].cast(), [].cast()); final List inDb = await _assetRepository.getAllByOwnerIdChecksum( @@ -824,17 +686,12 @@ class SyncService { if (asset.isTrashed) { final mediaUrl = await asset.local?.getMediaUrl(); if (mediaUrl == null) { - _log.warning( - "Failed to get media URL for asset ${asset.name} while moving to trash", - ); + _log.warning("Failed to get media URL for asset ${asset.name} while moving to trash"); continue; } trashMediaUrls.add(mediaUrl); } else { - await _localFilesManager.restoreFromTrash( - asset.fileName, - asset.type.index, - ); + await _localFilesManager.restoreFromTrash(asset.fileName, asset.type.index); } } @@ -847,10 +704,7 @@ class SyncService { Future upsertAssetsWithExif(List assets) async { if (assets.isEmpty) return; - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { _toggleTrashStatusForAssets(assets); } @@ -877,21 +731,15 @@ class SyncService { final Asset? b = inDb[i]; if (b == null) { if (!a.isInDb) { - _log.warning( - "Trying to update an asset that does not exist in DB:\n$a", - ); + _log.warning("Trying to update an asset that does not exist in DB:\n$a"); } } else if (a.id != b.id) { - _log.warning( - "Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a", - ); + _log.warning("Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a"); } } for (int i = 1; i < assets.length; i++) { if (Asset.compareByOwnerChecksum(assets[i - 1], assets[i]) == 0) { - _log.warning( - "Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}", - ); + _log.warning("Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}"); } } } @@ -922,10 +770,7 @@ class SyncService { List _removeDuplicates(List assets) { final int before = assets.length; assets.sort(Asset.compareByOwnerChecksumCreatedModified); - assets.uniqueConsecutive( - compare: Asset.compareByOwnerChecksum, - onDuplicate: (a, b) => {}, - ); + assets.uniqueConsecutive(compare: Asset.compareByOwnerChecksum, onDuplicate: (a, b) => {}); final int duplicates = before - assets.length; if (duplicates > 0) { _log.warning("Ignored $duplicates duplicate assets on device"); @@ -934,23 +779,18 @@ class SyncService { } /// returns `true` if the albums differ on the surface - Future _hasAlbumChangeOnDevice( - Album deviceAlbum, - Album dbAlbum, - ) async { + Future _hasAlbumChangeOnDevice(Album deviceAlbum, Album dbAlbum) async { return deviceAlbum.name != dbAlbum.name || deviceAlbum.description != dbAlbum.description || !deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) != - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount; + (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount; } Future _removeAllLocalAlbumsAndAssets() async { try { final assets = await _assetRepository.getAllLocal(); - final (toDelete, toUpdate) = - _handleAssetRemoval(assets, [], remote: false); + final (toDelete, toUpdate) = _handleAssetRemoval(assets, [], remote: false); await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); @@ -971,10 +811,8 @@ class SyncService { _log.warning("Failed to fetch users", e); users = null; } - final List sharedBy = - await _partnerApiRepository.getAll(Direction.sharedByMe); - final List sharedWith = - await _partnerApiRepository.getAll(Direction.sharedWithMe); + final List sharedBy = await _partnerApiRepository.getAll(Direction.sharedByMe); + final List sharedWith = await _partnerApiRepository.getAll(Direction.sharedWithMe); if (users == null) { _log.warning("Failed to refresh users"); @@ -1006,9 +844,7 @@ class SyncService { sharedWith, compare: (UserDto a, UserDto b) => a.id.compareTo(b.id), both: (UserDto a, UserDto b) { - updatedSharedWith.add( - a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true), - ); + updatedSharedWith.add(a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true)); return true; }, onlyFirst: (UserDto a) => updatedSharedWith.add(a), @@ -1105,8 +941,5 @@ bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) { !remoteAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || !isAtSameMomentAs(remoteAlbum.startDate, dbAlbum.startDate) || !isAtSameMomentAs(remoteAlbum.endDate, dbAlbum.endDate) || - !isAtSameMomentAs( - remoteAlbum.lastModifiedAssetTimestamp, - dbAlbum.lastModifiedAssetTimestamp, - ); + !isAtSameMomentAs(remoteAlbum.lastModifiedAssetTimestamp, dbAlbum.lastModifiedAssetTimestamp); } diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 47ad17fc25..eaff1027d8 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -21,11 +21,7 @@ class TimelineService { final AppSettingsService _appSettingsService; final UserService _userService; - const TimelineService( - this._timelineRepository, - this._appSettingsService, - this._userService, - ); + const TimelineService(this._timelineRepository, this._appSettingsService, this._userService); Future> getTimelineUserIds() async { final me = _userService.getMyUser(); @@ -42,10 +38,7 @@ class TimelineService { } Stream watchMultiUsersTimeline(List userIds) { - return _timelineRepository.watchMultiUsersTimeline( - userIds, - _getGroupByOption(), - ); + return _timelineRepository.watchMultiUsersTimeline(userIds, _getGroupByOption()); } Stream watchArchiveTimeline() async* { @@ -61,10 +54,7 @@ class TimelineService { } Stream watchAlbumTimeline(Album album) async* { - yield* _timelineRepository.watchAlbumTimeline( - album, - _getGroupByOption(), - ); + yield* _timelineRepository.watchAlbumTimeline(album, _getGroupByOption()); } Stream watchTrashTimeline() async* { @@ -79,10 +69,7 @@ class TimelineService { return _timelineRepository.watchAllVideosTimeline(user.id); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy? groupBy, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy? groupBy) { GroupAssetsBy groupOption = GroupAssetsBy.none; if (groupBy == null) { groupOption = _getGroupByOption(); @@ -90,10 +77,7 @@ class TimelineService { groupOption = groupBy; } - return _timelineRepository.getTimelineFromAssets( - assets, - groupOption, - ); + return _timelineRepository.getTimelineFromAssets(assets, groupOption); } Stream watchAssetSelectionTimeline() async* { @@ -103,16 +87,12 @@ class TimelineService { } GroupAssetsBy _getGroupByOption() { - return GroupAssetsBy - .values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return GroupAssetsBy.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; } Stream watchLockedTimelineProvider() async* { final user = _userService.getMyUser(); - yield* _timelineRepository.watchLockedTimeline( - user.id, - _getGroupByOption(), - ); + yield* _timelineRepository.watchLockedTimeline(user.id, _getGroupByOption()); } } diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 6cd7dfc641..2c51a68c59 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -20,17 +20,11 @@ class TrashService { final AssetRepository _assetRepository; final UserService _userService; - const TrashService( - this._apiService, - this._assetRepository, - this._userService, - ); + const TrashService(this._apiService, this._assetRepository, this._userService); Future restoreAssets(Iterable assetList) async { final remoteAssets = assetList.where((a) => a.isRemote); - await _apiService.trashApi.restoreAssets( - BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList()), - ); + await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList())); final updatedAssets = remoteAssets.map((asset) { asset.isTrashed = false; @@ -49,15 +43,9 @@ class TrashService { final ids = trashedAssets.map((e) => e.remoteId!).toList(); await _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - ids, - state: AssetState.remote, - ); + await _assetRepository.deleteAllByRemoteId(ids, state: AssetState.remote); - final merged = await _assetRepository.getAllByRemoteId( - ids, - state: AssetState.merged, - ); + final merged = await _assetRepository.getAllByRemoteId(ids, state: AssetState.merged); if (merged.isEmpty) { return; } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index b869624e52..e8e98562f7 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -1,53 +1,80 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; +import 'package:cancellation_token_http/http.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; +import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:path/path.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; final uploadServiceProvider = Provider((ref) { - final service = UploadService(ref.watch(uploadRepositoryProvider)); + final service = UploadService( + ref.watch(uploadRepositoryProvider), + ref.watch(backupRepositoryProvider), + ref.watch(storageRepositoryProvider), + ref.watch(localAssetRepository), + ref.watch(appSettingsServiceProvider), + ); + ref.onDispose(service.dispose); return service; }); class UploadService { - final UploadRepository _uploadRepository; - void Function(TaskStatusUpdate)? onUploadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; - - final StreamController _taskStatusController = - StreamController.broadcast(); - final StreamController _taskProgressController = - StreamController.broadcast(); - - Stream get taskStatusStream => _taskStatusController.stream; - Stream get taskProgressStream => - _taskProgressController.stream; - UploadService( this._uploadRepository, + this._backupRepository, + this._storageRepository, + this._localAssetRepository, + this._appSettingsService, ) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } + final UploadRepository _uploadRepository; + final DriftBackupRepository _backupRepository; + final StorageRepository _storageRepository; + final DriftLocalAssetRepository _localAssetRepository; + final AppSettingsService _appSettingsService; + final Logger _logger = Logger('UploadService'); + + final StreamController _taskStatusController = StreamController.broadcast(); + final StreamController _taskProgressController = StreamController.broadcast(); + + Stream get taskStatusStream => _taskStatusController.stream; + Stream get taskProgressStream => _taskProgressController.stream; + + bool shouldAbortQueuingTasks = false; + void _onTaskProgressCallback(TaskProgressUpdate update) { - onTaskProgress?.call(update); if (!_taskProgressController.isClosed) { _taskProgressController.add(update); } } void _onUploadCallback(TaskStatusUpdate update) { - onUploadStatus?.call(update); if (!_taskStatusController.isClosed) { _taskStatusController.add(update); } + _handleTaskStatusUpdate(update); } void dispose() { @@ -55,72 +82,344 @@ class UploadService { _taskProgressController.close(); } - Future cancelUpload(String id) { - return FileDownloader().cancelTaskWithId(id); + Future> enqueueTasks(List tasks) { + return _uploadRepository.enqueueBackgroundAll(tasks); } - Future cancelAllForGroup(String group) async { - await _uploadRepository.cancelAll(group); - await _uploadRepository.reset(group); - await _uploadRepository.deleteAllTrackingRecords(group); + Future> getActiveTasks(String group) { + return _uploadRepository.getActiveTasks(group); } - void enqueueTasks(List tasks) { - _uploadRepository.enqueueAll(tasks); + Future<({int total, int remainder, int processing})> getBackupCounts(String userId) { + return _backupRepository.getAllCounts(userId); + } + + Future manualBackup(List localAssets) async { + await _storageRepository.clearCache(); + List tasks = []; + for (final asset in localAssets) { + final task = await _getUploadTask( + asset, + group: kManualUploadGroup, + priority: 1, // High priority after upload motion photo part + ); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty) { + await enqueueTasks(tasks); + } + } + + /// Find backup candidates + /// Build the upload tasks + /// Enqueue the tasks + Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { + await _storageRepository.clearCache(); + + shouldAbortQueuingTasks = false; + + final candidates = await _backupRepository.getCandidates(userId); + if (candidates.isEmpty) { + return; + } + + const batchSize = 100; + int count = 0; + for (int i = 0; i < candidates.length; i += batchSize) { + if (shouldAbortQueuingTasks) { + break; + } + + final batch = candidates.skip(i).take(batchSize).toList(); + List tasks = []; + for (final asset in batch) { + final task = await _getUploadTask(asset); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty && !shouldAbortQueuingTasks) { + count += tasks.length; + await enqueueTasks(tasks); + + onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length)); + } + } + } + + Future startBackupWithHttpClient(String userId, bool hasWifi, CancellationToken token) async { + await _storageRepository.clearCache(); + + shouldAbortQueuingTasks = false; + + final candidates = await _backupRepository.getCandidates(userId); + if (candidates.isEmpty) { + return; + } + + const batchSize = 100; + for (int i = 0; i < candidates.length; i += batchSize) { + if (shouldAbortQueuingTasks || token.isCancelled) { + break; + } + + final batch = candidates.skip(i).take(batchSize).toList(); + List tasks = []; + for (final asset in batch) { + final requireWifi = _shouldRequireWiFi(asset); + if (requireWifi && !hasWifi) { + _logger.warning('Skipping upload for ${asset.id} because it requires WiFi'); + continue; + } + + final task = await _getUploadTaskWithFile(asset); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty && !shouldAbortQueuingTasks) { + await _uploadRepository.backupWithDartClient(tasks, token); + } + } + } + + /// Cancel all ongoing uploads and reset the upload queue + /// + /// Return the number of left over tasks in the queue + Future cancelBackup() async { + shouldAbortQueuingTasks = true; + + await _storageRepository.clearCache(); + await _uploadRepository.reset(kBackupGroup); + await _uploadRepository.deleteDatabaseRecords(kBackupGroup); + + final activeTasks = await _uploadRepository.getActiveTasks(kBackupGroup); + return activeTasks.length; + } + + Future resumeBackup() { + return _uploadRepository.start(); + } + + void _handleTaskStatusUpdate(TaskStatusUpdate update) async { + switch (update.status) { + case TaskStatus.complete: + _handleLivePhoto(update); + + if (CurrentPlatform.isIOS) { + try { + final path = await update.task.filePath(); + await File(path).delete(); + } catch (e) { + _logger.severe('Error deleting file path for iOS: $e'); + } + } + + break; + + default: + break; + } + } + + Future _handleLivePhoto(TaskStatusUpdate update) async { + try { + if (update.task.metaData.isEmpty || update.task.metaData == '') { + return; + } + + final metadata = UploadTaskMetadata.fromJson(update.task.metaData); + if (!metadata.isLivePhotos) { + return; + } + + if (update.responseBody == null || update.responseBody!.isEmpty) { + return; + } + final response = jsonDecode(update.responseBody!); + + final localAsset = await _localAssetRepository.getById(metadata.localAssetId); + if (localAsset == null) { + return; + } + + final uploadTask = await _getLivePhotoUploadTask(localAsset, response['id'] as String); + + if (uploadTask == null) { + return; + } + + enqueueTasks([uploadTask]); + } catch (error, stackTrace) { + dPrint(() => "Error handling live photo upload task: $error $stackTrace"); + } + } + + Future _getUploadTaskWithFile(LocalAsset asset) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + final file = await _storageRepository.getFileForAsset(asset.id); + if (file == null) { + return null; + } + + final originalFileName = entity.isLivePhoto ? p.setExtension(asset.name, p.extension(file.path)) : asset.name; + + String metadata = UploadTaskMetadata( + localAssetId: asset.id, + isLivePhotos: entity.isLivePhoto, + livePhotoVideoId: '', + ).toJson(); + + return UploadTaskWithFile( + file: file, + task: await buildUploadTask( + file, + createdAt: asset.createdAt, + modifiedAt: asset.updatedAt, + originalFileName: originalFileName, + deviceAssetId: asset.id, + metadata: metadata, + group: "group", + priority: 0, + isFavorite: asset.isFavorite, + requiresWiFi: false, + ), + ); + } + + Future _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + File? file; + + /// iOS LivePhoto has two files: a photo and a video. + /// They are uploaded separately, with video file being upload first, then returned with the assetId + /// The assetId is then used as a metadata for the photo file upload task. + /// + /// We implement two separate upload groups for this, the normal one for the video file + /// and the higher priority group for the photo file because the video file is already uploaded. + /// + /// The cancel operation will only cancel the video group (normal group), the photo group will not + /// be touched, as the video file is already uploaded. + + if (entity.isLivePhoto) { + file = await _storageRepository.getMotionFileForAsset(asset); + } else { + file = await _storageRepository.getFileForAsset(asset.id); + } + + if (file == null) { + return null; + } + + final originalFileName = entity.isLivePhoto ? p.setExtension(asset.name, p.extension(file.path)) : asset.name; + + String metadata = UploadTaskMetadata( + localAssetId: asset.id, + isLivePhotos: entity.isLivePhoto, + livePhotoVideoId: '', + ).toJson(); + + final requiresWiFi = _shouldRequireWiFi(asset); + + return buildUploadTask( + file, + createdAt: asset.createdAt, + modifiedAt: asset.updatedAt, + originalFileName: originalFileName, + deviceAssetId: asset.id, + metadata: metadata, + group: group, + priority: priority, + isFavorite: asset.isFavorite, + requiresWiFi: requiresWiFi, + ); + } + + Future _getLivePhotoUploadTask(LocalAsset asset, String livePhotoVideoId) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + final file = await _storageRepository.getFileForAsset(asset.id); + if (file == null) { + return null; + } + + final fields = {'livePhotoVideoId': livePhotoVideoId}; + + final requiresWiFi = _shouldRequireWiFi(asset); + + return buildUploadTask( + file, + createdAt: asset.createdAt, + modifiedAt: asset.updatedAt, + originalFileName: asset.name, + deviceAssetId: asset.id, + fields: fields, + group: kBackupLivePhotoGroup, + priority: 0, // Highest priority to get upload immediately + isFavorite: asset.isFavorite, + requiresWiFi: requiresWiFi, + ); + } + + bool _shouldRequireWiFi(LocalAsset asset) { + bool requiresWiFi = true; + + if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) { + requiresWiFi = false; + } else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) { + requiresWiFi = false; + } + + return requiresWiFi; } Future buildUploadTask( File file, { required String group, + required DateTime createdAt, + required DateTime modifiedAt, Map? fields, String? originalFileName, String? deviceAssetId, String? metadata, int? priority, - }) async { - return _buildTask( - deviceAssetId ?? hash(file.path).toString(), - file, - fields: fields, - originalFileName: originalFileName, - metadata: metadata, - group: group, - priority: priority, - ); - } - - Future _buildTask( - String id, - File file, { - required String group, - Map? fields, - String? originalFileName, - String? metadata, - int? priority, + bool? isFavorite, + bool requiresWiFi = true, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); final deviceId = Store.get(StoreKey.deviceId); - - final (baseDirectory, directory, filename) = - await Task.split(filePath: file.path); - final stats = await file.stat(); - final fileCreatedAt = stats.changed; - final fileModifiedAt = stats.modified; + final (baseDirectory, directory, filename) = await Task.split(filePath: file.path); final fieldsMap = { 'filename': originalFileName ?? filename, - 'deviceAssetId': id, + 'deviceAssetId': deviceAssetId ?? '', 'deviceId': deviceId, - 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), - 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), - 'isFavorite': 'false', + 'fileCreatedAt': createdAt.toUtc().toIso8601String(), + 'fileModifiedAt': modifiedAt.toUtc().toIso8601String(), + 'isFavorite': isFavorite?.toString() ?? 'false', 'duration': '0', if (fields != null) ...fields, }; return UploadTask( - taskId: id, + taskId: deviceAssetId, displayName: originalFileName ?? filename, httpRequestMethod: 'POST', url: url, @@ -132,9 +431,63 @@ class UploadService { fileField: 'assetData', metaData: metadata ?? '', group: group, + requiresWiFi: requiresWiFi, priority: priority ?? 5, updates: Updates.statusAndProgress, retries: 3, ); } } + +class UploadTaskMetadata { + final String localAssetId; + final bool isLivePhotos; + final String livePhotoVideoId; + + const UploadTaskMetadata({required this.localAssetId, required this.isLivePhotos, required this.livePhotoVideoId}); + + UploadTaskMetadata copyWith({String? localAssetId, bool? isLivePhotos, String? livePhotoVideoId}) { + return UploadTaskMetadata( + localAssetId: localAssetId ?? this.localAssetId, + isLivePhotos: isLivePhotos ?? this.isLivePhotos, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + ); + } + + Map toMap() { + return { + 'localAssetId': localAssetId, + 'isLivePhotos': isLivePhotos, + 'livePhotoVideoId': livePhotoVideoId, + }; + } + + factory UploadTaskMetadata.fromMap(Map map) { + return UploadTaskMetadata( + localAssetId: map['localAssetId'] as String, + isLivePhotos: map['isLivePhotos'] as bool, + livePhotoVideoId: map['livePhotoVideoId'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory UploadTaskMetadata.fromJson(String source) => + UploadTaskMetadata.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'UploadTaskMetadata(localAssetId: $localAssetId, isLivePhotos: $isLivePhotos, livePhotoVideoId: $livePhotoVideoId)'; + + @override + bool operator ==(covariant UploadTaskMetadata other) { + if (identical(this, other)) return true; + + return other.localAssetId == localAssetId && + other.isLivePhotos == isLivePhotos && + other.livePhotoVideoId == livePhotoVideoId; + } + + @override + int get hashCode => localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode; +} diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index fb2022784f..23ec0aa770 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/repositories/widget.repository.dart'; final widgetServiceProvider = Provider((ref) { - return WidgetService( - ref.watch(widgetRepositoryProvider), - ); + return WidgetService(ref.watch(widgetRepositoryProvider)); }); class WidgetService { @@ -13,11 +11,15 @@ class WidgetService { const WidgetService(this._repository); - Future writeCredentials(String serverURL, String sessionKey) async { + Future writeCredentials(String serverURL, String sessionKey, String? customHeaders) async { await _repository.setAppGroupId(appShareGroupId); await _repository.saveData(kWidgetServerEndpoint, serverURL); await _repository.saveData(kWidgetAuthToken, sessionKey); + if (customHeaders != null && customHeaders.isNotEmpty) { + await _repository.saveData(kWidgetCustomHeaders, customHeaders); + } + // wait 3 seconds to ensure the widget is updated, dont block Future.delayed(const Duration(seconds: 3), refreshWidgets); } @@ -26,6 +28,7 @@ class WidgetService { await _repository.setAppGroupId(appShareGroupId); await _repository.saveData(kWidgetServerEndpoint, ""); await _repository.saveData(kWidgetAuthToken, ""); + await _repository.saveData(kWidgetCustomHeaders, ""); // wait 3 seconds to ensure the widget is updated, dont block Future.delayed(const Duration(seconds: 3), refreshWidgets); diff --git a/mobile/lib/theme/color_scheme.dart b/mobile/lib/theme/color_scheme.dart index c01b7cfa5a..f32b358ad9 100644 --- a/mobile/lib/theme/color_scheme.dart +++ b/mobile/lib/theme/color_scheme.dart @@ -6,10 +6,7 @@ final Map _themePresets = { ImmichColorPreset.indigo: ImmichTheme( light: ColorScheme.fromSeed( seedColor: immichBrandColorLight, - ).copyWith( - primary: immichBrandColorLight, - onSurface: const Color.fromARGB(255, 34, 31, 32), - ), + ).copyWith(primary: immichBrandColorLight, onSurface: const Color.fromARGB(255, 34, 31, 32)), dark: ColorScheme.fromSeed( seedColor: immichBrandColorDark, brightness: Brightness.dark, @@ -17,24 +14,15 @@ final Map _themePresets = { ), ImmichColorPreset.deepPurple: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF6F43C0)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3BBFF), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3BBFF), brightness: Brightness.dark), ), ImmichColorPreset.pink: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFED79B5), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5), brightness: Brightness.dark), ), ImmichColorPreset.red: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFC51C16)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3302F), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3302F), brightness: Brightness.dark), ), ImmichColorPreset.orange: ImmichTheme( light: ColorScheme.fromSeed( @@ -49,37 +37,22 @@ final Map _themePresets = { ), ImmichColorPreset.yellow: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFFFB400), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400), brightness: Brightness.dark), ), ImmichColorPreset.lime: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFCDDC39), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39), brightness: Brightness.dark), ), ImmichColorPreset.green: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF18C249), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249), brightness: Brightness.dark), ), ImmichColorPreset.cyan: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF00BCD4), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4), brightness: Brightness.dark), ), ImmichColorPreset.slateGray: ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: const Color(0xFF696969), - dynamicSchemeVariant: DynamicSchemeVariant.neutral, - ), + light: ColorScheme.fromSeed(seedColor: const Color(0xFF696969), dynamicSchemeVariant: DynamicSchemeVariant.neutral), dark: ColorScheme.fromSeed( seedColor: const Color(0xff696969), brightness: Brightness.dark, diff --git a/mobile/lib/theme/dynamic_theme.dart b/mobile/lib/theme/dynamic_theme.dart index 8ebf783469..d0cb8e646f 100644 --- a/mobile/lib/theme/dynamic_theme.dart +++ b/mobile/lib/theme/dynamic_theme.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:immich_mobile/theme/theme_data.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; abstract final class DynamicTheme { const DynamicTheme._(); @@ -13,23 +14,17 @@ abstract final class DynamicTheme { final corePalette = await DynamicColorPlugin.getCorePalette(); if (corePalette != null) { final primaryColor = corePalette.toColorScheme().primary; - debugPrint('dynamic_color: Core palette detected.'); + dPrint(() => 'dynamic_color: Core palette detected.'); // Some palettes do not generate surface container colors accurately, // so we regenerate all colors using the primary color _theme = ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.light, - ), - dark: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.dark, - ), + light: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.light), + dark: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.dark), ); } } catch (error) { - debugPrint('dynamic_color: Failed to obtain core palette: $error'); + dPrint(() => 'dynamic_color: Failed to obtain core palette: $error'); } } diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index a351b09093..8e3773839c 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -10,10 +10,7 @@ class ImmichTheme { const ImmichTheme({required this.light, required this.dark}); } -ThemeData getThemeData({ - required ColorScheme colorScheme, - required Locale locale, -}) { +ThemeData getThemeData({required ColorScheme colorScheme, required Locale locale}) { final isDark = colorScheme.brightness == Brightness.dark; return ThemeData( @@ -26,9 +23,7 @@ ThemeData getThemeData({ scaffoldBackgroundColor: colorScheme.surface, splashColor: colorScheme.primary.withValues(alpha: 0.1), highlightColor: colorScheme.primary.withValues(alpha: 0.1), - bottomSheetTheme: BottomSheetThemeData( - backgroundColor: colorScheme.surfaceContainer, - ), + bottomSheetTheme: BottomSheetThemeData(backgroundColor: colorScheme.surfaceContainer), fontFamily: _getFontFamilyFromLocale(locale), snackBarTheme: SnackBarThemeData( contentTextStyle: TextStyle( @@ -45,38 +40,19 @@ ThemeData getThemeData({ fontWeight: FontWeight.w600, fontSize: 18, ), - backgroundColor: - isDark ? colorScheme.surfaceContainer : colorScheme.surface, + backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, foregroundColor: colorScheme.primary, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, ), textTheme: const TextTheme( - displayLarge: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - displayMedium: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - displaySmall: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - titleSmall: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - ), - titleMedium: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w600, - ), - titleLarge: TextStyle( - fontSize: 26.0, - fontWeight: FontWeight.w600, - ), + displayLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + displayMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + displaySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + titleSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600), + titleMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600), + titleLarge: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w600), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( @@ -84,82 +60,43 @@ ThemeData getThemeData({ foregroundColor: isDark ? Colors.black87 : Colors.white, ), ), - chipTheme: const ChipThemeData( - side: BorderSide.none, - ), - sliderTheme: const SliderThemeData( - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), - trackHeight: 2.0, - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - type: BottomNavigationBarType.fixed, - ), + chipTheme: const ChipThemeData(side: BorderSide.none), + sliderTheme: const SliderThemeData(thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), trackHeight: 2.0), + bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed), popupMenuTheme: const PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), ), navigationBarTheme: NavigationBarThemeData( - backgroundColor: - isDark ? colorScheme.surfaceContainer : colorScheme.surface, - labelTextStyle: const WidgetStatePropertyAll( - TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), + backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, + labelTextStyle: const WidgetStatePropertyAll(TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), + borderSide: BorderSide(color: colorScheme.primary), borderRadius: const BorderRadius.all(Radius.circular(15)), ), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), - ), - textSelectionTheme: TextSelectionThemeData( - cursorColor: colorScheme.primary, + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), + textSelectionTheme: TextSelectionThemeData(cursorColor: colorScheme.primary), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ), inputDecorationTheme: InputDecorationTheme( - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), - ), + focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: colorScheme.primary)), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), ), dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer), @@ -176,9 +113,7 @@ ThemeData getThemeData({ // This method replaces all surface shades in ImmichTheme to a static ones // as we are creating the colorscheme through seedColor the default surfaces are // tinted with primary color -ImmichTheme decolorizeSurfaces({ - required ImmichTheme theme, -}) { +ImmichTheme decolorizeSurfaces({required ImmichTheme theme}) { return ImmichTheme( light: theme.light.copyWith( surface: const Color(0xFFf9f9f9), diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart new file mode 100644 index 0000000000..c5a2583531 --- /dev/null +++ b/mobile/lib/utils/action_button.utils.dart @@ -0,0 +1,160 @@ +import 'package:flutter/widgets.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/like_activity_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/unstack_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; + +class ActionButtonContext { + final BaseAsset asset; + final bool isOwner; + final bool isArchived; + final bool isTrashEnabled; + final bool isInLockedView; + final bool isStacked; + final RemoteAlbum? currentAlbum; + final bool advancedTroubleshooting; + final ActionSource source; + + const ActionButtonContext({ + required this.asset, + required this.isOwner, + required this.isArchived, + required this.isTrashEnabled, + required this.isStacked, + required this.isInLockedView, + required this.currentAlbum, + required this.advancedTroubleshooting, + required this.source, + }); +} + +enum ActionButtonType { + advancedInfo, + share, + shareLink, + archive, + unarchive, + download, + trash, + deletePermanent, + delete, + moveToLockFolder, + removeFromLockFolder, + deleteLocal, + upload, + removeFromAlbum, + unstack, + likeActivity; + + bool shouldShow(ActionButtonContext context) { + return switch (this) { + ActionButtonType.advancedInfo => context.advancedTroubleshooting, + ActionButtonType.share => true, + ActionButtonType.shareLink => + !context.isInLockedView && // + context.asset.hasRemote, + ActionButtonType.archive => + context.isOwner && // + !context.isInLockedView && // + context.asset.hasRemote && // + !context.isArchived, + ActionButtonType.unarchive => + context.isOwner && // + !context.isInLockedView && // + context.asset.hasRemote && // + context.isArchived, + ActionButtonType.download => + !context.isInLockedView && // + context.asset.hasRemote && // + !context.asset.hasLocal, + ActionButtonType.trash => + context.isOwner && // + !context.isInLockedView && // + context.asset.hasRemote && // + context.isTrashEnabled, + ActionButtonType.deletePermanent => + context.isOwner && // + context.asset.hasRemote && // + !context.isTrashEnabled || + context.isInLockedView, + ActionButtonType.delete => + context.isOwner && // + !context.isInLockedView && // + context.asset.hasRemote, + ActionButtonType.moveToLockFolder => + context.isOwner && // + !context.isInLockedView && // + context.asset.hasRemote, + ActionButtonType.removeFromLockFolder => + context.isOwner && // + context.isInLockedView && // + context.asset.hasRemote, + ActionButtonType.deleteLocal => + !context.isInLockedView && // + context.asset.hasLocal, + ActionButtonType.upload => + !context.isInLockedView && // + context.asset.storage == AssetState.local, + ActionButtonType.removeFromAlbum => + context.isOwner && // + !context.isInLockedView && // + context.currentAlbum != null, + ActionButtonType.unstack => + context.isOwner && // + !context.isInLockedView && // + context.isStacked, + ActionButtonType.likeActivity => + !context.isInLockedView && + context.currentAlbum != null && + context.currentAlbum!.isActivityEnabled && + context.currentAlbum!.isShared, + }; + } + + Widget buildButton(ActionButtonContext context) { + return switch (this) { + ActionButtonType.advancedInfo => AdvancedInfoActionButton(source: context.source), + ActionButtonType.share => ShareActionButton(source: context.source), + ActionButtonType.shareLink => ShareLinkActionButton(source: context.source), + ActionButtonType.archive => ArchiveActionButton(source: context.source), + ActionButtonType.unarchive => UnArchiveActionButton(source: context.source), + ActionButtonType.download => DownloadActionButton(source: context.source), + ActionButtonType.trash => TrashActionButton(source: context.source), + ActionButtonType.deletePermanent => DeletePermanentActionButton(source: context.source), + ActionButtonType.delete => DeleteActionButton(source: context.source), + ActionButtonType.moveToLockFolder => MoveToLockFolderActionButton(source: context.source), + ActionButtonType.removeFromLockFolder => RemoveFromLockFolderActionButton(source: context.source), + ActionButtonType.deleteLocal => DeleteLocalActionButton(source: context.source), + ActionButtonType.upload => UploadActionButton(source: context.source), + ActionButtonType.removeFromAlbum => RemoveFromAlbumActionButton( + albumId: context.currentAlbum!.id, + source: context.source, + ), + ActionButtonType.likeActivity => const LikeActivityActionButton(), + ActionButtonType.unstack => UnStackActionButton(source: context.source), + }; + } +} + +class ActionButtonBuilder { + static const List _actionTypes = ActionButtonType.values; + + static List build(ActionButtonContext context) { + return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList(); + } +} diff --git a/mobile/lib/utils/album_filter.utils.dart b/mobile/lib/utils/album_filter.utils.dart new file mode 100644 index 0000000000..02142b1571 --- /dev/null +++ b/mobile/lib/utils/album_filter.utils.dart @@ -0,0 +1,25 @@ +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; + +class AlbumFilter { + String? userId; + String? query; + QuickFilterMode mode; + + AlbumFilter({required this.mode, this.userId, this.query}); + + AlbumFilter copyWith({String? userId, String? query, QuickFilterMode? mode}) { + return AlbumFilter(userId: userId ?? this.userId, query: query ?? this.query, mode: mode ?? this.mode); + } +} + +class AlbumSort { + RemoteAlbumSortMode mode; + bool isReverse; + + AlbumSort({required this.mode, this.isReverse = false}); + + AlbumSort copyWith({RemoteAlbumSortMode? mode, bool? isReverse}) { + return AlbumSort(mode: mode ?? this.mode, isReverse: isReverse ?? this.isReverse); + } +} diff --git a/mobile/lib/utils/async_mutex.dart b/mobile/lib/utils/async_mutex.dart index c61e6b13f3..b97ab9b052 100644 --- a/mobile/lib/utils/async_mutex.dart +++ b/mobile/lib/utils/async_mutex.dart @@ -5,7 +5,7 @@ class AsyncMutex { Future _running = Future.value(null); int _enqueued = 0; - get enqueued => _enqueued; + int get enqueued => _enqueued; /// Execute [operation] exclusively, after any currently running operations. /// Returns a [Future] with the result of the [operation]. diff --git a/mobile/lib/utils/backup_progress.dart b/mobile/lib/utils/backup_progress.dart index 38cdeacdb2..36050f5e20 100644 --- a/mobile/lib/utils/backup_progress.dart +++ b/mobile/lib/utils/backup_progress.dart @@ -50,8 +50,7 @@ String humanReadableBytesProgress(int bytes, int bytesTotal) { } class ThrottleProgressUpdate { - ThrottleProgressUpdate(this._fun, Duration interval) - : _interval = interval.inMicroseconds; + ThrottleProgressUpdate(this._fun, Duration interval) : _interval = interval.inMicroseconds; final void Function(String?, int, int) _fun; final int _interval; int _invokedAt = 0; @@ -61,11 +60,7 @@ class ThrottleProgressUpdate { int progress = 0; int total = 0; - void call({ - final String? title, - final int progress = 0, - final int total = 0, - }) { + void call({final String? title, final int progress = 0, final int total = 0}) { final time = Timeline.now; this.title = title ?? this.title; this.progress = progress; diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 26f3b49242..c77ceaa62d 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -1,6 +1,9 @@ import 'dart:io'; +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -10,33 +13,69 @@ import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; +void configureFileDownloaderNotifications() { + FileDownloader().configureNotificationForGroup( + kDownloadGroupImage, + running: TaskNotification('downloading_media'.t(), '${'file_name'.t()}: {filename}'), + complete: TaskNotification('download_finished'.t(), '${'file_name'.t()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kDownloadGroupVideo, + running: TaskNotification('downloading_media'.t(), '${'file_name'.t()}: {filename}'), + complete: TaskNotification('download_finished'.t(), '${'file_name'.t()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), + complete: TaskNotification('upload_finished'.t(), 'backup_background_service_in_progress_notification'.t()), + groupNotificationId: kManualUploadGroup, + ); + + FileDownloader().configureNotificationForGroup( + kBackupGroup, + running: TaskNotification('uploading_media'.t(), 'backup_background_service_in_progress_notification'.t()), + complete: TaskNotification('upload_finished'.t(), 'backup_background_service_in_progress_notification'.t()), + groupNotificationId: kBackupGroup, + ); +} + abstract final class Bootstrap { - static Future initIsar() async { - if (Isar.getInstance() != null) { - return Isar.getInstance()!; + static Future<(Isar isar, Drift drift, DriftLogger logDb)> initDB() async { + final drift = Drift(); + final logDb = DriftLogger(); + + Isar? isar = Isar.getInstance(); + + if (isar != null) { + return (isar, drift, logDb); } final dir = await getApplicationDocumentsDirectory(); - return await Isar.open( + isar = await Isar.open( [ StoreValueSchema, - ExifInfoSchema, AssetSchema, AlbumSchema, + ExifInfoSchema, UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, if (Platform.isAndroid) AndroidDeviceAssetSchema, if (Platform.isIOS) IOSDeviceAssetSchema, @@ -46,16 +85,25 @@ abstract final class Bootstrap { maxSizeMiB: 2048, inspector: kDebugMode, ); + + return (isar, drift, logDb); } static Future initDomain( - Isar db, { + Isar db, + Drift drift, + DriftLogger logDb, { + bool listenStoreUpdates = true, bool shouldBufferLogs = true, }) async { - await StoreService.init(storeRepository: IsarStoreRepository(db)); + final isBeta = await IsarStoreRepository(db).tryGet(StoreKey.betaTimeline) ?? true; + final IStoreRepository storeRepo = isBeta ? DriftStoreRepository(drift) : IsarStoreRepository(db); + + await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates); + await LogService.init( - logRepository: IsarLogRepository(db), - storeRepository: IsarStoreRepository(db), + logRepository: LogRepository(logDb), + storeRepository: storeRepo, shouldBuffer: shouldBufferLogs, ); } diff --git a/mobile/lib/utils/cache/custom_image_cache.dart b/mobile/lib/utils/cache/custom_image_cache.dart index 8c70472765..a3905baf9b 100644 --- a/mobile/lib/utils/cache/custom_image_cache.dart +++ b/mobile/lib/utils/cache/custom_image_cache.dart @@ -1,6 +1,7 @@ import 'package:flutter/painting.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart'; import 'package:immich_mobile/providers/image/immich_local_image_provider.dart'; import 'package:immich_mobile/providers/image/immich_local_thumbnail_provider.dart'; import 'package:immich_mobile/providers/image/immich_remote_image_provider.dart'; @@ -9,6 +10,7 @@ import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.d /// [ImageCache] that uses two caches for small and large images /// so that a single large image does not evict all small images final class CustomImageCache implements ImageCache { + final _thumbhash = ImageCache()..maximumSize = 0; final _small = ImageCache(); final _large = ImageCache()..maximumSize = 5; // Maximum 5 images @@ -39,12 +41,16 @@ final class CustomImageCache implements ImageCache { /// Gets the cache for the given key /// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] /// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider] - ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider || - key is ImmichRemoteImageProvider || - key is LocalFullImageProvider || - key is RemoteFullImageProvider) - ? _large - : _small; + ImageCache _cacheForKey(Object key) { + return switch (key) { + ImmichLocalImageProvider() || + ImmichRemoteImageProvider() || + LocalFullImageProvider() || + RemoteFullImageProvider() => _large, + ThumbHashProvider() => _thumbhash, + _ => _small, + }; + } @override bool containsKey(Object key) { @@ -60,25 +66,21 @@ final class CustomImageCache implements ImageCache { int get currentSizeBytes => _small.currentSizeBytes + _large.currentSizeBytes; @override - bool evict(Object key, {bool includeLive = true}) => - _cacheForKey(key).evict(key, includeLive: includeLive); + bool evict(Object key, {bool includeLive = true}) => _cacheForKey(key).evict(key, includeLive: includeLive); @override int get liveImageCount => _small.liveImageCount + _large.liveImageCount; @override - int get pendingImageCount => - _small.pendingImageCount + _large.pendingImageCount; + int get pendingImageCount => _small.pendingImageCount + _large.pendingImageCount; @override ImageStreamCompleter? putIfAbsent( Object key, ImageStreamCompleter Function() loader, { ImageErrorListener? onError, - }) => - _cacheForKey(key).putIfAbsent(key, loader, onError: onError); + }) => _cacheForKey(key).putIfAbsent(key, loader, onError: onError); @override - ImageCacheStatus statusForKey(Object key) => - _cacheForKey(key).statusForKey(key); + ImageCacheStatus statusForKey(Object key) => _cacheForKey(key).statusForKey(key); } diff --git a/mobile/lib/utils/color_filter_generator.dart b/mobile/lib/utils/color_filter_generator.dart index c155823264..92aed4b1a0 100644 --- a/mobile/lib/utils/color_filter_generator.dart +++ b/mobile/lib/utils/color_filter_generator.dart @@ -27,9 +27,7 @@ class BrightnessFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.brightnessAdjustMatrix(brightness), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.brightnessAdjustMatrix(brightness)), child: child, ); } @@ -44,9 +42,7 @@ class SaturationFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.saturationAdjustMatrix(saturation), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.saturationAdjustMatrix(saturation)), child: child, ); } @@ -82,8 +78,7 @@ class _ColorFilterGenerator { ]; } - double x = - ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble(); + double x = ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble(); double lumR = 0.3086; double lumG = 0.6094; double lumB = 0.082; diff --git a/mobile/lib/utils/database.utils.dart b/mobile/lib/utils/database.utils.dart deleted file mode 100644 index 446b92db19..0000000000 --- a/mobile/lib/utils/database.utils.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:immich_mobile/domain/models/album/local_album.model.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; - -extension LocalAlbumEntityDataHelper on LocalAlbumEntityData { - LocalAlbum toDto({int assetCount = 0}) { - return LocalAlbum( - id: id, - name: name, - updatedAt: updatedAt, - assetCount: assetCount, - backupSelection: backupSelection, - ); - } -} - -extension LocalAssetEntityDataHelper on LocalAssetEntityData { - LocalAsset toDto() { - return LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - ); - } -} diff --git a/mobile/lib/utils/datetime_comparison.dart b/mobile/lib/utils/datetime_comparison.dart index 8c53ea45ba..f8ddcfea11 100644 --- a/mobile/lib/utils/datetime_comparison.dart +++ b/mobile/lib/utils/datetime_comparison.dart @@ -1,3 +1,2 @@ bool isAtSameMomentAs(DateTime? a, DateTime? b) => - (a == null && b == null) || - ((a != null && b != null) && a.isAtSameMomentAs(b)); + (a == null && b == null) || ((a != null && b != null) && a.isAtSameMomentAs(b)); diff --git a/mobile/lib/utils/datetime_helpers.dart b/mobile/lib/utils/datetime_helpers.dart new file mode 100644 index 0000000000..c13c8ca312 --- /dev/null +++ b/mobile/lib/utils/datetime_helpers.dart @@ -0,0 +1,19 @@ +const int _maxMillisecondsSinceEpoch = 8640000000000000; // 275760-09-13 +const int _minMillisecondsSinceEpoch = -62135596800000; // 0001-01-01 + +DateTime? tryFromSecondsSinceEpoch(int? secondsSinceEpoch, {bool isUtc = false}) { + if (secondsSinceEpoch == null) { + return null; + } + + final milliSeconds = secondsSinceEpoch * 1000; + if (milliSeconds < _minMillisecondsSinceEpoch || milliSeconds > _maxMillisecondsSinceEpoch) { + return null; + } + + try { + return DateTime.fromMillisecondsSinceEpoch(milliSeconds, isUtc: isUtc); + } catch (e) { + return null; + } +} diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index 78870151a6..4c80601424 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -19,8 +19,7 @@ class Debouncer { if (maxWaitTime != null && // _actionFuture == null && // TODO: should this check be here? - (_lastActionTime == null || - DateTime.now().difference(_lastActionTime!) > maxWaitTime!)) { + (_lastActionTime == null || DateTime.now().difference(_lastActionTime!) > maxWaitTime!)) { _callAndRest(); return; } @@ -28,8 +27,9 @@ class Debouncer { } Future? drain() { - if (_timer != null && _timer!.isActive) { - _timer!.cancel(); + final timer = _timer; + if (timer != null && timer.isActive) { + timer.cancel(); if (_lastAction != null) { _callAndRest(); } @@ -60,8 +60,7 @@ class Debouncer { _actionFuture = null; } - bool get isActive => - _actionFuture != null || (_timer != null && _timer!.isActive); + bool get isActive => _actionFuture != null || (_timer != null && _timer!.isActive); } /// Creates a [Debouncer] that will be disposed automatically. If no [interval] is provided, a @@ -70,21 +69,10 @@ Debouncer useDebouncer({ Duration interval = const Duration(milliseconds: 300), Duration? maxWaitTime, List? keys, -}) => - use( - _DebouncerHook( - interval: interval, - maxWaitTime: maxWaitTime, - keys: keys, - ), - ); +}) => use(_DebouncerHook(interval: interval, maxWaitTime: maxWaitTime, keys: keys)); class _DebouncerHook extends Hook { - const _DebouncerHook({ - required this.interval, - this.maxWaitTime, - super.keys, - }); + const _DebouncerHook({required this.interval, this.maxWaitTime, super.keys}); final Duration interval; final Duration? maxWaitTime; @@ -94,10 +82,7 @@ class _DebouncerHook extends Hook { } class _DebouncerHookState extends HookState { - late final debouncer = Debouncer( - interval: hook.interval, - maxWaitTime: hook.maxWaitTime, - ); + late final debouncer = Debouncer(interval: hook.interval, maxWaitTime: hook.maxWaitTime); @override Debouncer build(_) => debouncer; diff --git a/mobile/lib/utils/debug_print.dart b/mobile/lib/utils/debug_print.dart new file mode 100644 index 0000000000..21f55fc6a5 --- /dev/null +++ b/mobile/lib/utils/debug_print.dart @@ -0,0 +1,8 @@ +import 'package:flutter/foundation.dart'; + +@pragma('vm:prefer-inline') +void dPrint(String Function() message) { + if (kDebugMode) { + debugPrint(message()); + } +} diff --git a/mobile/lib/utils/download.dart b/mobile/lib/utils/download.dart deleted file mode 100644 index c701f353a2..0000000000 --- a/mobile/lib/utils/download.dart +++ /dev/null @@ -1,3 +0,0 @@ -const downloadGroupImage = 'group_image'; -const downloadGroupVideo = 'group_video'; -const downloadGroupLivePhoto = 'group_livephoto'; diff --git a/mobile/lib/utils/draggable_scroll_controller.dart b/mobile/lib/utils/draggable_scroll_controller.dart index 1d22905d1f..bab5214446 100644 --- a/mobile/lib/utils/draggable_scroll_controller.dart +++ b/mobile/lib/utils/draggable_scroll_controller.dart @@ -5,29 +5,20 @@ import 'package:flutter_hooks/flutter_hooks.dart'; /// /// See also: /// - [DraggableScrollableController] -DraggableScrollableController useDraggableScrollController({ - List? keys, -}) { - return use( - _DraggableScrollControllerHook( - keys: keys, - ), - ); +DraggableScrollableController useDraggableScrollController({List? keys}) { + return use(_DraggableScrollControllerHook(keys: keys)); } -class _DraggableScrollControllerHook - extends Hook { - const _DraggableScrollControllerHook({ - super.keys, - }); +class _DraggableScrollControllerHook extends Hook { + const _DraggableScrollControllerHook({super.keys}); @override - HookState> - createState() => _DraggableScrollControllerHookState(); + HookState> createState() => + _DraggableScrollControllerHookState(); } -class _DraggableScrollControllerHookState extends HookState< - DraggableScrollableController, _DraggableScrollControllerHook> { +class _DraggableScrollControllerHookState + extends HookState { late final controller = DraggableScrollableController(); @override diff --git a/mobile/lib/utils/hooks/app_settings_update_hook.dart b/mobile/lib/utils/hooks/app_settings_update_hook.dart index a4968feeae..954e44229a 100644 --- a/mobile/lib/utils/hooks/app_settings_update_hook.dart +++ b/mobile/lib/utils/hooks/app_settings_update_hook.dart @@ -3,16 +3,11 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -ValueNotifier useAppSettingsState( - AppSettingsEnum key, -) { +ValueNotifier useAppSettingsState(AppSettingsEnum key) { final notifier = useState(Store.get(key.storeKey, key.defaultValue)); // Listen to changes to the notifier and update app settings - useValueChanged( - notifier.value, - (_, __) => Store.put(key.storeKey, notifier.value), - ); + useValueChanged(notifier.value, (_, __) => Store.put(key.storeKey, notifier.value)); return notifier; } diff --git a/mobile/lib/utils/hooks/blurhash_hook.dart b/mobile/lib/utils/hooks/blurhash_hook.dart index 62208c4cf5..ac5fd31724 100644 --- a/mobile/lib/utils/hooks/blurhash_hook.dart +++ b/mobile/lib/utils/hooks/blurhash_hook.dart @@ -10,9 +10,7 @@ ObjectRef useBlurHashRef(Asset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbhash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbhash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } @@ -22,9 +20,7 @@ ObjectRef useDriftBlurHashRef(RemoteAsset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbHash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbHash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart index 04bc978754..663bca3dbf 100644 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ b/mobile/lib/utils/hooks/crop_controller_hook.dart @@ -4,9 +4,5 @@ import 'dart:ui'; // Import the dart:ui library for Rect /// A hook that provides a [CropController] instance. CropController useCropController() { - return useMemoized( - () => CropController( - defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), - ), - ); + return useMemoized(() => CropController(defaultCrop: const Rect.fromLTRB(0, 0, 1, 1))); } diff --git a/mobile/lib/utils/hooks/interval_hook.dart b/mobile/lib/utils/hooks/interval_hook.dart index 0c346065f7..907fbad102 100644 --- a/mobile/lib/utils/hooks/interval_hook.dart +++ b/mobile/lib/utils/hooks/interval_hook.dart @@ -8,11 +8,8 @@ void useInterval(Duration delay, VoidCallback callback) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer.periodic(delay, (_) => savedCallback.value()); - return timer.cancel; - }, - [delay], - ); + useEffect(() { + final timer = Timer.periodic(delay, (_) => savedCallback.value()); + return timer.cancel; + }, [delay]); } diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart index a78fed42c3..36b78d8631 100644 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ b/mobile/lib/utils/hooks/timer_hook.dart @@ -2,29 +2,17 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -RestartableTimer useTimer( - Duration duration, - void Function() callback, -) { - return use( - _TimerHook( - duration: duration, - callback: callback, - ), - ); +RestartableTimer useTimer(Duration duration, void Function() callback) { + return use(_TimerHook(duration: duration, callback: callback)); } class _TimerHook extends Hook { final Duration duration; final void Function() callback; - const _TimerHook({ - required this.duration, - required this.callback, - }); + const _TimerHook({required this.duration, required this.callback}); @override - HookState> createState() => - _TimerHookState(); + HookState> createState() => _TimerHookState(); } class _TimerHookState extends HookState { diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index f64757cf9d..a4c97a532f 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -10,11 +10,7 @@ class HttpSSLCertOverride extends HttpOverrides { final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride( - this._allowSelfSignedSSLCert, - this._serverHost, - this._clientCert, - ) { + HttpSSLCertOverride(this._allowSelfSignedSSLCert, this._serverHost, this._clientCert) { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { diff --git a/mobile/lib/utils/http_ssl_options.dart b/mobile/lib/utils/http_ssl_options.dart index 04c01d36d9..c4e2ad69f7 100644 --- a/mobile/lib/utils/http_ssl_options.dart +++ b/mobile/lib/utils/http_ssl_options.dart @@ -10,18 +10,17 @@ import 'package:logging/logging.dart'; class HttpSSLOptions { static const MethodChannel _channel = MethodChannel('immich/httpSSLOptions'); - static void apply() { + static void apply({bool applyNative = true}) { AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; - bool allowSelfSignedSSLCert = - Store.get(setting.storeKey as StoreKey, setting.defaultValue); - _apply(allowSelfSignedSSLCert); + bool allowSelfSignedSSLCert = Store.get(setting.storeKey as StoreKey, setting.defaultValue); + _apply(allowSelfSignedSSLCert, applyNative: applyNative); } static void applyFromSettings(bool newValue) { _apply(newValue); } - static void _apply(bool allowSelfSignedSSLCert) { + static void _apply(bool allowSelfSignedSSLCert, {bool applyNative = true}) { String? serverHost; if (allowSelfSignedSSLCert && Store.tryGet(StoreKey.currentUser) != null) { serverHost = Uri.parse(Store.tryGet(StoreKey.serverEndpoint) ?? "").host; @@ -29,19 +28,15 @@ class HttpSSLOptions { SSLClientCertStoreVal? clientCert = SSLClientCertStoreVal.load(); - HttpOverrides.global = - HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); + HttpOverrides.global = HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); - if (Platform.isAndroid) { - _channel.invokeMethod("apply", [ - allowSelfSignedSSLCert, - serverHost, - clientCert?.data, - clientCert?.password, - ]).onError((e, _) { - final log = Logger("HttpSSLOptions"); - log.severe('Failed to set SSL options', e.message); - }); + if (applyNative && Platform.isAndroid) { + _channel + .invokeMethod("apply", [allowSelfSignedSSLCert, serverHost, clientCert?.data, clientCert?.password]) + .onError((e, _) { + final log = Logger("HttpSSLOptions"); + log.severe('Failed to set SSL options', e.message); + }); } } } diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index bde50f3a90..21722cb901 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -5,24 +5,15 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:openapi/api.dart'; -String getThumbnailUrl( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrl(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailUrlForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKey( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKey(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailCacheKeyForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKeyForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKeyForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (type == AssetMediaSize.thumbnail) { return 'thumbnail-image-$id'; } else { @@ -30,30 +21,18 @@ String getThumbnailCacheKeyForRemoteId( } } -String getAlbumThumbnailUrl( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbnailUrl(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailUrlForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailUrlForRemoteId(album.thumbnail.value!.remoteId!, type: type); } -String getAlbumThumbNailCacheKey( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbNailCacheKey(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailCacheKeyForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailCacheKeyForRemoteId(album.thumbnail.value!.remoteId!, type: type); } String getOriginalUrlForRemoteId(final String id) { @@ -66,10 +45,7 @@ String getImageCacheKey(final Asset asset) { return '${isFromDto ? asset.remoteId : asset.id}_fullStage'; } -String getThumbnailUrlForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } diff --git a/mobile/lib/utils/immich_loading_overlay.dart b/mobile/lib/utils/immich_loading_overlay.dart index fcc47b1542..be49c3bae9 100644 --- a/mobile/lib/utils/immich_loading_overlay.dart +++ b/mobile/lib/utils/immich_loading_overlay.dart @@ -7,13 +7,9 @@ final _loadingEntry = OverlayEntry( builder: (context) => SizedBox.square( dimension: double.infinity, child: DecoratedBox( - decoration: - BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), + decoration: BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), child: const Center( - child: DelayedLoadingIndicator( - delay: Duration(seconds: 1), - fadeInDuration: Duration(milliseconds: 400), - ), + child: DelayedLoadingIndicator(delay: Duration(seconds: 1), fadeInDuration: Duration(milliseconds: 400)), ), ), ), @@ -30,8 +26,7 @@ class _LoadingOverlay extends Hook> { _LoadingOverlayState createState() => _LoadingOverlayState(); } -class _LoadingOverlayState - extends HookState, _LoadingOverlay> { +class _LoadingOverlayState extends HookState, _LoadingOverlay> { late final _isLoading = ValueNotifier(false)..addListener(_listener); OverlayEntry? _loadingOverlay; diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 3c2aeed756..1ccf00d58b 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -1,14 +1,16 @@ import 'dart:async'; import 'dart:ui'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:logging/logging.dart'; import 'package:worker_manager/worker_manager.dart'; @@ -16,8 +18,7 @@ class InvalidIsolateUsageException implements Exception { const InvalidIsolateUsageException(); @override - String toString() => - "IsolateHelper should only be used from the root isolate"; + String toString() => "IsolateHelper should only be used from the root isolate"; } // !! Should be used only from the root isolate @@ -31,58 +32,63 @@ Cancelable runInIsolateGentle({ } return workerManager.executeGentle((cancelledChecker) async { - BackgroundIsolateBinaryMessenger.ensureInitialized(token); - DartPluginRegistrant.ensureInitialized(); + T? result; + await runZonedGuarded( + () async { + BackgroundIsolateBinaryMessenger.ensureInitialized(token); + DartPluginRegistrant.ensureInitialized(); - final db = await Bootstrap.initIsar(); - await Bootstrap.initDomain(db, shouldBufferLogs: false); - final ref = ProviderContainer( - overrides: [ - // TODO: Remove once isar is removed - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - cancellationProvider.overrideWithValue(cancelledChecker), - ], - ); + final (isar, drift, logDb) = await Bootstrap.initDB(); + await Bootstrap.initDomain(isar, drift, logDb, shouldBufferLogs: false, listenStoreUpdates: false); + final ref = ProviderContainer( + overrides: [ + // TODO: Remove once isar is removed + dbProvider.overrideWithValue(isar), + isarProvider.overrideWithValue(isar), + cancellationProvider.overrideWithValue(cancelledChecker), + driftProvider.overrideWith(driftOverride(drift)), + ], + ); - Logger log = Logger("IsolateLogger"); + Logger log = Logger("IsolateLogger"); - try { - return await computation(ref); - } on CanceledError { - log.warning( - "Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}", - ); - } catch (error, stack) { - log.severe( - "Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", - error, - stack, - ); - } finally { - try { - await LogService.I.flushBuffer(); - await ref.read(driftProvider).close(); - - // Close Isar safely try { - final isar = ref.read(isarProvider); - if (isar.isOpen) { - await isar.close(); - } - } catch (e) { - debugPrint("Error closing Isar: $e"); - } + HttpSSLOptions.apply(applyNative: false); + result = await computation(ref); + } on CanceledError { + log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}"); + } catch (error, stack) { + log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); + } finally { + try { + ref.dispose(); - ref.dispose(); - } catch (error) { - debugPrint("Error closing resources in isolate: $error"); - } finally { - ref.dispose(); - // Delay to ensure all resources are released - await Future.delayed(const Duration(seconds: 2)); - } - } - return null; + await Store.dispose(); + await LogService.I.dispose(); + await logDb.close(); + await drift.close(); + + // Close Isar safely + try { + if (isar.isOpen) { + await isar.close(); + } + } catch (e) { + dPrint(() => "Error closing Isar: $e"); + } + } catch (error, stack) { + dPrint(() => "Error closing resources in isolate: $error, $stack"); + } finally { + ref.dispose(); + // Delay to ensure all resources are released + await Future.delayed(const Duration(seconds: 2)); + } + } + }, + (error, stack) { + dPrint(() => "Error in isolate $debugLabel zone: $error, $stack"); + }, + ); + return result; }); } diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 39443fb225..80e20b7c6c 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -48,21 +48,18 @@ class MapUtils { ); static Map _addFeature(MapMarker marker) => { - 'type': 'Feature', - 'id': marker.assetRemoteId, - 'geometry': { - 'type': 'Point', - 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], - }, - }; + 'type': 'Feature', + 'id': marker.assetRemoteId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], + }, + }; - static Map generateGeoJsonForMarkers( - List markers, - ) => - { - 'type': 'FeatureCollection', - 'features': markers.map(_addFeature).toList(), - }; + static Map generateGeoJsonForMarkers(List markers) => { + 'type': 'FeatureCollection', + 'features': markers.map(_addFeature).toList(), + }; static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ required BuildContext context, @@ -71,10 +68,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog( - context: context, - builder: (context) => _LocationServiceDisabledDialog(), - ); + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); return (null, LocationPermission.deniedForever); } @@ -91,12 +85,9 @@ class MapUtils { } } - if (permission == LocationPermission.denied || - permission == LocationPermission.deniedForever) { + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { // Open app settings only if you did not request for permission before - if (permission == LocationPermission.deniedForever && - !shouldRequestPermission && - !silent) { + if (permission == LocationPermission.deniedForever && !shouldRequestPermission && !silent) { await Geolocator.openAppSettings(); } return (null, LocationPermission.deniedForever); @@ -119,24 +110,24 @@ class MapUtils { class _LocationServiceDisabledDialog extends ConfirmDialog { _LocationServiceDisabledDialog() - : super( - title: 'map_location_service_disabled_title'.tr(), - content: 'map_location_service_disabled_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () async { - await Geolocator.openLocationSettings(); - }, - ); + : super( + title: 'map_location_service_disabled_title'.tr(), + content: 'map_location_service_disabled_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); } class _LocationPermissionDisabledDialog extends ConfirmDialog { _LocationPermissionDisabledDialog() - : super( - title: 'map_no_location_permission_title'.tr(), - content: 'map_no_location_permission_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () {}, - ); + : super( + title: 'map_no_location_permission_title'.tr(), + content: 'map_no_location_permission_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () {}, + ); } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index a95c376ac2..2ed6d9549f 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -4,15 +4,12 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; -import 'package:flutter/foundation.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.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'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/backup_album.entity.dart' - as isar_backup_album; +import 'package:immich_mobile/entities/backup_album.entity.dart' as isar_backup_album; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -21,21 +18,22 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; -import 'package:immich_mobile/providers/background_sync.provider.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:isar/isar.dart'; -import 'package:logging/logging.dart'; // ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 13; +const int targetVersion = 17; -Future migrateDatabaseIfNeeded(Isar db) async { +Future migrateDatabaseIfNeeded(Isar db, Drift drift) async { + final hasVersion = Store.tryGet(StoreKey.version) != null; final int version = Store.get(StoreKey.version, targetVersion); - if (version < 9) { await Store.put(StoreKey.version, targetVersion); final value = await db.storeValues.get(StoreKey.currentUser.id); @@ -44,8 +42,7 @@ Future migrateDatabaseIfNeeded(Isar db) async { if (id != null) { await db.writeTxn(() async { final user = await db.users.get(id); - await db.storeValues - .put(StoreValue(StoreKey.currentUser.id, strValue: user?.id)); + await db.storeValues.put(StoreValue(StoreKey.currentUser.id, strValue: user?.id)); }); } } @@ -60,6 +57,21 @@ Future migrateDatabaseIfNeeded(Isar db) async { await Store.put(StoreKey.photoManagerCustomFilter, true); } + // This means that the SQLite DB is just created and has no version + if (version < 14 || !hasVersion) { + await migrateStoreToSqlite(db, drift); + await Store.populateCache(); + } + + await handleBetaMigration(version, await _isNewInstallation(db, drift), SyncStreamRepository(drift)); + + if (version < 17 && Store.isBetaTimelineEnabled) { + final delay = Store.get(StoreKey.backupTriggerDelay, AppSettingsEnum.backupTriggerDelay.defaultValue); + if (delay >= 1000) { + await Store.put(StoreKey.backupTriggerDelay, (delay / 1000).toInt()); + } + } + if (targetVersion >= 12) { await Store.put(StoreKey.version, targetVersion); return; @@ -72,6 +84,66 @@ Future migrateDatabaseIfNeeded(Isar db) async { } } +Future handleBetaMigration(int version, bool isNewInstallation, SyncStreamRepository syncStreamRepository) async { + // Handle migration only for this version + // TODO: remove when old timeline is removed + final isBeta = Store.tryGet(StoreKey.betaTimeline); + final needBetaMigration = Store.tryGet(StoreKey.needBetaMigration); + if (version <= 15 && needBetaMigration == null) { + // For new installations, no migration needed + // For existing installations, only migrate if beta timeline is not enabled (null or false) + if (isNewInstallation || isBeta == true) { + await Store.put(StoreKey.needBetaMigration, false); + await Store.put(StoreKey.betaTimeline, true); + } else { + await Store.put(StoreKey.needBetaMigration, true); + } + } + + if (version > 15) { + if (isBeta == null || isBeta) { + await Store.put(StoreKey.needBetaMigration, false); + await Store.put(StoreKey.betaTimeline, true); + } else { + await Store.put(StoreKey.needBetaMigration, false); + } + } + + if (version < 16) { + await syncStreamRepository.reset(); + await Store.put(StoreKey.shouldResetSync, true); + } +} + +Future _isNewInstallation(Isar db, Drift drift) async { + try { + final isarUserCount = await db.users.count(); + if (isarUserCount > 0) { + return false; + } + + final isarAssetCount = await db.assets.count(); + if (isarAssetCount > 0) { + return false; + } + + final driftStoreCount = await drift.storeEntity.select().get().then((list) => list.length); + if (driftStoreCount > 0) { + return false; + } + + final driftAssetCount = await drift.localAssetEntity.select().get().then((list) => list.length); + if (driftAssetCount > 0) { + return false; + } + + return true; + } catch (error) { + dPrint(() => "[MIGRATION] Error checking if new installation: $error"); + return false; + } +} + Future _migrateTo(Isar db, int version) async { await Store.delete(StoreKey.assetETag); await db.writeTxn(() async { @@ -87,50 +159,34 @@ Future _migrateTo(Isar db, int version) async { Future _migrateDeviceAsset(Isar db) async { final ids = Platform.isAndroid ? (await db.androidDeviceAssets.where().findAll()) - .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) - .toList() - : (await db.iOSDeviceAssets.where().findAll()) - .map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)) - .toList(); + .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) + .toList() + : (await db.iOSDeviceAssets.where().findAll()).map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)).toList(); final PermissionState ps = await PhotoManager.requestPermissionExtend(); if (!ps.hasAccess) { - if (kDebugMode) { - debugPrint( - "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", - ); - } - + dPrint(() => "[MIGRATION] Photo library permission not granted. Skipping device asset migration."); return; } List<_DeviceAsset> localAssets = []; - final List paths = - await PhotoManager.getAssetPathList(onlyAll: true); + final List 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), - ) + 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 allDeviceAssets = - await albumWithAll.getAssetListRange(start: 0, end: assetCount); + final List allDeviceAssets = await albumWithAll.getAssetListRange(start: 0, end: assetCount); - localAssets = allDeviceAssets - .map((a) => _DeviceAsset(assetId: a.id, dateTime: a.modifiedDateTime)) - .toList(); + 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}"); + dPrint(() => "[MIGRATION] Device Asset Ids length - ${ids.length}"); + dPrint(() => "[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 toAdd = []; @@ -140,35 +196,19 @@ Future _migrateDeviceAsset(Isar db) async { compare: (a, b) => a.assetId.compareTo(b.assetId), both: (deviceAsset, asset) { toAdd.add( - DeviceAssetEntity( - assetId: deviceAsset.assetId, - hash: deviceAsset.hash!, - modifiedTime: asset.dateTime!, - ), + DeviceAssetEntity(assetId: deviceAsset.assetId, hash: deviceAsset.hash!, modifiedTime: asset.dateTime!), ); return false; }, onlyFirst: (deviceAsset) { - if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', - ); - } + dPrint(() => '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}'); }, onlySecond: (asset) { - if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', - ); - } + dPrint(() => '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}'); }, ); - if (kDebugMode) { - debugPrint( - "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", - ); - } + dPrint(() => "[MIGRATION] Total number of device assets migrated - ${toAdd.length}"); await db.writeTxn(() async { await db.deviceAssetEntitys.putAll(toAdd); @@ -182,57 +222,42 @@ Future migrateDeviceAssetToSqlite(Isar db, Drift drift) async { for (final deviceAsset in isarDeviceAssets) { batch.update( drift.localAssetEntity, - LocalAssetEntityCompanion( - checksum: Value(base64.encode(deviceAsset.hash)), - ), + LocalAssetEntityCompanion(checksum: Value(base64.encode(deviceAsset.hash))), where: (t) => t.id.equals(deviceAsset.assetId), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating device assets to SQLite: $error", - ); + dPrint(() => "[MIGRATION] Error while migrating device assets to SQLite: $error"); } } -Future migrateBackupAlbumsToSqlite( - Isar db, - Drift drift, -) async { +Future migrateBackupAlbumsToSqlite(Isar db, Drift drift) async { try { final isarBackupAlbums = await db.backupAlbums.where().findAll(); // Recents is a virtual album on Android, and we don't have it with the new sync // If recents is selected previously, select all albums during migration except the excluded ones if (Platform.isAndroid) { - final recentAlbum = - isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); + final recentAlbum = isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); if (recentAlbum != null) { await drift.localAlbumEntity.update().write( - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.selected), - ), - ); + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.selected)), + ); final excluded = isarBackupAlbums - .where( - (album) => - album.selection == isar_backup_album.BackupSelection.exclude, - ) + .where((album) => album.selection == isar_backup_album.BackupSelection.exclude) .map((album) => album.id) .toList(); await drift.batch((batch) async { for (final id in excluded) { batch.update( drift.localAlbumEntity, - const LocalAlbumEntityCompanion( - backupSelection: Value(BackupSelection.excluded), - ), + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.excluded)), where: (t) => t.id.equals(id), ); } }); + return; } - return; } await drift.batch((batch) { @@ -240,24 +265,51 @@ Future migrateBackupAlbumsToSqlite( batch.update( drift.localAlbumEntity, LocalAlbumEntityCompanion( - backupSelection: Value( - switch (album.selection) { - isar_backup_album.BackupSelection.none => BackupSelection.none, - isar_backup_album.BackupSelection.select => - BackupSelection.selected, - isar_backup_album.BackupSelection.exclude => - BackupSelection.excluded, - }, - ), + backupSelection: Value(switch (album.selection) { + isar_backup_album.BackupSelection.none => BackupSelection.none, + isar_backup_album.BackupSelection.select => BackupSelection.selected, + isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, + }), ), where: (t) => t.id.equals(album.id), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating backup albums to SQLite: $error", - ); + dPrint(() => "[MIGRATION] Error while migrating backup albums to SQLite: $error"); + } +} + +Future migrateStoreToSqlite(Isar db, Drift drift) async { + try { + final isarStoreValues = await db.storeValues.where().findAll(); + await drift.batch((batch) { + for (final storeValue in isarStoreValues) { + final companion = StoreEntityCompanion( + id: Value(storeValue.id), + stringValue: Value(storeValue.strValue), + intValue: Value(storeValue.intValue), + ); + batch.insert(drift.storeEntity, companion, onConflict: DoUpdate((_) => companion)); + } + }); + } catch (error) { + dPrint(() => "[MIGRATION] Error while migrating store values to SQLite: $error"); + } +} + +Future migrateStoreToIsar(Isar db, Drift drift) async { + try { + final driftStoreValues = await drift.storeEntity + .select() + .map((entity) => StoreValue(entity.id, intValue: entity.intValue, strValue: entity.stringValue)) + .get(); + + await db.writeTxn(() async { + await db.storeValues.putAll(driftStoreValues); + }); + } catch (error) { + dPrint(() => "[MIGRATION] Error while migrating store values to Isar: $error"); } } @@ -268,18 +320,3 @@ class _DeviceAsset { const _DeviceAsset({required this.assetId, this.hash, this.dateTime}); } - -Future runNewSync(WidgetRef ref, {bool full = false}) async { - ref.read(backupProvider.notifier).cancelBackup(); - - final backgroundManager = ref.read(backgroundSyncProvider); - Future.wait([ - backgroundManager.syncLocal(full: full).then( - (_) { - Logger("runNewSync").fine("Hashing assets after syncLocal"); - backgroundManager.hashAssets(); - }, - ), - backgroundManager.syncRemote(), - ]); -} diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 8e14d232f8..33199d5225 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -17,16 +17,8 @@ dynamic upgradeDto(dynamic value, String targetType) { break; case 'ServerConfigDto': if (value is Map) { - addDefault( - value, - 'mapLightStyleUrl', - 'https://tiles.immich.cloud/v1/style/light.json', - ); - addDefault( - value, - 'mapDarkStyleUrl', - 'https://tiles.immich.cloud/v1/style/dark.json', - ); + addDefault(value, 'mapLightStyleUrl', 'https://tiles.immich.cloud/v1/style/light.json'); + addDefault(value, 'mapDarkStyleUrl', 'https://tiles.immich.cloud/v1/style/dark.json'); } case 'UserResponseDto': if (value is Map) { @@ -36,6 +28,7 @@ dynamic upgradeDto(dynamic value, String targetType) { case 'AssetResponseDto': if (value is Map) { addDefault(value, 'visibility', 'timeline'); + addDefault(value, 'createdAt', DateTime.now().toIso8601String()); } break; case 'UserAdminResponseDto': @@ -48,6 +41,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'isOnboarded', false); } break; + case 'SyncUserV1': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + addDefault(value, 'hasProfileImage', false); + } } } diff --git a/mobile/lib/utils/people.utils.dart b/mobile/lib/utils/people.utils.dart new file mode 100644 index 0000000000..ddd1867269 --- /dev/null +++ b/mobile/lib/utils/people.utils.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; + +String formatAge(DateTime birthDate, DateTime referenceDate) { + int ageInYears = _calculateAge(birthDate, referenceDate); + int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); + + if (ageInMonths <= 11) { + return "person_age_months".t(args: {'months': ageInMonths.toString()}); + } else if (ageInMonths > 12 && ageInMonths <= 23) { + return "person_age_year_months".t(args: {'months': (ageInMonths - 12).toString()}); + } else { + return "person_age_years".t(args: {'years': ageInYears.toString()}); + } +} + +int _calculateAge(DateTime birthDate, DateTime referenceDate) { + int age = referenceDate.year - birthDate.year; + if (referenceDate.month < birthDate.month || + (referenceDate.month == birthDate.month && referenceDate.day < birthDate.day)) { + age--; + } + return age; +} + +int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { + return (referenceDate.year - birthDate.year) * 12 + + referenceDate.month - + birthDate.month - + (referenceDate.day < birthDate.day ? 1 : 0); +} + +Future showNameEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); +} + +Future showBirthdayEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonBirthdayEditForm(person: person); + }, + ); +} diff --git a/mobile/lib/utils/provider_utils.dart b/mobile/lib/utils/provider_utils.dart index 7af0e61d3c..6c2d6e0f11 100644 --- a/mobile/lib/utils/provider_utils.dart +++ b/mobile/lib/utils/provider_utils.dart @@ -4,6 +4,7 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/repositories/activity_api.repository.dart'; import 'package:immich_mobile/repositories/album_api.repository.dart'; import 'package:immich_mobile/repositories/asset_api.repository.dart'; +import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/person_api.repository.dart'; import 'package:immich_mobile/repositories/timeline.repository.dart'; @@ -17,4 +18,7 @@ void invalidateAllApiRepositoryProviders(WidgetRef ref) { ref.invalidate(assetApiRepositoryProvider); ref.invalidate(timelineRepositoryProvider); ref.invalidate(searchApiRepositoryProvider); + + // Drift + ref.invalidate(driftAlbumApiRepositoryProvider); } diff --git a/mobile/lib/utils/remote_album.utils.dart b/mobile/lib/utils/remote_album.utils.dart deleted file mode 100644 index 04184ee367..0000000000 --- a/mobile/lib/utils/remote_album.utils.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:immich_mobile/domain/models/album/album.model.dart'; - -typedef AlbumSortFn = List Function( - List albums, - bool isReverse, -); - -class _RemoteAlbumSortHandlers { - const _RemoteAlbumSortHandlers._(); - - static const AlbumSortFn created = _sortByCreated; - static List _sortByCreated( - List albums, - bool isReverse, - ) { - final sorted = albums.sortedBy((album) => album.createdAt); - return (isReverse ? sorted.reversed : sorted).toList(); - } - - static const AlbumSortFn title = _sortByTitle; - static List _sortByTitle( - List albums, - bool isReverse, - ) { - final sorted = albums.sortedBy((album) => album.name); - return (isReverse ? sorted.reversed : sorted).toList(); - } - - static const AlbumSortFn lastModified = _sortByLastModified; - static List _sortByLastModified( - List albums, - bool isReverse, - ) { - final sorted = albums.sortedBy((album) => album.updatedAt); - return (isReverse ? sorted.reversed : sorted).toList(); - } - - static const AlbumSortFn assetCount = _sortByAssetCount; - static List _sortByAssetCount( - List albums, - bool isReverse, - ) { - final sorted = - albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); - return (isReverse ? sorted.reversed : sorted).toList(); - } - - static const AlbumSortFn mostRecent = _sortByMostRecent; - static List _sortByMostRecent( - List albums, - bool isReverse, - ) { - final sorted = albums.sorted((a, b) { - // For most recent, we sort by updatedAt in descending order - return b.updatedAt.compareTo(a.updatedAt); - }); - return (isReverse ? sorted.reversed : sorted).toList(); - } - - static const AlbumSortFn mostOldest = _sortByMostOldest; - static List _sortByMostOldest( - List albums, - bool isReverse, - ) { - final sorted = albums.sorted((a, b) { - // For oldest, we sort by createdAt in ascending order - return a.createdAt.compareTo(b.createdAt); - }); - return (isReverse ? sorted.reversed : sorted).toList(); - } -} - -enum RemoteAlbumSortMode { - title("library_page_sort_title", _RemoteAlbumSortHandlers.title), - assetCount( - "library_page_sort_asset_count", - _RemoteAlbumSortHandlers.assetCount, - ), - lastModified( - "library_page_sort_last_modified", - _RemoteAlbumSortHandlers.lastModified, - ), - created("library_page_sort_created", _RemoteAlbumSortHandlers.created), - mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent), - mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest); - - final String key; - final AlbumSortFn sortFn; - - const RemoteAlbumSortMode(this.key, this.sortFn); -} diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index a5466c83a2..d128ef8fac 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -16,30 +16,21 @@ import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -void handleShareAssets( - WidgetRef ref, - BuildContext context, - Iterable selection, -) { +void handleShareAssets(WidgetRef ref, BuildContext context, Iterable selection) { showDialog( context: context, builder: (BuildContext buildContext) { - ref - .watch(shareServiceProvider) - .shareAssets(selection.toList(), context) - .then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -56,20 +47,12 @@ Future handleArchiveAssets( }) async { if (selection.isNotEmpty) { shouldArchive ??= !selection.every((a) => a.isArchived); - await ref - .read(assetProvider.notifier) - .toggleArchive(selection, shouldArchive); + await ref.read(assetProvider.notifier).toggleArchive(selection, shouldArchive); final message = shouldArchive - ? 'moved_to_archive' - .t(context: context, args: {'count': selection.length}) - : 'moved_to_library' - .t(context: context, args: {'count': selection.length}); + ? 'moved_to_archive'.t(context: context, args: {'count': selection.length}) + : 'moved_to_library'.t(context: context, args: {'count': selection.length}); if (context.mounted) { - ImmichToast.show( - context: context, - msg: message, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: message, gravity: toastGravity); } } } @@ -83,29 +66,19 @@ Future handleFavoriteAssets( }) async { if (selection.isNotEmpty) { shouldFavorite ??= !selection.every((a) => a.isFavorite); - await ref - .watch(assetProvider.notifier) - .toggleFavorite(selection, shouldFavorite); + await ref.watch(assetProvider.notifier).toggleFavorite(selection, shouldFavorite); final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; final toastMessage = shouldFavorite ? 'Added ${selection.length} $assetOrAssets to favorites' : 'Removed ${selection.length} $assetOrAssets from favorites'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: toastGravity); } } } -Future handleEditDateTime( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditDateTime(WidgetRef ref, BuildContext context, List selection) async { DateTime? initialDate; String? timeZone; Duration? offset; @@ -131,28 +104,17 @@ Future handleEditDateTime( ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); } -Future handleEditLocation( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditLocation(WidgetRef ref, BuildContext context, List selection) async { LatLng? initialLatLng; if (selection.length == 1) { final asset = selection.first; final assetWithExif = await ref.watch(assetServiceProvider).loadExif(asset); - if (assetWithExif.exifInfo?.latitude != null && - assetWithExif.exifInfo?.longitude != null) { - initialLatLng = LatLng( - assetWithExif.exifInfo!.latitude!, - assetWithExif.exifInfo!.longitude!, - ); + if (assetWithExif.exifInfo?.latitude != null && assetWithExif.exifInfo?.longitude != null) { + initialLatLng = LatLng(assetWithExif.exifInfo!.latitude!, assetWithExif.exifInfo!.longitude!); } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return; @@ -168,20 +130,14 @@ Future handleSetAssetsVisibility( List selection, ) async { if (selection.isNotEmpty) { - await ref - .watch(assetProvider.notifier) - .setLockedView(selection, visibility); + await ref.watch(assetProvider.notifier).setLockedView(selection, visibility); final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; final toastMessage = visibility == AssetVisibilityEnum.locked ? 'Added ${selection.length} $assetOrAssets to locked folder' : 'Removed ${selection.length} $assetOrAssets from locked folder'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: ToastGravity.BOTTOM); } } } diff --git a/mobile/lib/utils/throttle.dart b/mobile/lib/utils/throttle.dart index bc0dcf9e2f..8b41d92318 100644 --- a/mobile/lib/utils/throttle.dart +++ b/mobile/lib/utils/throttle.dart @@ -9,8 +9,7 @@ class Throttler { Throttler({required this.interval}); T? run(T Function() action) { - if (_lastActionTime == null || - (DateTime.now().difference(_lastActionTime!) > interval)) { + if (_lastActionTime == null || (DateTime.now().difference(_lastActionTime!) > interval)) { final response = action(); _lastActionTime = DateTime.now(); return response; @@ -26,17 +25,11 @@ class Throttler { /// Creates a [Throttler] that will be disposed automatically. If no [interval] is provided, a /// default interval of 300ms is used to throttle the function calls -Throttler useThrottler({ - Duration interval = const Duration(milliseconds: 300), - List? keys, -}) => +Throttler useThrottler({Duration interval = const Duration(milliseconds: 300), List? keys}) => use(_ThrottleHook(interval: interval, keys: keys)); class _ThrottleHook extends Hook { - const _ThrottleHook({ - required this.interval, - super.keys, - }); + const _ThrottleHook({required this.interval, super.keys}); final Duration interval; diff --git a/mobile/lib/utils/thumbnail_utils.dart b/mobile/lib/utils/thumbnail_utils.dart index 33dd916980..685dc2b1c2 100644 --- a/mobile/lib/utils/thumbnail_utils.dart +++ b/mobile/lib/utils/thumbnail_utils.dart @@ -3,17 +3,11 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -String getAltText( - ExifInfo? exifInfo, - DateTime fileCreatedAt, - AssetType type, - List peopleNames, -) { +String getAltText(ExifInfo? exifInfo, DateTime fileCreatedAt, AssetType type, List peopleNames) { if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) { return exifInfo.description!; } - final (template, args) = - getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames); + final (template, args) = getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames); return template.t(args: args); } @@ -42,14 +36,14 @@ String getAltText( 1 => "image_alt_text_date_place_1_person", 2 => "image_alt_text_date_place_2_people", 3 => "image_alt_text_date_place_3_people", - _ => "image_alt_text_date_place_4_or_more_people" + _ => "image_alt_text_date_place_4_or_more_people", }) : (switch (peopleNames.length) { 0 => "image_alt_text_date", 1 => "image_alt_text_date_1_person", 2 => "image_alt_text_date_2_people", 3 => "image_alt_text_date_3_people", - _ => "image_alt_text_date_4_or_more_people" + _ => "image_alt_text_date_4_or_more_people", }); return (template, args); } diff --git a/mobile/lib/utils/url_helper.dart b/mobile/lib/utils/url_helper.dart index 187026b53c..e3d5b8ed57 100644 --- a/mobile/lib/utils/url_helper.dart +++ b/mobile/lib/utils/url_helper.dart @@ -4,8 +4,7 @@ import 'package:punycode/punycode.dart'; String sanitizeUrl(String url) { // Add schema if none is set - final urlWithSchema = - url.trimLeft().startsWith(RegExp(r"https?://")) ? url : "https://$url"; + final urlWithSchema = url.trimLeft().startsWith(RegExp(r"https?://")) ? url : "https://$url"; // Remove trailing slash(es) return urlWithSchema.trimRight().replaceFirst(RegExp(r"/+$"), ""); @@ -45,13 +44,14 @@ String punycodeEncodeUrl(String serverUrl) { final serverUri = Uri.tryParse(serverUrl); if (serverUri == null || serverUri.host.isEmpty) return ''; - final encodedHost = Uri.decodeComponent(serverUri.host).split('.').map( - (segment) { - // If segment is already ASCII, then return as it is. - if (segment.runes.every((c) => c < 0x80)) return segment; - return 'xn--${punycodeEncode(segment)}'; - }, - ).join('.'); + final encodedHost = Uri.decodeComponent(serverUri.host) + .split('.') + .map((segment) { + // If segment is already ASCII, then return as it is. + if (segment.runes.every((c) => c < 0x80)) return segment; + return 'xn--${punycodeEncode(segment)}'; + }) + .join('.'); return serverUri.replace(host: encodedHost).toString(); } @@ -77,15 +77,16 @@ String? punycodeDecodeUrl(String? serverUrl) { final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null; if (serverUri == null || serverUri.host.isEmpty) return null; - final decodedHost = serverUri.host.split('.').map( - (segment) { - if (segment.toLowerCase().startsWith('xn--')) { - return punycodeDecode(segment.substring(4)); - } - // If segment is not punycode encoded, then return as it is. - return segment; - }, - ).join('.'); + final decodedHost = serverUri.host + .split('.') + .map((segment) { + if (segment.toLowerCase().startsWith('xn--')) { + return punycodeDecode(segment.substring(4)); + } + // If segment is not punycode encoded, then return as it is. + return segment; + }) + .join('.'); return Uri.decodeFull(serverUri.replace(host: decodedHost).toString()); } diff --git a/mobile/lib/utils/version_compatibility.dart b/mobile/lib/utils/version_compatibility.dart index 19d9aa38d4..fa8dfb0b9e 100644 --- a/mobile/lib/utils/version_compatibility.dart +++ b/mobile/lib/utils/version_compatibility.dart @@ -1,9 +1,4 @@ -String? getVersionCompatibilityMessage( - int appMajor, - int appMinor, - int serverMajor, - int serverMinor, -) { +String? getVersionCompatibilityMessage(int appMajor, int appMinor, int serverMajor, int serverMinor) { if (serverMajor != appMajor) { return 'Your app major version is not compatible with the server!'; } diff --git a/mobile/lib/widgets/activities/activity_text_field.dart b/mobile/lib/widgets/activities/activity_text_field.dart index ce4f1364a3..e3958b6287 100644 --- a/mobile/lib/widgets/activities/activity_text_field.dart +++ b/mobile/lib/widgets/activities/activity_text_field.dart @@ -13,32 +13,23 @@ class ActivityTextField extends HookConsumerWidget { final String? likeId; final Function(String) onSubmit; - const ActivityTextField({ - required this.onSubmit, - this.isEnabled = true, - this.likeId, - super.key, - }); + const ActivityTextField({required this.onSubmit, this.isEnabled = true, this.likeId, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final album = ref.watch(currentAlbumProvider)!; final asset = ref.watch(currentAssetProvider); - final activityNotifier = ref - .read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); + final activityNotifier = ref.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); final user = ref.watch(currentUserProvider); final inputController = useTextEditingController(); final inputFocusNode = useFocusNode(); final liked = likeId != null; // Show keyboard immediately on activities open - useEffect( - () { - inputFocusNode.requestFocus(); - return null; - }, - [], - ); + useEffect(() { + inputFocusNode.requestFocus(); + return null; + }, []); // Pass text to callback and reset controller void onEditingComplete() { @@ -71,31 +62,19 @@ class ActivityTextField extends HookConsumerWidget { prefixIcon: user != null ? Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: UserCircleAvatar( - user: user, - size: 30, - radius: 15, - ), + child: UserCircleAvatar(user: user, size: 30, radius: 15), ) : null, suffixIcon: Padding( padding: const EdgeInsets.only(right: 10), child: IconButton( - icon: Icon( - liked ? Icons.favorite_rounded : Icons.favorite_border_rounded, - ), + icon: Icon(liked ? Icons.favorite_rounded : Icons.favorite_border_rounded), onPressed: liked ? removeLike : addLike, ), ), suffixIconColor: liked ? Colors.red[700] : null, - hintText: !isEnabled - ? 'shared_album_activities_input_disable'.tr() - : 'say_something'.tr(), - hintStyle: TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - color: Colors.grey[600], - ), + hintText: !isEnabled ? 'shared_album_activities_input_disable'.tr() : 'say_something'.tr(), + hintStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 14, color: Colors.grey[600]), ), onEditingComplete: onEditingComplete, onTapOutside: (_) => inputFocusNode.unfocus(), diff --git a/mobile/lib/widgets/activities/activity_tile.dart b/mobile/lib/widgets/activities/activity_tile.dart index 2dd16b73cb..4b66bd5eaf 100644 --- a/mobile/lib/widgets/activities/activity_tile.dart +++ b/mobile/lib/widgets/activities/activity_tile.dart @@ -26,10 +26,7 @@ class ActivityTile extends HookConsumerWidget { ? Container( width: 44, alignment: Alignment.center, - child: Icon( - Icons.favorite_rounded, - color: Colors.red[700], - ), + child: Icon(Icons.favorite_rounded, color: Colors.red[700]), ) : UserCircleAvatar(user: activity.user), title: _ActivityTitle( @@ -38,11 +35,8 @@ class ActivityTile extends HookConsumerWidget { leftAlign: isLike || showAssetThumbnail, ), // No subtitle for like, so center title - titleAlignment: - !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center, - trailing: showAssetThumbnail - ? _ActivityAssetThumbnail(activity.assetId!) - : null, + titleAlignment: !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center, + trailing: showAssetThumbnail ? _ActivityAssetThumbnail(activity.assetId!) : null, subtitle: !isLike ? Text(activity.comment!) : null, ); } @@ -53,33 +47,19 @@ class _ActivityTitle extends StatelessWidget { final String createdAt; final bool leftAlign; - const _ActivityTitle({ - required this.userName, - required this.createdAt, - required this.leftAlign, - }); + const _ActivityTitle({required this.userName, required this.createdAt, required this.leftAlign}); @override Widget build(BuildContext context) { final textColor = context.isDarkTheme ? Colors.white : Colors.black; - final textStyle = context.textTheme.bodyMedium - ?.copyWith(color: textColor.withValues(alpha: 0.6)); + final textStyle = context.textTheme.bodyMedium?.copyWith(color: textColor.withValues(alpha: 0.6)); return Row( - mainAxisAlignment: - leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, + mainAxisAlignment: leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max, children: [ - Text( - userName, - style: textStyle, - overflow: TextOverflow.ellipsis, - ), - if (leftAlign) - Text( - " â€ĸ ", - style: textStyle, - ), + Text(userName, style: textStyle, overflow: TextOverflow.ellipsis), + if (leftAlign) Text(" â€ĸ ", style: textStyle), Expanded( child: Text( createdAt, @@ -106,9 +86,7 @@ class _ActivityAssetThumbnail extends StatelessWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(4)), image: DecorationImage( - image: ImmichRemoteThumbnailProvider( - assetId: assetId, - ), + image: ImmichRemoteThumbnailProvider(assetId: assetId), fit: BoxFit.cover, ), ), diff --git a/mobile/lib/widgets/activities/dismissible_activity.dart b/mobile/lib/widgets/activities/dismissible_activity.dart index b6c083f616..2f017d51ed 100644 --- a/mobile/lib/widgets/activities/dismissible_activity.dart +++ b/mobile/lib/widgets/activities/dismissible_activity.dart @@ -8,20 +8,13 @@ class DismissibleActivity extends StatelessWidget { final ActivityTile body; final Function(String)? onDismiss; - const DismissibleActivity( - this.activityId, - this.body, { - this.onDismiss, - super.key, - }); + const DismissibleActivity(this.activityId, this.body, {this.onDismiss, super.key}); @override Widget build(BuildContext context) { return Dismissible( key: Key(activityId), - dismissThresholds: const { - DismissDirection.horizontal: 0.7, - }, + dismissThresholds: const {DismissDirection.horizontal: 0.7}, direction: DismissDirection.horizontal, confirmDismiss: (direction) => onDismiss != null ? showDialog( @@ -51,10 +44,7 @@ class _DismissBackground extends StatelessWidget { final AlignmentDirectional alignment; final bool withDeleteIcon; - const _DismissBackground({ - required this.withDeleteIcon, - this.alignment = AlignmentDirectional.centerStart, - }); + const _DismissBackground({required this.withDeleteIcon, this.alignment = AlignmentDirectional.centerStart}); @override Widget build(BuildContext context) { @@ -64,10 +54,7 @@ class _DismissBackground extends StatelessWidget { child: withDeleteIcon ? const Padding( padding: EdgeInsets.all(15), - child: Icon( - Icons.delete_sweep_rounded, - color: Colors.black, - ), + child: Icon(Icons.delete_sweep_rounded, color: Colors.black), ) : null, ); diff --git a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart index c256c558d6..d8f6a8885a 100644 --- a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart +++ b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart @@ -17,46 +17,33 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { /// The asset to add to an album final List assets; - const AddToAlbumBottomSheet({ - super.key, - required this.assets, - }); + const AddToAlbumBottomSheet({super.key, required this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); final albumService = ref.watch(albumServiceProvider); - useEffect( - () { - // Fetch album updates, e.g., cover image - ref.read(albumProvider.notifier).refreshRemoteAlbums(); + useEffect(() { + // Fetch album updates, e.g., cover image + ref.read(albumProvider.notifier).refreshRemoteAlbums(); - return null; - }, - [], - ); + return null; + }, []); void addToAlbum(Album album) async { - final result = await albumService.addAssets( - album, - assets, - ); + final result = await albumService.addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } } @@ -66,10 +53,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { return Card( elevation: 0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)), ), child: CustomScrollView( slivers: [ @@ -80,33 +64,17 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), - const Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), + const Align(alignment: Alignment.center, child: CustomDraggingHandle()), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'add_to_album'.tr(), - style: context.textTheme.displayMedium, - ), + Text('add_to_album'.tr(), style: context.textTheme.displayMedium), TextButton.icon( - icon: Icon( - Icons.add, - color: context.primaryColor, - ), - label: Text( - 'common_create_new_album'.tr(), - style: TextStyle(color: context.primaryColor), - ), + icon: Icon(Icons.add, color: context.primaryColor), + label: Text('common_create_new_album'.tr(), style: TextStyle(color: context.primaryColor)), onPressed: () { - context.pushRoute( - CreateAlbumRoute( - assets: assets, - ), - ); + context.pushRoute(CreateAlbumRoute(assets: assets)); }, ), ], diff --git a/mobile/lib/widgets/album/add_to_album_sliverlist.dart b/mobile/lib/widgets/album/add_to_album_sliverlist.dart index 3472e2179b..defbd90388 100644 --- a/mobile/lib/widgets/album/add_to_album_sliverlist.dart +++ b/mobile/lib/widgets/album/add_to_album_sliverlist.dart @@ -25,13 +25,13 @@ class AddToAlbumSliverList extends HookConsumerWidget { final albumSortMode = ref.watch(albumSortByOptionsProvider); final albumSortIsReverse = ref.watch(albumSortOrderProvider); final sortedAlbums = albumSortMode.sortFn(albums, albumSortIsReverse); - final sortedSharedAlbums = - albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); + final sortedSharedAlbums = albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); return SliverList( - delegate: SliverChildBuilderDelegate( - childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), - (context, index) { + delegate: SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), ( + context, + index, + ) { // Build shared expander if (index == 0 && sortedSharedAlbums.isNotEmpty) { return Padding( @@ -47,9 +47,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { itemCount: sortedSharedAlbums.length, itemBuilder: (context, index) => AlbumThumbnailListTile( album: sortedSharedAlbums[index], - onTap: enabled - ? () => onAddToAlbum(sortedSharedAlbums[index]) - : () {}, + onTap: enabled ? () => onAddToAlbum(sortedSharedAlbums[index]) : () {}, ), ), ], @@ -60,10 +58,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { // Build albums list final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0); final album = sortedAlbums[offset]; - return AlbumThumbnailListTile( - album: album, - onTap: enabled ? () => onAddToAlbum(album) : () {}, - ); + return AlbumThumbnailListTile(album: album, onTap: enabled ? () => onAddToAlbum(album) : () {}); }), ); } diff --git a/mobile/lib/widgets/album/album_action_filled_button.dart b/mobile/lib/widgets/album/album_action_filled_button.dart index 48a8a27f59..04447ffab6 100644 --- a/mobile/lib/widgets/album/album_action_filled_button.dart +++ b/mobile/lib/widgets/album/album_action_filled_button.dart @@ -6,12 +6,7 @@ class AlbumActionFilledButton extends StatelessWidget { final String labelText; final IconData iconData; - const AlbumActionFilledButton({ - super.key, - this.onPressed, - required this.labelText, - required this.iconData, - }); + const AlbumActionFilledButton({super.key, this.onPressed, required this.labelText, required this.iconData}); @override Widget build(BuildContext context) { @@ -20,24 +15,12 @@ class AlbumActionFilledButton extends StatelessWidget { child: OutlinedButton.icon( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), backgroundColor: context.colorScheme.surfaceContainerHigh, ), - icon: Icon( - iconData, - size: 18, - color: context.primaryColor, - ), - label: Text( - labelText, - style: context.textTheme.labelLarge?.copyWith(), - ), + icon: Icon(iconData, size: 18, color: context.primaryColor), + label: Text(labelText, style: context.textTheme.labelLarge?.copyWith()), onPressed: onPressed, ), ); diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index d78f391754..6c56f5d843 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -16,13 +16,7 @@ class AlbumThumbnailCard extends ConsumerWidget { final bool showOwner; final bool showTitle; - const AlbumThumbnailCard({ - super.key, - required this.album, - this.onTap, - this.showOwner = false, - this.showTitle = true, - }); + const AlbumThumbnailCard({super.key, required this.album, this.onTap, this.showOwner = false, this.showTitle = true}); final Album album; @@ -36,24 +30,14 @@ class AlbumThumbnailCard extends ConsumerWidget { return Container( height: cardSize, width: cardSize, - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainerHigh, - ), + decoration: BoxDecoration(color: context.colorScheme.surfaceContainerHigh), child: Center( - child: Icon( - Icons.no_photography, - size: cardSize * .15, - color: context.colorScheme.primary, - ), + child: Icon(Icons.no_photography, size: cardSize * .15, color: context.colorScheme.primary), ), ); } - buildAlbumThumbnail() => ImmichThumbnail( - asset: album.thumbnail.value, - width: cardSize, - height: cardSize, - ); + buildAlbumThumbnail() => ImmichThumbnail(asset: album.thumbnail.value, width: cardSize, height: cardSize); buildAlbumTextRow() { // Add the owner name to the subtitle @@ -62,12 +46,7 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName!, - }, - ); + owner = 'shared_by_user'.t(context: context, args: {'user': album.ownerName!}); } } @@ -75,19 +54,12 @@ class AlbumThumbnailCard extends ConsumerWidget { TextSpan( children: [ TextSpan( - text: 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), + text: 'items_count'.t(context: context, args: {'count': album.assetCount}), ), if (owner != null) const TextSpan(text: ' â€ĸ '), if (owner != null) TextSpan(text: owner), ], - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), overflow: TextOverflow.fade, ); @@ -106,12 +78,8 @@ class AlbumThumbnailCard extends ConsumerWidget { width: cardSize, height: cardSize, child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: album.thumbnail.value == null - ? buildEmptyThumbnail() - : buildAlbumThumbnail(), + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), ), if (showTitle) ...[ diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index f35d4b7ede..423410eedf 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailListTile extends StatelessWidget { - const AlbumThumbnailListTile({ - super.key, - required this.album, - this.onTap, - }); + const AlbumThumbnailListTile({super.key, required this.album, this.onTap}); final Album album; final void Function()? onTap; @@ -26,15 +22,11 @@ class AlbumThumbnailListTile extends StatelessWidget { buildEmptyThumbnail() { return Container( - decoration: BoxDecoration( - color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200]), child: SizedBox( height: cardSize, width: cardSize, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ), ); } @@ -45,21 +37,17 @@ class AlbumThumbnailListTile extends StatelessWidget { height: cardSize, fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 200), - imageUrl: getAlbumThumbnailUrl( - album, - type: AssetMediaSize.thumbnail, - ), + imageUrl: getAlbumThumbnailUrl(album, type: AssetMediaSize.thumbnail), httpHeaders: ApiService.getRequestHeaders(), - cacheKey: - getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + cacheKey: getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ); } return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: onTap ?? + onTap: + onTap ?? () { context.pushRoute(AlbumViewerRoute(albumId: album.id)); }, @@ -70,9 +58,7 @@ class AlbumThumbnailListTile extends StatelessWidget { children: [ ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(8)), - child: album.thumbnail.value == null - ? buildEmptyThumbnail() - : buildAlbumThumbnail(), + child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), Expanded( child: Padding( @@ -83,37 +69,18 @@ class AlbumThumbnailListTile extends StatelessWidget { Text( album.name, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( - 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), - style: const TextStyle( - fontSize: 12, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: const TextStyle(fontSize: 12), ), if (album.shared) ...[ - const Text( - ' â€ĸ ', - style: TextStyle( - fontSize: 12, - ), - ), - Text( - 'shared'.tr(), - style: const TextStyle( - fontSize: 12, - ), - ), + const Text(' â€ĸ ', style: TextStyle(fontSize: 12)), + Text('shared'.tr(), style: const TextStyle(fontSize: 12)), ], ], ), diff --git a/mobile/lib/widgets/album/album_title_text_field.dart b/mobile/lib/widgets/album/album_title_text_field.dart index 7807a6e6ae..0a7438b7ae 100644 --- a/mobile/lib/widgets/album/album_title_text_field.dart +++ b/mobile/lib/widgets/album/album_title_text_field.dart @@ -31,11 +31,7 @@ class AlbumTitleTextField extends ConsumerWidget { ref.watch(albumTitleProvider.notifier).setAlbumTitle(v); }, focusNode: albumTitleTextFieldFocusNode, - style: TextStyle( - fontSize: 28, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; @@ -52,24 +48,17 @@ class AlbumTitleTextField extends ConsumerWidget { albumTitleController.clear(); isAlbumTitleEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), hintText: 'add_a_title'.tr(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( diff --git a/mobile/lib/widgets/album/album_viewer_appbar.dart b/mobile/lib/widgets/album/album_viewer_appbar.dart index 14715e40a9..420218d7e5 100644 --- a/mobile/lib/widgets/album/album_viewer_appbar.dart +++ b/mobile/lib/widgets/album/album_viewer_appbar.dart @@ -12,8 +12,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class AlbumViewerAppbar extends HookConsumerWidget - implements PreferredSizeWidget { +class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidget { const AlbumViewerAppbar({ super.key, required this.userId, @@ -53,13 +52,10 @@ class AlbumViewerAppbar extends HookConsumerWidget final newAlbumDescription = albumViewer.editDescriptionText; final isEditAlbum = albumViewer.isEditAlbum; - final comments = album.shared - ? ref.watch(activityStatisticsProvider(album.remoteId!)) - : 0; + final comments = album.shared ? ref.watch(activityStatisticsProvider(album.remoteId!)) : 0; deleteAlbum() async { - final bool success = - await ref.watch(albumProvider.notifier).deleteAlbum(album); + final bool success = await ref.watch(albumProvider.notifier).deleteAlbum(album); context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); @@ -86,10 +82,7 @@ class AlbumViewerAppbar extends HookConsumerWidget onPressed: () => context.pop('Cancel'), child: Text( 'cancel', - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( @@ -99,10 +92,7 @@ class AlbumViewerAppbar extends HookConsumerWidget }, child: Text( 'confirm', - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.colorScheme.error, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.colorScheme.error), ).tr(), ), ], @@ -112,8 +102,7 @@ class AlbumViewerAppbar extends HookConsumerWidget } void onLeaveAlbumPressed() async { - bool isSuccess = - await ref.watch(albumProvider.notifier).leaveAlbum(album); + bool isSuccess = await ref.watch(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); @@ -133,10 +122,7 @@ class AlbumViewerAppbar extends HookConsumerWidget album.ownerId == userId ? ListTile( leading: const Icon(Icons.delete_forever_rounded), - title: const Text( - 'delete_album', - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text('delete_album', style: TextStyle(fontWeight: FontWeight.w500)).tr(), onTap: onDeleteAlbumPressed, ) : ListTile( @@ -152,8 +138,7 @@ class AlbumViewerAppbar extends HookConsumerWidget } void onSortOrderToggled() async { - final updatedAlbum = - await ref.read(albumProvider.notifier).toggleSortOrder(album); + final updatedAlbum = await ref.read(albumProvider.notifier).toggleSortOrder(album); if (updatedAlbum == null) { ImmichToast.show( @@ -178,18 +163,12 @@ class AlbumViewerAppbar extends HookConsumerWidget onAddUsers(); } }, - title: const Text( - "album_viewer_page_share_add_users", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("album_viewer_page_share_add_users", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.swap_vert_rounded), onTap: onSortOrderToggled, - title: const Text( - "change_display_order", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("change_display_order", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.link_rounded), @@ -197,18 +176,12 @@ class AlbumViewerAppbar extends HookConsumerWidget context.pushRoute(SharedLinkEditRoute(albumId: album.remoteId)); context.pop(); }, - title: const Text( - "control_bottom_app_bar_share_link", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("control_bottom_app_bar_share_link", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.settings_rounded), onTap: () => context.navigateTo(const AlbumOptionsRoute()), - title: const Text( - "options", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("options", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; @@ -222,10 +195,7 @@ class AlbumViewerAppbar extends HookConsumerWidget onAddPhotos(); } }, - title: const Text( - "add_photos", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("add_photos", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; showModalBottomSheet( @@ -241,8 +211,7 @@ class AlbumViewerAppbar extends HookConsumerWidget children: [ ...buildBottomSheetActions(), if (onAddPhotos != null) ...commonActions, - if (onAddPhotos != null && userId == album.ownerId) - ...ownerActions, + if (onAddPhotos != null && userId == album.ownerId) ...ownerActions, ], ), ), @@ -257,18 +226,13 @@ class AlbumViewerAppbar extends HookConsumerWidget icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.mode_comment_outlined, - ), + const Icon(Icons.mode_comment_outlined), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), ], @@ -281,9 +245,7 @@ class AlbumViewerAppbar extends HookConsumerWidget return IconButton( onPressed: () async { if (newAlbumTitle.isNotEmpty) { - bool isSuccess = await ref - .watch(albumViewerProvider.notifier) - .changeAlbumTitle(album, newAlbumTitle); + bool isSuccess = await ref.watch(albumViewerProvider.notifier).changeAlbumTitle(album, newAlbumTitle); if (!isSuccess) { ImmichToast.show( context: context, @@ -330,14 +292,9 @@ class AlbumViewerAppbar extends HookConsumerWidget leading: buildLeadingButton(), centerTitle: false, actions: [ - if (album.shared && (album.activityEnabled || comments != 0)) - buildActivitiesButton(), + if (album.shared && (album.activityEnabled || comments != 0)) buildActivitiesButton(), if (album.isRemote) ...[ - IconButton( - splashRadius: 25, - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_horiz_rounded), - ), + IconButton(splashRadius: 25, onPressed: buildBottomSheet, icon: const Icon(Icons.more_horiz_rounded)), ], ], ); diff --git a/mobile/lib/widgets/album/album_viewer_editable_description.dart b/mobile/lib/widgets/album/album_viewer_editable_description.dart index b82e7f3d83..decd268ff3 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_description.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_description.dart @@ -8,40 +8,31 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableDescription extends HookConsumerWidget { final String albumDescription; final FocusNode descriptionFocusNode; - const AlbumViewerEditableDescription({ - super.key, - required this.albumDescription, - required this.descriptionFocusNode, - }); + const AlbumViewerEditableDescription({super.key, required this.albumDescription, required this.descriptionFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { final albumViewerState = ref.watch(albumViewerProvider); final descriptionTextEditController = useTextEditingController( - text: albumViewerState.isEditAlbum && - albumViewerState.editDescriptionText.isNotEmpty + text: albumViewerState.isEditAlbum && albumViewerState.editDescriptionText.isNotEmpty ? albumViewerState.editDescriptionText : albumDescription, ); void onFocusModeChange() { - if (!descriptionFocusNode.hasFocus && - descriptionTextEditController.text.isEmpty) { + if (!descriptionFocusNode.hasFocus && descriptionTextEditController.text.isEmpty) { ref.watch(albumViewerProvider.notifier).setEditDescriptionText(""); descriptionTextEditController.text = ""; } } - useEffect( - () { - descriptionFocusNode.addListener(onFocusModeChange); - return () { - descriptionFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + descriptionFocusNode.addListener(onFocusModeChange); + return () { + descriptionFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -49,9 +40,7 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onChanged: (value) { if (value.isEmpty) { } else { - ref - .watch(albumViewerProvider.notifier) - .setEditDescriptionText(value); + ref.watch(albumViewerProvider.notifier).setEditDescriptionText(value); } }, focusNode: descriptionFocusNode, @@ -62,9 +51,7 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onTap: () { context.focusScope.requestFocus(descriptionFocusNode); - ref - .watch(albumViewerProvider.notifier) - .setEditDescriptionText(albumDescription); + ref.watch(albumViewerProvider.notifier).setEditDescriptionText(albumDescription); ref.watch(albumViewerProvider.notifier).enableEditAlbum(); if (descriptionTextEditController.text == '') { @@ -78,19 +65,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onPressed: () { descriptionTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: descriptionFocusNode.hasFocus, diff --git a/mobile/lib/widgets/album/album_viewer_editable_title.dart b/mobile/lib/widgets/album/album_viewer_editable_title.dart index 038c9a13d8..c84e613017 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_title.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_title.dart @@ -8,19 +8,14 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { final String albumName; final FocusNode titleFocusNode; - const AlbumViewerEditableTitle({ - super.key, - required this.albumName, - required this.titleFocusNode, - }); + const AlbumViewerEditableTitle({super.key, required this.albumName, required this.titleFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { final albumViewerState = ref.watch(albumViewerProvider); final titleTextEditController = useTextEditingController( - text: albumViewerState.isEditAlbum && - albumViewerState.editTitleText.isNotEmpty + text: albumViewerState.isEditAlbum && albumViewerState.editTitleText.isNotEmpty ? albumViewerState.editTitleText : albumName, ); @@ -32,15 +27,12 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } } - useEffect( - () { - titleFocusNode.addListener(onFocusModeChange); - return () { - titleFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + titleFocusNode.addListener(onFocusModeChange); + return () { + titleFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -52,9 +44,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, focusNode: titleFocusNode, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), + style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700), controller: titleTextEditController, onTap: () { context.focusScope.requestFocus(titleFocusNode); @@ -67,35 +57,23 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0), suffixIcon: titleFocusNode.hasFocus ? IconButton( onPressed: () { titleTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: titleFocusNode.hasFocus, hintText: 'add_a_title'.tr(), - hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( - fontSize: 28, - ), + hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(fontSize: 28), ), ), ); diff --git a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart index dd1a64abe0..9f88b23f92 100644 --- a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart +++ b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart @@ -1,13 +1,13 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class RemoteAlbumSharedUserIcons extends ConsumerWidget { - const RemoteAlbumSharedUserIcons({ - super.key, - }); + const RemoteAlbumSharedUserIcons({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -16,8 +16,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { return const SizedBox(); } - final sharedUsersAsync = - ref.watch(remoteAlbumSharedUsersProvider(currentAlbum.id)); + final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(currentAlbum.id)); return sharedUsersAsync.maybeWhen( data: (sharedUsers) { @@ -25,22 +24,20 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { return const SizedBox(); } - return SizedBox( - height: 50, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemBuilder: ((context, index) { - return Padding( - padding: const EdgeInsets.only(right: 4.0), - child: UserCircleAvatar( - user: sharedUsers[index], - radius: 18, - size: 36, - hasBorder: true, - ), - ); - }), - itemCount: sharedUsers.length, + return GestureDetector( + onTap: () => context.pushRoute(const DriftAlbumOptionsRoute()), + child: SizedBox( + height: 50, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemBuilder: ((context, index) { + return Padding( + padding: const EdgeInsets.only(right: 4.0), + child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), + ); + }), + itemCount: sharedUsers.length, + ), ), ); }, diff --git a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart index e485763114..b21e86d145 100644 --- a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart +++ b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart @@ -14,15 +14,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { onTap: () { // debugPrint("View ${asset.id}"); }, - child: Stack( - children: [ - ImmichThumbnail( - asset: asset, - width: 500, - height: 500, - ), - ], - ), + child: Stack(children: [ImmichThumbnail(asset: asset, width: 500, height: 500)]), ); } } diff --git a/mobile/lib/widgets/asset_grid/asset_drag_region.dart b/mobile/lib/widgets/asset_grid/asset_drag_region.dart index 6335a1d64d..71e55acbd6 100644 --- a/mobile/lib/widgets/asset_grid/asset_drag_region.dart +++ b/mobile/lib/widgets/asset_grid/asset_drag_region.dart @@ -65,8 +65,7 @@ class _AssetDragRegionState extends State { Widget build(BuildContext context) { return RawGestureDetector( gestures: { - _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers< - _CustomLongPressGestureRecognizer>( + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( () => _CustomLongPressGestureRecognizer(), _registerCallbacks, ), @@ -89,9 +88,7 @@ class _AssetDragRegionState extends State { final local = box.globalToLocal(position); if (!box.hitTest(hitTestResult, position: local)) return null; - return (hitTestResult.path - .firstWhereOrNull((hit) => hit.target is _AssetIndexProxy) - ?.target as _AssetIndexProxy?) + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _AssetIndexProxy)?.target as _AssetIndexProxy?) ?.index; } @@ -99,8 +96,7 @@ class _AssetDragRegionState extends State { /// Calculate widget height and scroll offset when long press starting instead of in [initState] /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size final height = context.size?.height; - if (height != null && - (topScrollOffset == null || bottomScrollOffset == null)) { + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { topScrollOffset = height * scrollOffset; bottomScrollOffset = height - topScrollOffset!; } @@ -167,12 +163,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { final int rowIndex; final int sectionIndex; - const AssetIndexWrapper({ - required Widget super.child, - required this.rowIndex, - required this.sectionIndex, - super.key, - }); + const AssetIndexWrapper({required Widget super.child, required this.rowIndex, required this.sectionIndex, super.key}); @override // ignore: library_private_types_in_public_api @@ -188,27 +179,21 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { // ignore: library_private_types_in_public_api _AssetIndexProxy renderObject, ) { - renderObject.index = - AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex); + renderObject.index = AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex); } } class _AssetIndexProxy extends RenderProxyBox { AssetIndex index; - _AssetIndexProxy({ - required this.index, - }); + _AssetIndexProxy({required this.index}); } class AssetIndex { final int rowIndex; final int sectionIndex; - const AssetIndex({ - required this.rowIndex, - required this.sectionIndex, - }); + const AssetIndex({required this.rowIndex, required this.sectionIndex}); @override bool operator ==(covariant AssetIndex other) { diff --git a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart index 6b94c04c4d..d95d6efe2e 100644 --- a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart @@ -8,12 +8,7 @@ import 'package:logging/logging.dart'; final log = Logger('AssetGridDataStructure'); -enum RenderAssetGridElementType { - assets, - assetRow, - groupDividerTitle, - monthTitle; -} +enum RenderAssetGridElementType { assets, assetRow, groupDividerTitle, monthTitle } class RenderAssetGridElement { final RenderAssetGridElementType type; @@ -33,13 +28,7 @@ class RenderAssetGridElement { }); } -enum GroupAssetsBy { - day, - month, - auto, - none, - ; -} +enum GroupAssetsBy { day, month, auto, none } class RenderList { final List elements; @@ -53,8 +42,7 @@ class RenderList { /// global offset of assets in [_buf] int _bufOffset = 0; - RenderList(this.elements, this.query, this.allAssets) - : totalAssets = allAssets?.length ?? query!.countSync(); + RenderList(this.elements, this.query, this.allAssets) : totalAssets = allAssets?.length ?? query!.countSync(); bool get isEmpty => totalAssets == 0; @@ -88,12 +76,7 @@ class RenderList { // when scrolling backward, end shortly after the requested offset... // ... to guard against the user scrolling in the other direction // a tiny bit resulting in a another required load from the DB - final start = max( - 0, - forward - ? offset - oppositeSize - : (len > batchSize ? offset : offset + count - len), - ); + final start = max(0, forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len)); // load the calculated batch (start:start+len) from the DB and put it into the buffer _buf = query!.offset(start).limit(len).findAllSync(); _bufOffset = start; @@ -120,19 +103,14 @@ class RenderList { // request the asset from the database (not changing the buffer!) final asset = query!.offset(index).findFirstSync(); if (asset == null) { - throw Exception( - "Asset at index $index does no longer exist in database", - ); + throw Exception("Asset at index $index does no longer exist in database"); } return asset; } throw Exception("RenderList has neither assets nor query"); } - static Future fromQuery( - QueryBuilder query, - GroupAssetsBy groupBy, - ) => + static Future fromQuery(QueryBuilder query, GroupAssetsBy groupBy) => _buildRenderList(null, query, groupBy); static Future _buildRenderList( @@ -148,17 +126,10 @@ class RenderList { if (groupBy == GroupAssetsBy.none) { final int total = assets?.length ?? query!.countSync(); - final dateLoader = query != null - ? DateBatchLoader( - query: query, - batchSize: 1000 * sectionSize, - ) - : null; + final dateLoader = query != null ? DateBatchLoader(query: query, batchSize: 1000 * sectionSize) : null; for (int i = 0; i < total; i += sectionSize) { - final date = assets != null - ? assets[i].fileCreatedAt - : await dateLoader?.getDate(i); + final date = assets != null ? assets[i].fileCreatedAt : await dateLoader?.getDate(i); final int count = i + sectionSize > total ? total - i : sectionSize; if (date == null) break; @@ -175,11 +146,8 @@ class RenderList { return RenderList(elements, query, assets); } - final formatSameYear = - groupBy == GroupAssetsBy.month ? DateFormat.MMMM() : DateFormat.MMMEd(); - final formatOtherYear = groupBy == GroupAssetsBy.month - ? DateFormat.yMMMM() - : DateFormat.yMMMEd(); + final formatSameYear = groupBy == GroupAssetsBy.month ? DateFormat.MMMM() : DateFormat.MMMEd(); + final formatOtherYear = groupBy == GroupAssetsBy.month ? DateFormat.yMMMM() : DateFormat.yMMMEd(); final currentYear = DateTime.now().year; final formatMergedSameYear = DateFormat.MMMd(); final formatMergedOtherYear = DateFormat.yMMMd(); @@ -193,16 +161,9 @@ class RenderList { int lastMonthIndex = 0; String formatDateRange(DateTime from, DateTime to) { - final startDate = (from.year == currentYear - ? formatMergedSameYear - : formatMergedOtherYear) - .format(from); - final endDate = (to.year == currentYear - ? formatMergedSameYear - : formatMergedOtherYear) - .format(to); - if (DateTime(from.year, from.month, from.day) == - DateTime(to.year, to.month, to.day)) { + final startDate = (from.year == currentYear ? formatMergedSameYear : formatMergedOtherYear).format(from); + final endDate = (to.year == currentYear ? formatMergedSameYear : formatMergedOtherYear).format(to); + if (DateTime(from.year, from.month, from.day) == DateTime(to.year, to.month, to.day)) { // format range with time when both dates are on the same day final startTime = DateFormat.Hm().format(from); final endTime = DateFormat.Hm().format(to); @@ -212,10 +173,7 @@ class RenderList { } void mergeMonth() { - if (last != null && - groupBy == GroupAssetsBy.auto && - monthCount <= 30 && - elements.length > lastMonthIndex + 1) { + if (last != null && groupBy == GroupAssetsBy.auto && monthCount <= 30 && elements.length > lastMonthIndex + 1) { // merge all days into a single section assert(elements[lastMonthIndex].date.month == last.month); final e = elements[lastMonthIndex]; @@ -233,8 +191,7 @@ class RenderList { } void addElems(DateTime d, DateTime? prevDate) { - final bool newMonth = - last == null || last.year != d.year || last.month != d.month; + final bool newMonth = last == null || last.year != d.year || last.month != d.month; if (newMonth) { mergeMonth(); lastMonthIndex = elements.length; @@ -243,11 +200,11 @@ class RenderList { for (int j = 0; j < count; j += sectionSize) { final type = j == 0 ? (groupBy != GroupAssetsBy.month && newMonth - ? RenderAssetGridElementType.monthTitle - : RenderAssetGridElementType.groupDividerTitle) + ? RenderAssetGridElementType.monthTitle + : RenderAssetGridElementType.groupDividerTitle) : (groupBy == GroupAssetsBy.auto - ? RenderAssetGridElementType.groupDividerTitle - : RenderAssetGridElementType.assets); + ? RenderAssetGridElementType.groupDividerTitle + : RenderAssetGridElementType.assets); final sectionCount = j + sectionSize > count ? count - j : sectionSize; assert(sectionCount > 0 && sectionCount <= sectionSize); elements.add( @@ -258,12 +215,8 @@ class RenderList { totalCount: groupBy == GroupAssetsBy.auto ? sectionCount : count, offset: lastOffset + j, title: j == 0 - ? (d.year == currentYear - ? formatSameYear.format(d) - : formatOtherYear.format(d)) - : (groupBy == GroupAssetsBy.auto - ? formatDateRange(d, prevDate ?? d) - : null), + ? (d.year == currentYear ? formatSameYear.format(d) : formatOtherYear.format(d)) + : (groupBy == GroupAssetsBy.auto ? formatDateRange(d, prevDate ?? d) : null), ), ); } @@ -277,18 +230,10 @@ class RenderList { // TODO replace with groupBy once Isar supports such queries final dates = assets != null ? assets.map((a) => a.fileCreatedAt) - : await query! - .offset(offset) - .limit(pageSize) - .fileCreatedAtProperty() - .findAll(); + : await query!.offset(offset).limit(pageSize).fileCreatedAtProperty().findAll(); int i = 0; for (final date in dates) { - final d = DateTime( - date.year, - date.month, - groupBy == GroupAssetsBy.month ? 1 : date.day, - ); + final d = DateTime(date.year, date.month, groupBy == GroupAssetsBy.month ? 1 : date.day); current ??= d; if (current != d) { addElems(current, prevDate); @@ -315,10 +260,7 @@ class RenderList { static RenderList empty() => RenderList([], null, []); - static Future fromAssets( - List assets, - GroupAssetsBy groupBy, - ) => + static Future fromAssets(List assets, GroupAssetsBy groupBy) => _buildRenderList(assets, null, groupBy); /// Deletes an asset from the render list and clears the buffer @@ -337,10 +279,7 @@ class DateBatchLoader { List _buffer = []; int _bufferStart = 0; - DateBatchLoader({ - required this.query, - required this.batchSize, - }); + DateBatchLoader({required this.query, required this.batchSize}); Future getDate(int index) async { if (!_isIndexInBuffer(index)) { @@ -357,11 +296,7 @@ class DateBatchLoader { Future _loadBatch(int targetIndex) async { final batchStart = (targetIndex ~/ batchSize) * batchSize; - _buffer = await query - .offset(batchStart) - .limit(batchSize) - .fileCreatedAtProperty() - .findAll(); + _buffer = await query.offset(batchStart).limit(batchSize).fileCreatedAtProperty().findAll(); _bufferStart = batchStart; } diff --git a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart index 3283b90b21..cd2dc70dae 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -8,12 +8,14 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/album/add_to_album_sliverlist.dart'; +import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart'; import 'package:immich_mobile/models/asset_selection_state.dart'; import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart'; import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/widgets/common/drag_sheet.dart'; import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/draggable_scroll_controller.dart'; final controlBottomAppBarNotifier = ControlBottomAppBarNotifier(); @@ -45,6 +47,7 @@ class ControlBottomAppBar extends HookConsumerWidget { final bool unfavorite; final bool unarchive; final AssetSelectionState selectionAssetState; + final List selectedAssets; const ControlBottomAppBar({ super.key, @@ -64,6 +67,7 @@ class ControlBottomAppBar extends HookConsumerWidget { this.onRemoveFromAlbum, this.onToggleLocked, this.selectionAssetState = const AssetSelectionState(), + this.selectedAssets = const [], this.enabled = true, this.unarchive = false, this.unfavorite = false, @@ -71,57 +75,48 @@ class ControlBottomAppBar extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasRemote = - selectionAssetState.hasRemote || selectionAssetState.hasMerged; - final hasLocal = - selectionAssetState.hasLocal || selectionAssetState.hasMerged; - final trashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final hasRemote = selectionAssetState.hasRemote || selectionAssetState.hasMerged; + final hasLocal = selectionAssetState.hasLocal || selectionAssetState.hasMerged; + final trashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); - final sharedAlbums = - ref.watch(albumProvider).where((a) => a.shared).toList(); + final sharedAlbums = ref.watch(albumProvider).where((a) => a.shared).toList(); const bottomPadding = 0.24; final scrollController = useDraggableScrollController(); final isInLockedView = ref.watch(inLockedViewProvider); void minimize() { - scrollController.animateTo( - bottomPadding, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - ); + scrollController.animateTo(bottomPadding, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } - useEffect( - () { - controlBottomAppBarNotifier.addListener(minimize); - return () { - controlBottomAppBarNotifier.removeListener(minimize); - }; - }, - [], - ); + useEffect(() { + controlBottomAppBarNotifier.addListener(minimize); + return () { + controlBottomAppBarNotifier.removeListener(minimize); + }; + }, []); - void showForceDeleteDialog( - Function(bool) deleteCb, { - String? alertMsg, - }) { + void showForceDeleteDialog(Function(bool) deleteCb, {String? alertMsg}) { showDialog( context: context, builder: (BuildContext context) { - return DeleteDialog( - alert: alertMsg, - onDelete: () => deleteCb(true), - ); + return DeleteDialog(alert: alertMsg, onDelete: () => deleteCb(true)); }, ); } - void handleRemoteDelete( - bool force, - Function(bool) deleteCb, { - String? alertMsg, - }) { + /// Show existing AddToAlbumBottomSheet + void showAddToAlbumBottomSheet() { + showModalBottomSheet( + elevation: 0, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), + context: context, + builder: (BuildContext _) { + return AddToAlbumBottomSheet(assets: selectedAssets); + }, + ); + } + + void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) { if (!force) { deleteCb(force); return; @@ -132,9 +127,7 @@ class ControlBottomAppBar extends HookConsumerWidget { List renderActionButtons() { return [ ControlBoxButton( - iconData: Platform.isAndroid - ? Icons.share_rounded - : Icons.ios_share_rounded, + iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, label: "share".tr(), onPressed: enabled ? () => onShare(true) : null, ), @@ -144,29 +137,31 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "share_link".tr(), onPressed: enabled ? () => onShare(false) : null, ), + if (!isInLockedView && hasRemote && albums.isNotEmpty) + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 100), + child: ControlBoxButton( + iconData: Icons.photo_album, + label: "add_to_album".tr(), + onPressed: enabled ? showAddToAlbumBottomSheet : null, + ), + ), if (hasRemote && onArchive != null) ControlBoxButton( - iconData: - unarchive ? Icons.unarchive_outlined : Icons.archive_outlined, + iconData: unarchive ? Icons.unarchive_outlined : Icons.archive_outlined, label: (unarchive ? "unarchive" : "archive").tr(), onPressed: enabled ? onArchive : null, ), if (hasRemote && onFavorite != null) ControlBoxButton( - iconData: unfavorite - ? Icons.favorite_border_rounded - : Icons.favorite_rounded, + iconData: unfavorite ? Icons.favorite_border_rounded : Icons.favorite_rounded, label: (unfavorite ? "unfavorite" : "favorite").tr(), onPressed: enabled ? onFavorite : null, ), if (hasRemote && onDownload != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), - child: ControlBoxButton( - iconData: Icons.download, - label: "download".tr(), - onPressed: onDownload, - ), + child: ControlBoxButton(iconData: Icons.download, label: "download".tr(), onPressed: onDownload), ), if (hasLocal && hasRemote && onDelete != null && !isInLockedView) ConstrainedBox( @@ -174,11 +169,8 @@ class ControlBottomAppBar extends HookConsumerWidget { child: ControlBoxButton( iconData: Icons.delete_sweep_outlined, label: "delete".tr(), - onPressed: enabled - ? () => handleRemoteDelete(!trashEnabled, onDelete!) - : null, - onLongPressed: - enabled ? () => showForceDeleteDialog(onDelete!) : null, + onPressed: enabled ? () => handleRemoteDelete(!trashEnabled, onDelete!) : null, + onLongPressed: enabled ? () => showForceDeleteDialog(onDelete!) : null, ), ), if (hasRemote && onDeleteServer != null && !isInLockedView) @@ -190,17 +182,10 @@ class ControlBottomAppBar extends HookConsumerWidget { ? "control_bottom_app_bar_trash_from_immich".tr() : "control_bottom_app_bar_delete_from_immich".tr(), onPressed: enabled - ? () => handleRemoteDelete( - !trashEnabled, - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => handleRemoteDelete(!trashEnabled, onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, onLongPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -211,10 +196,7 @@ class ControlBottomAppBar extends HookConsumerWidget { iconData: Icons.delete_forever, label: "delete_dialog_title".tr(), onPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -233,9 +215,7 @@ class ControlBottomAppBar extends HookConsumerWidget { showDialog( context: context, builder: (BuildContext context) { - return DeleteLocalOnlyDialog( - onDeleteLocal: onDeleteLocal!, - ); + return DeleteLocalOnlyDialog(onDeleteLocal: onDeleteLocal!); }, ); } @@ -264,18 +244,12 @@ class ControlBottomAppBar extends HookConsumerWidget { ConstrainedBox( constraints: const BoxConstraints(maxWidth: 100), child: ControlBoxButton( - iconData: isInLockedView - ? Icons.lock_open_rounded - : Icons.lock_outline_rounded, - label: isInLockedView - ? "remove_from_locked_folder".tr() - : "move_to_locked_folder".tr(), + iconData: isInLockedView ? Icons.lock_open_rounded : Icons.lock_outline_rounded, + label: isInLockedView ? "remove_from_locked_folder".tr() : "move_to_locked_folder".tr(), onPressed: enabled ? onToggleLocked : null, ), ), - if (!selectionAssetState.hasLocal && - selectionAssetState.selectedCount > 1 && - onStack != null) + if (!selectionAssetState.hasLocal && selectionAssetState.selectedCount > 1 && onStack != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), child: ControlBoxButton( @@ -299,13 +273,11 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "upload".tr(), onPressed: enabled ? () => showDialog( - context: context, - builder: (BuildContext context) { - return UploadDialog( - onUpload: onUpload, - ); - }, - ) + context: context, + builder: (BuildContext context) { + return UploadDialog(onUpload: onUpload); + }, + ) : null, ), ]; @@ -343,10 +315,7 @@ class ControlBottomAppBar extends HookConsumerWidget { surfaceTintColor: context.colorScheme.surfaceContainerHigh, elevation: 6.0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), ), margin: const EdgeInsets.all(0), child: CustomScrollView( @@ -367,14 +336,8 @@ class ControlBottomAppBar extends HookConsumerWidget { ), ), if (hasRemote && !isInLockedView) ...[ - const Divider( - indent: 16, - endIndent: 16, - thickness: 1, - ), - _AddToAlbumTitleRow( - onCreateNewAlbum: enabled ? onCreateNewAlbum : null, - ), + const Divider(indent: 16, endIndent: 16, thickness: 1), + _AddToAlbumTitleRow(onCreateNewAlbum: enabled ? onCreateNewAlbum : null), ], ], ), @@ -398,9 +361,7 @@ class ControlBottomAppBar extends HookConsumerWidget { } class _AddToAlbumTitleRow extends StatelessWidget { - const _AddToAlbumTitleRow({ - required this.onCreateNewAlbum, - }); + const _AddToAlbumTitleRow({required this.onCreateNewAlbum}); final VoidCallback? onCreateNewAlbum; @@ -411,23 +372,13 @@ class _AddToAlbumTitleRow extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( onPressed: onCreateNewAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart index ecfb4130dc..e7c7775e54 100644 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/delete_dialog.dart @@ -5,22 +5,19 @@ import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; class DeleteDialog extends ConfirmDialog { const DeleteDialog({super.key, String? alert, required Function onDelete}) - : super( - title: "delete_dialog_title", - content: alert ?? "delete_dialog_alert", - cancel: "cancel", - ok: "delete", - onOk: onDelete, - ); + : super( + title: "delete_dialog_title", + content: alert ?? "delete_dialog_alert", + cancel: "cancel", + ok: "delete", + onOk: onDelete, + ); } class DeleteLocalOnlyDialog extends StatelessWidget { final void Function(bool onlyMerged) onDeleteLocal; - const DeleteLocalOnlyDialog({ - super.key, - required this.onDeleteLocal, - }); + const DeleteLocalOnlyDialog({super.key, required this.onDeleteLocal}); @override Widget build(BuildContext context) { @@ -35,9 +32,7 @@ class DeleteLocalOnlyDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: const Text("delete_dialog_title").tr(), content: const Text("delete_dialog_alert_local_non_backed_up").tr(), actions: [ @@ -45,30 +40,21 @@ class DeleteLocalOnlyDialog extends StatelessWidget { onPressed: () => context.pop(), child: Text( "cancel", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onDeleteBackedUpOnly, child: Text( "delete_local_dialog_ok_backed_up_only", - style: TextStyle( - color: context.colorScheme.tertiary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onForceDelete, child: Text( "delete_local_dialog_ok_force", - style: TextStyle( - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart index 50b38c2a4a..93a1d53f4e 100644 --- a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart +++ b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart @@ -3,11 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class DisableMultiSelectButton extends ConsumerWidget { - const DisableMultiSelectButton({ - super.key, - required this.onPressed, - required this.selectedItemCount, - }); + const DisableMultiSelectButton({super.key, required this.onPressed, required this.selectedItemCount}); final Function onPressed; final int selectedItemCount; @@ -22,16 +18,10 @@ class DisableMultiSelectButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 4.0), child: ElevatedButton.icon( onPressed: () => onPressed(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( '$selectedItemCount', - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ), ), diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart index 779da8add7..3de52c2816 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart @@ -3,14 +3,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(double offsetY); @@ -79,8 +80,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.arrows({ super.key, @@ -95,8 +96,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.semicircle({ super.key, @@ -111,12 +112,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -149,17 +146,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -180,9 +170,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -198,9 +186,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbArrowBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbArrowBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -216,9 +202,7 @@ class DraggableScrollbar extends StatefulWidget { width: 20.0, decoration: BoxDecoration( color: backgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(12.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), ), ); @@ -235,9 +219,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbRRectBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbRRectBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -250,11 +232,7 @@ class DraggableScrollbar extends StatefulWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(7.0)), - child: Container( - constraints: BoxConstraints.tight( - Size(16.0, height), - ), - ), + child: Container(constraints: BoxConstraints.tight(Size(16.0, height))), ); return buildScrollThumbAndLabel( @@ -276,8 +254,7 @@ class ScrollLabel extends StatelessWidget { final Text child; final BoxConstraints? constraints; - static const BoxConstraints _defaultConstraints = - BoxConstraints.tightFor(width: 72.0, height: 28.0); + static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); const ScrollLabel({ super.key, @@ -297,19 +274,14 @@ class ScrollLabel extends StatelessWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Container( - constraints: constraints ?? _defaultConstraints, - alignment: Alignment.center, - child: child, - ), + child: Container(constraints: constraints ?? _defaultConstraints, alignment: Alignment.center, child: child), ), ), ); } } -class DraggableScrollbarState extends State - with TickerProviderStateMixin { +class DraggableScrollbarState extends State with TickerProviderStateMixin { late double _barOffset; late double _viewOffset; late bool _isDragInProcess; @@ -327,25 +299,13 @@ class DraggableScrollbarState extends State _viewOffset = 0.0; _isDragInProcess = false; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -356,8 +316,7 @@ class DraggableScrollbarState extends State super.dispose(); } - double get barMaxScrollExtent => - context.size!.height - widget.heightScrollThumb; + double get barMaxScrollExtent => context.size!.height - widget.heightScrollThumb; double get barMinScrollExtent => 0; @@ -369,9 +328,7 @@ class DraggableScrollbarState extends State Widget build(BuildContext context) { Text? labelText; if (widget.labelTextBuilder != null && _isDragInProcess) { - labelText = widget.labelTextBuilder!( - _viewOffset + _barOffset + widget.heightScrollThumb / 2, - ); + labelText = widget.labelTextBuilder!(_viewOffset + _barOffset + widget.heightScrollThumb / 2); } return LayoutBuilder( @@ -385,9 +342,7 @@ class DraggableScrollbarState extends State }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -425,11 +380,7 @@ class DraggableScrollbarState extends State setState(() { if (notification is ScrollUpdateNotification) { - _barOffset += getBarDelta( - notification.scrollDelta!, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + _barOffset += getBarDelta(notification.scrollDelta!, barMaxScrollExtent, viewMaxScrollExtent); if (_barOffset < barMinScrollExtent) { _barOffset = barMinScrollExtent; @@ -447,8 +398,7 @@ class DraggableScrollbarState extends State } } - if (notification is ScrollUpdateNotification || - notification is OverscrollNotification) { + if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { if (_thumbAnimationController.status != AnimationStatus.forward) { _thumbAnimationController.forward(); } @@ -463,19 +413,11 @@ class DraggableScrollbarState extends State }); } - double getBarDelta( - double scrollViewDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getBarDelta(double scrollViewDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return scrollViewDelta * barMaxScrollExtent / viewMaxScrollExtent; } - double getScrollViewDelta( - double barDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getScrollViewDelta(double barDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return barDelta * viewMaxScrollExtent / barMaxScrollExtent; } @@ -502,11 +444,7 @@ class DraggableScrollbarState extends State _barOffset = barMaxScrollExtent; } - double viewDelta = getScrollViewDelta( - details.delta.dy, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -549,14 +487,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -587,10 +519,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -599,10 +528,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -617,27 +543,16 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, - builder: (context, child) => - animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart index 06954f4ff4..17f35311f0 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(int item); @@ -75,12 +76,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -113,17 +110,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -144,9 +134,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -169,8 +157,7 @@ class ScrollLabel extends StatelessWidget { final Text child; final BoxConstraints? constraints; - static const BoxConstraints _defaultConstraints = - BoxConstraints.tightFor(width: 72.0, height: 28.0); + static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); const ScrollLabel({ super.key, @@ -202,8 +189,7 @@ class ScrollLabel extends StatelessWidget { } } -class DraggableScrollbarState extends State - with TickerProviderStateMixin { +class DraggableScrollbarState extends State with TickerProviderStateMixin { late double _barOffset; late bool _isDragInProcess; late int _currentItem; @@ -221,25 +207,13 @@ class DraggableScrollbarState extends State _isDragInProcess = false; _currentItem = 0; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -250,10 +224,7 @@ class DraggableScrollbarState extends State super.dispose(); } - double get barMaxScrollExtent => - (context.size?.height ?? 0) - - widget.heightScrollThumb - - (widget.heightOffset ?? 0); + double get barMaxScrollExtent => (context.size?.height ?? 0) - widget.heightScrollThumb - (widget.heightOffset ?? 0); double get barMinScrollExtent => 0; @@ -277,9 +248,7 @@ class DraggableScrollbarState extends State }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -317,8 +286,7 @@ class DraggableScrollbarState extends State setState(() { try { - int firstItemIndex = - widget.itemPositionsListener.itemPositions.value.first.index; + int firstItemIndex = widget.itemPositionsListener.itemPositions.value.first.index; if (notification is ScrollUpdateNotification) { _barOffset = (firstItemIndex / maxItemCount) * barMaxScrollExtent; @@ -331,8 +299,7 @@ class DraggableScrollbarState extends State } } - if (notification is ScrollUpdateNotification || - notification is OverscrollNotification) { + if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { if (_thumbAnimationController.status != AnimationStatus.forward) { _thumbAnimationController.forward(); } @@ -377,16 +344,12 @@ class DraggableScrollbarState extends State /// If the bar is at the bottom but the item position is still smaller than the max item count (due to rounding error) /// jump to the end of the list if (barMaxScrollExtent - _barOffset < 10 && itemPosition < maxItemCount) { - widget.controller.jumpTo( - index: maxItemCount, - ); + widget.controller.jumpTo(index: maxItemCount); return; } - widget.controller.jumpTo( - index: itemPosition, - ); + widget.controller.jumpTo(index: itemPosition); } Timer? dragHaltTimer; @@ -412,12 +375,9 @@ class DraggableScrollbarState extends State dragHaltTimer?.cancel(); widget.scrollStateListener(true); - dragHaltTimer = Timer( - const Duration(milliseconds: 500), - () { - widget.scrollStateListener(false); - }, - ); + dragHaltTimer = Timer(const Duration(milliseconds: 500), () { + widget.scrollStateListener(false); + }); } _jumpToBarPosition(); @@ -458,14 +418,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -496,10 +450,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -508,10 +459,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -526,27 +474,16 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, - builder: (context, child) => - animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/group_divider_title.dart b/mobile/lib/widgets/asset_grid/group_divider_title.dart index b9fe8e3c1d..1464c941f0 100644 --- a/mobile/lib/widgets/asset_grid/group_divider_title.dart +++ b/mobile/lib/widgets/asset_grid/group_divider_title.dart @@ -30,14 +30,10 @@ class GroupDividerTitle extends HookConsumerWidget { final appSettingService = ref.watch(appSettingsServiceProvider); final groupBy = useState(GroupAssetsBy.day); - useEffect( - () { - groupBy.value = GroupAssetsBy.values[ - appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; - return null; - }, - [], - ); + useEffect(() { + groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return null; + }, []); void handleTitleIconClick() { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -60,9 +56,7 @@ class GroupDividerTitle extends HookConsumerWidget { Text( text, style: groupBy.value == GroupAssetsBy.month - ? context.textTheme.bodyLarge?.copyWith( - fontSize: 24.0, - ) + ? context.textTheme.bodyLarge?.copyWith(fontSize: 24.0) : context.textTheme.labelLarge?.copyWith( color: context.textTheme.labelLarge?.color?.withAlpha(250), fontWeight: FontWeight.w500, @@ -75,14 +69,12 @@ class GroupDividerTitle extends HookConsumerWidget { ? Icon( Icons.check_circle_rounded, color: context.primaryColor, - semanticLabel: - "unselect_all_in".tr(namedArgs: {"group": text}), + semanticLabel: "unselect_all_in".tr(namedArgs: {"group": text}), ) : Icon( Icons.check_circle_outline_rounded, color: context.colorScheme.onSurfaceSecondary, - semanticLabel: - "select_all_in".tr(namedArgs: {"group": text}), + semanticLabel: "select_all_in".tr(namedArgs: {"group": text}), ), ), ], diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index da4c47e466..ab6b350a7b 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -27,8 +27,7 @@ class ImmichAssetGrid extends HookConsumerWidget { final bool canDeselect; final bool? dynamicLayout; final bool showMultiSelectIndicator; - final void Function(Iterable itemPositions)? - visibleItemsListener; + final void Function(Iterable itemPositions)? visibleItemsListener; final Widget? topWidget; final bool shrinkWrap; final bool showDragScroll; @@ -61,9 +60,7 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var settings = ref.watch(appSettingsServiceProvider); - final perRow = useState( - assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!, - ); + final perRow = useState(assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!); final scaleFactor = useState(7.0 - perRow.value); final baseScaleFactor = useState(7.0 - perRow.value); @@ -82,39 +79,34 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget buildAssetGridView(RenderList renderList) { return RawGestureDetector( gestures: { - CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers< - CustomScaleGestureRecognizer>( - () => CustomScaleGestureRecognizer(), - (CustomScaleGestureRecognizer scale) { - scale.onStart = (details) { - baseScaleFactor.value = scaleFactor.value; - }; + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + baseScaleFactor.value = scaleFactor.value; + }; - scale.onUpdate = (details) { - scaleFactor.value = max( - min(5.0, baseScaleFactor.value * details.scale), - 1.0, - ); - if (7 - scaleFactor.value.toInt() != perRow.value) { - perRow.value = 7 - scaleFactor.value.toInt(); - settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); - } - }; - }), + scale.onUpdate = (details) { + scaleFactor.value = max(min(5.0, baseScaleFactor.value * details.scale), 1.0); + if (7 - scaleFactor.value.toInt() != perRow.value) { + perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); + } + }; + }, + ), }, child: ImmichAssetGridView( onRefresh: onRefresh, assetsPerRow: perRow.value, listener: listener, - showStorageIndicator: showStorageIndicator ?? - settings.getSetting(AppSettingsEnum.storageIndicator), + showStorageIndicator: showStorageIndicator ?? settings.getSetting(AppSettingsEnum.storageIndicator), renderList: renderList, margin: margin, selectionActive: selectionActive, preselectedAssets: preselectedAssets, canDeselect: canDeselect, - dynamicLayout: dynamicLayout ?? - settings.getSetting(AppSettingsEnum.dynamicLayout), + dynamicLayout: dynamicLayout ?? settings.getSetting(AppSettingsEnum.dynamicLayout), showMultiSelectIndicator: showMultiSelectIndicator, visibleItemsListener: visibleItemsListener, topWidget: topWidget, @@ -130,9 +122,7 @@ class ImmichAssetGrid extends HookConsumerWidget { if (renderList != null) return buildAssetGridView(renderList!); final renderListFuture = ref.watch(assetsTimelineProvider(assets!)); - return renderListFuture.widgetWhen( - onData: (renderList) => buildAssetGridView(renderList), - ); + return renderListFuture.widgetWhen(onData: (renderList) => buildAssetGridView(renderList)); } } 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 060898e270..7db03a33aa 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -34,10 +34,7 @@ import 'disable_multi_select_button.dart'; import 'draggable_scrollbar_custom.dart'; import 'group_divider_title.dart'; -typedef ImmichAssetGridSelectionListener = void Function( - bool, - Set, -); +typedef ImmichAssetGridSelectionListener = void Function(bool, Set); class ImmichAssetGridView extends ConsumerStatefulWidget { final RenderList renderList; @@ -51,8 +48,7 @@ class ImmichAssetGridView extends ConsumerStatefulWidget { final bool canDeselect; final bool dynamicLayout; final bool showMultiSelectIndicator; - final void Function(Iterable itemPositions)? - visibleItemsListener; + final void Function(Iterable itemPositions)? visibleItemsListener; final Widget? topWidget; final int heroOffset; final bool shrinkWrap; @@ -90,24 +86,20 @@ class ImmichAssetGridView extends ConsumerStatefulWidget { class ImmichAssetGridViewState extends ConsumerState { final ItemScrollController _itemScrollController = ItemScrollController(); - final ScrollOffsetController _scrollOffsetController = - ScrollOffsetController(); - final ItemPositionsListener _itemPositionsListener = - ItemPositionsListener.create(); + final ScrollOffsetController _scrollOffsetController = ScrollOffsetController(); + final ItemPositionsListener _itemPositionsListener = ItemPositionsListener.create(); late final KeepAliveLink currentAssetLink; /// The timestamp when the haptic feedback was last invoked int _hapticFeedbackTS = 0; DateTime? _prevItemTime; bool _scrolling = false; - final Set _selectedAssets = - LinkedHashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); + final Set _selectedAssets = LinkedHashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); bool _dragging = false; int? _dragAnchorAssetIndex; int? _dragAnchorSectionIndex; - final Set _draggedAssets = - HashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); + final Set _draggedAssets = HashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); ScrollPhysics? _scrollPhysics; @@ -131,9 +123,7 @@ class ImmichAssetGridViewState extends ConsumerState { void _deselectAssets(List assets) { final assetsToDeselect = assets.where( - (a) => - widget.canDeselect || - !(widget.preselectedAssets?.contains(a) ?? false), + (a) => widget.canDeselect || !(widget.preselectedAssets?.contains(a) ?? false), ); setState(() { @@ -152,9 +142,7 @@ class ImmichAssetGridViewState extends ConsumerState { _dragAnchorSectionIndex = null; _draggedAssets.clear(); _dragging = false; - if (!widget.canDeselect && - widget.preselectedAssets != null && - widget.preselectedAssets!.isNotEmpty) { + if (!widget.canDeselect && widget.preselectedAssets != null && widget.preselectedAssets!.isNotEmpty) { _selectedAssets.addAll(widget.preselectedAssets!); } _callSelectionListener(false); @@ -162,8 +150,7 @@ class ImmichAssetGridViewState extends ConsumerState { } bool _allAssetsSelected(List assets) { - return widget.selectionActive && - assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null; + return widget.selectionActive && assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null; } Future _scrollToIndex(int index) async { @@ -171,16 +158,10 @@ class ImmichAssetGridViewState extends ConsumerState { // the scroll_position widget crashes. This is a workaround to prevent this. // If the index is within the last 10 elements, we jump instead of scrolling. if (widget.renderList.elements.length <= index + 10) { - _itemScrollController.jumpTo( - index: index, - ); + _itemScrollController.jumpTo(index: index); return; } - await _itemScrollController.scrollTo( - index: index, - alignment: 0, - duration: const Duration(milliseconds: 500), - ); + await _itemScrollController.scrollTo(index: index, alignment: 0, duration: const Duration(milliseconds: 500)); } Widget _itemBuilder(BuildContext c, int position) { @@ -229,23 +210,16 @@ class ImmichAssetGridViewState extends ConsumerState { return Text( DateFormat.yMMMM().format(date), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ); } Widget _buildMultiSelectIndicator() { - return DisableMultiSelectButton( - onPressed: () => _deselectAll(), - selectedItemCount: _selectedAssets.length, - ); + return DisableMultiSelectButton(onPressed: () => _deselectAll(), selectedItemCount: _selectedAssets.length); } Widget _buildAssetGrid() { - final useDragScrolling = - widget.showDragScroll && widget.renderList.totalAssets >= 20; + final useDragScrolling = widget.showDragScroll && widget.renderList.totalAssets >= 20; void dragScrolling(bool active) { if (active != _scrolling) { @@ -256,24 +230,18 @@ class ImmichAssetGridViewState extends ConsumerState { } bool appBarOffset() { - return (ref.watch(tabProvider).index == 0 && - ModalRoute.of(context)?.settings.name == - TabControllerRoute.name) || + return (ref.watch(tabProvider).index == 0 && ModalRoute.of(context)?.settings.name == TabControllerRoute.name) || (ModalRoute.of(context)?.settings.name == AlbumViewerRoute.name); } final listWidget = ScrollablePositionedList.builder( - padding: EdgeInsets.only( - top: appBarOffset() ? 60 : 0, - bottom: 220, - ), + padding: EdgeInsets.only(top: appBarOffset() ? 60 : 0, bottom: 220), itemBuilder: _itemBuilder, itemPositionsListener: _itemPositionsListener, physics: _scrollPhysics, itemScrollController: _itemScrollController, scrollOffsetController: _scrollOffsetController, - itemCount: widget.renderList.elements.length + - (widget.topWidget != null ? 1 : 0), + itemCount: widget.renderList.elements.length + (widget.topWidget != null ? 1 : 0), addRepaintBoundaries: true, shrinkWrap: widget.shrinkWrap, ); @@ -287,9 +255,7 @@ class ImmichAssetGridViewState extends ConsumerState { ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary, labelTextBuilder: widget.showLabel ? _labelBuilder : null, - padding: appBarOffset() - ? const EdgeInsets.only(top: 60) - : const EdgeInsets.only(), + padding: appBarOffset() ? const EdgeInsets.only(top: 60) : const EdgeInsets.only(), heightOffset: appBarOffset() ? 60 : 0, labelConstraints: const BoxConstraints(maxHeight: 28), scrollbarAnimationDuration: const Duration(milliseconds: 300), @@ -301,12 +267,8 @@ class ImmichAssetGridViewState extends ConsumerState { return widget.onRefresh == null ? child : appBarOffset() - ? RefreshIndicator( - onRefresh: widget.onRefresh!, - edgeOffset: 30, - child: child, - ) - : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); + ? RefreshIndicator(onRefresh: widget.onRefresh!, edgeOffset: 30, child: child) + : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); } void _scrollToDate() { @@ -323,18 +285,13 @@ class ImmichAssetGridViewState extends ConsumerState { // Search for the index of the exact date in the list var index = widget.renderList.elements.indexWhere( - (e) => - e.date.year == date.year && - e.date.month == date.month && - e.date.day == date.day, + (e) => e.date.year == date.year && e.date.month == date.month && e.date.day == date.day, ); // If the exact date is not found, the timeline is grouped by month, // thus we search for the month if (index == -1) { - index = widget.renderList.elements.indexWhere( - (e) => e.date.year == date.year && e.date.month == date.month, - ); + index = widget.renderList.elements.indexWhere((e) => e.date.year == date.year && e.date.month == date.month); } if (index < widget.renderList.elements.length) { @@ -343,8 +300,7 @@ class ImmichAssetGridViewState extends ConsumerState { } else { ImmichToast.show( context: context, - msg: - "The date (${DateFormat.yMd().format(date)}) could not be found in the timeline.", + msg: "The date (${DateFormat.yMd().format(date)}) could not be found in the timeline.", gravity: ToastGravity.BOTTOM, toastType: ToastType.error, ); @@ -417,8 +373,7 @@ class ImmichAssetGridViewState extends ConsumerState { // on startup. if (_prevItemTime == null) { _prevItemTime = date; - } else if (_prevItemTime?.year != date.year || - _prevItemTime?.month != date.month) { + } else if (_prevItemTime?.year != date.year || _prevItemTime?.month != date.month) { _prevItemTime = date; final now = Timeline.now; @@ -433,13 +388,8 @@ class ImmichAssetGridViewState extends ConsumerState { void _scrollToTop() { // for some reason, this is necessary as well in order // to correctly reposition the drag thumb scroll bar - _itemScrollController.jumpTo( - index: 0, - ); - _itemScrollController.scrollTo( - index: 0, - duration: const Duration(milliseconds: 200), - ); + _itemScrollController.jumpTo(index: 0); + _itemScrollController.scrollTo(index: 0, duration: const Duration(milliseconds: 200)); } void _setDragStartIndex(AssetIndex index) { @@ -511,17 +461,13 @@ class ImmichAssetGridViewState extends ConsumerState { final selectedAssets = {}; var currentSectionIndex = startSectionIndex; while (currentSectionIndex < endSectionIndex) { - final section = - widget.renderList.elements.elementAtOrNull(currentSectionIndex); + final section = widget.renderList.elements.elementAtOrNull(currentSectionIndex); if (section == null) continue; - final sectionAssets = - widget.renderList.loadAssets(section.offset, section.count); + final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (currentSectionIndex == startSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, sectionAssets.length), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, sectionAssets.length)); } else { selectedAssets.addAll(sectionAssets); } @@ -531,16 +477,11 @@ class ImmichAssetGridViewState extends ConsumerState { final section = widget.renderList.elements.elementAtOrNull(endSectionIndex); if (section != null) { - final sectionAssets = - widget.renderList.loadAssets(section.offset, section.count); + final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (startSectionIndex == endSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1)); } else { - selectedAssets.addAll( - sectionAssets.slice(0, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(0, endSectionAssetIndex + 1)); } } @@ -562,8 +503,7 @@ class ImmichAssetGridViewState extends ConsumerState { /// "add to album" button. /// /// `_selectedAssets` includes `preselectedAssets` on initialization. - if (_selectedAssets.length > - (widget.preselectedAssets?.length ?? 0)) { + if (_selectedAssets.length > (widget.preselectedAssets?.length ?? 0)) { /// `_deselectAll` only deselects the selected assets, /// doesn't affect the preselected ones. _deselectAll(); @@ -580,13 +520,11 @@ class ImmichAssetGridViewState extends ConsumerState { onAssetEnter: _handleDragAssetEnter, onEnd: _stopDrag, onScroll: _dragDragScroll, - onScrollStart: () => WidgetsBinding.instance.addPostFrameCallback( - (_) => controlBottomAppBarNotifier.minimize(), - ), + onScrollStart: () => + WidgetsBinding.instance.addPostFrameCallback((_) => controlBottomAppBarNotifier.minimize()), child: _buildAssetGrid(), ), - if (widget.showMultiSelectIndicator && widget.selectionActive) - _buildMultiSelectIndicator(), + if (widget.showMultiSelectIndicator && widget.selectionActive) _buildMultiSelectIndicator(), ], ), ); @@ -617,10 +555,7 @@ class _PlaceholderRow extends StatelessWidget { key: ValueKey(i), width: width, height: height, - margin: EdgeInsets.only( - bottom: margin, - right: i + 1 == number ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: i + 1 == number ? 0.0 : margin), ), ], ); @@ -666,31 +601,23 @@ class _Section extends StatelessWidget { }); @override - Widget build( - BuildContext context, - ) { + Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { - final width = constraints.maxWidth / assetsPerRow - - margin * (assetsPerRow - 1) / assetsPerRow; + final width = constraints.maxWidth / assetsPerRow - margin * (assetsPerRow - 1) / assetsPerRow; final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow; - final List assetsToRender = scrolling - ? [] - : renderList.loadAssets(section.offset, section.count); + final List assetsToRender = scrolling ? [] : renderList.loadAssets(section.offset, section.count); return Column( key: ValueKey(section.offset), crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (section.type == RenderAssetGridElementType.monthTitle) - _MonthTitle(date: section.date), + if (section.type == RenderAssetGridElementType.monthTitle) _MonthTitle(date: section.date), if (section.type == RenderAssetGridElementType.groupDividerTitle || section.type == RenderAssetGridElementType.monthTitle) _Title( selectionActive: selectionActive, title: section.title!, - assets: scrolling - ? [] - : renderList.loadAssets(section.offset, section.totalCount), + assets: scrolling ? [] : renderList.loadAssets(section.offset, section.totalCount), allAssetsSelected: allAssetsSelected, selectAssets: selectAssets, deselectAssets: deselectAssets, @@ -699,9 +626,7 @@ class _Section extends StatelessWidget { scrolling ? _PlaceholderRow( key: ValueKey(i), - number: i + 1 == rows - ? section.count - i * assetsPerRow - : assetsPerRow, + number: i + 1 == rows ? section.count - i * assetsPerRow : assetsPerRow, width: width, height: width, margin: margin, @@ -710,10 +635,7 @@ class _Section extends StatelessWidget { key: ValueKey(i), rowStartIndex: i * assetsPerRow, sectionIndex: sectionIndex, - assets: assetsToRender.nestedSlice( - i * assetsPerRow, - min((i + 1) * assetsPerRow, section.count), - ), + assets: assetsToRender.nestedSlice(i * assetsPerRow, min((i + 1) * assetsPerRow, section.count)), absoluteOffset: section.offset + i * assetsPerRow, width: width, assetsPerRow: assetsPerRow, @@ -741,25 +663,18 @@ class _Section extends StatelessWidget { class _MonthTitle extends StatelessWidget { final DateTime date; - const _MonthTitle({ - required this.date, - }); + const _MonthTitle({required this.date}); @override Widget build(BuildContext context) { - final monthFormat = DateTime.now().year == date.year - ? DateFormat.MMMM() - : DateFormat.yMMMM(); + final monthFormat = DateTime.now().year == date.year ? DateFormat.MMMM() : DateFormat.yMMMM(); final String title = monthFormat.format(date); return Padding( key: Key("month-$title"), padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( toBeginningOfSentenceCase(title, context.locale.languageCode), - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w500), ), ); } @@ -844,8 +759,7 @@ class _AssetRow extends StatelessWidget { final widthDistribution = List.filled(assets.length, 1.0); if (dynamicLayout) { - final aspectRatios = - assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); + final aspectRatios = assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); final meanAspectRatio = aspectRatios.sum / assets.length; // 1: mean width @@ -859,11 +773,7 @@ class _AssetRow extends StatelessWidget { // Normalize: final sum = arConfiguration.sum; - widthDistribution.setRange( - 0, - widthDistribution.length, - arConfiguration.map((e) => (e * assets.length) / sum), - ); + widthDistribution.setRange(0, widthDistribution.length, arConfiguration.map((e) => (e * assets.length) / sum)); } return Row( key: key, @@ -873,10 +783,7 @@ class _AssetRow extends StatelessWidget { return Container( width: width * widthDistribution[index], height: width, - margin: EdgeInsets.only( - bottom: margin, - right: last ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: last ? 0.0 : margin), child: GestureDetector( onTap: () { if (selectionActive) { diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index 98b1c6f601..da2957c027 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -65,11 +65,9 @@ class MultiselectGrid extends HookConsumerWidget { final bool unfavorite; final bool editEnabled; final Widget? emptyIndicator; - Widget buildDefaultLoadingIndicator() => - const Center(child: CircularProgressIndicator()); + Widget buildDefaultLoadingIndicator() => const Center(child: CircularProgressIndicator()); - Widget buildEmptyIndicator() => - emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()); + Widget buildEmptyIndicator() => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()); @override Widget build(BuildContext context, WidgetRef ref) { @@ -81,57 +79,38 @@ class MultiselectGrid extends HookConsumerWidget { final currentUser = ref.watch(currentUserProvider); final processing = useProcessingOverlay(); - useEffect( - () { - selectionEnabledHook.addListener(() { - multiselectEnabled.state = selectionEnabledHook.value; - }); + useEffect(() { + selectionEnabledHook.addListener(() { + multiselectEnabled.state = selectionEnabledHook.value; + }); - return () { - // This does not work in tests - if (kReleaseMode) { - selectionEnabledHook.dispose(); - } - }; - }, - [], - ); + return () { + // This does not work in tests + if (kReleaseMode) { + selectionEnabledHook.dispose(); + } + }; + }, []); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; - selectionAssetState.value = - AssetSelectionState.fromSelection(selectedAssets); + selectionAssetState.value = AssetSelectionState.fromSelection(selectedAssets); } errorBuilder(String? msg) => msg != null && msg.isNotEmpty - ? () => ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ) + ? () => ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM) : null; - Iterable ownedRemoteSelection({ - String? localErrorMessage, - String? ownerErrorMessage, - }) { + Iterable ownedRemoteSelection({String? localErrorMessage, String? ownerErrorMessage}) { final assets = selection.value; return assets .remoteOnly(errorCallback: errorBuilder(localErrorMessage)) - .ownedOnly( - currentUser, - errorCallback: errorBuilder(ownerErrorMessage), - ); + .ownedOnly(currentUser, errorCallback: errorBuilder(ownerErrorMessage)); } Iterable remoteSelection({String? errorMessage}) => - selection.value.remoteOnly( - errorCallback: errorBuilder(errorMessage), - ); + selection.value.remoteOnly(errorCallback: errorBuilder(errorMessage)); void onShareAssets(bool shareLocal) { processing.value = true; @@ -139,9 +118,7 @@ class MultiselectGrid extends HookConsumerWidget { // Share = Download + Send to OS specific share sheet handleShareAssets(ref, context, selection.value); } else { - final ids = - remoteSelection(errorMessage: "home_page_share_err_local".tr()) - .map((e) => e.remoteId!); + final ids = remoteSelection(errorMessage: "home_page_share_err_local".tr()).map((e) => e.remoteId!); context.pushRoute(SharedLinkEditRoute(assetsList: ids.toList())); } processing.value = false; @@ -182,23 +159,16 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; try { final toDelete = selection.value - .ownedOnly( - currentUser, - errorCallback: errorBuilder('home_page_delete_err_partner'.tr()), - ) + .ownedOnly(currentUser, errorCallback: errorBuilder('home_page_delete_err_partner'.tr())) .toList(); - final isDeleted = await ref - .read(assetProvider.notifier) - .deleteAssets(toDelete, force: force); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(toDelete, force: force); if (isDeleted) { ImmichToast.show( context: context, msg: force - ? 'assets_deleted_permanently' - .tr(namedArgs: {'count': "${selection.value.length}"}) - : 'assets_trashed' - .tr(namedArgs: {'count': "${selection.value.length}"}), + ? 'assets_deleted_permanently'.tr(namedArgs: {'count': "${selection.value.length}"}) + : 'assets_trashed'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); selectionEnabledHook.value = false; @@ -213,26 +183,20 @@ class MultiselectGrid extends HookConsumerWidget { try { final localAssets = selection.value.where((a) => a.isLocal).toList(); - final toDelete = isMergedAsset - ? localAssets.where((e) => e.storage == AssetState.merged) - : localAssets; + final toDelete = isMergedAsset ? localAssets.where((e) => e.storage == AssetState.merged) : localAssets; if (toDelete.isEmpty) { return; } - final isDeleted = await ref - .read(assetProvider.notifier) - .deleteLocalAssets(toDelete.toList()); + final isDeleted = await ref.read(assetProvider.notifier).deleteLocalAssets(toDelete.toList()); if (isDeleted) { - final deletedCount = - localAssets.where((e) => !isMergedAsset || e.isRemote).length; + final deletedCount = localAssets.where((e) => !isMergedAsset || e.isRemote).length; ImmichToast.show( context: context, - msg: 'assets_removed_permanently_from_device' - .tr(namedArgs: {'count': "$deletedCount"}), + msg: 'assets_removed_permanently_from_device'.tr(namedArgs: {'count': "$deletedCount"}), gravity: ToastGravity.BOTTOM, ); @@ -248,34 +212,17 @@ class MultiselectGrid extends HookConsumerWidget { try { final toDownload = selection.value.toList(); - final results = await ref - .read(downloadStateProvider.notifier) - .downloadAllAsset(toDownload); + final results = await ref.read(downloadStateProvider.notifier).downloadAllAsset(toDownload); final totalCount = toDownload.length; final successCount = results.where((e) => e).length; final failedCount = totalCount - successCount; final msg = failedCount > 0 - ? 'assets_downloaded_failed'.t( - context: context, - args: { - 'count': successCount, - 'error': failedCount, - }, - ) - : 'assets_downloaded_successfully'.t( - context: context, - args: { - 'count': successCount, - }, - ); + ? 'assets_downloaded_failed'.t(context: context, args: {'count': successCount, 'error': failedCount}) + : 'assets_downloaded_successfully'.t(context: context, args: {'count': successCount}); - ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -290,19 +237,15 @@ class MultiselectGrid extends HookConsumerWidget { ownerErrorMessage: 'home_page_delete_err_partner'.tr(), ).toList(); - final isDeleted = - await ref.read(assetProvider.notifier).deleteRemoteAssets( - toDelete, - shouldDeletePermanently: shouldDeletePermanently, - ); + final isDeleted = await ref + .read(assetProvider.notifier) + .deleteRemoteAssets(toDelete, shouldDeletePermanently: shouldDeletePermanently); if (isDeleted) { ImmichToast.show( context: context, msg: shouldDeletePermanently - ? 'assets_deleted_permanently_from_server' - .tr(namedArgs: {'count': "${toDelete.length}"}) - : 'assets_trashed_from_server' - .tr(namedArgs: {'count': "${toDelete.length}"}), + ? 'assets_deleted_permanently_from_server'.tr(namedArgs: {'count': "${toDelete.length}"}) + : 'assets_trashed_from_server'.tr(namedArgs: {'count': "${toDelete.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -316,10 +259,9 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; selectionEnabledHook.value = false; try { - ref.read(manualUploadProvider.notifier).uploadAssets( - context, - selection.value.where((a) => a.storage == AssetState.local), - ); + ref + .read(manualUploadProvider.notifier) + .uploadAssets(context, selection.value.where((a) => a.storage == AssetState.local)); } finally { processing.value = false; } @@ -328,16 +270,11 @@ class MultiselectGrid extends HookConsumerWidget { void onAddToAlbum(Album album) async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref.read(albumServiceProvider).addAssets( - album, - assets, - ); + final result = await ref.read(albumServiceProvider).addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { @@ -355,10 +292,7 @@ class MultiselectGrid extends HookConsumerWidget { ImmichToast.show( context: context, msg: "home_page_add_to_album_success".tr( - namedArgs: { - "album": album.name, - "added": result.successfullyAdded.toString(), - }, + namedArgs: {"album": album.name, "added": result.successfullyAdded.toString()}, ), toastType: ToastType.success, ); @@ -373,15 +307,11 @@ class MultiselectGrid extends HookConsumerWidget { void onCreateNewAlbum() async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref - .read(albumServiceProvider) - .createAlbumWithGeneratedName(assets); + final result = await ref.read(albumServiceProvider).createAlbumWithGeneratedName(assets); if (result != null) { ref.watch(albumProvider.notifier).refreshRemoteAlbums(); @@ -401,9 +331,7 @@ class MultiselectGrid extends HookConsumerWidget { return; } - await ref.read(stackServiceProvider).createStack( - selection.value.map((e) => e.remoteId!).toList(), - ); + await ref.read(stackServiceProvider).createStack(selection.value.map((e) => e.remoteId!).toList()); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -449,16 +377,9 @@ class MultiselectGrid extends HookConsumerWidget { ); if (remoteAssets.isNotEmpty) { final isInLockedView = ref.read(inLockedViewProvider); - final visibility = isInLockedView - ? AssetVisibilityEnum.timeline - : AssetVisibilityEnum.locked; + final visibility = isInLockedView ? AssetVisibilityEnum.timeline : AssetVisibilityEnum.locked; - await handleSetAssetsVisibility( - ref, - context, - visibility, - remoteAssets.toList(), - ); + await handleSetAssetsVisibility(ref, context, visibility, remoteAssets.toList()); } } finally { processing.value = false; @@ -466,42 +387,34 @@ class MultiselectGrid extends HookConsumerWidget { } } - Future Function() wrapLongRunningFun( - Future Function() fun, { - bool showOverlay = true, - }) => - () async { - if (showOverlay) processing.value = true; - try { - final result = await fun(); - if (result.runtimeType != bool || result == true) { - selectionEnabledHook.value = false; - } - return result; - } finally { - if (showOverlay) processing.value = false; - } - }; + Future Function() wrapLongRunningFun(Future Function() fun, {bool showOverlay = true}) => () async { + if (showOverlay) processing.value = true; + try { + final result = await fun(); + if (result.runtimeType != bool || result == true) { + selectionEnabledHook.value = false; + } + return result; + } finally { + if (showOverlay) processing.value = false; + } + }; return SafeArea( top: true, bottom: false, child: Stack( children: [ - ref.watch(renderListProvider).when( - data: (data) => data.isEmpty && - (buildLoadingIndicator != null || topWidget == null) + ref + .watch(renderListProvider) + .when( + data: (data) => data.isEmpty && (buildLoadingIndicator != null || topWidget == null) ? (buildLoadingIndicator ?? buildEmptyIndicator)() : ImmichAssetGrid( renderList: data, listener: selectionListener, selectionActive: selectionEnabledHook.value, - onRefresh: onRefresh == null - ? null - : wrapLongRunningFun( - onRefresh!, - showOverlay: false, - ), + onRefresh: onRefresh == null ? null : wrapLongRunningFun(onRefresh!, showOverlay: false), topWidget: topWidget, showStack: stackEnabled, showDragScrollLabel: dragScrollLabelEnabled, @@ -527,6 +440,7 @@ class MultiselectGrid extends HookConsumerWidget { onUpload: onUpload, enabled: !processing.value, selectionAssetState: selectionAssetState.value, + selectedAssets: selection.value.toList(), onStack: stackEnabled ? onStack : null, onEditTime: editEnabled ? onEditTime : null, onEditLocation: editEnabled ? onEditLocation : null, @@ -534,9 +448,7 @@ class MultiselectGrid extends HookConsumerWidget { unarchive: unarchive, onToggleLocked: onToggleLockedVisibility, onRemoveFromAlbum: onRemoveFromAlbum != null - ? wrapLongRunningFun( - () => onRemoveFromAlbum!(selection.value), - ) + ? wrapLongRunningFun(() => onRemoveFromAlbum!(selection.value)) : null, ), ], diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart index b17029f2af..3a1fa82a28 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/providers/asset_viewer/render_list_status_provider import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class MultiselectGridStatusIndicator extends HookConsumerWidget { - const MultiselectGridStatusIndicator({ - super.key, - this.buildLoadingIndicator, - this.emptyIndicator, - }); + const MultiselectGridStatusIndicator({super.key, this.buildLoadingIndicator, this.emptyIndicator}); final Widget Function()? buildLoadingIndicator; final Widget? emptyIndicator; @@ -18,18 +14,13 @@ class MultiselectGridStatusIndicator extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final renderListStatus = ref.watch(renderListStatusProvider); return switch (renderListStatus) { - RenderListStatusEnum.loading => buildLoadingIndicator == null - ? const Center( - child: DelayedLoadingIndicator( - delay: Duration(milliseconds: 500), - ), - ) - : buildLoadingIndicator!(), - RenderListStatusEnum.empty => - emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), - RenderListStatusEnum.error => - Center(child: const Text("error_loading_assets").tr()), - RenderListStatusEnum.complete => const SizedBox() + RenderListStatusEnum.loading => + buildLoadingIndicator == null + ? const Center(child: DelayedLoadingIndicator(delay: Duration(milliseconds: 500))) + : buildLoadingIndicator!(), + RenderListStatusEnum.empty => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), + RenderListStatusEnum.error => Center(child: const Text("error_loading_assets").tr()), + RenderListStatusEnum.complete => const SizedBox(), }; } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 5b98c8a756..93385b88b3 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -53,16 +53,13 @@ class ThumbnailImage extends StatelessWidget { decoration: BoxDecoration( border: multiselectEnabled && isSelected ? canDeselect - ? Border.all( - color: assetContainerColor, - width: 8, - ) - : const Border( - top: BorderSide(color: Colors.grey, width: 8), - right: BorderSide(color: Colors.grey, width: 8), - bottom: BorderSide(color: Colors.grey, width: 8), - left: BorderSide(color: Colors.grey, width: 8), - ) + ? Border.all(color: assetContainerColor, width: 8) + : const Border( + top: BorderSide(color: Colors.grey, width: 8), + right: BorderSide(color: Colors.grey, width: 8), + bottom: BorderSide(color: Colors.grey, width: 8), + left: BorderSide(color: Colors.grey, width: 8), + ) : const Border(), ), child: Stack( @@ -77,21 +74,9 @@ class ThumbnailImage extends StatelessWidget { ), if (showStorageIndicator) _StorageIcon(storage: asset.storage), if (asset.isFavorite) - const Positioned( - left: 8, - bottom: 5, - child: Icon( - Icons.favorite, - color: Colors.white, - size: 16, - ), - ), + const Positioned(left: 8, bottom: 5, child: Icon(Icons.favorite, color: Colors.white, size: 16)), if (asset.isVideo) _VideoIcon(duration: asset.duration), - if (asset.stackCount > 0) - _StackIcon( - isVideo: asset.isVideo, - stackCount: asset.stackCount, - ), + if (asset.stackCount > 0) _StackIcon(isVideo: asset.isVideo, stackCount: asset.stackCount), ], ), ), @@ -99,15 +84,9 @@ class ThumbnailImage extends StatelessWidget { isSelected ? const Padding( padding: EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: _SelectedIcon(), - ), + child: Align(alignment: Alignment.topLeft, child: _SelectedIcon()), ) - : const Icon( - Icons.circle_outlined, - color: Colors.white, - ), + : const Icon(Icons.circle_outlined, color: Colors.white), ], ); } @@ -123,14 +102,8 @@ class _SelectedIcon extends StatelessWidget { : context.primaryColor.lighten(amount: 0.8); return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assetContainerColor, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: assetContainerColor), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } } @@ -149,18 +122,10 @@ class _VideoIcon extends StatelessWidget { children: [ Text( duration.format(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), const SizedBox(width: 3), - const Icon( - Icons.play_circle_fill_rounded, - color: Colors.white, - size: 18, - ), + const Icon(Icons.play_circle_fill_rounded, color: Colors.white, size: 18), ], ), ); @@ -183,21 +148,10 @@ class _StackIcon extends StatelessWidget { if (stackCount > 1) Text( "$stackCount", - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), - if (stackCount > 1) - const SizedBox( - width: 3, - ), - const Icon( - Icons.burst_mode_rounded, - color: Colors.white, - size: 18, - ), + if (stackCount > 1) const SizedBox(width: 3), + const Icon(Icons.burst_mode_rounded, color: Colors.white, size: 18), ], ), ); @@ -213,53 +167,35 @@ class _StorageIcon extends StatelessWidget { Widget build(BuildContext context) { return switch (storage) { AssetState.local => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_off_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_off_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.remote => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.merged => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_done_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_done_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), }; } } @@ -290,13 +226,7 @@ class _ImageIcon extends StatelessWidget { tag: isDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, child: Stack( children: [ - SizedBox.expand( - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, - ), - ), + SizedBox.expand(child: ImmichThumbnail(asset: asset, height: 250, width: 250)), const DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( @@ -322,13 +252,8 @@ class _ImageIcon extends StatelessWidget { } return DecoratedBox( - decoration: canDeselect - ? BoxDecoration(color: assetContainerColor) - : const BoxDecoration(color: Colors.grey), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(15.0)), - child: image, - ), + decoration: canDeselect ? BoxDecoration(color: assetContainerColor) : const BoxDecoration(color: Colors.grey), + child: ClipRRect(borderRadius: const BorderRadius.all(Radius.circular(15.0)), child: image), ); } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart index 5b12426a50..a84dfbae37 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart @@ -7,12 +7,7 @@ class ThumbnailPlaceholder extends StatelessWidget { final double width; final double height; - const ThumbnailPlaceholder({ - super.key, - this.margin = EdgeInsets.zero, - this.width = 250, - this.height = 250, - }); + const ThumbnailPlaceholder({super.key, this.margin = EdgeInsets.zero, this.width = 250, this.height = 250}); @override Widget build(BuildContext context) { @@ -26,11 +21,7 @@ class ThumbnailPlaceholder extends StatelessWidget { height: height, margin: margin, decoration: BoxDecoration( - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), + gradient: LinearGradient(colors: gradientColors, begin: Alignment.topCenter, end: Alignment.bottomCenter), ), ); } diff --git a/mobile/lib/widgets/asset_grid/upload_dialog.dart b/mobile/lib/widgets/asset_grid/upload_dialog.dart index c2a38fab8c..86e2759566 100644 --- a/mobile/lib/widgets/asset_grid/upload_dialog.dart +++ b/mobile/lib/widgets/asset_grid/upload_dialog.dart @@ -4,11 +4,11 @@ class UploadDialog extends ConfirmDialog { final Function onUpload; const UploadDialog({super.key, required this.onUpload}) - : super( - title: 'upload_dialog_title', - content: 'upload_dialog_info', - cancel: 'cancel', - ok: 'upload', - onOk: onUpload, - ); + : super( + title: 'upload_dialog_title', + content: 'upload_dialog_info', + cancel: 'cancel', + ok: 'upload', + onOk: onUpload, + ); } diff --git a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart index f6a617bf1c..faa058ced4 100644 --- a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart +++ b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart @@ -8,11 +8,7 @@ class AdvancedBottomSheet extends HookConsumerWidget { final Asset assetDetail; final ScrollController? scrollController; - const AdvancedBottomSheet({ - super.key, - required this.assetDetail, - this.scrollController, - }); + const AdvancedBottomSheet({super.key, required this.assetDetail, this.scrollController}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,29 +22,15 @@ class AdvancedBottomSheet extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Align( - child: Text( - "ADVANCED INFO", - style: TextStyle(fontSize: 12.0), - ), - ), + const Align(child: Text("ADVANCED INFO", style: TextStyle(fontSize: 12.0))), const SizedBox(height: 32.0), Container( decoration: BoxDecoration( - color: context.isDarkTheme - ? Colors.grey[900] - : Colors.grey[200], - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( - padding: const EdgeInsets.only( - right: 16.0, - left: 16, - top: 8, - bottom: 16, - ), + padding: const EdgeInsets.only(right: 16.0, left: 16, top: 8, bottom: 16), child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -57,29 +39,18 @@ class AdvancedBottomSheet extends HookConsumerWidget { alignment: Alignment.centerRight, child: IconButton( onPressed: () { - Clipboard.setData( - ClipboardData( - text: assetDetail.toString(), - ), - ).then((_) { + Clipboard.setData(ClipboardData(text: assetDetail.toString())).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "Copied to clipboard", - style: - context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ), SelectableText( diff --git a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart index 7935567b8c..e7ceac6105 100644 --- a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart +++ b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; /// A widget that animates implicitly between a play and a pause icon. class AnimatedPlayPause extends StatefulWidget { - const AnimatedPlayPause({ - super.key, - required this.playing, - this.size, - this.color, - }); + const AnimatedPlayPause({super.key, required this.playing, this.size, this.color}); final double? size; final bool playing; @@ -17,8 +12,7 @@ class AnimatedPlayPause extends StatefulWidget { State createState() => AnimatedPlayPauseState(); } -class AnimatedPlayPauseState extends State - with SingleTickerProviderStateMixin { +class AnimatedPlayPauseState extends State with SingleTickerProviderStateMixin { late final animationController = AnimationController( vsync: this, value: widget.playing ? 1 : 0, diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 59d97bf0c7..00f7bc494d 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -52,45 +52,34 @@ class BottomGalleryBar extends ConsumerWidget { if (asset == null) { return const SizedBox(); } - final isOwner = - asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); + final isOwner = asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); final showControls = ref.watch(showControlsProvider); final stackId = asset.stackId; - final stackItems = showStack && stackId != null - ? ref.watch(assetStackStateProvider(stackId)) - : []; + final stackItems = showStack && stackId != null ? ref.watch(assetStackStateProvider(stackId)) : []; bool isStackPrimaryAsset = asset.stackPrimaryAssetId == null; final navStack = AutoRouter.of(context).stackData; - final isTrashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); - final isFromTrash = isTrashEnabled && - navStack.length > 2 && - navStack.elementAt(navStack.length - 2).name == TrashRoute.name; + final isTrashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final isFromTrash = + isTrashEnabled && navStack.length > 2 && navStack.elementAt(navStack.length - 2).name == TrashRoute.name; final isInAlbum = ref.watch(currentAlbumProvider)?.isRemote ?? false; void removeAssetFromStack() { if (stackIndex.value > 0 && showStack && stackId != null) { - ref - .read(assetStackStateProvider(stackId).notifier) - .removeChild(stackIndex.value - 1); + ref.read(assetStackStateProvider(stackId).notifier).removeChild(stackIndex.value - 1); } } void handleDelete() async { Future onDelete(bool force) async { - final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( - {asset}, - force: force, - ); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets({asset}, force: force); if (isDeleted && isStackPrimaryAsset) { // Workaround for asset remaining in the gallery renderList.deleteAsset(asset); // `assetIndex == totalAssets.value - 1` handle the case of removing the last asset // to not throw the error when the next preCache index is called - if (totalAssets.value == 1 || - assetIndex.value == totalAssets.value - 1) { + if (totalAssets.value == 1 || assetIndex.value == totalAssets.value - 1) { // Handle only one asset context.maybePop(); } @@ -98,9 +87,7 @@ class BottomGalleryBar extends ConsumerWidget { totalAssets.value -= 1; } if (isDeleted) { - ref - .read(currentAssetProvider.notifier) - .set(renderList.loadAsset(assetIndex.value)); + ref.read(currentAssetProvider.notifier).set(renderList.loadAsset(assetIndex.value)); } return isDeleted; } @@ -114,7 +101,7 @@ class BottomGalleryBar extends ConsumerWidget { ImmichToast.show( durationInSecond: 1, context: context, - msg: 'Asset trashed', + msg: 'asset_trashed'.tr(), gravity: ToastGravity.BOTTOM, ); } @@ -144,9 +131,7 @@ class BottomGalleryBar extends ConsumerWidget { return; } - await ref - .read(stackServiceProvider) - .deleteStack(asset.stackId!, stackItems); + await ref.read(stackServiceProvider).deleteStack(asset.stackId!, stackItems); } void showStackActionItems() { @@ -161,19 +146,13 @@ class BottomGalleryBar extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - leading: const Icon( - Icons.filter_none_outlined, - size: 18, - ), + leading: const Icon(Icons.filter_none_outlined, size: 18), onTap: () async { await unStack(); ctx.pop(); context.maybePop(); }, - title: const Text( - "viewer_unstack", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ], ), @@ -201,11 +180,7 @@ class BottomGalleryBar extends ConsumerWidget { context.navigator.push( MaterialPageRoute( - builder: (context) => EditImagePage( - asset: asset, - image: image, - isEdited: false, - ), + builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), ), ); } @@ -233,15 +208,12 @@ class BottomGalleryBar extends ConsumerWidget { return; } - ref.read(downloadStateProvider.notifier).downloadAsset( - asset, - ); + ref.read(downloadStateProvider.notifier).downloadAsset(asset); } handleRemoveFromAlbum() async { final album = ref.read(currentAlbumProvider); - final bool isSuccess = album != null && - await ref.read(albumProvider.notifier).removeAsset(album, [asset]); + final bool isSuccess = album != null && await ref.read(albumProvider.notifier).removeAsset(album, [asset]); if (isSuccess) { // Workaround for asset remaining in the gallery @@ -271,12 +243,11 @@ class BottomGalleryBar extends ConsumerWidget { final List> albumActions = [ { BottomNavigationBarItem( - icon: Icon( - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, - ), + icon: Icon(Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded), label: 'share'.tr(), tooltip: 'share'.tr(), - ): (_) => shareAsset(), + ): (_) => + shareAsset(), }, if (asset.isImage && !isInLockedView) { @@ -284,7 +255,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.tune_outlined), label: 'edit'.tr(), tooltip: 'edit'.tr(), - ): (_) => handleEdit(), + ): (_) => + handleEdit(), }, if (isOwner && !isInLockedView) { @@ -298,7 +270,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.archive_outlined), label: 'archive'.tr(), tooltip: 'archive'.tr(), - ): (_) => handleArchive(), + ): (_) => + handleArchive(), }, if (isOwner && asset.stackCount > 0 && !isInLockedView) { @@ -306,7 +279,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.burst_mode_outlined), label: 'stack'.tr(), tooltip: 'stack'.tr(), - ): (_) => showStackActionItems(), + ): (_) => + showStackActionItems(), }, if (isOwner && !isInAlbum) { @@ -314,7 +288,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.delete_outline), label: 'delete'.tr(), tooltip: 'delete'.tr(), - ): (_) => handleDelete(), + ): (_) => + handleDelete(), }, if (!isOwner) { @@ -322,7 +297,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.download_outlined), label: 'download'.tr(), tooltip: 'download'.tr(), - ): (_) => handleDownload(), + ): (_) => + handleDownload(), }, if (isInAlbum) { @@ -330,7 +306,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.remove_circle_outline), label: 'remove_from_album'.tr(), tooltip: 'remove_from_album'.tr(), - ): (_) => handleRemoveFromAlbum(), + ): (_) => + handleRemoveFromAlbum(), }, ]; return IgnorePointer( @@ -357,25 +334,15 @@ class BottomGalleryBar extends ConsumerWidget { backgroundColor: Colors.transparent, unselectedIconTheme: const IconThemeData(color: Colors.white), selectedIconTheme: const IconThemeData(color: Colors.white), - unselectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - selectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), + unselectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), + selectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), unselectedFontSize: 14, selectedFontSize: 14, selectedItemColor: Colors.white, unselectedItemColor: Colors.white, showSelectedLabels: true, showUnselectedLabels: true, - items: albumActions - .map((e) => e.keys.first) - .toList(growable: false), + items: albumActions.map((e) => e.keys.first).toList(growable: false), onTap: (index) { albumActions[index].values.first.call(index); }, diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index 9043ea4bea..f7c80cca3d 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -21,10 +21,7 @@ class CastDialog extends ConsumerWidget { } return AlertDialog( - title: const Text( - "cast", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("cast", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SizedBox( width: 250, height: 250, @@ -32,27 +29,18 @@ class CastDialog extends ConsumerWidget { future: ref.read(castProvider.notifier).getDevices(), builder: (context, snapshot) { if (snapshot.hasError) { - return Text( - 'Error: ${snapshot.error.toString()}', - ); + return Text('error_saving_image'.tr(args: [snapshot.error.toString()])); } else if (!snapshot.hasData) { - return const SizedBox( - height: 48, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 48, child: Center(child: CircularProgressIndicator())); } if (snapshot.data!.isEmpty) { - return const Text( - 'no_cast_devices_found', - ).tr(); + return const Text('no_cast_devices_found').tr(); } final devices = snapshot.data!; - final connected = - devices.where((d) => isCurrentDevice(d.$1)).toList(); - final others = - devices.where((d) => !isCurrentDevice(d.$1)).toList(); + final connected = devices.where((d) => isCurrentDevice(d.$1)).toList(); + final others = devices.where((d) => !isCurrentDevice(d.$1)).toList(); final List sectionedList = []; @@ -76,40 +64,25 @@ class CastDialog extends ConsumerWidget { // It's a section header return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - item, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ).tr(), + child: Text(item, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)).tr(), ); } else { - final (deviceName, type, deviceObj) = - item as (String, CastDestinationType, dynamic); + final (deviceName, type, deviceObj) = item as (String, CastDestinationType, dynamic); return ListTile( title: Text( deviceName, - style: TextStyle( - color: isCurrentDevice(deviceName) - ? context.colorScheme.primary - : null, - ), + style: TextStyle(color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null), ), leading: Icon( - type == CastDestinationType.googleCast - ? Icons.cast - : Icons.cast_connected, - color: isCurrentDevice(deviceName) - ? context.colorScheme.primary - : null, + type == CastDestinationType.googleCast ? Icons.cast : Icons.cast_connected, + color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, ), trailing: isCurrentDevice(deviceName) ? Icon(Icons.check, color: context.colorScheme.primary) : isDeviceConnecting(deviceName) - ? const CircularProgressIndicator() - : null, + ? const CircularProgressIndicator() + : null, onTap: () async { if (isDeviceConnecting(deviceName)) { return; @@ -120,9 +93,7 @@ class CastDialog extends ConsumerWidget { } if (!isCurrentDevice(deviceName)) { - ref - .read(castProvider.notifier) - .connect(type, deviceObj); + ref.read(castProvider.notifier).connect(type, deviceObj); } }, ); @@ -138,20 +109,14 @@ class CastDialog extends ConsumerWidget { onPressed: () => ref.read(castProvider.notifier).disconnect(), child: Text( "stop_casting", - style: TextStyle( - color: context.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.secondary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () => context.pop(), child: Text( "close", - style: TextStyle( - color: context.colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.primary, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_viewer/center_play_button.dart b/mobile/lib/widgets/asset_viewer/center_play_button.dart index 6d7aead9d1..26d0a41129 100644 --- a/mobile/lib/widgets/asset_viewer/center_play_button.dart +++ b/mobile/lib/widgets/asset_viewer/center_play_button.dart @@ -29,19 +29,13 @@ class CenterPlayButton extends StatelessWidget { opacity: show ? 1.0 : 0.0, duration: const Duration(milliseconds: 100), child: DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle), child: IconButton( iconSize: 32, padding: const EdgeInsets.all(12.0), icon: isFinished ? Icon(Icons.replay, color: iconColor) - : AnimatedPlayPause( - color: iconColor, - playing: isPlaying, - ), + : AnimatedPlayPause(color: iconColor, playing: isPlaying), onPressed: onPressed, ), ), diff --git a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart index 18565c8332..0e766c77b9 100644 --- a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart +++ b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart @@ -13,41 +13,29 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class CustomVideoPlayerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const CustomVideoPlayerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const CustomVideoPlayerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetProvider.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetProvider.select((asset) => asset != null && asset.isVideo)); final showControls = ref.watch(showControlsProvider); - final VideoPlaybackState state = - ref.watch(videoPlaybackValueProvider.select((value) => value.state)); + final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && - state != VideoPlaybackState.completed && - assetIsVideo) { - ref.read(showControlsProvider.notifier).show = false; - } - }, - ); - final showBuffering = - state == VideoPlaybackState.buffering && !cast.isCasting; + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(showControlsProvider.notifier).show = false; + } + }); + final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them void showControlsAndStartHideTimer() { @@ -56,8 +44,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget { } // When we change position, show or hide timer - ref.listen(videoPlayerControlsProvider.select((v) => v.position), - (previous, next) { + ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) { showControlsAndStartHideTimer(); }); @@ -98,21 +85,16 @@ class CustomVideoPlayerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( - onTap: () => - ref.read(showControlsProvider.notifier).show = false, + onTap: () => ref.read(showControlsProvider.notifier).show = false, child: CenterPlayButton( backgroundColor: Colors.black54, iconColor: Colors.white, isFinished: state == VideoPlaybackState.completed, - isPlaying: state == VideoPlaybackState.playing || - (cast.isCasting && cast.castState == CastState.playing), + isPlaying: + state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing), show: assetIsVideo && showControls, onPressed: togglePlay, ), diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart index 3ac60fd613..b0cefd63fa 100644 --- a/mobile/lib/widgets/asset_viewer/description_input.dart +++ b/mobile/lib/widgets/asset_viewer/description_input.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; class DescriptionInput extends HookConsumerWidget { - DescriptionInput({ - super.key, - required this.asset, - this.exifInfo, - }); + DescriptionInput({super.key, required this.asset, this.exifInfo}); final Asset asset; final ExifInfo? exifInfo; @@ -37,16 +33,13 @@ class DescriptionInput extends HookConsumerWidget { final hasDescription = useState(false); final isOwner = fastHash(owner?.id ?? '') == asset.ownerId; - useEffect( - () { - assetService.getDescription(asset).then((value) { - controller.text = value; - hasDescription.value = value.isNotEmpty; - }); - return null; - }, - [assetWithExif.value], - ); + useEffect(() { + assetService.getDescription(asset).then((value) { + controller.text = value; + hasDescription.value = value.isNotEmpty; + }); + return null; + }, [assetWithExif.value]); if (!isOwner && !hasDescription.value) { return const SizedBox.shrink(); @@ -55,19 +48,12 @@ class DescriptionInput extends HookConsumerWidget { submitDescription(String description) async { hasError.value = false; try { - await assetService.setDescription( - asset, - description, - ); + await assetService.setDescription(asset, description); controller.text = description; } catch (error, stack) { hasError.value = true; _log.severe("Error updating description", error, stack); - ImmichToast.show( - context: context, - msg: "description_input_submit_error".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "description_input_submit_error".tr(), toastType: ToastType.error); } } @@ -80,10 +66,7 @@ class DescriptionInput extends HookConsumerWidget { controller.clear(); isTextEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.colorScheme.onSurfaceSecondary, - ), + icon: Icon(Icons.cancel_rounded, color: context.colorScheme.onSurfaceSecondary), splashRadius: 10, ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart index e29da52280..df8f6593df 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart @@ -36,18 +36,8 @@ class AssetDateTime extends ConsumerWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - formattedDateTime, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - if (asset.isRemote) - IconButton( - onPressed: editDateTime, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + Text(formattedDateTime, style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600)), + if (asset.isRemote) IconButton(onPressed: editDateTime, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart index 59b52344e7..f0f9a2efcb 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart @@ -12,11 +12,7 @@ class AssetDetails extends ConsumerWidget { final Asset asset; final ExifInfo? exifInfo; - const AssetDetails({ - super.key, - required this.asset, - this.exifInfo, - }); + const AssetDetails({super.key, required this.asset, this.exifInfo}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart index 2a9e6f4a24..7ad290c152 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart @@ -11,10 +11,7 @@ import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart'; class AssetLocation extends HookConsumerWidget { final Asset asset; - const AssetLocation({ - super.key, - required this.asset, - }); + const AssetLocation({super.key, required this.asset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,10 +32,7 @@ class AssetLocation extends HookConsumerWidget { leading: const Icon(Icons.location_on), title: Text( "add_a_location", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), onTap: editLocation, ) @@ -56,10 +50,7 @@ class AssetLocation extends HookConsumerWidget { bool hasLocationName = (cityName != null && stateName != null); return hasLocationName - ? Text( - "$cityName, $stateName", - style: context.textTheme.labelLarge, - ) + ? Text("$cityName, $stateName", style: context.textTheme.labelLarge) : const SizedBox.shrink(); } @@ -79,25 +70,16 @@ class AssetLocation extends HookConsumerWidget { ), ).tr(), if (asset.isRemote) - IconButton( - onPressed: editLocation, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + IconButton(onPressed: editLocation, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ), asset.isRemote ? const SizedBox.shrink() : const SizedBox(height: 16), - ExifMap( - exifInfo: exifInfo!, - markerId: asset.remoteId, - ), + ExifMap(exifInfo: exifInfo!, markerId: asset.remoteId), const SizedBox(height: 16), getLocationName(), Text( "${exifInfo.latitude!.toStringAsFixed(4)}, ${exifInfo.longitude!.toStringAsFixed(4)}", - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart index aec18c6a16..5ae29d32c7 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class CameraInfo extends StatelessWidget { final ExifInfo exifInfo; - const CameraInfo({ - super.key, - required this.exifInfo, - }); + const CameraInfo({super.key, required this.exifInfo}); @override Widget build(BuildContext context) { @@ -16,18 +13,9 @@ class CameraInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.camera, - color: textColor.withAlpha(200), - ), - title: Text( - "${exifInfo.make} ${exifInfo.model}", - style: context.textTheme.labelLarge, - ), - subtitle: exifInfo.f != null || - exifInfo.exposureSeconds != null || - exifInfo.mm != null || - exifInfo.iso != null + leading: Icon(Icons.camera, color: textColor.withAlpha(200)), + title: Text("${exifInfo.make} ${exifInfo.model}", style: context.textTheme.labelLarge), + subtitle: exifInfo.f != null || exifInfo.exposureSeconds != null || exifInfo.mm != null || exifInfo.iso != null ? Text( "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ", style: context.textTheme.bodySmall, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart b/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart index 8ad2cdc687..97c9477c97 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart @@ -24,9 +24,7 @@ class DetailPanel extends HookConsumerWidget { child: Column( children: [ AssetDateTime(asset: asset), - asset.isRemote - ? DescriptionInput(asset: asset) - : const SizedBox.shrink(), + asset.isRemote ? DescriptionInput(asset: asset) : const SizedBox.shrink(), PeopleInfo(asset: asset), AssetLocation(asset: asset), AssetDetails(asset: asset), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index 7b6325cf2c..0edafa88c5 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -5,18 +5,14 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class ExifMap extends StatelessWidget { final ExifInfo exifInfo; final String? markerId; final MapCreatedCallback? onMapCreated; - const ExifMap({ - super.key, - required this.exifInfo, - this.markerId = 'marker', - this.onMapCreated, - }); + const ExifMap({super.key, required this.exifInfo, this.markerId = 'marker', this.onMapCreated}); @override Widget build(BuildContext context) { @@ -35,20 +31,13 @@ class ExifMap extends StatelessWidget { Uri uri = Uri( scheme: 'geo', host: '$latitude,$longitude', - queryParameters: { - 'z': '$zoomLevel', - 'q': '$latitude,$longitude', - }, + queryParameters: {'z': '$zoomLevel', 'q': '$latitude,$longitude'}, ); if (await canLaunchUrl(uri)) { return uri; } } else if (Platform.isIOS) { - var params = { - 'll': '$latitude,$longitude', - 'q': '$latitude,$longitude', - 'z': '$zoomLevel', - }; + var params = {'ll': '$latitude,$longitude', 'q': '$latitude,$longitude', 'z': '$zoomLevel'}; Uri uri = Uri.https('maps.apple.com', '/', params); if (await canLaunchUrl(uri)) { return uri; @@ -66,10 +55,7 @@ class ExifMap extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { return MapThumbnail( - centre: LatLng( - exifInfo.latitude ?? 0, - exifInfo.longitude ?? 0, - ), + centre: LatLng(exifInfo.latitude ?? 0, exifInfo.longitude ?? 0), height: 150, width: constraints.maxWidth, zoom: 12.0, @@ -81,7 +67,7 @@ class ExifMap extends StatelessWidget { return; } - debugPrint('Opening Map Uri: $uri'); + dPrint(() => 'Opening Map Uri: $uri'); launchUrl(uri); }, onCreated: onMapCreated, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart index 4af9846cf6..78d9ac1776 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; class FileInfo extends StatelessWidget { final Asset asset; - const FileInfo({ - super.key, - required this.asset, - }); + const FileInfo({super.key, required this.asset}); @override Widget build(BuildContext context) { @@ -17,11 +14,8 @@ class FileInfo extends StatelessWidget { final height = asset.orientatedHeight ?? asset.height; final width = asset.orientatedWidth ?? asset.width; - String resolution = - height != null && width != null ? "$width x $height " : ""; - String fileSize = asset.exifInfo?.fileSize != null - ? formatBytes(asset.exifInfo!.fileSize!) - : ""; + String resolution = height != null && width != null ? "$width x $height " : ""; + String fileSize = asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo!.fileSize!) : ""; String text = resolution + fileSize; final imgSizeString = text.isNotEmpty ? text : null; @@ -44,15 +38,9 @@ class FileInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.image, - color: textColor.withAlpha(200), - ), + leading: Icon(Icons.image, color: textColor.withAlpha(200)), titleAlignment: ListTileTitleAlignment.center, - title: Text( - title, - style: context.textTheme.labelLarge, - ), + title: Text(title, style: context.textTheme.labelLarge), subtitle: subtitle == null ? null : Text(subtitle), ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index cbb003bd72..b96cbc777d 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -7,6 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/search/search_curated_content.model.dart'; import 'package:immich_mobile/providers/asset_viewer/asset_people.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; import 'package:immich_mobile/widgets/search/curated_people_row.dart'; import 'package:immich_mobile/widgets/search/person_name_edit_form.dart'; @@ -18,17 +19,10 @@ class PeopleInfo extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final peopleProvider = - ref.watch(assetPeopleNotifierProvider(asset).notifier); - final people = ref - .watch(assetPeopleNotifierProvider(asset)) - .value - ?.where((p) => !p.isHidden); + final peopleProvider = ref.watch(assetPeopleNotifierProvider(asset).notifier); + final people = ref.watch(assetPeopleNotifierProvider(asset)).value?.where((p) => !p.isHidden); - showPersonNameEditModel( - String personId, - String personName, - ) { + showPersonNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -41,14 +35,14 @@ class PeopleInfo extends ConsumerWidget { }); } - final curatedPeople = people + final curatedPeople = + people ?.map( (p) => SearchCuratedContent( id: p.id, label: p.name, - subtitle: p.birthDate != null && - p.birthDate!.isBefore(asset.fileCreatedAt) - ? _formatAge(p.birthDate!, asset.fileCreatedAt) + subtitle: p.birthDate != null && p.birthDate!.isBefore(asset.fileCreatedAt) + ? formatAge(p.birthDate!, asset.fileCreatedAt) : null, ), ) @@ -56,9 +50,7 @@ class PeopleInfo extends ConsumerWidget { []; return AnimatedCrossFade( - crossFadeState: (people?.isEmpty ?? true) - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, + crossFadeState: (people?.isEmpty ?? true) ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: const Duration(milliseconds: 200), firstChild: Container(), secondChild: Padding( @@ -85,17 +77,10 @@ class PeopleInfo extends ConsumerWidget { content: curatedPeople, onTap: (content, index) { context - .pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + .pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) .then((_) => peopleProvider.refresh()); }, - onNameTap: (person, index) => { - showPersonNameEditModel(person.id, person.label), - }, + onNameTap: (person, index) => {showPersonNameEditModel(person.id, person.label)}, ), ), ], @@ -103,37 +88,4 @@ class PeopleInfo extends ConsumerWidget { ), ); } - - String _formatAge(DateTime birthDate, DateTime referenceDate) { - int ageInYears = _calculateAge(birthDate, referenceDate); - int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); - - if (ageInMonths <= 11) { - return "exif_bottom_sheet_person_age_months" - .tr(namedArgs: {'months': ageInMonths.toString()}); - } else if (ageInMonths > 12 && ageInMonths <= 23) { - return "exif_bottom_sheet_person_age_year_months" - .tr(namedArgs: {'months': (ageInMonths - 12).toString()}); - } else { - return "exif_bottom_sheet_person_age_years" - .tr(namedArgs: {'years': ageInYears.toString()}); - } - } - - int _calculateAge(DateTime birthDate, DateTime referenceDate) { - int age = referenceDate.year - birthDate.year; - if (referenceDate.month < birthDate.month || - (referenceDate.month == birthDate.month && - referenceDate.day < birthDate.day)) { - age--; - } - return age; - } - - int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { - return (referenceDate.year - birthDate.year) * 12 + - referenceDate.month - - birthDate.month - - (referenceDate.day < birthDate.day ? 1 : 0); - } } diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index d18dc92575..fbcc8e6482 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -11,11 +11,7 @@ class FormattedDuration extends StatelessWidget { width: data.inHours > 0 ? 70 : 60, // use a fixed width to prevent jitter child: Text( data.format(), - style: const TextStyle( - fontSize: 14.0, - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 14.0, color: Colors.white, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ); diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart index f47e73ea78..dcb0334801 100644 --- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart @@ -34,17 +34,12 @@ class GalleryAppBar extends ConsumerWidget { return const SizedBox(); } final album = ref.watch(currentAlbumProvider); - final isOwner = - asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); + final isOwner = asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); final showControls = ref.watch(showControlsProvider); - final isPartner = ref - .watch(partnerSharedWithProvider) - .map((e) => fastHash(e.id)) - .contains(asset.ownerId); + final isPartner = ref.watch(partnerSharedWithProvider).map((e) => fastHash(e.id)).contains(asset.ownerId); - toggleFavorite(Asset asset) => - ref.read(assetProvider.notifier).toggleFavorite([asset]); + toggleFavorite(Asset asset) => ref.read(assetProvider.notifier).toggleFavorite([asset]); handleActivities() { if (album != null && album.shared && album.remoteId != null) { @@ -53,15 +48,10 @@ class GalleryAppBar extends ConsumerWidget { } handleRestore(Asset asset) async { - final result = - await ref.read(trashProvider.notifier).restoreAssets([asset]); + final result = await ref.read(trashProvider.notifier).restoreAssets([asset]); if (result && context.mounted) { - ImmichToast.show( - context: context, - msg: 'asset_restored_successfully'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'asset_restored_successfully'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -71,9 +61,7 @@ class GalleryAppBar extends ConsumerWidget { builder: (BuildContext _) { return UploadDialog( onUpload: () { - ref - .read(manualUploadProvider.notifier) - .uploadAssets(context, [asset]); + ref.read(manualUploadProvider.notifier).uploadAssets(context, [asset]); }, ); }, @@ -83,16 +71,10 @@ class GalleryAppBar extends ConsumerWidget { addToAlbum(Asset addToAlbumAsset) { showModalBottomSheet( elevation: 0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(15.0), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), context: context, builder: (BuildContext _) { - return AddToAlbumBottomSheet( - assets: [addToAlbumAsset], - ); + return AddToAlbumBottomSheet(assets: [addToAlbumAsset]); }, ); } @@ -104,8 +86,7 @@ class GalleryAppBar extends ConsumerWidget { handleLocateAsset() async { // Go back to the gallery await context.maybePop(); - await context - .navigateTo(const TabControllerRoute(children: [PhotosRoute()])); + await context.navigateTo(const TabControllerRoute(children: [PhotosRoute()])); ref.read(tabProvider.notifier).update((state) => state = TabEnum.home); // Scroll to the asset's date scrollToDateNotifierProvider.scrollToDate(asset.fileCreatedAt); diff --git a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart index a868aff617..35f3840797 100644 --- a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart @@ -49,22 +49,16 @@ class TopControlAppBar extends HookConsumerWidget { final a = ref.watch(assetWatcher(asset)).value ?? asset; final album = ref.watch(currentAlbumProvider); final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); - final websocketConnected = - ref.watch(websocketProvider.select((c) => c.isConnected)); + final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected)); - final comments = album != null && - album.remoteId != null && - asset.remoteId != null + final comments = album != null && album.remoteId != null && asset.remoteId != null ? ref.watch(activityStatisticsProvider(album.remoteId!, asset.remoteId)) : 0; Widget buildFavoriteButton(a) { return IconButton( onPressed: () => onFavorite(a), - icon: Icon( - a.isFavorite ? Icons.favorite : Icons.favorite_border, - color: Colors.grey[200], - ), + icon: Icon(a.isFavorite ? Icons.favorite : Icons.favorite_border, color: Colors.grey[200]), ); } @@ -73,10 +67,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onLocatePressed(); }, - icon: Icon( - Icons.image_search, - color: Colors.grey[200], - ), + icon: Icon(Icons.image_search, color: Colors.grey[200]), ); } @@ -85,20 +76,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onMoreInfoPressed(); }, - icon: Icon( - Icons.info_outline_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.info_outline_rounded, color: Colors.grey[200]), ); } Widget buildDownloadButton() { return IconButton( onPressed: onDownloadPressed, - icon: Icon( - Icons.cloud_download_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.cloud_download_outlined, color: Colors.grey[200]), ); } @@ -107,10 +92,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onAddToAlbumPressed(); }, - icon: Icon( - Icons.add, - color: Colors.grey[200], - ), + icon: Icon(Icons.add, color: Colors.grey[200]), ); } @@ -119,10 +101,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onRestorePressed(); }, - icon: Icon( - Icons.history_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.history_rounded, color: Colors.grey[200]), ); } @@ -134,19 +113,13 @@ class TopControlAppBar extends HookConsumerWidget { icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon( - Icons.mode_comment_outlined, - color: Colors.grey[200], - ), + Icon(Icons.mode_comment_outlined, color: Colors.grey[200]), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey[200], - ), + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey[200]), ), ), ], @@ -157,10 +130,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildUploadButton() { return IconButton( onPressed: onUploadPressed, - icon: Icon( - Icons.backup_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.backup_outlined, color: Colors.grey[200]), ); } @@ -169,21 +139,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - color: Colors.grey[200], - ), + icon: Icon(Icons.arrow_back_ios_new_rounded, size: 20.0, color: Colors.grey[200]), ); } Widget buildCastButton() { return IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, icon: Icon( isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, @@ -204,24 +167,14 @@ class TopControlAppBar extends HookConsumerWidget { shape: const Border(), actions: [ if (asset.isRemote && isOwner) buildFavoriteButton(a), - if (isOwner && - !isInHomePage && - !(isInTrash ?? false) && - !isInLockedView) - buildLocateButton(), + if (isOwner && !isInHomePage && !(isInTrash ?? false) && !isInLockedView) buildLocateButton(), if (asset.livePhotoVideoId != null) const MotionPhotoButton(), if (asset.isLocal && !asset.isRemote) buildUploadButton(), if (asset.isRemote && !asset.isLocal && isOwner) buildDownloadButton(), - if (asset.isRemote && - (isOwner || isPartner) && - !asset.isTrashed && - !isInLockedView) - buildAddToAlbumButton(), - if (isCasting || (asset.isRemote && websocketConnected)) - buildCastButton(), + if (asset.isRemote && (isOwner || isPartner) && !asset.isTrashed && !isInLockedView) buildAddToAlbumButton(), + if (isCasting || (asset.isRemote && websocketConnected)) buildCastButton(), if (asset.isTrashed) buildRestoreButton(), - if (album != null && album.shared && !isInLockedView) - buildActivitiesButton(), + if (album != null && album.shared && !isInLockedView) buildActivitiesButton(), buildMoreInfoButton(), ], ); diff --git a/mobile/lib/widgets/asset_viewer/video_controls.dart b/mobile/lib/widgets/asset_viewer/video_controls.dart index 22aa2b17d1..42f6078478 100644 --- a/mobile/lib/widgets/asset_viewer/video_controls.dart +++ b/mobile/lib/widgets/asset_viewer/video_controls.dart @@ -12,9 +12,6 @@ class VideoControls extends ConsumerWidget { final isPortrait = context.orientation == Orientation.portrait; return isPortrait ? const VideoPosition() - : const Padding( - padding: EdgeInsets.symmetric(horizontal: 60.0), - child: VideoPosition(), - ); + : const Padding(padding: EdgeInsets.symmetric(horizontal: 60.0), child: VideoPosition()); } } diff --git a/mobile/lib/widgets/asset_viewer/video_position.dart b/mobile/lib/widgets/asset_viewer/video_position.dart index 0e90669fe3..c12bb5e682 100644 --- a/mobile/lib/widgets/asset_viewer/video_position.dart +++ b/mobile/lib/widgets/asset_viewer/video_position.dart @@ -17,12 +17,8 @@ class VideoPosition extends HookConsumerWidget { final isCasting = ref.watch(castProvider).isCasting; final (position, duration) = isCasting - ? ref.watch( - castProvider.select((c) => (c.currentTime, c.duration)), - ) - : ref.watch( - videoPlaybackValueProvider.select((v) => (v.position, v.duration)), - ); + ? ref.watch(castProvider.select((c) => (c.currentTime, c.duration))) + : ref.watch(videoPlaybackValueProvider.select((v) => (v.position, v.duration))); final wasPlaying = useRef(true); return duration == Duration.zero @@ -34,28 +30,21 @@ class VideoPosition extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(position), - FormattedDuration(duration), - ], + children: [FormattedDuration(position), FormattedDuration(duration)], ), ), Row( children: [ Expanded( child: Slider( - value: min( - position.inMicroseconds / duration.inMicroseconds * 100, - 100, - ), + value: min(position.inMicroseconds / duration.inMicroseconds * 100, 100), min: 0, max: 100, thumbColor: Colors.white, activeColor: Colors.white, inactiveColor: whiteOpacity75, onChangeStart: (value) { - final state = - ref.read(videoPlaybackValueProvider).state; + final state = ref.read(videoPlaybackValueProvider).state; wasPlaying.value = state != VideoPlaybackState.paused; ref.read(videoPlayerControlsProvider.notifier).pause(); }, @@ -68,19 +57,14 @@ class VideoPosition extends HookConsumerWidget { final seekToDuration = (duration * (value / 100.0)); if (isCasting) { - ref - .read(castProvider.notifier) - .seekTo(seekToDuration); + ref.read(castProvider.notifier).seekTo(seekToDuration); return; } - ref - .read(videoPlayerControlsProvider.notifier) - .position = seekToDuration.inSeconds.toDouble(); + ref.read(videoPlayerControlsProvider.notifier).position = seekToDuration.inSeconds.toDouble(); // This immediately updates the slider position without waiting for the video to update - ref.read(videoPlaybackValueProvider.notifier).position = - seekToDuration; + ref.read(videoPlaybackValueProvider.notifier).position = seekToDuration; }, ), ), @@ -104,10 +88,7 @@ class _VideoPositionPlaceholder extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(Duration.zero), - FormattedDuration(Duration.zero), - ], + children: [FormattedDuration(Duration.zero), FormattedDuration(Duration.zero)], ), ), Row( diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index fa113c6291..d635e136bc 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -16,39 +16,25 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { final AvailableAlbum album; - const AlbumInfoCard({ - super.key, - required this.album, - }); + const AlbumInfoCard({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(album); - final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(album); - final syncAlbum = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); + final bool isSelected = ref.watch(backupProvider).selectedBackupAlbums.contains(album); + final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; - ColorFilter selectedFilter = ColorFilter.mode( - context.primaryColor.withAlpha(100), - BlendMode.darken, - ); - ColorFilter excludedFilter = - ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); - ColorFilter unselectedFilter = - const ColorFilter.mode(Colors.black, BlendMode.color); + ColorFilter selectedFilter = ColorFilter.mode(context.primaryColor.withAlpha(100), BlendMode.darken); + ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); + ColorFilter unselectedFilter = const ColorFilter.mode(Colors.black, BlendMode.color); buildSelectedTextBox() { if (isSelected) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_included", style: TextStyle( @@ -62,9 +48,7 @@ class AlbumInfoCard extends HookConsumerWidget { } else if (isExcluded) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_excluded", style: TextStyle( @@ -133,9 +117,7 @@ class AlbumInfoCard extends HookConsumerWidget { Radius.circular(12), // if you need this ), side: BorderSide( - color: isDarkTheme - ? const Color.fromARGB(255, 37, 35, 35) - : const Color(0xFFC9C9C9), + color: isDarkTheme ? const Color.fromARGB(255, 37, 35, 35) : const Color(0xFFC9C9C9), width: 1, ), ), @@ -153,24 +135,16 @@ class AlbumInfoCard extends HookConsumerWidget { child: const Image( width: double.infinity, height: double.infinity, - image: AssetImage( - 'assets/immich-logo.png', - ), + image: AssetImage('assets/immich-logo.png'), fit: BoxFit.cover, ), ), - Positioned( - bottom: 10, - right: 25, - child: buildSelectedTextBox(), - ), + Positioned(bottom: 10, right: 25, child: buildSelectedTextBox()), ], ), ), Padding( - padding: const EdgeInsets.only( - left: 25, - ), + padding: const EdgeInsets.only(left: 25), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -181,21 +155,13 @@ class AlbumInfoCard extends HookConsumerWidget { children: [ Text( album.name, - style: TextStyle( - fontSize: 14, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: context.primaryColor, fontWeight: FontWeight.bold), ), Padding( padding: const EdgeInsets.only(top: 2.0), child: Text( - album.assetCount.toString() + - (album.isAll ? " (${'all'.tr()})" : ""), - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - ), + album.assetCount.toString() + (album.isAll ? " (${'all'.tr()})" : ""), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ), ], @@ -203,15 +169,9 @@ class AlbumInfoCard extends HookConsumerWidget { ), IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ], diff --git a/mobile/lib/widgets/backup/album_info_list_tile.dart b/mobile/lib/widgets/backup/album_info_list_tile.dart index a263c004bd..9796f45e8b 100644 --- a/mobile/lib/widgets/backup/album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/album_info_list_tile.dart @@ -19,23 +19,15 @@ class AlbumInfoListTile extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(album); - final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(album); - final syncAlbum = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); + final bool isSelected = ref.watch(backupProvider).selectedBackupAlbums.contains(album); + final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); buildTileColor() { if (isSelected) { - return context.isDarkTheme - ? context.primaryColor.withAlpha(100) - : context.primaryColor.withAlpha(25); + return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25); } else if (isExcluded) { - return context.isDarkTheme - ? Colors.red[300]?.withAlpha(150) - : Colors.red[100]?.withAlpha(150); + return context.isDarkTheme ? Colors.red[300]?.withAlpha(150) : Colors.red[100]?.withAlpha(150); } else { return Colors.transparent; } @@ -43,23 +35,14 @@ class AlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -100,25 +83,13 @@ class AlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart index 98bcc2b3da..2cccded2bb 100644 --- a/mobile/lib/widgets/backup/asset_info_table.dart +++ b/mobile/lib/widgets/backup/asset_info_table.dart @@ -14,9 +14,7 @@ class BackupAssetInfoTable extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isUploadInProgress = ref.watch( @@ -29,18 +27,13 @@ class BackupAssetInfoTable extends ConsumerWidget { ); final asset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset), - ) + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset)) : ref.watch(backupProvider.select((value) => value.currentUploadAsset)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Table( - border: TableBorder.all( - color: context.colorScheme.outlineVariant, - width: 1, - ), + border: TableBorder.all(color: context.colorScheme.outlineVariant, width: 1), children: [ TableRow( children: [ @@ -48,21 +41,19 @@ class BackupAssetInfoTable extends ConsumerWidget { verticalAlignment: TableCellVerticalAlignment.middle, child: Padding( padding: const EdgeInsets.all(6.0), - child: Text( - 'backup_controller_page_filename', - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - fontWeight: FontWeight.bold, - fontSize: 10.0, - ), - ).tr( - namedArgs: isUploadInProgress - ? { - 'filename': asset.fileName, - 'size': asset.fileType.toLowerCase(), - } - : {'filename': "-", 'size': "-"}, - ), + child: + Text( + 'backup_controller_page_filename', + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary, + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + namedArgs: isUploadInProgress + ? {'filename': asset.fileName, 'size': asset.fileType.toLowerCase()} + : {'filename': "-", 'size': "-"}, + ), ), ), ], @@ -80,13 +71,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: { - 'date': isUploadInProgress - ? _getAssetCreationDate(asset) - : "-", - }, - ), + ).tr(namedArgs: {'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-"}), ), ), ], @@ -103,9 +88,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: {'id': isUploadInProgress ? asset.id : "-"}, - ), + ).tr(namedArgs: {'id': isUploadInProgress ? asset.id : "-"}), ), ), ], diff --git a/mobile/lib/widgets/backup/backup_info_card.dart b/mobile/lib/widgets/backup/backup_info_card.dart index 54551da35a..2ef7e24cd7 100644 --- a/mobile/lib/widgets/backup/backup_info_card.dart +++ b/mobile/lib/widgets/backup/backup_info_card.dart @@ -2,16 +2,22 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; final String info; + + final VoidCallback? onTap; + final bool isLoading; const BackupInfoCard({ super.key, required this.title, required this.subtitle, required this.info, + this.onTap, + this.isLoading = false, }); @override @@ -21,42 +27,78 @@ class BackupInfoCard extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(20), // if you need this ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, - child: ListTile( - minVerticalPadding: 18, - isThreeLine: true, - title: Text( - title, - style: context.textTheme.titleMedium, - ), - subtitle: Padding( - padding: const EdgeInsets.only(top: 4.0, right: 18.0), - child: Text( - subtitle, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, + child: Column( + children: [ + ListTile( + minVerticalPadding: 18, + isThreeLine: true, + title: Text(title, style: context.textTheme.titleMedium), + subtitle: Padding( + padding: const EdgeInsets.only(top: 4.0, right: 18.0), + child: Text( + subtitle, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + ), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Stack( + children: [ + Text( + info, + style: context.textTheme.titleLarge?.copyWith( + color: context.colorScheme.onSurface.withAlpha(isLoading ? 50 : 255), + ), + ), + if (isLoading) + Positioned.fill( + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + color: context.colorScheme.onSurface.withAlpha(150), + ), + ), + ), + ), + ], + ), + Text( + "backup_info_card_assets", + style: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.onSurface.withAlpha(isLoading ? 50 : 255), + ), + ).tr(), + ], ), ), - ), - trailing: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - info, - style: context.textTheme.titleLarge, + + if (onTap != null) ...[ + const Divider(height: 0), + ListTile( + enableFeedback: true, + visualDensity: VisualDensity.compact, + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), + ), + onTap: onTap, + title: Text( + "view_details".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + ), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: context.colorScheme.onSurfaceVariant), ), - Text( - "backup_info_card_assets", - style: context.textTheme.labelLarge, - ).tr(), ], - ), + ], ), ); } diff --git a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart index b6d0edb200..c2f94e706a 100644 --- a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart +++ b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart @@ -16,18 +16,11 @@ class CurrentUploadingAssetInfoBox extends StatelessWidget { Widget build(BuildContext context) { return ListTile( isThreeLine: true, - leading: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 30, - ), + leading: Icon(Icons.image_outlined, color: context.primaryColor, size: 30), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "backup_controller_page_uploading_file_info", - style: context.textTheme.titleSmall, - ).tr(), + Text("backup_controller_page_uploading_file_info", style: context.textTheme.titleSmall).tr(), const BackupErrorChip(), ], ), diff --git a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart index 42178c972e..596e46d934 100644 --- a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart @@ -4,12 +4,9 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/providers/album/album.provider.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class DriftAlbumInfoListTile extends HookConsumerWidget { @@ -22,19 +19,11 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { final bool isSelected = album.backupSelection == BackupSelection.selected; final bool isExcluded = album.backupSelection == BackupSelection.excluded; - final syncAlbum = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); - buildTileColor() { if (isSelected) { - return context.isDarkTheme - ? context.primaryColor.withAlpha(100) - : context.primaryColor.withAlpha(25); + return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25); } else if (isExcluded) { - return context.isDarkTheme - ? Colors.red[300]?.withAlpha(150) - : Colors.red[100]?.withAlpha(150); + return context.isDarkTheme ? Colors.red[300]?.withAlpha(150) : Colors.red[100]?.withAlpha(150); } else { return Colors.transparent; } @@ -42,23 +31,14 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -90,29 +70,16 @@ class DriftAlbumInfoListTile extends HookConsumerWidget { ref.read(backupAlbumProvider.notifier).deselectAlbum(album); } else { ref.read(backupAlbumProvider.notifier).selectAlbum(album); - if (syncAlbum) { - ref.read(albumProvider.notifier).createSyncAlbum(album.name); - } } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { context.pushRoute(LocalTimelineRoute(album: album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/error_chip.dart b/mobile/lib/widgets/backup/error_chip.dart index 4df3e50f64..191049cd75 100644 --- a/mobile/lib/widgets/backup/error_chip.dart +++ b/mobile/lib/widgets/backup/error_chip.dart @@ -11,17 +11,13 @@ class BackupErrorChip extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasErrors = - ref.watch(errorBackupListProvider.select((value) => value.isNotEmpty)); + final hasErrors = ref.watch(errorBackupListProvider.select((value) => value.isNotEmpty)); if (!hasErrors) { return const SizedBox(); } return ActionChip( - avatar: const Icon( - Icons.info, - color: red400, - ), + avatar: const Icon(Icons.info, color: red400), elevation: 1, visualDensity: VisualDensity.compact, label: const BackupErrorChipText(), diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart index 38c527ccfa..c987dfd331 100644 --- a/mobile/lib/widgets/backup/error_chip_text.dart +++ b/mobile/lib/widgets/backup/error_chip_text.dart @@ -16,11 +16,7 @@ class BackupErrorChipText extends ConsumerWidget { return const Text( "backup_controller_page_failed", - style: TextStyle( - color: red400, - fontWeight: FontWeight.bold, - fontSize: 11, - ), + style: TextStyle(color: red400, fontWeight: FontWeight.bold, fontSize: 11), ).tr(namedArgs: {'count': count.toString()}); } } diff --git a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart index c61fb1a0d1..9f0f7ec3eb 100644 --- a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart +++ b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart @@ -10,39 +10,24 @@ class IcloudDownloadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); if (!isIcloudAsset) { return const SizedBox(); } - final iCloudDownloadProgress = ref - .watch(backupProvider.select((value) => value.iCloudDownloadProgress)); + final iCloudDownloadProgress = ref.watch(backupProvider.select((value) => value.iCloudDownloadProgress)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - SizedBox( - width: 110, - child: Text( - "iCloud Download", - style: context.textTheme.labelSmall, - ), - ), + SizedBox(width: 110, child: Text("iCloud Download", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, @@ -50,10 +35,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), - Text( - " ${iCloudDownloadProgress ~/ 1}%", - style: const TextStyle(fontSize: 12), - ), + Text(" ${iCloudDownloadProgress ~/ 1}%", style: const TextStyle(fontSize: 12)), ], ), ); diff --git a/mobile/lib/widgets/backup/ios_debug_info_tile.dart b/mobile/lib/widgets/backup/ios_debug_info_tile.dart index de80b3bfd1..be333c6460 100644 --- a/mobile/lib/widgets/backup/ios_debug_info_tile.dart +++ b/mobile/lib/widgets/backup/ios_debug_info_tile.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/providers/backup/ios_background_settings.provider. /// more confident about background sync class IosDebugInfoTile extends HookConsumerWidget { final IOSBackgroundSettings settings; - const IosDebugInfoTile({ - super.key, - required this.settings, - }); + const IosDebugInfoTile({super.key, required this.settings}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,8 +21,7 @@ class IosDebugInfoTile extends HookConsumerWidget { if (processes == 0) { title = 'ios_debug_info_no_processes_queued'.t(context: context); } else { - title = 'ios_debug_info_processes_queued' - .t(context: context, args: {'count': processes}); + title = 'ios_debug_info_processes_queued'.t(context: context, args: {'count': processes}); } final df = DateFormat.yMd().add_jm(); @@ -33,39 +29,21 @@ class IosDebugInfoTile extends HookConsumerWidget { if (fetch == null && processing == null) { subtitle = 'ios_debug_info_no_sync_yet'.t(context: context); } else if (fetch != null && processing == null) { - subtitle = 'ios_debug_info_fetch_ran_at' - .t(context: context, args: {'dateTime': df.format(fetch)}); + subtitle = 'ios_debug_info_fetch_ran_at'.t(context: context, args: {'dateTime': df.format(fetch)}); } else if (processing != null && fetch == null) { - subtitle = 'ios_debug_info_processing_ran_at' - .t(context: context, args: {'dateTime': df.format(processing)}); + subtitle = 'ios_debug_info_processing_ran_at'.t(context: context, args: {'dateTime': df.format(processing)}); } else { - final fetchOrProcessing = - fetch!.isAfter(processing!) ? fetch : processing; - subtitle = 'ios_debug_info_last_sync_at'.t( - context: context, - args: {'dateTime': df.format(fetchOrProcessing)}, - ); + final fetchOrProcessing = fetch!.isAfter(processing!) ? fetch : processing; + subtitle = 'ios_debug_info_last_sync_at'.t(context: context, args: {'dateTime': df.format(fetchOrProcessing)}); } return ListTile( title: Text( title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: const TextStyle( - fontSize: 14, - ), - ), - leading: Icon( - Icons.bug_report, - color: context.primaryColor, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: context.primaryColor), ), + subtitle: Text(subtitle, style: const TextStyle(fontSize: 14)), + leading: Icon(Icons.bug_report, color: context.primaryColor), ); } } diff --git a/mobile/lib/widgets/backup/upload_progress_bar.dart b/mobile/lib/widgets/backup/upload_progress_bar.dart index 9281914d9c..65ff6c758a 100644 --- a/mobile/lib/widgets/backup/upload_progress_bar.dart +++ b/mobile/lib/widgets/backup/upload_progress_bar.dart @@ -11,41 +11,22 @@ class BackupUploadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); final uploadProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInPercentage), - ) - : ref.watch( - backupProvider.select((value) => value.progressInPercentage), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInPercentage)) + : ref.watch(backupProvider.select((value) => value.progressInPercentage)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - if (isIcloudAsset) - SizedBox( - width: 110, - child: Text( - "Immich Upload", - style: context.textTheme.labelSmall, - ), - ), + if (isIcloudAsset) SizedBox(width: 110, child: Text("Immich Upload", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, diff --git a/mobile/lib/widgets/backup/upload_stats.dart b/mobile/lib/widgets/backup/upload_stats.dart index 965202ce33..c9b626c51c 100644 --- a/mobile/lib/widgets/backup/upload_stats.dart +++ b/mobile/lib/widgets/backup/upload_stats.dart @@ -10,34 +10,23 @@ class BackupUploadStats extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final uploadFileProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSize), - ) + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSize)) : ref.watch(backupProvider.select((value) => value.progressInFileSize)); final uploadFileSpeed = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSpeed), - ) - : ref.watch( - backupProvider.select((value) => value.progressInFileSpeed), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSpeed)) + : ref.watch(backupProvider.select((value) => value.progressInFileSpeed)); return Padding( padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - uploadFileProgress, - style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), - ), + Text(uploadFileProgress, style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono")), Text( _formatUploadFileSpeed(uploadFileSpeed), style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index 388f202b6d..e504cf0675 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -1,7 +1,8 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; @@ -12,6 +13,7 @@ import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/common/app_bar_dialog/app_bar_profile_info.dart'; @@ -33,34 +35,24 @@ class ImmichAppBarDialog extends HookConsumerWidget { final horizontalPadding = isHorizontal ? 100.0 : 20.0; final user = ref.watch(currentUserProvider); final isLoggingOut = useState(false); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); - useEffect( - () { - ref.read(backupProvider.notifier).updateDiskInfo(); - ref.read(currentUserProvider.notifier).refresh(); - return null; - }, - [], - ); + useEffect(() { + ref.read(backupProvider.notifier).updateDiskInfo(); + ref.read(currentUserProvider.notifier).refresh(); + return null; + }, []); buildTopRow() { return Stack( children: [ Align( alignment: Alignment.topLeft, - child: InkWell( - onTap: () => context.pop(), - child: const Icon( - Icons.close, - size: 20, - ), - ), + child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)), ), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/immich-text-dark.png' - : 'assets/immich-text-light.png', + context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png', height: 16, ), ), @@ -68,29 +60,16 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } - buildActionButton( - IconData icon, - String text, - Function() onTap, { - Widget? trailing, - }) { + buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) { return ListTile( dense: true, visualDensity: VisualDensity.standard, contentPadding: const EdgeInsets.only(left: 30, right: 30), minLeadingWidth: 40, - leading: SizedBox( - child: Icon( - icon, - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - size: 20, - ), - ), + leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)), title: Text( text, - style: theme.textTheme.labelLarge?.copyWith( - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - ), + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), ).tr(), onTap: onTap, trailing: trailing, @@ -98,11 +77,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { } buildSettingButton() { - return buildActionButton( - Icons.settings_outlined, - "settings", - () => context.pushRoute(const SettingsRoute()), - ); + return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute())); } buildAppLogButton() { @@ -131,10 +106,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ok: "yes", onOk: () async { isLoggingOut.value = true; - await ref - .read(authProvider.notifier) - .logout() - .whenComplete(() => isLoggingOut.value = false); + await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false); ref.read(manualUploadProvider.notifier).cancelBackup(); ref.read(backupProvider.notifier).cancelBackup(); @@ -147,10 +119,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); }, trailing: isLoggingOut.value - ? const SizedBox.square( - dimension: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) + ? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2)) : null, ); } @@ -170,20 +139,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3), child: Container( padding: const EdgeInsets.symmetric(vertical: 4), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: ListTile( minLeadingWidth: 50, - leading: Icon( - Icons.storage_rounded, - color: theme.primaryColor, - ), + leading: Icon(Icons.storage_rounded, color: theme.primaryColor), title: Text( "backup_controller_page_server_storage", - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(), isThreeLine: true, subtitle: Padding( @@ -196,19 +158,14 @@ class ImmichAppBarDialog extends HookConsumerWidget { child: LinearProgressIndicator( minHeight: 10.0, value: percentage, - borderRadius: - const BorderRadius.all(Radius.circular(10.0)), + borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), Padding( padding: const EdgeInsets.only(top: 12.0), - child: - const Text('backup_controller_page_storage_format').tr( - namedArgs: { - 'used': usedDiskSpace, - 'total': totalDiskSpace, - }, - ), + child: const Text( + 'backup_controller_page_storage_format', + ).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}), ), ], ), @@ -227,43 +184,19 @@ class ImmichAppBarDialog extends HookConsumerWidget { InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://immich.app'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication); }, - child: Text( - "documentation", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "â€ĸ", - textAlign: TextAlign.center, - ), + child: Text("documentation", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("â€ĸ", textAlign: TextAlign.center)), InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://github.com/immich-app/immich'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication); }, - child: Text( - "profile_drawer_github", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "â€ĸ", - textAlign: TextAlign.center, - ), + child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("â€ĸ", textAlign: TextAlign.center)), InkWell( onTap: () async { context.pop(); @@ -284,6 +217,25 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } + buildReadonlyMessage() { + return Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10.0), + child: ListTile( + dense: true, + visualDensity: VisualDensity.standard, + contentPadding: const EdgeInsets.only(left: 20, right: 20), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + minLeadingWidth: 20, + tileColor: theme.primaryColor.withAlpha(80), + title: Text( + "profile_drawer_readonly_mode", + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), + textAlign: TextAlign.center, + ).tr(), + ), + ); + } + return Dismissible( behavior: HitTestBehavior.translucent, direction: DismissDirection.down, @@ -298,23 +250,17 @@ class ImmichAppBarDialog extends HookConsumerWidget { right: horizontalPadding, bottom: isHorizontal ? 20 : 100, ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), child: SizedBox( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - Container( - padding: const EdgeInsets.all(20), - child: buildTopRow(), - ), + Container(padding: const EdgeInsets.all(20), child: buildTopRow()), const AppBarProfileInfoBox(), buildStorageInformation(), const AppBarServerInfo(), + if (Store.isBetaTimelineEnabled && isReadonlyModeEnabled) buildReadonlyMessage(), buildAppLogButton(), buildSettingButton(), buildSignOutButton(), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 0224ff030f..00366ca580 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -1,24 +1,26 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/upload_profile_image.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AppBarProfileInfoBox extends HookConsumerWidget { - const AppBarProfileInfoBox({ - super.key, - }); + const AppBarProfileInfoBox({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); - final uploadProfileImageStatus = - ref.watch(uploadProfileImageProvider).status; + final uploadProfileImageStatus = ref.watch(uploadProfileImageProvider).status; + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); final user = ref.watch(currentUserProvider); buildUserProfileImage() { @@ -30,100 +32,93 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ); } - final userImage = UserCircleAvatar( - radius: 22, - size: 44, - user: user, - ); + final userImage = UserCircleAvatar(radius: 22, size: 44, user: user); if (uploadProfileImageStatus == UploadProfileStatus.loading) { - return const SizedBox( - height: 40, - width: 40, - child: ImmichLoadingIndicator(borderRadius: 20), - ); + return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20)); } return userImage; } pickUserProfileImage() async { - final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, - maxHeight: 1024, - maxWidth: 1024, - ); + final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); if (image != null) { - var success = - await ref.watch(uploadProfileImageProvider.notifier).upload(image); + var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image); if (success) { - final profileImagePath = - ref.read(uploadProfileImageProvider).profileImagePath; - ref.watch(authProvider.notifier).updateUserProfileImagePath( - profileImagePath, - ); + final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath; + ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath); if (user != null) { ref.read(currentUserProvider.notifier).refresh(); } + + ref.read(backupProvider.notifier).updateDiskInfo(); } } } + void toggleReadonlyMode() { + // read only mode is only supported int he beta experience + // TODO: remove this check when the beta UI goes stable + if (!Store.isBetaTimelineEnabled) return; + + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); + ref.read(readonlyModeProvider.notifier).toggleReadonlyMode(); + + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + } + return Padding( padding: const EdgeInsets.symmetric(horizontal: 10.0), child: Container( width: double.infinity, decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), ), child: ListTile( minLeadingWidth: 50, leading: GestureDetector( onTap: pickUserProfileImage, + onLongPress: toggleReadonlyMode, child: Stack( clipBehavior: Clip.none, children: [ - buildUserProfileImage(), - Positioned( - bottom: -5, - right: -8, - child: Material( - color: context.colorScheme.surfaceContainerHighest, - elevation: 3, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(50.0)), - ), - child: Padding( - padding: const EdgeInsets.all(5.0), - child: Icon( - Icons.camera_alt_outlined, - color: context.primaryColor, - size: 14, + AbsorbPointer(child: buildUserProfileImage()), + if (!isReadonlyModeEnabled) + Positioned( + bottom: -5, + right: -8, + child: Material( + color: context.colorScheme.surfaceContainerHighest, + elevation: 3, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), + child: Padding( + padding: const EdgeInsets.all(5.0), + child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), ), ), ), - ), ], ), ), title: Text( authState.name, - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500), ), subtitle: Text( authState.userEmail, - style: context.textTheme.bodySmall?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart index a867a11e00..4aacfb3322 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:package_info_plus/package_info_plus.dart'; class AppBarServerInfo extends HookConsumerWidget { - const AppBarServerInfo({ - super.key, - }); + const AppBarServerInfo({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget { getPackageInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - appInfo.value = { - "version": packageInfo.version, - "buildNumber": packageInfo.buildNumber, - }; + appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber}; } - useEffect( - () { - getPackageInfo(); - return null; - }, - [], - ); + useEffect(() { + getPackageInfo(); + return null; + }, []); return Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), child: Container( decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), @@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget { ? serverInfoState.versionMismatchErrorMessage : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500), ), ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -173,12 +149,10 @@ class AppBarServerInfo extends HookConsumerWidget { verticalOffset: 0, decoration: BoxDecoration( color: context.primaryColor.withValues(alpha: 0.9), - borderRadius: - const BorderRadius.all(Radius.circular(10)), + borderRadius: const BorderRadius.all(Radius.circular(10)), ), textStyle: TextStyle( - color: - context.isDarkTheme ? Colors.black : Colors.white, + color: context.isDarkTheme ? Colors.black : Colors.white, fontWeight: FontWeight.bold, ), message: getServerUrl() ?? '--', @@ -199,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -214,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget { if (serverInfoState.isNewReleaseAvailable) const Padding( padding: EdgeInsets.only(right: 5.0), - child: Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: 12, - ), + child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12), ), Text( "latest_version".tr(), diff --git a/mobile/lib/widgets/common/confirm_dialog.dart b/mobile/lib/widgets/common/confirm_dialog.dart index 411770e78f..153c124595 100644 --- a/mobile/lib/widgets/common/confirm_dialog.dart +++ b/mobile/lib/widgets/common/confirm_dialog.dart @@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: Text(title).tr(), content: Text(content).tr(), actions: [ @@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget { onPressed: () => context.pop(false), child: Text( cancel, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onOkPressed, child: Text( ok, - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 064c4845fe..9cc8de29ee 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -16,11 +16,8 @@ Future showDateTimePicker({ }) { return showDialog( context: context, - builder: (context) => _DateTimePicker( - initialDateTime: initialDateTime, - initialTZ: initialTZ, - initialTZOffset: initialTZOffset, - ), + builder: (context) => + _DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset), ); } @@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget { final String? initialTZ; final Duration? initialTZOffset; - const _DateTimePicker({ - this.initialDateTime, - this.initialTZ, - this.initialTZOffset, - }); + const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset}); _TimeZoneOffset _getInitiationLocation() { if (initialTZ != null) { try { - return _TimeZoneOffset.fromLocation( - tz.timeZoneDatabase.get(initialTZ!), - ); + return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!)); } on LocationNotFoundException { // no-op } @@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget { (location) => location.currentTimeZone.offset == offsetInMilli, ); // Prefer locations with abbreviation first - final location = locations.firstWhereOrNull( - (e) => !e.currentTimeZone.abbreviation.contains("0"), - ) ?? - locations.firstOrNull; + final location = + locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull; if (location != null) { return _TimeZoneOffset.fromLocation(location); } @@ -73,10 +62,7 @@ class _DateTimePicker extends HookWidget { // returns a list of location along with it's offset in duration List<_TimeZoneOffset> getAllTimeZones() { - return tz.timeZoneDatabase.locations.values - .map(_TimeZoneOffset.fromLocation) - .sorted() - .toList(); + return tz.timeZoneDatabase.locations.values.map(_TimeZoneOffset.fromLocation).sorted().toList(); } @override @@ -89,11 +75,7 @@ class _DateTimePicker extends HookWidget { (timezone) => DropdownMenuEntry<_TimeZoneOffset>( value: timezone, label: timezone.display, - style: ButtonStyle( - textStyle: WidgetStatePropertyAll( - context.textTheme.bodyMedium, - ), - ), + style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)), ), ) .toList(); @@ -112,10 +94,7 @@ class _DateTimePicker extends HookWidget { return; } - final newTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.fromDateTime(date.value), - ); + final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value)); if (newTime == null) { return; @@ -125,11 +104,9 @@ class _DateTimePicker extends HookWidget { } void popWithDateTime() { - final formattedDateTime = - DateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date.value); - final dtWithOffset = formattedDateTime + - Duration(milliseconds: tzOffset.value.offsetInMilliseconds) - .formatAsOffset(); + final formattedDateTime = DateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date.value); + final dtWithOffset = + formattedDateTime + Duration(milliseconds: tzOffset.value.offsetInMilliseconds).formatAsOffset(); context.pop(dtWithOffset); } @@ -150,10 +127,7 @@ class _DateTimePicker extends HookWidget { onPressed: popWithDateTime, child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -161,46 +135,22 @@ class _DateTimePicker extends HookWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - "date_and_time", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ).tr(), + const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(), const SizedBox(height: 32), ListTile( tileColor: context.colorScheme.surfaceContainerHighest, shape: ShapeBorder.lerp( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), 1, ), - trailing: Icon( - Icons.edit_outlined, - size: 18, - color: context.primaryColor, - ), - title: Text( - DateFormat("dd-MM-yyyy hh:mm a").format(date.value), - style: context.textTheme.bodyMedium, - ).tr(), + trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium), onTap: pickDate, ), const SizedBox(height: 24), DropdownSearchMenu( - trailingIcon: Icon( - Icons.arrow_drop_down, - color: context.primaryColor, - ), + trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor), hintText: "timezone".tr(), label: const Text('timezone').tr(), textStyle: context.textTheme.bodyMedium, @@ -218,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { final String display; final Location location; - const _TimeZoneOffset({ - required this.display, - required this.location, - }); + const _TimeZoneOffset({required this.display, required this.location}); - _TimeZoneOffset copyWith({ - String? display, - Location? location, - }) { - return _TimeZoneOffset( - display: display ?? this.display, - location: location ?? this.location, - ); + _TimeZoneOffset copyWith({String? display, Location? location}) { + return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location); } int get offsetInMilliseconds => location.currentTimeZone.offset; _TimeZoneOffset.fromLocation(tz.Location l) - : display = _getFormattedOffset(l.currentTimeZone.offset, l), - location = l; + : display = _getFormattedOffset(l.currentTimeZone.offset, l), + location = l; @override int compareTo(_TimeZoneOffset other) { @@ -245,19 +186,15 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { } @override - String toString() => - '_TimeZoneOffset(display: $display, location: $location)'; + String toString() => '_TimeZoneOffset(display: $display, location: $location)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is _TimeZoneOffset && - other.display == display && - other.offsetInMilliseconds == offsetInMilliseconds; + return other is _TimeZoneOffset && other.display == display && other.offsetInMilliseconds == offsetInMilliseconds; } @override - int get hashCode => - display.hashCode ^ offsetInMilliseconds.hashCode ^ location.hashCode; + int get hashCode => display.hashCode ^ offsetInMilliseconds.hashCode ^ location.hashCode; } diff --git a/mobile/lib/widgets/common/delayed_loading_indicator.dart b/mobile/lib/widgets/common/delayed_loading_indicator.dart index e54762bb9f..5fad10530e 100644 --- a/mobile/lib/widgets/common/delayed_loading_indicator.dart +++ b/mobile/lib/widgets/common/delayed_loading_indicator.dart @@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget { /// An optional fade in duration to animate the loading final Duration? fadeInDuration; - const DelayedLoadingIndicator({ - super.key, - this.delay = const Duration(seconds: 3), - this.child, - this.fadeInDuration, - }); + const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration}); @override Widget build(BuildContext context) { @@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget { builder: (context, snapshot) { late Widget c; if (snapshot.connectionState == ConnectionState.done) { - c = child ?? - const ImmichLoadingIndicator( - key: ValueKey('loading'), - ); + c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading')); } else { c = Container(key: const ValueKey('hiding')); } - return AnimatedSwitcher( - duration: fadeInDuration ?? Duration.zero, - child: c, - ); + return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c); }, ); } diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index a98e000e51..5d1fda1beb 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget { } class ControlBoxButton extends StatelessWidget { - const ControlBoxButton({ - super.key, - required this.label, - required this.iconData, - this.onPressed, - this.onLongPressed, - }); + const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed}); final String label; final IconData iconData; @@ -33,14 +27,11 @@ class ControlBoxButton extends StatelessWidget { @override Widget build(BuildContext context) { - final minWidth = - context.isMobile ? MediaQuery.sizeOf(context).width / 4.5 : 75.0; + final minWidth = context.isMobile ? MediaQuery.sizeOf(context).width / 4.5 : 75.0; return MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), onPressed: onPressed, onLongPress: onLongPressed, minWidth: minWidth, diff --git a/mobile/lib/widgets/common/dropdown_search_menu.dart b/mobile/lib/widgets/common/dropdown_search_menu.dart index cf954a8977..bf0c75c8aa 100644 --- a/mobile/lib/widgets/common/dropdown_search_menu.dart +++ b/mobile/lib/widgets/common/dropdown_search_menu.dart @@ -30,18 +30,12 @@ class DropdownSearchMenu extends HookWidget { @override Widget build(BuildContext context) { final selectedItem = useState?>( - dropdownMenuEntries - .firstWhereOrNull((item) => item.value == initialSelection), + dropdownMenuEntries.firstWhereOrNull((item) => item.value == initialSelection), ); final showTimeZoneDropdown = useState(false); - final effectiveConstraints = menuConstraints ?? - const BoxConstraints( - minWidth: 280, - maxWidth: 280, - minHeight: 0, - maxHeight: 280, - ); + final effectiveConstraints = + menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280); final inputDecoration = InputDecoration( contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4), @@ -59,12 +53,7 @@ class DropdownSearchMenu extends HookWidget { child: InputDecorator( decoration: inputDecoration, child: selectedItem.value != null - ? Text( - selectedItem.value!.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textStyle, - ) + ? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle) : null, ), ), @@ -77,10 +66,7 @@ class DropdownSearchMenu extends HookWidget { displayStringForOption: (option) => option.label, optionsBuilder: (textEditingValue) { return dropdownMenuEntries.where( - (item) => item.label - .toLowerCase() - .trim() - .contains(textEditingValue.text.toLowerCase().trim()), + (item) => item.label.toLowerCase().trim().contains(textEditingValue.text.toLowerCase().trim()), ); }, onSelected: (option) { @@ -93,9 +79,7 @@ class DropdownSearchMenu extends HookWidget { autofocus: true, focusNode: focusNode, controller: textEditingController, - decoration: inputDecoration.copyWith( - hintText: "search_timezone".tr(), - ), + decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()), maxLines: 1, style: context.textTheme.bodyMedium, expands: false, @@ -127,32 +111,16 @@ class DropdownSearchMenu extends HookWidget { onTap: () => onSelected(option), child: Builder( builder: (BuildContext context) { - final bool highlight = - AutocompleteHighlightedOption.of(context) == - index; + final bool highlight = AutocompleteHighlightedOption.of(context) == index; if (highlight) { - SchedulerBinding.instance.addPostFrameCallback( - (Duration timeStamp) { - Scrollable.ensureVisible( - context, - alignment: 0.5, - ); - }, - debugLabel: 'AutocompleteOptions.ensureVisible', - ); + SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { + Scrollable.ensureVisible(context, alignment: 0.5); + }, debugLabel: 'AutocompleteOptions.ensureVisible'); } return Container( - color: highlight - ? Theme.of(context) - .colorScheme - .onSurface - .withValues(alpha: 0.12) - : null, + color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null, padding: const EdgeInsets.all(16.0), - child: Text( - option.label, - style: textStyle, - ), + child: Text(option.label, style: textStyle), ); }, ), diff --git a/mobile/lib/widgets/common/fade_in_placeholder_image.dart b/mobile/lib/widgets/common/fade_in_placeholder_image.dart index 2be32fa8ba..2461dbe6bf 100644 --- a/mobile/lib/widgets/common/fade_in_placeholder_image.dart +++ b/mobile/lib/widgets/common/fade_in_placeholder_image.dart @@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget { fit: StackFit.expand, children: [ placeholder, - FadeInImage( - fadeInDuration: duration, - image: image, - fit: fit, - placeholder: MemoryImage(kTransparentImage), - ), + FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)), ], ), ); diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 799cf17f3f..28b5c535d2 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -28,8 +27,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { @override Widget build(BuildContext context, WidgetRef ref) { final BackUpState backupState = ref.watch(backupProvider); - final bool isEnableAutoBackup = - backupState.backgroundBackup || backupState.autoBackup; + final bool isEnableAutoBackup = backupState.backgroundBackup || backupState.autoBackup; final ServerInfo serverInfoState = ref.watch(serverInfoProvider); final user = ref.watch(currentUserProvider); final isDarkTheme = context.isDarkTheme; @@ -38,42 +36,24 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { buildProfileIndicator() { return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => + showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, - isLabelVisible: serverInfoState.isVersionMismatch || - ((user?.isAdmin ?? false) && - serverInfoState.isNewReleaseAvailable), + isLabelVisible: + serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -93,8 +73,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { semanticsLabel: 'backup_controller_page_backup'.tr(), ), ); - } else if (backupState.backupProgress != - BackUpProgressEnum.inBackground && + } else if (backupState.backupProgress != BackUpProgressEnum.inBackground && backupState.backupProgress != BackUpProgressEnum.manualInProgress) { return Icon( Icons.check_outlined, @@ -128,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -139,40 +116,37 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } return AppBar( backgroundColor: context.themeData.appBarTheme.backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: Builder( builder: (BuildContext context) { return Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Builder( - builder: (context) { - return Padding( - padding: const EdgeInsets.only(top: 3.0), - child: SvgPicture.asset( - context.isDarkTheme - ? 'assets/immich-logo-inline-dark.svg' - : 'assets/immich-logo-inline-light.svg', - height: 40, - ), - ); - }, + Padding( + padding: const EdgeInsets.only(top: 3.0), + child: SvgPicture.asset( + context.isDarkTheme ? 'assets/immich-logo-inline-dark.svg' : 'assets/immich-logo-inline-light.svg', + height: 40, + ), + ), + const Tooltip( + triggerMode: TooltipTriggerMode.tap, + showDuration: Duration(seconds: 4), + message: + "The old timeline is deprecated and will be removed in a future release. Kindly switch to the new timeline under Advanced Settings.", + child: Padding( + padding: EdgeInsets.only(top: 3.0), + child: Icon(Icons.error_rounded, fill: 1, color: Colors.amber, size: 20), + ), ), ], ); @@ -180,13 +154,8 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { ), actions: [ if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), - if (kDebugMode || kProfileMode || appFlavor == 'beta') + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), + if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), @@ -196,25 +165,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), - if (showUploadButton) - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildBackupIndicator(), - ), - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildProfileIndicator(), - ), + if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()), + Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()), ], ); } diff --git a/mobile/lib/widgets/common/immich_image.dart b/mobile/lib/widgets/common/immich_image.dart index cbad9037c0..c8bc9c1f6a 100644 --- a/mobile/lib/widgets/common/immich_image.dart +++ b/mobile/lib/widgets/common/immich_image.dart @@ -28,39 +28,25 @@ class ImmichImage extends StatelessWidget { // either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - double width = 1080, - double height = 1920, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteImageProvider( - assetId: assetId!, - ); + return ImmichRemoteImageProvider(assetId: assetId!); } if (useLocal(asset)) { - return ImmichLocalImageProvider( - asset: asset, - width: width, - height: height, - ); + return ImmichLocalImageProvider(asset: asset, width: width, height: height); } else { - return ImmichRemoteImageProvider( - assetId: asset.remoteId!, - ); + return ImmichRemoteImageProvider(assetId: asset.remoteId!); } } // Whether to use the local asset image provider or a remote one static bool useLocal(Asset asset) => - !asset.isRemote || - asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false); + !asset.isRemote || asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false); @override Widget build(BuildContext context) { @@ -69,17 +55,11 @@ class ImmichImage extends StatelessWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final imageProviderInstance = ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ); + final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height); return OctoImage( fadeInDuration: const Duration(milliseconds: 0), @@ -97,11 +77,7 @@ class ImmichImage extends StatelessWidget { errorBuilder: (context, error, stackTrace) { imageProviderInstance.evict(); - return Icon( - Icons.image_not_supported_outlined, - size: 32, - color: Colors.red[200], - ); + return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]); }, ); } diff --git a/mobile/lib/widgets/common/immich_loading_indicator.dart b/mobile/lib/widgets/common/immich_loading_indicator.dart index 8f9eaeaa99..52f957f7e7 100644 --- a/mobile/lib/widgets/common/immich_loading_indicator.dart +++ b/mobile/lib/widgets/common/immich_loading_indicator.dart @@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart'; class ImmichLoadingIndicator extends HookWidget { final double? borderRadius; - const ImmichLoadingIndicator({ - super.key, - this.borderRadius, - }); + const ImmichLoadingIndicator({super.key, this.borderRadius}); @override Widget build(BuildContext context) { - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - ) + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6)) ..reverse() ..repeat(); - final borderAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - )..repeat(); + final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat(); return Container( height: 80, @@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget { animation: borderAnimationController, builder: (context, child) { return CustomPaint( - painter: GradientBorderPainter( - animation: borderAnimationController.value, - strokeWidth: 3, - ), + painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3), child: child, ); }, @@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget { padding: const EdgeInsets.all(15), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), ), @@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter { const Color(0xFF18C249), ]; - GradientBorderPainter({ - required this.animation, - required this.strokeWidth, - }); + GradientBorderPainter({required this.animation, required this.strokeWidth}); @override void paint(Canvas canvas, Size size) { @@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter { colors.first.withValues(alpha: opacity), ], // Add evenly distributed stops - stops: List.generate( - colors.length + 1, - (index) => index / colors.length, - ), + stops: List.generate(colors.length + 1, (index) => index / colors.length), tileMode: TileMode.clamp, // Use transformations to rotate the gradient transform: GradientRotation(-animation * 2 * 3.14159), diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 43987878cb..a369275282 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget { final double size; final dynamic heroTag; - const ImmichLogo({ - super.key, - this.size = 100, - this.heroTag, - }); + const ImmichLogo({super.key, this.size = 100, this.heroTag}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index c7ddeca6e0..90c213599c 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -4,11 +4,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -41,8 +43,8 @@ class ImmichSliverAppBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); return SliverAnimatedOpacity( duration: Durations.medium1, @@ -52,52 +54,32 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: pinned, snap: snap, expandedHeight: expandedHeight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: title ?? const _ImmichLogoWithText(), actions: [ - if (isCasting) + if (isCasting && !isReadonlyModeEnabled) Padding( padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), const _SyncStatusIndicator(), if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), - if (kDebugMode || kProfileMode) + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), + if ((kDebugMode || kProfileMode) && !isReadonlyModeEnabled) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), ), - if (showUploadButton) - const Padding( - padding: EdgeInsets.only(right: 20), - child: _BackupIndicator(), - ), - const Padding( - padding: EdgeInsets.only(right: 20), - child: _ProfileIndicator(), - ), + if (showUploadButton && !isReadonlyModeEnabled) + const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), + const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()), ], ), ); @@ -115,30 +97,11 @@ class _ImmichLogoWithText extends StatelessWidget { children: [ Builder( builder: (context) { - return Badge( - padding: - const EdgeInsets.symmetric(horizontal: 6, vertical: 4), - backgroundColor: context.primaryColor, - alignment: Alignment.centerRight, - offset: const Offset(16, -8), - label: Text( - 'β', - style: TextStyle( - fontSize: 11, - color: context.colorScheme.onPrimary, - fontWeight: FontWeight.bold, - fontFamily: 'OverpassMono', - height: 1.2, - ), - ), - child: Padding( - padding: const EdgeInsets.only(top: 3.0), - child: SvgPicture.asset( - context.isDarkTheme - ? 'assets/immich-logo-inline-dark.svg' - : 'assets/immich-logo-inline-light.svg', - height: 40, - ), + return Padding( + padding: const EdgeInsets.only(top: 3.0), + child: SvgPicture.asset( + context.isDarkTheme ? 'assets/immich-logo-inline-dark.svg' : 'assets/immich-logo-inline-light.svg', + height: 40, ), ); }, @@ -159,126 +122,150 @@ class _ProfileIndicator extends ConsumerWidget { final user = ref.watch(currentUserProvider); const widgetSize = 30.0; + void toggleReadonlyMode() { + final isReadonlyModeEnabled = ref.watch(readonlyModeProvider); + ref.read(readonlyModeProvider.notifier).toggleReadonlyMode(); + + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (isReadonlyModeEnabled ? "readonly_mode_disabled" : "readonly_mode_enabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + } + return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), + onLongPress: () => toggleReadonlyMode(), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, - isLabelVisible: serverInfoState.isVersionMismatch || - ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), + isLabelVisible: + serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: AbsorbPointer(child: UserCircleAvatar(radius: 17, size: 31, user: user)), ), ), ); } } +const double _kBadgeWidgetSize = 30.0; + class _BackupIndicator extends ConsumerWidget { const _BackupIndicator(); @override Widget build(BuildContext context, WidgetRef ref) { - const widgetSize = 30.0; final indicatorIcon = _getBackupBadgeIcon(context, ref); - final badgeBackground = context.colorScheme.surfaceContainer; return InkWell( onTap: () => context.pushRoute(const DriftBackupRoute()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( - label: Container( - width: widgetSize / 2, - height: widgetSize / 2, - decoration: BoxDecoration( - color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: indicatorIcon, - ), + label: indicatorIcon, backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: _kBadgeWidgetSize, color: context.primaryColor), ), ); } Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) { - final BackUpState backupState = ref.watch(backupProvider); - final bool isEnableAutoBackup = - backupState.backgroundBackup || backupState.autoBackup; + final backupStateStream = ref.watch(settingsProvider).watch(Setting.enableBackup); + final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none)); final isDarkTheme = context.isDarkTheme; final iconColor = isDarkTheme ? Colors.white : Colors.black; + final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty)); - if (isEnableAutoBackup) { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - return Container( - padding: const EdgeInsets.all(3.5), - child: CircularProgressIndicator( - strokeWidth: 2, - strokeCap: StrokeCap.round, - valueColor: AlwaysStoppedAnimation(iconColor), - semanticsLabel: 'backup_controller_page_backup'.tr(), - ), + return StreamBuilder( + stream: backupStateStream, + initialData: false, + builder: (ctx, snapshot) { + final backupEnabled = snapshot.data ?? false; + + if (!backupEnabled) { + return _BadgeLabel( + Icon( + Icons.cloud_off_rounded, + size: 9, + color: iconColor, + semanticLabel: 'backup_controller_page_backup'.tr(), + ), + ); + } + + if (hasError) { + return _BadgeLabel( + Icon( + Icons.warning_rounded, + size: 12, + color: context.colorScheme.error, + semanticLabel: 'backup_controller_page_backup'.tr(), + ), + backgroundColor: context.colorScheme.errorContainer, + ); + } + + if (isUploading) { + return _BadgeLabel( + Container( + padding: const EdgeInsets.all(3.5), + child: Theme( + data: context.themeData.copyWith( + progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), + ), + child: CircularProgressIndicator( + strokeWidth: 2, + strokeCap: StrokeCap.round, + valueColor: AlwaysStoppedAnimation(iconColor), + semanticsLabel: 'backup_controller_page_backup'.tr(), + ), + ), + ), + ); + } + + return _BadgeLabel( + Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()), ); - } else if (backupState.backupProgress != - BackUpProgressEnum.inBackground && - backupState.backupProgress != BackUpProgressEnum.manualInProgress) { - return Icon( - Icons.check_outlined, - size: 9, - color: iconColor, - semanticLabel: 'backup_controller_page_backup'.tr(), - ); - } - } + }, + ); + } +} - if (!isEnableAutoBackup) { - return Icon( - Icons.cloud_off_rounded, - size: 9, - color: iconColor, - semanticLabel: 'backup_controller_page_backup'.tr(), - ); - } +class _BadgeLabel extends StatelessWidget { + final Widget indicator; + final Color? backgroundColor; - return null; + const _BadgeLabel(this.indicator, {this.backgroundColor}); + + @override + Widget build(BuildContext context) { + return Container( + width: _kBadgeWidgetSize / 2, + height: _kBadgeWidgetSize / 2, + decoration: BoxDecoration( + color: backgroundColor ?? context.colorScheme.surfaceContainer, + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), + borderRadius: BorderRadius.circular(_kBadgeWidgetSize / 2), + ), + child: indicator, + ); } } @@ -286,12 +273,10 @@ class _SyncStatusIndicator extends ConsumerStatefulWidget { const _SyncStatusIndicator(); @override - ConsumerState<_SyncStatusIndicator> createState() => - _SyncStatusIndicatorState(); + ConsumerState<_SyncStatusIndicator> createState() => _SyncStatusIndicatorState(); } -class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> - with TickerProviderStateMixin { +class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with TickerProviderStateMixin { late AnimationController _rotationController; late AnimationController _dismissalController; late Animation _rotationAnimation; @@ -300,27 +285,13 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> @override void initState() { super.initState(); - _rotationController = AnimationController( - duration: const Duration(seconds: 2), - vsync: this, - ); - _dismissalController = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); - _rotationAnimation = Tween( - begin: 0.0, - end: 1.0, - ).animate(_rotationController); + _rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); + _dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this); + _rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(_rotationController); _dismissalAnimation = Tween( begin: 1.0, end: 0.0, - ).animate( - CurvedAnimation( - parent: _dismissalController, - curve: Curves.easeOutQuart, - ), - ); + ).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart)); } @override @@ -333,7 +304,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> @override Widget build(BuildContext context) { final syncStatus = ref.watch(syncStatusProvider); - final isSyncing = syncStatus.isRemoteSyncing; + final isSyncing = syncStatus.isRemoteSyncing || syncStatus.isLocalSyncing; // Control animations based on sync status if (isSyncing) { @@ -349,8 +320,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> } // Don't show anything if not syncing and dismissal animation is complete - if (!isSyncing && - _dismissalController.status == AnimationStatus.completed) { + if (!isSyncing && _dismissalController.status == AnimationStatus.completed) { return const SizedBox.shrink(); } @@ -364,12 +334,8 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> child: Opacity( opacity: isSyncing ? 1.0 : _dismissalAnimation.value, child: Transform.rotate( - angle: _rotationAnimation.value * 2 * 3.14159, - child: Icon( - Icons.sync, - size: 24, - color: context.primaryColor, - ), + angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise + child: Icon(Icons.sync, size: 24, color: context.primaryColor), ), ), ), diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 0918348f4c..612a6a4bd0 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart'; import 'package:immich_mobile/providers/user.provider.dart'; class ImmichThumbnail extends HookConsumerWidget { - const ImmichThumbnail({ - this.asset, - this.width = 250, - this.height = 250, - this.fit = BoxFit.cover, - super.key, - }); + const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key}); final Asset? asset; final double width; @@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget { /// either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - String? userId, - int thumbnailSize = 256, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteThumbnailProvider( - assetId: assetId!, - ); + return ImmichRemoteThumbnailProvider(assetId: assetId!); } if (ImmichImage.useLocal(asset)) { - return ImmichLocalThumbnailProvider( - asset: asset, - height: thumbnailSize, - width: thumbnailSize, - userId: userId, - ); + return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId); } else { - return ImmichRemoteThumbnailProvider( - assetId: asset.remoteId!, - height: thumbnailSize, - width: thumbnailSize, - ); + return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize); } } @@ -72,29 +50,18 @@ class ImmichThumbnail extends HookConsumerWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final assetAltText = getAltText( - asset!.exifInfo, - asset!.fileCreatedAt, - asset!.type, - [], - ); + final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []); - final thumbnailProviderInstance = ImmichThumbnail.imageProvider( - asset: asset, - userId: userId, - ); + final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId); customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) { thumbnailProviderInstance.evict(); - final originalErrorWidgetBuilder = - blurHashErrorBuilder(blurhash, fit: fit); + final originalErrorWidgetBuilder = blurHashErrorBuilder(blurhash, fit: fit); return originalErrorWidgetBuilder(ctx, error, stackTrace); } diff --git a/mobile/lib/widgets/common/immich_title_text.dart b/mobile/lib/widgets/common/immich_title_text.dart index 711d0bf396..3a848a1db6 100644 --- a/mobile/lib/widgets/common/immich_title_text.dart +++ b/mobile/lib/widgets/common/immich_title_text.dart @@ -5,20 +5,12 @@ class ImmichTitleText extends StatelessWidget { final double fontSize; final Color? color; - const ImmichTitleText({ - super.key, - this.fontSize = 48, - this.color, - }); + const ImmichTitleText({super.key, this.fontSize = 48, this.color}); @override Widget build(BuildContext context) { return Image( - image: AssetImage( - context.isDarkTheme - ? 'assets/immich-text-dark.png' - : 'assets/immich-text-light.png', - ), + image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'), width: fontSize * 4, filterQuality: FilterQuality.high, color: context.primaryColor, diff --git a/mobile/lib/widgets/common/immich_toast.dart b/mobile/lib/widgets/common/immich_toast.dart index 945568a74c..dad8b33283 100644 --- a/mobile/lib/widgets/common/immich_toast.dart +++ b/mobile/lib/widgets/common/immich_toast.dart @@ -16,25 +16,16 @@ class ImmichToast { fToast.init(context); Color getColor(ToastType type, BuildContext context) => switch (type) { - ToastType.info => context.primaryColor, - ToastType.success => const Color.fromARGB(255, 78, 140, 124), - ToastType.error => const Color.fromARGB(255, 220, 48, 85), - }; + ToastType.info => context.primaryColor, + ToastType.success => const Color.fromARGB(255, 78, 140, 124), + ToastType.error => const Color.fromARGB(255, 220, 48, 85), + }; Icon getIcon(ToastType type) => switch (type) { - ToastType.info => Icon( - Icons.info_outline_rounded, - color: context.primaryColor, - ), - ToastType.success => const Icon( - Icons.check_circle_rounded, - color: Color.fromARGB(255, 78, 140, 124), - ), - ToastType.error => const Icon( - Icons.error_outline_rounded, - color: Color.fromARGB(255, 240, 162, 156), - ), - }; + ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor), + ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)), + ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)), + }; fToast.showToast( child: Container( @@ -42,26 +33,17 @@ class ImmichToast { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16.0)), color: context.colorScheme.surfaceContainer, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .5), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ getIcon(toastType), - const SizedBox( - width: 12.0, - ), + const SizedBox(width: 12.0), Flexible( child: Text( msg, - style: TextStyle( - color: getColor(toastType, context), - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14), ), ), ], diff --git a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart index 4880865e66..85d8f4bb01 100644 --- a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart @@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: true, centerTitle: true, - title: Text( - "on_this_device".t(context: context), - ), + title: Text("on_this_device".t(context: context)), ); } } diff --git a/mobile/lib/widgets/common/location_picker.dart b/mobile/lib/widgets/common/location_picker.dart index 7bfdf296bb..1f63299dd7 100644 --- a/mobile/lib/widgets/common/location_picker.dart +++ b/mobile/lib/widgets/common/location_picker.dart @@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -Future showLocationPicker({ - required BuildContext context, - LatLng? initialLatLng, -}) { +Future showLocationPicker({required BuildContext context, LatLng? initialLatLng}) { return showDialog( context: context, useRootNavigator: false, - builder: (ctx) => _LocationPicker( - initialLatLng: initialLatLng, - ), + builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng), ); } @@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual } class _LocationPicker extends HookWidget { final LatLng? initialLatLng; - const _LocationPicker({ - this.initialLatLng, - }); + const _LocationPicker({this.initialLatLng}); @override Widget build(BuildContext context) { @@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget { final pickerMode = useState(_LocationPickerMode.map); Future onMapTap() async { - final newLatLng = await context.pushRoute( - MapLocationPickerRoute(initialLatLng: latlng), - ); + final newLatLng = await context.pushRoute(MapLocationPickerRoute(initialLatLng: latlng)); if (newLatLng != null) { latitude.value = newLatLng.latitude; longitude.value = newLatLng.longitude; @@ -56,8 +47,7 @@ class _LocationPicker extends HookWidget { ? _MapPicker( key: ValueKey(latlng), latlng: latlng, - onModeSwitch: () => - pickerMode.value = _LocationPickerMode.manual, + onModeSwitch: () => pickerMode.value = _LocationPickerMode.manual, onMapTap: onMapTap, ) : _ManualPicker( @@ -82,10 +72,7 @@ class _LocationPicker extends HookWidget { onPressed: () => context.maybePop(latlng), child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -130,10 +117,7 @@ class _ManualPickerInput extends HookWidget { autofocus: false, decoration: InputDecoration( labelText: decorationText.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), floatingLabelBehavior: FloatingLabelBehavior.auto, border: const OutlineInputBorder(), hintText: hintText.tr(), @@ -141,8 +125,7 @@ class _ManualPickerInput extends HookWidget { errorText: isValid.value ? null : errorText.tr(), ), onEditingComplete: onEditingComplete, - keyboardType: - const TextInputType.numberWithOptions(decimal: true, signed: true), + keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true), inputFormatters: [LengthLimitingTextInputFormatter(8)], onTapOutside: (_) => focusNode.unfocus(), ); @@ -190,10 +173,7 @@ class _ManualPicker extends HookWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( icon: const Text("location_picker_choose_on_map").tr(), @@ -230,27 +210,17 @@ class _MapPicker extends StatelessWidget { final Function() onModeSwitch; final Function() onMapTap; - const _MapPicker({ - required this.latlng, - required this.onModeSwitch, - required this.onMapTap, - super.key, - }); + const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( - icon: Text( - "${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}", - ), + icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"), label: const Icon(Icons.edit_outlined, size: 16), onPressed: onModeSwitch, ), diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index eecc099a9e..73dbbfc85b 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -14,21 +14,15 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class MesmerizingSliverAppBar extends ConsumerStatefulWidget { - const MesmerizingSliverAppBar({ - super.key, - required this.title, - this.icon = Icons.camera, - }); + const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera}); final String title; final IconData icon; @override - ConsumerState createState() => - _MesmerizingSliverAppBarState(); + ConsumerState createState() => _MesmerizingSliverAppBarState(); } -class _MesmerizingSliverAppBarState - extends ConsumerState { +class _MesmerizingSliverAppBarState extends ConsumerState { double _scrollProgress = 0.0; double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { @@ -41,14 +35,12 @@ class _MesmerizingSliverAppBarState return 1.0; } - return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent) - .clamp(0.0, 1.0); + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); } @override Widget build(BuildContext context) { - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); return isMultiSelectEnabled ? SliverToBoxAdapter( @@ -65,26 +57,12 @@ class _MesmerizingSliverAppBarState elevation: 0, leading: IconButton( icon: Icon( - Platform.isIOS - ? Icons.arrow_back_ios_new_rounded - : Icons.arrow_back, - color: Color.lerp( - Colors.white, - context.primaryColor, - _scrollProgress, - ), + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), shadows: [ _scrollProgress < 0.95 - ? Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) - : const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ], ), onPressed: () { @@ -93,8 +71,7 @@ class _MesmerizingSliverAppBarState ), flexibleSpace: Builder( builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType< - FlexibleSpaceBarSettings>(); + final settings = context.dependOnInheritedWidgetOfExactType(); final scrollProgress = _calculateScrollProgress(settings); // Update scroll progress for the leading button @@ -113,11 +90,7 @@ class _MesmerizingSliverAppBarState child: scrollProgress > 0.95 ? Text( widget.title, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -138,19 +111,13 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final String title; final IconData icon; - const _ExpandedBackground({ - required this.scrollProgress, - required this.title, - required this.icon, - }); + const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon}); @override - ConsumerState<_ExpandedBackground> createState() => - _ExpandedBackgroundState(); + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); } -class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> - with SingleTickerProviderStateMixin { +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { late AnimationController _slideController; late Animation _slideAnimation; @@ -158,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -197,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), Container( @@ -211,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> colors: [ Colors.transparent, Colors.transparent, - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.2), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)), ], stops: const [0.0, 0.65, 1.0], ), @@ -241,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], ), ), ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), ), @@ -278,8 +223,7 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); } @override @@ -290,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( // letterSpacing: 0.2, fontWeight: FontWeight.bold, color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 1), - blurRadius: 6, - color: Colors.black45, - ), - ], + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], ), ); } @@ -319,17 +252,13 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); } -class _RandomAssetBackgroundState extends State<_RandomAssetBackground> - with TickerProviderStateMixin { +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { late AnimationController _zoomController; late AnimationController _crossFadeController; late Animation _zoomAnimation; @@ -346,47 +275,31 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> _zoomController = AnimationController( duration: const Duration(seconds: 12), vsync: this, + animationBehavior: AnimationBehavior.preserve, ); _crossFadeController = AnimationController( duration: const Duration(milliseconds: 1200), vsync: this, + animationBehavior: AnimationBehavior.preserve, ); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -476,9 +389,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -500,8 +411,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_currentAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -511,11 +421,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -532,8 +438,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_nextAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -543,11 +448,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart new file mode 100644 index 0000000000..0f9555a101 --- /dev/null +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -0,0 +1,570 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class PersonSliverAppBar extends ConsumerStatefulWidget { + const PersonSliverAppBar({ + super.key, + required this.person, + required this.onNameTap, + required this.onShowOptions, + required this.onBirthdayTap, + }); + + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + final VoidCallback onShowOptions; + + @override + ConsumerState createState() => _MesmerizingSliverAppBarState(); +} + +class _MesmerizingSliverAppBarState extends ConsumerState { + double _scrollProgress = 0.0; + + double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { + if (settings?.maxExtent == null || settings?.minExtent == null) { + return 1.0; + } + + final deltaExtent = settings!.maxExtent - settings.minExtent; + if (deltaExtent <= 0.0) { + return 1.0; + } + + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); + } + + @override + Widget build(BuildContext context) { + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); + List actionIconShadows = [ + if (_scrollProgress < 0.95) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + else + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ]; + + return isMultiSelectEnabled + ? SliverToBoxAdapter( + child: switch (_scrollProgress) { + < 0.8 => const SizedBox(height: 120), + _ => const SizedBox(height: 352), + }, + ) + : SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ], + ), + onPressed: () { + context.pop(); + }, + ), + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); + + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); + + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.person.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: _ExpandedBackground( + scrollProgress: scrollProgress, + person: widget.person, + onNameTap: widget.onNameTap, + onBirthdayTap: widget.onBirthdayTap, + ), + ); + }, + ), + ); + } +} + +class _ExpandedBackground extends ConsumerStatefulWidget { + final double scrollProgress; + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + + const _ExpandedBackground({ + required this.scrollProgress, + required this.person, + required this.onNameTap, + required this.onBirthdayTap, + }); + + @override + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); +} + +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { + late AnimationController _slideController; + late Animation _slideAnimation; + + @override + void initState() { + super.initState(); + + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); + + _slideAnimation = Tween( + begin: const Offset(0, 1.5), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); + + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted) { + _slideController.forward(); + } + }); + } + + @override + void dispose() { + _slideController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final timelineService = ref.watch(timelineServiceProvider); + + return Stack( + fit: StackFit.expand, + children: [ + Transform.translate( + offset: Offset(0, widget.scrollProgress * 50), + child: Transform.scale( + scale: 1.4 - (widget.scrollProgress * 0.2), + child: _RandomAssetBackground(timelineService: timelineService), + ), + ), + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.05), + Colors.transparent, + Colors.black.withValues(alpha: 0.3), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), + ], + stops: const [0.0, 0.15, 0.55, 1.0], + ), + ), + ), + ), + ), + Positioned( + bottom: 16, + left: 16, + right: 16, + child: SlideTransition( + position: _slideAnimation, + child: Row( + children: [ + SizedBox( + height: 84, + width: 84, + child: Material( + shape: const CircleBorder(side: BorderSide(color: Colors.grey, width: 1.0)), + elevation: 3, + child: CircleAvatar( + maxRadius: 84 / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(widget.person.id), + headers: ApiService.getRequestHeaders(), + ), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => widget.onNameTap.call(), + child: SizedBox( + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: widget.person.name.isNotEmpty + ? Text( + widget.person.name, + maxLines: 1, + style: const TextStyle( + color: Colors.white, + fontSize: 36, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], + ), + ) + : Text( + 'add_a_name'.tr(), + style: context.textTheme.titleLarge?.copyWith( + color: Colors.grey[400], + fontSize: 36, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), + const SizedBox(height: 8), + GestureDetector( + onTap: widget.onBirthdayTap, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.cake_rounded, color: Colors.white, size: 14), + const SizedBox(width: 4), + + if (widget.person.birthDate != null) + Text( + "${DateFormat.yMMMd(context.locale.toString()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})", + style: context.textTheme.labelLarge?.copyWith( + color: Colors.white, + height: 1.2, + fontSize: 14, + ), + ) + else + Text( + 'add_birthday'.tr(), + style: context.textTheme.labelLarge?.copyWith( + color: Colors.grey[400], + height: 1.2, + fontSize: 14, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _ItemCountText extends ConsumerStatefulWidget { + const _ItemCountText(); + + @override + ConsumerState<_ItemCountText> createState() => _ItemCountTextState(); +} + +class _ItemCountTextState extends ConsumerState<_ItemCountText> { + StreamSubscription? _reloadSubscription; + + @override + void initState() { + super.initState(); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); + } + + @override + void dispose() { + _reloadSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); + + return Text( + 'items_count'.t(context: context, args: {"count": assetCount}), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], + ), + ); + } +} + +class _RandomAssetBackground extends StatefulWidget { + final TimelineService timelineService; + + const _RandomAssetBackground({required this.timelineService}); + + @override + State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); +} + +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { + late AnimationController _zoomController; + late AnimationController _crossFadeController; + late Animation _zoomAnimation; + late Animation _panAnimation; + late Animation _crossFadeAnimation; + BaseAsset? _currentAsset; + BaseAsset? _nextAsset; + bool _isZoomingIn = true; + + @override + void initState() { + super.initState(); + + _zoomController = AnimationController( + duration: const Duration(seconds: 12), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); + + _crossFadeController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + animationBehavior: AnimationBehavior.preserve, + ); + + _zoomAnimation = Tween( + begin: 1.0, + end: 1.2, + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _panAnimation = Tween( + begin: Offset.zero, + end: const Offset(0.5, -0.5), + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _crossFadeAnimation = Tween( + begin: 0.0, + end: 1.0, + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); + + Future.delayed(Durations.medium1, () => _loadFirstAsset()); + } + + @override + void dispose() { + _zoomController.dispose(); + _crossFadeController.dispose(); + super.dispose(); + } + + void _startAnimationCycle() { + if (_isZoomingIn) { + _zoomController.forward().then((_) { + _loadNextAsset(); + }); + } else { + _zoomController.reverse().then((_) { + _loadNextAsset(); + }); + } + } + + Future _loadFirstAsset() async { + if (!mounted) { + return; + } + + if (widget.timelineService.totalAssets == 0) { + setState(() { + _currentAsset = null; + }); + + return; + } + + setState(() { + _currentAsset = widget.timelineService.getRandomAsset(); + }); + + await _crossFadeController.forward(); + + if (_zoomController.status == AnimationStatus.dismissed) { + if (_isZoomingIn) { + _zoomController.reset(); + } else { + _zoomController.value = 1.0; + } + _startAnimationCycle(); + } + } + + Future _loadNextAsset() async { + if (!mounted) { + return; + } + + try { + if (widget.timelineService.totalAssets > 1) { + // Load next asset while keeping current one visible + final nextAsset = widget.timelineService.getRandomAsset(); + + setState(() { + _nextAsset = nextAsset; + }); + + await _crossFadeController.reverse(); + setState(() { + _currentAsset = _nextAsset; + _nextAsset = null; + }); + + _crossFadeController.value = 1.0; + + _isZoomingIn = !_isZoomingIn; + + _startAnimationCycle(); + } + } catch (e) { + _zoomController.reset(); + _startAnimationCycle(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.timelineService.totalAssets == 0) { + return const SizedBox.shrink(); + } + + return AnimatedBuilder( + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), + builder: (context, child) { + return Transform.scale( + scale: _zoomAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Transform.translate( + offset: _panAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Stack( + fit: StackFit.expand, + children: [ + // Current image + if (_currentAsset != null) + Opacity( + opacity: _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_currentAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return Container(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + + if (_nextAsset != null) + Opacity( + opacity: 1.0 - _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_nextAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return const SizedBox.shrink(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 41eed09d8c..f75dd6e803 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -13,11 +14,11 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/datetime_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; -import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/album/remote_album_shared_user_icons.dart'; class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget { @@ -27,20 +28,20 @@ class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget { this.onShowOptions, this.onToggleAlbumOrder, this.onEditTitle, + this.onActivity, }); final IconData icon; final void Function()? onShowOptions; final void Function()? onToggleAlbumOrder; final void Function()? onEditTitle; + final void Function()? onActivity; @override - ConsumerState createState() => - _MesmerizingSliverAppBarState(); + ConsumerState createState() => _MesmerizingSliverAppBarState(); } -class _MesmerizingSliverAppBarState - extends ConsumerState { +class _MesmerizingSliverAppBarState extends ConsumerState { double _scrollProgress = 0.0; double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { @@ -53,126 +54,100 @@ class _MesmerizingSliverAppBarState return 1.0; } - return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent) - .clamp(0.0, 1.0); + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); } @override Widget build(BuildContext context) { - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); final currentAlbum = ref.watch(currentRemoteAlbumProvider); if (currentAlbum == null) { return const SliverToBoxAdapter(child: SizedBox.shrink()); } - Color? actionIconColor = Color.lerp( - Colors.white, - context.primaryColor, - _scrollProgress, - ); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); List actionIconShadows = [ if (_scrollProgress < 0.95) - Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) else - const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ]; - return isMultiSelectEnabled - ? SliverToBoxAdapter( - child: switch (_scrollProgress) { - < 0.8 => const SizedBox(height: 120), - _ => const SizedBox(height: 452), - }, - ) - : SliverAppBar( - expandedHeight: 400.0, - floating: false, - pinned: true, - snap: false, - elevation: 0, - leading: IconButton( + return SliverAppBar( + expandedHeight: 400.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: isMultiSelectEnabled + ? const SizedBox.shrink() + : IconButton( icon: Icon( - Platform.isIOS - ? Icons.arrow_back_ios_new_rounded - : Icons.arrow_back, + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, color: actionIconColor, shadows: actionIconShadows, ), - onPressed: () { - ref.read(remoteAlbumProvider.notifier).refresh(); - context.pop(); - }, + onPressed: () => context.navigateTo(const TabShellRoute(children: [DriftAlbumsRoute()])), ), - actions: [ - if (widget.onToggleAlbumOrder != null) - IconButton( - icon: Icon( - Icons.swap_vert_rounded, - color: actionIconColor, - shadows: actionIconShadows, - ), - onPressed: widget.onToggleAlbumOrder, - ), - if (widget.onShowOptions != null) - IconButton( - icon: Icon( - Icons.more_vert, - color: actionIconColor, - shadows: actionIconShadows, - ), - onPressed: widget.onShowOptions, - ), - ], - flexibleSpace: Builder( - builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType< - FlexibleSpaceBarSettings>(); - final scrollProgress = _calculateScrollProgress(settings); + actions: [ + if (widget.onToggleAlbumOrder != null) + IconButton( + icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onToggleAlbumOrder, + ), + if (currentAlbum.isActivityEnabled && currentAlbum.isShared) + IconButton( + icon: Icon(Icons.chat_outlined, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onActivity, + ), + if (widget.onShowOptions != null) + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + title: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); - // Update scroll progress for the leading button - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && _scrollProgress != scrollProgress) { - setState(() { - _scrollProgress = scrollProgress; - }); - } - }); + return AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + currentAlbum.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ); + }, + ), + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); - return FlexibleSpaceBar( - centerTitle: true, - title: AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: scrollProgress > 0.95 - ? Text( - currentAlbum.name, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), - ) - : null, - ), - background: _ExpandedBackground( - scrollProgress: scrollProgress, - icon: widget.icon, - onEditTitle: widget.onEditTitle, - ), - ); - }, + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); + + return FlexibleSpaceBar( + background: _ExpandedBackground( + scrollProgress: scrollProgress, + icon: widget.icon, + onEditTitle: widget.onEditTitle, ), ); + }, + ), + ); } } @@ -181,19 +156,13 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final IconData icon; final void Function()? onEditTitle; - const _ExpandedBackground({ - required this.scrollProgress, - required this.icon, - this.onEditTitle, - }); + const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle}); @override - ConsumerState<_ExpandedBackground> createState() => - _ExpandedBackgroundState(); + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); } -class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> - with SingleTickerProviderStateMixin { +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { late AnimationController _slideController; late Animation _slideAnimation; @@ -201,20 +170,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -238,9 +199,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> return const SizedBox.shrink(); } - final dateRange = ref.watch( - remoteAlbumDateRangeProvider(currentAlbum.id), - ); + final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id)); return Stack( fit: StackFit.expand, children: [ @@ -248,18 +207,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), ClipRect( child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: widget.scrollProgress * 2.0, - sigmaY: widget.scrollProgress * 2.0, - ), + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), child: Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -269,9 +222,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> Colors.black.withValues(alpha: 0.05), Colors.transparent, Colors.black.withValues(alpha: 0.3), - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.25), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), ], stops: const [0.0, 0.15, 0.55, 1.0], ), @@ -300,32 +251,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> ), style: const TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), const Text( " â€ĸ ", style: TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), GestureDetector( @@ -342,13 +278,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)], ), ), ), @@ -358,31 +288,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> GestureDetector( onTap: widget.onEditTitle, child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 80, - ), + constraints: const BoxConstraints(maxHeight: 80), child: SingleChildScrollView( child: Text( currentAlbum.description, style: const TextStyle( color: Colors.white, fontSize: 14, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 8, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)], ), ), ), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0), - child: RemoteAlbumSharedUserIcons(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()), ], ), ), @@ -405,8 +324,7 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); } @override @@ -417,24 +335,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ); } @@ -444,17 +351,13 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); } -class _RandomAssetBackgroundState extends State<_RandomAssetBackground> - with TickerProviderStateMixin { +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { late AnimationController _zoomController; late AnimationController _crossFadeController; late Animation _zoomAnimation; @@ -471,47 +374,31 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> _zoomController = AnimationController( duration: const Duration(seconds: 12), vsync: this, + animationBehavior: AnimationBehavior.preserve, ); _crossFadeController = AnimationController( duration: const Duration(milliseconds: 1200), vsync: this, + animationBehavior: AnimationBehavior.preserve, ); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -601,9 +488,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -625,8 +510,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_currentAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -636,11 +520,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -657,8 +537,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_nextAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -668,11 +547,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/scaffold_error_body.dart b/mobile/lib/widgets/common/scaffold_error_body.dart index 5011d229e7..2e2d8fb506 100644 --- a/mobile/lib/widgets/common/scaffold_error_body.dart +++ b/mobile/lib/widgets/common/scaffold_error_body.dart @@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "scaffold_body_error_occurred", - style: context.textTheme.displayMedium, - textAlign: TextAlign.center, - ).tr(), + Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(), if (withIcon) Center( child: Padding( @@ -27,19 +23,14 @@ class ScaffoldErrorBody extends StatelessWidget { child: Icon( Icons.error_outline, size: 100, - color: - context.themeData.iconTheme.color?.withValues(alpha: 0.5), + color: context.themeData.iconTheme.color?.withValues(alpha: 0.5), ), ), ), if (withIcon && errorMsg != null) Padding( padding: const EdgeInsets.all(20), - child: Text( - errorMsg!, - style: context.textTheme.displaySmall, - textAlign: TextAlign.center, - ), + child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center), ), ], ); diff --git a/mobile/lib/widgets/common/search_field.dart b/mobile/lib/widgets/common/search_field.dart index 97ac75a63b..84af2d050c 100644 --- a/mobile/lib/widgets/common/search_field.dart +++ b/mobile/lib/widgets/common/search_field.dart @@ -43,40 +43,22 @@ class SearchField extends StatelessWidget { contentPadding: contentPadding, filled: filled, fillColor: context.primaryColor.withValues(alpha: 0.1), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurfaceSecondary, - ), + hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary), border: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainer, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceContainer), ), disabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), focusedBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.primary.withAlpha(100), - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)), ), prefixIcon: prefixIcon, suffixIcon: suffixIcon, diff --git a/mobile/lib/widgets/common/selection_sliver_app_bar.dart b/mobile/lib/widgets/common/selection_sliver_app_bar.dart index 2f06934bc0..ac74e69e64 100644 --- a/mobile/lib/widgets/common/selection_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/selection_sliver_app_bar.dart @@ -1,4 +1,3 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; @@ -7,25 +6,18 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class SelectionSliverAppBar extends ConsumerStatefulWidget { - const SelectionSliverAppBar({ - super.key, - }); + const SelectionSliverAppBar({super.key}); @override - ConsumerState createState() => - _SelectionSliverAppBarState(); + ConsumerState createState() => _SelectionSliverAppBarState(); } class _SelectionSliverAppBarState extends ConsumerState { @override Widget build(BuildContext context) { - final selection = ref.watch( - multiSelectProvider.select((s) => s.selectedAssets), - ); + final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - final toExclude = ref.watch( - multiSelectProvider.select((s) => s.lockedSelectionAssets), - ); + final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets)); final filteredAssets = selection.where((asset) { return !toExclude.contains(asset); @@ -33,7 +25,7 @@ class _SelectionSliverAppBarState extends ConsumerState { onDone(Set selected) { ref.read(multiSelectProvider.notifier).reset(); - context.maybePop>(selected); + context.pop>(selected); } return SliverAppBar( @@ -41,9 +33,7 @@ class _SelectionSliverAppBarState extends ConsumerState { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, leading: IconButton( icon: const Icon(Icons.close_rounded), @@ -53,22 +43,13 @@ class _SelectionSliverAppBarState extends ConsumerState { }, ), centerTitle: true, - title: Text( - "Select {count}".t( - context: context, - args: { - 'count': filteredAssets.length.toString(), - }, - ), - ), + title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})), actions: [ TextButton( onPressed: () => onDone(filteredAssets), child: Text( 'done'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), ), ], diff --git a/mobile/lib/widgets/common/share_dialog.dart b/mobile/lib/widgets/common/share_dialog.dart index 1c7eb3580a..625390c4b7 100644 --- a/mobile/lib/widgets/common/share_dialog.dart +++ b/mobile/lib/widgets/common/share_dialog.dart @@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), - Container( - margin: const EdgeInsets.only(top: 12), - child: const Text('share_dialog_preparing').tr(), - ), + Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()), ], ), ); diff --git a/mobile/lib/widgets/common/thumbhash_placeholder.dart b/mobile/lib/widgets/common/thumbhash_placeholder.dart index aa320f4230..0cb1222989 100644 --- a/mobile/lib/widgets/common/thumbhash_placeholder.dart +++ b/mobile/lib/widgets/common/thumbhash_placeholder.dart @@ -6,22 +6,14 @@ import 'package:octo_image/octo_image.dart'; /// Simple set to show [OctoPlaceholder.circularProgressIndicator] as /// placeholder and [OctoError.icon] as error. -OctoSet blurHashOrPlaceholder( - Uint8List? blurhash, { - BoxFit? fit, - Text? errorMessage, -}) { +OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) { return OctoSet( placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), - errorBuilder: - blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), + errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), ); } -OctoPlaceholderBuilder blurHashPlaceholderBuilder( - Uint8List? blurhash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) { return (context) => blurhash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart index a5a6fa2bdd..ff0e39f371 100644 --- a/mobile/lib/widgets/common/user_avatar.dart +++ b/mobile/lib/widgets/common/user_avatar.dart @@ -7,8 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; Widget userAvatar(BuildContext context, UserDto u, {double? radius}) { - final url = - "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image"; + final url = "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image"; final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : ""; return CircleAvatar( radius: radius, diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 479c30d6da..b46f560122 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget { double size; bool hasBorder; - UserCircleAvatar({ - super.key, - this.radius = 22, - this.size = 44, - this.hasBorder = false, - required this.user, - }); + UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -34,34 +28,27 @@ class UserCircleAvatar extends ConsumerWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12, - color: userAvatarColor.computeLuminance() > 0.5 - ? Colors.black - : Colors.white, + color: userAvatarColor.computeLuminance() > 0.5 ? Colors.black : Colors.white, ), child: Text(user.name[0].toUpperCase()), ); + return Tooltip( message: user.name, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - border: hasBorder - ? Border.all( - color: Colors.grey[500]!, - width: 1, - ) - : null, + border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null, ), child: CircleAvatar( backgroundColor: userAvatarColor, radius: radius, - child: user.profileImagePath == null - ? textIcon - : ClipRRect( + child: user.hasProfileImage + ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), child: CachedNetworkImage( fit: BoxFit.cover, - cacheKey: user.profileImagePath, + cacheKey: '${user.id}-${user.profileChangedAt.toIso8601String()}', width: size, height: size, placeholder: (_, __) => Image.memory(kTransparentImage), @@ -70,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget { fadeInDuration: const Duration(milliseconds: 300), errorWidget: (context, error, stackTrace) => textIcon, ), - ), + ) + : textIcon, ), ), ); diff --git a/mobile/lib/widgets/forms/change_password_form.dart b/mobile/lib/widgets/forms/change_password_form.dart index 1c7eed7e5b..179b05a712 100644 --- a/mobile/lib/widgets/forms/change_password_form.dart +++ b/mobile/lib/widgets/forms/change_password_form.dart @@ -17,10 +17,8 @@ class ChangePasswordForm extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final passwordController = - useTextEditingController.fromValue(TextEditingValue.empty); - final confirmPasswordController = - useTextEditingController.fromValue(TextEditingValue.empty); + final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); + final confirmPasswordController = useTextEditingController.fromValue(TextEditingValue.empty); final authState = ref.watch(authProvider); final formKey = GlobalKey(); @@ -35,25 +33,13 @@ class ChangePasswordForm extends HookConsumerWidget { children: [ Text( 'change_password'.tr(), - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: context.primaryColor), ), Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: Text( - 'change_password_form_description'.tr( - namedArgs: { - 'name': authState.name, - }, - ), - style: TextStyle( - fontSize: 14, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w600, - ), + 'change_password_form_description'.tr(namedArgs: {'name': authState.name}), + style: TextStyle(fontSize: 14, color: context.colorScheme.onSurface, fontWeight: FontWeight.w600), ), ), Form( @@ -79,13 +65,9 @@ class ChangePasswordForm extends HookConsumerWidget { if (isSuccess) { await ref.read(authProvider.notifier).logout(); - ref - .read(manualUploadProvider.notifier) - .cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); ref.read(backupProvider.notifier).cancelBackup(); - await ref - .read(assetProvider.notifier) - .clearAllAssets(); + await ref.read(assetProvider.notifier).clearAllAssets(); ref.read(websocketProvider.notifier).disconnect(); AutoRouter.of(context).back(); @@ -146,11 +128,7 @@ class ConfirmPasswordInput extends StatelessWidget { final TextEditingController originalController; final TextEditingController confirmController; - const ConfirmPasswordInput({ - super.key, - required this.originalController, - required this.confirmController, - }); + const ConfirmPasswordInput({super.key, required this.originalController, required this.confirmController}); String? _validateInput(String? email) { if (confirmController.value != originalController.value) { @@ -178,11 +156,7 @@ class ConfirmPasswordInput extends StatelessWidget { class ChangePasswordButton extends ConsumerWidget { final TextEditingController passwordController; final VoidCallback onPressed; - const ChangePasswordButton({ - super.key, - required this.passwordController, - required this.onPressed, - }); + const ChangePasswordButton({super.key, required this.passwordController, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -192,10 +166,7 @@ class ChangePasswordButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), ), onPressed: onPressed, - child: Text( - 'change_password'.tr(), - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + child: Text('change_password'.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/email_input.dart b/mobile/lib/widgets/forms/login/email_input.dart index 52f2a598f9..4d90d918ac 100644 --- a/mobile/lib/widgets/forms/login/email_input.dart +++ b/mobile/lib/widgets/forms/login/email_input.dart @@ -6,12 +6,7 @@ class EmailInput extends StatelessWidget { final FocusNode? focusNode; final Function()? onSubmit; - const EmailInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const EmailInput({super.key, required this.controller, this.focusNode, this.onSubmit}); String? _validateInput(String? email) { if (email == null || email == '') return null; @@ -32,10 +27,7 @@ class EmailInput extends StatelessWidget { labelText: 'email'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), ), validator: _validateInput, autovalidateMode: AutovalidateMode.always, diff --git a/mobile/lib/widgets/forms/login/loading_icon.dart b/mobile/lib/widgets/forms/login/loading_icon.dart index 9d3f5eab64..052ce43ac7 100644 --- a/mobile/lib/widgets/forms/login/loading_icon.dart +++ b/mobile/lib/widgets/forms/login/loading_icon.dart @@ -7,15 +7,7 @@ class LoadingIcon extends StatelessWidget { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.only(top: 18.0), - child: SizedBox( - width: 24, - height: 24, - child: FittedBox( - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - ), + child: SizedBox(width: 24, height: 24, child: FittedBox(child: CircularProgressIndicator(strokeWidth: 2))), ); } } diff --git a/mobile/lib/widgets/forms/login/login_button.dart b/mobile/lib/widgets/forms/login/login_button.dart index 479c53a9b7..0f9fb21d8f 100644 --- a/mobile/lib/widgets/forms/login/login_button.dart +++ b/mobile/lib/widgets/forms/login/login_button.dart @@ -5,23 +5,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; class LoginButton extends ConsumerWidget { final Function() onPressed; - const LoginButton({ - super.key, - required this.onPressed, - }); + const LoginButton({super.key, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton.icon( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), onPressed: onPressed, icon: const Icon(Icons.login_rounded), - label: const Text( - "login", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text("login", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ); } } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 24a73b2cbc..f100b58649 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -10,15 +10,17 @@ import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/backup/backup.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/oauth.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/utils/migration.dart'; import 'package:immich_mobile/utils/provider_utils.dart'; import 'package:immich_mobile/utils/url_helper.dart'; import 'package:immich_mobile/utils/version_compatibility.dart'; @@ -43,12 +45,9 @@ class LoginForm extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final emailController = - useTextEditingController.fromValue(TextEditingValue.empty); - final passwordController = - useTextEditingController.fromValue(TextEditingValue.empty); - final serverEndpointController = - useTextEditingController.fromValue(TextEditingValue.empty); + final emailController = useTextEditingController.fromValue(TextEditingValue.empty); + final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); + final serverEndpointController = useTextEditingController.fromValue(TextEditingValue.empty); final emailFocusNode = useFocusNode(); final passwordFocusNode = useFocusNode(); final serverEndpointFocusNode = useFocusNode(); @@ -57,9 +56,7 @@ class LoginForm extends HookConsumerWidget { final isOauthEnable = useState(false); final isPasswordLoginEnable = useState(false); final oAuthButtonLabel = useState('OAuth'); - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 60), - )..repeat(); + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 60))..repeat(); final serverInfo = ref.watch(serverInfoProvider); final warningMessage = useState(null); final loginFormKey = GlobalKey(); @@ -93,17 +90,12 @@ class LoginForm extends HookConsumerWidget { // Guard empty URL if (serverUrl.isEmpty) { - ImmichToast.show( - context: context, - msg: "login_form_server_empty".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "login_form_server_empty".tr(), toastType: ToastType.error); } try { isLoadingServer.value = true; - final endpoint = - await ref.read(authProvider.notifier).validateServerUrl(serverUrl); + final endpoint = await ref.read(authProvider.notifier).validateServerUrl(serverUrl); // Fetch and load server config and features await ref.read(serverInfoProvider.notifier).getServerInfo(); @@ -114,9 +106,7 @@ class LoginForm extends HookConsumerWidget { isOauthEnable.value = features.oauthEnabled; isPasswordLoginEnable.value = features.passwordLogin; - oAuthButtonLabel.value = config.oauthButtonText.isNotEmpty - ? config.oauthButtonText - : 'OAuth'; + oAuthButtonLabel.value = config.oauthButtonText.isNotEmpty ? config.oauthButtonText : 'OAuth'; serverEndpoint.value = endpoint; } on ApiException catch (e) { @@ -154,16 +144,13 @@ class LoginForm extends HookConsumerWidget { isLoadingServer.value = false; } - useEffect( - () { - final serverUrl = getServerUrl(); - if (serverUrl != null) { - serverEndpointController.text = serverUrl; - } - return null; - }, - [], - ); + useEffect(() { + final serverUrl = getServerUrl(); + if (serverUrl != null) { + serverEndpointController.text = serverUrl; + } + return null; + }, []); populateTestLoginInfo() { emailController.text = 'demo@immich.app'; @@ -177,6 +164,18 @@ class LoginForm extends HookConsumerWidget { serverEndpointController.text = 'http://10.1.15.216:2283/api'; } + Future handleSyncFlow() async { + final backgroundManager = ref.read(backgroundSyncProvider); + + await backgroundManager.syncLocal(full: true); + await backgroundManager.syncRemote(); + await backgroundManager.hashAssets(); + + if (Store.get(StoreKey.syncAlbums, false)) { + await backgroundManager.syncLinkedAlbum(); + } + } + login() async { TextInput.finishAutofillContext(); @@ -186,20 +185,16 @@ class LoginForm extends HookConsumerWidget { invalidateAllApiRepositoryProviders(ref); try { - final result = await ref.read(authProvider.notifier).login( - emailController.text, - passwordController.text, - ); + final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); if (result.shouldChangePassword && !result.isAdmin) { context.pushRoute(const ChangePasswordRoute()); } else { final isBeta = Store.isBetaTimelineEnabled; if (isBeta) { - await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); - await runNewSync(ref); + await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); + handleSyncFlow(); + ref.read(websocketProvider.notifier).connect(); context.replaceRoute(const TabShellRoute()); return; } @@ -218,15 +213,9 @@ class LoginForm extends HookConsumerWidget { } String generateRandomString(int length) { - const chars = - 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; final random = Random.secure(); - return String.fromCharCodes( - Iterable.generate( - length, - (_) => chars.codeUnitAt(random.nextInt(chars.length)), - ), - ); + return String.fromCharCodes(Iterable.generate(length, (_) => chars.codeUnitAt(random.nextInt(chars.length)))); } List randomBytes(int length) { @@ -282,23 +271,17 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = await oAuthService.oAuthLogin( - oAuthServerUrl, - state, - codeVerifier, - ); + final loginResponseDto = await oAuthService.oAuthLogin(oAuthServerUrl, state, codeVerifier); if (loginResponseDto == null) { return; } - log.info( - "Finished OAuth login with response: ${loginResponseDto.userEmail}", - ); + log.info("Finished OAuth login with response: ${loginResponseDto.userEmail}"); - final isSuccess = await ref.watch(authProvider.notifier).saveAuthInfo( - accessToken: loginResponseDto.accessToken, - ); + final isSuccess = await ref + .watch(authProvider.notifier) + .saveAuthInfo(accessToken: loginResponseDto.accessToken); if (isSuccess) { isLoading.value = false; @@ -308,10 +291,8 @@ class LoginForm extends HookConsumerWidget { ref.watch(backupProvider.notifier).resumeBackup(); } if (isBeta) { - await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); - await runNewSync(ref); + await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); + handleSyncFlow(); context.replaceRoute(const TabShellRoute()); return; } @@ -383,13 +364,9 @@ class LoginForm extends HookConsumerWidget { ), ), ), - onPressed: - isLoadingServer.value ? null : getServerAuthSettings, + onPressed: isLoadingServer.value ? null : getServerAuthSettings, icon: const Icon(Icons.arrow_forward_rounded), - label: const Text( - 'next', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text('next', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ], @@ -412,20 +389,11 @@ class LoginForm extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: - context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), - border: Border.all( - color: - context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!, - ), - ), - child: Text( - warningMessage.value!, - textAlign: TextAlign.center, + color: context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border.all(color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!), ), + child: Text(warningMessage.value!, textAlign: TextAlign.center), ), ); } @@ -449,11 +417,7 @@ class LoginForm extends HookConsumerWidget { onSubmit: passwordFocusNode.requestFocus, ), const SizedBox(height: 8), - PasswordInput( - controller: passwordController, - focusNode: passwordFocusNode, - onSubmit: login, - ), + PasswordInput(controller: passwordController, focusNode: passwordFocusNode, onSubmit: login), ], // Note: This used to have an AnimatedSwitcher, but was removed @@ -465,19 +429,12 @@ class LoginForm extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 18), - if (isPasswordLoginEnable.value) - LoginButton(onPressed: login), + if (isPasswordLoginEnable.value) LoginButton(onPressed: login), if (isOauthEnable.value) ...[ if (isPasswordLoginEnable.value) Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Divider( - color: context.isDarkTheme - ? Colors.white - : Colors.black, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Divider(color: context.isDarkTheme ? Colors.white : Colors.black), ), OAuthLoginButton( serverEndpointController: serverEndpointController, @@ -488,10 +445,7 @@ class LoginForm extends HookConsumerWidget { ], ], ), - if (!isOauthEnable.value && !isPasswordLoginEnable.value) - Center( - child: const Text('login_disabled').tr(), - ), + if (!isOauthEnable.value && !isPasswordLoginEnable.value) Center(child: const Text('login_disabled').tr()), const SizedBox(height: 12), TextButton.icon( icon: const Icon(Icons.arrow_back), @@ -503,8 +457,7 @@ class LoginForm extends HookConsumerWidget { ); } - final serverSelectionOrLogin = - serverEndpoint.value == null ? buildSelectServer() : buildLogin(); + final serverSelectionOrLogin = serverEndpoint.value == null ? buildSelectServer() : buildLogin(); return LayoutBuilder( builder: (context, constraints) { @@ -516,9 +469,7 @@ class LoginForm extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( - height: constraints.maxHeight / 5, - ), + SizedBox(height: constraints.maxHeight / 5), Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, @@ -528,24 +479,16 @@ class LoginForm extends HookConsumerWidget { onLongPress: () => populateTestLoginInfo1(), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0, bottom: 16), - child: ImmichTitleText(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0, bottom: 16), child: ImmichTitleText()), ], ), // Note: This used to have an AnimatedSwitcher, but was removed // because of https://github.com/flutter/flutter/issues/120874 - Form( - key: loginFormKey, - child: serverSelectionOrLogin, - ), + Form(key: loginFormKey, child: serverSelectionOrLogin), ], ), ), diff --git a/mobile/lib/widgets/forms/login/o_auth_login_button.dart b/mobile/lib/widgets/forms/login/o_auth_login_button.dart index 465d88a4d2..2d9b603b3c 100644 --- a/mobile/lib/widgets/forms/login/o_auth_login_button.dart +++ b/mobile/lib/widgets/forms/login/o_auth_login_button.dart @@ -25,10 +25,7 @@ class OAuthLoginButton extends ConsumerWidget { ), onPressed: onPressed, icon: const Icon(Icons.pin_rounded), - label: Text( - buttonLabel, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + label: Text(buttonLabel, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/password_input.dart b/mobile/lib/widgets/forms/login/password_input.dart index 2d2c1923f7..5cdfcc9567 100644 --- a/mobile/lib/widgets/forms/login/password_input.dart +++ b/mobile/lib/widgets/forms/login/password_input.dart @@ -8,12 +8,7 @@ class PasswordInput extends HookConsumerWidget { final FocusNode? focusNode; final Function()? onSubmit; - const PasswordInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const PasswordInput({super.key, required this.controller, this.focusNode, this.onSubmit}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,17 +21,10 @@ class PasswordInput extends HookConsumerWidget { labelText: 'password'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), suffixIcon: IconButton( onPressed: () => isPasswordVisible.value = !isPasswordVisible.value, - icon: Icon( - isPasswordVisible.value - ? Icons.visibility_off_sharp - : Icons.visibility_sharp, - ), + icon: Icon(isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp), ), ), autofillHints: const [AutofillHints.password], diff --git a/mobile/lib/widgets/forms/login/server_endpoint_input.dart b/mobile/lib/widgets/forms/login/server_endpoint_input.dart index 37bcad9d82..f9bc1690af 100644 --- a/mobile/lib/widgets/forms/login/server_endpoint_input.dart +++ b/mobile/lib/widgets/forms/login/server_endpoint_input.dart @@ -7,21 +7,13 @@ class ServerEndpointInput extends StatelessWidget { final FocusNode focusNode; final Function()? onSubmit; - const ServerEndpointInput({ - super.key, - required this.controller, - required this.focusNode, - this.onSubmit, - }); + const ServerEndpointInput({super.key, required this.controller, required this.focusNode, this.onSubmit}); String? _validateInput(String? url) { if (url == null || url.isEmpty) return null; final parsedUrl = Uri.tryParse(sanitizeUrl(url)); - if (parsedUrl == null || - !parsedUrl.isAbsolute || - !parsedUrl.scheme.startsWith("http") || - parsedUrl.host.isEmpty) { + if (parsedUrl == null || !parsedUrl.isAbsolute || !parsedUrl.scheme.startsWith("http") || parsedUrl.host.isEmpty) { return 'login_form_err_invalid_url'.tr(); } diff --git a/mobile/lib/widgets/forms/pin_input.dart b/mobile/lib/widgets/forms/pin_input.dart index 1588a65c60..88e27f005e 100644 --- a/mobile/lib/widgets/forms/pin_input.dart +++ b/mobile/lib/widgets/forms/pin_input.dart @@ -30,8 +30,7 @@ class PinInput extends StatelessWidget { final minimumPadding = 18.0; final gapWidth = 3.0; final screenWidth = context.width; - final pinWidth = - (screenWidth - (minimumPadding * 2) - (gapWidth * 5)) / (length ?? 6); + final pinWidth = (screenWidth - (minimumPadding * 2) - (gapWidth * 5)) / (length ?? 6); if (pinWidth > 60) { return const Size(60, 64); @@ -44,11 +43,7 @@ class PinInput extends StatelessWidget { final defaultPinTheme = PinTheme( width: getPinSize().width, height: getPinSize().height, - textStyle: TextStyle( - fontSize: 24, - color: context.colorScheme.onSurface, - fontFamily: 'Overpass Mono', - ), + textStyle: TextStyle(fontSize: 24, color: context.colorScheme.onSurface, fontFamily: 'Overpass Mono'), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), border: Border.all(color: context.colorScheme.surfaceBright), @@ -62,8 +57,7 @@ class PinInput extends StatelessWidget { if (label != null) ...[ Text( label!, - style: context.textTheme.displayLarge - ?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + style: context.textTheme.displayLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), const SizedBox(height: 4), ], @@ -72,34 +66,19 @@ class PinInput extends StatelessWidget { forceErrorState: hasError ?? false, autofocus: autoFocus ?? false, obscureText: obscureText ?? false, - obscuringWidget: Icon( - Icons.vpn_key_rounded, - color: context.primaryColor, - size: 20, - ), - separatorBuilder: (index) => const SizedBox( - height: 64, - width: 3, - ), + obscuringWidget: Icon(Icons.vpn_key_rounded, color: context.primaryColor, size: 20), + separatorBuilder: (index) => const SizedBox(height: 64, width: 3), cursor: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Container( - margin: const EdgeInsets.only(bottom: 9), - width: 18, - height: 2, - color: context.primaryColor, - ), + Container(margin: const EdgeInsets.only(bottom: 9), width: 18, height: 2, color: context.primaryColor), ], ), defaultPinTheme: defaultPinTheme, focusedPinTheme: defaultPinTheme.copyWith( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.primaryColor.withValues(alpha: 0.5), - width: 2, - ), + border: Border.all(color: context.primaryColor.withValues(alpha: 0.5), width: 2), color: context.colorScheme.surfaceContainerHigh, ), ), @@ -107,10 +86,7 @@ class PinInput extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.error.withAlpha(15), borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.colorScheme.error.withAlpha(100), - width: 2, - ), + border: Border.all(color: context.colorScheme.error.withAlpha(100), width: 2), ), ), pinputAutovalidateMode: PinputAutovalidateMode.onSubmit, diff --git a/mobile/lib/widgets/forms/pin_registration_form.dart b/mobile/lib/widgets/forms/pin_registration_form.dart index c3cfd3a864..d126169aad 100644 --- a/mobile/lib/widgets/forms/pin_registration_form.dart +++ b/mobile/lib/widgets/forms/pin_registration_form.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/widgets/forms/pin_input.dart'; class PinRegistrationForm extends HookConsumerWidget { final Function() onDone; - const PinRegistrationForm({ - super.key, - required this.onDone, - }); + const PinRegistrationForm({super.key, required this.onDone}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -40,35 +37,25 @@ class PinRegistrationForm extends HookConsumerWidget { } try { - await ref.read(authProvider.notifier).setupPinCode( - newPinCodeController.text, - ); + await ref.read(authProvider.notifier).setupPinCode(newPinCodeController.text); onDone(); } catch (error) { hasError.value = true; - context.showSnackBar( - SnackBar(content: Text(error.toString())), - ); + context.showSnackBar(SnackBar(content: Text(error.toString()))); } } return Form( child: Column( children: [ - Icon( - Icons.pin_outlined, - size: 64, - color: context.primaryColor, - ), + Icon(Icons.pin_outlined, size: 64, color: context.primaryColor), const SizedBox(height: 32), SizedBox( width: context.width * 0.7, child: Text( 'setup_pin_code'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 24, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 24), textAlign: TextAlign.center, ), ), @@ -76,9 +63,7 @@ class PinRegistrationForm extends HookConsumerWidget { width: context.width * 0.8, child: Text( 'new_pin_code_subtitle'.tr(), - style: context.textTheme.bodyLarge!.copyWith( - fontSize: 16, - ), + style: context.textTheme.bodyLarge!.copyWith(fontSize: 16), textAlign: TextAlign.center, ), ), @@ -113,10 +98,7 @@ class PinRegistrationForm extends HookConsumerWidget { child: Row( children: [ Expanded( - child: ElevatedButton( - onPressed: createNewPinCode, - child: Text('create'.tr()), - ), + child: ElevatedButton(onPressed: createNewPinCode, child: Text('create'.tr())), ), ], ), diff --git a/mobile/lib/widgets/forms/pin_verification_form.dart b/mobile/lib/widgets/forms/pin_verification_form.dart index f4ebf4272f..2b7e3e8251 100644 --- a/mobile/lib/widgets/forms/pin_verification_form.dart +++ b/mobile/lib/widgets/forms/pin_verification_form.dart @@ -30,8 +30,7 @@ class PinVerificationForm extends HookConsumerWidget { final isVerified = useState(false); verifyPin(String pinCode) async { - final isUnlocked = - await ref.read(authProvider.notifier).unlockPinCode(pinCode); + final isUnlocked = await ref.read(authProvider.notifier).unlockPinCode(pinCode); if (isUnlocked) { isVerified.value = true; @@ -50,17 +49,11 @@ class PinVerificationForm extends HookConsumerWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: isVerified.value - ? Icon( - successIcon ?? Icons.lock_open_rounded, - size: 64, - color: Colors.green[300], - ) + ? Icon(successIcon ?? Icons.lock_open_rounded, size: 64, color: Colors.green[300]) : Icon( icon ?? Icons.lock_outline_rounded, size: 64, - color: hasError.value - ? context.colorScheme.error - : context.primaryColor, + color: hasError.value ? context.colorScheme.error : context.primaryColor, ), ), const SizedBox(height: 36), @@ -68,9 +61,7 @@ class PinVerificationForm extends HookConsumerWidget { width: context.width * 0.7, child: Text( description ?? 'enter_your_pin_code_subtitle'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 18, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 18), textAlign: TextAlign.center, ), ), diff --git a/mobile/lib/widgets/map/map_app_bar.dart b/mobile/lib/widgets/map/map_app_bar.dart index ccadd2ad15..73706c7661 100644 --- a/mobile/lib/widgets/map/map_app_bar.dart +++ b/mobile/lib/widgets/map/map_app_bar.dart @@ -22,9 +22,8 @@ class MapAppBar extends HookWidget implements PreferredSizeWidget { padding: EdgeInsets.only(top: context.padding.top + 25), child: ValueListenableBuilder( valueListenable: selectedAssets, - builder: (ctx, value, child) => value.isNotEmpty - ? _SelectionRow(selectedAssets: selectedAssets) - : const _NonSelectionRow(), + builder: (ctx, value, child) => + value.isNotEmpty ? _SelectionRow(selectedAssets: selectedAssets) : const _NonSelectionRow(), ), ); } @@ -53,16 +52,12 @@ class _NonSelectionRow extends StatelessWidget { children: [ ElevatedButton( onPressed: () => context.maybePop(), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ElevatedButton( onPressed: onSettingsPressed, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.more_vert_rounded), ), ], @@ -79,10 +74,7 @@ class _SelectionRow extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isProcessing = useProcessingOverlay(); - Future handleProcessing( - FutureOr Function() action, [ - bool reloadMarkers = false, - ]) async { + Future handleProcessing(FutureOr Function() action, [bool reloadMarkers = false]) async { isProcessing.value = true; await action(); // Reset state @@ -102,9 +94,7 @@ class _SelectionRow extends HookConsumerWidget { icon: const Icon(Icons.close_rounded), label: Text( '${selectedAssets.value.length}', - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onPrimary), ), ), ), @@ -113,43 +103,20 @@ class _SelectionRow extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( - onPressed: () => handleProcessing( - () => handleShareAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => handleProcessing(() => handleShareAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.ios_share_rounded), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleFavoriteAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleFavoriteAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.favorite), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleArchiveAssets( - ref, - context, - selectedAssets.value.toList(), - ), - true, - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleArchiveAssets(ref, context, selectedAssets.value.toList()), true), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.archive), ), ], diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index c8c0816551..b6c1e708a7 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/map/map_event.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/timeline.provider.dart'; @@ -44,8 +45,7 @@ class MapAssetGrid extends HookConsumerWidget { final cachedRenderList = useRef(null); final lastRenderElementIndex = useRef(null); final assetInSheet = useValueNotifier(null); - final gridScrollThrottler = - useThrottler(interval: const Duration(milliseconds: 300)); + final gridScrollThrottler = useThrottler(interval: const Duration(milliseconds: 300)); // Add a cache for assets we've already loaded final assetCache = useRef>({}); @@ -67,8 +67,7 @@ class MapAssetGrid extends HookConsumerWidget { // Only fetch missing assets if (missingIds.isNotEmpty) { - final newAssets = - await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); + final newAssets = await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); // Add new assets to cache and current list for (final asset in newAssets) { @@ -93,8 +92,7 @@ class MapAssetGrid extends HookConsumerWidget { final orderedPos = positions.sortedByField((p) => p.index); // Index of row where the items are mostly visible const partialOffset = 0.20; - final item = orderedPos - .firstWhereOrNull((p) => p.itemTrailingEdge > partialOffset); + final item = orderedPos.firstWhereOrNull((p) => p.itemTrailingEdge > partialOffset); // Guard no elements, reset state // Also fail fast when the sheet is just opened and the user is yet to scroll (i.e leading = 0) @@ -103,8 +101,7 @@ class MapAssetGrid extends HookConsumerWidget { return; } - final renderElement = - cachedRenderList.value?.elements.elementAtOrNull(item.index); + final renderElement = cachedRenderList.value?.elements.elementAtOrNull(item.index); // Guard no render list or render element if (renderElement == null) { return; @@ -123,18 +120,15 @@ class MapAssetGrid extends HookConsumerWidget { final rowOffset = renderElement.offset; // Column offset = (total trailingEdge - trailingEdge crossed) / offset for each asset final totalOffset = item.itemTrailingEdge - item.itemLeadingEdge; - final edgeOffset = (totalOffset - partialOffset) / + final edgeOffset = + (totalOffset - partialOffset) / // Round the total count to the next multiple of [assetsPerRow] ((renderElement.totalCount / assetsPerRow) * assetsPerRow).floor(); // trailing should never be above the totalOffset - final columnOffset = - (totalOffset - math.min(item.itemTrailingEdge, totalOffset)) ~/ - edgeOffset; + final columnOffset = (totalOffset - math.min(item.itemTrailingEdge, totalOffset)) ~/ edgeOffset; final assetOffset = rowOffset + columnOffset; - final selectedAsset = cachedRenderList.value?.allAssets - ?.elementAtOrNull(assetOffset) - ?.remoteId; + final selectedAsset = cachedRenderList.value?.allAssets?.elementAtOrNull(assetOffset)?.remoteId; if (selectedAsset != null) { onGridAssetChanged?.call(selectedAsset); @@ -155,36 +149,31 @@ class MapAssetGrid extends HookConsumerWidget { heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty ? ref - .watch(assetsTimelineProvider(assetsInBounds.value)) - .when( - data: (renderList) { - // Cache render list here to use it back during visibleItemsListener - cachedRenderList.value = renderList; - return ValueListenableBuilder( - valueListenable: selectedAssets, - builder: (_, value, __) => ImmichAssetGrid( - shrinkWrap: true, - renderList: renderList, - showDragScroll: false, - assetsPerRow: assetsPerRow, - showMultiSelectIndicator: false, - selectionActive: value.isNotEmpty, - listener: onAssetsSelected, - visibleItemsListener: (pos) => gridScrollThrottler - .run(() => handleVisibleItems(pos)), - ), - ); - }, - error: (error, stackTrace) { - log.warning( - "Cannot get assets in the current map bounds", - error, - stackTrace, - ); - return const SizedBox.shrink(); - }, - loading: () => const SizedBox.shrink(), - ) + .watch(assetsTimelineProvider(assetsInBounds.value)) + .when( + data: (renderList) { + // Cache render list here to use it back during visibleItemsListener + cachedRenderList.value = renderList; + return ValueListenableBuilder( + valueListenable: selectedAssets, + builder: (_, value, __) => ImmichAssetGrid( + shrinkWrap: true, + renderList: renderList, + showDragScroll: false, + assetsPerRow: assetsPerRow, + showMultiSelectIndicator: false, + selectionActive: value.isNotEmpty, + listener: onAssetsSelected, + visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), + ), + ); + }, + error: (error, stackTrace) { + log.warning("Cannot get assets in the current map bounds", error, stackTrace); + return const SizedBox.shrink(); + }, + loading: () => const SizedBox.shrink(), + ) : const _MapNoAssetsInSheet(), ), ), @@ -205,11 +194,7 @@ class _MapNoAssetsInSheet extends StatelessWidget { @override Widget build(BuildContext context) { - const image = Image( - height: 150, - width: 150, - image: AssetImage('assets/lighthouse.png'), - ); + const image = Image(height: 150, width: 150, image: AssetImage('assets/lighthouse.png')); return Center( child: ListView( @@ -217,21 +202,12 @@ class _MapNoAssetsInSheet extends StatelessWidget { children: [ context.isDarkTheme ? const InvertionFilter( - child: SaturationFilter( - saturation: -1, - child: BrightnessFilter( - brightness: -5, - child: image, - ), - ), + child: SaturationFilter(saturation: -1, child: BrightnessFilter(brightness: -5, child: image)), ) : image, const SizedBox(height: 20), Center( - child: Text( - "map_zoom_to_see_photos".tr(), - style: context.textTheme.displayLarge?.copyWith(fontSize: 18), - ), + child: Text("map_zoom_to_see_photos".tr(), style: context.textTheme.displayLarge?.copyWith(fontSize: 18)), ), ], ), @@ -254,10 +230,7 @@ class _MapSheetDragRegion extends StatelessWidget { @override Widget build(BuildContext context) { - final assetsInBoundsText = assetsInBoundCount > 0 - ? "map_assets_in_bounds" - .tr(namedArgs: {'count': assetsInBoundCount.toString()}) - : "map_no_assets_in_bounds".tr(); + final assetsInBoundsText = "map_assets_in_bounds".t(context: context, args: {'count': assetsInBoundCount}); return SingleChildScrollView( controller: controller, @@ -266,10 +239,7 @@ class _MapSheetDragRegion extends StatelessWidget { margin: EdgeInsets.zero, shape: context.isMobile ? const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), + borderRadius: BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)), ) : const BeveledRectangleBorder(), elevation: 0.0, @@ -287,8 +257,7 @@ class _MapSheetDragRegion extends StatelessWidget { assetsInBoundsText, style: TextStyle( fontSize: 20, - color: context.textTheme.displayLarge?.color - ?.withValues(alpha: 0.75), + color: context.textTheme.displayLarge?.color?.withValues(alpha: 0.75), fontWeight: FontWeight.w500, ), ), @@ -304,12 +273,9 @@ class _MapSheetDragRegion extends StatelessWidget { right: 18, top: 24, child: IconButton( - icon: Icon( - Icons.map_outlined, - color: context.textTheme.displayLarge?.color, - ), + icon: Icon(Icons.map_outlined, color: context.textTheme.displayLarge?.color), iconSize: 24, - tooltip: 'Zoom to bounds', + tooltip: 'zoom_to_bounds'.tr(), onPressed: () => onZoomToAsset?.call(value!), ), ), diff --git a/mobile/lib/widgets/map/map_bottom_sheet.dart b/mobile/lib/widgets/map/map_bottom_sheet.dart index 0249ca70dc..baf85e8075 100644 --- a/mobile/lib/widgets/map/map_bottom_sheet.dart +++ b/mobile/lib/widgets/map/map_bottom_sheet.dart @@ -34,19 +34,14 @@ class MapBottomSheet extends HookConsumerWidget { void handleMapEvents(MapEvent event) async { if (event is MapCloseBottomSheet) { - sheetController.animateTo( - 0.1, - duration: const Duration(milliseconds: 200), - curve: Curves.linearToEaseOut, - ); + sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut); } } useOnStreamChange(mapEventStream, onData: handleMapEvents); bool onScrollNotification(DraggableScrollableNotification notification) { - isBottomSheetOpened.value = - notification.extent > (notification.maxExtent * 0.9); + isBottomSheetOpened.value = notification.extent > (notification.maxExtent * 0.9); bottomSheetOffset.value = notification.extent; // do not bubble return true; @@ -70,9 +65,7 @@ class MapBottomSheet extends HookConsumerWidget { selectedAssets: selectedAssets, onAssetsSelected: onAssetsSelected, // Do not bother with the event if the bottom sheet is not user scrolled - onGridAssetChanged: (assetId) => isBottomSheetOpened.value - ? onGridAssetChanged?.call(assetId) - : null, + onGridAssetChanged: (assetId) => isBottomSheetOpened.value ? onGridAssetChanged?.call(assetId) : null, onZoomToAsset: onZoomToAsset, ), ), @@ -88,9 +81,7 @@ class MapBottomSheet extends HookConsumerWidget { duration: const Duration(milliseconds: 150), child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), diff --git a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart index 1abe64ce31..e97875fd90 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart @@ -8,22 +8,13 @@ class MapSettingsListTile extends StatelessWidget { final bool selected; final Function(bool) onChanged; - const MapSettingsListTile({ - super.key, - required this.title, - required this.selected, - required this.onChanged, - }); + const MapSettingsListTile({super.key, required this.title, required this.selected, required this.onChanged}); @override Widget build(BuildContext context) { return SwitchListTile.adaptive( - activeColor: context.primaryColor, - title: Text( - title, - style: - context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + activeThumbColor: context.primaryColor, + title: Text(title, style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), value: selected, onChanged: onChanged, ); diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart index e23716af95..b601887e1e 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart @@ -5,11 +5,7 @@ class MapTimeDropDown extends StatelessWidget { final int relativeTime; final Function(int) onTimeChange; - const MapTimeDropDown({ - super.key, - required this.relativeTime, - required this.onTimeChange, - }); + const MapTimeDropDown({super.key, required this.relativeTime, required this.onTimeChange}); @override Widget build(BuildContext context) { @@ -20,10 +16,7 @@ class MapTimeDropDown extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - "date_range".tr(), - style: const TextStyle(fontWeight: FontWeight.bold), - ), + child: Text("date_range".tr(), style: const TextStyle(fontWeight: FontWeight.bold)), ), LayoutBuilder( builder: (_, constraints) => DropdownMenu( @@ -33,56 +26,21 @@ class MapTimeDropDown extends StatelessWidget { initialSelection: relativeTime, onSelected: (value) => onTimeChange(value!), dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "all".tr(), - ), - DropdownMenuEntry( - value: 1, - label: "map_settings_date_range_option_day".tr(), - ), - DropdownMenuEntry( - value: 7, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "7"}, - ), - ), - DropdownMenuEntry( - value: 30, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "30"}, - ), - ), + DropdownMenuEntry(value: 0, label: "all".tr()), + DropdownMenuEntry(value: 1, label: "map_settings_date_range_option_day".tr()), + DropdownMenuEntry(value: 7, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "7"})), + DropdownMenuEntry(value: 30, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "30"})), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 1, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 1, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_year".tr(), ), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 3, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 3, now.month, now.day, now.hour, now.minute, now.second)) .inDays, - label: "map_settings_date_range_option_years" - .tr(namedArgs: {'years': "3"}), + label: "map_settings_date_range_option_years".tr(namedArgs: {'years': "3"}), ), ], ), diff --git a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart index 19298df076..63f35ebe4c 100644 --- a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart +++ b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart @@ -8,11 +8,7 @@ class MapThemePicker extends StatelessWidget { final ThemeMode themeMode; final Function(ThemeMode) onThemeChange; - const MapThemePicker({ - super.key, - required this.themeMode, - required this.onThemeChange, - }); + const MapThemePicker({super.key, required this.themeMode, required this.onThemeChange}); @override Widget build(BuildContext context) { @@ -23,8 +19,7 @@ class MapThemePicker extends StatelessWidget { child: Center( child: Text( "map_settings_theme_settings", - style: context.textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold), ).tr(), ), ), @@ -77,12 +72,7 @@ class _BorderedMapThumbnail extends StatelessWidget { Container( decoration: BoxDecoration( border: Border.fromBorderSide( - BorderSide( - width: 4, - color: shouldHighlight - ? context.colorScheme.onSurface - : Colors.transparent, - ), + BorderSide(width: 4, color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent), ), borderRadius: const BorderRadius.all(Radius.circular(20)), ), @@ -98,9 +88,7 @@ class _BorderedMapThumbnail extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: Text( name, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: shouldHighlight ? FontWeight.bold : null, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: shouldHighlight ? FontWeight.bold : null), ), ), ], diff --git a/mobile/lib/widgets/map/map_settings_sheet.dart b/mobile/lib/widgets/map/map_settings_sheet.dart index 78d8aec75f..644056d153 100644 --- a/mobile/lib/widgets/map/map_settings_sheet.dart +++ b/mobile/lib/widgets/map/map_settings_sheet.dart @@ -26,37 +26,30 @@ class MapSettingsSheet extends HookConsumerWidget { children: [ MapThemePicker( themeMode: mapState.themeMode, - onThemeChange: (mode) => ref - .read(mapStateNotifierProvider.notifier) - .switchTheme(mode), + onThemeChange: (mode) => ref.read(mapStateNotifierProvider.notifier).switchTheme(mode), ), const Divider(height: 30, thickness: 2), MapSettingsListTile( title: "map_settings_only_show_favorites", selected: mapState.showFavoriteOnly, - onChanged: (favoriteOnly) => ref - .read(mapStateNotifierProvider.notifier) - .switchFavoriteOnly(favoriteOnly), + onChanged: (favoriteOnly) => + ref.read(mapStateNotifierProvider.notifier).switchFavoriteOnly(favoriteOnly), ), MapSettingsListTile( title: "map_settings_include_show_archived", selected: mapState.includeArchived, - onChanged: (includeArchive) => ref - .read(mapStateNotifierProvider.notifier) - .switchIncludeArchived(includeArchive), + onChanged: (includeArchive) => + ref.read(mapStateNotifierProvider.notifier).switchIncludeArchived(includeArchive), ), MapSettingsListTile( title: "map_settings_include_show_partners", selected: mapState.withPartners, - onChanged: (withPartners) => ref - .read(mapStateNotifierProvider.notifier) - .switchWithPartners(withPartners), + onChanged: (withPartners) => + ref.read(mapStateNotifierProvider.notifier).switchWithPartners(withPartners), ), MapTimeDropDown( relativeTime: mapState.relativeTime, - onTimeChange: (time) => ref - .read(mapStateNotifierProvider.notifier) - .setRelativeTime(time), + onTimeChange: (time) => ref.read(mapStateNotifierProvider.notifier).setRelativeTime(time), ), const SizedBox(height: 20), ], diff --git a/mobile/lib/widgets/map/map_theme_override.dart b/mobile/lib/widgets/map/map_theme_override.dart index 65425f9e78..57f970b0d1 100644 --- a/mobile/lib/widgets/map/map_theme_override.dart +++ b/mobile/lib/widgets/map/map_theme_override.dart @@ -18,25 +18,20 @@ class MapThemeOverride extends StatefulHookConsumerWidget { ConsumerState createState() => _MapThemeOverrideState(); } -class _MapThemeOverrideState extends ConsumerState - with WidgetsBindingObserver { +class _MapThemeOverrideState extends ConsumerState with WidgetsBindingObserver { late ThemeMode _theme; bool _isDarkTheme = false; - bool get _isSystemDark => - WidgetsBinding.instance.platformDispatcher.platformBrightness == - Brightness.dark; + bool get _isSystemDark => WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; bool checkDarkTheme() { - return _theme == ThemeMode.dark || - _theme == ThemeMode.system && _isSystemDark; + return _theme == ThemeMode.dark || _theme == ThemeMode.system && _isSystemDark; } @override void initState() { super.initState(); - _theme = widget.themeMode ?? - ref.read(mapStateNotifierProvider.select((v) => v.themeMode)); + _theme = widget.themeMode ?? ref.read(mapStateNotifierProvider.select((v) => v.themeMode)); setState(() { _isDarkTheme = checkDarkTheme(); }); @@ -70,8 +65,7 @@ class _MapThemeOverrideState extends ConsumerState @override Widget build(BuildContext context) { - _theme = widget.themeMode ?? - ref.watch(mapStateNotifierProvider.select((v) => v.themeMode)); + _theme = widget.themeMode ?? ref.watch(mapStateNotifierProvider.select((v) => v.themeMode)); var appTheme = ref.watch(immichThemeProvider); final locale = ref.watch(localeProvider); @@ -91,11 +85,7 @@ class _MapThemeOverrideState extends ConsumerState ? getThemeData(colorScheme: appTheme.dark, locale: locale) : getThemeData(colorScheme: appTheme.light, locale: locale), child: widget.mapBuilder.call( - ref.watch( - mapStateNotifierProvider.select( - (v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched, - ), - ), + ref.watch(mapStateNotifierProvider.select((v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched)), ), ); } diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 1e4b061be6..55f5ff77c6 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -55,8 +55,7 @@ class MapThumbnail extends HookConsumerWidget { // The iOS impl returns wrong toScreenLocation without the delay Future.delayed( const Duration(milliseconds: 100), - () async => - position.value = await mapController.toScreenLocation(centre), + () async => position.value = await mapController.toScreenLocation(centre), ); } onCreated?.call(mapController); @@ -81,8 +80,7 @@ class MapThumbnail extends HookConsumerWidget { duration: Durations.medium2, curve: Curves.easeOut, foregroundDecoration: BoxDecoration( - color: context.colorScheme.inverseSurface - .withAlpha(styleLoaded.value ? 0 : 200), + color: context.colorScheme.inverseSurface.withAlpha(styleLoaded.value ? 0 : 200), borderRadius: const BorderRadius.all(Radius.circular(15)), ), height: height, @@ -94,8 +92,7 @@ class MapThumbnail extends HookConsumerWidget { children: [ style.widgetWhen( onData: (style) => MapLibreMap( - initialCameraPosition: - CameraPosition(target: offsettedCentre, zoom: zoom), + initialCameraPosition: CameraPosition(target: offsettedCentre, zoom: zoom), styleString: style, onMapCreated: onMapCreated, onStyleLoadedCallback: onStyleLoaded, @@ -107,18 +104,13 @@ class MapThumbnail extends HookConsumerWidget { scrollGesturesEnabled: false, rotateGesturesEnabled: false, myLocationEnabled: false, - attributionButtonMargins: - showAttribution == false ? const Point(-100, 0) : null, + attributionButtonMargins: showAttribution == false ? const Point(-100, 0) : null, ), ), ValueListenableBuilder( valueListenable: position, - builder: (_, value, __) => value != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) + builder: (_, value, __) => value != null && assetMarkerRemoteId != null + ? PositionedAssetMarkerIcon(size: height / 2, point: value, assetRemoteId: assetMarkerRemoteId!) : const SizedBox.shrink(), ), ], diff --git a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart index 5ad49b42f8..0944f7ce3e 100644 --- a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart +++ b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart @@ -35,10 +35,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { onTap: () => onTap?.call(), child: SizedBox.square( dimension: size, - child: _AssetMarkerIcon( - id: assetRemoteId, - key: Key(assetRemoteId), - ), + child: _AssetMarkerIcon(id: assetRemoteId, key: Key(assetRemoteId)), ), ), ); @@ -46,10 +43,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { } class _AssetMarkerIcon extends StatelessWidget { - const _AssetMarkerIcon({ - required this.id, - super.key, - }); + const _AssetMarkerIcon({required this.id, super.key}); final String id; @@ -71,10 +65,7 @@ class _AssetMarkerIcon extends StatelessWidget { primaryRadius: constraints.maxHeight * 0.06, secondaryRadius: constraints.maxHeight * 0.038, ), - child: SizedBox( - height: constraints.maxHeight * 0.14, - width: constraints.maxWidth * 0.14, - ), + child: SizedBox(height: constraints.maxHeight * 0.14, width: constraints.maxWidth * 0.14), ), ), Positioned( @@ -89,8 +80,7 @@ class _AssetMarkerIcon extends StatelessWidget { imageUrl, cacheKey: cacheKey, headers: ApiService.getRequestHeaders(), - errorListener: (_) => - const Icon(Icons.image_not_supported_outlined), + errorListener: (_) => const Icon(Icons.image_not_supported_outlined), ), ), ), @@ -130,26 +120,11 @@ class _PinPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 2; - canvas.drawCircle( - Offset(size.width / 2, size.height), - primaryRadius, - primaryBrush, - ); - canvas.drawCircle( - Offset(size.width / 2, size.height), - secondaryRadius, - secondaryBrush, - ); + canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush); + canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush); canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush); // The line is to make the above triangluar path more prominent since it has a slight curve - canvas.drawLine( - Offset(size.width / 2, 0), - Offset( - size.width / 2, - size.height, - ), - lineBrush, - ); + canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush); } Path getTrianglePath(double x, double y) { @@ -158,24 +133,13 @@ class _PinPainter extends CustomPainter { final secondEndPoint = Offset(x, 0); return Path() - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - firstEndPoint.dx, - firstEndPoint.dy, - ) - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - secondEndPoint.dx, - secondEndPoint.dy, - ) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy) ..lineTo(0, 0); } @override bool shouldRepaint(_PinPainter old) { - return old.primaryColor != primaryColor || - old.secondaryColor != secondaryColor; + return old.primaryColor != primaryColor || old.secondaryColor != secondaryColor; } } diff --git a/mobile/lib/widgets/memories/memory_bottom_info.dart b/mobile/lib/widgets/memories/memory_bottom_info.dart index 6adf1d46b0..4b43821782 100644 --- a/mobile/lib/widgets/memories/memory_bottom_info.dart +++ b/mobile/lib/widgets/memories/memory_bottom_info.dart @@ -16,46 +16,35 @@ class MemoryBottomInfo extends StatelessWidget { final df = DateFormat.yMMMMd(); return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - memory.title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + memory.title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format( - memory.assets[0].fileCreatedAt, + Text( + df.format(memory.assets[0].fileCreatedAt), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider - .scrollToDate(memory.assets[0].fileCreatedAt); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + MaterialButton( + minWidth: 0, + onPressed: () { + context.maybePop(); + scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ], + ), ); } } diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index 31f4d5ed94..189cc67428 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -14,13 +14,7 @@ class MemoryCard extends StatelessWidget { final bool showTitle; final Function()? onVideoEnded; - const MemoryCard({ - required this.asset, - required this.title, - required this.showTitle, - this.onVideoEnded, - super.key, - }); + const MemoryCard({required this.asset, required this.title, required this.showTitle, this.onVideoEnded, super.key}); @override Widget build(BuildContext context) { @@ -28,28 +22,21 @@ class MemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio BoxFit fit = BoxFit.contain; if (asset.width != null && asset.height != null) { final aspectRatio = asset.width! / asset.height!; - final phoneAspectRatio = - constraints.maxWidth / constraints.maxHeight; + final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight; // Look for a 25% difference in either direction - if (phoneAspectRatio * .75 < aspectRatio && - phoneAspectRatio * 1.25 > aspectRatio) { + if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) { // Cover to look nice if we have nearly the same aspect ratio fit = BoxFit.cover; } @@ -58,12 +45,7 @@ class MemoryCard extends StatelessWidget { if (asset.isImage) { return Hero( tag: 'memory-${asset.id}', - child: ImmichImage( - asset, - fit: fit, - height: double.infinity, - width: double.infinity, - ), + child: ImmichImage(asset, fit: fit, height: double.infinity, width: double.infinity), ); } else { return Hero( @@ -76,12 +58,7 @@ class MemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: ImmichImage( - asset, - width: context.width, - height: context.height, - fit: BoxFit.contain, - ), + image: ImmichImage(asset, width: context.width, height: context.height, fit: BoxFit.contain), ), ), ); @@ -94,10 +71,7 @@ class MemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -118,16 +92,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -138,17 +105,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: ImmichImage.imageProvider( - asset: asset, - height: context.height, - width: context.width, - ), + image: ImmichImage.imageProvider(asset: asset, height: context.height, width: context.width), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/widgets/memories/memory_epilogue.dart b/mobile/lib/widgets/memories/memory_epilogue.dart index 9796bee6b1..b866ed049a 100644 --- a/mobile/lib/widgets/memories/memory_epilogue.dart +++ b/mobile/lib/widgets/memories/memory_epilogue.dart @@ -11,26 +11,16 @@ class MemoryEpilogue extends StatefulWidget { State createState() => _MemoryEpilogueState(); } -class _MemoryEpilogueState extends State - with TickerProviderStateMixin { - late final _animationController = AnimationController( - vsync: this, - duration: const Duration( - seconds: 2, - ), - )..repeat( - reverse: true, - ); +class _MemoryEpilogueState extends State with TickerProviderStateMixin { + late final _animationController = AnimationController(vsync: this, duration: const Duration(seconds: 2)) + ..repeat(reverse: true); late final Animation _animation; @override void initState() { super.initState(); - _animation = CurvedAnimation( - parent: _animationController, - curve: Curves.easeIn, - ); + _animation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn); } @override @@ -50,24 +40,18 @@ class _MemoryEpilogueState extends State children: [ Icon( Icons.check_circle_outline_sharp, - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, size: 64.0, ), const SizedBox(height: 16.0), Text( "memories_all_caught_up", - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), Text( "memories_check_back_tomorrow", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), TextButton( @@ -75,9 +59,7 @@ class _MemoryEpilogueState extends State child: Text( "memories_start_over", style: context.textTheme.displayMedium?.copyWith( - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, ), ).tr(), ), @@ -97,23 +79,14 @@ class _MemoryEpilogueState extends State child: AnimatedBuilder( animation: _animation, builder: (context, child) { - return Transform.translate( - offset: Offset(0, 8 * _animationController.value), - child: child, - ); + return Transform.translate(offset: Offset(0, 8 * _animationController.value), child: child); }, - child: const Icon( - size: 32, - Icons.expand_less_sharp, - color: Colors.white, - ), + child: const Icon(size: 32, Icons.expand_less_sharp, color: Colors.white), ), ), Text( "memories_swipe_to_close", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), ], ), diff --git a/mobile/lib/widgets/memories/memory_lane.dart b/mobile/lib/widgets/memories/memory_lane.dart index 3f97bd1ea4..727950fd86 100644 --- a/mobile/lib/widgets/memories/memory_lane.dart +++ b/mobile/lib/widgets/memories/memory_lane.dart @@ -22,17 +22,13 @@ class MemoryLane extends HookConsumerWidget { .whenData( (memories) => memories != null ? ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (memoryIndex) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); if (memories[memoryIndex].assets.isNotEmpty) { @@ -42,20 +38,10 @@ class MemoryLane extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); } } - context.pushRoute( - MemoryRoute( - memories: memories, - memoryIndex: memoryIndex, - ), - ); + context.pushRoute(MemoryRoute(memories: memories, memoryIndex: memoryIndex)); }, children: memories - .mapIndexed( - (index, memory) => MemoryCard( - index: index, - memory: memory, - ), - ) + .mapIndexed((index, memory) => MemoryCard(index: index, memory: memory)) .toList(), ), ) @@ -68,11 +54,7 @@ class MemoryLane extends HookConsumerWidget { } class MemoryCard extends ConsumerWidget { - const MemoryCard({ - super.key, - required this.index, - required this.memory, - }); + const MemoryCard({super.key, required this.index, required this.memory}); final int index; final Memory memory; @@ -83,10 +65,7 @@ class MemoryCard extends ConsumerWidget { child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: Hero( tag: 'memory-${memory.assets[0].id}', child: ImmichImage( @@ -94,10 +73,7 @@ class MemoryCard extends ConsumerWidget { fit: BoxFit.cover, width: 205, height: 200, - placeholder: const ThumbnailPlaceholder( - width: 105, - height: 200, - ), + placeholder: const ThumbnailPlaceholder(width: 105, height: 200), ), ), ), @@ -105,16 +81,10 @@ class MemoryCard extends ConsumerWidget { bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( memory.title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/widgets/memories/memory_progress_indicator.dart b/mobile/lib/widgets/memories/memory_progress_indicator.dart index 438816d99c..aab1dc1a97 100644 --- a/mobile/lib/widgets/memories/memory_progress_indicator.dart +++ b/mobile/lib/widgets/memories/memory_progress_indicator.dart @@ -8,11 +8,7 @@ class MemoryProgressIndicator extends StatelessWidget { /// The current value of the indicator final double value; - const MemoryProgressIndicator({ - super.key, - required this.ticks, - required this.value, - }); + const MemoryProgressIndicator({super.key, required this.ticks, required this.value}); @override Widget build(BuildContext context) { @@ -27,9 +23,7 @@ class MemoryProgressIndicator extends StatelessWidget { value: value, borderRadius: const BorderRadius.all(Radius.circular(10.0)), backgroundColor: Colors.grey[800], - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -39,14 +33,7 @@ class MemoryProgressIndicator extends StatelessWidget { width: tickWidth, height: 4, decoration: BoxDecoration( - border: i == 0 - ? null - : const Border( - left: BorderSide( - color: Colors.black, - width: 1, - ), - ), + border: i == 0 ? null : const Border(left: BorderSide(color: Colors.black, width: 1)), ), ), ), diff --git a/mobile/lib/widgets/photo_view/photo_view.dart b/mobile/lib/widgets/photo_view/photo_view.dart index 30e08748b8..69be96ed53 100644 --- a/mobile/lib/widgets/photo_view/photo_view.dart +++ b/mobile/lib/widgets/photo_view/photo_view.dart @@ -9,16 +9,13 @@ import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attri export 'src/controller/photo_view_controller.dart'; export 'src/controller/photo_view_scalestate_controller.dart'; -export 'src/core/photo_view_gesture_detector.dart' - show PhotoViewGestureDetectorScope, PhotoViewPageViewScrollPhysics; +export 'src/core/photo_view_gesture_detector.dart' show PhotoViewGestureDetectorScope, PhotoViewPageViewScrollPhysics; export 'src/photo_view_computed_scale.dart'; export 'src/photo_view_scale_state.dart'; export 'src/utils/photo_view_hero_attributes.dart'; typedef PhotoViewControllerCallback = PhotoViewControllerBase Function(); -typedef PhotoViewControllerCallbackBuilder = void Function( - PhotoViewControllerCallback photoViewMethod, -); +typedef PhotoViewControllerCallbackBuilder = void Function(PhotoViewControllerCallback photoViewMethod); /// A [StatefulWidget] that contains all the photo view rendering elements. /// @@ -270,8 +267,8 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.errorBuilder, this.enablePanAlways, - }) : child = null, - childSize = null; + }) : child = null, + childSize = null; /// Creates a widget that displays a zoomable child. /// @@ -311,12 +308,12 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.disableGestures, this.enablePanAlways, - }) : semanticLabel = null, - errorBuilder = null, - imageProvider = null, - gaplessPlayback = false, - loadingBuilder = null, - index = 0; + }) : semanticLabel = null, + errorBuilder = null, + imageProvider = null, + gaplessPlayback = false, + loadingBuilder = null, + index = 0; /// Given a [imageProvider] it resolves into an zoomable image widget using. It /// is required @@ -461,8 +458,7 @@ class PhotoView extends StatefulWidget { } } -class _PhotoViewState extends State - with AutomaticKeepAliveClientMixin { +class _PhotoViewState extends State with AutomaticKeepAliveClientMixin { // image retrieval // controller @@ -545,13 +541,9 @@ class _PhotoViewState extends State Widget build(BuildContext context) { super.build(context); return LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { + builder: (BuildContext context, BoxConstraints constraints) { final computedOuterSize = widget.customSize ?? constraints.biggest; - final backgroundDecoration = widget.backgroundDecoration ?? - const BoxDecoration(color: Colors.black); + final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.black); return widget._isCustomChild ? CustomChildWrapper( @@ -625,76 +617,50 @@ class _PhotoViewState extends State } /// The default [ScaleStateCycle] -PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => - switch (actual) { - PhotoViewScaleState.initial => PhotoViewScaleState.covering, - PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, - PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, - PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - PhotoViewScaleState.initial, - }; +PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => switch (actual) { + PhotoViewScaleState.initial => PhotoViewScaleState.covering, + PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, + PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, + PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, +}; /// A type definition for a [Function] that receives the actual [PhotoViewScaleState] and returns the next one /// It is used internally to walk in the "doubletap gesture cycle". /// It is passed to [PhotoView.scaleStateCycle] -typedef ScaleStateCycle = PhotoViewScaleState Function( - PhotoViewScaleState actual, -); +typedef ScaleStateCycle = PhotoViewScaleState Function(PhotoViewScaleState actual); /// A type definition for a callback when the user taps up the photoview region -typedef PhotoViewImageTapUpCallback = Function( - BuildContext context, - TapUpDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapUpCallback = + Function(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageTapDownCallback = Function( - BuildContext context, - TapDownDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapDownCallback = + Function(BuildContext context, TapDownDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user drags up -typedef PhotoViewImageDragStartCallback = Function( - BuildContext context, - DragStartDetails details, - PhotoViewControllerBase controllerValue, - PhotoViewScaleStateController scaleStateController, -); +typedef PhotoViewImageDragStartCallback = + Function( + BuildContext context, + DragStartDetails details, + PhotoViewControllerBase controllerValue, + PhotoViewScaleStateController scaleStateController, + ); /// A type definition for a callback when the user drags -typedef PhotoViewImageDragUpdateCallback = Function( - BuildContext context, - DragUpdateDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragUpdateCallback = + Function(BuildContext context, DragUpdateDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageDragEndCallback = Function( - BuildContext context, - DragEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragEndCallback = + Function(BuildContext context, DragEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when a user finished scale -typedef PhotoViewImageScaleEndCallback = Function( - BuildContext context, - ScaleEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageScaleEndCallback = + Function(BuildContext context, ScaleEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user long press start -typedef PhotoViewImageLongPressStartCallback = Function( - BuildContext context, - LongPressStartDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageLongPressStartCallback = + Function(BuildContext context, LongPressStartDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress -typedef LoadingBuilder = Widget Function( - BuildContext context, - ImageChunkEvent? event, - int index, -); +typedef LoadingBuilder = Widget Function(BuildContext context, ImageChunkEvent? event, int index); diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 1cd4d4b217..af5b9a7ce7 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -20,16 +20,10 @@ import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart'; /// A type definition for a [Function] that receives a index after a page change in [PhotoViewGallery] -typedef PhotoViewGalleryPageChangedCallback = void Function( - int index, - PhotoViewControllerBase? controller, -); +typedef PhotoViewGalleryPageChangedCallback = void Function(int index, PhotoViewControllerBase? controller); /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build] -typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function( - BuildContext context, - int index, -); +typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function(BuildContext context, int index); /// A [StatefulWidget] that shows multiple [PhotoView] widgets in a [PageView] /// @@ -126,8 +120,8 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : itemCount = null, - builder = null; + }) : itemCount = null, + builder = null; /// Construct a gallery with dynamic items. /// @@ -151,9 +145,9 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : pageOptions = null, - assert(itemCount != null), - assert(builder != null); + }) : pageOptions = null, + assert(itemCount != null), + assert(builder != null); /// A list of options to describe the items in the gallery final List? pageOptions; @@ -218,8 +212,7 @@ class PhotoViewGallery extends StatefulWidget { } class _PhotoViewGalleryState extends State { - late final PageController _controller = - widget.pageController ?? PageController(); + late final PageController _controller = widget.pageController ?? PageController(); PhotoViewControllerCallback? _getController; void scaleStateChangedCallback(PhotoViewScaleState scaleState) { @@ -274,7 +267,7 @@ class _PhotoViewGalleryState extends State { key: pageOption.key ?? ObjectKey(index), childSize: pageOption.childSize, backgroundDecoration: widget.backgroundDecoration, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, @@ -310,7 +303,7 @@ class _PhotoViewGalleryState extends State { loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, semanticLabel: pageOption.semanticLabel, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, onPageBuild: widget.onPageBuild, controllerCallbackBuilder: _getControllerCallbackBuilder, @@ -341,15 +334,10 @@ class _PhotoViewGalleryState extends State { heroAttributes: pageOption.heroAttributes, ); - return ClipRect( - child: photoView, - ); + return ClipRect(child: photoView); } - PhotoViewGalleryPageOptions _buildPageOption( - BuildContext context, - int index, - ) { + PhotoViewGalleryPageOptions _buildPageOption(BuildContext context, int index) { if (widget._isBuilder) { return widget.builder!(context, index); } @@ -387,9 +375,9 @@ class PhotoViewGalleryPageOptions { this.disableScaleGestures, this.disableGestures, this.errorBuilder, - }) : child = null, - childSize = null, - assert(imageProvider != null); + }) : child = null, + childSize = null, + assert(imageProvider != null); const PhotoViewGalleryPageOptions.customChild({ this.key, @@ -416,8 +404,8 @@ class PhotoViewGalleryPageOptions { this.filterQuality, this.disableScaleGestures, this.disableGestures, - }) : errorBuilder = null, - imageProvider = null; + }) : errorBuilder = null, + imageProvider = null; final Key? key; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart index 37d1c78de1..2c8b406385 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart @@ -72,12 +72,7 @@ abstract class PhotoViewControllerBase { Offset? rotationFocusPoint; /// Update multiple fields of the state with only one update streamed. - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }); + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}); } /// The state value stored and streamed by [PhotoViewController]. @@ -106,11 +101,7 @@ class PhotoViewControllerValue { rotationFocusPoint == other.rotationFocusPoint; @override - int get hashCode => - position.hashCode ^ - scale.hashCode ^ - rotation.hashCode ^ - rotationFocusPoint.hashCode; + int get hashCode => position.hashCode ^ scale.hashCode ^ rotation.hashCode ^ rotationFocusPoint.hashCode; @override String toString() { @@ -125,21 +116,17 @@ class PhotoViewControllerValue { /// /// For details of fields and methods, check [PhotoViewControllerBase]. /// -class PhotoViewController - implements PhotoViewControllerBase { - PhotoViewController({ - Offset initialPosition = Offset.zero, - double initialRotation = 0.0, - double? initialScale, - }) : _valueNotifier = IgnorableValueNotifier( - PhotoViewControllerValue( - position: initialPosition, - rotation: initialRotation, - scale: initialScale, - rotationFocusPoint: null, - ), +class PhotoViewController implements PhotoViewControllerBase { + PhotoViewController({Offset initialPosition = Offset.zero, double initialRotation = 0.0, double? initialScale}) + : _valueNotifier = IgnorableValueNotifier( + PhotoViewControllerValue( + position: initialPosition, + rotation: initialRotation, + scale: initialScale, + rotationFocusPoint: null, ), - super() { + ), + super() { initial = value; prevValue = initial; @@ -304,12 +291,7 @@ class PhotoViewController Offset? get rotationFocusPoint => value.rotationFocusPoint; @override - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { prevValue = value; value = PhotoViewControllerValue( position: position ?? value.position, diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart index e2e668199a..6825bf8ee1 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart @@ -1,10 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart' - show - PhotoViewControllerBase, - PhotoViewScaleState, - PhotoViewScaleStateController, - ScaleStateCycle; + show PhotoViewControllerBase, PhotoViewScaleState, PhotoViewScaleStateController, ScaleStateCycle; import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_core.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; @@ -14,8 +10,7 @@ import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart mixin PhotoViewControllerDelegate on State { PhotoViewControllerBase get controller => widget.controller; - PhotoViewScaleStateController get scaleStateController => - widget.scaleStateController; + PhotoViewScaleStateController get scaleStateController => widget.scaleStateController; ScaleBoundaries get scaleBoundaries => widget.scaleBoundaries; @@ -40,23 +35,15 @@ mixin PhotoViewControllerDelegate on State { controller.setScaleInvisibly(scale); return; } - final double prevScale = controller.scale ?? - getScaleForScaleState( - scaleStateController.prevScaleState, - scaleBoundaries, - ); + final double prevScale = + controller.scale ?? getScaleForScaleState(scaleStateController.prevScaleState, scaleBoundaries); - final double nextScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final double nextScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); _animateScale!(prevScale, nextScale); } - void addAnimateOnScaleStateUpdate( - void Function(double prevScale, double nextScale) animateScale, - ) { + void addAnimateOnScaleStateUpdate(void Function(double prevScale, double nextScale) animateScale) { _animateScale = animateScale; } @@ -67,10 +54,9 @@ mixin PhotoViewControllerDelegate on State { if (controller.scale == controller.prevValue.scale) { return; } - final PhotoViewScaleState newScaleState = - (scale > scaleBoundaries.initialScale) - ? PhotoViewScaleState.zoomedIn - : PhotoViewScaleState.zoomedOut; + final PhotoViewScaleState newScaleState = (scale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; scaleStateController.setInvisibly(newScaleState); } @@ -79,15 +65,11 @@ mixin PhotoViewControllerDelegate on State { double get scale { // for figuring out initial scale - final needsRecalc = markNeedsScaleRecalc && - !scaleStateController.scaleState.isScaleStateZooming; + final needsRecalc = markNeedsScaleRecalc && !scaleStateController.scaleState.isScaleStateZooming; final scaleExistsOnController = controller.scale != null; if (needsRecalc || !scaleExistsOnController) { - final newScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final newScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); markNeedsScaleRecalc = false; scale = newScale; return newScale; @@ -97,12 +79,7 @@ mixin PhotoViewControllerDelegate on State { set scale(double scale) => controller.setScaleInvisibly(scale); - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { controller.updateMultiple( position: position, scale: scale, @@ -133,15 +110,11 @@ mixin PhotoViewControllerDelegate on State { void nextScaleState() { final PhotoViewScaleState scaleState = scaleStateController.scaleState; - if (scaleState == PhotoViewScaleState.zoomedIn || - scaleState == PhotoViewScaleState.zoomedOut) { + if (scaleState == PhotoViewScaleState.zoomedIn || scaleState == PhotoViewScaleState.zoomedOut) { scaleStateController.scaleState = scaleStateCycle(scaleState); return; } - final double originalScale = getScaleForScaleState( - scaleState, - scaleBoundaries, - ); + final double originalScale = getScaleForScaleState(scaleState, scaleBoundaries); double prevScale = originalScale; PhotoViewScaleState prevScaleState = scaleState; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart index dea8be1a0f..8d078db2c8 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart @@ -19,18 +19,16 @@ typedef ScaleStateListener = void Function(double prevScale, double nextScale); /// The updates should be done via [scaleState] setter and the updated listened via [outputScaleStateStream] /// class PhotoViewScaleStateController { - late final IgnorableValueNotifier _scaleStateNotifier = - IgnorableValueNotifier(PhotoViewScaleState.initial) - ..addListener(_scaleStateChangeListener); - final StreamController _outputScaleStateCtrl = - StreamController.broadcast() - ..sink.add(PhotoViewScaleState.initial); + late final IgnorableValueNotifier _scaleStateNotifier = IgnorableValueNotifier( + PhotoViewScaleState.initial, + )..addListener(_scaleStateChangeListener); + final StreamController _outputScaleStateCtrl = StreamController.broadcast() + ..sink.add(PhotoViewScaleState.initial); bool _hasZoomedOutManually = false; /// The output for state/value updates - Stream get outputScaleStateStream => - _outputScaleStateCtrl.stream; + Stream get outputScaleStateStream => _outputScaleStateCtrl.stream; /// The state value before the last change or the initial state if the state has not been changed. PhotoViewScaleState prevScaleState = PhotoViewScaleState.initial; @@ -62,9 +60,7 @@ class PhotoViewScaleStateController { bool get hasChanged => prevScaleState != scaleState; /// Check if is `zoomedIn` & `zoomedOut` - bool get isZooming => - scaleState == PhotoViewScaleState.zoomedIn || - scaleState == PhotoViewScaleState.zoomedOut; + bool get isZooming => scaleState == PhotoViewScaleState.zoomedIn || scaleState == PhotoViewScaleState.zoomedOut; /// Resets the state to the initial value; void reset() { diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index 6b6e5067c5..d21b49f020 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -18,9 +18,7 @@ import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_gesture_det import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_hit_corners.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; -const _defaultDecoration = BoxDecoration( - color: Color.fromRGBO(0, 0, 0, 1.0), -); +const _defaultDecoration = BoxDecoration(color: Color.fromRGBO(0, 0, 0, 1.0)); /// Internal widget in which controls all animations lifecycle, core responses /// to user gestures, updates to the controller state and mounts the entire PhotoView Layout @@ -77,9 +75,9 @@ class PhotoViewCore extends StatefulWidget { required this.disableGestures, required this.disableScaleGestures, required this.enablePanAlways, - }) : semanticLabel = null, - imageProvider = null, - gaplessPlayback = false; + }) : semanticLabel = null, + imageProvider = null, + gaplessPlayback = false; final Decoration? backgroundDecoration; final ImageProvider? imageProvider; @@ -122,11 +120,7 @@ class PhotoViewCore extends StatefulWidget { } class PhotoViewCoreState extends State - with - TickerProviderStateMixin, - PhotoViewControllerDelegate, - HitCornersDetector { - Offset? _normalizedPosition; + with TickerProviderStateMixin, PhotoViewControllerDelegate, HitCornersDetector { double? _scaleBefore; double? _rotationBefore; @@ -136,8 +130,8 @@ class PhotoViewCoreState extends State late final AnimationController _positionAnimationController; Animation? _positionAnimation; - late final AnimationController _rotationAnimationController = - AnimationController(vsync: this)..addListener(handleRotationAnimation); + late final AnimationController _rotationAnimationController = AnimationController(vsync: this) + ..addListener(handleRotationAnimation); Animation? _rotationAnimation; PhotoViewHeroAttributes? get heroAttributes => widget.heroAttributes; @@ -159,21 +153,25 @@ class PhotoViewCoreState extends State void onScaleStart(ScaleStartDetails details) { _rotationBefore = controller.rotation; _scaleBefore = scale; - _normalizedPosition = details.focalPoint - controller.position; _scaleAnimationController.stop(); _positionAnimationController.stop(); _rotationAnimationController.stop(); } bool _shouldAllowPanRotate() => switch (scaleStateController.scaleState) { - PhotoViewScaleState.zoomedIn => - scaleStateController.hasZoomedOutManually, - _ => true, - }; + PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, + _ => true, + }; void onScaleUpdate(ScaleUpdateDetails details) { + final centeredFocalPoint = Offset( + details.focalPoint.dx - scaleBoundaries.outerSize.width / 2, + details.focalPoint.dy - scaleBoundaries.outerSize.height / 2, + ); final double newScale = _scaleBefore! * details.scale; - Offset delta = details.focalPoint - _normalizedPosition!; + final double scaleDelta = newScale / scale; + final Offset newPosition = + (controller.position + details.focalPointDelta) * scaleDelta - centeredFocalPoint * (scaleDelta - 1); updateScaleStateFromNewScale(newScale); @@ -182,8 +180,7 @@ class PhotoViewCoreState extends State updateMultiple( scale: newScale, - position: - panEnabled ? delta : clampPosition(position: delta * details.scale), + position: panEnabled ? newPosition : clampPosition(position: newPosition), rotation: rotationEnabled ? _rotationBefore! + details.rotation : null, rotationFocusPoint: rotationEnabled ? details.focalPoint : null, ); @@ -199,7 +196,7 @@ class PhotoViewCoreState extends State final scaleState = getScaleStateFromNewScale(scale); if (scaleState == PhotoViewScaleState.zoomedOut) { - scaleStateController.scaleState = PhotoViewScaleState.originalSize; + scaleStateController.scaleState = PhotoViewScaleState.initial; } else if (scaleState == PhotoViewScaleState.zoomedIn) { animateRotation(controller.rotation, 0); if (_shouldAllowPanRotate()) { @@ -211,10 +208,7 @@ class PhotoViewCoreState extends State if (s > maxScale) { final double scaleComebackRatio = maxScale / s; animateScale(s, maxScale); - final Offset clampedPosition = clampPosition( - position: p * scaleComebackRatio, - scale: maxScale, - ); + final Offset clampedPosition = clampPosition(position: p * scaleComebackRatio, scale: maxScale); animatePosition(p, clampedPosition); return; } @@ -223,13 +217,7 @@ class PhotoViewCoreState extends State if (s < minScale) { final double scaleComebackRatio = minScale / s; animateScale(s, minScale); - animatePosition( - p, - clampPosition( - position: p * scaleComebackRatio, - scale: minScale, - ), - ); + animatePosition(p, clampPosition(position: p * scaleComebackRatio, scale: minScale)); return; } // get magnitude from gesture velocity @@ -238,10 +226,7 @@ class PhotoViewCoreState extends State // animate velocity only if there is no scale change and a significant magnitude if (_scaleBefore! / s == 1.0 && magnitude >= 400.0) { final Offset direction = details.velocity.pixelsPerSecond / magnitude; - animatePosition( - p, - clampPosition(position: p + direction * 100.0), - ); + animatePosition(p, clampPosition(position: p + direction * 100.0)); } } @@ -253,10 +238,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _scaleAnimation = Tween( - begin: from, - end: to, - ).animate(_scaleAnimationController); + _scaleAnimation = Tween(begin: from, end: to).animate(_scaleAnimationController); _scaleAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -266,8 +248,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _positionAnimation = Tween(begin: from, end: to) - .animate(_positionAnimationController); + _positionAnimation = Tween(begin: from, end: to).animate(_positionAnimationController); _positionAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -277,8 +258,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _rotationAnimation = Tween(begin: from, end: to) - .animate(_rotationAnimationController); + _rotationAnimation = Tween(begin: from, end: to).animate(_rotationAnimationController); _rotationAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -292,8 +272,7 @@ class PhotoViewCoreState extends State /// Check if scale is equal to initial after scale animation update void onAnimationStatusCompleted() { - if (scaleStateController.scaleState != PhotoViewScaleState.initial && - scale == scaleBoundaries.initialScale) { + if (scaleStateController.scaleState != PhotoViewScaleState.initial && scale == scaleBoundaries.initialScale) { scaleStateController.setInvisibly(PhotoViewScaleState.initial); } } @@ -326,8 +305,7 @@ class PhotoViewCoreState extends State _scaleAnimationController = AnimationController(vsync: this) ..addListener(handleScaleAnimation) ..addStatusListener(onAnimationStatus); - _positionAnimationController = AnimationController(vsync: this) - ..addListener(handlePositionAnimate); + _positionAnimationController = AnimationController(vsync: this)..addListener(handlePositionAnimate); } void animateOnScaleStateUpdate(double prevScale, double nextScale) { @@ -364,10 +342,7 @@ class PhotoViewCoreState extends State return StreamBuilder( stream: controller.outputStateStream, initialData: controller.prevValue, - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final PhotoViewControllerValue value = snapshot.data!; final useImageScale = widget.filterQuality != FilterQuality.none; @@ -375,30 +350,20 @@ class PhotoViewCoreState extends State final computedScale = useImageScale ? 1.0 : scale; final matrix = Matrix4.identity() - ..translate(value.position.dx, value.position.dy) - ..scale(computedScale) + ..translateByDouble(value.position.dx, value.position.dy, 0, 1.0) + ..scaleByDouble(computedScale, computedScale, computedScale, 1.0) ..rotateZ(value.rotation); final Widget customChildLayout = CustomSingleChildLayout( - delegate: _CenterWithOriginalSizeDelegate( - scaleBoundaries.childSize, - basePosition, - useImageScale, - ), + delegate: _CenterWithOriginalSizeDelegate(scaleBoundaries.childSize, basePosition, useImageScale), child: _buildHero(_buildChild()), ); final child = Container( - constraints: widget.tightMode - ? BoxConstraints.tight(scaleBoundaries.childSize * scale) - : null, + constraints: widget.tightMode ? BoxConstraints.tight(scaleBoundaries.childSize * scale) : null, decoration: widget.backgroundDecoration ?? _defaultDecoration, child: Center( - child: Transform( - transform: matrix, - alignment: basePosition, - child: customChildLayout, - ), + child: Transform(transform: matrix, alignment: basePosition, child: customChildLayout), ), ); @@ -413,31 +378,17 @@ class PhotoViewCoreState extends State onScaleUpdate: widget.disableScaleGestures ? null : onScaleUpdate, onScaleEnd: widget.disableScaleGestures ? null : onScaleEnd, onDragStart: widget.onDragStart != null - ? (details) => widget.onDragStart!( - context, - details, - widget.controller, - widget.scaleStateController, - ) + ? (details) => widget.onDragStart!(context, details, widget.controller, widget.scaleStateController) : null, onDragEnd: widget.onDragEnd != null - ? (details) => - widget.onDragEnd!(context, details, widget.controller.value) + ? (details) => widget.onDragEnd!(context, details, widget.controller.value) : null, onDragUpdate: widget.onDragUpdate != null - ? (details) => widget.onDragUpdate!( - context, - details, - widget.controller.value, - ) + ? (details) => widget.onDragUpdate!(context, details, widget.controller.value) : null, hitDetector: this, - onTapUp: widget.onTapUp != null - ? (details) => widget.onTapUp!(context, details, value) - : null, - onTapDown: widget.onTapDown != null - ? (details) => widget.onTapDown!(context, details, value) - : null, + onTapUp: widget.onTapUp != null ? (details) => widget.onTapUp!(context, details, value) : null, + onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null, onLongPressStart: widget.onLongPressStart != null ? (details) => widget.onLongPressStart!(context, details, value) : null, @@ -467,25 +418,20 @@ class PhotoViewCoreState extends State return widget.hasCustomChild ? widget.customChild! : Image( - key: widget.heroAttributes?.tag != null - ? ObjectKey(widget.heroAttributes!.tag) - : null, + key: widget.heroAttributes?.tag != null ? ObjectKey(widget.heroAttributes!.tag) : null, image: widget.imageProvider!, semanticLabel: widget.semanticLabel, gaplessPlayback: widget.gaplessPlayback ?? false, filterQuality: widget.filterQuality, width: scaleBoundaries.childSize.width * scale, fit: BoxFit.cover, + isAntiAlias: widget.filterQuality == FilterQuality.high, ); } } class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { - const _CenterWithOriginalSizeDelegate( - this.subjectSize, - this.basePosition, - this.useImageScale, - ); + const _CenterWithOriginalSizeDelegate(this.subjectSize, this.basePosition, this.useImageScale); final Size subjectSize; final Alignment basePosition; @@ -507,9 +453,7 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return useImageScale - ? const BoxConstraints() - : BoxConstraints.tight(subjectSize); + return useImageScale ? const BoxConstraints() : BoxConstraints.tight(subjectSize); } @override @@ -527,6 +471,5 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { useImageScale == other.useImageScale; @override - int get hashCode => - subjectSize.hashCode ^ basePosition.hashCode ^ useImageScale.hashCode; + int get hashCode => subjectSize.hashCode ^ basePosition.hashCode ^ useImageScale.hashCode; } diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart index 93fd1526da..7a5406c675 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart @@ -53,12 +53,10 @@ class PhotoViewGestureDetector extends StatelessWidget { final Axis? axis = scope?.axis; final touchSlopFactor = scope?.touchSlopFactor ?? 2; - final Map gestures = - {}; + final Map gestures = {}; if (onTapDown != null || onTapUp != null) { - gestures[TapGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => TapGestureRecognizer(debugOwner: this), (TapGestureRecognizer instance) { instance @@ -69,8 +67,7 @@ class PhotoViewGestureDetector extends StatelessWidget { } if (onDragStart != null || onDragEnd != null || onDragUpdate != null) { - gestures[VerticalDragGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => VerticalDragGestureRecognizer(debugOwner: this), (VerticalDragGestureRecognizer instance) { instance @@ -81,16 +78,14 @@ class PhotoViewGestureDetector extends StatelessWidget { ); } - gestures[DoubleTapGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => DoubleTapGestureRecognizer(debugOwner: this), (DoubleTapGestureRecognizer instance) { instance.onDoubleTap = onDoubleTap; }, ); - gestures[PhotoViewGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[PhotoViewGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => PhotoViewGestureRecognizer( hitDetector: hitDetector, debugOwner: this, @@ -107,18 +102,14 @@ class PhotoViewGestureDetector extends StatelessWidget { }, ); - gestures[LongPressGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(debugOwner: this), - (LongPressGestureRecognizer instance) { - instance.onLongPressStart = onLongPressStart; - }); - - return RawGestureDetector( - behavior: behavior, - gestures: gestures, - child: child, + gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(debugOwner: this), + (LongPressGestureRecognizer instance) { + instance.onLongPressStart = onLongPressStart; + }, ); + + return RawGestureDetector(behavior: behavior, gestures: gestures, child: child); } } @@ -198,16 +189,14 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { for (final int pointer in _pointerLocations.keys) { focalPoint += _pointerLocations[pointer]!; } - _currentFocalPoint = - count > 0 ? focalPoint / count.toDouble() : Offset.zero; + _currentFocalPoint = count > 0 ? focalPoint / count.toDouble() : Offset.zero; // Span is the average deviation from focal point. Horizontal and vertical // spans are the average deviations from the focal point's horizontal and // vertical coordinates, respectively. double totalDeviation = 0.0; for (final int pointer in _pointerLocations.keys) { - totalDeviation += - (_currentFocalPoint! - _pointerLocations[pointer]!).distance; + totalDeviation += (_currentFocalPoint! - _pointerLocations[pointer]!).distance; } _currentSpan = count > 0 ? totalDeviation / count : 0.0; } @@ -219,15 +208,13 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { : hitDetector!.shouldMove(move, Axis.horizontal); if (shouldMove || _pointerLocations.keys.length > 1) { final double spanDelta = (_currentSpan! - _initialSpan!).abs(); - final double focalPointDelta = - (_currentFocalPoint! - _initialFocalPoint!).distance; + final double focalPointDelta = (_currentFocalPoint! - _initialFocalPoint!).distance; // warning: do not compare `focalPointDelta` to `kPanSlop` // `ScaleGestureRecognizer` uses `kPanSlop`, but `HorizontalDragGestureRecognizer` uses `kTouchSlop` // and PhotoView recognizer may compete with the `HorizontalDragGestureRecognizer` from a containing `PageView` // setting `touchSlopFactor` to 2 restores default `ScaleGestureRecognizer` behaviour as `kPanSlop = kTouchSlop * 2.0` // setting `touchSlopFactor` in [0, 1] will allow this recognizer to accept the gesture before the one from `PageView` - if (spanDelta > kScaleSlop || - focalPointDelta > kTouchSlop * touchSlopFactor) { + if (spanDelta > kScaleSlop || focalPointDelta > kTouchSlop * touchSlopFactor) { acceptGesture(event.pointer); } } @@ -252,12 +239,7 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { /// ); /// ``` class PhotoViewGestureDetectorScope extends InheritedWidget { - const PhotoViewGestureDetectorScope({ - super.key, - this.axis, - this.touchSlopFactor = .2, - required super.child, - }); + const PhotoViewGestureDetectorScope({super.key, this.axis, this.touchSlopFactor = .2, required super.child}); static PhotoViewGestureDetectorScope? of(BuildContext context) { final PhotoViewGestureDetectorScope? scope = context @@ -275,8 +257,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { @override bool updateShouldNotify(PhotoViewGestureDetectorScope oldWidget) { - return axis != oldWidget.axis && - touchSlopFactor != oldWidget.touchSlopFactor; + return axis != oldWidget.axis && touchSlopFactor != oldWidget.touchSlopFactor; } } @@ -285,10 +266,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { // we cannot change that, but we can prevent the scrollable from panning until this threshold is reached // and let other recognizers accept the gesture instead class PhotoViewPageViewScrollPhysics extends ScrollPhysics { - const PhotoViewPageViewScrollPhysics({ - this.touchSlopFactor = 0.1, - super.parent, - }); + const PhotoViewPageViewScrollPhysics({this.touchSlopFactor = 0.1, super.parent}); // in [0, 1] // 0: most reactive but will not let PhotoView recognizers accept gestures @@ -297,10 +275,7 @@ class PhotoViewPageViewScrollPhysics extends ScrollPhysics { @override PhotoViewPageViewScrollPhysics applyTo(ScrollPhysics? ancestor) { - return PhotoViewPageViewScrollPhysics( - touchSlopFactor: touchSlopFactor, - parent: buildParent(ancestor), - ); + return PhotoViewPageViewScrollPhysics(touchSlopFactor: touchSlopFactor, parent: buildParent(ancestor)); } @override diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart index 768e5d9cc7..eac0cb50ce 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart @@ -25,18 +25,14 @@ mixin HitCornersDetector on PhotoViewControllerDelegate { return HitCorners(y <= cornersY.min, y >= cornersY.max); } - bool _shouldMoveAxis( - HitCorners hitCorners, - double mainAxisMove, - ) { + bool _shouldMoveAxis(HitCorners hitCorners, double mainAxisMove) { if (mainAxisMove == 0) { return false; } if (!hitCorners.hasHitAny) { return true; } - final axisBlocked = hitCorners.hasHitBoth || - (hitCorners.hasHitMax ? mainAxisMove > 0 : mainAxisMove < 0); + final axisBlocked = hitCorners.hasHitBoth || (hitCorners.hasHitMax ? mainAxisMove > 0 : mainAxisMove < 0); if (axisBlocked) { return false; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart b/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart index a01db562c7..52bb8a0a50 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart @@ -27,9 +27,7 @@ class PhotoViewComputedScale { @override bool operator ==(Object other) => identical(this, other) || - other is PhotoViewComputedScale && - runtimeType == other.runtimeType && - _value == other._value; + other is PhotoViewComputedScale && runtimeType == other.runtimeType && _value == other._value; @override int get hashCode => _value.hashCode; diff --git a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart index a843087bad..fac0550c3b 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart @@ -9,13 +9,7 @@ class PhotoViewDefaultError extends StatelessWidget { Widget build(BuildContext context) { return DecoratedBox( decoration: decoration, - child: Center( - child: Icon( - Icons.broken_image, - color: Colors.grey[400], - size: 40.0, - ), - ), + child: Center(child: Icon(Icons.broken_image, color: Colors.grey[400], size: 40.0)), ); } } @@ -29,16 +23,10 @@ class PhotoViewDefaultLoading extends StatelessWidget { Widget build(BuildContext context) { final expectedBytes = event?.expectedTotalBytes; final loadedBytes = event?.cumulativeBytesLoaded; - final value = loadedBytes != null && expectedBytes != null - ? loadedBytes / expectedBytes - : null; + final value = loadedBytes != null && expectedBytes != null ? loadedBytes / expectedBytes : null; return Center( - child: SizedBox( - width: 20.0, - height: 20.0, - child: CircularProgressIndicator(value: value), - ), + child: SizedBox(width: 20.0, height: 20.0, child: CircularProgressIndicator(value: value)), ); } } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart b/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart index fc6d4db3f9..0d1d4715e8 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart @@ -6,7 +6,5 @@ enum PhotoViewScaleState { zoomedIn, zoomedOut; - bool get isScaleStateZooming => - this == PhotoViewScaleState.zoomedIn || - this == PhotoViewScaleState.zoomedOut; + bool get isScaleStateZooming => this == PhotoViewScaleState.zoomedIn || this == PhotoViewScaleState.zoomedOut; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index d4afe85d2b..a2ad04e6b5 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -86,6 +86,7 @@ class _ImageWrapperState extends State { Size? _imageSize; Object? _lastException; StackTrace? _lastStack; + bool _didLoadSynchronously = false; @override void dispose() { @@ -109,9 +110,7 @@ class _ImageWrapperState extends State { // retrieve image from the provider void _resolveImage() { - final ImageStream newStream = widget.imageProvider.resolve( - const ImageConfiguration(), - ); + final ImageStream newStream = widget.imageProvider.resolve(const ImageConfiguration()); _updateSourceStream(newStream); } @@ -125,19 +124,18 @@ class _ImageWrapperState extends State { void handleImageFrame(ImageInfo info, bool synchronousCall) { setupCB() { - _imageSize = Size( - info.image.width.toDouble(), - info.image.height.toDouble(), - ); + _imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble()); _loading = false; _imageInfo = _imageInfo; _loadingProgress = null; _lastException = null; _lastStack = null; + + _didLoadSynchronously = synchronousCall; } - synchronousCall ? setupCB() : setState(setupCB); + synchronousCall && !_didLoadSynchronously ? setupCB() : setState(setupCB); } void handleError(dynamic error, StackTrace? stackTrace) { @@ -154,11 +152,7 @@ class _ImageWrapperState extends State { }()); } - _imageStreamListener = ImageStreamListener( - handleImageFrame, - onChunk: handleImageChunk, - onError: handleError, - ); + _imageStreamListener = ImageStreamListener(handleImageFrame, onChunk: handleImageChunk, onError: handleError); return _imageStreamListener!; } @@ -178,12 +172,36 @@ class _ImageWrapperState extends State { @override Widget build(BuildContext context) { - if (_loading) { - return _buildLoading(context); - } - - if (_lastException != null) { - return _buildError(context); + if (_loading || _lastException != null) { + return CustomChildWrapper( + childSize: null, + backgroundDecoration: widget.backgroundDecoration, + heroAttributes: widget.heroAttributes, + scaleStateChangedCallback: widget.scaleStateChangedCallback, + enableRotation: widget.enableRotation, + controller: widget.controller, + scaleStateController: widget.scaleStateController, + maxScale: widget.maxScale, + minScale: widget.minScale, + initialScale: widget.initialScale, + basePosition: widget.basePosition, + scaleStateCycle: widget.scaleStateCycle, + onTapUp: widget.onTapUp, + onTapDown: widget.onTapDown, + onDragStart: widget.onDragStart, + onDragEnd: widget.onDragEnd, + onDragUpdate: widget.onDragUpdate, + onScaleEnd: widget.onScaleEnd, + onLongPressStart: widget.onLongPressStart, + outerSize: widget.outerSize, + gestureDetectorBehavior: widget.gestureDetectorBehavior, + tightMode: widget.tightMode, + filterQuality: widget.filterQuality, + disableGestures: widget.disableGestures, + disableScaleGestures: true, + enablePanAlways: widget.enablePanAlways, + child: _loading ? _buildLoading(context) : _buildError(context), + ); } final scaleBoundaries = ScaleBoundaries( @@ -227,20 +245,14 @@ class _ImageWrapperState extends State { return widget.loadingBuilder!(context, _loadingProgress, widget.index); } - return PhotoViewDefaultLoading( - event: _loadingProgress, - ); + return PhotoViewDefaultLoading(event: _loadingProgress); } - Widget _buildError( - BuildContext context, - ) { + Widget _buildError(BuildContext context) { if (widget.errorBuilder != null) { return widget.errorBuilder!(context, _lastException!, _lastStack); } - return PhotoViewDefaultError( - decoration: widget.backgroundDecoration, - ); + return PhotoViewDefaultError(decoration: widget.backgroundDecoration); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart index d061b7b76c..3ca31cb8f9 100644 --- a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart +++ b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart @@ -8,8 +8,7 @@ import 'package:flutter/foundation.dart'; /// The common collection of listeners inherited from [ChangeNotifier] will be fired /// every time. class IgnorableChangeNotifier extends ChangeNotifier { - ObserverList? _ignorableListeners = - ObserverList(); + ObserverList? _ignorableListeners = ObserverList(); bool _debugAssertNotDisposed() { assert(() { @@ -51,8 +50,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { void notifyListeners() { super.notifyListeners(); if (_ignorableListeners != null) { - final List localListeners = - List.from(_ignorableListeners!); + final List localListeners = List.from(_ignorableListeners!); for (VoidCallback listener in localListeners) { try { if (_ignorableListeners!.contains(listener)) { @@ -60,11 +58,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { } } catch (exception, stack) { FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'Photoview library', - ), + FlutterErrorDetails(exception: exception, stack: stack, library: 'Photoview library'), ); } } @@ -80,8 +74,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { /// Just like [ValueNotifier] except it extends [IgnorableChangeNotifier] which has /// listeners that wont fire when [updateIgnoring] is called. -class IgnorableValueNotifier extends IgnorableChangeNotifier - implements ValueListenable { +class IgnorableValueNotifier extends IgnorableChangeNotifier implements ValueListenable { IgnorableValueNotifier(this._value); @override diff --git a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart index facd701725..d120955250 100644 --- a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart +++ b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart @@ -5,22 +5,15 @@ import "package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.d import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart'; /// Given a [PhotoViewScaleState], returns a scale value considering [scaleBoundaries]. -double getScaleForScaleState( - PhotoViewScaleState scaleState, - ScaleBoundaries scaleBoundaries, -) { +double getScaleForScaleState(PhotoViewScaleState scaleState, ScaleBoundaries scaleBoundaries) { return switch (scaleState) { PhotoViewScaleState.initial || PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - _clampSize(scaleBoundaries.initialScale, scaleBoundaries), + PhotoViewScaleState.zoomedOut => _clampSize(scaleBoundaries.initialScale, scaleBoundaries), PhotoViewScaleState.covering => _clampSize( - _scaleForCovering( - scaleBoundaries.outerSize, - scaleBoundaries.childSize, - ), - scaleBoundaries, - ), + _scaleForCovering(scaleBoundaries.outerSize, scaleBoundaries.childSize), + scaleBoundaries, + ), PhotoViewScaleState.originalSize => _clampSize(1.0, scaleBoundaries), }; } @@ -28,13 +21,7 @@ double getScaleForScaleState( /// Internal class to wraps custom scale boundaries (min, max and initial) /// Also, stores values regarding the two sizes: the container and the child. class ScaleBoundaries { - const ScaleBoundaries( - this._minScale, - this._maxScale, - this._initialScale, - this.outerSize, - this.childSize, - ); + const ScaleBoundaries(this._minScale, this._maxScale, this._initialScale, this.outerSize, this.childSize); final dynamic _minScale; final dynamic _maxScale; @@ -101,11 +88,7 @@ class ScaleBoundaries { @override int get hashCode => - _minScale.hashCode ^ - _maxScale.hashCode ^ - _initialScale.hashCode ^ - outerSize.hashCode ^ - childSize.hashCode; + _minScale.hashCode ^ _maxScale.hashCode ^ _initialScale.hashCode ^ outerSize.hashCode ^ childSize.hashCode; } double _scaleForContained(Size size, Size childSize) { diff --git a/mobile/lib/widgets/search/curated_people_row.dart b/mobile/lib/widgets/search/curated_people_row.dart index 10c19c7e60..74fc3e1c34 100644 --- a/mobile/lib/widgets/search/curated_people_row.dart +++ b/mobile/lib/widgets/search/curated_people_row.dart @@ -15,13 +15,7 @@ class CuratedPeopleRow extends StatelessWidget { final Function(SearchCuratedContent, int)? onTap; final Function(SearchCuratedContent, int)? onNameTap; - const CuratedPeopleRow({ - super.key, - required this.content, - this.onTap, - this.padding, - required this.onNameTap, - }); + const CuratedPeopleRow({super.key, required this.content, this.onTap, this.padding, required this.onNameTap}); @override Widget build(BuildContext context) { @@ -50,19 +44,13 @@ class CuratedPeopleRow extends StatelessWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), ), const SizedBox(height: 8), - SizedBox( - width: imageSize, - child: _buildPersonLabel(context, person, index), - ), + SizedBox(width: imageSize, child: _buildPersonLabel(context, person, index)), ], ), ); @@ -72,19 +60,13 @@ class CuratedPeopleRow extends StatelessWidget { ); } - Widget _buildPersonLabel( - BuildContext context, - SearchCuratedContent person, - int index, - ) { + Widget _buildPersonLabel(BuildContext context, SearchCuratedContent person, int index) { if (person.label.isEmpty) { return GestureDetector( onTap: () => onNameTap?.call(person, index), child: Text( "exif_bottom_sheet_person_add_person", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -101,11 +83,7 @@ class CuratedPeopleRow extends StatelessWidget { style: context.textTheme.labelLarge, maxLines: 2, ), - if (person.subtitle != null) - Text( - person.subtitle!, - textAlign: TextAlign.center, - ), + if (person.subtitle != null) Text(person.subtitle!, textAlign: TextAlign.center), ], ); } diff --git a/mobile/lib/widgets/search/curated_places_row.dart b/mobile/lib/widgets/search/curated_places_row.dart index 502b09bc4b..9d21292bde 100644 --- a/mobile/lib/widgets/search/curated_places_row.dart +++ b/mobile/lib/widgets/search/curated_places_row.dart @@ -31,9 +31,7 @@ class CuratedPlacesRow extends StatelessWidget { height: imageSize, child: ListView.separated( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), separatorBuilder: (context, index) => const SizedBox(width: 10), itemBuilder: (context, index) { // Injecting Map thumbnail as the first element @@ -45,8 +43,7 @@ class CuratedPlacesRow extends StatelessWidget { } final actualIndex = index - actualContentIndex; final object = content[actualIndex]; - final thumbnailRequestUrl = - '${Store.get(StoreKey.serverEndpoint)}/assets/${object.id}/thumbnail'; + final thumbnailRequestUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/${object.id}/thumbnail'; return SizedBox.square( dimension: imageSize, child: ThumbnailWithInfo( diff --git a/mobile/lib/widgets/search/explore_grid.dart b/mobile/lib/widgets/search/explore_grid.dart index 1841f7f051..a6e1cf5aac 100644 --- a/mobile/lib/widgets/search/explore_grid.dart +++ b/mobile/lib/widgets/search/explore_grid.dart @@ -13,11 +13,7 @@ class ExploreGrid extends StatelessWidget { final List curatedContent; final bool isPeople; - const ExploreGrid({ - super.key, - required this.curatedContent, - this.isPeople = false, - }); + const ExploreGrid({super.key, required this.curatedContent, this.isPeople = false}); @override Widget build(BuildContext context) { @@ -27,10 +23,7 @@ class ExploreGrid extends StatelessWidget { child: SizedBox( height: 100, width: 100, - child: ThumbnailWithInfo( - textInfo: '', - onTap: () {}, - ), + child: ThumbnailWithInfo(textInfo: '', onTap: () {}), ), ); } @@ -53,26 +46,15 @@ class ExploreGrid extends StatelessWidget { borderRadius: 0, onTap: () { isPeople - ? context.pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + ? context.pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) : context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: content.label, - ), + location: SearchLocationFilter(city: content.label), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), diff --git a/mobile/lib/widgets/search/person_name_edit_form.dart b/mobile/lib/widgets/search/person_name_edit_form.dart index 886f17b2cc..d95d7c7483 100644 --- a/mobile/lib/widgets/search/person_name_edit_form.dart +++ b/mobile/lib/widgets/search/person_name_edit_form.dart @@ -16,11 +16,7 @@ class PersonNameEditForm extends HookConsumerWidget { final String personId; final String personName; - const PersonNameEditForm({ - super.key, - required this.personId, - required this.personName, - }); + const PersonNameEditForm({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,10 +24,7 @@ class PersonNameEditForm extends HookConsumerWidget { final isError = useState(false); return AlertDialog( - title: const Text( - "add_a_name", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("add_a_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SingleChildScrollView( child: TextFormField( controller: controller, @@ -46,23 +39,16 @@ class PersonNameEditForm extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: () => context.pop( - const PersonNameEditFormResult(false, ''), - ), + onPressed: () => context.pop(const PersonNameEditFormResult(false, '')), child: Text( "cancel", - style: TextStyle( - color: Colors.red[300], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () async { isError.value = false; - final result = await ref.read( - updatePersonNameProvider(personId, controller.text).future, - ); + final result = await ref.read(updatePersonNameProvider(personId, controller.text).future); isError.value = !result; if (result) { context.pop(PersonNameEditFormResult(true, controller.text)); @@ -70,10 +56,7 @@ class PersonNameEditForm extends HookConsumerWidget { }, child: Text( "save", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/search_filter/camera_picker.dart b/mobile/lib/widgets/search/search_filter/camera_picker.dart index a7c0bb89af..a5204c2fbc 100644 --- a/mobile/lib/widgets/search/search_filter/camera_picker.dart +++ b/mobile/lib/widgets/search/search_filter/camera_picker.dart @@ -21,30 +21,14 @@ class CameraPicker extends HookConsumerWidget { final selectedMake = useState(filter?.make); final selectedModel = useState(filter?.model); - final make = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraMake, - ), - ); + final make = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraMake)); - final models = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraModel, - make: selectedMake.value, - ), - ); + final models = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraModel, make: selectedMake.value)); final makeWidget = SearchDropdown( dropdownMenuEntries: switch (make) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('make').tr(), @@ -56,24 +40,14 @@ class CameraPicker extends HookConsumerWidget { } selectedMake.value = value.toString(); modelTextController.value = TextEditingValue.empty; - onSelect({ - 'make': selectedMake.value, - 'model': null, - }); + onSelect({'make': selectedMake.value, 'model': null}); }, ); final modelWidget = SearchDropdown( dropdownMenuEntries: switch (models) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('model').tr(), @@ -81,21 +55,12 @@ class CameraPicker extends HookConsumerWidget { leadingIcon: const Icon(Icons.camera), onSelected: (value) { selectedModel.value = value.toString(); - onSelect({ - 'make': selectedMake.value, - 'model': selectedModel.value, - }); + onSelect({'make': selectedMake.value, 'model': selectedModel.value}); }, ); if (context.isMobile) { - return Column( - children: [ - makeWidget, - const SizedBox(height: 8), - modelWidget, - ], - ); + return Column(children: [makeWidget, const SizedBox(height: 8), modelWidget]); } return Row( diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index dd0ec44e45..70cbfd2c15 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -20,9 +20,7 @@ class SearchDropdown extends StatelessWidget { Widget build(BuildContext context) { final menuStyle = const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ); diff --git a/mobile/lib/widgets/search/search_filter/display_option_picker.dart b/mobile/lib/widgets/search/search_filter/display_option_picker.dart index 5deed5fe1b..a64eab7b71 100644 --- a/mobile/lib/widgets/search/search_filter/display_option_picker.dart +++ b/mobile/lib/widgets/search/search_filter/display_option_picker.dart @@ -3,18 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; -enum DisplayOption { - notInAlbum, - favorite, - archive, -} +enum DisplayOption { notInAlbum, favorite, archive } class DisplayOptionPicker extends HookWidget { - const DisplayOptionPicker({ - super.key, - required this.onSelect, - this.filter, - }); + const DisplayOptionPicker({super.key, required this.onSelect, this.filter}); final Function(Map) onSelect; final SearchDisplayFilters? filter; @@ -34,10 +26,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('search_filter_display_option_not_in_album').tr(), value: options.value[DisplayOption.notInAlbum], onChanged: (bool? value) { - options.value = { - ...options.value, - DisplayOption.notInAlbum: value!, - }; + options.value = {...options.value, DisplayOption.notInAlbum: value!}; onSelect(options.value); }, ), @@ -45,10 +34,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('favorite').tr(), value: options.value[DisplayOption.favorite], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.favorite: value!, - }; + options.value = {...options.value, DisplayOption.favorite: value!}; onSelect(options.value); }, ), @@ -56,10 +42,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('archive').tr(), value: options.value[DisplayOption.archive], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.archive: value!, - }; + options.value = {...options.value, DisplayOption.archive: value!}; onSelect(options.value); }, ), diff --git a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart index f534b94256..e8226b5b3a 100644 --- a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart +++ b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart @@ -33,10 +33,7 @@ class FilterBottomSheetScaffold extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Text( - title, - style: context.textTheme.headlineSmall, - ), + child: Text(title, style: context.textTheme.headlineSmall), ), buildChildWidget(), Padding( diff --git a/mobile/lib/widgets/search/search_filter/location_picker.dart b/mobile/lib/widgets/search/search_filter/location_picker.dart index 499c1e6a50..608183a2f6 100644 --- a/mobile/lib/widgets/search/search_filter/location_picker.dart +++ b/mobile/lib/widgets/search/search_filter/location_picker.dart @@ -15,8 +15,7 @@ class LocationPicker extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final countryTextController = - useTextEditingController(text: filter?.country); + final countryTextController = useTextEditingController(text: filter?.country); final stateTextController = useTextEditingController(text: filter?.state); final cityTextController = useTextEditingController(text: filter?.city); @@ -53,14 +52,7 @@ class LocationPicker extends HookConsumerWidget { SearchDropdown( dropdownMenuEntries: switch (countries) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('country').tr(), @@ -72,27 +64,14 @@ class LocationPicker extends HookConsumerWidget { selectedCountry.value = value.toString(); stateTextController.value = TextEditingValue.empty; cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': null, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': null, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (states) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('state').tr(), @@ -103,38 +82,21 @@ class LocationPicker extends HookConsumerWidget { } selectedState.value = value.toString(); cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (cities) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('city').tr(), controller: cityTextController, onSelected: (value) { selectedCity.value = value.toString(); - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': selectedCity.value, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': selectedCity.value}); }, ), ], diff --git a/mobile/lib/widgets/search/search_filter/media_type_picker.dart b/mobile/lib/widgets/search/search_filter/media_type_picker.dart index 589ce6262b..e0e34b654e 100644 --- a/mobile/lib/widgets/search/search_filter/media_type_picker.dart +++ b/mobile/lib/widgets/search/search_filter/media_type_picker.dart @@ -13,40 +13,19 @@ class MediaTypePicker extends HookWidget { Widget build(BuildContext context) { final selectedMediaType = useState(filter ?? AssetType.other); - return ListView( - shrinkWrap: true, - children: [ - RadioListTile( - key: const Key("all"), - title: const Text("all").tr(), - value: AssetType.other, - onChanged: (value) { - selectedMediaType.value = value!; - onSelect(value); - }, - groupValue: selectedMediaType.value, - ), - RadioListTile( - key: const Key("image"), - title: const Text("image").tr(), - value: AssetType.image, - onChanged: (value) { - selectedMediaType.value = value!; - onSelect(value); - }, - groupValue: selectedMediaType.value, - ), - RadioListTile( - key: const Key("video"), - title: const Text("video").tr(), - value: AssetType.video, - onChanged: (value) { - selectedMediaType.value = value!; - onSelect(value); - }, - groupValue: selectedMediaType.value, - ), - ], + return RadioGroup( + onChanged: (value) { + selectedMediaType.value = value!; + onSelect(value); + }, + groupValue: selectedMediaType.value, + child: Column( + children: [ + RadioListTile(key: const Key("all"), title: const Text("all").tr(), value: AssetType.other), + RadioListTile(key: const Key("image"), title: const Text("image").tr(), value: AssetType.image), + RadioListTile(key: const Key("video"), title: const Text("video").tr(), value: AssetType.video), + ], + ), ); } } diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index 05f699b44b..b2a7a18c7c 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -40,10 +40,7 @@ class PeoplePicker extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - thickness: 1, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest, thickness: 1), ), Expanded( child: people.widgetWhen( @@ -51,20 +48,12 @@ class PeoplePicker extends HookConsumerWidget { return ListView.builder( shrinkWrap: true, itemCount: people - .where( - (person) => person.name - .toLowerCase() - .contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final person = people - .where( - (person) => person.name - .toLowerCase() - .contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .toList()[index]; final isSelected = selectedPeople.value.contains(person); @@ -76,9 +65,7 @@ class PeoplePicker extends HookConsumerWidget { style: context.textTheme.bodyLarge?.copyWith( fontSize: 20, fontWeight: FontWeight.w500, - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, ), ), leading: SizedBox( @@ -88,10 +75,7 @@ class PeoplePicker extends HookConsumerWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), diff --git a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart index c1e628adeb..a72b4668dd 100644 --- a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart +++ b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart @@ -7,13 +7,7 @@ class SearchFilterChip extends StatelessWidget { final Widget? currentFilter; final IconData icon; - const SearchFilterChip({ - super.key, - required this.label, - required this.onTap, - required this.icon, - this.currentFilter, - }); + const SearchFilterChip({super.key, required this.label, required this.onTap, required this.icon, this.currentFilter}); @override Widget build(BuildContext context) { @@ -23,22 +17,10 @@ class SearchFilterChip extends StatelessWidget { child: Card( elevation: 0, color: context.primaryColor.withValues(alpha: .5), - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.secondaryContainer), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.secondaryContainer)), child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - currentFilter!, - ], - ), + padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), currentFilter!]), ), ), ); @@ -47,21 +29,10 @@ class SearchFilterChip extends StatelessWidget { onTap: onTap, child: Card( elevation: 0, - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.outline.withAlpha(15)), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.outline.withAlpha(15))), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - Text(label), - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), Text(label)]), ), ), ); diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index 78af8f936b..7533e46f1a 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart' import 'package:maplibre_gl/maplibre_gl.dart'; class SearchMapThumbnail extends StatelessWidget { - const SearchMapThumbnail({ - super.key, - this.size = 60.0, - }); + const SearchMapThumbnail({super.key, this.size = 60.0}); final double size; final bool showTitle = true; @@ -23,16 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { context.pushRoute(MapRoute()); }, child: IgnorePointer( - child: MapThumbnail( - zoom: 2, - centre: const LatLng( - 47, - 5, - ), - height: size, - width: size, - showAttribution: false, - ), + child: MapThumbnail(zoom: 2, centre: const LatLng(47, 5), height: size, width: size, showAttribution: false), ), ); } diff --git a/mobile/lib/widgets/search/search_row_section.dart b/mobile/lib/widgets/search/search_row_section.dart index 352c7f6a40..b8584fefef 100644 --- a/mobile/lib/widgets/search/search_row_section.dart +++ b/mobile/lib/widgets/search/search_row_section.dart @@ -25,10 +25,7 @@ class SearchRowSection extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: SearchRowTitle( - onViewAllPressed: onViewAllPressed, - title: title, - ), + child: SearchRowTitle(onViewAllPressed: onViewAllPressed, title: title), ), child, ], diff --git a/mobile/lib/widgets/search/search_row_title.dart b/mobile/lib/widgets/search/search_row_title.dart index 4fa0d1f854..dc0a4ba6cb 100644 --- a/mobile/lib/widgets/search/search_row_title.dart +++ b/mobile/lib/widgets/search/search_row_title.dart @@ -3,11 +3,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SearchRowTitle extends StatelessWidget { - const SearchRowTitle({ - super.key, - required this.onViewAllPressed, - required this.title, - }); + const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title}); final Function() onViewAllPressed; final String title; @@ -17,19 +13,12 @@ class SearchRowTitle extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), TextButton( onPressed: onViewAllPressed, child: Text( 'search_page_view_all_button', - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/thumbnail_with_info.dart b/mobile/lib/widgets/search/thumbnail_with_info.dart index 8722bf8db8..af9460f929 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info.dart @@ -22,8 +22,7 @@ class ThumbnailWithInfo extends StatelessWidget { @override Widget build(BuildContext context) { - var textAndIconColor = - context.isDarkTheme ? Colors.grey[100] : Colors.grey[700]; + var textAndIconColor = context.isDarkTheme ? Colors.grey[100] : Colors.grey[700]; return ThumbnailWithInfoContainer( onTap: onTap, borderRadius: borderRadius, @@ -37,16 +36,10 @@ class ThumbnailWithInfo extends StatelessWidget { fit: BoxFit.cover, imageUrl: imageUrl!, httpHeaders: ApiService.getRequestHeaders(), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ) - : Center( - child: Icon( - noImageIcon ?? Icons.not_listed_location, - color: textAndIconColor, - ), - ), + : Center(child: Icon(noImageIcon ?? Icons.not_listed_location, color: textAndIconColor)), ); } } diff --git a/mobile/lib/widgets/search/thumbnail_with_info_container.dart b/mobile/lib/widgets/search/thumbnail_with_info_container.dart index 1f5f3c2d16..e4b8f69f83 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info_container.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info_container.dart @@ -27,10 +27,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), gradient: LinearGradient( - colors: [ - context.colorScheme.surfaceContainer, - context.colorScheme.surfaceContainer.darken(amount: .1), - ], + colors: [context.colorScheme.surfaceContainer, context.colorScheme.surfaceContainer.darken(amount: .1)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -43,9 +40,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { end: FractionalOffset.bottomCenter, colors: [ Colors.transparent, - label == '' - ? Colors.black.withValues(alpha: 0.1) - : Colors.black.withValues(alpha: 0.5), + label == '' ? Colors.black.withValues(alpha: 0.1) : Colors.black.withValues(alpha: 0.5), ], stops: const [0.0, 1.0], ), @@ -53,15 +48,10 @@ class ThumbnailWithInfoContainer extends StatelessWidget { child: child, ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 8) + - const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 8) + const EdgeInsets.only(bottom: 8), child: Text( label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14), maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index bd501ffcf7..7a107b47d8 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -6,11 +6,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/services/log.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart'; import 'package:immich_mobile/widgets/settings/local_storage_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -25,24 +29,17 @@ class AdvancedSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { bool isLoggedIn = ref.read(currentUserProvider) != null; - final advancedTroubleshooting = - useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); - final manageLocalMediaAndroid = - useAppSettingsState(AppSettingsEnum.manageLocalMediaAndroid); + final advancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); + final manageLocalMediaAndroid = useAppSettingsState(AppSettingsEnum.manageLocalMediaAndroid); final levelId = useAppSettingsState(AppSettingsEnum.logLevel); final preferRemote = useAppSettingsState(AppSettingsEnum.preferRemoteImage); - final allowSelfSignedSSLCert = - useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); - final useAlternatePMFilter = - useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); + final allowSelfSignedSSLCert = useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); + final useAlternatePMFilter = useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); + final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled); final logLevel = Level.LEVELS[levelId.value].name; - useValueChanged( - levelId.value, - (_, __) => - LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel()), - ); + useValueChanged(levelId.value, (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel())); Future checkAndroidVersion() async { if (Platform.isAndroid) { @@ -72,9 +69,7 @@ class AdvancedSettings extends HookConsumerWidget { subtitle: "advanced_settings_sync_remote_deletions_subtitle".tr(), onChanged: (value) async { if (value) { - final result = await ref - .read(localFilesManagerRepositoryProvider) - .requestManageMediaPermission(); + final result = await ref.read(localFilesManagerRepositoryProvider).requestManageMediaPermission(); manageLocalMediaAndroid.value = result; } }, @@ -85,8 +80,7 @@ class AdvancedSettings extends HookConsumerWidget { }, ), SettingsSliderListTile( - text: "advanced_settings_log_level_title" - .tr(namedArgs: {'level': logLevel}), + text: "advanced_settings_log_level_title".tr(namedArgs: {'level': logLevel}), valueNotifier: levelId, maxValue: 8, minValue: 1, @@ -98,7 +92,7 @@ class AdvancedSettings extends HookConsumerWidget { title: "advanced_settings_prefer_remote_title".tr(), subtitle: "advanced_settings_prefer_remote_subtitle".tr(), ), - const LocalStorageSettings(), + if (!Store.isBetaTimelineEnabled) const LocalStorageSettings(), SettingsSwitchListTile( enabled: !isLoggedIn, valueNotifier: allowSelfSignedSSLCert, @@ -108,12 +102,32 @@ class AdvancedSettings extends HookConsumerWidget { ), const CustomeProxyHeaderSettings(), SslClientCertSettings(isLoggedIn: ref.read(currentUserProvider) != null), - SettingsSwitchListTile( - valueNotifier: useAlternatePMFilter, - title: "advanced_settings_enable_alternate_media_filter_title".tr(), - subtitle: - "advanced_settings_enable_alternate_media_filter_subtitle".tr(), - ), + if (!Store.isBetaTimelineEnabled) + SettingsSwitchListTile( + valueNotifier: useAlternatePMFilter, + title: "advanced_settings_enable_alternate_media_filter_title".tr(), + subtitle: "advanced_settings_enable_alternate_media_filter_subtitle".tr(), + ), + const BetaTimelineListTile(), + if (Store.isBetaTimelineEnabled) + SettingsSwitchListTile( + valueNotifier: readonlyModeEnabled, + title: "advanced_settings_readonly_mode_title".tr(), + subtitle: "advanced_settings_readonly_mode_subtitle".tr(), + onChanged: (value) { + readonlyModeEnabled.value = value; + ref.read(readonlyModeProvider.notifier).setReadonlyMode(value); + context.scaffoldMessenger.showSnackBar( + SnackBar( + duration: const Duration(seconds: 2), + content: Text( + (value ? "readonly_mode_enabled" : "readonly_mode_disabled").tr(), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), + ), + ), + ); + }, + ), ]; return SettingsSubPageScaffold(settings: advancedSettings); diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index df974fff30..04786bf916 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; class GroupSettings extends HookConsumerWidget { - const GroupSettings({ - super.key, - }); + const GroupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,10 +19,7 @@ class GroupSettings extends HookConsumerWidget { final groupBy = GroupAssetsBy.values[groupByIndex.value]; Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.groupAssetsBy, - groupBy.index, - ); + await ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.groupAssetsBy, groupBy.index); ref.invalidate(appSettingsServiceProvider); } @@ -41,18 +36,9 @@ class GroupSettings extends HookConsumerWidget { SettingsSubTitle(title: "asset_list_group_by_sub_title".tr()), SettingsRadioListTile( groups: [ - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_by_month_day'.tr(), - value: GroupAssetsBy.day, - ), - SettingsRadioGroup( - title: 'month'.tr(), - value: GroupAssetsBy.month, - ), - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_automatically'.tr(), - value: GroupAssetsBy.auto, - ), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_by_month_day'.tr(), value: GroupAssetsBy.day), + SettingsRadioGroup(title: 'month'.tr(), value: GroupAssetsBy.month), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_automatically'.tr(), value: GroupAssetsBy.auto), ], groupBy: groupBy, onRadioChanged: changeGroupValue, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index 72402c8d55..bcb4a5ec9c 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class LayoutSettings extends HookConsumerWidget { - const LayoutSettings({ - super.key, - }); + const LayoutSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,8 +27,7 @@ class LayoutSettings extends HookConsumerWidget { ), SettingsSliderListTile( valueNotifier: tilesPerRow, - text: 'theme_setting_asset_list_tiles_per_row_title' - .tr(namedArgs: {'count': "${tilesPerRow.value}"}), + text: 'theme_setting_asset_list_tiles_per_row_title'.tr(namedArgs: {'count': "${tilesPerRow.value}"}), label: "${tilesPerRow.value}", maxValue: 6, minValue: 2, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index cd12ea3eb2..907cd19843 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -2,36 +2,35 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_group_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; + import 'asset_list_layout_settings.dart'; class AssetListSettings extends HookConsumerWidget { - const AssetListSettings({ - super.key, - }); + const AssetListSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showStorageIndicator = - useAppSettingsState(AppSettingsEnum.storageIndicator); + final showStorageIndicator = useAppSettingsState(AppSettingsEnum.storageIndicator); final assetListSetting = [ SettingsSwitchListTile( valueNotifier: showStorageIndicator, title: 'theme_setting_asset_list_storage_indicator_title'.tr(), - onChanged: (_) => ref.invalidate(appSettingsServiceProvider), + onChanged: (_) { + ref.invalidate(appSettingsServiceProvider); + ref.invalidate(settingsProvider); + }, ), const LayoutSettings(), const GroupSettings(), ]; - return SettingsSubPageScaffold( - settings: assetListSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetListSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart index 23dca85b6f..5dea38d85e 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'video_viewer_settings.dart'; class AssetViewerSettings extends StatelessWidget { - const AssetViewerSettings({ - super.key, - }); + const AssetViewerSettings({super.key}); @override Widget build(BuildContext context) { - final assetViewerSetting = [ - const ImageViewerQualitySetting(), - const VideoViewerSettings(), - ]; + final assetViewerSetting = [const ImageViewerQualitySetting(), const VideoViewerSettings()]; - return SettingsSubPageScaffold( - settings: assetViewerSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetViewerSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index 444977d9a2..aed88b90b0 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ImageViewerQualitySetting extends HookConsumerWidget { - const ImageViewerQualitySetting({ - super.key, - }); + const ImageViewerQualitySetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,10 +22,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget { SettingsSubTitle(title: "setting_image_viewer_title".tr()), ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), - title: Text( - 'setting_image_viewer_help', - style: context.textTheme.bodyMedium, - ).tr(), + title: Text('setting_image_viewer_help', style: context.textTheme.bodyMedium).tr(), ), SettingsSwitchListTile( valueNotifier: isPreview, diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 4534b6ee09..1d8d9812be 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -8,15 +8,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class VideoViewerSettings extends HookConsumerWidget { - const VideoViewerSettings({ - super.key, - }); + const VideoViewerSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo); - final useOriginalVideo = - useAppSettingsState(AppSettingsEnum.loadOriginalVideo); + final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo); return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index 619a410545..038a567dc2 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -19,18 +19,12 @@ class BackgroundBackupSettings extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isBackgroundEnabled = - ref.watch(backupProvider.select((s) => s.backgroundBackup)); + final isBackgroundEnabled = ref.watch(backupProvider.select((s) => s.backgroundBackup)); final iosSettings = ref.watch(iOSBackgroundSettingsProvider); void showErrorToUser(String msg) { final snackBar = SnackBar( - content: Text( - msg.tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), - ), + content: Text(msg.tr(), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor)), backgroundColor: Colors.red, ); context.scaffoldMessenger.showSnackBar(snackBar); @@ -42,20 +36,14 @@ class BackgroundBackupSettings extends ConsumerWidget { barrierDismissible: false, builder: (BuildContext ctx) { return AlertDialog( - title: const Text( - 'backup_controller_page_background_battery_info_title', - ).tr(), + title: const Text('backup_controller_page_background_battery_info_title').tr(), content: SingleChildScrollView( - child: const Text( - 'backup_controller_page_background_battery_info_message', - ).tr(), + child: const Text('backup_controller_page_background_battery_info_message').tr(), ), actions: [ ElevatedButton( - onPressed: () => launchUrl( - Uri.parse('https://dontkillmyapp.com'), - mode: LaunchMode.externalApplication, - ), + onPressed: () => + launchUrl(Uri.parse('https://dontkillmyapp.com'), mode: LaunchMode.externalApplication), child: const Text( "backup_controller_page_background_battery_info_link", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), @@ -80,26 +68,22 @@ class BackgroundBackupSettings extends ConsumerWidget { title: 'backup_controller_page_background_is_off'.tr(), subtileText: 'backup_controller_page_background_description'.tr(), buttonText: 'backup_controller_page_background_turn_on'.tr(), - onButtonTap: () => - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: true, - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( + enabled: true, + onError: showErrorToUser, + onBatteryInfo: showBatteryOptimizationInfoToUser, + ), ); } return Column( children: [ if (!Platform.isIOS || iosSettings?.appRefreshEnabled == true) - _BackgroundSettingsEnabled( - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), - if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) - const _IOSBackgroundRefreshDisabled(), - if (Platform.isIOS && iosSettings != null) - IosDebugInfoTile(settings: iosSettings), + _BackgroundSettingsEnabled(onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser), + if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) const _IOSBackgroundRefreshDisabled(), + if (Platform.isIOS && iosSettings != null) IosDebugInfoTile(settings: iosSettings), ], ); } @@ -112,13 +96,9 @@ class _IOSBackgroundRefreshDisabled extends StatelessWidget { Widget build(BuildContext context) { return SettingsButtonListTile( icon: Icons.task_outlined, - title: - 'backup_controller_page_background_app_refresh_disabled_title'.tr(), - subtileText: - 'backup_controller_page_background_app_refresh_disabled_content'.tr(), - buttonText: - 'backup_controller_page_background_app_refresh_enable_button_text' - .tr(), + title: 'backup_controller_page_background_app_refresh_disabled_title'.tr(), + subtileText: 'backup_controller_page_background_app_refresh_disabled_content'.tr(), + buttonText: 'backup_controller_page_background_app_refresh_enable_button_text'.tr(), onButtonTap: () => openAppSettings(), ); } @@ -128,60 +108,53 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final void Function(String msg) onError; final void Function() onBatteryInfo; - const _BackgroundSettingsEnabled({ - required this.onError, - required this.onBatteryInfo, - }); + const _BackgroundSettingsEnabled({required this.onError, required this.onBatteryInfo}); @override Widget build(BuildContext context, WidgetRef ref) { - final isWifiRequired = - ref.watch(backupProvider.select((s) => s.backupRequireWifi)); + final isWifiRequired = ref.watch(backupProvider.select((s) => s.backupRequireWifi)); final isWifiRequiredNotifier = useValueNotifier(isWifiRequired); useValueChanged( isWifiRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isWifiRequiredNotifier.value = isWifiRequired, - ), + (_, __) => WidgetsBinding.instance.addPostFrameCallback((_) => isWifiRequiredNotifier.value = isWifiRequired), ); - final isChargingRequired = - ref.watch(backupProvider.select((s) => s.backupRequireCharging)); + final isChargingRequired = ref.watch(backupProvider.select((s) => s.backupRequireCharging)); final isChargingRequiredNotifier = useValueNotifier(isChargingRequired); useValueChanged( isChargingRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isChargingRequiredNotifier.value = isChargingRequired, - ), + (_, __) => + WidgetsBinding.instance.addPostFrameCallback((_) => isChargingRequiredNotifier.value = isChargingRequired), ); int backupDelayToSliderValue(int ms) => switch (ms) { - 5000 => 0, - 30000 => 1, - 120000 => 2, - _ => 3, - }; + 5000 => 0, + 30000 => 1, + 120000 => 2, + _ => 3, + }; - int backupDelayToMilliseconds(int v) => - switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 }; + int backupDelayToMilliseconds(int v) => switch (v) { + 0 => 5000, + 1 => 30000, + 2 => 120000, + _ => 600000, + }; String formatBackupDelaySliderValue(int v) => switch (v) { - 0 => 'setting_notifications_notify_seconds' - .tr(namedArgs: {'count': '5'}), - 1 => 'setting_notifications_notify_seconds' - .tr(namedArgs: {'count': '30'}), - 2 => 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '2'}), - _ => 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '10'}), - }; + 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), + }; - final backupTriggerDelay = - ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); + final backupTriggerDelay = ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); final triggerDelay = useState(backupDelayToSliderValue(backupTriggerDelay)); useValueChanged( triggerDelay.value, - (_, __) => ref.read(backupProvider.notifier).configureBackgroundBackup( + (_, __) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( triggerDelay: backupDelayToMilliseconds(triggerDelay.value), onError: onError, onBatteryInfo: onBatteryInfo, @@ -193,43 +166,32 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { iconColor: context.primaryColor, title: 'backup_controller_page_background_is_on'.tr(), buttonText: 'backup_controller_page_background_turn_off'.tr(), - onButtonTap: () => - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onError: onError, onBatteryInfo: onBatteryInfo), subtitle: Column( children: [ SettingsSwitchListTile( valueNotifier: isWifiRequiredNotifier, title: 'backup_controller_page_background_wifi'.tr(), icon: Icons.wifi, - onChanged: (enabled) => - ref.read(backupProvider.notifier).configureBackgroundBackup( - requireWifi: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireWifi: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), SettingsSwitchListTile( valueNotifier: isChargingRequiredNotifier, title: 'backup_controller_page_background_charging'.tr(), icon: Icons.charging_station, - onChanged: (enabled) => - ref.read(backupProvider.notifier).configureBackgroundBackup( - requireCharging: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireCharging: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), if (Platform.isAndroid) SettingsSliderListTile( valueNotifier: triggerDelay, text: 'backup_controller_page_background_delay'.tr( - namedArgs: { - 'duration': formatBackupDelaySliderValue(triggerDelay.value), - }, + namedArgs: {'duration': formatBackupDelaySliderValue(triggerDelay.value)}, ), maxValue: 3.0, noDivisons: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart index 20f172cb28..50aa57da9f 100644 --- a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart @@ -15,16 +15,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class BackupSettings extends HookConsumerWidget { - const BackupSettings({ - super.key, - }); + const BackupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final ignoreIcloudAssets = - useAppSettingsState(AppSettingsEnum.ignoreIcloudAssets); - final isAdvancedTroubleshooting = - useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); + final ignoreIcloudAssets = useAppSettingsState(AppSettingsEnum.ignoreIcloudAssets); + final isAdvancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); final albumSync = useAppSettingsState(AppSettingsEnum.syncAlbums); final isCorruptCheckInProgress = ref.watch(backupVerificationProvider); final isAlbumSyncInProgress = useState(false); @@ -63,36 +59,24 @@ class BackupSettings extends HookConsumerWidget { ], ) : null, - subtileText: !isCorruptCheckInProgress - ? 'check_corrupt_asset_backup_description'.tr() - : null, + subtileText: !isCorruptCheckInProgress ? 'check_corrupt_asset_backup_description'.tr() : null, buttonText: 'check_corrupt_asset_backup_button'.tr(), onButtonTap: !isCorruptCheckInProgress - ? () => ref - .read(backupVerificationProvider.notifier) - .performBackupCheck(context) + ? () => ref.read(backupVerificationProvider.notifier).performBackupCheck(context) : null, ), if (albumSync.value) SettingsButtonListTile( icon: Icons.photo_album_outlined, title: 'sync_albums'.tr(), - subtitle: Text( - "sync_albums_manual_subtitle".tr(), - ), + subtitle: Text("sync_albums_manual_subtitle".tr()), buttonText: 'sync_albums'.tr(), child: isAlbumSyncInProgress.value ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: syncAlbums, - child: Text('sync'.tr()), - ), + : ElevatedButton(onPressed: syncAlbums, child: Text('sync'.tr())), ), ]; - return SettingsSubPageScaffold( - settings: backupSettings, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: backupSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart new file mode 100644 index 0000000000..743d38fc48 --- /dev/null +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -0,0 +1,379 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/sync_linked_album.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/platform_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; + +class DriftBackupSettings extends ConsumerWidget { + const DriftBackupSettings({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SettingsSubPageScaffold( + settings: [ + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + "network_requirements".t(context: context).toUpperCase(), + style: context.textTheme.labelSmall?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.7)), + ), + ), + const _UseWifiForUploadVideosButton(), + const _UseWifiForUploadPhotosButton(), + if (CurrentPlatform.isAndroid) ...[ + const Divider(), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + "background_options".t(context: context).toUpperCase(), + style: context.textTheme.labelSmall?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + ), + ), + ), + const _BackupOnlyWhenChargingButton(), + const _BackupDelaySlider(), + ], + const Divider(), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + "backup_albums_sync".t(context: context).toUpperCase(), + style: context.textTheme.labelSmall?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.7)), + ), + ), + const _AlbumSyncActionButton(), + ], + ); + } +} + +class _AlbumSyncActionButton extends ConsumerStatefulWidget { + const _AlbumSyncActionButton(); + + @override + ConsumerState<_AlbumSyncActionButton> createState() => _AlbumSyncActionButtonState(); +} + +class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton> { + bool isAlbumSyncInProgress = false; + + Future _manualSyncAlbums() async { + setState(() { + isAlbumSyncInProgress = true; + }); + + try { + await ref.read(backgroundSyncProvider).syncLinkedAlbum(); + await ref.read(backgroundSyncProvider).syncRemote(); + } catch (_) { + } finally { + Future.delayed(const Duration(seconds: 1), () { + setState(() { + isAlbumSyncInProgress = false; + }); + }); + } + } + + Future _manageLinkedAlbums() async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + final localAlbums = ref.read(backupAlbumProvider); + final selectedBackupAlbums = localAlbums + .where((album) => album.backupSelection == BackupSelection.selected) + .toList(); + + await ref.read(syncLinkedAlbumServiceProvider).manageLinkedAlbums(selectedBackupAlbums, currentUser.id); + } + + @override + Widget build(BuildContext context) { + return ListView( + shrinkWrap: true, + children: [ + StreamBuilder( + stream: Store.watch(StoreKey.syncAlbums), + initialData: Store.tryGet(StoreKey.syncAlbums) ?? false, + builder: (context, snapshot) { + final albumSyncEnable = snapshot.data ?? false; + return Column( + children: [ + ListTile( + title: Text( + "sync_albums".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text( + "sync_upload_album_setting_subtitle".t(context: context), + style: context.textTheme.labelLarge, + ), + trailing: Switch( + value: albumSyncEnable, + onChanged: (bool newValue) async { + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue); + + if (newValue == true) { + await _manageLinkedAlbums(); + } + }, + ), + ), + AnimatedSize( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: albumSyncEnable ? 1.0 : 0.0, + child: albumSyncEnable + ? ListTile( + onTap: _manualSyncAlbums, + contentPadding: const EdgeInsets.only(left: 32, right: 16), + title: Text( + "organize_into_albums".t(context: context), + style: context.textTheme.titleSmall?.copyWith( + color: context.colorScheme.onSurface, + fontWeight: FontWeight.normal, + ), + ), + subtitle: Text( + "organize_into_albums_description".t(context: context), + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + ), + ), + trailing: isAlbumSyncInProgress + ? const SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ) + : IconButton( + onPressed: _manualSyncAlbums, + icon: const Icon(Icons.sync_rounded), + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + iconSize: 20, + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), + ), + ) + : const SizedBox.shrink(), + ), + ), + ], + ); + }, + ), + ], + ); + } +} + +class _SettingsSwitchTile extends ConsumerStatefulWidget { + final AppSettingsEnum appSettingsEnum; + final String titleKey; + final String subtitleKey; + final void Function(bool?)? onChanged; + + const _SettingsSwitchTile({ + required this.appSettingsEnum, + required this.titleKey, + required this.subtitleKey, + this.onChanged, + }); + + @override + ConsumerState createState() => _SettingsSwitchTileState(); +} + +class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> { + late final Stream valueStream; + late final StreamSubscription subscription; + + @override + void initState() { + super.initState(); + valueStream = Store.watch(widget.appSettingsEnum.storeKey).asBroadcastStream(); + subscription = valueStream.listen((value) { + widget.onChanged?.call(value); + }); + } + + @override + void dispose() { + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text( + widget.titleKey.t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text(widget.subtitleKey.t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(widget.appSettingsEnum.storeKey) ?? widget.appSettingsEnum.defaultValue, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref.read(appSettingsServiceProvider).setSetting(widget.appSettingsEnum, newValue); + }, + ); + }, + ), + ); + } +} + +class _UseWifiForUploadVideosButton extends ConsumerWidget { + const _UseWifiForUploadVideosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const _SettingsSwitchTile( + appSettingsEnum: AppSettingsEnum.useCellularForUploadVideos, + titleKey: "videos", + subtitleKey: "network_requirement_videos_upload", + ); + } +} + +class _UseWifiForUploadPhotosButton extends ConsumerWidget { + const _UseWifiForUploadPhotosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const _SettingsSwitchTile( + appSettingsEnum: AppSettingsEnum.useCellularForUploadPhotos, + titleKey: "photos", + subtitleKey: "network_requirement_photos_upload", + ); + } +} + +class _BackupOnlyWhenChargingButton extends ConsumerWidget { + const _BackupOnlyWhenChargingButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return _SettingsSwitchTile( + appSettingsEnum: AppSettingsEnum.backupRequireCharging, + titleKey: "charging", + subtitleKey: "charging_requirement_mobile_backup", + onChanged: (value) { + ref.read(backgroundWorkerFgServiceProvider).configure(requireCharging: value ?? false); + }, + ); + } +} + +class _BackupDelaySlider extends ConsumerStatefulWidget { + const _BackupDelaySlider(); + + @override + ConsumerState<_BackupDelaySlider> createState() => _BackupDelaySliderState(); +} + +class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> { + late final Stream valueStream; + late final StreamSubscription subscription; + late int currentValue; + + static int backupDelayToSliderValue(int ms) => switch (ms) { + 5 => 0, + 30 => 1, + 120 => 2, + _ => 3, + }; + + static int backupDelayToSeconds(int v) => switch (v) { + 0 => 5, + 1 => 30, + 2 => 120, + _ => 600, + }; + + static String formatBackupDelaySliderValue(int v) => switch (v) { + 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), + }; + + @override + void initState() { + super.initState(); + final initialValue = + Store.tryGet(AppSettingsEnum.backupTriggerDelay.storeKey) ?? AppSettingsEnum.backupTriggerDelay.defaultValue; + currentValue = backupDelayToSliderValue(initialValue); + + valueStream = Store.watch(AppSettingsEnum.backupTriggerDelay.storeKey).asBroadcastStream(); + subscription = valueStream.listen((value) { + if (mounted && value != null) { + setState(() { + currentValue = backupDelayToSliderValue(value); + }); + } + }); + } + + @override + void dispose() { + subscription.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16.0, top: 8.0), + child: Text( + 'backup_controller_page_background_delay'.tr( + namedArgs: {'duration': formatBackupDelaySliderValue(currentValue)}, + ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + ), + Slider( + value: currentValue.toDouble(), + onChanged: (double v) { + setState(() { + currentValue = v.toInt(); + }); + }, + onChangeEnd: (double v) async { + final milliseconds = backupDelayToSeconds(v.toInt()); + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.backupTriggerDelay, milliseconds); + }, + max: 3.0, + min: 0.0, + divisions: 3, + label: formatBackupDelaySliderValue(currentValue), + ), + ], + ); + } +} diff --git a/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart b/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart index fc3b32b203..a2ff00fe45 100644 --- a/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart @@ -12,8 +12,7 @@ class ForegroundBackupSettings extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isAutoBackup = ref.watch(backupProvider.select((s) => s.autoBackup)); - void onButtonTap() => - ref.read(backupProvider.notifier).setAutoBackup(!isAutoBackup); + void onButtonTap() => ref.read(backupProvider.notifier).setAutoBackup(!isAutoBackup); if (isAutoBackup) { return SettingsButtonListTile( diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart new file mode 100644 index 0000000000..ac357c2dee --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; + +class EntitiyCountTile extends StatelessWidget { + final int count; + final String label; + final IconData icon; + + const EntitiyCountTile({super.key, required this.count, required this.label, required this.icon}); + + String zeroPadding(int number, int targetWidth) { + final numStr = number.toString(); + return numStr.length < targetWidth ? "0" * (targetWidth - numStr.length) : ""; + } + + int calculateMaxDigits(double availableWidth) { + const double charWidth = 11.0; + return (availableWidth / charWidth).floor().clamp(1, 8); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon and Label + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(icon, color: context.primaryColor), + const SizedBox(width: 8), + Text( + label, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ], + ), + const SizedBox(height: 12), + // Number + LayoutBuilder( + builder: (context, constraints) { + final maxDigits = calculateMaxDigits(constraints.maxWidth); + return RichText( + text: TextSpan( + style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600), + children: [ + TextSpan( + text: zeroPadding(count, maxDigits), + style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), + ), + TextSpan( + text: count.toString(), + style: TextStyle(color: context.primaryColor), + ), + ], + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart new file mode 100644 index 0000000000..a5bca24f81 --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart @@ -0,0 +1,359 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; + +class SyncStatusAndActions extends HookConsumerWidget { + const SyncStatusAndActions({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database file not found".t(context: context))), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); + + await dbFile.copy(exportFile.path); + + final size = MediaQuery.of(context).size; + await Share.shareXFiles( + [XFile(exportFile.path)], + text: 'Immich Database Export', + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), + ); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database exported successfully".t(context: context))), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Failed to export database: $e".t(context: context))), + ); + } + } + } + + Future clearFileCache() async { + await ref.read(storageRepositoryProvider).clearCache(); + } + + Future resetSqliteDb(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text("reset_sqlite".t(context: context)), + content: Text("reset_sqlite_confirmation".t(context: context)), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text("cancel".t(context: context)), + ), + TextButton( + onPressed: () async { + await ref.read(driftProvider).reset(); + context.pop(); + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("reset_sqlite_success".t(context: context))), + ); + }, + child: Text( + "confirm".t(context: context), + style: TextStyle(color: context.colorScheme.error), + ), + ), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: ListView( + children: [ + const _SyncStatsCounts(), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "jobs".t(context: context)), + ListTile( + title: Text( + "sync_local".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncLocal(full: true); + }, + ), + ListTile( + title: Text( + "sync_remote".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.cloud_sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncRemote(); + }, + ), + ListTile( + title: Text( + "hash_asset".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.tag), + subtitle: Text("tap_to_run_job".t(context: context)), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), + onTap: () { + ref.read(backgroundSyncProvider).hashAssets(); + }, + ), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "clear_file_cache".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.playlist_remove_rounded), + onTap: clearFileCache, + ), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("export_database_description".t(context: context)), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), + ListTile( + title: Text( + "reset_sqlite".t(context: context), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500), + ), + leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error), + onTap: () async { + await resetSqliteDb(context); + }, + ), + ], + ), + ); + } +} + +class _SyncStatusIcon extends StatelessWidget { + final SyncStatus status; + + const _SyncStatusIcon({required this.status}); + + @override + Widget build(BuildContext context) { + return switch (status) { + SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), + SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), + SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), + SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), + }; + } +} + +class _SectionHeaderText extends StatelessWidget { + final String text; + + const _SectionHeaderText({required this.text}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + text.toUpperCase(), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + ); + } +} + +class _SyncStatsCounts extends ConsumerWidget { + const _SyncStatsCounts(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + final localAlbumService = ref.watch(localAlbumServiceProvider); + final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); + final memoryService = ref.watch(driftMemoryServiceProvider); + + Future> loadCounts() async { + final assetCounts = assetService.getAssetCounts(); + final localAlbumCounts = localAlbumService.getCount(); + final remoteAlbumCounts = remoteAlbumService.getCount(); + final memoryCount = memoryService.getCount(); + final getLocalHashedCount = assetService.getLocalHashedCount(); + + return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); + } + + return FutureBuilder( + future: loadCounts(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Center(child: SizedBox(height: 48, width: 48, child: CircularProgressIndicator())); + } + + if (snapshot.hasError) { + return ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + "Error occur, reset the local database by tapping the button below", + style: context.textTheme.bodyLarge, + ), + ), + ), + ], + ); + } + + final assetCounts = snapshot.data![0]! as (int, int); + final localAssetCount = assetCounts.$1; + final remoteAssetCount = assetCounts.$2; + + final localAlbumCount = snapshot.data![1]! as int; + final remoteAlbumCount = snapshot.data![2]! as int; + final memoryCount = snapshot.data![3]! as int; + final localHashedCount = snapshot.data![4]! as int; + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _SectionHeaderText(text: "assets".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAssetCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAssetCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "albums".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAlbumCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAlbumCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "other".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "memories".t(context: context), + count: memoryCount, + icon: Icons.calendar_today, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: localHashedCount, + icon: Icons.tag, + ), + ), + ], + ), + ), + ], + ); + }, + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index a9c873cb67..1fefb3dcfa 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,278 +1,68 @@ -import 'dart:math' as math; - import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -class BetaTimelineListTile extends ConsumerStatefulWidget { - const BetaTimelineListTile({ - super.key, - }); +class BetaTimelineListTile extends ConsumerWidget { + const BetaTimelineListTile({super.key}); @override - ConsumerState createState() => - _BetaTimelineListTileState(); -} + Widget build(BuildContext context, WidgetRef ref) { + final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.betaTimeline); + final auth = ref.watch(authProvider); -class _BetaTimelineListTileState extends ConsumerState - with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _rotationAnimation; - late Animation _pulseAnimation; - late Animation _gradientAnimation; + if (!auth.isAuthenticated) { + return const SizedBox.shrink(); + } - @override - void initState() { - super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 3), - vsync: this, - ); - - _rotationAnimation = Tween(begin: 0, end: 2 * math.pi).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.linear, - ), - ); - - _pulseAnimation = Tween(begin: 1, end: 1.1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); - - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); - - _animationController.repeat(reverse: true); - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final betaTimelineValue = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.betaTimeline); - - return AnimatedBuilder( - animation: _animationController, - builder: (context, child) { - void onSwitchChanged(bool value) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: value - ? const Text("Enable Beta Timeline") - : const Text("Disable Beta Timeline"), - content: value - ? const Text( - "Are you sure you want to enable the beta timeline?", - ) - : const Text( - "Are you sure you want to disable the beta timeline?", - ), - actions: [ - TextButton( - onPressed: () { - context.pop(); - }, - child: Text( - "cancel".t(context: context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.colorScheme.outline, - ), - ), - ), - ElevatedButton( - onPressed: () async { - Navigator.of(context).pop(); - await ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.betaTimeline, - value, - ); - context.router.replaceAll( - [ChangeExperienceRoute(switchingToBeta: value)], - ); - }, - child: Text( - "ok".t(context: context), - ), - ), - ], - ); - }, - ); - } - - final gradientColors = [ - Color.lerp( - context.primaryColor.withValues(alpha: 0.5), - context.primaryColor.withValues(alpha: 0.3), - _gradientAnimation.value, - )!, - Color.lerp( - context.logoPink.withValues(alpha: 0.2), - context.logoPink.withValues(alpha: 0.4), - _gradientAnimation.value, - )!, - Color.lerp( - context.logoRed.withValues(alpha: 0.3), - context.logoRed.withValues(alpha: 0.5), - _gradientAnimation.value, - )!, - ]; - - return Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(12)), - gradient: LinearGradient( - colors: gradientColors, - stops: const [0.0, 0.5, 1.0], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - transform: GradientRotation(_rotationAnimation.value * 0.5), - ), - boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 8, - offset: const Offset(0, 2), - ), - ], - ), - child: Container( - margin: const EdgeInsets.all(2), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - color: context.scaffoldBackgroundColor, - ), - child: Material( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - child: InkWell( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - onTap: () => onSwitchChanged(!betaTimelineValue), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: Row( - children: [ - Transform.scale( - scale: _pulseAnimation.value, - child: Transform.rotate( - angle: _rotationAnimation.value * 0.02, - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - context.primaryColor.withValues(alpha: 0.2), - context.primaryColor.withValues(alpha: 0.1), - ], - ), - ), - child: Icon( - Icons.auto_awesome, - color: context.primaryColor, - size: 20, - ), - ), - ), - ), - const SizedBox(width: 28), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "advanced_settings_beta_timeline_title" - .t(context: context), - style: - context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(width: 8), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 2, - ), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), - gradient: LinearGradient( - colors: [ - context.primaryColor - .withValues(alpha: 0.8), - context.primaryColor - .withValues(alpha: 0.6), - ], - ), - ), - child: Text( - 'NEW', - style: - context.textTheme.labelSmall?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 10, - height: 1.2, - ), - ), - ), - ], - ), - const SizedBox(height: 4), - Text( - "advanced_settings_beta_timeline_subtitle" - .t(context: context), - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color - ?.withValues(alpha: 0.9), - ), - maxLines: 2, - ), - ], - ), - ), - Switch.adaptive( - value: betaTimelineValue, - onChanged: onSwitchChanged, - activeColor: context.primaryColor, - ), - ], - ), + void onSwitchChanged(bool value) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: value ? const Text("Enable New Timeline") : const Text("Disable New Timeline"), + content: value + ? const Text("Are you sure you want to enable the new timeline?") + : const Text("Are you sure you want to disable the new timeline?"), + actions: [ + TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + "cancel".t(context: context), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), ), ), - ), - ), - ); - }, + ElevatedButton( + onPressed: () async { + Navigator.of(context).pop(); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); + }, + child: Text("ok".t(context: context)), + ), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.only(left: 4.0), + child: ListTile( + title: Text("new_timeline".t(context: context)), + trailing: Switch.adaptive( + value: betaTimelineValue, + onChanged: onSwitchChanged, + activeThumbColor: context.primaryColor, + ), + onTap: () => onSwitchChanged(!betaTimelineValue), + ), ); } } diff --git a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart index 2e1f165602..f0e248b39d 100644 --- a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart +++ b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart @@ -15,15 +15,11 @@ class CustomeProxyHeaderSettings extends StatelessWidget { dense: true, title: Text( "headers_settings_tile_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), subtitle: Text( "headers_settings_tile_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(const HeaderSettingsRoute()), ); diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 4d41d5b19b..d0b3ac0021 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -31,13 +31,11 @@ class LanguageSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final localeEntries = useMemoized(() => locales.entries.toList(), const []); final currentLocale = context.locale; - final filteredLocaleEntries = - useState>>(localeEntries); + final filteredLocaleEntries = useState>>(localeEntries); final selectedLocale = useState(currentLocale); final isLoading = useState(false); - final isButtonDisabled = - selectedLocale.value == currentLocale || isLoading.value; + final isButtonDisabled = selectedLocale.value == currentLocale || isLoading.value; final searchController = useTextEditingController(); final searchFocusNode = useFocusNode(); @@ -50,10 +48,7 @@ class LanguageSettings extends HookConsumerWidget { filteredLocaleEntries.value = localeEntries; } else { filteredLocaleEntries.value = localeEntries - .where( - (entry) => - entry.key.toLowerCase().contains(searchTerm.toLowerCase()), - ) + .where((entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase())) .toList(); } }); @@ -64,17 +59,14 @@ class LanguageSettings extends HookConsumerWidget { onSearch(''); } - useEffect( - () { - void searchListener() => onSearch(searchController.text); - searchController.addListener(searchListener); - return () { - searchController.removeListener(searchListener); - debounceTimer.value?.cancel(); - }; - }, - [searchController], - ); + useEffect(() { + void searchListener() => onSearch(searchController.text); + searchController.addListener(searchListener); + return () { + searchController.removeListener(searchListener); + debounceTimer.value?.cancel(); + }; + }, [searchController]); return SafeArea( child: Column( @@ -94,12 +86,9 @@ class LanguageSettings extends HookConsumerWidget { itemExtent: 64.0, cacheExtent: 100, itemBuilder: (context, index) { - final countryName = - filteredLocaleEntries.value[index].key; - final localeValue = - filteredLocaleEntries.value[index].value; - final bool isSelected = - selectedLocale.value == localeValue; + final countryName = filteredLocaleEntries.value[index].key; + final localeValue = filteredLocaleEntries.value[index].value; + final bool isSelected = selectedLocale.value == localeValue; return _LanguageItem( key: ValueKey(localeValue.toString()), @@ -117,11 +106,7 @@ class LanguageSettings extends HookConsumerWidget { _LanguageApplyButton( isDisabled: isButtonDisabled, isLoading: isLoading.value, - onPressed: () => _applyLanguageChange( - context, - selectedLocale, - isLoading, - ), + onPressed: () => _applyLanguageChange(context, selectedLocale, isLoading), ), ], ), @@ -146,9 +131,7 @@ class _LanguageSearchBar extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(top: 16, bottom: 8, left: 50, right: 50), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(24)), @@ -168,10 +151,7 @@ class _LanguageSearchBar extends StatelessWidget { hintText: 'language_search_hint'.t(context: context), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: controller.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClear, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClear) : null, controller: controller, onChanged: onChanged, @@ -192,24 +172,16 @@ class _LanguageNotFound extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.search_off_rounded, - size: 64, - color: context.colorScheme.onSurface.withValues(alpha: 0.4), - ), + Icon(Icons.search_off_rounded, size: 64, color: context.colorScheme.onSurface.withValues(alpha: 0.4)), const SizedBox(height: 8), Text( 'language_no_results_title'.t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface), ), const SizedBox(height: 4), Text( 'language_no_results_subtitle'.t(context: context), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.8), - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.8)), ), ], ), @@ -218,11 +190,7 @@ class _LanguageNotFound extends StatelessWidget { } class _LanguageApplyButton extends StatelessWidget { - const _LanguageApplyButton({ - required this.isDisabled, - required this.isLoading, - required this.onPressed, - }); + const _LanguageApplyButton({required this.isDisabled, required this.isLoading, required this.onPressed}); final bool isDisabled; final bool isLoading; @@ -231,9 +199,7 @@ class _LanguageApplyButton extends StatelessWidget { @override Widget build(BuildContext context) { return DecoratedBox( - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( @@ -242,18 +208,10 @@ class _LanguageApplyButton extends StatelessWidget { child: ElevatedButton( onPressed: isDisabled ? null : onPressed, child: isLoading - ? const SizedBox.square( - dimension: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) + ? const SizedBox.square(dimension: 24, child: CircularProgressIndicator(strokeWidth: 2)) : Text( 'setting_languages_apply'.t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16.0, - ), + style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0), ), ), ), @@ -279,48 +237,27 @@ class _LanguageItem extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), child: DecoratedBox( decoration: BoxDecoration( - color: - context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), - border: Border.all( - color: context.colorScheme.outlineVariant.withValues(alpha: .4), - width: 1.0, - ), + color: context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + border: Border.all(color: context.colorScheme.outlineVariant.withValues(alpha: .4), width: 1.0), ), child: ListTile( title: Text( countryName, style: context.textTheme.titleSmall?.copyWith( fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected - ? context.colorScheme.primary - : context.colorScheme.onSurfaceVariant, + color: isSelected ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant, ), ), - trailing: isSelected - ? Icon( - Icons.check, - color: context.colorScheme.primary, - size: 20, - ) - : null, + trailing: isSelected ? Icon(Icons.check, color: context.colorScheme.primary, size: 20) : null, onTap: onTap, selected: isSelected, selectedTileColor: context.colorScheme.primary.withValues(alpha: .15), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0))), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), ), ), ); diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart index 06db78cb97..af9e4079bb 100644 --- a/mobile/lib/widgets/settings/local_storage_settings.dart +++ b/mobile/lib/widgets/settings/local_storage_settings.dart @@ -14,13 +14,10 @@ class LocalStorageSettings extends HookConsumerWidget { final isarDb = ref.watch(dbProvider); final cacheItemCount = useState(0); - useEffect( - () { - cacheItemCount.value = isarDb.duplicatedAssets.countSync(); - return null; - }, - [], - ); + useEffect(() { + cacheItemCount.value = isarDb.duplicatedAssets.countSync(); + return null; + }, []); void clearCache() async { await isarDb.writeTxn(() => isarDb.duplicatedAssets.clear()); @@ -32,15 +29,11 @@ class LocalStorageSettings extends HookConsumerWidget { dense: true, title: Text( "cache_settings_duplicated_assets_title", - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(namedArgs: {'count': "${cacheItemCount.value}"}), subtitle: Text( "cache_settings_duplicated_assets_subtitle", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), trailing: TextButton( onPressed: cacheItemCount.value > 0 ? clearCache : null, diff --git a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart index 6302f9422a..a712ce416c 100644 --- a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart +++ b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart @@ -61,8 +61,7 @@ class EndpointInputState extends ConsumerState { final url = controller.text; setState(() => auxCheckStatus = AuxCheckStatus.loading); - final isValid = - await ref.read(authProvider.notifier).validateAuxilaryServerUrl(url); + final isValid = await ref.read(authProvider.notifier).validateAuxilaryServerUrl(url); setState(() { if (mounted) { @@ -98,10 +97,7 @@ class EndpointInputState extends ConsumerState { color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 16), - child: const Icon( - Icons.delete, - color: Colors.white, - ), + child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 24), @@ -121,28 +117,19 @@ class EndpointInputState extends ConsumerState { autovalidateMode: AutovalidateMode.onUserInteraction, validator: validateUrl, keyboardType: TextInputType.url, - style: const TextStyle( - fontFamily: 'Inconsolata', - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: const TextStyle(fontFamily: 'Inconsolata', fontWeight: FontWeight.w600, fontSize: 14), decoration: InputDecoration( hintText: 'http(s)://immich.domain.com', contentPadding: const EdgeInsets.all(16), filled: true, fillColor: context.colorScheme.surfaceContainer, - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(16))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.red[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: - context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!, - ), + borderSide: BorderSide(color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index d38ba06d76..8cc6079961 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -17,30 +17,21 @@ class ExternalNetworkPreference extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final entries = useState( - [const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)], - ); + final entries = useState([const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)]); final canSave = useState(false); saveEndpointList() { - canSave.value = - entries.value.every((e) => e.status == AuxCheckStatus.valid); + canSave.value = entries.value.every((e) => e.status == AuxCheckStatus.valid); - final endpointList = entries.value - .where((url) => url.status == AuxCheckStatus.valid) - .toList(); + final endpointList = entries.value.where((url) => url.status == AuxCheckStatus.valid).toList(); final jsonString = jsonEncode(endpointList); - Store.put( - StoreKey.externalEndpointList, - jsonString, - ); + Store.put(StoreKey.externalEndpointList, jsonString); } updateValidationStatus(String url, int index, AuxCheckStatus status) { - entries.value[index] = - entries.value[index].copyWith(url: url, status: status); + entries.value[index] = entries.value[index].copyWith(url: url, status: status); saveEndpointList(); } @@ -63,11 +54,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { saveEndpointList(); } - Widget proxyDecorator( - Widget child, - int index, - Animation animation, - ) { + Widget proxyDecorator(Widget child, int index, Animation animation) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget? child) { @@ -81,21 +68,17 @@ class ExternalNetworkPreference extends HookConsumerWidget { ); } - useEffect( - () { - final jsonString = Store.tryGet(StoreKey.externalEndpointList); + useEffect(() { + final jsonString = Store.tryGet(StoreKey.externalEndpointList); - if (jsonString == null) { - return null; - } - - final List jsonList = jsonDecode(jsonString); - entries.value = - jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + if (jsonString == null) { return null; - }, - const [], - ); + } + + final List jsonList = jsonDecode(jsonString); + entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + return null; + }, const []); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -104,21 +87,14 @@ class ExternalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.dns_rounded, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.dns_rounded, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -126,14 +102,8 @@ class ExternalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "external_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("external_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), Divider(color: context.colorScheme.surfaceContainerHighest), @@ -170,10 +140,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { ? () { entries.value = [ ...entries.value, - const AuxilaryEndpoint( - url: '', - status: AuxCheckStatus.unknown, - ), + const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown), ]; } : null, diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index a50d216a9d..9fbc43a429 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -7,19 +7,11 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; class LocalNetworkPreference extends HookConsumerWidget { - const LocalNetworkPreference({ - super.key, - required this.enabled, - }); + const LocalNetworkPreference({super.key, required this.enabled}); final bool enabled; - Future _showEditDialog( - BuildContext context, - String title, - String hintText, - String initialValue, - ) { + Future _showEditDialog(BuildContext context, String title, String hintText, String initialValue) { final controller = TextEditingController(text: initialValue); return showDialog( @@ -29,23 +21,14 @@ class LocalNetworkPreference extends HookConsumerWidget { content: TextField( controller: controller, autofocus: true, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - ), + decoration: InputDecoration(border: const OutlineInputBorder(), hintText: hintText), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: Text( - 'cancel'.tr().toUpperCase(), - style: const TextStyle(color: Colors.red), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, controller.text), - child: Text('save'.tr().toUpperCase()), + child: Text('cancel'.tr().toUpperCase(), style: const TextStyle(color: Colors.red)), ), + TextButton(onPressed: () => Navigator.pop(context, controller.text), child: Text('save'.tr().toUpperCase())), ], ), ); @@ -56,24 +39,20 @@ class LocalNetworkPreference extends HookConsumerWidget { final wifiNameText = useState(""); final localEndpointText = useState(""); - useEffect( - () { - final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); - final localEndpoint = - ref.read(authProvider.notifier).getSavedLocalEndpoint(); + useEffect(() { + final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); + final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); - if (wifiName != null) { - wifiNameText.value = wifiName; - } + if (wifiName != null) { + wifiNameText.value = wifiName; + } - if (localEndpoint != null) { - localEndpointText.value = localEndpoint; - } + if (localEndpoint != null) { + localEndpointText.value = localEndpoint; + } - return null; - }, - [], - ); + return null; + }, []); saveWifiName(String wifiName) { wifiNameText.value = wifiName; @@ -86,12 +65,7 @@ class LocalNetworkPreference extends HookConsumerWidget { } handleEditWifiName() async { - final wifiName = await _showEditDialog( - context, - "wifi_name".tr(), - "your_wifi_name".tr(), - wifiNameText.value, - ); + final wifiName = await _showEditDialog(context, "wifi_name".tr(), "your_wifi_name".tr(), wifiNameText.value); if (wifiName != null) { await saveWifiName(wifiName); @@ -131,8 +105,7 @@ class LocalNetworkPreference extends HookConsumerWidget { saveWifiName(wifiName); } - final serverEndpoint = - ref.read(authProvider.notifier).getServerEndpoint(); + final serverEndpoint = ref.read(authProvider.notifier).getServerEndpoint(); if (serverEndpoint != null) { saveLocalEndpoint(serverEndpoint); @@ -148,21 +121,14 @@ class LocalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.home_outlined, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.home_outlined, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -170,19 +136,11 @@ class LocalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "local_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("local_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), - Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + Divider(color: context.colorScheme.surfaceContainerHighest), ListTile( enabled: enabled, contentPadding: const EdgeInsets.only(left: 24, right: 8), @@ -194,10 +152,7 @@ class LocalNetworkPreference extends HookConsumerWidget { wifiNameText.value, style: context.textTheme.labelLarge?.copyWith( fontWeight: FontWeight.bold, - color: enabled - ? context.primaryColor - : context.colorScheme.onSurface - .withAlpha(100), + color: enabled ? context.primaryColor : context.colorScheme.onSurface.withAlpha(100), fontFamily: 'Inconsolata', ), ), @@ -217,10 +172,7 @@ class LocalNetworkPreference extends HookConsumerWidget { localEndpointText.value, style: context.textTheme.labelLarge?.copyWith( fontWeight: FontWeight.bold, - color: enabled - ? context.primaryColor - : context.colorScheme.onSurface - .withAlpha(100), + color: enabled ? context.primaryColor : context.colorScheme.onSurface.withAlpha(100), fontFamily: 'Inconsolata', ), ), @@ -231,15 +183,12 @@ class LocalNetworkPreference extends HookConsumerWidget { ), const SizedBox(height: 16), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 24.0), child: SizedBox( height: 48, child: OutlinedButton.icon( icon: const Icon(Icons.wifi_find_rounded), - label: - Text('use_current_connection'.tr().toUpperCase()), + label: Text('use_current_connection'.tr().toUpperCase()), onPressed: enabled ? autofillCurrentNetwork : null, ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 587a0ce6d3..426ea5ac0f 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -18,8 +18,7 @@ class NetworkingSettings extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final currentEndpoint = getServerUrl(); - final featureEnabled = - useAppSettingsState(AppSettingsEnum.autoEndpointSwitching); + final featureEnabled = useAppSettingsState(AppSettingsEnum.autoEndpointSwitching); Future checkWifiReadPermission() async { final [hasLocationInUse, hasLocationAlways] = await Future.wait([ @@ -39,9 +38,7 @@ class NetworkingSettings extends HookConsumerWidget { actions: [ TextButton( onPressed: () async { - final isGrant = await ref - .read(networkProvider.notifier) - .requestWifiReadPermission(); + final isGrant = await ref.read(networkProvider.notifier).requestWifiReadPermission(); Navigator.pop(context, isGrant); }, @@ -63,9 +60,7 @@ class NetworkingSettings extends HookConsumerWidget { actions: [ TextButton( onPressed: () async { - final isGrant = await ref - .read(networkProvider.notifier) - .requestWifiReadBackgroundPermission(); + final isGrant = await ref.read(networkProvider.notifier).requestWifiReadBackgroundPermission(); Navigator.pop(context, isGrant); }, @@ -77,21 +72,17 @@ class NetworkingSettings extends HookConsumerWidget { ); } - if (isGrantLocationAlwaysPermission != null && - !isGrantLocationAlwaysPermission) { + if (isGrantLocationAlwaysPermission != null && !isGrantLocationAlwaysPermission) { await ref.read(networkProvider.notifier).openSettings(); } } - useEffect( - () { - if (featureEnabled.value == true) { - checkWifiReadPermission(); - } - return null; - }, - [featureEnabled.value], - ); + useEffect(() { + if (featureEnabled.value == true) { + checkWifiReadPermission(); + } + return null; + }, [featureEnabled.value]); return ListView( padding: const EdgeInsets.only(bottom: 96), @@ -101,9 +92,7 @@ class NetworkingSettings extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8, left: 16, bottom: 8), child: NetworkPreferenceTitle( title: "current_server_address".tr().toUpperCase(), - icon: (currentEndpoint?.startsWith('https') ?? false) - ? Icons.https_outlined - : Icons.http_outlined, + icon: (currentEndpoint?.startsWith('https') ?? false) ? Icons.https_outlined : Icons.http_outlined, ), ), Padding( @@ -112,20 +101,12 @@ class NetworkingSettings extends HookConsumerWidget { elevation: 0, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(16)), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: ListTile( leading: currentEndpoint != null - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - ) - : const Icon( - Icons.circle_outlined, - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green) + : const Icon(Icons.circle_outlined), title: Text( currentEndpoint ?? "--", style: TextStyle( @@ -140,9 +121,7 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 10.0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest), ), SettingsSwitchListTile( enabled: true, @@ -152,35 +131,21 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 8, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "local_network".tr().toUpperCase(), - icon: Icons.home_outlined, - ), - ), - LocalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "local_network".tr().toUpperCase(), icon: Icons.home_outlined), ), + LocalNetworkPreference(enabled: featureEnabled.value), Padding( padding: const EdgeInsets.only(top: 32, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "external_network".tr().toUpperCase(), - icon: Icons.dns_outlined, - ), - ), - ExternalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "external_network".tr().toUpperCase(), icon: Icons.dns_outlined), ), + ExternalNetworkPreference(enabled: featureEnabled.value), ], ); } } class NetworkPreferenceTitle extends StatelessWidget { - const NetworkPreferenceTitle({ - super.key, - required this.icon, - required this.title, - }); + const NetworkPreferenceTitle({super.key, required this.icon, required this.title}); final IconData icon; final String title; @@ -189,10 +154,7 @@ class NetworkPreferenceTitle extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Icon( - icon, - color: context.colorScheme.onSurface.withAlpha(150), - ), + Icon(icon, color: context.colorScheme.onSurface.withAlpha(150)), const SizedBox(width: 8), Text( title, @@ -207,58 +169,37 @@ class NetworkPreferenceTitle extends StatelessWidget { } class NetworkStatusIcon extends StatelessWidget { - const NetworkStatusIcon({ - super.key, - required this.status, - this.enabled = true, - }) : super(); + const NetworkStatusIcon({super.key, required this.status, this.enabled = true}) : super(); final AuxCheckStatus status; final bool enabled; @override Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: _buildIcon(context), - ); + return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: _buildIcon(context)); } Widget _buildIcon(BuildContext context) => switch (status) { - AuxCheckStatus.loading => Padding( - padding: const EdgeInsets.only(left: 4.0), - child: SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: context.primaryColor, - strokeWidth: 2, - key: const ValueKey('loading'), - ), + AuxCheckStatus.loading => Padding( + padding: const EdgeInsets.only(left: 4.0), + child: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(color: context.primaryColor, strokeWidth: 2, key: const ValueKey('loading')), + ), + ), + AuxCheckStatus.valid => + enabled + ? const Icon(Icons.check_circle_rounded, color: Colors.green, key: ValueKey('success')) + : Icon( + Icons.check_circle_rounded, + color: context.colorScheme.onSurface.withAlpha(100), + key: const ValueKey('success'), ), - ), - AuxCheckStatus.valid => enabled - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - key: ValueKey('success'), - ) - : Icon( - Icons.check_circle_rounded, - color: context.colorScheme.onSurface.withAlpha(100), - key: const ValueKey('success'), - ), - AuxCheckStatus.error => enabled - ? const Icon( - Icons.error_rounded, - color: Colors.red, - key: ValueKey('error'), - ) - : const Icon( - Icons.error_rounded, - color: Colors.grey, - key: ValueKey('error'), - ), - _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), - }; + AuxCheckStatus.error => + enabled + ? const Icon(Icons.error_rounded, color: Colors.red, key: ValueKey('error')) + : const Icon(Icons.error_rounded, color: Colors.grey, key: ValueKey('error')), + _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), + }; } diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart index cf6745199e..d9eab26bda 100644 --- a/mobile/lib/widgets/settings/notification_setting.dart +++ b/mobile/lib/widgets/settings/notification_setting.dart @@ -12,20 +12,15 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:permission_handler/permission_handler.dart'; class NotificationSetting extends HookConsumerWidget { - const NotificationSetting({ - super.key, - }); + const NotificationSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final permissionService = ref.watch(notificationPermissionProvider); - final sliderValue = - useAppSettingsState(AppSettingsEnum.uploadErrorNotificationGracePeriod); - final totalProgressValue = - useAppSettingsState(AppSettingsEnum.backgroundBackupTotalProgress); - final singleProgressValue = - useAppSettingsState(AppSettingsEnum.backgroundBackupSingleProgress); + final sliderValue = useAppSettingsState(AppSettingsEnum.uploadErrorNotificationGracePeriod); + final totalProgressValue = useAppSettingsState(AppSettingsEnum.backgroundBackupTotalProgress); + final singleProgressValue = useAppSettingsState(AppSettingsEnum.backgroundBackupSingleProgress); final hasPermission = permissionService == PermissionStatus.granted; @@ -42,21 +37,14 @@ class NotificationSetting extends HookConsumerWidget { builder: (ctx) => AlertDialog( content: const Text('notification_permission_dialog_content').tr(), actions: [ - TextButton( - child: const Text('cancel').tr(), - onPressed: () => ctx.pop(), - ), - TextButton( - onPressed: () => openAppNotificationSettings(ctx), - child: const Text('settings').tr(), - ), + TextButton(child: const Text('cancel').tr(), onPressed: () => ctx.pop()), + TextButton(onPressed: () => openAppNotificationSettings(ctx), child: const Text('settings').tr()), ], ), ); } - final String formattedValue = - _formatSliderValue(sliderValue.value.toDouble()); + final String formattedValue = _formatSliderValue(sliderValue.value.toDouble()); final notificationSettings = [ if (!hasPermission) @@ -65,14 +53,12 @@ class NotificationSetting extends HookConsumerWidget { title: 'notification_permission_list_tile_title'.tr(), subtileText: 'notification_permission_list_tile_content'.tr(), buttonText: 'notification_permission_list_tile_enable_button'.tr(), - onButtonTap: () => ref - .watch(notificationPermissionProvider.notifier) - .requestNotificationPermission() - .then((permission) { - if (permission == PermissionStatus.permanentlyDenied) { - showPermissionsDialog(); - } - }), + onButtonTap: () => + ref.watch(notificationPermissionProvider.notifier).requestNotificationPermission().then((permission) { + if (permission == PermissionStatus.permanentlyDenied) { + showPermissionsDialog(); + } + }), ), SettingsSwitchListTile( enabled: hasPermission, @@ -89,8 +75,7 @@ class NotificationSetting extends HookConsumerWidget { SettingsSliderListTile( enabled: hasPermission, valueNotifier: sliderValue, - text: 'setting_notifications_notify_failures_grace_period' - .tr(namedArgs: {'duration': formattedValue}), + text: 'setting_notifications_notify_failures_grace_period'.tr(namedArgs: {'duration': formattedValue}), maxValue: 5.0, noDivisons: 5, label: formattedValue, @@ -105,8 +90,7 @@ String _formatSliderValue(double v) { if (v == 0.0) { return 'setting_notifications_notify_immediately'.tr(); } else if (v == 1.0) { - return 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '30'}); + return 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '30'}); } else if (v == 2.0) { return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '2'}); } else if (v == 3.0) { diff --git a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart index 90a123bfbd..49f57a5e94 100644 --- a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart @@ -8,16 +8,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class HapticSetting extends HookConsumerWidget { - const HapticSetting({ - super.key, - }); + const HapticSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final hapticFeedbackSetting = - useAppSettingsState(AppSettingsEnum.enableHapticFeedback); - final isHapticFeedbackEnabled = - useValueNotifier(hapticFeedbackSetting.value); + final hapticFeedbackSetting = useAppSettingsState(AppSettingsEnum.enableHapticFeedback); + final isHapticFeedbackEnabled = useValueNotifier(hapticFeedbackSetting.value); onHapticFeedbackChange(bool isEnabled) { hapticFeedbackSetting.value = isEnabled; diff --git a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart index 8a3684e093..144fbf9758 100644 --- a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/preference_settings/theme_setting import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; class PreferenceSetting extends StatelessWidget { - const PreferenceSetting({ - super.key, - }); + const PreferenceSetting({super.key}); @override Widget build(BuildContext context) { - const preferenceSettings = [ - ThemeSetting(), - HapticSetting(), - ]; + const preferenceSettings = [ThemeSetting(), HapticSetting()]; - return const SettingsSubPageScaffold( - settings: preferenceSettings, - showDivider: true, - ); + return const SettingsSubPageScaffold(settings: preferenceSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart index 011904c310..22c9154981 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -12,26 +12,21 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class PrimaryColorSetting extends HookConsumerWidget { - const PrimaryColorSetting({ - super.key, - }); + const PrimaryColorSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final themeProvider = ref.read(immichThemeProvider); - final primaryColorSetting = - useAppSettingsState(AppSettingsEnum.primaryColor); - final systemPrimaryColorSetting = - useAppSettingsState(AppSettingsEnum.dynamicTheme); + final primaryColorSetting = useAppSettingsState(AppSettingsEnum.primaryColor); + final systemPrimaryColorSetting = useAppSettingsState(AppSettingsEnum.dynamicTheme); final currentPreset = useValueNotifier(ref.read(immichThemePresetProvider)); const tileSize = 55.0; useValueChanged( primaryColorSetting.value, - (_, __) => currentPreset.value = ImmichColorPreset.values - .firstWhere((e) => e.name == primaryColorSetting.value), + (_, __) => currentPreset.value = ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorSetting.value), ); void popBottomSheet() { @@ -73,20 +68,14 @@ class PrimaryColorSetting extends HookConsumerWidget { Container( height: tileSize, width: tileSize, - decoration: BoxDecoration( - color: bottomColor, - borderRadius: const BorderRadius.all(Radius.circular(100)), - ), + decoration: BoxDecoration(color: bottomColor, borderRadius: const BorderRadius.all(Radius.circular(100))), ), Container( height: tileSize / 2, width: tileSize, decoration: BoxDecoration( color: topColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(100), - topRight: Radius.circular(100), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(100), topRight: Radius.circular(100)), ), ), if (showSelector) @@ -102,11 +91,7 @@ class PrimaryColorSetting extends HookConsumerWidget { ), child: const Padding( padding: EdgeInsets.all(3), - child: Icon( - Icons.check_rounded, - color: Colors.white, - size: 25, - ), + child: Icon(Icons.check_rounded, color: Colors.white, size: 25), ), ), ), @@ -121,30 +106,21 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Align( alignment: Alignment.center, - child: Text( - "theme_setting_primary_color_title".tr(), - style: context.textTheme.titleLarge, - ), + child: Text("theme_setting_primary_color_title".tr(), style: context.textTheme.titleLarge), ), if (DynamicTheme.isAvailable) Container( padding: const EdgeInsets.symmetric(horizontal: 20), margin: const EdgeInsets.only(top: 10), child: SwitchListTile.adaptive( - contentPadding: - const EdgeInsets.symmetric(vertical: 6, horizontal: 20), + contentPadding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20), dense: true, - activeColor: context.primaryColor, + activeThumbColor: context.primaryColor, tileColor: context.colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), title: Text( 'theme_setting_system_primary_color_title'.tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - height: 1.5, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5), ), value: systemPrimaryColorSetting.value, onChanged: onUseSystemColorChange, @@ -164,8 +140,7 @@ class PrimaryColorSetting extends HookConsumerWidget { topColor: theme.light.primary, bottomColor: theme.dark.primary, tileSize: tileSize, - showSelector: currentPreset.value == preset && - !systemPrimaryColorSetting.value, + showSelector: currentPreset.value == preset && !systemPrimaryColorSetting.value, ), ); }).toList(), @@ -180,10 +155,7 @@ class PrimaryColorSetting extends HookConsumerWidget { context: context, isScrollControlled: true, builder: (BuildContext ctx) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), - child: bottomSheetContent(), - ); + return Padding(padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), child: bottomSheetContent()); }, ), contentPadding: const EdgeInsets.symmetric(horizontal: 20), @@ -195,14 +167,11 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Text( "theme_setting_primary_color_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), Text( "theme_setting_primary_color_subtitle".tr(), - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colorScheme.onSurfaceSecondary), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], ), diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index 0c68de4c52..123f7c9921 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -11,22 +11,17 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ThemeSetting extends HookConsumerWidget { - const ThemeSetting({ - super.key, - }); + const ThemeSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentThemeString = useAppSettingsState(AppSettingsEnum.themeMode); final currentTheme = useValueNotifier(ref.read(immichThemeModeProvider)); final isDarkTheme = useValueNotifier(currentTheme.value == ThemeMode.dark); - final isSystemTheme = - useValueNotifier(currentTheme.value == ThemeMode.system); + final isSystemTheme = useValueNotifier(currentTheme.value == ThemeMode.system); - final applyThemeToBackgroundSetting = - useAppSettingsState(AppSettingsEnum.colorfulInterface); - final applyThemeToBackgroundProvider = - useValueNotifier(ref.read(colorfulInterfaceSettingProvider)); + final applyThemeToBackgroundSetting = useAppSettingsState(AppSettingsEnum.colorfulInterface); + final applyThemeToBackgroundProvider = useValueNotifier(ref.read(colorfulInterfaceSettingProvider)); useValueChanged( currentThemeString.value, @@ -39,8 +34,7 @@ class ThemeSetting extends HookConsumerWidget { useValueChanged( applyThemeToBackgroundSetting.value, - (_, __) => applyThemeToBackgroundProvider.value = - applyThemeToBackgroundSetting.value, + (_, __) => applyThemeToBackgroundProvider.value = applyThemeToBackgroundSetting.value, ); void onThemeChange(bool isDark) { @@ -74,8 +68,7 @@ class ThemeSetting extends HookConsumerWidget { void onSurfaceColorSettingChange(bool useColorfulInterface) { applyThemeToBackgroundSetting.value = useColorfulInterface; - ref.watch(colorfulInterfaceSettingProvider.notifier).state = - useColorfulInterface; + ref.watch(colorfulInterfaceSettingProvider.notifier).state = useColorfulInterface; } return Column( diff --git a/mobile/lib/widgets/settings/settings_button_list_tile.dart b/mobile/lib/widgets/settings/settings_button_list_tile.dart index c8bd8e4b58..0e8d75b22f 100644 --- a/mobile/lib/widgets/settings/settings_button_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_button_list_tile.dart @@ -31,12 +31,7 @@ class SettingsButtonListTile extends StatelessWidget { horizontalTitleGap: 20, isThreeLine: true, leading: Icon(icon, color: iconColor), - title: Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,14 +39,11 @@ class SettingsButtonListTile extends StatelessWidget { if (subtileText != null) Text( subtileText!, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), if (subtitle != null) subtitle!, const SizedBox(height: 6), - child ?? - ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), + child ?? ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), ], ), ); diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart new file mode 100644 index 0000000000..36eff7bae1 --- /dev/null +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -0,0 +1,49 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SettingsCard extends StatelessWidget { + const SettingsCard({ + super.key, + required this.icon, + required this.title, + required this.subtitle, + required this.settingRoute, + }); + + final IconData icon; + final String title; + final String subtitle; + final PageRouteInfo settingRoute; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Card( + elevation: 0, + clipBehavior: Clip.antiAlias, + color: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), + leading: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(16)), + color: context.isDarkTheme ? Colors.black26 : Colors.white.withAlpha(100), + ), + padding: const EdgeInsets.all(16.0), + child: Icon(icon, color: context.primaryColor), + ), + title: Text( + title, + style: context.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), + ), + subtitle: Text(subtitle, style: context.textTheme.labelLarge), + onTap: () => context.pushRoute(settingRoute), + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/settings_radio_list_tile.dart b/mobile/lib/widgets/settings/settings_radio_list_tile.dart index 3f3a6cbe69..6b02a65159 100644 --- a/mobile/lib/widgets/settings/settings_radio_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_radio_list_tile.dart @@ -9,39 +9,31 @@ class SettingsRadioGroup { } class SettingsRadioListTile extends StatelessWidget { - final List groups; + final List> groups; final T groupBy; final void Function(T?) onRadioChanged; - const SettingsRadioListTile({ - super.key, - required this.groups, - required this.groupBy, - required this.onRadioChanged, - }); + const SettingsRadioListTile({super.key, required this.groups, required this.groupBy, required this.onRadioChanged}); @override Widget build(BuildContext context) { - return Column( - children: groups - .map( - (g) => RadioListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 20), - dense: true, - activeColor: context.primaryColor, - title: Text( - g.title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + return RadioGroup( + groupValue: groupBy, + onChanged: onRadioChanged, + child: Column( + children: groups + .map( + (g) => RadioListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 20), + dense: true, + activeColor: context.primaryColor, + title: Text(g.title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), + value: g.value, + controlAffinity: ListTileControlAffinity.trailing, ), - value: g.value, - groupValue: groupBy, - onChanged: onRadioChanged, - controlAffinity: ListTileControlAffinity.trailing, - ), - ) - .toList(), + ) + .toList(), + ), ); } } diff --git a/mobile/lib/widgets/settings/settings_slider_list_tile.dart b/mobile/lib/widgets/settings/settings_slider_list_tile.dart index 386a690864..500591badb 100644 --- a/mobile/lib/widgets/settings/settings_slider_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_slider_list_tile.dart @@ -28,12 +28,7 @@ class SettingsSliderListTile extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, - title: Text( - text, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(text, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Slider( value: valueNotifier.value.toDouble(), onChanged: (double v) => valueNotifier.value = v.toInt(), diff --git a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart index 96c4678ede..b4cb67239e 100644 --- a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart +++ b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart @@ -4,11 +4,7 @@ class SettingsSubPageScaffold extends StatelessWidget { final List settings; final bool showDivider; - const SettingsSubPageScaffold({ - super.key, - required this.settings, - this.showDivider = false, - }); + const SettingsSubPageScaffold({super.key, required this.settings, this.showDivider = false}); @override Widget build(BuildContext context) { @@ -18,11 +14,7 @@ class SettingsSubPageScaffold extends StatelessWidget { itemBuilder: (ctx, index) => settings[index], separatorBuilder: (context, index) => showDivider ? const Column( - children: [ - SizedBox(height: 5), - Divider(height: 10, indent: 15, endIndent: 15), - SizedBox(height: 15), - ], + children: [SizedBox(height: 5), Divider(height: 10, indent: 15, endIndent: 15), SizedBox(height: 15)], ) : const SizedBox(height: 10), ); diff --git a/mobile/lib/widgets/settings/settings_sub_title.dart b/mobile/lib/widgets/settings/settings_sub_title.dart index 9a3fb6947d..d98f1929b2 100644 --- a/mobile/lib/widgets/settings/settings_sub_title.dart +++ b/mobile/lib/widgets/settings/settings_sub_title.dart @@ -4,10 +4,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SettingsSubTitle extends StatelessWidget { final String title; - const SettingsSubTitle({ - super.key, - required this.title, - }); + const SettingsSubTitle({super.key, required this.title}); @override Widget build(BuildContext context) { @@ -15,10 +12,7 @@ class SettingsSubTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 20), child: Text( title, - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w700, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w700), ), ); } diff --git a/mobile/lib/widgets/settings/settings_switch_list_tile.dart b/mobile/lib/widgets/settings/settings_switch_list_tile.dart index 8aa4ec0a60..f5d6dfd05a 100644 --- a/mobile/lib/widgets/settings/settings_switch_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_switch_list_tile.dart @@ -40,18 +40,13 @@ class SettingsSwitchListTile extends StatelessWidget { selectedTileColor: enabled ? null : context.themeData.disabledColor, value: valueNotifier.value, onChanged: onSwitchChanged, - activeColor: - enabled ? context.primaryColor : context.themeData.disabledColor, + activeThumbColor: enabled ? context.primaryColor : context.themeData.disabledColor, dense: true, - secondary: icon != null - ? Icon( - icon!, - color: valueNotifier.value ? context.primaryColor : null, - ) - : null, + secondary: icon != null ? Icon(icon!, color: valueNotifier.value ? context.primaryColor : null) : null, title: Text( title, - style: titleStyle ?? + style: + titleStyle ?? context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, color: enabled ? null : context.themeData.disabledColor, @@ -61,11 +56,10 @@ class SettingsSwitchListTile extends StatelessWidget { subtitle: subtitle != null ? Text( subtitle!, - style: subtitleStyle ?? + style: + subtitleStyle ?? context.textTheme.bodyMedium?.copyWith( - color: enabled - ? context.colorScheme.onSurfaceSecondary - : context.themeData.disabledColor, + color: enabled ? context.colorScheme.onSurfaceSecondary : context.themeData.disabledColor, ), ) : null, diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index 6fdbb156d9..dc31acf0a4 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -20,8 +20,7 @@ class SslClientCertSettings extends StatefulWidget { } class _SslClientCertSettingsState extends State { - _SslClientCertSettingsState() - : isCertExist = SSLClientCertStoreVal.load() != null; + _SslClientCertSettingsState() : isCertExist = SSLClientCertStoreVal.load() != null; bool isCertExist; @@ -31,24 +30,15 @@ class _SslClientCertSettingsState extends State { contentPadding: const EdgeInsets.symmetric(horizontal: 20), horizontalTitleGap: 20, isThreeLine: true, - title: Text( - "client_cert_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text("client_cert_title".tr(), style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "client_cert_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - const SizedBox( - height: 6, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), + const SizedBox(height: 6), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -58,13 +48,9 @@ class _SslClientCertSettingsState extends State { onPressed: widget.isLoggedIn ? null : () => importCert(context), child: Text("client_cert_import".tr()), ), - const SizedBox( - width: 15, - ), + const SizedBox(width: 15), ElevatedButton( - onPressed: widget.isLoggedIn || !isCertExist - ? null - : () => removeCert(context), + onPressed: widget.isLoggedIn || !isCertExist ? null : () async => await removeCert(context), child: Text("remove".tr()), ), ], @@ -79,35 +65,25 @@ class _SslClientCertSettingsState extends State { context: context, builder: (ctx) => AlertDialog( content: Text(message), - actions: [ - TextButton( - onPressed: () => ctx.pop(), - child: Text("client_cert_dialog_msg_confirm".tr()), - ), - ], + actions: [TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()))], ), ); } - void storeCert(BuildContext context, Uint8List data, String? password) { + Future storeCert(BuildContext context, Uint8List data, String? password) async { if (password != null && password.isEmpty) { password = null; } final cert = SSLClientCertStoreVal(data, password); // Test whether the certificate is valid - final isCertValid = HttpSSLCertOverride.setClientCert( - SecurityContext(withTrustedRoots: true), - cert, - ); + final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; } - cert.save(); + await cert.save(); HttpSSLOptions.apply(); - setState( - () => isCertExist = true, - ); + setState(() => isCertExist = true); showMessage(context, "client_cert_import_success_msg".tr()); } @@ -121,14 +97,11 @@ class _SslClientCertSettingsState extends State { controller: password, obscureText: true, obscuringCharacter: "*", - decoration: InputDecoration( - hintText: "client_cert_enter_password".tr(), - ), + decoration: InputDecoration(hintText: "client_cert_enter_password".tr()), ), actions: [ TextButton( - onPressed: () => - {ctx.pop(), storeCert(context, data, password.text)}, + onPressed: () async => {ctx.pop(), await storeCert(context, data, password.text)}, child: Text("client_cert_dialog_msg_confirm".tr()), ), ], @@ -139,10 +112,7 @@ class _SslClientCertSettingsState extends State { Future importCert(BuildContext ctx) async { FilePickerResult? res = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: [ - 'p12', - 'pfx', - ], + allowedExtensions: ['p12', 'pfx'], ); if (res != null) { File file = File(res.files.single.path!); @@ -151,12 +121,10 @@ class _SslClientCertSettingsState extends State { } } - void removeCert(BuildContext context) { - SSLClientCertStoreVal.delete(); + Future removeCert(BuildContext context) async { + await SSLClientCertStoreVal.delete(); HttpSSLOptions.apply(); - setState( - () => isCertExist = false, - ); + setState(() => isCertExist = false); showMessage(context, "client_cert_remove_msg".tr()); } } diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index a9707eb0d3..cbd6e1f077 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -16,6 +16,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/search/thumbnail_with_info.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class SharedLinkItem extends ConsumerWidget { final SharedLink sharedLink; @@ -33,52 +34,37 @@ class SharedLinkItem extends ConsumerWidget { var expiresText = "shared_link_expires_never".tr(); if (sharedLink.expiresAt != null) { if (isExpired()) { - return Text( - "expired", - style: TextStyle(color: Colors.red[300]), - ).tr(); + return Text("expired", style: TextStyle(color: Colors.red[300])).tr(); } final difference = sharedLink.expiresAt!.difference(DateTime.now()); - debugPrint("Difference: $difference"); + dPrint(() => "Difference: $difference"); if (difference.inDays > 0) { var dayDifference = difference.inDays; if (difference.inHours % 24 > 12) { dayDifference += 1; } - expiresText = "shared_link_expires_days" - .tr(namedArgs: {'count': dayDifference.toString()}); + expiresText = "shared_link_expires_days".tr(namedArgs: {'count': dayDifference.toString()}); } else if (difference.inHours > 0) { - expiresText = "shared_link_expires_hours" - .tr(namedArgs: {'count': difference.inHours.toString()}); + expiresText = "shared_link_expires_hours".tr(namedArgs: {'count': difference.inHours.toString()}); } else if (difference.inMinutes > 0) { - expiresText = "shared_link_expires_minutes" - .tr(namedArgs: {'count': difference.inMinutes.toString()}); + expiresText = "shared_link_expires_minutes".tr(namedArgs: {'count': difference.inMinutes.toString()}); } else if (difference.inSeconds > 0) { - expiresText = "shared_link_expires_seconds" - .tr(namedArgs: {'count': difference.inSeconds.toString()}); + expiresText = "shared_link_expires_seconds".tr(namedArgs: {'count': difference.inSeconds.toString()}); } } - return Text( - expiresText, - style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600]), - ); + return Text(expiresText, style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600])); } @override Widget build(BuildContext context, WidgetRef ref) { final colorScheme = context.colorScheme; final isDarkMode = colorScheme.brightness == Brightness.dark; - final thumbnailUrl = sharedLink.thumbAssetId != null - ? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!) - : null; + final thumbnailUrl = sharedLink.thumbAssetId != null ? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!) : null; final imageSize = math.min(context.width / 4, 100.0); void copyShareLinkToClipboard() { - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); - var serverUrl = - externalDomain.isNotEmpty ? externalDomain : getServerUrl(); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); + var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; } @@ -92,16 +78,12 @@ class SharedLinkItem extends ConsumerWidget { return; } - Clipboard.setData( - ClipboardData(text: "${serverUrl}share/${sharedLink.key}"), - ).then((_) { + Clipboard.setData(ClipboardData(text: "${serverUrl}share/${sharedLink.key}")).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -116,9 +98,7 @@ class SharedLinkItem extends ConsumerWidget { return ConfirmDialog( title: "delete_shared_link_dialog_title", content: "confirm_delete_shared_link", - onOk: () => ref - .read(sharedLinksStateProvider.notifier) - .deleteLink(sharedLink.id), + onOk: () => ref.read(sharedLinksStateProvider.notifier).deleteLink(sharedLink.id), ); }, ); @@ -129,14 +109,9 @@ class SharedLinkItem extends ConsumerWidget { return Container( height: imageSize * 1.2, width: imageSize, - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: isDarkMode ? Colors.grey[800] : Colors.grey[200]), child: Center( - child: Icon( - Icons.image_not_supported_outlined, - color: isDarkMode ? Colors.grey[100] : Colors.grey[700], - ), + child: Icon(Icons.image_not_supported_outlined, color: isDarkMode ? Colors.grey[100] : Colors.grey[700]), ), ); } @@ -169,9 +144,7 @@ class SharedLinkItem extends ConsumerWidget { color: isDarkMode ? Colors.black : Colors.white, ), ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(25)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25))), ), ); } @@ -181,8 +154,7 @@ class SharedLinkItem extends ConsumerWidget { children: [ if (sharedLink.allowUpload) buildInfoChip("upload".tr()), if (sharedLink.allowDownload) buildInfoChip("download".tr()), - if (sharedLink.showMetadata) - buildInfoChip("shared_link_info_chip_metadata".tr()), + if (sharedLink.showMetadata) buildInfoChip("shared_link_info_chip_metadata".tr()), ], ); } @@ -197,8 +169,7 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.delete_outline), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), onPressed: deleteShareLink, ), @@ -208,11 +179,9 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.edit_outlined), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), - onPressed: () => context - .pushRoute(SharedLinkEditRoute(existingLink: sharedLink)), + onPressed: () => context.pushRoute(SharedLinkEditRoute(existingLink: sharedLink)), ), IconButton( splashRadius: 25, @@ -220,8 +189,7 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.copy_outlined), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), onPressed: copyShareLinkToClipboard, ), @@ -242,10 +210,7 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.title, preferBelow: false, triggerMode: TooltipTriggerMode.tap, @@ -270,23 +235,14 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.description ?? "", preferBelow: false, triggerMode: TooltipTriggerMode.tap, - child: Text( - sharedLink.description ?? "", - overflow: TextOverflow.ellipsis, - ), + child: Text(sharedLink.description ?? "", overflow: TextOverflow.ellipsis), ), ), - Padding( - padding: const EdgeInsets.only(right: 15), - child: buildSharedLinkActions(), - ), + Padding(padding: const EdgeInsets.only(right: 15), child: buildSharedLinkActions()), ], ), buildBottomInfo(), @@ -300,24 +256,13 @@ class SharedLinkItem extends ConsumerWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 15), - child: buildThumbnail(), - ), + Padding(padding: const EdgeInsets.only(left: 15), child: buildThumbnail()), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 15), - child: buildSharedLinkDetails(), - ), + child: Padding(padding: const EdgeInsets.only(left: 15), child: buildSharedLinkDetails()), ), ], ), - const Padding( - padding: EdgeInsets.all(20), - child: Divider( - height: 0, - ), - ), + const Padding(padding: EdgeInsets.all(20), child: Divider(height: 0)), ], ); } diff --git a/mobile/makefile b/mobile/makefile index 37d33fa817..b90e95c902 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -1,4 +1,4 @@ -.PHONY: build watch create_app_icon create_splash build_release_android pigeon +.PHONY: build watch create_app_icon create_splash build_release_android pigeon test analyze format build: dart run build_runner build --delete-conflicting-outputs @@ -7,7 +7,15 @@ build: pigeon: dart run pigeon --input pigeon/native_sync_api.dart + dart run pigeon --input pigeon/thumbnail_api.dart + dart run pigeon --input pigeon/background_worker_api.dart + dart run pigeon --input pigeon/background_worker_lock_api.dart + dart run pigeon --input pigeon/connectivity_api.dart dart format lib/platform/native_sync_api.g.dart + dart format lib/platform/thumbnail_api.g.dart + dart format lib/platform/background_worker_api.g.dart + dart format lib/platform/background_worker_lock_api.g.dart + dart format lib/platform/connectivity_api.g.dart watch: dart run build_runner watch --delete-conflicting-outputs @@ -25,10 +33,19 @@ migration: dart run drift_dev make-migrations translation: + npm --prefix ../web run format:i18n dart run easy_localization:generate -S ../i18n dart run bin/generate_keys.dart dart format lib/generated/codegen_loader.g.dart dart format lib/generated/intl_keys.g.dart -build-beta: - flutter build apk --flavor beta --release +analyze: + dart analyze --fatal-infos + dcm analyze lib --fatal-style --fatal-warnings + +format: +# Ignore generated files manually until https://github.com/dart-lang/dart_style/issues/864 is resolved + dart format --set-exit-if-changed $$(find lib -name '*.dart' -not \( -name 'generated_plugin_registrant.dart' -o -name '*.g.dart' -o -name '*.drift.dart' \)) + +test: + flutter test diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index 545955a184..df2c2226b1 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -34,6 +34,7 @@ part 'api/api_keys_api.dart'; part 'api/activities_api.dart'; part 'api/albums_api.dart'; part 'api/assets_api.dart'; +part 'api/auth_admin_api.dart'; part 'api/authentication_api.dart'; part 'api/deprecated_api.dart'; part 'api/download_api.dart'; @@ -78,6 +79,8 @@ part 'model/album_user_add_dto.dart'; part 'model/album_user_create_dto.dart'; part 'model/album_user_response_dto.dart'; part 'model/album_user_role.dart'; +part 'model/albums_add_assets_dto.dart'; +part 'model/albums_add_assets_response_dto.dart'; part 'model/albums_response.dart'; part 'model/albums_update.dart'; part 'model/all_job_status_response_dto.dart'; @@ -103,6 +106,10 @@ part 'model/asset_jobs_dto.dart'; part 'model/asset_media_response_dto.dart'; part 'model/asset_media_size.dart'; part 'model/asset_media_status.dart'; +part 'model/asset_metadata_key.dart'; +part 'model/asset_metadata_response_dto.dart'; +part 'model/asset_metadata_upsert_dto.dart'; +part 'model/asset_metadata_upsert_item_dto.dart'; part 'model/asset_order.dart'; part 'model/asset_response_dto.dart'; part 'model/asset_stack_response_dto.dart'; @@ -112,6 +119,7 @@ part 'model/asset_visibility.dart'; part 'model/audio_codec.dart'; part 'model/auth_status_response_dto.dart'; part 'model/avatar_update.dart'; +part 'model/bulk_id_error_reason.dart'; part 'model/bulk_id_response_dto.dart'; part 'model/bulk_ids_dto.dart'; part 'model/clip_config.dart'; @@ -156,6 +164,7 @@ part 'model/log_level.dart'; part 'model/login_credential_dto.dart'; part 'model/login_response_dto.dart'; part 'model/logout_response_dto.dart'; +part 'model/machine_learning_availability_checks_dto.dart'; part 'model/manual_job_name.dart'; part 'model/map_marker_response_dto.dart'; part 'model/map_reverse_geocode_response_dto.dart'; @@ -182,8 +191,10 @@ part 'model/o_auth_token_endpoint_auth_method.dart'; part 'model/on_this_day_dto.dart'; part 'model/onboarding_dto.dart'; part 'model/onboarding_response_dto.dart'; +part 'model/partner_create_dto.dart'; part 'model/partner_direction.dart'; part 'model/partner_response_dto.dart'; +part 'model/partner_update_dto.dart'; part 'model/people_response.dart'; part 'model/people_response_dto.dart'; part 'model/people_update.dart'; @@ -259,7 +270,10 @@ part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; part 'model/sync_asset_face_delete_v1.dart'; part 'model/sync_asset_face_v1.dart'; +part 'model/sync_asset_metadata_delete_v1.dart'; +part 'model/sync_asset_metadata_v1.dart'; part 'model/sync_asset_v1.dart'; +part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; part 'model/sync_memory_asset_v1.dart'; @@ -329,7 +343,6 @@ part 'model/update_album_dto.dart'; part 'model/update_album_user_dto.dart'; part 'model/update_asset_dto.dart'; part 'model/update_library_dto.dart'; -part 'model/update_partner_dto.dart'; part 'model/usage_by_user_dto.dart'; part 'model/user_admin_create_dto.dart'; part 'model/user_admin_delete_dto.dart'; @@ -337,6 +350,7 @@ part 'model/user_admin_response_dto.dart'; part 'model/user_admin_update_dto.dart'; part 'model/user_avatar_color.dart'; part 'model/user_license.dart'; +part 'model/user_metadata_key.dart'; part 'model/user_preferences_response_dto.dart'; part 'model/user_preferences_update_dto.dart'; part 'model/user_response_dto.dart'; diff --git a/mobile/openapi/lib/api/activities_api.dart b/mobile/openapi/lib/api/activities_api.dart index 5c83ba7db9..67015499fa 100644 --- a/mobile/openapi/lib/api/activities_api.dart +++ b/mobile/openapi/lib/api/activities_api.dart @@ -16,7 +16,10 @@ class ActivitiesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -45,6 +48,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.create` permission. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -63,7 +68,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'DELETE /activities/{id}' operation and returns the [Response]. + /// This endpoint requires the `activity.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class ActivitiesApi { } } - /// Performs an HTTP 'GET /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -154,6 +167,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.read` permission. + /// /// Parameters: /// /// * [String] albumId (required): @@ -183,7 +198,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'GET /activities/statistics' operation and returns the [Response]. + /// This endpoint requires the `activity.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -219,6 +237,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.statistics` permission. + /// /// Parameters: /// /// * [String] albumId (required): diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index cf54ac5c04..3ac829c30c 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -16,7 +16,10 @@ class APIKeysApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -45,6 +48,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.create` permission. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -63,7 +68,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'DELETE /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class APIKeysApi { } } - /// Performs an HTTP 'GET /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -133,6 +146,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,9 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'GET /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApiKeysWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -177,6 +194,7 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. Future?> getApiKeys() async { final response = await getApiKeysWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -195,7 +213,51 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'PUT /api-keys/{id}' operation and returns the [Response]. + /// Performs an HTTP 'GET /api-keys/me' operation and returns the [Response]. + Future getMyApiKeyWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/api-keys/me'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getMyApiKey() async { + final response = await getMyApiKeyWithHttpInfo(); + 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), 'APIKeyResponseDto',) as APIKeyResponseDto; + + } + return null; + } + + /// This endpoint requires the `apiKey.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -227,6 +289,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index db6a2e78a3..063f9ea43b 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -18,7 +18,7 @@ class AssetsApi { /// checkBulkUpload /// - /// Checks if assets exist by checksums + /// Checks if assets exist by checksums. This endpoint requires the `asset.upload` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -52,7 +52,7 @@ class AssetsApi { /// checkBulkUpload /// - /// Checks if assets exist by checksums + /// Checks if assets exist by checksums. This endpoint requires the `asset.upload` permission. /// /// Parameters: /// @@ -128,7 +128,60 @@ class AssetsApi { return null; } - /// Performs an HTTP 'DELETE /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future deleteAssetMetadataWithHttpInfo(String id, AssetMetadataKey key,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata/{key}' + .replaceAll('{id}', id) + .replaceAll('{key}', key.toString()); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.update` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future deleteAssetMetadata(String id, AssetMetadataKey key,) async { + final response = await deleteAssetMetadataWithHttpInfo(id, key,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -157,6 +210,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -167,13 +222,18 @@ class AssetsApi { } } - /// Performs an HTTP 'GET /assets/{id}/original' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future downloadAssetWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future downloadAssetWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -188,6 +248,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -203,13 +266,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future downloadAsset(String id, { String? key, }) async { - final response = await downloadAssetWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future downloadAsset(String id, { String? key, String? slug, }) async { + final response = await downloadAssetWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -283,13 +350,18 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future getAssetInfoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -304,6 +376,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -319,13 +394,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future getAssetInfo(String id, { String? key, }) async { - final response = await getAssetInfoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future getAssetInfo(String id, { String? key, String? slug, }) async { + final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -339,7 +418,124 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + Future getAssetMetadataWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + Future?> getAssetMetadata(String id,) async { + final response = await getAssetMetadataWithHttpInfo(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) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future getAssetMetadataByKeyWithHttpInfo(String id, AssetMetadataKey key,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata/{key}' + .replaceAll('{id}', id) + .replaceAll('{key}', key.toString()); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataKey] key (required): + Future getAssetMetadataByKey(String id, AssetMetadataKey key,) async { + final response = await getAssetMetadataByKeyWithHttpInfo(id, key,); + 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), 'AssetMetadataResponseDto',) as AssetMetadataResponseDto; + + } + return null; + } + + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -382,6 +578,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -404,7 +602,7 @@ class AssetsApi { return null; } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -440,7 +638,7 @@ class AssetsApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// @@ -463,13 +661,18 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/video/playback' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future playAssetVideoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/video/playback' .replaceAll('{id}', id); @@ -484,6 +687,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -499,13 +705,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future playAssetVideo(String id, { String? key, }) async { - final response = await playAssetVideoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future playAssetVideo(String id, { String? key, String? slug, }) async { + final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -519,10 +729,10 @@ class AssetsApi { return null; } - /// replaceAsset - /// /// Replace the asset with new file, without changing its id /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// /// Note: This method returns the HTTP [Response]. /// /// Parameters: @@ -541,10 +751,12 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -559,6 +771,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['multipart/form-data']; @@ -608,10 +823,10 @@ class AssetsApi { ); } - /// replaceAsset - /// /// Replace the asset with new file, without changing its id /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -628,11 +843,13 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { - final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, ); + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -685,7 +902,10 @@ class AssetsApi { } } - /// Performs an HTTP 'PUT /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -717,6 +937,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -737,7 +959,70 @@ class AssetsApi { return null; } - /// Performs an HTTP 'PUT /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): + Future updateAssetMetadataWithHttpInfo(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/metadata' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody = assetMetadataUpsertDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.update` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [AssetMetadataUpsertDto] assetMetadataUpsertDto (required): + Future?> updateAssetMetadata(String id, AssetMetadataUpsertDto assetMetadataUpsertDto,) async { + final response = await updateAssetMetadataWithHttpInfo(id, assetMetadataUpsertDto,); + 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') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -766,6 +1051,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -776,7 +1063,10 @@ class AssetsApi { } } - /// Performs an HTTP 'POST /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.upload` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -789,8 +1079,12 @@ class AssetsApi { /// /// * [DateTime] fileModifiedAt (required): /// + /// * [List] metadata (required): + /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -805,7 +1099,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -819,6 +1113,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (xImmichChecksum != null) { headerParams[r'x-immich-checksum'] = parameterToString(xImmichChecksum); @@ -865,6 +1162,10 @@ class AssetsApi { hasFields = true; mp.fields[r'livePhotoVideoId'] = parameterToString(livePhotoVideoId); } + if (metadata != null) { + hasFields = true; + mp.fields[r'metadata'] = parameterToString(metadata); + } if (sidecarData != null) { hasFields = true; mp.fields[r'sidecarData'] = sidecarData.field; @@ -889,6 +1190,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.upload` permission. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -901,8 +1204,12 @@ class AssetsApi { /// /// * [DateTime] fileModifiedAt (required): /// + /// * [List] metadata (required): + /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -917,8 +1224,8 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, List metadata, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, metadata, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -932,7 +1239,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -940,7 +1250,9 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, }) async { + /// + /// * [String] slug: + Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/thumbnail' .replaceAll('{id}', id); @@ -958,6 +1270,9 @@ class AssetsApi { if (size != null) { queryParams.addAll(_queryParams('', 'size', size)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -973,6 +1288,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -980,8 +1297,10 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAsset(String id, { String? key, AssetMediaSize? size, }) async { - final response = await viewAssetWithHttpInfo(id, key: key, size: size, ); + /// + /// * [String] slug: + Future viewAsset(String id, { String? key, AssetMediaSize? size, String? slug, }) async { + final response = await viewAssetWithHttpInfo(id, key: key, size: size, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/auth_admin_api.dart b/mobile/openapi/lib/api/auth_admin_api.dart new file mode 100644 index 0000000000..d22b449aab --- /dev/null +++ b/mobile/openapi/lib/api/auth_admin_api.dart @@ -0,0 +1,54 @@ +// +// 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 AuthAdminApi { + AuthAdminApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; + + final ApiClient apiClient; + + /// This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + /// + /// Note: This method returns the HTTP [Response]. + Future unlinkAllOAuthAccountsAdminWithHttpInfo() async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/auth/unlink-all'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + Future unlinkAllOAuthAccountsAdmin() async { + final response = await unlinkAllOAuthAccountsAdminWithHttpInfo(); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } +} diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index 5482a9fc51..a74af33a43 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -16,7 +16,10 @@ class AuthenticationApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response]. + /// This endpoint requires the `auth.changePassword` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -45,6 +48,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `auth.changePassword` permission. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -63,7 +68,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'PUT /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -92,6 +100,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.update` permission. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -264,7 +274,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -293,6 +306,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.delete` permission. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -303,7 +318,10 @@ class AuthenticationApi { } } - /// Performs an HTTP 'POST /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): @@ -332,6 +350,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.create` permission. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index 7aa9662c23..9246998ca2 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -16,7 +16,60 @@ class DeprecatedApi { final ApiClient apiClient; - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.141.0. This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + Future createPartnerDeprecatedWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/partners/{id}' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This property was deprecated in v1.141.0. This endpoint requires the `partner.create` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + Future createPartnerDeprecated(String id,) async { + final response = await createPartnerDeprecatedWithHttpInfo(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), 'PartnerResponseDto',) as PartnerResponseDto; + + } + return null; + } + + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -52,7 +105,7 @@ class DeprecatedApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// @@ -74,4 +127,138 @@ class DeprecatedApi { } return null; } + + /// Replace the asset with new file, without changing its id + /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [MultipartFile] assetData (required): + /// + /// * [String] deviceAssetId (required): + /// + /// * [String] deviceId (required): + /// + /// * [DateTime] fileCreatedAt (required): + /// + /// * [DateTime] fileModifiedAt (required): + /// + /// * [String] key: + /// + /// * [String] slug: + /// + /// * [String] duration: + /// + /// * [String] filename: + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/assets/{id}/original' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } + + const contentTypes = ['multipart/form-data']; + + bool hasFields = false; + final mp = MultipartRequest('PUT', Uri.parse(apiPath)); + if (assetData != null) { + hasFields = true; + mp.fields[r'assetData'] = assetData.field; + mp.files.add(assetData); + } + if (deviceAssetId != null) { + hasFields = true; + mp.fields[r'deviceAssetId'] = parameterToString(deviceAssetId); + } + if (deviceId != null) { + hasFields = true; + mp.fields[r'deviceId'] = parameterToString(deviceId); + } + if (duration != null) { + hasFields = true; + mp.fields[r'duration'] = parameterToString(duration); + } + if (fileCreatedAt != null) { + hasFields = true; + mp.fields[r'fileCreatedAt'] = parameterToString(fileCreatedAt); + } + if (fileModifiedAt != null) { + hasFields = true; + mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt); + } + if (filename != null) { + hasFields = true; + mp.fields[r'filename'] = parameterToString(filename); + } + if (hasFields) { + postBody = mp; + } + + return apiClient.invokeAPI( + apiPath, + 'PUT', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Replace the asset with new file, without changing its id + /// + /// This property was deprecated in v1.142.0. Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + /// + /// * [MultipartFile] assetData (required): + /// + /// * [String] deviceAssetId (required): + /// + /// * [String] deviceId (required): + /// + /// * [DateTime] fileCreatedAt (required): + /// + /// * [DateTime] fileModifiedAt (required): + /// + /// * [String] key: + /// + /// * [String] slug: + /// + /// * [String] duration: + /// + /// * [String] filename: + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); + 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), 'AssetMediaResponseDto',) as AssetMediaResponseDto; + + } + return null; + } } diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 3b11c2f630..62c97bfc9c 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -16,13 +16,18 @@ class DownloadApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /download/archive' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/archive'; @@ -36,6 +41,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -51,13 +59,17 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -71,13 +83,18 @@ class DownloadApi { return null; } - /// Performs an HTTP 'POST /download/info' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { + /// + /// * [String] slug: + Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/info'; @@ -91,6 +108,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -106,13 +126,17 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { - final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, ); + /// + /// * [String] slug: + Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { + final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/duplicates_api.dart b/mobile/openapi/lib/api/duplicates_api.dart index d8b45d21a2..9df6e46586 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -16,7 +16,10 @@ class DuplicatesApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class DuplicatesApi { } } - /// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -85,6 +93,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -95,7 +105,9 @@ class DuplicatesApi { } } - /// Performs an HTTP 'GET /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetDuplicatesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -121,6 +133,7 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.read` permission. Future?> getAssetDuplicates() async { final response = await getAssetDuplicatesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/faces_api.dart b/mobile/openapi/lib/api/faces_api.dart index 44e3d53f8e..2f8e6be60d 100644 --- a/mobile/openapi/lib/api/faces_api.dart +++ b/mobile/openapi/lib/api/faces_api.dart @@ -16,7 +16,10 @@ class FacesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /faces' operation and returns the [Response]. + /// This endpoint requires the `face.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -45,6 +48,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.create` permission. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -55,7 +60,10 @@ class FacesApi { } } - /// Performs an HTTP 'DELETE /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -87,6 +95,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -99,7 +109,10 @@ class FacesApi { } } - /// Performs an HTTP 'GET /faces' operation and returns the [Response]. + /// This endpoint requires the `face.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -130,6 +143,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,10 @@ class FacesApi { return null; } - /// Performs an HTTP 'PUT /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -183,6 +201,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/jobs_api.dart b/mobile/openapi/lib/api/jobs_api.dart index 182bb14e4f..4c935828a0 100644 --- a/mobile/openapi/lib/api/jobs_api.dart +++ b/mobile/openapi/lib/api/jobs_api.dart @@ -16,7 +16,10 @@ class JobsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -45,6 +48,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -55,7 +60,9 @@ class JobsApi { } } - /// Performs an HTTP 'GET /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllJobsStatusWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -81,6 +88,7 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.read` permission. Future getAllJobsStatus() async { final response = await getAllJobsStatusWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class JobsApi { return null; } - /// Performs an HTTP 'PUT /jobs/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobName] id (required): @@ -128,6 +139,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobName] id (required): diff --git a/mobile/openapi/lib/api/libraries_api.dart b/mobile/openapi/lib/api/libraries_api.dart index 86acce76b4..9258f8e3eb 100644 --- a/mobile/openapi/lib/api/libraries_api.dart +++ b/mobile/openapi/lib/api/libraries_api.dart @@ -16,7 +16,10 @@ class LibrariesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -45,6 +48,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -63,7 +68,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'DELETE /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,9 @@ class LibrariesApi { } } - /// Performs an HTTP 'GET /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllLibrariesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -129,6 +141,7 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. Future?> getAllLibraries() async { final response = await getAllLibrariesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -147,7 +160,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -177,6 +193,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -195,7 +213,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -225,6 +246,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -243,7 +266,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'POST /libraries/{id}/scan' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -273,6 +299,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -283,7 +311,10 @@ class LibrariesApi { } } - /// Performs an HTTP 'PUT /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -315,6 +346,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/map_api.dart b/mobile/openapi/lib/api/map_api.dart index ffe72df453..da4f3dffcc 100644 --- a/mobile/openapi/lib/api/map_api.dart +++ b/mobile/openapi/lib/api/map_api.dart @@ -19,18 +19,18 @@ class MapApi { /// Performs an HTTP 'GET /map/markers' operation and returns the [Response]. /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { + Future getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/markers'; @@ -41,18 +41,18 @@ class MapApi { final headerParams = {}; final formParams = {}; - if (fileCreatedAfter != null) { - queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); - } - if (fileCreatedBefore != null) { - queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); - } if (isArchived != null) { queryParams.addAll(_queryParams('', 'isArchived', isArchived)); } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } + if (fileCreatedAfter != null) { + queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); + } + if (fileCreatedBefore != null) { + queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -76,19 +76,19 @@ class MapApi { /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { - final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); + Future?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { + final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/memories_api.dart b/mobile/openapi/lib/api/memories_api.dart index 9b62cce9c0..f9280101e6 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -16,7 +16,10 @@ class MemoriesApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -48,6 +51,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -71,7 +76,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'POST /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -100,6 +108,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.create` permission. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -118,7 +128,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -148,6 +161,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -158,7 +173,10 @@ class MemoriesApi { } } - /// Performs an HTTP 'GET /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -206,7 +226,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories/statistics' operation and returns the [Response]. + /// This endpoint requires the `memory.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -254,6 +277,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.statistics` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -278,7 +303,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -310,6 +338,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -333,7 +363,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -381,6 +414,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -408,7 +443,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'PUT /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -440,6 +478,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 501cc70a29..1d276efaaf 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,7 +16,10 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -85,6 +93,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -95,7 +105,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -125,6 +138,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -143,7 +158,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -191,6 +209,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -218,7 +238,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -250,6 +273,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -270,7 +295,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): @@ -299,6 +327,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): diff --git a/mobile/openapi/lib/api/partners_api.dart b/mobile/openapi/lib/api/partners_api.dart index 9f10ea4d1e..a5fdf53ab5 100644 --- a/mobile/openapi/lib/api/partners_api.dart +++ b/mobile/openapi/lib/api/partners_api.dart @@ -16,11 +16,66 @@ class PartnersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [PartnerCreateDto] partnerCreateDto (required): + Future createPartnerWithHttpInfo(PartnerCreateDto partnerCreateDto,) async { + // ignore: prefer_const_declarations + final apiPath = r'/partners'; + + // ignore: prefer_final_locals + Object? postBody = partnerCreateDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `partner.create` permission. + /// + /// Parameters: + /// + /// * [PartnerCreateDto] partnerCreateDto (required): + Future createPartner(PartnerCreateDto partnerCreateDto,) async { + final response = await createPartnerWithHttpInfo(partnerCreateDto,); + 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), 'PartnerResponseDto',) as PartnerResponseDto; + + } + return null; + } + + /// This property was deprecated in v1.141.0. This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): - Future createPartnerWithHttpInfo(String id,) async { + Future createPartnerDeprecatedWithHttpInfo(String id,) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); @@ -46,11 +101,13 @@ class PartnersApi { ); } + /// This property was deprecated in v1.141.0. This endpoint requires the `partner.create` permission. + /// /// Parameters: /// /// * [String] id (required): - Future createPartner(String id,) async { - final response = await createPartnerWithHttpInfo(id,); + Future createPartnerDeprecated(String id,) async { + final response = await createPartnerDeprecatedWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -64,7 +121,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'GET /partners' operation and returns the [Response]. + /// This endpoint requires the `partner.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -95,6 +155,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.read` permission. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -116,7 +178,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'DELETE /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -146,6 +211,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -156,19 +223,22 @@ class PartnersApi { } } - /// Performs an HTTP 'PUT /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// - /// * [UpdatePartnerDto] updatePartnerDto (required): - Future updatePartnerWithHttpInfo(String id, UpdatePartnerDto updatePartnerDto,) async { + /// * [PartnerUpdateDto] partnerUpdateDto (required): + Future updatePartnerWithHttpInfo(String id, PartnerUpdateDto partnerUpdateDto,) async { // ignore: prefer_const_declarations final apiPath = r'/partners/{id}' .replaceAll('{id}', id); // ignore: prefer_final_locals - Object? postBody = updatePartnerDto; + Object? postBody = partnerUpdateDto; final queryParams = []; final headerParams = {}; @@ -188,13 +258,15 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.update` permission. + /// /// Parameters: /// /// * [String] id (required): /// - /// * [UpdatePartnerDto] updatePartnerDto (required): - Future updatePartner(String id, UpdatePartnerDto updatePartnerDto,) async { - final response = await updatePartnerWithHttpInfo(id, updatePartnerDto,); + /// * [PartnerUpdateDto] partnerUpdateDto (required): + Future updatePartner(String id, PartnerUpdateDto partnerUpdateDto,) async { + final response = await updatePartnerWithHttpInfo(id, partnerUpdateDto,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/people_api.dart b/mobile/openapi/lib/api/people_api.dart index 35dbac4e97..68c16785cc 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -16,7 +16,10 @@ class PeopleApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /people' operation and returns the [Response]. + /// This endpoint requires the `person.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -45,6 +48,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.create` permission. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -63,7 +68,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'DELETE /people' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -92,6 +100,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -102,7 +112,10 @@ class PeopleApi { } } - /// Performs an HTTP 'DELETE /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -132,6 +145,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -142,7 +157,10 @@ class PeopleApi { } } - /// Performs an HTTP 'GET /people' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -197,6 +215,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -225,7 +245,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -255,6 +278,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -273,7 +298,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/statistics' operation and returns the [Response]. + /// This endpoint requires the `person.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -303,6 +331,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -321,7 +351,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -351,6 +384,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -369,7 +404,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'POST /people/{id}/merge' operation and returns the [Response]. + /// This endpoint requires the `person.merge` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -401,6 +439,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.merge` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -424,7 +464,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}/reassign' operation and returns the [Response]. + /// This endpoint requires the `person.reassign` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -456,6 +499,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.reassign` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -479,7 +524,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -508,6 +556,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -529,7 +579,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -561,6 +614,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 5c7a8de59d..4d9e1172b8 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -16,7 +16,9 @@ class SearchApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /search/cities' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetsByCityWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/cities'; @@ -42,6 +44,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getAssetsByCity() async { final response = await getAssetsByCityWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -60,7 +63,9 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/explore' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getExploreDataWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/explore'; @@ -86,6 +91,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getExploreData() async { final response = await getExploreDataWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -104,7 +110,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/suggestions' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -161,6 +170,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -193,7 +204,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -222,6 +236,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -240,7 +256,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/metadata' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -269,6 +288,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -287,7 +308,279 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/search/large-assets'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (albumIds != null) { + queryParams.addAll(_queryParams('multi', 'albumIds', albumIds)); + } + if (city != null) { + queryParams.addAll(_queryParams('', 'city', city)); + } + if (country != null) { + queryParams.addAll(_queryParams('', 'country', country)); + } + if (createdAfter != null) { + queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); + } + if (createdBefore != null) { + queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); + } + if (deviceId != null) { + queryParams.addAll(_queryParams('', 'deviceId', deviceId)); + } + if (isEncoded != null) { + queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); + } + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isMotion != null) { + queryParams.addAll(_queryParams('', 'isMotion', isMotion)); + } + if (isNotInAlbum != null) { + queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum)); + } + if (isOffline != null) { + queryParams.addAll(_queryParams('', 'isOffline', isOffline)); + } + if (lensModel != null) { + queryParams.addAll(_queryParams('', 'lensModel', lensModel)); + } + if (libraryId != null) { + queryParams.addAll(_queryParams('', 'libraryId', libraryId)); + } + if (make != null) { + queryParams.addAll(_queryParams('', 'make', make)); + } + if (minFileSize != null) { + queryParams.addAll(_queryParams('', 'minFileSize', minFileSize)); + } + if (model != null) { + queryParams.addAll(_queryParams('', 'model', model)); + } + if (personIds != null) { + queryParams.addAll(_queryParams('multi', 'personIds', personIds)); + } + if (rating != null) { + queryParams.addAll(_queryParams('', 'rating', rating)); + } + if (size != null) { + queryParams.addAll(_queryParams('', 'size', size)); + } + if (state != null) { + queryParams.addAll(_queryParams('', 'state', state)); + } + if (tagIds != null) { + queryParams.addAll(_queryParams('multi', 'tagIds', tagIds)); + } + if (takenAfter != null) { + queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); + } + if (takenBefore != null) { + queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); + } + if (trashedAfter != null) { + queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); + } + if (trashedBefore != null) { + queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (updatedAfter != null) { + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + } + if (updatedBefore != null) { + queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); + } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } + if (withDeleted != null) { + queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); + } + if (withExif != null) { + queryParams.addAll(_queryParams('', 'withExif', withExif)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + final response = await searchLargeAssetsWithHttpInfo( albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, ); + 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') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -323,6 +616,8 @@ class SearchApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -346,7 +641,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/places' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -377,6 +675,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -398,7 +698,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/random' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -427,6 +730,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -448,7 +753,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/smart' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): @@ -477,6 +785,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 7abdabcd3e..9fa8f2016d 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -16,7 +16,9 @@ class ServerApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -42,6 +44,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. Future deleteServerLicense() async { final response = await deleteServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -49,7 +52,9 @@ class ServerApi { } } - /// Performs an HTTP 'GET /server/about' operation and returns the [Response]. + /// This endpoint requires the `server.about` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAboutInfoWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/about'; @@ -75,6 +80,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.about` permission. Future getAboutInfo() async { final response = await getAboutInfoWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -90,7 +96,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/apk-links' operation and returns the [Response]. + /// This endpoint requires the `server.apkLinks` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApkLinksWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/apk-links'; @@ -116,6 +124,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.apkLinks` permission. Future getApkLinks() async { final response = await getApkLinksWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +222,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -239,6 +250,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. Future getServerLicense() async { final response = await getServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +266,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/statistics'; @@ -280,6 +294,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. Future getServerStatistics() async { final response = await getServerStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +351,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/storage' operation and returns the [Response]. + /// This endpoint requires the `server.storage` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/storage'; @@ -362,6 +379,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.storage` permission. Future getStorage() async { final response = await getStorageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -459,7 +477,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/version-check' operation and returns the [Response]. + /// This endpoint requires the `server.versionCheck` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/version-check'; @@ -485,6 +505,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.versionCheck` permission. Future getVersionCheck() async { final response = await getVersionCheckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -585,7 +606,10 @@ class ServerApi { return null; } - /// Performs an HTTP 'PUT /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -614,6 +638,8 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index d54f520641..63528d17a7 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -16,7 +16,10 @@ class SessionsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -45,6 +48,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.create` permission. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -63,7 +68,9 @@ class SessionsApi { return null; } - /// Performs an HTTP 'DELETE /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteAllSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -89,6 +96,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. Future deleteAllSessions() async { final response = await deleteAllSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class SessionsApi { } } - /// Performs an HTTP 'DELETE /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -126,6 +137,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -136,7 +149,9 @@ class SessionsApi { } } - /// Performs an HTTP 'GET /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -162,6 +177,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.read` permission. Future?> getSessions() async { final response = await getSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -180,7 +196,10 @@ class SessionsApi { return null; } - /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. + /// This endpoint requires the `session.lock` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -210,6 +229,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.lock` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -220,7 +241,10 @@ class SessionsApi { } } - /// Performs an HTTP 'PUT /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -252,6 +276,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index 5bac8988dc..e32c566754 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -24,7 +24,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -79,7 +86,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'POST /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -108,6 +118,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.create` permission. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -126,7 +138,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -159,6 +174,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -183,12 +200,14 @@ class SharedLinksApi { /// Performs an HTTP 'GET /shared-links/me' operation and returns the [Response]. /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async { + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -199,15 +218,18 @@ class SharedLinksApi { final headerParams = {}; final formParams = {}; - if (key != null) { - queryParams.addAll(_queryParams('', 'key', key)); - } if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -225,13 +247,15 @@ class SharedLinksApi { /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, token: token, ); + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLink({ String? password, String? token, String? key, String? slug, }) async { + final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -245,7 +269,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -275,6 +302,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -293,7 +322,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'DELETE /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -323,6 +355,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -341,7 +375,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -356,6 +392,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -378,8 +417,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -396,7 +437,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'PATCH /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -428,6 +472,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 84f23ec55d..0f76f3396b 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -16,7 +16,10 @@ class StacksApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -45,6 +48,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.create` permission. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -63,7 +68,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class StacksApi { } } - /// Performs an HTTP 'DELETE /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -132,6 +145,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -142,7 +157,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -172,6 +190,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -190,7 +210,60 @@ class StacksApi { return null; } - /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStackWithHttpInfo(String assetId, String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/stacks/{id}/assets/{assetId}' + .replaceAll('{assetId}', assetId) + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `stack.update` permission. + /// + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStack(String assetId, String id,) async { + final response = await removeAssetFromStackWithHttpInfo(assetId, id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -223,6 +296,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -244,7 +319,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'PUT /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -276,6 +354,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart index fe2876ddd8..9e594d6ace 100644 --- a/mobile/openapi/lib/api/sync_api.dart +++ b/mobile/openapi/lib/api/sync_api.dart @@ -16,7 +16,10 @@ class SyncApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -45,6 +48,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -152,7 +157,9 @@ class SyncApi { return null; } - /// Performs an HTTP 'GET /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSyncAckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -178,6 +185,7 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.read` permission. Future?> getSyncAck() async { final response = await getSyncAckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -196,7 +204,10 @@ class SyncApi { return null; } - /// Performs an HTTP 'POST /sync/stream' operation and returns the [Response]. + /// This endpoint requires the `sync.stream` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -225,6 +236,8 @@ class SyncApi { ); } + /// This endpoint requires the `sync.stream` permission. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -235,7 +248,10 @@ class SyncApi { } } - /// Performs an HTTP 'POST /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): @@ -264,6 +280,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.update` permission. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): diff --git a/mobile/openapi/lib/api/system_config_api.dart b/mobile/openapi/lib/api/system_config_api.dart index a03b9d3e72..2ab3879b8a 100644 --- a/mobile/openapi/lib/api/system_config_api.dart +++ b/mobile/openapi/lib/api/system_config_api.dart @@ -16,7 +16,9 @@ class SystemConfigApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -42,6 +44,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfig() async { final response = await getConfigWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/defaults' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigDefaultsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/defaults'; @@ -83,6 +88,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfigDefaults() async { final response = await getConfigDefaultsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/storage-template-options' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageTemplateOptionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/storage-template-options'; @@ -124,6 +132,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getStorageTemplateOptions() async { final response = await getStorageTemplateOptionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'PUT /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): @@ -168,6 +180,8 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 3fcceb8e42..f6b9bad1d6 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -16,7 +16,9 @@ class SystemMetadataApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAdminOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -42,6 +44,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getAdminOnboarding() async { final response = await getAdminOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/reverse-geocoding-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getReverseGeocodingStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/reverse-geocoding-state'; @@ -83,6 +88,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getReverseGeocodingState() async { final response = await getReverseGeocodingStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/version-check-state'; @@ -124,6 +132,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getVersionCheckState() async { final response = await getVersionCheckStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): @@ -168,6 +180,8 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index f6cfc8720b..a0cdb91acf 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -16,7 +16,10 @@ class TagsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /tags/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -45,6 +48,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -63,7 +68,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'POST /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -92,6 +100,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -110,7 +120,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -140,6 +153,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -150,7 +165,9 @@ class TagsApi { } } - /// Performs an HTTP 'GET /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllTagsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -176,6 +193,7 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. Future?> getAllTags() async { final response = await getAllTagsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -194,7 +212,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -224,6 +245,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -242,7 +265,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -274,6 +300,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -297,7 +325,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -329,6 +360,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -352,7 +385,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -384,6 +420,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -404,7 +442,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): @@ -433,6 +474,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 042bc70401..70ac076c9d 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -16,7 +16,10 @@ class TimelineApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /timeline/bucket' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -39,6 +42,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -48,12 +53,15 @@ class TimelineApi { /// * [AssetVisibility] visibility: /// Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED) /// + /// * [bool] withCoordinates: + /// Include location data in the response + /// /// * [bool] withPartners: /// Include assets shared by partners /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -82,6 +90,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -92,6 +103,9 @@ class TimelineApi { if (visibility != null) { queryParams.addAll(_queryParams('', 'visibility', visibility)); } + if (withCoordinates != null) { + queryParams.addAll(_queryParams('', 'withCoordinates', withCoordinates)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -113,6 +127,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -135,6 +151,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -144,13 +162,16 @@ class TimelineApi { /// * [AssetVisibility] visibility: /// Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED) /// + /// * [bool] withCoordinates: + /// Include location data in the response + /// /// * [bool] withPartners: /// Include assets shared by partners /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -164,7 +185,10 @@ class TimelineApi { return null; } - /// Performs an HTTP 'GET /timeline/buckets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -184,6 +208,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -193,12 +219,15 @@ class TimelineApi { /// * [AssetVisibility] visibility: /// Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED) /// + /// * [bool] withCoordinates: + /// Include location data in the response + /// /// * [bool] withPartners: /// Include assets shared by partners /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -227,6 +256,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -236,6 +268,9 @@ class TimelineApi { if (visibility != null) { queryParams.addAll(_queryParams('', 'visibility', visibility)); } + if (withCoordinates != null) { + queryParams.addAll(_queryParams('', 'withCoordinates', withCoordinates)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -257,6 +292,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -276,6 +313,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -285,13 +324,16 @@ class TimelineApi { /// * [AssetVisibility] visibility: /// Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED) /// + /// * [bool] withCoordinates: + /// Include location data in the response + /// /// * [bool] withPartners: /// Include assets shared by partners /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withCoordinates, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withCoordinates: withCoordinates, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/trash_api.dart b/mobile/openapi/lib/api/trash_api.dart index 982dbcbeda..480d19960a 100644 --- a/mobile/openapi/lib/api/trash_api.dart +++ b/mobile/openapi/lib/api/trash_api.dart @@ -16,7 +16,9 @@ class TrashApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /trash/empty' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future emptyTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/empty'; @@ -42,6 +44,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future emptyTrash() async { final response = await emptyTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,10 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore/assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -86,6 +92,8 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -104,7 +112,9 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future restoreTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore'; @@ -130,6 +140,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future restoreTrash() async { final response = await restoreTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index 58263504ce..e4fc1673ef 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -16,7 +16,10 @@ class UsersAdminApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -45,6 +48,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -63,7 +68,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -95,6 +103,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -115,7 +125,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -145,6 +158,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -163,7 +178,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -193,6 +211,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -211,7 +231,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -257,6 +280,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -281,7 +306,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -311,6 +339,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -329,7 +359,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -367,6 +400,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -390,7 +425,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -422,6 +460,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -442,7 +482,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -474,6 +517,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/users_api.dart b/mobile/openapi/lib/api/users_api.dart index cd31617e74..c8891ba0c2 100644 --- a/mobile/openapi/lib/api/users_api.dart +++ b/mobile/openapi/lib/api/users_api.dart @@ -16,7 +16,10 @@ class UsersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -55,6 +58,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.update` permission. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -73,7 +78,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'DELETE /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteProfileImageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -99,6 +106,7 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.delete` permission. Future deleteProfileImage() async { final response = await deleteProfileImageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -106,7 +114,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -132,6 +142,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.delete` permission. Future deleteUserLicense() async { final response = await deleteUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +150,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -165,6 +178,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.delete` permission. Future deleteUserOnboarding() async { final response = await deleteUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -172,7 +186,9 @@ class UsersApi { } } - /// Performs an HTTP 'GET /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyPreferencesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -198,6 +214,7 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.read` permission. Future getMyPreferences() async { final response = await getMyPreferencesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +230,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyUserWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -239,6 +258,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future getMyUser() async { final response = await getMyUserWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +274,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -284,6 +307,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -302,7 +327,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -332,6 +360,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -350,7 +380,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -376,6 +408,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.read` permission. Future getUserLicense() async { final response = await getUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -391,7 +424,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -417,6 +452,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.read` permission. Future getUserOnboarding() async { final response = await getUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -432,7 +468,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future searchUsersWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users'; @@ -458,6 +496,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future?> searchUsers() async { final response = await searchUsersWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -476,7 +515,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -505,6 +547,8 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -523,7 +567,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -552,6 +599,8 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.update` permission. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -570,7 +619,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -599,6 +651,8 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.update` permission. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -617,7 +671,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): @@ -646,6 +703,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.update` permission. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 55d6f4108b..06d27593c9 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -212,6 +212,10 @@ class ApiClient { return AlbumUserResponseDto.fromJson(value); case 'AlbumUserRole': return AlbumUserRoleTypeTransformer().decode(value); + case 'AlbumsAddAssetsDto': + return AlbumsAddAssetsDto.fromJson(value); + case 'AlbumsAddAssetsResponseDto': + return AlbumsAddAssetsResponseDto.fromJson(value); case 'AlbumsResponse': return AlbumsResponse.fromJson(value); case 'AlbumsUpdate': @@ -262,6 +266,14 @@ class ApiClient { return AssetMediaSizeTypeTransformer().decode(value); case 'AssetMediaStatus': return AssetMediaStatusTypeTransformer().decode(value); + case 'AssetMetadataKey': + return AssetMetadataKeyTypeTransformer().decode(value); + case 'AssetMetadataResponseDto': + return AssetMetadataResponseDto.fromJson(value); + case 'AssetMetadataUpsertDto': + return AssetMetadataUpsertDto.fromJson(value); + case 'AssetMetadataUpsertItemDto': + return AssetMetadataUpsertItemDto.fromJson(value); case 'AssetOrder': return AssetOrderTypeTransformer().decode(value); case 'AssetResponseDto': @@ -280,6 +292,8 @@ class ApiClient { return AuthStatusResponseDto.fromJson(value); case 'AvatarUpdate': return AvatarUpdate.fromJson(value); + case 'BulkIdErrorReason': + return BulkIdErrorReasonTypeTransformer().decode(value); case 'BulkIdResponseDto': return BulkIdResponseDto.fromJson(value); case 'BulkIdsDto': @@ -368,6 +382,8 @@ class ApiClient { return LoginResponseDto.fromJson(value); case 'LogoutResponseDto': return LogoutResponseDto.fromJson(value); + case 'MachineLearningAvailabilityChecksDto': + return MachineLearningAvailabilityChecksDto.fromJson(value); case 'ManualJobName': return ManualJobNameTypeTransformer().decode(value); case 'MapMarkerResponseDto': @@ -420,10 +436,14 @@ class ApiClient { return OnboardingDto.fromJson(value); case 'OnboardingResponseDto': return OnboardingResponseDto.fromJson(value); + case 'PartnerCreateDto': + return PartnerCreateDto.fromJson(value); case 'PartnerDirection': return PartnerDirectionTypeTransformer().decode(value); case 'PartnerResponseDto': return PartnerResponseDto.fromJson(value); + case 'PartnerUpdateDto': + return PartnerUpdateDto.fromJson(value); case 'PeopleResponse': return PeopleResponse.fromJson(value); case 'PeopleResponseDto': @@ -574,8 +594,14 @@ class ApiClient { return SyncAssetFaceDeleteV1.fromJson(value); case 'SyncAssetFaceV1': return SyncAssetFaceV1.fromJson(value); + case 'SyncAssetMetadataDeleteV1': + return SyncAssetMetadataDeleteV1.fromJson(value); + case 'SyncAssetMetadataV1': + return SyncAssetMetadataV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); + case 'SyncAuthUserV1': + return SyncAuthUserV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); case 'SyncMemoryAssetDeleteV1': @@ -714,8 +740,6 @@ class ApiClient { return UpdateAssetDto.fromJson(value); case 'UpdateLibraryDto': return UpdateLibraryDto.fromJson(value); - case 'UpdatePartnerDto': - return UpdatePartnerDto.fromJson(value); case 'UsageByUserDto': return UsageByUserDto.fromJson(value); case 'UserAdminCreateDto': @@ -730,6 +754,8 @@ class ApiClient { return UserAvatarColorTypeTransformer().decode(value); case 'UserLicense': return UserLicense.fromJson(value); + case 'UserMetadataKey': + return UserMetadataKeyTypeTransformer().decode(value); case 'UserPreferencesResponseDto': return UserPreferencesResponseDto.fromJson(value); case 'UserPreferencesUpdateDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 1618f4a670..b34e9210c8 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -67,6 +67,9 @@ String parameterToString(dynamic value) { if (value is AssetMediaStatus) { return AssetMediaStatusTypeTransformer().encode(value).toString(); } + if (value is AssetMetadataKey) { + return AssetMetadataKeyTypeTransformer().encode(value).toString(); + } if (value is AssetOrder) { return AssetOrderTypeTransformer().encode(value).toString(); } @@ -79,6 +82,9 @@ String parameterToString(dynamic value) { if (value is AudioCodec) { return AudioCodecTypeTransformer().encode(value).toString(); } + if (value is BulkIdErrorReason) { + return BulkIdErrorReasonTypeTransformer().encode(value).toString(); + } if (value is CQMode) { return CQModeTypeTransformer().encode(value).toString(); } @@ -151,6 +157,9 @@ String parameterToString(dynamic value) { if (value is UserAvatarColor) { return UserAvatarColorTypeTransformer().encode(value).toString(); } + if (value is UserMetadataKey) { + return UserMetadataKeyTypeTransformer().encode(value).toString(); + } if (value is UserStatus) { return UserStatusTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/albums_add_assets_dto.dart b/mobile/openapi/lib/model/albums_add_assets_dto.dart new file mode 100644 index 0000000000..bdbf68980c --- /dev/null +++ b/mobile/openapi/lib/model/albums_add_assets_dto.dart @@ -0,0 +1,111 @@ +// +// 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 AlbumsAddAssetsDto { + /// Returns a new [AlbumsAddAssetsDto] instance. + AlbumsAddAssetsDto({ + this.albumIds = const [], + this.assetIds = const [], + }); + + List albumIds; + + List assetIds; + + @override + bool operator ==(Object other) => identical(this, other) || other is AlbumsAddAssetsDto && + _deepEquality.equals(other.albumIds, albumIds) && + _deepEquality.equals(other.assetIds, assetIds); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (albumIds.hashCode) + + (assetIds.hashCode); + + @override + String toString() => 'AlbumsAddAssetsDto[albumIds=$albumIds, assetIds=$assetIds]'; + + Map toJson() { + final json = {}; + json[r'albumIds'] = this.albumIds; + json[r'assetIds'] = this.assetIds; + return json; + } + + /// Returns a new [AlbumsAddAssetsDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AlbumsAddAssetsDto? fromJson(dynamic value) { + upgradeDto(value, "AlbumsAddAssetsDto"); + if (value is Map) { + final json = value.cast(); + + return AlbumsAddAssetsDto( + albumIds: json[r'albumIds'] is Iterable + ? (json[r'albumIds'] as Iterable).cast().toList(growable: false) + : const [], + assetIds: json[r'assetIds'] is Iterable + ? (json[r'assetIds'] as Iterable).cast().toList(growable: false) + : const [], + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AlbumsAddAssetsDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AlbumsAddAssetsDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AlbumsAddAssetsDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AlbumsAddAssetsDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'albumIds', + 'assetIds', + }; +} + diff --git a/mobile/openapi/lib/model/albums_add_assets_response_dto.dart b/mobile/openapi/lib/model/albums_add_assets_response_dto.dart new file mode 100644 index 0000000000..4ad2c5e150 --- /dev/null +++ b/mobile/openapi/lib/model/albums_add_assets_response_dto.dart @@ -0,0 +1,116 @@ +// +// 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 AlbumsAddAssetsResponseDto { + /// Returns a new [AlbumsAddAssetsResponseDto] instance. + AlbumsAddAssetsResponseDto({ + this.error, + required this.success, + }); + + /// + /// 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. + /// + BulkIdErrorReason? error; + + bool success; + + @override + bool operator ==(Object other) => identical(this, other) || other is AlbumsAddAssetsResponseDto && + other.error == error && + other.success == success; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (error == null ? 0 : error!.hashCode) + + (success.hashCode); + + @override + String toString() => 'AlbumsAddAssetsResponseDto[error=$error, success=$success]'; + + Map toJson() { + final json = {}; + if (this.error != null) { + json[r'error'] = this.error; + } else { + // json[r'error'] = null; + } + json[r'success'] = this.success; + return json; + } + + /// Returns a new [AlbumsAddAssetsResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AlbumsAddAssetsResponseDto? fromJson(dynamic value) { + upgradeDto(value, "AlbumsAddAssetsResponseDto"); + if (value is Map) { + final json = value.cast(); + + return AlbumsAddAssetsResponseDto( + error: BulkIdErrorReason.fromJson(json[r'error']), + success: mapValueOfType(json, r'success')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AlbumsAddAssetsResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AlbumsAddAssetsResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AlbumsAddAssetsResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AlbumsAddAssetsResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'success', + }; +} + diff --git a/mobile/openapi/lib/model/asset_bulk_update_dto.dart b/mobile/openapi/lib/model/asset_bulk_update_dto.dart index 571badf029..d7e75ae365 100644 --- a/mobile/openapi/lib/model/asset_bulk_update_dto.dart +++ b/mobile/openapi/lib/model/asset_bulk_update_dto.dart @@ -14,6 +14,7 @@ class AssetBulkUpdateDto { /// Returns a new [AssetBulkUpdateDto] instance. AssetBulkUpdateDto({ this.dateTimeOriginal, + this.dateTimeRelative, this.description, this.duplicateId, this.ids = const [], @@ -21,6 +22,7 @@ class AssetBulkUpdateDto { this.latitude, this.longitude, this.rating, + this.timeZone, this.visibility, }); @@ -32,6 +34,14 @@ class AssetBulkUpdateDto { /// String? dateTimeOriginal; + /// + /// 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. + /// + num? dateTimeRelative; + /// /// 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 @@ -78,6 +88,14 @@ class AssetBulkUpdateDto { /// num? rating; + /// + /// 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? timeZone; + /// /// 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 @@ -89,6 +107,7 @@ class AssetBulkUpdateDto { @override bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto && other.dateTimeOriginal == dateTimeOriginal && + other.dateTimeRelative == dateTimeRelative && other.description == description && other.duplicateId == duplicateId && _deepEquality.equals(other.ids, ids) && @@ -96,12 +115,14 @@ class AssetBulkUpdateDto { other.latitude == latitude && other.longitude == longitude && other.rating == rating && + other.timeZone == timeZone && other.visibility == visibility; @override int get hashCode => // ignore: unnecessary_parenthesis (dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) + + (dateTimeRelative == null ? 0 : dateTimeRelative!.hashCode) + (description == null ? 0 : description!.hashCode) + (duplicateId == null ? 0 : duplicateId!.hashCode) + (ids.hashCode) + @@ -109,10 +130,11 @@ class AssetBulkUpdateDto { (latitude == null ? 0 : latitude!.hashCode) + (longitude == null ? 0 : longitude!.hashCode) + (rating == null ? 0 : rating!.hashCode) + + (timeZone == null ? 0 : timeZone!.hashCode) + (visibility == null ? 0 : visibility!.hashCode); @override - String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, description=$description, duplicateId=$duplicateId, ids=$ids, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating, visibility=$visibility]'; + String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, dateTimeRelative=$dateTimeRelative, description=$description, duplicateId=$duplicateId, ids=$ids, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, rating=$rating, timeZone=$timeZone, visibility=$visibility]'; Map toJson() { final json = {}; @@ -121,6 +143,11 @@ class AssetBulkUpdateDto { } else { // json[r'dateTimeOriginal'] = null; } + if (this.dateTimeRelative != null) { + json[r'dateTimeRelative'] = this.dateTimeRelative; + } else { + // json[r'dateTimeRelative'] = null; + } if (this.description != null) { json[r'description'] = this.description; } else { @@ -152,6 +179,11 @@ class AssetBulkUpdateDto { } else { // json[r'rating'] = null; } + if (this.timeZone != null) { + json[r'timeZone'] = this.timeZone; + } else { + // json[r'timeZone'] = null; + } if (this.visibility != null) { json[r'visibility'] = this.visibility; } else { @@ -170,6 +202,7 @@ class AssetBulkUpdateDto { return AssetBulkUpdateDto( dateTimeOriginal: mapValueOfType(json, r'dateTimeOriginal'), + dateTimeRelative: num.parse('${json[r'dateTimeRelative']}'), description: mapValueOfType(json, r'description'), duplicateId: mapValueOfType(json, r'duplicateId'), ids: json[r'ids'] is Iterable @@ -179,6 +212,7 @@ class AssetBulkUpdateDto { latitude: num.parse('${json[r'latitude']}'), longitude: num.parse('${json[r'longitude']}'), rating: num.parse('${json[r'rating']}'), + timeZone: mapValueOfType(json, r'timeZone'), visibility: AssetVisibility.fromJson(json[r'visibility']), ); } diff --git a/mobile/openapi/lib/model/asset_metadata_key.dart b/mobile/openapi/lib/model/asset_metadata_key.dart new file mode 100644 index 0000000000..70186cd41c --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_key.dart @@ -0,0 +1,82 @@ +// +// 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 AssetMetadataKey { + /// Instantiate a new enum with the provided [value]. + const AssetMetadataKey._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const mobileApp = AssetMetadataKey._(r'mobile-app'); + + /// List of all possible values in this [enum][AssetMetadataKey]. + static const values = [ + mobileApp, + ]; + + static AssetMetadataKey? fromJson(dynamic value) => AssetMetadataKeyTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataKey.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [AssetMetadataKey] to String, +/// and [decode] dynamic data back to [AssetMetadataKey]. +class AssetMetadataKeyTypeTransformer { + factory AssetMetadataKeyTypeTransformer() => _instance ??= const AssetMetadataKeyTypeTransformer._(); + + const AssetMetadataKeyTypeTransformer._(); + + String encode(AssetMetadataKey data) => data.value; + + /// Decodes a [dynamic value][data] to a AssetMetadataKey. + /// + /// 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. + AssetMetadataKey? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'mobile-app': return AssetMetadataKey.mobileApp; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [AssetMetadataKeyTypeTransformer] instance. + static AssetMetadataKeyTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_response_dto.dart b/mobile/openapi/lib/model/asset_metadata_response_dto.dart new file mode 100644 index 0000000000..af5769b9bb --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_response_dto.dart @@ -0,0 +1,115 @@ +// +// 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 AssetMetadataResponseDto { + /// Returns a new [AssetMetadataResponseDto] instance. + AssetMetadataResponseDto({ + required this.key, + required this.updatedAt, + required this.value, + }); + + AssetMetadataKey key; + + DateTime updatedAt; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataResponseDto && + other.key == key && + other.updatedAt == updatedAt && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (key.hashCode) + + (updatedAt.hashCode) + + (value.hashCode); + + @override + String toString() => 'AssetMetadataResponseDto[key=$key, updatedAt=$updatedAt, value=$value]'; + + Map toJson() { + final json = {}; + json[r'key'] = this.key; + json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); + json[r'value'] = this.value; + return json; + } + + /// Returns a new [AssetMetadataResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataResponseDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataResponseDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataResponseDto( + key: AssetMetadataKey.fromJson(json[r'key'])!, + updatedAt: mapDateTime(json, r'updatedAt', r'')!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'key', + 'updatedAt', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart b/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart new file mode 100644 index 0000000000..45d044feb0 --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_upsert_dto.dart @@ -0,0 +1,99 @@ +// +// 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 AssetMetadataUpsertDto { + /// Returns a new [AssetMetadataUpsertDto] instance. + AssetMetadataUpsertDto({ + this.items = const [], + }); + + List items; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataUpsertDto && + _deepEquality.equals(other.items, items); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (items.hashCode); + + @override + String toString() => 'AssetMetadataUpsertDto[items=$items]'; + + Map toJson() { + final json = {}; + json[r'items'] = this.items; + return json; + } + + /// Returns a new [AssetMetadataUpsertDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataUpsertDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataUpsertDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataUpsertDto( + items: AssetMetadataUpsertItemDto.listFromJson(json[r'items']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataUpsertDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataUpsertDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataUpsertDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataUpsertDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'items', + }; +} + diff --git a/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart b/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart new file mode 100644 index 0000000000..4b7e6579a1 --- /dev/null +++ b/mobile/openapi/lib/model/asset_metadata_upsert_item_dto.dart @@ -0,0 +1,107 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AssetMetadataUpsertItemDto { + /// Returns a new [AssetMetadataUpsertItemDto] instance. + AssetMetadataUpsertItemDto({ + required this.key, + required this.value, + }); + + AssetMetadataKey key; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetMetadataUpsertItemDto && + other.key == key && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (key.hashCode) + + (value.hashCode); + + @override + String toString() => 'AssetMetadataUpsertItemDto[key=$key, value=$value]'; + + Map toJson() { + final json = {}; + json[r'key'] = this.key; + json[r'value'] = this.value; + return json; + } + + /// Returns a new [AssetMetadataUpsertItemDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetMetadataUpsertItemDto? fromJson(dynamic value) { + upgradeDto(value, "AssetMetadataUpsertItemDto"); + if (value is Map) { + final json = value.cast(); + + return AssetMetadataUpsertItemDto( + key: AssetMetadataKey.fromJson(json[r'key'])!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetMetadataUpsertItemDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetMetadataUpsertItemDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetMetadataUpsertItemDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetMetadataUpsertItemDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'key', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index e2f60937f8..dc957b3bfc 100644 --- a/mobile/openapi/lib/model/asset_response_dto.dart +++ b/mobile/openapi/lib/model/asset_response_dto.dart @@ -14,6 +14,7 @@ class AssetResponseDto { /// Returns a new [AssetResponseDto] instance. AssetResponseDto({ required this.checksum, + required this.createdAt, required this.deviceAssetId, required this.deviceId, this.duplicateId, @@ -49,6 +50,9 @@ class AssetResponseDto { /// base64 encoded sha1 hash String checksum; + /// The UTC timestamp when the asset was originally uploaded to Immich. + DateTime createdAt; + String deviceAssetId; String deviceId; @@ -142,6 +146,7 @@ class AssetResponseDto { @override bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto && other.checksum == checksum && + other.createdAt == createdAt && other.deviceAssetId == deviceAssetId && other.deviceId == deviceId && other.duplicateId == duplicateId && @@ -177,6 +182,7 @@ class AssetResponseDto { int get hashCode => // ignore: unnecessary_parenthesis (checksum.hashCode) + + (createdAt.hashCode) + (deviceAssetId.hashCode) + (deviceId.hashCode) + (duplicateId == null ? 0 : duplicateId!.hashCode) + @@ -209,11 +215,12 @@ class AssetResponseDto { (visibility.hashCode); @override - String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt, visibility=$visibility]'; + String toString() => 'AssetResponseDto[checksum=$checksum, createdAt=$createdAt, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalMimeType=$originalMimeType, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, stack=$stack, tags=$tags, thumbhash=$thumbhash, type=$type, unassignedFaces=$unassignedFaces, updatedAt=$updatedAt, visibility=$visibility]'; Map toJson() { final json = {}; json[r'checksum'] = this.checksum; + json[r'createdAt'] = this.createdAt.toUtc().toIso8601String(); json[r'deviceAssetId'] = this.deviceAssetId; json[r'deviceId'] = this.deviceId; if (this.duplicateId != null) { @@ -293,6 +300,7 @@ class AssetResponseDto { return AssetResponseDto( checksum: mapValueOfType(json, r'checksum')!, + createdAt: mapDateTime(json, r'createdAt', r'')!, deviceAssetId: mapValueOfType(json, r'deviceAssetId')!, deviceId: mapValueOfType(json, r'deviceId')!, duplicateId: mapValueOfType(json, r'duplicateId'), @@ -371,6 +379,7 @@ class AssetResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { 'checksum', + 'createdAt', 'deviceAssetId', 'deviceId', 'duration', diff --git a/mobile/openapi/lib/model/bulk_id_error_reason.dart b/mobile/openapi/lib/model/bulk_id_error_reason.dart new file mode 100644 index 0000000000..cdaf70217e --- /dev/null +++ b/mobile/openapi/lib/model/bulk_id_error_reason.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 BulkIdErrorReason { + /// Instantiate a new enum with the provided [value]. + const BulkIdErrorReason._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const duplicate = BulkIdErrorReason._(r'duplicate'); + static const noPermission = BulkIdErrorReason._(r'no_permission'); + static const notFound = BulkIdErrorReason._(r'not_found'); + static const unknown = BulkIdErrorReason._(r'unknown'); + + /// List of all possible values in this [enum][BulkIdErrorReason]. + static const values = [ + duplicate, + noPermission, + notFound, + unknown, + ]; + + static BulkIdErrorReason? fromJson(dynamic value) => BulkIdErrorReasonTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = BulkIdErrorReason.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [BulkIdErrorReason] to String, +/// and [decode] dynamic data back to [BulkIdErrorReason]. +class BulkIdErrorReasonTypeTransformer { + factory BulkIdErrorReasonTypeTransformer() => _instance ??= const BulkIdErrorReasonTypeTransformer._(); + + const BulkIdErrorReasonTypeTransformer._(); + + String encode(BulkIdErrorReason data) => data.value; + + /// Decodes a [dynamic value][data] to a BulkIdErrorReason. + /// + /// 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. + BulkIdErrorReason? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'duplicate': return BulkIdErrorReason.duplicate; + case r'no_permission': return BulkIdErrorReason.noPermission; + case r'not_found': return BulkIdErrorReason.notFound; + case r'unknown': return BulkIdErrorReason.unknown; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [BulkIdErrorReasonTypeTransformer] instance. + static BulkIdErrorReasonTypeTransformer? _instance; +} + diff --git a/mobile/openapi/lib/model/machine_learning_availability_checks_dto.dart b/mobile/openapi/lib/model/machine_learning_availability_checks_dto.dart new file mode 100644 index 0000000000..84b3181426 --- /dev/null +++ b/mobile/openapi/lib/model/machine_learning_availability_checks_dto.dart @@ -0,0 +1,115 @@ +// +// 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 MachineLearningAvailabilityChecksDto { + /// Returns a new [MachineLearningAvailabilityChecksDto] instance. + MachineLearningAvailabilityChecksDto({ + required this.enabled, + required this.interval, + required this.timeout, + }); + + bool enabled; + + num interval; + + num timeout; + + @override + bool operator ==(Object other) => identical(this, other) || other is MachineLearningAvailabilityChecksDto && + other.enabled == enabled && + other.interval == interval && + other.timeout == timeout; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (enabled.hashCode) + + (interval.hashCode) + + (timeout.hashCode); + + @override + String toString() => 'MachineLearningAvailabilityChecksDto[enabled=$enabled, interval=$interval, timeout=$timeout]'; + + Map toJson() { + final json = {}; + json[r'enabled'] = this.enabled; + json[r'interval'] = this.interval; + json[r'timeout'] = this.timeout; + return json; + } + + /// Returns a new [MachineLearningAvailabilityChecksDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static MachineLearningAvailabilityChecksDto? fromJson(dynamic value) { + upgradeDto(value, "MachineLearningAvailabilityChecksDto"); + if (value is Map) { + final json = value.cast(); + + return MachineLearningAvailabilityChecksDto( + enabled: mapValueOfType(json, r'enabled')!, + interval: num.parse('${json[r'interval']}'), + timeout: num.parse('${json[r'timeout']}'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = MachineLearningAvailabilityChecksDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = MachineLearningAvailabilityChecksDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of MachineLearningAvailabilityChecksDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = MachineLearningAvailabilityChecksDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'enabled', + 'interval', + 'timeout', + }; +} + diff --git a/mobile/openapi/lib/model/partner_create_dto.dart b/mobile/openapi/lib/model/partner_create_dto.dart new file mode 100644 index 0000000000..09d60c5c77 --- /dev/null +++ b/mobile/openapi/lib/model/partner_create_dto.dart @@ -0,0 +1,99 @@ +// +// 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 PartnerCreateDto { + /// Returns a new [PartnerCreateDto] instance. + PartnerCreateDto({ + required this.sharedWithId, + }); + + String sharedWithId; + + @override + bool operator ==(Object other) => identical(this, other) || other is PartnerCreateDto && + other.sharedWithId == sharedWithId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (sharedWithId.hashCode); + + @override + String toString() => 'PartnerCreateDto[sharedWithId=$sharedWithId]'; + + Map toJson() { + final json = {}; + json[r'sharedWithId'] = this.sharedWithId; + return json; + } + + /// Returns a new [PartnerCreateDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static PartnerCreateDto? fromJson(dynamic value) { + upgradeDto(value, "PartnerCreateDto"); + if (value is Map) { + final json = value.cast(); + + return PartnerCreateDto( + sharedWithId: mapValueOfType(json, r'sharedWithId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = PartnerCreateDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = PartnerCreateDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of PartnerCreateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = PartnerCreateDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'sharedWithId', + }; +} + diff --git a/mobile/openapi/lib/model/update_partner_dto.dart b/mobile/openapi/lib/model/partner_update_dto.dart similarity index 66% rename from mobile/openapi/lib/model/update_partner_dto.dart rename to mobile/openapi/lib/model/partner_update_dto.dart index 3af3c83ad1..25cf217764 100644 --- a/mobile/openapi/lib/model/update_partner_dto.dart +++ b/mobile/openapi/lib/model/partner_update_dto.dart @@ -10,16 +10,16 @@ part of openapi.api; -class UpdatePartnerDto { - /// Returns a new [UpdatePartnerDto] instance. - UpdatePartnerDto({ +class PartnerUpdateDto { + /// Returns a new [PartnerUpdateDto] instance. + PartnerUpdateDto({ required this.inTimeline, }); bool inTimeline; @override - bool operator ==(Object other) => identical(this, other) || other is UpdatePartnerDto && + bool operator ==(Object other) => identical(this, other) || other is PartnerUpdateDto && other.inTimeline == inTimeline; @override @@ -28,7 +28,7 @@ class UpdatePartnerDto { (inTimeline.hashCode); @override - String toString() => 'UpdatePartnerDto[inTimeline=$inTimeline]'; + String toString() => 'PartnerUpdateDto[inTimeline=$inTimeline]'; Map toJson() { final json = {}; @@ -36,26 +36,26 @@ class UpdatePartnerDto { return json; } - /// Returns a new [UpdatePartnerDto] instance and imports its values from + /// Returns a new [PartnerUpdateDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static UpdatePartnerDto? fromJson(dynamic value) { - upgradeDto(value, "UpdatePartnerDto"); + static PartnerUpdateDto? fromJson(dynamic value) { + upgradeDto(value, "PartnerUpdateDto"); if (value is Map) { final json = value.cast(); - return UpdatePartnerDto( + return PartnerUpdateDto( inTimeline: mapValueOfType(json, r'inTimeline')!, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = UpdatePartnerDto.fromJson(row); + final value = PartnerUpdateDto.fromJson(row); if (value != null) { result.add(value); } @@ -64,12 +64,12 @@ class UpdatePartnerDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = UpdatePartnerDto.fromJson(entry.value); + final value = PartnerUpdateDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -78,14 +78,14 @@ class UpdatePartnerDto { return map; } - // maps a json object with a list of UpdatePartnerDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of PartnerUpdateDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = UpdatePartnerDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = PartnerUpdateDto.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index a85b5002bf..95b9a55fba 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -36,25 +36,35 @@ class Permission { static const assetPeriodRead = Permission._(r'asset.read'); static const assetPeriodUpdate = Permission._(r'asset.update'); static const assetPeriodDelete = Permission._(r'asset.delete'); + static const assetPeriodStatistics = Permission._(r'asset.statistics'); static const assetPeriodShare = Permission._(r'asset.share'); static const assetPeriodView = Permission._(r'asset.view'); static const assetPeriodDownload = Permission._(r'asset.download'); static const assetPeriodUpload = Permission._(r'asset.upload'); + static const assetPeriodReplace = Permission._(r'asset.replace'); static const albumPeriodCreate = Permission._(r'album.create'); static const albumPeriodRead = Permission._(r'album.read'); static const albumPeriodUpdate = Permission._(r'album.update'); static const albumPeriodDelete = Permission._(r'album.delete'); static const albumPeriodStatistics = Permission._(r'album.statistics'); - static const albumPeriodAddAsset = Permission._(r'album.addAsset'); - static const albumPeriodRemoveAsset = Permission._(r'album.removeAsset'); static const albumPeriodShare = Permission._(r'album.share'); static const albumPeriodDownload = Permission._(r'album.download'); + static const albumAssetPeriodCreate = Permission._(r'albumAsset.create'); + static const albumAssetPeriodDelete = Permission._(r'albumAsset.delete'); + static const albumUserPeriodCreate = Permission._(r'albumUser.create'); + static const albumUserPeriodUpdate = Permission._(r'albumUser.update'); + static const albumUserPeriodDelete = Permission._(r'albumUser.delete'); + static const authPeriodChangePassword = Permission._(r'auth.changePassword'); static const authDevicePeriodDelete = Permission._(r'authDevice.delete'); static const archivePeriodRead = Permission._(r'archive.read'); + static const duplicatePeriodRead = Permission._(r'duplicate.read'); + static const duplicatePeriodDelete = Permission._(r'duplicate.delete'); static const facePeriodCreate = Permission._(r'face.create'); static const facePeriodRead = Permission._(r'face.read'); static const facePeriodUpdate = Permission._(r'face.update'); static const facePeriodDelete = Permission._(r'face.delete'); + static const jobPeriodCreate = Permission._(r'job.create'); + static const jobPeriodRead = Permission._(r'job.read'); static const libraryPeriodCreate = Permission._(r'library.create'); static const libraryPeriodRead = Permission._(r'library.read'); static const libraryPeriodUpdate = Permission._(r'library.update'); @@ -66,6 +76,9 @@ 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 memoryPeriodStatistics = Permission._(r'memory.statistics'); + static const memoryAssetPeriodCreate = Permission._(r'memoryAsset.create'); + static const memoryAssetPeriodDelete = Permission._(r'memoryAsset.delete'); static const notificationPeriodCreate = Permission._(r'notification.create'); static const notificationPeriodRead = Permission._(r'notification.read'); static const notificationPeriodUpdate = Permission._(r'notification.update'); @@ -81,6 +94,17 @@ class Permission { static const personPeriodStatistics = Permission._(r'person.statistics'); static const personPeriodMerge = Permission._(r'person.merge'); static const personPeriodReassign = Permission._(r'person.reassign'); + static const pinCodePeriodCreate = Permission._(r'pinCode.create'); + static const pinCodePeriodUpdate = Permission._(r'pinCode.update'); + static const pinCodePeriodDelete = Permission._(r'pinCode.delete'); + static const serverPeriodAbout = Permission._(r'server.about'); + static const serverPeriodApkLinks = Permission._(r'server.apkLinks'); + static const serverPeriodStorage = Permission._(r'server.storage'); + static const serverPeriodStatistics = Permission._(r'server.statistics'); + static const serverPeriodVersionCheck = Permission._(r'server.versionCheck'); + static const serverLicensePeriodRead = Permission._(r'serverLicense.read'); + static const serverLicensePeriodUpdate = Permission._(r'serverLicense.update'); + static const serverLicensePeriodDelete = Permission._(r'serverLicense.delete'); static const sessionPeriodCreate = Permission._(r'session.create'); static const sessionPeriodRead = Permission._(r'session.read'); static const sessionPeriodUpdate = Permission._(r'session.update'); @@ -94,6 +118,10 @@ class Permission { static const stackPeriodRead = Permission._(r'stack.read'); static const stackPeriodUpdate = Permission._(r'stack.update'); static const stackPeriodDelete = Permission._(r'stack.delete'); + static const syncPeriodStream = Permission._(r'sync.stream'); + static const syncCheckpointPeriodRead = Permission._(r'syncCheckpoint.read'); + static const syncCheckpointPeriodUpdate = Permission._(r'syncCheckpoint.update'); + static const syncCheckpointPeriodDelete = Permission._(r'syncCheckpoint.delete'); static const systemConfigPeriodRead = Permission._(r'systemConfig.read'); static const systemConfigPeriodUpdate = Permission._(r'systemConfig.update'); static const systemMetadataPeriodRead = Permission._(r'systemMetadata.read'); @@ -103,10 +131,26 @@ class Permission { static const tagPeriodUpdate = Permission._(r'tag.update'); static const tagPeriodDelete = Permission._(r'tag.delete'); static const tagPeriodAsset = Permission._(r'tag.asset'); - static const adminPeriodUserPeriodCreate = Permission._(r'admin.user.create'); - static const adminPeriodUserPeriodRead = Permission._(r'admin.user.read'); - static const adminPeriodUserPeriodUpdate = Permission._(r'admin.user.update'); - static const adminPeriodUserPeriodDelete = Permission._(r'admin.user.delete'); + static const userPeriodRead = Permission._(r'user.read'); + static const userPeriodUpdate = Permission._(r'user.update'); + static const userLicensePeriodCreate = Permission._(r'userLicense.create'); + static const userLicensePeriodRead = Permission._(r'userLicense.read'); + static const userLicensePeriodUpdate = Permission._(r'userLicense.update'); + static const userLicensePeriodDelete = Permission._(r'userLicense.delete'); + static const userOnboardingPeriodRead = Permission._(r'userOnboarding.read'); + static const userOnboardingPeriodUpdate = Permission._(r'userOnboarding.update'); + static const userOnboardingPeriodDelete = Permission._(r'userOnboarding.delete'); + static const userPreferencePeriodRead = Permission._(r'userPreference.read'); + static const userPreferencePeriodUpdate = Permission._(r'userPreference.update'); + static const userProfileImagePeriodCreate = Permission._(r'userProfileImage.create'); + static const userProfileImagePeriodRead = Permission._(r'userProfileImage.read'); + static const userProfileImagePeriodUpdate = Permission._(r'userProfileImage.update'); + static const userProfileImagePeriodDelete = Permission._(r'userProfileImage.delete'); + static const adminUserPeriodCreate = Permission._(r'adminUser.create'); + static const adminUserPeriodRead = Permission._(r'adminUser.read'); + static const adminUserPeriodUpdate = Permission._(r'adminUser.update'); + static const adminUserPeriodDelete = Permission._(r'adminUser.delete'); + static const adminAuthPeriodUnlinkAll = Permission._(r'adminAuth.unlinkAll'); /// List of all possible values in this [enum][Permission]. static const values = [ @@ -123,25 +167,35 @@ class Permission { assetPeriodRead, assetPeriodUpdate, assetPeriodDelete, + assetPeriodStatistics, assetPeriodShare, assetPeriodView, assetPeriodDownload, assetPeriodUpload, + assetPeriodReplace, albumPeriodCreate, albumPeriodRead, albumPeriodUpdate, albumPeriodDelete, albumPeriodStatistics, - albumPeriodAddAsset, - albumPeriodRemoveAsset, albumPeriodShare, albumPeriodDownload, + albumAssetPeriodCreate, + albumAssetPeriodDelete, + albumUserPeriodCreate, + albumUserPeriodUpdate, + albumUserPeriodDelete, + authPeriodChangePassword, authDevicePeriodDelete, archivePeriodRead, + duplicatePeriodRead, + duplicatePeriodDelete, facePeriodCreate, facePeriodRead, facePeriodUpdate, facePeriodDelete, + jobPeriodCreate, + jobPeriodRead, libraryPeriodCreate, libraryPeriodRead, libraryPeriodUpdate, @@ -153,6 +207,9 @@ class Permission { memoryPeriodRead, memoryPeriodUpdate, memoryPeriodDelete, + memoryPeriodStatistics, + memoryAssetPeriodCreate, + memoryAssetPeriodDelete, notificationPeriodCreate, notificationPeriodRead, notificationPeriodUpdate, @@ -168,6 +225,17 @@ class Permission { personPeriodStatistics, personPeriodMerge, personPeriodReassign, + pinCodePeriodCreate, + pinCodePeriodUpdate, + pinCodePeriodDelete, + serverPeriodAbout, + serverPeriodApkLinks, + serverPeriodStorage, + serverPeriodStatistics, + serverPeriodVersionCheck, + serverLicensePeriodRead, + serverLicensePeriodUpdate, + serverLicensePeriodDelete, sessionPeriodCreate, sessionPeriodRead, sessionPeriodUpdate, @@ -181,6 +249,10 @@ class Permission { stackPeriodRead, stackPeriodUpdate, stackPeriodDelete, + syncPeriodStream, + syncCheckpointPeriodRead, + syncCheckpointPeriodUpdate, + syncCheckpointPeriodDelete, systemConfigPeriodRead, systemConfigPeriodUpdate, systemMetadataPeriodRead, @@ -190,10 +262,26 @@ class Permission { tagPeriodUpdate, tagPeriodDelete, tagPeriodAsset, - adminPeriodUserPeriodCreate, - adminPeriodUserPeriodRead, - adminPeriodUserPeriodUpdate, - adminPeriodUserPeriodDelete, + userPeriodRead, + userPeriodUpdate, + userLicensePeriodCreate, + userLicensePeriodRead, + userLicensePeriodUpdate, + userLicensePeriodDelete, + userOnboardingPeriodRead, + userOnboardingPeriodUpdate, + userOnboardingPeriodDelete, + userPreferencePeriodRead, + userPreferencePeriodUpdate, + userProfileImagePeriodCreate, + userProfileImagePeriodRead, + userProfileImagePeriodUpdate, + userProfileImagePeriodDelete, + adminUserPeriodCreate, + adminUserPeriodRead, + adminUserPeriodUpdate, + adminUserPeriodDelete, + adminAuthPeriodUnlinkAll, ]; static Permission? fromJson(dynamic value) => PermissionTypeTransformer().decode(value); @@ -245,25 +333,35 @@ class PermissionTypeTransformer { case r'asset.read': return Permission.assetPeriodRead; case r'asset.update': return Permission.assetPeriodUpdate; case r'asset.delete': return Permission.assetPeriodDelete; + case r'asset.statistics': return Permission.assetPeriodStatistics; case r'asset.share': return Permission.assetPeriodShare; case r'asset.view': return Permission.assetPeriodView; case r'asset.download': return Permission.assetPeriodDownload; case r'asset.upload': return Permission.assetPeriodUpload; + case r'asset.replace': return Permission.assetPeriodReplace; case r'album.create': return Permission.albumPeriodCreate; case r'album.read': return Permission.albumPeriodRead; case r'album.update': return Permission.albumPeriodUpdate; case r'album.delete': return Permission.albumPeriodDelete; case r'album.statistics': return Permission.albumPeriodStatistics; - case r'album.addAsset': return Permission.albumPeriodAddAsset; - case r'album.removeAsset': return Permission.albumPeriodRemoveAsset; case r'album.share': return Permission.albumPeriodShare; case r'album.download': return Permission.albumPeriodDownload; + case r'albumAsset.create': return Permission.albumAssetPeriodCreate; + case r'albumAsset.delete': return Permission.albumAssetPeriodDelete; + case r'albumUser.create': return Permission.albumUserPeriodCreate; + case r'albumUser.update': return Permission.albumUserPeriodUpdate; + case r'albumUser.delete': return Permission.albumUserPeriodDelete; + case r'auth.changePassword': return Permission.authPeriodChangePassword; case r'authDevice.delete': return Permission.authDevicePeriodDelete; case r'archive.read': return Permission.archivePeriodRead; + case r'duplicate.read': return Permission.duplicatePeriodRead; + case r'duplicate.delete': return Permission.duplicatePeriodDelete; case r'face.create': return Permission.facePeriodCreate; case r'face.read': return Permission.facePeriodRead; case r'face.update': return Permission.facePeriodUpdate; case r'face.delete': return Permission.facePeriodDelete; + case r'job.create': return Permission.jobPeriodCreate; + case r'job.read': return Permission.jobPeriodRead; case r'library.create': return Permission.libraryPeriodCreate; case r'library.read': return Permission.libraryPeriodRead; case r'library.update': return Permission.libraryPeriodUpdate; @@ -275,6 +373,9 @@ 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'memory.statistics': return Permission.memoryPeriodStatistics; + case r'memoryAsset.create': return Permission.memoryAssetPeriodCreate; + case r'memoryAsset.delete': return Permission.memoryAssetPeriodDelete; case r'notification.create': return Permission.notificationPeriodCreate; case r'notification.read': return Permission.notificationPeriodRead; case r'notification.update': return Permission.notificationPeriodUpdate; @@ -290,6 +391,17 @@ class PermissionTypeTransformer { case r'person.statistics': return Permission.personPeriodStatistics; case r'person.merge': return Permission.personPeriodMerge; case r'person.reassign': return Permission.personPeriodReassign; + case r'pinCode.create': return Permission.pinCodePeriodCreate; + case r'pinCode.update': return Permission.pinCodePeriodUpdate; + case r'pinCode.delete': return Permission.pinCodePeriodDelete; + case r'server.about': return Permission.serverPeriodAbout; + case r'server.apkLinks': return Permission.serverPeriodApkLinks; + case r'server.storage': return Permission.serverPeriodStorage; + case r'server.statistics': return Permission.serverPeriodStatistics; + case r'server.versionCheck': return Permission.serverPeriodVersionCheck; + case r'serverLicense.read': return Permission.serverLicensePeriodRead; + case r'serverLicense.update': return Permission.serverLicensePeriodUpdate; + case r'serverLicense.delete': return Permission.serverLicensePeriodDelete; case r'session.create': return Permission.sessionPeriodCreate; case r'session.read': return Permission.sessionPeriodRead; case r'session.update': return Permission.sessionPeriodUpdate; @@ -303,6 +415,10 @@ class PermissionTypeTransformer { case r'stack.read': return Permission.stackPeriodRead; case r'stack.update': return Permission.stackPeriodUpdate; case r'stack.delete': return Permission.stackPeriodDelete; + case r'sync.stream': return Permission.syncPeriodStream; + case r'syncCheckpoint.read': return Permission.syncCheckpointPeriodRead; + case r'syncCheckpoint.update': return Permission.syncCheckpointPeriodUpdate; + case r'syncCheckpoint.delete': return Permission.syncCheckpointPeriodDelete; case r'systemConfig.read': return Permission.systemConfigPeriodRead; case r'systemConfig.update': return Permission.systemConfigPeriodUpdate; case r'systemMetadata.read': return Permission.systemMetadataPeriodRead; @@ -312,10 +428,26 @@ class PermissionTypeTransformer { case r'tag.update': return Permission.tagPeriodUpdate; case r'tag.delete': return Permission.tagPeriodDelete; case r'tag.asset': return Permission.tagPeriodAsset; - case r'admin.user.create': return Permission.adminPeriodUserPeriodCreate; - case r'admin.user.read': return Permission.adminPeriodUserPeriodRead; - case r'admin.user.update': return Permission.adminPeriodUserPeriodUpdate; - case r'admin.user.delete': return Permission.adminPeriodUserPeriodDelete; + case r'user.read': return Permission.userPeriodRead; + case r'user.update': return Permission.userPeriodUpdate; + case r'userLicense.create': return Permission.userLicensePeriodCreate; + case r'userLicense.read': return Permission.userLicensePeriodRead; + case r'userLicense.update': return Permission.userLicensePeriodUpdate; + case r'userLicense.delete': return Permission.userLicensePeriodDelete; + case r'userOnboarding.read': return Permission.userOnboardingPeriodRead; + case r'userOnboarding.update': return Permission.userOnboardingPeriodUpdate; + case r'userOnboarding.delete': return Permission.userOnboardingPeriodDelete; + case r'userPreference.read': return Permission.userPreferencePeriodRead; + case r'userPreference.update': return Permission.userPreferencePeriodUpdate; + case r'userProfileImage.create': return Permission.userProfileImagePeriodCreate; + case r'userProfileImage.read': return Permission.userProfileImagePeriodRead; + case r'userProfileImage.update': return Permission.userProfileImagePeriodUpdate; + case r'userProfileImage.delete': return Permission.userProfileImagePeriodDelete; + case r'adminUser.create': return Permission.adminUserPeriodCreate; + case r'adminUser.read': return Permission.adminUserPeriodRead; + case r'adminUser.update': return Permission.adminUserPeriodUpdate; + case r'adminUser.delete': return Permission.adminUserPeriodDelete; + case r'adminAuth.unlinkAll': return Permission.adminAuthPeriodUnlinkAll; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/shared_link_create_dto.dart b/mobile/openapi/lib/model/shared_link_create_dto.dart index bc96b31fd2..644227bd6e 100644 --- a/mobile/openapi/lib/model/shared_link_create_dto.dart +++ b/mobile/openapi/lib/model/shared_link_create_dto.dart @@ -21,6 +21,7 @@ class SharedLinkCreateDto { this.expiresAt, this.password, this.showMetadata = true, + this.slug, required this.type, }); @@ -44,26 +45,16 @@ class SharedLinkCreateDto { List assetIds; - /// - /// 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; DateTime? expiresAt; - /// - /// 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? password; bool showMetadata; + String? slug; + SharedLinkType type; @override @@ -76,6 +67,7 @@ class SharedLinkCreateDto { other.expiresAt == expiresAt && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.type == type; @override @@ -89,10 +81,11 @@ class SharedLinkCreateDto { (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (type.hashCode); @override - String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, type=$type]'; + String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug, type=$type]'; Map toJson() { final json = {}; @@ -124,6 +117,11 @@ class SharedLinkCreateDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } json[r'type'] = this.type; return json; } @@ -147,6 +145,7 @@ class SharedLinkCreateDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata') ?? true, + slug: mapValueOfType(json, r'slug'), type: SharedLinkType.fromJson(json[r'type'])!, ); } diff --git a/mobile/openapi/lib/model/shared_link_edit_dto.dart b/mobile/openapi/lib/model/shared_link_edit_dto.dart index a394ba9b3b..f13bc6977b 100644 --- a/mobile/openapi/lib/model/shared_link_edit_dto.dart +++ b/mobile/openapi/lib/model/shared_link_edit_dto.dart @@ -20,6 +20,7 @@ class SharedLinkEditDto { this.expiresAt, this.password, this.showMetadata, + this.slug, }); /// @@ -47,22 +48,10 @@ class SharedLinkEditDto { /// bool? changeExpiryTime; - /// - /// 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; DateTime? expiresAt; - /// - /// 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? password; /// @@ -73,6 +62,8 @@ class SharedLinkEditDto { /// bool? showMetadata; + String? slug; + @override bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto && other.allowDownload == allowDownload && @@ -81,7 +72,8 @@ class SharedLinkEditDto { other.description == description && other.expiresAt == expiresAt && other.password == password && - other.showMetadata == showMetadata; + other.showMetadata == showMetadata && + other.slug == slug; @override int get hashCode => @@ -92,10 +84,11 @@ class SharedLinkEditDto { (description == null ? 0 : description!.hashCode) + (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + - (showMetadata == null ? 0 : showMetadata!.hashCode); + (showMetadata == null ? 0 : showMetadata!.hashCode) + + (slug == null ? 0 : slug!.hashCode); @override - String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata]'; + String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug]'; Map toJson() { final json = {}; @@ -134,6 +127,11 @@ class SharedLinkEditDto { } else { // json[r'showMetadata'] = null; } + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } return json; } @@ -153,6 +151,7 @@ class SharedLinkEditDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata'), + slug: mapValueOfType(json, r'slug'), ); } return null; diff --git a/mobile/openapi/lib/model/shared_link_response_dto.dart b/mobile/openapi/lib/model/shared_link_response_dto.dart index 9cc8b3ac80..d81e1dfa31 100644 --- a/mobile/openapi/lib/model/shared_link_response_dto.dart +++ b/mobile/openapi/lib/model/shared_link_response_dto.dart @@ -24,6 +24,7 @@ class SharedLinkResponseDto { required this.key, required this.password, required this.showMetadata, + required this.slug, this.token, required this.type, required this.userId, @@ -57,6 +58,8 @@ class SharedLinkResponseDto { bool showMetadata; + String? slug; + String? token; SharedLinkType type; @@ -76,6 +79,7 @@ class SharedLinkResponseDto { other.key == key && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.token == token && other.type == type && other.userId == userId; @@ -94,12 +98,13 @@ class SharedLinkResponseDto { (key.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (token == null ? 0 : token!.hashCode) + (type.hashCode) + (userId.hashCode); @override - String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, token=$token, type=$type, userId=$userId]'; + String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, slug=$slug, token=$token, type=$type, userId=$userId]'; Map toJson() { final json = {}; @@ -130,6 +135,11 @@ class SharedLinkResponseDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } if (this.token != null) { json[r'token'] = this.token; } else { @@ -160,6 +170,7 @@ class SharedLinkResponseDto { key: mapValueOfType(json, r'key')!, password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata')!, + slug: mapValueOfType(json, r'slug'), token: mapValueOfType(json, r'token'), type: SharedLinkType.fromJson(json[r'type'])!, userId: mapValueOfType(json, r'userId')!, @@ -220,6 +231,7 @@ class SharedLinkResponseDto { 'key', 'password', 'showMetadata', + 'slug', 'type', 'userId', }; diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart index 0d16b56d74..90902b9791 100644 --- a/mobile/openapi/lib/model/smart_search_dto.dart +++ b/mobile/openapi/lib/model/smart_search_dto.dart @@ -31,7 +31,8 @@ class SmartSearchDto { this.model, this.page, this.personIds = const [], - required this.query, + this.query, + this.queryAssetId, this.rating, this.size, this.state, @@ -151,7 +152,21 @@ class SmartSearchDto { List personIds; - String query; + /// + /// 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? query; + + /// + /// 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? queryAssetId; /// Minimum value: -1 /// Maximum value: 5 @@ -278,6 +293,7 @@ class SmartSearchDto { other.page == page && _deepEquality.equals(other.personIds, personIds) && other.query == query && + other.queryAssetId == queryAssetId && other.rating == rating && other.size == size && other.state == state && @@ -314,7 +330,8 @@ class SmartSearchDto { (model == null ? 0 : model!.hashCode) + (page == null ? 0 : page!.hashCode) + (personIds.hashCode) + - (query.hashCode) + + (query == null ? 0 : query!.hashCode) + + (queryAssetId == null ? 0 : queryAssetId!.hashCode) + (rating == null ? 0 : rating!.hashCode) + (size == null ? 0 : size!.hashCode) + (state == null ? 0 : state!.hashCode) + @@ -331,7 +348,7 @@ class SmartSearchDto { (withExif == null ? 0 : withExif!.hashCode); @override - String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]'; + String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, personIds=$personIds, query=$query, queryAssetId=$queryAssetId, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]'; Map toJson() { final json = {}; @@ -417,7 +434,16 @@ class SmartSearchDto { // json[r'page'] = null; } json[r'personIds'] = this.personIds; + if (this.query != null) { json[r'query'] = this.query; + } else { + // json[r'query'] = null; + } + if (this.queryAssetId != null) { + json[r'queryAssetId'] = this.queryAssetId; + } else { + // json[r'queryAssetId'] = null; + } if (this.rating != null) { json[r'rating'] = this.rating; } else { @@ -522,7 +548,8 @@ class SmartSearchDto { personIds: json[r'personIds'] is Iterable ? (json[r'personIds'] as Iterable).cast().toList(growable: false) : const [], - query: mapValueOfType(json, r'query')!, + query: mapValueOfType(json, r'query'), + queryAssetId: mapValueOfType(json, r'queryAssetId'), rating: num.parse('${json[r'rating']}'), size: num.parse('${json[r'size']}'), state: mapValueOfType(json, r'state'), @@ -586,7 +613,6 @@ class SmartSearchDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { - 'query', }; } diff --git a/mobile/openapi/lib/model/sync_asset_face_v1.dart b/mobile/openapi/lib/model/sync_asset_face_v1.dart index 853a8a1514..60d1766e34 100644 --- a/mobile/openapi/lib/model/sync_asset_face_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_face_v1.dart @@ -27,19 +27,19 @@ class SyncAssetFaceV1 { String assetId; - num boundingBoxX1; + int boundingBoxX1; - num boundingBoxX2; + int boundingBoxX2; - num boundingBoxY1; + int boundingBoxY1; - num boundingBoxY2; + int boundingBoxY2; String id; - num imageHeight; + int imageHeight; - num imageWidth; + int imageWidth; String? personId; @@ -104,13 +104,13 @@ class SyncAssetFaceV1 { return SyncAssetFaceV1( assetId: mapValueOfType(json, r'assetId')!, - boundingBoxX1: num.parse('${json[r'boundingBoxX1']}'), - boundingBoxX2: num.parse('${json[r'boundingBoxX2']}'), - boundingBoxY1: num.parse('${json[r'boundingBoxY1']}'), - boundingBoxY2: num.parse('${json[r'boundingBoxY2']}'), + boundingBoxX1: mapValueOfType(json, r'boundingBoxX1')!, + boundingBoxX2: mapValueOfType(json, r'boundingBoxX2')!, + boundingBoxY1: mapValueOfType(json, r'boundingBoxY1')!, + boundingBoxY2: mapValueOfType(json, r'boundingBoxY2')!, id: mapValueOfType(json, r'id')!, - imageHeight: num.parse('${json[r'imageHeight']}'), - imageWidth: num.parse('${json[r'imageWidth']}'), + imageHeight: mapValueOfType(json, r'imageHeight')!, + imageWidth: mapValueOfType(json, r'imageWidth')!, personId: mapValueOfType(json, r'personId'), sourceType: mapValueOfType(json, r'sourceType')!, ); diff --git a/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart b/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart new file mode 100644 index 0000000000..c9a7ef4670 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_metadata_delete_v1.dart @@ -0,0 +1,107 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetMetadataDeleteV1 { + /// Returns a new [SyncAssetMetadataDeleteV1] instance. + SyncAssetMetadataDeleteV1({ + required this.assetId, + required this.key, + }); + + String assetId; + + AssetMetadataKey key; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetMetadataDeleteV1 && + other.assetId == assetId && + other.key == key; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (key.hashCode); + + @override + String toString() => 'SyncAssetMetadataDeleteV1[assetId=$assetId, key=$key]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'key'] = this.key; + return json; + } + + /// Returns a new [SyncAssetMetadataDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetMetadataDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetMetadataDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetMetadataDeleteV1( + assetId: mapValueOfType(json, r'assetId')!, + key: AssetMetadataKey.fromJson(json[r'key'])!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetMetadataDeleteV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetMetadataDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetMetadataDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetMetadataDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'key', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_metadata_v1.dart b/mobile/openapi/lib/model/sync_asset_metadata_v1.dart new file mode 100644 index 0000000000..720fcef947 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_metadata_v1.dart @@ -0,0 +1,115 @@ +// +// 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 SyncAssetMetadataV1 { + /// Returns a new [SyncAssetMetadataV1] instance. + SyncAssetMetadataV1({ + required this.assetId, + required this.key, + required this.value, + }); + + String assetId; + + AssetMetadataKey key; + + Object value; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetMetadataV1 && + other.assetId == assetId && + other.key == key && + other.value == value; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (key.hashCode) + + (value.hashCode); + + @override + String toString() => 'SyncAssetMetadataV1[assetId=$assetId, key=$key, value=$value]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'key'] = this.key; + json[r'value'] = this.value; + return json; + } + + /// Returns a new [SyncAssetMetadataV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetMetadataV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetMetadataV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetMetadataV1( + assetId: mapValueOfType(json, r'assetId')!, + key: AssetMetadataKey.fromJson(json[r'key'])!, + value: mapValueOfType(json, r'value')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetMetadataV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetMetadataV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetMetadataV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetMetadataV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'key', + 'value', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 4c42d08a5f..f0d5097ea4 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -20,6 +20,7 @@ class SyncAssetV1 { required this.fileModifiedAt, required this.id, required this.isFavorite, + required this.libraryId, required this.livePhotoVideoId, required this.localDateTime, required this.originalFileName, @@ -44,6 +45,8 @@ class SyncAssetV1 { bool isFavorite; + String? libraryId; + String? livePhotoVideoId; DateTime? localDateTime; @@ -69,6 +72,7 @@ class SyncAssetV1 { other.fileModifiedAt == fileModifiedAt && other.id == id && other.isFavorite == isFavorite && + other.libraryId == libraryId && other.livePhotoVideoId == livePhotoVideoId && other.localDateTime == localDateTime && other.originalFileName == originalFileName && @@ -88,6 +92,7 @@ class SyncAssetV1 { (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + (isFavorite.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (localDateTime == null ? 0 : localDateTime!.hashCode) + (originalFileName.hashCode) + @@ -98,7 +103,7 @@ class SyncAssetV1 { (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -125,6 +130,11 @@ class SyncAssetV1 { } json[r'id'] = this.id; json[r'isFavorite'] = this.isFavorite; + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } if (this.livePhotoVideoId != null) { json[r'livePhotoVideoId'] = this.livePhotoVideoId; } else { @@ -168,6 +178,7 @@ class SyncAssetV1 { fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, isFavorite: mapValueOfType(json, r'isFavorite')!, + libraryId: mapValueOfType(json, r'libraryId'), livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'), localDateTime: mapDateTime(json, r'localDateTime', r''), originalFileName: mapValueOfType(json, r'originalFileName')!, @@ -230,6 +241,7 @@ class SyncAssetV1 { 'fileModifiedAt', 'id', 'isFavorite', + 'libraryId', 'livePhotoVideoId', 'localDateTime', 'originalFileName', diff --git a/mobile/openapi/lib/model/sync_auth_user_v1.dart b/mobile/openapi/lib/model/sync_auth_user_v1.dart new file mode 100644 index 0000000000..1dab7f47e3 --- /dev/null +++ b/mobile/openapi/lib/model/sync_auth_user_v1.dart @@ -0,0 +1,215 @@ +// +// 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 SyncAuthUserV1 { + /// Returns a new [SyncAuthUserV1] instance. + SyncAuthUserV1({ + required this.avatarColor, + required this.deletedAt, + required this.email, + required this.hasProfileImage, + required this.id, + required this.isAdmin, + required this.name, + required this.oauthId, + required this.pinCode, + required this.profileChangedAt, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + required this.storageLabel, + }); + + UserAvatarColor? avatarColor; + + DateTime? deletedAt; + + String email; + + bool hasProfileImage; + + String id; + + bool isAdmin; + + String name; + + String oauthId; + + String? pinCode; + + DateTime profileChangedAt; + + int? quotaSizeInBytes; + + int quotaUsageInBytes; + + String? storageLabel; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAuthUserV1 && + other.avatarColor == avatarColor && + other.deletedAt == deletedAt && + other.email == email && + other.hasProfileImage == hasProfileImage && + other.id == id && + other.isAdmin == isAdmin && + other.name == name && + other.oauthId == oauthId && + other.pinCode == pinCode && + other.profileChangedAt == profileChangedAt && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes && + other.storageLabel == storageLabel; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (email.hashCode) + + (hasProfileImage.hashCode) + + (id.hashCode) + + (isAdmin.hashCode) + + (name.hashCode) + + (oauthId.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + + (profileChangedAt.hashCode) + + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + + (quotaUsageInBytes.hashCode) + + (storageLabel == null ? 0 : storageLabel!.hashCode); + + @override + String toString() => 'SyncAuthUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, pinCode=$pinCode, profileChangedAt=$profileChangedAt, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, storageLabel=$storageLabel]'; + + Map toJson() { + final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; + json[r'id'] = this.id; + json[r'isAdmin'] = this.isAdmin; + json[r'name'] = this.name; + json[r'oauthId'] = this.oauthId; + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); + if (this.quotaSizeInBytes != null) { + json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; + } else { + // json[r'quotaSizeInBytes'] = null; + } + json[r'quotaUsageInBytes'] = this.quotaUsageInBytes; + if (this.storageLabel != null) { + json[r'storageLabel'] = this.storageLabel; + } else { + // json[r'storageLabel'] = null; + } + return json; + } + + /// Returns a new [SyncAuthUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAuthUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAuthUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAuthUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), + deletedAt: mapDateTime(json, r'deletedAt', r''), + email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, + id: mapValueOfType(json, r'id')!, + isAdmin: mapValueOfType(json, r'isAdmin')!, + name: mapValueOfType(json, r'name')!, + oauthId: mapValueOfType(json, r'oauthId')!, + pinCode: mapValueOfType(json, r'pinCode'), + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, + quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), + quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes')!, + storageLabel: mapValueOfType(json, r'storageLabel'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAuthUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAuthUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAuthUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAuthUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'avatarColor', + 'deletedAt', + 'email', + 'hasProfileImage', + 'id', + 'isAdmin', + 'name', + 'oauthId', + 'pinCode', + 'profileChangedAt', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'storageLabel', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 65ed78105c..1b4ca91f3b 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -23,11 +23,14 @@ class SyncEntityType { String toJson() => value; + static const authUserV1 = SyncEntityType._(r'AuthUserV1'); static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1'); static const assetExifV1 = SyncEntityType._(r'AssetExifV1'); + static const assetMetadataV1 = SyncEntityType._(r'AssetMetadataV1'); + static const assetMetadataDeleteV1 = SyncEntityType._(r'AssetMetadataDeleteV1'); static const partnerV1 = SyncEntityType._(r'PartnerV1'); static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1'); static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1'); @@ -43,9 +46,11 @@ class SyncEntityType { static const albumUserV1 = SyncEntityType._(r'AlbumUserV1'); static const albumUserBackfillV1 = SyncEntityType._(r'AlbumUserBackfillV1'); static const albumUserDeleteV1 = SyncEntityType._(r'AlbumUserDeleteV1'); - static const albumAssetV1 = SyncEntityType._(r'AlbumAssetV1'); + static const albumAssetCreateV1 = SyncEntityType._(r'AlbumAssetCreateV1'); + static const albumAssetUpdateV1 = SyncEntityType._(r'AlbumAssetUpdateV1'); static const albumAssetBackfillV1 = SyncEntityType._(r'AlbumAssetBackfillV1'); - static const albumAssetExifV1 = SyncEntityType._(r'AlbumAssetExifV1'); + static const albumAssetExifCreateV1 = SyncEntityType._(r'AlbumAssetExifCreateV1'); + static const albumAssetExifUpdateV1 = SyncEntityType._(r'AlbumAssetExifUpdateV1'); static const albumAssetExifBackfillV1 = SyncEntityType._(r'AlbumAssetExifBackfillV1'); static const albumToAssetV1 = SyncEntityType._(r'AlbumToAssetV1'); static const albumToAssetDeleteV1 = SyncEntityType._(r'AlbumToAssetDeleteV1'); @@ -64,14 +69,18 @@ class SyncEntityType { static const userMetadataDeleteV1 = SyncEntityType._(r'UserMetadataDeleteV1'); static const syncAckV1 = SyncEntityType._(r'SyncAckV1'); static const syncResetV1 = SyncEntityType._(r'SyncResetV1'); + static const syncCompleteV1 = SyncEntityType._(r'SyncCompleteV1'); /// List of all possible values in this [enum][SyncEntityType]. static const values = [ + authUserV1, userV1, userDeleteV1, assetV1, assetDeleteV1, assetExifV1, + assetMetadataV1, + assetMetadataDeleteV1, partnerV1, partnerDeleteV1, partnerAssetV1, @@ -87,9 +96,11 @@ class SyncEntityType { albumUserV1, albumUserBackfillV1, albumUserDeleteV1, - albumAssetV1, + albumAssetCreateV1, + albumAssetUpdateV1, albumAssetBackfillV1, - albumAssetExifV1, + albumAssetExifCreateV1, + albumAssetExifUpdateV1, albumAssetExifBackfillV1, albumToAssetV1, albumToAssetDeleteV1, @@ -108,6 +119,7 @@ class SyncEntityType { userMetadataDeleteV1, syncAckV1, syncResetV1, + syncCompleteV1, ]; static SyncEntityType? fromJson(dynamic value) => SyncEntityTypeTypeTransformer().decode(value); @@ -146,11 +158,14 @@ class SyncEntityTypeTypeTransformer { SyncEntityType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { + case r'AuthUserV1': return SyncEntityType.authUserV1; case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1; case r'AssetExifV1': return SyncEntityType.assetExifV1; + case r'AssetMetadataV1': return SyncEntityType.assetMetadataV1; + case r'AssetMetadataDeleteV1': return SyncEntityType.assetMetadataDeleteV1; case r'PartnerV1': return SyncEntityType.partnerV1; case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1; case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1; @@ -166,9 +181,11 @@ class SyncEntityTypeTypeTransformer { case r'AlbumUserV1': return SyncEntityType.albumUserV1; case r'AlbumUserBackfillV1': return SyncEntityType.albumUserBackfillV1; case r'AlbumUserDeleteV1': return SyncEntityType.albumUserDeleteV1; - case r'AlbumAssetV1': return SyncEntityType.albumAssetV1; + case r'AlbumAssetCreateV1': return SyncEntityType.albumAssetCreateV1; + case r'AlbumAssetUpdateV1': return SyncEntityType.albumAssetUpdateV1; case r'AlbumAssetBackfillV1': return SyncEntityType.albumAssetBackfillV1; - case r'AlbumAssetExifV1': return SyncEntityType.albumAssetExifV1; + case r'AlbumAssetExifCreateV1': return SyncEntityType.albumAssetExifCreateV1; + case r'AlbumAssetExifUpdateV1': return SyncEntityType.albumAssetExifUpdateV1; case r'AlbumAssetExifBackfillV1': return SyncEntityType.albumAssetExifBackfillV1; case r'AlbumToAssetV1': return SyncEntityType.albumToAssetV1; case r'AlbumToAssetDeleteV1': return SyncEntityType.albumToAssetDeleteV1; @@ -187,6 +204,7 @@ class SyncEntityTypeTypeTransformer { case r'UserMetadataDeleteV1': return SyncEntityType.userMetadataDeleteV1; case r'SyncAckV1': return SyncEntityType.syncAckV1; case r'SyncResetV1': return SyncEntityType.syncResetV1; + case r'SyncCompleteV1': return SyncEntityType.syncCompleteV1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 800b3f4485..c3dc1c4d61 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,8 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const assetMetadataV1 = SyncRequestType._(r'AssetMetadataV1'); + static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); static const partnersV1 = SyncRequestType._(r'PartnersV1'); @@ -51,6 +53,8 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + assetMetadataV1, + authUsersV1, memoriesV1, memoryToAssetsV1, partnersV1, @@ -107,6 +111,8 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AssetMetadataV1': return SyncRequestType.assetMetadataV1; + case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; case r'PartnersV1': return SyncRequestType.partnersV1; diff --git a/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart b/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart index e9dd733295..f39acc617b 100644 --- a/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart +++ b/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart @@ -17,7 +17,7 @@ class SyncUserMetadataDeleteV1 { required this.userId, }); - String key; + UserMetadataKey key; String userId; @@ -51,7 +51,7 @@ class SyncUserMetadataDeleteV1 { final json = value.cast(); return SyncUserMetadataDeleteV1( - key: mapValueOfType(json, r'key')!, + key: UserMetadataKey.fromJson(json[r'key'])!, userId: mapValueOfType(json, r'userId')!, ); } diff --git a/mobile/openapi/lib/model/sync_user_metadata_v1.dart b/mobile/openapi/lib/model/sync_user_metadata_v1.dart index 0b060dc17c..cf39b6d960 100644 --- a/mobile/openapi/lib/model/sync_user_metadata_v1.dart +++ b/mobile/openapi/lib/model/sync_user_metadata_v1.dart @@ -18,7 +18,7 @@ class SyncUserMetadataV1 { required this.value, }); - String key; + UserMetadataKey key; String userId; @@ -57,7 +57,7 @@ class SyncUserMetadataV1 { final json = value.cast(); return SyncUserMetadataV1( - key: mapValueOfType(json, r'key')!, + key: UserMetadataKey.fromJson(json[r'key'])!, userId: mapValueOfType(json, r'userId')!, value: mapValueOfType(json, r'value')!, ); diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index b9b41bb723..b9fad5ae8c 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -13,48 +13,70 @@ part of openapi.api; class SyncUserV1 { /// Returns a new [SyncUserV1] instance. SyncUserV1({ + required this.avatarColor, required this.deletedAt, required this.email, + required this.hasProfileImage, required this.id, required this.name, + required this.profileChangedAt, }); + UserAvatarColor? avatarColor; + DateTime? deletedAt; String email; + bool hasProfileImage; + String id; String name; + DateTime profileChangedAt; + @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && + other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && + other.hasProfileImage == hasProfileImage && other.id == id && - other.name == name; + other.name == name && + other.profileChangedAt == profileChangedAt; @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + + (hasProfileImage.hashCode) + (id.hashCode) + - (name.hashCode); + (name.hashCode) + + (profileChangedAt.hashCode); @override - String toString() => 'SyncUserV1[deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, name=$name, profileChangedAt=$profileChangedAt]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.deletedAt != null) { json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); } else { // json[r'deletedAt'] = null; } json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; json[r'id'] = this.id; json[r'name'] = this.name; + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); return json; } @@ -67,10 +89,13 @@ class SyncUserV1 { final json = value.cast(); return SyncUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, ); } return null; @@ -118,10 +143,13 @@ class SyncUserV1 { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'avatarColor', 'deletedAt', 'email', + 'hasProfileImage', 'id', 'name', + 'profileChangedAt', }; } diff --git a/mobile/openapi/lib/model/system_config_machine_learning_dto.dart b/mobile/openapi/lib/model/system_config_machine_learning_dto.dart index a4a9ca7d82..d7b2566d59 100644 --- a/mobile/openapi/lib/model/system_config_machine_learning_dto.dart +++ b/mobile/openapi/lib/model/system_config_machine_learning_dto.dart @@ -13,14 +13,16 @@ part of openapi.api; class SystemConfigMachineLearningDto { /// Returns a new [SystemConfigMachineLearningDto] instance. SystemConfigMachineLearningDto({ + required this.availabilityChecks, required this.clip, required this.duplicateDetection, required this.enabled, required this.facialRecognition, - this.url, this.urls = const [], }); + MachineLearningAvailabilityChecksDto availabilityChecks; + CLIPConfig clip; DuplicateDetectionConfig duplicateDetection; @@ -29,50 +31,37 @@ class SystemConfigMachineLearningDto { FacialRecognitionConfig facialRecognition; - /// This property was deprecated in v1.122.0 - /// - /// 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? url; - List urls; @override bool operator ==(Object other) => identical(this, other) || other is SystemConfigMachineLearningDto && + other.availabilityChecks == availabilityChecks && other.clip == clip && other.duplicateDetection == duplicateDetection && other.enabled == enabled && other.facialRecognition == facialRecognition && - other.url == url && _deepEquality.equals(other.urls, urls); @override int get hashCode => // ignore: unnecessary_parenthesis + (availabilityChecks.hashCode) + (clip.hashCode) + (duplicateDetection.hashCode) + (enabled.hashCode) + (facialRecognition.hashCode) + - (url == null ? 0 : url!.hashCode) + (urls.hashCode); @override - String toString() => 'SystemConfigMachineLearningDto[clip=$clip, duplicateDetection=$duplicateDetection, enabled=$enabled, facialRecognition=$facialRecognition, url=$url, urls=$urls]'; + String toString() => 'SystemConfigMachineLearningDto[availabilityChecks=$availabilityChecks, clip=$clip, duplicateDetection=$duplicateDetection, enabled=$enabled, facialRecognition=$facialRecognition, urls=$urls]'; Map toJson() { final json = {}; + json[r'availabilityChecks'] = this.availabilityChecks; json[r'clip'] = this.clip; json[r'duplicateDetection'] = this.duplicateDetection; json[r'enabled'] = this.enabled; json[r'facialRecognition'] = this.facialRecognition; - if (this.url != null) { - json[r'url'] = this.url; - } else { - // json[r'url'] = null; - } json[r'urls'] = this.urls; return json; } @@ -86,11 +75,11 @@ class SystemConfigMachineLearningDto { final json = value.cast(); return SystemConfigMachineLearningDto( + availabilityChecks: MachineLearningAvailabilityChecksDto.fromJson(json[r'availabilityChecks'])!, clip: CLIPConfig.fromJson(json[r'clip'])!, duplicateDetection: DuplicateDetectionConfig.fromJson(json[r'duplicateDetection'])!, enabled: mapValueOfType(json, r'enabled')!, facialRecognition: FacialRecognitionConfig.fromJson(json[r'facialRecognition'])!, - url: mapValueOfType(json, r'url'), urls: json[r'urls'] is Iterable ? (json[r'urls'] as Iterable).cast().toList(growable: false) : const [], @@ -141,6 +130,7 @@ class SystemConfigMachineLearningDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'availabilityChecks', 'clip', 'duplicateDetection', 'enabled', diff --git a/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart index 886b353f68..58032b7c51 100644 --- a/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart +++ b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart @@ -21,8 +21,10 @@ class TimeBucketAssetResponseDto { this.isFavorite = const [], this.isImage = const [], this.isTrashed = const [], + this.latitude = const [], this.livePhotoVideoId = const [], this.localOffsetHours = const [], + this.longitude = const [], this.ownerId = const [], this.projectionType = const [], this.ratio = const [], @@ -55,12 +57,18 @@ class TimeBucketAssetResponseDto { /// Array indicating whether each asset is in the trash List isTrashed; + /// Array of latitude coordinates extracted from EXIF GPS data + List latitude; + /// Array of live photo video asset IDs (null for non-live photos) List livePhotoVideoId; /// Array of UTC offset hours at the time each photo was taken. Positive values are east of UTC, negative values are west of UTC. Values may be fractional (e.g., 5.5 for +05:30, -9.75 for -09:45). Applying this offset to 'fileCreatedAt' will give you the time the photo was taken from the photographer's perspective. List localOffsetHours; + /// Array of longitude coordinates extracted from EXIF GPS data + List longitude; + /// Array of owner IDs for each asset List ownerId; @@ -89,8 +97,10 @@ class TimeBucketAssetResponseDto { _deepEquality.equals(other.isFavorite, isFavorite) && _deepEquality.equals(other.isImage, isImage) && _deepEquality.equals(other.isTrashed, isTrashed) && + _deepEquality.equals(other.latitude, latitude) && _deepEquality.equals(other.livePhotoVideoId, livePhotoVideoId) && _deepEquality.equals(other.localOffsetHours, localOffsetHours) && + _deepEquality.equals(other.longitude, longitude) && _deepEquality.equals(other.ownerId, ownerId) && _deepEquality.equals(other.projectionType, projectionType) && _deepEquality.equals(other.ratio, ratio) && @@ -109,8 +119,10 @@ class TimeBucketAssetResponseDto { (isFavorite.hashCode) + (isImage.hashCode) + (isTrashed.hashCode) + + (latitude.hashCode) + (livePhotoVideoId.hashCode) + (localOffsetHours.hashCode) + + (longitude.hashCode) + (ownerId.hashCode) + (projectionType.hashCode) + (ratio.hashCode) + @@ -119,7 +131,7 @@ class TimeBucketAssetResponseDto { (visibility.hashCode); @override - String toString() => 'TimeBucketAssetResponseDto[city=$city, country=$country, duration=$duration, fileCreatedAt=$fileCreatedAt, id=$id, isFavorite=$isFavorite, isImage=$isImage, isTrashed=$isTrashed, livePhotoVideoId=$livePhotoVideoId, localOffsetHours=$localOffsetHours, ownerId=$ownerId, projectionType=$projectionType, ratio=$ratio, stack=$stack, thumbhash=$thumbhash, visibility=$visibility]'; + String toString() => 'TimeBucketAssetResponseDto[city=$city, country=$country, duration=$duration, fileCreatedAt=$fileCreatedAt, id=$id, isFavorite=$isFavorite, isImage=$isImage, isTrashed=$isTrashed, latitude=$latitude, livePhotoVideoId=$livePhotoVideoId, localOffsetHours=$localOffsetHours, longitude=$longitude, ownerId=$ownerId, projectionType=$projectionType, ratio=$ratio, stack=$stack, thumbhash=$thumbhash, visibility=$visibility]'; Map toJson() { final json = {}; @@ -131,8 +143,10 @@ class TimeBucketAssetResponseDto { json[r'isFavorite'] = this.isFavorite; json[r'isImage'] = this.isImage; json[r'isTrashed'] = this.isTrashed; + json[r'latitude'] = this.latitude; json[r'livePhotoVideoId'] = this.livePhotoVideoId; json[r'localOffsetHours'] = this.localOffsetHours; + json[r'longitude'] = this.longitude; json[r'ownerId'] = this.ownerId; json[r'projectionType'] = this.projectionType; json[r'ratio'] = this.ratio; @@ -175,12 +189,18 @@ class TimeBucketAssetResponseDto { isTrashed: json[r'isTrashed'] is Iterable ? (json[r'isTrashed'] as Iterable).cast().toList(growable: false) : const [], + latitude: json[r'latitude'] is Iterable + ? (json[r'latitude'] as Iterable).cast().toList(growable: false) + : const [], livePhotoVideoId: json[r'livePhotoVideoId'] is Iterable ? (json[r'livePhotoVideoId'] as Iterable).cast().toList(growable: false) : const [], localOffsetHours: json[r'localOffsetHours'] is Iterable ? (json[r'localOffsetHours'] as Iterable).cast().toList(growable: false) : const [], + longitude: json[r'longitude'] is Iterable + ? (json[r'longitude'] as Iterable).cast().toList(growable: false) + : const [], ownerId: json[r'ownerId'] is Iterable ? (json[r'ownerId'] as Iterable).cast().toList(growable: false) : const [], diff --git a/mobile/openapi/lib/model/user_metadata_key.dart b/mobile/openapi/lib/model/user_metadata_key.dart new file mode 100644 index 0000000000..845b5ae9bb --- /dev/null +++ b/mobile/openapi/lib/model/user_metadata_key.dart @@ -0,0 +1,88 @@ +// +// 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 UserMetadataKey { + /// Instantiate a new enum with the provided [value]. + const UserMetadataKey._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const preferences = UserMetadataKey._(r'preferences'); + static const license = UserMetadataKey._(r'license'); + static const onboarding = UserMetadataKey._(r'onboarding'); + + /// List of all possible values in this [enum][UserMetadataKey]. + static const values = [ + preferences, + license, + onboarding, + ]; + + static UserMetadataKey? fromJson(dynamic value) => UserMetadataKeyTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = UserMetadataKey.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [UserMetadataKey] to String, +/// and [decode] dynamic data back to [UserMetadataKey]. +class UserMetadataKeyTypeTransformer { + factory UserMetadataKeyTypeTransformer() => _instance ??= const UserMetadataKeyTypeTransformer._(); + + const UserMetadataKeyTypeTransformer._(); + + String encode(UserMetadataKey data) => data.value; + + /// Decodes a [dynamic value][data] to a UserMetadataKey. + /// + /// 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. + UserMetadataKey? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'preferences': return UserMetadataKey.preferences; + case r'license': return UserMetadataKey.license; + case r'onboarding': return UserMetadataKey.onboarding; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [UserMetadataKeyTypeTransformer] instance. + static UserMetadataKeyTypeTransformer? _instance; +} + diff --git a/mobile/pigeon/background_worker_api.dart b/mobile/pigeon/background_worker_api.dart new file mode 100644 index 0000000000..a40d290199 --- /dev/null +++ b/mobile/pigeon/background_worker_api.dart @@ -0,0 +1,54 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/platform/background_worker_api.g.dart', + swiftOut: 'ios/Runner/Background/BackgroundWorker.g.swift', + swiftOptions: SwiftOptions(includeErrorClass: false), + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt', + kotlinOptions: KotlinOptions(package: 'app.alextran.immich.background'), + dartOptions: DartOptions(), + dartPackageName: 'immich_mobile', + ), +) +class BackgroundWorkerSettings { + final bool requiresCharging; + final int minimumDelaySeconds; + + const BackgroundWorkerSettings({required this.requiresCharging, required this.minimumDelaySeconds}); +} + +@HostApi() +abstract class BackgroundWorkerFgHostApi { + void enable(); + + void saveNotificationMessage(String title, String body); + + void configure(BackgroundWorkerSettings settings); + + void disable(); +} + +@HostApi() +abstract class BackgroundWorkerBgHostApi { + // Called from the background flutter engine when it has bootstrapped and established the + // required platform channels to notify the native side to start the background upload + void onInitialized(); + + // Called from the background flutter engine to request the native side to cleanup + void close(); +} + +@FlutterApi() +abstract class BackgroundWorkerFlutterApi { + // iOS Only: Called when the iOS background upload is triggered + @async + void onIosUpload(bool isRefresh, int? maxSeconds); + + // Android Only: Called when the Android background upload is triggered + @async + void onAndroidUpload(); + + @async + void cancel(); +} diff --git a/mobile/pigeon/background_worker_lock_api.dart b/mobile/pigeon/background_worker_lock_api.dart new file mode 100644 index 0000000000..44c5220367 --- /dev/null +++ b/mobile/pigeon/background_worker_lock_api.dart @@ -0,0 +1,17 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/platform/background_worker_lock_api.g.dart', + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt', + kotlinOptions: KotlinOptions(package: 'app.alextran.immich.background', includeErrorClass: false), + dartOptions: DartOptions(), + dartPackageName: 'immich_mobile', + ), +) +@HostApi() +abstract class BackgroundWorkerLockApi { + void lock(); + + void unlock(); +} diff --git a/mobile/pigeon/connectivity_api.dart b/mobile/pigeon/connectivity_api.dart new file mode 100644 index 0000000000..c5677ee20e --- /dev/null +++ b/mobile/pigeon/connectivity_api.dart @@ -0,0 +1,20 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/platform/connectivity_api.g.dart', + swiftOut: 'ios/Runner/Connectivity/Connectivity.g.swift', + swiftOptions: SwiftOptions(includeErrorClass: false), + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt', + kotlinOptions: KotlinOptions(package: 'app.alextran.immich.connectivity'), + dartOptions: DartOptions(), + dartPackageName: 'immich_mobile', + ), +) +enum NetworkCapability { cellular, wifi, vpn, unmetered } + +@HostApi() +abstract class ConnectivityApi { + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List getCapabilities(); +} diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 4f14b7a0b9..ac08a68ca3 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -5,8 +5,7 @@ import 'package:pigeon/pigeon.dart'; dartOut: 'lib/platform/native_sync_api.g.dart', swiftOut: 'ios/Runner/Sync/Messages.g.swift', swiftOptions: SwiftOptions(), - kotlinOut: - 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', kotlinOptions: KotlinOptions(package: 'app.alextran.immich.sync'), dartOptions: DartOptions(), dartPackageName: 'immich_mobile', @@ -24,6 +23,7 @@ class PlatformAsset { final int? height; final int durationInSeconds; final int orientation; + final bool isFavorite; const PlatformAsset({ required this.id, @@ -35,6 +35,7 @@ class PlatformAsset { this.height, this.durationInSeconds = 0, this.orientation = 0, + this.isFavorite = false, }); } @@ -70,6 +71,14 @@ class SyncDelta { }); } +class HashResult { + final String assetId; + final String? error; + final String? hash; + + const HashResult({required this.assetId, this.error, this.hash}); +} + @HostApi() abstract class NativeSyncApi { bool shouldFullSync(); @@ -93,6 +102,9 @@ abstract class NativeSyncApi { @TaskQueue(type: TaskQueueType.serialBackgroundThread) List getAssetsForAlbum(String albumId, {int? updatedTimeCond}); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) - List hashPaths(List paths); + List hashAssets(List assetIds, {bool allowNetworkAccess = false}); + + void cancelHashing(); } diff --git a/mobile/pigeon/thumbnail_api.dart b/mobile/pigeon/thumbnail_api.dart new file mode 100644 index 0000000000..0698e7cdc9 --- /dev/null +++ b/mobile/pigeon/thumbnail_api.dart @@ -0,0 +1,30 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/platform/thumbnail_api.g.dart', + swiftOut: 'ios/Runner/Images/Thumbnails.g.swift', + swiftOptions: SwiftOptions(includeErrorClass: false), + kotlinOut: + 'android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt', + kotlinOptions: KotlinOptions(package: 'app.alextran.immich.images'), + dartOptions: DartOptions(), + dartPackageName: 'immich_mobile', + ), +) +@HostApi() +abstract class ThumbnailApi { + @async + Map requestImage( + String assetId, { + required int requestId, + required int width, + required int height, + required bool isVideo, + }); + + void cancelImageRequest(int requestId); + + @async + Map getThumbhash(String thumbhash); +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 95aa866ea3..125e4d46e2 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -77,10 +77,10 @@ packages: dependency: "direct main" description: name: background_downloader - sha256: "2d4c2b7438e7643585880f9cc00ace16a52d778088751f1bfbf714627b315462" + sha256: "9ed74c55750932178f6989ba8a659687c2a102e05b70f561a1b3f047a5dda790" url: "https://pub.dev" source: hosted - version: "9.2.0" + version: "9.2.5" bonsoir: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27" + sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "6.1.5" connectivity_plus_platform_interface: dependency: transitive description: @@ -437,18 +437,18 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513" + sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6" url: "https://pub.dev" source: hosted - version: "11.3.3" + version: "12.1.0" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f url: "https://pub.dev" source: hosted - version: "7.0.2" + version: "7.0.3" drift: dependency: "direct main" description: @@ -514,7 +514,7 @@ packages: source: hosted version: "1.3.3" ffi: - dependency: transitive + dependency: "direct main" description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" @@ -1022,25 +1022,34 @@ packages: isar: dependency: "direct main" description: - name: isar - sha256: e17a9555bc7f22ff26568b8c64d019b4ffa2dc6bd4cb1c8d9b269aefd32e53ad - url: "https://pub.isar-community.dev" - source: hosted + path: "packages/isar" + ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + url: "https://github.com/immich-app/isar" + source: git version: "3.1.8" - isar_flutter_libs: + isar_community: + dependency: transitive + description: + name: isar_community + sha256: "28f59e54636c45ba0bb1b3b7f2656f1c50325f740cea6efcd101900be3fba546" + url: "https://pub.dev" + source: hosted + version: "3.3.0-dev.3" + isar_community_flutter_libs: dependency: "direct main" description: - name: isar_flutter_libs - sha256: "78710781e658ce4bff59b3f38c5b2735e899e627f4e926e1221934e77b95231a" - url: "https://pub.isar-community.dev" + name: isar_community_flutter_libs + sha256: c2934fe755bb3181cb67602fd5df0d080b3d3eb52799f98623aa4fc5acbea010 + url: "https://pub.dev" source: hosted - version: "3.1.8" + version: "3.3.0-dev.3" isar_generator: dependency: "direct dev" description: path: "packages/isar_generator" - ref: v3 - resolved-ref: ad574f60ed6f39d2995cd16fc7dc3de9a646ef30 + ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a + resolved-ref: bb1dca40fe87a001122e5d43bc6254718cb49f3a url: "https://github.com/immich-app/isar" source: git version: "3.1.8" @@ -1064,26 +1073,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -1208,8 +1217,8 @@ packages: dependency: "direct main" description: path: "." - ref: "5459d54" - resolved-ref: "5459d54cdc1cf4d99e2193b310052f1ebb5dcf43" + ref: "893894b" + resolved-ref: "893894b98b832be8a995a8d5d4c2289d0ad2d246" url: "https://github.com/immich-app/native_video_player" source: git version: "1.3.1" @@ -1416,10 +1425,10 @@ packages: dependency: "direct dev" description: name: pigeon - sha256: a093af76026160bb5ff6eb98e3e678a301ffd1001ac0d90be558bc133a0c73f5 + sha256: b65acb352dc5a5f8615d074a83419388cbcc249f07c6d8c78b5bc16680a55dda url: "https://pub.dev" source: hosted - version: "25.3.2" + version: "26.0.0" pinput: dependency: "direct main" description: @@ -1429,7 +1438,7 @@ packages: source: hosted version: "5.0.1" platform: - dependency: "direct main" + dependency: transitive description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" @@ -1556,6 +1565,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + scroll_date_picker: + dependency: "direct main" + description: + name: scroll_date_picker + sha256: "1b00a3e24d92c77aa84d5856cfe6a57fd5df5f645ce1a6af0feb3ec84bdffb34" + url: "https://pub.dev" + source: hosted + version: "3.8.0" scrollable_positioned_list: dependency: "direct main" description: @@ -1689,6 +1706,14 @@ packages: description: flutter source: sdk version: "0.0.0" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 + url: "https://pub.dev" + source: hosted + version: "0.2.12" socket_io_client: dependency: "direct main" description: @@ -1861,10 +1886,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" thumbhash: dependency: "direct main" description: @@ -2021,10 +2046,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -2154,5 +2179,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.32.6" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.35.4" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index bbffb9f51b..7dc34807b1 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,13 +2,11 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.135.3+204 +version: 2.0.1+3021 environment: - sdk: '>=3.3.0 <4.0.0' - flutter: 3.32.6 - -isar_version: &isar_version 3.1.8 + sdk: '>=3.8.0 <4.0.0' + flutter: 3.35.4 dependencies: flutter: @@ -16,7 +14,7 @@ dependencies: async: ^2.11.0 auto_route: ^9.2.0 - background_downloader: ^9.2.0 + background_downloader: ^9.2.5 cached_network_image: ^3.4.1 cancellation_token_http: ^2.1.0 cast: ^2.1.0 @@ -24,7 +22,7 @@ dependencies: connectivity_plus: ^6.1.3 crop_image: ^1.0.16 crypto: ^3.0.6 - device_info_plus: ^11.3.3 + device_info_plus: ^12.0.0 dynamic_color: ^1.7.0 easy_image_viewer: ^1.5.1 easy_localization: ^3.0.7+1 @@ -57,12 +55,12 @@ dependencies: photo_manager: ^3.6.4 photo_manager_image_provider: ^2.2.0 pinput: ^5.0.1 - platform: ^3.1.6 punycode: ^1.0.0 riverpod_annotation: ^2.6.1 scrollable_positioned_list: ^0.3.8 share_handler: ^0.0.22 share_plus: ^10.1.4 + sliver_tools: ^0.2.12 socket_io_client: ^2.0.3+1 stream_transform: ^2.1.1 thumbhash: 0.1.0+1 @@ -71,19 +69,21 @@ dependencies: uuid: ^4.5.1 wakelock_plus: ^1.2.10 worker_manager: ^7.2.3 + scroll_date_picker: ^3.8.0 + ffi: ^2.1.4 native_video_player: git: url: https://github.com/immich-app/native_video_player - ref: '5459d54' + ref: '893894b' openapi: path: openapi isar: - version: *isar_version - hosted: https://pub.isar-community.dev/ - isar_flutter_libs: # contains Isar Core - version: *isar_version - hosted: https://pub.isar-community.dev/ + git: + url: https://github.com/immich-app/isar + ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' + path: packages/isar/ + isar_community_flutter_libs: 3.3.0-dev.3 # DB drift: ^2.23.1 drift_flutter: ^0.2.4 @@ -99,7 +99,7 @@ dev_dependencies: isar_generator: git: url: https://github.com/immich-app/isar - ref: v3 + ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' path: packages/isar_generator/ integration_test: sdk: flutter @@ -114,7 +114,7 @@ dev_dependencies: # Drift generator drift_dev: ^2.23.1 # Type safe platform code - pigeon: ^25.3.1 + pigeon: ^26.0.0 flutter: uses-material-design: true diff --git a/mobile/test/domain/service.mock.dart b/mobile/test/domain/service.mock.dart index f4c5a32a4b..8293faf125 100644 --- a/mobile/test/domain/service.mock.dart +++ b/mobile/test/domain/service.mock.dart @@ -2,6 +2,8 @@ import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:mocktail/mocktail.dart'; class MockStoreService extends Mock implements StoreService {} @@ -11,3 +13,7 @@ class MockUserService extends Mock implements UserService {} class MockBackgroundSyncManager extends Mock implements BackgroundSyncManager {} class MockNativeSyncApi extends Mock implements NativeSyncApi {} + +class MockAppSettingsService extends Mock implements AppSettingsService {} + +class MockUploadService extends Mock implements UploadService {} diff --git a/mobile/test/domain/services/album.service_test.dart b/mobile/test/domain/services/album.service_test.dart new file mode 100644 index 0000000000..ebd94a9450 --- /dev/null +++ b/mobile/test/domain/services/album.service_test.dart @@ -0,0 +1,118 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; +import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../infrastructure/repository.mock.dart'; + +void main() { + late RemoteAlbumService sut; + late DriftRemoteAlbumRepository mockRemoteAlbumRepo; + late DriftAlbumApiRepository mockAlbumApiRepo; + + setUp(() { + mockRemoteAlbumRepo = MockRemoteAlbumRepository(); + mockAlbumApiRepo = MockDriftAlbumApiRepository(); + sut = RemoteAlbumService(mockRemoteAlbumRepo, mockAlbumApiRepo); + + when(() => mockRemoteAlbumRepo.getNewestAssetTimestamp(any())).thenAnswer((invocation) { + // Simulate a timestamp for the newest asset in the album + final albumID = invocation.positionalArguments[0] as String; + + if (albumID == '1') { + return Future.value(DateTime(2023, 1, 1)); + } else if (albumID == '2') { + return Future.value(DateTime(2023, 2, 1)); + } + + return Future.value(DateTime.fromMillisecondsSinceEpoch(0)); + }); + + when(() => mockRemoteAlbumRepo.getOldestAssetTimestamp(any())).thenAnswer((invocation) { + // Simulate a timestamp for the oldest asset in the album + final albumID = invocation.positionalArguments[0] as String; + + if (albumID == '1') { + return Future.value(DateTime(2019, 1, 1)); + } else if (albumID == '2') { + return Future.value(DateTime(2019, 2, 1)); + } + + return Future.value(DateTime.fromMillisecondsSinceEpoch(0)); + }); + }); + + final albumA = RemoteAlbum( + id: '1', + name: 'Album A', + description: "", + isActivityEnabled: false, + order: AlbumAssetOrder.asc, + assetCount: 1, + createdAt: DateTime(2023, 1, 1), + updatedAt: DateTime(2023, 1, 2), + ownerId: 'owner1', + ownerName: "Test User", + isShared: false, + ); + + final albumB = RemoteAlbum( + id: '2', + name: 'Album B', + description: "", + isActivityEnabled: false, + order: AlbumAssetOrder.desc, + assetCount: 2, + createdAt: DateTime(2023, 2, 1), + updatedAt: DateTime(2023, 2, 2), + ownerId: 'owner2', + ownerName: "Test User", + isShared: false, + ); + + group('sortAlbums', () { + test('should sort correctly based on name', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.title); + expect(result, [albumA, albumB]); + }); + + test('should sort correctly based on createdAt', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.created); + expect(result, [albumA, albumB]); + }); + + test('should sort correctly based on updatedAt', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.lastModified); + expect(result, [albumA, albumB]); + }); + + test('should sort correctly based on assetCount', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.assetCount); + expect(result, [albumA, albumB]); + }); + + test('should sort correctly based on newestAssetTimestamp', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.mostRecent); + expect(result, [albumA, albumB]); + }); + + test('should sort correctly based on oldestAssetTimestamp', () async { + final albums = [albumB, albumA]; + + final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.mostOldest); + expect(result, [albumB, albumA]); + }); + }); +} diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index b2ab803ac2..20d60b6866 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -1,11 +1,7 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - import 'package:flutter_test/flutter_test.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/services/hash.service.dart'; -import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/platform/native_sync_api.g.dart'; import 'package:mocktail/mocktail.dart'; import '../../fixtures/album.stub.dart'; @@ -13,227 +9,137 @@ import '../../fixtures/asset.stub.dart'; import '../../infrastructure/repository.mock.dart'; import '../service.mock.dart'; -class MockFile extends Mock implements File {} - void main() { late HashService sut; late MockLocalAlbumRepository mockAlbumRepo; late MockLocalAssetRepository mockAssetRepo; - late MockStorageRepository mockStorageRepo; late MockNativeSyncApi mockNativeApi; - final sortBy = { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }; setUp(() { mockAlbumRepo = MockLocalAlbumRepository(); mockAssetRepo = MockLocalAssetRepository(); - mockStorageRepo = MockStorageRepository(); mockNativeApi = MockNativeSyncApi(); sut = HashService( localAlbumRepository: mockAlbumRepo, localAssetRepository: mockAssetRepo, - storageRepository: mockStorageRepo, nativeSyncApi: mockNativeApi, ); registerFallbackValue(LocalAlbumStub.recent); registerFallbackValue(LocalAssetStub.image1); + registerFallbackValue({}); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); }); group('HashService hashAssets', () { test('skips albums with no assets to hash', () async { - when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer( - (_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)], - ); - when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)) - .thenAnswer((_) async => []); + when( + () => mockAlbumRepo.getBackupAlbums(), + ).thenAnswer((_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)]); + when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)).thenAnswer((_) async => []); await sut.hashAssets(); - verifyNever(() => mockStorageRepo.getFileForAsset(any())); - verifyNever(() => mockNativeApi.hashPaths(any())); + verifyNever(() => mockNativeApi.hashAssets(any(), allowNetworkAccess: any(named: 'allowNetworkAccess'))); }); }); group('HashService _hashAssets', () { - test('skips assets without files', () async { + test('skips empty batches', () async { final album = LocalAlbumStub.recent; - final asset = LocalAssetStub.image1; - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => null); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => []); await sut.hashAssets(); - verifyNever(() => mockNativeApi.hashPaths(any())); + verifyNever(() => mockNativeApi.hashAssets(any(), allowNetworkAccess: any(named: 'allowNetworkAccess'))); }); test('processes assets when available', () async { final album = LocalAlbumStub.recent; final asset = LocalAssetStub.image1; - final mockFile = MockFile(); - final hash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockFile.length()).thenAnswer((_) async => 1000); - when(() => mockFile.path).thenReturn('image-path'); - - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer( - (_) async => [hash], - ); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when( + () => mockNativeApi.hashAssets([asset.id], allowNetworkAccess: false), + ).thenAnswer((_) async => [HashResult(assetId: asset.id, hash: 'test-hash')]); await sut.hashAssets(); - verify(() => mockNativeApi.hashPaths(['image-path'])).called(1); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + verify(() => mockNativeApi.hashAssets([asset.id], allowNetworkAccess: false)).called(1); + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as Map; expect(captured.length, 1); - expect(captured[0].checksum, base64.encode(hash)); + expect(captured[asset.id], 'test-hash'); }); test('handles failed hashes', () async { final album = LocalAlbumStub.recent; final asset = LocalAssetStub.image1; - final mockFile = MockFile(); - when(() => mockFile.length()).thenAnswer((_) async => 1000); - when(() => mockFile.path).thenReturn('image-path'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])) - .thenAnswer((_) async => [null]); - when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when( + () => mockNativeApi.hashAssets([asset.id], allowNetworkAccess: false), + ).thenAnswer((_) async => [HashResult(assetId: asset.id, error: 'Failed to hash')]); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as Map; expect(captured.length, 0); }); - test('handles invalid hash length', () async { + test('handles null hash results', () async { final album = LocalAlbumStub.recent; final asset = LocalAssetStub.image1; - final mockFile = MockFile(); - when(() => mockFile.length()).thenAnswer((_) async => 1000); - when(() => mockFile.path).thenReturn('image-path'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); - - final invalidHash = Uint8List.fromList([1, 2, 3]); - when(() => mockNativeApi.hashPaths(['image-path'])) - .thenAnswer((_) async => [invalidHash]); - when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when( + () => mockNativeApi.hashAssets([asset.id], allowNetworkAccess: false), + ).thenAnswer((_) async => [HashResult(assetId: asset.id, hash: null)]); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as Map; expect(captured.length, 0); }); - test('batches by file count limit', () async { - final sut = HashService( - localAlbumRepository: mockAlbumRepo, - localAssetRepository: mockAssetRepo, - storageRepository: mockStorageRepo, - nativeSyncApi: mockNativeApi, - batchFileLimit: 1, - ); - - final album = LocalAlbumStub.recent; - final asset1 = LocalAssetStub.image1; - final asset2 = LocalAssetStub.image2; - final mockFile1 = MockFile(); - final mockFile2 = MockFile(); - when(() => mockFile1.length()).thenAnswer((_) async => 100); - when(() => mockFile1.path).thenReturn('path-1'); - when(() => mockFile2.length()).thenAnswer((_) async => 100); - when(() => mockFile2.path).thenReturn('path-2'); - - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); - - final hash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(any())) - .thenAnswer((_) async => [hash]); - when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); - - await sut.hashAssets(); - - verify(() => mockNativeApi.hashPaths(['path-1'])).called(1); - verify(() => mockNativeApi.hashPaths(['path-2'])).called(1); - verify(() => mockAssetRepo.updateHashes(any())).called(2); - }); - test('batches by size limit', () async { + const batchSize = 2; final sut = HashService( localAlbumRepository: mockAlbumRepo, localAssetRepository: mockAssetRepo, - storageRepository: mockStorageRepo, nativeSyncApi: mockNativeApi, - batchSizeLimit: 80, + batchSize: batchSize, ); final album = LocalAlbumStub.recent; final asset1 = LocalAssetStub.image1; final asset2 = LocalAssetStub.image2; - final mockFile1 = MockFile(); - final mockFile2 = MockFile(); - when(() => mockFile1.length()).thenAnswer((_) async => 100); - when(() => mockFile1.path).thenReturn('path-1'); - when(() => mockFile2.length()).thenAnswer((_) async => 100); - when(() => mockFile2.path).thenReturn('path-2'); + final asset3 = LocalAssetStub.image1.copyWith(id: 'image3', name: 'image3.jpg'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); + final capturedCalls = >[]; - final hash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(any())) - .thenAnswer((_) async => [hash]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2, asset3]); + when(() => mockNativeApi.hashAssets(any(), allowNetworkAccess: any(named: 'allowNetworkAccess'))).thenAnswer(( + invocation, + ) async { + final assetIds = invocation.positionalArguments[0] as List; + capturedCalls.add(List.from(assetIds)); + return assetIds.map((id) => HashResult(assetId: id, hash: '$id-hash')).toList(); + }); await sut.hashAssets(); - verify(() => mockNativeApi.hashPaths(['path-1'])).called(1); - verify(() => mockNativeApi.hashPaths(['path-2'])).called(1); + expect(capturedCalls.length, 2, reason: 'Should make exactly 2 calls to hashAssets'); + expect(capturedCalls[0], [asset1.id, asset2.id], reason: 'First call should batch the first two assets'); + expect(capturedCalls[1], [asset3.id], reason: 'Second call should have the remaining asset'); + verify(() => mockAssetRepo.updateHashes(any())).called(2); }); @@ -241,34 +147,43 @@ void main() { final album = LocalAlbumStub.recent; final asset1 = LocalAssetStub.image1; final asset2 = LocalAssetStub.image2; - final mockFile1 = MockFile(); - final mockFile2 = MockFile(); - when(() => mockFile1.length()).thenAnswer((_) async => 100); - when(() => mockFile1.path).thenReturn('path-1'); - when(() => mockFile2.length()).thenAnswer((_) async => 100); - when(() => mockFile2.path).thenReturn('path-2'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); - - final validHash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(['path-1', 'path-2'])) - .thenAnswer((_) async => [validHash, null]); - when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]); + when(() => mockNativeApi.hashAssets([asset1.id, asset2.id], allowNetworkAccess: false)).thenAnswer( + (_) async => [ + HashResult(assetId: asset1.id, hash: 'asset1-hash'), + HashResult(assetId: asset2.id, error: 'Failed to hash asset2'), + ], + ); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as Map; expect(captured.length, 1); - expect(captured.first.id, asset1.id); + expect(captured[asset1.id], 'asset1-hash'); + }); + + test('uses allowNetworkAccess based on album backup selection', () async { + final selectedAlbum = LocalAlbumStub.recent.copyWith(backupSelection: BackupSelection.selected); + final nonSelectedAlbum = LocalAlbumStub.recent.copyWith(id: 'album2', backupSelection: BackupSelection.excluded); + final asset1 = LocalAssetStub.image1; + final asset2 = LocalAssetStub.image2; + + when(() => mockAlbumRepo.getBackupAlbums()).thenAnswer((_) async => [selectedAlbum, nonSelectedAlbum]); + when(() => mockAlbumRepo.getAssetsToHash(selectedAlbum.id)).thenAnswer((_) async => [asset1]); + when(() => mockAlbumRepo.getAssetsToHash(nonSelectedAlbum.id)).thenAnswer((_) async => [asset2]); + when(() => mockNativeApi.hashAssets(any(), allowNetworkAccess: any(named: 'allowNetworkAccess'))).thenAnswer(( + invocation, + ) async { + final assetIds = invocation.positionalArguments[0] as List; + return assetIds.map((id) => HashResult(assetId: id, hash: '$id-hash')).toList(); + }); + + await sut.hashAssets(); + + verify(() => mockNativeApi.hashAssets([asset1.id], allowNetworkAccess: true)).called(1); + verify(() => mockNativeApi.hashAssets([asset2.id], allowNetworkAccess: false)).called(1); }); }); } diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index 3efa717f97..95f677ba98 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -28,7 +28,7 @@ final _kWarnLog = LogMessage( void main() { late LogService sut; - late IsarLogRepository mockLogRepo; + late LogRepository mockLogRepo; late IsarStoreRepository mockStoreRepo; setUp(() async { @@ -37,18 +37,13 @@ void main() { registerFallbackValue(_kInfoLog); - when(() => mockLogRepo.truncate(limit: any(named: 'limit'))) - .thenAnswer((_) async => {}); - when(() => mockStoreRepo.tryGet(StoreKey.logLevel)) - .thenAnswer((_) async => LogLevel.fine.index); + when(() => mockLogRepo.truncate(limit: any(named: 'limit'))).thenAnswer((_) async => {}); + when(() => mockStoreRepo.tryGet(StoreKey.logLevel)).thenAnswer((_) async => LogLevel.fine.index); when(() => mockLogRepo.getAll()).thenAnswer((_) async => []); when(() => mockLogRepo.insert(any())).thenAnswer((_) async => true); when(() => mockLogRepo.insertAll(any())).thenAnswer((_) async => true); - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo); }); tearDown(() async { @@ -57,10 +52,7 @@ void main() { group("Log Service Init:", () { test('Truncates the existing logs on init', () { - final limit = - verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit'))) - .captured - .firstOrNull as int?; + final limit = verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit'))).captured.firstOrNull as int?; expect(limit, kLogTruncateLimit); }); @@ -72,15 +64,12 @@ void main() { group("Log Service Set Level:", () { setUp(() async { - when(() => mockStoreRepo.insert(StoreKey.logLevel, any())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.upsert(StoreKey.logLevel, any())).thenAnswer((_) async => true); await sut.setLogLevel(LogLevel.shout); }); test('Updates the log level in store', () { - final index = verify( - () => mockStoreRepo.insert(StoreKey.logLevel, captureAny()), - ).captured.firstOrNull; + final index = verify(() => mockStoreRepo.upsert(StoreKey.logLevel, captureAny())).captured.firstOrNull; expect(index, LogLevel.shout.index); }); @@ -92,11 +81,7 @@ void main() { group("Log Service Buffer:", () { test('Buffers logs until timer elapses', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -110,11 +95,7 @@ void main() { test('Batch inserts all logs on timer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -131,11 +112,7 @@ void main() { test('Does not buffer when off', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: false, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: false); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -165,11 +142,7 @@ void main() { test('Combines result from both DB + Buffer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kWarnLog.logger!); logger.warning(_kWarnLog.message); diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index 7eab532ef3..d03e493843 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -16,11 +16,13 @@ final _kBackupFailedSince = DateTime.utc(2023); void main() { late StoreService sut; late IsarStoreRepository mockStoreRepo; - late StreamController> controller; + late DriftStoreRepository mockDriftStoreRepo; + late StreamController>> controller; setUp(() async { - controller = StreamController>.broadcast(); + controller = StreamController>>.broadcast(); mockStoreRepo = MockStoreRepository(); + mockDriftStoreRepo = MockDriftStoreRepository(); // For generics, we need to provide fallback to each concrete type to avoid runtime errors registerFallbackValue(StoreKey.accessToken); registerFallbackValue(StoreKey.backupTriggerDelay); @@ -37,6 +39,16 @@ void main() { ); when(() => mockStoreRepo.watchAll()).thenAnswer((_) => controller.stream); + when(() => mockDriftStoreRepo.getAll()).thenAnswer( + (_) async => [ + const StoreDto(StoreKey.accessToken, _kAccessToken), + const StoreDto(StoreKey.backgroundBackup, _kBackgroundBackup), + const StoreDto(StoreKey.groupAssetsBy, _kGroupAssetsBy), + StoreDto(StoreKey.backupFailedSince, _kBackupFailedSince), + ], + ); + when(() => mockDriftStoreRepo.watchAll()).thenAnswer((_) => controller.stream); + sut = await StoreService.create(storeRepository: mockStoreRepo); }); @@ -58,7 +70,7 @@ void main() { test('Listens to stream of store updates', () async { final event = StoreDto(StoreKey.accessToken, _kAccessToken.toUpperCase()); - controller.add(event); + controller.add([event]); await pumpEventQueue(); @@ -73,10 +85,7 @@ void main() { }); test('Throws StoreKeyNotFoundException for nonexistent keys', () { - expect( - () => sut.get(StoreKey.currentUser), - throwsA(isA()), - ); + expect(() => sut.get(StoreKey.currentUser), throwsA(isA())); }); test('Returns the stored value for the given key or the defaultValue', () { @@ -86,24 +95,19 @@ void main() { group('Store Service put:', () { setUp(() { - when(() => mockStoreRepo.insert(any>(), any())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.upsert(any>(), any())).thenAnswer((_) async => true); + when(() => mockDriftStoreRepo.upsert(any>(), any())).thenAnswer((_) async => true); }); test('Skip insert when value is not modified', () async { await sut.put(StoreKey.accessToken, _kAccessToken); - verifyNever( - () => mockStoreRepo.insert(StoreKey.accessToken, any()), - ); + verifyNever(() => mockStoreRepo.upsert(StoreKey.accessToken, any())); }); test('Insert value when modified', () async { final newAccessToken = _kAccessToken.toUpperCase(); await sut.put(StoreKey.accessToken, newAccessToken); - verify( - () => - mockStoreRepo.insert(StoreKey.accessToken, newAccessToken), - ).called(1); + verify(() => mockStoreRepo.upsert(StoreKey.accessToken, newAccessToken)).called(1); expect(sut.tryGet(StoreKey.accessToken), newAccessToken); }); }); @@ -113,8 +117,8 @@ void main() { setUp(() { valueController = StreamController.broadcast(); - when(() => mockStoreRepo.watch(any>())) - .thenAnswer((_) => valueController.stream); + when(() => mockStoreRepo.watch(any>())).thenAnswer((_) => valueController.stream); + when(() => mockDriftStoreRepo.watch(any>())).thenAnswer((_) => valueController.stream); }); tearDown(() async { @@ -123,12 +127,7 @@ void main() { test('Watches a specific key for changes', () async { final stream = sut.watch(StoreKey.accessToken); - final events = [ - _kAccessToken, - _kAccessToken.toUpperCase(), - null, - _kAccessToken.toLowerCase(), - ]; + final events = [_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; expectLater(stream, emitsInOrder(events)); @@ -143,14 +142,13 @@ void main() { group('Store Service delete:', () { setUp(() { - when(() => mockStoreRepo.delete(any>())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.delete(any>())).thenAnswer((_) async => true); + when(() => mockDriftStoreRepo.delete(any>())).thenAnswer((_) async => true); }); test('Removes the value from the DB', () async { await sut.delete(StoreKey.accessToken); - verify(() => mockStoreRepo.delete(StoreKey.accessToken)) - .called(1); + verify(() => mockStoreRepo.delete(StoreKey.accessToken)).called(1); }); test('Removes the value from the cache', () async { @@ -162,6 +160,7 @@ void main() { group('Store Service clear:', () { setUp(() { when(() => mockStoreRepo.deleteAll()).thenAnswer((_) async => true); + when(() => mockDriftStoreRepo.deleteAll()).thenAnswer((_) async => true); }); test('Clears all values from the store', () async { diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index deb19dfcf8..0126b11e46 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -30,8 +30,9 @@ void main() { late SyncStreamService sut; late SyncStreamRepository mockSyncStreamRepo; late SyncApiRepository mockSyncApiRepo; - late Function(List, Function()) handleEventsCallback; + late Future Function(List, Function(), Function()) handleEventsCallback; late _MockAbortCallbackWrapper mockAbortCallbackWrapper; + late _MockAbortCallbackWrapper mockResetCallbackWrapper; successHandler(Invocation _) async => true; @@ -39,124 +40,91 @@ void main() { mockSyncStreamRepo = MockSyncStreamRepository(); mockSyncApiRepo = MockSyncApiRepository(); mockAbortCallbackWrapper = _MockAbortCallbackWrapper(); + mockResetCallbackWrapper = _MockAbortCallbackWrapper(); when(() => mockAbortCallbackWrapper()).thenReturn(false); - when(() => mockSyncApiRepo.streamChanges(any())) - .thenAnswer((invocation) async { + when(() => mockSyncApiRepo.streamChanges(any())).thenAnswer((invocation) async { + handleEventsCallback = invocation.positionalArguments.first; + }); + + when(() => mockSyncApiRepo.streamChanges(any(), onReset: any(named: 'onReset'))).thenAnswer((invocation) async { handleEventsCallback = invocation.positionalArguments.first; }); when(() => mockSyncApiRepo.ack(any())).thenAnswer((_) async => {}); - when(() => mockSyncStreamRepo.updateUsersV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteUsersV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updatePartnerV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deletePartnerV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateUsersV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePartnerV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePartnerV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateAssetsExifV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsExifV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsExifV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsExifV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateMemoriesV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteMemoriesV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteMemoriesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updatePeopleV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deletePeopleV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePeopleV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePeopleV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetFacesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())).thenAnswer(successHandler); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - ); + sut = SyncStreamService(syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo); }); Future simulateEvents(List events) async { await sut.sync(); - await handleEventsCallback(events, mockAbortCallbackWrapper.call); + await handleEventsCallback(events, mockAbortCallbackWrapper.call, mockResetCallbackWrapper.call); } group("SyncStreamService - _handleEvents", () { - test( - "processes events and acks successfully when handlers succeed", - () async { - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.userV1User, - SyncStreamStub.partnerDeleteV1, - SyncStreamStub.partnerV1, - ]; - - await simulateEvents(events); - - verifyInOrder([ - () => mockSyncStreamRepo.deleteUsersV1(any()), - () => mockSyncApiRepo.ack(["2"]), - () => mockSyncStreamRepo.updateUsersV1(any()), - () => mockSyncApiRepo.ack(["5"]), - () => mockSyncStreamRepo.deletePartnerV1(any()), - () => mockSyncApiRepo.ack(["4"]), - () => mockSyncStreamRepo.updatePartnerV1(any()), - () => mockSyncApiRepo.ack(["3"]), - ]); - verifyNever(() => mockAbortCallbackWrapper()); - }, - ); - - test("processes final batch correctly", () async { + test("processes events and acks successfully when handlers succeed", () async { final events = [ SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, + SyncStreamStub.userV1User, + SyncStreamStub.partnerDeleteV1, + SyncStreamStub.partnerV1, ]; await simulateEvents(events); + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deletePartnerV1(any()), + () => mockSyncApiRepo.ack(["4"]), + () => mockSyncStreamRepo.updatePartnerV1(any()), + () => mockSyncApiRepo.ack(["3"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("processes final batch correctly", () async { + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin]; + + await simulateEvents(events); + verifyInOrder([ () => mockSyncStreamRepo.deleteUsersV1(any()), () => mockSyncApiRepo.ack(["2"]), @@ -188,17 +156,13 @@ void main() { ); await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { when(() => cancellationChecker()).thenReturn(true); }); - await handleEventsCallback(events, mockAbortCallbackWrapper.call); + await handleEventsCallback(events, mockAbortCallbackWrapper.call, mockResetCallbackWrapper.call); verify(() => mockSyncStreamRepo.deleteUsersV1(any())).called(1); verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); @@ -209,52 +173,47 @@ void main() { verify(() => mockSyncApiRepo.ack(["2"])).called(1); }); - test( - "aborts and stops processing if cancelled before processing batch", - () async { - final cancellationChecker = _MockCancellationWrapper(); - when(() => cancellationChecker()).thenReturn(false); + test("aborts and stops processing if cancelled before processing batch", () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); - final processingCompleter = Completer(); - bool handler1Started = false; - when(() => mockSyncStreamRepo.deleteUsersV1(any())) - .thenAnswer((_) async { - handler1Started = true; - return processingCompleter.future; - }); + final processingCompleter = Completer(); + bool handler1Started = false; + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { + handler1Started = true; + return processingCompleter.future; + }); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - cancelChecker: cancellationChecker.call, - ); + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); - await sut.sync(); + await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; - final processingFuture = - handleEventsCallback(events, mockAbortCallbackWrapper.call); - await pumpEventQueue(); + final processingFuture = handleEventsCallback( + events, + mockAbortCallbackWrapper.call, + mockResetCallbackWrapper.call, + ); + await pumpEventQueue(); - expect(handler1Started, isTrue); + expect(handler1Started, isTrue); - // Signal cancellation while handler 1 is waiting - when(() => cancellationChecker()).thenReturn(true); - await pumpEventQueue(); + // Signal cancellation while handler 1 is waiting + when(() => cancellationChecker()).thenReturn(true); + await pumpEventQueue(); - processingCompleter.complete(); - await processingFuture; + processingCompleter.complete(); + await processingFuture; - verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); - verify(() => mockSyncApiRepo.ack(["2"])).called(1); - }, - ); + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }); test("processes memory sync events successfully", () async { final events = [ @@ -303,18 +262,11 @@ void main() { }); test("handles memory sync failure gracefully", () async { - when(() => mockSyncStreamRepo.updateMemoriesV1(any())) - .thenThrow(Exception("Memory sync failed")); + when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenThrow(Exception("Memory sync failed")); - final events = [ - SyncStreamStub.memoryV1, - SyncStreamStub.userV1Admin, - ]; + final events = [SyncStreamStub.memoryV1, SyncStreamStub.userV1Admin]; - expect( - () async => await simulateEvents(events), - throwsA(isA()), - ); + expect(() async => await simulateEvents(events), throwsA(isA())); }); test("processes memory asset events with correct data types", () async { @@ -335,8 +287,7 @@ void main() { verify(() => mockSyncApiRepo.ack(["6"])).called(1); }); - test("processes memory create/update events with correct data types", - () async { + test("processes memory create/update events with correct data types", () async { final events = [SyncStreamStub.memoryV1]; await simulateEvents(events); diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index 5cce565477..395f38a207 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -29,10 +29,8 @@ void main() { ); registerFallbackValue(UserStub.admin); - when(() => mockStoreService.get(StoreKey.currentUser)) - .thenReturn(UserStub.admin); - when(() => mockStoreService.tryGet(StoreKey.currentUser)) - .thenReturn(UserStub.admin); + when(() => mockStoreService.get(StoreKey.currentUser)).thenReturn(UserStub.admin); + when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(UserStub.admin); }); group('getMyUser', () { @@ -42,8 +40,7 @@ void main() { }); test('should handle user not found scenario', () { - when(() => mockStoreService.get(StoreKey.currentUser)) - .thenThrow(Exception('User not found')); + when(() => mockStoreService.get(StoreKey.currentUser)).thenThrow(Exception('User not found')); expect(() => sut.getMyUser(), throwsA(isA())); }); @@ -56,8 +53,7 @@ void main() { }); test('should return null if user not found', () { - when(() => mockStoreService.tryGet(StoreKey.currentUser)) - .thenReturn(null); + when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(null); final result = sut.tryGetMyUser(); expect(result, isNull); }); @@ -65,15 +61,13 @@ void main() { group('watchMyUser', () { test('should return user stream from store', () { - when(() => mockStoreService.watch(StoreKey.currentUser)) - .thenAnswer((_) => Stream.value(UserStub.admin)); + when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => Stream.value(UserStub.admin)); final result = sut.watchMyUser(); expect(result, emits(UserStub.admin)); }); test('should return an empty stream if user not found', () { - when(() => mockStoreService.watch(StoreKey.currentUser)) - .thenAnswer((_) => const Stream.empty()); + when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => const Stream.empty()); final result = sut.watchMyUser(); expect(result, emitsInOrder([])); }); @@ -81,16 +75,12 @@ void main() { group('refreshMyUser', () { test('should return user from api and store it', () async { - when(() => mockUserApiRepo.getMyUser()) - .thenAnswer((_) async => UserStub.admin); - when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)) - .thenAnswer((_) async => true); - when(() => mockUserRepo.update(UserStub.admin)) - .thenAnswer((_) async => UserStub.admin); + when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => UserStub.admin); + when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).thenAnswer((_) async => true); + when(() => mockUserRepo.update(UserStub.admin)).thenAnswer((_) async => UserStub.admin); final result = await sut.refreshMyUser(); - verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)) - .called(1); + verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).called(1); verify(() => mockUserRepo.update(UserStub.admin)).called(1); expect(result, UserStub.admin); }); @@ -99,9 +89,7 @@ void main() { when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null); final result = await sut.refreshMyUser(); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, UserStub.admin), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)); verifyNever(() => mockUserRepo.update(UserStub.admin)); expect(result, isNull); }); @@ -110,46 +98,31 @@ void main() { group('createProfileImage', () { test('should return profile image path', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = - UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenAnswer((_) async => profileImagePath); - when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)) - .thenAnswer((_) async => true); - when(() => mockUserRepo.update(updatedUser)) - .thenAnswer((_) async => UserStub.admin); + when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true); + when(() => mockUserRepo.update(updatedUser)).thenAnswer((_) async => UserStub.admin); - final result = - await sut.createProfileImage(profileImagePath, Uint8List(0)); + final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); - verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)) - .called(1); + verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).called(1); verify(() => mockUserRepo.update(updatedUser)).called(1); expect(result, profileImagePath); }); test('should return null if profile image creation fails', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = - UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenThrow(Exception('Failed to create profile image')); - final result = - await sut.createProfileImage(profileImagePath, Uint8List(0)); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, updatedUser), - ); + final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, updatedUser)); verifyNever(() => mockUserRepo.update(updatedUser)); expect(result, isNull); }); diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index 209e70d788..073a86078f 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -6,6 +6,15 @@ import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; import 'schema_v3.dart' as v3; +import 'schema_v4.dart' as v4; +import 'schema_v5.dart' as v5; +import 'schema_v6.dart' as v6; +import 'schema_v7.dart' as v7; +import 'schema_v8.dart' as v8; +import 'schema_v9.dart' as v9; +import 'schema_v10.dart' as v10; +import 'schema_v11.dart' as v11; +import 'schema_v12.dart' as v12; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -17,10 +26,28 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v2.DatabaseAtV2(db); case 3: return v3.DatabaseAtV3(db); + case 4: + return v4.DatabaseAtV4(db); + case 5: + return v5.DatabaseAtV5(db); + case 6: + return v6.DatabaseAtV6(db); + case 7: + return v7.DatabaseAtV7(db); + case 8: + return v8.DatabaseAtV8(db); + case 9: + return v9.DatabaseAtV9(db); + case 10: + return v10.DatabaseAtV10(db); + case 11: + return v11.DatabaseAtV11(db); + case 12: + return v12.DatabaseAtV12(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2, 3]; + static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; } diff --git a/mobile/test/drift/main/generated/schema_v1.dart b/mobile/test/drift/main/generated/schema_v1.dart index d7b88ea3cf..ca9e6ca1b0 100644 --- a/mobile/test/drift/main/generated/schema_v1.dart +++ b/mobile/test/drift/main/generated/schema_v1.dart @@ -9,48 +9,78 @@ class UserEntity extends Table with TableInfo { final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isAdmin = GeneratedColumn( - 'is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn email = GeneratedColumn( - 'email', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn profileImagePath = GeneratedColumn( - 'profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( - 'quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -62,22 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -101,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -128,8 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -157,29 +206,29 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -215,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -259,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -284,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -358,87 +416,154 @@ class RemoteAssetEntity extends Table final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn localDateTime = - GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbHash = GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn visibility = GeneratedColumn( - 'visibility', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn stackId = GeneratedColumn( - 'stack_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -450,40 +575,74 @@ class RemoteAssetEntity extends Table RemoteAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -517,24 +676,25 @@ class RemoteAssetEntityData extends DataClass final String? livePhotoVideoId; final int visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -574,8 +734,10 @@ class RemoteAssetEntityData extends DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -621,48 +783,49 @@ class RemoteAssetEntityData extends DataClass }; } - RemoteAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -676,8 +839,9 @@ class RemoteAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -687,8 +851,9 @@ class RemoteAssetEntityData extends DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -719,23 +884,24 @@ class RemoteAssetEntityData extends DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -815,12 +981,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -861,24 +1027,25 @@ class RemoteAssetEntityCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -989,62 +1156,103 @@ class LocalAssetEntity extends Table final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1056,28 +1264,50 @@ class LocalAssetEntity extends Table LocalAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1105,18 +1335,19 @@ class LocalAssetEntityData extends DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1142,8 +1373,10 @@ class LocalAssetEntityData extends DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1177,33 +1410,33 @@ class LocalAssetEntityData extends DataClass }; } - LocalAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1217,10 +1450,12 @@ class LocalAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -1243,8 +1478,19 @@ class LocalAssetEntityData extends DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1299,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1330,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1421,33 +1668,56 @@ class StackEntity extends Table with TableInfo { final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1459,16 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1489,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1506,8 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1529,19 +1812,19 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -1599,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1618,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1674,17 +1958,29 @@ class UserMetadataEntity extends Table final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1698,12 +1994,18 @@ class UserMetadataEntity extends Table UserMetadataEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1723,8 +2025,11 @@ class UserMetadataEntityData extends DataClass final String userId; final int key; final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,8 +2039,10 @@ class UserMetadataEntityData extends DataClass return map; } - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1753,13 +2060,15 @@ class UserMetadataEntityData extends DataClass }; } - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1803,9 +2112,9 @@ class UserMetadataEntityCompanion required String userId, required int key, required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); + }) : userId = Value(userId), + key = Value(key), + value = Value(value); static Insertable custom({ Expression? userId, Expression? key, @@ -1818,8 +2127,11 @@ class UserMetadataEntityCompanion }); } - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1860,24 +2172,36 @@ class PartnerEntity extends Table final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1891,12 +2215,18 @@ class PartnerEntity extends Table PartnerEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1916,10 +2246,11 @@ class PartnerEntityData extends DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1929,8 +2260,10 @@ class PartnerEntityData extends DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1948,22 +2281,26 @@ class PartnerEntityData extends DataClass }; } - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2001,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2015,10 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2059,35 +2397,64 @@ class LocalAlbumEntity extends Table final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn backupSelection = GeneratedColumn( - 'backup_selection', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( - 'is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn marker_ = GeneratedColumn( - 'marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2099,18 +2466,30 @@ class LocalAlbumEntity extends Table LocalAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, isIosSharedAlbum: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -2133,13 +2512,14 @@ class LocalAlbumEntityData extends DataClass final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2154,8 +2534,10 @@ class LocalAlbumEntityData extends DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -2179,21 +2561,21 @@ class LocalAlbumEntityData extends DataClass }; } - LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - int? backupSelection, - bool? isIosSharedAlbum, - Value marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -2224,7 +2606,13 @@ class LocalAlbumEntityData extends DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2259,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2280,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2342,17 +2731,25 @@ class LocalAlbumAssetEntity extends Table final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2363,14 +2760,20 @@ class LocalAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2389,8 +2792,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2399,8 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2422,7 +2829,8 @@ class LocalAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2459,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -2471,8 +2879,10 @@ class LocalAlbumAssetEntityCompanion }); } - LocalAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2508,99 +2918,188 @@ class RemoteExifEntity extends Table final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn city = GeneratedColumn( - 'city', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn country = GeneratedColumn( - 'country', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn dateTimeOriginal = - GeneratedColumn('date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn exposureTime = GeneratedColumn( - 'exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn fNumber = GeneratedColumn( - 'f_number', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn fileSize = GeneratedColumn( - 'file_size', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn focalLength = GeneratedColumn( - 'focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn latitude = GeneratedColumn( - 'latitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn longitude = GeneratedColumn( - 'longitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn iso = GeneratedColumn( - 'iso', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn make = GeneratedColumn( - 'make', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn model = GeneratedColumn( - 'model', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn lens = GeneratedColumn( - 'lens', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn timeZone = GeneratedColumn( - 'time_zone', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn rating = GeneratedColumn( - 'rating', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn projectionType = GeneratedColumn( - 'projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2612,50 +3111,94 @@ class RemoteExifEntity extends Table RemoteExifEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2694,29 +3237,30 @@ class RemoteExifEntityData extends DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2787,16 +3331,19 @@ class RemoteExifEntityData extends DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2845,57 +3392,57 @@ class RemoteExifEntityData extends DataClass }; } - RemoteExifEntityData copyWith( - {String? assetId, - Value city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -2905,8 +3452,9 @@ class RemoteExifEntityData extends DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -2914,16 +3462,18 @@ class RemoteExifEntityData extends DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -2963,29 +3513,29 @@ class RemoteExifEntityData extends DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -3135,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -3293,60 +3844,93 @@ class RemoteAlbumEntity extends Table final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const CustomExpression('\'\'')); + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn thumbnailAssetId = GeneratedColumn( - 'thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); late final GeneratedColumn isActivityEnabled = GeneratedColumn( - 'is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); late final GeneratedColumn order = GeneratedColumn( - 'order', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3358,24 +3942,42 @@ class RemoteAlbumEntity extends Table RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3401,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final int order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3428,8 +4031,10 @@ class RemoteAlbumEntityData extends DataClass return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3459,35 +4064,36 @@ class RemoteAlbumEntityData extends DataClass }; } - RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - Value thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -3518,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3567,10 +4182,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3595,16 +4210,17 @@ class RemoteAlbumEntityCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3675,17 +4291,25 @@ class RemoteAlbumAssetEntity extends Table final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3696,14 +4320,20 @@ class RemoteAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3722,8 +4352,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3732,8 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3755,7 +4389,8 @@ class RemoteAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3792,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -3804,8 +4439,10 @@ class RemoteAlbumAssetEntityCompanion }); } - RemoteAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3841,20 +4478,32 @@ class RemoteAlbumUserEntity extends Table final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3865,16 +4514,24 @@ class RemoteAlbumUserEntity extends Table @override Set get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3894,8 +4551,11 @@ class RemoteAlbumUserEntityData extends DataClass final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3905,8 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3924,15 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass }; } - RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, int? role}) => - RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3975,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required int role, - }) : albumId = Value(albumId), - userId = Value(userId), - role = Value(role); + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); static Insertable custom({ Expression? albumId, Expression? userId, @@ -3990,8 +4655,11 @@ class RemoteAlbumUserEntityCompanion }); } - RemoteAlbumUserEntityCompanion copyWith( - {Value? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -4032,67 +4700,113 @@ class MemoryEntity extends Table final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn data = GeneratedColumn( - 'data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isSaved = GeneratedColumn( - 'is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn memoryAt = GeneratedColumn( - 'memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); late final GeneratedColumn seenAt = GeneratedColumn( - 'seen_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn showAt = GeneratedColumn( - 'show_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn hideAt = GeneratedColumn( - 'hide_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4104,30 +4818,54 @@ class MemoryEntity extends Table MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -4156,19 +4894,20 @@ class MemoryEntityData extends DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4195,8 +4934,10 @@ class MemoryEntityData extends DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -4232,33 +4973,33 @@ class MemoryEntityData extends DataClass }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -4296,8 +5037,20 @@ class MemoryEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4356,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4391,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4489,17 +5243,25 @@ class MemoryAssetEntity extends Table final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn memoryId = GeneratedColumn( - 'memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4513,10 +5275,14 @@ class MemoryAssetEntity extends Table MemoryAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4544,8 +5310,10 @@ class MemoryAssetEntityData extends DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4603,8 +5371,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4615,8 +5383,10 @@ class MemoryAssetEntityCompanion }); } - MemoryAssetEntityCompanion copyWith( - {Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4652,65 +5422,107 @@ class PersonEntity extends Table final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn faceAssetId = GeneratedColumn( - 'face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbnailPath = GeneratedColumn( - 'thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); late final GeneratedColumn isHidden = GeneratedColumn( - 'is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); late final GeneratedColumn color = GeneratedColumn( - 'color', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn birthDate = GeneratedColumn( - 'birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - thumbnailPath, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4722,28 +5534,50 @@ class PersonEntity extends Table PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4771,18 +5605,19 @@ class PersonEntityData extends DataClass final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4806,8 +5641,10 @@ class PersonEntityData extends DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4841,31 +5678,31 @@ class PersonEntityData extends DataClass }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4873,13 +5710,15 @@ class PersonEntityData extends DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4905,8 +5744,19 @@ class PersonEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4961,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4995,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -5086,13 +5937,18 @@ class DatabaseAtV1 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); late final Index uQRemoteAssetOwnerChecksum = Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); @@ -5112,25 +5968,25 @@ class DatabaseAtV1 extends GeneratedDatabase { allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 1; @override diff --git a/mobile/test/drift/main/generated/schema_v10.dart b/mobile/test/drift/main/generated/schema_v10.dart new file mode 100644 index 0000000000..ba75530242 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v10.dart @@ -0,0 +1,7159 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + const UserEntityData({ + required this.id, + required this.name, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn linkedRemoteAlbumId = + GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = Variable(linkedRemoteAlbumId); + } + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value linkedRemoteAlbumId = const Value.absent(), + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value linkedRemoteAlbumId; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? linkedRemoteAlbumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? linkedRemoteAlbumId, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = Variable( + linkedRemoteAlbumId.value, + ); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class AuthUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AuthUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinCode = GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'auth_user_entity'; + @override + Set get $primaryKey => {id}; + @override + AuthUserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AuthUserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + )!, + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + pinCode: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pin_code'], + ), + ); + } + + @override + AuthUserEntity createAlias(String alias) { + return AuthUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AuthUserEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String email; + final bool isAdmin; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + final int quotaSizeInBytes; + final int quotaUsageInBytes; + final String? pinCode; + const AuthUserEntityData({ + required this.id, + required this.name, + required this.email, + required this.isAdmin, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + this.pinCode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['is_admin'] = Variable(isAdmin); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + if (!nullToAbsent || pinCode != null) { + map['pin_code'] = Variable(pinCode); + } + return map; + } + + factory AuthUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AuthUserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + isAdmin: serializer.fromJson(json['isAdmin']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + pinCode: serializer.fromJson(json['pinCode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'isAdmin': serializer.toJson(isAdmin), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'pinCode': serializer.toJson(pinCode), + }; + } + + AuthUserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? isAdmin, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + int? quotaSizeInBytes, + int? quotaUsageInBytes, + Value pinCode = const Value.absent(), + }) => AuthUserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode.present ? pinCode.value : this.pinCode, + ); + AuthUserEntityData copyWithCompanion(AuthUserEntityCompanion data) { + return AuthUserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode, + ); + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AuthUserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.isAdmin == this.isAdmin && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes && + other.pinCode == this.pinCode); +} + +class AuthUserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value isAdmin; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + final Value pinCode; + const AuthUserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }); + AuthUserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + required int avatarColor, + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email), + avatarColor = Value(avatarColor); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? isAdmin, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + Expression? pinCode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (isAdmin != null) 'is_admin': isAdmin, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (pinCode != null) 'pin_code': pinCode, + }); + } + + AuthUserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? isAdmin, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + Value? pinCode, + }) { + return AuthUserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode ?? this.pinCode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + if (pinCode.present) { + map['pin_code'] = Variable(pinCode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV10 extends GeneratedDatabase { + DatabaseAtV10(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final AuthUserEntity authUserEntity = AuthUserEntity(this); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 10; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v11.dart b/mobile/test/drift/main/generated/schema_v11.dart new file mode 100644 index 0000000000..fe27f1bb3d --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v11.dart @@ -0,0 +1,7198 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + const UserEntityData({ + required this.id, + required this.name, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn linkedRemoteAlbumId = + GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = Variable(linkedRemoteAlbumId); + } + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value linkedRemoteAlbumId = const Value.absent(), + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value linkedRemoteAlbumId; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? linkedRemoteAlbumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? linkedRemoteAlbumId, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = Variable( + linkedRemoteAlbumId.value, + ); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [assetId, albumId, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + final bool? marker_; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumAssetEntityData copyWith({ + String? assetId, + String? albumId, + Value marker_ = const Value.absent(), + }) => LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId && + other.marker_ == this.marker_); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + final Value marker_; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + this.marker_ = const Value.absent(), + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + Value? marker_, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class AuthUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AuthUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinCode = GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'auth_user_entity'; + @override + Set get $primaryKey => {id}; + @override + AuthUserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AuthUserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + )!, + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + pinCode: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pin_code'], + ), + ); + } + + @override + AuthUserEntity createAlias(String alias) { + return AuthUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AuthUserEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String email; + final bool isAdmin; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + final int quotaSizeInBytes; + final int quotaUsageInBytes; + final String? pinCode; + const AuthUserEntityData({ + required this.id, + required this.name, + required this.email, + required this.isAdmin, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + this.pinCode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['is_admin'] = Variable(isAdmin); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + if (!nullToAbsent || pinCode != null) { + map['pin_code'] = Variable(pinCode); + } + return map; + } + + factory AuthUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AuthUserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + isAdmin: serializer.fromJson(json['isAdmin']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + pinCode: serializer.fromJson(json['pinCode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'isAdmin': serializer.toJson(isAdmin), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'pinCode': serializer.toJson(pinCode), + }; + } + + AuthUserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? isAdmin, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + int? quotaSizeInBytes, + int? quotaUsageInBytes, + Value pinCode = const Value.absent(), + }) => AuthUserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode.present ? pinCode.value : this.pinCode, + ); + AuthUserEntityData copyWithCompanion(AuthUserEntityCompanion data) { + return AuthUserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode, + ); + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AuthUserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.isAdmin == this.isAdmin && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes && + other.pinCode == this.pinCode); +} + +class AuthUserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value isAdmin; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + final Value pinCode; + const AuthUserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }); + AuthUserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + required int avatarColor, + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email), + avatarColor = Value(avatarColor); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? isAdmin, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + Expression? pinCode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (isAdmin != null) 'is_admin': isAdmin, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (pinCode != null) 'pin_code': pinCode, + }); + } + + AuthUserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? isAdmin, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + Value? pinCode, + }) { + return AuthUserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode ?? this.pinCode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + if (pinCode.present) { + map['pin_code'] = Variable(pinCode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV11 extends GeneratedDatabase { + DatabaseAtV11(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final AuthUserEntity authUserEntity = AuthUserEntity(this); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 11; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v12.dart b/mobile/test/drift/main/generated/schema_v12.dart new file mode 100644 index 0000000000..c42df284ec --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v12.dart @@ -0,0 +1,7198 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + const UserEntityData({ + required this.id, + required this.name, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + hasProfileImage, + profileChangedAt, + avatarColor, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn linkedRemoteAlbumId = + GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = Variable(linkedRemoteAlbumId); + } + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value linkedRemoteAlbumId = const Value.absent(), + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value linkedRemoteAlbumId; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? linkedRemoteAlbumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? linkedRemoteAlbumId, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = Variable( + linkedRemoteAlbumId.value, + ); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [assetId, albumId, marker_]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + final bool? marker_; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumAssetEntityData copyWith({ + String? assetId, + String? albumId, + Value marker_ = const Value.absent(), + }) => LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId, marker_); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId && + other.marker_ == this.marker_); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + final Value marker_; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + this.marker_ = const Value.absent(), + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + Value? marker_, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class AuthUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AuthUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn avatarColor = GeneratedColumn( + 'avatar_color', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn pinCode = GeneratedColumn( + 'pin_code', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'auth_user_entity'; + @override + Set get $primaryKey => {id}; + @override + AuthUserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AuthUserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + avatarColor: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}avatar_color'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + )!, + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + pinCode: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}pin_code'], + ), + ); + } + + @override + AuthUserEntity createAlias(String alias) { + return AuthUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AuthUserEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String email; + final bool isAdmin; + final bool hasProfileImage; + final DateTime profileChangedAt; + final int avatarColor; + final int quotaSizeInBytes; + final int quotaUsageInBytes; + final String? pinCode; + const AuthUserEntityData({ + required this.id, + required this.name, + required this.email, + required this.isAdmin, + required this.hasProfileImage, + required this.profileChangedAt, + required this.avatarColor, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + this.pinCode, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['email'] = Variable(email); + map['is_admin'] = Variable(isAdmin); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['avatar_color'] = Variable(avatarColor); + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + if (!nullToAbsent || pinCode != null) { + map['pin_code'] = Variable(pinCode); + } + return map; + } + + factory AuthUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AuthUserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + email: serializer.fromJson(json['email']), + isAdmin: serializer.fromJson(json['isAdmin']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + avatarColor: serializer.fromJson(json['avatarColor']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + pinCode: serializer.fromJson(json['pinCode']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'email': serializer.toJson(email), + 'isAdmin': serializer.toJson(isAdmin), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'avatarColor': serializer.toJson(avatarColor), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + 'pinCode': serializer.toJson(pinCode), + }; + } + + AuthUserEntityData copyWith({ + String? id, + String? name, + String? email, + bool? isAdmin, + bool? hasProfileImage, + DateTime? profileChangedAt, + int? avatarColor, + int? quotaSizeInBytes, + int? quotaUsageInBytes, + Value pinCode = const Value.absent(), + }) => AuthUserEntityData( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode.present ? pinCode.value : this.pinCode, + ); + AuthUserEntityData copyWithCompanion(AuthUserEntityCompanion data) { + return AuthUserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + email: data.email.present ? data.email.value : this.email, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + avatarColor: data.avatarColor.present + ? data.avatarColor.value + : this.avatarColor, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + pinCode: data.pinCode.present ? data.pinCode.value : this.pinCode, + ); + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + email, + isAdmin, + hasProfileImage, + profileChangedAt, + avatarColor, + quotaSizeInBytes, + quotaUsageInBytes, + pinCode, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AuthUserEntityData && + other.id == this.id && + other.name == this.name && + other.email == this.email && + other.isAdmin == this.isAdmin && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.avatarColor == this.avatarColor && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes && + other.pinCode == this.pinCode); +} + +class AuthUserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value email; + final Value isAdmin; + final Value hasProfileImage; + final Value profileChangedAt; + final Value avatarColor; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + final Value pinCode; + const AuthUserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.email = const Value.absent(), + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.avatarColor = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }); + AuthUserEntityCompanion.insert({ + required String id, + required String name, + required String email, + this.isAdmin = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + required int avatarColor, + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + this.pinCode = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email), + avatarColor = Value(avatarColor); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? email, + Expression? isAdmin, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? avatarColor, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + Expression? pinCode, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (email != null) 'email': email, + if (isAdmin != null) 'is_admin': isAdmin, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (avatarColor != null) 'avatar_color': avatarColor, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + if (pinCode != null) 'pin_code': pinCode, + }); + } + + AuthUserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? email, + Value? isAdmin, + Value? hasProfileImage, + Value? profileChangedAt, + Value? avatarColor, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + Value? pinCode, + }) { + return AuthUserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + email: email ?? this.email, + isAdmin: isAdmin ?? this.isAdmin, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + avatarColor: avatarColor ?? this.avatarColor, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + pinCode: pinCode ?? this.pinCode, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (avatarColor.present) { + map['avatar_color'] = Variable(avatarColor.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + if (pinCode.present) { + map['pin_code'] = Variable(pinCode.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AuthUserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('email: $email, ') + ..write('isAdmin: $isAdmin, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('avatarColor: $avatarColor, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes, ') + ..write('pinCode: $pinCode') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV12 extends GeneratedDatabase { + DatabaseAtV12(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final AuthUserEntity authUserEntity = AuthUserEntity(this); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + authUserEntity, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 12; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v2.dart b/mobile/test/drift/main/generated/schema_v2.dart index e3edac3501..903d13b9a2 100644 --- a/mobile/test/drift/main/generated/schema_v2.dart +++ b/mobile/test/drift/main/generated/schema_v2.dart @@ -9,48 +9,78 @@ class UserEntity extends Table with TableInfo { final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isAdmin = GeneratedColumn( - 'is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn email = GeneratedColumn( - 'email', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn profileImagePath = GeneratedColumn( - 'profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( - 'quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -62,22 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -101,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -128,8 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -157,29 +206,29 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -215,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -259,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -284,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -358,87 +416,154 @@ class RemoteAssetEntity extends Table final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn localDateTime = - GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbHash = GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn visibility = GeneratedColumn( - 'visibility', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn stackId = GeneratedColumn( - 'stack_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -450,40 +575,74 @@ class RemoteAssetEntity extends Table RemoteAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -517,24 +676,25 @@ class RemoteAssetEntityData extends DataClass final String? livePhotoVideoId; final int visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -574,8 +734,10 @@ class RemoteAssetEntityData extends DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -621,48 +783,49 @@ class RemoteAssetEntityData extends DataClass }; } - RemoteAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -676,8 +839,9 @@ class RemoteAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -687,8 +851,9 @@ class RemoteAssetEntityData extends DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -719,23 +884,24 @@ class RemoteAssetEntityData extends DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -815,12 +981,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -861,24 +1027,25 @@ class RemoteAssetEntityCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -989,62 +1156,103 @@ class LocalAssetEntity extends Table final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1056,28 +1264,50 @@ class LocalAssetEntity extends Table LocalAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1105,18 +1335,19 @@ class LocalAssetEntityData extends DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1142,8 +1373,10 @@ class LocalAssetEntityData extends DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1177,33 +1410,33 @@ class LocalAssetEntityData extends DataClass }; } - LocalAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1217,10 +1450,12 @@ class LocalAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -1243,8 +1478,19 @@ class LocalAssetEntityData extends DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1299,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1330,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1421,33 +1668,56 @@ class StackEntity extends Table with TableInfo { final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1459,16 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1489,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1506,8 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1529,19 +1812,19 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -1599,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1618,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1674,17 +1958,29 @@ class UserMetadataEntity extends Table final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1698,12 +1994,18 @@ class UserMetadataEntity extends Table UserMetadataEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1723,8 +2025,11 @@ class UserMetadataEntityData extends DataClass final String userId; final int key; final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,8 +2039,10 @@ class UserMetadataEntityData extends DataClass return map; } - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1753,13 +2060,15 @@ class UserMetadataEntityData extends DataClass }; } - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1803,9 +2112,9 @@ class UserMetadataEntityCompanion required String userId, required int key, required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); + }) : userId = Value(userId), + key = Value(key), + value = Value(value); static Insertable custom({ Expression? userId, Expression? key, @@ -1818,8 +2127,11 @@ class UserMetadataEntityCompanion }); } - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1860,24 +2172,36 @@ class PartnerEntity extends Table final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1891,12 +2215,18 @@ class PartnerEntity extends Table PartnerEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1916,10 +2246,11 @@ class PartnerEntityData extends DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1929,8 +2260,10 @@ class PartnerEntityData extends DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1948,22 +2281,26 @@ class PartnerEntityData extends DataClass }; } - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2001,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2015,10 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2059,35 +2397,64 @@ class LocalAlbumEntity extends Table final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn backupSelection = GeneratedColumn( - 'backup_selection', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( - 'is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn marker_ = GeneratedColumn( - 'marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2099,18 +2466,30 @@ class LocalAlbumEntity extends Table LocalAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, isIosSharedAlbum: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -2133,13 +2512,14 @@ class LocalAlbumEntityData extends DataClass final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2154,8 +2534,10 @@ class LocalAlbumEntityData extends DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -2179,21 +2561,21 @@ class LocalAlbumEntityData extends DataClass }; } - LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - int? backupSelection, - bool? isIosSharedAlbum, - Value marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -2224,7 +2606,13 @@ class LocalAlbumEntityData extends DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2259,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2280,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2342,17 +2731,25 @@ class LocalAlbumAssetEntity extends Table final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2363,14 +2760,20 @@ class LocalAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2389,8 +2792,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2399,8 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2422,7 +2829,8 @@ class LocalAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2459,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -2471,8 +2879,10 @@ class LocalAlbumAssetEntityCompanion }); } - LocalAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2508,99 +2918,188 @@ class RemoteExifEntity extends Table final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn city = GeneratedColumn( - 'city', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn country = GeneratedColumn( - 'country', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn dateTimeOriginal = - GeneratedColumn('date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn exposureTime = GeneratedColumn( - 'exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn fNumber = GeneratedColumn( - 'f_number', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn fileSize = GeneratedColumn( - 'file_size', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn focalLength = GeneratedColumn( - 'focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn latitude = GeneratedColumn( - 'latitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn longitude = GeneratedColumn( - 'longitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn iso = GeneratedColumn( - 'iso', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn make = GeneratedColumn( - 'make', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn model = GeneratedColumn( - 'model', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn lens = GeneratedColumn( - 'lens', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn timeZone = GeneratedColumn( - 'time_zone', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn rating = GeneratedColumn( - 'rating', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn projectionType = GeneratedColumn( - 'projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2612,50 +3111,94 @@ class RemoteExifEntity extends Table RemoteExifEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2694,29 +3237,30 @@ class RemoteExifEntityData extends DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2787,16 +3331,19 @@ class RemoteExifEntityData extends DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2845,57 +3392,57 @@ class RemoteExifEntityData extends DataClass }; } - RemoteExifEntityData copyWith( - {String? assetId, - Value city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -2905,8 +3452,9 @@ class RemoteExifEntityData extends DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -2914,16 +3462,18 @@ class RemoteExifEntityData extends DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -2963,29 +3513,29 @@ class RemoteExifEntityData extends DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -3135,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -3293,60 +3844,93 @@ class RemoteAlbumEntity extends Table final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const CustomExpression('\'\'')); + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn thumbnailAssetId = GeneratedColumn( - 'thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); late final GeneratedColumn isActivityEnabled = GeneratedColumn( - 'is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); late final GeneratedColumn order = GeneratedColumn( - 'order', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3358,24 +3942,42 @@ class RemoteAlbumEntity extends Table RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3401,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final int order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3428,8 +4031,10 @@ class RemoteAlbumEntityData extends DataClass return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3459,35 +4064,36 @@ class RemoteAlbumEntityData extends DataClass }; } - RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - Value thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -3518,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3567,10 +4182,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3595,16 +4210,17 @@ class RemoteAlbumEntityCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3675,17 +4291,25 @@ class RemoteAlbumAssetEntity extends Table final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3696,14 +4320,20 @@ class RemoteAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3722,8 +4352,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3732,8 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3755,7 +4389,8 @@ class RemoteAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3792,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -3804,8 +4439,10 @@ class RemoteAlbumAssetEntityCompanion }); } - RemoteAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3841,20 +4478,32 @@ class RemoteAlbumUserEntity extends Table final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3865,16 +4514,24 @@ class RemoteAlbumUserEntity extends Table @override Set get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3894,8 +4551,11 @@ class RemoteAlbumUserEntityData extends DataClass final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3905,8 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3924,15 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass }; } - RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, int? role}) => - RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3975,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required int role, - }) : albumId = Value(albumId), - userId = Value(userId), - role = Value(role); + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); static Insertable custom({ Expression? albumId, Expression? userId, @@ -3990,8 +4655,11 @@ class RemoteAlbumUserEntityCompanion }); } - RemoteAlbumUserEntityCompanion copyWith( - {Value? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -4032,67 +4700,113 @@ class MemoryEntity extends Table final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn data = GeneratedColumn( - 'data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isSaved = GeneratedColumn( - 'is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn memoryAt = GeneratedColumn( - 'memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); late final GeneratedColumn seenAt = GeneratedColumn( - 'seen_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn showAt = GeneratedColumn( - 'show_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn hideAt = GeneratedColumn( - 'hide_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4104,30 +4818,54 @@ class MemoryEntity extends Table MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -4156,19 +4894,20 @@ class MemoryEntityData extends DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4195,8 +4934,10 @@ class MemoryEntityData extends DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -4232,33 +4973,33 @@ class MemoryEntityData extends DataClass }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -4296,8 +5037,20 @@ class MemoryEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4356,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4391,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4489,17 +5243,25 @@ class MemoryAssetEntity extends Table final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn memoryId = GeneratedColumn( - 'memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4513,10 +5275,14 @@ class MemoryAssetEntity extends Table MemoryAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4544,8 +5310,10 @@ class MemoryAssetEntityData extends DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4603,8 +5371,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4615,8 +5383,10 @@ class MemoryAssetEntityCompanion }); } - MemoryAssetEntityCompanion copyWith( - {Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4652,65 +5422,107 @@ class PersonEntity extends Table final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn faceAssetId = GeneratedColumn( - 'face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbnailPath = GeneratedColumn( - 'thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); late final GeneratedColumn isHidden = GeneratedColumn( - 'is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); late final GeneratedColumn color = GeneratedColumn( - 'color', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn birthDate = GeneratedColumn( - 'birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - thumbnailPath, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4722,28 +5534,50 @@ class PersonEntity extends Table PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4771,18 +5605,19 @@ class PersonEntityData extends DataClass final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4806,8 +5641,10 @@ class PersonEntityData extends DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4841,31 +5678,31 @@ class PersonEntityData extends DataClass }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4873,13 +5710,15 @@ class PersonEntityData extends DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4905,8 +5744,19 @@ class PersonEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4961,12 +5811,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4995,18 +5845,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -5086,13 +5937,18 @@ class DatabaseAtV2 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); late final Index uQRemoteAssetOwnerChecksum = Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); @@ -5112,25 +5968,25 @@ class DatabaseAtV2 extends GeneratedDatabase { allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 2; @override diff --git a/mobile/test/drift/main/generated/schema_v3.dart b/mobile/test/drift/main/generated/schema_v3.dart index 8f655b3f7d..e4382a9fb9 100644 --- a/mobile/test/drift/main/generated/schema_v3.dart +++ b/mobile/test/drift/main/generated/schema_v3.dart @@ -9,48 +9,78 @@ class UserEntity extends Table with TableInfo { final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isAdmin = GeneratedColumn( - 'is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn email = GeneratedColumn( - 'email', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn profileImagePath = GeneratedColumn( - 'profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( - 'quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -62,22 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -101,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -128,8 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -157,29 +206,29 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -215,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -259,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -284,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -358,87 +416,154 @@ class RemoteAssetEntity extends Table final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn localDateTime = - GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbHash = GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn visibility = GeneratedColumn( - 'visibility', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn stackId = GeneratedColumn( - 'stack_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -450,40 +575,74 @@ class RemoteAssetEntity extends Table RemoteAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -517,24 +676,25 @@ class RemoteAssetEntityData extends DataClass final String? livePhotoVideoId; final int visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -574,8 +734,10 @@ class RemoteAssetEntityData extends DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -621,48 +783,49 @@ class RemoteAssetEntityData extends DataClass }; } - RemoteAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -676,8 +839,9 @@ class RemoteAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -687,8 +851,9 @@ class RemoteAssetEntityData extends DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -719,23 +884,24 @@ class RemoteAssetEntityData extends DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -815,12 +981,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -861,24 +1027,25 @@ class RemoteAssetEntityCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -989,62 +1156,103 @@ class LocalAssetEntity extends Table final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1056,28 +1264,50 @@ class LocalAssetEntity extends Table LocalAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1105,18 +1335,19 @@ class LocalAssetEntityData extends DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1142,8 +1373,10 @@ class LocalAssetEntityData extends DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1177,33 +1410,33 @@ class LocalAssetEntityData extends DataClass }; } - LocalAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1217,10 +1450,12 @@ class LocalAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -1243,8 +1478,19 @@ class LocalAssetEntityData extends DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1299,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1330,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1421,30 +1668,53 @@ class StackEntity extends Table with TableInfo { final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1456,16 +1726,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1486,12 +1766,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1503,8 +1784,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1526,19 +1809,19 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -1596,9 +1879,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1615,12 +1898,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1671,17 +1955,29 @@ class UserMetadataEntity extends Table final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1695,12 +1991,18 @@ class UserMetadataEntity extends Table UserMetadataEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1720,8 +2022,11 @@ class UserMetadataEntityData extends DataClass final String userId; final int key; final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1731,8 +2036,10 @@ class UserMetadataEntityData extends DataClass return map; } - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1750,13 +2057,15 @@ class UserMetadataEntityData extends DataClass }; } - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1800,9 +2109,9 @@ class UserMetadataEntityCompanion required String userId, required int key, required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); + }) : userId = Value(userId), + key = Value(key), + value = Value(value); static Insertable custom({ Expression? userId, Expression? key, @@ -1815,8 +2124,11 @@ class UserMetadataEntityCompanion }); } - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1857,24 +2169,36 @@ class PartnerEntity extends Table final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1888,12 +2212,18 @@ class PartnerEntity extends Table PartnerEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1913,10 +2243,11 @@ class PartnerEntityData extends DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1926,8 +2257,10 @@ class PartnerEntityData extends DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1945,22 +2278,26 @@ class PartnerEntityData extends DataClass }; } - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -1998,8 +2335,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2012,10 +2349,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2056,35 +2394,64 @@ class LocalAlbumEntity extends Table final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn backupSelection = GeneratedColumn( - 'backup_selection', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( - 'is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn marker_ = GeneratedColumn( - 'marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2096,18 +2463,30 @@ class LocalAlbumEntity extends Table LocalAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, isIosSharedAlbum: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -2130,13 +2509,14 @@ class LocalAlbumEntityData extends DataClass final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2151,8 +2531,10 @@ class LocalAlbumEntityData extends DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -2176,21 +2558,21 @@ class LocalAlbumEntityData extends DataClass }; } - LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - int? backupSelection, - bool? isIosSharedAlbum, - Value marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -2221,7 +2603,13 @@ class LocalAlbumEntityData extends DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2256,9 +2644,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2277,13 +2665,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2339,17 +2728,25 @@ class LocalAlbumAssetEntity extends Table final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2360,14 +2757,20 @@ class LocalAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2386,8 +2789,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2396,8 +2801,10 @@ class LocalAlbumAssetEntityData extends DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2419,7 +2826,8 @@ class LocalAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2456,8 +2864,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -2468,8 +2876,10 @@ class LocalAlbumAssetEntityCompanion }); } - LocalAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2505,99 +2915,188 @@ class RemoteExifEntity extends Table final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn city = GeneratedColumn( - 'city', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn country = GeneratedColumn( - 'country', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn dateTimeOriginal = - GeneratedColumn('date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn exposureTime = GeneratedColumn( - 'exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn fNumber = GeneratedColumn( - 'f_number', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn fileSize = GeneratedColumn( - 'file_size', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn focalLength = GeneratedColumn( - 'focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn latitude = GeneratedColumn( - 'latitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn longitude = GeneratedColumn( - 'longitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn iso = GeneratedColumn( - 'iso', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn make = GeneratedColumn( - 'make', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn model = GeneratedColumn( - 'model', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn lens = GeneratedColumn( - 'lens', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn timeZone = GeneratedColumn( - 'time_zone', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn rating = GeneratedColumn( - 'rating', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn projectionType = GeneratedColumn( - 'projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2609,50 +3108,94 @@ class RemoteExifEntity extends Table RemoteExifEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2691,29 +3234,30 @@ class RemoteExifEntityData extends DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2784,16 +3328,19 @@ class RemoteExifEntityData extends DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2842,57 +3389,57 @@ class RemoteExifEntityData extends DataClass }; } - RemoteExifEntityData copyWith( - {String? assetId, - Value city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -2902,8 +3449,9 @@ class RemoteExifEntityData extends DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -2911,16 +3459,18 @@ class RemoteExifEntityData extends DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -2960,29 +3510,29 @@ class RemoteExifEntityData extends DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -3132,29 +3682,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -3290,60 +3841,93 @@ class RemoteAlbumEntity extends Table final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const CustomExpression('\'\'')); + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn thumbnailAssetId = GeneratedColumn( - 'thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); late final GeneratedColumn isActivityEnabled = GeneratedColumn( - 'is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); late final GeneratedColumn order = GeneratedColumn( - 'order', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3355,24 +3939,42 @@ class RemoteAlbumEntity extends Table RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3398,16 +4000,17 @@ class RemoteAlbumEntityData extends DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final int order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3425,8 +4028,10 @@ class RemoteAlbumEntityData extends DataClass return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3456,35 +4061,36 @@ class RemoteAlbumEntityData extends DataClass }; } - RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - Value thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -3515,8 +4121,17 @@ class RemoteAlbumEntityData extends DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3564,10 +4179,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3592,16 +4207,17 @@ class RemoteAlbumEntityCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3672,17 +4288,25 @@ class RemoteAlbumAssetEntity extends Table final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3693,14 +4317,20 @@ class RemoteAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3719,8 +4349,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3729,8 +4361,10 @@ class RemoteAlbumAssetEntityData extends DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3752,7 +4386,8 @@ class RemoteAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3789,8 +4424,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -3801,8 +4436,10 @@ class RemoteAlbumAssetEntityCompanion }); } - RemoteAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3838,20 +4475,32 @@ class RemoteAlbumUserEntity extends Table final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3862,16 +4511,24 @@ class RemoteAlbumUserEntity extends Table @override Set get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3891,8 +4548,11 @@ class RemoteAlbumUserEntityData extends DataClass final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3902,8 +4562,10 @@ class RemoteAlbumUserEntityData extends DataClass return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3921,15 +4583,18 @@ class RemoteAlbumUserEntityData extends DataClass }; } - RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, int? role}) => - RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3972,9 +4637,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required int role, - }) : albumId = Value(albumId), - userId = Value(userId), - role = Value(role); + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); static Insertable custom({ Expression? albumId, Expression? userId, @@ -3987,8 +4652,11 @@ class RemoteAlbumUserEntityCompanion }); } - RemoteAlbumUserEntityCompanion copyWith( - {Value? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -4029,67 +4697,113 @@ class MemoryEntity extends Table final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn data = GeneratedColumn( - 'data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isSaved = GeneratedColumn( - 'is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn memoryAt = GeneratedColumn( - 'memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); late final GeneratedColumn seenAt = GeneratedColumn( - 'seen_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn showAt = GeneratedColumn( - 'show_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn hideAt = GeneratedColumn( - 'hide_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4101,30 +4815,54 @@ class MemoryEntity extends Table MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -4153,19 +4891,20 @@ class MemoryEntityData extends DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4192,8 +4931,10 @@ class MemoryEntityData extends DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -4229,33 +4970,33 @@ class MemoryEntityData extends DataClass }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -4293,8 +5034,20 @@ class MemoryEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4353,11 +5106,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4388,19 +5141,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4486,17 +5240,25 @@ class MemoryAssetEntity extends Table final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn memoryId = GeneratedColumn( - 'memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4510,10 +5272,14 @@ class MemoryAssetEntity extends Table MemoryAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4541,8 +5307,10 @@ class MemoryAssetEntityData extends DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4600,8 +5368,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4612,8 +5380,10 @@ class MemoryAssetEntityCompanion }); } - MemoryAssetEntityCompanion copyWith( - {Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4649,65 +5419,107 @@ class PersonEntity extends Table final String? _alias; PersonEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn faceAssetId = GeneratedColumn( - 'face_asset_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbnailPath = GeneratedColumn( - 'thumbnail_path', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); late final GeneratedColumn isHidden = GeneratedColumn( - 'is_hidden', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: true, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_hidden" IN (0, 1))')); + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); late final GeneratedColumn color = GeneratedColumn( - 'color', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn birthDate = GeneratedColumn( - 'birth_date', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - ownerId, - name, - faceAssetId, - thumbnailPath, - isFavorite, - isHidden, - color, - birthDate - ]; + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4719,28 +5531,50 @@ class PersonEntity extends Table PersonEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PersonEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - faceAssetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}face_asset_id']), - thumbnailPath: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumbnail_path'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - isHidden: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_hidden'])!, - color: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}color']), - birthDate: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}birth_date']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), ); } @@ -4768,18 +5602,19 @@ class PersonEntityData extends DataClass final bool isHidden; final String? color; final DateTime? birthDate; - const PersonEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.name, - this.faceAssetId, - required this.thumbnailPath, - required this.isFavorite, - required this.isHidden, - this.color, - this.birthDate}); + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4803,8 +5638,10 @@ class PersonEntityData extends DataClass return map; } - factory PersonEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PersonEntityData( id: serializer.fromJson(json['id']), @@ -4838,31 +5675,31 @@ class PersonEntityData extends DataClass }; } - PersonEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? name, - Value faceAssetId = const Value.absent(), - String? thumbnailPath, - bool? isFavorite, - bool? isHidden, - Value color = const Value.absent(), - Value birthDate = const Value.absent()}) => - PersonEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - name: name ?? this.name, - faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, - thumbnailPath: thumbnailPath ?? this.thumbnailPath, - isFavorite: isFavorite ?? this.isFavorite, - isHidden: isHidden ?? this.isHidden, - color: color.present ? color.value : this.color, - birthDate: birthDate.present ? birthDate.value : this.birthDate, - ); + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); PersonEntityData copyWithCompanion(PersonEntityCompanion data) { return PersonEntityData( id: data.id.present ? data.id.value : this.id, @@ -4870,13 +5707,15 @@ class PersonEntityData extends DataClass updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, name: data.name.present ? data.name.value : this.name, - faceAssetId: - data.faceAssetId.present ? data.faceAssetId.value : this.faceAssetId, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, thumbnailPath: data.thumbnailPath.present ? data.thumbnailPath.value : this.thumbnailPath, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, color: data.color.present ? data.color.value : this.color, birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, @@ -4902,8 +5741,19 @@ class PersonEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, ownerId, name, - faceAssetId, thumbnailPath, isFavorite, isHidden, color, birthDate); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4958,12 +5808,12 @@ class PersonEntityCompanion extends UpdateCompanion { required bool isHidden, this.color = const Value.absent(), this.birthDate = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - name = Value(name), - thumbnailPath = Value(thumbnailPath), - isFavorite = Value(isFavorite), - isHidden = Value(isHidden); + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4992,18 +5842,19 @@ class PersonEntityCompanion extends UpdateCompanion { }); } - PersonEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? name, - Value? faceAssetId, - Value? thumbnailPath, - Value? isFavorite, - Value? isHidden, - Value? color, - Value? birthDate}) { + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { return PersonEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -5083,13 +5934,18 @@ class DatabaseAtV3 extends GeneratedDatabase { late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); late final Index uQRemoteAssetOwnerChecksum = Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); @@ -5109,25 +5965,25 @@ class DatabaseAtV3 extends GeneratedDatabase { allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity, - personEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 3; @override diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart new file mode 100644 index 0000000000..9eabea714f --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -0,0 +1,6441 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV4 extends GeneratedDatabase { + DatabaseAtV4(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 4; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v5.dart b/mobile/test/drift/main/generated/schema_v5.dart new file mode 100644 index 0000000000..5c94ff26cb --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v5.dart @@ -0,0 +1,6402 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV5 extends GeneratedDatabase { + DatabaseAtV5(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 5; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v6.dart b/mobile/test/drift/main/generated/schema_v6.dart new file mode 100644 index 0000000000..97b91caa07 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v6.dart @@ -0,0 +1,6448 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV6 extends GeneratedDatabase { + DatabaseAtV6(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 6; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v7.dart b/mobile/test/drift/main/generated/schema_v7.dart new file mode 100644 index 0000000000..276a0cdc74 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v7.dart @@ -0,0 +1,6453 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV7 extends GeneratedDatabase { + DatabaseAtV7(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 7; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v8.dart b/mobile/test/drift/main/generated/schema_v8.dart new file mode 100644 index 0000000000..13520e6372 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v8.dart @@ -0,0 +1,6663 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV8 extends GeneratedDatabase { + DatabaseAtV8(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 8; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v9.dart b/mobile/test/drift/main/generated/schema_v9.dart new file mode 100644 index 0000000000..c2ccb58737 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v9.dart @@ -0,0 +1,6712 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn linkedRemoteAlbumId = + GeneratedColumn( + 'linked_remote_album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + linkedRemoteAlbumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}linked_remote_album_id'], + ), + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final String? linkedRemoteAlbumId; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.linkedRemoteAlbumId, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || linkedRemoteAlbumId != null) { + map['linked_remote_album_id'] = Variable(linkedRemoteAlbumId); + } + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + linkedRemoteAlbumId: serializer.fromJson( + json['linkedRemoteAlbumId'], + ), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'linkedRemoteAlbumId': serializer.toJson(linkedRemoteAlbumId), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value linkedRemoteAlbumId = const Value.absent(), + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId.present + ? linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + linkedRemoteAlbumId: data.linkedRemoteAlbumId.present + ? data.linkedRemoteAlbumId.value + : this.linkedRemoteAlbumId, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + linkedRemoteAlbumId, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.linkedRemoteAlbumId == this.linkedRemoteAlbumId && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value linkedRemoteAlbumId; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.linkedRemoteAlbumId = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? linkedRemoteAlbumId, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (linkedRemoteAlbumId != null) + 'linked_remote_album_id': linkedRemoteAlbumId, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? linkedRemoteAlbumId, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (linkedRemoteAlbumId.present) { + map['linked_remote_album_id'] = Variable( + linkedRemoteAlbumId.value, + ); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class StoreEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StoreEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stringValue = GeneratedColumn( + 'string_value', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn intValue = GeneratedColumn( + 'int_value', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, stringValue, intValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + Set get $primaryKey => {id}; + @override + StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StoreEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + stringValue: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}string_value'], + ), + intValue: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}int_value'], + ), + ); + } + + @override + StoreEntity createAlias(String alias) { + return StoreEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends DataClass implements Insertable { + final int id; + final String? stringValue; + final int? intValue; + const StoreEntityData({required this.id, this.stringValue, this.intValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + if (!nullToAbsent || stringValue != null) { + map['string_value'] = Variable(stringValue); + } + if (!nullToAbsent || intValue != null) { + map['int_value'] = Variable(intValue); + } + return map; + } + + factory StoreEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + stringValue: serializer.fromJson(json['stringValue']), + intValue: serializer.fromJson(json['intValue']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'stringValue': serializer.toJson(stringValue), + 'intValue': serializer.toJson(intValue), + }; + } + + StoreEntityData copyWith({ + int? id, + Value stringValue = const Value.absent(), + Value intValue = const Value.absent(), + }) => StoreEntityData( + id: id ?? this.id, + stringValue: stringValue.present ? stringValue.value : this.stringValue, + intValue: intValue.present ? intValue.value : this.intValue, + ); + StoreEntityData copyWithCompanion(StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + stringValue: data.stringValue.present + ? data.stringValue.value + : this.stringValue, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, stringValue, intValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StoreEntityData && + other.id == this.id && + other.stringValue == this.stringValue && + other.intValue == this.intValue); +} + +class StoreEntityCompanion extends UpdateCompanion { + final Value id; + final Value stringValue; + final Value intValue; + const StoreEntityCompanion({ + this.id = const Value.absent(), + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.stringValue = const Value.absent(), + this.intValue = const Value.absent(), + }) : id = Value(id); + static Insertable custom({ + Expression? id, + Expression? stringValue, + Expression? intValue, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (stringValue != null) 'string_value': stringValue, + if (intValue != null) 'int_value': intValue, + }); + } + + StoreEntityCompanion copyWith({ + Value? id, + Value? stringValue, + Value? intValue, + }) { + return StoreEntityCompanion( + id: id ?? this.id, + stringValue: stringValue ?? this.stringValue, + intValue: intValue ?? this.intValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (stringValue.present) { + map['string_value'] = Variable(stringValue.value); + } + if (intValue.present) { + map['int_value'] = Variable(intValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('stringValue: $stringValue, ') + ..write('intValue: $intValue') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV9 extends GeneratedDatabase { + DatabaseAtV9(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = Index( + 'UQ_remote_assets_owner_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = Index( + 'UQ_remote_assets_owner_library_checksum', + 'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + late final StoreEntity storeEntity = StoreEntity(this); + late final Index idxLatLng = Index( + 'idx_lat_lng', + 'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + remoteAlbumEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + storeEntity, + idxLatLng, + ]; + @override + int get schemaVersion => 9; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index 1e79f62faf..a22a4b72ab 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -42,20 +42,21 @@ final class AlbumStub { endDate: DateTime(2023), )..assets.addAll([AssetStub.image1]); - static final twoAsset = Album( - name: "album-with-two-assets", - localId: "album-with-two-assets-local", - remoteId: "album-with-two-assets-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..assets.addAll([AssetStub.image1, AssetStub.image2]) - ..activityEnabled = true - ..owner.value = User.fromDto(UserStub.admin); + static final twoAsset = + Album( + name: "album-with-two-assets", + localId: "album-with-two-assets-local", + remoteId: "album-with-two-assets-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..assets.addAll([AssetStub.image1, AssetStub.image2]) + ..activityEnabled = true + ..owner.value = User.fromDto(UserStub.admin); static final create2020end2020Album = Album( name: "create2020update2020Album", diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index de2d58bc9d..23c750d6d9 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -9,6 +9,9 @@ abstract final class SyncStreamStub { email: "admin@admin", id: "1", name: "Admin", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), ), ack: "1", ); @@ -19,6 +22,9 @@ abstract final class SyncStreamStub { email: "user@user", id: "5", name: "User", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), ), ack: "5", ); @@ -30,11 +36,7 @@ abstract final class SyncStreamStub { static final partnerV1 = SyncEvent( type: SyncEntityType.partnerV1, - data: SyncPartnerV1( - inTimeline: true, - sharedById: "1", - sharedWithId: "2", - ), + data: SyncPartnerV1(inTimeline: true, sharedById: "1", sharedWithId: "2"), ack: "3", ); static final partnerDeleteV1 = SyncEvent( @@ -70,19 +72,13 @@ abstract final class SyncStreamStub { static final memoryToAssetV1 = SyncEvent( type: SyncEntityType.memoryToAssetV1, - data: SyncMemoryAssetV1( - assetId: "asset-1", - memoryId: "memory-1", - ), + data: SyncMemoryAssetV1(assetId: "asset-1", memoryId: "memory-1"), ack: "7", ); static final memoryToAssetDeleteV1 = SyncEvent( type: SyncEntityType.memoryToAssetDeleteV1, - data: SyncMemoryAssetDeleteV1( - assetId: "asset-2", - memoryId: "memory-1", - ), + data: SyncMemoryAssetDeleteV1(assetId: "asset-2", memoryId: "memory-1"), ack: "8", ); } diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 764342520f..2ba7177f89 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -1,5 +1,4 @@ import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/domain/models/user_metadata.model.dart'; abstract final class UserStub { const UserStub._(); @@ -10,7 +9,7 @@ abstract final class UserStub { name: "admin", isAdmin: true, updatedAt: DateTime(2021), - profileImagePath: null, + profileChangedAt: DateTime(2021), avatarColor: AvatarColor.green, ); @@ -20,7 +19,7 @@ abstract final class UserStub { name: "user1", isAdmin: false, updatedAt: DateTime(2022), - profileImagePath: null, + profileChangedAt: DateTime(2022), avatarColor: AvatarColor.red, ); @@ -30,7 +29,7 @@ abstract final class UserStub { name: "user2", isAdmin: false, updatedAt: DateTime(2023), - profileImagePath: null, + profileChangedAt: DateTime(2023), avatarColor: AvatarColor.primary, ); } diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index bab25de52a..fae0e09171 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -12,49 +12,21 @@ void main() { late MediumFactory mediumFactory; setUp(() { - db = Drift( - DatabaseConnection( - NativeDatabase.memory(), - closeStreamsSynchronously: true, - ), - ); + db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); mediumFactory = MediumFactory(db); }); group('getAll', () { test('sorts albums by backupSelection & isIosSharedAlbum', () async { - final localAlbumRepo = - mediumFactory.getRepository(); + final localAlbumRepo = mediumFactory.getRepository(); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '1', backupSelection: BackupSelection.none)); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '2', backupSelection: BackupSelection.excluded)); await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '1', - backupSelection: BackupSelection.none, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '2', - backupSelection: BackupSelection.excluded, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '3', - backupSelection: BackupSelection.selected, - isIosSharedAlbum: true, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '4', - backupSelection: BackupSelection.selected, - ), + mediumFactory.localAlbum(id: '3', backupSelection: BackupSelection.selected, isIosSharedAlbum: true), ); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '4', backupSelection: BackupSelection.selected)); final albums = await localAlbumRepo.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); expect(albums.length, 4); expect(albums[0].id, '4'); // selected diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index ce13c1ecdd..f6424beabc 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -24,16 +24,8 @@ Future _addStrStoreValue(Isar db, StoreKey key, String? value) async { Future _populateStore(Isar db) async { await db.writeTxn(() async { - await _addIntStoreValue( - db, - StoreKey.colorfulInterface, - _kTestColorfulInterface ? 1 : 0, - ); - await _addIntStoreValue( - db, - StoreKey.backupFailedSince, - _kTestBackupFailed.millisecondsSinceEpoch, - ); + await _addIntStoreValue(db, StoreKey.colorfulInterface, _kTestColorfulInterface ? 1 : 0); + await _addIntStoreValue(db, StoreKey.backupFailedSince, _kTestBackupFailed.millisecondsSinceEpoch); await _addStrStoreValue(db, StoreKey.accessToken, _kTestAccessToken); await _addIntStoreValue(db, StoreKey.version, _kTestVersion); }); @@ -52,7 +44,7 @@ void main() { test('converts int', () async { int? version = await sut.tryGet(StoreKey.version); expect(version, isNull); - await sut.insert(StoreKey.version, _kTestVersion); + await sut.upsert(StoreKey.version, _kTestVersion); version = await sut.tryGet(StoreKey.version); expect(version, _kTestVersion); }); @@ -60,16 +52,15 @@ void main() { test('converts string', () async { String? accessToken = await sut.tryGet(StoreKey.accessToken); expect(accessToken, isNull); - await sut.insert(StoreKey.accessToken, _kTestAccessToken); + await sut.upsert(StoreKey.accessToken, _kTestAccessToken); accessToken = await sut.tryGet(StoreKey.accessToken); expect(accessToken, _kTestAccessToken); }); test('converts datetime', () async { - DateTime? backupFailedSince = - await sut.tryGet(StoreKey.backupFailedSince); + DateTime? backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince); expect(backupFailedSince, isNull); - await sut.insert(StoreKey.backupFailedSince, _kTestBackupFailed); + await sut.upsert(StoreKey.backupFailedSince, _kTestBackupFailed); backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince); expect(backupFailedSince, _kTestBackupFailed); }); @@ -77,7 +68,7 @@ void main() { test('converts bool', () async { bool? colorfulInterface = await sut.tryGet(StoreKey.colorfulInterface); expect(colorfulInterface, isNull); - await sut.insert(StoreKey.colorfulInterface, _kTestColorfulInterface); + await sut.upsert(StoreKey.colorfulInterface, _kTestColorfulInterface); colorfulInterface = await sut.tryGet(StoreKey.colorfulInterface); expect(colorfulInterface, _kTestColorfulInterface); }); @@ -85,7 +76,7 @@ void main() { test('converts user', () async { UserDto? user = await sut.tryGet(StoreKey.currentUser); expect(user, isNull); - await sut.insert(StoreKey.currentUser, _kTestUser); + await sut.upsert(StoreKey.currentUser, _kTestUser); user = await sut.tryGet(StoreKey.currentUser); expect(user, _kTestUser); }); @@ -117,10 +108,10 @@ void main() { await _populateStore(db); }); - test('update()', () async { + test('upsert()', () async { int? version = await sut.tryGet(StoreKey.version); expect(version, _kTestVersion); - await sut.update(StoreKey.version, _kTestVersion + 10); + await sut.upsert(StoreKey.version, _kTestVersion + 10); version = await sut.tryGet(StoreKey.version); expect(version, _kTestVersion + 10); }); @@ -135,33 +126,29 @@ void main() { final stream = sut.watch(StoreKey.version); expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10])); await pumpEventQueue(); - await sut.update(StoreKey.version, _kTestVersion + 10); + await sut.upsert(StoreKey.version, _kTestVersion + 10); }); test('watchAll()', () async { final stream = sut.watchAll(); expectLater( stream, - emitsInAnyOrder([ - emits(const StoreDto(StoreKey.version, _kTestVersion)), - emits( + emitsInOrder([ + [ + const StoreDto(StoreKey.version, _kTestVersion), StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - ), - emits( const StoreDto(StoreKey.accessToken, _kTestAccessToken), - ), - emits( - const StoreDto( - StoreKey.colorfulInterface, - _kTestColorfulInterface, - ), - ), - emits( + const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), + ], + [ const StoreDto(StoreKey.version, _kTestVersion + 10), - ), + StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), + const StoreDto(StoreKey.accessToken, _kTestAccessToken), + const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface), + ], ]), ); - await sut.update(StoreKey.version, _kTestVersion + 10); + await sut.upsert(StoreKey.version, _kTestVersion + 10); }); }); } diff --git a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart index 55b03a8116..467e19bf3f 100644 --- a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart +++ b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart @@ -4,12 +4,15 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:immich_mobile/domain/models/sync_event.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:openapi/api.dart'; import '../../api.mocks.dart'; import '../../service.mocks.dart'; +import '../../test_utils.dart'; class MockHttpClient extends Mock implements http.Client {} @@ -33,29 +36,29 @@ void main() { late StreamController> responseStreamController; late int testBatchSize = 3; + setUpAll(() async { + await StoreService.init(storeRepository: IsarStoreRepository(await TestUtils.initIsar())); + }); + setUp(() { mockApiService = MockApiService(); mockApiClient = MockApiClient(); mockSyncApi = MockSyncApi(); mockHttpClient = MockHttpClient(); mockStreamedResponse = MockStreamedResponse(); - responseStreamController = - StreamController>.broadcast(sync: true); + responseStreamController = StreamController>.broadcast(sync: true); registerFallbackValue(FakeBaseRequest()); when(() => mockApiService.apiClient).thenReturn(mockApiClient); when(() => mockApiService.syncApi).thenReturn(mockSyncApi); when(() => mockApiClient.basePath).thenReturn('http://demo.immich.app/api'); - when(() => mockApiService.applyToParams(any(), any())) - .thenAnswer((_) async => {}); + when(() => mockApiService.applyToParams(any(), any())).thenAnswer((_) async => {}); // Mock HTTP client behavior - when(() => mockHttpClient.send(any())) - .thenAnswer((_) async => mockStreamedResponse); + when(() => mockHttpClient.send(any())).thenAnswer((_) async => mockStreamedResponse); when(() => mockStreamedResponse.statusCode).thenReturn(200); - when(() => mockStreamedResponse.stream) - .thenAnswer((_) => http.ByteStream(responseStreamController.stream)); + when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(responseStreamController.stream)); when(() => mockHttpClient.close()).thenAnswer((_) => {}); sut = SyncApiRepository(mockApiService); @@ -68,13 +71,9 @@ void main() { }); Future streamChanges( - Function(List, Function() abort) onDataCallback, + Future Function(List, Function() abort, Function() reset) onDataCallback, ) { - return sut.streamChanges( - onDataCallback, - batchSize: testBatchSize, - httpClient: mockHttpClient, - ); + return sut.streamChanges(onDataCallback, batchSize: testBatchSize, httpClient: mockHttpClient); } test('streamChanges stops processing stream when abort is called', () async { @@ -82,7 +81,7 @@ void main() { bool abortWasCalledInCallback = false; List receivedEventsBatch1 = []; - onDataCallback(List events, Function() abort) { + Future onDataCallback(List events, Function() abort, Function() _) async { onDataCallCount++; if (onDataCallCount == 1) { receivedEventsBatch1 = events; @@ -100,11 +99,7 @@ void main() { for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -112,11 +107,7 @@ void main() { for (int i = testBatchSize; i < testBatchSize * 2; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -130,119 +121,97 @@ void main() { verify(() => mockHttpClient.close()).called(1); }); - test( - 'streamChanges does not process remaining lines in finally block if aborted', - () async { - int onDataCallCount = 0; - bool abortWasCalledInCallback = false; + test('streamChanges does not process remaining lines in finally block if aborted', () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; - onDataCallback(List events, Function() abort) { - onDataCallCount++; - if (onDataCallCount == 1) { - abort(); - abortWasCalledInCallback = true; - } else { - fail("onData called more than once after abort was invoked"); - } + Future onDataCallback(List events, Function() abort, Function() _) async { + onDataCallCount++; + if (onDataCallCount == 1) { + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // emit a single event to skip batching and trigger finally + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // emit a single event to skip batching and trigger finally + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 1); - expect(abortWasCalledInCallback, isTrue); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); - test( - 'streamChanges processes remaining lines in finally block if not aborted', - () async { - int onDataCallCount = 0; - List receivedEventsBatch1 = []; - List receivedEventsBatch2 = []; + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + verify(() => mockHttpClient.close()).called(1); + }); - onDataCallback(List events, Function() _) { - onDataCallCount++; - if (onDataCallCount == 1) { - receivedEventsBatch1 = events; - } else if (onDataCallCount == 2) { - receivedEventsBatch2 = events; - } else { - fail("onData called more than expected"); - } + test('streamChanges processes remaining lines in finally block if not aborted', () async { + int onDataCallCount = 0; + List receivedEventsBatch1 = []; + List receivedEventsBatch2 = []; + + Future onDataCallback(List events, Function() _, Function() __) async { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + } else if (onDataCallCount == 2) { + receivedEventsBatch2 = events; + } else { + fail("onData called more than expected"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - // Batch 1 - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // Partial Batch 2 + // Batch 1 + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // Partial Batch 2 + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 2); - expect(receivedEventsBatch1.length, testBatchSize); - expect(receivedEventsBatch2.length, 1); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 2); + expect(receivedEventsBatch1.length, testBatchSize); + expect(receivedEventsBatch2.length, 1); + verify(() => mockHttpClient.close()).called(1); + }); test('streamChanges handles stream error gracefully', () async { final streamError = Exception("Network Error"); int onDataCallCount = 0; - onDataCallback(List events, Function() _) { + Future onDataCallback(List events, Function() _, Function() __) async { onDataCallCount++; } @@ -252,11 +221,7 @@ void main() { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user1").toJson(), - 'ack1', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user1").toJson(), 'ack1'), ), ); @@ -270,12 +235,10 @@ void main() { test('streamChanges throws ApiException on non-200 status code', () async { when(() => mockStreamedResponse.statusCode).thenReturn(401); final errorBodyController = StreamController>(sync: true); - when(() => mockStreamedResponse.stream) - .thenAnswer((_) => http.ByteStream(errorBodyController.stream)); + when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(errorBodyController.stream)); int onDataCallCount = 0; - - onDataCallback(List events, Function() _) { + Future onDataCallback(List events, Function() _, Function() __) async { onDataCallCount++; } diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index 1fde303863..1b66451dda 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -2,30 +2,33 @@ import 'package:immich_mobile/infrastructure/repositories/device_asset.repositor import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; +import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:mocktail/mocktail.dart'; class MockStoreRepository extends Mock implements IsarStoreRepository {} -class MockLogRepository extends Mock implements IsarLogRepository {} +class MockDriftStoreRepository extends Mock implements DriftStoreRepository {} + +class MockLogRepository extends Mock implements LogRepository {} class MockIsarUserRepository extends Mock implements IsarUserRepository {} -class MockDeviceAssetRepository extends Mock - implements IsarDeviceAssetRepository {} +class MockDeviceAssetRepository extends Mock implements IsarDeviceAssetRepository {} class MockSyncStreamRepository extends Mock implements SyncStreamRepository {} -class MockLocalAlbumRepository extends Mock - implements DriftLocalAlbumRepository {} +class MockLocalAlbumRepository extends Mock implements DriftLocalAlbumRepository {} -class MockLocalAssetRepository extends Mock - implements DriftLocalAssetRepository {} +class MockRemoteAlbumRepository extends Mock implements DriftRemoteAlbumRepository {} + +class MockLocalAssetRepository extends Mock implements DriftLocalAssetRepository {} class MockStorageRepository extends Mock implements StorageRepository {} @@ -33,3 +36,5 @@ class MockStorageRepository extends Mock implements StorageRepository {} class MockUserApiRepository extends Mock implements UserApiRepository {} class MockSyncApiRepository extends Mock implements SyncApiRepository {} + +class MockDriftAlbumApiRepository extends Mock implements DriftAlbumApiRepository {} diff --git a/mobile/test/mock_http_override.dart b/mobile/test/mock_http_override.dart index c25fb79b50..877d6f8726 100644 --- a/mobile/test/mock_http_override.dart +++ b/mobile/test/mock_http_override.dart @@ -18,15 +18,12 @@ class MockHttpOverrides extends HttpOverrides { // Request mocks when(() => request.headers).thenAnswer((_) => headers); - when(() => request.close()) - .thenAnswer((_) => Future.value(response)); + when(() => request.close()).thenAnswer((_) => Future.value(response)); // Response mocks when(() => response.statusCode).thenReturn(HttpStatus.ok); - when(() => response.compressionState) - .thenReturn(HttpClientResponseCompressionState.decompressed); - when(() => response.contentLength) - .thenAnswer((_) => kTransparentImage.length); + when(() => response.compressionState).thenReturn(HttpClientResponseCompressionState.decompressed); + when(() => response.contentLength).thenAnswer((_) => kTransparentImage.length); when( () => response.listen( captureAny(), @@ -35,23 +32,17 @@ class MockHttpOverrides extends HttpOverrides { onError: captureAny(named: 'onError'), ), ).thenAnswer((invocation) { - final onData = - invocation.positionalArguments[0] as void Function(List); + final onData = invocation.positionalArguments[0] as void Function(List); final onDone = invocation.namedArguments[#onDone] as void Function(); - final onError = invocation.namedArguments[#onError] as void - Function(Object, [StackTrace]); + final onError = invocation.namedArguments[#onError] as void Function(Object, [StackTrace]); final cancelOnError = invocation.namedArguments[#cancelOnError] as bool; - return Stream>.fromIterable([kTransparentImage.toList()]) - .listen( - onData, - onDone: onDone, - onError: onError, - cancelOnError: cancelOnError, - ); + return Stream>.fromIterable([ + kTransparentImage.toList(), + ]).listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); return client; diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index cf9238d205..05eac98111 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -49,19 +49,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.user2, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.user2), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -85,10 +74,7 @@ void main() { mockCurrentAssetProvider = MockCurrentAssetProvider(AssetStub.image1); activityMock = MockAlbumActivity(_activities); overrides = [ - albumActivityProvider( - AlbumStub.twoAsset.remoteId!, - AssetStub.image1.remoteId!, - ).overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!, AssetStub.image1.remoteId!).overrideWith(() => activityMock), currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), currentAssetProvider.overrideWith(() => mockCurrentAssetProvider), ]; @@ -108,148 +94,82 @@ void main() { }); group("App bar", () { - testWidgets( - "No title when currentAsset != null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); + testWidgets("No title when currentAsset != null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNull); - }, - ); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNull); + }); - testWidgets( - "Album name as title when currentAsset == null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Album name as title when currentAsset == null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - mockCurrentAssetProvider.state = null; - await tester.pumpAndSettle(); + mockCurrentAssetProvider.state = null; + await tester.pumpAndSettle(); - expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNotNull); - }, - ); + expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNotNull); + }); }); group("Body", () { - testWidgets( - "Contains a stack with Activity List and Activity Input", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Contains a stack with Activity List and Activity Input", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ActivityTextField), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ActivityTextField)), findsOneWidget); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ), - findsOneWidget, - ); - }, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ListView)), findsOneWidget); + }); - testWidgets( - "List Contains all dismissible activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("List Contains all dismissible activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final listFinder = find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ); - final listChildren = find.descendant( - of: listFinder, - matching: find.byType(DismissibleActivity), - ); - expect(listChildren, findsNWidgets(_activities.length)); - }, - ); + final listFinder = find.descendant(of: find.byType(Stack), matching: find.byType(ListView)); + final listChildren = find.descendant(of: listFinder, matching: find.byType(DismissibleActivity)); + expect(listChildren, findsNWidgets(_activities.length)); + }); - testWidgets( - "Submitting text input adds a comment with the text", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Submitting text input adds a comment with the text", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - when(() => activityMock.addComment(any())) - .thenAnswer((_) => Future.value()); + when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); - final textField = find.byType(TextField); - await tester.enterText(textField, 'Test comment'); - await tester.testTextInput.receiveAction(TextInputAction.done); + final textField = find.byType(TextField); + await tester.enterText(textField, 'Test comment'); + await tester.testTextInput.receiveAction(TextInputAction.done); - verify(() => activityMock.addComment('Test comment')); - }, - ); + verify(() => activityMock.addComment('Test comment')); + }); - testWidgets( - "Owner can remove all activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Owner can remove all activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect(deletableActivityFinder, findsNWidgets(_activities.length)); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.length)); + }); - testWidgets( - "Non-Owner can remove only their activities", - (tester) async { - final mockCurrentUser = MockCurrentUserProvider(); + testWidgets("Non-Owner can remove only their activities", (tester) async { + final mockCurrentUser = MockCurrentUserProvider(); - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: [ - ...overrides, - currentUserProvider.overrideWith((ref) => mockCurrentUser), - ], - ); - mockCurrentUser.state = UserStub.user1; - await tester.pumpAndSettle(); + await tester.pumpConsumerWidget( + const ActivitiesPage(), + overrides: [...overrides, currentUserProvider.overrideWith((ref) => mockCurrentUser)], + ); + mockCurrentUser.state = UserStub.user1; + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect( - deletableActivityFinder, - findsNWidgets( - _activities.where((a) => a.user == UserStub.user1).length, - ), - ); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.where((a) => a.user == UserStub.user1).length)); + }); }); } diff --git a/mobile/test/modules/activity/activity_mocks.dart b/mobile/test/modules/activity/activity_mocks.dart index 22fbafdbf3..c50810795e 100644 --- a/mobile/test/modules/activity/activity_mocks.dart +++ b/mobile/test/modules/activity/activity_mocks.dart @@ -6,9 +6,7 @@ import 'package:mocktail/mocktail.dart'; class ActivityServiceMock extends Mock implements ActivityService {} -class MockAlbumActivity extends AlbumActivityInternal - with Mock - implements AlbumActivity { +class MockAlbumActivity extends AlbumActivityInternal with Mock implements AlbumActivity { List? initActivities; MockAlbumActivity([this.initActivities]); @@ -18,6 +16,4 @@ class MockAlbumActivity extends AlbumActivityInternal } } -class ActivityStatisticsMock extends ActivityStatisticsInternal - with Mock - implements ActivityStatistics {} +class ActivityStatisticsMock extends ActivityStatisticsInternal with Mock implements ActivityStatistics {} diff --git a/mobile/test/modules/activity/activity_provider_test.dart b/mobile/test/modules/activity/activity_provider_test.dart index a3b3e2466e..7964b43cad 100644 --- a/mobile/test/modules/activity/activity_provider_test.dart +++ b/mobile/test/modules/activity/activity_provider_test.dart @@ -26,19 +26,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.admin, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.admin), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -58,8 +47,7 @@ void main() { container = TestUtils.createContainer( overrides: [ activityServiceProvider.overrideWith((ref) => activityMock), - activityStatisticsProvider('test-album', 'test-asset') - .overrideWith(() => activityStatisticsMock), + activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock), ], ); @@ -71,11 +59,7 @@ void main() { // Init and wait for providers future to complete provider = albumActivityProvider('test-album', 'test-asset'); listener = ListenerMock(); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); await container.read(provider.future); }); @@ -84,19 +68,14 @@ void main() { verifyInOrder([ () => listener.call(null, const AsyncLoading()), () => listener.call( - const AsyncLoading(), - any( - that: allOf( - [ - isA>>(), - predicate( - (AsyncData> ad) => - ad.requireValue.every((e) => _activities.contains(e)), - ), - ], - ), - ), - ), + const AsyncLoading(), + any( + that: allOf([ + isA>>(), + predicate((AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e))), + ]), + ), + ), ]); verifyNoMoreInteractions(listener); @@ -104,30 +83,15 @@ void main() { group('addLike()', () { test('Like successfully added', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), ).thenAnswer((_) async => AsyncData(like)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(5)); @@ -138,31 +102,14 @@ void main() { }); test('Like failed', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ).thenAnswer( - (_) async => AsyncError(Exception('Mock'), StackTrace.current), - ); + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), + ).thenAnswer((_) async => AsyncError(Exception('Mock'), StackTrace.current)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(4)); @@ -172,50 +119,36 @@ void main() { group('removeActivity()', () { test('Like successfully removed', () async { - when(() => activityMock.removeActivity('3')) - .thenAnswer((_) async => true); + when(() => activityMock.removeActivity('3')).thenAnswer((_) async => true); await container.read(provider.notifier).removeActivity('3'); - verify( - () => activityMock.removeActivity('3'), - ); + verify(() => activityMock.removeActivity('3')); final activities = await container.read(provider.future); expect(activities, hasLength(3)); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '3'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '3')))); verifyNever(() => activityStatisticsMock.removeActivity()); }); test('Remove Like failed', () async { - when(() => activityMock.removeActivity('3')) - .thenAnswer((_) async => false); + when(() => activityMock.removeActivity('3')).thenAnswer((_) async => false); await container.read(provider.notifier).removeActivity('3'); final activities = await container.read(provider.future); expect(activities, hasLength(4)); - expect( - activities, - anyElement(predicate((Activity a) => a.id == '3')), - ); + expect(activities, anyElement(predicate((Activity a) => a.id == '3'))); }); test('Comment successfully removed', () async { - when(() => activityMock.removeActivity('1')) - .thenAnswer((_) async => true); + when(() => activityMock.removeActivity('1')).thenAnswer((_) async => true); await container.read(provider.notifier).removeActivity('1'); final activities = await container.read(provider.future); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '1'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '1')))); verify(() => activityStatisticsMock.removeActivity()); }); @@ -229,10 +162,8 @@ void main() { container = TestUtils.createContainer( overrides: [ activityServiceProvider.overrideWith((ref) => activityMock), - activityStatisticsProvider('test-album', 'test-asset') - .overrideWith(() => activityStatisticsMock), - activityStatisticsProvider('test-album') - .overrideWith(() => albumActivityStatisticsMock), + activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock), + activityStatisticsProvider('test-album').overrideWith(() => albumActivityStatisticsMock), ], ); }); @@ -255,8 +186,7 @@ void main() { comment: 'Test-Comment', ), ).thenAnswer((_) async => AsyncData(comment)); - when(() => activityStatisticsMock.build('test-album', 'test-asset')) - .thenReturn(4); + when(() => activityStatisticsMock.build('test-album', 'test-asset')).thenReturn(4); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); await container.read(provider.notifier).addComment('Test-Comment'); @@ -289,26 +219,16 @@ void main() { ); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, comment: 'Test-Comment'), ).thenAnswer((_) async => AsyncData(comment)); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); - when(() => activityMock.getAllActivities('test-album')) - .thenAnswer((_) async => [..._activities]); + when(() => activityMock.getAllActivities('test-album')).thenAnswer((_) async => [..._activities]); final albumProvider = albumActivityProvider('test-album'); await container.read(albumProvider.notifier).addComment('Test-Comment'); verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - assetId: null, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, assetId: null, comment: 'Test-Comment'), ); final activities = await container.read(albumProvider.future); @@ -336,9 +256,7 @@ void main() { assetId: 'test-asset', comment: 'Test-Comment', ), - ).thenAnswer( - (_) async => AsyncError(Exception('Error'), StackTrace.current), - ); + ).thenAnswer((_) async => AsyncError(Exception('Error'), StackTrace.current)); await container.read(provider.notifier).addComment('Test-Comment'); diff --git a/mobile/test/modules/activity/activity_statistics_provider_test.dart b/mobile/test/modules/activity/activity_statistics_provider_test.dart index 0216528ddd..7fe73868f5 100644 --- a/mobile/test/modules/activity/activity_statistics_provider_test.dart +++ b/mobile/test/modules/activity/activity_statistics_provider_test.dart @@ -15,11 +15,7 @@ void main() { setUp(() async { activityMock = ActivityServiceMock(); - container = TestUtils.createContainer( - overrides: [ - activityServiceProvider.overrideWith((ref) => activityMock), - ], - ); + container = TestUtils.createContainer(overrides: [activityServiceProvider.overrideWith((ref) => activityMock)]); listener = ListenerMock(); }); @@ -31,34 +27,21 @@ void main() { // Read here to make the getStatistics call container.read(activityStatisticsProvider('test-album', 'test-asset')); - container.listen( - activityStatisticsProvider('test-album', 'test-asset'), - listener.call, - fireImmediately: true, - ); + container.listen(activityStatisticsProvider('test-album', 'test-asset'), listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); - verifyInOrder([ - () => listener.call(null, 0), - () => listener.call(0, 5), - ]); + verifyInOrder([() => listener.call(null, 0), () => listener.call(0, 5)]); verifyNoMoreInteractions(listener); }); test('Adds activity', () async { - when( - () => activityMock.getStatistics('test-album'), - ).thenAnswer((_) async => const ActivityStats(comments: 10)); + when(() => activityMock.getStatistics('test-album')).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('test-album'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); @@ -75,11 +58,7 @@ void main() { ).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('new-album', 'test-asset'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); diff --git a/mobile/test/modules/activity/activity_text_field_test.dart b/mobile/test/modules/activity/activity_text_field_test.dart index a124af0db9..1163330c54 100644 --- a/mobile/test/modules/activity/activity_text_field_test.dart +++ b/mobile/test/modules/activity/activity_text_field_test.dart @@ -44,18 +44,12 @@ void main() { activityMock = MockAlbumActivity(); overrides = [ currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), - albumActivityProvider(AlbumStub.twoAsset.remoteId!) - .overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!).overrideWith(() => activityMock), ]; }); testWidgets('Returns an Input text field', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(TextField), findsOneWidget); }); @@ -64,76 +58,38 @@ void main() { final userProvider = MockCurrentUserProvider(); await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: [ - currentUserProvider.overrideWith((ref) => userProvider), - ...overrides, - ], + ActivityTextField(onSubmit: (_) {}), + overrides: [currentUserProvider.overrideWith((ref) => userProvider), ...overrides], ); expect(find.byType(UserCircleAvatar), findsNothing); }); testWidgets('UserCircleAvatar displayed when user != null', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(UserCircleAvatar), findsOneWidget); }); - testWidgets( - 'Filled icon if likedId != null', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: '1', - ), - overrides: overrides, - ); - - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsNothing, - ); - }, - ); - - testWidgets('Bordered icon if likedId == null', (tester) async { + testWidgets('Filled icon if likedId != null', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), + ActivityTextField(onSubmit: (_) {}, likeId: '1'), overrides: overrides, ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsNothing, - ); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsNothing); + }); + + testWidgets('Bordered icon if likedId == null', (tester) async { + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); + + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsNothing); }); testWidgets('Adds new like', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); when(() => activityMock.addLike()).thenAnswer((_) => Future.value()); @@ -145,15 +101,11 @@ void main() { testWidgets('Removes like if already liked', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (_) {}, likeId: 'test-suffix'), overrides: overrides, ); - when(() => activityMock.removeActivity(any())) - .thenAnswer((_) => Future.value()); + when(() => activityMock.removeActivity(any())).thenAnswer((_) => Future.value()); final suffixIcon = find.byType(IconButton); await tester.tap(suffixIcon); @@ -165,10 +117,7 @@ void main() { String? receivedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receivedText = text, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receivedText = text, likeId: 'test-suffix'), overrides: overrides, ); @@ -182,11 +131,7 @@ void main() { String? receviedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receviedText = text, - isEnabled: false, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receviedText = text, isEnabled: false, likeId: 'test-suffix'), overrides: overrides, ); diff --git a/mobile/test/modules/activity/activity_tile_test.dart b/mobile/test/modules/activity/activity_tile_test.dart index 22dd606540..eb4bb25848 100644 --- a/mobile/test/modules/activity/activity_tile_test.dart +++ b/mobile/test/modules/activity/activity_tile_test.dart @@ -43,31 +43,16 @@ void main() { testWidgets('Returns a ListTile', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); expect(find.byType(ListTile), findsOneWidget); }); - testWidgets('No trailing widget when activity assetId == null', - (tester) async { + testWidgets('No trailing widget when activity assetId == null', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -75,18 +60,10 @@ void main() { expect(listTile.trailing, isNull); }); - testWidgets( - 'Asset Thumbanil as trailing widget when activity assetId != null', - (tester) async { + testWidgets('Asset Thumbanil as trailing widget when activity assetId != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -99,13 +76,7 @@ void main() { testWidgets('No trailing widget when current asset != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -118,37 +89,23 @@ void main() { }); group('Like Activity', () { - final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ); + final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); testWidgets('Like contains filled heart as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); // Leading widget should not be null final listTile = tester.widget(find.byType(ListTile)); expect(listTile.leading, isNotNull); // And should have a favorite icon - final favoIconFinder = find.widgetWithIcon( - listTile.leading!.runtimeType, - Icons.favorite_rounded, - ); + final favoIconFinder = find.widgetWithIcon(listTile.leading!.runtimeType, Icons.favorite_rounded); expect(favoIconFinder, findsOneWidget); }); testWidgets('Like title is center aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -156,10 +113,7 @@ void main() { }); testWidgets('No subtitle for likes', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -176,12 +130,8 @@ void main() { user: UserStub.admin, ); - testWidgets('Comment contains User Circle Avatar as leading', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + testWidgets('Comment contains User Circle Avatar as leading', (tester) async { + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final userAvatarFinder = find.byType(UserCircleAvatar); expect(userAvatarFinder, findsOneWidget); @@ -196,10 +146,7 @@ void main() { }); testWidgets('Comment title is top aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -207,21 +154,12 @@ void main() { }); testWidgets('Contains comment text as subtitle', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); expect(listTile.subtitle, isNotNull); - expect( - find.descendant( - of: find.byType(ListTile), - matching: find.text(activity.comment!), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(ListTile), matching: find.text(activity.comment!)), findsOneWidget); }); }); } diff --git a/mobile/test/modules/activity/dismissible_activity_test.dart b/mobile/test/modules/activity/dismissible_activity_test.dart index 7bfa400a37..e5f6258ee9 100644 --- a/mobile/test/modules/activity/dismissible_activity_test.dart +++ b/mobile/test/modules/activity/dismissible_activity_test.dart @@ -15,12 +15,7 @@ import '../../test_utils.dart'; import '../../widget_tester_extensions.dart'; import '../asset_viewer/asset_viewer_mocks.dart'; -final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, -); +final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); void main() { late MockCurrentAssetProvider assetProvider; @@ -34,10 +29,7 @@ void main() { }); testWidgets('Returns a Dismissible', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); expect(find.byType(Dismissible), findsOneWidget); }); @@ -55,16 +47,10 @@ void main() { expect(find.byType(ConfirmDialog), findsOneWidget); }); - testWidgets( - 'Ok action in ConfirmDialog should call onDismiss with activityId', - (tester) async { + testWidgets('Ok action in ConfirmDialog should call onDismiss with activityId', (tester) async { String? receivedActivityId; await tester.pumpConsumerWidget( - DismissibleActivity( - '1', - ActivityTile(activity), - onDismiss: (id) => receivedActivityId = id, - ), + DismissibleActivity('1', ActivityTile(activity), onDismiss: (id) => receivedActivityId = id), overrides: overrides, ); @@ -93,10 +79,7 @@ void main() { }); testWidgets('No delete dialog if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(500, 0)); @@ -106,10 +89,7 @@ void main() { }); testWidgets('No icon for background if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(-500, 0)); diff --git a/mobile/test/modules/album/album_mocks.dart b/mobile/test/modules/album/album_mocks.dart index 147d7b4221..7a1b76e0c7 100644 --- a/mobile/test/modules/album/album_mocks.dart +++ b/mobile/test/modules/album/album_mocks.dart @@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentAlbumProvider extends CurrentAlbum - with Mock - implements CurrentAlbumInternal { +class MockCurrentAlbumProvider extends CurrentAlbum with Mock implements CurrentAlbumInternal { Album? initAlbum; MockCurrentAlbumProvider([this.initAlbum]); diff --git a/mobile/test/modules/album/album_sort_by_options_provider_test.dart b/mobile/test/modules/album/album_sort_by_options_provider_test.dart index df59f03c56..a35255bc21 100644 --- a/mobile/test/modules/album/album_sort_by_options_provider_test.dart +++ b/mobile/test/modules/album/album_sort_by_options_provider_test.dart @@ -22,12 +22,7 @@ void main() { db = await TestUtils.initIsar(); }); - final albums = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final albums = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; setUp(() { db.writeTxnSync(() { @@ -48,23 +43,13 @@ void main() { const created = AlbumSortMode.created; test("Created time - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created time - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -73,23 +58,13 @@ void main() { const assetCount = AlbumSortMode.assetCount; test("Asset Count - ASC", () { final sorted = assetCount.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Asset Count - DESC", () { final sorted = assetCount.sortFn(albums, true); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -98,23 +73,13 @@ void main() { const lastModified = AlbumSortMode.lastModified; test("Last modified - ASC", () { final sorted = lastModified.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Last modified - DESC", () { final sorted = lastModified.sortFn(albums, true); - final sortedList = [ - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -123,23 +88,13 @@ void main() { const created = AlbumSortMode.created; test("Created - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -148,15 +103,12 @@ void main() { const mostRecent = AlbumSortMode.mostRecent; test("Most Recent - DESC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - false, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], false); final sortedList = [ AlbumStub.create2020end2026Album, AlbumStub.create2020end2024Album, @@ -167,15 +119,12 @@ void main() { }); test("Most Recent - ASC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - true, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], true); final sortedList = [ AlbumStub.create2020end2020Album, AlbumStub.create2020end2022Album, @@ -191,23 +140,13 @@ void main() { test("Most Oldest - ASC", () { final sorted = mostOldest.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Most Oldest - DESC", () { final sorted = mostOldest.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -221,79 +160,49 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort mode when none set', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); expect(container.read(albumSortByOptionsProvider), AlbumSortMode.created); }); test('Returns the correct sort mode with index from Store', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(3); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(3); - expect( - container.read(albumSortByOptionsProvider), - AlbumSortMode.lastModified, - ); + expect(container.read(albumSortByOptionsProvider), AlbumSortMode.lastModified); }); test('Properly saves the correct store index of sort mode', () { - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.mostOldest); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - AlbumSortMode.mostOldest.storeIndex, - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, AlbumSortMode.mostOldest.storeIndex), ); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); final listener = ListenerMock(); - container.listen( - albumSortByOptionsProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortByOptionsProvider, listener.call, fireImmediately: true); // Created -> Most Oldest - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.mostOldest); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); // Most Oldest -> Title - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.title); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.title); verifyInOrder([ () => listener.call(null, AlbumSortMode.created), @@ -315,28 +224,18 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort order when none set - false', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); expect(container.read(albumSortOrderProvider), isFalse); }); @@ -344,33 +243,20 @@ void main() { test('Properly saves the correct order', () { container.read(albumSortOrderProvider.notifier).changeSortDirection(true); - verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - true, - ), - ); + verify(() => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, true)); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); final listener = ListenerMock(); - container.listen( - albumSortOrderProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortOrderProvider, listener.call, fireImmediately: true); // false -> true container.read(albumSortOrderProvider.notifier).changeSortDirection(true); // true -> false - container - .read(albumSortOrderProvider.notifier) - .changeSortDirection(false); + container.read(albumSortOrderProvider.notifier).changeSortDirection(false); verifyInOrder([ () => listener.call(null, false), diff --git a/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart b/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart index f81f5a9a19..89b06d3c09 100644 --- a/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart +++ b/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart @@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentAssetProvider extends CurrentAssetInternal - with Mock - implements CurrentAsset { +class MockCurrentAssetProvider extends CurrentAssetInternal with Mock implements CurrentAsset { Asset? initAsset; MockCurrentAssetProvider([this.initAsset]); diff --git a/mobile/test/modules/extensions/asset_extensions_test.dart b/mobile/test/modules/extensions/asset_extensions_test.dart index dd334c7b9d..2b9b740ca7 100644 --- a/mobile/test/modules/extensions/asset_extensions_test.dart +++ b/mobile/test/modules/extensions/asset_extensions_test.dart @@ -5,21 +5,11 @@ import 'package:immich_mobile/extensions/asset_extensions.dart'; import 'package:timezone/data/latest.dart'; import 'package:timezone/timezone.dart'; -ExifInfo makeExif({ - DateTime? dateTimeOriginal, - String? timeZone, -}) { - return ExifInfo( - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - ); +ExifInfo makeExif({DateTime? dateTimeOriginal, String? timeZone}) { + return ExifInfo(dateTimeOriginal: dateTimeOriginal, timeZone: timeZone); } -Asset makeAsset({ - required String id, - required DateTime createdAt, - ExifInfo? exifInfo, -}) { +Asset makeAsset({required String id, required DateTime createdAt, ExifInfo? exifInfo}) { return Asset( checksum: '', localId: id, @@ -76,14 +66,10 @@ void main() { expect(dateTimeInUTC.timeZoneOffset, tz); }); - test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', - () { + test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', () { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); - final e = makeExif( - dateTimeOriginal: dateTimeOriginal, - timeZone: "#_#", - ); // Invalid timezone + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: "#_#"); // Invalid timezone final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); @@ -98,13 +84,11 @@ void main() { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); const location = "Asia/Hong_Kong"; - final e = - makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location); + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location); final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); - final adjustedTime = - TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location)); + final adjustedTime = TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location)); expect(adjustedTime, dt); expect(adjustedTime.timeZoneOffset, tz); }); @@ -118,8 +102,7 @@ void main() { final (dt, tz) = a.getTZAdjustedTimeAndOffset(); final location = getLocation("Asia/Hong_Kong"); - final offsetFromLocation = - Duration(milliseconds: location.currentTimeZone.offset); + final offsetFromLocation = Duration(milliseconds: location.currentTimeZone.offset); final adjustedTime = dateTimeOriginal.toUtc().add(offsetFromLocation); // Adds the offset to the actual time and returns the offset separately diff --git a/mobile/test/modules/extensions/builtin_extensions_test.dart b/mobile/test/modules/extensions/builtin_extensions_test.dart index 2de450a952..e52362f3d3 100644 --- a/mobile/test/modules/extensions/builtin_extensions_test.dart +++ b/mobile/test/modules/extensions/builtin_extensions_test.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/string_extensions.dart'; void main() { group('Test toDuration', () { test('ok', () { - expect( - "1:02:33".toDuration(), - const Duration(hours: 1, minutes: 2, seconds: 33), - ); + expect("1:02:33".toDuration(), const Duration(hours: 1, minutes: 2, seconds: 33)); }); test('malformed', () { expect("".toDuration(), isNull); @@ -45,9 +42,7 @@ void main() { test('withKey', () { final a = ["a", "bb", "cc", "ddd"]; expect( - a.uniqueConsecutive( - compare: (s1, s2) => s1.length.compareTo(s2.length), - ), + a.uniqueConsecutive(compare: (s1, s2) => s1.length.compareTo(s2.length)), orderedEquals(["a", "bb", "ddd"]), ); }); diff --git a/mobile/test/modules/extensions/datetime_extensions_test.dart b/mobile/test/modules/extensions/datetime_extensions_test.dart index 412d946b1f..b1b1542fcd 100644 --- a/mobile/test/modules/extensions/datetime_extensions_test.dart +++ b/mobile/test/modules/extensions/datetime_extensions_test.dart @@ -25,24 +25,21 @@ void main() { test('returns date range format for this year', () { final startDate = DateTime(currentYear, 3, 23); // Mar 23 final endDate = DateTime(currentYear, 5, 31); // May 31 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Mar 23 - May 31'); }); test('returns date range format for other year (same year)', () { final startDate = DateTime(2023, 8, 28); // Aug 28 final endDate = DateTime(2023, 9, 30); // Sep 30 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Aug 28 - Sep 30, 2023'); }); test('returns date range format over multiple years', () { final startDate = DateTime(2021, 4, 17); // Apr 17, 2021 final endDate = DateTime(2022, 4, 9); // Apr 9, 2022 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Apr 17, 2021 - Apr 9, 2022'); }); }); diff --git a/mobile/test/modules/home/asset_grid_data_structure_test.dart b/mobile/test/modules/home/asset_grid_data_structure_test.dart index b4ee851969..3e1fe06c68 100644 --- a/mobile/test/modules/home/asset_grid_data_structure_test.dart +++ b/mobile/test/modules/home/asset_grid_data_structure_test.dart @@ -58,10 +58,7 @@ void main() { group('Test grouped', () { test('test grouped check months', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 @@ -75,33 +72,18 @@ void main() { // Day 1 // 5 Assets => 2 Rows expect(renderList.elements, hasLength(4)); - expect( - renderList.elements[0].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[0].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[0].date.month, 1); - expect( - renderList.elements[1].type, - RenderAssetGridElementType.groupDividerTitle, - ); + expect(renderList.elements[1].type, RenderAssetGridElementType.groupDividerTitle); expect(renderList.elements[1].date.month, 1); - expect( - renderList.elements[2].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[2].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[2].date.month, 2); - expect( - renderList.elements[3].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[3].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[3].date.month, 10); }); test('test grouped check types', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 diff --git a/mobile/test/modules/map/map_mocks.dart b/mobile/test/modules/map/map_mocks.dart index cb525b2d17..959cad3da6 100644 --- a/mobile/test/modules/map/map_mocks.dart +++ b/mobile/test/modules/map/map_mocks.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/models/map/map_state.model.dart'; import 'package:immich_mobile/providers/map/map_state.provider.dart'; import 'package:mocktail/mocktail.dart'; -class MockMapStateNotifier extends Notifier - with Mock - implements MapStateNotifier { +class MockMapStateNotifier extends Notifier with Mock implements MapStateNotifier { final MapState initState; MockMapStateNotifier(this.initState); diff --git a/mobile/test/modules/map/map_theme_override_test.dart b/mobile/test/modules/map/map_theme_override_test.dart index 59f9b9e246..de16b7f24f 100644 --- a/mobile/test/modules/map/map_theme_override_test.dart +++ b/mobile/test/modules/map/map_theme_override_test.dart @@ -38,8 +38,7 @@ void main() { ]; }); - testWidgets("Return dark theme style when theme mode is dark", - (tester) async { + testWidgets("Return dark theme style when theme mode is dark", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -51,8 +50,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = - mapState.copyWith(darkStyleFetched: const AsyncData("dark")); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncData("dark")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "dark"); }); @@ -69,15 +67,12 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - darkStyleFetched: const AsyncError("Error", StackTrace.empty), - ); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncError("Error", StackTrace.empty)); await tester.pumpAndSettle(); expect(mapStyle?.hasError, isTrue); }); - testWidgets("Return light theme style when theme mode is light", - (tester) async { + testWidgets("Return light theme style when theme mode is light", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -89,10 +84,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - themeMode: ThemeMode.light, - lightStyleFetched: const AsyncData("light"), - ); + mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); }); @@ -110,8 +102,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.dark; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, darkStyleFetched: const AsyncData("dark"), @@ -121,8 +112,7 @@ void main() { expect(mapStyle?.valueOrNull, "dark"); }); - testWidgets("Return light theme style when system is light", - (tester) async { + testWidgets("Return light theme style when system is light", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -134,8 +124,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.light; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, lightStyleFetched: const AsyncData("light"), @@ -145,8 +134,7 @@ void main() { expect(mapStyle?.valueOrNull, "light"); }); - testWidgets("Switches style when system brightness changes", - (tester) async { + testWidgets("Switches style when system brightness changes", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -158,8 +146,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.light; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, lightStyleFetched: const AsyncData("light"), @@ -168,8 +155,7 @@ void main() { await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.dark; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark; await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "dark"); }); diff --git a/mobile/test/modules/shared/shared_mocks.dart b/mobile/test/modules/shared/shared_mocks.dart index f50fde7040..790bbbd815 100644 --- a/mobile/test/modules/shared/shared_mocks.dart +++ b/mobile/test/modules/shared/shared_mocks.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentUserProvider extends StateNotifier - with Mock - implements CurrentUserProvider { +class MockCurrentUserProvider extends StateNotifier with Mock implements CurrentUserProvider { MockCurrentUserProvider() : super(null); @override diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index a78f65af67..767a52b8d8 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -1,3 +1,5 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -9,9 +11,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; -import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/repositories/asset.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:mocktail/mocktail.dart'; @@ -49,6 +52,28 @@ void main() { ); } + final owner = UserDto( + id: "1", + updatedAt: DateTime.now(), + email: "a@b.c", + name: "first last", + isAdmin: false, + profileChangedAt: DateTime.now(), + ); + + setUpAll(() async { + final loggerDb = DriftLogger(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + final LogRepository logRepository = LogRepository(loggerDb); + + WidgetsFlutterBinding.ensureInitialized(); + final db = await TestUtils.initIsar(); + + db.writeTxnSync(() => db.clearSync()); + await StoreService.init(storeRepository: IsarStoreRepository(db)); + await Store.put(StoreKey.currentUser, owner); + await LogService.init(logRepository: logRepository, storeRepository: IsarStoreRepository(db)); + }); + group('Test SyncService grouped', () { final MockHashService hs = MockHashService(); final MockEntityService entityService = MockEntityService(); @@ -57,14 +82,11 @@ void main() { final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository(); final MockIsarUserRepository userRepository = MockIsarUserRepository(); final MockETagRepository eTagRepository = MockETagRepository(); - final MockAlbumMediaRepository albumMediaRepository = - MockAlbumMediaRepository(); + final MockAlbumMediaRepository albumMediaRepository = MockAlbumMediaRepository(); final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository(); final MockAppSettingService appSettingService = MockAppSettingService(); - final MockLocalFilesManagerRepository localFilesManagerRepository = - MockLocalFilesManagerRepository(); - final MockPartnerApiRepository partnerApiRepository = - MockPartnerApiRepository(); + final MockLocalFilesManagerRepository localFilesManagerRepository = MockLocalFilesManagerRepository(); + final MockPartnerApiRepository partnerApiRepository = MockPartnerApiRepository(); final MockUserApiRepository userApiRepository = MockUserApiRepository(); final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); @@ -75,20 +97,11 @@ void main() { email: "a@b.c", name: "first last", isAdmin: false, + profileChangedAt: DateTime(2021), ); - late SyncService s; - setUpAll(() async { - WidgetsFlutterBinding.ensureInitialized(); - final db = await TestUtils.initIsar(); - db.writeTxnSync(() => db.clearSync()); - await StoreService.init(storeRepository: IsarStoreRepository(db)); - await Store.put(StoreKey.currentUser, owner); - await LogService.init( - logRepository: IsarLogRepository(db), - storeRepository: IsarStoreRepository(db), - ); - }); + late SyncService s; + final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), makeAsset(checksum: "b", remoteId: "2-1"), @@ -115,36 +128,30 @@ void main() { userApiRepository, ); when(() => userService.getMyUser()).thenReturn(owner); - when(() => eTagRepository.get(owner.id)) - .thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now())); + when(() => eTagRepository.get(owner.id)).thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now())); when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {}); when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {}); when(() => partnerRepository.getSharedWith()).thenAnswer((_) async => []); - when(() => userRepository.getAll(sortBy: SortUserBy.id)) - .thenAnswer((_) async => [owner]); + when(() => userRepository.getAll(sortBy: SortUserBy.id)).thenAnswer((_) async => [owner]); when(() => userRepository.getAll()).thenAnswer((_) async => [owner]); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => initialAssets); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[3], null, null]); + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[3], null, null]); when(() => assetRepository.updateAll(any())).thenAnswer((_) async => []); when(() => assetRepository.deleteByIds(any())).thenAnswer((_) async {}); - when(() => exifInfoRepository.updateAll(any())) - .thenAnswer((_) async => []); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when(() => exifInfoRepository.updateAll(any())).thenAnswer((_) async => []); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]); registerFallbackValue(Direction.sharedByMe); - when(() => partnerApiRepository.getAll(any())) - .thenAnswer((_) async => []); + when(() => partnerApiRepository.getAll(any())).thenAnswer((_) async => []); }); test('test inserting existing assets', () async { final List remoteAssets = [ @@ -177,10 +184,7 @@ void main() { ); expect(c1, isTrue); final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]); - verify( - () => assetRepository - .updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]), - ); + verify(() => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset])); }); test('test syncing duplicate assets', () async { @@ -199,10 +203,7 @@ void main() { ); expect(c1, isTrue); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => remoteAssets); final bool c2 = await s.syncRemoteAssetsToDb( users: [owner], @@ -212,10 +213,7 @@ void main() { expect(c2, isFalse); final currentState = [...remoteAssets]; when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => currentState); remoteAssets.removeAt(4); final bool c3 = await s.syncRemoteAssetsToDb( @@ -236,19 +234,19 @@ void main() { test('test efficient sync', () async { when( - () => assetRepository.deleteAllByRemoteId( - [initialAssets[1].remoteId!, initialAssets[2].remoteId!], - state: AssetState.remote, - ), + () => assetRepository.deleteAllByRemoteId([ + initialAssets[1].remoteId!, + initialAssets[2].remoteId!, + ], state: AssetState.remote), ).thenAnswer((_) async { return; }); when( - () => assetRepository - .getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), + () => assetRepository.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), ).thenAnswer((_) async => [initialAssets[2]]); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[0], null, null]); //afg + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[0], null, null]); //afg final List toUpsert = [ makeAsset(checksum: "a", remoteId: "0-1"), // changed makeAsset(checksum: "f", remoteId: "0-2"), // new @@ -271,18 +269,11 @@ void main() { test('test upsert with EXIF data', () async { final assets = [AssetStub.image1, AssetStub.image2]; - expect( - assets.map((a) => a.exifInfo?.assetId), - List.filled(assets.length, null), - ); + expect(assets.map((a) => a.exifInfo?.assetId), List.filled(assets.length, null)); await s.upsertAssetsWithExif(assets); verify( () => exifInfoRepository.updateAll( - any( - that: containsAll( - assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)), - ), - ), + any(that: containsAll(assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)))), ), ); expect(assets.map((a) => a.exifInfo?.assetId), assets.map((a) => a.id)); @@ -291,8 +282,4 @@ void main() { }); } -Future<(List?, List?)> _failDiff( - List user, - DateTime time, -) => - Future.value((null, null)); +Future<(List?, List?)> _failDiff(List user, DateTime time) => Future.value((null, null)); diff --git a/mobile/test/modules/utils/async_mutex_test.dart b/mobile/test/modules/utils/async_mutex_test.dart index c1b39035b4..d50567721b 100644 --- a/mobile/test/modules/utils/async_mutex_test.dart +++ b/mobile/test/modules/utils/async_mutex_test.dart @@ -7,33 +7,13 @@ void main() { AsyncMutex lock = AsyncMutex(); List events = []; expect(0, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(1), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1))); expect(1, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 3), - () => events.add(2), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2))); expect(2, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 1), - () => events.add(3), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3))); expect(3, lock.enqueued); - await lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(4), - ), - ); + await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); expect(0, lock.enqueued); expect(events, [1, 2, 3, 4]); }); diff --git a/mobile/test/modules/utils/datetime_helpers_test.dart b/mobile/test/modules/utils/datetime_helpers_test.dart new file mode 100644 index 0000000000..dfe83b4925 --- /dev/null +++ b/mobile/test/modules/utils/datetime_helpers_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/utils/datetime_helpers.dart'; + +void main() { + group('tryFromSecondsSinceEpoch', () { + test('returns null for null input', () { + final result = tryFromSecondsSinceEpoch(null); + expect(result, isNull); + }); + + test('returns null for value below minimum allowed range', () { + // _minMillisecondsSinceEpoch = -62135596800000 + final seconds = -62135596800000 ~/ 1000 - 1; // One second before min allowed + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, isNull); + }); + + test('returns null for value above maximum allowed range', () { + // _maxMillisecondsSinceEpoch = 8640000000000000 + final seconds = 8640000000000000 ~/ 1000 + 1; // One second after max allowed + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, isNull); + }); + + test('returns correct DateTime for minimum allowed value', () { + final seconds = -62135596800000 ~/ 1000; // Minimum allowed timestamp + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, DateTime.fromMillisecondsSinceEpoch(-62135596800000)); + }); + + test('returns correct DateTime for maximum allowed value', () { + final seconds = 8640000000000000 ~/ 1000; // Maximum allowed timestamp + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, DateTime.fromMillisecondsSinceEpoch(8640000000000000)); + }); + + test('returns correct DateTime for negative timestamp', () { + final seconds = -1577836800; // Dec 31, 1919 (pre-epoch) + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, DateTime.fromMillisecondsSinceEpoch(-1577836800 * 1000)); + }); + + test('returns correct DateTime for zero timestamp', () { + final seconds = 0; // Jan 1, 1970 (epoch) + final result = tryFromSecondsSinceEpoch(seconds); + expect(result, DateTime.fromMillisecondsSinceEpoch(0)); + }); + + test('returns correct DateTime for recent timestamp', () { + final now = DateTime.now(); + final seconds = now.millisecondsSinceEpoch ~/ 1000; + final result = tryFromSecondsSinceEpoch(seconds); + expect(result?.year, now.year); + expect(result?.month, now.month); + expect(result?.day, now.day); + }); + }); +} diff --git a/mobile/test/modules/utils/migration_test.dart b/mobile/test/modules/utils/migration_test.dart new file mode 100644 index 0000000000..08ab1204a6 --- /dev/null +++ b/mobile/test/modules/utils/migration_test.dart @@ -0,0 +1,131 @@ +import 'package:drift/drift.dart' hide isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; +import 'package:immich_mobile/utils/migration.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../infrastructure/repository.mock.dart'; + +void main() { + late Drift db; + late SyncStreamRepository mockSyncStreamRepository; + + setUpAll(() async { + db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); + await StoreService.init(storeRepository: DriftStoreRepository(db)); + mockSyncStreamRepository = MockSyncStreamRepository(); + when(() => mockSyncStreamRepository.reset()).thenAnswer((_) async => {}); + }); + + tearDown(() async { + await Store.clear(); + }); + + group('handleBetaMigration Tests', () { + group("version < 15", () { + test('already on new timeline', () async { + await Store.put(StoreKey.betaTimeline, true); + + await handleBetaMigration(14, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + + test('already on old timeline', () async { + await Store.put(StoreKey.betaTimeline, false); + + await handleBetaMigration(14, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.needBetaMigration), true); + }); + + test('fresh install', () async { + await Store.delete(StoreKey.betaTimeline); + await handleBetaMigration(14, true, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + }); + + group("version == 15", () { + test('already on new timeline', () async { + await Store.put(StoreKey.betaTimeline, true); + + await handleBetaMigration(15, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + + test('already on old timeline', () async { + await Store.put(StoreKey.betaTimeline, false); + + await handleBetaMigration(15, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.needBetaMigration), true); + }); + + test('fresh install', () async { + await Store.delete(StoreKey.betaTimeline); + await handleBetaMigration(15, true, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + }); + + group("version > 15", () { + test('already on new timeline', () async { + await Store.put(StoreKey.betaTimeline, true); + + await handleBetaMigration(16, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + + test('already on old timeline', () async { + await Store.put(StoreKey.betaTimeline, false); + + await handleBetaMigration(16, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), false); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + + test('fresh install', () async { + await Store.delete(StoreKey.betaTimeline); + await handleBetaMigration(16, true, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.betaTimeline), true); + expect(Store.tryGet(StoreKey.needBetaMigration), false); + }); + }); + }); + + group('sync reset tests', () { + test('version < 16', () async { + await Store.put(StoreKey.shouldResetSync, false); + + await handleBetaMigration(15, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.shouldResetSync), true); + }); + + test('version >= 16', () async { + await Store.put(StoreKey.shouldResetSync, false); + + await handleBetaMigration(16, false, mockSyncStreamRepository); + + expect(Store.tryGet(StoreKey.shouldResetSync), false); + }); + }); +} diff --git a/mobile/test/modules/utils/throttler_test.dart b/mobile/test/modules/utils/throttler_test.dart index 76d8bd2ad7..1757826daf 100644 --- a/mobile/test/modules/utils/throttler_test.dart +++ b/mobile/test/modules/utils/throttler_test.dart @@ -1,6 +1,6 @@ -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/utils/throttle.dart'; +import 'package:immich_mobile/utils/debug_print.dart'; class _Counter { int _count = 0; @@ -8,14 +8,13 @@ class _Counter { int get count => _count; void increment() { - debugPrint("Counter inside increment: $count"); + dPrint(() => "Counter inside increment: $count"); _count = _count + 1; } } void main() { - test('Executes the method immediately if no calls received previously', - () async { + test('Executes the method immediately if no calls received previously', () async { var counter = _Counter(); final throttler = Throttler(interval: const Duration(milliseconds: 300)); throttler.run(() => counter.increment()); diff --git a/mobile/test/modules/utils/thumbnail_utils_test.dart b/mobile/test/modules/utils/thumbnail_utils_test.dart index 6fa0127f16..dd4588fc80 100644 --- a/mobile/test/modules/utils/thumbnail_utils_test.dart +++ b/mobile/test/modules/utils/thumbnail_utils_test.dart @@ -9,22 +9,12 @@ void main() { final dateTimeString = DateFormat.yMMMMd().format(dateTime); test('returns description if it has one', () { - final result = getAltText( - const ExifInfo(description: 'description'), - dateTime, - AssetType.image, - [], - ); + final result = getAltText(const ExifInfo(description: 'description'), dateTime, AssetType.image, []); expect(result, 'description'); }); test('returns image alt text with date if no location', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - [], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, []); expect(template, "image_alt_text_date"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); @@ -45,12 +35,7 @@ void main() { }); test('returns image alt text with date and some people', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - ["Alice", "Bob"], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, ["Alice", "Bob"]); expect(template, "image_alt_text_date_2_people"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); diff --git a/mobile/test/modules/utils/url_helper_test.dart b/mobile/test/modules/utils/url_helper_test.dart index 840ac91f1f..0e8a8e2aa0 100644 --- a/mobile/test/modules/utils/url_helper_test.dart +++ b/mobile/test/modules/utils/url_helper_test.dart @@ -28,9 +28,7 @@ void main() { expect(punycodeEncodeUrl(url), equals(expected)); }); - test( - 'should encode multi-segment Unicode host with multiple non-ASCII segments', - () { + test('should encode multi-segment Unicode host with multiple non-ASCII segments', () { const url = 'https://bÃŧcher.mÃŧnchen'; const expected = 'https://xn--bcher-kva.xn--mnchen-3ya'; expect(punycodeEncodeUrl(url), equals(expected)); diff --git a/mobile/test/modules/utils/version_compatibility_test.dart b/mobile/test/modules/utils/version_compatibility_test.dart index bdcc0c8fce..82f89237c0 100644 --- a/mobile/test/modules/utils/version_compatibility_test.dart +++ b/mobile/test/modules/utils/version_compatibility_test.dart @@ -6,10 +6,7 @@ void main() { String? result; result = getVersionCompatibilityMessage(1, 0, 2, 0); - expect( - result, - 'Your app major version is not compatible with the server!', - ); + expect(result, 'Your app major version is not compatible with the server!'); result = getVersionCompatibilityMessage(1, 106, 1, 105); expect( diff --git a/mobile/test/pages/search/search.page_test.dart b/mobile/test/pages/search/search.page_test.dart index fa7f037da5..9592623a28 100644 --- a/mobile/test/pages/search/search.page_test.dart +++ b/mobile/test/pages/search/search.page_test.dart @@ -42,22 +42,14 @@ void main() { ]; }); - final emptyTextSearch = isA() - .having((s) => s.originalFileName, 'originalFileName', null); + final emptyTextSearch = isA().having((s) => s.originalFileName, 'originalFileName', null); testWidgets('contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.abc_rounded), - findsOneWidget, - reason: 'Should have contextual search icon', - ); + expect(find.byIcon(Icons.abc_rounded), findsOneWidget, reason: 'Should have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -65,14 +57,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchSmart(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchSmart(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.query, 'query', 'test'), - ); + expect(captured.first, isA().having((s) => s.query, 'query', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); @@ -82,10 +69,7 @@ void main() { }); testWidgets('not contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); @@ -93,11 +77,7 @@ void main() { await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.image_search_rounded), - findsOneWidget, - reason: 'Should not have contextual search icon', - ); + expect(find.byIcon(Icons.image_search_rounded), findsOneWidget, reason: 'Should not have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -105,15 +85,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchAssets(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchAssets(captureAny())).captured; - expect( - captured.first, - isA() - .having((s) => s.originalFileName, 'originalFileName', 'test'), - ); + expect(captured.first, isA().having((s) => s.originalFileName, 'originalFileName', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); diff --git a/mobile/test/repository.mocks.dart b/mobile/test/repository.mocks.dart index 54a7e2d4a4..4b54ec4055 100644 --- a/mobile/test/repository.mocks.dart +++ b/mobile/test/repository.mocks.dart @@ -45,5 +45,4 @@ class MockPartnerRepository extends Mock implements PartnerRepository {} class MockPartnerApiRepository extends Mock implements PartnerApiRepository {} -class MockLocalFilesManagerRepository extends Mock - implements LocalFilesManagerRepository {} +class MockLocalFilesManagerRepository extends Mock implements LocalFilesManagerRepository {} diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart index 443e37e75d..97683cdab1 100644 --- a/mobile/test/services/album.service_test.dart +++ b/mobile/test/services/album.service_test.dart @@ -33,12 +33,12 @@ void main() { when(() => userService.getMyUser()).thenReturn(UserStub.user1); - when(() => albumRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => albumRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); sut = AlbumService( syncService, @@ -54,33 +54,26 @@ void main() { group('refreshDeviceAlbums', () { test('empty selection with one album in db', () async { - when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)) - .thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => []); + when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); + when(() => backupRepository.getIdsBySelection(BackupSelection.select)).thenAnswer((_) async => []); when(() => albumMediaRepository.getAll()).thenAnswer((_) async => []); when(() => albumRepository.count(local: true)).thenAnswer((_) async => 1); - when(() => syncService.removeAllLocalAlbumsAndAssets()) - .thenAnswer((_) async => true); + when(() => syncService.removeAllLocalAlbumsAndAssets()).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, false); verify(() => syncService.removeAllLocalAlbumsAndAssets()); }); test('one selected albums, two on device', () async { - when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)) - .thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); - when(() => albumMediaRepository.getAll()) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); - when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())) - .thenAnswer((_) async => true); + when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); + when( + () => backupRepository.getIdsBySelection(BackupSelection.select), + ).thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); + when(() => albumMediaRepository.getAll()).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, true); - verify( - () => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null), - ).called(1); + verify(() => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null)).called(1); verifyNoMoreInteractions(syncService); }); }); @@ -88,20 +81,15 @@ void main() { group('refreshRemoteAlbums', () { test('is working', () async { when(() => syncService.getUsersFromServer()).thenAnswer((_) async => []); - when(() => syncService.syncUsersFromServer(any())) - .thenAnswer((_) async => true); - when(() => albumApiRepository.getAll(shared: true)) - .thenAnswer((_) async => [AlbumStub.sharedWithUser]); - - when(() => albumApiRepository.getAll(shared: null)) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when(() => syncService.syncUsersFromServer(any())).thenAnswer((_) async => true); + when(() => albumApiRepository.getAll(shared: true)).thenAnswer((_) async => [AlbumStub.sharedWithUser]); when( - () => syncService.syncRemoteAlbumsToDb([ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]), + () => albumApiRepository.getAll(shared: null), + ).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + + when( + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).thenAnswer((_) async => true); final result = await sut.refreshRemoteAlbums(); expect(result, true); @@ -110,13 +98,7 @@ void main() { verify(() => albumApiRepository.getAll(shared: true)).called(1); verify(() => albumApiRepository.getAll(shared: null)).called(1); verify( - () => syncService.syncRemoteAlbumsToDb( - [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ], - ), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).called(1); verifyNoMoreInteractions(userService); verifyNoMoreInteractions(albumApiRepository); @@ -138,12 +120,9 @@ void main() { () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.create(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.twoAsset); + when(() => albumRepository.create(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.twoAsset); - final result = - await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); + final result = await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); expect(result, AlbumStub.twoAsset); verify( () => albumApiRepository.create( @@ -152,9 +131,7 @@ void main() { sharedUserIds: [UserStub.user1.id], ), ).called(1); - verify( - () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), - ).called(1); + verify(() => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset)).called(1); }); }); @@ -162,32 +139,14 @@ void main() { test('one added, one duplicate', () async { when( () => albumApiRepository.addAssets(AlbumStub.oneAsset.remoteId!, any()), - ).thenAnswer( - (_) async => ( - added: [AssetStub.image2.remoteId!], - duplicates: [AssetStub.image1.remoteId!] - ), - ); - when( - () => albumRepository.get(AlbumStub.oneAsset.id), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2]), - ).thenAnswer((_) async {}); - when( - () => albumRepository.removeAssets(AlbumStub.oneAsset, []), - ).thenAnswer((_) async {}); - when( - () => albumRepository.recalculateMetadata(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.update(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); + ).thenAnswer((_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!])); + when(() => albumRepository.get(AlbumStub.oneAsset.id)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2])).thenAnswer((_) async {}); + when(() => albumRepository.removeAssets(AlbumStub.oneAsset, [])).thenAnswer((_) async {}); + when(() => albumRepository.recalculateMetadata(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.update(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); - final result = await sut.addAssets( - AlbumStub.oneAsset, - [AssetStub.image1, AssetStub.image2], - ); + final result = await sut.addAssets(AlbumStub.oneAsset, [AssetStub.image1, AssetStub.image2]); expect(result != null, true); expect(result!.alreadyInAlbum, [AssetStub.image1.remoteId!]); @@ -198,11 +157,8 @@ void main() { group('addAdditionalUserToAlbum', () { test('one added', () async { when( - () => - albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), - ).thenAnswer( - (_) async => AlbumStub.sharedWithUser, - ); + () => albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), + ).thenAnswer((_) async => AlbumStub.sharedWithUser); when( () => albumRepository.addUsers( @@ -211,14 +167,9 @@ void main() { ), ).thenAnswer((_) async => AlbumStub.emptyAlbum); - when( - () => albumRepository.update(AlbumStub.emptyAlbum), - ).thenAnswer((_) async => AlbumStub.emptyAlbum); + when(() => albumRepository.update(AlbumStub.emptyAlbum)).thenAnswer((_) async => AlbumStub.emptyAlbum); - final result = await sut.addUsers( - AlbumStub.emptyAlbum, - [UserStub.user2.id], - ); + final result = await sut.addUsers(AlbumStub.emptyAlbum, [UserStub.user2.id]); expect(result, true); }); diff --git a/mobile/test/services/asset.service_test.dart b/mobile/test/services/asset.service_test.dart index 293e5ec76b..b741150165 100644 --- a/mobile/test/services/asset.service_test.dart +++ b/mobile/test/services/asset.service_test.dart @@ -69,8 +69,7 @@ void main() { setUp(() { assetsApi = MockAssetsApi(); when(() => apiService.assetsApi).thenReturn(assetsApi); - when(() => assetsApi.updateAssets(any())) - .thenAnswer((_) async => Future.value()); + when(() => assetsApi.updateAssets(any())).thenAnswer((_) async => Future.value()); }); test("asset is updated with DateTime", () async { @@ -79,14 +78,10 @@ void main() { await sut.changeDateTime(assets, dateTime.toIso8601String()); verify(() => assetsApi.updateAssets(any())).called(1); - final upsertExifCallback = - verify(() => syncService.upsertAssetsWithExif(captureAny())); + final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); - final receivedAssets = - upsertExifCallback.captured.firstOrNull as List? ?? []; - final receivedDatetime = receivedAssets.cast().map( - (a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0), - ); + final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; + final receivedDatetime = receivedAssets.cast().map((a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0)); expect(receivedDatetime.every((d) => d == dateTime), isTrue); }); @@ -96,15 +91,12 @@ void main() { await sut.changeLocation(assets, latLng); verify(() => assetsApi.updateAssets(any())).called(1); - final upsertExifCallback = - verify(() => syncService.upsertAssetsWithExif(captureAny())); + final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); - final receivedAssets = - upsertExifCallback.captured.firstOrNull as List? ?? []; + final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; final receivedCoords = receivedAssets.cast().map( - (a) => - LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), - ); + (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), + ); expect(receivedCoords.every((l) => l == latLng), isTrue); }); }); diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index 46ad8bbc4c..1bad780ca7 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; import 'package:isar/isar.dart'; import 'package:mocktail/mocktail.dart'; @@ -20,6 +21,8 @@ void main() { late MockApiService apiService; late MockNetworkService networkService; late MockBackgroundSyncManager backgroundSyncManager; + late MockUploadService uploadService; + late MockAppSettingService appSettingsService; late Isar db; setUp(() async { @@ -28,6 +31,8 @@ void main() { apiService = MockApiService(); networkService = MockNetworkService(); backgroundSyncManager = MockBackgroundSyncManager(); + uploadService = MockUploadService(); + appSettingsService = MockAppSettingService(); sut = AuthService( authApiRepository, @@ -35,6 +40,7 @@ void main() { apiService, networkService, backgroundSyncManager, + appSettingsService, ); registerFallbackValue(Uri()); @@ -58,8 +64,7 @@ void main() { const testUrl = 'http://ip:2283'; const resolvedUrl = 'http://ip:2283/api'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenAnswer((_) async => resolvedUrl); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl); when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {}); final result = await sut.validateServerUrl(testUrl); @@ -74,8 +79,7 @@ void main() { const testUrl = 'https://immich.domain.com'; const resolvedUrl = 'https://immich.domain.com/api'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenAnswer((_) async => resolvedUrl); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl); when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {}); final result = await sut.validateServerUrl(testUrl); @@ -89,13 +93,9 @@ void main() { test('Should throw error on invalid URL', () async { const testUrl = 'invalid-url'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenThrow(Exception('Invalid URL')); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Invalid URL')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -104,13 +104,9 @@ void main() { test('Should throw error on unreachable server', () async { const testUrl = 'https://unreachable.server'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenThrow(Exception('Server is not reachable')); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Server is not reachable')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -121,9 +117,11 @@ void main() { test('Should logout user', () async { when(() => authApiRepository.logout()).thenAnswer((_) async => {}); when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); - when(() => authRepository.clearLocalData()) - .thenAnswer((_) => Future.value(null)); - + when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); + when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); + when( + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), + ).thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); @@ -132,12 +130,13 @@ void main() { }); test('Should clear local data even on server error', () async { - when(() => authApiRepository.logout()) - .thenThrow(Exception('Server error')); + when(() => authApiRepository.logout()).thenThrow(Exception('Server error')); when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); - when(() => authRepository.clearLocalData()) - .thenAnswer((_) => Future.value(null)); - + when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); + when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); + when( + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), + ).thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); @@ -148,13 +147,11 @@ void main() { group('setOpenApiServiceEndpoint', () { setUp(() { - when(() => networkService.getWifiName()) - .thenAnswer((_) async => 'TestWifi'); + when(() => networkService.getWifiName()).thenAnswer((_) async => 'TestWifi'); }); test('Should return null if auto endpoint switching is disabled', () async { - when(() => authRepository.getEndpointSwitchingFeature()) - .thenReturn((false)); + when(() => authRepository.getEndpointSwitchingFeature()).thenReturn((false)); final result = await sut.setOpenApiServiceEndpoint(); @@ -166,10 +163,10 @@ void main() { test('Should set local connection if wifi name matches', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); - when(() => authRepository.getLocalEndpoint()) - .thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenAnswer((_) async => 'http://local.endpoint'); + when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenAnswer((_) async => 'http://local.endpoint'); final result = await sut.setOpenApiServiceEndpoint(); @@ -178,20 +175,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getLocalEndpoint()).called(1); - verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .called(1); + verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1); }); test('Should set external endpoint if wifi name not matching', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenAnswer((_) async => 'https://external.endpoint/api'); @@ -203,25 +195,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); - test('Should set second external endpoint if the first throw any error', - () async { + test('Should set second external endpoint if the first throw any error', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -238,25 +220,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); - test('Should set second external endpoint if the first throw ApiException', - () async { + test('Should set second external endpoint if the first throw ApiException', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -273,18 +245,16 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should handle error when setting local connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); - when(() => authRepository.getLocalEndpoint()) - .thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenThrow(Exception('Local endpoint error')); + when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenThrow(Exception('Local endpoint error')); final result = await sut.setOpenApiServiceEndpoint(); @@ -293,20 +263,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getLocalEndpoint()).called(1); - verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .called(1); + verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1); }); test('Should handle error when setting external connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenThrow(Exception('External endpoint error')); @@ -318,9 +283,7 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); }); } diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 1642be7fb3..64b9fc604b 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -21,60 +21,50 @@ void main() { }); group('fillAlbumWithDatabaseEntities', () { - test('remote album with owner, thumbnail, sharedUsers and assets', - () async { - final Album album = Album( - name: "album-with-two-assets-and-two-users", - localId: "album-with-two-assets-and-two-users-local", - remoteId: "album-with-two-assets-and-two-users-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: true, - activityEnabled: true, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..remoteThumbnailAssetId = AssetStub.image1.remoteId - ..assets.addAll([AssetStub.image1, AssetStub.image1]) - ..owner.value = User.fromDto(UserStub.user1) - ..sharedUsers.addAll( - [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)], - ); + test('remote album with owner, thumbnail, sharedUsers and assets', () async { + final Album album = + Album( + name: "album-with-two-assets-and-two-users", + localId: "album-with-two-assets-and-two-users-local", + remoteId: "album-with-two-assets-and-two-users-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: true, + activityEnabled: true, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..remoteThumbnailAssetId = AssetStub.image1.remoteId + ..assets.addAll([AssetStub.image1, AssetStub.image1]) + ..owner.value = User.fromDto(UserStub.user1) + ..sharedUsers.addAll([User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)]); - when(() => userRepository.getByUserId(any())) - .thenAnswer((_) async => UserStub.admin); - when(() => userRepository.getByUserId(any())) - .thenAnswer((_) async => UserStub.admin); + when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); + when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); - when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)) - .thenAnswer((_) async => AssetStub.image1); + when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)).thenAnswer((_) async => AssetStub.image1); - when(() => userRepository.getByUserIds(any())) - .thenAnswer((_) async => [UserStub.user1, UserStub.user2]); + when(() => userRepository.getByUserIds(any())).thenAnswer((_) async => [UserStub.user1, UserStub.user2]); - when(() => assetRepository.getAllByRemoteId(any())) - .thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]); + when(() => assetRepository.getAllByRemoteId(any())).thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]); await sut.fillAlbumWithDatabaseEntities(album); expect(album.owner.value?.toDto(), UserStub.admin); expect(album.thumbnail.value, AssetStub.image1); - expect( - album.remoteUsers.map((u) => u.toDto()).toSet(), - {UserStub.user1, UserStub.user2}, - ); + expect(album.remoteUsers.map((u) => u.toDto()).toSet(), {UserStub.user1, UserStub.user2}); expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2}); }); test('remote album without any info', () async { makeEmptyAlbum() => Album( - name: "album-without-info", - localId: "album-without-info-local", - remoteId: "album-without-info-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - ); + name: "album-without-info", + localId: "album-without-info-local", + remoteId: "album-without-info-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + ); final album = makeEmptyAlbum(); await sut.fillAlbumWithDatabaseEntities(album); diff --git a/mobile/test/services/hash_service_test.dart b/mobile/test/services/hash_service_test.dart index 2ba9c356a0..74b8575e40 100644 --- a/mobile/test/services/hash_service_test.dart +++ b/mobile/test/services/hash_service_test.dart @@ -31,48 +31,32 @@ void main() { mockBackgroundService = MockBackgroundService(); mockDeviceAssetRepository = MockDeviceAssetRepository(); - sut = HashService( - deviceAssetRepository: mockDeviceAssetRepository, - backgroundService: mockBackgroundService, - ); + sut = HashService(deviceAssetRepository: mockDeviceAssetRepository, backgroundService: mockBackgroundService); - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); }); - when(() => mockDeviceAssetRepository.updateAll(any())) - .thenAnswer((_) async => true); - when(() => mockDeviceAssetRepository.deleteIds(any())) - .thenAnswer((_) async => true); + when(() => mockDeviceAssetRepository.updateAll(any())).thenAnswer((_) async => true); + when(() => mockDeviceAssetRepository.deleteIds(any())).thenAnswer((_) async => true); }); group("HashService: No DeviceAsset entry", () { test("hash successfully", () async { - final (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); // No DB entries for this asset - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer((_) async => []); final result = await sut.hashAssets([mockAsset]); // Verify we stored the new hash in DB - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( () => mockDeviceAssetRepository.updateAll([ deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt), @@ -80,10 +64,7 @@ void main() { ).called(1); verify(() => mockDeviceAssetRepository.deleteIds([])).called(1); }); - expect( - result, - [AssetStub.image1.copyWith(checksum: base64.encode(hash))], - ); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -91,15 +72,9 @@ void main() { test("when the asset is not modified", () async { final hash = utf8.encode("image1-hash"); - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer( + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer( (_) async => [ - DeviceAsset( - assetId: AssetStub.image1.localId!, - hash: hash, - modifiedTime: AssetStub.image1.fileModifiedAt, - ), + DeviceAsset(assetId: AssetStub.image1.localId!, hash: hash, modifiedTime: AssetStub.image1.fileModifiedAt), ], ); final result = await sut.hashAssets([AssetStub.image1]); @@ -109,31 +84,23 @@ void main() { verifyNever(() => mockDeviceAssetRepository.updateAll(any())); verifyNever(() => mockDeviceAssetRepository.deleteIds(any())); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); test("hashed successful when asset is modified", () async { - final (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); when( () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), ).thenAnswer((_) async => [deviceAsset]); final result = await sut.hashAssets([mockAsset]); - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( () => mockDeviceAssetRepository.updateAll([ deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt), @@ -144,9 +111,7 @@ void main() { verify(() => mockBackgroundService.digestFiles([file.path])).called(1); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -157,11 +122,9 @@ void main() { late File file; setUp(() async { - (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); when( () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), ).thenAnswer((_) async => [deviceAsset]); @@ -174,22 +137,16 @@ void main() { verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockBackgroundService.digestFile(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); expect(result, isEmpty); }); test("cleanups DeviceAsset when hashing failed", () async { - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); // Verify the callback inside the transaction because, doing it outside results // in a small delay before the callback is invoked, resulting in other LOCs getting executed @@ -209,10 +166,7 @@ void main() { // To avoid this, we capture the callback and execute it within the transaction stub itself // and verify the results inside the transaction stub verify(() => mockDeviceAssetRepository.updateAll([])).called(1); - verify( - () => - mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); }); when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer( @@ -240,14 +194,11 @@ void main() { final (asset2, file2, deviceAsset2, hash2) = mock2; final (asset3, file3, deviceAsset3, hash3) = mock3; - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); // Setup for multiple batch processing calls - when(() => mockBackgroundService.digestFiles([file1.path, file2.path])) - .thenAnswer((_) async => [hash1, hash2]); - when(() => mockBackgroundService.digestFiles([file3.path])) - .thenAnswer((_) async => [hash3]); + when(() => mockBackgroundService.digestFiles([file1.path, file2.path])).thenAnswer((_) async => [hash1, hash2]); + when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]); final size = await file1.length() + await file2.length(); @@ -259,18 +210,14 @@ void main() { final result = await sut.hashAssets([asset1, asset2, asset3]); // Verify multiple batch process calls - verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])) - .called(1); + verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("processes assets in batches when file limit is reached", () async { @@ -285,15 +232,11 @@ void main() { final (asset2, file2, deviceAsset2, hash2) = mock2; final (asset3, file3, deviceAsset3, hash3) = mock3; - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); - when(() => mockBackgroundService.digestFiles([file1.path])) - .thenAnswer((_) async => [hash1]); - when(() => mockBackgroundService.digestFiles([file2.path])) - .thenAnswer((_) async => [hash2]); - when(() => mockBackgroundService.digestFiles([file3.path])) - .thenAnswer((_) async => [hash3]); + when(() => mockBackgroundService.digestFiles([file1.path])).thenAnswer((_) async => [hash1]); + when(() => mockBackgroundService.digestFiles([file2.path])).thenAnswer((_) async => [hash2]); + when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]); sut = HashService( deviceAssetRepository: mockDeviceAssetRepository, @@ -307,28 +250,20 @@ void main() { verify(() => mockBackgroundService.digestFiles([file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("HashService: Sort & Process different states", () async { - final (asset1, file1, deviceAsset1, hash1) = - await _createAssetMock(AssetStub.image1); // Will need rehashing - final (asset2, file2, deviceAsset2, hash2) = - await _createAssetMock(AssetStub.image2); // Will have matching hash - final (asset3, file3, deviceAsset3, hash3) = - await _createAssetMock(AssetStub.image3); // No DB entry - final asset4 = - AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed + final (asset1, file1, deviceAsset1, hash1) = await _createAssetMock(AssetStub.image1); // Will need rehashing + final (asset2, file2, deviceAsset2, hash2) = await _createAssetMock(AssetStub.image2); // Will have matching hash + final (asset3, file3, deviceAsset3, hash3) = await _createAssetMock(AssetStub.image3); // No DB entry + final asset4 = AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed - when(() => mockBackgroundService.digestFiles([file1.path, file3.path])) - .thenAnswer((_) async => [hash1, hash3]); + when(() => mockBackgroundService.digestFiles([file1.path, file3.path])).thenAnswer((_) async => [hash1, hash3]); // DB entries are not sorted and a dummy entry added when( () => mockDeviceAssetRepository.getByIds([ @@ -349,8 +284,7 @@ void main() { final result = await sut.hashAssets([asset1, asset2, asset3, asset4]); // Verify correct processing of all assets - verify(() => mockBackgroundService.digestFiles([file1.path, file3.path])) - .called(1); + verify(() => mockBackgroundService.digestFiles([file1.path, file3.path])).called(1); expect(result.length, 3); expect(result, [ AssetStub.image2.copyWith(checksum: base64.encode(hash2)), @@ -361,8 +295,7 @@ void main() { group("HashService: Edge cases", () { test("handles empty list of assets", () async { - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); final result = await sut.hashAssets([]); @@ -376,15 +309,10 @@ void main() { test("handles all file access failures", () async { // No DB entries when( - () => mockDeviceAssetRepository.getByIds( - [AssetStub.image1.localId!, AssetStub.image2.localId!], - ), + () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!, AssetStub.image2.localId!]), ).thenAnswer((_) async => []); - final result = await sut.hashAssets([ - AssetStub.image1, - AssetStub.image2, - ]); + final result = await sut.hashAssets([AssetStub.image1, AssetStub.image2]); verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); @@ -394,12 +322,9 @@ void main() { }); } -Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( - Asset asset, -) async { +Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock(Asset asset) async { final random = Random(); - final hash = - Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); + final hash = Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); final mockAsset = MockAsset(); final mockAssetEntity = MockAssetEntity(); final fs = MemoryFileSystem(); @@ -416,8 +341,9 @@ Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( when(() => mockAsset.fileName).thenReturn(asset.fileName); when(() => mockAsset.fileCreatedAt).thenReturn(asset.fileCreatedAt); when(() => mockAsset.fileModifiedAt).thenReturn(asset.fileModifiedAt); - when(() => mockAsset.copyWith(checksum: any(named: "checksum"))) - .thenReturn(asset.copyWith(checksum: base64.encode(hash))); + when( + () => mockAsset.copyWith(checksum: any(named: "checksum")), + ).thenReturn(asset.copyWith(checksum: base64.encode(hash))); when(() => mockAsset.local).thenAnswer((_) => mockAssetEntity); when(() => mockAssetEntity.originFile).thenAnswer((_) async => file); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index 596d3bcd1c..9b59773d3b 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -14,7 +14,6 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:isar/isar.dart'; @@ -48,7 +47,6 @@ abstract final class TestUtils { UserSchema, BackupAlbumSchema, DuplicatedAssetSchema, - LoggerMessageSchema, ETagSchema, AndroidDeviceAssetSchema, IOSDeviceAssetSchema, @@ -73,11 +71,7 @@ abstract final class TestUtils { List overrides = const [], List? observers, }) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); + final container = ProviderContainer(parent: parent, overrides: overrides, observers: observers); // Dispose on test end addTearDown(container.dispose); @@ -94,23 +88,22 @@ abstract final class TestUtils { // Workaround till the following issue is resolved // https://github.com/dart-lang/test/issues/2307 - static T fakeAsync( - Future Function(FakeAsync _) callback, { - DateTime? initialTime, - }) { + static T fakeAsync(Future Function(FakeAsync _) callback, {DateTime? initialTime}) { late final T result; Object? error; StackTrace? stack; FakeAsync(initialTime: initialTime).run((FakeAsync async) { bool shouldPump = true; unawaited( - callback(async).then( - (value) => result = value, - onError: (e, s) { - error = e; - stack = s; - }, - ).whenComplete(() => shouldPump = false), + callback(async) + .then( + (value) => result = value, + onError: (e, s) { + error = e; + stack = s; + }, + ) + .whenComplete(() => shouldPump = false), ); while (shouldPump) { diff --git a/mobile/test/test_utils/medium_factory.dart b/mobile/test/test_utils/medium_factory.dart index 8dafc564c1..19ad7166c6 100644 --- a/mobile/test/test_utils/medium_factory.dart +++ b/mobile/test/test_utils/medium_factory.dart @@ -25,10 +25,8 @@ class MediumFactory { name: name ?? 'Asset ${random.nextInt(1000000)}', checksum: checksum ?? '${random.nextInt(1000000)}', type: type ?? AssetType.image, - createdAt: createdAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), - updatedAt: updatedAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + createdAt: createdAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), ); } @@ -45,8 +43,7 @@ class MediumFactory { return LocalAlbum( id: id ?? '${random.nextInt(1000000)}', name: name ?? 'Album ${random.nextInt(1000000)}', - updatedAt: updatedAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), assetCount: assetCount ?? random.nextInt(100), backupSelection: backupSelection ?? BackupSelection.none, isIosSharedAlbum: isIosSharedAlbum ?? false, diff --git a/mobile/test/utils/action_button_utils_test.dart b/mobile/test/utils/action_button_utils_test.dart new file mode 100644 index 0000000000..274176ae88 --- /dev/null +++ b/mobile/test/utils/action_button_utils_test.dart @@ -0,0 +1,913 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/utils/action_button.utils.dart'; + +LocalAsset createLocalAsset({ + String? remoteId, + String name = 'test.jpg', + String? checksum = 'test-checksum', + AssetType type = AssetType.image, + DateTime? createdAt, + DateTime? updatedAt, + bool isFavorite = false, +}) { + return LocalAsset( + id: 'local-id', + remoteId: remoteId, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt ?? DateTime.now(), + updatedAt: updatedAt ?? DateTime.now(), + isFavorite: isFavorite, + ); +} + +RemoteAsset createRemoteAsset({ + String? localId, + String name = 'test.jpg', + String checksum = 'test-checksum', + AssetType type = AssetType.image, + DateTime? createdAt, + DateTime? updatedAt, + bool isFavorite = false, +}) { + return RemoteAsset( + id: 'remote-id', + localId: localId, + name: name, + checksum: checksum, + type: type, + ownerId: 'owner-id', + createdAt: createdAt ?? DateTime.now(), + updatedAt: updatedAt ?? DateTime.now(), + isFavorite: isFavorite, + ); +} + +RemoteAlbum createRemoteAlbum({ + String id = 'test-album-id', + String name = 'Test Album', + bool isActivityEnabled = false, + bool isShared = false, +}) { + return RemoteAlbum( + id: id, + name: name, + ownerId: 'owner-id', + description: 'Test Description', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + isActivityEnabled: isActivityEnabled, + isShared: isShared, + order: AlbumAssetOrder.asc, + assetCount: 0, + ownerName: 'Test Owner', + ); +} + +void main() { + group('ActionButtonContext', () { + test('should create context with all required parameters', () { + final asset = createLocalAsset(); + + final context = ActionButtonContext( + asset: asset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(context.asset, isA()); + expect(context.isOwner, isTrue); + expect(context.isArchived, isFalse); + expect(context.isTrashEnabled, isTrue); + expect(context.isInLockedView, isFalse); + expect(context.currentAlbum, isNull); + expect(context.source, ActionSource.timeline); + }); + }); + + group('ActionButtonType.shouldShow', () { + late BaseAsset mergedAsset; + + setUp(() { + mergedAsset = createLocalAsset(remoteId: 'remote-id'); + }); + + group('share button', () { + test('should show when not in locked view', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.share.shouldShow(context), isTrue); + }); + + test('should show when in locked view', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: true, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.share.shouldShow(context), isTrue); + }); + }); + + group('shareLink button', () { + test('should show when not in locked view and asset has remote', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.shareLink.shouldShow(context), isTrue); + }); + + test('should not show when in locked view', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: true, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.shareLink.shouldShow(context), isFalse); + }); + + test('should not show when asset has no remote', () { + final localAsset = createLocalAsset(); + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.shareLink.shouldShow(context), isFalse); + }); + }); + + group('archive button', () { + test('should show when owner, not locked, has remote, and not archived', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.archive.shouldShow(context), isTrue); + }); + + test('should not show when not owner', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: false, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.archive.shouldShow(context), isFalse); + }); + + test('should not show when in locked view', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: true, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.archive.shouldShow(context), isFalse); + }); + + test('should not show when asset has no remote', () { + final localAsset = createLocalAsset(); + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.archive.shouldShow(context), isFalse); + }); + + test('should not show when already archived', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.archive.shouldShow(context), isFalse); + }); + }); + + group('unarchive button', () { + test('should show when owner, not locked, has remote, and is archived', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unarchive.shouldShow(context), isTrue); + }); + + test('should not show when not archived', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unarchive.shouldShow(context), isFalse); + }); + + test('should not show when not owner', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: false, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unarchive.shouldShow(context), isFalse); + }); + }); + + group('download button', () { + test('should show when not locked, has remote, and no local copy', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.download.shouldShow(context), isTrue); + }); + + test('should not show when has local copy', () { + final mergedAsset = createLocalAsset(remoteId: 'remote-id'); + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.download.shouldShow(context), isFalse); + }); + + test('should not show when in locked view', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: true, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.download.shouldShow(context), isFalse); + }); + }); + + group('trash button', () { + test('should show when owner, not locked, has remote, and trash enabled', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.trash.shouldShow(context), isTrue); + }); + + test('should not show when trash disabled', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: false, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.trash.shouldShow(context), isFalse); + }); + }); + + group('deletePermanent button', () { + test('should show when owner, not locked, has remote, and trash disabled', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: false, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deletePermanent.shouldShow(context), isTrue); + }); + + test('should not show when trash enabled', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deletePermanent.shouldShow(context), isFalse); + }); + }); + + group('delete button', () { + test('should show when owner, not locked, and has remote', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.delete.shouldShow(context), isTrue); + }); + }); + + group('moveToLockFolder button', () { + test('should show when owner, not locked, and has remote', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.moveToLockFolder.shouldShow(context), isTrue); + }); + }); + + group('deleteLocal button', () { + test('should show when not locked and asset is local only', () { + final localAsset = createLocalAsset(); + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deleteLocal.shouldShow(context), isTrue); + }); + + test('should not show when asset is not local only', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deleteLocal.shouldShow(context), isFalse); + }); + + test('should show when asset is merged', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.deleteLocal.shouldShow(context), isTrue); + }); + }); + + group('upload button', () { + test('should show when not locked and asset is local only', () { + final localAsset = createLocalAsset(); + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.upload.shouldShow(context), isTrue); + }); + }); + + group('removeFromAlbum button', () { + test('should show when owner, not locked, and has current album', () { + final album = createRemoteAlbum(); + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.removeFromAlbum.shouldShow(context), isTrue); + }); + + test('should not show when no current album', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.removeFromAlbum.shouldShow(context), isFalse); + }); + }); + + group('likeActivity button', () { + test('should show when not locked, has album, activity enabled, and shared', () { + final album = createRemoteAlbum(isActivityEnabled: true, isShared: true); + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.likeActivity.shouldShow(context), isTrue); + }); + + test('should not show when activity not enabled', () { + final album = createRemoteAlbum(isActivityEnabled: false, isShared: true); + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.likeActivity.shouldShow(context), isFalse); + }); + + test('should not show when album not shared', () { + final album = createRemoteAlbum(isActivityEnabled: true, isShared: false); + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.likeActivity.shouldShow(context), isFalse); + }); + + test('should not show when no album', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.likeActivity.shouldShow(context), isFalse); + }); + }); + + group('advancedTroubleshooting button', () { + test('should show when in advanced troubleshooting mode', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: true, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.advancedInfo.shouldShow(context), isTrue); + }); + + test('should not show when not in advanced troubleshooting mode', () { + final context = ActionButtonContext( + asset: mergedAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.advancedInfo.shouldShow(context), isFalse); + }); + }); + }); + + group('unstack button', () { + test('should show when owner, not locked, has remote, and is stacked', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: true, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isTrue); + }); + + test('should not show when not stacked', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isFalse); + }); + + test('should not show when not owner', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: false, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + expect(ActionButtonType.unstack.shouldShow(context), isFalse); + }); + }); + + group('ActionButtonType.buildButton', () { + late BaseAsset asset; + late ActionButtonContext context; + + setUp(() { + asset = createLocalAsset(remoteId: 'remote-id'); + context = ActionButtonContext( + asset: asset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + }); + + test('should build correct widget for each button type', () { + for (final buttonType in ActionButtonType.values) { + if (buttonType == ActionButtonType.removeFromAlbum) { + final album = createRemoteAlbum(); + final contextWithAlbum = ActionButtonContext( + asset: asset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + final widget = buttonType.buildButton(contextWithAlbum); + expect(widget, isA()); + } else if (buttonType == ActionButtonType.unstack) { + final album = createRemoteAlbum(); + final contextWithAlbum = ActionButtonContext( + asset: asset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: true, + source: ActionSource.timeline, + ); + final widget = buttonType.buildButton(contextWithAlbum); + expect(widget, isA()); + } else { + final widget = buttonType.buildButton(context); + expect(widget, isA()); + } + } + }); + }); + + group('ActionButtonBuilder', () { + test('should return buttons that should show', () { + final remoteAsset = createRemoteAsset(); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + final widgets = ActionButtonBuilder.build(context); + + expect(widgets, isNotEmpty); + expect(widgets.length, greaterThan(0)); + }); + + test('should include album-specific buttons when album is present', () { + final remoteAsset = createRemoteAsset(); + final album = createRemoteAlbum(isActivityEnabled: true, isShared: true); + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: album, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + final widgets = ActionButtonBuilder.build(context); + + expect(widgets, isNotEmpty); + }); + + test('should only include local buttons for local assets', () { + final localAsset = createLocalAsset(); + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + final widgets = ActionButtonBuilder.build(context); + + expect(widgets, isNotEmpty); + }); + + test('should respect archived state', () { + final remoteAsset = createRemoteAsset(); + + final archivedContext = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: true, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + final archivedWidgets = ActionButtonBuilder.build(archivedContext); + + final nonArchivedContext = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.timeline, + ); + + final nonArchivedWidgets = ActionButtonBuilder.build(nonArchivedContext); + + expect(archivedWidgets, isNotEmpty); + expect(nonArchivedWidgets, isNotEmpty); + }); + }); +} diff --git a/mobile/test/widget_tester_extensions.dart b/mobile/test/widget_tester_extensions.dart index 7d5b266224..bb3fc3f418 100644 --- a/mobile/test/widget_tester_extensions.dart +++ b/mobile/test/widget_tester_extensions.dart @@ -18,10 +18,7 @@ extension PumpConsumerWidget on WidgetTester { return pumpWidget( ProviderScope( overrides: overrides, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: Material(child: widget), - ), + child: MaterialApp(debugShowCheckedModeBanner: false, home: Material(child: widget)), ), duration: duration, phase: phase, diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index d6f1333489..1ce33b96e6 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -15,7 +15,7 @@ function dart { patch --no-backup-if-mismatch -u api.mustache =14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index f2885f4a7b..05f0b320e0 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.135.3", + "version": "2.0.1", "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.15.33", + "@types/node": "^22.18.8", "typescript": "^5.3.3" }, "repository": { @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" } } diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 0ac41e0bca..8b9d8d794b 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.135.3 + * 2.0.1 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -317,6 +317,8 @@ export type TagResponseDto = { export type AssetResponseDto = { /** base64 encoded sha1 hash */ checksum: string; + /** The UTC timestamp when the asset was originally uploaded to Immich. */ + createdAt: string; deviceAssetId: string; deviceId: string; duplicateId?: string | null; @@ -384,6 +386,14 @@ export type CreateAlbumDto = { assetIds?: string[]; description?: string; }; +export type AlbumsAddAssetsDto = { + albumIds: string[]; + assetIds: string[]; +}; +export type AlbumsAddAssetsResponseDto = { + error?: BulkIdErrorReason; + success: boolean; +}; export type AlbumStatisticsResponseDto = { notShared: number; owned: number; @@ -437,6 +447,10 @@ export type AssetBulkDeleteDto = { force?: boolean; ids: string[]; }; +export type AssetMetadataUpsertItemDto = { + key: AssetMetadataKey; + value: object; +}; export type AssetMediaCreateDto = { assetData: Blob; deviceAssetId: string; @@ -447,6 +461,7 @@ export type AssetMediaCreateDto = { filename?: string; isFavorite?: boolean; livePhotoVideoId?: string; + metadata: AssetMetadataUpsertItemDto[]; sidecarData?: Blob; visibility?: AssetVisibility; }; @@ -456,6 +471,7 @@ export type AssetMediaResponseDto = { }; export type AssetBulkUpdateDto = { dateTimeOriginal?: string; + dateTimeRelative?: number; description?: string; duplicateId?: string | null; ids: string[]; @@ -463,6 +479,7 @@ export type AssetBulkUpdateDto = { latitude?: number; longitude?: number; rating?: number; + timeZone?: string; visibility?: AssetVisibility; }; export type AssetBulkUploadCheckItem = { @@ -504,6 +521,14 @@ export type UpdateAssetDto = { rating?: number; visibility?: AssetVisibility; }; +export type AssetMetadataResponseDto = { + key: AssetMetadataKey; + updatedAt: string; + value: object; +}; +export type AssetMetadataUpsertDto = { + items: AssetMetadataUpsertItemDto[]; +}; export type AssetMediaReplaceDto = { assetData: Blob; deviceAssetId: string; @@ -786,7 +811,10 @@ export type PartnerResponseDto = { profileChangedAt: string; profileImagePath: string; }; -export type UpdatePartnerDto = { +export type PartnerCreateDto = { + sharedWithId: string; +}; +export type PartnerUpdateDto = { inTimeline: boolean; }; export type PeopleResponseDto = { @@ -989,7 +1017,8 @@ export type SmartSearchDto = { model?: string | null; page?: number; personIds?: string[]; - query: string; + query?: string; + queryAssetId?: string; rating?: number; size?: number; state?: string | null; @@ -1199,6 +1228,7 @@ export type SharedLinkResponseDto = { key: string; password: string | null; showMetadata: boolean; + slug: string | null; token?: string | null; "type": SharedLinkType; userId: string; @@ -1208,10 +1238,11 @@ export type SharedLinkCreateDto = { allowDownload?: boolean; allowUpload?: boolean; assetIds?: string[]; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; "type": SharedLinkType; }; export type SharedLinkEditDto = { @@ -1221,10 +1252,11 @@ export type SharedLinkEditDto = { Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. */ changeExpiryTime?: boolean; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; }; export type AssetIdsResponseDto = { assetId: string; @@ -1351,6 +1383,11 @@ export type SystemConfigLoggingDto = { enabled: boolean; level: LogLevel; }; +export type MachineLearningAvailabilityChecksDto = { + enabled: boolean; + interval: number; + timeout: number; +}; export type ClipConfig = { enabled: boolean; modelName: string; @@ -1367,12 +1404,11 @@ export type FacialRecognitionConfig = { modelName: string; }; export type SystemConfigMachineLearningDto = { + availabilityChecks: MachineLearningAvailabilityChecksDto; clip: ClipConfig; duplicateDetection: DuplicateDetectionConfig; enabled: boolean; facialRecognition: FacialRecognitionConfig; - /** This property was deprecated in v1.122.0 */ - url?: string; urls: string[]; }; export type SystemConfigMapDto = { @@ -1529,10 +1565,14 @@ export type TimeBucketAssetResponseDto = { isImage: boolean[]; /** Array indicating whether each asset is in the trash */ isTrashed: boolean[]; + /** Array of latitude coordinates extracted from EXIF GPS data */ + latitude?: (number | null)[]; /** Array of live photo video asset IDs (null for non-live photos) */ livePhotoVideoId: (string | null)[]; /** Array of UTC offset hours at the time each photo was taken. Positive values are east of UTC, negative values are west of UTC. Values may be fractional (e.g., 5.5 for +05:30, -9.75 for -09:45). Applying this offset to 'fileCreatedAt' will give you the time the photo was taken from the photographer's perspective. */ localOffsetHours: number[]; + /** Array of longitude coordinates extracted from EXIF GPS data */ + longitude?: (number | null)[]; /** Array of owner IDs for each asset */ ownerId: string[]; /** Array of projection types for 360° content (e.g., "EQUIRECTANGULAR", "CUBEFACE", "CYLINDRICAL") */ @@ -1575,6 +1615,9 @@ export type CreateProfileImageResponseDto = { profileImagePath: string; userId: string; }; +/** + * This endpoint requires the `activity.read` permission. + */ export function getActivities({ albumId, assetId, level, $type, userId }: { albumId: string; assetId?: string; @@ -1595,6 +1638,9 @@ export function getActivities({ albumId, assetId, level, $type, userId }: { ...opts })); } +/** + * This endpoint requires the `activity.create` permission. + */ export function createActivity({ activityCreateDto }: { activityCreateDto: ActivityCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1607,6 +1653,9 @@ export function createActivity({ activityCreateDto }: { body: activityCreateDto }))); } +/** + * This endpoint requires the `activity.statistics` permission. + */ export function getActivityStatistics({ albumId, assetId }: { albumId: string; assetId?: string; @@ -1621,6 +1670,9 @@ export function getActivityStatistics({ albumId, assetId }: { ...opts })); } +/** + * This endpoint requires the `activity.delete` permission. + */ export function deleteActivity({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1629,6 +1681,15 @@ export function deleteActivity({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `adminAuth.unlinkAll` permission. + */ +export function unlinkAllOAuthAccountsAdmin(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText("/admin/auth/unlink-all", { + ...opts, + method: "POST" + })); +} export function createNotification({ notificationCreateDto }: { notificationCreateDto: NotificationCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1666,6 +1727,9 @@ export function sendTestEmailAdmin({ systemConfigSmtpDto }: { body: systemConfigSmtpDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function searchUsersAdmin({ id, withDeleted }: { id?: string; withDeleted?: boolean; @@ -1680,6 +1744,9 @@ export function searchUsersAdmin({ id, withDeleted }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.create` permission. + */ export function createUserAdmin({ userAdminCreateDto }: { userAdminCreateDto: UserAdminCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1692,6 +1759,9 @@ export function createUserAdmin({ userAdminCreateDto }: { body: userAdminCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function deleteUserAdmin({ id, userAdminDeleteDto }: { id: string; userAdminDeleteDto: UserAdminDeleteDto; @@ -1705,6 +1775,9 @@ export function deleteUserAdmin({ id, userAdminDeleteDto }: { body: userAdminDeleteDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1715,6 +1788,9 @@ export function getUserAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserAdmin({ id, userAdminUpdateDto }: { id: string; userAdminUpdateDto: UserAdminUpdateDto; @@ -1728,6 +1804,9 @@ export function updateUserAdmin({ id, userAdminUpdateDto }: { body: userAdminUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserPreferencesAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1738,6 +1817,9 @@ export function getUserPreferencesAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { id: string; userPreferencesUpdateDto: UserPreferencesUpdateDto; @@ -1751,6 +1833,9 @@ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function restoreUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1762,6 +1847,9 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility }: { id: string; isFavorite?: boolean; @@ -1779,6 +1867,9 @@ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility } ...opts })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAllAlbums({ assetId, shared }: { assetId?: string; shared?: boolean; @@ -1793,6 +1884,9 @@ export function getAllAlbums({ assetId, shared }: { ...opts })); } +/** + * This endpoint requires the `album.create` permission. + */ export function createAlbum({ createAlbumDto }: { createAlbumDto: CreateAlbumDto; }, opts?: Oazapfts.RequestOpts) { @@ -1819,6 +1913,29 @@ export function getAllAlbumsSlim({ assetId, shared }: { ...opts })); } +/** + * This endpoint requires the `albumAsset.create` permission. + */ +export function addAssetsToAlbums({ key, slug, albumsAddAssetsDto }: { + key?: string; + slug?: string; + albumsAddAssetsDto: AlbumsAddAssetsDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AlbumsAddAssetsResponseDto; + }>(`/albums/assets${QS.query(QS.explode({ + key, + slug + }))}`, oazapfts.json({ + ...opts, + method: "PUT", + body: albumsAddAssetsDto + }))); +} +/** + * This endpoint requires the `album.statistics` permission. + */ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1827,6 +1944,9 @@ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `album.delete` permission. + */ export function deleteAlbum({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1835,9 +1955,13 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } -export function getAlbumInfo({ id, key, withoutAssets }: { +/** + * This endpoint requires the `album.read` permission. + */ +export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; + slug?: string; withoutAssets?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -1845,11 +1969,15 @@ export function getAlbumInfo({ id, key, withoutAssets }: { data: AlbumResponseDto; }>(`/albums/${encodeURIComponent(id)}${QS.query(QS.explode({ key, + slug, withoutAssets }))}`, { ...opts })); } +/** + * This endpoint requires the `album.update` permission. + */ export function updateAlbumInfo({ id, updateAlbumDto }: { id: string; updateAlbumDto: UpdateAlbumDto; @@ -1863,6 +1991,9 @@ export function updateAlbumInfo({ id, updateAlbumDto }: { body: updateAlbumDto }))); } +/** + * This endpoint requires the `albumAsset.delete` permission. + */ export function removeAssetFromAlbum({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -1876,22 +2007,30 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function addAssetsToAlbum({ id, key, bulkIdsDto }: { +/** + * This endpoint requires the `albumAsset.create` permission. + */ +export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; + slug?: string; bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: BulkIdResponseDto[]; }>(`/albums/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumUser.delete` permission. + */ export function removeUserFromAlbum({ id, userId }: { id: string; userId: string; @@ -1901,6 +2040,9 @@ export function removeUserFromAlbum({ id, userId }: { method: "DELETE" })); } +/** + * This endpoint requires the `albumUser.update` permission. + */ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { id: string; userId: string; @@ -1912,6 +2054,9 @@ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { body: updateAlbumUserDto }))); } +/** + * This endpoint requires the `albumUser.create` permission. + */ export function addUsersToAlbum({ id, addUsersDto }: { id: string; addUsersDto: AddUsersDto; @@ -1925,6 +2070,9 @@ export function addUsersToAlbum({ id, addUsersDto }: { body: addUsersDto }))); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKeys(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1933,6 +2081,9 @@ export function getApiKeys(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `apiKey.create` permission. + */ export function createApiKey({ apiKeyCreateDto }: { apiKeyCreateDto: ApiKeyCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1945,6 +2096,17 @@ export function createApiKey({ apiKeyCreateDto }: { body: apiKeyCreateDto }))); } +export function getMyApiKey(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: ApiKeyResponseDto; + }>("/api-keys/me", { + ...opts + })); +} +/** + * This endpoint requires the `apiKey.delete` permission. + */ export function deleteApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1953,6 +2115,9 @@ export function deleteApiKey({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1963,6 +2128,9 @@ export function getApiKey({ id }: { ...opts })); } +/** + * This endpoint requires the `apiKey.update` permission. + */ export function updateApiKey({ id, apiKeyUpdateDto }: { id: string; apiKeyUpdateDto: ApiKeyUpdateDto; @@ -1976,6 +2144,9 @@ export function updateApiKey({ id, apiKeyUpdateDto }: { body: apiKeyUpdateDto }))); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function deleteAssets({ assetBulkDeleteDto }: { assetBulkDeleteDto: AssetBulkDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -1985,8 +2156,12 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } -export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { +/** + * This endpoint requires the `asset.upload` permission. + */ +export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; + slug?: string; xImmichChecksum?: string; assetMediaCreateDto: AssetMediaCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1994,7 +2169,8 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { status: 201; data: AssetMediaResponseDto; }>(`/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "POST", @@ -2004,6 +2180,9 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { }) }))); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAssets({ assetBulkUpdateDto }: { assetBulkUpdateDto: AssetBulkUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2066,7 +2245,7 @@ export function runAssetJobs({ assetJobsDto }: { }))); } /** - * This property was deprecated in v1.116.0 + * This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. */ export function getRandom({ count }: { count?: number; @@ -2080,6 +2259,9 @@ export function getRandom({ count }: { ...opts })); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { isFavorite?: boolean; isTrashed?: boolean; @@ -2096,19 +2278,27 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } -export function getAssetInfo({ id, key }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetInfo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetResponseDto; }>(`/assets/${encodeURIComponent(id)}${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAsset({ id, updateAssetDto }: { id: string; updateAssetDto: UpdateAssetDto; @@ -2122,62 +2312,134 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } -export function downloadAsset({ id, key }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetMetadata({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto[]; + }>(`/assets/${encodeURIComponent(id)}/metadata`, { + ...opts + })); +} +/** + * This endpoint requires the `asset.update` permission. + */ +export function updateAssetMetadata({ id, assetMetadataUpsertDto }: { + id: string; + assetMetadataUpsertDto: AssetMetadataUpsertDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto[]; + }>(`/assets/${encodeURIComponent(id)}/metadata`, oazapfts.json({ + ...opts, + method: "PUT", + body: assetMetadataUpsertDto + }))); +} +/** + * This endpoint requires the `asset.update` permission. + */ +export function deleteAssetMetadata({ id, key }: { + id: string; + key: AssetMetadataKey; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/assets/${encodeURIComponent(id)}/metadata/${encodeURIComponent(key)}`, { + ...opts, + method: "DELETE" + })); +} +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetMetadataByKey({ id, key }: { + id: string; + key: AssetMetadataKey; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetMetadataResponseDto; + }>(`/assets/${encodeURIComponent(id)}/metadata/${encodeURIComponent(key)}`, { + ...opts + })); +} +/** + * This endpoint requires the `asset.download` permission. + */ +export function downloadAsset({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); } /** - * replaceAsset + * Replace the asset with new file, without changing its id */ -export function replaceAsset({ id, key, assetMediaReplaceDto }: { +export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { id: string; key?: string; + slug?: string; assetMediaReplaceDto: AssetMediaReplaceDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetMediaResponseDto; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "PUT", body: assetMediaReplaceDto }))); } -export function viewAsset({ id, key, size }: { +/** + * This endpoint requires the `asset.view` permission. + */ +export function viewAsset({ id, key, size, slug }: { id: string; key?: string; size?: AssetMediaSize; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/thumbnail${QS.query(QS.explode({ key, - size + size, + slug }))}`, { ...opts })); } -export function playAssetVideo({ id, key }: { +/** + * This endpoint requires the `asset.view` permission. + */ +export function playAssetVideo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/video/playback${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2194,6 +2456,9 @@ export function signUpAdmin({ signUpDto }: { body: signUpDto }))); } +/** + * This endpoint requires the `auth.changePassword` permission. + */ export function changePassword({ changePasswordDto }: { changePasswordDto: ChangePasswordDto; }, opts?: Oazapfts.RequestOpts) { @@ -2227,6 +2492,9 @@ export function logout(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `pinCode.delete` permission. + */ export function resetPinCode({ pinCodeResetDto }: { pinCodeResetDto: PinCodeResetDto; }, opts?: Oazapfts.RequestOpts) { @@ -2236,6 +2504,9 @@ export function resetPinCode({ pinCodeResetDto }: { body: pinCodeResetDto }))); } +/** + * This endpoint requires the `pinCode.create` permission. + */ export function setupPinCode({ pinCodeSetupDto }: { pinCodeSetupDto: PinCodeSetupDto; }, opts?: Oazapfts.RequestOpts) { @@ -2245,6 +2516,9 @@ export function setupPinCode({ pinCodeSetupDto }: { body: pinCodeSetupDto }))); } +/** + * This endpoint requires the `pinCode.update` permission. + */ export function changePinCode({ pinCodeChangeDto }: { pinCodeChangeDto: PinCodeChangeDto; }, opts?: Oazapfts.RequestOpts) { @@ -2286,36 +2560,49 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } -export function downloadArchive({ key, assetIdsDto }: { +/** + * This endpoint requires the `asset.download` permission. + */ +export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/download/archive${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: assetIdsDto }))); } -export function getDownloadInfo({ key, downloadInfoDto }: { +/** + * This endpoint requires the `asset.download` permission. + */ +export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; + slug?: string; downloadInfoDto: DownloadInfoDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 201; data: DownloadResponseDto; }>(`/download/info${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: downloadInfoDto }))); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicates({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2325,6 +2612,9 @@ export function deleteDuplicates({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `duplicate.read` permission. + */ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2333,6 +2623,9 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicate({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2341,6 +2634,9 @@ export function deleteDuplicate({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `face.read` permission. + */ export function getFaces({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2353,6 +2649,9 @@ export function getFaces({ id }: { ...opts })); } +/** + * This endpoint requires the `face.create` permission. + */ export function createFace({ assetFaceCreateDto }: { assetFaceCreateDto: AssetFaceCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2362,6 +2661,9 @@ export function createFace({ assetFaceCreateDto }: { body: assetFaceCreateDto }))); } +/** + * This endpoint requires the `face.delete` permission. + */ export function deleteFace({ id, assetFaceDeleteDto }: { id: string; assetFaceDeleteDto: AssetFaceDeleteDto; @@ -2372,6 +2674,9 @@ export function deleteFace({ id, assetFaceDeleteDto }: { body: assetFaceDeleteDto }))); } +/** + * This endpoint requires the `face.update` permission. + */ export function reassignFacesById({ id, faceDto }: { id: string; faceDto: FaceDto; @@ -2385,6 +2690,9 @@ export function reassignFacesById({ id, faceDto }: { body: faceDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.read` permission. + */ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2393,6 +2701,9 @@ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function createJob({ jobCreateDto }: { jobCreateDto: JobCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2402,6 +2713,9 @@ export function createJob({ jobCreateDto }: { body: jobCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function sendJobCommand({ id, jobCommandDto }: { id: JobName; jobCommandDto: JobCommandDto; @@ -2415,6 +2729,9 @@ export function sendJobCommand({ id, jobCommandDto }: { body: jobCommandDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2423,6 +2740,9 @@ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.create` permission. + */ export function createLibrary({ createLibraryDto }: { createLibraryDto: CreateLibraryDto; }, opts?: Oazapfts.RequestOpts) { @@ -2435,6 +2755,9 @@ export function createLibrary({ createLibraryDto }: { body: createLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.delete` permission. + */ export function deleteLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2443,6 +2766,9 @@ export function deleteLibrary({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2453,6 +2779,9 @@ export function getLibrary({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function updateLibrary({ id, updateLibraryDto }: { id: string; updateLibraryDto: UpdateLibraryDto; @@ -2466,6 +2795,9 @@ export function updateLibrary({ id, updateLibraryDto }: { body: updateLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function scanLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2474,6 +2806,9 @@ export function scanLibrary({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `library.statistics` permission. + */ export function getLibraryStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2497,11 +2832,11 @@ export function validate({ id, validateLibraryDto }: { body: validateLibraryDto }))); } -export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners, withSharedAlbums }: { - fileCreatedAfter?: string; - fileCreatedBefore?: string; +export function getMapMarkers({ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, withPartners, withSharedAlbums }: { isArchived?: boolean; isFavorite?: boolean; + fileCreatedAfter?: string; + fileCreatedBefore?: string; withPartners?: boolean; withSharedAlbums?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -2509,10 +2844,10 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, status: 200; data: MapMarkerResponseDto[]; }>(`/map/markers${QS.query(QS.explode({ - fileCreatedAfter, - fileCreatedBefore, isArchived, isFavorite, + fileCreatedAfter, + fileCreatedBefore, withPartners, withSharedAlbums }))}`, { @@ -2533,6 +2868,9 @@ export function reverseGeocode({ lat, lon }: { ...opts })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function searchMemories({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2551,6 +2889,9 @@ export function searchMemories({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.create` permission. + */ export function createMemory({ memoryCreateDto }: { memoryCreateDto: MemoryCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2563,6 +2904,9 @@ export function createMemory({ memoryCreateDto }: { body: memoryCreateDto }))); } +/** + * This endpoint requires the `memory.statistics` permission. + */ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2581,6 +2925,9 @@ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.delete` permission. + */ export function deleteMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2589,6 +2936,9 @@ export function deleteMemory({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function getMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2599,6 +2949,9 @@ export function getMemory({ id }: { ...opts })); } +/** + * This endpoint requires the `memory.update` permission. + */ export function updateMemory({ id, memoryUpdateDto }: { id: string; memoryUpdateDto: MemoryUpdateDto; @@ -2612,6 +2965,9 @@ export function updateMemory({ id, memoryUpdateDto }: { body: memoryUpdateDto }))); } +/** + * This endpoint requires the `memoryAsset.delete` permission. + */ export function removeMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2625,6 +2981,9 @@ export function removeMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `memoryAsset.create` permission. + */ export function addMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2638,6 +2997,9 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotifications({ notificationDeleteAllDto }: { notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2647,6 +3009,9 @@ export function deleteNotifications({ notificationDeleteAllDto }: { body: notificationDeleteAllDto }))); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotifications({ id, level, $type, unread }: { id?: string; level?: NotificationLevel; @@ -2665,6 +3030,9 @@ export function getNotifications({ id, level, $type, unread }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotifications({ notificationUpdateAllDto }: { notificationUpdateAllDto: NotificationUpdateAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2674,6 +3042,9 @@ export function updateNotifications({ notificationUpdateAllDto }: { body: notificationUpdateAllDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2682,6 +3053,9 @@ export function deleteNotification({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2692,6 +3066,9 @@ export function getNotification({ id }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotification({ id, notificationUpdateDto }: { id: string; notificationUpdateDto: NotificationUpdateDto; @@ -2733,7 +3110,7 @@ export function linkOAuthAccount({ oAuthCallbackDto }: { oAuthCallbackDto: OAuthCallbackDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; + status: 200; data: UserAdminResponseDto; }>("/oauth/link", oazapfts.json({ ...opts, @@ -2755,6 +3132,9 @@ export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `partner.read` permission. + */ export function getPartners({ direction }: { direction: PartnerDirection; }, opts?: Oazapfts.RequestOpts) { @@ -2767,6 +3147,24 @@ export function getPartners({ direction }: { ...opts })); } +/** + * This endpoint requires the `partner.create` permission. + */ +export function createPartner({ partnerCreateDto }: { + partnerCreateDto: PartnerCreateDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 201; + data: PartnerResponseDto; + }>("/partners", oazapfts.json({ + ...opts, + method: "POST", + body: partnerCreateDto + }))); +} +/** + * This endpoint requires the `partner.delete` permission. + */ export function removePartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2775,7 +3173,10 @@ export function removePartner({ id }: { method: "DELETE" })); } -export function createPartner({ id }: { +/** + * This property was deprecated in v1.141.0. This endpoint requires the `partner.create` permission. + */ +export function createPartnerDeprecated({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -2786,9 +3187,12 @@ export function createPartner({ id }: { method: "POST" })); } -export function updatePartner({ id, updatePartnerDto }: { +/** + * This endpoint requires the `partner.update` permission. + */ +export function updatePartner({ id, partnerUpdateDto }: { id: string; - updatePartnerDto: UpdatePartnerDto; + partnerUpdateDto: PartnerUpdateDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2796,9 +3200,12 @@ export function updatePartner({ id, updatePartnerDto }: { }>(`/partners/${encodeURIComponent(id)}`, oazapfts.json({ ...opts, method: "PUT", - body: updatePartnerDto + body: partnerUpdateDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePeople({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2808,6 +3215,9 @@ export function deletePeople({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function getAllPeople({ closestAssetId, closestPersonId, page, size, withHidden }: { closestAssetId?: string; closestPersonId?: string; @@ -2828,6 +3238,9 @@ export function getAllPeople({ closestAssetId, closestPersonId, page, size, with ...opts })); } +/** + * This endpoint requires the `person.create` permission. + */ export function createPerson({ personCreateDto }: { personCreateDto: PersonCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2840,6 +3253,9 @@ export function createPerson({ personCreateDto }: { body: personCreateDto }))); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePeople({ peopleUpdateDto }: { peopleUpdateDto: PeopleUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2852,6 +3268,9 @@ export function updatePeople({ peopleUpdateDto }: { body: peopleUpdateDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2860,6 +3279,9 @@ export function deletePerson({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2870,6 +3292,9 @@ export function getPerson({ id }: { ...opts })); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePerson({ id, personUpdateDto }: { id: string; personUpdateDto: PersonUpdateDto; @@ -2883,12 +3308,15 @@ export function updatePerson({ id, personUpdateDto }: { body: personUpdateDto }))); } +/** + * This endpoint requires the `person.merge` permission. + */ export function mergePerson({ id, mergePersonDto }: { id: string; mergePersonDto: MergePersonDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ - status: 201; + status: 200; data: BulkIdResponseDto[]; }>(`/people/${encodeURIComponent(id)}/merge`, oazapfts.json({ ...opts, @@ -2896,6 +3324,9 @@ export function mergePerson({ id, mergePersonDto }: { body: mergePersonDto }))); } +/** + * This endpoint requires the `person.reassign` permission. + */ export function reassignFaces({ id, assetFaceUpdateDto }: { id: string; assetFaceUpdateDto: AssetFaceUpdateDto; @@ -2909,6 +3340,9 @@ export function reassignFaces({ id, assetFaceUpdateDto }: { body: assetFaceUpdateDto }))); } +/** + * This endpoint requires the `person.statistics` permission. + */ export function getPersonStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2919,6 +3353,9 @@ export function getPersonStatistics({ id }: { ...opts })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPersonThumbnail({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2929,6 +3366,9 @@ export function getPersonThumbnail({ id }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2937,6 +3377,9 @@ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getExploreData(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2945,6 +3388,85 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ +export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { + albumIds?: string[]; + city?: string | null; + country?: string | null; + createdAfter?: string; + createdBefore?: string; + deviceId?: string; + isEncoded?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + lensModel?: string | null; + libraryId?: string | null; + make?: string; + minFileSize?: number; + model?: string | null; + personIds?: string[]; + rating?: number; + size?: number; + state?: string | null; + tagIds?: string[] | null; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + $type?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + visibility?: AssetVisibility; + withDeleted?: boolean; + withExif?: boolean; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetResponseDto[]; + }>(`/search/large-assets${QS.query(QS.explode({ + albumIds, + city, + country, + createdAfter, + createdBefore, + deviceId, + isEncoded, + isFavorite, + isMotion, + isNotInAlbum, + isOffline, + lensModel, + libraryId, + make, + minFileSize, + model, + personIds, + rating, + size, + state, + tagIds, + takenAfter, + takenBefore, + trashedAfter, + trashedBefore, + "type": $type, + updatedAfter, + updatedBefore, + visibility, + withDeleted, + withExif + }))}`, { + ...opts, + method: "POST" + })); +} +/** + * This endpoint requires the `asset.read` permission. + */ export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -2957,6 +3479,9 @@ export function searchAssets({ metadataSearchDto }: { body: metadataSearchDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function searchPerson({ name, withHidden }: { name: string; withHidden?: boolean; @@ -2971,6 +3496,9 @@ export function searchPerson({ name, withHidden }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchPlaces({ name }: { name: string; }, opts?: Oazapfts.RequestOpts) { @@ -2983,6 +3511,9 @@ export function searchPlaces({ name }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchRandom({ randomSearchDto }: { randomSearchDto: RandomSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -2995,6 +3526,9 @@ export function searchRandom({ randomSearchDto }: { body: randomSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchSmart({ smartSearchDto }: { smartSearchDto: SmartSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3007,6 +3541,9 @@ export function searchSmart({ smartSearchDto }: { body: smartSearchDto }))); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function searchAssetStatistics({ statisticsSearchDto }: { statisticsSearchDto: StatisticsSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3019,6 +3556,9 @@ export function searchAssetStatistics({ statisticsSearchDto }: { body: statisticsSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getSearchSuggestions({ country, includeNull, make, model, state, $type }: { country?: string; includeNull?: boolean; @@ -3041,6 +3581,9 @@ export function getSearchSuggestions({ country, includeNull, make, model, state, ...opts })); } +/** + * This endpoint requires the `server.about` permission. + */ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3049,6 +3592,9 @@ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.apkLinks` permission. + */ export function getApkLinks(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3073,12 +3619,18 @@ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + */ export function deleteServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/server/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + */ export function getServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3089,6 +3641,9 @@ export function getServerLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + */ export function setServerLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3117,6 +3672,9 @@ export function pingServer(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `server.statistics` permission. + */ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3125,6 +3683,9 @@ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.storage` permission. + */ export function getStorage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3149,6 +3710,9 @@ export function getServerVersion(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.versionCheck` permission. + */ export function getVersionCheck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3165,12 +3729,18 @@ export function getVersionHistory(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteAllSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/sessions", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `session.read` permission. + */ export function getSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3179,6 +3749,9 @@ export function getSessions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.create` permission. + */ export function createSession({ sessionCreateDto }: { sessionCreateDto: SessionCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3191,6 +3764,9 @@ export function createSession({ sessionCreateDto }: { body: sessionCreateDto }))); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3199,6 +3775,9 @@ export function deleteSession({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `session.update` permission. + */ export function updateSession({ id, sessionUpdateDto }: { id: string; sessionUpdateDto: SessionUpdateDto; @@ -3212,6 +3791,9 @@ export function updateSession({ id, sessionUpdateDto }: { body: sessionUpdateDto }))); } +/** + * This endpoint requires the `session.lock` permission. + */ export function lockSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3220,6 +3802,9 @@ export function lockSession({ id }: { method: "POST" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getAllSharedLinks({ albumId }: { albumId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3232,6 +3817,9 @@ export function getAllSharedLinks({ albumId }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.create` permission. + */ export function createSharedLink({ sharedLinkCreateDto }: { sharedLinkCreateDto: SharedLinkCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3244,22 +3832,27 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, token }: { - key?: string; +export function getMySharedLink({ password, token, key, slug }: { password?: string; token?: string; + key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: SharedLinkResponseDto; }>(`/shared-links/me${QS.query(QS.explode({ - key, password, - token + token, + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `sharedLink.delete` permission. + */ export function removeSharedLink({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3268,6 +3861,9 @@ export function removeSharedLink({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getSharedLinkById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3278,6 +3874,9 @@ export function getSharedLinkById({ id }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.update` permission. + */ export function updateSharedLink({ id, sharedLinkEditDto }: { id: string; sharedLinkEditDto: SharedLinkEditDto; @@ -3291,38 +3890,45 @@ export function updateSharedLink({ id, sharedLinkEditDto }: { body: sharedLinkEditDto }))); } -export function removeSharedLinkAssets({ id, key, assetIdsDto }: { +export function removeSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "DELETE", body: assetIdsDto }))); } -export function addSharedLinkAssets({ id, key, assetIdsDto }: { +export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", body: assetIdsDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStacks({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3332,6 +3938,9 @@ export function deleteStacks({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `stack.read` permission. + */ export function searchStacks({ primaryAssetId }: { primaryAssetId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3344,6 +3953,9 @@ export function searchStacks({ primaryAssetId }: { ...opts })); } +/** + * This endpoint requires the `stack.create` permission. + */ export function createStack({ stackCreateDto }: { stackCreateDto: StackCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3356,6 +3968,9 @@ export function createStack({ stackCreateDto }: { body: stackCreateDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3364,6 +3979,9 @@ export function deleteStack({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `stack.read` permission. + */ export function getStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3374,6 +3992,9 @@ export function getStack({ id }: { ...opts })); } +/** + * This endpoint requires the `stack.update` permission. + */ export function updateStack({ id, stackUpdateDto }: { id: string; stackUpdateDto: StackUpdateDto; @@ -3387,6 +4008,21 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +/** + * This endpoint requires the `stack.update` permission. + */ +export function removeAssetFromStack({ assetId, id }: { + assetId: string; + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/stacks/${encodeURIComponent(id)}/assets/${encodeURIComponent(assetId)}`, { + ...opts, + method: "DELETE" + })); +} +/** + * This endpoint requires the `syncCheckpoint.delete` permission. + */ export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -3396,6 +4032,9 @@ export function deleteSyncAck({ syncAckDeleteDto }: { body: syncAckDeleteDto }))); } +/** + * This endpoint requires the `syncCheckpoint.read` permission. + */ export function getSyncAck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3404,6 +4043,9 @@ export function getSyncAck(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `syncCheckpoint.update` permission. + */ export function sendSyncAck({ syncAckSetDto }: { syncAckSetDto: SyncAckSetDto; }, opts?: Oazapfts.RequestOpts) { @@ -3437,6 +4079,9 @@ export function getFullSyncForUser({ assetFullSyncDto }: { body: assetFullSyncDto }))); } +/** + * This endpoint requires the `sync.stream` permission. + */ export function getSyncStream({ syncStreamDto }: { syncStreamDto: SyncStreamDto; }, opts?: Oazapfts.RequestOpts) { @@ -3446,6 +4091,9 @@ export function getSyncStream({ syncStreamDto }: { body: syncStreamDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3454,6 +4102,9 @@ export function getConfig(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + */ export function updateConfig({ systemConfigDto }: { systemConfigDto: SystemConfigDto; }, opts?: Oazapfts.RequestOpts) { @@ -3466,6 +4117,9 @@ export function updateConfig({ systemConfigDto }: { body: systemConfigDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3474,6 +4128,9 @@ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3482,6 +4139,9 @@ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3490,6 +4150,9 @@ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + */ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { adminOnboardingUpdateDto: AdminOnboardingUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3499,6 +4162,9 @@ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { body: adminOnboardingUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3507,6 +4173,9 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3515,6 +4184,9 @@ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getAllTags(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3523,6 +4195,9 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.create` permission. + */ export function createTag({ tagCreateDto }: { tagCreateDto: TagCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3535,6 +4210,9 @@ export function createTag({ tagCreateDto }: { body: tagCreateDto }))); } +/** + * This endpoint requires the `tag.create` permission. + */ export function upsertTags({ tagUpsertDto }: { tagUpsertDto: TagUpsertDto; }, opts?: Oazapfts.RequestOpts) { @@ -3547,6 +4225,9 @@ export function upsertTags({ tagUpsertDto }: { body: tagUpsertDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function bulkTagAssets({ tagBulkAssetsDto }: { tagBulkAssetsDto: TagBulkAssetsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3559,6 +4240,9 @@ export function bulkTagAssets({ tagBulkAssetsDto }: { body: tagBulkAssetsDto }))); } +/** + * This endpoint requires the `tag.delete` permission. + */ export function deleteTag({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3567,6 +4251,9 @@ export function deleteTag({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getTagById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3577,6 +4264,9 @@ export function getTagById({ id }: { ...opts })); } +/** + * This endpoint requires the `tag.update` permission. + */ export function updateTag({ id, tagUpdateDto }: { id: string; tagUpdateDto: TagUpdateDto; @@ -3590,6 +4280,9 @@ export function updateTag({ id, tagUpdateDto }: { body: tagUpdateDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function untagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3603,6 +4296,9 @@ export function untagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function tagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3616,17 +4312,22 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withCoordinates, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; timeBucket: string; userId?: string; visibility?: AssetVisibility; + withCoordinates?: boolean; withPartners?: boolean; withStacked?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -3640,26 +4341,33 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers key, order, personId, + slug, tagId, timeBucket, userId, visibility, + withCoordinates, withPartners, withStacked }))}`, { ...opts })); } -export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, tagId, userId, visibility, withPartners, withStacked }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withCoordinates, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; userId?: string; visibility?: AssetVisibility; + withCoordinates?: boolean; withPartners?: boolean; withStacked?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -3673,15 +4381,20 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per key, order, personId, + slug, tagId, userId, visibility, + withCoordinates, withPartners, withStacked }))}`, { ...opts })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function emptyTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3691,6 +4404,9 @@ export function emptyTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3700,6 +4416,9 @@ export function restoreTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreAssets({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3712,6 +4431,9 @@ export function restoreAssets({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function searchUsers(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3720,6 +4442,9 @@ export function searchUsers(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.read` permission. + */ export function getMyUser(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3728,6 +4453,9 @@ export function getMyUser(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.update` permission. + */ export function updateMyUser({ userUpdateMeDto }: { userUpdateMeDto: UserUpdateMeDto; }, opts?: Oazapfts.RequestOpts) { @@ -3740,12 +4468,18 @@ export function updateMyUser({ userUpdateMeDto }: { body: userUpdateMeDto }))); } +/** + * This endpoint requires the `userLicense.delete` permission. + */ export function deleteUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userLicense.read` permission. + */ export function getUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3754,6 +4488,9 @@ export function getUserLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userLicense.update` permission. + */ export function setUserLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3766,12 +4503,18 @@ export function setUserLicense({ licenseKeyDto }: { body: licenseKeyDto }))); } +/** + * This endpoint requires the `userOnboarding.delete` permission. + */ export function deleteUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/onboarding", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userOnboarding.read` permission. + */ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3780,6 +4523,9 @@ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userOnboarding.update` permission. + */ export function setUserOnboarding({ onboardingDto }: { onboardingDto: OnboardingDto; }, opts?: Oazapfts.RequestOpts) { @@ -3792,6 +4538,9 @@ export function setUserOnboarding({ onboardingDto }: { body: onboardingDto }))); } +/** + * This endpoint requires the `userPreference.read` permission. + */ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3800,6 +4549,9 @@ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userPreference.update` permission. + */ export function updateMyPreferences({ userPreferencesUpdateDto }: { userPreferencesUpdateDto: UserPreferencesUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3812,12 +4564,18 @@ export function updateMyPreferences({ userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint requires the `userProfileImage.delete` permission. + */ export function deleteProfileImage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/profile-image", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userProfileImage.update` permission. + */ export function createProfileImage({ createProfileImageDto }: { createProfileImageDto: CreateProfileImageDto; }, opts?: Oazapfts.RequestOpts) { @@ -3830,6 +4588,9 @@ export function createProfileImage({ createProfileImageDto }: { body: createProfileImageDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function getUser({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3840,6 +4601,9 @@ export function getUser({ id }: { ...opts })); } +/** + * This endpoint requires the `userProfileImage.read` permission. + */ export function getProfileImage({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3932,6 +4696,12 @@ export enum AssetTypeEnum { Audio = "AUDIO", Other = "OTHER" } +export enum BulkIdErrorReason { + Duplicate = "duplicate", + NoPermission = "no_permission", + NotFound = "not_found", + Unknown = "unknown" +} export enum Error { Duplicate = "duplicate", NoPermission = "no_permission", @@ -3952,25 +4722,35 @@ export enum Permission { AssetRead = "asset.read", AssetUpdate = "asset.update", AssetDelete = "asset.delete", + AssetStatistics = "asset.statistics", AssetShare = "asset.share", AssetView = "asset.view", AssetDownload = "asset.download", AssetUpload = "asset.upload", + AssetReplace = "asset.replace", AlbumCreate = "album.create", AlbumRead = "album.read", AlbumUpdate = "album.update", AlbumDelete = "album.delete", AlbumStatistics = "album.statistics", - AlbumAddAsset = "album.addAsset", - AlbumRemoveAsset = "album.removeAsset", AlbumShare = "album.share", AlbumDownload = "album.download", + AlbumAssetCreate = "albumAsset.create", + AlbumAssetDelete = "albumAsset.delete", + AlbumUserCreate = "albumUser.create", + AlbumUserUpdate = "albumUser.update", + AlbumUserDelete = "albumUser.delete", + AuthChangePassword = "auth.changePassword", AuthDeviceDelete = "authDevice.delete", ArchiveRead = "archive.read", + DuplicateRead = "duplicate.read", + DuplicateDelete = "duplicate.delete", FaceCreate = "face.create", FaceRead = "face.read", FaceUpdate = "face.update", FaceDelete = "face.delete", + JobCreate = "job.create", + JobRead = "job.read", LibraryCreate = "library.create", LibraryRead = "library.read", LibraryUpdate = "library.update", @@ -3982,6 +4762,9 @@ export enum Permission { MemoryRead = "memory.read", MemoryUpdate = "memory.update", MemoryDelete = "memory.delete", + MemoryStatistics = "memory.statistics", + MemoryAssetCreate = "memoryAsset.create", + MemoryAssetDelete = "memoryAsset.delete", NotificationCreate = "notification.create", NotificationRead = "notification.read", NotificationUpdate = "notification.update", @@ -3997,6 +4780,17 @@ export enum Permission { PersonStatistics = "person.statistics", PersonMerge = "person.merge", PersonReassign = "person.reassign", + PinCodeCreate = "pinCode.create", + PinCodeUpdate = "pinCode.update", + PinCodeDelete = "pinCode.delete", + ServerAbout = "server.about", + ServerApkLinks = "server.apkLinks", + ServerStorage = "server.storage", + ServerStatistics = "server.statistics", + ServerVersionCheck = "server.versionCheck", + ServerLicenseRead = "serverLicense.read", + ServerLicenseUpdate = "serverLicense.update", + ServerLicenseDelete = "serverLicense.delete", SessionCreate = "session.create", SessionRead = "session.read", SessionUpdate = "session.update", @@ -4010,6 +4804,10 @@ export enum Permission { StackRead = "stack.read", StackUpdate = "stack.update", StackDelete = "stack.delete", + SyncStream = "sync.stream", + SyncCheckpointRead = "syncCheckpoint.read", + SyncCheckpointUpdate = "syncCheckpoint.update", + SyncCheckpointDelete = "syncCheckpoint.delete", SystemConfigRead = "systemConfig.read", SystemConfigUpdate = "systemConfig.update", SystemMetadataRead = "systemMetadata.read", @@ -4019,10 +4817,29 @@ export enum Permission { TagUpdate = "tag.update", TagDelete = "tag.delete", TagAsset = "tag.asset", - AdminUserCreate = "admin.user.create", - AdminUserRead = "admin.user.read", - AdminUserUpdate = "admin.user.update", - AdminUserDelete = "admin.user.delete" + UserRead = "user.read", + UserUpdate = "user.update", + UserLicenseCreate = "userLicense.create", + UserLicenseRead = "userLicense.read", + UserLicenseUpdate = "userLicense.update", + UserLicenseDelete = "userLicense.delete", + UserOnboardingRead = "userOnboarding.read", + UserOnboardingUpdate = "userOnboarding.update", + UserOnboardingDelete = "userOnboarding.delete", + UserPreferenceRead = "userPreference.read", + UserPreferenceUpdate = "userPreference.update", + UserProfileImageCreate = "userProfileImage.create", + UserProfileImageRead = "userProfileImage.read", + UserProfileImageUpdate = "userProfileImage.update", + UserProfileImageDelete = "userProfileImage.delete", + AdminUserCreate = "adminUser.create", + AdminUserRead = "adminUser.read", + AdminUserUpdate = "adminUser.update", + AdminUserDelete = "adminUser.delete", + AdminAuthUnlinkAll = "adminAuth.unlinkAll" +} +export enum AssetMetadataKey { + MobileApp = "mobile-app" } export enum AssetMediaStatus { Created = "created", @@ -4104,11 +4921,14 @@ export enum Error2 { NotFound = "not_found" } export enum SyncEntityType { + AuthUserV1 = "AuthUserV1", UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", AssetV1 = "AssetV1", AssetDeleteV1 = "AssetDeleteV1", AssetExifV1 = "AssetExifV1", + AssetMetadataV1 = "AssetMetadataV1", + AssetMetadataDeleteV1 = "AssetMetadataDeleteV1", PartnerV1 = "PartnerV1", PartnerDeleteV1 = "PartnerDeleteV1", PartnerAssetV1 = "PartnerAssetV1", @@ -4124,9 +4944,11 @@ export enum SyncEntityType { AlbumUserV1 = "AlbumUserV1", AlbumUserBackfillV1 = "AlbumUserBackfillV1", AlbumUserDeleteV1 = "AlbumUserDeleteV1", - AlbumAssetV1 = "AlbumAssetV1", + AlbumAssetCreateV1 = "AlbumAssetCreateV1", + AlbumAssetUpdateV1 = "AlbumAssetUpdateV1", AlbumAssetBackfillV1 = "AlbumAssetBackfillV1", - AlbumAssetExifV1 = "AlbumAssetExifV1", + AlbumAssetExifCreateV1 = "AlbumAssetExifCreateV1", + AlbumAssetExifUpdateV1 = "AlbumAssetExifUpdateV1", AlbumAssetExifBackfillV1 = "AlbumAssetExifBackfillV1", AlbumToAssetV1 = "AlbumToAssetV1", AlbumToAssetDeleteV1 = "AlbumToAssetDeleteV1", @@ -4144,7 +4966,8 @@ export enum SyncEntityType { UserMetadataV1 = "UserMetadataV1", UserMetadataDeleteV1 = "UserMetadataDeleteV1", SyncAckV1 = "SyncAckV1", - SyncResetV1 = "SyncResetV1" + SyncResetV1 = "SyncResetV1", + SyncCompleteV1 = "SyncCompleteV1" } export enum SyncRequestType { AlbumsV1 = "AlbumsV1", @@ -4154,6 +4977,8 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AssetMetadataV1 = "AssetMetadataV1", + AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", PartnersV1 = "PartnersV1", diff --git a/open-api/typescript-sdk/src/index.ts b/open-api/typescript-sdk/src/index.ts index 77be18f0e7..7adbca4d7e 100644 --- a/open-api/typescript-sdk/src/index.ts +++ b/open-api/typescript-sdk/src/index.ts @@ -6,11 +6,15 @@ export * from './fetch-errors.js'; export interface InitOptions { baseUrl: string; apiKey: string; + headers?: Record; } -export const init = ({ baseUrl, apiKey }: InitOptions) => { +export const init = ({ baseUrl, apiKey, headers }: InitOptions) => { setBaseUrl(baseUrl); setApiKey(apiKey); + if (headers) { + setHeaders(headers); + } }; export const getBaseUrl = () => defaults.baseUrl; @@ -24,6 +28,26 @@ export const setApiKey = (apiKey: string) => { defaults.headers['x-api-key'] = apiKey; }; +export const setHeader = (key: string, value: string) => { + assertNoApiKey(key); + defaults.headers = defaults.headers || {}; + defaults.headers[key] = value; +}; + +export const setHeaders = (headers: Record) => { + defaults.headers = defaults.headers || {}; + for (const [key, value] of Object.entries(headers)) { + assertNoApiKey(key); + defaults.headers[key] = value; + } +}; + +const assertNoApiKey = (headerKey: string) => { + if (headerKey.toLowerCase() === 'x-api-key') { + throw new Error('The API key header can only be set using setApiKey().'); + } +}; + export const getAssetOriginalPath = (id: string) => `/assets/${id}/original`; export const getAssetThumbnailPath = (id: string) => `/assets/${id}/thumbnail`; diff --git a/package.json b/package.json new file mode 100644 index 0000000000..18610fe4bc --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "immich-monorepo", + "version": "0.0.1", + "description": "Monorepo for Immich", + "private": true, + "packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67", + "engines": { + "pnpm": ">=10.0.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000000..7a40c88c2e --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,24352 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + injectWorkspacePackages: true + +overrides: + canvas: 2.11.2 + sharp: ^0.34.3 + +packageExtensionsChecksum: sha256-DAYr0FTkvKYnvBH4muAER9UE1FVGKhqfRU4/QwA2xPQ= + +pnpmfileChecksum: sha256-AG/qwrPNpmy9q60PZwCpecoYVptglTHgH+N6RKQHOM0= + +importers: + + .: {} + + .github: + devDependencies: + prettier: + specifier: ^3.5.3 + version: 3.6.2 + + cli: + dependencies: + chokidar: + specifier: ^4.0.3 + version: 4.0.3 + fast-glob: + specifier: ^3.3.2 + version: 3.3.3 + fastq: + specifier: ^1.17.1 + version: 1.19.1 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + micromatch: + specifier: ^4.0.8 + version: 4.0.8 + devDependencies: + '@eslint/js': + specifier: ^9.8.0 + version: 9.36.0 + '@immich/sdk': + specifier: file:../open-api/typescript-sdk + version: link:../open-api/typescript-sdk + '@types/byte-size': + specifier: ^8.1.0 + version: 8.1.2 + '@types/cli-progress': + specifier: ^3.11.0 + version: 3.11.6 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/micromatch': + specifier: ^4.0.9 + version: 4.0.9 + '@types/mock-fs': + specifier: ^4.13.1 + version: 4.13.4 + '@types/node': + specifier: ^22.18.8 + version: 22.18.8 + '@vitest/coverage-v8': + specifier: ^3.0.0 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + byte-size: + specifier: ^9.0.0 + version: 9.0.1 + cli-progress: + specifier: ^3.12.0 + version: 3.12.0 + commander: + specifier: ^12.0.0 + version: 12.1.0 + eslint: + specifier: ^9.14.0 + version: 9.36.0(jiti@2.5.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.36.0(jiti@2.5.1)) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.5.1)))(eslint@9.36.0(jiti@2.5.1))(prettier@3.6.2) + eslint-plugin-unicorn: + specifier: ^60.0.0 + version: 60.0.0(eslint@9.36.0(jiti@2.5.1)) + globals: + specifier: ^16.0.0 + version: 16.4.0 + mock-fs: + specifier: ^5.2.0 + version: 5.5.0 + prettier: + specifier: ^3.2.5 + version: 3.6.2 + prettier-plugin-organize-imports: + specifier: ^4.0.0 + version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + typescript: + specifier: ^5.3.3 + version: 5.9.2 + typescript-eslint: + specifier: ^8.28.0 + version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + vite: + specifier: ^7.0.0 + version: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-tsconfig-paths: + specifier: ^5.0.0 + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest-fetch-mock: + specifier: ^0.4.0 + version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + yaml: + specifier: ^2.3.1 + version: 2.8.1 + + docs: + dependencies: + '@docusaurus/core': + specifier: ~3.8.0 + version: 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/preset-classic': + specifier: ~3.8.0 + version: 3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2) + '@docusaurus/theme-common': + specifier: ~3.8.0 + version: 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdi/js': + specifier: ^7.3.67 + version: 7.4.47 + '@mdi/react': + specifier: ^1.6.1 + version: 1.6.1 + '@mdx-js/react': + specifier: ^3.0.0 + version: 3.1.1(@types/react@19.1.13)(react@18.3.1) + autoprefixer: + specifier: ^10.4.17 + version: 10.4.21(postcss@8.5.6) + docusaurus-lunr-search: + specifier: ^3.3.2 + version: 3.6.0(@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lunr: + specifier: ^2.3.9 + version: 2.3.9 + postcss: + specifier: ^8.4.25 + version: 8.5.6 + prism-react-renderer: + specifier: ^2.3.1 + version: 2.4.1(react@18.3.1) + raw-loader: + specifier: ^4.0.2 + version: 4.0.2(webpack@5.100.2) + react: + specifier: ^18.0.0 + version: 18.3.1 + react-dom: + specifier: ^18.0.0 + version: 18.3.1(react@18.3.1) + tailwindcss: + specifier: ^3.2.4 + version: 3.4.17 + url: + specifier: ^0.11.0 + version: 0.11.4 + devDependencies: + '@docusaurus/module-type-aliases': + specifier: ~3.8.0 + version: 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/tsconfig': + specifier: ^3.7.0 + version: 3.8.1 + '@docusaurus/types': + specifier: ^3.7.0 + version: 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + prettier: + specifier: ^3.2.4 + version: 3.6.2 + typescript: + specifier: ^5.1.6 + version: 5.9.2 + + e2e: + devDependencies: + '@eslint/js': + specifier: ^9.8.0 + version: 9.36.0 + '@immich/cli': + specifier: file:../cli + version: link:../cli + '@immich/sdk': + specifier: file:../open-api/typescript-sdk + version: link:../open-api/typescript-sdk + '@playwright/test': + specifier: ^1.44.1 + version: 1.55.0 + '@socket.io/component-emitter': + specifier: ^3.1.2 + version: 3.1.2 + '@types/luxon': + specifier: ^3.4.2 + version: 3.7.1 + '@types/node': + specifier: ^22.18.8 + version: 22.18.8 + '@types/oidc-provider': + specifier: ^9.0.0 + version: 9.5.0 + '@types/pg': + specifier: ^8.15.1 + version: 8.15.5 + '@types/pngjs': + specifier: ^6.0.4 + version: 6.0.5 + '@types/supertest': + specifier: ^6.0.2 + version: 6.0.3 + eslint: + specifier: ^9.14.0 + version: 9.36.0(jiti@2.5.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.36.0(jiti@2.5.1)) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.5.1)))(eslint@9.36.0(jiti@2.5.1))(prettier@3.6.2) + eslint-plugin-unicorn: + specifier: ^60.0.0 + version: 60.0.0(eslint@9.36.0(jiti@2.5.1)) + exiftool-vendored: + specifier: ^28.3.1 + version: 28.8.0 + globals: + specifier: ^16.0.0 + version: 16.4.0 + jose: + specifier: ^5.6.3 + version: 5.10.0 + luxon: + specifier: ^3.4.4 + version: 3.7.2 + oidc-provider: + specifier: ^9.0.0 + version: 9.5.1 + pg: + specifier: ^8.11.3 + version: 8.16.3 + pngjs: + specifier: ^7.0.0 + version: 7.0.0 + prettier: + specifier: ^3.2.5 + version: 3.6.2 + prettier-plugin-organize-imports: + specifier: ^4.0.0 + version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + sharp: + specifier: ^0.34.3 + version: 0.34.3 + socket.io-client: + specifier: ^4.7.4 + version: 4.8.1 + supertest: + specifier: ^7.0.0 + version: 7.1.4 + typescript: + specifier: ^5.3.3 + version: 5.9.2 + typescript-eslint: + specifier: ^8.28.0 + version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + utimes: + specifier: ^5.2.1 + version: 5.2.1(encoding@0.1.13) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + open-api/typescript-sdk: + dependencies: + '@oazapfts/runtime': + specifier: ^1.0.2 + version: 1.0.4 + devDependencies: + '@types/node': + specifier: ^22.18.8 + version: 22.18.8 + typescript: + specifier: ^5.3.3 + version: 5.9.2 + + server: + dependencies: + '@nestjs/bullmq': + specifier: ^11.0.1 + version: 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.58.5) + '@nestjs/common': + specifier: ^11.0.4 + version: 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': + specifier: ^11.0.4 + version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/platform-express': + specifier: ^11.0.4 + version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/platform-socket.io': + specifier: ^11.0.4 + version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.6)(rxjs@7.8.2) + '@nestjs/schedule': + specifier: ^6.0.0 + version: 6.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/swagger': + specifier: ^11.0.2 + version: 11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) + '@nestjs/websockets': + specifier: ^11.0.4 + version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-socket.io@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@opentelemetry/api': + specifier: ^1.9.0 + version: 1.9.0 + '@opentelemetry/context-async-hooks': + specifier: ^2.0.0 + version: 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-prometheus': + specifier: ^0.205.0 + version: 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': + specifier: ^0.205.0 + version: 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': + specifier: ^0.53.0 + version: 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': + specifier: ^0.51.0 + version: 0.51.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': + specifier: ^0.58.0 + version: 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': + specifier: ^2.0.1 + version: 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': + specifier: ^2.0.1 + version: 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': + specifier: ^0.205.0 + version: 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': + specifier: ^1.34.0 + version: 1.37.0 + '@react-email/components': + specifier: ^0.5.0 + version: 0.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@react-email/render': + specifier: ^1.1.2 + version: 1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@socket.io/redis-adapter': + specifier: ^8.3.0 + version: 8.3.0(socket.io-adapter@2.5.5) + archiver: + specifier: ^7.0.0 + version: 7.0.1 + async-lock: + specifier: ^1.4.0 + version: 1.4.1 + bcrypt: + specifier: ^6.0.0 + version: 6.0.0 + body-parser: + specifier: ^2.2.0 + version: 2.2.0 + bullmq: + specifier: ^5.51.0 + version: 5.58.5 + chokidar: + specifier: ^4.0.3 + version: 4.0.3 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.14.0 + version: 0.14.2 + compression: + specifier: ^1.8.0 + version: 1.8.1 + cookie: + specifier: ^1.0.2 + version: 1.0.2 + cookie-parser: + specifier: ^1.4.7 + version: 1.4.7 + cron: + specifier: 4.3.0 + version: 4.3.0 + exiftool-vendored: + specifier: ^28.8.0 + version: 28.8.0 + express: + specifier: ^5.1.0 + version: 5.1.0 + fast-glob: + specifier: ^3.3.2 + version: 3.3.3 + fluent-ffmpeg: + specifier: ^2.1.2 + version: 2.1.3 + geo-tz: + specifier: ^8.0.0 + version: 8.1.4 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 + i18n-iso-countries: + specifier: ^7.6.0 + version: 7.14.0 + ioredis: + specifier: ^5.3.2 + version: 5.7.0 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + kysely: + specifier: 0.28.2 + version: 0.28.2 + kysely-postgres-js: + specifier: ^2.0.0 + version: 2.0.0(kysely@0.28.2)(postgres@3.4.7) + lodash: + specifier: ^4.17.21 + version: 4.17.21 + luxon: + specifier: ^3.4.2 + version: 3.7.2 + mnemonist: + specifier: ^0.40.3 + version: 0.40.3 + multer: + specifier: ^2.0.2 + version: 2.0.2 + nest-commander: + specifier: ^3.16.0 + version: 3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2) + nestjs-cls: + specifier: ^5.0.0 + version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + nestjs-kysely: + specifier: ^3.0.0 + version: 3.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2) + nestjs-otel: + specifier: ^7.0.0 + version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + nodemailer: + specifier: ^7.0.0 + version: 7.0.7 + openid-client: + specifier: ^6.3.3 + version: 6.8.0 + pg: + specifier: ^8.11.3 + version: 8.16.3 + pg-connection-string: + specifier: ^2.9.1 + version: 2.9.1 + picomatch: + specifier: ^4.0.2 + version: 4.0.3 + postgres: + specifier: 3.4.7 + version: 3.4.7 + react: + specifier: ^19.0.0 + version: 19.1.1 + react-dom: + specifier: ^19.0.0 + version: 19.1.1(react@19.1.1) + react-email: + specifier: ^4.0.0 + version: 4.2.11 + reflect-metadata: + specifier: ^0.2.0 + version: 0.2.2 + rxjs: + specifier: ^7.8.1 + version: 7.8.2 + sanitize-filename: + specifier: ^1.6.3 + version: 1.6.3 + sanitize-html: + specifier: ^2.14.0 + version: 2.17.0 + semver: + specifier: ^7.6.2 + version: 7.7.2 + sharp: + specifier: ^0.34.3 + version: 0.34.3 + sirv: + specifier: ^3.0.0 + version: 3.0.2 + socket.io: + specifier: ^4.8.1 + version: 4.8.1 + tailwindcss-preset-email: + specifier: ^1.4.0 + version: 1.4.0(tailwindcss@3.4.17) + thumbhash: + specifier: ^0.1.1 + version: 0.1.1 + ua-parser-js: + specifier: ^2.0.0 + version: 2.0.5 + uuid: + specifier: ^11.1.0 + version: 11.1.0 + validator: + specifier: ^13.12.0 + version: 13.15.15 + devDependencies: + '@eslint/js': + specifier: ^9.8.0 + version: 9.36.0 + '@nestjs/cli': + specifier: ^11.0.2 + version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8) + '@nestjs/schematics': + specifier: ^11.0.0 + version: 11.0.7(chokidar@4.0.3)(typescript@5.9.2) + '@nestjs/testing': + specifier: ^11.0.4 + version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@11.1.6) + '@swc/core': + specifier: ^1.4.14 + version: 1.13.5(@swc/helpers@0.5.17) + '@types/archiver': + specifier: ^6.0.0 + version: 6.0.3 + '@types/async-lock': + specifier: ^1.4.2 + version: 1.4.2 + '@types/bcrypt': + specifier: ^6.0.0 + version: 6.0.0 + '@types/body-parser': + specifier: ^1.19.6 + version: 1.19.6 + '@types/compression': + specifier: ^1.7.5 + version: 1.8.1 + '@types/cookie-parser': + specifier: ^1.4.8 + version: 1.4.9(@types/express@5.0.3) + '@types/express': + specifier: ^5.0.0 + version: 5.0.3 + '@types/fluent-ffmpeg': + specifier: ^2.1.21 + version: 2.1.27 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + '@types/lodash': + specifier: ^4.14.197 + version: 4.17.20 + '@types/luxon': + specifier: ^3.6.2 + version: 3.7.1 + '@types/mock-fs': + specifier: ^4.13.1 + version: 4.13.4 + '@types/multer': + specifier: ^2.0.0 + version: 2.0.0 + '@types/node': + specifier: ^22.18.8 + version: 22.18.8 + '@types/nodemailer': + specifier: ^7.0.0 + version: 7.0.1 + '@types/picomatch': + specifier: ^4.0.0 + version: 4.0.2 + '@types/pngjs': + specifier: ^6.0.5 + version: 6.0.5 + '@types/react': + specifier: ^19.0.0 + version: 19.1.13 + '@types/sanitize-html': + specifier: ^2.13.0 + version: 2.16.0 + '@types/semver': + specifier: ^7.5.8 + version: 7.7.1 + '@types/supertest': + specifier: ^6.0.0 + version: 6.0.3 + '@types/ua-parser-js': + specifier: ^0.7.36 + version: 0.7.39 + '@types/validator': + specifier: ^13.15.2 + version: 13.15.3 + '@vitest/coverage-v8': + specifier: ^3.0.0 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + eslint: + specifier: ^9.14.0 + version: 9.36.0(jiti@2.5.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.36.0(jiti@2.5.1)) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.5.1)))(eslint@9.36.0(jiti@2.5.1))(prettier@3.6.2) + eslint-plugin-unicorn: + specifier: ^60.0.0 + version: 60.0.0(eslint@9.36.0(jiti@2.5.1)) + globals: + specifier: ^16.0.0 + version: 16.4.0 + mock-fs: + specifier: ^5.2.0 + version: 5.5.0 + node-gyp: + specifier: ^11.2.0 + version: 11.4.2 + pngjs: + specifier: ^7.0.0 + version: 7.0.0 + prettier: + specifier: ^3.0.2 + version: 3.6.2 + prettier-plugin-organize-imports: + specifier: ^4.0.0 + version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + sql-formatter: + specifier: ^15.0.0 + version: 15.6.9 + supertest: + specifier: ^7.1.0 + version: 7.1.4 + tailwindcss: + specifier: ^3.4.0 + version: 3.4.17 + testcontainers: + specifier: ^11.0.0 + version: 11.5.1 + typescript: + specifier: ^5.9.2 + version: 5.9.2 + typescript-eslint: + specifier: ^8.28.0 + version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + unplugin-swc: + specifier: ^1.4.5 + version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1) + vite-tsconfig-paths: + specifier: ^5.0.0 + version: 5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + web: + dependencies: + '@formatjs/icu-messageformat-parser': + specifier: ^2.9.8 + version: 2.11.2 + '@immich/sdk': + specifier: file:../open-api/typescript-sdk + version: link:../open-api/typescript-sdk + '@immich/ui': + specifier: ^0.29.0 + version: 0.29.0(@internationalized/date@3.8.2)(svelte@5.38.10) + '@mapbox/mapbox-gl-rtl-text': + specifier: 0.2.3 + version: 0.2.3(mapbox-gl@1.13.3) + '@mdi/js': + specifier: ^7.4.47 + version: 7.4.47 + '@photo-sphere-viewer/core': + specifier: ^5.11.5 + version: 5.14.0 + '@photo-sphere-viewer/equirectangular-video-adapter': + specifier: ^5.11.5 + version: 5.14.0(@photo-sphere-viewer/core@5.14.0)(@photo-sphere-viewer/video-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0)) + '@photo-sphere-viewer/resolution-plugin': + specifier: ^5.11.5 + version: 5.14.0(@photo-sphere-viewer/core@5.14.0)(@photo-sphere-viewer/settings-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0)) + '@photo-sphere-viewer/settings-plugin': + specifier: ^5.11.5 + version: 5.14.0(@photo-sphere-viewer/core@5.14.0) + '@photo-sphere-viewer/video-plugin': + specifier: ^5.11.5 + version: 5.14.0(@photo-sphere-viewer/core@5.14.0) + '@types/geojson': + specifier: ^7946.0.16 + version: 7946.0.16 + '@zoom-image/core': + specifier: ^0.41.0 + version: 0.41.0 + '@zoom-image/svelte': + specifier: ^0.3.0 + version: 0.3.4(svelte@5.38.10) + async-mutex: + specifier: ^0.5.0 + version: 0.5.0 + dom-to-image: + specifier: ^2.6.0 + version: 2.6.0 + fabric: + specifier: ^6.5.4 + version: 6.7.1 + geo-coordinates-parser: + specifier: ^1.7.4 + version: 1.7.4 + geojson: + specifier: ^0.5.0 + version: 0.5.0 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 + happy-dom: + specifier: ^18.0.1 + version: 18.0.1 + intl-messageformat: + specifier: ^10.7.11 + version: 10.7.16 + justified-layout: + specifier: ^4.1.0 + version: 4.1.0 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + luxon: + specifier: ^3.4.4 + version: 3.7.2 + maplibre-gl: + specifier: ^5.6.2 + version: 5.7.1 + pmtiles: + specifier: ^4.3.0 + version: 4.3.0 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 + simple-icons: + specifier: ^15.15.0 + version: 15.15.0 + socket.io-client: + specifier: ~4.8.0 + version: 4.8.1 + svelte-gestures: + specifier: ^5.2.2 + version: 5.2.2 + svelte-i18n: + specifier: ^4.0.1 + version: 4.0.1(svelte@5.38.10) + svelte-maplibre: + specifier: ^1.2.0 + version: 1.2.1(svelte@5.38.10) + svelte-persisted-store: + specifier: ^0.12.0 + version: 0.12.0(svelte@5.38.10) + tabbable: + specifier: ^6.2.0 + version: 6.2.0 + thumbhash: + specifier: ^0.1.1 + version: 0.1.1 + devDependencies: + '@eslint/js': + specifier: ^9.36.0 + version: 9.36.0 + '@faker-js/faker': + specifier: ^10.0.0 + version: 10.0.0 + '@koddsson/eslint-plugin-tscompat': + specifier: ^0.2.0 + version: 0.2.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@socket.io/component-emitter': + specifier: ^3.1.0 + version: 3.1.2 + '@sveltejs/adapter-static': + specifier: ^3.0.8 + version: 3.0.9(@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + '@sveltejs/enhanced-img': + specifier: ^0.8.0 + version: 0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': + specifier: ^2.27.1 + version: 2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': + specifier: 6.2.0 + version: 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@tailwindcss/vite': + specifier: ^4.1.7 + version: 4.1.13(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@testing-library/jest-dom': + specifier: ^6.4.2 + version: 6.8.0 + '@testing-library/svelte': + specifier: ^5.2.8 + version: 5.2.8(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.6.1(@testing-library/dom@10.4.0) + '@types/chromecast-caf-sender': + specifier: ^1.0.11 + version: 1.0.11 + '@types/dom-to-image': + specifier: ^2.6.7 + version: 2.6.7 + '@types/justified-layout': + specifier: ^4.1.4 + version: 4.1.4 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/luxon': + specifier: ^3.4.2 + version: 3.7.1 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.5 + '@vitest/coverage-v8': + specifier: ^3.0.0 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + dotenv: + specifier: ^17.0.0 + version: 17.2.2 + eslint: + specifier: ^9.36.0 + version: 9.36.0(jiti@2.5.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.36.0(jiti@2.5.1)) + eslint-plugin-compat: + specifier: ^6.0.2 + version: 6.0.2(eslint@9.36.0(jiti@2.5.1)) + eslint-plugin-svelte: + specifier: ^3.12.4 + version: 3.12.4(eslint@9.36.0(jiti@2.5.1))(svelte@5.38.10) + eslint-plugin-unicorn: + specifier: ^61.0.2 + version: 61.0.2(eslint@9.36.0(jiti@2.5.1)) + factory.ts: + specifier: ^1.4.1 + version: 1.4.2 + globals: + specifier: ^16.0.0 + version: 16.4.0 + prettier: + specifier: ^3.4.2 + version: 3.6.2 + prettier-plugin-organize-imports: + specifier: ^4.0.0 + version: 4.2.0(prettier@3.6.2)(typescript@5.9.2) + prettier-plugin-sort-json: + specifier: ^4.1.1 + version: 4.1.1(prettier@3.6.2) + prettier-plugin-svelte: + specifier: ^3.3.3 + version: 3.4.0(prettier@3.6.2)(svelte@5.38.10) + rollup-plugin-visualizer: + specifier: ^6.0.0 + version: 6.0.3(rollup@4.50.1) + svelte: + specifier: 5.38.10 + version: 5.38.10 + svelte-check: + specifier: ^4.1.5 + version: 4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2) + svelte-eslint-parser: + specifier: ^1.3.3 + version: 1.3.3(svelte@5.38.10) + tailwindcss: + specifier: ^4.1.7 + version: 4.1.13 + typescript: + specifier: ^5.8.3 + version: 5.9.2 + typescript-eslint: + specifier: ^8.45.0 + version: 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + vite: + specifier: ^7.1.2 + version: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@algolia/autocomplete-core@1.17.9': + resolution: {integrity: sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.9': + resolution: {integrity: sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.9': + resolution: {integrity: sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.9': + resolution: {integrity: sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.29.0': + resolution: {integrity: sha512-AM/6LYMSTnZvAT5IarLEKjYWOdV+Fb+LVs8JRq88jn8HH6bpVUtjWdOZXqX1hJRXuCAY8SdQfb7F8uEiMNXdYQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.29.0': + resolution: {integrity: sha512-La34HJh90l0waw3wl5zETO8TuukeUyjcXhmjYZL3CAPLggmKv74mobiGRIb+mmBENybiFDXf/BeKFLhuDYWMMQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.29.0': + resolution: {integrity: sha512-T0lzJH/JiCxQYtCcnWy7Jf1w/qjGDXTi2npyF9B9UsTvXB97GRC6icyfXxe21mhYvhQcaB1EQ/J2575FXxi2rA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.29.0': + resolution: {integrity: sha512-A39F1zmHY9aev0z4Rt3fTLcGN5AG1VsVUkVWy6yQG5BRDScktH+U5m3zXwThwniBTDV1HrPgiGHZeWb67GkR2Q==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.29.0': + resolution: {integrity: sha512-ibxmh2wKKrzu5du02gp8CLpRMeo+b/75e4ORct98CT7mIxuYFXowULwCd6cMMkz/R0LpKXIbTUl15UL5soaiUQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.29.0': + resolution: {integrity: sha512-VZq4/AukOoJC2WSwF6J5sBtt+kImOoBwQc1nH3tgI+cxJBg7B77UsNC+jT6eP2dQCwGKBBRTmtPLUTDDnHpMgA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.29.0': + resolution: {integrity: sha512-cZ0Iq3OzFUPpgszzDr1G1aJV5UMIZ4VygJ2Az252q4Rdf5cQMhYEIKArWY/oUjMhQmosM8ygOovNq7gvA9CdCg==} + engines: {node: '>= 14.0.0'} + + '@algolia/events@4.0.1': + resolution: {integrity: sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==} + + '@algolia/ingestion@1.29.0': + resolution: {integrity: sha512-scBXn0wO5tZCxmO6evfa7A3bGryfyOI3aoXqSQBj5SRvNYXaUlFWQ/iKI70gRe/82ICwE0ICXbHT/wIvxOW7vw==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.29.0': + resolution: {integrity: sha512-FGWWG9jLFhsKB7YiDjM2dwQOYnWu//7Oxrb2vT96N7+s+hg1mdHHfHNRyEudWdxd4jkMhBjeqNA21VbTiOIPVg==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.29.0': + resolution: {integrity: sha512-xte5+mpdfEARAu61KXa4ewpjchoZuJlAlvQb8ptK6hgHlBHDnYooy1bmOFpokaAICrq/H9HpoqNUX71n+3249A==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.29.0': + resolution: {integrity: sha512-og+7Em75aPHhahEUScq2HQ3J7ULN63Levtd87BYMpn6Im5d5cNhaC4QAUsXu6LWqxRPgh4G+i+wIb6tVhDhg2A==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.29.0': + resolution: {integrity: sha512-JCxapz7neAy8hT/nQpCvOrI5JO8VyQ1kPvBiaXWNC1prVq0UMYHEL52o1BsPvtXfdQ7BVq19OIq6TjOI06mV/w==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.29.0': + resolution: {integrity: sha512-lVBD81RBW5VTdEYgnzCz7Pf9j2H44aymCP+/eHGJu4vhU+1O8aKf3TVBgbQr5UM6xoe8IkR/B112XY6YIG2vtg==} + engines: {node: '>= 14.0.0'} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@angular-devkit/core@19.2.15': + resolution: {integrity: sha512-pU2RZYX6vhd7uLSdLwPnuBcr0mXJSjp3EgOXKsrlQFQZevc+Qs+2JdXgIElnOT/aDqtRtriDmLlSbtdE8n3ZbA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/schematics-cli@19.2.15': + resolution: {integrity: sha512-1ESFmFGMpGQmalDB3t2EtmWDGv6gOFYBMxmHO2f1KI/UDl8UmZnCGL4mD3EWo8Hv0YIsZ9wOH9Q7ZHNYjeSpzg==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + + '@angular-devkit/schematics@19.2.15': + resolution: {integrity: sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-sesv2@3.890.0': + resolution: {integrity: sha512-AM9Lt4QkNet8xKgCwJxfkyqlorwG9S+tvtpSfHYCVq0j2Z6PbkDaUBnvwjGOMBV7Um5IzZ7yhvQXrBg7omZciQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sso@3.890.0': + resolution: {integrity: sha512-vefYNwh/K5V5YiJpFJfoMPNqsoiRTqD7ZnkvR0cjJdwhOIwFnSKN1vz0OMjySTQmVMcG4JKGVul82ou7ErtOhQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/core@3.890.0': + resolution: {integrity: sha512-CT+yjhytHdyKvV3Nh/fqBjnZ8+UiQZVz4NMm4LrPATgVSOdfygXHqrWxrPTVgiBtuJWkotg06DF7+pTd5ekLBw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-env@3.890.0': + resolution: {integrity: sha512-BtsUa2y0Rs8phmB2ScZ5RuPqZVmxJJXjGfeiXctmLFTxTwoayIK1DdNzOWx6SRMPVc3s2RBGN4vO7T1TwN+ajA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-http@3.890.0': + resolution: {integrity: sha512-0sru3LVwsuGYyzbD90EC/d5HnCZ9PL4O9BA2LYT6b9XceC005Oj86uzE47LXb+mDhTAt3T6ZO0+ZcVQe0DDi8w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-ini@3.890.0': + resolution: {integrity: sha512-Mxv7ByftHKH7dE6YXu9gQ6ODXwO1iSO32t8tBrZLS3g8K1knWADIqDFv3yErQtJ8hp27IDxbAbVH/1RQdSkmhA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-node@3.890.0': + resolution: {integrity: sha512-zbPz3mUtaBdch0KoH8/LouRDcYSzyT2ecyCOo5OAFVil7AxT1jvsn4vX78FlnSVpZ4mLuHY8pHTVGi235XiyBA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-process@3.890.0': + resolution: {integrity: sha512-dWZ54TI1Q+UerF5YOqGiCzY+x2YfHsSQvkyM3T4QDNTJpb/zjiVv327VbSOULOlI7gHKWY/G3tMz0D9nWI7YbA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-sso@3.890.0': + resolution: {integrity: sha512-ajYCZ6f2+98w8zG/IXcQ+NhWYoI5qPUDovw+gMqMWX/jL1cmZ9PFAwj2Vyq9cbjum5RNWwPLArWytTCgJex4AQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.890.0': + resolution: {integrity: sha512-qZ2Mx7BeYR1s0F/H6wePI0MAmkFswmBgrpgMCOt2S4b2IpQPnUa2JbxY3GwW2WqX3nV0KjPW08ctSLMmlq/tKA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-host-header@3.887.0': + resolution: {integrity: sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-logger@3.887.0': + resolution: {integrity: sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.887.0': + resolution: {integrity: sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.890.0': + resolution: {integrity: sha512-58P1lrE606zpp29xH9Keh3j2BWfa2ciGBtygJTpulRMlqPL3U1gFfU2g5nDYJbjKgRtCgNIBqfmtkL4eikCb9w==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-user-agent@3.890.0': + resolution: {integrity: sha512-x4+gLrOFGN7PnfxCaQbs3QEF8bMQE4CVxcOp066UEJqr2Pn4yB12Q3O+YntOtESK5NcTxIh7JlhGss95EHzNng==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/nested-clients@3.890.0': + resolution: {integrity: sha512-D5qVNd+qlqdL8duJShzffAqPllGRA4tG7n/GEpL13eNfHChPvGkkUFBMrxSgCAETaTna13G6kq+dMO+SAdbm1A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/region-config-resolver@3.890.0': + resolution: {integrity: sha512-VfdT+tkF9groRYNzKvQCsCGDbOQdeBdzyB1d6hWiq22u13UafMIoskJ1ec0i0H1X29oT6mjTitfnvPq1UiKwzQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.890.0': + resolution: {integrity: sha512-il8kb2/wDLXhemN3p7v4MvbvqoMuo7Ug3ihuIUIhPtSVjcnn+BISJU0S+5YTl8TXf6qxML9VrfxL0pmuhO3BsA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.890.0': + resolution: {integrity: sha512-+pK/0iQEpPmnztbAw0NNmb+B5pPy8VLu+Ab4SJLgVp41RE9NO13VQtrzUbh61TTAVMrzqWlLQ2qmAl2Fk4VNgw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.887.0': + resolution: {integrity: sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-arn-parser@3.873.0': + resolution: {integrity: sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-endpoints@3.890.0': + resolution: {integrity: sha512-nJ8v1x9ZQKzMRK4dS4oefOMIHqb6cguctTcx1RB9iTaFOR5pP7bvq+D4mvNZ6vBxiHg1dQGBUUgl5XJmdR7atQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-locate-window@3.873.0': + resolution: {integrity: sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-user-agent-browser@3.887.0': + resolution: {integrity: sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA==} + + '@aws-sdk/util-user-agent-node@3.890.0': + resolution: {integrity: sha512-s85NkCxKoAlUvx7UP7OelxLqwTi27Tps9/Q+4N+9rEUjThxEnDsqJSStJ1XiYhddz1xc/vxMvPjYN0qX6EKPtA==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.887.0': + resolution: {integrity: sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ==} + engines: {node: '>=18.0.0'} + + '@aws/lambda-invoke-store@0.0.1': + resolution: {integrity: sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==} + engines: {node: '>=18.0.0'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.27.7': + resolution: {integrity: sha512-xgu/ySj2mTiUFmdE9yCMfBxLp4DHd5DwmbbD05YAuICfodYT3VvRxbrh81LGQ/8UpSdtMdfKMn3KouYDX59DGQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.27.7': + resolution: {integrity: sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.27.1': + resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': + resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': + resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.27.1': + resolution: {integrity: sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.27.5': + resolution: {integrity: sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.27.1': + resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.27.7': + resolution: {integrity: sha512-CuLkokN1PEZ0Fsjtq+001aog/C2drDK9nTfK/NRK0n6rBin6cBrvM+zfQjDE+UllhR6/J4a6w8Xq9i4yi3mQrw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.27.7': + resolution: {integrity: sha512-pg3ZLdIKWCP0CrJm0O4jYjVthyBeioVfvz9nwt6o5paUxsgJ/8GucSMAIaj6M7xA4WY+SrvtGu2LijzkdyecWQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.27.1': + resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.27.1': + resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.27.7': + resolution: {integrity: sha512-201B1kFTWhckclcXpWHc8uUpYziDX/Pl4rxl0ZX0DiCZ3jknwfSUALL3QCYeeXXB37yWxJbo+g+Vfq8pAaHi3w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.27.1': + resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.27.1': + resolution: {integrity: sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.27.5': + resolution: {integrity: sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.27.4': + resolution: {integrity: sha512-D68nR5zxU64EUzV8i7T3R5XP0Xhrou/amNnddsRQssx6GrTLdZl1rLxyjtVZBd+v/NVX4AbTPOB5aU8thAZV1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.27.1': + resolution: {integrity: sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.27.2': + resolution: {integrity: sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.27.1': + resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime-corejs3@7.27.6': + resolution: {integrity: sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@borewit/text-codec@0.1.1': + resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@csstools/cascade-layer-name-parser@2.0.5': + resolution: {integrity: sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@4.0.3': + resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/postcss-cascade-layers@5.0.2': + resolution: {integrity: sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function@4.0.10': + resolution: {integrity: sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-function@3.0.10': + resolution: {integrity: sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-variadic-function-arguments@1.0.0': + resolution: {integrity: sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-content-alt-text@2.0.6': + resolution: {integrity: sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-exponential-functions@2.0.9': + resolution: {integrity: sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-font-format-keywords@4.0.0': + resolution: {integrity: sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gamut-mapping@2.0.10': + resolution: {integrity: sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gradients-interpolation-method@5.0.10': + resolution: {integrity: sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-hwb-function@4.0.10': + resolution: {integrity: sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-ic-unit@4.0.2': + resolution: {integrity: sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-initial@2.0.1': + resolution: {integrity: sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-is-pseudo-class@5.0.3': + resolution: {integrity: sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-light-dark-function@2.0.9': + resolution: {integrity: sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-float-and-clear@3.0.0': + resolution: {integrity: sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overflow@2.0.0': + resolution: {integrity: sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0': + resolution: {integrity: sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-resize@3.0.0': + resolution: {integrity: sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-viewport-units@3.0.4': + resolution: {integrity: sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-minmax@2.0.9': + resolution: {integrity: sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.5': + resolution: {integrity: sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-nested-calc@4.0.0': + resolution: {integrity: sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-normalize-display-values@4.0.0': + resolution: {integrity: sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-oklab-function@4.0.10': + resolution: {integrity: sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-progressive-custom-properties@4.1.0': + resolution: {integrity: sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-random-function@2.0.1': + resolution: {integrity: sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-relative-color-syntax@3.0.10': + resolution: {integrity: sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-scope-pseudo-class@4.0.1': + resolution: {integrity: sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-sign-functions@1.1.4': + resolution: {integrity: sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-stepped-value-functions@4.0.9': + resolution: {integrity: sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-text-decoration-shorthand@4.0.2': + resolution: {integrity: sha512-8XvCRrFNseBSAGxeaVTaNijAu+FzUvjwFXtcrynmazGb/9WUdsPCpBX+mHEHShVRq47Gy4peYAoxYs8ltUnmzA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-trigonometric-functions@4.0.9': + resolution: {integrity: sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-unset-value@4.0.0': + resolution: {integrity: sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/selector-resolve-nested@3.1.0': + resolution: {integrity: sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/utilities@2.0.0': + resolution: {integrity: sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@discoveryjs/json-ext@0.5.7': + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + + '@docsearch/css@3.9.0': + resolution: {integrity: sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==} + + '@docsearch/react@3.9.0': + resolution: {integrity: sha512-mb5FOZYZIkRQ6s/NWnM98k879vu5pscWqTLubLFBO87igYYT4VzVazh4h5o/zCvTIZgEt3PvsCOMOswOUo9yHQ==} + peerDependencies: + '@types/react': '>= 16.8.0 < 20.0.0' + react: '>= 16.8.0 < 20.0.0' + react-dom: '>= 16.8.0 < 20.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@docusaurus/babel@3.8.1': + resolution: {integrity: sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==} + engines: {node: '>=18.0'} + + '@docusaurus/bundler@3.8.1': + resolution: {integrity: sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==} + engines: {node: '>=18.0'} + peerDependencies: + '@docusaurus/faster': '*' + peerDependenciesMeta: + '@docusaurus/faster': + optional: true + + '@docusaurus/core@3.8.1': + resolution: {integrity: sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==} + engines: {node: '>=18.0'} + hasBin: true + peerDependencies: + '@mdx-js/react': ^3.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/cssnano-preset@3.8.1': + resolution: {integrity: sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==} + engines: {node: '>=18.0'} + + '@docusaurus/logger@3.8.1': + resolution: {integrity: sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==} + engines: {node: '>=18.0'} + + '@docusaurus/mdx-loader@3.8.1': + resolution: {integrity: sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/module-type-aliases@3.8.1': + resolution: {integrity: sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==} + peerDependencies: + react: '*' + react-dom: '*' + + '@docusaurus/plugin-content-blog@3.8.1': + resolution: {integrity: sha512-vNTpMmlvNP9n3hGEcgPaXyvTljanAKIUkuG9URQ1DeuDup0OR7Ltvoc8yrmH+iMZJbcQGhUJF+WjHLwuk8HSdw==} + engines: {node: '>=18.0'} + peerDependencies: + '@docusaurus/plugin-content-docs': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-content-docs@3.8.1': + resolution: {integrity: sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-content-pages@3.8.1': + resolution: {integrity: sha512-a+V6MS2cIu37E/m7nDJn3dcxpvXb6TvgdNI22vJX8iUTp8eoMoPa0VArEbWvCxMY/xdC26WzNv4wZ6y0iIni/w==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-css-cascade-layers@3.8.1': + resolution: {integrity: sha512-VQ47xRxfNKjHS5ItzaVXpxeTm7/wJLFMOPo1BkmoMG4Cuz4nuI+Hs62+RMk1OqVog68Swz66xVPK8g9XTrBKRw==} + engines: {node: '>=18.0'} + + '@docusaurus/plugin-debug@3.8.1': + resolution: {integrity: sha512-nT3lN7TV5bi5hKMB7FK8gCffFTBSsBsAfV84/v293qAmnHOyg1nr9okEw8AiwcO3bl9vije5nsUvP0aRl2lpaw==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-google-analytics@3.8.1': + resolution: {integrity: sha512-Hrb/PurOJsmwHAsfMDH6oVpahkEGsx7F8CWMjyP/dw1qjqmdS9rcV1nYCGlM8nOtD3Wk/eaThzUB5TSZsGz+7Q==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-google-gtag@3.8.1': + resolution: {integrity: sha512-tKE8j1cEZCh8KZa4aa80zpSTxsC2/ZYqjx6AAfd8uA8VHZVw79+7OTEP2PoWi0uL5/1Is0LF5Vwxd+1fz5HlKg==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-google-tag-manager@3.8.1': + resolution: {integrity: sha512-iqe3XKITBquZq+6UAXdb1vI0fPY5iIOitVjPQ581R1ZKpHr0qe+V6gVOrrcOHixPDD/BUKdYwkxFjpNiEN+vBw==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-sitemap@3.8.1': + resolution: {integrity: sha512-+9YV/7VLbGTq8qNkjiugIelmfUEVkTyLe6X8bWq7K5qPvGXAjno27QAfFq63mYfFFbJc7z+pudL63acprbqGzw==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/plugin-svgr@3.8.1': + resolution: {integrity: sha512-rW0LWMDsdlsgowVwqiMb/7tANDodpy1wWPwCcamvhY7OECReN3feoFwLjd/U4tKjNY3encj0AJSTxJA+Fpe+Gw==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/preset-classic@3.8.1': + resolution: {integrity: sha512-yJSjYNHXD8POMGc2mKQuj3ApPrN+eG0rO1UPgSx7jySpYU+n4WjBikbrA2ue5ad9A7aouEtMWUoiSRXTH/g7KQ==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/react-loadable@6.0.0': + resolution: {integrity: sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==} + peerDependencies: + react: '*' + + '@docusaurus/theme-classic@3.8.1': + resolution: {integrity: sha512-bqDUCNqXeYypMCsE1VcTXSI1QuO4KXfx8Cvl6rYfY0bhhqN6d2WZlRkyLg/p6pm+DzvanqHOyYlqdPyP0iz+iw==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/theme-common@3.8.1': + resolution: {integrity: sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==} + engines: {node: '>=18.0'} + peerDependencies: + '@docusaurus/plugin-content-docs': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/theme-search-algolia@3.8.1': + resolution: {integrity: sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==} + engines: {node: '>=18.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/theme-translations@3.8.1': + resolution: {integrity: sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==} + engines: {node: '>=18.0'} + + '@docusaurus/tsconfig@3.8.1': + resolution: {integrity: sha512-XBWCcqhRHhkhfolnSolNL+N7gj3HVE3CoZVqnVjfsMzCoOsuQw2iCLxVVHtO+rePUUfouVZHURDgmqIySsF66A==} + + '@docusaurus/types@3.8.1': + resolution: {integrity: sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + '@docusaurus/utils-common@3.8.1': + resolution: {integrity: sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==} + engines: {node: '>=18.0'} + + '@docusaurus/utils-validation@3.8.1': + resolution: {integrity: sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==} + engines: {node: '>=18.0'} + + '@docusaurus/utils@3.8.1': + resolution: {integrity: sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==} + engines: {node: '>=18.0'} + + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.36.0': + resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@faker-js/faker@10.0.0': + resolution: {integrity: sha512-UollFEUkVXutsaP+Vndjxar40Gs5JL2HeLcl8xO1QAjJgOdhc3OmBFWyEylS+RddWaaBiAzH+5/17PLQJwDiLw==} + engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} + + '@fig/complete-commander@3.2.0': + resolution: {integrity: sha512-1Holl3XtRiANVKURZwgpjCnPuV4RsHp+XC0MhgvyAX/avQwj7F2HUItYOvGi/bXjJCkEzgBZmVfCr0HBA+q+Bw==} + peerDependencies: + commander: ^11.1.0 + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@formatjs/ecma402-abstract@2.3.4': + resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.2': + resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==} + + '@formatjs/icu-skeleton-parser@1.8.14': + resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==} + + '@formatjs/intl-localematcher@0.6.1': + resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==} + + '@golevelup/nestjs-discovery@4.0.3': + resolution: {integrity: sha512-8w3CsXHN7+7Sn2i419Eal1Iw/kOjAd6Kb55M/ZqKBBwACCMn4WiEuzssC71LpBMI1090CiDxuelfPRwwIrQK+A==} + peerDependencies: + '@nestjs/common': ^10.x || ^11.0.0 + '@nestjs/core': ^10.x || ^11.0.0 + + '@grpc/grpc-js@1.13.4': + resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@immich/ui@0.29.0': + resolution: {integrity: sha512-An9cf1L4nMO6+C1Tkktd+qjGmZvyGz/Un33cGsKQa2I7IdZHd67KbbC2v3wN3bQMiTjxtFJ8YR9EONohJ8jDtQ==} + peerDependencies: + svelte: ^5.0.0 + + '@inquirer/checkbox@4.2.1': + resolution: {integrity: sha512-bevKGO6kX1eM/N+pdh9leS5L7TBF4ICrzi9a+cbWkrxeAeIcwlo/7OfWGCDERdRCI2/Q6tjltX4bt07ALHDwFw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.15': + resolution: {integrity: sha512-SwHMGa8Z47LawQN0rog0sT+6JpiL0B7eW9p1Bb7iCeKDGTI5Ez25TSc2l8kw52VV7hA4sX/C78CGkMrKXfuspA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.15': + resolution: {integrity: sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.17': + resolution: {integrity: sha512-r6bQLsyPSzbWrZZ9ufoWL+CztkSatnJ6uSxqd6N+o41EZC51sQeWOzI6s5jLb+xxTWxl7PlUppqm8/sow241gg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.17': + resolution: {integrity: sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.2': + resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.13': + resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} + engines: {node: '>=18'} + + '@inquirer/input@4.2.1': + resolution: {integrity: sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.17': + resolution: {integrity: sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.17': + resolution: {integrity: sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.3.2': + resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.8.0': + resolution: {integrity: sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.5': + resolution: {integrity: sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.1.0': + resolution: {integrity: sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.3.1': + resolution: {integrity: sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.8': + resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@internationalized/date@3.8.2': + resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==} + + '@ioredis/commands@1.3.0': + resolution: {integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==} + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@koa/cors@5.0.0': + resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} + engines: {node: '>= 14.0.0'} + + '@koa/router@14.0.0': + resolution: {integrity: sha512-LBSu5K0qAaaQcXX/0WIB9PGDevyCxxpnc1uq13vV/CgObaVxuis5hKl3Eboq/8gcb6ebnkAStW9NB/Em2eYyFA==} + engines: {node: '>= 20'} + + '@koddsson/eslint-plugin-tscompat@0.2.0': + resolution: {integrity: sha512-Oqd4kWSX0LiO9wWHjcmDfXZNC7TotFV/tLRhwCFU3XUeb//KYvJ75c9OmeSJ+vBv5lkCeB+xYsqyNrBc5j18XA==} + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + '@mapbox/geojson-rewind@0.5.2': + resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} + hasBin: true + + '@mapbox/geojson-types@1.0.2': + resolution: {integrity: sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==} + + '@mapbox/jsonlint-lines-primitives@2.0.2': + resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==} + engines: {node: '>= 0.6'} + + '@mapbox/mapbox-gl-rtl-text@0.2.3': + resolution: {integrity: sha512-RaCYfnxULUUUxNwcUimV9C/o2295ktTyLEUzD/+VWkqXqvaVfFcZ5slytGzb2Sd/Jj4MlbxD0DCZbfa6CzcmMw==} + peerDependencies: + mapbox-gl: '>=0.32.1 <2.0.0' + + '@mapbox/mapbox-gl-supported@1.5.0': + resolution: {integrity: sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==} + peerDependencies: + mapbox-gl: '>=0.32.1 <2.0.0' + + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + + '@mapbox/point-geometry@0.1.0': + resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} + + '@mapbox/point-geometry@1.1.0': + resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==} + + '@mapbox/tiny-sdf@1.2.5': + resolution: {integrity: sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==} + + '@mapbox/tiny-sdf@2.0.7': + resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==} + + '@mapbox/unitbezier@0.0.0': + resolution: {integrity: sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==} + + '@mapbox/unitbezier@0.0.1': + resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} + + '@mapbox/vector-tile@1.3.1': + resolution: {integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==} + + '@mapbox/vector-tile@2.0.4': + resolution: {integrity: sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==} + + '@mapbox/whoots-js@3.1.0': + resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} + engines: {node: '>=6.0.0'} + + '@maplibre/maplibre-gl-style-spec@23.3.0': + resolution: {integrity: sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==} + hasBin: true + + '@maplibre/vt-pbf@4.0.3': + resolution: {integrity: sha512-YsW99BwnT+ukJRkseBcLuZHfITB4puJoxnqPVjo72rhW/TaawVYsgQHcqWLzTxqknttYoDpgyERzWSa/XrETdA==} + + '@mdi/js@7.4.47': + resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==} + + '@mdi/react@1.6.1': + resolution: {integrity: sha512-4qZeDcluDFGFTWkHs86VOlHkm6gnKaMql13/gpIcUQ8kzxHgpj31NuCkD8abECVfbULJ3shc7Yt4HJ6Wu6SN4w==} + + '@mdn/browser-compat-data@5.7.6': + resolution: {integrity: sha512-7xdrMX0Wk7grrTZQwAoy1GkvPMFoizStUoL+VmtUkAxegbCCec+3FKwOM6yc/uGU5+BEczQHXAlWiqvM8JeENg==} + + '@mdn/browser-compat-data@6.0.27': + resolution: {integrity: sha512-s5kTuDih5Ysb7DS2T2MhvneFLvDS0NwnLY5Jv6dL+zBXbcNVcyFcGdjwn3rttiHvcbb/qF02HP9ywufdwHZbIw==} + + '@mdx-js/mdx@3.1.0': + resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} + + '@mdx-js/react@3.1.1': + resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} + peerDependencies: + '@types/react': '>=16' + react: '>=16' + + '@microsoft/tsdoc@0.15.1': + resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@namnode/store@0.1.0': + resolution: {integrity: sha512-4NGTldxKcmY0UuZ7OEkvCjs8ZEoeYB6M2UwMu74pdLiFMKxXbj9HdNk1Qn213bxX1O7bY5h+PLh5DZsTURZkYA==} + + '@nestjs/bull-shared@11.0.3': + resolution: {integrity: sha512-CaHniPkLAxis6fAB1DB8WZELQv8VPCLedbj7iP0VQ1pz74i6NSzG9mBg6tOomXq/WW4la4P4OMGEQ48UAJh20A==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + + '@nestjs/bullmq@11.0.3': + resolution: {integrity: sha512-0Qr7Fk3Ir3V2OBIKJk+ArEM0AesGjKaNZA8QQ4fH3qGogudYADSjaNe910/OAfmX8q+cjCRorvwTLdcShwWEMw==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + bullmq: ^3.0.0 || ^4.0.0 || ^5.0.0 + + '@nestjs/cli@11.0.10': + resolution: {integrity: sha512-4waDT0yGWANg0pKz4E47+nUrqIJv/UqrZ5wLPkCqc7oMGRMWKAaw1NDZ9rKsaqhqvxb2LfI5+uXOWr4yi94DOQ==} + engines: {node: '>= 20.11'} + hasBin: true + peerDependencies: + '@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: + '@swc/cli': + optional: true + '@swc/core': + optional: true + + '@nestjs/common@11.1.6': + resolution: {integrity: sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==} + peerDependencies: + class-transformer: '>=0.4.1' + class-validator: '>=0.13.2' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@11.1.6': + resolution: {integrity: sha512-siWX7UDgErisW18VTeJA+x+/tpNZrJewjTBsRPF3JVxuWRuAB1kRoiJcxHgln8Lb5UY9NdvklITR84DUEXD0Cg==} + engines: {node: '>= 20'} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + '@nestjs/websockets': ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + + '@nestjs/mapped-types@2.1.0': + resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + class-transformer: ^0.4.0 || ^0.5.0 + class-validator: ^0.13.0 || ^0.14.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/platform-express@11.1.6': + resolution: {integrity: sha512-HErwPmKnk+loTq8qzu1up+k7FC6Kqa8x6lJ4cDw77KnTxLzsCaPt+jBvOq6UfICmfqcqCCf3dKXg+aObQp+kIQ==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + + '@nestjs/platform-socket.io@11.1.6': + resolution: {integrity: sha512-ozm+OKiRiFLNQdFLA3ULDuazgdVaPrdRdgtG/+404T7tcROXpbUuFL0eEmWJpG64CxMkBNwamclUSH6J0AeU7A==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/websockets': ^11.0.0 + rxjs: ^7.1.0 + + '@nestjs/schedule@6.0.0': + resolution: {integrity: sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + + '@nestjs/schematics@11.0.7': + resolution: {integrity: sha512-t8dNYYMwEeEsrlwc2jbkfwCfXczq4AeNEgx1KVQuJ6wYibXk0ZbXbPdfp8scnEAaQv1grpncNV5gWgzi7ZwbvQ==} + peerDependencies: + typescript: '>=4.8.2' + + '@nestjs/swagger@11.2.0': + resolution: {integrity: sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg==} + peerDependencies: + '@fastify/static': ^8.0.0 + '@nestjs/common': ^11.0.1 + '@nestjs/core': ^11.0.1 + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/testing@11.1.6': + resolution: {integrity: sha512-srYzzDNxGvVCe1j0SpTS9/ix75PKt6Sn6iMaH1rpJ6nj2g8vwNrhK0CoJJXvpCYgrnI+2WES2pprYnq8rAMYHA==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + + '@nestjs/websockets@11.1.6': + resolution: {integrity: sha512-jlBX5QpqhfEVfxkwxTesIjgl0bdhgFMoORQYzjRg1i+Z+Qouf4KmjNPv5DZE3DZRDg91E+3Bpn0VgW0Yfl94ng==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + '@nestjs/platform-socket.io': ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/platform-socket.io': + optional: true + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@nuxt/opencollective@0.4.1': + resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} + engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} + hasBin: true + + '@oazapfts/runtime@1.0.4': + resolution: {integrity: sha512-7t6C2shug/6tZhQgkCa532oTYBLEnbASV/i1SG1rH2GB4h3aQQujYciYSPT92hvN4IwTe8S2hPkN/6iiOyTlCg==} + + '@opentelemetry/api-logs@0.205.0': + resolution: {integrity: sha512-wBlPk1nFB37Hsm+3Qy73yQSobVn28F4isnWIBvKpd5IUH/eat8bwcL02H9yzmHyyPmukeccSl2mbN5sDQZYnPg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@2.1.0': + resolution: {integrity: sha512-zOyetmZppnwTyPrt4S7jMfXiSX9yyfF0hxlA8B5oo2TtKl+/RGCy7fi4DrBfIf3lCPrkKsRBWZZD7RFojK7FDg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.1.0': + resolution: {integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-logs-otlp-grpc@0.205.0': + resolution: {integrity: sha512-jQlw7OHbqZ8zPt+pOrW2KGN7T55P50e3NXBMr4ckPOF+DWDwSy4W7mkG09GpYWlQAQ5C9BXg5gfUlv5ldTgWsw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-http@0.205.0': + resolution: {integrity: sha512-5JteMyVWiro4ghF0tHQjfE6OJcF7UBUcoEqX3UIQ5jutKP1H+fxFdyhqjjpmeHMFxzOHaYuLlNR1Bn7FOjGyJg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-proto@0.205.0': + resolution: {integrity: sha512-q3VS9wS+lpZ01txKxiDGBtBpTNge3YhbVEFDgem9ZQR9eI3EZ68+9tVZH9zJcSxI37nZPJ6lEEZO58yEjYZsVA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-grpc@0.205.0': + resolution: {integrity: sha512-1Vxlo4lUwqSKYX+phFkXHKYR3DolFHxCku6lVMP1H8sVE3oj4wwmwxMzDsJ7zF+sXd8M0FCr+ckK4SnNNKkV+w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-http@0.205.0': + resolution: {integrity: sha512-fFxNQ/HbbpLmh1pgU6HUVbFD1kNIjrkoluoKJkh88+gnmpFD92kMQ8WFNjPnSbjg2mNVnEkeKXgCYEowNW+p1w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-proto@0.205.0': + resolution: {integrity: sha512-qIbNnedw9QfFjwpx4NQvdgjK3j3R2kWH/2T+7WXAm1IfMFe9fwatYxE61i7li4CIJKf8HgUC3GS8Du0C3D+AuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.205.0': + resolution: {integrity: sha512-xsot/Qm9VLDTag4GEwAunD1XR1U8eBHTLAgO7IZNo2JuD/c/vL7xmDP7mQIUr6Lk3gtj/yGGIR2h3vhTeVzv4w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.205.0': + resolution: {integrity: sha512-ZBksUk84CcQOuDJB65yu5A4PORkC4qEsskNwCrPZxDLeWjPOFZNSWt0E0jQxKCY8PskLhjNXJYo12YaqsYvGFA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-http@0.205.0': + resolution: {integrity: sha512-vr2bwwPCSc9u7rbKc74jR+DXFvyMFQo9o5zs+H/fgbK672Whw/1izUKVf+xfWOdJOvuwTnfWxy+VAY+4TSo74Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.205.0': + resolution: {integrity: sha512-bGtFzqiENO2GpJk988mOBMe0MfeNpTQjbLm/LBijas6VRyEDQarUzdBHpFlu89A25k1+BCntdWGsWTa9Ai4FyA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-zipkin@2.1.0': + resolution: {integrity: sha512-0mEI0VDZrrX9t5RE1FhAyGz+jAGt96HSuXu73leswtY3L5YZD11gtcpARY2KAx/s6Z2+rj5Mhj566JsI2C7mfA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/host-metrics@0.36.0': + resolution: {integrity: sha512-14lNY57qa21V3ZOl6xrqLMHR0HGlnPIApR6hr3oCw/Dqs5IzxhTwt2X8Stn82vWJJis7j/ezn11oODsizHj2dQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.205.0': + resolution: {integrity: sha512-6fOgRlV7ypBuEzCQP7vXkLQxz3UL1FhE24rAlMRbwGvPAnZLvutcG/fq9FI/n+VU23dOpYexocYsXCf5oy/AXw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.53.0': + resolution: {integrity: sha512-Ah2wU347vOJYbE563Tgm3UX2J3DAXoI8gsr8qH0OOO4uDuEv3kVS/eDCfXApt11bvvDDPlOoc60/TGn6m9IoPw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.51.0': + resolution: {integrity: sha512-Se/m4887W94OO12pjKMjI3398L7HCoWeCjcbwoPvNOWpSpMkljBOHA9vE/fyo63CaVG1XAM5xA4ad60wmJKl9A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.58.0': + resolution: {integrity: sha512-WHntZAorf6CZ0n5a3oHlwGkSeu5Xa4AiCmXkNTKg24TbYSFWzJUtWvPQSkxePvQ3ku71lhAY/M20WgwHlvpZpQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.205.0': + resolution: {integrity: sha512-cgvm7tvQdu9Qo7VurJP84wJ7ZV9F6WqDDGZpUc6rUEXwjV7/bXWs0kaYp9v+1Vh1+3TZCD3i6j/lUBcPhu8NhA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.205.0': + resolution: {integrity: sha512-2MN0C1IiKyo34M6NZzD6P9Nv9Dfuz3OJ3rkZwzFmF6xzjDfqqCTatc9v1EpNfaP55iDOCLHFyYNCgs61FFgtUQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.205.0': + resolution: {integrity: sha512-AeuLfrciGYffqsp4EUTdYYc6Ee2BQS+hr08mHZk1C524SFWx0WnfcTnV0NFXbVURUNU6DZu1DhS89zRRrcx/hg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.205.0': + resolution: {integrity: sha512-KmObgqPtk9k/XTlWPJHdMbGCylRAmMJNXIRh6VYJmvlRDMfe+DonH41G7eenG8t4FXn3fxOGh14o/WiMRR6vPg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagator-b3@2.1.0': + resolution: {integrity: sha512-yOdHmFseIChYanddMMz0mJIFQHyjwbNhoxc65fEAA8yanxcBPwoFDoh1+WBUWAO/Z0NRgk+k87d+aFIzAZhcBw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@2.1.0': + resolution: {integrity: sha512-QYo7vLyMjrBCUTpwQBF/e+rvP7oGskrSELGxhSvLj5gpM0az9oJnu/0O4l2Nm7LEhAff80ntRYKkAcSwVgvSVQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/redis-common@0.38.0': + resolution: {integrity: sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==} + engines: {node: ^18.19.0 || >=20.6.0} + + '@opentelemetry/resources@2.1.0': + resolution: {integrity: sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.205.0': + resolution: {integrity: sha512-nyqhNQ6eEzPWQU60Nc7+A5LIq8fz3UeIzdEVBQYefB4+msJZ2vuVtRuk9KxPMw1uHoHDtYEwkr2Ct0iG29jU8w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.1.0': + resolution: {integrity: sha512-J9QX459mzqHLL9Y6FZ4wQPRZG4TOpMCyPOh6mkr/humxE1W2S3Bvf4i75yiMW9uyed2Kf5rxmLhTm/UK8vNkAw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-node@0.205.0': + resolution: {integrity: sha512-Y4Wcs8scj/Wy1u61pX1ggqPXPtCsGaqx/UnFu7BtRQE1zCQR+b0h56K7I0jz7U2bRlPUZIFdnNLtoaJSMNzz2g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.1.0': + resolution: {integrity: sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@2.1.0': + resolution: {integrity: sha512-SvVlBFc/jI96u/mmlKm86n9BbTCbQ35nsPoOohqJX6DXH92K0kTe73zGY5r8xoI1QkjR9PizszVJLzMC966y9Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.37.0': + resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.41.0': + resolution: {integrity: sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + + '@photo-sphere-viewer/core@5.14.0': + resolution: {integrity: sha512-V0JeDSB1D2Q60Zqn7+0FPjq8gqbKEwuxMzNdTLydefkQugVztLvdZykO+4k5XTpweZ2QAWPH/QOI1xZbsdvR9A==} + + '@photo-sphere-viewer/equirectangular-video-adapter@5.14.0': + resolution: {integrity: sha512-Ez88sZ4sj3fONpZSortnN3gLXlvV/hn5U/88LsWtxI73YwhkZ06ZtXFYLXU4MBaJvqCbMGaR6j39uVXTWFo5rw==} + peerDependencies: + '@photo-sphere-viewer/core': 5.14.0 + '@photo-sphere-viewer/video-plugin': 5.14.0 + + '@photo-sphere-viewer/resolution-plugin@5.14.0': + resolution: {integrity: sha512-PvDMX1h+8FzWdySxiorQ2bSmyBGTPsZjNNFRBqIfmb5C+01aWCIE7kuXodXGHwpXQNcOojsVX9IiX0Vz4CiW4A==} + peerDependencies: + '@photo-sphere-viewer/core': 5.14.0 + '@photo-sphere-viewer/settings-plugin': 5.14.0 + + '@photo-sphere-viewer/settings-plugin@5.14.0': + resolution: {integrity: sha512-sMLX4hFSE2PjiP2iUmH9qUAz6GV+UN2WX1zu/D58BBWzF3+8mV+FC9l50qxruO8qvWqqLwYysHUElHnmPPtpTg==} + peerDependencies: + '@photo-sphere-viewer/core': 5.14.0 + + '@photo-sphere-viewer/video-plugin@5.14.0': + resolution: {integrity: sha512-jWMZBNlfwYq8Lgc8ncs3ptwHR6Yk7Wl8o1BCFYhmhoRkGZFHEjoOQj7gMPXCET+3iYXQ1TsjTh4ZCW8UUOi+pg==} + peerDependencies: + '@photo-sphere-viewer/core': 5.14.0 + + '@photostructure/tz-lookup@11.2.0': + resolution: {integrity: sha512-DwrvodcXHNSdGdeSF7SBL5o8aBlsaeuCuG7633F04nYsL3hn5Hxe3z/5kCqxv61J1q7ggKZ27GPylR3x0cPNXQ==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@playwright/test@1.55.0': + resolution: {integrity: sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==} + engines: {node: '>=18'} + hasBin: true + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} + engines: {node: '>=12'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@react-email/body@0.1.0': + resolution: {integrity: sha512-o1bcSAmDYNNHECbkeyceCVPGmVsYvT+O3sSO/Ct7apKUu3JphTi31hu+0Nwqr/pgV5QFqdoT5vdS3SW5DJFHgQ==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/button@0.2.0': + resolution: {integrity: sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/code-block@0.1.0': + resolution: {integrity: sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/code-inline@0.0.5': + resolution: {integrity: sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/column@0.0.13': + resolution: {integrity: sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/components@0.5.3': + resolution: {integrity: sha512-8G5vsoMehuGOT4cDqaYLdpagtqCYPl4vThXNylClxO6SrN2w9Mh1+i2RNGj/rdqh/woamHORjlXMYCA/kzDMew==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/container@0.0.15': + resolution: {integrity: sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/font@0.0.9': + resolution: {integrity: sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/head@0.0.12': + resolution: {integrity: sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/heading@0.0.15': + resolution: {integrity: sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/hr@0.0.11': + resolution: {integrity: sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/html@0.0.11': + resolution: {integrity: sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/img@0.0.11': + resolution: {integrity: sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/link@0.0.12': + resolution: {integrity: sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/markdown@0.0.15': + resolution: {integrity: sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/preview@0.0.13': + resolution: {integrity: sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/render@1.2.3': + resolution: {integrity: sha512-qu3XYNkHGao3teJexVD5CrcgFkNLrzbZvpZN17a7EyQYUN3kHkTkE9saqY4VbvGx6QoNU3p8rsk/Xm++D/+pTw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/row@0.0.12': + resolution: {integrity: sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/section@0.0.16': + resolution: {integrity: sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/tailwind@1.2.2': + resolution: {integrity: sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/text@0.1.5': + resolution: {integrity: sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + cpu: [x64] + os: [win32] + + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + + '@selderee/plugin-htmlparser2@0.11.0': + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@slorber/react-helmet-async@1.3.0': + resolution: {integrity: sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@slorber/remark-comment@1.0.0': + resolution: {integrity: sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==} + + '@smithy/abort-controller@4.1.1': + resolution: {integrity: sha512-vkzula+IwRvPR6oKQhMYioM3A/oX/lFCZiwuxkQbRhqJS2S4YRY2k7k/SyR2jMf3607HLtbEwlRxi0ndXHMjRg==} + engines: {node: '>=18.0.0'} + + '@smithy/config-resolver@4.2.2': + resolution: {integrity: sha512-IT6MatgBWagLybZl1xQcURXRICvqz1z3APSCAI9IqdvfCkrA7RaQIEfgC6G/KvfxnDfQUDqFV+ZlixcuFznGBQ==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.11.0': + resolution: {integrity: sha512-Abs5rdP1o8/OINtE49wwNeWuynCu0kme1r4RI3VXVrHr4odVDG7h7mTnw1WXXfN5Il+c25QOnrdL2y56USfxkA==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.1.2': + resolution: {integrity: sha512-JlYNq8TShnqCLg0h+afqe2wLAwZpuoSgOyzhYvTgbiKBWRov+uUve+vrZEQO6lkdLOWPh7gK5dtb9dS+KGendg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.2.1': + resolution: {integrity: sha512-5/3wxKNtV3wO/hk1is+CZUhL8a1yy/U+9u9LKQ9kZTkMsHaQjJhc3stFfiujtMnkITjzWfndGA2f7g9Uh9vKng==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.1.1': + resolution: {integrity: sha512-H9DIU9WBLhYrvPs9v4sYvnZ1PiAI0oc8CgNQUJ1rpN3pP7QADbTOUjchI2FB764Ub0DstH5xbTqcMJu1pnVqxA==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.1.1': + resolution: {integrity: sha512-1AqLyFlfrrDkyES8uhINRlJXmHA2FkG+3DY8X+rmLSqmFwk3DJnvhyGzyByPyewh2jbmV+TYQBEfngQax8IFGg==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.1.0': + resolution: {integrity: sha512-ePTYUOV54wMogio+he4pBybe8fwg4sDvEVDBU8ZlHOZXbXK3/C0XfJgUCu6qAZcawv05ZhZzODGUerFBPsPUDQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.1.1': + resolution: {integrity: sha512-9wlfBBgTsRvC2JxLJxv4xDGNBrZuio3AgSl0lSFX7fneW2cGskXTYpFxCdRYD2+5yzmsiTuaAJD1Wp7gWt9y9w==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.2.2': + resolution: {integrity: sha512-M51KcwD+UeSOFtpALGf5OijWt915aQT5eJhqnMKJt7ZTfDfNcvg2UZgIgTZUoiORawb6o5lk4n3rv7vnzQXgsA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.2.2': + resolution: {integrity: sha512-KZJueEOO+PWqflv2oGx9jICpHdBYXwCI19j7e2V3IMwKgFcXc9D9q/dsTf4B+uCnYxjNoS1jpyv6pGNGRsKOXA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.1.1': + resolution: {integrity: sha512-lh48uQdbCoj619kRouev5XbWhCwRKLmphAif16c4J6JgJ4uXjub1PI6RL38d3BLliUvSso6klyB/LTNpWSNIyg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.1.1': + resolution: {integrity: sha512-ygRnniqNcDhHzs6QAPIdia26M7e7z9gpkIMUe/pK0RsrQ7i5MblwxY8078/QCnGq6AmlUUWgljK2HlelsKIb/A==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.2.2': + resolution: {integrity: sha512-SYGTKyPvyCfEzIN5rD8q/bYaOPZprYUPD2f5g9M7OjaYupWOoQFYJ5ho+0wvxIRf471i2SR4GoiZ2r94Jq9h6A==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.2.1': + resolution: {integrity: sha512-REyybygHlxo3TJICPF89N2pMQSf+p+tBJqpVe1+77Cfi9HBPReNjTgtZ1Vg73exq24vkqJskKDpfF74reXjxfw==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.1.1': + resolution: {integrity: sha512-gm3ZS7DHxUbzC2wr8MUCsAabyiXY0gaj3ROWnhSx/9sPMc6eYLMM4rX81w1zsMaObj2Lq3PZtNCC1J6lpEY7zg==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.2.1': + resolution: {integrity: sha512-T8SlkLYCwfT/6m33SIU/JOVGNwoelkrvGjFKDSDtVvAXj/9gOT78JVJEas5a+ETjOu4SVvpCstKgd0PxSu/aHw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.1.1': + resolution: {integrity: sha512-J9b55bfimP4z/Jg1gNo+AT84hr90p716/nvxDkPGCD4W70MPms0h8KF50RDRgBGZeL83/u59DWNqJv6tEP/DHA==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.1.1': + resolution: {integrity: sha512-63TEp92YFz0oQ7Pj9IuI3IgnprP92LrZtRAkE3c6wLWJxfy/yOPRt39IOKerVr0JS770olzl0kGafXlAXZ1vng==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.1.1': + resolution: {integrity: sha512-Iam75b/JNXyDE41UvrlM6n8DNOa/r1ylFyvgruTUx7h2Uk7vDNV9AAwP1vfL1fOL8ls0xArwEGVcGZVd7IO/Cw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.2.0': + resolution: {integrity: sha512-OQTfmIEp2LLuWdxa8nEEPhZmiOREO6bcB6pjs0AySf4yiZhl6kMOfqmcwcY8BaBPX+0Tb+tG7/Ia/6mwpoZ7Pw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.2.1': + resolution: {integrity: sha512-M9rZhWQLjlQVCCR37cSjHfhriGRN+FQ8UfgrYNufv66TJgk+acaggShl3KS5U/ssxivvZLlnj7QH2CUOKlxPyA==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.6.2': + resolution: {integrity: sha512-u82cjh/x7MlMat76Z38TRmEcG6JtrrxN4N2CSNG5o2v2S3hfLAxRgSgFqf0FKM3dglH41Evknt/HOX+7nfzZ3g==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.5.0': + resolution: {integrity: sha512-RkUpIOsVlAwUIZXO1dsz8Zm+N72LClFfsNqf173catVlvRZiwPy0x2u0JLEA4byreOPKDZPGjmPDylMoP8ZJRg==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.1.1': + resolution: {integrity: sha512-bx32FUpkhcaKlEoOMbScvc93isaSiRM75pQ5IgIBaMkT7qMlIibpPRONyx/0CvrXHzJLpOn/u6YiDX2hcvs7Dg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.1.0': + resolution: {integrity: sha512-RUGd4wNb8GeW7xk+AY5ghGnIwM96V0l2uzvs/uVHf+tIuVX2WSvynk5CxNoBCsM2rQRSZElAo9rt3G5mJ/gktQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.1.0': + resolution: {integrity: sha512-V2E2Iez+bo6bUMOTENPr6eEmepdY8Hbs+Uc1vkDKgKNA/brTJqOW/ai3JO1BGj9GbCeLqw90pbbH7HFQyFotGQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.1.0': + resolution: {integrity: sha512-BOI5dYjheZdgR9XiEM3HJcEMCXSoqbzu7CzIgYrx0UtmvtC3tC2iDGpJLsSRFffUpy8ymsg2ARMP5fR8mtuUQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.1.0': + resolution: {integrity: sha512-N6yXcjfe/E+xKEccWEKzK6M+crMrlwaCepKja0pNnlSkm6SjAeLKKA++er5Ba0I17gvKfN/ThV+ZOx/CntKTVw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.1.0': + resolution: {integrity: sha512-swXz2vMjrP1ZusZWVTB/ai5gK+J8U0BWvP10v9fpcFvg+Xi/87LHvHfst2IgCs1i0v4qFZfGwCmeD/KNCdJZbQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.1.2': + resolution: {integrity: sha512-QKrOw01DvNHKgY+3p4r9Ut4u6EHLVZ01u6SkOMe6V6v5C+nRPXJeWh72qCT1HgwU3O7sxAIu23nNh+FOpYVZKA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.1.2': + resolution: {integrity: sha512-l2yRmSfx5haYHswPxMmCR6jGwgPs5LjHLuBwlj9U7nNBMS43YV/eevj+Xq1869UYdiynnMrCKtoOYQcwtb6lKg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.1.2': + resolution: {integrity: sha512-+AJsaaEGb5ySvf1SKMRrPZdYHRYSzMkCoK16jWnIMpREAnflVspMIDeCVSZJuj+5muZfgGpNpijE3mUNtjv01Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.1.0': + resolution: {integrity: sha512-1LcueNN5GYC4tr8mo14yVYbh/Ur8jHhWOxniZXii+1+ePiIbsLZ5fEI0QQGtbRRP5mOhmooos+rLmVASGGoq5w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.1.1': + resolution: {integrity: sha512-CGmZ72mL29VMfESz7S6dekqzCh8ZISj3B+w0g1hZFXaOjGTVaSqfAEFAq8EGp8fUL+Q2l8aqNmt8U1tglTikeg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.1.1': + resolution: {integrity: sha512-jGeybqEZ/LIordPLMh5bnmnoIgsqnp4IEimmUp5c5voZ8yx+5kAlN5+juyr7p+f7AtZTgvhmInQk4Q0UVbrZ0Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.3.1': + resolution: {integrity: sha512-khKkW/Jqkgh6caxMWbMuox9+YfGlsk9OnHOYCGVEdYQb/XVzcORXHLYUubHmmda0pubEDncofUrPNniS9d+uAA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.1.0': + resolution: {integrity: sha512-b0EFQkq35K5NHUYxU72JuoheM6+pytEVUGlTwiFxWFpmddA+Bpz3LgsPRIpBk8lnPE47yT7AF2Egc3jVnKLuPg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.1.0': + resolution: {integrity: sha512-mEu1/UIXAdNYuBcyEPbjScKi/+MQVXNIuY/7Cm5XLIWe319kDrT5SizBE95jqtmEXoDbGoZxKLCMttdZdqTZKQ==} + engines: {node: '>=18.0.0'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@socket.io/redis-adapter@8.3.0': + resolution: {integrity: sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==} + engines: {node: '>=10.0.0'} + peerDependencies: + socket.io-adapter: ^2.5.4 + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-static@3.0.9': + resolution: {integrity: sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/enhanced-img@0.8.1': + resolution: {integrity: sha512-Ibom8j6F9vdmOeR+ljQ4q7TEJV0FWN1kB5wJZ2S/GuDVgCrKYrsXBw5gpSKcHwGYpKA3o9EoijPhep/GHgRUWQ==} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0 + svelte: ^5.0.0 + vite: ^6.3.0 || >=7.0.0 + + '@sveltejs/kit@2.38.1': + resolution: {integrity: sha512-5JJBPu3U2KXpRwc+e/D2Pl+DJM9oBcCl6XtWenrb6xc6H4lFa0XIJaSch4wMiADrhX512sVIUf13VnEp7aWO1w==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.0': + resolution: {integrity: sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.0': + resolution: {integrity: sha512-nJsV36+o7rZUDlrnSduMNl11+RoDE1cKqOI0yUEBCcqFoAZOk47TwD3dPKS2WmRutke9StXnzsPBslY7prDM9w==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/webpack@8.1.0': + resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} + engines: {node: '>=14'} + + '@swc/core-darwin-arm64@1.13.5': + resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.13.5': + resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.13.5': + resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.13.5': + resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.13.5': + resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.13.5': + resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.13.5': + resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.13.5': + resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.13.5': + resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.13.5': + resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.13.5': + resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.13': + resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.8.0': + resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/svelte@5.2.8': + resolution: {integrity: sha512-ucQOtGsJhtawOEtUmbR4rRh53e6RbM1KUluJIXRmh6D4UzxR847iIqqjRtg9mHNFmGQ8Vkam9yVcR5d1mhIHKA==} + engines: {node: '>= 10'} + peerDependencies: + svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0 + vite: '*' + vitest: '*' + peerDependenciesMeta: + vite: + optional: true + vitest: + optional: true + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@turf/boolean-point-in-polygon@7.2.0': + resolution: {integrity: sha512-lvEOjxeXIp+wPXgl9kJA97dqzMfNexjqHou+XHVcfxQgolctoJiRYmcVCWGpiZ9CBf/CJha1KmD1qQoRIsjLaA==} + + '@turf/helpers@7.2.0': + resolution: {integrity: sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==} + + '@turf/invariant@7.2.0': + resolution: {integrity: sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==} + + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + + '@types/archiver@6.0.3': + resolution: {integrity: sha512-a6wUll6k3zX6qs5KlxIggs1P1JcYJaTCx2gnlr+f0S1yd2DoaEwoIK10HmBaLnZwWneBz+JBm0dwcZu0zECBcQ==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/async-lock@1.4.2': + resolution: {integrity: sha512-HlZ6Dcr205BmNhwkdXqrg2vkFMN2PluI7Lgr8In3B3wE5PiQHhjRqtW/lGdVU9gw+sM0JcIDx2AN+cW8oSWIcw==} + + '@types/bcrypt@6.0.0': + resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/braces@3.0.5': + resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==} + + '@types/byte-size@8.1.2': + resolution: {integrity: sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/chrome@0.0.328': + resolution: {integrity: sha512-Mv/TQMFMpGBTqWQOQsYcAd3qDncH/Q+Dz7iJVjkwb98uLeM8xZVEYH3VR5Jh+su9IBksbumJeazakK8WqBAgFA==} + + '@types/chromecast-caf-sender@1.0.11': + resolution: {integrity: sha512-Pv3xvNYtxD/cTM/tKfuZRlLasvpxAm+CFni0GJd6Cp8XgiZS9g9tMZkR1uymsi5fIFv057SZKKAWVFFgy7fJtw==} + + '@types/cli-progress@3.11.6': + resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} + + '@types/compression@1.8.1': + resolution: {integrity: sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/content-disposition@0.5.9': + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} + + '@types/cookie-parser@1.4.9': + resolution: {integrity: sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==} + peerDependencies: + '@types/express': '*' + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + + '@types/cookies@0.9.1': + resolution: {integrity: sha512-E/DPgzifH4sM1UMadJMWd6mO2jOd4g1Ejwzx8/uRCDpJis1IrlyQEcGAYEomtAqRYmD5ORbNXMeI9U0RiVGZbg==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@3.3.42': + resolution: {integrity: sha512-U1jqHMShibMEWHdxYhj3rCMNCiLx5f35i4e3CEUuW+JSSszc/tVqc6WCAPdhwBymG5R/vgbcceagK0St7Cq6Eg==} + + '@types/dom-to-image@2.6.7': + resolution: {integrity: sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + + '@types/express-serve-static-core@5.0.6': + resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} + + '@types/express@4.17.23': + resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/filesystem@0.0.36': + resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} + + '@types/filewriter@0.0.33': + resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} + + '@types/fluent-ffmpeg@2.1.27': + resolution: {integrity: sha512-QiDWjihpUhriISNoBi2hJBRUUmoj/BMTYcfz+F+ZM9hHWBYABFAE6hjP/TbCZC0GWwlpa3FzvHH9RzFeRusZ7A==} + + '@types/geojson-vt@3.2.5': + resolution: {integrity: sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/gtag.js@0.0.12': + resolution: {integrity: sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==} + + '@types/har-format@1.2.16': + resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/history@4.7.11': + resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + + '@types/http-assert@1.5.6': + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.16': + resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} + + '@types/inquirer@8.2.11': + resolution: {integrity: sha512-15UboTvxb9SOaPG7CcXZ9dkv8lNqfiAwuh/5WxJDLjmElBt9tbx1/FDsEnJddUBKvN4mlPKvr8FyO1rAmBanzg==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/justified-layout@4.1.4': + resolution: {integrity: sha512-q2ybP0u0NVj87oMnGZOGxY2iUN8ddr48zPOBHBdbOLpsMTA/keGj+93ou+OMCnJk0xewzlNIaVEkxM6VBD3E2w==} + + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + + '@types/koa-compose@3.2.8': + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + + '@types/koa@3.0.0': + resolution: {integrity: sha512-MOcVYdVYmkSutVHZZPh8j3+dAjLyR5Tl59CN0eKgpkE1h/LBSmPAsQQuWs+bKu7WtGNn+hKfJH9Gzml+PulmDg==} + + '@types/leaflet@1.9.20': + resolution: {integrity: sha512-rooalPMlk61LCaLOvBF2VIf9M47HgMQqi5xQ9QRi7c8PkdIe0WrIi5IxXUXQjAdL0c+vcQ01mYWbthzmp9GHWw==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + + '@types/luxon@3.6.2': + resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==} + + '@types/luxon@3.7.1': + resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + + '@types/micromatch@4.0.9': + resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/mock-fs@4.13.4': + resolution: {integrity: sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/multer@2.0.0': + resolution: {integrity: sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==} + + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + + '@types/node@18.19.126': + resolution: {integrity: sha512-8AXQlBfrGmtYJEJUPs63F/uZQqVeFiN9o6NUjbDJYfxNxFnArlZufANPw4h6dGhYGKxcyw+TapXFvEsguzIQow==} + + '@types/node@20.19.2': + resolution: {integrity: sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==} + + '@types/node@22.18.8': + resolution: {integrity: sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==} + + '@types/node@24.5.1': + resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==} + + '@types/nodemailer@7.0.1': + resolution: {integrity: sha512-UfHAghPmGZVzaL8x9y+mKZMWyHC399+iq0MOmya5tIyenWX3lcdSb60vOmp0DocR6gCDTYTozv/ULQnREyyjkg==} + + '@types/oidc-provider@9.5.0': + resolution: {integrity: sha512-eEzCRVTSqIHD9Bo/qRJ4XQWQ5Z/zBcG+Z2cGJluRsSuWx1RJihqRyPxhIEpMXTwPzHYRTQkVp7hwisQOwzzSAg==} + + '@types/parse5@5.0.3': + resolution: {integrity: sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==} + + '@types/pg-pool@2.0.6': + resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} + + '@types/pg@8.15.5': + resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} + + '@types/picomatch@4.0.2': + resolution: {integrity: sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==} + + '@types/pngjs@6.0.5': + resolution: {integrity: sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==} + + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} + + '@types/qrcode@1.5.5': + resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-router-config@5.0.11': + resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==} + + '@types/react-router-dom@5.3.3': + resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + + '@types/react-router@5.1.20': + resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} + + '@types/react@19.1.13': + resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} + + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/sanitize-html@2.16.0': + resolution: {integrity: sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/ssh2-streams@0.1.12': + resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.5': + resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==} + + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supercluster@7.1.3': + resolution: {integrity: sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + + '@types/ua-parser-js@0.7.39': + resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/validator@13.15.3': + resolution: {integrity: sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==} + + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@typescript-eslint/eslint-plugin@8.45.0': + resolution: {integrity: sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.45.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.45.0': + resolution: {integrity: sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.45.0': + resolution: {integrity: sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.45.0': + resolution: {integrity: sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.45.0': + resolution: {integrity: sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.45.0': + resolution: {integrity: sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.45.0': + resolution: {integrity: sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.45.0': + resolution: {integrity: sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.45.0': + resolution: {integrity: sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.45.0': + resolution: {integrity: sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitest/coverage-v8@3.2.4': + resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} + peerDependencies: + '@vitest/browser': 3.2.4 + vitest: 3.2.4 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@zoom-image/core@0.41.0': + resolution: {integrity: sha512-LAxGru91286gFmyiQB4RjM277YOWxJX+OZcwtIH/N0dyo73y4NfaAE1eGVdnhjxEYv7yVV3xToMyYnm+uQboTw==} + + '@zoom-image/svelte@0.3.4': + resolution: {integrity: sha512-8cPkFUjh+t3/eYkoT2krvz8hoFiXoiYZKpcHOnYCHLhEwaHr1yjgXg/ttWehotVH9V3Z51JQgIcGF3uhYWKB/Q==} + peerDependencies: + svelte: ^3.0.0 || ^4.0.0 || ^5.0.0 + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + algoliasearch-helper@3.26.0: + resolution: {integrity: sha512-Rv2x3GXleQ3ygwhkhJubhhYGsICmShLAiqtUuJTUkr9uOCOXyF2E71LVT4XDnVffbknv8XgScP4U0Oxtgm+hIw==} + peerDependencies: + algoliasearch: '>= 3.1 < 6' + + algoliasearch@5.29.0: + resolution: {integrity: sha512-E2l6AlTWGznM2e7vEE6T6hzObvEyXukxMOlBmVlMyixZyK1umuO/CiVc6sDBbzVH0oEviCE5IfVY1oZBmccYPQ==} + engines: {node: '>= 14.0.0'} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansis@4.1.0: + resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} + engines: {node: '>=14'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-source@0.0.4: + resolution: {integrity: sha512-frNdc+zBn80vipY+GdcJkLEbMWj3xmzArYApmUGxoiV8uAu/ygcs9icPdsGdA26h0MkHUMW6EN2piIvVx+M5Mw==} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-metadata-inferer@0.8.1: + resolution: {integrity: sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==} + + ast-v8-to-istanbul@0.3.3: + resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + async@0.2.10: + resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autocomplete.js@0.37.1: + resolution: {integrity: sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: '>=5' + + babel-plugin-dynamic-import-node@2.3.3: + resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} + + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.11.1: + resolution: {integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + bail@1.0.5: + resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.6.1: + resolution: {integrity: sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==} + + bare-fs@4.2.0: + resolution: {integrity: sha512-oRfrw7gwwBVAWx9S5zPMo2iiOjxyiZE12DmblmMQREgcogbNO0AFaZ+QBxxkEXiPspcpvO/Qtqn8LabUx4uYXg==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.1: + resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + batch-cluster@13.0.0: + resolution: {integrity: sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==} + engines: {node: '>=14'} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + bcp-47-match@1.0.3: + resolution: {integrity: sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bcrypt@6.0.0: + resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} + engines: {node: '>= 18'} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bits-ui@2.9.8: + resolution: {integrity: sha512-oVAqdhLSuGIgEiT0yu3ShSI7AxncCxX26Gv6Lul94BuKHV2uzHoKfIodtnMQSq+udJ54svuCIRqA58whsv7vaA==} + engines: {node: '>=20'} + peerDependencies: + '@internationalized/date': ^3.8.1 + svelte: ^5.33.0 + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bowser@2.12.1: + resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + + boxen@6.2.1: + resolution: {integrity: sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.3: + resolution: {integrity: sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + + builtin-modules@5.0.0: + resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} + engines: {node: '>=18.20'} + + bullmq@5.58.5: + resolution: {integrity: sha512-0A6Qjxdn8j7aOcxfRZY798vO/aMuwvoZwfE6a9EOXHb1pzpBVAogsc/OfRWeUf+5wMBoYB5nthstnJo/zrQOeQ==} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + + byte-size@9.0.1: + resolution: {integrity: sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==} + engines: {node: '>=12.17'} + peerDependencies: + '@75lb/nature': latest + peerDependenciesMeta: + '@75lb/nature': + optional: true + + bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001735: + resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} + engines: {node: '>=8'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + + class-validator@0.14.2: + resolution: {integrity: sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-color@2.0.4: + resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==} + engines: {node: '>=0.10'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-progress@3.12.0: + resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} + engines: {node: '>=4'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combine-promises@1.2.0: + resolution: {integrity: sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==} + engines: {node: '>=10'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + comment-json@4.2.5: + resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} + engines: {node: '>= 6'} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + content-disposition@0.5.2: + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} + engines: {node: '>= 0.6'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-parser@1.4.7: + resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} + engines: {node: '>= 0.8.0'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + + cookies@0.9.1: + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} + engines: {node: '>= 0.8'} + + copy-text-to-clipboard@3.2.0: + resolution: {integrity: sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==} + engines: {node: '>=12'} + + copy-webpack-plugin@11.0.0: + resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} + engines: {node: '>= 14.15.0'} + peerDependencies: + webpack: ^5.1.0 + + core-js-compat@3.45.0: + resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==} + + core-js-pure@3.43.0: + resolution: {integrity: sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==} + + core-js@3.43.0: + resolution: {integrity: sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + + cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + + cron@4.3.0: + resolution: {integrity: sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==} + engines: {node: '>=18.x'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + css-blank-pseudo@7.0.1: + resolution: {integrity: sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-has-pseudo@7.0.2: + resolution: {integrity: sha512-nzol/h+E0bId46Kn2dQH5VElaknX2Sr0hFuB/1EomdC7j+OISt2ZzK7EHX9DZDY53WbIVAR7FYKSO2XnSf07MQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-minimizer-webpack-plugin@5.0.1: + resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@parcel/css': '*' + '@swc/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + lightningcss: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@parcel/css': + optional: true + '@swc/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + lightningcss: + optional: true + + css-prefers-color-scheme@10.0.0: + resolution: {integrity: sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-selector-parser@1.4.1: + resolution: {integrity: sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + csscolorparser@1.0.3: + resolution: {integrity: sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==} + + cssdb@8.3.1: + resolution: {integrity: sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-advanced@6.1.2: + resolution: {integrity: sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-preset-default@6.1.2: + resolution: {integrity: sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-utils@4.0.2: + resolution: {integrity: sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano@6.1.2: + resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + + debounce@2.2.0: + resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} + engines: {node: '>=18'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-equal@1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-gateway@6.0.3: + resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} + engines: {node: '>= 10'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-europe-js@0.1.2: + resolution: {integrity: sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==} + + detect-libc@2.1.0: + resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + detect-port@1.6.1: + resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} + engines: {node: '>= 4.0.0'} + hasBin: true + + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diacritics@1.3.0: + resolution: {integrity: sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + direction@1.0.4: + resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==} + hasBin: true + + discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + docker-compose@1.2.0: + resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==} + engines: {node: '>= 6.0.0'} + + docker-modem@5.0.6: + resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} + engines: {node: '>= 8.0'} + + dockerode@4.0.7: + resolution: {integrity: sha512-R+rgrSRTRdU5mH14PZTCPZtW/zw3HDWNTS/1ZAQpL/5Upe/ye5K9WQkIysu4wBoiMwKynsz0a8qWuGsHgEvSAA==} + engines: {node: '>= 8.0'} + + docusaurus-lunr-search@3.6.0: + resolution: {integrity: sha512-CCEAnj5e67sUZmIb2hOl4xb4nDN07fb0fvRDDmdWlYpUvyS1CSKbw4lsGInLyUFEEEBzxQmT6zaVQdF/8Zretg==} + engines: {node: '>= 8.10.0'} + peerDependencies: + '@docusaurus/core': ^2.0.0-alpha.60 || ^2.0.0 || ^3.0.0 + react: ^16.8.4 || ^17 || ^18 || ^19 + react-dom: ^16.8.4 || ^17 || ^18 || ^19 + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom-to-image@2.6.0: + resolution: {integrity: sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + earcut@2.2.4: + resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} + + earcut@3.0.2: + resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.207: + resolution: {integrity: sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==} + + emoji-regex@10.5.0: + resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + emoticon@4.1.0: + resolution: {integrity: sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + es6-weak-map@2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-compat@6.0.2: + resolution: {integrity: sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA==} + engines: {node: '>=18.x'} + peerDependencies: + eslint: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-svelte@3.12.4: + resolution: {integrity: sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-plugin-unicorn@60.0.0: + resolution: {integrity: sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==} + engines: {node: ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.29.0' + + eslint-plugin-unicorn@61.0.2: + resolution: {integrity: sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==} + engines: {node: ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.29.0' + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.36.0: + resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-value-to-estree@3.4.0: + resolution: {integrity: sha512-Zlp+gxis+gCfK12d3Srl2PdX2ybsEA8ZYy6vQGVQTNNYLEGRQQ56XB64bjemN8kxIKXP1nC9ip4Z+ILy9LGzvQ==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eta@2.2.0: + resolution: {integrity: sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==} + engines: {node: '>=6.0.0'} + + eta@3.5.0: + resolution: {integrity: sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==} + engines: {node: '>=6.0.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eval@0.1.8: + resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} + engines: {node: '>= 0.8'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exiftool-vendored.exe@13.0.0: + resolution: {integrity: sha512-4zAMuFGgxZkOoyQIzZMHv1HlvgyJK3AkNqjAgm8A8V0UmOZO7yv3pH49cDV1OduzFJqgs6yQ6eG4OGydhKtxlg==} + os: [win32] + + exiftool-vendored.pl@13.0.1: + resolution: {integrity: sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==} + os: ['!win32'] + + exiftool-vendored@28.8.0: + resolution: {integrity: sha512-R7tirJLr9fWuH9JS/KFFLB+O7jNGKuPXGxREc6YybYangEudGb+X8ERsYXk9AifMiAWh/2agNfbgkbcQcF+MxA==} + + expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + engines: {node: '>=12.0.0'} + + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fabric@6.7.1: + resolution: {integrity: sha512-dLxSmIvN4InJf4xOjbl1LFWh8WGOUIYtcuDIGs2IN0Z9lI0zGobfesDauyEhI1+owMLTPCCiEv01rpYXm7g2EQ==} + engines: {node: '>=16.20.0'} + + factory.ts@1.4.2: + resolution: {integrity: sha512-8x2hqK1+EGkja4Ah8H3nkP7rDUJsBK1N3iFDqzqsaOV114o2IphSdVkFIw9nDHHr37gFFy2NXeN6n10ieqHzZg==} + engines: {node: '>= 14'} + + fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fault@2.0.1: + resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + feed@4.2.2: + resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} + engines: {node: '>=0.4.0'} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-loader@6.2.0: + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + file-source@0.6.1: + resolution: {integrity: sha512-1R1KneL7eTXmXfKxC10V/9NeGOdbsAXJ+lQ//fvvcHUgtaZcZDWNJNblxAoVOyV1cj45pOtUrR3vZTBwqcW8XA==} + + file-type@21.0.0: + resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} + engines: {node: '>=20'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fluent-ffmpeg@2.1.3: + resolution: {integrity: sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==} + engines: {node: '>=18'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fork-ts-checker-webpack-plugin@9.1.0: + resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + geo-coordinates-parser@1.7.4: + resolution: {integrity: sha512-gVGxBW+s1csexXVMf5bIwz3TH9n4sCEglOOOqmrPk8YazUI5f79jCowKjTw05m/0h1//3+Z2m/nv8IIozgZyUw==} + + geo-tz@8.1.4: + resolution: {integrity: sha512-xayeOC05wgy6JATU/k7GFHTMfSimzL1Fi3KSzt2GqvEnP1ZFXyQ9V4VAiTrTYhZSmRr0dbchZkximSegHZNUfA==} + engines: {node: '>=16'} + + geobuf@3.0.2: + resolution: {integrity: sha512-ASgKwEAQQRnyNFHNvpd5uAwstbVYmiTW0Caw3fBb509tNTqXyAAPMyFs5NNihsLZhLxU1j/kjFhkhLWA9djuVg==} + hasBin: true + + geojson-vt@3.2.1: + resolution: {integrity: sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==} + + geojson-vt@4.0.2: + resolution: {integrity: sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==} + + geojson@0.5.0: + resolution: {integrity: sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==} + engines: {node: '>= 0.10'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + github-slugger@1.5.0: + resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} + + gl-matrix@3.4.4: + resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + engines: {node: 20 || >=22} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + + grid-index@1.1.0: + resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==} + + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + happy-dom@18.0.1: + resolution: {integrity: sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==} + engines: {node: '>=20.0.0'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-own-prop@2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-from-parse5@6.0.1: + resolution: {integrity: sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-has-property@1.0.4: + resolution: {integrity: sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==} + + hast-util-is-element@1.1.0: + resolution: {integrity: sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-select@4.0.2: + resolution: {integrity: sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-string@1.0.4: + resolution: {integrity: sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==} + + hast-util-to-text@2.0.1: + resolution: {integrity: sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==} + + hast-util-whitespace@1.0.4: + resolution: {integrity: sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + history@4.10.1: + resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} + + hogan.js@3.0.2: + resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} + hasBin: true + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + html-to-text@9.0.5: + resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} + engines: {node: '>=14'} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + html-webpack-plugin@5.6.3: + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} + engines: {node: '>=10.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.20.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-assert@1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-proxy-middleware@2.0.9: + resolution: {integrity: sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/express': ^4.17.13 + peerDependenciesMeta: + '@types/express': + optional: true + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + i18n-iso-countries@7.14.0: + resolution: {integrity: sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==} + engines: {node: '>= 12'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + + imagetools-core@8.0.0: + resolution: {integrity: sha512-5i4Cx5vrBpVdvT3gvkSGAzzkUCrg/5Jm54UwWbDUSTMp4AjDI4IxiC6dI4+X1PRJYi6eKqWuE+684NJY2iOn3w==} + engines: {node: '>=18.0.0'} + + immediate@3.3.0: + resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-in-the-middle@1.14.2: + resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + infima@0.2.0-alpha.45: + resolution: {integrity: sha512-uyH0zfr1erU1OohLk0fT4Rrb94AOhguWNOcD9uGrSpRvNB+6gZXUoJX5J0NtvzBO10YZ9PgvA4NFgt+fYg8ojw==} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + inquirer@8.2.7: + resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} + engines: {node: '>=12.0.0'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + intl-messageformat@10.7.16: + resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + ioredis@5.7.0: + resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==} + engines: {node: '>=12.22.0'} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} + + is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-standalone-pwa@0.1.1: + resolution: {integrity: sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + + jose@6.1.0: + resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: 2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: 2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-pretty-compact@4.0.0: + resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + just-compare@2.3.0: + resolution: {integrity: sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg==} + + justified-layout@4.1.0: + resolution: {integrity: sha512-M5FimNMXgiOYerVRGsXZ2YK9YNCaTtwtYp7Hb2308U1Q9TXXHx5G0p08mcVR5O53qf8bWY4NJcPBxE6zuayXSg==} + + kdbush@3.0.0: + resolution: {integrity: sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==} + + kdbush@4.0.2: + resolution: {integrity: sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==} + + keygrip@1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + koa-compose@4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + + koa@3.0.1: + resolution: {integrity: sha512-oDxVkRwPOHhGlxKIDiDB2h+/l05QPtefD7nSqRgDfZt8P+QVYFWjfeK8jANf5O2YXjk8egd7KntvXKYx82wOag==} + engines: {node: '>= 18'} + + kysely-postgres-js@2.0.0: + resolution: {integrity: sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ==} + peerDependencies: + kysely: '>= 0.24.0 < 1' + postgres: '>= 3.4.0 < 4' + + kysely@0.28.2: + resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} + engines: {node: '>=18.0.0'} + + latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + + launch-editor@2.10.0: + resolution: {integrity: sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + leac@0.6.0: + resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + libphonenumber-js@1.12.9: + resolution: {integrity: sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==} + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-esm@1.0.2: + resolution: {integrity: sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==} + engines: {node: '>=13.2.0'} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-queue@0.1.0: + resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} + + lunr-languages@1.14.0: + resolution: {integrity: sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA==} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + + luxon@3.6.1: + resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==} + engines: {node: '>=12'} + + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + mapbox-gl@1.13.3: + resolution: {integrity: sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==} + engines: {node: '>=6.4.0'} + + maplibre-gl@5.7.1: + resolution: {integrity: sha512-iCOQB6W/EGgQx8aU4SyfU5a5/GR2E+ELF92NMsqYfs3x+vnY+8mARmz4gor6XZHCz3tv19mnotVDRlRTMNKyGw==} + engines: {node: '>=16.14.0', npm: '>=8.1.0'} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@2.0.0: + resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + marked@7.0.4: + resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==} + engines: {node: '>= 16'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md-to-react-email@5.0.5: + resolution: {integrity: sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==} + peerDependencies: + react: ^18.0 || ^19.0 + + mdast-util-directive@3.1.0: + resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-frontmatter@2.0.1: + resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memoizee@0.4.17: + resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} + engines: {node: '>=0.12'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@3.0.2: + resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} + + micromark-extension-frontmatter@2.0.0: + resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@1.1.0: + resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@1.2.0: + resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@1.1.0: + resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@1.1.0: + resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mini-css-extract-plugin@2.9.2: + resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.3.0: + resolution: {integrity: sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==} + deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mnemonist@0.40.3: + resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==} + + mock-fs@5.5.0: + resolution: {integrity: sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==} + engines: {node: '>=12.0.0'} + + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.5: + resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} + + multer@2.0.2: + resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==} + engines: {node: '>= 10.16.0'} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + murmurhash-js@1.0.0: + resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nan@2.23.0: + resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + engines: {node: ^18 || >=20} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + nest-commander@3.19.1: + resolution: {integrity: sha512-Pn6xcMeSnidlzZozNLnbe7P4TqXL7g0JuxqTAtJ89KT4S63ntJZKtRU6g/56h/aHUQa+m98j/c9OxBSduK7EPg==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@types/inquirer': ^8.1.3 + + nestjs-cls@5.4.3: + resolution: {integrity: sha512-yHEHyVoe6rsvj3XRPFonBKPXPjDREyHfKZ9PTStSLJTZAV3wey1Q89TquSj6QciqXB5387GiHv9DG+ja6iAUHw==} + engines: {node: '>=18'} + peerDependencies: + '@nestjs/common': '>= 10 < 12' + '@nestjs/core': '>= 10 < 12' + reflect-metadata: '*' + rxjs: '>= 7' + + nestjs-kysely@3.0.0: + resolution: {integrity: sha512-YA6tHBgXQYPNpMBPII2OvUOiaWjCCoh5pP5dUHirQcMUHxNFzInBL6MDk8y74rk2z/5IvAK9AUlsdPyJtToO6g==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + kysely: 0.x + reflect-metadata: ^0.1.13 || ^0.2.2 + + nestjs-otel@7.0.1: + resolution: {integrity: sha512-NKce9aAJ263rcqaj3etHmv5KE+VALBqjGkPmZYvaesIb7AT7WBA3YXiEXmkJdKsnF2ZwmNFUJXCQWPn91Hrc8A==} + engines: {node: '>= 20'} + peerDependencies: + '@nestjs/common': '>= 11 < 12' + '@nestjs/core': '>= 11 < 12' + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-addon-api@4.3.0: + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + + node-addon-api@8.5.0: + resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} + engines: {node: ^18 || ^20 || >= 21} + + node-emoji@1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-gyp@11.4.2: + resolution: {integrity: sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nodemailer@7.0.7: + resolution: {integrity: sha512-jGOaRznodf62TVzdyhKt/f1Q/c3kYynk8629sgJHpRzGZj01ezbgMMWJSAjHADcwTKxco3B68/R+KHJY2T5BaA==} + engines: {node: '>=6.0.0'} + + nopt@1.0.10: + resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.2: + resolution: {integrity: sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==} + engines: {node: '>=14.16'} + + not@0.1.0: + resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==} + + notepack.io@3.0.1: + resolution: {integrity: sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + nprogress@0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + null-loader@4.0.1: + resolution: {integrity: sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + + nypm@0.6.0: + resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + oauth4webapi@3.8.1: + resolution: {integrity: sha512-olkZDELNycOWQf9LrsELFq8n05LwJgV8UkrS0cburk6FOwf8GvLam+YB+Uj5Qvryee+vwWOfQVeI5Vm0MVg7SA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + obliterator@2.0.5: + resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + oidc-provider@9.5.1: + resolution: {integrity: sha512-19Wa4bfz3reoudxrY7sF5SeQKxe5b3dY8hWzQdnBGS87rH0BoYoDDUDRTYciJMN3oI6S02C9xM6vuaHtoZ48eA==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + openid-client@6.8.0: + resolution: {integrity: sha512-oG1d1nAVhIIE+JSjLS+7E9wY1QOJpZltkzlJdbZ7kEn7Hp3hqur2TEeQ8gLOHoHkhbRAGZJKoOnEQcLOQJuIyg==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-numeric-range@1.3.0: + resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} + + parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseley@0.12.1: + resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-source@0.1.3: + resolution: {integrity: sha512-dWRHm5mIw5kw0cs3QZLNmpUWty48f5+5v9nWD2dw3Y0Hf+s01Ag8iJEWV0Sm0kocE8kK27DrIowha03e1YR+Qw==} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-to-regexp@1.9.0: + resolution: {integrity: sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==} + + path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + path-to-regexp@8.3.0: + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + pbf@3.3.0: + resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} + hasBin: true + + pbf@4.0.1: + resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==} + hasBin: true + + peberminta@0.9.0: + resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + + playwright-core@1.55.0: + resolution: {integrity: sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.55.0: + resolution: {integrity: sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==} + engines: {node: '>=18'} + hasBin: true + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + pmtiles@3.2.1: + resolution: {integrity: sha512-3R4fBwwoli5mw7a6t1IGwOtfmcSAODq6Okz0zkXhS1zi9sz1ssjjIfslwPvcWw5TNhdjNBUg9fgfPLeqZlH6ng==} + + pmtiles@4.3.0: + resolution: {integrity: sha512-wnzQeSiYT/MyO63o7AVxwt7+uKqU0QUy2lHrivM7GvecNy0m1A4voVyGey7bujnEW5Hn+ZzLdvHPoFaqrOzbPA==} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + point-in-polygon-hao@1.2.4: + resolution: {integrity: sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==} + + postcss-attribute-case-insensitive@7.0.1: + resolution: {integrity: sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-calc@9.0.1: + resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.2.2 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@7.0.10: + resolution: {integrity: sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-hex-alpha@10.0.0: + resolution: {integrity: sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@10.0.0: + resolution: {integrity: sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-colormin@6.1.0: + resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-convert-values@6.1.0: + resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-custom-media@11.0.6: + resolution: {integrity: sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-properties@14.0.6: + resolution: {integrity: sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-selectors@8.0.5: + resolution: {integrity: sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-dir-pseudo-class@9.0.1: + resolution: {integrity: sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-discard-comments@6.0.2: + resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-duplicates@6.0.3: + resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-empty@6.0.3: + resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-overridden@6.0.2: + resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-unused@6.0.5: + resolution: {integrity: sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-double-position-gradients@6.0.2: + resolution: {integrity: sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-visible@10.0.1: + resolution: {integrity: sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@9.0.1: + resolution: {integrity: sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@6.0.0: + resolution: {integrity: sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-image-set-function@7.0.0: + resolution: {integrity: sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-lab-function@7.0.10: + resolution: {integrity: sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-loader@7.3.4: + resolution: {integrity: sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A==} + engines: {node: '>= 14.15.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + + postcss-logical@8.1.0: + resolution: {integrity: sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-merge-idents@6.0.3: + resolution: {integrity: sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-longhand@6.0.5: + resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-rules@6.1.1: + resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-font-values@6.1.0: + resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-gradients@6.0.3: + resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-params@6.1.0: + resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-selectors@6.0.4: + resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nesting@13.0.2: + resolution: {integrity: sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-normalize-charset@6.0.2: + resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-display-values@6.0.2: + resolution: {integrity: sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-positions@6.0.2: + resolution: {integrity: sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-repeat-style@6.0.2: + resolution: {integrity: sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-string@6.0.2: + resolution: {integrity: sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-timing-functions@6.0.2: + resolution: {integrity: sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-unicode@6.1.0: + resolution: {integrity: sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-url@6.0.2: + resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-whitespace@6.0.2: + resolution: {integrity: sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-opacity-percentage@3.0.0: + resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-ordered-values@6.0.2: + resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-overflow-shorthand@6.0.0: + resolution: {integrity: sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@10.0.0: + resolution: {integrity: sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-preset-env@10.2.4: + resolution: {integrity: sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-pseudo-class-any-link@10.0.1: + resolution: {integrity: sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-reduce-idents@6.0.3: + resolution: {integrity: sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-initial@6.1.0: + resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-transforms@6.0.2: + resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-not@8.0.1: + resolution: {integrity: sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-sort-media-queries@5.2.0: + resolution: {integrity: sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.4.23 + + postcss-svgo@6.0.3: + resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} + engines: {node: ^14 || ^16 || >= 18} + peerDependencies: + postcss: ^8.4.31 + + postcss-unique-selectors@6.0.4: + resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss-zindex@6.0.2: + resolution: {integrity: sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + + potpack@1.0.2: + resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} + + potpack@2.1.0: + resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier-plugin-organize-imports@4.2.0: + resolution: {integrity: sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==} + peerDependencies: + prettier: '>=2.0' + typescript: '>=2.9' + vue-tsc: ^2.1.0 || 3 + peerDependenciesMeta: + vue-tsc: + optional: true + + prettier-plugin-sort-json@4.1.1: + resolution: {integrity: sha512-uJ49wCzwJ/foKKV4tIPxqi4jFFvwUzw4oACMRG2dcmDhBKrxBv0L2wSKkAqHCmxKCvj0xcCZS4jO2kSJO/tRJw==} + engines: {node: '>=18.0.0'} + peerDependencies: + prettier: ^3.0.0 + + prettier-plugin-svelte@3.4.0: + resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + pretty-time@1.1.0: + resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==} + engines: {node: '>=4'} + + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + protocol-buffers-schema@3.6.0: + resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + quick-lru@7.2.0: + resolution: {integrity: sha512-fG4L8TlD1CacJiGMGPxM1/K8l/GaKL2eFQZ6DWAjxZYxSf07DkumbC/Mhh+u/NHvxkfQVL25By0pxBS8QE9ZrQ==} + engines: {node: '>=18'} + + quickselect@2.0.0: + resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==} + + quickselect@3.0.0: + resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} + + railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + + randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.0: + resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} + engines: {node: '>= 0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.1: + resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + engines: {node: '>= 0.10'} + + raw-loader@4.0.2: + resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-email@4.2.11: + resolution: {integrity: sha512-/7TXRgsTrXcV1u7kc5ZXDVlPvZqEBaYcflMhE2FgWIJh3OHLjj2FqctFTgYcp0iwzbR59a7gzJLmSKyD0wYJEQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-json-view-lite@2.4.1: + resolution: {integrity: sha512-fwFYknRIBxjbFm0kBDrzgBy1xa5tDg2LyXXBepC5f1b+MY3BUClMCsvanMPn089JbV1Eg3nZcrp0VCuH43aXnA==} + engines: {node: '>=18'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + + react-loadable-ssr-addon-v5-slorber@1.0.1: + resolution: {integrity: sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==} + engines: {node: '>=10.13.0'} + peerDependencies: + react-loadable: '*' + webpack: '>=4.41.1 || 5.x' + + react-promise-suspense@0.3.4: + resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} + + react-router-config@5.1.1: + resolution: {integrity: sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==} + peerDependencies: + react: '>=15' + react-router: '>=5' + + react-router-dom@5.3.4: + resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==} + peerDependencies: + react: '>=15' + + react-router@5.3.4: + resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==} + peerDependencies: + react: '>=15' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.0: + resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==} + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + regexpu-core@6.2.0: + resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} + engines: {node: '>=4'} + + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + hasBin: true + + rehype-parse@7.0.1: + resolution: {integrity: sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remark-directive@3.0.1: + resolution: {integrity: sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==} + + remark-emoji@4.0.1: + resolution: {integrity: sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + remark-frontmatter@5.0.0: + resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.0: + resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} + engines: {node: '>=8.6.0'} + + require-like@0.1.2: + resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pathname@3.0.0: + resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} + + resolve-protobuf-schema@2.1.0: + resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + response-time@2.3.4: + resolution: {integrity: sha512-fiyq1RvW5/Br6iAtT8jN1XrNY8WPu2+yEypLbaijWry8WDZmn12azG9p/+c+qpEebURLlQmqCB8BNSu7ji+xQQ==} + engines: {node: '>= 0.8.0'} + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rollup-plugin-visualizer@6.0.3: + resolution: {integrity: sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + rtlcss@4.3.0: + resolution: {integrity: sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==} + engines: {node: '>=12.0.0'} + hasBin: true + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + runed@0.29.2: + resolution: {integrity: sha512-0cq6cA6sYGZwl/FvVqjx9YN+1xEBu9sDDyuWdDW1yWX7JF2wmvmVKfH+hVCZs+csW+P3ARH92MjI3H9QTagOQA==} + peerDependencies: + svelte: ^5.7.0 + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize-filename@1.6.3: + resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + + sanitize-html@2.17.0: + resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + schema-dts@1.1.5: + resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + + selderee@0.11.0: + resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-handler@6.1.6: + resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==} + + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + shapefile@0.6.6: + resolution: {integrity: sha512-rLGSWeK2ufzCVx05wYd+xrWnOOdSV7xNUW5/XFgx3Bc02hBkpMlrd2F1dDII7/jhWzv0MSyBFh5uJIy9hLdfuw==} + hasBin: true + + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + + simple-icons@15.15.0: + resolution: {integrity: sha512-ohh1Uo9AjH10WN5wpPmtjnmbSLv6MjiULHS4dYA821uIsPAp0Q3XoluPnjBnQAFsztasmM6z2dezJBrQbtserg==} + engines: {node: '>=0.12.18'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@7.1.2: + resolution: {integrity: sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==} + engines: {node: '>=12.0.0', npm: '>=5.6.0'} + hasBin: true + + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slice-source@0.4.1: + resolution: {integrity: sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg==} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sort-css-media-queries@2.2.0: + resolution: {integrity: sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==} + engines: {node: '>= 6.3.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sql-formatter@15.6.9: + resolution: {integrity: sha512-r9VKnkRfKW7jbhTgytwbM+JqmFclQYN9L58Z3UTktuy9V1f1Y+rGK3t70Truh2wIOJzvZkzobAQ2PwGjjXsr6Q==} + hasBin: true + + srcset@4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2@1.16.0: + resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} + engines: {node: '>=10.16.0'} + + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + stream-source@0.3.5: + resolution: {integrity: sha512-ZuEDP9sgjiAwUVoDModftG0JtYiLUV8K4ljYD1VyUMRWtbVf92474o4kuuul43iZ8t/hRuiDAx1dIJSvirrK/g==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + strnum@2.1.1: + resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + + strtok3@10.3.4: + resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} + engines: {node: '>=18'} + + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + + stylehacks@6.1.1: + resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} + engines: {node: ^14 || ^16 || >=18.0} + peerDependencies: + postcss: ^8.4.31 + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + superagent@10.2.3: + resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} + engines: {node: '>=14.18.0'} + + supercluster@7.1.5: + resolution: {integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==} + + supercluster@8.0.1: + resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==} + + supertest@7.1.4: + resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==} + engines: {node: '>=14.18.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte-check@4.3.1: + resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-eslint-parser@1.3.3: + resolution: {integrity: sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte-gestures@5.2.2: + resolution: {integrity: sha512-Y+chXPaSx8OsPoFppUwPk8PJzgrZ7xoDJKXeiEc7JBqyKKzXer9hlf8F9O34eFuAWB4/WQEvccACvyBplESL7A==} + + svelte-i18n@4.0.1: + resolution: {integrity: sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==} + engines: {node: '>= 16'} + hasBin: true + peerDependencies: + svelte: ^3 || ^4 || ^5 + + svelte-maplibre@1.2.1: + resolution: {integrity: sha512-IVkbc54hQXznyaiFN69RIdjqbLHriNYPVEo1DQMtWSm1kLovrt/aZuhV4eOoZKn6wIvY2Vz34jXPS33f/d/GNw==} + peerDependencies: + '@deck.gl/core': ^9 + '@deck.gl/layers': ^9 + '@deck.gl/mapbox': ^9 + svelte: ^5.0.0 + peerDependenciesMeta: + '@deck.gl/core': + optional: true + '@deck.gl/layers': + optional: true + '@deck.gl/mapbox': + optional: true + + svelte-parse-markup@0.1.5: + resolution: {integrity: sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==} + peerDependencies: + svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1 + + svelte-persisted-store@0.12.0: + resolution: {integrity: sha512-BdBQr2SGSJ+rDWH8/aEV5GthBJDapVP0GP3fuUCA7TjYG5ctcB+O9Mj9ZC0+Jo1oJMfZUd1y9H68NFRR5MyIJA==} + engines: {node: '>=0.14'} + peerDependencies: + svelte: ^3.48.0 || ^4 || ^5 + + svelte-toolbelt@0.9.3: + resolution: {integrity: sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.30.2 + + svelte@5.38.10: + resolution: {integrity: sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg==} + engines: {node: '>=18'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + swagger-ui-dist@5.21.0: + resolution: {integrity: sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==} + + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + systeminformation@5.23.8: + resolution: {integrity: sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ==} + engines: {node: '>=8.0.0'} + os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] + hasBin: true + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwind-variants@3.1.1: + resolution: {integrity: sha512-ftLXe3krnqkMHsuBTEmaVUXYovXtPyTK7ckEfDRXS8PBZx0bAUas+A0jYxuKA5b8qg++wvQ3d2MQ7l/xeZxbZQ==} + engines: {node: '>=16.x', pnpm: '>=7.x'} + peerDependencies: + tailwind-merge: '>=3.0.0' + tailwindcss: '*' + peerDependenciesMeta: + tailwind-merge: + optional: true + + tailwindcss-email-variants@3.0.4: + resolution: {integrity: sha512-ohtLSifyWQDAtddJnfbcxkIDCIyXp6Yb83hXRprrS+/2dSyme4OlUZAP+TDwQc0K8D0LAw80eKI6psgejxys8A==} + engines: {node: '>=18'} + peerDependencies: + tailwindcss: '>=3.4.0 < 4' + + tailwindcss-mso@2.0.2: + resolution: {integrity: sha512-GaR8RW/Kan+YWEQ9Y9Ah6AYy7R2wEQ3X++YK4ffJVWycCTd6ryMLezqmyhi7KWHqsgQOb4nhjJYayI+JF44BXw==} + engines: {node: '>=18.20'} + peerDependencies: + tailwindcss: '>=3.4.0 < 4' + + tailwindcss-preset-email@1.4.0: + resolution: {integrity: sha512-UgvLHT5UsPEEXjto1WlR1wYXmYKeMaS2OPTJQqyufsU12os/EjBpeygEjTdrId7U2/mwDF4grlgo81qlzYSByg==} + peerDependencies: + tailwindcss: '>=3.4.17' + + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-fs@3.1.0: + resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + + testcontainers@11.5.1: + resolution: {integrity: sha512-YSSP4lSJB8498zTeu4HYTZYgSky54ozBmIDdC8PFU5inj+vBo5hPpilhcYTgmsqsYjrXOJGV7jl0MWByS7GwuA==} + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + text-encoding@0.6.4: + resolution: {integrity: sha512-hJnc6Qg3dWoOMkqP53F0dzRIgtmsAge09kxUIqGrEUS4qr5rWLckGYaQAVr+opBrIMRErGgy6f5aPnyPpyGRfg==} + deprecated: no longer maintained + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + three@0.179.1: + resolution: {integrity: sha512-5y/elSIQbrvKOISxpwXCR4sQqHtGiOI+MKLc3SsBdDXA2hz3Mdp3X59aUp8DyybMa34aeBwbFTpdoLJaUDEWSw==} + + three@0.180.0: + resolution: {integrity: sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + thumbhash@0.1.1: + resolution: {integrity: sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + timers-ext@0.1.8: + resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} + engines: {node: '>=0.12'} + + tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyqueue@2.0.3: + resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==} + + tinyqueue@3.0.0: + resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-vfile@6.1.0: + resolution: {integrity: sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + token-types@6.1.1: + resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + engines: {node: '>=14.16'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@1.0.5: + resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + truncate-utf8-bytes@1.0.2: + resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript-eslint@8.45.0: + resolution: {integrity: sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + ua-is-frozen@0.1.2: + resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==} + + ua-parser-js@2.0.5: + resolution: {integrity: sha512-sZErtx3rhpvZQanWW5umau4o/snfoLqRcQwQIZ54377WtRzIecnIKvjpkd5JwPcSUMglGnbIgcsQBGAbdi3S9Q==} + hasBin: true + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + uid2@1.0.0: + resolution: {integrity: sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==} + engines: {node: '>= 4.0.0'} + + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + uint8array-extras@1.4.1: + resolution: {integrity: sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw==} + engines: {node: '>=18'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici-types@7.12.0: + resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + + undici@7.16.0: + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + engines: {node: '>=20.18.1'} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unified@9.2.2: + resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==} + + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + unist-util-find-after@3.0.0: + resolution: {integrity: sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==} + + unist-util-is@4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@2.0.3: + resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unplugin-swc@1.5.7: + resolution: {integrity: sha512-Ng4uuLAodZToA0kQk3+oY8b0C/Q9oV0ohRMixH2nqWMhCF/wNuMYZXZznYpwRLmF7wC36TFIOywBAxCLOReoeg==} + peerDependencies: + '@swc/core': ^1.2.108 + + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-loader@4.1.1: + resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + file-loader: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + utf8-byte-length@1.0.5: + resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + utimes@5.2.1: + resolution: {integrity: sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==} + engines: {node: '>=10.0.0'} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + validator@13.15.15: + resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} + engines: {node: '>= 0.10'} + + value-equal@1.0.1: + resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-location@3.2.0: + resolution: {integrity: sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@4.2.1: + resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-imagetools@8.0.0: + resolution: {integrity: sha512-3bkkA0vQ57tMynsetY2j4QhCnZKrxFv0RScaZipzYgkjkkUBEmZL5UIVHOUHhVMfwCetAeM9e3DNwyPK1ff4xg==} + engines: {node: '>=18.0.0'} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.1.5: + resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + vitest-fetch-mock@0.4.5: + resolution: {integrity: sha512-nhWdCQIGtaSEUVl96pMm0WggyDGPDv5FUy/Q9Hx3cs2RGmh3Q/uRsLClGbdG3kXBkJ3br5yTUjB2MeW25TwdOA==} + engines: {node: '>=18.0.0'} + peerDependencies: + vitest: '>=2.0.0' + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vt-pbf@3.1.3: + resolution: {integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==} + + w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-namespaces@1.1.4: + resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-bundle-analyzer@4.10.2: + resolution: {integrity: sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==} + engines: {node: '>= 10.13.0'} + hasBin: true + + webpack-dev-middleware@5.3.4: + resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + webpack-dev-server@4.15.2: + resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} + engines: {node: '>= 12.13.0'} + hasBin: true + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-merge@5.10.0: + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} + engines: {node: '>=10.0.0'} + + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + webpack@5.100.2: + resolution: {integrity: sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + webpackbar@6.0.1: + resolution: {integrity: sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + webpack: 3 || 4 || 5 + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + 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 + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + 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 + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xml-js@1.6.11: + resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} + hasBin: true + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + + zwitch@1.0.5: + resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + '@algolia/client-search': 5.29.0 + algoliasearch: 5.29.0 + + '@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)': + dependencies: + '@algolia/client-search': 5.29.0 + algoliasearch: 5.29.0 + + '@algolia/client-abtesting@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/client-analytics@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/client-common@5.29.0': {} + + '@algolia/client-insights@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/client-personalization@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/client-query-suggestions@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/client-search@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/events@4.0.1': {} + + '@algolia/ingestion@1.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/monitoring@1.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/recommend@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + '@algolia/requester-browser-xhr@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + + '@algolia/requester-fetch@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + + '@algolia/requester-node-http@5.29.0': + dependencies: + '@algolia/client-common': 5.29.0 + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@angular-devkit/core@19.2.15(chokidar@4.0.3)': + dependencies: + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + jsonc-parser: 3.3.1 + picomatch: 4.0.2 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.8)(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + '@inquirer/prompts': 7.3.2(@types/node@22.18.8) + ansi-colors: 4.1.3 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@types/node' + - chokidar + + '@angular-devkit/schematics@19.2.15(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + optional: true + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-locate-window': 3.873.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.887.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-sesv2@3.890.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.890.0 + '@aws-sdk/credential-provider-node': 3.890.0 + '@aws-sdk/middleware-host-header': 3.887.0 + '@aws-sdk/middleware-logger': 3.887.0 + '@aws-sdk/middleware-recursion-detection': 3.887.0 + '@aws-sdk/middleware-user-agent': 3.890.0 + '@aws-sdk/region-config-resolver': 3.890.0 + '@aws-sdk/signature-v4-multi-region': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-endpoints': 3.890.0 + '@aws-sdk/util-user-agent-browser': 3.887.0 + '@aws-sdk/util-user-agent-node': 3.890.0 + '@smithy/config-resolver': 4.2.2 + '@smithy/core': 3.11.0 + '@smithy/fetch-http-handler': 5.2.1 + '@smithy/hash-node': 4.1.1 + '@smithy/invalid-dependency': 4.1.1 + '@smithy/middleware-content-length': 4.1.1 + '@smithy/middleware-endpoint': 4.2.2 + '@smithy/middleware-retry': 4.2.2 + '@smithy/middleware-serde': 4.1.1 + '@smithy/middleware-stack': 4.1.1 + '@smithy/node-config-provider': 4.2.2 + '@smithy/node-http-handler': 4.2.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + '@smithy/util-base64': 4.1.0 + '@smithy/util-body-length-browser': 4.1.0 + '@smithy/util-body-length-node': 4.1.0 + '@smithy/util-defaults-mode-browser': 4.1.2 + '@smithy/util-defaults-mode-node': 4.1.2 + '@smithy/util-endpoints': 3.1.2 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-retry': 4.1.1 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.890.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.890.0 + '@aws-sdk/middleware-host-header': 3.887.0 + '@aws-sdk/middleware-logger': 3.887.0 + '@aws-sdk/middleware-recursion-detection': 3.887.0 + '@aws-sdk/middleware-user-agent': 3.890.0 + '@aws-sdk/region-config-resolver': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-endpoints': 3.890.0 + '@aws-sdk/util-user-agent-browser': 3.887.0 + '@aws-sdk/util-user-agent-node': 3.890.0 + '@smithy/config-resolver': 4.2.2 + '@smithy/core': 3.11.0 + '@smithy/fetch-http-handler': 5.2.1 + '@smithy/hash-node': 4.1.1 + '@smithy/invalid-dependency': 4.1.1 + '@smithy/middleware-content-length': 4.1.1 + '@smithy/middleware-endpoint': 4.2.2 + '@smithy/middleware-retry': 4.2.2 + '@smithy/middleware-serde': 4.1.1 + '@smithy/middleware-stack': 4.1.1 + '@smithy/node-config-provider': 4.2.2 + '@smithy/node-http-handler': 4.2.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + '@smithy/util-base64': 4.1.0 + '@smithy/util-body-length-browser': 4.1.0 + '@smithy/util-body-length-node': 4.1.0 + '@smithy/util-defaults-mode-browser': 4.1.2 + '@smithy/util-defaults-mode-node': 4.1.2 + '@smithy/util-endpoints': 3.1.2 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-retry': 4.1.1 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.890.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@aws-sdk/xml-builder': 3.887.0 + '@smithy/core': 3.11.0 + '@smithy/node-config-provider': 4.2.2 + '@smithy/property-provider': 4.1.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/signature-v4': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/util-base64': 4.1.0 + '@smithy/util-body-length-browser': 4.1.0 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-utf8': 4.1.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/property-provider': 4.1.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/fetch-http-handler': 5.2.1 + '@smithy/node-http-handler': 4.2.1 + '@smithy/property-provider': 4.1.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/util-stream': 4.3.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/credential-provider-env': 3.890.0 + '@aws-sdk/credential-provider-http': 3.890.0 + '@aws-sdk/credential-provider-process': 3.890.0 + '@aws-sdk/credential-provider-sso': 3.890.0 + '@aws-sdk/credential-provider-web-identity': 3.890.0 + '@aws-sdk/nested-clients': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/credential-provider-imds': 4.1.2 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.890.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.890.0 + '@aws-sdk/credential-provider-http': 3.890.0 + '@aws-sdk/credential-provider-ini': 3.890.0 + '@aws-sdk/credential-provider-process': 3.890.0 + '@aws-sdk/credential-provider-sso': 3.890.0 + '@aws-sdk/credential-provider-web-identity': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/credential-provider-imds': 4.1.2 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.890.0': + dependencies: + '@aws-sdk/client-sso': 3.890.0 + '@aws-sdk/core': 3.890.0 + '@aws-sdk/token-providers': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/nested-clients': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-host-header@3.887.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.887.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.887.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@aws/lambda-invoke-store': 0.0.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-arn-parser': 3.873.0 + '@smithy/core': 3.11.0 + '@smithy/node-config-provider': 4.2.2 + '@smithy/protocol-http': 5.2.1 + '@smithy/signature-v4': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/util-config-provider': 4.1.0 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-stream': 4.3.1 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-endpoints': 3.890.0 + '@smithy/core': 3.11.0 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.890.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.890.0 + '@aws-sdk/middleware-host-header': 3.887.0 + '@aws-sdk/middleware-logger': 3.887.0 + '@aws-sdk/middleware-recursion-detection': 3.887.0 + '@aws-sdk/middleware-user-agent': 3.890.0 + '@aws-sdk/region-config-resolver': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@aws-sdk/util-endpoints': 3.890.0 + '@aws-sdk/util-user-agent-browser': 3.887.0 + '@aws-sdk/util-user-agent-node': 3.890.0 + '@smithy/config-resolver': 4.2.2 + '@smithy/core': 3.11.0 + '@smithy/fetch-http-handler': 5.2.1 + '@smithy/hash-node': 4.1.1 + '@smithy/invalid-dependency': 4.1.1 + '@smithy/middleware-content-length': 4.1.1 + '@smithy/middleware-endpoint': 4.2.2 + '@smithy/middleware-retry': 4.2.2 + '@smithy/middleware-serde': 4.1.1 + '@smithy/middleware-stack': 4.1.1 + '@smithy/node-config-provider': 4.2.2 + '@smithy/node-http-handler': 4.2.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + '@smithy/util-base64': 4.1.0 + '@smithy/util-body-length-browser': 4.1.0 + '@smithy/util-body-length-node': 4.1.0 + '@smithy/util-defaults-mode-browser': 4.1.2 + '@smithy/util-defaults-mode-node': 4.1.2 + '@smithy/util-endpoints': 3.1.2 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-retry': 4.1.1 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.890.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/node-config-provider': 4.2.2 + '@smithy/types': 4.5.0 + '@smithy/util-config-provider': 4.1.0 + '@smithy/util-middleware': 4.1.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.890.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/protocol-http': 5.2.1 + '@smithy/signature-v4': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.890.0': + dependencies: + '@aws-sdk/core': 3.890.0 + '@aws-sdk/nested-clients': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.887.0': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/util-arn-parser@3.873.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.890.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + '@smithy/util-endpoints': 3.1.2 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.873.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.887.0': + dependencies: + '@aws-sdk/types': 3.887.0 + '@smithy/types': 4.5.0 + bowser: 2.12.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.890.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.890.0 + '@aws-sdk/types': 3.887.0 + '@smithy/node-config-provider': 4.2.2 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.887.0': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.0.1': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.27.7': {} + + '@babel/core@7.27.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.27.7 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.2.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.27.1': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.7) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.27.5(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.27.7(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) + '@babel/traverse': 7.28.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.27.7(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.27.7(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.7) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-display-name@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.27.5(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.27.7) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.7) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.27.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.27.2(@babel/core@7.27.7)': + dependencies: + '@babel/compat-data': 7.27.7 + '@babel/core': 7.27.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.7) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.7) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-block-scoping': 7.27.5(@babel/core@7.27.7) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-classes': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-destructuring': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-object-rest-spread': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-regenerator': 7.27.5(@babel/core@7.27.7) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.7) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.27.7) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.7) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.27.7) + core-js-compat: 3.45.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.4 + esutils: 2.0.3 + + '@babel/preset-react@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/runtime-corejs3@7.27.6': + dependencies: + core-js-pure: 3.43.0 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@balena/dockerignore@1.0.2': {} + + '@bcoe/v8-coverage@1.0.2': {} + + '@borewit/text-codec@0.1.1': {} + + '@colors/colors@1.5.0': + optional: true + + '@csstools/cascade-layer-name-parser@2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/postcss-cascade-layers@5.0.2(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-color-function@4.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-mix-function@3.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-mix-variadic-function-arguments@1.0.0(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-content-alt-text@2.0.6(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-exponential-functions@2.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-font-format-keywords@4.0.0(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-gamut-mapping@2.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-gradients-interpolation-method@5.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-hwb-function@4.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-ic-unit@4.0.2(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-initial@2.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-is-pseudo-class@5.0.3(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-light-dark-function@2.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-logical-float-and-clear@3.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overflow@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-resize@3.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-logical-viewport-units@3.0.4(postcss@8.5.6)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-media-minmax@2.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.5(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + '@csstools/postcss-nested-calc@4.0.0(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@4.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@4.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-progressive-custom-properties@4.1.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-random-function@2.0.1(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-relative-color-syntax@3.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-scope-pseudo-class@4.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-sign-functions@1.1.4(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-stepped-value-functions@4.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-text-decoration-shorthand@4.0.2(postcss@8.5.6)': + dependencies: + '@csstools/color-helpers': 5.1.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@4.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-unset-value@4.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/selector-resolve-nested@3.1.0(postcss-selector-parser@7.1.0)': + dependencies: + postcss-selector-parser: 7.1.0 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': + dependencies: + postcss-selector-parser: 7.1.0 + + '@csstools/utilities@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@discoveryjs/json-ext@0.5.7': {} + + '@docsearch/css@3.9.0': {} + + '@docsearch/react@3.9.0(@algolia/client-search@5.29.0)(@types/react@19.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.9(@algolia/client-search@5.29.0)(algoliasearch@5.29.0) + '@docsearch/css': 3.9.0 + algoliasearch: 5.29.0 + optionalDependencies: + '@types/react': 19.1.13 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@docusaurus/babel@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/core': 7.27.7 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.27.7) + '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.27.7) + '@babel/preset-env': 7.27.2(@babel/core@7.27.7) + '@babel/preset-react': 7.27.1(@babel/core@7.27.7) + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) + '@babel/runtime': 7.28.4 + '@babel/runtime-corejs3': 7.27.6 + '@babel/traverse': 7.28.4 + '@docusaurus/logger': 3.8.1 + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + babel-plugin-dynamic-import-node: 2.3.3 + fs-extra: 11.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/bundler@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@babel/core': 7.27.7 + '@docusaurus/babel': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/cssnano-preset': 3.8.1 + '@docusaurus/logger': 3.8.1 + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.100.2) + clean-css: 5.3.3 + copy-webpack-plugin: 11.0.0(webpack@5.100.2) + css-loader: 6.11.0(webpack@5.100.2) + css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.100.2) + cssnano: 6.1.2(postcss@8.5.6) + file-loader: 6.2.0(webpack@5.100.2) + html-minifier-terser: 7.2.0 + mini-css-extract-plugin: 2.9.2(webpack@5.100.2) + null-loader: 4.0.1(webpack@5.100.2) + postcss: 8.5.6 + postcss-loader: 7.3.4(postcss@8.5.6)(typescript@5.9.2)(webpack@5.100.2) + postcss-preset-env: 10.2.4(postcss@8.5.6) + terser-webpack-plugin: 5.3.14(webpack@5.100.2) + tslib: 2.8.1 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) + webpack: 5.100.2 + webpackbar: 6.0.1(webpack@5.100.2) + transitivePeerDependencies: + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - csso + - esbuild + - lightningcss + - react + - react-dom + - supports-color + - typescript + - uglify-js + - webpack-cli + + '@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/babel': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/bundler': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.1(@types/react@19.1.13)(react@18.3.1) + boxen: 6.2.1 + chalk: 4.1.2 + chokidar: 3.6.0 + cli-table3: 0.6.5 + combine-promises: 1.2.0 + commander: 5.1.0 + core-js: 3.43.0 + detect-port: 1.6.1 + escape-html: 1.0.3 + eta: 2.2.0 + eval: 0.1.8 + execa: 5.1.1 + fs-extra: 11.3.0 + html-tags: 3.3.1 + html-webpack-plugin: 5.6.3(webpack@5.100.2) + leven: 3.1.0 + lodash: 4.17.21 + open: 8.4.2 + p-map: 4.0.0 + prompts: 2.4.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' + react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' + react-loadable-ssr-addon-v5-slorber: 1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2) + react-router: 5.3.4(react@18.3.1) + react-router-config: 5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1) + react-router-dom: 5.3.4(react@18.3.1) + semver: 7.7.2 + serve-handler: 6.1.6 + tinypool: 1.1.1 + tslib: 2.8.1 + update-notifier: 6.0.2 + webpack: 5.100.2 + webpack-bundle-analyzer: 4.10.2 + webpack-dev-server: 4.15.2(webpack@5.100.2) + webpack-merge: 6.0.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/cssnano-preset@3.8.1': + dependencies: + cssnano-preset-advanced: 6.1.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-sort-media-queries: 5.2.0(postcss@8.5.6) + tslib: 2.8.1 + + '@docusaurus/logger@3.8.1': + dependencies: + chalk: 4.1.2 + tslib: 2.8.1 + + '@docusaurus/mdx-loader@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/logger': 3.8.1 + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@slorber/remark-comment': 1.0.0 + escape-html: 1.0.3 + estree-util-value-to-estree: 3.4.0 + file-loader: 6.2.0(webpack@5.100.2) + fs-extra: 11.3.0 + image-size: 2.0.2 + mdast-util-mdx: 3.0.0 + mdast-util-to-string: 4.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rehype-raw: 7.0.0 + remark-directive: 3.0.1 + remark-emoji: 4.0.1 + remark-frontmatter: 5.0.0 + remark-gfm: 4.0.1 + stringify-object: 3.3.0 + tslib: 2.8.1 + unified: 11.0.5 + unist-util-visit: 5.0.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) + vfile: 6.0.3 + webpack: 5.100.2 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/module-type-aliases@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/history': 4.7.11 + '@types/react': 19.1.13 + '@types/react-router-config': 5.0.11 + '@types/react-router-dom': 5.3.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' + react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/plugin-content-blog@3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + cheerio: 1.0.0-rc.12 + feed: 4.2.2 + fs-extra: 11.3.0 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + schema-dts: 1.1.5 + srcset: 4.0.0 + tslib: 2.8.1 + unist-util-visit: 5.0.0 + utility-types: 3.11.0 + webpack: 5.100.2 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/react-router-config': 5.0.11 + combine-promises: 1.2.0 + fs-extra: 11.3.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + schema-dts: 1.1.5 + tslib: 2.8.1 + utility-types: 3.11.0 + webpack: 5.100.2 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-content-pages@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + webpack: 5.100.2 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-css-cascade-layers@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - react + - react-dom + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-debug@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-json-view-lite: 2.4.1(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-google-analytics@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-google-gtag@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/gtag.js': 0.0.12 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-google-tag-manager@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-sitemap@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + sitemap: 7.1.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/plugin-svgr@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/webpack': 8.1.0(typescript@5.9.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + webpack: 5.100.2 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/preset-classic@3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-content-blog': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-content-pages': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-css-cascade-layers': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-debug': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-google-analytics': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-google-gtag': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-google-tag-manager': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-sitemap': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-svgr': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/theme-classic': 3.8.1(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-search-algolia': 3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2) + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@algolia/client-search' + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - '@types/react' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - search-insights + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/react-loadable@6.0.0(react@18.3.1)': + dependencies: + '@types/react': 19.1.13 + react: 18.3.1 + + '@docusaurus/theme-classic@3.8.1(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2)': + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-blog': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/plugin-content-pages': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.8.1 + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/react': 3.1.1(@types/react@19.1.13)(react@18.3.1) + clsx: 2.1.1 + copy-text-to-clipboard: 3.2.0 + infima: 0.2.0-alpha.45 + lodash: 4.17.21 + nprogress: 0.2.0 + postcss: 8.5.6 + prism-react-renderer: 2.4.1(react@18.3.1) + prismjs: 1.30.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router-dom: 5.3.4(react@18.3.1) + rtlcss: 4.3.0 + tslib: 2.8.1 + utility-types: 3.11.0 + transitivePeerDependencies: + - '@docusaurus/faster' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - '@types/react' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/theme-common@3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/mdx-loader': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/module-type-aliases': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/history': 4.7.11 + '@types/react': 19.1.13 + '@types/react-router-config': 5.0.11 + clsx: 2.1.1 + parse-numeric-range: 1.3.0 + prism-react-renderer: 2.4.1(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + utility-types: 3.11.0 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/theme-search-algolia@3.8.1(@algolia/client-search@5.29.0)(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(@types/react@19.1.13)(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.9.2)': + dependencies: + '@docsearch/react': 3.9.0(@algolia/client-search@5.29.0)(@types/react@19.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3) + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/logger': 3.8.1 + '@docusaurus/plugin-content-docs': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + '@docusaurus/theme-common': 3.8.1(@docusaurus/plugin-content-docs@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/theme-translations': 3.8.1 + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-validation': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + algoliasearch: 5.29.0 + algoliasearch-helper: 3.26.0(algoliasearch@5.29.0) + clsx: 2.1.1 + eta: 2.2.0 + fs-extra: 11.3.0 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + utility-types: 3.11.0 + transitivePeerDependencies: + - '@algolia/client-search' + - '@docusaurus/faster' + - '@mdx-js/react' + - '@parcel/css' + - '@rspack/core' + - '@swc/core' + - '@swc/css' + - '@types/react' + - acorn + - bufferutil + - csso + - debug + - esbuild + - lightningcss + - search-insights + - supports-color + - typescript + - uglify-js + - utf-8-validate + - webpack-cli + + '@docusaurus/theme-translations@3.8.1': + dependencies: + fs-extra: 11.3.0 + tslib: 2.8.1 + + '@docusaurus/tsconfig@3.8.1': {} + + '@docusaurus/types@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@types/history': 4.7.11 + '@types/react': 19.1.13 + commander: 5.1.0 + joi: 17.13.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-helmet-async: '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' + utility-types: 3.11.0 + webpack: 5.100.2 + webpack-merge: 5.10.0 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/utils-common@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/utils-validation@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/logger': 3.8.1 + '@docusaurus/utils': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + fs-extra: 11.3.0 + joi: 17.13.3 + js-yaml: 4.1.0 + lodash: 4.17.21 + tslib: 2.8.1 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli + + '@docusaurus/utils@3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@docusaurus/logger': 3.8.1 + '@docusaurus/types': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@docusaurus/utils-common': 3.8.1(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + escape-string-regexp: 4.0.0 + execa: 5.1.1 + file-loader: 6.2.0(webpack@5.100.2) + fs-extra: 11.3.0 + github-slugger: 1.5.0 + globby: 11.1.0 + gray-matter: 4.0.3 + jiti: 1.21.7 + js-yaml: 4.1.0 + lodash: 4.17.21 + micromatch: 4.0.8 + p-queue: 6.6.2 + prompts: 2.4.2 + resolve-pathname: 3.0.0 + tslib: 2.8.1 + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2) + utility-types: 3.11.0 + webpack: 5.100.2 + transitivePeerDependencies: + - '@swc/core' + - acorn + - esbuild + - react + - react-dom + - supports-color + - uglify-js + - webpack-cli + + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.19.12': + optional: true + + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.19.12': + optional: true + + '@esbuild/android-arm64@0.25.9': + optional: true + + '@esbuild/android-arm@0.19.12': + optional: true + + '@esbuild/android-arm@0.25.9': + optional: true + + '@esbuild/android-x64@0.19.12': + optional: true + + '@esbuild/android-x64@0.25.9': + optional: true + + '@esbuild/darwin-arm64@0.19.12': + optional: true + + '@esbuild/darwin-arm64@0.25.9': + optional: true + + '@esbuild/darwin-x64@0.19.12': + optional: true + + '@esbuild/darwin-x64@0.25.9': + optional: true + + '@esbuild/freebsd-arm64@0.19.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.9': + optional: true + + '@esbuild/freebsd-x64@0.19.12': + optional: true + + '@esbuild/freebsd-x64@0.25.9': + optional: true + + '@esbuild/linux-arm64@0.19.12': + optional: true + + '@esbuild/linux-arm64@0.25.9': + optional: true + + '@esbuild/linux-arm@0.19.12': + optional: true + + '@esbuild/linux-arm@0.25.9': + optional: true + + '@esbuild/linux-ia32@0.19.12': + optional: true + + '@esbuild/linux-ia32@0.25.9': + optional: true + + '@esbuild/linux-loong64@0.19.12': + optional: true + + '@esbuild/linux-loong64@0.25.9': + optional: true + + '@esbuild/linux-mips64el@0.19.12': + optional: true + + '@esbuild/linux-mips64el@0.25.9': + optional: true + + '@esbuild/linux-ppc64@0.19.12': + optional: true + + '@esbuild/linux-ppc64@0.25.9': + optional: true + + '@esbuild/linux-riscv64@0.19.12': + optional: true + + '@esbuild/linux-riscv64@0.25.9': + optional: true + + '@esbuild/linux-s390x@0.19.12': + optional: true + + '@esbuild/linux-s390x@0.25.9': + optional: true + + '@esbuild/linux-x64@0.19.12': + optional: true + + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': + optional: true + + '@esbuild/netbsd-x64@0.19.12': + optional: true + + '@esbuild/netbsd-x64@0.25.9': + optional: true + + '@esbuild/openbsd-arm64@0.25.9': + optional: true + + '@esbuild/openbsd-x64@0.19.12': + optional: true + + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.19.12': + optional: true + + '@esbuild/sunos-x64@0.25.9': + optional: true + + '@esbuild/win32-arm64@0.19.12': + optional: true + + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.19.12': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.19.12': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0(jiti@2.5.1))': + dependencies: + eslint: 9.36.0(jiti@2.5.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.36.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@faker-js/faker@10.0.0': {} + + '@fig/complete-commander@3.2.0(commander@11.1.0)': + dependencies: + commander: 11.1.0 + prettier: 3.6.2 + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@formatjs/ecma402-abstract@2.3.4': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.1 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.2': + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/icu-skeleton-parser': 1.8.14 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.14': + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.1': + dependencies: + tslib: 2.8.1 + + '@golevelup/nestjs-discovery@4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + lodash: 4.17.21 + + '@grpc/grpc-js@1.13.4': + dependencies: + '@grpc/proto-loader': 0.7.15 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@hapi/hoek@9.3.0': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/sharp-darwin-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.0 + optional: true + + '@img/sharp-darwin-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + optional: true + + '@img/sharp-linux-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.0 + optional: true + + '@img/sharp-linux-arm@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.0 + optional: true + + '@img/sharp-linux-ppc64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.0 + optional: true + + '@img/sharp-linux-s390x@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.0 + optional: true + + '@img/sharp-linux-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + + '@img/sharp-wasm32@0.34.3': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + + '@img/sharp-win32-arm64@0.34.3': + optional: true + + '@img/sharp-win32-ia32@0.34.3': + optional: true + + '@img/sharp-win32-x64@0.34.3': + optional: true + + '@immich/ui@0.29.0(@internationalized/date@3.8.2)(svelte@5.38.10)': + dependencies: + '@mdi/js': 7.4.47 + bits-ui: 2.9.8(@internationalized/date@3.8.2)(svelte@5.38.10) + simple-icons: 15.15.0 + svelte: 5.38.10 + tailwind-merge: 3.3.1 + tailwind-variants: 3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.13) + tailwindcss: 4.1.13 + transitivePeerDependencies: + - '@internationalized/date' + + '@inquirer/checkbox@4.2.1(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.8) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/confirm@5.1.15(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/core@10.1.15(@types/node@22.18.8)': + dependencies: + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.8) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/editor@4.2.17(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/expand@4.0.17(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/external-editor@1.0.2(@types/node@22.18.8)': + dependencies: + chardet: 2.1.0 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/figures@1.0.13': {} + + '@inquirer/input@4.2.1(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/number@3.0.17(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/password@4.0.17(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/prompts@7.3.2(@types/node@22.18.8)': + dependencies: + '@inquirer/checkbox': 4.2.1(@types/node@22.18.8) + '@inquirer/confirm': 5.1.15(@types/node@22.18.8) + '@inquirer/editor': 4.2.17(@types/node@22.18.8) + '@inquirer/expand': 4.0.17(@types/node@22.18.8) + '@inquirer/input': 4.2.1(@types/node@22.18.8) + '@inquirer/number': 3.0.17(@types/node@22.18.8) + '@inquirer/password': 4.0.17(@types/node@22.18.8) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.8) + '@inquirer/search': 3.1.0(@types/node@22.18.8) + '@inquirer/select': 4.3.1(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/prompts@7.8.0(@types/node@22.18.8)': + dependencies: + '@inquirer/checkbox': 4.2.1(@types/node@22.18.8) + '@inquirer/confirm': 5.1.15(@types/node@22.18.8) + '@inquirer/editor': 4.2.17(@types/node@22.18.8) + '@inquirer/expand': 4.0.17(@types/node@22.18.8) + '@inquirer/input': 4.2.1(@types/node@22.18.8) + '@inquirer/number': 3.0.17(@types/node@22.18.8) + '@inquirer/password': 4.0.17(@types/node@22.18.8) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.8) + '@inquirer/search': 3.1.0(@types/node@22.18.8) + '@inquirer/select': 4.3.1(@types/node@22.18.8) + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/rawlist@4.1.5(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/type': 3.0.8(@types/node@22.18.8) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/search@3.1.0(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.8) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/select@4.3.1(@types/node@22.18.8)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@22.18.8) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.8) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.18.8 + + '@inquirer/type@3.0.8(@types/node@22.18.8)': + optionalDependencies: + '@types/node': 22.18.8 + + '@internationalized/date@3.8.2': + dependencies: + '@swc/helpers': 0.5.17 + + '@ioredis/commands@1.3.0': {} + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.18.8 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@koa/cors@5.0.0': + dependencies: + vary: 1.1.2 + + '@koa/router@14.0.0': + dependencies: + debug: 4.4.3 + http-errors: 2.0.0 + koa-compose: 4.1.0 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + '@koddsson/eslint-plugin-tscompat@0.2.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@mdn/browser-compat-data': 6.0.27 + '@typescript-eslint/type-utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + browserslist: 4.25.3 + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@lukeed/csprng@1.1.0': {} + + '@mapbox/geojson-rewind@0.5.2': + dependencies: + get-stream: 6.0.1 + minimist: 1.2.8 + + '@mapbox/geojson-types@1.0.2': {} + + '@mapbox/jsonlint-lines-primitives@2.0.2': {} + + '@mapbox/mapbox-gl-rtl-text@0.2.3(mapbox-gl@1.13.3)': + dependencies: + mapbox-gl: 1.13.3 + + '@mapbox/mapbox-gl-supported@1.5.0(mapbox-gl@1.13.3)': + dependencies: + mapbox-gl: 1.13.3 + + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.1.0 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': + dependencies: + detect-libc: 2.1.0 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0(encoding@0.1.13) + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@mapbox/point-geometry@0.1.0': {} + + '@mapbox/point-geometry@1.1.0': {} + + '@mapbox/tiny-sdf@1.2.5': {} + + '@mapbox/tiny-sdf@2.0.7': {} + + '@mapbox/unitbezier@0.0.0': {} + + '@mapbox/unitbezier@0.0.1': {} + + '@mapbox/vector-tile@1.3.1': + dependencies: + '@mapbox/point-geometry': 0.1.0 + + '@mapbox/vector-tile@2.0.4': + dependencies: + '@mapbox/point-geometry': 1.1.0 + '@types/geojson': 7946.0.16 + pbf: 4.0.1 + + '@mapbox/whoots-js@3.1.0': {} + + '@maplibre/maplibre-gl-style-spec@23.3.0': + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/unitbezier': 0.0.1 + json-stringify-pretty-compact: 4.0.0 + minimist: 1.2.8 + quickselect: 3.0.0 + rw: 1.3.3 + tinyqueue: 3.0.0 + + '@maplibre/vt-pbf@4.0.3': + dependencies: + '@mapbox/point-geometry': 1.1.0 + '@mapbox/vector-tile': 2.0.4 + '@types/geojson-vt': 3.2.5 + '@types/supercluster': 7.1.3 + geojson-vt: 4.0.2 + pbf: 4.0.1 + supercluster: 8.0.1 + + '@mdi/js@7.4.47': {} + + '@mdi/react@1.6.1': + dependencies: + prop-types: 15.8.1 + + '@mdn/browser-compat-data@5.7.6': {} + + '@mdn/browser-compat-data@6.0.27': {} + + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + + '@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.1.13 + react: 18.3.1 + + '@microsoft/tsdoc@0.15.1': {} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@namnode/store@0.1.0': {} + + '@nestjs/bull-shared@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + tslib: 2.8.1 + + '@nestjs/bullmq@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.58.5)': + dependencies: + '@nestjs/bull-shared': 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + bullmq: 5.58.5 + tslib: 2.8.1 + + '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.8)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.8)(chokidar@4.0.3) + '@inquirer/prompts': 7.8.0(@types/node@22.18.8) + '@nestjs/schematics': 11.0.7(chokidar@4.0.3)(typescript@5.8.3) + ansis: 4.1.0 + chokidar: 4.0.3 + cli-table3: 0.6.5 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.8.3)(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))) + glob: 11.0.3 + node-emoji: 1.11.0 + ora: 5.4.1 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.2.0 + typescript: 5.8.3 + webpack: 5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)) + webpack-node-externals: 3.0.0 + optionalDependencies: + '@swc/core': 1.13.5(@swc/helpers@0.5.17) + transitivePeerDependencies: + - '@types/node' + - esbuild + - uglify-js + - webpack-cli + + '@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + file-type: 21.0.0 + iterare: 1.2.1 + load-esm: 1.0.2 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.14.2 + transitivePeerDependencies: + - supports-color + + '@nestjs/core@11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nuxt/opencollective': 0.4.1 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 8.2.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + '@nestjs/platform-express': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/websockets': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-socket.io@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + + '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + reflect-metadata: 0.2.2 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.14.2 + + '@nestjs/platform-express@11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cors: 2.8.5 + express: 5.1.0 + multer: 2.0.2 + path-to-regexp: 8.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@nestjs/platform-socket.io@11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.6)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-socket.io@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + rxjs: 7.8.2 + socket.io: 4.8.1 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@nestjs/schedule@6.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cron: 4.3.0 + + '@nestjs/schematics@11.0.7(chokidar@4.0.3)(typescript@5.8.3)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + comment-json: 4.2.5 + jsonc-parser: 3.3.1 + pluralize: 8.0.0 + typescript: 5.8.3 + transitivePeerDependencies: + - chokidar + + '@nestjs/schematics@11.0.7(chokidar@4.0.3)(typescript@5.9.2)': + dependencies: + '@angular-devkit/core': 19.2.15(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) + comment-json: 4.2.5 + jsonc-parser: 3.3.1 + pluralize: 8.0.0 + typescript: 5.9.2 + transitivePeerDependencies: + - chokidar + + '@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)': + dependencies: + '@microsoft/tsdoc': 0.15.1 + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.1.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) + js-yaml: 4.1.0 + lodash: 4.17.21 + path-to-regexp: 8.2.0 + reflect-metadata: 0.2.2 + swagger-ui-dist: 5.21.0 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.14.2 + + '@nestjs/testing@11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@11.1.6)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + tslib: 2.8.1 + optionalDependencies: + '@nestjs/platform-express': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + + '@nestjs/websockets@11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-socket.io@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + iterare: 1.2.1 + object-hash: 3.0.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + optionalDependencies: + '@nestjs/platform-socket.io': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.6)(rxjs@7.8.2) + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.2 + + '@nuxt/opencollective@0.4.1': + dependencies: + consola: 3.4.2 + + '@oazapfts/runtime@1.0.4': {} + + '@opentelemetry/api-logs@0.205.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/exporter-logs-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-prometheus@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-grpc@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-http@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-zipkin@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/host-metrics@0.36.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + systeminformation: 5.23.8 + + '@opentelemetry/instrumentation-http@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.53.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.0 + '@opentelemetry/semantic-conventions': 1.37.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.51.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.58.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/sql-common': 0.41.0(@opentelemetry/api@1.9.0) + '@types/pg': 8.15.5 + '@types/pg-pool': 2.0.6 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + import-in-the-middle: 1.14.2 + require-in-the-middle: 7.5.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/otlp-exporter-base@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.205.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/propagator-b3@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-jaeger@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/redis-common@0.38.0': {} + + '@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/sdk-logs@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-node@0.205.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.205.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-prometheus': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-http': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-zipkin': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-b3': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-jaeger': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.205.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.37.0 + + '@opentelemetry/sdk-trace-node@2.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.1.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/semantic-conventions@1.37.0': {} + + '@opentelemetry/sql-common@0.41.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@photo-sphere-viewer/core@5.14.0': + dependencies: + three: 0.179.1 + + '@photo-sphere-viewer/equirectangular-video-adapter@5.14.0(@photo-sphere-viewer/core@5.14.0)(@photo-sphere-viewer/video-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0))': + dependencies: + '@photo-sphere-viewer/core': 5.14.0 + '@photo-sphere-viewer/video-plugin': 5.14.0(@photo-sphere-viewer/core@5.14.0) + three: 0.180.0 + + '@photo-sphere-viewer/resolution-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0)(@photo-sphere-viewer/settings-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0))': + dependencies: + '@photo-sphere-viewer/core': 5.14.0 + '@photo-sphere-viewer/settings-plugin': 5.14.0(@photo-sphere-viewer/core@5.14.0) + + '@photo-sphere-viewer/settings-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0)': + dependencies: + '@photo-sphere-viewer/core': 5.14.0 + + '@photo-sphere-viewer/video-plugin@5.14.0(@photo-sphere-viewer/core@5.14.0)': + dependencies: + '@photo-sphere-viewer/core': 5.14.0 + three: 0.180.0 + + '@photostructure/tz-lookup@11.2.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@playwright/test@1.55.0': + dependencies: + playwright: 1.55.0 + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.3.1': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@polka/url@1.0.0-next.29': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@react-email/body@0.1.0(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/button@0.2.0(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/code-block@0.1.0(react@19.1.1)': + dependencies: + prismjs: 1.30.0 + react: 19.1.1 + + '@react-email/code-inline@0.0.5(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/column@0.0.13(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/components@0.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@react-email/body': 0.1.0(react@19.1.1) + '@react-email/button': 0.2.0(react@19.1.1) + '@react-email/code-block': 0.1.0(react@19.1.1) + '@react-email/code-inline': 0.0.5(react@19.1.1) + '@react-email/column': 0.0.13(react@19.1.1) + '@react-email/container': 0.0.15(react@19.1.1) + '@react-email/font': 0.0.9(react@19.1.1) + '@react-email/head': 0.0.12(react@19.1.1) + '@react-email/heading': 0.0.15(react@19.1.1) + '@react-email/hr': 0.0.11(react@19.1.1) + '@react-email/html': 0.0.11(react@19.1.1) + '@react-email/img': 0.0.11(react@19.1.1) + '@react-email/link': 0.0.12(react@19.1.1) + '@react-email/markdown': 0.0.15(react@19.1.1) + '@react-email/preview': 0.0.13(react@19.1.1) + '@react-email/render': 1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@react-email/row': 0.0.12(react@19.1.1) + '@react-email/section': 0.0.16(react@19.1.1) + '@react-email/tailwind': 1.2.2(react@19.1.1) + '@react-email/text': 0.1.5(react@19.1.1) + react: 19.1.1 + transitivePeerDependencies: + - react-dom + + '@react-email/container@0.0.15(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/font@0.0.9(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/head@0.0.12(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/heading@0.0.15(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/hr@0.0.11(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/html@0.0.11(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/img@0.0.11(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/link@0.0.12(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/markdown@0.0.15(react@19.1.1)': + dependencies: + md-to-react-email: 5.0.5(react@19.1.1) + react: 19.1.1 + + '@react-email/preview@0.0.13(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/render@1.2.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + html-to-text: 9.0.5 + prettier: 3.6.2 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-promise-suspense: 0.3.4 + + '@react-email/row@0.0.12(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/section@0.0.16(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/tailwind@1.2.2(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@react-email/text@0.1.5(react@19.1.1)': + dependencies: + react: 19.1.1 + + '@rollup/pluginutils@5.3.0(rollup@4.50.1)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/rollup-android-arm-eabi@4.50.1': + optional: true + + '@rollup/rollup-android-arm64@4.50.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.50.1': + optional: true + + '@rollup/rollup-darwin-x64@4.50.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.50.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.50.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.50.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.50.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.50.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.1': + optional: true + + '@scarf/scarf@1.4.0': {} + + '@selderee/plugin-htmlparser2@0.11.0': + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + + '@sideway/formula@3.0.1': {} + + '@sideway/pinpoint@2.0.0': {} + + '@sinclair/typebox@0.27.8': {} + + '@sindresorhus/is@4.6.0': {} + + '@sindresorhus/is@5.6.0': {} + + '@slorber/react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + invariant: 2.2.4 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + + '@slorber/remark-comment@1.0.0': + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + + '@smithy/abort-controller@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/config-resolver@4.2.2': + dependencies: + '@smithy/node-config-provider': 4.2.2 + '@smithy/types': 4.5.0 + '@smithy/util-config-provider': 4.1.0 + '@smithy/util-middleware': 4.1.1 + tslib: 2.8.1 + + '@smithy/core@3.11.0': + dependencies: + '@smithy/middleware-serde': 4.1.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + '@smithy/util-base64': 4.1.0 + '@smithy/util-body-length-browser': 4.1.0 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-stream': 4.3.1 + '@smithy/util-utf8': 4.1.0 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/credential-provider-imds@4.1.2': + dependencies: + '@smithy/node-config-provider': 4.2.2 + '@smithy/property-provider': 4.1.1 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.2.1': + dependencies: + '@smithy/protocol-http': 5.2.1 + '@smithy/querystring-builder': 4.1.1 + '@smithy/types': 4.5.0 + '@smithy/util-base64': 4.1.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + '@smithy/util-buffer-from': 4.1.0 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.1.1': + dependencies: + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.2.2': + dependencies: + '@smithy/core': 3.11.0 + '@smithy/middleware-serde': 4.1.1 + '@smithy/node-config-provider': 4.2.2 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + '@smithy/url-parser': 4.1.1 + '@smithy/util-middleware': 4.1.1 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.2.2': + dependencies: + '@smithy/node-config-provider': 4.2.2 + '@smithy/protocol-http': 5.2.1 + '@smithy/service-error-classification': 4.1.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-retry': 4.1.1 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/middleware-serde@4.1.1': + dependencies: + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.2.2': + dependencies: + '@smithy/property-provider': 4.1.1 + '@smithy/shared-ini-file-loader': 4.2.0 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.2.1': + dependencies: + '@smithy/abort-controller': 4.1.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/querystring-builder': 4.1.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.2.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + '@smithy/util-uri-escape': 4.1.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + + '@smithy/shared-ini-file-loader@4.2.0': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.2.1': + dependencies: + '@smithy/is-array-buffer': 4.1.0 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + '@smithy/util-hex-encoding': 4.1.0 + '@smithy/util-middleware': 4.1.1 + '@smithy/util-uri-escape': 4.1.0 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.6.2': + dependencies: + '@smithy/core': 3.11.0 + '@smithy/middleware-endpoint': 4.2.2 + '@smithy/middleware-stack': 4.1.1 + '@smithy/protocol-http': 5.2.1 + '@smithy/types': 4.5.0 + '@smithy/util-stream': 4.3.1 + tslib: 2.8.1 + + '@smithy/types@4.5.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.1.1': + dependencies: + '@smithy/querystring-parser': 4.1.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.1.0': + dependencies: + '@smithy/util-buffer-from': 4.1.0 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.1.0': + dependencies: + '@smithy/is-array-buffer': 4.1.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.1.2': + dependencies: + '@smithy/property-provider': 4.1.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + bowser: 2.12.1 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.1.2': + dependencies: + '@smithy/config-resolver': 4.2.2 + '@smithy/credential-provider-imds': 4.1.2 + '@smithy/node-config-provider': 4.2.2 + '@smithy/property-provider': 4.1.1 + '@smithy/smithy-client': 4.6.2 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.1.2': + dependencies: + '@smithy/node-config-provider': 4.2.2 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.1.1': + dependencies: + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.1.1': + dependencies: + '@smithy/service-error-classification': 4.1.1 + '@smithy/types': 4.5.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.3.1': + dependencies: + '@smithy/fetch-http-handler': 5.2.1 + '@smithy/node-http-handler': 4.2.1 + '@smithy/types': 4.5.0 + '@smithy/util-base64': 4.1.0 + '@smithy/util-buffer-from': 4.1.0 + '@smithy/util-hex-encoding': 4.1.0 + '@smithy/util-utf8': 4.1.0 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.1.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.1.0': + dependencies: + '@smithy/util-buffer-from': 4.1.0 + tslib: 2.8.1 + + '@socket.io/component-emitter@3.1.2': {} + + '@socket.io/redis-adapter@8.3.0(socket.io-adapter@2.5.5)': + dependencies: + debug: 4.3.7 + notepack.io: 3.0.1 + socket.io-adapter: 2.5.5 + uid2: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + dependencies: + '@sveltejs/kit': 2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + + '@sveltejs/enhanced-img@0.8.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.50.1)(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + magic-string: 0.30.19 + sharp: 0.34.3 + svelte: 5.38.10 + svelte-parse-markup: 0.1.5(svelte@5.38.10) + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.50.1) + zimmerframe: 1.1.2 + transitivePeerDependencies: + - rollup + - supports-color + + '@sveltejs/kit@2.38.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.3.2 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.19 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.2 + svelte: 5.38.10 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + optionalDependencies: + '@opentelemetry/api': 1.9.0 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + debug: 4.4.3 + svelte: 5.38.10 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + debug: 4.4.3 + deepmerge: 4.3.1 + magic-string: 0.30.19 + svelte: 5.38.10 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + transitivePeerDependencies: + - supports-color + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + + '@svgr/babel-preset@8.1.0(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.27.7) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.27.7) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.27.7) + + '@svgr/core@8.1.0(typescript@5.9.2)': + dependencies: + '@babel/core': 7.27.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.27.7) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.2) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.28.4 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': + dependencies: + '@babel/core': 7.27.7 + '@svgr/babel-preset': 8.1.0(@babel/core@7.27.7) + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.2) + cosmiconfig: 8.3.6(typescript@5.9.2) + deepmerge: 4.3.1 + svgo: 3.3.2 + transitivePeerDependencies: + - typescript + + '@svgr/webpack@8.1.0(typescript@5.9.2)': + dependencies: + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.27.7) + '@babel/preset-env': 7.27.2(@babel/core@7.27.7) + '@babel/preset-react': 7.27.1(@babel/core@7.27.7) + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) + transitivePeerDependencies: + - supports-color + - typescript + + '@swc/core-darwin-arm64@1.13.5': + optional: true + + '@swc/core-darwin-x64@1.13.5': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.13.5': + optional: true + + '@swc/core-linux-arm64-gnu@1.13.5': + optional: true + + '@swc/core-linux-arm64-musl@1.13.5': + optional: true + + '@swc/core-linux-x64-gnu@1.13.5': + optional: true + + '@swc/core-linux-x64-musl@1.13.5': + optional: true + + '@swc/core-win32-arm64-msvc@1.13.5': + optional: true + + '@swc/core-win32-ia32-msvc@1.13.5': + optional: true + + '@swc/core-win32-x64-msvc@1.13.5': + optional: true + + '@swc/core@1.13.5(@swc/helpers@0.5.17)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.13.5 + '@swc/core-darwin-x64': 1.13.5 + '@swc/core-linux-arm-gnueabihf': 1.13.5 + '@swc/core-linux-arm64-gnu': 1.13.5 + '@swc/core-linux-arm64-musl': 1.13.5 + '@swc/core-linux-x64-gnu': 1.13.5 + '@swc/core-linux-x64-musl': 1.13.5 + '@swc/core-win32-arm64-msvc': 1.13.5 + '@swc/core-win32-ia32-msvc': 1.13.5 + '@swc/core-win32-x64-msvc': 1.13.5 + '@swc/helpers': 0.5.17 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + + '@szmarczak/http-timer@5.0.1': + dependencies: + defer-to-connect: 2.0.1 + + '@tailwindcss/node@4.1.13': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.13 + + '@tailwindcss/oxide-android-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide@4.1.13': + dependencies: + detect-libc: 2.1.0 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/vite@4.1.13(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 + tailwindcss: 4.1.13 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.4 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.8.0': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/svelte@5.2.8(svelte@5.38.10)(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@testing-library/dom': 10.4.0 + svelte: 5.38.10 + optionalDependencies: + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': + dependencies: + '@testing-library/dom': 10.4.0 + + '@tokenizer/inflate@0.2.7': + dependencies: + debug: 4.4.3 + fflate: 0.8.2 + token-types: 6.1.1 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@tootallnate/once@2.0.0': + optional: true + + '@trysound/sax@0.2.0': {} + + '@turf/boolean-point-in-polygon@7.2.0': + dependencies: + '@turf/helpers': 7.2.0 + '@turf/invariant': 7.2.0 + '@types/geojson': 7946.0.16 + point-in-polygon-hao: 1.2.4 + tslib: 2.8.1 + + '@turf/helpers@7.2.0': + dependencies: + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/invariant@7.2.0': + dependencies: + '@turf/helpers': 7.2.0 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@types/accepts@1.3.7': + dependencies: + '@types/node': 22.18.8 + + '@types/archiver@6.0.3': + dependencies: + '@types/readdir-glob': 1.1.5 + + '@types/aria-query@5.0.4': {} + + '@types/async-lock@1.4.2': {} + + '@types/bcrypt@6.0.0': + dependencies: + '@types/node': 22.18.8 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.18.8 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 22.18.8 + + '@types/braces@3.0.5': {} + + '@types/byte-size@8.1.2': {} + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/chrome@0.0.328': + dependencies: + '@types/filesystem': 0.0.36 + '@types/har-format': 1.2.16 + + '@types/chromecast-caf-sender@1.0.11': + dependencies: + '@types/chrome': 0.0.328 + + '@types/cli-progress@3.11.6': + dependencies: + '@types/node': 22.18.8 + + '@types/compression@1.8.1': + dependencies: + '@types/express': 5.0.3 + '@types/node': 22.18.8 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 5.0.6 + '@types/node': 22.18.8 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.18.8 + + '@types/content-disposition@0.5.9': {} + + '@types/cookie-parser@1.4.9(@types/express@5.0.3)': + dependencies: + '@types/express': 5.0.3 + + '@types/cookie@0.6.0': {} + + '@types/cookiejar@2.1.5': {} + + '@types/cookies@0.9.1': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 5.0.3 + '@types/keygrip': 1.0.6 + '@types/node': 22.18.8 + + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.18.8 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/docker-modem@3.0.6': + dependencies: + '@types/node': 22.18.8 + '@types/ssh2': 1.15.5 + + '@types/dockerode@3.3.42': + dependencies: + '@types/docker-modem': 3.0.6 + '@types/node': 22.18.8 + '@types/ssh2': 1.15.5 + + '@types/dom-to-image@2.6.7': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@4.19.6': + dependencies: + '@types/node': 22.18.8 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express-serve-static-core@5.0.6': + dependencies: + '@types/node': 22.18.8 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@4.17.23': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.8 + + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.0.6 + '@types/serve-static': 1.15.8 + + '@types/filesystem@0.0.36': + dependencies: + '@types/filewriter': 0.0.33 + + '@types/filewriter@0.0.33': {} + + '@types/fluent-ffmpeg@2.1.27': + dependencies: + '@types/node': 22.18.8 + + '@types/geojson-vt@3.2.5': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/geojson@7946.0.16': {} + + '@types/gtag.js@0.0.12': {} + + '@types/har-format@1.2.16': {} + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/history@4.7.11': {} + + '@types/html-minifier-terser@6.1.0': {} + + '@types/http-assert@1.5.6': {} + + '@types/http-cache-semantics@4.0.4': {} + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.16': + dependencies: + '@types/node': 22.18.8 + + '@types/inquirer@8.2.11': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.2 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/js-yaml@4.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/justified-layout@4.1.4': {} + + '@types/keygrip@1.0.6': {} + + '@types/koa-compose@3.2.8': + dependencies: + '@types/koa': 3.0.0 + + '@types/koa@3.0.0': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.9 + '@types/cookies': 0.9.1 + '@types/http-assert': 1.5.6 + '@types/http-errors': 2.0.5 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 22.18.8 + + '@types/leaflet@1.9.20': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + + '@types/luxon@3.6.2': {} + + '@types/luxon@3.7.1': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/methods@1.1.4': {} + + '@types/micromatch@4.0.9': + dependencies: + '@types/braces': 3.0.5 + + '@types/mime@1.3.5': {} + + '@types/mock-fs@4.13.4': + dependencies: + '@types/node': 22.18.8 + + '@types/ms@2.1.0': {} + + '@types/multer@2.0.0': + dependencies: + '@types/express': 5.0.3 + + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 22.18.8 + + '@types/node@17.0.45': {} + + '@types/node@18.19.126': + dependencies: + undici-types: 5.26.5 + + '@types/node@20.19.2': + dependencies: + undici-types: 6.21.0 + + '@types/node@22.18.8': + dependencies: + undici-types: 6.21.0 + + '@types/node@24.5.1': + dependencies: + undici-types: 7.12.0 + optional: true + + '@types/nodemailer@7.0.1': + dependencies: + '@aws-sdk/client-sesv2': 3.890.0 + '@types/node': 22.18.8 + transitivePeerDependencies: + - aws-crt + + '@types/oidc-provider@9.5.0': + dependencies: + '@types/keygrip': 1.0.6 + '@types/koa': 3.0.0 + '@types/node': 22.18.8 + + '@types/parse5@5.0.3': {} + + '@types/pg-pool@2.0.6': + dependencies: + '@types/pg': 8.15.5 + + '@types/pg@8.15.5': + dependencies: + '@types/node': 22.18.8 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + + '@types/picomatch@4.0.2': {} + + '@types/pngjs@6.0.5': + dependencies: + '@types/node': 22.18.8 + + '@types/prismjs@1.26.5': {} + + '@types/qrcode@1.5.5': + dependencies: + '@types/node': 22.18.8 + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-router-config@5.0.11': + dependencies: + '@types/history': 4.7.11 + '@types/react': 19.1.13 + '@types/react-router': 5.1.20 + + '@types/react-router-dom@5.3.3': + dependencies: + '@types/history': 4.7.11 + '@types/react': 19.1.13 + '@types/react-router': 5.1.20 + + '@types/react-router@5.1.20': + dependencies: + '@types/history': 4.7.11 + '@types/react': 19.1.13 + + '@types/react@19.1.13': + dependencies: + csstype: 3.1.3 + + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 22.18.8 + + '@types/retry@0.12.0': {} + + '@types/sanitize-html@2.16.0': + dependencies: + htmlparser2: 8.0.2 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 22.18.8 + + '@types/semver@7.7.1': {} + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.18.8 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 5.0.3 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.18.8 + '@types/send': 0.17.5 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 22.18.8 + + '@types/ssh2-streams@0.1.12': + dependencies: + '@types/node': 22.18.8 + + '@types/ssh2@0.5.52': + dependencies: + '@types/node': 22.18.8 + '@types/ssh2-streams': 0.1.12 + + '@types/ssh2@1.15.5': + dependencies: + '@types/node': 18.19.126 + + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 22.18.8 + form-data: 4.0.4 + + '@types/supercluster@7.1.3': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + + '@types/through@0.0.33': + dependencies: + '@types/node': 22.18.8 + + '@types/ua-parser-js@0.7.39': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/uuid@9.0.8': {} + + '@types/validator@13.15.3': {} + + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.18.8 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.45.0 + '@typescript-eslint/type-utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.45.0 + eslint: 9.36.0(jiti@2.5.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.45.0 + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.45.0 + debug: 4.4.3 + eslint: 9.36.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.45.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.2) + '@typescript-eslint/types': 8.45.0 + debug: 4.4.3 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.45.0': + dependencies: + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/visitor-keys': 8.45.0 + + '@typescript-eslint/tsconfig-utils@8.45.0(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + + '@typescript-eslint/type-utils@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + debug: 4.4.3 + eslint: 9.36.0(jiti@2.5.1) + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.45.0': {} + + '@typescript-eslint/typescript-estree@8.45.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/project-service': 8.45.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.2) + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/visitor-keys': 8.45.0 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.45.0 + '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2) + eslint: 9.36.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.45.0': + dependencies: + '@typescript-eslint/types': 8.45.0 + eslint-visitor-keys: 4.2.1 + + '@ungap/structured-clone@1.3.0': {} + + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + ast-v8-to-istanbul: 0.3.3 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.19 + magicast: 0.3.5 + std-env: 3.9.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + ast-v8-to-istanbul: 0.3.3 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.19 + magicast: 0.3.5 + std-env: 3.9.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.3 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.1.4 + tinyrainbow: 2.0.0 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@zoom-image/core@0.41.0': + dependencies: + '@namnode/store': 0.1.0 + + '@zoom-image/svelte@0.3.4(svelte@5.38.10)': + dependencies: + '@zoom-image/core': 0.41.0 + svelte: 5.38.10 + + abab@2.0.6: + optional: true + + abbrev@1.1.1: {} + + abbrev@3.0.1: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-globals@7.0.1: + dependencies: + acorn: 8.15.0 + acorn-walk: 8.3.4 + optional: true + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + address@1.2.2: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + algoliasearch-helper@3.26.0(algoliasearch@5.29.0): + dependencies: + '@algolia/events': 4.0.1 + algoliasearch: 5.29.0 + + algoliasearch@5.29.0: + dependencies: + '@algolia/client-abtesting': 5.29.0 + '@algolia/client-analytics': 5.29.0 + '@algolia/client-common': 5.29.0 + '@algolia/client-insights': 5.29.0 + '@algolia/client-personalization': 5.29.0 + '@algolia/client-query-suggestions': 5.29.0 + '@algolia/client-search': 5.29.0 + '@algolia/ingestion': 1.29.0 + '@algolia/monitoring': 1.29.0 + '@algolia/recommend': 5.29.0 + '@algolia/requester-browser-xhr': 5.29.0 + '@algolia/requester-fetch': 5.29.0 + '@algolia/requester-node-http': 5.29.0 + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + ansis@4.1.0: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + append-field@1.0.0: {} + + aproba@2.0.0: {} + + archiver-utils@5.0.2: + dependencies: + glob: 10.4.5 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + array-flatten@1.1.1: {} + + array-source@0.0.4: {} + + array-timsort@1.0.3: {} + + array-union@2.1.0: {} + + asap@2.0.6: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assertion-error@2.0.1: {} + + ast-metadata-inferer@0.8.1: + dependencies: + '@mdn/browser-compat-data': 5.7.6 + + ast-v8-to-istanbul@0.3.3: + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + + astring@1.9.0: {} + + async-lock@1.4.1: {} + + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + + async@0.2.10: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + autocomplete.js@0.37.1: + dependencies: + immediate: 3.3.0 + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + caniuse-lite: 1.0.30001735 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + axobject-query@4.1.0: {} + + b4a@1.6.7: {} + + babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.100.2): + dependencies: + '@babel/core': 7.27.7 + find-cache-dir: 4.0.0 + schema-utils: 4.3.2 + webpack: 5.100.2 + + babel-plugin-dynamic-import-node@2.3.3: + dependencies: + object.assign: 4.1.7 + + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.27.7): + dependencies: + '@babel/compat-data': 7.27.7 + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.7): + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) + core-js-compat: 3.45.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.27.7): + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + bail@1.0.5: {} + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + bare-events@2.6.1: + optional: true + + bare-fs@4.2.0: + dependencies: + bare-events: 2.6.1 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.6.1) + optional: true + + bare-os@3.6.1: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.1 + optional: true + + bare-stream@2.7.0(bare-events@2.6.1): + dependencies: + streamx: 2.22.1 + optionalDependencies: + bare-events: 2.6.1 + optional: true + + base64-js@1.5.1: {} + + base64id@2.0.0: {} + + batch-cluster@13.0.0: {} + + batch@0.6.1: {} + + bcp-47-match@1.0.3: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + bcrypt@6.0.0: + dependencies: + node-addon-api: 8.5.0 + node-gyp-build: 4.8.4 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bits-ui@2.9.8(@internationalized/date@3.8.2)(svelte@5.38.10): + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/dom': 1.7.4 + '@internationalized/date': 3.8.2 + esm-env: 1.2.2 + runed: 0.29.2(svelte@5.38.10) + svelte: 5.38.10 + svelte-toolbelt: 0.9.3(svelte@5.38.10) + tabbable: 6.2.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.1 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.3.0: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + bowser@2.12.1: {} + + boxen@6.2.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.3: + dependencies: + caniuse-lite: 1.0.30001735 + electron-to-chromium: 1.5.207 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.3) + + buffer-crc32@1.0.0: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buildcheck@0.0.6: + optional: true + + builtin-modules@5.0.0: {} + + bullmq@5.58.5: + dependencies: + cron-parser: 4.9.0 + ioredis: 5.7.0 + msgpackr: 1.11.5 + node-abort-controller: 3.1.1 + semver: 7.7.2 + tslib: 2.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + byline@5.0.0: {} + + byte-size@9.0.1: {} + + bytes@3.0.0: {} + + bytes@3.1.2: {} + + cac@6.7.14: {} + + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 10.4.3 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.3 + ssri: 12.0.0 + tar: 7.4.3 + unique-filename: 4.0.0 + + cacheable-lookup@7.0.0: {} + + cacheable-request@10.2.14: + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.2 + responselike: 3.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelcase@7.0.1: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.25.3 + caniuse-lite: 1.0.30001735 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001735: {} + + canvas@2.11.2: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + nan: 2.23.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + canvas@2.11.2(encoding@0.1.13): + dependencies: + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + nan: 2.23.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + ccount@2.0.1: {} + + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.4 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + change-case@5.4.4: {} + + char-regex@1.0.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chardet@2.1.0: {} + + check-error@2.1.1: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0-rc.12: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + htmlparser2: 8.0.2 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@1.1.4: {} + + chownr@2.0.0: {} + + chownr@3.0.0: {} + + chrome-trace-event@1.0.4: {} + + ci-info@3.9.0: {} + + ci-info@4.3.0: {} + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + cjs-module-lexer@1.4.3: {} + + class-transformer@0.5.1: {} + + class-validator@0.14.2: + dependencies: + '@types/validator': 13.15.3 + libphonenumber-js: 1.12.9 + validator: 13.15.15 + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + clean-regexp@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + clean-stack@2.2.0: {} + + cli-boxes@3.0.0: {} + + cli-color@2.0.4: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + memoizee: 0.4.17 + timers-ext: 0.1.8 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-progress@3.12.0: + dependencies: + string-width: 4.2.3 + + cli-spinners@2.9.2: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-width@3.0.0: {} + + cli-width@4.1.0: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone@1.0.4: {} + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: {} + + collapse-white-space@2.1.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color-support@1.1.3: {} + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combine-promises@1.2.0: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + + commander@10.0.1: {} + + commander@11.1.0: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@5.1.0: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + comment-json@4.2.5: + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + + common-path-prefix@3.0.0: {} + + component-emitter@1.3.1: {} + + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@2.0.0: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + + confbox@0.2.2: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@6.0.0: + dependencies: + dot-prop: 6.0.1 + graceful-fs: 4.2.11 + unique-string: 3.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 5.1.0 + + connect-history-api-fallback@2.0.0: {} + + consola@3.4.2: {} + + console-control-strings@1.1.0: {} + + content-disposition@0.5.2: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-parser@1.4.7: + dependencies: + cookie: 0.7.2 + cookie-signature: 1.0.6 + + cookie-signature@1.0.6: {} + + cookie-signature@1.2.2: {} + + cookie@0.6.0: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + cookie@1.0.2: {} + + cookiejar@2.1.4: {} + + cookies@0.9.1: + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + + copy-text-to-clipboard@3.2.0: {} + + copy-webpack-plugin@11.0.0(webpack@5.100.2): + dependencies: + fast-glob: 3.3.3 + glob-parent: 6.0.2 + globby: 13.2.2 + normalize-path: 3.0.0 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + webpack: 5.100.2 + + core-js-compat@3.45.0: + dependencies: + browserslist: 4.25.3 + + core-js-pure@3.43.0: {} + + core-js@3.43.0: {} + + core-util-is@1.0.3: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@8.3.6(typescript@5.8.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.8.3 + + cosmiconfig@8.3.6(typescript@5.9.2): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.2 + + cpu-features@0.0.10: + dependencies: + buildcheck: 0.0.6 + nan: 2.23.0 + optional: true + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + cron-parser@4.9.0: + dependencies: + luxon: 3.7.2 + + cron@4.3.0: + dependencies: + '@types/luxon': 3.6.2 + luxon: 3.6.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@4.0.0: + dependencies: + type-fest: 1.4.0 + + css-blank-pseudo@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + css-declaration-sorter@7.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-has-pseudo@7.0.2(postcss@8.5.6): + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + css-loader@6.11.0(webpack@5.100.2): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) + postcss-value-parser: 4.2.0 + semver: 7.7.2 + optionalDependencies: + webpack: 5.100.2 + + css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.100.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + cssnano: 6.1.2(postcss@8.5.6) + jest-worker: 29.7.0 + postcss: 8.5.6 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + webpack: 5.100.2 + optionalDependencies: + clean-css: 5.3.3 + + css-prefers-color-scheme@10.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-selector-parser@1.4.1: {} + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + css.escape@1.5.1: {} + + csscolorparser@1.0.3: {} + + cssdb@8.3.1: {} + + cssesc@3.0.0: {} + + cssnano-preset-advanced@6.1.2(postcss@8.5.6): + dependencies: + autoprefixer: 10.4.21(postcss@8.5.6) + browserslist: 4.25.3 + cssnano-preset-default: 6.1.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-discard-unused: 6.0.5(postcss@8.5.6) + postcss-merge-idents: 6.0.3(postcss@8.5.6) + postcss-reduce-idents: 6.0.3(postcss@8.5.6) + postcss-zindex: 6.0.2(postcss@8.5.6) + + cssnano-preset-default@6.1.2(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + css-declaration-sorter: 7.2.0(postcss@8.5.6) + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 9.0.1(postcss@8.5.6) + postcss-colormin: 6.1.0(postcss@8.5.6) + postcss-convert-values: 6.1.0(postcss@8.5.6) + postcss-discard-comments: 6.0.2(postcss@8.5.6) + postcss-discard-duplicates: 6.0.3(postcss@8.5.6) + postcss-discard-empty: 6.0.3(postcss@8.5.6) + postcss-discard-overridden: 6.0.2(postcss@8.5.6) + postcss-merge-longhand: 6.0.5(postcss@8.5.6) + postcss-merge-rules: 6.1.1(postcss@8.5.6) + postcss-minify-font-values: 6.1.0(postcss@8.5.6) + postcss-minify-gradients: 6.0.3(postcss@8.5.6) + postcss-minify-params: 6.1.0(postcss@8.5.6) + postcss-minify-selectors: 6.0.4(postcss@8.5.6) + postcss-normalize-charset: 6.0.2(postcss@8.5.6) + postcss-normalize-display-values: 6.0.2(postcss@8.5.6) + postcss-normalize-positions: 6.0.2(postcss@8.5.6) + postcss-normalize-repeat-style: 6.0.2(postcss@8.5.6) + postcss-normalize-string: 6.0.2(postcss@8.5.6) + postcss-normalize-timing-functions: 6.0.2(postcss@8.5.6) + postcss-normalize-unicode: 6.1.0(postcss@8.5.6) + postcss-normalize-url: 6.0.2(postcss@8.5.6) + postcss-normalize-whitespace: 6.0.2(postcss@8.5.6) + postcss-ordered-values: 6.0.2(postcss@8.5.6) + postcss-reduce-initial: 6.1.0(postcss@8.5.6) + postcss-reduce-transforms: 6.0.2(postcss@8.5.6) + postcss-svgo: 6.0.3(postcss@8.5.6) + postcss-unique-selectors: 6.0.4(postcss@8.5.6) + + cssnano-utils@4.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano@6.1.2(postcss@8.5.6): + dependencies: + cssnano-preset-default: 6.1.2(postcss@8.5.6) + lilconfig: 3.1.3 + postcss: 8.5.6 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + cssom@0.3.8: + optional: true + + cssom@0.5.0: + optional: true + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + optional: true + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + optional: true + + csstype@3.1.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + optional: true + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + optional: true + + debounce@1.2.1: {} + + debounce@2.2.0: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + decimal.js@10.6.0: {} + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + optional: true + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-eql@5.0.2: {} + + deep-equal@1.0.1: {} + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-gateway@6.0.3: + dependencies: + execa: 5.1.1 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + defer-to-connect@2.0.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: {} + + denque@2.1.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destroy@1.2.0: {} + + detect-europe-js@0.1.2: {} + + detect-libc@2.1.0: {} + + detect-node@2.1.0: {} + + detect-port@1.6.1: + dependencies: + address: 1.2.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + devalue@5.3.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diacritics@1.3.0: {} + + didyoumean@1.2.2: {} + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + direction@1.0.4: {} + + discontinuous-range@1.0.0: {} + + dlv@1.1.3: {} + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + docker-compose@1.2.0: + dependencies: + yaml: 2.8.1 + + docker-modem@5.0.6: + dependencies: + debug: 4.4.3 + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.16.0 + transitivePeerDependencies: + - supports-color + + dockerode@4.0.7: + dependencies: + '@balena/dockerignore': 1.0.2 + '@grpc/grpc-js': 1.13.4 + '@grpc/proto-loader': 0.7.15 + docker-modem: 5.0.6 + protobufjs: 7.5.4 + tar-fs: 2.1.3 + uuid: 10.0.0 + transitivePeerDependencies: + - supports-color + + docusaurus-lunr-search@3.6.0(@docusaurus/core@3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@docusaurus/core': 3.8.1(@mdx-js/react@3.1.1(@types/react@19.1.13)(react@18.3.1))(acorn@8.15.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.2) + autocomplete.js: 0.37.1 + clsx: 2.1.1 + gauge: 3.0.2 + hast-util-select: 4.0.2 + hast-util-to-text: 2.0.1 + hogan.js: 3.0.2 + lunr: 2.3.9 + lunr-languages: 1.14.0 + mark.js: 8.11.1 + minimatch: 3.1.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rehype-parse: 7.0.1 + to-vfile: 6.1.0 + unified: 9.2.2 + unist-util-is: 4.1.0 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom-to-image@2.6.0: {} + + domelementtype@2.3.0: {} + + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + optional: true + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@6.0.1: + dependencies: + is-obj: 2.0.0 + + dotenv@17.2.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + earcut@2.2.4: {} + + earcut@3.0.2: {} + + eastasianwidth@0.2.0: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.207: {} + + emoji-regex@10.5.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojilib@2.4.0: {} + + emojis-list@3.0.0: {} + + emoticon@4.1.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 22.18.8 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + entities@2.2.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: {} + + err-code@2.0.3: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + es6-weak-map@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.15.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 + + esbuild@0.19.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escalade@3.2.0: {} + + escape-goat@4.0.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + optional: true + + eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.5.1)): + dependencies: + eslint: 9.36.0(jiti@2.5.1) + + eslint-plugin-compat@6.0.2(eslint@9.36.0(jiti@2.5.1)): + dependencies: + '@mdn/browser-compat-data': 5.7.6 + ast-metadata-inferer: 0.8.1 + browserslist: 4.25.3 + caniuse-lite: 1.0.30001735 + eslint: 9.36.0(jiti@2.5.1) + find-up: 5.0.0 + globals: 15.15.0 + lodash.memoize: 4.1.2 + semver: 7.7.2 + + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.5.1)))(eslint@9.36.0(jiti@2.5.1))(prettier@3.6.2): + dependencies: + eslint: 9.36.0(jiti@2.5.1) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + '@types/eslint': 9.6.1 + eslint-config-prettier: 10.1.8(eslint@9.36.0(jiti@2.5.1)) + + eslint-plugin-svelte@3.12.4(eslint@9.36.0(jiti@2.5.1))(svelte@5.38.10): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1)) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.36.0(jiti@2.5.1) + esutils: 2.0.3 + globals: 16.4.0 + known-css-properties: 0.37.0 + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6) + postcss-safe-parser: 7.0.1(postcss@8.5.6) + semver: 7.7.2 + svelte-eslint-parser: 1.3.3(svelte@5.38.10) + optionalDependencies: + svelte: 5.38.10 + transitivePeerDependencies: + - ts-node + + eslint-plugin-unicorn@60.0.0(eslint@9.36.0(jiti@2.5.1)): + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1)) + '@eslint/plugin-kit': 0.3.5 + change-case: 5.4.4 + ci-info: 4.3.0 + clean-regexp: 1.0.0 + core-js-compat: 3.45.0 + eslint: 9.36.0(jiti@2.5.1) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.4.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.12.0 + semver: 7.7.2 + strip-indent: 4.0.0 + + eslint-plugin-unicorn@61.0.2(eslint@9.36.0(jiti@2.5.1)): + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1)) + '@eslint/plugin-kit': 0.3.5 + change-case: 5.4.4 + ci-info: 4.3.0 + clean-regexp: 1.0.0 + core-js-compat: 3.45.0 + eslint: 9.36.0(jiti@2.5.1) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.4.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.12.0 + semver: 7.7.2 + strip-indent: 4.0.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.36.0(jiti@2.5.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.5.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.36.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.5.1 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.4 + + estree-util-value-to-estree@3.4.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + eta@2.2.0: {} + + eta@3.5.0: {} + + etag@1.8.1: {} + + eval@0.1.8: + dependencies: + '@types/node': 22.18.8 + require-like: 0.1.2 + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exiftool-vendored.exe@13.0.0: + optional: true + + exiftool-vendored.pl@13.0.1: {} + + exiftool-vendored@28.8.0: + dependencies: + '@photostructure/tz-lookup': 11.2.0 + '@types/luxon': 3.7.1 + batch-cluster: 13.0.0 + exiftool-vendored.pl: 13.0.1 + he: 1.2.0 + luxon: 3.7.2 + optionalDependencies: + exiftool-vendored.exe: 13.0.0 + + expect-type@1.2.1: {} + + exponential-backoff@3.1.2: {} + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + exsolve@1.0.7: {} + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend@3.0.2: {} + + fabric@6.7.1: + optionalDependencies: + canvas: 2.11.2 + jsdom: 20.0.3(canvas@2.11.2) + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + factory.ts@1.4.2: + dependencies: + clone-deep: 4.0.1 + source-map-support: 0.5.21 + + fast-deep-equal@2.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.0.6: {} + + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.1 + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fault@2.0.1: + dependencies: + format: 0.2.2 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + feed@4.2.2: + dependencies: + xml-js: 1.6.11 + + fflate@0.8.2: {} + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-loader@6.2.0(webpack@5.100.2): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.100.2 + + file-source@0.6.1: + dependencies: + stream-source: 0.3.5 + + file-type@21.0.0: + dependencies: + '@tokenizer/inflate': 0.2.7 + strtok3: 10.3.4 + token-types: 6.1.1 + uint8array-extras: 1.4.1 + transitivePeerDependencies: + - supports-color + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-up-simple@1.0.1: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatted@3.3.3: {} + + fluent-ffmpeg@2.1.3: + dependencies: + async: 0.2.10 + which: 1.3.1 + + follow-redirects@1.15.9: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.8.3)(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))): + dependencies: + '@babel/code-frame': 7.27.1 + chalk: 4.1.2 + chokidar: 4.0.3 + cosmiconfig: 8.3.6(typescript@5.8.3) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.7.2 + tapable: 2.2.2 + typescript: 5.8.3 + webpack: 5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)) + + form-data-encoder@2.1.4: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + format@0.2.2: {} + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + + forwarded-parse@2.1.2: {} + + forwarded@0.2.0: {} + + fraction.js@4.3.7: {} + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + fs-monkey@1.1.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + gensync@1.0.0-beta.2: {} + + geo-coordinates-parser@1.7.4: {} + + geo-tz@8.1.4: + dependencies: + '@turf/boolean-point-in-polygon': 7.2.0 + '@turf/helpers': 7.2.0 + geobuf: 3.0.2 + pbf: 3.3.0 + + geobuf@3.0.2: + dependencies: + concat-stream: 2.0.0 + pbf: 3.3.0 + shapefile: 0.6.6 + + geojson-vt@3.2.1: {} + + geojson-vt@4.0.2: {} + + geojson@0.5.0: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-port@7.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + github-slugger@1.5.0: {} + + gl-matrix@3.4.4: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-dirs@3.0.1: + dependencies: + ini: 2.0.0 + + globals@11.12.0: {} + + globals@14.0.0: {} + + globals@15.15.0: {} + + globals@16.4.0: {} + + globalyzer@0.1.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@13.2.2: + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 4.0.0 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + got@12.6.1: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + grid-index@1.1.0: {} + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + handle-thing@2.0.1: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + happy-dom@18.0.1: + dependencies: + '@types/node': 20.19.2 + '@types/whatwg-mimetype': 3.0.2 + whatwg-mimetype: 3.0.0 + + has-flag@4.0.0: {} + + has-own-prop@2.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: {} + + has-yarn@3.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-from-parse5@6.0.1: + dependencies: + '@types/parse5': 5.0.3 + hastscript: 6.0.0 + property-information: 5.6.0 + vfile: 4.2.1 + vfile-location: 3.2.0 + web-namespaces: 1.1.4 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-has-property@1.0.4: {} + + hast-util-is-element@1.1.0: {} + + hast-util-parse-selector@2.2.5: {} + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-select@4.0.2: + dependencies: + bcp-47-match: 1.0.3 + comma-separated-tokens: 1.0.8 + css-selector-parser: 1.4.1 + direction: 1.0.4 + hast-util-has-property: 1.0.4 + hast-util-is-element: 1.1.0 + hast-util-to-string: 1.0.4 + hast-util-whitespace: 1.0.4 + not: 0.1.0 + nth-check: 2.1.1 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + unist-util-visit: 2.0.3 + zwitch: 1.0.5 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@1.0.4: {} + + hast-util-to-text@2.0.1: + dependencies: + hast-util-is-element: 1.1.0 + repeat-string: 1.6.1 + unist-util-find-after: 3.0.0 + + hast-util-whitespace@1.0.4: {} + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + he@1.2.0: {} + + history@4.10.1: + dependencies: + '@babel/runtime': 7.28.4 + loose-envify: 1.4.0 + resolve-pathname: 3.0.0 + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + value-equal: 1.0.1 + + hogan.js@3.0.2: + dependencies: + mkdirp: 0.3.0 + nopt: 1.0.10 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + optional: true + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + optional: true + + html-entities@2.6.0: {} + + html-escaper@2.0.2: {} + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.43.1 + + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.43.1 + + html-tags@3.3.1: {} + + html-to-text@9.0.5: + dependencies: + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 + + html-void-elements@3.0.0: {} + + html-webpack-plugin@5.6.3(webpack@5.100.2): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.17.21 + pretty-error: 4.0.0 + tapable: 2.2.2 + optionalDependencies: + webpack: 5.100.2 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-assert@1.5.0: + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + + http-cache-semantics@4.2.0: {} + + http-deceiver@1.2.7: {} + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + http-proxy-middleware@2.0.9(@types/express@4.17.23): + dependencies: + '@types/http-proxy': 1.17.16 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + optionalDependencies: + '@types/express': 4.17.23 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.9 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http2-wrapper@2.2.1: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + i18n-iso-countries@7.14.0: + dependencies: + diacritics: 1.3.0 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + image-size@2.0.2: {} + + imagetools-core@8.0.0: {} + + immediate@3.3.0: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-in-the-middle@1.14.2: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + import-lazy@4.0.0: {} + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + indent-string@5.0.0: {} + + infima@0.2.0-alpha.45: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@2.0.0: {} + + inline-style-parser@0.2.4: {} + + inquirer@8.2.7(@types/node@22.18.8): + dependencies: + '@inquirer/external-editor': 1.0.2(@types/node@22.18.8) + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.2 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + transitivePeerDependencies: + - '@types/node' + + internmap@2.0.3: {} + + intl-messageformat@10.7.16: + dependencies: + '@formatjs/ecma402-abstract': 2.3.4 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.2 + tslib: 2.8.1 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + ioredis@5.7.0: + dependencies: + '@ioredis/commands': 1.3.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + ip-address@10.0.1: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.2.0: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-buffer@2.0.5: {} + + is-builtin-module@5.0.0: + dependencies: + builtin-modules: 5.0.0 + + is-ci@3.0.1: + dependencies: + ci-info: 3.9.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-decimal@2.0.1: {} + + is-docker@2.2.1: {} + + is-extendable@0.1.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-installed-globally@0.4.0: + dependencies: + global-dirs: 3.0.1 + is-path-inside: 3.0.3 + + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + + is-npm@6.0.0: {} + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-obj@2.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: + optional: true + + is-promise@2.2.2: {} + + is-promise@4.0.0: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + is-regexp@1.0.0: {} + + is-standalone-pwa@0.1.1: {} + + is-stream@2.0.1: {} + + is-typedarray@1.0.0: {} + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-yarn-global@0.4.1: {} + + isarray@0.0.1: {} + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + isobject@3.0.1: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterare@1.2.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.18.8 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.18.8 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@29.7.0: + dependencies: + '@types/node': 22.18.8 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jiti@1.21.7: {} + + jiti@2.4.2: {} + + jiti@2.5.1: {} + + joi@17.13.3: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + + jose@5.10.0: {} + + jose@6.1.0: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@20.0.3(canvas@2.11.2): + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.4 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.22 + parse5: 7.3.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.18.3 + xml-name-validator: 4.0.0 + optionalDependencies: + canvas: 2.11.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + + jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)): + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.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.22 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 2.11.2(encoding@0.1.13) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + + jsdom@26.1.0(canvas@2.11.2): + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.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.22 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 2.11.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + + jsesc@3.0.2: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-pretty-compact@4.0.0: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + just-compare@2.3.0: {} + + justified-layout@4.1.0: {} + + kdbush@3.0.0: {} + + kdbush@4.0.2: {} + + keygrip@1.1.0: + dependencies: + tsscmp: 1.0.6 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + known-css-properties@0.37.0: {} + + koa-compose@4.1.0: {} + + koa@3.0.1: + dependencies: + accepts: 1.3.8 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.9.1 + delegates: 1.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 2.0.0 + koa-compose: 4.1.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + + kysely-postgres-js@2.0.0(kysely@0.28.2)(postgres@3.4.7): + dependencies: + kysely: 0.28.2 + postgres: 3.4.7 + + kysely@0.28.2: {} + + latest-version@7.0.0: + dependencies: + package-json: 8.1.1 + + launch-editor@2.10.0: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + leac@0.6.0: {} + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + libphonenumber-js@1.12.9: {} + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.1.0 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lilconfig@2.1.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-esm@1.0.2: {} + + load-tsconfig@0.2.5: {} + + loader-runner@4.3.0: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + locate-character@3.0.0: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.debounce@4.0.8: {} + + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + long@5.3.2: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.1.4: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowercase-keys@3.0.0: {} + + lru-cache@10.4.3: {} + + lru-cache@11.2.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-queue@0.1.0: + dependencies: + es5-ext: 0.10.64 + + lunr-languages@1.14.0: {} + + lunr@2.3.9: {} + + luxon@3.6.1: {} + + luxon@3.7.2: {} + + lz-string@1.5.0: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + source-map-js: 1.2.1 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + 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 + transitivePeerDependencies: + - supports-color + + mapbox-gl@1.13.3: + dependencies: + '@mapbox/geojson-rewind': 0.5.2 + '@mapbox/geojson-types': 1.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/mapbox-gl-supported': 1.5.0(mapbox-gl@1.13.3) + '@mapbox/point-geometry': 0.1.0 + '@mapbox/tiny-sdf': 1.2.5 + '@mapbox/unitbezier': 0.0.0 + '@mapbox/vector-tile': 1.3.1 + '@mapbox/whoots-js': 3.1.0 + csscolorparser: 1.0.3 + earcut: 2.2.4 + geojson-vt: 3.2.1 + gl-matrix: 3.4.4 + grid-index: 1.1.0 + murmurhash-js: 1.0.0 + pbf: 3.3.0 + potpack: 1.0.2 + quickselect: 2.0.0 + rw: 1.3.3 + supercluster: 7.1.5 + tinyqueue: 2.0.3 + vt-pbf: 3.1.3 + + maplibre-gl@5.7.1: + dependencies: + '@mapbox/geojson-rewind': 0.5.2 + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/point-geometry': 1.1.0 + '@mapbox/tiny-sdf': 2.0.7 + '@mapbox/unitbezier': 0.0.1 + '@mapbox/vector-tile': 2.0.4 + '@mapbox/whoots-js': 3.1.0 + '@maplibre/maplibre-gl-style-spec': 23.3.0 + '@maplibre/vt-pbf': 4.0.3 + '@types/geojson': 7946.0.16 + '@types/geojson-vt': 3.2.5 + '@types/supercluster': 7.1.3 + earcut: 3.0.2 + geojson-vt: 4.0.2 + gl-matrix: 3.4.4 + kdbush: 4.0.2 + murmurhash-js: 1.0.0 + pbf: 4.0.1 + potpack: 2.1.0 + quickselect: 3.0.0 + supercluster: 8.0.1 + tinyqueue: 3.0.0 + + mark.js@8.11.1: {} + + markdown-extensions@2.0.0: {} + + markdown-table@2.0.0: + dependencies: + repeat-string: 1.6.1 + + markdown-table@3.0.4: {} + + marked@7.0.4: {} + + math-intrinsics@1.1.0: {} + + md-to-react-email@5.0.5(react@19.1.1): + dependencies: + marked: 7.0.4 + react: 19.1.1 + + mdast-util-directive@3.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-frontmatter@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + escape-string-regexp: 5.0.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-extension-frontmatter: 2.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + memoizee@0.4.17: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.8 + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@3.0.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-frontmatter@2.0.0: + dependencies: + fault: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-factory-space@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-types: 1.1.0 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@1.2.0: + dependencies: + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@1.1.0: {} + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@1.1.0: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.33.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.18: + dependencies: + mime-db: 1.33.0 + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mimic-response@2.1.0: + optional: true + + mimic-response@3.1.0: {} + + mimic-response@4.0.0: {} + + min-indent@1.0.1: {} + + mini-css-extract-plugin@2.9.2(webpack@5.100.2): + dependencies: + schema-utils: 4.3.2 + tapable: 2.2.2 + webpack: 5.100.2 + + minimalistic-assert@1.0.1: {} + + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 3.0.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp-classic@0.5.3: {} + + mkdirp@0.3.0: {} + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + mkdirp@3.0.1: {} + + mnemonist@0.40.3: + dependencies: + obliterator: 2.0.5 + + mock-fs@5.5.0: {} + + module-details-from-path@1.0.4: {} + + moo@0.5.2: {} + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.5: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multer@2.0.2: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 2.0.0 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + murmurhash-js@1.0.0: {} + + mute-stream@0.0.8: {} + + mute-stream@2.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nan@2.23.0: + optional: true + + nanoid@3.3.11: {} + + nanoid@5.1.5: {} + + natural-compare@1.4.0: {} + + nearley@2.20.1: + dependencies: + commander: 2.20.3 + moo: 0.5.2 + railroad-diagrams: 1.0.0 + randexp: 0.4.6 + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + nest-commander@3.19.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.8)(typescript@5.9.2): + dependencies: + '@fig/complete-commander': 3.2.0(commander@11.1.0) + '@golevelup/nestjs-discovery': 4.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@types/inquirer': 8.2.11 + commander: 11.1.0 + cosmiconfig: 8.3.6(typescript@5.9.2) + inquirer: 8.2.7(@types/node@22.18.8) + transitivePeerDependencies: + - '@types/node' + - typescript + + nestjs-cls@5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2): + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + + nestjs-kysely@3.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2): + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + kysely: 0.28.2 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + + nestjs-otel@7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6): + dependencies: + '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/host-metrics': 0.36.0(@opentelemetry/api@1.9.0) + response-time: 2.3.4 + tslib: 2.8.1 + + next-tick@1.1.0: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abort-controller@3.1.1: {} + + node-addon-api@4.3.0: {} + + node-addon-api@8.5.0: {} + + node-emoji@1.11.0: + dependencies: + lodash: 4.17.21 + + node-emoji@2.2.0: + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + optional: true + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-forge@1.3.1: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.0 + optional: true + + node-gyp-build@4.8.4: {} + + node-gyp@11.4.2: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.2 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.2 + tar: 7.4.3 + tinyglobby: 0.2.15 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + + node-releases@2.0.19: {} + + nodemailer@7.0.7: {} + + nopt@1.0.10: + dependencies: + abbrev: 1.1.1 + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@8.0.2: {} + + not@0.1.0: {} + + notepack.io@3.0.1: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + null-loader@4.0.1(webpack@5.100.2): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.100.2 + + nwsapi@2.2.22: + optional: true + + nypm@0.6.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.3.0 + tinyexec: 0.3.2 + + oauth4webapi@3.8.1: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + obliterator@2.0.5: {} + + obuf@1.1.2: {} + + oidc-provider@9.5.1: + dependencies: + '@koa/cors': 5.0.0 + '@koa/router': 14.0.0 + debug: 4.4.3 + eta: 3.5.0 + jose: 6.1.0 + jsesc: 3.1.0 + koa: 3.0.1 + nanoid: 5.1.5 + quick-lru: 7.2.0 + raw-body: 3.0.1 + transitivePeerDependencies: + - supports-color + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + opener@1.5.2: {} + + openid-client@6.8.0: + dependencies: + jose: 6.1.0 + oauth4webapi: 3.8.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ora@8.2.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + p-cancelable@3.0.0: {} + + p-finally@1.0.0: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-map@7.0.3: {} + + p-queue@6.6.2: + dependencies: + eventemitter3: 4.0.7 + p-timeout: 3.2.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-json@8.1.1: + dependencies: + got: 12.6.1 + registry-auth-token: 5.1.0 + registry-url: 6.0.1 + semver: 7.7.2 + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-numeric-range@1.3.0: {} + + parse-srcset@1.0.2: {} + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5@6.0.1: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseley@0.12.1: + dependencies: + leac: 0.6.0 + peberminta: 0.9.0 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-is-inside@1.0.2: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.2.1 + minipass: 7.1.2 + + path-source@0.1.3: + dependencies: + array-source: 0.0.4 + file-source: 0.6.1 + + path-to-regexp@0.1.12: {} + + path-to-regexp@1.9.0: + dependencies: + isarray: 0.0.1 + + path-to-regexp@3.3.0: {} + + path-to-regexp@8.2.0: {} + + path-to-regexp@8.3.0: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + pbf@3.3.0: + dependencies: + ieee754: 1.2.1 + resolve-protobuf-schema: 2.1.0 + + pbf@4.0.1: + dependencies: + resolve-protobuf-schema: 2.1.0 + + peberminta@0.9.0: {} + + pg-cloudflare@1.2.7: + optional: true + + pg-connection-string@2.9.1: {} + + pg-int8@1.0.1: {} + + pg-pool@3.10.1(pg@8.16.3): + dependencies: + pg: 8.16.3 + + pg-protocol@1.10.3: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.16.3: + dependencies: + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.2.7 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + picomatch@4.0.3: {} + + pify@2.3.0: {} + + pirates@4.0.7: {} + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + + playwright-core@1.55.0: {} + + playwright@1.55.0: + dependencies: + playwright-core: 1.55.0 + optionalDependencies: + fsevents: 2.3.2 + + pluralize@8.0.0: {} + + pmtiles@3.2.1: + dependencies: + '@types/leaflet': 1.9.20 + fflate: 0.8.2 + + pmtiles@4.3.0: + dependencies: + fflate: 0.8.2 + + pngjs@5.0.0: {} + + pngjs@7.0.0: {} + + point-in-polygon-hao@1.2.4: + dependencies: + robust-predicates: 3.0.2 + + postcss-attribute-case-insensitive@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-calc@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-clamp@4.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-functional-notation@7.0.10(postcss@8.5.6): + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-color-hex-alpha@10.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-rebeccapurple@10.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-colormin@6.1.0(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-convert-values@6.1.0(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-media@11.0.6(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + postcss-custom-properties@14.0.6(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-selectors@8.0.5(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-dir-pseudo-class@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-discard-comments@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-duplicates@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-empty@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-overridden@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-unused@6.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-double-position-gradients@6.0.2(postcss@8.5.6): + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-focus-visible@10.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-focus-within@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-font-variant@5.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-gap-properties@6.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-image-set-function@7.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-js@4.1.0(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-lab-function@7.0.10(postcss@8.5.6): + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-load-config@3.1.4(postcss@8.5.6): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.6 + + postcss-load-config@4.0.2(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.1 + optionalDependencies: + postcss: 8.5.6 + + postcss-loader@7.3.4(postcss@8.5.6)(typescript@5.9.2)(webpack@5.100.2): + dependencies: + cosmiconfig: 8.3.6(typescript@5.9.2) + jiti: 1.21.7 + postcss: 8.5.6 + semver: 7.7.2 + webpack: 5.100.2 + transitivePeerDependencies: + - typescript + + postcss-logical@8.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-merge-idents@6.0.3(postcss@8.5.6): + dependencies: + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-merge-longhand@6.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + stylehacks: 6.1.1(postcss@8.5.6) + + postcss-merge-rules@6.1.1(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + caniuse-api: 3.0.0 + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@6.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@6.0.3(postcss@8.5.6): + dependencies: + colord: 2.9.3 + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-params@6.1.0(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@6.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-modules-values@4.0.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-nesting@13.0.2(postcss@8.5.6): + dependencies: + '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.0) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-normalize-charset@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-normalize-display-values@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@6.1.0(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-opacity-percentage@3.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-ordered-values@6.0.2(postcss@8.5.6): + dependencies: + cssnano-utils: 4.0.2(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-overflow-shorthand@6.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-page-break@3.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-place@10.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-preset-env@10.2.4(postcss@8.5.6): + dependencies: + '@csstools/postcss-cascade-layers': 5.0.2(postcss@8.5.6) + '@csstools/postcss-color-function': 4.0.10(postcss@8.5.6) + '@csstools/postcss-color-mix-function': 3.0.10(postcss@8.5.6) + '@csstools/postcss-color-mix-variadic-function-arguments': 1.0.0(postcss@8.5.6) + '@csstools/postcss-content-alt-text': 2.0.6(postcss@8.5.6) + '@csstools/postcss-exponential-functions': 2.0.9(postcss@8.5.6) + '@csstools/postcss-font-format-keywords': 4.0.0(postcss@8.5.6) + '@csstools/postcss-gamut-mapping': 2.0.10(postcss@8.5.6) + '@csstools/postcss-gradients-interpolation-method': 5.0.10(postcss@8.5.6) + '@csstools/postcss-hwb-function': 4.0.10(postcss@8.5.6) + '@csstools/postcss-ic-unit': 4.0.2(postcss@8.5.6) + '@csstools/postcss-initial': 2.0.1(postcss@8.5.6) + '@csstools/postcss-is-pseudo-class': 5.0.3(postcss@8.5.6) + '@csstools/postcss-light-dark-function': 2.0.9(postcss@8.5.6) + '@csstools/postcss-logical-float-and-clear': 3.0.0(postcss@8.5.6) + '@csstools/postcss-logical-overflow': 2.0.0(postcss@8.5.6) + '@csstools/postcss-logical-overscroll-behavior': 2.0.0(postcss@8.5.6) + '@csstools/postcss-logical-resize': 3.0.0(postcss@8.5.6) + '@csstools/postcss-logical-viewport-units': 3.0.4(postcss@8.5.6) + '@csstools/postcss-media-minmax': 2.0.9(postcss@8.5.6) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 3.0.5(postcss@8.5.6) + '@csstools/postcss-nested-calc': 4.0.0(postcss@8.5.6) + '@csstools/postcss-normalize-display-values': 4.0.0(postcss@8.5.6) + '@csstools/postcss-oklab-function': 4.0.10(postcss@8.5.6) + '@csstools/postcss-progressive-custom-properties': 4.1.0(postcss@8.5.6) + '@csstools/postcss-random-function': 2.0.1(postcss@8.5.6) + '@csstools/postcss-relative-color-syntax': 3.0.10(postcss@8.5.6) + '@csstools/postcss-scope-pseudo-class': 4.0.1(postcss@8.5.6) + '@csstools/postcss-sign-functions': 1.1.4(postcss@8.5.6) + '@csstools/postcss-stepped-value-functions': 4.0.9(postcss@8.5.6) + '@csstools/postcss-text-decoration-shorthand': 4.0.2(postcss@8.5.6) + '@csstools/postcss-trigonometric-functions': 4.0.9(postcss@8.5.6) + '@csstools/postcss-unset-value': 4.0.0(postcss@8.5.6) + autoprefixer: 10.4.21(postcss@8.5.6) + browserslist: 4.25.3 + css-blank-pseudo: 7.0.1(postcss@8.5.6) + css-has-pseudo: 7.0.2(postcss@8.5.6) + css-prefers-color-scheme: 10.0.0(postcss@8.5.6) + cssdb: 8.3.1 + postcss: 8.5.6 + postcss-attribute-case-insensitive: 7.0.1(postcss@8.5.6) + postcss-clamp: 4.1.0(postcss@8.5.6) + postcss-color-functional-notation: 7.0.10(postcss@8.5.6) + postcss-color-hex-alpha: 10.0.0(postcss@8.5.6) + postcss-color-rebeccapurple: 10.0.0(postcss@8.5.6) + postcss-custom-media: 11.0.6(postcss@8.5.6) + postcss-custom-properties: 14.0.6(postcss@8.5.6) + postcss-custom-selectors: 8.0.5(postcss@8.5.6) + postcss-dir-pseudo-class: 9.0.1(postcss@8.5.6) + postcss-double-position-gradients: 6.0.2(postcss@8.5.6) + postcss-focus-visible: 10.0.1(postcss@8.5.6) + postcss-focus-within: 9.0.1(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-gap-properties: 6.0.0(postcss@8.5.6) + postcss-image-set-function: 7.0.0(postcss@8.5.6) + postcss-lab-function: 7.0.10(postcss@8.5.6) + postcss-logical: 8.1.0(postcss@8.5.6) + postcss-nesting: 13.0.2(postcss@8.5.6) + postcss-opacity-percentage: 3.0.0(postcss@8.5.6) + postcss-overflow-shorthand: 6.0.0(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + postcss-place: 10.0.0(postcss@8.5.6) + postcss-pseudo-class-any-link: 10.0.1(postcss@8.5.6) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.6) + postcss-selector-not: 8.0.1(postcss@8.5.6) + + postcss-pseudo-class-any-link@10.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-reduce-idents@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@6.1.0(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + caniuse-api: 3.0.0 + postcss: 8.5.6 + + postcss-reduce-transforms@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-not@8.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sort-media-queries@5.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + sort-css-media-queries: 2.2.0 + + postcss-svgo@6.0.3(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 + + postcss-unique-selectors@6.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss-zindex@6.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres@3.4.7: {} + + potpack@1.0.2: {} + + potpack@2.1.0: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-organize-imports@4.2.0(prettier@3.6.2)(typescript@5.9.2): + dependencies: + prettier: 3.6.2 + typescript: 5.9.2 + + prettier-plugin-sort-json@4.1.1(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.38.10): + dependencies: + prettier: 3.6.2 + svelte: 5.38.10 + + prettier@3.6.2: {} + + pretty-error@4.0.0: + dependencies: + lodash: 4.17.21 + renderkid: 3.0.0 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + pretty-time@1.1.0: {} + + prism-react-renderer@2.4.1(react@18.3.1): + dependencies: + '@types/prismjs': 1.26.5 + clsx: 2.1.1 + react: 18.3.1 + + prismjs@1.30.0: {} + + proc-log@5.0.0: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + properties-reader@2.3.0: + dependencies: + mkdirp: 1.0.4 + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + + proto-list@1.2.4: {} + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.18.8 + long: 5.3.2 + + protocol-buffers-schema@3.6.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + optional: true + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@1.4.1: {} + + punycode@2.3.1: {} + + pupa@3.1.0: + dependencies: + escape-goat: 4.0.0 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + querystringify@2.2.0: + optional: true + + queue-microtask@1.2.3: {} + + quick-lru@5.1.1: {} + + quick-lru@7.2.0: {} + + quickselect@2.0.0: {} + + quickselect@3.0.0: {} + + railroad-diagrams@1.0.0: {} + + randexp@0.4.6: + dependencies: + discontinuous-range: 1.0.0 + ret: 0.1.15 + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + range-parser@1.2.0: {} + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.1: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.7.0 + unpipe: 1.0.0 + + raw-loader@4.0.2(webpack@5.100.2): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.100.2 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react-email@4.2.11: + dependencies: + '@babel/parser': 7.28.4 + '@babel/traverse': 7.28.4 + chokidar: 4.0.3 + commander: 13.1.0 + debounce: 2.2.0 + esbuild: 0.25.9 + glob: 11.0.3 + jiti: 2.4.2 + log-symbols: 7.0.1 + mime-types: 3.0.1 + normalize-path: 3.0.0 + nypm: 0.6.0 + ora: 8.2.0 + prompts: 2.4.2 + socket.io: 4.8.1 + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + react-fast-compare@3.2.2: {} + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-json-view-lite@2.4.1(react@18.3.1): + dependencies: + react: 18.3.1 + + react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@18.3.1))(webpack@5.100.2): + dependencies: + '@babel/runtime': 7.28.4 + react-loadable: '@docusaurus/react-loadable@6.0.0(react@18.3.1)' + webpack: 5.100.2 + + react-promise-suspense@0.3.4: + dependencies: + fast-deep-equal: 2.0.1 + + react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + react: 18.3.1 + react-router: 5.3.4(react@18.3.1) + + react-router-dom@5.3.4(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + history: 4.10.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-router: 5.3.4(react@18.3.1) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + react-router@5.3.4(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + history: 4.10.1 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + path-to-regexp: 1.9.0 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 16.13.1 + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + react@19.1.1: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.15.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + + reflect-metadata@0.2.2: {} + + regenerate-unicode-properties@10.2.0: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexp-tree@0.1.27: {} + + regexpu-core@6.2.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.12.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.0 + + registry-auth-token@5.1.0: + dependencies: + '@pnpm/npm-conf': 2.3.1 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + regjsgen@0.8.0: {} + + regjsparser@0.12.0: + dependencies: + jsesc: 3.0.2 + + rehype-parse@7.0.1: + dependencies: + hast-util-from-parse5: 6.0.1 + parse5: 6.0.1 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + relateurl@0.2.7: {} + + remark-directive@3.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-directive: 3.1.0 + micromark-extension-directive: 3.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-emoji@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + emoticon: 4.1.0 + mdast-util-find-and-replace: 3.0.2 + node-emoji: 2.2.0 + unified: 11.0.5 + + remark-frontmatter@5.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-frontmatter: 2.0.1 + micromark-extension-frontmatter: 2.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.0: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + renderkid@3.0.0: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.17.21 + strip-ansi: 6.0.1 + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + require-like@0.1.2: {} + + require-main-filename@2.0.0: {} + + requires-port@1.0.0: {} + + resolve-alpn@1.2.1: {} + + resolve-from@4.0.0: {} + + resolve-pathname@3.0.0: {} + + resolve-protobuf-schema@2.1.0: + dependencies: + protocol-buffers-schema: 3.6.0 + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + response-time@2.3.4: + dependencies: + depd: 2.0.0 + on-headers: 1.1.0 + + responselike@3.0.0: + dependencies: + lowercase-keys: 3.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + ret@0.1.15: {} + + retry@0.12.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + robust-predicates@3.0.2: {} + + rollup-plugin-visualizer@6.0.3(rollup@4.50.1): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.4 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.50.1 + + rollup@4.50.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + + rrweb-cssom@0.8.0: + optional: true + + rtlcss@4.3.0: + dependencies: + escalade: 3.2.0 + picocolors: 1.1.1 + postcss: 8.5.6 + strip-json-comments: 3.1.1 + + run-async@2.4.1: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + runed@0.29.2(svelte@5.38.10): + dependencies: + esm-env: 1.2.2 + svelte: 5.38.10 + + rw@1.3.3: {} + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + sanitize-filename@1.6.3: + dependencies: + truncate-utf8-bytes: 1.0.2 + + sanitize-html@2.17.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.5.6 + + sax@1.4.1: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + optional: true + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + scheduler@0.26.0: {} + + schema-dts@1.1.5: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + + search-insights@2.17.3: {} + + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + + selderee@0.11.0: + dependencies: + parseley: 0.12.1 + + select-hose@2.0.0: {} + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + + semver-diff@4.0.0: + dependencies: + semver: 7.7.2 + + semver@6.3.1: {} + + semver@7.7.2: {} + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serve-handler@6.1.6: + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 3.3.0 + range-parser: 1.2.0 + + serve-index@1.9.1: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-cookie-parser@2.7.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setprototypeof@1.1.0: {} + + setprototypeof@1.2.0: {} + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + shallowequal@1.1.0: {} + + shapefile@0.6.6: + dependencies: + array-source: 0.0.4 + commander: 2.20.3 + path-source: 0.1.3 + slice-source: 0.4.1 + stream-source: 0.3.5 + text-encoding: 0.6.4 + + sharp@0.34.3: + dependencies: + color: 4.2.3 + detect-libc: 2.1.0 + node-addon-api: 8.5.0 + node-gyp: 11.4.2 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 + transitivePeerDependencies: + - supports-color + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: + optional: true + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + + simple-icons@15.15.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sirv@2.0.4: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + sitemap@7.1.2: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + + slash@3.0.0: {} + + slash@4.0.0: {} + + slice-source@0.4.1: {} + + smart-buffer@4.2.0: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + + sort-css-media-queries@2.2.0: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.3 + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.3 + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + split-ca@1.0.1: {} + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + sql-formatter@15.6.9: + dependencies: + argparse: 2.0.1 + nearley: 2.20.1 + + srcset@4.0.0: {} + + ssh-remote-port-forward@1.0.4: + dependencies: + '@types/ssh2': 0.5.52 + ssh2: 1.16.0 + + ssh2@1.16.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.23.0 + + ssri@12.0.0: + dependencies: + minipass: 7.1.2 + + stackback@0.0.2: {} + + standard-as-callback@2.1.0: {} + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + std-env@3.9.0: {} + + stdin-discarder@0.2.2: {} + + stream-source@0.3.5: {} + + streamsearch@1.1.0: {} + + streamx@2.22.1: + dependencies: + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.6.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.5.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom-string@1.0.0: {} + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-indent@4.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + + strnum@2.1.1: {} + + strtok3@10.3.4: + dependencies: + '@tokenizer/token': 0.3.0 + + style-to-js@1.1.17: + dependencies: + style-to-object: 1.0.9 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + + stylehacks@6.1.1(postcss@8.5.6): + dependencies: + browserslist: 4.25.3 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + + superagent@10.2.3: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.3 + fast-safe-stringify: 2.1.1 + form-data: 4.0.4 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supercluster@7.1.5: + dependencies: + kdbush: 3.0.0 + + supercluster@8.0.1: + dependencies: + kdbush: 4.0.2 + + supertest@7.1.4: + dependencies: + methods: 1.1.2 + superagent: 10.2.3 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte-check@4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.38.10 + typescript: 5.9.2 + transitivePeerDependencies: + - picomatch + + svelte-eslint-parser@1.3.3(svelte@5.38.10): + dependencies: + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + postcss: 8.5.6 + postcss-scss: 4.0.9(postcss@8.5.6) + postcss-selector-parser: 7.1.0 + optionalDependencies: + svelte: 5.38.10 + + svelte-gestures@5.2.2: {} + + svelte-i18n@4.0.1(svelte@5.38.10): + dependencies: + cli-color: 2.0.4 + deepmerge: 4.3.1 + esbuild: 0.19.12 + estree-walker: 2.0.2 + intl-messageformat: 10.7.16 + sade: 1.8.1 + svelte: 5.38.10 + tiny-glob: 0.2.9 + + svelte-maplibre@1.2.1(svelte@5.38.10): + dependencies: + d3-geo: 3.1.1 + dequal: 2.0.3 + just-compare: 2.3.0 + maplibre-gl: 5.7.1 + pmtiles: 3.2.1 + svelte: 5.38.10 + + svelte-parse-markup@0.1.5(svelte@5.38.10): + dependencies: + svelte: 5.38.10 + + svelte-persisted-store@0.12.0(svelte@5.38.10): + dependencies: + svelte: 5.38.10 + + svelte-toolbelt@0.9.3(svelte@5.38.10): + dependencies: + clsx: 2.1.1 + runed: 0.29.2(svelte@5.38.10) + style-to-object: 1.0.9 + svelte: 5.38.10 + + svelte@5.38.10: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 2.1.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.19 + zimmerframe: 1.1.2 + + svg-parser@2.0.4: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + + swagger-ui-dist@5.21.0: + dependencies: + '@scarf/scarf': 1.4.0 + + symbol-observable@4.0.0: {} + + symbol-tree@3.2.4: + optional: true + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + systeminformation@5.23.8: {} + + tabbable@6.2.0: {} + + tailwind-merge@3.3.1: {} + + tailwind-variants@3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.13): + dependencies: + tailwindcss: 4.1.13 + optionalDependencies: + tailwind-merge: 3.3.1 + + tailwindcss-email-variants@3.0.4(tailwindcss@3.4.17): + dependencies: + tailwindcss: 3.4.17 + + tailwindcss-mso@2.0.2(tailwindcss@3.4.17): + dependencies: + tailwindcss: 3.4.17 + + tailwindcss-preset-email@1.4.0(tailwindcss@3.4.17): + dependencies: + tailwindcss: 3.4.17 + tailwindcss-email-variants: 3.0.4(tailwindcss@3.4.17) + tailwindcss-mso: 2.0.2(tailwindcss@3.4.17) + + tailwindcss@3.4.17: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tailwindcss@4.1.13: {} + + tapable@2.2.2: {} + + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-fs@3.1.0: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.2.0 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-buffer + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.22.1 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + terser-webpack-plugin@5.3.14(@swc/core@1.13.5(@swc/helpers@0.5.17))(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.43.1 + webpack: 5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)) + optionalDependencies: + '@swc/core': 1.13.5(@swc/helpers@0.5.17) + + terser-webpack-plugin@5.3.14(webpack@5.100.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.43.1 + webpack: 5.100.2 + + terser@5.43.1: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + + testcontainers@11.5.1: + dependencies: + '@balena/dockerignore': 1.0.2 + '@types/dockerode': 3.3.42 + archiver: 7.0.1 + async-lock: 1.4.1 + byline: 5.0.0 + debug: 4.4.3 + docker-compose: 1.2.0 + dockerode: 4.0.7 + get-port: 7.1.0 + proper-lockfile: 4.1.2 + properties-reader: 2.3.0 + ssh-remote-port-forward: 1.0.4 + tar-fs: 3.1.0 + tmp: 0.2.5 + undici: 7.16.0 + transitivePeerDependencies: + - bare-buffer + - supports-color + + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + + text-encoding@0.6.4: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + three@0.179.1: {} + + three@0.180.0: {} + + through@2.3.8: {} + + thumbhash@0.1.1: {} + + thunky@1.1.0: {} + + timers-ext@0.1.8: + dependencies: + es5-ext: 0.10.64 + next-tick: 1.1.0 + + tiny-glob@0.2.9: + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyqueue@2.0.3: {} + + tinyqueue@3.0.0: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.3: {} + + tldts-core@6.1.86: + optional: true + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + optional: true + + tmp@0.2.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-vfile@6.1.0: + dependencies: + is-buffer: 2.0.5 + vfile: 4.2.1 + + toidentifier@1.0.1: {} + + token-types@6.1.1: + dependencies: + '@borewit/text-codec': 0.1.1 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + totalist@3.0.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + optional: true + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + optional: true + + tr46@0.0.3: {} + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + optional: true + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + optional: true + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + trough@1.0.5: {} + + trough@2.2.0: {} + + truncate-utf8-bytes@1.0.2: + dependencies: + utf8-byte-length: 1.0.5 + + ts-api-utils@2.1.0(typescript@5.9.2): + dependencies: + typescript: 5.9.2 + + ts-interface-checker@0.1.13: {} + + tsconfck@3.1.6(typescript@5.9.2): + optionalDependencies: + typescript: 5.9.2 + + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.18.3 + tapable: 2.2.2 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tsscmp@1.0.6: {} + + tweetnacl@0.14.5: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.21.3: {} + + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + type@2.7.3: {} + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typedarray@0.0.6: {} + + typescript-eslint@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.36.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + typescript@5.9.2: {} + + ua-is-frozen@0.1.2: {} + + ua-parser-js@2.0.5: + dependencies: + detect-europe-js: 0.1.2 + is-standalone-pwa: 0.1.1 + ua-is-frozen: 0.1.2 + undici: 7.16.0 + + uglify-js@3.19.3: + optional: true + + uid2@1.0.0: {} + + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + uint8array-extras@1.4.1: {} + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + undici-types@7.12.0: + optional: true + + undici@7.16.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-emoji-modifier-base@1.0.0: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.1.0 + + unicode-match-property-value-ecmascript@2.2.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unified@9.2.2: + dependencies: + '@types/unist': 2.0.11 + bail: 1.0.5 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 2.1.0 + trough: 1.0.5 + vfile: 4.2.1 + + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + + unique-string@3.0.0: + dependencies: + crypto-random-string: 4.0.0 + + unist-util-find-after@3.0.0: + dependencies: + unist-util-is: 4.1.0 + + unist-util-is@4.1.0: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@2.0.3: + dependencies: + '@types/unist': 2.0.11 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@3.1.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@2.0.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + universalify@0.2.0: + optional: true + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + unplugin-swc@1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.50.1): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + '@swc/core': 1.13.5(@swc/helpers@0.5.17) + load-tsconfig: 0.2.5 + unplugin: 2.3.10 + transitivePeerDependencies: + - rollup + + unplugin@2.3.10: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.3(browserslist@4.25.3): + dependencies: + browserslist: 4.25.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-notifier@6.0.2: + dependencies: + boxen: 7.1.1 + chalk: 5.6.2 + configstore: 6.0.0 + has-yarn: 3.0.0 + import-lazy: 4.0.0 + is-ci: 3.0.1 + is-installed-globally: 0.4.0 + is-npm: 6.0.0 + is-yarn-global: 0.4.1 + latest-version: 7.0.0 + pupa: 3.1.0 + semver: 7.7.2 + semver-diff: 4.0.0 + xdg-basedir: 5.1.0 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-loader@4.1.1(file-loader@6.2.0(webpack@5.100.2))(webpack@5.100.2): + dependencies: + loader-utils: 2.0.4 + mime-types: 2.1.35 + schema-utils: 3.3.0 + webpack: 5.100.2 + optionalDependencies: + file-loader: 6.2.0(webpack@5.100.2) + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + optional: true + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.14.0 + + utf8-byte-length@1.0.5: {} + + util-deprecate@1.0.2: {} + + utila@0.4.0: {} + + utility-types@3.11.0: {} + + utils-merge@1.0.1: {} + + utimes@5.2.1(encoding@0.1.13): + dependencies: + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + node-addon-api: 4.3.0 + transitivePeerDependencies: + - encoding + - supports-color + + uuid@10.0.0: {} + + uuid@11.1.0: {} + + uuid@8.3.2: {} + + uuid@9.0.1: {} + + validator@13.15.15: {} + + value-equal@1.0.1: {} + + vary@1.1.2: {} + + vfile-location@3.2.0: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@2.0.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position: 2.0.3 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@4.2.1: + dependencies: + '@types/unist': 2.0.11 + is-buffer: 2.0.5 + unist-util-stringify-position: 2.0.3 + vfile-message: 2.0.4 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite-imagetools@8.0.0(rollup@4.50.1): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.50.1) + imagetools-core: 8.0.0 + sharp: 0.34.3 + transitivePeerDependencies: + - rollup + - supports-color + + vite-node@3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-node@3.2.4(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.2) + optionalDependencies: + vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.8 + fsevents: 2.3.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + terser: 5.43.1 + yaml: 2.8.1 + + vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.5.1 + fsevents: 2.3.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + terser: 5.43.1 + yaml: 2.8.1 + + vitefu@1.1.1(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + optionalDependencies: + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + dependencies: + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.3 + expect-type: 1.2.1 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.18.8 + happy-dom: 18.0.1 + jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13)) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.8)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.3 + expect-type: 1.2.1 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.5(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.8)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.18.8 + happy-dom: 18.0.1 + jsdom: 26.1.0(canvas@2.11.2) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.5.1)(happy-dom@18.0.1)(jiti@2.5.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.3 + expect-type: 1.2.1 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.5.1 + happy-dom: 18.0.1 + jsdom: 26.1.0(canvas@2.11.2) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vt-pbf@3.1.3: + dependencies: + '@mapbox/point-geometry': 0.1.0 + '@mapbox/vector-tile': 1.3.1 + pbf: 3.3.0 + + w3c-xmlserializer@4.0.0: + dependencies: + xml-name-validator: 4.0.0 + optional: true + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + optional: true + + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + web-namespaces@1.1.4: {} + + web-namespaces@2.0.1: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: + optional: true + + webpack-bundle-analyzer@4.10.2: + dependencies: + '@discoveryjs/json-ext': 0.5.7 + acorn: 8.15.0 + acorn-walk: 8.3.4 + commander: 7.2.0 + debounce: 1.2.1 + escape-string-regexp: 4.0.0 + gzip-size: 6.0.0 + html-escaper: 2.0.2 + opener: 1.5.2 + picocolors: 1.1.1 + sirv: 2.0.4 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + webpack-dev-middleware@5.3.4(webpack@5.100.2): + dependencies: + colorette: 2.0.20 + memfs: 3.5.3 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.3.2 + webpack: 5.100.2 + + webpack-dev-server@4.15.2(webpack@5.100.2): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.23 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.8 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.21.2 + graceful-fs: 4.2.11 + html-entities: 2.6.0 + http-proxy-middleware: 2.0.9(@types/express@4.17.23) + ipaddr.js: 2.2.0 + launch-editor: 2.10.0 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.3.2 + selfsigned: 2.4.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 5.3.4(webpack@5.100.2) + ws: 8.18.3 + optionalDependencies: + webpack: 5.100.2 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-merge@5.10.0: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-node-externals@3.0.0: {} + + webpack-sources@3.3.3: {} + + webpack-virtual-modules@0.6.2: {} + + webpack@5.100.2: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(webpack@5.100.2) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17)): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.2 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(@swc/core@1.13.5(@swc/helpers@0.5.17))(webpack@5.100.2(@swc/core@1.13.5(@swc/helpers@0.5.17))) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpackbar@6.0.1(webpack@5.100.2): + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + consola: 3.4.2 + figures: 3.2.0 + markdown-table: 2.0.0 + pretty-time: 1.1.0 + std-env: 3.9.0 + webpack: 5.100.2 + wrap-ansi: 7.0.0 + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + optional: true + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + optional: true + + whatwg-mimetype@3.0.0: {} + + whatwg-mimetype@4.0.0: + optional: true + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + optional: true + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + optional: true + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@5.0.0: + dependencies: + isexe: 3.1.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + + wildcard@2.0.1: {} + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + + ws@7.5.10: {} + + ws@8.17.1: {} + + ws@8.18.3: {} + + xdg-basedir@5.1.0: {} + + xml-js@1.6.11: + dependencies: + sax: 1.4.1 + + xml-name-validator@4.0.0: + optional: true + + xml-name-validator@5.0.0: + optional: true + + xmlchars@2.2.0: + optional: true + + xmlhttprequest-ssl@2.1.2: {} + + xtend@4.0.2: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@1.10.2: {} + + yaml@2.8.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.1: {} + + yoctocolors-cjs@2.1.2: {} + + yoctocolors@2.1.2: {} + + zimmerframe@1.1.2: {} + + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + + zwitch@1.0.5: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000000..880d115880 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,58 @@ +packages: + - cli + - docs + - e2e + - open-api/typescript-sdk + - server + - web + - .github +ignoredBuiltDependencies: + - '@nestjs/core' + - '@scarf/scarf' + - '@swc/core' + - bcrypt + - canvas + - core-js + - core-js-pure + - cpu-features + - es5-ext + - esbuild + - msgpackr-extract + - postman-code-generators + - protobufjs + - ssh2 + - utimes +onlyBuiltDependencies: + - sharp + - '@tailwindcss/oxide' +overrides: + canvas: 2.11.2 + sharp: ^0.34.3 +packageExtensions: + nestjs-kysely: + dependencies: + tslib: '*' + nestjs-otel: + dependencies: + tslib: '*' + '@photo-sphere-viewer/equirectangular-video-adapter': + dependencies: + three: '*' + '@photo-sphere-viewer/video-plugin': + dependencies: + three: '*' + sharp: + dependencies: + node-addon-api: '*' + node-gyp: '*' + '@immich/ui': + dependencies: + tailwindcss: '>=4.1' + tailwind-variants: + dependencies: + tailwindcss: '>=4.1' +dedupePeerDependents: false +preferWorkspacePackages: true +injectWorkspacePackages: true +shamefullyHoist: false +verifyDepsBeforeRun: install diff --git a/readme_i18n/README_ar_JO.md b/readme_i18n/README_ar_JO.md index 4e7ba99dd2..e0e13eeaf6 100644 --- a/readme_i18n/README_ar_JO.md +++ b/readme_i18n/README_ar_JO.md @@ -28,7 +28,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -46,13 +47,13 @@ ## Ų…Ø­ØĒŲˆŲ‰ -- [Ø§Ų„ŲˆØĢاØĻŲ‚ Ø§Ų„ØąØŗŲ…ŲŠØŠ](https://immich.app/docs) +- [Ø§Ų„ŲˆØĢاØĻŲ‚ Ø§Ų„ØąØŗŲ…ŲŠØŠ](https://docs.immich.app) - [ØŽØąŲŠØˇØŠ Ø§Ų„ØˇØąŲŠŲ‚](https://github.com/orgs/immich-app/projects/1) - [ØĒØŦØąŲŠØ¨ŲŠ](#demo) - [ØŗŲ…Ø§ØĒ](#features) -- [Ų…Ų‚Ø¯Ų…ØŠ](https://immich.app/docs/overview/introduction) -- [ØĒØšŲ„ŲŠŲ…Ø§ØĒ Ø§Ų„ØĒØ­Ų…ŲŠŲ„](https://immich.app/docs/install/requirements) -- [Ų‚ŲˆØ§ØšØ¯ Ø§Ų„Ų…ØŗØ§Ų‡Ų…ØŠ](https://immich.app/docs/overview/support-the-project) +- [Ų…Ų‚Ø¯Ų…ØŠ](https://docs.immich.app/overview/introduction) +- [ØĒØšŲ„ŲŠŲ…Ø§ØĒ Ø§Ų„ØĒØ­Ų…ŲŠŲ„](https://docs.immich.app/install/requirements) +- [Ų‚ŲˆØ§ØšØ¯ Ø§Ų„Ų…ØŗØ§Ų‡Ų…ØŠ](https://docs.immich.app/overview/support-the-project) ## ØĒ؈ØĢŲŠŲ‚ diff --git a/readme_i18n/README_ca_ES.md b/readme_i18n/README_ca_ES.md index 0b8dd5999b..d09362aa0f 100644 --- a/readme_i18n/README_ca_ES.md +++ b/readme_i18n/README_ca_ES.md @@ -27,7 +27,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -44,13 +45,13 @@ ## Contingut -- [DocumentaciÃŗ oficial](https://immich.app/docs) +- [DocumentaciÃŗ oficial](https://docs.immich.app) - [Mapa de ruta](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funcionalitats](#funcionalitats) -- [IntroducciÃŗ](https://immich.app/docs/overview/introduction) -- [Instal¡laciÃŗ](https://immich.app/docs/install/requirements) -- [Directrius de contribuciÃŗ](https://immich.app/docs/overview/support-the-project) +- [IntroducciÃŗ](https://docs.immich.app/overview/introduction) +- [Instal¡laciÃŗ](https://docs.immich.app/install/requirements) +- [Directrius de contribuciÃŗ](https://docs.immich.app/overview/support-the-project) ## DocumentaciÃŗ diff --git a/readme_i18n/README_de_DE.md b/readme_i18n/README_de_DE.md index d24818b881..a8685e0902 100644 --- a/readme_i18n/README_de_DE.md +++ b/readme_i18n/README_de_DE.md @@ -27,7 +27,8 @@ 한ęĩ­ė–´ Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -50,14 +51,14 @@ ## Inhalt -- [Offizielle Dokumentation](https://immich.app/docs) -- [Über Immich](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) +- [Offizielle Dokumentation](https://docs.immich.app) +- [Über Immich](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funktionen](#funktionen) -- [Übersetzungen](https://immich.app/docs/developer/translations) -- [Beitragsrichtlinien](https://immich.app/docs/overview/support-the-project) +- [Übersetzungen](https://docs.immich.app/developer/translations) +- [Beitragsrichtlinien](https://docs.immich.app/overview/support-the-project) ## Demo @@ -107,7 +108,7 @@ Die Web-Demo kannst Du unter https://demo.immich.app finden. FÃŧr die Handy-App ## Übersetzungen -Mehr zum Thema Übersetzungen kannst du [hier](https://immich.app/docs/developer/translations) erfahren. +Mehr zum Thema Übersetzungen kannst du [hier](https://docs.immich.app/developer/translations) erfahren. Translation status diff --git a/readme_i18n/README_es_ES.md b/readme_i18n/README_es_ES.md index 00417c9188..032f8c50a8 100644 --- a/readme_i18n/README_es_ES.md +++ b/readme_i18n/README_es_ES.md @@ -27,7 +27,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -45,13 +46,13 @@ ## Contenido -- [DocumentaciÃŗn oficial](https://immich.app/docs) +- [DocumentaciÃŗn oficial](https://docs.immich.app) - [Hoja de ruta](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funciones](#funciones) -- [IntroducciÃŗn](https://immich.app/docs/overview/introduction) -- [InstalaciÃŗn](https://immich.app/docs/install/requirements) -- [Directrices para contribuir](https://immich.app/docs/overview/support-the-project) +- [IntroducciÃŗn](https://docs.immich.app/overview/introduction) +- [InstalaciÃŗn](https://docs.immich.app/install/requirements) +- [Directrices para contribuir](https://docs.immich.app/overview/support-the-project) ## DocumentaciÃŗn @@ -99,7 +100,7 @@ contraseÃąa: demo ## Traducciones -Lea mas acerca de las traducciones [acÃĄ](https://immich.app/docs/developer/translations). +Lea mas acerca de las traducciones [acÃĄ](https://docs.immich.app/developer/translations). Translation status diff --git a/readme_i18n/README_fr_FR.md b/readme_i18n/README_fr_FR.md index e1d4fb1cbc..349a0c49ce 100644 --- a/readme_i18n/README_fr_FR.md +++ b/readme_i18n/README_fr_FR.md @@ -27,7 +27,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -45,13 +46,13 @@ ## Sommaire -- [Documentation officielle](https://immich.app/docs) +- [Documentation officielle](https://docs.immich.app) - [Feuille de route](https://github.com/orgs/immich-app/projects/1) - [DÊmo](#dÊmo) - [FonctionnalitÊs](#fonctionnalitÊs) -- [Introduction](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) -- [Contribution](https://immich.app/docs/overview/support-the-project) +- [Introduction](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) +- [Contribution](https://docs.immich.app/overview/support-the-project) ## Documentation diff --git a/readme_i18n/README_it_IT.md b/readme_i18n/README_it_IT.md index 358faeb40c..711840fd9d 100644 --- a/readme_i18n/README_it_IT.md +++ b/readme_i18n/README_it_IT.md @@ -1,22 +1,23 @@

-
- License: AGPLv3 +
+ Licenza: AGPLv3 - + Discord -
-
+
+

- +

-

Immich - Soluzione self-hosted ad alte prestazioni per backup di foto e video

+

Soluzione ad alte prestazioni per la gestione self-hosted di foto e video


- +
+

English Català @@ -27,7 +28,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -36,64 +38,97 @@ ā¸ ā¸˛ā¸Šā¸˛āš„ā¸—ā¸ĸ

-## Declino di responsabilità +## Avvertenze -- âš ī¸ Il progetto è in una fase **molto intensa** di sviluppo. -- âš ī¸ Possibilità di bug e cambiamenti rilevanti. -- âš ī¸ **Non utilizzare l'app come unico salvataggio delle tue foto e dei tuoi video.** -- âš ī¸ Utilizza sempre una tecnica [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) di backup per le foto e i video a cui tieni! +- âš ī¸ Il progetto è in fase di sviluppo **molto attivo**. +- âš ī¸ Possono esserci bug o cambiamenti radicali, che possono non essere retrocompatibili (breaking changes). +- âš ī¸ **Non usare l’app come unico modo per archiviare le tue foto e i tuoi video.** +- âš ī¸ Segui sempre la regola di backup [3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) per proteggere i tuoi ricordi e le foto a cui tieni! -## Contenuto +> [!NOTE] +> La documentazione principale, comprese le guide all’installazione, si trova su https://immich.app/. -- [Documentazione Ufficiale](https://immich.app/docs) -- [Roadmap](https://github.com/orgs/immich-app/projects/1) -- [Demo](#demo) -- [Funzionalità](#features) -- [Introduzione](https://immich.app/docs/overview/introduction) -- [Installazione](https://immich.app/docs/install/requirements) -- [Linee Guida per Contribuire](https://immich.app/docs/overview/support-the-project) +## Link utili -## Documentazione - -La documentazione ufficiale, inclusa la guida all'installazione, è disponibile qui: https://immich.app/. +- [Documentazione](https://docs.immich.app) +- [Informazioni](https://docs.immich.app/overview/introduction) +- [Installazione](https://docs.immich.app/install/requirements) +- [Roadmap](https://immich.app/roadmap) +- [Demo](#demo) +- [Funzionalità](#funzionalità) +- [Traduzioni](https://docs.immich.app/developer/translations) +- [Contribuire](https://docs.immich.app/overview/support-the-project) ## Demo -Prova la demo del progetto https://demo.immich.app. Sull'app mobile, imposta `https://demo.immich.app` come `Server Endpoint URL` +Accedi alla demo [qui](https://demo.immich.app). +Per l’app mobile puoi usare `https://demo.immich.app` come `Server Endpoint URL`. -```bash title="Demo Credential" -Credenziali di accesso -email: demo@immich.app -password: demo -``` +### Credenziali di accesso -# Funzionalità +| Email | Password | +| --------------- | -------- | +| demo@immich.app | demo | -| Funzionalità | Mobile | Web | -| ---------------------------------------------- | ------ | --- | -| Caricamento e visualizzazione di foto e video | SÃŦ | SÃŦ | -| Backup automatico quando l'app è in esecuzione | SÃŦ | N/D | -| Selezione degli album per backup | SÃŦ | N/D | -| Download foto e video sul dispositivo | SÃŦ | SÃŦ | -| Supporto multi utente | SÃŦ | SÃŦ | -| Album e album condivisi | SÃŦ | SÃŦ | -| Barra di scorrimento con trascinamento | SÃŦ | SÃŦ | -| Supporto formati raw | SÃŦ | SÃŦ | -| Visualizzazione metadata (EXIF, map) | SÃŦ | SÃŦ | -| Ricerca per metadata, oggetti, volti e CLIP | SÃŦ | SÃŦ | -| Funzioni di amministrazione degli utenti | No | SÃŦ | -| Backup in background | SÃŦ | N/D | -| Scroll virtuale | SÃŦ | SÃŦ | -| Supporto OAuth | SÃŦ | SÃŦ | -| API Keys | N/D | SÃŦ | -| Backup e riproduzione di LivePhoto | iOS | SÃŦ | -| Archiviazione impostata dall'utente | SÃŦ | SÃŦ | -| Condivisione pubblica | No | SÃŦ | -| Archivio e Preferiti | SÃŦ | SÃŦ | -| Mappa globale | SÃŦ | SÃŦ | -| Collaborazione con utenti | SÃŦ | SÃŦ | -| Riconoscimento facciale e categorizzazione | SÃŦ | SÃŦ | -| Ricordi (x anni fa) | SÃŦ | SÃŦ | -| Supporto offline | SÃŦ | No | -| Galleria sola lettura | SÃŦ | SÃŦ | -| Foto raggruppate | SÃŦ | SÃŦ | +## Funzionalità + +| Funzionalità | Mobile | Web | +| :------------------------------------------ | ------ | --- | +| Caricare e visualizzare foto e video | SÃŦ | SÃŦ | +| Backup automatico all’apertura dell’app | SÃŦ | N/D | +| Evita la duplicazione dei file | SÃŦ | SÃŦ | +| Backup selettivo di album | SÃŦ | N/D | +| Scaricare foto e video sul dispositivo | SÃŦ | SÃŦ | +| Supporto multi-utente | SÃŦ | SÃŦ | +| Album e album condivisi | SÃŦ | SÃŦ | +| Barra di scorrimento trascinabile | SÃŦ | SÃŦ | +| Supporto ai formati RAW | SÃŦ | SÃŦ | +| Visualizzazione metadati (EXIF, mappa) | SÃŦ | SÃŦ | +| Ricerca per metadati, oggetti, volti, CLIP | SÃŦ | SÃŦ | +| Funzioni amministrative (gestione utenti) | No | SÃŦ | +| Backup in background | SÃŦ | N/D | +| Scorrimento virtuale | SÃŦ | SÃŦ | +| Supporto OAuth | SÃŦ | SÃŦ | +| Chiavi API | N/D | SÃŦ | +| Backup e riproduzione LivePhoto/MotionPhoto | SÃŦ | SÃŦ | +| Supporto immagini a 360° | No | SÃŦ | +| Struttura di archiviazione personalizzata | SÃŦ | SÃŦ | +| Condivisione pubblica | SÃŦ | SÃŦ | +| Archivio e preferiti | SÃŦ | SÃŦ | +| Mappa globale | SÃŦ | SÃŦ | +| Condivisione con partner | SÃŦ | SÃŦ | +| Riconoscimento e raggruppamento facciale | SÃŦ | SÃŦ | +| Ricordi (anni fa) | SÃŦ | SÃŦ | +| Supporto offline | SÃŦ | No | +| Galleria in sola lettura | SÃŦ | SÃŦ | +| Foto impilate | SÃŦ | SÃŦ | +| Tag | No | SÃŦ | +| Vista per cartelle | SÃŦ | SÃŦ | + +## Traduzioni + +Scopri di piÚ sulle traduzioni [qui](https://docs.immich.app/developer/translations). + + +Stato traduzioni + + +## Attività del repository + +![Attività](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Immagine analisi repobeats") + +## Cronologia delle stelle + + + + + + Grafico storico delle stelle + + + +## Contributori + + + + diff --git a/readme_i18n/README_ja_JP.md b/readme_i18n/README_ja_JP.md index 60dd0f3ed7..0e74077895 100644 --- a/readme_i18n/README_ja_JP.md +++ b/readme_i18n/README_ja_JP.md @@ -27,7 +27,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro Svenska @@ -44,13 +45,13 @@ ## ã‚ŗãƒŗãƒ†ãƒŗãƒ„ -- [å…Ŧåŧãƒ‰ã‚­ãƒĨãƒĄãƒŗãƒˆ](https://immich.app/docs) +- [å…Ŧåŧãƒ‰ã‚­ãƒĨãƒĄãƒŗãƒˆ](https://docs.immich.app) - [ロãƒŧドマップ](https://github.com/orgs/immich-app/projects/1) - [デãƒĸ](#デãƒĸ) - [抟čƒŊ](#抟čƒŊ) -- [į´šäģ‹](https://immich.app/docs/overview/introduction) -- [ã‚¤ãƒŗã‚šãƒˆãƒŧãƒĢ](https://immich.app/docs/install/requirements) -- [ã‚ŗãƒŗãƒˆãƒĒビãƒĨãƒŧã‚ˇãƒ§ãƒŗã‚Ŧイド](https://immich.app/docs/overview/support-the-project) +- [į´šäģ‹](https://docs.immich.app/overview/introduction) +- [ã‚¤ãƒŗã‚šãƒˆãƒŧãƒĢ](https://docs.immich.app/install/requirements) +- [ã‚ŗãƒŗãƒˆãƒĒビãƒĨãƒŧã‚ˇãƒ§ãƒŗã‚Ŧイド](https://docs.immich.app/overview/support-the-project) ## ドキãƒĨãƒĄãƒŗãƒˆ diff --git a/readme_i18n/README_ko_KR.md b/readme_i18n/README_ko_KR.md index 031e2fd9ca..c2dfd11dd3 100644 --- a/readme_i18n/README_ko_KR.md +++ b/readme_i18n/README_ko_KR.md @@ -28,7 +28,8 @@ Deutsch Nederlands TÃŧrkçe -中文 +įŽ€äŊ“中文 +æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -50,14 +51,14 @@ ## 링íŦ -- [ëŦ¸ė„œ](https://immich.app/docs) -- [ė†Œę°œ](https://immich.app/docs/overview/introduction) -- [ė„¤ėš˜](https://immich.app/docs/install/requirements) +- [ëŦ¸ė„œ](https://docs.immich.app) +- [ė†Œę°œ](https://docs.immich.app/overview/introduction) +- [ė„¤ėš˜](https://docs.immich.app/install/requirements) - [로드ë§ĩ](https://immich.app/roadmap) - [데ëǍ](#데ëǍ) - [기ëŠĨ](#기ëŠĨ) -- [ë˛ˆė—­](https://immich.app/docs/developer/tranlations) -- [기ė—Ŧ](https://immich.app/docs/overview/support-the-project) +- [ë˛ˆė—­](https://docs.immich.app/developer/tranlations) +- [기ė—Ŧ](https://docs.immich.app/overview/support-the-project) ## 데ëǍ @@ -104,7 +105,7 @@ ## ë˛ˆė—­ -ë˛ˆė—­ė— 대한 ėžė„¸í•œ ė •ëŗ´ëŠ” [ė´ęŗŗ](https://immich.app/docs/developer/translations)ė—ė„œ í™•ė¸í•˜ė„¸ėš”. +ë˛ˆė—­ė— 대한 ėžė„¸í•œ ė •ëŗ´ëŠ” [ė´ęŗŗ](https://docs.immich.app/developer/translations)ė—ė„œ í™•ė¸í•˜ė„¸ėš”. ë˛ˆė—­ 현황 diff --git a/readme_i18n/README_nl_NL.md b/readme_i18n/README_nl_NL.md index 46692bc612..ac72e9d238 100644 --- a/readme_i18n/README_nl_NL.md +++ b/readme_i18n/README_nl_NL.md @@ -27,7 +27,8 @@ 한ęĩ­ė–´ Deutsch TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -45,13 +46,13 @@ ## Inhoud -- [OfficiÃĢle documentatie](https://immich.app/docs) +- [OfficiÃĢle documentatie](https://docs.immich.app) - [Toekomstplannen](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Functies](#functies) -- [Introductie](https://immich.app/docs/overview/introduction) -- [Installatie](https://immich.app/docs/install/requirements) -- [Richtlijnen voor bijdragen](https://immich.app/docs/overview/support-the-project) +- [Introductie](https://docs.immich.app/overview/introduction) +- [Installatie](https://docs.immich.app/install/requirements) +- [Richtlijnen voor bijdragen](https://docs.immich.app/overview/support-the-project) ## Documentatie @@ -102,7 +103,7 @@ Je kunt de demo [hier](https://demo.immich.app/) bekijken. Voor de mobiele app k ## Vertalingen -Je kunt [hier](https://immich.app/docs/developer/translations) meer over vertalingen lezen. +Je kunt [hier](https://docs.immich.app/developer/translations) meer over vertalingen lezen. ## Repository activiteit diff --git a/readme_i18n/README_pt_BR.md b/readme_i18n/README_pt_BR.md index 2320e8fd6f..d6f51cd779 100644 --- a/readme_i18n/README_pt_BR.md +++ b/readme_i18n/README_pt_BR.md @@ -29,7 +29,8 @@ Deutsch Nederlands TÃŧrkçe -中文 +įŽ€äŊ“中文 +æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК Svenska @@ -55,14 +56,14 @@ ## Links -- [DocumentaÃ§ÃŖo](https://immich.app/docs) -- [Sobre](https://immich.app/docs/overview/introduction) -- [InstalaÃ§ÃŖo](https://immich.app/docs/install/requirements) +- [DocumentaÃ§ÃŖo](https://docs.immich.app) +- [Sobre](https://docs.immich.app/overview/introduction) +- [InstalaÃ§ÃŖo](https://docs.immich.app/install/requirements) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [DemonstraÃ§ÃŖo](#demonstraÃ§ÃŖo) - [Funcionalidades](#funcionalidades) -- [TraduçÃĩes](https://immich.app/docs/developer/translations) -- [Diretrizes de ContribuiÃ§ÃŖo](https://immich.app/docs/overview/support-the-project) +- [TraduçÃĩes](https://docs.immich.app/developer/translations) +- [Diretrizes de ContribuiÃ§ÃŖo](https://docs.immich.app/overview/support-the-project) ## DemonstraÃ§ÃŖo @@ -115,7 +116,7 @@ Acesse a demonstraÃ§ÃŖo [aqui](https://demo.immich.app). No aplicativo para disp ## TraduçÃĩes Leia mais sobre as traduçÃĩes -[aqui](https://immich.app/docs/developer/translations). +[aqui](https://docs.immich.app/developer/translations). Status da traduÃ§ÃŖo diff --git a/readme_i18n/README_ru_RU.md b/readme_i18n/README_ru_RU.md index be97259acc..e29adde9c1 100644 --- a/readme_i18n/README_ru_RU.md +++ b/readme_i18n/README_ru_RU.md @@ -28,7 +28,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа PortuguÃĒs Brasileiro Svenska @@ -51,14 +52,14 @@ ## ХОдĐĩŅ€ĐļаĐŊиĐĩ -- [ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģҌĐŊĐ°Ņ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ](https://immich.app/docs) -- [ВвĐĩĐ´ĐĩĐŊиĐĩ](https://immich.app/docs/overview/introduction) -- [ĐŖŅŅ‚Đ°ĐŊОвĐēа](https://immich.app/docs/install/requirements) +- [ĐžŅ„Đ¸Ņ†Đ¸Đ°ĐģҌĐŊĐ°Ņ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Đ¸Ņ](https://docs.immich.app) +- [ВвĐĩĐ´ĐĩĐŊиĐĩ](https://docs.immich.app/overview/introduction) +- [ĐŖŅŅ‚Đ°ĐŊОвĐēа](https://docs.immich.app/install/requirements) - [ПĐģаĐŊ Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Đēи](https://github.com/orgs/immich-app/projects/1) - [ДĐĩĐŧĐž](#demo) - [ВозĐŧĐžĐļĐŊĐžŅŅ‚Đ¸](#features) -- [ПĐĩŅ€ĐĩвОд](https://immich.app/docs/developer/translations) -- [Гид ĐŋĐž ŅƒŅ‡Đ°ŅŅ‚Đ¸ŅŽ и ĐŋОддĐĩŅ€ĐļĐēĐĩ ĐŋŅ€ĐžĐĩĐēŅ‚Đ°](https://immich.app/docs/overview/support-the-project) +- [ПĐĩŅ€ĐĩвОд](https://docs.immich.app/developer/translations) +- [Гид ĐŋĐž ŅƒŅ‡Đ°ŅŅ‚Đ¸ŅŽ и ĐŋОддĐĩŅ€ĐļĐēĐĩ ĐŋŅ€ĐžĐĩĐēŅ‚Đ°](https://docs.immich.app/overview/support-the-project) ## ДĐĩĐŧĐž @@ -107,7 +108,7 @@ ## ПĐĩŅ€ĐĩвОд -Đ’ŅŅ‘ ĐŋŅ€Đž ĐŋĐĩŅ€ĐĩвОд ĐŋŅ€ĐžĐĩĐēŅ‚Đ° [ЗдĐĩҁҌ](https://immich.app/docs/developer/translations). +Đ’ŅŅ‘ ĐŋŅ€Đž ĐŋĐĩŅ€ĐĩвОд ĐŋŅ€ĐžĐĩĐēŅ‚Đ° [ЗдĐĩҁҌ](https://docs.immich.app/developer/translations). Translation status diff --git a/readme_i18n/README_sv_SE.md b/readme_i18n/README_sv_SE.md index daa68b9874..a421c23c2e 100644 --- a/readme_i18n/README_sv_SE.md +++ b/readme_i18n/README_sv_SE.md @@ -29,7 +29,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -46,13 +47,13 @@ ## InnehÃĨll -- [Officiell Dokumentation](https://immich.app/docs) +- [Officiell Dokumentation](https://docs.immich.app) - [Roadmap](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Funktioner](#features) -- [Introduktion](https://immich.app/docs/overview/introduction) -- [Installation](https://immich.app/docs/install/requirements) -- [Riktlinjer fÃļr Bidrag](https://immich.app/docs/overview/support-the-project) +- [Introduktion](https://docs.immich.app/overview/introduction) +- [Installation](https://docs.immich.app/install/requirements) +- [Riktlinjer fÃļr Bidrag](https://docs.immich.app/overview/support-the-project) ## Dokumentation diff --git a/readme_i18n/README_th_TH.md b/readme_i18n/README_th_TH.md index bdb6db868d..cdc28b14e6 100644 --- a/readme_i18n/README_th_TH.md +++ b/readme_i18n/README_th_TH.md @@ -31,7 +31,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -52,14 +53,14 @@ ## ā¸Ĩā¸´ā¸‡ā¸āšŒ -- [ā¸„ā¸šāšˆā¸Ąā¸ˇā¸­](https://immich.app/docs) -- [āš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸š](https://immich.app/docs/overview/introduction) -- [ā¸ā¸˛ā¸Ŗā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡](https://immich.app/docs/install/requirements) +- [ā¸„ā¸šāšˆā¸Ąā¸ˇā¸­](https://docs.immich.app) +- [āš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸š](https://docs.immich.app/overview/introduction) +- [ā¸ā¸˛ā¸Ŗā¸•ā¸´ā¸”ā¸•ā¸ąāš‰ā¸‡](https://docs.immich.app/install/requirements) - [āš‚ā¸Ŗā¸”āšā¸Ąā¸›](https://immich.app/roadmap) - [ā¸Ēā¸˛ā¸˜ā¸´ā¸•](#ā¸Ēā¸˛ā¸˜ā¸´ā¸•) - [⏄⏏⏓ā¸Ēā¸Ąā¸šā¸ąā¸•ā¸´](#⏄⏏⏓ā¸Ēā¸Ąā¸šā¸ąā¸•ā¸´) -- [ā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩā¸ ā¸˛ā¸Šā¸˛](https://immich.app/docs/developer/translations) -- [ā¸Ēā¸™ā¸ąā¸šā¸Ēā¸™ā¸¸ā¸™āš‚ā¸žā¸Ŗāš€ā¸ˆā¸ā¸•āšŒ](https://immich.app/docs/overview/support-the-project) +- [ā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩā¸ ā¸˛ā¸Šā¸˛](https://docs.immich.app/developer/translations) +- [ā¸Ēā¸™ā¸ąā¸šā¸Ēā¸™ā¸¸ā¸™āš‚ā¸žā¸Ŗāš€ā¸ˆā¸ā¸•āšŒ](https://docs.immich.app/overview/support-the-project) ## ā¸Ēā¸˛ā¸˜ā¸´ā¸• @@ -106,7 +107,7 @@ ## ā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩā¸ ā¸˛ā¸Šā¸˛ -ā¸­āšˆā¸˛ā¸™āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩ [⏗ā¸ĩāšˆā¸™ā¸ĩāšˆ](https://immich.app/docs/developer/translations) +ā¸­āšˆā¸˛ā¸™āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩ [⏗ā¸ĩāšˆā¸™ā¸ĩāšˆ](https://docs.immich.app/developer/translations) ā¸Ēā¸–ā¸˛ā¸™ā¸°ā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩ diff --git a/readme_i18n/README_tr_TR.md b/readme_i18n/README_tr_TR.md index 6285ab55a2..46aef49745 100644 --- a/readme_i18n/README_tr_TR.md +++ b/readme_i18n/README_tr_TR.md @@ -27,7 +27,8 @@ 한ęĩ­ė–´ Deutsch Nederlands - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -44,13 +45,13 @@ ## Content -- [Resmi Belgeler](https://immich.app/docs) +- [Resmi Belgeler](https://docs.immich.app) - [Yol HaritasÄą](https://github.com/orgs/immich-app/projects/1) - [Demo](#demo) - [Özellikler](#Ãļzellikler) -- [Giriş](https://immich.app/docs/overview/introduction) -- [Kurulum](https://immich.app/docs/install/requirements) -- [KatkÄą Sağlama Rehberi](https://immich.app/docs/overview/support-the-project) +- [Giriş](https://docs.immich.app/overview/introduction) +- [Kurulum](https://docs.immich.app/install/requirements) +- [KatkÄą Sağlama Rehberi](https://docs.immich.app/overview/support-the-project) ## Belgeler diff --git a/readme_i18n/README_uk_UA.md b/readme_i18n/README_uk_UA.md index 5a33fa210d..297054ee42 100644 --- a/readme_i18n/README_uk_UA.md +++ b/readme_i18n/README_uk_UA.md @@ -29,7 +29,8 @@ Deutsch Nederlands TÃŧrkçe - 中文 + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro Svenska @@ -42,26 +43,26 @@ - âš ī¸ ĐĻĐĩĐš ĐŋŅ€ĐžŅ”ĐēŅ‚ ĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°Ņ” **в Đ´ŅƒĐļĐĩ аĐēŅ‚Đ¸Đ˛ĐŊŅ–Đš** Ņ€ĐžĐˇŅ€ĐžĐąŅ†Ņ–. - âš ī¸ ĐžŅ‡Ņ–ĐēŅƒĐšŅ‚Đĩ ĐąĐĩСĐģҖ҇ ĐŋĐžĐŧиĐģĐžĐē Ņ– ĐŗĐģОйаĐģҌĐŊĐ¸Ņ… СĐŧŅ–ĐŊ. -- âš ī¸ **НĐĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš Đ´ĐžĐ´Đ°Ņ‚ĐžĐē ŅĐē Ņ”Đ´Đ¸ĐŊĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ ŅĐ˛ĐžŅ—Ņ… Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.** +- âš ī¸ **НĐĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐšŅ‚Đĩ ҆ĐĩĐš ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐžĐē ŅĐē Ņ”Đ´Đ¸ĐŊĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ ŅĐ˛ĐžŅ—Ņ… Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž.** - âš ī¸ ЗавĐļди Đ´ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐšŅ‚ĐĩҁҌ [ĐŋĐģаĐŊ҃ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ 3-2-1](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/) Đ´ĐģŅ Đ˛Đ°ŅˆĐ¸Ņ… Đ´ĐžŅ€ĐžĐŗĐžŅ†Ņ–ĐŊĐŊĐ¸Ņ… Ņ„ĐžŅ‚ĐžĐŗŅ€Đ°Ņ„Ņ–Đš Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž! > [!NOTE] -> ĐžŅĐŊОвĐŊ҃ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–ŅŽ, СОĐēŅ€ĐĩĐŧа ĐŋĐžŅŅ–ĐąĐŊиĐēи С Đ˛ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ, ĐŧĐžĐļĐŊа СĐŊĐ°ĐšŅ‚Đ¸ Са Đ°Đ´Ņ€ĐĩŅĐžŅŽ https://immich.app/. +> ĐžŅĐŊОвĐŊ҃ Đ´ĐžĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–ŅŽ, СОĐēŅ€ĐĩĐŧа ĐŋĐžŅŅ–ĐąĐŊиĐēи ĐˇŅ– Đ˛ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ, ĐŧĐžĐļĐŊа СĐŊĐ°ĐšŅ‚Đ¸ Са Đ°Đ´Ņ€ĐĩŅĐžŅŽ https://immich.app/. ## ĐŸĐžŅĐ¸ĐģаĐŊĐŊŅ -- [ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ](https://immich.app/docs) -- [ĐŸŅ€Đž ĐŋŅ€ĐžŅ”ĐēŅ‚](https://immich.app/docs/overview/introduction) -- [Đ’ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ](https://immich.app/docs/install/requirements) +- [ДоĐē҃ĐŧĐĩĐŊŅ‚Đ°Ņ†Ņ–Ņ](https://docs.immich.app) +- [ĐŸŅ€Đž ĐŋŅ€ĐžŅ”ĐēŅ‚](https://docs.immich.app/overview/introduction) +- [Đ’ŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ](https://docs.immich.app/install/requirements) - [Đ”ĐžŅ€ĐžĐļĐŊŅ ĐēĐ°Ņ€Ņ‚Đ°](https://immich.app/roadmap) - [ДĐĩĐŧĐž](#Đ´ĐĩĐŧĐž) - [Đ¤ŅƒĐŊĐē҆Җҗ](#Ņ„ŅƒĐŊĐē҆Җҗ) -- [ПĐĩŅ€ĐĩĐēĐģади](https://immich.app/docs/developer/translations) -- [Đ“Ņ–Đ´ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐēи ĐŋŅ€ĐžŅ”ĐēŅ‚Ņƒ](https://immich.app/docs/overview/support-the-project) +- [ПĐĩŅ€ĐĩĐēĐģади](https://docs.immich.app/developer/translations) +- [Đ“Ņ–Đ´ Đ´ĐģŅ Ņ€ĐžĐˇŅ€ĐžĐąĐēи ĐŋŅ€ĐžŅ”ĐēŅ‚Ņƒ](https://docs.immich.app/overview/support-the-project) ## ДĐĩĐŧĐž -Đ”ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ´ĐĩĐŧĐž-вĐĩҀҁҖҗ [Ņ‚ŅƒŅ‚](https://demo.immich.app). ДĐģŅ ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž Đ´ĐžĐ´Đ°Ņ‚Đē҃ ви ĐŧĐžĐļĐĩŅ‚Đĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ `https://demo.immich.app` в ŅĐēĐžŅŅ‚Ņ– `Server Endpoint URL`. +Đ”ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ´ĐĩĐŧĐž-вĐĩҀҁҖҗ [Ņ‚ŅƒŅ‚](https://demo.immich.app). ДĐģŅ ĐŧĐžĐąŅ–ĐģҌĐŊĐžĐŗĐž ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃ ви ĐŧĐžĐļĐĩŅ‚Đĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ `https://demo.immich.app` в ŅĐēĐžŅŅ‚Ņ– `Server Endpoint URL`. ### ОбĐģŅ–ĐēĐžĐ˛Ņ– даĐŊŅ– Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Ņƒ @@ -74,7 +75,7 @@ | Đ¤ŅƒĐŊĐē҆Җҗ | Đ”ĐžĐ´Đ°Ņ‚ĐžĐē | ВĐĩĐą | | :------------------------------------------------------- | ------- | --- | | ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Ņ‚Đ° ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´ Đ˛Ņ–Đ´ĐĩĐž Đš Ņ„ĐžŅ‚Đž | ĐĸаĐē | ĐĸаĐē | -| ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đ¸ Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Ņ‚Ņ– Đ´ĐžĐ´Đ°Ņ‚Đēа | ĐĸаĐē | Н/Д | +| ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐĩ ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đ¸ Đ˛Ņ–Đ´ĐēŅ€Đ¸Ņ‚Ņ‚Ņ– ĐˇĐ°ŅŅ‚ĐžŅŅƒĐŊĐē҃ | ĐĸаĐē | Н/Д | | ЗаĐŋĐžĐąŅ–ĐŗĐ°ĐŊĐŊŅ Đ´ŅƒĐąĐģŅŽĐ˛Đ°ĐŊĐŊŅŽ Ņ„Đ°ĐšĐģŅ–Đ˛ | ĐĸаĐē | ĐĸаĐē | | Đ’Đ¸ĐąŅ–Ņ€ аĐģŅŒĐąĐžĐŧŅ–Đ˛ Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ĐŊĐžĐŗĐž ĐēĐžĐŋŅ–ŅŽĐ˛Đ°ĐŊĐŊŅ | ĐĸаĐē | Н/Д | | ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Ņ„ĐžŅ‚Đž Ņ‚Đ° Đ˛Ņ–Đ´ĐĩĐž ĐŊа ĐģĐžĐēаĐģҌĐŊиК ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš | ĐĸаĐē | ĐĸаĐē | @@ -106,13 +107,13 @@ ## ПĐĩŅ€ĐĩĐēĐģади -Đ‘Ņ–ĐģҌ҈Đĩ ĐŋŅ€Đž ĐŋĐĩŅ€ĐĩĐēĐģади [Ņ‚ŅƒŅ‚](https://immich.app/docs/developer/translations). +Đ‘Ņ–ĐģҌ҈Đĩ ĐŋŅ€Đž ĐŋĐĩŅ€ĐĩĐēĐģади [Ņ‚ŅƒŅ‚](https://docs.immich.app/developer/translations). ĐĄŅ‚Đ°Ņ‚ŅƒŅ ĐŋĐĩŅ€ĐĩĐēĐģĐ°Đ´Ņ–Đ˛ -## АĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ Ņ€ĐĩĐŋĐžĐˇĐ¸Ņ‚Đ°Ņ€Ņ–ŅŽ +## АĐēŅ‚Đ¸Đ˛ĐŊŅ–ŅŅ‚ŅŒ Ņ€ĐĩĐŋĐžĐˇĐ¸Ņ‚ĐžŅ€Ņ–ŅŽ ![Đ”Ņ–ŅĐģҌĐŊŅ–ŅŅ‚ŅŒ](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Đ—ĐžĐąŅ€Đ°ĐļĐĩĐŊĐŊŅ аĐŊаĐģŅ–Ņ‚Đ¸Đēи Repobeats") diff --git a/readme_i18n/README_vi_VN.md b/readme_i18n/README_vi_VN.md index fd04bd9fa1..b6b22ff610 100644 --- a/readme_i18n/README_vi_VN.md +++ b/readme_i18n/README_vi_VN.md @@ -29,7 +29,8 @@ Deutsch Nederlands TÃŧrkçe -中文 +įŽ€äŊ“中文 +æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -52,14 +53,14 @@ ## LiÃĒn káēŋt -- [Tài liáģ‡u](https://immich.app/docs) -- [Giáģ›i thiáģ‡u](https://immich.app/docs/overview/introduction) -- [Cài đáēˇt](https://immich.app/docs/install/requirements) +- [Tài liáģ‡u](https://docs.immich.app) +- [Giáģ›i thiáģ‡u](https://docs.immich.app/overview/introduction) +- [Cài đáēˇt](https://docs.immich.app/install/requirements) - [Láģ™ trÃŦnh](https://immich.app/roadmap) - [Demo](#demo) - [Tính năng](#Tính-năng) -- [Dáģ‹ch thuáē­t](https://immich.app/docs/developer/translations) -- [ÄÃŗng gÃŗp](https://immich.app/docs/overview/support-the-project) +- [Dáģ‹ch thuáē­t](https://docs.immich.app/developer/translations) +- [ÄÃŗng gÃŗp](https://docs.immich.app/overview/support-the-project) ## Demo @@ -106,7 +107,7 @@ Truy cáē­p báēŖn demo [taĖŖi đÃĸy](https://demo.immich.app). Đáģ‘i váģ›i áģŠng ## Dáģ‹ch thuáē­t -Đáģc thÃĒm váģ dáģ‹ch thuáē­t [taĖŖi đÃĸy](https://immich.app/docs/developer/translations). +Đáģc thÃĒm váģ dáģ‹ch thuáē­t [taĖŖi đÃĸy](https://docs.immich.app/developer/translations). TÃŦnh tráēĄng dáģ‹ch thuáē­t diff --git a/readme_i18n/README_zh_CN.md b/readme_i18n/README_zh_CN.md index 5151e35379..b48e69f94d 100644 --- a/readme_i18n/README_zh_CN.md +++ b/readme_i18n/README_zh_CN.md @@ -32,6 +32,7 @@ Deutsch Nederlands TÃŧrkçe + æ­ŖéĢ”ä¸­æ–‡ ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа Đ ŅƒŅŅĐēиК PortuguÃĒs Brasileiro @@ -54,14 +55,14 @@ ## į›ŽåŊ• -- [åŽ˜æ–šæ–‡æĄŖ](https://immich.app/docs) -- [éĄšį›Žæ€ģ览](https://immich.app/docs/overview/introduction) -- [åŽ‰čŖ…æ•™į¨‹](https://immich.app/docs/install/requirements) +- [åŽ˜æ–šæ–‡æĄŖ](https://docs.immich.app) +- [éĄšį›Žæ€ģ览](https://docs.immich.app/overview/introduction) +- [åŽ‰čŖ…æ•™į¨‹](https://docs.immich.app/install/requirements) - [莝įēŋ回](https://immich.app/roadmap) - [在įēŋæŧ”į¤ē](#į¤ē例) - [功čƒŊį‰šæ€§](#功čƒŊį‰šæ€§) -- [å¤šč¯­č¨€](https://immich.app/docs/developer/translations) -- [č´ĄįŒŽč€…](https://immich.app/docs/overview/support-the-project) +- [å¤šč¯­č¨€](https://docs.immich.app/developer/translations) +- [č´ĄįŒŽč€…](https://docs.immich.app/overview/support-the-project) ## į¤ē例 @@ -110,7 +111,7 @@ ## å¤šč¯­č¨€ -å…ŗäēŽįŋģč¯‘įš„æ›´å¤šäŋĄæ¯č¯ˇå‚见[此处](https://immich.app/docs/developer/translations)。 +å…ŗäēŽįŋģč¯‘įš„æ›´å¤šäŋĄæ¯č¯ˇå‚见[此处](https://docs.immich.app/developer/translations)。 įŋģ蝑čŋ›åēĻ diff --git a/readme_i18n/README_zh_TW.md b/readme_i18n/README_zh_TW.md new file mode 100644 index 0000000000..bf14ce2dc0 --- /dev/null +++ b/readme_i18n/README_zh_TW.md @@ -0,0 +1,132 @@ +

+
+
授æŦŠæĸæŦžīŧšAGPLv3 + + Discord + +
+
+

+ +

+ +

+

éĢ˜æ•ˆčƒŊįš„č‡Ēæžļį…§į‰‡å’ŒåŊąį‰‡įŽĄį†č§Ŗæąēæ–šæĄˆ

+
+ + + +
+ +

+ English + Català + EspaÃąol + Français + Italiano + æ—ĨæœŦčĒž + 한ęĩ­ė–´ + Deutsch + Nederlands + TÃŧrkçe + įŽ€äŊ“中文 + æ­ŖéĢ”ä¸­æ–‡ + ĐŖĐēŅ€Đ°Ņ—ĐŊҁҌĐēа + Đ ŅƒŅŅĐēиК + PortuguÃĒs Brasileiro + Svenska + Ø§Ų„ØšØąØ¨ŲŠØŠ + Tiáēŋng Viáģ‡t + ā¸ ā¸˛ā¸Šā¸˛āš„ā¸—ā¸ĸ +

+ +> [!WARNING] +> âš ī¸ č̋務åŋ…éĩåžĒ [3-2-1 備äģŊ原則](https://www.backblaze.com/blog/the-3-2-1-backup-strategy/)īŧŒåŽˆč­ˇįč˛´įš„į…§į‰‡čˆ‡åŊąį‰‡īŧ +> + +> [!NOTE] +> ä¸ģčρčĒĒæ˜Žæ–‡äģļīŧˆåŒ…åĢåŽ‰čŖæŒ‡å—īŧ‰å¯æ–ŧ https://immich.app/ 取垗。 + +## 逪įĩ + +- [čĒĒæ˜Žæ–‡äģļ](https://docs.immich.app/) +- [關æ–ŧ](https://docs.immich.app/overview/introduction) +- [åŽ‰čŖ](https://docs.immich.app/install/requirements) +- [į™ŧåą•č—åœ–](https://immich.app/roadmap) +- [᎚䏊éĢ”éŠ—](#᎚䏊éĢ”éŠ—) +- [功čƒŊ](#功čƒŊ) +- [įŋģč­¯](https://docs.immich.app/developer/translations) +- [č˛ĸįģ指南](https://docs.immich.app/overview/support-the-project) + +## ᎚䏊éĢ”éŠ— + +čĢ‹å‰åž€ [Demoo įļ˛įĢ™](https://demo.immich.app) įĢ‹åŗéĢ”éŠ— Immich。č‹ĨčĻåœ¨æ‰‹æŠŸ App čŠĻᔍīŧŒčĢ‹åœ¨ `äŧ翜å™¨į̝éģž URL` æŦ„äŊčŧ¸å…Ĩ `https://demo.immich.app`。 + +### į™ģå…Ĩčŗ‡č¨Š + +| é›ģ子éƒĩäģļ | 密įĸŧ | +| --------------- | ------ | +| demo@immich.app | demo | + +## 功čƒŊ + +| 功čƒŊ | æ‰‹æŠŸį‰ˆ | įļ˛é į‰ˆ | +| :----------------------------------------- | ------ | ------ | +| ä¸Šå‚ŗčˆ‡æĒĸčĻ–į…§į‰‡čˆ‡åŊąį‰‡ | 是 | 是 | +| 開啟 App 時č‡Ē動備äģŊ | 是 | ä¸éŠį”¨ | +| éŋå…é‡č¤‡åĒ’éĢ” | 是 | 是 | +| 選擇čρ備äģŊįš„į›¸į°ŋ | 是 | ä¸éŠį”¨ | +| 下čŧ‰į…§į‰‡čˆ‡åŊąį‰‡åˆ°æœŦæŠŸčŖįŊŽ | 是 | 是 | +| 多äŊŋį”¨č€…æ”¯æ´ | 是 | 是 | +| ᛏį°ŋčˆ‡å…ąäēĢᛏį°ŋ | 是 | 是 | +| å¯æ‹–æ›ŗįš„æ˛čģ¸ | 是 | 是 | +| 支援 RAW æ ŧåŧ | 是 | 是 | +| 中įšŧčŗ‡æ–™æĒĸčĻ–īŧˆEXIF、地圖īŧ‰ | 是 | 是 | +| 䞝中įšŧčŗ‡æ–™ã€į‰Šäģļã€č‡‰å­”čˆ‡ CLIP 搜尋 | 是 | 是 | +| įŽĄį†åŠŸčƒŊīŧˆäŊŋį”¨č€…įŽĄį†īŧ‰ | åĻ | 是 | +| čƒŒæ™¯å‚™äģŊ | 是 | ä¸éŠį”¨ | +| 虛æ“Ŧæģžå‹• | 是 | 是 | +| 支援 OAuth | 是 | 是 | +| API 金鑰 | ä¸éŠį”¨ | 是 | +| Live Photo/å‹•æ…‹į…§į‰‡å‚™äģŊčˆ‡æ’­æ”ž | 是 | 是 | +| 支援 360 åēĻå…¨æ™¯į…§į‰‡éĄ¯į¤ē | åĻ | 是 | +| äŊŋᔍ者č‡Ēč¨‚å„˛å­˜įĩæ§‹ | 是 | 是 | +| å…Ŧ開分äēĢ | 是 | 是 | +| å°å­˜čˆ‡æ”ļ藏 | 是 | 是 | +| ä¸–į•Œåœ°åœ– | 是 | 是 | +| čĻĒæœ‹åĨŊ友分äēĢ | 是 | 是 | +| č‡‰éƒ¨čž¨č­˜čˆ‡åˆ†įž¤ | 是 | 是 | +| 回æ†ļīŧˆx 嚴前īŧ‰ | 是 | 是 | +| é›ĸįˇšæ”¯æ´ | 是 | åĻ | +| å”¯čŽ€åĒ’éĢ”åēĢ | 是 | 是 | +| į…§į‰‡å †į–Š | 是 | 是 | +| æ¨™įą¤ | åĻ | 是 | +| čŗ‡æ–™å¤žæĒĸčĻ– | 是 | 是 | + +## įŋģč­¯ + +更多įŋģč­¯į›¸é—œčŗ‡č¨ŠčĢ‹čĻ‹ [æ­¤č™•](https://docs.immich.app/developer/translations)。 + + +įŋģč­¯į‹€æ…‹ + + +## å°ˆæĄˆæ´ģčēåēĻ + +![å°ˆæĄˆæ´ģčēåēĻ](https://repobeats.axiom.co/api/embed/9e86d9dc3ddd137161f2f6d2e758d7863b1789cb.svg "Repobeats åˆ†æžåœ–į‰‡") + +## Star æ•¸é‡æ­ˇå˛į´€éŒ„ + + + + + + Star æ­ˇå˛į´€éŒ„åœ–čĄ¨ + + + +## č˛ĸįģ者 + + + + diff --git a/server/.npmignore b/server/.npmignore new file mode 100644 index 0000000000..cb975079af --- /dev/null +++ b/server/.npmignore @@ -0,0 +1,5 @@ +src +tsconfig* +eslint* +pnpm* +coverage diff --git a/server/.nvmrc b/server/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/server/Dockerfile b/server/Dockerfile index e082d0e69e..05cd4601be 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,118 +1,67 @@ -# dev build -FROM ghcr.io/immich-app/base-server-dev:202507162011@sha256:85d4230c2208646bd6c528db41b2213d780b11b7a311397ca6a2aaba7cf697c8 AS dev +FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS builder +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ + CI=1 \ + COREPACK_HOME=/tmp \ + PNPM_HOME=/buildcache/pnpm-store \ + PATH="/buildcache/pnpm-store:$PATH" + +RUN npm install --global corepack@latest && \ + corepack enable pnpm && \ + pnpm config set store-dir "$PNPM_HOME" + +FROM builder AS server WORKDIR /usr/src/app -COPY ./server/package* ./server/ -WORKDIR /usr/src/app/server -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 -ENV PATH="${PATH}:/usr/src/app/server/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] +COPY ./server ./server/ +RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \ + SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned -FROM dev AS dev-container-server - -RUN rm -rf /usr/src/app -RUN apt-get update && \ - apt-get install sudo inetutils-ping openjdk-11-jre-headless \ - vim nano \ - -y --no-install-recommends --fix-missing - -RUN usermod -aG sudo node -RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -RUN mkdir -p /workspaces/immich -RUN chown node -R /workspaces -COPY --chown=node:node --chmod=777 ../.devcontainer/server/*.sh /immich-devcontainer/ - -USER node -COPY --chown=node:node .. /tmp/create-dep-cache/ -WORKDIR /tmp/create-dep-cache -RUN make ci-all && rm -rf /tmp/create-dep-cache - - -FROM dev-container-server AS dev-container-mobile -USER root -# Enable multiarch for arm64 if necessary -RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ - dpkg --add-architecture amd64 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64; \ - fi - -# Flutter SDK -# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux -ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.32.6" -ENV FLUTTER_HOME=/flutter -ENV PATH=${PATH}:${FLUTTER_HOME}/bin - -# Flutter SDK -RUN mkdir -p ${FLUTTER_HOME} \ - && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ - && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ - && rm flutter.tar.xz \ - && chown -R node ${FLUTTER_HOME} - -USER node -RUN sudo apt-get update \ - && wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ - && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list \ - && sudo apt-get update \ - && sudo apt-get install dcm -y - -COPY --chmod=777 ../.devcontainer/mobile/container-mobile-post-create.sh /immich-devcontainer/container-mobile-post-create.sh - -RUN dart --disable-analytics - -FROM dev AS prod - -COPY server . -RUN npm run build -RUN npm prune --omit=dev --omit=optional -COPY --from=dev /usr/src/app/server/node_modules/@img ./node_modules/@img -COPY --from=dev /usr/src/app/server/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl - -# web build -FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web +FROM builder AS web WORKDIR /usr/src/app COPY ./web ./web/ COPY ./i18n ./i18n/ -COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/ +COPY ./open-api ./open-api/ +RUN --mount=type=cache,id=pnpm-web,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter @immich/sdk --filter immich-web --frozen-lockfile --force install && \ + pnpm --filter @immich/sdk --filter immich-web build -WORKDIR /usr/src/app/open-api/typescript-sdk -RUN npm ci && npm run build +FROM builder AS cli -WORKDIR /usr/src/app/web -RUN npm ci && npm run build +COPY ./cli ./cli/ +COPY ./open-api ./open-api/ +RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \ + --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \ + --mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \ + --mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \ + pnpm --filter @immich/sdk --filter @immich/cli --frozen-lockfile install && \ + pnpm --filter @immich/sdk --filter @immich/cli build && \ + pnpm --filter @immich/cli --prod --no-optional deploy /output/cli-pruned -# prod build -FROM ghcr.io/immich-app/base-server-prod:202507162011@sha256:636f3ddb6106628ef851d51c23f3fa2c6e4829390cc315b27b38c288c82b23a7 +FROM ghcr.io/immich-app/base-server-prod:202509210934@sha256:0c7eacf0ba88ca52e1a267cfc62d20d07792ea2c604818c2cbd37dc7dcefdac9 WORKDIR /usr/src/app ENV NODE_ENV=production \ NVIDIA_DRIVER_CAPABILITIES=all \ NVIDIA_VISIBLE_DEVICES=all -COPY --from=prod /usr/src/app/server/node_modules ./server/node_modules -COPY --from=prod /usr/src/app/server/dist ./server/dist -COPY --from=prod /usr/src/app/server/bin ./server/bin +COPY --from=server /output/server-pruned ./server COPY --from=web /usr/src/app/web/build /build/www -COPY ./server/resources ./server/resources -COPY ./server/package.json server/package-lock.json ./ +COPY --from=cli /output/cli-pruned ./cli +RUN ln -s ../../cli/bin/immich server/bin/immich COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE -RUN npm install -g @immich/cli && npm cache clean --force ENV PATH="${PATH}:/usr/src/app/server/bin" ARG BUILD_ID @@ -130,7 +79,7 @@ ENV IMMICH_SOURCE_REF=${BUILD_SOURCE_REF} ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT} ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT} -VOLUME /usr/src/app/upload +VOLUME /data EXPOSE 2283 ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] CMD ["start.sh"] diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev new file mode 100644 index 0000000000..717094cb6b --- /dev/null +++ b/server/Dockerfile.dev @@ -0,0 +1,82 @@ +# dev build +FROM ghcr.io/immich-app/base-server-dev:202509210934@sha256:b5ce2d7eaf379d4cf15efd4bab180d8afc8a80d20b36c9800f4091aca6ae267e AS dev + +ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ + CI=1 \ + COREPACK_HOME=/tmp + +RUN npm install --global corepack@latest && \ + corepack enable pnpm && \ + echo "store-dir=/buildcache/pnpm-store" >> /usr/local/etc/npmrc && \ + echo "devdir=/buildcache/node-gyp" >> /usr/local/etc/npmrc + +COPY ./package* ./pnpm* .pnpmfile.cjs /tmp/create-dep-cache/ +COPY ./web/package* ./web/pnpm* /tmp/create-dep-cache/web/ +COPY ./server/package* ./server/pnpm* /tmp/create-dep-cache/server/ +COPY ./open-api/typescript-sdk/package* ./open-api/typescript-sdk/pnpm* /tmp/create-dep-cache/open-api/typescript-sdk/ +WORKDIR /tmp/create-dep-cache +RUN pnpm fetch && rm -rf /tmp/create-dep-cache && chmod -R o+rw /buildcache +WORKDIR /usr/src/app + +ENV PATH="${PATH}:/usr/src/app/server/bin:/usr/src/app/web/bin" \ + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] + +FROM dev AS dev-container-server + +RUN apt-get update --allow-releaseinfo-change && \ + apt-get install sudo inetutils-ping openjdk-21-jre-headless \ + vim nano curl \ + -y --no-install-recommends --fix-missing + +RUN usermod -aG sudo node && \ + echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \ + mkdir -p /workspaces/immich + +RUN chown node:node -R /workspaces +COPY --chown=node:node --chmod=755 ../.devcontainer/server/*.sh /immich-devcontainer/ + +WORKDIR /workspaces/immich + +FROM dev-container-server AS dev-container-mobile +USER root +# Enable multiarch for arm64 if necessary +RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + gnupg \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + else \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + gnupg; \ + fi + +# Flutter SDK +# https://flutter.dev/docs/development/tools/sdk/releases?tab=linux +ENV FLUTTER_CHANNEL="stable" +ENV FLUTTER_VERSION="3.35.4" +ENV FLUTTER_HOME=/flutter +ENV PATH=${PATH}:${FLUTTER_HOME}/bin + +# Flutter SDK +RUN mkdir -p ${FLUTTER_HOME} \ + && curl -C - --output flutter.tar.xz https://storage.googleapis.com/flutter_infra_release/releases/${FLUTTER_CHANNEL}/linux/flutter_linux_${FLUTTER_VERSION}-${FLUTTER_CHANNEL}.tar.xz \ + && tar -xf flutter.tar.xz --strip-components=1 -C ${FLUTTER_HOME} \ + && rm flutter.tar.xz \ + && chown -R node ${FLUTTER_HOME} \ + && git config --global --add safe.directory ${FLUTTER_HOME} + + +RUN wget -qO- https://dcm.dev/pgp-key.public | gpg --dearmor -o /usr/share/keyrings/dcm.gpg \ + && echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | tee /etc/apt/sources.list.d/dart_stable.list \ + && apt-get update \ + && apt-get install dcm -y + +RUN dart --disable-analytics diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 533c10ef9d..28a0443be7 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -5,5 +5,5 @@ if [ "$IMMICH_ENV" != "development" ]; then exit 1 fi -cd /usr/src/app/server || exit 1 -npm exec nest start --debug "0.0.0.0:9230" --watch -- "$@" +cd /usr/src/app || exit +pnpm --filter immich exec nest start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/bin/start.sh b/server/bin/start.sh index 2b4351a6bc..0afff4c3a8 100755 --- a/server/bin/start.sh +++ b/server/bin/start.sh @@ -1,18 +1,23 @@ #!/usr/bin/env bash echo "Initializing Immich $IMMICH_SOURCE_REF" -lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" -if [ -f "$lib_path" ]; then - export LD_PRELOAD="$lib_path" -else - echo "skipping libmimalloc - path not found $lib_path" -fi +# TODO: Update to mimalloc v3 when verified memory isn't released issue is fixed +# lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.3" +# if [ -f "$lib_path" ]; then +# export LD_PRELOAD="$lib_path" +# else +# echo "skipping libmimalloc - path not found $lib_path" +# fi export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" -SERVER_HOME=/usr/src/app/server +SERVER_HOME="$(readlink -f "$(dirname "$0")/..")" read_file_and_export() { - if [ -n "${!1}" ]; then - content="$(cat "${!1}")" + fname="${!1}" + if [[ -z $fname ]] && [[ -e "$CREDENTIALS_DIRECTORY/$2" ]]; then + fname="${CREDENTIALS_DIRECTORY}/$2" + fi + if [[ -n $fname ]]; then + content="$(< "$fname")" export "$2"="${content}" unset "$1" fi diff --git a/server/nest-cli.json b/server/nest-cli.json index 1eaf1888d5..16a8b8a09b 100644 --- a/server/nest-cli.json +++ b/server/nest-cli.json @@ -3,7 +3,7 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true, + "deleteOutDir": false, "webpack": false, "plugins": [ { diff --git a/server/package-lock.json b/server/package-lock.json deleted file mode 100644 index 11be49ecb6..0000000000 --- a/server/package-lock.json +++ /dev/null @@ -1,19580 +0,0 @@ -{ - "name": "immich", - "version": "1.135.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "immich", - "version": "1.135.3", - "license": "GNU Affero General Public License version 3", - "dependencies": { - "@nestjs/bullmq": "^11.0.1", - "@nestjs/common": "^11.0.4", - "@nestjs/core": "^11.0.4", - "@nestjs/event-emitter": "^3.0.0", - "@nestjs/platform-express": "^11.0.4", - "@nestjs/platform-socket.io": "^11.0.4", - "@nestjs/schedule": "^6.0.0", - "@nestjs/swagger": "^11.0.2", - "@nestjs/websockets": "^11.0.4", - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.62.0", - "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.203.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/instrumentation-ioredis": "^0.51.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", - "@opentelemetry/instrumentation-pg": "^0.55.0", - "@opentelemetry/resources": "^2.0.1", - "@opentelemetry/sdk-metrics": "^2.0.1", - "@opentelemetry/sdk-node": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", - "@react-email/render": "^1.1.2", - "@socket.io/redis-adapter": "^8.3.0", - "archiver": "^7.0.0", - "async-lock": "^1.4.0", - "bcrypt": "^6.0.0", - "body-parser": "^2.2.0", - "bullmq": "^5.51.0", - "chokidar": "^4.0.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "compression": "^1.8.0", - "cookie": "^1.0.2", - "cookie-parser": "^1.4.7", - "cron": "4.3.0", - "exiftool-vendored": "^28.8.0", - "express": "^5.1.0", - "fast-glob": "^3.3.2", - "fluent-ffmpeg": "^2.1.2", - "geo-tz": "^8.0.0", - "handlebars": "^4.7.8", - "i18n-iso-countries": "^7.6.0", - "ioredis": "^5.3.2", - "js-yaml": "^4.1.0", - "kysely": "^0.28.0", - "kysely-postgres-js": "^2.0.0", - "lodash": "^4.17.21", - "luxon": "^3.4.2", - "mnemonist": "^0.40.3", - "multer": "^2.0.1", - "nest-commander": "^3.16.0", - "nestjs-cls": "^5.0.0", - "nestjs-kysely": "^3.0.0", - "nestjs-otel": "^7.0.0", - "nodemailer": "^7.0.0", - "openid-client": "^6.3.3", - "pg": "^8.11.3", - "pg-connection-string": "^2.9.1", - "picomatch": "^4.0.2", - "postgres": "3.4.7", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-email": "^4.0.0", - "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.1", - "sanitize-filename": "^1.6.3", - "sanitize-html": "^2.14.0", - "semver": "^7.6.2", - "sharp": "^0.34.2", - "sirv": "^3.0.0", - "socket.io": "^4.8.1", - "tailwindcss-preset-email": "^1.4.0", - "thumbhash": "^0.1.1", - "typeorm": "^0.3.17", - "ua-parser-js": "^2.0.0", - "validator": "^13.12.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.8.0", - "@nestjs/cli": "^11.0.2", - "@nestjs/schematics": "^11.0.0", - "@nestjs/testing": "^11.0.4", - "@swc/core": "^1.4.14", - "@testcontainers/postgresql": "^11.0.0", - "@testcontainers/redis": "^11.0.0", - "@types/archiver": "^6.0.0", - "@types/async-lock": "^1.4.2", - "@types/bcrypt": "^5.0.0", - "@types/body-parser": "^1.19.6", - "@types/compression": "^1.7.5", - "@types/cookie-parser": "^1.4.8", - "@types/express": "^5.0.0", - "@types/fluent-ffmpeg": "^2.1.21", - "@types/js-yaml": "^4.0.9", - "@types/lodash": "^4.14.197", - "@types/luxon": "^3.6.2", - "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", - "@types/nodemailer": "^6.4.14", - "@types/picomatch": "^4.0.0", - "@types/pngjs": "^6.0.5", - "@types/react": "^19.0.0", - "@types/sanitize-html": "^2.13.0", - "@types/semver": "^7.5.8", - "@types/supertest": "^6.0.0", - "@types/ua-parser-js": "^0.7.36", - "@types/validator": "^13.15.2", - "@vitest/coverage-v8": "^3.0.0", - "canvas": "^3.1.0", - "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", - "globals": "^16.0.0", - "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.1", - "node-gyp": "^11.2.0", - "pngjs": "^7.0.0", - "prettier": "^3.0.2", - "prettier-plugin-organize-imports": "^4.0.0", - "rimraf": "^6.0.0", - "source-map-support": "^0.5.21", - "sql-formatter": "^15.0.0", - "supertest": "^7.1.0", - "tailwindcss": "^3.4.0", - "testcontainers": "^11.0.0", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.3.3", - "typescript-eslint": "^8.28.0", - "unplugin-swc": "^1.4.5", - "utimes": "^5.2.1", - "vite-tsconfig-paths": "^5.0.0", - "vitest": "^3.0.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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": { - "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.8", - "@angular-devkit/schematics": "19.2.8", - "@inquirer/prompts": "7.3.2", - "ansi-colors": "4.1.3", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" - }, - "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/@angular-devkit/core": { - "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": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "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", - "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.1.2", - "@inquirer/confirm": "^5.1.6", - "@inquirer/editor": "^4.2.7", - "@inquirer/expand": "^4.0.9", - "@inquirer/input": "^4.1.6", - "@inquirer/number": "^3.0.9", - "@inquirer/password": "^4.0.9", - "@inquirer/rawlist": "^4.0.9", - "@inquirer/search": "^3.0.9", - "@inquirer/select": "^4.0.9" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { - "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": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/@babel/generator": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@balena/dockerignore": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", - "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "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", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/runtime": { - "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.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "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/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "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/@golevelup/nestjs-discovery": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", - "integrity": "sha512-8w3CsXHN7+7Sn2i419Eal1Iw/kOjAd6Kb55M/ZqKBBwACCMn4WiEuzssC71LpBMI1090CiDxuelfPRwwIrQK+A==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - }, - "peerDependencies": { - "@nestjs/common": "^10.x || ^11.0.0", - "@nestjs/core": "^10.x || ^11.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", - "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.7.13", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "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.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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "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" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz", - "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "5.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz", - "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", - "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/editor": { - "version": "4.2.13", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz", - "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "external-editor": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz", - "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz", - "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz", - "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz", - "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.1.tgz", - "integrity": "sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.1.5", - "@inquirer/confirm": "^5.1.9", - "@inquirer/editor": "^4.2.10", - "@inquirer/expand": "^4.0.12", - "@inquirer/input": "^4.1.9", - "@inquirer/number": "^3.0.12", - "@inquirer/password": "^4.0.12", - "@inquirer/rawlist": "^4.0.12", - "@inquirer/search": "^3.0.12", - "@inquirer/select": "^4.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz", - "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz", - "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.3.tgz", - "integrity": "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", - "license": "MIT" - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "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", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/@lukeed/csprng": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass/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/@mapbox/node-pre-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib/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/@mapbox/node-pre-gyp/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/@microsoft/tsdoc": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", - "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", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "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", - "integrity": "sha512-dFlttJvBqIFD6M8JVFbkrR4Feb39OTAJPJpFVILU50NOJCM4qziRw3dSNG84Q3v+7/M6xUGMFdZRRGvBBKxoSA==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/bullmq": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/bullmq/-/bullmq-11.0.2.tgz", - "integrity": "sha512-Lq6lGpKkETsm0RDcUktlzsthFoE3A5QTMp2FwPi1eztKqKD6/90KS1TcnC9CJFzjpUaYnQzIMrlNs55e+/wsHA==", - "license": "MIT", - "dependencies": { - "@nestjs/bull-shared": "^11.0.2", - "tslib": "2.8.1" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0", - "bullmq": "^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/@nestjs/cli": { - "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.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", - "chokidar": "4.0.3", - "cli-table3": "0.6.5", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "9.1.0", - "glob": "11.0.1", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.2.0", - "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.8.3", - "webpack": "5.99.6", - "webpack-node-externals": "3.0.0" - }, - "bin": { - "nest": "bin/nest.js" - }, - "engines": { - "node": ">= 20.11" - }, - "peerDependencies": { - "@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": { - "@swc/cli": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { - "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": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@nestjs/cli/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/cli/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@nestjs/cli/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", - "license": "MIT", - "dependencies": { - "file-type": "21.0.0", - "iterare": "1.2.1", - "load-esm": "1.0.2", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": ">=0.4.1", - "class-validator": ">=0.13.2", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@nuxt/opencollective": "0.4.1", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "8.2.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "engines": { - "node": ">= 20" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - }, - "@nestjs/websockets": { - "optional": true - } - } - }, - "node_modules/@nestjs/event-emitter": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", - "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", - "license": "MIT", - "dependencies": { - "eventemitter2": "6.4.9" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/mapped-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", - "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "class-transformer": "^0.4.0 || ^0.5.0", - "class-validator": "^0.13.0 || ^0.14.0", - "reflect-metadata": "^0.1.12 || ^0.2.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", - "license": "MIT", - "dependencies": { - "cors": "2.8.5", - "express": "5.1.0", - "multer": "2.0.1", - "path-to-regexp": "8.2.0", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", - "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", - "license": "MIT", - "dependencies": { - "socket.io": "4.8.1", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "rxjs": "^7.1.0" - } - }, - "node_modules/@nestjs/schedule": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.0.0.tgz", - "integrity": "sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==", - "license": "MIT", - "dependencies": { - "cron": "4.3.0" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/schematics": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.5.tgz", - "integrity": "sha512-T50SCNyqCZ/fDssaOD7meBKLZ87ebRLaJqZTJPvJKjlib1VYhMOCwXYsr7bjMPmuPgiQHOwvppz77xN/m6GM7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.6", - "@angular-devkit/schematics": "19.2.6", - "comment-json": "4.2.5", - "jsonc-parser": "3.3.1", - "pluralize": "8.0.0" - }, - "peerDependencies": { - "typescript": ">=4.8.2" - } - }, - "node_modules/@nestjs/schematics/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.6.tgz", - "integrity": "sha512-YTAxNnT++5eflx19OUHmOWu597/TbTel+QARiZCv1xQw99+X8DCKKOUXtqBRd53CAHlREDI33Rn/JLY3NYgMLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.6", - "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/schematics/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nestjs/swagger": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.0.tgz", - "integrity": "sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg==", - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.15.1", - "@nestjs/mapped-types": "2.1.0", - "js-yaml": "4.1.0", - "lodash": "4.17.21", - "path-to-regexp": "8.2.0", - "swagger-ui-dist": "5.21.0" - }, - "peerDependencies": { - "@fastify/static": "^8.0.0", - "@nestjs/common": "^11.0.1", - "@nestjs/core": "^11.0.1", - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12 || ^0.2.0" - }, - "peerDependenciesMeta": { - "@fastify/static": { - "optional": true - }, - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - } - } - }, - "node_modules/@nestjs/websockets": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", - "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", - "license": "MIT", - "dependencies": { - "iterare": "1.2.1", - "object-hash": "3.0.0", - "tslib": "2.8.1" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0", - "@nestjs/platform-socket.io": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/platform-socket.io": { - "optional": true - } - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "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/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", - "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": "^14.18.0 || >=16.10.0", - "npm": ">=5.10.0" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.62.0.tgz", - "integrity": "sha512-h5g+VNJjiyX6u/IQpn36ZCHOENg1QW0GgBOHBcFGnHBBhmTww4R3brExdeuYbvLj3UQY09n+UHFEoMOqkhq07A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/instrumentation-amqplib": "^0.50.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.54.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.56.0", - "@opentelemetry/instrumentation-bunyan": "^0.49.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.49.0", - "@opentelemetry/instrumentation-connect": "^0.47.0", - "@opentelemetry/instrumentation-cucumber": "^0.18.0", - "@opentelemetry/instrumentation-dataloader": "^0.21.0", - "@opentelemetry/instrumentation-dns": "^0.47.0", - "@opentelemetry/instrumentation-express": "^0.52.0", - "@opentelemetry/instrumentation-fastify": "^0.48.0", - "@opentelemetry/instrumentation-fs": "^0.23.0", - "@opentelemetry/instrumentation-generic-pool": "^0.47.0", - "@opentelemetry/instrumentation-graphql": "^0.51.0", - "@opentelemetry/instrumentation-grpc": "^0.203.0", - "@opentelemetry/instrumentation-hapi": "^0.50.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/instrumentation-ioredis": "^0.51.0", - "@opentelemetry/instrumentation-kafkajs": "^0.12.0", - "@opentelemetry/instrumentation-knex": "^0.48.0", - "@opentelemetry/instrumentation-koa": "^0.51.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.48.0", - "@opentelemetry/instrumentation-memcached": "^0.47.0", - "@opentelemetry/instrumentation-mongodb": "^0.56.0", - "@opentelemetry/instrumentation-mongoose": "^0.50.0", - "@opentelemetry/instrumentation-mysql": "^0.49.0", - "@opentelemetry/instrumentation-mysql2": "^0.49.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", - "@opentelemetry/instrumentation-net": "^0.47.0", - "@opentelemetry/instrumentation-oracledb": "^0.29.0", - "@opentelemetry/instrumentation-pg": "^0.55.0", - "@opentelemetry/instrumentation-pino": "^0.50.0", - "@opentelemetry/instrumentation-redis": "^0.51.0", - "@opentelemetry/instrumentation-restify": "^0.49.0", - "@opentelemetry/instrumentation-router": "^0.48.0", - "@opentelemetry/instrumentation-runtime-node": "^0.17.0", - "@opentelemetry/instrumentation-socket.io": "^0.50.0", - "@opentelemetry/instrumentation-tedious": "^0.22.0", - "@opentelemetry/instrumentation-undici": "^0.14.0", - "@opentelemetry/instrumentation-winston": "^0.48.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.31.3", - "@opentelemetry/resource-detector-aws": "^2.3.0", - "@opentelemetry/resource-detector-azure": "^0.10.0", - "@opentelemetry/resource-detector-container": "^0.7.3", - "@opentelemetry/resource-detector-gcp": "^0.37.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-node": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.4.1", - "@opentelemetry/core": "^2.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.0.1.tgz", - "integrity": "sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz", - "integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.203.0.tgz", - "integrity": "sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/sdk-logs": "0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.203.0.tgz", - "integrity": "sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/sdk-logs": "0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.203.0.tgz", - "integrity": "sha512-nl/7S91MXn5R1aIzoWtMKGvqxgJgepB/sH9qW0rZvZtabnsjbf8OQ1uSx3yogtvLr0GzwD596nQKz2fV7q2RBw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-logs": "0.203.0", - "@opentelemetry/sdk-trace-base": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.203.0.tgz", - "integrity": "sha512-FCCj9nVZpumPQSEI57jRAA89hQQgONuoC35Lt+rayWY/mzCAc6BQT7RFyFaZKJ2B7IQ8kYjOCPsF/HGFWjdQkQ==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-metrics": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.203.0.tgz", - "integrity": "sha512-HFSW10y8lY6BTZecGNpV3GpoSy7eaO0Z6GATwZasnT4bEsILp8UJXNG5OmEsz4SdwCSYvyCbTJdNbZP3/8LGCQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-metrics": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.203.0.tgz", - "integrity": "sha512-OZnhyd9npU7QbyuHXFEPVm3LnjZYifuKpT3kTnF84mXeEQ84pJJZgyLBpU4FSkSwUkt/zbMyNAI7y5+jYTWGIg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-metrics": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.203.0.tgz", - "integrity": "sha512-2jLuNuw5m4sUj/SncDf/mFPabUxMZmmYetx5RKIMIQyPnl6G6ooFzfeE8aXNRf8YD1ZXNlCnRPcISxjveGJHNg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-metrics": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.203.0.tgz", - "integrity": "sha512-322coOTf81bm6cAA8+ML6A+m4r2xTCdmAZzGNTboPXRzhwPt4JEmovsFAs+grpdarObd68msOJ9FfH3jxM6wqA==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.203.0.tgz", - "integrity": "sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.203.0.tgz", - "integrity": "sha512-1xwNTJ86L0aJmWRwENCJlH4LULMG2sOXWIVw+Szta4fkqKVY50Eo4HoVKKq6U9QEytrWCr8+zjw0q/ZOeXpcAQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/exporter-zipkin": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.0.1.tgz", - "integrity": "sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/host-metrics": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/host-metrics/-/host-metrics-0.36.0.tgz", - "integrity": "sha512-14lNY57qa21V3ZOl6xrqLMHR0HGlnPIApR6hr3oCw/Dqs5IzxhTwt2X8Stn82vWJJis7j/ezn11oODsizHj2dQ==", - "license": "Apache-2.0", - "dependencies": { - "systeminformation": "5.23.8" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.50.0.tgz", - "integrity": "sha512-kwNs/itehHG/qaQBcVrLNcvXVPW0I4FCOVtw3LHMLdYIqD7GJ6Yv2nX+a4YHjzbzIeRYj8iyMp0Bl7tlkidq5w==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.54.0.tgz", - "integrity": "sha512-uiYI+kcMUJ/H9cxAwB8c9CaG8behLRgcYSOEA8M/tMQ54Y1ZmzAuEE3QKOi21/s30x5Q+by9g7BwiVfDtqzeMA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/aws-lambda": "8.10.150" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-lambda/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-lambda/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.56.0.tgz", - "integrity": "sha512-Jl2B/FYEb6tBCk9G31CMomKPikGU2g+CEhrGddDI0o1YeNpg3kAO9dExF+w489/IJUGZX6/wudyNvV7z4k9NjQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/propagation-utils": "^0.31.3", - "@opentelemetry/semantic-conventions": "^1.34.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-sdk/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-aws-sdk/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.49.0.tgz", - "integrity": "sha512-ky5Am1y6s3Ex/3RygHxB/ZXNG07zPfg9Z6Ora+vfeKcr/+I6CJbWXWhSBJor3gFgKN3RvC11UWVURnmDpBS6Pg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "^0.203.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@types/bunyan": "1.8.11" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.49.0.tgz", - "integrity": "sha512-BNIvqldmLkeikfI5w5Rlm9vG5NnQexfPoxOgEMzfDVOEF+vS6351I6DzWLLgWWR9CNF/jQJJi/lr6am2DLp0Rw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cassandra-driver/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cassandra-driver/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.47.0.tgz", - "integrity": "sha512-pjenvjR6+PMRb6/4X85L4OtkQCootgb/Jzh/l/Utu3SJHBid1F+gk9sTGU2FWuhhEfV6P7MZ7BmCdHXQjgJ42g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/connect": "3.4.38" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.18.0.tgz", - "integrity": "sha512-i+cUbLHvRShuevtM0NwjQR9wnABhmYw8+dbgD57LNBde7xkuSDot0CTzX+pYn32djtQ1bPYZiLf+uwS0JsMUrw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cucumber/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-cucumber/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.21.0.tgz", - "integrity": "sha512-Xu4CZ1bfhdkV3G6iVHFgKTgHx8GbKSqrTU01kcIJRGHpowVnyOPEv1CW5ow+9GU2X4Eki8zoNuVUenFc3RluxQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.47.0.tgz", - "integrity": "sha512-775fOnewWkTF4iXMGKgwvOGqEmPrU1PZpXjjqvTrEErYBJe7Fz1WlEeUStHepyKOdld7Ghv7TOF/kE3QDctvrg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dns/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dns/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.52.0.tgz", - "integrity": "sha512-W7pizN0Wh1/cbNhhTf7C62NpyYw7VfCFTYg0DYieSTrtPBT1vmoSZei19wfKLnrMsz3sHayCg0HxCVL2c+cz5w==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.48.0.tgz", - "integrity": "sha512-3zQlE/DoVfVH6/ycuTv7vtR/xib6WOa0aLFfslYcvE62z0htRu/ot8PV/zmMZfnzpTQj8S/4ULv36R6UIbpJIg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fastify/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fastify/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.23.0.tgz", - "integrity": "sha512-Puan+QopWHA/KNYvDfOZN6M/JtF6buXEyD934vrb8WhsX1/FuM7OtoMlQyIqAadnE8FqqDL4KDPiEfCQH6pQcQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.47.0.tgz", - "integrity": "sha512-UfHqf3zYK+CwDwEtTjaD12uUqGGTswZ7ofLBEdQ4sEJp9GHSSJMQ2hT3pgBxyKADzUdoxQAv/7NqvL42ZI+Qbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.51.0.tgz", - "integrity": "sha512-LchkOu9X5DrXAnPI1+Z06h/EH/zC7D6sA86hhPrk3evLlsJTz0grPrkL/yUJM9Ty0CL/y2HSvmWQCjbJEz/ADg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.203.0.tgz", - "integrity": "sha512-Qmjx2iwccHYRLoE4RFS46CvQE9JG9Pfeae4EPaNZjvIuJxb/pZa2R9VWzRlTehqQWpAvto/dGhtkw8Tv+o0LTg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "0.203.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.50.0.tgz", - "integrity": "sha512-5xGusXOFQXKacrZmDbpHQzqYD1gIkrMWuwvlrEPkYOsjUqGUjl1HbxCsn5Y9bUXOCgP1Lj6A4PcKt1UiJ2MujA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.203.0.tgz", - "integrity": "sha512-y3uQAcCOAwnO6vEuNVocmpVzG3PER6/YZqbPbbffDdJ9te5NkHEkfSMNzlC3+v7KlE+WinPGc3N7MR30G1HY2g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/instrumentation": "0.203.0", - "@opentelemetry/semantic-conventions": "^1.29.0", - "forwarded-parse": "2.1.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.51.0.tgz", - "integrity": "sha512-9IUws0XWCb80NovS+17eONXsw1ZJbHwYYMXiwsfR9TSurkLV5UNbRSKb9URHO+K+pIJILy9wCxvyiOneMr91Ig==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/redis-common": "^0.38.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/redis-common": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.0.tgz", - "integrity": "sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.12.0.tgz", - "integrity": "sha512-bIe4aSAAxytp88nzBstgr6M7ZiEpW6/D1/SuKXdxxuprf18taVvFL2H5BDNGZ7A14K27haHqzYqtCTqFXHZOYg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.30.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.48.0.tgz", - "integrity": "sha512-V5wuaBPv/lwGxuHjC6Na2JFRjtPgstw19jTFl1B1b6zvaX8zVDYUDaR5hL7glnQtUSCMktPttQsgK4dhXpddcA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.33.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.51.0.tgz", - "integrity": "sha512-XNLWeMTMG1/EkQBbgPYzCeBD0cwOrfnn8ao4hWgLv0fNCFQu1kCsJYygz2cvKuCs340RlnG4i321hX7R8gj3Rg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.48.0.tgz", - "integrity": "sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.47.0.tgz", - "integrity": "sha512-vXDs/l4hlWy1IepPG1S6aYiIZn+tZDI24kAzwKKJmR2QEJRL84PojmALAEJGazIOLl/VdcCPZdMb0U2K0VzojA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/memcached": "^2.2.6" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-memcached/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-memcached/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.56.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.56.0.tgz", - "integrity": "sha512-YG5IXUUmxX3Md2buVMvxm9NWlKADrnavI36hbJsihqqvBGsWnIfguf0rUP5Srr0pfPqhQjUP+agLMsvu0GmUpA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.50.0.tgz", - "integrity": "sha512-Am8pk1Ct951r4qCiqkBcGmPIgGhoDiFcRtqPSLbJrUZqEPUsigjtMjoWDRLG1Ki1NHgOF7D0H7d+suWz1AAizw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.49.0.tgz", - "integrity": "sha512-QU9IUNqNsrlfE3dJkZnFHqLjlndiU39ll/YAAEvWE40sGOCi9AtOF6rmEGzJ1IswoZ3oyePV7q2MP8SrhJfVAA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/mysql": "2.15.27" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.49.0.tgz", - "integrity": "sha512-dCub9wc02mkJWNyHdVEZ7dvRzy295SmNJa+LrAJY2a/+tIiVBQqEAajFzKwp9zegVVnel9L+WORu34rGLQDzxA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.41.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.49.0.tgz", - "integrity": "sha512-1R/JFwdmZIk3T/cPOCkVvFQeKYzbbUvDxVH3ShXamUwBlGkdEu5QJitlRMyVNZaHkKZKWgYrBarGQsqcboYgaw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.30.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-nestjs-core/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-nestjs-core/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.47.0.tgz", - "integrity": "sha512-csoJ++Njpf7C09JH+0HNGenuNbDZBqO1rFhMRo6s0rAmJwNh9zY3M/urzptmKlqbKnf4eH0s+CKHy/+M8fbFsQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-net/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-net/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-oracledb": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.29.0.tgz", - "integrity": "sha512-2aHLiJdkyiUbooIUm7FaZf+O4jyqEl+RfFpgud1dxT87QeeYM216wi+xaMNzsb5yKtRBqbA3qeHBCyenYrOZwA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/oracledb": "6.5.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-oracledb/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-oracledb/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.55.0.tgz", - "integrity": "sha512-yfJ5bYE7CnkW/uNsnrwouG/FR7nmg09zdk2MSs7k0ZOMkDDAE3WBGpVFFApGgNu2U+gtzLgEzOQG4I/X+60hXw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.41.0", - "@types/pg": "8.15.4", - "@types/pg-pool": "2.0.6" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.50.0.tgz", - "integrity": "sha512-Pi0cWGp4f2gresq2xqef4IsuunLdebJ9n9tZxytDz2ci4euIfW36ILpszQmRNhwCVDCZLmUgGDKZGj4PXyPd0w==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "^0.203.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.51.0.tgz", - "integrity": "sha512-uL/GtBA0u72YPPehwOvthAe+Wf8k3T+XQPBssJmTYl6fzuZjNq8zTfxVFhl9nRFjFVEe+CtiYNT0Q3AyqW1Z0A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/redis-common": "^0.38.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/redis-common": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.0.tgz", - "integrity": "sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - } - }, - "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.49.0.tgz", - "integrity": "sha512-tsGZZhS4mVZH7omYxw5jpsrD3LhWizqWc0PYtAnzpFUvL5ZINHE+cm57bssTQ2AK/GtZMxu9LktwCvIIf3dSmw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-restify/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-restify/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.48.0.tgz", - "integrity": "sha512-Wixrc8CchuJojXpaS/dCQjFOMc+3OEil1H21G+WLYQb8PcKt5kzW9zDBT19nyjjQOx/D/uHPfgbrT+Dc7cfJ9w==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-router/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-router/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.17.0.tgz", - "integrity": "sha512-O+xc0woqrSjue5IgpCCMvlgsuDrq6DDEfiHW3S3vRMCjXE1ZoPjaDE/K6EURorN+tjnzZQN1gOMSrscSGAbjHg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.50.0.tgz", - "integrity": "sha512-6JN6lnKN9ZuZtZdMQIR+no1qHzQvXSZUsNe3sSWMgqmNRyEXuDUWBIyKKeG0oHRHtR4xE4QhJyD4D5kKRPWZFA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-socket.io/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-socket.io/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.22.0.tgz", - "integrity": "sha512-XrrNSUCyEjH1ax9t+Uo6lv0S2FCCykcF7hSxBMxKf7Xn0bPRxD3KyFUZy25aQXzbbbUHhtdxj3r2h88SfEM3aA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/tedious": "^4.0.14" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.14.0.tgz", - "integrity": "sha512-2HN+7ztxAReXuxzrtA3WboAKlfP5OsPA57KQn2AdYZbJ3zeRPcLXyW4uO/jpLE6PLm0QRtmeGCmfYpqRlwgSwg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.7.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.48.0.tgz", - "integrity": "sha512-QuKbswAaQfRULhtlYbeNC9gOAXPxOSCE4BjIzuY1oEsc84kIsHUjn3yvY9Q83s3eg3j0JycNcAMi8u0yTl5PIQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "^0.203.0", - "@opentelemetry/instrumentation": "^0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.203.0.tgz", - "integrity": "sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-transformer": "0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.203.0.tgz", - "integrity": "sha512-te0Ze1ueJF+N/UOFl5jElJW4U0pZXQ8QklgSfJ2linHN0JJsuaHG8IabEUi2iqxY8ZBDlSiz1Trfv5JcjWWWwQ==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/otlp-exporter-base": "0.203.0", - "@opentelemetry/otlp-transformer": "0.203.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.203.0.tgz", - "integrity": "sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-logs": "0.203.0", - "@opentelemetry/sdk-metrics": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1", - "protobufjs": "^7.3.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/propagation-utils": { - "version": "0.31.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.31.3.tgz", - "integrity": "sha512-ZI6LKjyo+QYYZY5SO8vfoCQ9A69r1/g+pyjvtu5RSK38npINN1evEmwqbqhbg2CdcIK3a4PN6pDAJz/yC5/gAA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/propagator-b3": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.0.1.tgz", - "integrity": "sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/propagator-jaeger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.0.1.tgz", - "integrity": "sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.31.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.31.3.tgz", - "integrity": "sha512-I556LHcLVsBXEgnbPgQISP/JezDt5OfpgOaJNR1iVJl202r+K145OSSOxnH5YOc/KvrydBD0FOE03F7x0xnVTw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-aws": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.3.0.tgz", - "integrity": "sha512-PkD/lyXG3B3REq1Y6imBLckljkJYXavtqGYSryAeJYvGOf5Ds3doR+BCGjmKeF6ObAtI5MtpBeUStTDtGtBsWA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-azure": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.10.0.tgz", - "integrity": "sha512-5cNAiyPBg53Uxe/CW7hsCq8HiKNAUGH+gi65TtgpzSR9bhJG4AEbuZhbJDFwe97tn2ifAD1JTkbc/OFuaaFWbA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.7.3.tgz", - "integrity": "sha512-SK+xUFw6DKYbQniaGmIFsFxAZsr8RpRSRWxKi5/ZJAoqqPnjcyGI/SeUx8zzPk4XLO084zyM4pRHgir0hRTaSQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.37.0.tgz", - "integrity": "sha512-LGpJBECIMsVKhiulb4nxUw++m1oF4EiDDPmFGW2aqYaAF0oUvJNv8Z/55CAzcZ7SxvlTgUwzewXDBsuCup7iqw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "gcp-metadata": "^6.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz", - "integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.203.0.tgz", - "integrity": "sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz", - "integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-node": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.203.0.tgz", - "integrity": "sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0", - "@opentelemetry/exporter-logs-otlp-http": "0.203.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.203.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.203.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0", - "@opentelemetry/exporter-prometheus": "0.203.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0", - "@opentelemetry/exporter-trace-otlp-http": "0.203.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.203.0", - "@opentelemetry/exporter-zipkin": "2.0.1", - "@opentelemetry/instrumentation": "0.203.0", - "@opentelemetry/propagator-b3": "2.0.1", - "@opentelemetry/propagator-jaeger": "2.0.1", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/sdk-logs": "0.203.0", - "@opentelemetry/sdk-metrics": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1", - "@opentelemetry/sdk-trace-node": "2.0.1", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.203.0.tgz", - "integrity": "sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { - "version": "0.203.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.203.0.tgz", - "integrity": "sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.203.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz", - "integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.0.1", - "@opentelemetry/resources": "2.0.1", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.0.1.tgz", - "integrity": "sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/context-async-hooks": "2.0.1", - "@opentelemetry/core": "2.0.1", - "@opentelemetry/sdk-trace-base": "2.0.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", - "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.0.tgz", - "integrity": "sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@photostructure/tz-lookup": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-11.2.0.tgz", - "integrity": "sha512-DwrvodcXHNSdGdeSF7SBL5o8aBlsaeuCuG7633F04nYsL3hn5Hxe3z/5kCqxv61J1q7ggKZ27GPylR3x0cPNXQ==", - "license": "CC0-1.0" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "license": "MIT" - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@react-email/body": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.0.11.tgz", - "integrity": "sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==", - "license": "MIT", - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/button": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.0.tgz", - "integrity": "sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/code-block": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.1.0.tgz", - "integrity": "sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/code-inline": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.5.tgz", - "integrity": "sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/column": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.13.tgz", - "integrity": "sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/components": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.2.0.tgz", - "integrity": "sha512-y45D+oYDgvL1fuFnauwUk8MwT54l0hWwnUAzzP0bVuwhsmVJFelKOGGMCRch0pcgyINilVlAEk0Xjtcu0Su4cw==", - "license": "MIT", - "dependencies": { - "@react-email/body": "0.0.11", - "@react-email/button": "0.2.0", - "@react-email/code-block": "0.1.0", - "@react-email/code-inline": "0.0.5", - "@react-email/column": "0.0.13", - "@react-email/container": "0.0.15", - "@react-email/font": "0.0.9", - "@react-email/head": "0.0.12", - "@react-email/heading": "0.0.15", - "@react-email/hr": "0.0.11", - "@react-email/html": "0.0.11", - "@react-email/img": "0.0.11", - "@react-email/link": "0.0.12", - "@react-email/markdown": "0.0.15", - "@react-email/preview": "0.0.13", - "@react-email/render": "1.1.3", - "@react-email/row": "0.0.12", - "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.1.0", - "@react-email/text": "0.1.5" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/container": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.15.tgz", - "integrity": "sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/font": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.9.tgz", - "integrity": "sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==", - "license": "MIT", - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/head": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.12.tgz", - "integrity": "sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/heading": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.15.tgz", - "integrity": "sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/hr": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.11.tgz", - "integrity": "sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/html": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.11.tgz", - "integrity": "sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/img": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.11.tgz", - "integrity": "sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/link": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.12.tgz", - "integrity": "sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/markdown": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.15.tgz", - "integrity": "sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==", - "license": "MIT", - "dependencies": { - "md-to-react-email": "^5.0.5" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/preview": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.13.tgz", - "integrity": "sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/render": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-1.1.3.tgz", - "integrity": "sha512-TjjF1tdTmOqYEIWWg9wMx5q9JbQRbWmnG7owQbSGEHkNfc/c/vBu7hjfrki907lgQEAkYac9KPTyIjOKhvhJCg==", - "license": "MIT", - "dependencies": { - "html-to-text": "^9.0.5", - "prettier": "^3.5.3", - "react-promise-suspense": "^0.3.4" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/row": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.12.tgz", - "integrity": "sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/section": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.16.tgz", - "integrity": "sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/tailwind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.1.0.tgz", - "integrity": "sha512-m4sh5d1c8P9TPA6Ea8qHrboE5s9PmRQREIreYMn1l5ca0pCV/UBEY15e1RgoaseAzy2cy+gwI+nKhMwqUJsD1g==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/text": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.5.tgz", - "integrity": "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", - "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", - "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", - "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", - "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", - "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", - "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", - "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", - "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", - "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", - "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", - "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", - "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", - "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", - "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", - "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", - "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", - "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", - "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", - "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", - "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", - "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", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" - }, - "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@socket.io/redis-adapter": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.3.0.tgz", - "integrity": "sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.1", - "notepack.io": "~3.0.1", - "uid2": "1.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "socket.io-adapter": "^2.5.4" - } - }, - "node_modules/@socket.io/redis-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@sqltools/formatter": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", - "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", - "license": "MIT" - }, - "node_modules/@swc/core": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.11.tgz", - "integrity": "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.23" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.11", - "@swc/core-darwin-x64": "1.12.11", - "@swc/core-linux-arm-gnueabihf": "1.12.11", - "@swc/core-linux-arm64-gnu": "1.12.11", - "@swc/core-linux-arm64-musl": "1.12.11", - "@swc/core-linux-x64-gnu": "1.12.11", - "@swc/core-linux-x64-musl": "1.12.11", - "@swc/core-win32-arm64-msvc": "1.12.11", - "@swc/core-win32-ia32-msvc": "1.12.11", - "@swc/core-win32-x64-msvc": "1.12.11" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.11.tgz", - "integrity": "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.11.tgz", - "integrity": "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.11.tgz", - "integrity": "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.11.tgz", - "integrity": "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.11.tgz", - "integrity": "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.11.tgz", - "integrity": "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.11.tgz", - "integrity": "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.11.tgz", - "integrity": "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.11.tgz", - "integrity": "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==", - "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.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.11.tgz", - "integrity": "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==", - "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", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@swc/types": { - "version": "0.1.23", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", - "integrity": "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/@testcontainers/postgresql": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.2.1.tgz", - "integrity": "sha512-u0XLsjUmAHaUmB9Q1bitBu8uoxRKteDI65S5/zpJ6TeZabx9qB4EENwKqzuqEwOCzzlko9at7ZY4frwRcchgvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "testcontainers": "^11.2.1" - } - }, - "node_modules/@testcontainers/redis": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.2.1.tgz", - "integrity": "sha512-Q5j+irNw0BLec3he30s2E0fhE06Zr9ROVutkyKUgcwQoZxEVW3xV69ke2AFCT5teEcIvTKqevObN4UDkq33Qow==", - "dev": true, - "license": "MIT", - "dependencies": { - "testcontainers": "^11.2.1" - } - }, - "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", - "integrity": "sha512-lvEOjxeXIp+wPXgl9kJA97dqzMfNexjqHou+XHVcfxQgolctoJiRYmcVCWGpiZ9CBf/CJha1KmD1qQoRIsjLaA==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^7.2.0", - "@turf/invariant": "^7.2.0", - "@types/geojson": "^7946.0.10", - "point-in-polygon-hao": "^1.1.0", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/helpers": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", - "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", - "license": "MIT", - "dependencies": { - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@turf/invariant": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.2.0.tgz", - "integrity": "sha512-kV4u8e7Gkpq+kPbAKNC21CmyrXzlbBgFjO1PhrHPgEdNqXqDawoZ3i6ivE3ULJj2rSesCjduUaC/wyvH/sNr2Q==", - "license": "MIT", - "dependencies": { - "@turf/helpers": "^7.2.0", - "@types/geojson": "^7946.0.10", - "tslib": "^2.8.1" - }, - "funding": { - "url": "https://opencollective.com/turf" - } - }, - "node_modules/@types/archiver": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.3.tgz", - "integrity": "sha512-a6wUll6k3zX6qs5KlxIggs1P1JcYJaTCx2gnlr+f0S1yd2DoaEwoIK10HmBaLnZwWneBz+JBm0dwcZu0zECBcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/readdir-glob": "*" - } - }, - "node_modules/@types/async-lock": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/async-lock/-/async-lock-1.4.2.tgz", - "integrity": "sha512-HlZ6Dcr205BmNhwkdXqrg2vkFMN2PluI7Lgr8In3B3wE5PiQHhjRqtW/lGdVU9gw+sM0JcIDx2AN+cW8oSWIcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/aws-lambda": { - "version": "8.10.150", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.150.tgz", - "integrity": "sha512-AX+AbjH/rH5ezX1fbK8onC/a+HyQHo7QGmvoxAE42n22OsciAxvZoZNEr22tbXs8WfP1nIsBjKDpgPm3HjOZbA==", - "license": "MIT" - }, - "node_modules/@types/bcrypt": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", - "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bunyan": { - "version": "1.8.11", - "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", - "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", - "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*" - } - }, - "node_modules/@types/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookie-parser": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", - "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/docker-modem": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", - "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/dockerode": { - "version": "3.3.42", - "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.42.tgz", - "integrity": "sha512-U1jqHMShibMEWHdxYhj3rCMNCiLx5f35i4e3CEUuW+JSSszc/tVqc6WCAPdhwBymG5R/vgbcceagK0St7Cq6Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/docker-modem": "*", - "@types/node": "*", - "@types/ssh2": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/fluent-ffmpeg": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.27.tgz", - "integrity": "sha512-QiDWjihpUhriISNoBi2hJBRUUmoj/BMTYcfz+F+ZM9hHWBYABFAE6hjP/TbCZC0GWwlpa3FzvHH9RzFeRusZ7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/inquirer": { - "version": "8.2.11", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.11.tgz", - "integrity": "sha512-15UboTvxb9SOaPG7CcXZ9dkv8lNqfiAwuh/5WxJDLjmElBt9tbx1/FDsEnJddUBKvN4mlPKvr8FyO1rAmBanzg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/through": "*", - "rxjs": "^7.2.0" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", - "license": "MIT" - }, - "node_modules/@types/memcached": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", - "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mock-fs": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", - "integrity": "sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/mysql": { - "version": "2.15.27", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", - "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/nodemailer": { - "version": "6.4.17", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", - "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/oracledb": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", - "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/pg": { - "version": "8.15.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.4.tgz", - "integrity": "sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pg-pool": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", - "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", - "license": "MIT", - "dependencies": { - "@types/pg": "*" - } - }, - "node_modules/@types/picomatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", - "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/pngjs": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", - "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/readdir-glob": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", - "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/sanitize-html": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.16.0.tgz", - "integrity": "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "htmlparser2": "^8.0.0" - } - }, - "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/ssh2": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", - "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.11.18" - } - }, - "node_modules/@types/ssh2-streams": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz", - "integrity": "sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.112", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.112.tgz", - "integrity": "sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/ssh2/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/supertest": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", - "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, - "node_modules/@types/tedious": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", - "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/through": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", - "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/validator": { - "version": "13.15.2", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", - "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "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/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", - "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/app-root-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", - "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/archiver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", - "license": "MIT", - "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/archiver-utils/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==", - "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/archiver-utils/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==", - "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/archiver-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/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==", - "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/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-source": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/array-source/-/array-source-0.0.4.tgz", - "integrity": "sha512-frNdc+zBn80vipY+GdcJkLEbMWj3xmzArYApmUGxoiV8uAu/ygcs9icPdsGdA26h0MkHUMW6EN2piIvVx+M5Mw==", - "license": "BSD-3-Clause" - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-lock": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", - "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/b4a": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", - "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/bare-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.5.tgz", - "integrity": "sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", - "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/batch-cluster": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", - "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bignumber.js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", - "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/buildcheck": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", - "integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/builtin-modules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bullmq": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.2.tgz", - "integrity": "sha512-bq0PSxPCWeNlFBc5yjBs3eR+e6GxIEIeHY0xxq6WELzG65GPjL+A2ni1NS7NroKsur0C3UJdabw51IswiSTSYw==", - "license": "MIT", - "dependencies": { - "cron-parser": "^4.9.0", - "ioredis": "^5.4.1", - "msgpackr": "^1.11.2", - "node-abort-controller": "^3.1.1", - "semver": "^7.5.4", - "tslib": "^2.0.0", - "uuid": "^9.0.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "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.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.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/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/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001724", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", - "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/canvas": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.1.2.tgz", - "integrity": "sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, - "node_modules/canvas/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "license": "MIT" - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "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/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "license": "MIT" - }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" - }, - "node_modules/class-validator": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", - "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", - "license": "MIT", - "dependencies": { - "@types/validator": "^13.11.8", - "libphonenumber-js": "^1.11.1", - "validator": "^13.9.0" - } - }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/comment-json": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", - "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "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/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", - "license": "MIT", - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/cron": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.0.tgz", - "integrity": "sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==", - "license": "MIT", - "dependencies": { - "@types/luxon": "~3.6.0", - "luxon": "~3.6.0" - }, - "engines": { - "node": ">=18.x" - } - }, - "node_modules/cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "license": "MIT", - "dependencies": { - "luxon": "^3.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/cron/node_modules/luxon": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", - "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssstyle": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.5.0.tgz", - "integrity": "sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "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", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "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", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, - "node_modules/debounce": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", - "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "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", - "optional": true, - "peer": true - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-europe-js": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", - "integrity": "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diacritics": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", - "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/docker-compose": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.2.0.tgz", - "integrity": "sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==", - "dev": true, - "license": "MIT", - "dependencies": { - "yaml": "^2.2.2" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/docker-modem": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz", - "integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.1", - "readable-stream": "^3.5.0", - "split-ca": "^1.0.1", - "ssh2": "^1.15.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/docker-modem/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/dockerode": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.7.tgz", - "integrity": "sha512-R+rgrSRTRdU5mH14PZTCPZtW/zw3HDWNTS/1ZAQpL/5Upe/ye5K9WQkIysu4wBoiMwKynsz0a8qWuGsHgEvSAA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "@grpc/grpc-js": "^1.11.1", - "@grpc/proto-loader": "^0.7.13", - "docker-modem": "^5.0.6", - "protobufjs": "^7.3.2", - "tar-fs": "~2.1.2", - "uuid": "^10.0.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/dockerode/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC" - }, - "node_modules/dockerode/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/dockerode/node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/dockerode/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dockerode/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.171", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", - "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "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.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "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", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "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" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", - "esquery": "^1.6.0", - "find-up-simple": "^1.0.1", - "globals": "^16.0.0", - "indent-string": "^5.0.0", - "is-builtin-module": "^5.0.0", - "jsesc": "^3.1.0", - "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.12.0", - "semver": "^7.7.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/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/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter2": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/exiftool-vendored": { - "version": "28.8.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.8.0.tgz", - "integrity": "sha512-R7tirJLr9fWuH9JS/KFFLB+O7jNGKuPXGxREc6YybYangEudGb+X8ERsYXk9AifMiAWh/2agNfbgkbcQcF+MxA==", - "license": "MIT", - "dependencies": { - "@photostructure/tz-lookup": "^11.0.0", - "@types/luxon": "^3.4.2", - "batch-cluster": "^13.0.0", - "he": "^1.2.0", - "luxon": "^3.5.0" - }, - "optionalDependencies": { - "exiftool-vendored.exe": "13.0.0", - "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", - "integrity": "sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==", - "license": "MIT", - "optional": true, - "os": [ - "!win32" - ] - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true, - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "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", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "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", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-source": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-source/-/file-source-0.6.1.tgz", - "integrity": "sha512-1R1KneL7eTXmXfKxC10V/9NeGOdbsAXJ+lQ//fvvcHUgtaZcZDWNJNblxAoVOyV1cj45pOtUrR3vZTBwqcW8XA==", - "license": "BSD-3-Clause", - "dependencies": { - "stream-source": "0.3" - } - }, - "node_modules/file-type": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", - "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.2.7", - "strtok3": "^10.2.2", - "token-types": "^6.0.0", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "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", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fluent-ffmpeg": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", - "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", - "dependencies": { - "async": "^0.2.9", - "which": "^1.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/fluent-ffmpeg/node_modules/async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" - }, - "node_modules/fluent-ffmpeg/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", - "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^4.0.1", - "cosmiconfig": "^8.2.0", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "typescript": ">3.6.0", - "webpack": "^5.11.0" - } - }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formidable": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/geo-tz": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/geo-tz/-/geo-tz-8.1.4.tgz", - "integrity": "sha512-xayeOC05wgy6JATU/k7GFHTMfSimzL1Fi3KSzt2GqvEnP1ZFXyQ9V4VAiTrTYhZSmRr0dbchZkximSegHZNUfA==", - "license": "MIT", - "dependencies": { - "@turf/boolean-point-in-polygon": "^7.1.0", - "@turf/helpers": "^7.1.0", - "geobuf": "^3.0.2", - "pbf": "^3.2.1" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/evansiroky" - } - }, - "node_modules/geobuf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/geobuf/-/geobuf-3.0.2.tgz", - "integrity": "sha512-ASgKwEAQQRnyNFHNvpd5uAwstbVYmiTW0Caw3fBb509tNTqXyAAPMyFs5NNihsLZhLxU1j/kjFhkhLWA9djuVg==", - "license": "ISC", - "dependencies": { - "concat-stream": "^2.0.0", - "pbf": "^3.2.1", - "shapefile": "~0.6.6" - }, - "bin": { - "geobuf2json": "bin/geobuf2json", - "json2geobuf": "bin/json2geobuf", - "shp2geobuf": "bin/shp2geobuf" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true, - "license": "MIT" - }, - "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/google-logging-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "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": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", - "license": "MIT", - "dependencies": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", - "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "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", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "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/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==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/i18n-iso-countries": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz", - "integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==", - "license": "MIT", - "dependencies": { - "diacritics": "1.3.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-in-the-middle": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz", - "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.14.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/inquirer/node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "license": "ISC" - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ioredis": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz", - "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "^1.1.1", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "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", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-builtin-module": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", - "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "builtin-modules": "^5.0.0" - }, - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "license": "MIT", - "engines": { - "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", - "optional": true, - "peer": true - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/is-standalone-pwa": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz", - "integrity": "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "license": "MIT" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", - "license": "ISC", - "engines": { - "node": ">=6" - } - }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/jose": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", - "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "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", - "optional": true, - "peer": true, - "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/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/kysely": { - "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" - } - }, - "node_modules/kysely-postgres-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kysely-postgres-js/-/kysely-postgres-js-2.0.0.tgz", - "integrity": "sha512-R1tWx6/x3tSatWvsmbHJxpBZYhNNxcnMw52QzZaHKg7ZOWtHib4iZyEaw4gb2hNKVctWQ3jfMxZT/ZaEMK6kBQ==", - "license": "MIT", - "peerDependencies": { - "kysely": ">= 0.24.0 < 1", - "postgres": ">= 3.4.0 < 4" - } - }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/libphonenumber-js": { - "version": "1.12.9", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz", - "integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==", - "license": "MIT" - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "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", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "license": "MIT" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, - "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", - "dev": true, - "license": "MIT" - }, - "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/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/make-fetch-happen/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/marked": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.4.tgz", - "integrity": "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 16" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/md-to-react-email": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/md-to-react-email/-/md-to-react-email-5.0.5.tgz", - "integrity": "sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==", - "license": "MIT", - "dependencies": { - "marked": "7.0.4" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "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-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-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "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-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "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/minipass-sized/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "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/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/mnemonist": { - "version": "0.40.3", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.3.tgz", - "integrity": "sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==", - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.4" - } - }, - "node_modules/mock-fs": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz", - "integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, - "node_modules/moo": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", - "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/msgpackr": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.4.tgz", - "integrity": "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } - }, - "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/multer/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nan": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", - "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nearley": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^2.19.0", - "moo": "^0.5.0", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6" - }, - "bin": { - "nearley-railroad": "bin/nearley-railroad.js", - "nearley-test": "bin/nearley-test.js", - "nearley-unparse": "bin/nearley-unparse.js", - "nearleyc": "bin/nearleyc.js" - }, - "funding": { - "type": "individual", - "url": "https://nearley.js.org/#give-to-nearley" - } - }, - "node_modules/nearley/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/nest-commander": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.17.0.tgz", - "integrity": "sha512-1R9vppZT2j/9njKiG0zYTDLAyQOj14KdGWdNuhluveK8VXoQepXNb0t09dRNWy4KCWrI7wDZ2tQTEwb43JyHOw==", - "license": "MIT", - "dependencies": { - "@fig/complete-commander": "^3.0.0", - "@golevelup/nestjs-discovery": "4.0.3", - "commander": "11.1.0", - "cosmiconfig": "8.3.6", - "inquirer": "8.2.6" - }, - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "@types/inquirer": "^8.1.3" - } - }, - "node_modules/nest-commander/node_modules/@fig/complete-commander": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fig/complete-commander/-/complete-commander-3.2.0.tgz", - "integrity": "sha512-1Holl3XtRiANVKURZwgpjCnPuV4RsHp+XC0MhgvyAX/avQwj7F2HUItYOvGi/bXjJCkEzgBZmVfCr0HBA+q+Bw==", - "license": "MIT", - "dependencies": { - "prettier": "^3.2.5" - }, - "peerDependencies": { - "commander": "^11.1.0" - } - }, - "node_modules/nest-commander/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/nestjs-cls": { - "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" - }, - "peerDependencies": { - "@nestjs/common": ">= 10 < 12", - "@nestjs/core": ">= 10 < 12", - "reflect-metadata": "*", - "rxjs": ">= 7" - } - }, - "node_modules/nestjs-kysely": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/nestjs-kysely/-/nestjs-kysely-3.0.0.tgz", - "integrity": "sha512-YA6tHBgXQYPNpMBPII2OvUOiaWjCCoh5pP5dUHirQcMUHxNFzInBL6MDk8y74rk2z/5IvAK9AUlsdPyJtToO6g==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "kysely": "0.x", - "reflect-metadata": "^0.1.13 || ^0.2.2" - } - }, - "node_modules/nestjs-otel": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/nestjs-otel/-/nestjs-otel-7.0.0.tgz", - "integrity": "sha512-BjuzY+fJrlbooIZds15XOHvdv2LrtUiVjIBcV3DMw/VrIXK9szWe+njjMZS6Pqft9hV78M8lJE+Ux2ZowDQ/Hw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/host-metrics": "^0.36.0", - "response-time": "^2.3.3" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@nestjs/common": ">= 11 < 12", - "@nestjs/core": ">= 11 < 12" - } - }, - "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "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": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "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", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "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/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-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", - "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "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/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/notepack.io": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", - "integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==", - "license": "MIT" - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "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", - "optional": true, - "peer": true - }, - "node_modules/nypm": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", - "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "pathe": "^2.0.3", - "pkg-types": "^2.0.0", - "tinyexec": "^0.3.2" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": "^14.16.0 || >=16.10.0" - } - }, - "node_modules/oauth4webapi": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.5.tgz", - "integrity": "sha512-1K88D2GiAydGblHo39NBro5TebGXa+7tYoyIbxvqv3+haDDry7CBE1eSYuNbOSsYCCU6y0gdynVZAkm4YPw4hg==", - "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", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openid-client": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.2.tgz", - "integrity": "sha512-Xya5TNMnnZuTM6DbHdB4q0S3ig2NTAELnii/ASie1xDEr8iiB8zZbO871OWBdrw++sd3hW6bqWjgcmSy1RTWHA==", - "license": "MIT", - "dependencies": { - "jose": "^6.0.11", - "oauth4webapi": "^3.5.4" - }, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "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", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-srcset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", - "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", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "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", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", - "license": "MIT", - "dependencies": { - "leac": "^0.6.0", - "peberminta": "^0.9.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-source": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/path-source/-/path-source-0.1.3.tgz", - "integrity": "sha512-dWRHm5mIw5kw0cs3QZLNmpUWty48f5+5v9nWD2dw3Y0Hf+s01Ag8iJEWV0Sm0kocE8kK27DrIowha03e1YR+Qw==", - "license": "BSD-3-Clause", - "dependencies": { - "array-source": "0.0", - "file-source": "0.6" - } - }, - "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, - "node_modules/peberminta": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", - "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pngjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", - "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.19.0" - } - }, - "node_modules/point-in-polygon-hao": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", - "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", - "license": "MIT", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/postgres": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", - "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", - "license": "Unlicense", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/porsager" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prebuild-install/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "license": "ISC" - }, - "node_modules/prebuild-install/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/prebuild-install/node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": ">=2.0", - "typescript": ">=2.9", - "vue-tsc": "^2.1.0" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "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", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "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/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proper-lockfile/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/properties-reader": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", - "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/properties?sponsor=1" - } - }, - "node_modules/properties-reader/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/protobufjs": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", - "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-email": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", - "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/traverse": "^7.27.0", - "chalk": "^5.0.0", - "chokidar": "^4.0.3", - "commander": "^13.0.0", - "debounce": "^2.0.0", - "esbuild": "^0.25.0", - "glob": "^11.0.0", - "jiti": "2.4.2", - "log-symbols": "^7.0.0", - "mime-types": "^3.0.0", - "normalize-path": "^3.0.0", - "nypm": "0.6.0", - "ora": "^8.0.0", - "prompts": "2.4.2", - "socket.io": "^4.8.1", - "tsconfig-paths": "4.2.0" - }, - "bin": { - "email": "dist/index.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/react-email/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-email/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/react-email/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "license": "MIT" - }, - "node_modules/react-email/node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/react-email/node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-email/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-promise-suspense": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", - "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^2.0.1" - } - }, - "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", - "license": "MIT" - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/readable-stream/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "license": "MIT", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-in-the-middle": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", - "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", - "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" - } - }, - "node_modules/response-time": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.3.tgz", - "integrity": "sha512-SsjjOPHl/FfrTQNgmc5oen8Hr1Jxpn6LlHNXxCIFdYMHuK1kMeYMobb9XN3mvxaGQm3dbegqYFMX4+GDORfbWg==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "on-headers": "~1.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "license": "Unlicense" - }, - "node_modules/rollup": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", - "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.0", - "@rollup/rollup-android-arm64": "4.44.0", - "@rollup/rollup-darwin-arm64": "4.44.0", - "@rollup/rollup-darwin-x64": "4.44.0", - "@rollup/rollup-freebsd-arm64": "4.44.0", - "@rollup/rollup-freebsd-x64": "4.44.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", - "@rollup/rollup-linux-arm-musleabihf": "4.44.0", - "@rollup/rollup-linux-arm64-gnu": "4.44.0", - "@rollup/rollup-linux-arm64-musl": "4.44.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-musl": "4.44.0", - "@rollup/rollup-linux-s390x-gnu": "4.44.0", - "@rollup/rollup-linux-x64-gnu": "4.44.0", - "@rollup/rollup-linux-x64-musl": "4.44.0", - "@rollup/rollup-win32-arm64-msvc": "4.44.0", - "@rollup/rollup-win32-ia32-msvc": "4.44.0", - "@rollup/rollup-win32-x64-msvc": "4.44.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "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", - "optional": true, - "peer": true - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", - "license": "WTFPL OR ISC", - "dependencies": { - "truncate-utf8-bytes": "^1.0.0" - } - }, - "node_modules/sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", - "license": "MIT", - "dependencies": { - "deepmerge": "^4.2.2", - "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", - "is-plain-object": "^5.0.0", - "parse-srcset": "^1.0.2", - "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", - "optional": true, - "peer": true, - "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", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/selderee": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", - "license": "MIT", - "dependencies": { - "parseley": "^0.12.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shapefile": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/shapefile/-/shapefile-0.6.6.tgz", - "integrity": "sha512-rLGSWeK2ufzCVx05wYd+xrWnOOdSV7xNUW5/XFgx3Bc02hBkpMlrd2F1dDII7/jhWzv0MSyBFh5uJIy9hLdfuw==", - "license": "BSD-3-Clause", - "dependencies": { - "array-source": "0.0", - "commander": "2", - "path-source": "0.1", - "slice-source": "0.4", - "stream-source": "0.3", - "text-encoding": "^0.6.4" - }, - "bin": { - "dbf2json": "bin/dbf2json", - "shp2json": "bin/shp2json" - } - }, - "node_modules/shapefile/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@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.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/sirv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slice-source": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/slice-source/-/slice-source-0.4.1.tgz", - "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", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socks": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", - "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", - "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/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "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.6.6", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.6.6.tgz", - "integrity": "sha512-bZydXEXhaNDQBr8xYHC3a8thwcaMuTBp0CkKGjwGYDsIB26tnlWeWPwJtSQ0TEwiJcz9iJJON5mFPkx7XroHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "nearley": "^2.20.1" - }, - "bin": { - "sql-formatter": "bin/sql-formatter-cli.cjs" - } - }, - "node_modules/sql-highlight": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", - "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", - "funding": [ - "https://github.com/scriptcoded/sql-highlight?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/scriptcoded" - } - ], - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/ssh-remote-port-forward": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", - "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/ssh2": "^0.5.48", - "ssh2": "^1.4.0" - } - }, - "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { - "version": "0.5.52", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", - "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/ssh2-streams": "*" - } - }, - "node_modules/ssh2": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz", - "integrity": "sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.10", - "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", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true, - "license": "MIT" - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stream-source": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz", - "integrity": "sha512-ZuEDP9sgjiAwUVoDModftG0JtYiLUV8K4ljYD1VyUMRWtbVf92474o4kuuul43iZ8t/hRuiDAx1dIJSvirrK/g==", - "license": "BSD-3-Clause" - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", - "license": "MIT", - "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strtok3": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", - "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/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==", - "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/sucrase/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==", - "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/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/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==", - "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/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^10.2.2" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz", - "integrity": "sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==", - "license": "Apache-2.0", - "dependencies": { - "@scarf/scarf": "=1.4.0" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "license": "MIT", - "engines": { - "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", - "optional": true, - "peer": true - }, - "node_modules/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/systeminformation": { - "version": "5.23.8", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.8.tgz", - "integrity": "sha512-Osd24mNKe6jr/YoXLLK3k8TMdzaxDffhpCxgkfgBHcapykIkd50HXThM3TCEuHO2pPuCsSx2ms/SunqhU5MmsQ==", - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32", - "freebsd", - "openbsd", - "netbsd", - "sunos", - "android" - ], - "bin": { - "systeminformation": "lib/cli.js" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "Buy me a coffee", - "url": "https://www.buymeacoffee.com/systeminfo" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss-email-variants": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tailwindcss-email-variants/-/tailwindcss-email-variants-3.0.4.tgz", - "integrity": "sha512-ohtLSifyWQDAtddJnfbcxkIDCIyXp6Yb83hXRprrS+/2dSyme4OlUZAP+TDwQc0K8D0LAw80eKI6psgejxys8A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "tailwindcss": ">=3.4.0 < 4" - } - }, - "node_modules/tailwindcss-mso": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tailwindcss-mso/-/tailwindcss-mso-2.0.2.tgz", - "integrity": "sha512-GaR8RW/Kan+YWEQ9Y9Ah6AYy7R2wEQ3X++YK4ffJVWycCTd6ryMLezqmyhi7KWHqsgQOb4nhjJYayI+JF44BXw==", - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "peerDependencies": { - "tailwindcss": ">=3.4.0 < 4" - } - }, - "node_modules/tailwindcss-preset-email": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/tailwindcss-preset-email/-/tailwindcss-preset-email-1.4.0.tgz", - "integrity": "sha512-UgvLHT5UsPEEXjto1WlR1wYXmYKeMaS2OPTJQqyufsU12os/EjBpeygEjTdrId7U2/mwDF4grlgo81qlzYSByg==", - "license": "MIT", - "dependencies": { - "tailwindcss-email-variants": "^3.0.3", - "tailwindcss-mso": "^2.0.1" - }, - "peerDependencies": { - "tailwindcss": ">=3.4.17" - } - }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tailwindcss/node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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/tar-fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", - "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/tar/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/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/test-exclude/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/test-exclude/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/test-exclude/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/test-exclude/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/testcontainers": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.2.1.tgz", - "integrity": "sha512-KJALGi8ButKDZgzHr0PtJUVNBOSlSFncumZ34MCQTN4VEU9AK4tWTn9gCcAFzG4zBmzzC2aEbHMFUujqkbDvBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@balena/dockerignore": "^1.0.2", - "@types/dockerode": "^3.3.42", - "archiver": "^7.0.1", - "async-lock": "^1.4.1", - "byline": "^5.0.0", - "debug": "^4.4.1", - "docker-compose": "^1.2.0", - "dockerode": "^4.0.7", - "get-port": "^7.1.0", - "proper-lockfile": "^4.1.2", - "properties-reader": "^2.3.0", - "ssh-remote-port-forward": "^1.0.4", - "tar-fs": "^3.1.0", - "tmp": "^0.2.3", - "undici": "^7.11.0" - } - }, - "node_modules/testcontainers/node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha512-hJnc6Qg3dWoOMkqP53F0dzRIgtmsAge09kxUIqGrEUS4qr5rWLckGYaQAVr+opBrIMRErGgy6f5aPnyPpyGRfg==", - "deprecated": "no longer maintained", - "license": "Unlicense" - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, - "node_modules/thumbhash": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz", - "integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==", - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "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.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", - "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", - "dev": true, - "license": "MIT", - "engines": { - "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", - "optional": true, - "peer": true, - "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", - "optional": true, - "peer": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "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", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "license": "MIT", - "engines": { - "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", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "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", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", - "license": "WTFPL", - "dependencies": { - "utf8-byte-length": "^1.0.1" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", - "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tapable": "^2.2.1", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typeorm": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz", - "integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==", - "license": "MIT", - "dependencies": { - "@sqltools/formatter": "^1.2.5", - "ansis": "^3.17.0", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "dayjs": "^1.11.13", - "debug": "^4.4.0", - "dedent": "^1.6.0", - "dotenv": "^16.4.7", - "glob": "^10.4.5", - "sha.js": "^2.4.11", - "sql-highlight": "^6.0.0", - "tslib": "^2.8.1", - "uuid": "^11.1.0", - "yargs": "^17.7.2" - }, - "bin": { - "typeorm": "cli.js", - "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", - "typeorm-ts-node-esm": "cli-ts-node-esm.js" - }, - "engines": { - "node": ">=16.13.0" - }, - "funding": { - "url": "https://opencollective.com/typeorm" - }, - "peerDependencies": { - "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", - "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "hdb-pool": "^0.1.6", - "ioredis": "^5.0.4", - "mongodb": "^5.8.0 || ^6.0.0", - "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", - "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^6.3.0", - "pg": "^8.5.1", - "pg-native": "^3.0.0", - "pg-query-stream": "^4.0.0", - "redis": "^3.1.1 || ^4.0.0", - "reflect-metadata": "^0.1.14 || ^0.2.0", - "sql.js": "^1.4.0", - "sqlite3": "^5.0.3", - "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" - }, - "peerDependenciesMeta": { - "@google-cloud/spanner": { - "optional": true - }, - "@sap/hana-client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "hdb-pool": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "mssql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-native": { - "optional": true - }, - "pg-query-stream": { - "optional": true - }, - "redis": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typeorm-aurora-data-api-driver": { - "optional": true - } - } - }, - "node_modules/typeorm/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typeorm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/typeorm/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==", - "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/typeorm/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==", - "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/typeorm/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typeorm/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==", - "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/typeorm/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/ua-is-frozen": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", - "integrity": "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "license": "MIT" - }, - "node_modules/ua-parser-js": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-2.0.4.tgz", - "integrity": "sha512-XiBOnM/UpUq21ZZ91q2AVDOnGROE6UQd37WrO9WBgw4u2eGvUCNOheMmZ3EfEUj7DLHr8tre+Um/436Of/Vwzg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "AGPL-3.0-or-later", - "dependencies": { - "@types/node-fetch": "^2.6.12", - "detect-europe-js": "^0.1.2", - "is-standalone-pwa": "^0.1.1", - "node-fetch": "^2.7.0", - "ua-is-frozen": "^0.1.2" - }, - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", - "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "license": "MIT", - "dependencies": { - "@lukeed/csprng": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uid2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz", - "integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==", - "license": "MIT", - "engines": { - "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": "7.11.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", - "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "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", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unplugin": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz", - "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.1", - "picomatch": "^4.0.2", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/unplugin-swc": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.5.tgz", - "integrity": "sha512-BahYtYvQ/KSgOqHoy5FfQgp/oZNAB7jwERxNeFVeN/PtJhg4fpK/ybj9OwKtqGPseOadS7+TGbq6tH2DmDAYvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.2.0", - "load-tsconfig": "^0.2.5", - "unplugin": "^2.3.5" - }, - "peerDependencies": { - "@swc/core": "^1.2.108" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utf8-byte-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utimes": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/utimes/-/utimes-5.2.1.tgz", - "integrity": "sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^4.3.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/utimes/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "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", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "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", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/webpack": { - "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": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "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", - "optional": true, - "peer": true, - "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", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "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", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "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/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", - "optional": true, - "peer": true, - "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", - "optional": true, - "peer": true - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "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/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zip-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - } - } -} diff --git a/server/package.json b/server/package.json index c765786960..4d4c125474 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.135.3", + "version": "2.0.1", "description": "", "author": "", "private": true, @@ -37,25 +37,23 @@ "@nestjs/bullmq": "^11.0.1", "@nestjs/common": "^11.0.4", "@nestjs/core": "^11.0.4", - "@nestjs/event-emitter": "^3.0.0", "@nestjs/platform-express": "^11.0.4", "@nestjs/platform-socket.io": "^11.0.4", "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.0.2", "@nestjs/websockets": "^11.0.4", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.62.0", "@opentelemetry/context-async-hooks": "^2.0.0", - "@opentelemetry/exporter-prometheus": "^0.203.0", - "@opentelemetry/instrumentation-http": "^0.203.0", - "@opentelemetry/instrumentation-ioredis": "^0.51.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.49.0", - "@opentelemetry/instrumentation-pg": "^0.55.0", + "@opentelemetry/exporter-prometheus": "^0.205.0", + "@opentelemetry/instrumentation-http": "^0.205.0", + "@opentelemetry/instrumentation-ioredis": "^0.53.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.51.0", + "@opentelemetry/instrumentation-pg": "^0.58.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-metrics": "^2.0.1", - "@opentelemetry/sdk-node": "^0.203.0", + "@opentelemetry/sdk-node": "^0.205.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.5.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", @@ -79,12 +77,12 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", "mnemonist": "^0.40.3", - "multer": "^2.0.1", + "multer": "^2.0.2", "nest-commander": "^3.16.0", "nestjs-cls": "^5.0.0", "nestjs-kysely": "^3.0.0", @@ -103,28 +101,24 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.14.0", "semver": "^7.6.2", - "sharp": "^0.34.2", + "sharp": "^0.34.3", "sirv": "^3.0.0", "socket.io": "^4.8.1", "tailwindcss-preset-email": "^1.4.0", "thumbhash": "^0.1.1", - "typeorm": "^0.3.17", "ua-parser-js": "^2.0.0", + "uuid": "^11.1.0", "validator": "^13.12.0" }, "devDependencies": { - "canvas": "^3.1.0", - "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", "@nestjs/schematics": "^11.0.0", "@nestjs/testing": "^11.0.4", "@swc/core": "^1.4.14", - "@testcontainers/postgresql": "^11.0.0", - "@testcontainers/redis": "^11.0.0", "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", - "@types/bcrypt": "^5.0.0", + "@types/bcrypt": "^6.0.0", "@types/body-parser": "^1.19.6", "@types/compression": "^1.7.5", "@types/cookie-parser": "^1.4.8", @@ -134,9 +128,9 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", - "@types/nodemailer": "^6.4.14", + "@types/multer": "^2.0.0", + "@types/node": "^22.18.8", + "@types/nodemailer": "^7.0.0", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", "@types/react": "^19.0.0", @@ -147,34 +141,29 @@ "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "globals": "^16.0.0", "mock-fs": "^5.2.0", - "node-addon-api": "^8.3.1", "node-gyp": "^11.2.0", "pngjs": "^7.0.0", "prettier": "^3.0.2", "prettier-plugin-organize-imports": "^4.0.0", - "rimraf": "^6.0.0", - "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", "supertest": "^7.1.0", "tailwindcss": "^3.4.0", "testcontainers": "^11.0.0", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.3.3", + "typescript": "^5.9.2", "typescript-eslint": "^8.28.0", "unplugin-swc": "^1.4.5", - "utimes": "^5.2.1", "vite-tsconfig-paths": "^5.0.0", "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" }, "overrides": { - "sharp": "^0.34.2" + "sharp": "^0.34.3" } } diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 8d261463e7..a1cd1edfdf 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -19,6 +19,7 @@ import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; +import { UserRepository } from 'src/repositories/user.repository'; import { services } from 'src/services'; import { AuthService } from 'src/services/auth.service'; import { CliService } from 'src/services/cli.service'; @@ -55,6 +56,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { private jobService: JobService, private telemetryRepository: TelemetryRepository, private authService: AuthService, + private userRepository: UserRepository, ) { logger.setAppName(this.worker); } diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index 3bdfb3bbc6..ebb07af442 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -98,7 +98,7 @@ const create = (path: string, up: string[], down: string[]) => { const folder = dirname(path); const fullPath = join(folder, filename); mkdirSync(folder, { recursive: true }); - writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down })); + writeFileSync(fullPath, asMigration({ up, down })); console.log(`Wrote ${fullPath}`); }; @@ -128,34 +128,11 @@ const compare = async () => { }; type MigrationProps = { - name: string; - timestamp: number; up: string[]; down: string[]; }; -const asMigration = (type: 'kysely' | 'typeorm', options: MigrationProps) => - type === 'typeorm' ? asTypeOrmMigration(options) : asKyselyMigration(options); - -const asTypeOrmMigration = ({ timestamp, name, up, down }: MigrationProps) => { - const upSql = up.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - const downSql = down.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - - return `import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ${name}${timestamp} implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { -${upSql} - } - - public async down(queryRunner: QueryRunner): Promise { -${downSql} - } -} -`; -}; - -const asKyselyMigration = ({ up, down }: MigrationProps) => { +const asMigration = ({ up, down }: MigrationProps) => { const upSql = up.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); const downSql = down.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); diff --git a/server/src/bin/sync-sql.ts b/server/src/bin/sync-sql.ts index 6d3cb42fae..b632332069 100644 --- a/server/src/bin/sync-sql.ts +++ b/server/src/bin/sync-sql.ts @@ -15,6 +15,7 @@ import { repositories } from 'src/repositories'; import { AccessRepository } from 'src/repositories/access.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; +import { MachineLearningRepository } from 'src/repositories/machine-learning.repository'; import { SyncRepository } from 'src/repositories/sync.repository'; import { AuthService } from 'src/services/auth.service'; import { getKyselyConfig } from 'src/utils/database'; @@ -57,7 +58,7 @@ class SqlGenerator { try { await this.setup(); for (const Repository of repositories) { - if (Repository === LoggingRepository) { + if (Repository === LoggingRepository || Repository === MachineLearningRepository) { continue; } await this.process(Repository); diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts index 0935fe202d..0d32749c02 100644 --- a/server/src/commands/media-location.command.ts +++ b/server/src/commands/media-location.command.ts @@ -61,7 +61,7 @@ export class ChangeMediaLocationCommand extends CommandRunner { immich-server: ... volumes: - - \${UPLOAD_LOCATION}:/usr/src/app/upload + - \${UPLOAD_LOCATION}:/data ... )`; diff --git a/server/src/config.ts b/server/src/config.ts index 33a6f19ba1..66c03450fa 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -54,6 +54,11 @@ export interface SystemConfig { machineLearning: { enabled: boolean; urls: string[]; + availabilityChecks: { + enabled: boolean; + timeout: number; + interval: number; + }; clip: { enabled: boolean; modelName: string; @@ -176,6 +181,8 @@ export interface SystemConfig { }; } +export type MachineLearningConfig = SystemConfig['machineLearning']; + export const defaults = Object.freeze({ backup: { database: { @@ -191,7 +198,7 @@ export const defaults = Object.freeze({ targetVideoCodec: VideoCodec.H264, acceptedVideoCodecs: [VideoCodec.H264], targetAudioCodec: AudioCodec.Aac, - acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.LibOpus, AudioCodec.PcmS16le], + acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.LibOpus], acceptedContainers: [VideoContainer.Mov, VideoContainer.Ogg, VideoContainer.Webm], targetResolution: '720', maxBitrate: '0', @@ -227,6 +234,11 @@ export const defaults = Object.freeze({ machineLearning: { enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false', urls: [process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003'], + availabilityChecks: { + enabled: true, + timeout: Number(process.env.IMMICH_MACHINE_LEARNING_PING_TIMEOUT) || 2000, + interval: 30_000, + }, clip: { enabled: true, modelName: 'ViT-B-32__openai', diff --git a/server/src/constants.ts b/server/src/constants.ts index 2d803c2e95..3b75ca9f7e 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -1,10 +1,11 @@ import { Duration } from 'luxon'; import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; import { SemVer } from 'semver'; import { DatabaseExtension, ExifOrientation, VectorIndex } from 'src/enum'; export const POSTGRES_VERSION_RANGE = '>=14.0.0'; -export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.5'; +export const VECTORCHORD_VERSION_RANGE = '>=0.3 <0.6'; export const VECTORS_VERSION_RANGE = '>=0.2 <0.4'; export const VECTOR_VERSION_RANGE = '>=0.5 <1'; @@ -41,20 +42,17 @@ export const SALT_ROUNDS = 10; export const IWorker = 'IWorker'; -const { version } = JSON.parse(readFileSync('./package.json', 'utf8')); +// eslint-disable-next-line unicorn/prefer-module +const basePath = dirname(__filename); +const packageFile = join(basePath, '..', 'package.json'); +const { version } = JSON.parse(readFileSync(packageFile, 'utf8')); export const serverVersion = new SemVer(version); export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || '/usr/src/app/upload'; - -export const MACHINE_LEARNING_PING_TIMEOUT = Number(process.env.MACHINE_LEARNING_PING_TIMEOUT || 2000); -export const MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME = Number( - process.env.MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME || 30_000, -); - export const citiesFile = 'cities500.txt'; +export const reverseGeocodeMaxDistance = 25_000; export const MOBILE_REDIRECT = 'app.immich:///oauth-callback'; export const LOGIN_URL = '/auth/login?autoLaunch=0'; diff --git a/server/src/controllers/activity.controller.ts b/server/src/controllers/activity.controller.ts index d2d34da102..75b2e2f8a3 100644 --- a/server/src/controllers/activity.controller.ts +++ b/server/src/controllers/activity.controller.ts @@ -46,8 +46,8 @@ export class ActivityController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.ActivityDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteActivity(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/album.controller.spec.ts b/server/src/controllers/album.controller.spec.ts index 9ef28c5188..c864d5da5a 100644 --- a/server/src/controllers/album.controller.spec.ts +++ b/server/src/controllers/album.controller.spec.ts @@ -84,6 +84,13 @@ describe(AlbumController.name, () => { }); }); + describe('PUT /albums/assets', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/albums/assets`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + describe('PATCH /albums/:id', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).patch(`/albums/${factory.uuid()}`).send({ albumName: 'New album name' }); diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index f3ad312823..95ce1cff2b 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -1,9 +1,11 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AddUsersDto, AlbumInfoDto, AlbumResponseDto, + AlbumsAddAssetsDto, + AlbumsAddAssetsResponseDto, AlbumStatisticsResponseDto, CreateAlbumDto, GetAlbumsDto, @@ -68,12 +70,13 @@ export class AlbumController { @Delete(':id') @Authenticated({ permission: Permission.AlbumDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteAlbum(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto) { return this.service.delete(auth, id); } @Put(':id/assets') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AlbumAssetCreate, sharedLink: true }) addAssetsToAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -82,8 +85,14 @@ export class AlbumController { return this.service.addAssets(auth, id, dto); } + @Put('assets') + @Authenticated({ permission: Permission.AlbumAssetCreate, sharedLink: true }) + addAssetsToAlbums(@Auth() auth: AuthDto, @Body() dto: AlbumsAddAssetsDto): Promise { + return this.service.addAssetsToAlbums(auth, dto); + } + @Delete(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.AlbumAssetDelete }) removeAssetFromAlbum( @Auth() auth: AuthDto, @Body() dto: BulkIdsDto, @@ -93,7 +102,7 @@ export class AlbumController { } @Put(':id/users') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserCreate }) addUsersToAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -103,7 +112,8 @@ export class AlbumController { } @Put(':id/user/:userId') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) updateAlbumUser( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -114,12 +124,13 @@ export class AlbumController { } @Delete(':id/user/:userId') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removeUserFromAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Param('userId', new ParseMeUUIDPipe({ version: '4' })) userId: string, - ) { + ): Promise { return this.service.removeUser(auth, id, userId); } } diff --git a/server/src/controllers/api-key.controller.spec.ts b/server/src/controllers/api-key.controller.spec.ts index 993ad012cc..c6dab09a3c 100644 --- a/server/src/controllers/api-key.controller.spec.ts +++ b/server/src/controllers/api-key.controller.spec.ts @@ -1,16 +1,16 @@ -import { APIKeyController } from 'src/controllers/api-key.controller'; +import { ApiKeyController } from 'src/controllers/api-key.controller'; import { Permission } from 'src/enum'; import { ApiKeyService } from 'src/services/api-key.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -describe(APIKeyController.name, () => { +describe(ApiKeyController.name, () => { let ctx: ControllerContext; const service = mockBaseService(ApiKeyService); beforeAll(async () => { - ctx = await controllerSetup(APIKeyController, [{ provide: ApiKeyService, useValue: service }]); + ctx = await controllerSetup(ApiKeyController, [{ provide: ApiKeyService, useValue: service }]); return () => ctx.close(); }); @@ -33,6 +33,13 @@ describe(APIKeyController.name, () => { }); }); + describe('GET /api-keys/me', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/api-keys/me`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + describe('GET /api-keys/:id', () => { it('should be an authenticated route', async () => { await request(ctx.getHttpServer()).get(`/api-keys/${factory.uuid()}`); diff --git a/server/src/controllers/api-key.controller.ts b/server/src/controllers/api-key.controller.ts index 6347a1274a..59b6908128 100644 --- a/server/src/controllers/api-key.controller.ts +++ b/server/src/controllers/api-key.controller.ts @@ -9,7 +9,7 @@ import { UUIDParamDto } from 'src/validation'; @ApiTags('API Keys') @Controller('api-keys') -export class APIKeyController { +export class ApiKeyController { constructor(private service: ApiKeyService) {} @Post() @@ -24,6 +24,12 @@ export class APIKeyController { return this.service.getAll(auth); } + @Get('me') + @Authenticated({ permission: false }) + async getMyApiKey(@Auth() auth: AuthDto): Promise { + return this.service.getMine(auth); + } + @Get(':id') @Authenticated({ permission: Permission.ApiKeyRead }) getApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { @@ -41,8 +47,8 @@ export class APIKeyController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.ApiKeyDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteApiKey(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/asset-media.controller.spec.ts b/server/src/controllers/asset-media.controller.spec.ts index 67bdeff222..eb594fbe47 100644 --- a/server/src/controllers/asset-media.controller.spec.ts +++ b/server/src/controllers/asset-media.controller.spec.ts @@ -1,4 +1,6 @@ import { AssetMediaController } from 'src/controllers/asset-media.controller'; +import { AssetMediaStatus } from 'src/dtos/asset-media-response.dto'; +import { AssetMetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AssetMediaService } from 'src/services/asset-media.service'; import request from 'supertest'; @@ -11,7 +13,7 @@ const makeUploadDto = (options?: { omit: string }): Record => { deviceId: 'TEST', fileCreatedAt: new Date().toISOString(), fileModifiedAt: new Date().toISOString(), - isFavorite: 'testing', + isFavorite: 'false', duration: '0:00:00.000000', }; @@ -27,16 +29,20 @@ describe(AssetMediaController.name, () => { let ctx: ControllerContext; const assetData = Buffer.from('123'); const filename = 'example.png'; + const service = mockBaseService(AssetMediaService); beforeAll(async () => { ctx = await controllerSetup(AssetMediaController, [ { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, - { provide: AssetMediaService, useValue: mockBaseService(AssetMediaService) }, + { provide: AssetMediaService, useValue: service }, ]); return () => ctx.close(); }); beforeEach(() => { + service.resetAllMocks(); + service.uploadAsset.mockResolvedValue({ status: AssetMediaStatus.DUPLICATE, id: factory.uuid() }); + ctx.reset(); }); @@ -46,13 +52,61 @@ describe(AssetMediaController.name, () => { expect(ctx.authenticate).toHaveBeenCalled(); }); + it('should accept metadata', async () => { + const mobileMetadata = { key: AssetMetadataKey.MobileApp, value: { iCloudId: '123' } }; + const { status } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ + ...makeUploadDto(), + metadata: JSON.stringify([mobileMetadata]), + }); + + expect(service.uploadAsset).toHaveBeenCalledWith( + undefined, + expect.objectContaining({ metadata: [mobileMetadata] }), + expect.objectContaining({ originalName: 'example.png' }), + undefined, + ); + + expect(status).toBe(200); + }); + + it('should handle invalid metadata json', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ + ...makeUploadDto(), + metadata: 'not-a-string-string', + }); + + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['metadata must be valid JSON'])); + }); + + it('should validate iCloudId is a string', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post('/assets') + .attach('assetData', assetData, filename) + .field({ + ...makeUploadDto(), + metadata: JSON.stringify([{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 123 } }]), + }); + + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['metadata.0.value.iCloudId must be a string'])); + }); + it('should require `deviceAssetId`', async () => { const { status, body } = await request(ctx.getHttpServer()) .post('/assets') .attach('assetData', assetData, filename) .field({ ...makeUploadDto({ omit: 'deviceAssetId' }) }); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual( + factory.responses.badRequest(['deviceAssetId must be a string', 'deviceAssetId should not be empty']), + ); }); it('should require `deviceId`', async () => { @@ -61,7 +115,7 @@ describe(AssetMediaController.name, () => { .attach('assetData', assetData, filename) .field({ ...makeUploadDto({ omit: 'deviceId' }) }); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual(factory.responses.badRequest(['deviceId must be a string', 'deviceId should not be empty'])); }); it('should require `fileCreatedAt`', async () => { @@ -70,25 +124,20 @@ describe(AssetMediaController.name, () => { .attach('assetData', assetData, filename) .field({ ...makeUploadDto({ omit: 'fileCreatedAt' }) }); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual( + factory.responses.badRequest(['fileCreatedAt must be a Date instance', 'fileCreatedAt should not be empty']), + ); }); it('should require `fileModifiedAt`', async () => { const { status, body } = await request(ctx.getHttpServer()) .post('/assets') .attach('assetData', assetData, filename) - .field({ ...makeUploadDto({ omit: 'fileModifiedAt' }) }); + .field(makeUploadDto({ omit: 'fileModifiedAt' })); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); - }); - - it('should require `duration`', async () => { - const { status, body } = await request(ctx.getHttpServer()) - .post('/assets') - .attach('assetData', assetData, filename) - .field({ ...makeUploadDto({ omit: 'duration' }) }); - expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual( + factory.responses.badRequest(['fileModifiedAt must be a Date instance', 'fileModifiedAt should not be empty']), + ); }); it('should throw if `isFavorite` is not a boolean', async () => { @@ -97,16 +146,18 @@ describe(AssetMediaController.name, () => { .attach('assetData', assetData, filename) .field({ ...makeUploadDto(), isFavorite: 'not-a-boolean' }); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual(factory.responses.badRequest(['isFavorite must be a boolean value'])); }); it('should throw if `visibility` is not an enum', async () => { const { status, body } = await request(ctx.getHttpServer()) .post('/assets') .attach('assetData', assetData, filename) - .field({ ...makeUploadDto(), visibility: 'not-a-boolean' }); + .field({ ...makeUploadDto(), visibility: 'not-an-option' }); expect(status).toBe(400); - expect(body).toEqual(factory.responses.badRequest()); + expect(body).toEqual( + factory.responses.badRequest([expect.stringContaining('visibility must be one of the following values:')]), + ); }); // TODO figure out how to deal with `sendFile` diff --git a/server/src/controllers/asset-media.controller.ts b/server/src/controllers/asset-media.controller.ts index ea6c9602c8..688e513b64 100644 --- a/server/src/controllers/asset-media.controller.ts +++ b/server/src/controllers/asset-media.controller.ts @@ -34,7 +34,7 @@ import { UploadFieldName, } from 'src/dtos/asset-media.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichHeader, RouteKey } from 'src/enum'; +import { ImmichHeader, Permission, RouteKey } from 'src/enum'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor, getFiles } from 'src/middleware/file-upload.interceptor'; @@ -61,7 +61,7 @@ export class AssetMediaController { required: false, }) @ApiBody({ description: 'Asset Upload Information', type: AssetMediaCreateDto }) - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetUpload, sharedLink: true }) async uploadAsset( @Auth() auth: AuthDto, @UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] })) files: UploadFiles, @@ -80,7 +80,7 @@ export class AssetMediaController { @Get(':id/original') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) async downloadAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -96,12 +96,13 @@ export class AssetMediaController { @Put(':id/original') @UseInterceptors(FileUploadInterceptor) @ApiConsumes('multipart/form-data') - @EndpointLifecycle({ addedAt: 'v1.106.0' }) - @ApiOperation({ + @EndpointLifecycle({ + addedAt: 'v1.106.0', + deprecatedAt: 'v1.142.0', summary: 'replaceAsset', description: 'Replace the asset with new file, without changing its id', }) - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetReplace, sharedLink: true }) async replaceAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -120,7 +121,7 @@ export class AssetMediaController { @Get(':id/thumbnail') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) async viewAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -157,7 +158,7 @@ export class AssetMediaController { @Get(':id/video/playback') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) async playAssetVideo( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -171,12 +172,12 @@ export class AssetMediaController { * Checks if multiple assets exist on the server and returns all existing - used by background backup */ @Post('exist') - @HttpCode(HttpStatus.OK) + @Authenticated() @ApiOperation({ summary: 'checkExistingAssets', description: 'Checks if multiple assets exist on the server and returns all existing - used by background backup', }) - @Authenticated() + @HttpCode(HttpStatus.OK) checkExistingAssets( @Auth() auth: AuthDto, @Body() dto: CheckExistingAssetsDto, @@ -188,12 +189,12 @@ export class AssetMediaController { * Checks if assets exist by checksums */ @Post('bulk-upload-check') - @HttpCode(HttpStatus.OK) + @Authenticated({ permission: Permission.AssetUpload }) @ApiOperation({ summary: 'checkBulkUpload', description: 'Checks if assets exist by checksums', }) - @Authenticated() + @HttpCode(HttpStatus.OK) checkBulkUpload( @Auth() auth: AuthDto, @Body() dto: AssetBulkUploadCheckDto, diff --git a/server/src/controllers/asset.controller.spec.ts b/server/src/controllers/asset.controller.spec.ts index 66d2d7c206..7a7a37fe2e 100644 --- a/server/src/controllers/asset.controller.spec.ts +++ b/server/src/controllers/asset.controller.spec.ts @@ -1,4 +1,5 @@ import { AssetController } from 'src/controllers/asset.controller'; +import { AssetMetadataKey } from 'src/enum'; import { AssetService } from 'src/services/asset.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; @@ -6,14 +7,16 @@ import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils' describe(AssetController.name, () => { let ctx: ControllerContext; + const service = mockBaseService(AssetService); beforeAll(async () => { - ctx = await controllerSetup(AssetController, [{ provide: AssetService, useValue: mockBaseService(AssetService) }]); + ctx = await controllerSetup(AssetController, [{ provide: AssetService, useValue: service }]); return () => ctx.close(); }); beforeEach(() => { ctx.reset(); + service.resetAllMocks(); }); describe('PUT /assets', () => { @@ -115,4 +118,120 @@ describe(AssetController.name, () => { ); }); }); + + describe('GET /assets/:id/metadata', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /assets/:id/metadata', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}/metadata`).send({ items: [] }); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/123/metadata`).send({ items: [] }); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['id must be a UUID']))); + }); + + it('should require items to be an array', async () => { + const { status, body } = await request(ctx.getHttpServer()).put(`/assets/${factory.uuid()}/metadata`).send({}); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['items must be an array'])); + }); + + it('should require each item to have a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/assets/${factory.uuid()}/metadata`) + .send({ items: [{ key: 'someKey' }] }); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest( + expect.arrayContaining([expect.stringContaining('items.0.key must be one of the following values')]), + ), + ); + }); + + it('should require each item to have a value', async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/assets/${factory.uuid()}/metadata`) + .send({ items: [{ key: 'mobile-app', value: null }] }); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest(expect.arrayContaining([expect.stringContaining('value must be an object')])), + ); + }); + + describe(AssetMetadataKey.MobileApp, () => { + it('should accept valid data and pass to service correctly', async () => { + const assetId = factory.uuid(); + const { status } = await request(ctx.getHttpServer()) + .put(`/assets/${assetId}/metadata`) + .send({ items: [{ key: 'mobile-app', value: { iCloudId: '123' } }] }); + expect(service.upsertMetadata).toHaveBeenCalledWith(undefined, assetId, { + items: [{ key: 'mobile-app', value: { iCloudId: '123' } }], + }); + expect(status).toBe(200); + }); + + it('should work without iCloudId', async () => { + const assetId = factory.uuid(); + const { status } = await request(ctx.getHttpServer()) + .put(`/assets/${assetId}/metadata`) + .send({ items: [{ key: 'mobile-app', value: {} }] }); + expect(service.upsertMetadata).toHaveBeenCalledWith(undefined, assetId, { + items: [{ key: 'mobile-app', value: {} }], + }); + expect(status).toBe(200); + }); + }); + }); + + describe('GET /assets/:id/metadata/:key', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata/mobile-app`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/assets/123/metadata/mobile-app`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(expect.arrayContaining(['id must be a UUID']))); + }); + + it('should require a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/metadata/invalid`); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest( + expect.arrayContaining([expect.stringContaining('key must be one of the following value')]), + ), + ); + }); + }); + + describe('DELETE /assets/:id/metadata/:key', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/assets/${factory.uuid()}/metadata/mobile-app`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it('should require a valid id', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/assets/123/metadata/mobile-app`); + expect(status).toBe(400); + expect(body).toEqual(factory.responses.badRequest(['id must be a UUID'])); + }); + + it('should require a valid key', async () => { + const { status, body } = await request(ctx.getHttpServer()).delete(`/assets/${factory.uuid()}/metadata/invalid`); + expect(status).toBe(400); + expect(body).toEqual( + factory.responses.badRequest([expect.stringContaining('key must be one of the following values')]), + ); + }); + }); }); diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index bb17daddf3..1f320f6595 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -6,6 +6,9 @@ import { AssetBulkDeleteDto, AssetBulkUpdateDto, AssetJobsDto, + AssetMetadataResponseDto, + AssetMetadataRouteParams, + AssetMetadataUpsertDto, AssetStatsDto, AssetStatsResponseDto, DeviceIdDto, @@ -13,7 +16,7 @@ import { UpdateAssetDto, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { RouteKey } from 'src/enum'; +import { Permission, RouteKey } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { AssetService } from 'src/services/asset.service'; import { UUIDParamDto } from 'src/validation'; @@ -24,7 +27,7 @@ export class AssetController { constructor(private service: AssetService) {} @Get('random') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) @EndpointLifecycle({ deprecatedAt: 'v1.116.0' }) getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise { return this.service.getRandom(auth, dto.count ?? 1); @@ -44,7 +47,7 @@ export class AssetController { } @Get('statistics') - @Authenticated() + @Authenticated({ permission: Permission.AssetStatistics }) getAssetStatistics(@Auth() auth: AuthDto, @Query() dto: AssetStatsDto): Promise { return this.service.getStatistics(auth, dto); } @@ -57,27 +60,27 @@ export class AssetController { } @Put() + @Authenticated({ permission: Permission.AssetUpdate }) @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise { return this.service.updateAll(auth, dto); } @Delete() + @Authenticated({ permission: Permission.AssetDelete }) @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise { return this.service.deleteAll(auth, dto); } @Get(':id') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetRead, sharedLink: true }) getAssetInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.get(auth, id) as Promise; } @Put(':id') - @Authenticated() + @Authenticated({ permission: Permission.AssetUpdate }) updateAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -85,4 +88,36 @@ export class AssetController { ): Promise { return this.service.update(auth, id, dto); } + + @Get(':id/metadata') + @Authenticated({ permission: Permission.AssetRead }) + getAssetMetadata(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.getMetadata(auth, id); + } + + @Put(':id/metadata') + @Authenticated({ permission: Permission.AssetUpdate }) + updateAssetMetadata( + @Auth() auth: AuthDto, + @Param() { id }: UUIDParamDto, + @Body() dto: AssetMetadataUpsertDto, + ): Promise { + return this.service.upsertMetadata(auth, id, dto); + } + + @Get(':id/metadata/:key') + @Authenticated({ permission: Permission.AssetRead }) + getAssetMetadataByKey( + @Auth() auth: AuthDto, + @Param() { id, key }: AssetMetadataRouteParams, + ): Promise { + return this.service.getMetadataByKey(auth, id, key); + } + + @Delete(':id/metadata/:key') + @Authenticated({ permission: Permission.AssetUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + deleteAssetMetadata(@Auth() auth: AuthDto, @Param() { id, key }: AssetMetadataRouteParams): Promise { + return this.service.deleteMetadataByKey(auth, id, key); + } } diff --git a/server/src/controllers/auth-admin.controller.ts b/server/src/controllers/auth-admin.controller.ts new file mode 100644 index 0000000000..dba352783e --- /dev/null +++ b/server/src/controllers/auth-admin.controller.ts @@ -0,0 +1,18 @@ +import { Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { Permission } from 'src/enum'; +import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { AuthAdminService } from 'src/services/auth-admin.service'; + +@ApiTags('Auth (admin)') +@Controller('admin/auth') +export class AuthAdminController { + constructor(private service: AuthAdminService) {} + @Post('unlink-all') + @Authenticated({ permission: Permission.AdminAuthUnlinkAll, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) + unlinkAllOAuthAccountsAdmin(@Auth() auth: AuthDto): Promise { + return this.service.unlinkAll(auth); + } +} diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 9bc5fd0fbb..636e3a3047 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -16,7 +16,7 @@ import { ValidateAccessTokenResponseDto, } from 'src/dtos/auth.dto'; import { UserAdminResponseDto } from 'src/dtos/user.dto'; -import { AuthType, ImmichCookie } from 'src/enum'; +import { AuthType, ImmichCookie, Permission } from 'src/enum'; import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { respondWithCookie, respondWithoutCookie } from 'src/utils/response'; @@ -49,22 +49,22 @@ export class AuthController { } @Post('validateToken') + @Authenticated({ permission: false }) @HttpCode(HttpStatus.OK) - @Authenticated() validateAccessToken(): ValidateAccessTokenResponseDto { return { authStatus: true }; } @Post('change-password') + @Authenticated({ permission: Permission.AuthChangePassword }) @HttpCode(HttpStatus.OK) - @Authenticated() changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise { return this.service.changePassword(auth, dto); } @Post('logout') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) async logout( @Req() request: Request, @Res({ passthrough: true }) res: Response, @@ -87,33 +87,36 @@ export class AuthController { } @Post('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeCreate }) + @HttpCode(HttpStatus.NO_CONTENT) setupPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise { return this.service.setupPinCode(auth, dto); } @Put('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) async changePinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise { return this.service.changePinCode(auth, dto); } @Delete('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise { return this.service.resetPinCode(auth, dto); } @Post('session/unlock') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.NO_CONTENT) async unlockAuthSession(@Auth() auth: AuthDto, @Body() dto: SessionUnlockDto): Promise { return this.service.unlockSession(auth, dto); } @Post('session/lock') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.NO_CONTENT) async lockAuthSession(@Auth() auth: AuthDto): Promise { return this.service.lockSession(auth); } diff --git a/server/src/controllers/download.controller.spec.ts b/server/src/controllers/download.controller.spec.ts index 9385c445b5..00d03fc46f 100644 --- a/server/src/controllers/download.controller.spec.ts +++ b/server/src/controllers/download.controller.spec.ts @@ -1,9 +1,9 @@ +import { Readable } from 'node:stream'; import { DownloadController } from 'src/controllers/download.controller'; import { DownloadService } from 'src/services/download.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; describe(DownloadController.name, () => { let ctx: ControllerContext; diff --git a/server/src/controllers/download.controller.ts b/server/src/controllers/download.controller.ts index 880e636dd1..a7c2af78ed 100644 --- a/server/src/controllers/download.controller.ts +++ b/server/src/controllers/download.controller.ts @@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { DownloadService } from 'src/services/download.service'; import { asStreamableFile } from 'src/utils/file'; @@ -13,15 +14,15 @@ export class DownloadController { constructor(private service: DownloadService) {} @Post('info') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) getDownloadInfo(@Auth() auth: AuthDto, @Body() dto: DownloadInfoDto): Promise { return this.service.getDownloadInfo(auth, dto); } @Post('archive') - @HttpCode(HttpStatus.OK) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) @FileResponse() - @Authenticated({ sharedLink: true }) + @HttpCode(HttpStatus.OK) downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise { return this.service.downloadArchive(auth, dto).then(asStreamableFile); } diff --git a/server/src/controllers/duplicate.controller.ts b/server/src/controllers/duplicate.controller.ts index f6b09e6e7a..9cf5ae97a6 100644 --- a/server/src/controllers/duplicate.controller.ts +++ b/server/src/controllers/duplicate.controller.ts @@ -1,8 +1,9 @@ -import { Body, Controller, Delete, Get, Param } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { DuplicateService } from 'src/services/duplicate.service'; import { UUIDParamDto } from 'src/validation'; @@ -13,19 +14,21 @@ export class DuplicateController { constructor(private service: DuplicateService) {} @Get() - @Authenticated() + @Authenticated({ permission: Permission.DuplicateRead }) getAssetDuplicates(@Auth() auth: AuthDto): Promise { return this.service.getDuplicates(auth); } @Delete() - @Authenticated() + @Authenticated({ permission: Permission.DuplicateDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.deleteAll(auth, dto); } @Delete(':id') - @Authenticated() + @Authenticated({ permission: Permission.DuplicateDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/face.controller.ts b/server/src/controllers/face.controller.ts index 20b6db6039..564b217c16 100644 --- a/server/src/controllers/face.controller.ts +++ b/server/src/controllers/face.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -42,7 +42,8 @@ export class FaceController { @Delete(':id') @Authenticated({ permission: Permission.FaceDelete }) - deleteFace(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: AssetFaceDeleteDto) { + @HttpCode(HttpStatus.NO_CONTENT) + deleteFace(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @Body() dto: AssetFaceDeleteDto): Promise { return this.service.deleteFace(auth, id, dto); } } diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index 9c39e580b6..e3661ec794 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -1,9 +1,10 @@ import { ActivityController } from 'src/controllers/activity.controller'; import { AlbumController } from 'src/controllers/album.controller'; -import { APIKeyController } from 'src/controllers/api-key.controller'; +import { ApiKeyController } from 'src/controllers/api-key.controller'; import { AppController } from 'src/controllers/app.controller'; import { AssetMediaController } from 'src/controllers/asset-media.controller'; import { AssetController } from 'src/controllers/asset.controller'; +import { AuthAdminController } from 'src/controllers/auth-admin.controller'; import { AuthController } from 'src/controllers/auth.controller'; import { DownloadController } from 'src/controllers/download.controller'; import { DuplicateController } from 'src/controllers/duplicate.controller'; @@ -33,13 +34,14 @@ import { UserController } from 'src/controllers/user.controller'; import { ViewController } from 'src/controllers/view.controller'; export const controllers = [ - APIKeyController, + ApiKeyController, ActivityController, AlbumController, AppController, AssetController, AssetMediaController, AuthController, + AuthAdminController, DownloadController, DuplicateController, FaceController, diff --git a/server/src/controllers/job.controller.ts b/server/src/controllers/job.controller.ts index 7da19e207f..9c4e819649 100644 --- a/server/src/controllers/job.controller.ts +++ b/server/src/controllers/job.controller.ts @@ -1,6 +1,7 @@ -import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common'; +import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto'; +import { Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { JobService } from 'src/services/job.service'; @@ -10,19 +11,20 @@ export class JobController { constructor(private service: JobService) {} @Get() - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobRead, admin: true }) getAllJobsStatus(): Promise { return this.service.getAllJobsStatus(); } @Post() - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobCreate, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) createJob(@Body() dto: JobCreateDto): Promise { return this.service.create(dto); } @Put(':id') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobCreate, admin: true }) sendJobCommand(@Param() { id }: JobIdParamDto, @Body() dto: JobCommandDto): Promise { return this.service.handleCommand(id, dto); } diff --git a/server/src/controllers/library.controller.ts b/server/src/controllers/library.controller.ts index e090586f57..b37bc40ce7 100644 --- a/server/src/controllers/library.controller.ts +++ b/server/src/controllers/library.controller.ts @@ -43,15 +43,15 @@ export class LibraryController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.LibraryDelete, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) deleteLibrary(@Param() { id }: UUIDParamDto): Promise { return this.service.delete(id); } @Post(':id/validate') - @HttpCode(200) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) // TODO: change endpoint to validate current settings instead validate(@Param() { id }: UUIDParamDto, @Body() dto: ValidateLibraryDto): Promise { return this.service.validate(id, dto); @@ -64,9 +64,9 @@ export class LibraryController { } @Post(':id/scan') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.LibraryUpdate, admin: true }) - scanLibrary(@Param() { id }: UUIDParamDto) { + @HttpCode(HttpStatus.NO_CONTENT) + scanLibrary(@Param() { id }: UUIDParamDto): Promise { return this.service.queueScan(id); } } diff --git a/server/src/controllers/memory.controller.ts b/server/src/controllers/memory.controller.ts index a5bbbd7411..3b5ad2bb4e 100644 --- a/server/src/controllers/memory.controller.ts +++ b/server/src/controllers/memory.controller.ts @@ -32,7 +32,7 @@ export class MemoryController { } @Get('statistics') - @Authenticated({ permission: Permission.MemoryRead }) + @Authenticated({ permission: Permission.MemoryStatistics }) memoriesStatistics(@Auth() auth: AuthDto, @Query() dto: MemorySearchDto): Promise { return this.service.statistics(auth, dto); } @@ -54,14 +54,14 @@ export class MemoryController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.MemoryDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteMemory(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } @Put(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.MemoryAssetCreate }) addMemoryAssets( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -71,8 +71,8 @@ export class MemoryController { } @Delete(':id/assets') + @Authenticated({ permission: Permission.MemoryAssetDelete }) @HttpCode(HttpStatus.OK) - @Authenticated() removeMemoryAssets( @Auth() auth: AuthDto, @Body() dto: BulkIdsDto, diff --git a/server/src/controllers/notification-admin.controller.ts b/server/src/controllers/notification-admin.controller.ts index 9bac865bdf..28ca7bfd30 100644 --- a/server/src/controllers/notification-admin.controller.ts +++ b/server/src/controllers/notification-admin.controller.ts @@ -25,15 +25,15 @@ export class NotificationAdminController { } @Post('test-email') - @HttpCode(HttpStatus.OK) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) sendTestEmailAdmin(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto): Promise { return this.service.sendTestEmail(auth.user.id, dto); } @Post('templates/:name') - @HttpCode(HttpStatus.OK) @Authenticated({ admin: true }) + @HttpCode(HttpStatus.OK) getNotificationTemplateAdmin( @Auth() auth: AuthDto, @Param('name') name: EmailTemplate, diff --git a/server/src/controllers/notification.controller.ts b/server/src/controllers/notification.controller.ts index af4eb198b6..8ce183c5d0 100644 --- a/server/src/controllers/notification.controller.ts +++ b/server/src/controllers/notification.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Param, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -26,12 +26,14 @@ export class NotificationController { @Put() @Authenticated({ permission: Permission.NotificationUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) updateNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationUpdateAllDto): Promise { return this.service.updateAll(auth, dto); } @Delete() @Authenticated({ permission: Permission.NotificationDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteNotifications(@Auth() auth: AuthDto, @Body() dto: NotificationDeleteAllDto): Promise { return this.service.deleteAll(auth, dto); } @@ -54,6 +56,7 @@ export class NotificationController { @Delete(':id') @Authenticated({ permission: Permission.NotificationDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteNotification(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts index 7da75f573a..f81a184557 100644 --- a/server/src/controllers/oauth.controller.ts +++ b/server/src/controllers/oauth.controller.ts @@ -70,6 +70,7 @@ export class OAuthController { @Post('link') @Authenticated() + @HttpCode(HttpStatus.OK) linkOAuthAccount( @Req() request: Request, @Auth() auth: AuthDto, @@ -79,8 +80,8 @@ export class OAuthController { } @Post('unlink') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) unlinkOAuthAccount(@Auth() auth: AuthDto): Promise { return this.service.unlink(auth); } diff --git a/server/src/controllers/partner.controller.spec.ts b/server/src/controllers/partner.controller.spec.ts new file mode 100644 index 0000000000..2c507a634f --- /dev/null +++ b/server/src/controllers/partner.controller.spec.ts @@ -0,0 +1,101 @@ +import { PartnerController } from 'src/controllers/partner.controller'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerService } from 'src/services/partner.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { factory } from 'test/small.factory'; +import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(PartnerController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(PartnerService); + + beforeAll(async () => { + ctx = await controllerSetup(PartnerController, [ + { provide: PartnerService, useValue: service }, + { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /partners', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/partners'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should require a direction`, async () => { + const { status, body } = await request(ctx.getHttpServer()).get(`/partners`).set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([ + 'direction should not be empty', + expect.stringContaining('direction must be one of the following values:'), + ]), + ); + }); + + it(`should require direction to be an enum`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .get(`/partners`) + .query({ direction: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual( + errorDto.badRequest([expect.stringContaining('direction must be one of the following values:')]), + ); + }); + }); + + describe('POST /partners', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/partners'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should require sharedWithId to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .post(`/partners`) + .send({ sharedWithId: 'invalid' }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + }); + + describe('PUT /partners/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/partners/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should require id to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/partners/invalid`) + .send({ inTimeline: true }) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + }); + + describe('DELETE /partners/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).delete(`/partners/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should require id to be a uuid`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .delete(`/partners/invalid`) + .set('Authorization', `Bearer token`); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest([expect.stringContaining('must be a UUID')])); + }); + }); +}); diff --git a/server/src/controllers/partner.controller.ts b/server/src/controllers/partner.controller.ts index 6b6efaa570..7cb5c1c274 100644 --- a/server/src/controllers/partner.controller.ts +++ b/server/src/controllers/partner.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { EndpointLifecycle } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; -import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; +import { PartnerCreateDto, PartnerResponseDto, PartnerSearchDto, PartnerUpdateDto } from 'src/dtos/partner.dto'; import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { PartnerService } from 'src/services/partner.service'; @@ -18,10 +19,17 @@ export class PartnerController { return this.service.search(auth, dto); } - @Post(':id') + @Post() @Authenticated({ permission: Permission.PartnerCreate }) - createPartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { - return this.service.create(auth, id); + createPartner(@Auth() auth: AuthDto, @Body() dto: PartnerCreateDto): Promise { + return this.service.create(auth, dto); + } + + @Post(':id') + @EndpointLifecycle({ deprecatedAt: 'v1.141.0' }) + @Authenticated({ permission: Permission.PartnerCreate }) + createPartnerDeprecated(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.create(auth, { sharedWithId: id }); } @Put(':id') @@ -29,13 +37,14 @@ export class PartnerController { updatePartner( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, - @Body() dto: UpdatePartnerDto, + @Body() dto: PartnerUpdateDto, ): Promise { return this.service.update(auth, id, dto); } @Delete(':id') @Authenticated({ permission: Permission.PartnerDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removePartner(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/person.controller.ts b/server/src/controllers/person.controller.ts index ec66f7a9ca..84bb864cd3 100644 --- a/server/src/controllers/person.controller.ts +++ b/server/src/controllers/person.controller.ts @@ -63,8 +63,8 @@ export class PersonController { } @Delete() - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.PersonDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deletePeople(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.deleteAll(auth, dto); } @@ -86,8 +86,8 @@ export class PersonController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.PersonDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deletePerson(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } @@ -122,6 +122,7 @@ export class PersonController { @Post(':id/merge') @Authenticated({ permission: Permission.PersonMerge }) + @HttpCode(HttpStatus.OK) mergePerson( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, diff --git a/server/src/controllers/search.controller.spec.ts b/server/src/controllers/search.controller.spec.ts index 39d2cb8fcd..adbc8be0f3 100644 --- a/server/src/controllers/search.controller.spec.ts +++ b/server/src/controllers/search.controller.spec.ts @@ -128,12 +128,6 @@ describe(SearchController.name, () => { await request(ctx.getHttpServer()).post('/search/smart'); expect(ctx.authenticate).toHaveBeenCalled(); }); - - it('should require a query', async () => { - const { status, body } = await request(ctx.getHttpServer()).post('/search/smart').send({}); - expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest(['query should not be empty', 'query must be a string'])); - }); }); describe('GET /search/explore', () => { diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 9bda1fcada..f9aa6bce81 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -4,6 +4,7 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, MetadataSearchDto, PlacesResponseDto, RandomSearchDto, @@ -16,6 +17,7 @@ import { SmartSearchDto, StatisticsSearchDto, } from 'src/dtos/search.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { SearchService } from 'src/services/search.service'; @@ -25,59 +27,66 @@ export class SearchController { constructor(private service: SearchService) {} @Post('metadata') + @Authenticated({ permission: Permission.AssetRead }) @HttpCode(HttpStatus.OK) - @Authenticated() searchAssets(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise { return this.service.searchMetadata(auth, dto); } @Post('statistics') + @Authenticated({ permission: Permission.AssetStatistics }) @HttpCode(HttpStatus.OK) - @Authenticated() searchAssetStatistics(@Auth() auth: AuthDto, @Body() dto: StatisticsSearchDto): Promise { return this.service.searchStatistics(auth, dto); } @Post('random') + @Authenticated({ permission: Permission.AssetRead }) @HttpCode(HttpStatus.OK) - @Authenticated() searchRandom(@Auth() auth: AuthDto, @Body() dto: RandomSearchDto): Promise { return this.service.searchRandom(auth, dto); } - @Post('smart') + @Post('large-assets') + @Authenticated({ permission: Permission.AssetRead }) + @HttpCode(HttpStatus.OK) + searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise { + return this.service.searchLargeAssets(auth, dto); + } + + @Post('smart') + @Authenticated({ permission: Permission.AssetRead }) @HttpCode(HttpStatus.OK) - @Authenticated() searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise { return this.service.searchSmart(auth, dto); } @Get('explore') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getExploreData(@Auth() auth: AuthDto): Promise { return this.service.getExploreData(auth); } @Get('person') - @Authenticated() + @Authenticated({ permission: Permission.PersonRead }) searchPerson(@Auth() auth: AuthDto, @Query() dto: SearchPeopleDto): Promise { return this.service.searchPerson(auth, dto); } @Get('places') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) searchPlaces(@Query() dto: SearchPlacesDto): Promise { return this.service.searchPlaces(dto); } @Get('cities') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getAssetsByCity(@Auth() auth: AuthDto): Promise { return this.service.getAssetsByCity(auth); } @Get('suggestions') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise { // TODO fix open api generation to indicate that results can be nullable return this.service.getSearchSuggestions(auth, dto) as Promise; diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 3544fce2a0..f9a340eb31 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Delete, Get, Put } from '@nestjs/common'; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Put } from '@nestjs/common'; import { ApiNotFoundResponse, ApiTags } from '@nestjs/swagger'; import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto'; import { @@ -15,6 +15,7 @@ import { ServerVersionResponseDto, } from 'src/dtos/server.dto'; import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto'; +import { Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { ServerService } from 'src/services/server.service'; import { SystemMetadataService } from 'src/services/system-metadata.service'; @@ -30,19 +31,19 @@ export class ServerController { ) {} @Get('about') - @Authenticated() + @Authenticated({ permission: Permission.ServerAbout }) getAboutInfo(): Promise { return this.service.getAboutInfo(); } @Get('apk-links') - @Authenticated() + @Authenticated({ permission: Permission.ServerApkLinks }) getApkLinks(): ServerApkLinksDto { return this.service.getApkLinks(); } @Get('storage') - @Authenticated() + @Authenticated({ permission: Permission.ServerStorage }) getStorage(): Promise { return this.service.getStorage(); } @@ -78,7 +79,7 @@ export class ServerController { } @Get('statistics') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.ServerStatistics, admin: true }) getServerStatistics(): Promise { return this.service.getStatistics(); } @@ -88,27 +89,28 @@ export class ServerController { return this.service.getSupportedMediaTypes(); } - @Put('license') - @Authenticated({ admin: true }) - setServerLicense(@Body() license: LicenseKeyDto): Promise { - return this.service.setLicense(license); - } - - @Delete('license') - @Authenticated({ admin: true }) - deleteServerLicense(): Promise { - return this.service.deleteLicense(); - } - @Get('license') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.ServerLicenseRead, admin: true }) @ApiNotFoundResponse() getServerLicense(): Promise { return this.service.getLicense(); } + @Put('license') + @Authenticated({ permission: Permission.ServerLicenseUpdate, admin: true }) + setServerLicense(@Body() license: LicenseKeyDto): Promise { + return this.service.setLicense(license); + } + + @Delete('license') + @Authenticated({ permission: Permission.ServerLicenseDelete, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) + deleteServerLicense(): Promise { + return this.service.deleteLicense(); + } + @Get('version-check') - @Authenticated() + @Authenticated({ permission: Permission.ServerVersionCheck }) getVersionCheck(): Promise { return this.systemMetadataService.getVersionCheckState(); } diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 273d625ca7..ef0a93e012 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -1,4 +1,18 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req, Res } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + Patch, + Post, + Put, + Query, + Req, + Res, +} from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Request, Response } from 'express'; import { AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; @@ -73,6 +87,7 @@ export class SharedLinkController { @Delete(':id') @Authenticated({ permission: Permission.SharedLinkDelete }) + @HttpCode(HttpStatus.NO_CONTENT) removeSharedLink(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/stack.controller.ts b/server/src/controllers/stack.controller.ts index 238753734c..6acd4abc24 100644 --- a/server/src/controllers/stack.controller.ts +++ b/server/src/controllers/stack.controller.ts @@ -6,7 +6,7 @@ import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto } from import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { StackService } from 'src/services/stack.service'; -import { UUIDParamDto } from 'src/validation'; +import { UUIDAssetIDParamDto, UUIDParamDto } from 'src/validation'; @ApiTags('Stacks') @Controller('stacks') @@ -49,9 +49,16 @@ export class StackController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.StackDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteStack(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } + + @Delete(':id/assets/:assetId') + @Authenticated({ permission: Permission.StackUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + removeAssetFromStack(@Auth() auth: AuthDto, @Param() dto: UUIDAssetIDParamDto): Promise { + return this.service.removeAsset(auth, dto); + } } diff --git a/server/src/controllers/sync.controller.ts b/server/src/controllers/sync.controller.ts index 0945810be7..61432e43e3 100644 --- a/server/src/controllers/sync.controller.ts +++ b/server/src/controllers/sync.controller.ts @@ -12,6 +12,7 @@ import { SyncAckSetDto, SyncStreamDto, } from 'src/dtos/sync.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; import { SyncService } from 'src/services/sync.service'; @@ -25,23 +26,23 @@ export class SyncController { ) {} @Post('full-sync') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) getFullSyncForUser(@Auth() auth: AuthDto, @Body() dto: AssetFullSyncDto): Promise { return this.service.getFullSync(auth, dto); } @Post('delta-sync') - @HttpCode(HttpStatus.OK) @Authenticated() + @HttpCode(HttpStatus.OK) getDeltaSync(@Auth() auth: AuthDto, @Body() dto: AssetDeltaSyncDto): Promise { return this.service.getDeltaSync(auth, dto); } @Post('stream') + @Authenticated({ permission: Permission.SyncStream }) @Header('Content-Type', 'application/jsonlines+json') @HttpCode(HttpStatus.OK) - @Authenticated() async getSyncStream(@Auth() auth: AuthDto, @Res() res: Response, @Body() dto: SyncStreamDto) { try { await this.service.stream(auth, res, dto); @@ -52,22 +53,22 @@ export class SyncController { } @Get('ack') - @Authenticated() + @Authenticated({ permission: Permission.SyncCheckpointRead }) getSyncAck(@Auth() auth: AuthDto): Promise { return this.service.getAcks(auth); } @Post('ack') + @Authenticated({ permission: Permission.SyncCheckpointUpdate }) @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() sendSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckSetDto) { return this.service.setAcks(auth, dto); } @Delete('ack') + @Authenticated({ permission: Permission.SyncCheckpointDelete }) @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() - deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto) { + deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto): Promise { return this.service.deleteAcks(auth, dto); } } diff --git a/server/src/controllers/system-metadata.controller.ts b/server/src/controllers/system-metadata.controller.ts index ad2245a391..d6634e9444 100644 --- a/server/src/controllers/system-metadata.controller.ts +++ b/server/src/controllers/system-metadata.controller.ts @@ -21,8 +21,8 @@ export class SystemMetadataController { } @Post('admin-onboarding') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.SystemMetadataUpdate, admin: true }) + @HttpCode(HttpStatus.NO_CONTENT) updateAdminOnboarding(@Body() dto: AdminOnboardingUpdateDto): Promise { return this.service.updateAdminOnboarding(dto); } diff --git a/server/src/controllers/tag.controller.ts b/server/src/controllers/tag.controller.ts index 4906bc0c6e..59915ef2a4 100644 --- a/server/src/controllers/tag.controller.ts +++ b/server/src/controllers/tag.controller.ts @@ -57,8 +57,8 @@ export class TagController { } @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) @Authenticated({ permission: Permission.TagDelete }) + @HttpCode(HttpStatus.NO_CONTENT) deleteTag(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.remove(auth, id); } diff --git a/server/src/controllers/trash.controller.ts b/server/src/controllers/trash.controller.ts index 1bb46e4f98..eaf489f104 100644 --- a/server/src/controllers/trash.controller.ts +++ b/server/src/controllers/trash.controller.ts @@ -13,22 +13,22 @@ export class TrashController { constructor(private service: TrashService) {} @Post('empty') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) emptyTrash(@Auth() auth: AuthDto): Promise { return this.service.empty(auth); } @Post('restore') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) restoreTrash(@Auth() auth: AuthDto): Promise { return this.service.restore(auth); } @Post('restore/assets') - @HttpCode(HttpStatus.OK) @Authenticated({ permission: Permission.AssetDelete }) + @HttpCode(HttpStatus.OK) restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.restoreAssets(auth, dto); } diff --git a/server/src/controllers/user-admin.controller.spec.ts b/server/src/controllers/user-admin.controller.spec.ts new file mode 100644 index 0000000000..bd9c966d42 --- /dev/null +++ b/server/src/controllers/user-admin.controller.spec.ts @@ -0,0 +1,79 @@ +import { UserAdminController } from 'src/controllers/user-admin.controller'; +import { UserAdminCreateDto } from 'src/dtos/user.dto'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { UserAdminService } from 'src/services/user-admin.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { factory } from 'test/small.factory'; +import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +describe(UserAdminController.name, () => { + let ctx: ControllerContext; + const service = mockBaseService(UserAdminService); + + beforeAll(async () => { + ctx = await controllerSetup(UserAdminController, [ + { provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) }, + { provide: UserAdminService, useValue: service }, + ]); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /admin/users', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get('/admin/users'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('POST /admin/users', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).post('/admin/users'); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should not allow decimal quota`, async () => { + const dto: UserAdminCreateDto = { + email: 'user@immich.app', + password: 'test', + name: 'Test User', + quotaSizeInBytes: 1.2, + }; + + const { status, body } = await request(ctx.getHttpServer()) + .post(`/admin/users`) + .set('Authorization', `Bearer token`) + .send(dto); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['quotaSizeInBytes must be an integer number']))); + }); + }); + + describe('GET /admin/users/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).get(`/admin/users/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + }); + + describe('PUT /admin/users/:id', () => { + it('should be an authenticated route', async () => { + await request(ctx.getHttpServer()).put(`/admin/users/${factory.uuid()}`); + expect(ctx.authenticate).toHaveBeenCalled(); + }); + + it(`should not allow decimal quota`, async () => { + const { status, body } = await request(ctx.getHttpServer()) + .put(`/admin/users/${factory.uuid()}`) + .set('Authorization', `Bearer token`) + .send({ quotaSizeInBytes: 1.2 }); + expect(status).toBe(400); + expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['quotaSizeInBytes must be an integer number']))); + }); + }); +}); diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts index 76d2cf4c5a..d72b088c54 100644 --- a/server/src/controllers/user.controller.ts +++ b/server/src/controllers/user.controller.ts @@ -21,7 +21,7 @@ import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto'; -import { RouteKey } from 'src/enum'; +import { Permission, RouteKey } from 'src/enum'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -38,31 +38,31 @@ export class UserController { ) {} @Get() - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) searchUsers(@Auth() auth: AuthDto): Promise { return this.service.search(auth); } @Get('me') - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) getMyUser(@Auth() auth: AuthDto): Promise { return this.service.getMe(auth); } @Put('me') - @Authenticated() + @Authenticated({ permission: Permission.UserUpdate }) updateMyUser(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto): Promise { return this.service.updateMe(auth, dto); } @Get('me/preferences') - @Authenticated() + @Authenticated({ permission: Permission.UserPreferenceRead }) getMyPreferences(@Auth() auth: AuthDto): Promise { return this.service.getMyPreferences(auth); } @Put('me/preferences') - @Authenticated() + @Authenticated({ permission: Permission.UserPreferenceUpdate }) updateMyPreferences( @Auth() auth: AuthDto, @Body() dto: UserPreferencesUpdateDto, @@ -71,52 +71,54 @@ export class UserController { } @Get('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseRead }) getUserLicense(@Auth() auth: AuthDto): Promise { return this.service.getLicense(auth); } @Put('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseUpdate }) async setUserLicense(@Auth() auth: AuthDto, @Body() license: LicenseKeyDto): Promise { return this.service.setLicense(auth, license); } @Delete('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async deleteUserLicense(@Auth() auth: AuthDto): Promise { await this.service.deleteLicense(auth); } @Get('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingRead }) getUserOnboarding(@Auth() auth: AuthDto): Promise { return this.service.getOnboarding(auth); } @Put('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingUpdate }) async setUserOnboarding(@Auth() auth: AuthDto, @Body() Onboarding: OnboardingDto): Promise { return this.service.setOnboarding(auth, Onboarding); } @Delete('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingDelete }) + @HttpCode(HttpStatus.NO_CONTENT) async deleteUserOnboarding(@Auth() auth: AuthDto): Promise { await this.service.deleteOnboarding(auth); } @Get(':id') - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) getUser(@Param() { id }: UUIDParamDto): Promise { return this.service.get(id); } + @Post('profile-image') + @Authenticated({ permission: Permission.UserProfileImageUpdate }) @UseInterceptors(FileUploadInterceptor) @ApiConsumes('multipart/form-data') @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto }) - @Post('profile-image') - @Authenticated() createProfileImage( @Auth() auth: AuthDto, @UploadedFile() fileInfo: Express.Multer.File, @@ -125,15 +127,15 @@ export class UserController { } @Delete('profile-image') + @Authenticated({ permission: Permission.UserProfileImageDelete }) @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() deleteProfileImage(@Auth() auth: AuthDto): Promise { return this.service.deleteProfileImage(auth); } @Get(':id/profile-image') @FileResponse() - @Authenticated() + @Authenticated({ permission: Permission.UserProfileImageRead }) async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) { await sendFile(res, next, () => this.service.getProfileImage(id), this.logger); } diff --git a/server/src/cores/storage.core.spec.ts b/server/src/cores/storage.core.spec.ts index 7bb2cdb1be..ed446f9259 100644 --- a/server/src/cores/storage.core.spec.ts +++ b/server/src/cores/storage.core.spec.ts @@ -2,7 +2,6 @@ import { StorageCore } from 'src/cores/storage.core'; import { vitest } from 'vitest'; vitest.mock('src/constants', () => ({ - APP_MEDIA_LOCATION: '/photos', ADDED_IN_PREFIX: 'This property was added in ', DEPRECATED_IN_PREFIX: 'This property was deprecated in ', IWorker: 'IWorker', @@ -10,6 +9,10 @@ vitest.mock('src/constants', () => ({ describe('StorageCore', () => { describe('isImmichPath', () => { + beforeAll(() => { + StorageCore.setMediaLocation('/photos'); + }); + it('should return true for APP_MEDIA_LOCATION path', () => { const immichPath = '/photos'; expect(StorageCore.isImmichPath(immichPath)).toBe(true); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 6576b397e3..25573cb08e 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,6 +1,5 @@ import { randomUUID } from 'node:crypto'; import { dirname, join, resolve } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageAsset } from 'src/database'; import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -32,6 +31,8 @@ export type ThumbnailPathEntity = { id: string; ownerId: string }; let instance: StorageCore | null; +let mediaLocation: string | undefined; + export class StorageCore { private constructor( private assetRepository: AssetRepository, @@ -42,7 +43,9 @@ export class StorageCore { private storageRepository: StorageRepository, private systemMetadataRepository: SystemMetadataRepository, private logger: LoggingRepository, - ) {} + ) { + this.logger.setContext(StorageCore.name); + } static create( assetRepository: AssetRepository, @@ -74,6 +77,18 @@ export class StorageCore { instance = null; } + static getMediaLocation(): string { + if (mediaLocation === undefined) { + throw new Error('Media location is not set.'); + } + + return mediaLocation; + } + + static setMediaLocation(location: string) { + mediaLocation = location; + } + static getFolderLocation(folder: StorageFolder, userId: string) { return join(StorageCore.getBaseFolder(folder), userId); } @@ -83,7 +98,7 @@ export class StorageCore { } static getBaseFolder(folder: StorageFolder) { - return join(APP_MEDIA_LOCATION, folder); + return join(StorageCore.getMediaLocation(), folder); } static getPersonThumbnailPath(person: ThumbnailPathEntity) { @@ -108,7 +123,7 @@ export class StorageCore { static isImmichPath(path: string) { const resolvedPath = resolve(path); - const resolvedAppMediaLocation = resolve(APP_MEDIA_LOCATION); + const resolvedAppMediaLocation = StorageCore.getMediaLocation(); const normalizedPath = resolvedPath.endsWith('/') ? resolvedPath : resolvedPath + '/'; const normalizedAppMediaLocation = resolvedAppMediaLocation.endsWith('/') ? resolvedAppMediaLocation diff --git a/server/src/database.ts b/server/src/database.ts index dc99fc5b31..f472c643ee 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -192,6 +192,7 @@ export type SharedLink = { showExif: boolean; type: SharedLinkType; userId: string; + slug: string | null; }; export type Album = Selectable & { @@ -201,7 +202,6 @@ export type Album = Selectable & { export type AuthSession = { id: string; - isPendingSyncReset: boolean; hasElevatedPermission: boolean; }; @@ -308,7 +308,7 @@ export const columns = { assetFiles: ['asset_file.id', 'asset_file.path', 'asset_file.type'], authUser: ['user.id', 'user.name', 'user.email', 'user.isAdmin', 'user.quotaUsageInBytes', 'user.quotaSizeInBytes'], authApiKey: ['api_key.id', 'api_key.permissions'], - authSession: ['session.id', 'session.isPendingSyncReset', 'session.updatedAt', 'session.pinExpiresAt'], + authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt'], authSharedLink: [ 'shared_link.id', 'shared_link.userId', @@ -353,9 +353,11 @@ export const columns = { 'asset.duration', 'asset.livePhotoVideoId', 'asset.stackId', + 'asset.libraryId', ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId', 'profileImagePath', 'profileChangedAt'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/decorators.ts b/server/src/decorators.ts index b88f2d2d7e..8a8e23d880 100644 --- a/server/src/decorators.ts +++ b/server/src/decorators.ts @@ -1,5 +1,5 @@ import { SetMetadata, applyDecorators } from '@nestjs/common'; -import { ApiExtension, ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger'; +import { ApiExtension, ApiOperation, ApiOperationOptions, ApiProperty, ApiTags } from '@nestjs/swagger'; import _ from 'lodash'; import { ADDED_IN_PREFIX, DEPRECATED_IN_PREFIX, LIFECYCLE_EXTENSION } from 'src/constants'; import { ImmichWorker, JobName, MetadataKey, QueueName } from 'src/enum'; @@ -87,7 +87,7 @@ export function Chunked( return Promise.all( chunks(argument, chunkSize).map(async (chunk) => { - await Reflect.apply(originalMethod, this, [ + return await Reflect.apply(originalMethod, this, [ ...arguments_.slice(0, parameterIndex), chunk, ...arguments_.slice(parameterIndex + 1), @@ -103,7 +103,7 @@ export function ChunkedArray(options?: { paramIndex?: number }): MethodDecorator } export function ChunkedSet(options?: { paramIndex?: number }): MethodDecorator { - return Chunked({ ...options, mergeFn: setUnion }); + return Chunked({ ...options, mergeFn: (args: Set[]) => setUnion(...args) }); } const UUID = '00000000-0000-4000-a000-000000000000'; @@ -159,12 +159,21 @@ type LifecycleMetadata = { deprecatedAt?: LifecycleRelease; }; -export const EndpointLifecycle = ({ addedAt, deprecatedAt }: LifecycleMetadata) => { +export const EndpointLifecycle = ({ + addedAt, + deprecatedAt, + description, + ...options +}: LifecycleMetadata & ApiOperationOptions) => { const decorators: MethodDecorator[] = [ApiExtension(LIFECYCLE_EXTENSION, { addedAt, deprecatedAt })]; if (deprecatedAt) { decorators.push( ApiTags('Deprecated'), - ApiOperation({ deprecated: true, description: DEPRECATED_IN_PREFIX + deprecatedAt }), + ApiOperation({ + deprecated: true, + description: DEPRECATED_IN_PREFIX + deprecatedAt + (description ? `. ${description}` : ''), + ...options, + }), ); } diff --git a/server/src/dtos/album.dto.ts b/server/src/dtos/album.dto.ts index 3a88ba5be3..00f5759aac 100644 --- a/server/src/dtos/album.dto.ts +++ b/server/src/dtos/album.dto.ts @@ -3,6 +3,7 @@ import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsString, ValidateNested } from 'class-validator'; import _ from 'lodash'; import { AlbumUser, AuthSharedLink, User } from 'src/database'; +import { BulkIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { UserResponseDto, mapUser } from 'src/dtos/user.dto'; @@ -54,6 +55,20 @@ export class CreateAlbumDto { assetIds?: string[]; } +export class AlbumsAddAssetsDto { + @ValidateUUID({ each: true }) + albumIds!: string[]; + + @ValidateUUID({ each: true }) + assetIds!: string[]; +} + +export class AlbumsAddAssetsResponseDto { + success!: boolean; + @ValidateEnum({ enum: BulkIdErrorReason, name: 'BulkIdErrorReason', optional: true }) + error?: BulkIdErrorReason; +} + export class UpdateAlbumDto { @Optional() @IsString() diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index ea86e087d8..755069d827 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -1,6 +1,8 @@ +import { BadRequestException } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; +import { plainToInstance, Transform, Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator'; +import { AssetMetadataUpsertItemDto } from 'src/dtos/asset.dto'; import { AssetVisibility } from 'src/enum'; import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation'; @@ -64,6 +66,20 @@ export class AssetMediaCreateDto extends AssetMediaBase { @ValidateUUID({ optional: true }) livePhotoVideoId?: string; + @Transform(({ value }) => { + try { + const json = JSON.parse(value); + const items = Array.isArray(json) ? json : [json]; + return items.map((item) => plainToInstance(AssetMetadataUpsertItemDto, item)); + } catch { + throw new BadRequestException(['metadata must be valid JSON']); + } + }) + @Optional() + @ValidateNested({ each: true }) + @IsArray() + metadata!: AssetMetadataUpsertItemDto[]; + @ApiProperty({ type: 'string', format: 'binary', required: false }) [UploadFieldName.SIDECAR_DATA]?: any; } diff --git a/server/src/dtos/asset-response.dto.ts b/server/src/dtos/asset-response.dto.ts index 98ed8669f0..f60f2a8824 100644 --- a/server/src/dtos/asset-response.dto.ts +++ b/server/src/dtos/asset-response.dto.ts @@ -37,6 +37,13 @@ export class SanitizedAssetResponseDto { } export class AssetResponseDto extends SanitizedAssetResponseDto { + @ApiProperty({ + type: 'string', + format: 'date-time', + description: 'The UTC timestamp when the asset was originally uploaded to Immich.', + example: '2024-01-15T20:30:00.000Z', + }) + createdAt!: Date; deviceAssetId!: string; deviceId!: string; ownerId!: string; @@ -190,6 +197,7 @@ export function mapAsset(entity: MapAsset, options: AssetMapOptions = {}): Asset return { id: entity.id, + createdAt: entity.createdAt, deviceAssetId: entity.deviceAssetId, ownerId: entity.ownerId, owner: entity.owner ? mapUser(entity.owner) : undefined, diff --git a/server/src/dtos/asset.dto.ts b/server/src/dtos/asset.dto.ts index 5728d21646..6a89b7e2cf 100644 --- a/server/src/dtos/asset.dto.ts +++ b/server/src/dtos/asset.dto.ts @@ -1,21 +1,26 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { + IsArray, IsDateString, IsInt, IsLatitude, IsLongitude, IsNotEmpty, + IsObject, IsPositive, IsString, + IsTimeZone, Max, Min, ValidateIf, + ValidateNested, } from 'class-validator'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; -import { AssetType, AssetVisibility } from 'src/enum'; +import { AssetMetadataKey, AssetType, AssetVisibility } from 'src/enum'; import { AssetStats } from 'src/repositories/asset.repository'; -import { Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation'; +import { AssetMetadata, AssetMetadataItem } from 'src/types'; +import { IsNotSiblingOf, Optional, ValidateBoolean, ValidateEnum, ValidateUUID } from 'src/validation'; export class DeviceIdDto { @IsNotEmpty() @@ -65,6 +70,16 @@ export class AssetBulkUpdateDto extends UpdateAssetBase { @Optional() duplicateId?: string | null; + + @IsNotSiblingOf(['dateTimeOriginal']) + @Optional() + @IsInt() + dateTimeRelative?: number; + + @IsNotSiblingOf(['dateTimeOriginal']) + @IsTimeZone() + @Optional() + timeZone?: string; } export class UpdateAssetDto extends UpdateAssetBase { @@ -124,6 +139,53 @@ export class AssetStatsResponseDto { total!: number; } +export class AssetMetadataRouteParams { + @ValidateUUID() + id!: string; + + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; +} + +export class AssetMetadataUpsertDto { + @IsArray() + @ValidateNested({ each: true }) + @Type(() => AssetMetadataUpsertItemDto) + items!: AssetMetadataUpsertItemDto[]; +} + +export class AssetMetadataUpsertItemDto implements AssetMetadataItem { + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + + @IsObject() + @ValidateNested() + @Type((options) => { + switch (options?.object.key) { + case AssetMetadataKey.MobileApp: { + return AssetMetadataMobileAppDto; + } + default: { + return Object; + } + } + }) + value!: AssetMetadata[AssetMetadataKey]; +} + +export class AssetMetadataMobileAppDto { + @IsString() + @Optional() + iCloudId?: string; +} + +export class AssetMetadataResponseDto { + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + value!: object; + updatedAt!: Date; +} + export const mapStats = (stats: AssetStats): AssetStatsResponseDto => { return { images: stats[AssetType.Image], diff --git a/server/src/dtos/partner.dto.ts b/server/src/dtos/partner.dto.ts index 28d4adf8b7..599213f662 100644 --- a/server/src/dtos/partner.dto.ts +++ b/server/src/dtos/partner.dto.ts @@ -1,9 +1,14 @@ import { IsNotEmpty } from 'class-validator'; import { UserResponseDto } from 'src/dtos/user.dto'; import { PartnerDirection } from 'src/repositories/partner.repository'; -import { ValidateEnum } from 'src/validation'; +import { ValidateEnum, ValidateUUID } from 'src/validation'; -export class UpdatePartnerDto { +export class PartnerCreateDto { + @ValidateUUID() + sharedWithId!: string; +} + +export class PartnerUpdateDto { @IsNotEmpty() inTimeline!: boolean; } diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index aef78e51ea..5f8b018afe 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -6,7 +6,7 @@ import { PropertyLifecycle } from 'src/decorators'; import { AlbumResponseDto } from 'src/dtos/album.dto'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AssetOrder, AssetType, AssetVisibility } from 'src/enum'; -import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation'; +import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateString, ValidateUUID } from 'src/validation'; class BaseSearchDto { @ValidateUUID({ optional: true, nullable: true }) @@ -126,6 +126,15 @@ export class RandomSearchDto extends BaseSearchWithResultsDto { withPeople?: boolean; } +export class LargeAssetSearchDto extends BaseSearchWithResultsDto { + @Optional() + @IsInt() + @Min(0) + @Type(() => Number) + @ApiProperty({ type: 'integer' }) + minFileSize?: number; +} + export class MetadataSearchDto extends RandomSearchDto { @ValidateUUID({ optional: true }) id?: string; @@ -135,9 +144,7 @@ export class MetadataSearchDto extends RandomSearchDto { @Optional() deviceAssetId?: string; - @IsString() - @IsNotEmpty() - @Optional() + @ValidateString({ optional: true, trim: true }) description?: string; @IsString() @@ -145,9 +152,7 @@ export class MetadataSearchDto extends RandomSearchDto { @Optional() checksum?: string; - @IsString() - @IsNotEmpty() - @Optional() + @ValidateString({ optional: true, trim: true }) originalFileName?: string; @IsString() @@ -181,16 +186,17 @@ export class MetadataSearchDto extends RandomSearchDto { } export class StatisticsSearchDto extends BaseSearchDto { - @IsString() - @IsNotEmpty() - @Optional() + @ValidateString({ optional: true, trim: true }) description?: string; } export class SmartSearchDto extends BaseSearchWithResultsDto { - @IsString() - @IsNotEmpty() - query!: string; + @ValidateString({ optional: true, trim: true }) + query?: string; + + @ValidateUUID({ optional: true }) + @Optional() + queryAssetId?: string; @IsString() @IsNotEmpty() diff --git a/server/src/dtos/session.dto.ts b/server/src/dtos/session.dto.ts index 0babbb9182..7ccc72a5f1 100644 --- a/server/src/dtos/session.dto.ts +++ b/server/src/dtos/session.dto.ts @@ -1,4 +1,4 @@ -import { IsInt, IsPositive, IsString } from 'class-validator'; +import { Equals, IsInt, IsPositive, IsString } from 'class-validator'; import { Session } from 'src/database'; import { Optional, ValidateBoolean } from 'src/validation'; @@ -22,7 +22,8 @@ export class SessionCreateDto { export class SessionUpdateDto { @ValidateBoolean({ optional: true }) - isPendingSyncReset?: boolean; + @Equals(true) + isPendingSyncReset?: true; } export class SessionResponseDto { diff --git a/server/src/dtos/shared-link.dto.ts b/server/src/dtos/shared-link.dto.ts index 299590c0e3..011707f1f7 100644 --- a/server/src/dtos/shared-link.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -22,13 +22,17 @@ export class SharedLinkCreateDto { @ValidateUUID({ optional: true }) albumId?: string; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - description?: string; + description?: string | null; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - password?: string; + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @ValidateDate({ optional: true, nullable: true }) expiresAt?: Date | null = null; @@ -44,16 +48,22 @@ export class SharedLinkCreateDto { } export class SharedLinkEditDto { - @Optional() - description?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + description?: string | null; - @Optional() - password?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @Optional({ nullable: true }) expiresAt?: Date | null; - @Optional() + @ValidateBoolean({ optional: true }) allowUpload?: boolean; @ValidateBoolean({ optional: true }) @@ -99,6 +109,8 @@ export class SharedLinkResponseDto { allowDownload!: boolean; showMetadata!: boolean; + + slug!: string | null; } export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { @@ -118,6 +130,7 @@ export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } @@ -141,5 +154,6 @@ export function mapSharedLinkWithoutMetadata(sharedLink: SharedLink): SharedLink allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index e0c9c059c4..c936ec52cc 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -4,12 +4,14 @@ import { ArrayMaxSize, IsInt, IsPositive, IsString } from 'class-validator'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AlbumUserRole, + AssetMetadataKey, AssetOrder, AssetType, AssetVisibility, MemoryType, SyncEntityType, SyncRequestType, + UserAvatarColor, UserMetadataKey, } from 'src/enum'; import { UserMetadata } from 'src/types'; @@ -58,7 +60,23 @@ export class SyncUserV1 { id!: string; name!: string; email!: string; + @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) + avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; + hasProfileImage!: boolean; + profileChangedAt!: Date; +} + +@ExtraModel() +export class SyncAuthUserV1 extends SyncUserV1 { + isAdmin!: boolean; + pinCode!: string | null; + oauthId!: string; + storageLabel!: string | null; + @ApiProperty({ type: 'integer' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer' }) + quotaUsageInBytes!: number; } @ExtraModel() @@ -98,6 +116,7 @@ export class SyncAssetV1 { visibility!: AssetVisibility; livePhotoVideoId!: string | null; stackId!: string | null; + libraryId!: string | null; } @ExtraModel() @@ -144,6 +163,21 @@ export class SyncAssetExifV1 { fps!: number | null; } +@ExtraModel() +export class SyncAssetMetadataV1 { + assetId!: string; + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; + value!: object; +} + +@ExtraModel() +export class SyncAssetMetadataDeleteV1 { + assetId!: string; + @ValidateEnum({ enum: AssetMetadataKey, name: 'AssetMetadataKey' }) + key!: AssetMetadataKey; +} + @ExtraModel() export class SyncAlbumDeleteV1 { albumId!: string; @@ -261,11 +295,17 @@ export class SyncAssetFaceV1 { id!: string; assetId!: string; personId!: string | null; + @ApiProperty({ type: 'integer' }) imageWidth!: number; + @ApiProperty({ type: 'integer' }) imageHeight!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY1!: number; + @ApiProperty({ type: 'integer' }) boundingBoxX2!: number; + @ApiProperty({ type: 'integer' }) boundingBoxY2!: number; sourceType!: string; } @@ -278,14 +318,16 @@ export class SyncAssetFaceDeleteV1 { @ExtraModel() export class SyncUserMetadataV1 { userId!: string; - key!: string; + @ValidateEnum({ enum: UserMetadataKey, name: 'UserMetadataKey' }) + key!: UserMetadataKey; value!: UserMetadata[UserMetadataKey]; } @ExtraModel() export class SyncUserMetadataDeleteV1 { userId!: string; - key!: string; + @ValidateEnum({ enum: UserMetadataKey, name: 'UserMetadataKey' }) + key!: UserMetadataKey; } @ExtraModel() @@ -294,13 +336,19 @@ export class SyncAckV1 {} @ExtraModel() export class SyncResetV1 {} +@ExtraModel() +export class SyncCompleteV1 {} + export type SyncItem = { + [SyncEntityType.AuthUserV1]: SyncAuthUserV1; [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; [SyncEntityType.PartnerV1]: SyncPartnerV1; [SyncEntityType.PartnerDeleteV1]: SyncPartnerDeleteV1; [SyncEntityType.AssetV1]: SyncAssetV1; [SyncEntityType.AssetDeleteV1]: SyncAssetDeleteV1; + [SyncEntityType.AssetMetadataV1]: SyncAssetMetadataV1; + [SyncEntityType.AssetMetadataDeleteV1]: SyncAssetMetadataDeleteV1; [SyncEntityType.AssetExifV1]: SyncAssetExifV1; [SyncEntityType.PartnerAssetV1]: SyncAssetV1; [SyncEntityType.PartnerAssetBackfillV1]: SyncAssetV1; @@ -312,9 +360,11 @@ export type SyncItem = { [SyncEntityType.AlbumUserV1]: SyncAlbumUserV1; [SyncEntityType.AlbumUserBackfillV1]: SyncAlbumUserV1; [SyncEntityType.AlbumUserDeleteV1]: SyncAlbumUserDeleteV1; - [SyncEntityType.AlbumAssetV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetCreateV1]: SyncAssetV1; + [SyncEntityType.AlbumAssetUpdateV1]: SyncAssetV1; [SyncEntityType.AlbumAssetBackfillV1]: SyncAssetV1; - [SyncEntityType.AlbumAssetExifV1]: SyncAssetExifV1; + [SyncEntityType.AlbumAssetExifCreateV1]: SyncAssetExifV1; + [SyncEntityType.AlbumAssetExifUpdateV1]: SyncAssetExifV1; [SyncEntityType.AlbumAssetExifBackfillV1]: SyncAssetExifV1; [SyncEntityType.AlbumToAssetV1]: SyncAlbumToAssetV1; [SyncEntityType.AlbumToAssetBackfillV1]: SyncAlbumToAssetV1; @@ -335,6 +385,7 @@ export type SyncItem = { [SyncEntityType.UserMetadataV1]: SyncUserMetadataV1; [SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1; [SyncEntityType.SyncAckV1]: SyncAckV1; + [SyncEntityType.SyncCompleteV1]: SyncCompleteV1; [SyncEntityType.SyncResetV1]: SyncResetV1; }; diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts index 8a58995de7..1facc6c331 100644 --- a/server/src/dtos/system-config.dto.ts +++ b/server/src/dtos/system-config.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Exclude, Transform, Type } from 'class-transformer'; +import { Type } from 'class-transformer'; import { ArrayMinSize, IsInt, @@ -15,7 +15,6 @@ import { ValidateNested, } from 'class-validator'; import { SystemConfig } from 'src/config'; -import { PropertyLifecycle } from 'src/decorators'; import { CLIPConfig, DuplicateDetectionConfig, FacialRecognitionConfig } from 'src/dtos/model-config.dto'; import { AudioCodec, @@ -257,21 +256,32 @@ class SystemConfigLoggingDto { level!: LogLevel; } +class MachineLearningAvailabilityChecksDto { + @ValidateBoolean() + enabled!: boolean; + + @IsInt() + timeout!: number; + + @IsInt() + interval!: number; +} + class SystemConfigMachineLearningDto { @ValidateBoolean() enabled!: boolean; - @PropertyLifecycle({ deprecatedAt: 'v1.122.0' }) - @Exclude() - url?: string; - @IsUrl({ require_tld: false, allow_underscores: true }, { each: true }) @ArrayMinSize(1) - @Transform(({ obj, value }) => (obj.url ? [obj.url] : value)) @ValidateIf((dto) => dto.enabled) @ApiProperty({ type: 'array', items: { type: 'string', format: 'uri' }, minItems: 1 }) urls!: string[]; + @Type(() => MachineLearningAvailabilityChecksDto) + @ValidateNested() + @IsObject() + availabilityChecks!: MachineLearningAvailabilityChecksDto; + @Type(() => CLIPConfig) @ValidateNested() @IsObject() diff --git a/server/src/dtos/time-bucket.dto.ts b/server/src/dtos/time-bucket.dto.ts index 449cec3207..58772da00b 100644 --- a/server/src/dtos/time-bucket.dto.ts +++ b/server/src/dtos/time-bucket.dto.ts @@ -53,6 +53,12 @@ export class TimeBucketDto { description: 'Filter by asset visibility status (ARCHIVE, TIMELINE, HIDDEN, LOCKED)', }) visibility?: AssetVisibility; + + @ValidateBoolean({ + optional: true, + description: 'Include location data in the response', + }) + withCoordinates?: boolean; } export class TimeBucketAssetDto extends TimeBucketDto { @@ -185,6 +191,22 @@ export class TimeBucketAssetResponseDto { description: 'Array of country names extracted from EXIF GPS data', }) country!: (string | null)[]; + + @ApiProperty({ + type: 'array', + required: false, + items: { type: 'number', nullable: true }, + description: 'Array of latitude coordinates extracted from EXIF GPS data', + }) + latitude!: number[]; + + @ApiProperty({ + type: 'array', + required: false, + items: { type: 'number', nullable: true }, + description: 'Array of longitude coordinates extracted from EXIF GPS data', + }) + longitude!: number[]; } export class TimeBucketsResponseDto { diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts index 0da86bfcb5..443178aa10 100644 --- a/server/src/dtos/user.dto.ts +++ b/server/src/dtos/user.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsEmail, IsNotEmpty, IsNumber, IsString, Min } from 'class-validator'; +import { IsEmail, IsInt, IsNotEmpty, IsString, Min } from 'class-validator'; import { User, UserAdmin } from 'src/database'; import { UserAvatarColor, UserMetadataKey, UserStatus } from 'src/enum'; import { UserMetadataItem } from 'src/types'; @@ -91,7 +91,7 @@ export class UserAdminCreateDto { storageLabel?: string | null; @Optional({ nullable: true }) - @IsNumber() + @IsInt() @Min(0) @ApiProperty({ type: 'integer', format: 'int64' }) quotaSizeInBytes?: number | null; @@ -137,7 +137,7 @@ export class UserAdminUpdateDto { shouldChangePassword?: boolean; @Optional({ nullable: true }) - @IsNumber() + @IsInt() @Min(0) @ApiProperty({ type: 'integer', format: 'int64' }) quotaSizeInBytes?: number | null; diff --git a/server/src/emails/album-update.email.tsx b/server/src/emails/album-update.email.tsx index 3bed3a5b36..6fd2abb055 100644 --- a/server/src/emails/album-update.email.tsx +++ b/server/src/emails/album-update.email.tsx @@ -29,8 +29,8 @@ export const AlbumUpdateEmail = ({ - New media has been added to {albumName}, -
check it out! + New media has been added to {albumName}. +
Check it out!
); diff --git a/server/src/emails/components/button.component.tsx b/server/src/emails/components/button.component.tsx index b490e36650..a1fc4636cc 100644 --- a/server/src/emails/components/button.component.tsx +++ b/server/src/emails/components/button.component.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { Button, ButtonProps } from '@react-email/components'; +import { Button, ButtonProps, Text } from '@react-email/components'; export const ImmichButton = ({ children, ...props }: ButtonProps) => ( ); diff --git a/server/src/enum.ts b/server/src/enum.ts index f2eae615ab..646138b060 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -17,12 +17,14 @@ export enum ImmichHeader { UserToken = 'x-immich-user-token', SessionToken = 'x-immich-session-token', SharedLinkKey = 'x-immich-share-key', + SharedLinkSlug = 'x-immich-share-slug', Checksum = 'x-immich-checksum', Cid = 'x-immich-cid', } export enum ImmichQuery { SharedLinkKey = 'key', + SharedLinkSlug = 'slug', ApiKey = 'apiKey', SessionKey = 'sessionKey', } @@ -87,31 +89,45 @@ export enum Permission { AssetRead = 'asset.read', AssetUpdate = 'asset.update', AssetDelete = 'asset.delete', + AssetStatistics = 'asset.statistics', AssetShare = 'asset.share', AssetView = 'asset.view', AssetDownload = 'asset.download', AssetUpload = 'asset.upload', + AssetReplace = 'asset.replace', AlbumCreate = 'album.create', AlbumRead = 'album.read', AlbumUpdate = 'album.update', AlbumDelete = 'album.delete', AlbumStatistics = 'album.statistics', - - AlbumAddAsset = 'album.addAsset', - AlbumRemoveAsset = 'album.removeAsset', AlbumShare = 'album.share', AlbumDownload = 'album.download', + AlbumAssetCreate = 'albumAsset.create', + AlbumAssetDelete = 'albumAsset.delete', + + AlbumUserCreate = 'albumUser.create', + AlbumUserUpdate = 'albumUser.update', + AlbumUserDelete = 'albumUser.delete', + + AuthChangePassword = 'auth.changePassword', + AuthDeviceDelete = 'authDevice.delete', ArchiveRead = 'archive.read', + DuplicateRead = 'duplicate.read', + DuplicateDelete = 'duplicate.delete', + FaceCreate = 'face.create', FaceRead = 'face.read', FaceUpdate = 'face.update', FaceDelete = 'face.delete', + JobCreate = 'job.create', + JobRead = 'job.read', + LibraryCreate = 'library.create', LibraryRead = 'library.read', LibraryUpdate = 'library.update', @@ -125,6 +141,10 @@ export enum Permission { MemoryRead = 'memory.read', MemoryUpdate = 'memory.update', MemoryDelete = 'memory.delete', + MemoryStatistics = 'memory.statistics', + + MemoryAssetCreate = 'memoryAsset.create', + MemoryAssetDelete = 'memoryAsset.delete', NotificationCreate = 'notification.create', NotificationRead = 'notification.read', @@ -144,6 +164,20 @@ export enum Permission { PersonMerge = 'person.merge', PersonReassign = 'person.reassign', + PinCodeCreate = 'pinCode.create', + PinCodeUpdate = 'pinCode.update', + PinCodeDelete = 'pinCode.delete', + + ServerAbout = 'server.about', + ServerApkLinks = 'server.apkLinks', + ServerStorage = 'server.storage', + ServerStatistics = 'server.statistics', + ServerVersionCheck = 'server.versionCheck', + + ServerLicenseRead = 'serverLicense.read', + ServerLicenseUpdate = 'serverLicense.update', + ServerLicenseDelete = 'serverLicense.delete', + SessionCreate = 'session.create', SessionRead = 'session.read', SessionUpdate = 'session.update', @@ -160,6 +194,11 @@ export enum Permission { StackUpdate = 'stack.update', StackDelete = 'stack.delete', + SyncStream = 'sync.stream', + SyncCheckpointRead = 'syncCheckpoint.read', + SyncCheckpointUpdate = 'syncCheckpoint.update', + SyncCheckpointDelete = 'syncCheckpoint.delete', + SystemConfigRead = 'systemConfig.read', SystemConfigUpdate = 'systemConfig.update', @@ -172,10 +211,32 @@ export enum Permission { TagDelete = 'tag.delete', TagAsset = 'tag.asset', - AdminUserCreate = 'admin.user.create', - AdminUserRead = 'admin.user.read', - AdminUserUpdate = 'admin.user.update', - AdminUserDelete = 'admin.user.delete', + UserRead = 'user.read', + UserUpdate = 'user.update', + + UserLicenseCreate = 'userLicense.create', + UserLicenseRead = 'userLicense.read', + UserLicenseUpdate = 'userLicense.update', + UserLicenseDelete = 'userLicense.delete', + + UserOnboardingRead = 'userOnboarding.read', + UserOnboardingUpdate = 'userOnboarding.update', + UserOnboardingDelete = 'userOnboarding.delete', + + UserPreferenceRead = 'userPreference.read', + UserPreferenceUpdate = 'userPreference.update', + + UserProfileImageCreate = 'userProfileImage.create', + UserProfileImageRead = 'userProfileImage.read', + UserProfileImageUpdate = 'userProfileImage.update', + UserProfileImageDelete = 'userProfileImage.delete', + + AdminUserCreate = 'adminUser.create', + AdminUserRead = 'adminUser.read', + AdminUserUpdate = 'adminUser.update', + AdminUserDelete = 'adminUser.delete', + + AdminAuthUnlinkAll = 'adminAuth.unlinkAll', } export enum SharedLinkType { @@ -215,6 +276,10 @@ export enum UserMetadataKey { Onboarding = 'onboarding', } +export enum AssetMetadataKey { + MobileApp = 'mobile-app', +} + export enum UserAvatarColor { Primary = 'primary', Pink = 'pink', @@ -355,6 +420,11 @@ export enum LogLevel { Fatal = 'fatal', } +export enum ApiCustomExtension { + Permission = 'x-immich-permission', + AdminOnly = 'x-immich-admin-only', +} + export enum MetadataKey { AuthRoute = 'auth_route', AdminRoute = 'admin_route', @@ -417,6 +487,8 @@ export enum DatabaseExtension { export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Detect and configure the media location before jobs are queued which may use it + StorageService = -195, // 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 @@ -458,6 +530,7 @@ export enum JobName { AssetGenerateThumbnails = 'AssetGenerateThumbnails', AuditLogCleanup = 'AuditLogCleanup', + AuditTableCleanup = 'AuditTableCleanup', DatabaseBackup = 'DatabaseBackup', @@ -498,8 +571,7 @@ export enum JobName { SendMail = 'SendMail', SidecarQueueAll = 'SidecarQueueAll', - SidecarDiscovery = 'SidecarDiscovery', - SidecarSync = 'SidecarSync', + SidecarCheck = 'SidecarCheck', SidecarWrite = 'SidecarWrite', SmartSearchQueueAll = 'SmartSearchQueueAll', @@ -559,6 +631,8 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AssetMetadataV1 = 'AssetMetadataV1', + AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', PartnersV1 = 'PartnersV1', @@ -573,12 +647,16 @@ export enum SyncRequestType { } export enum SyncEntityType { + AuthUserV1 = 'AuthUserV1', + UserV1 = 'UserV1', UserDeleteV1 = 'UserDeleteV1', AssetV1 = 'AssetV1', AssetDeleteV1 = 'AssetDeleteV1', AssetExifV1 = 'AssetExifV1', + AssetMetadataV1 = 'AssetMetadataV1', + AssetMetadataDeleteV1 = 'AssetMetadataDeleteV1', PartnerV1 = 'PartnerV1', PartnerDeleteV1 = 'PartnerDeleteV1', @@ -599,9 +677,11 @@ export enum SyncEntityType { AlbumUserBackfillV1 = 'AlbumUserBackfillV1', AlbumUserDeleteV1 = 'AlbumUserDeleteV1', - AlbumAssetV1 = 'AlbumAssetV1', + AlbumAssetCreateV1 = 'AlbumAssetCreateV1', + AlbumAssetUpdateV1 = 'AlbumAssetUpdateV1', AlbumAssetBackfillV1 = 'AlbumAssetBackfillV1', - AlbumAssetExifV1 = 'AlbumAssetExifV1', + AlbumAssetExifCreateV1 = 'AlbumAssetExifCreateV1', + AlbumAssetExifUpdateV1 = 'AlbumAssetExifUpdateV1', AlbumAssetExifBackfillV1 = 'AlbumAssetExifBackfillV1', AlbumToAssetV1 = 'AlbumToAssetV1', @@ -628,6 +708,7 @@ export enum SyncEntityType { SyncAckV1 = 'SyncAckV1', SyncResetV1 = 'SyncResetV1', + SyncCompleteV1 = 'SyncCompleteV1', } export enum NotificationLevel { diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 238f99257a..8af7bf7fb3 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -7,28 +7,39 @@ import { createParamDecorator, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; +import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UAParser } from 'ua-parser-js'; type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; -type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute); +type AuthenticatedOptions = { permission?: Permission | false } & (AdminRoute | SharedLinkRoute); -export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => { +export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => { const decorators: MethodDecorator[] = [ ApiBearerAuth(), ApiCookieAuth(), ApiSecurity(MetadataKey.ApiKeySecurity), - SetMetadata(MetadataKey.AuthRoute, options || {}), + SetMetadata(MetadataKey.AuthRoute, options), ]; + if ((options as AdminRoute).admin) { + decorators.push(ApiExtension(ApiCustomExtension.AdminOnly, true)); + } + + if (options?.permission) { + decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission)); + } + if ((options as SharedLinkRoute)?.sharedLink) { - decorators.push(ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false })); + decorators.push( + ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), + ApiQuery({ name: ImmichQuery.SharedLinkSlug, type: String, required: false }), + ); } return applyDecorators(...decorators); diff --git a/server/src/middleware/file-upload.interceptor.ts b/server/src/middleware/file-upload.interceptor.ts index 59c28849e1..6dfd11ee4b 100644 --- a/server/src/middleware/file-upload.interceptor.ts +++ b/server/src/middleware/file-upload.interceptor.ts @@ -12,7 +12,7 @@ import { AuthRequest } from 'src/middleware/auth.guard'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AssetMediaService } from 'src/services/asset-media.service'; import { ImmichFile, UploadFile, UploadFiles } from 'src/types'; -import { asRequest, mapToUploadFile } from 'src/utils/asset.util'; +import { asUploadRequest, mapToUploadFile } from 'src/utils/asset.util'; export function getFile(files: UploadFiles, property: 'assetData' | 'sidecarData') { const file = files[property]?.[0]; @@ -99,18 +99,21 @@ export class FileUploadInterceptor implements NestInterceptor { } private fileFilter(request: AuthRequest, file: Express.Multer.File, callback: multer.FileFilterCallback) { - return callbackify(() => this.assetService.canUploadFile(asRequest(request, file)), callback); + return callbackify(() => this.assetService.canUploadFile(asUploadRequest(request, file)), callback); } private filename(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) { return callbackify( - () => this.assetService.getUploadFilename(asRequest(request, file)), + () => this.assetService.getUploadFilename(asUploadRequest(request, file)), callback as Callback, ); } private destination(request: AuthRequest, file: Express.Multer.File, callback: DiskStorageCallback) { - return callbackify(() => this.assetService.getUploadFolder(asRequest(request, file)), callback as Callback); + return callbackify( + () => this.assetService.getUploadFolder(asUploadRequest(request, file)), + callback as Callback, + ); } private handleFile(request: AuthRequest, file: Express.Multer.File, callback: Callback>) { diff --git a/server/src/migrations/1645130759468-CreateUserTable.ts b/server/src/migrations/1645130759468-CreateUserTable.ts deleted file mode 100644 index 1aedfb67d4..0000000000 --- a/server/src/migrations/1645130759468-CreateUserTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateUserTable1645130759468 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); - await queryRunner.query(` - create table if not exists users - ( - id uuid default uuid_generate_v4() not null - constraint "PK_a3ffb1c0c8416b9fc6f907b7433" - primary key, - email varchar not null, - password varchar not null, - salt varchar not null, - "createdAt" timestamp default now() not null - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table users`); - } -} diff --git a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts b/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts deleted file mode 100644 index bf53d7910b..0000000000 --- a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateDeviceInfoTable1645130777674 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } -} diff --git a/server/src/migrations/1645130805273-CreateAssetsTable.ts b/server/src/migrations/1645130805273-CreateAssetsTable.ts deleted file mode 100644 index 82727e18a5..0000000000 --- a/server/src/migrations/1645130805273-CreateAssetsTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetsTable1645130805273 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists assets - ( - id uuid default uuid_generate_v4() not null - constraint "PK_da96729a8b113377cfb6a62439c" - primary key, - "deviceAssetId" varchar not null, - "userId" varchar not null, - "deviceId" varchar not null, - type varchar not null, - "originalPath" varchar not null, - "resizePath" varchar, - "createdAt" varchar not null, - "modifiedAt" varchar not null, - "isFavorite" boolean default false not null, - "mimeType" varchar, - duration varchar, - constraint "UQ_b599ab0bd9574958acb0b30a90e" - unique ("deviceAssetId", "userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table assets`); - } -} diff --git a/server/src/migrations/1645130817965-CreateExifTable.ts b/server/src/migrations/1645130817965-CreateExifTable.ts deleted file mode 100644 index af46b86507..0000000000 --- a/server/src/migrations/1645130817965-CreateExifTable.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTable1645130817965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists exif - ( - id serial - constraint "PK_28663352d85078ad0046dafafaa" - primary key, - "assetId" uuid not null - constraint "REL_c0117fdbc50b917ef9067740c4" - unique - constraint "FK_c0117fdbc50b917ef9067740c44" - references assets - on delete cascade, - make varchar, - model varchar, - "imageName" varchar, - "exifImageWidth" integer, - "exifImageHeight" integer, - "fileSizeInByte" integer, - orientation varchar, - "dateTimeOriginal" timestamp with time zone, - "modifyDate" timestamp with time zone, - "lensModel" varchar, - "fNumber" double precision, - "focalLength" double precision, - iso integer, - "exposureTime" double precision, - latitude double precision, - longitude double precision - ); - - create unique index if not exists "IDX_c0117fdbc50b917ef9067740c4" on exif ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table exif`); - } -} diff --git a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts b/server/src/migrations/1645130870184-CreateSmartInfoTable.ts deleted file mode 100644 index 9c81f6099a..0000000000 --- a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTable1645130870184 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists smart_info - ( - id serial - constraint "PK_0beace66440e9713f5c40470e46" - primary key, - "assetId" uuid not null - constraint "UQ_5e3753aadd956110bf3ec0244ac" - unique - constraint "FK_5e3753aadd956110bf3ec0244ac" - references assets - on delete cascade, - tags text[] - ); - - create unique index if not exists "IDX_5e3753aadd956110bf3ec0244a" - on smart_info ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table smart_info; - `); - } -} diff --git a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts b/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts deleted file mode 100644 index 071d4bd40d..0000000000 --- a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifTextSearchColumn1646249209023 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') - ) - ) STORED; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - `); - } -} diff --git a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts b/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts deleted file mode 100644 index 664d06c4bc..0000000000 --- a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTextSearchIndex1646249734844 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts b/server/src/migrations/1646709533213-AddRegionCityToExIf.ts deleted file mode 100644 index e2d226cfa4..0000000000 --- a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRegionCityToExIf1646709533213 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN if not exists city varchar; - - ALTER TABLE exif - ADD COLUMN if not exists state varchar; - - ALTER TABLE exif - ADD COLUMN if not exists country varchar; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN city; - - ALTER TABLE exif - DROP COLUMN state; - - ALTER TABLE exif - DROP COLUMN country; - `); - } -} diff --git a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts b/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts deleted file mode 100644 index 9116bf2866..0000000000 --- a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocationToExifTextSearch1646710459852 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts b/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts deleted file mode 100644 index bdf3dff5df..0000000000 --- a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddObjectColumnToSmartInfo1648317474768 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - ADD COLUMN if not exists objects text[]; - - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - DROP COLUMN objects; - `); - } -} diff --git a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts b/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts deleted file mode 100644 index ef633d6f12..0000000000 --- a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSharedAlbumAndRelatedTables1649643216111 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Create shared_albums - await queryRunner.query(` - create table if not exists shared_albums - ( - id uuid default uuid_generate_v4() not null - constraint "PK_7f71c7b5bc7c87b8f94c9a93a00" - primary key, - "ownerId" varchar not null, - "albumName" varchar default 'Untitled Album'::character varying not null, - "createdAt" timestamp with time zone default now() not null, - "albumThumbnailAssetId" varchar - ); - - comment on column shared_albums."albumThumbnailAssetId" is 'Asset ID to be used as thumbnail'; - `); - - // Create user_shared_album - await queryRunner.query(` - create table if not exists user_shared_album - ( - id serial - constraint "PK_b6562316a98845a7b3e9a25cdd0" - primary key, - "albumId" uuid not null - constraint "FK_7b3bf0f5f8da59af30519c25f18" - references shared_albums - on delete cascade, - "sharedUserId" uuid not null - constraint "FK_543c31211653e63e080ba882eb5" - references users, - constraint "PK_unique_user_in_album" - unique ("albumId", "sharedUserId") - ); - `); - - // Create asset_shared_album - await queryRunner.query( - ` - create table if not exists asset_shared_album - ( - id serial - constraint "PK_a34e076afbc601d81938e2c2277" - primary key, - "albumId" uuid not null - constraint "FK_a8b79a84996cef6ba6a3662825d" - references shared_albums - on delete cascade, - "assetId" uuid not null - constraint "FK_64f2e7d68d1d1d8417acc844a4a" - references assets - on delete cascade, - constraint "UQ_a1e2734a1ce361e7a26f6b28288" - unique ("albumId", "assetId") - ); - `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table asset_shared_album; - drop table user_shared_album; - drop table shared_albums; - `); - } -} diff --git a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts b/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts deleted file mode 100644 index af5082ebb2..0000000000 --- a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateUserTableWithAdminAndName1652633525943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - add column if not exists "firstName" varchar default ''; - - alter table users - add column if not exists "lastName" varchar default ''; - - alter table users - add column if not exists "profileImagePath" varchar default ''; - - alter table users - add column if not exists "isAdmin" bool default false; - - alter table users - add column if not exists "isFirstLoggedIn" bool default true; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - drop column "firstName"; - - alter table users - drop column "lastName"; - - alter table users - drop column "isAdmin"; - - `); - } -} diff --git a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts b/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts deleted file mode 100644 index 4de9684f18..0000000000 --- a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithWebpPath1653214255670 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "webpPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "webpPath"; - `); - } -} diff --git a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts b/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts deleted file mode 100644 index 169f7db171..0000000000 --- a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithEncodeVideoPath1654299904583 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "encodedVideoPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "encodedVideoPath"; - `); - } -} diff --git a/server/src/migrations/1655401127251-RenameSharedAlbums.ts b/server/src/migrations/1655401127251-RenameSharedAlbums.ts deleted file mode 100644 index 9bb71fb08c..0000000000 --- a/server/src/migrations/1655401127251-RenameSharedAlbums.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSharedAlbums1655401127251 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE shared_albums RENAME TO albums; - - ALTER TABLE asset_shared_album RENAME TO asset_album; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_album RENAME TO asset_shared_album; - - ALTER TABLE albums RENAME TO shared_albums; - `); - } -} diff --git a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts b/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts deleted file mode 100644 index c4e4d7cd63..0000000000 --- a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameIsFirstLoggedInColumn1656338626260 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "isFirstLoggedIn" to "shouldChangePassword"; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "shouldChangePassword" to "isFirstLoggedIn"; - `); - } -} diff --git a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts b/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts deleted file mode 100644 index 07d5592f53..0000000000 --- a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameAssetAlbumIdSequence1656888591977 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_shared_album_id_seq rename to asset_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_album_id_seq'::regclass);`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_album_id_seq rename to asset_shared_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_shared_album_id_seq'::regclass);`, - ); - } -} diff --git a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts b/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts deleted file mode 100644 index 305b67ee84..0000000000 --- a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropExifTextSearchableColumns1656888918620 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exif_text_searchable_column"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } -} diff --git a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts b/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts deleted file mode 100644 index 00a66d78e9..0000000000 --- a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MatchMigrationsWithTypeORMEntities1656889061566 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts b/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts deleted file mode 100644 index 3b175be3e5..0000000000 --- a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifImageNameAsSearchableText1658860470248 implements MigrationInterface { - name = 'AddExifImageNameAsSearchableText1658860470248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector NOT NULL`); - } -} diff --git a/server/src/migrations/1661011331242-AddCaption.ts b/server/src/migrations/1661011331242-AddCaption.ts deleted file mode 100644 index f6370a7b66..0000000000 --- a/server/src/migrations/1661011331242-AddCaption.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCaption1661011331242 implements MigrationInterface { - name = 'AddCaption1661011331242'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "description" text DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "exif" ADD "fps" double precision`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "fps"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts b/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts deleted file mode 100644 index da614e7f9c..0000000000 --- a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ChangeExifFileSizeInByteToBigInt1661528919411 implements MigrationInterface { - name = 'ChangeExifFileSizeInByteToBigInt1661528919411'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type bigint using "fileSizeInByte"::bigint; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type integer using "fileSizeInByte"::integer; - `); - } -} diff --git a/server/src/migrations/1661881837496-AddAssetChecksum.ts b/server/src/migrations/1661881837496-AddAssetChecksum.ts deleted file mode 100644 index 2901b4f554..0000000000 --- a/server/src/migrations/1661881837496-AddAssetChecksum.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetChecksum1661881837496 implements MigrationInterface { - name = 'AddAssetChecksum1661881837496'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "checksum" bytea`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE 'checksum' IS NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`); - } -} diff --git a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts b/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts deleted file mode 100644 index 15fa467878..0000000000 --- a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithNewUniqueConstraint1661971370662 implements MigrationInterface { - name = 'UpdateAssetTableWithNewUniqueConstraint1661971370662'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("userId", "checksum")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e" UNIQUE ("deviceAssetId", "userId", "deviceId")`, - ); - } -} diff --git a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts b/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts deleted file mode 100644 index a0ce4dc8c6..0000000000 --- a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixTimestampDataTypeInAssetTable1662427365521 implements MigrationInterface { - name = 'FixTimestampDataTypeInAssetTable1662427365521'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts b/server/src/migrations/1665540663419-CreateSystemConfigTable.ts deleted file mode 100644 index 40dd87c644..0000000000 --- a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSystemConfigTable1665540663419 implements MigrationInterface { - name = 'CreateSystemConfigTable1665540663419'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_config"`); - } -} diff --git a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts b/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts deleted file mode 100644 index 1e80fc089a..0000000000 --- a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddingDeletedAtColumnInUserEntity1667762360744 implements MigrationInterface { - name = 'AddingDeletedAtColumnInUserEntity1667762360744'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "deletedAt" TIMESTAMP`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts b/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts deleted file mode 100644 index 62ce314f30..0000000000 --- a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddLivePhotosRelatedColumnToAssetTable1668383120461 implements MigrationInterface { - name = 'AddLivePhotosRelatedColumnToAssetTable1668383120461' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isVisible" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "assets" ADD "livePhotoVideoId" uuid`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "livePhotoVideoId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isVisible"`); - } - -} diff --git a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts b/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts deleted file mode 100644 index 044b79c808..0000000000 --- a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateUserTableForOIDC1668835311083 implements MigrationInterface { - name = 'UpdateUserTableForOIDC1668835311083' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" SET DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" SET DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1670104716264-OAuthId.ts b/server/src/migrations/1670104716264-OAuthId.ts deleted file mode 100644 index 46b99a79d5..0000000000 --- a/server/src/migrations/1670104716264-OAuthId.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class OAuthId1670104716264 implements MigrationInterface { - name = 'OAuthId1670104716264' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "oauthId" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "oauthId"`); - } - -} diff --git a/server/src/migrations/1670257571385-CreateTagsTable.ts b/server/src/migrations/1670257571385-CreateTagsTable.ts deleted file mode 100644 index 75fba9249c..0000000000 --- a/server/src/migrations/1670257571385-CreateTagsTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateTagsTable1670257571385 implements MigrationInterface { - name = 'CreateTagsTable1670257571385' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "tags" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" character varying NOT NULL, "name" character varying NOT NULL, "userId" uuid NOT NULL, "renameTagId" uuid, CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId"), CONSTRAINT "PK_e7dc17249a1148a1970748eda99" PRIMARY KEY ("id")); COMMENT ON COLUMN "tags"."renameTagId" IS 'The new renamed tagId'`); - await queryRunner.query(`CREATE TABLE "tag_asset" ("assetsId" uuid NOT NULL, "tagsId" uuid NOT NULL, CONSTRAINT "PK_ef5346fe522b5fb3bc96454747e" PRIMARY KEY ("assetsId", "tagsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_f8e8a9e893cb5c54907f1b798e" ON "tag_asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4" ON "tag_asset" ("tagsId") `); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`DROP INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4"`); - await queryRunner.query(`DROP INDEX "IDX_f8e8a9e893cb5c54907f1b798e"`); - await queryRunner.query(`DROP TABLE "tag_asset"`); - await queryRunner.query(`DROP TABLE "tags"`); - } - -} diff --git a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts b/server/src/migrations/1670607437008-TruncateOldConfigItems.ts deleted file mode 100644 index 0a82783f89..0000000000 --- a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TruncateOldConfigItems1670607437008 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "system_config"`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts b/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts deleted file mode 100644 index 50a67ae94f..0000000000 --- a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserEmailUniqueConstraint1670633210032 implements MigrationInterface { - name = 'AddUserEmailUniqueConstraint1670633210032' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3"`); - } - -} diff --git a/server/src/migrations/1672109862870-DropSaltColumn.ts b/server/src/migrations/1672109862870-DropSaltColumn.ts deleted file mode 100644 index 91ca5ade11..0000000000 --- a/server/src/migrations/1672109862870-DropSaltColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropSaltColumn1672109862870 implements MigrationInterface { - name = 'DropSaltColumn1672109862870' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "salt"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "salt" character varying NOT NULL DEFAULT ''`); - } - -} diff --git a/server/src/migrations/1672502270115-AddAPIKeys.ts b/server/src/migrations/1672502270115-AddAPIKeys.ts deleted file mode 100644 index e72b3dc2fe..0000000000 --- a/server/src/migrations/1672502270115-AddAPIKeys.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAPIKeys1672502270115 implements MigrationInterface { - name = 'AddAPIKeys1672502270115' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "api_keys" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "key" character varying NOT NULL, "userId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`DROP TABLE "api_keys"`); - } - -} diff --git a/server/src/migrations/1673150490490-AddSharedLinkTable.ts b/server/src/migrations/1673150490490-AddSharedLinkTable.ts deleted file mode 100644 index 8d5bd2f5a5..0000000000 --- a/server/src/migrations/1673150490490-AddSharedLinkTable.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkTable1673150490490 implements MigrationInterface { - name = 'AddSharedLinkTable1673150490490' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "shared_links" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "description" character varying, "userId" character varying NOT NULL, "key" bytea NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "allowUpload" boolean NOT NULL DEFAULT false, "albumId" uuid, CONSTRAINT "UQ_sharedlink_key" UNIQUE ("key"), CONSTRAINT "PK_642e2b0f619e4876e5f90a43465" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_key" ON "shared_links" ("key") `); - await queryRunner.query(`CREATE TABLE "shared_link__asset" ("assetsId" uuid NOT NULL, "sharedLinksId" uuid NOT NULL, CONSTRAINT "PK_9b4f3687f9b31d1e311336b05e3" PRIMARY KEY ("assetsId", "sharedLinksId"))`); - await queryRunner.query(`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId") `); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`DROP INDEX "IDX_c9fab4aa97ffd1b034f3d6581a"`); - await queryRunner.query(`DROP INDEX "IDX_5b7decce6c8d3db9593d6111a6"`); - await queryRunner.query(`DROP TABLE "shared_link__asset"`); - await queryRunner.query(`DROP INDEX "IDX_sharedlink_key"`); - await queryRunner.query(`DROP TABLE "shared_links"`); - } - -} diff --git a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts b/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts deleted file mode 100644 index af0a0280a8..0000000000 --- a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMorePermissionToSharedLink1673907194740 implements MigrationInterface { - name = 'AddMorePermissionToSharedLink1673907194740'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "allowDownload" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD "showExif" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "showExif"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "allowDownload"`); - } -} diff --git a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts b/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts deleted file mode 100644 index 5f64b11559..0000000000 --- a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {MigrationInterface, QueryRunner} from 'typeorm'; - -export class RemoveVideoCodecConfigOption1674263302006 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetVideoCodec'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetAudioCodec'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts b/server/src/migrations/1674342044239-CreateUserTokenEntity.ts deleted file mode 100644 index e289787f91..0000000000 --- a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateUserTokenEntity1674342044239 implements MigrationInterface { - name = 'CreateUserTokenEntity1674342044239' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "user_token" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid, CONSTRAINT "PK_48cb6b5c20faa63157b3c1baf7f" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`DROP TABLE "user_token"`); - } - -} diff --git a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts b/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts deleted file mode 100644 index de21e180b7..0000000000 --- a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AlterExifExposureTimeToString1674757936889 implements MigrationInterface { - name = 'AlterExifExposureTimeToString1674757936889' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" double precision`); - } - -} diff --git a/server/src/migrations/1674774248319-TruncateAPIKeys.ts b/server/src/migrations/1674774248319-TruncateAPIKeys.ts deleted file mode 100644 index efbb5c41af..0000000000 --- a/server/src/migrations/1674774248319-TruncateAPIKeys.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class TruncateAPIKeys1674774248319 implements MigrationInterface { - name = 'TruncateAPIKeys1674774248319' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "api_keys"`); - } - - public async down(): Promise { - //noop - } - -} diff --git a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts b/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts deleted file mode 100644 index 9119c57065..0000000000 --- a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSharedLinkUserForeignKeyConstraint1674939383309 implements MigrationInterface { - name = 'AddSharedLinkUserForeignKeyConstraint1674939383309'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE uuid using "userId"::uuid`); - await queryRunner.query( - `ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts b/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts deleted file mode 100644 index 90c00b38e9..0000000000 --- a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtColumnToAlbumsUsersAssets1675667878312 implements MigrationInterface { - name = 'AddUpdatedAtColumnToAlbumsUsersAssets1675667878312'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "users" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updatedAt"`); - } -} diff --git a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts b/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts deleted file mode 100644 index 0898accfb7..0000000000 --- a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumUserForeignKeyConstraint1675701909594 implements MigrationInterface { - name = 'AddAlbumUserForeignKeyConstraint1675701909594'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "albums" WHERE "ownerId"::uuid NOT IN (SELECT id FROM "users") `) - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE uuid using "ownerId"::uuid`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts b/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts deleted file mode 100644 index 368a9ca9c5..0000000000 --- a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class APIKeyUUIDPrimaryKey1675808874445 implements MigrationInterface { - name = 'APIKeyUUIDPrimaryKey1675808874445' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - -} diff --git a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts b/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts deleted file mode 100644 index 6f48ac736d..0000000000 --- a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface { - name = 'FixAlbumEntityTypeORM1675812532822' - - public async up(queryRunner: QueryRunner): Promise { - - await queryRunner.query(`ALTER TABLE "asset_album" RENAME TO "albums_assets_assets"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "PK_a34e076afbc601d81938e2c2277"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "assetId" TO "assetsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId")`); - await queryRunner.query(`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "user_shared_album" RENAME TO "albums_shared_users_users"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_543c31211653e63e080ba882eb5"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_unique_user_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "sharedUserId" TO "usersId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId")`); - await queryRunner.query(`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId") `); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`) - await queryRunner.query(`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME TO "asset_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME TO "user_shared_album"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_e590fa396c6898fcd4a50e40927"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`); - await queryRunner.query(`DROP INDEX "IDX_427c350ad49bd3935a50baab73"`); - await queryRunner.query(`DROP INDEX "IDX_f48513bf9bccefd6ff3ad30bd0"`); - await queryRunner.query(`DROP INDEX "IDX_e590fa396c6898fcd4a50e4092"`); - await queryRunner.query(`DROP INDEX "IDX_4bd1303d199f4e72ccdf998c62"`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_323f8dcbe85373722886940f143" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "usersId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_323f8dcbe85373722886940f143"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "albumsId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "sharedUserId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "albumId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_unique_user_in_album" UNIQUE ("albumId", "sharedUserId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_543c31211653e63e080ba882eb5" FOREIGN KEY ("sharedUserId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "albumsId" TO "albumId"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "assetsId" TO "assetId"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_a34e076afbc601d81938e2c2277" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1676437878377-AppleContentIdentifier.ts b/server/src/migrations/1676437878377-AppleContentIdentifier.ts deleted file mode 100644 index 8d11139878..0000000000 --- a/server/src/migrations/1676437878377-AppleContentIdentifier.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AppleContentIdentifier1676437878377 implements MigrationInterface { - name = 'AppleContentIdentifier1676437878377'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "livePhotoCID" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_live_photo_cid"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`); - } -} diff --git a/server/src/migrations/1676680127415-FixAssetRelations.ts b/server/src/migrations/1676680127415-FixAssetRelations.ts deleted file mode 100644 index 439e86a78a..0000000000 --- a/server/src/migrations/1676680127415-FixAssetRelations.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAssetRelations1676680127415 implements MigrationInterface { - name = 'FixAssetRelations1676680127415' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "modifiedAt" TO "fileModifiedAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "createdAt" TO "fileCreatedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "userId" TO "ownerId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "ownerId" TYPE uuid USING "ownerId"::uuid;`); - - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileCreatedAt" TO "createdAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileModifiedAt" TO "modifiedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "ownerId" TO "userId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "userId" TYPE varchar`); - } - -} diff --git a/server/src/migrations/1676721296440-AssetCreatedAtField.ts b/server/src/migrations/1676721296440-AssetCreatedAtField.ts deleted file mode 100644 index 304c7b2190..0000000000 --- a/server/src/migrations/1676721296440-AssetCreatedAtField.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetCreatedAtField1676721296440 implements MigrationInterface { - name = 'AssetCreatedAtField1676721296440' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "createdAt"`); - } - -} diff --git a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts b/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts deleted file mode 100644 index 947559ed2d..0000000000 --- a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ExifEntityDefinitionFixes1676848629119 implements MigrationInterface { - name = 'ExifEntityDefinitionFixes1676848629119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`); - - await queryRunner.query(`DROP INDEX "IDX_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_c0117fdbc50b917ef9067740c44" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" DROP NOT NULL`); - - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "exif" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_28663352d85078ad0046dafafaa" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c0117fdbc50b917ef9067740c4" ON "exif" ("assetId") `); - } - -} diff --git a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts b/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts deleted file mode 100644 index d48f543fef..0000000000 --- a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SharedLinkEntityDefinitionFixes1676848694786 implements MigrationInterface { - name = 'SharedLinkEntityDefinitionFixes1676848694786' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" SET DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts b/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts deleted file mode 100644 index e089619c6d..0000000000 --- a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SmartInfoEntityDefinitionFixes1676852143506 implements MigrationInterface { - name = 'SmartInfoEntityDefinitionFixes1676852143506' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_5e3753aadd956110bf3ec0244a"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_0beace66440e9713f5c40470e46" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5e3753aadd956110bf3ec0244a" ON "smart_info" ("assetId") `); - } - -} diff --git a/server/src/migrations/1677497925328-AddExifTimeZone.ts b/server/src/migrations/1677497925328-AddExifTimeZone.ts deleted file mode 100644 index 33f958336e..0000000000 --- a/server/src/migrations/1677497925328-AddExifTimeZone.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifTimeZone1677497925328 implements MigrationInterface { - name = 'AddExifTimeZone1677497925328' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "timeZone" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "timeZone"`); - } - -} diff --git a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts b/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts deleted file mode 100644 index 986b5ebd20..0000000000 --- a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIndexForAlbumInSharedLinkTable1677535643119 implements MigrationInterface { - name = 'AddIndexForAlbumInSharedLinkTable1677535643119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_sharedlink_albumId"`); - } - -} diff --git a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts b/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts deleted file mode 100644 index 71f022dcf6..0000000000 --- a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AlbumThumbnailRelation1677613712565 implements MigrationInterface { - name = 'AlbumThumbnailRelation1677613712565'; - - public async up(queryRunner: QueryRunner): Promise { - // Make sure all albums have a valid albumThumbnailAssetId UUID or NULL. - await queryRunner.query(` - UPDATE "albums" - SET - "albumThumbnailAssetId" = ( - SELECT - "albums_assets2"."assetsId" - FROM - "assets" "assets", - "albums_assets_assets" "albums_assets2" - WHERE - "albums_assets2"."assetsId" = "assets"."id" - AND "albums_assets2"."albumsId" = "albums"."id" - ORDER BY - "assets"."fileCreatedAt" DESC - LIMIT 1 - ), - "updatedAt" = CURRENT_TIMESTAMP - WHERE - "albums"."albumThumbnailAssetId" IS NULL - AND EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE "albums"."id" = "albums_assets"."albumsId" - ) - OR "albums"."albumThumbnailAssetId" IS NOT NULL - AND NOT EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE - "albums"."id" = "albums_assets"."albumsId" - AND "albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"::varchar - ) - `); - - await queryRunner.query(` - ALTER TABLE "albums" - ALTER COLUMN "albumThumbnailAssetId" - TYPE uuid USING "albumThumbnailAssetId"::uuid - `); - - await queryRunner.query(` - ALTER TABLE "albums" ADD CONSTRAINT "FK_05895aa505a670300d4816debce" FOREIGN KEY ("albumThumbnailAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_05895aa505a670300d4816debce"`); - - await queryRunner.query(` - ALTER TABLE "albums" ALTER COLUMN "albumThumbnailAssetId" TYPE varchar USING "albumThumbnailAssetId"::varchar - `); - } -} diff --git a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts b/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts deleted file mode 100644 index 82f8176b0d..0000000000 --- a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEncodeDataColumn1677971458822 implements MigrationInterface { - name = 'AddCLIPEncodeDataColumn1677971458822'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD "clipEmbedding" numeric(20,19) array`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "clipEmbedding"`); - } -} diff --git a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts b/server/src/migrations/1679751316282-UpdateTranscodeOption.ts deleted file mode 100644 index 989622e831..0000000000 --- a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateTranscodeOption1679751316282 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcode', - value = '"all"' - WHERE - key = 'ffmpeg.transcodeAll' AND value = 'true' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcodeAll', - value = 'true' - WHERE - key = 'ffmpeg.transcode' AND value = '"all"' - `); - - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.transcode'`); - } -} diff --git a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts b/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts deleted file mode 100644 index 3afa8c6d10..0000000000 --- a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ClipEmbeddingFloat41679901204458 implements MigrationInterface { - name = 'ClipEmbeddingFloat41679901204458'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE real array USING "clipEmbedding"::real array`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE numeric(20,19) array USING "clipEmbedding"::numeric(20,19) array`, - ); - } -} diff --git a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts b/server/src/migrations/1680632845740-AddIsArchivedColumn.ts deleted file mode 100644 index 325e37f489..0000000000 --- a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsArchivedColumn1680632845740 implements MigrationInterface { - name = 'AddIsArchivedColumn1680632845740' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isArchived" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isArchived"`); - } - -} diff --git a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts b/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts deleted file mode 100644 index 1c3b051b02..0000000000 --- a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveRedundantConstraints1680694465853 implements MigrationInterface { - name = 'RemoveRedundantConstraints1680694465853' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "REL_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac" UNIQUE ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "REL_c0117fdbc50b917ef9067740c4" UNIQUE ("assetId")`); - } - -} diff --git a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts b/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts deleted file mode 100644 index 7c547108b5..0000000000 --- a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameToAssetTable1681144628393 implements MigrationInterface { - name = 'AddOriginalFileNameToAssetTable1681144628393'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "originalFileName" character varying`); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = ( - select e."imageName" - from exif e - where e."assetId" = a.id - ) - `); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = a.id - where a."originalFileName" IS NULL or a."originalFileName" = '' - `); - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "originalFileName" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "originalFileName"`); - } -} diff --git a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts b/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts deleted file mode 100644 index e188ea3506..0000000000 --- a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveImageNameFromEXIFTable1681159594469 implements MigrationInterface { - name = 'RemoveImageNameFromEXIFTable1681159594469'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN IF EXISTS "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "imageName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD "imageName" character varying`); - } -} diff --git a/server/src/migrations/1682371561743-FixNullableRelations.ts b/server/src/migrations/1682371561743-FixNullableRelations.ts deleted file mode 100644 index 42c34f9399..0000000000 --- a/server/src/migrations/1682371561743-FixNullableRelations.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixNullableRelations1682371561743 implements MigrationInterface { - name = 'FixNullableRelations1682371561743'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" DROP NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts b/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts deleted file mode 100644 index bb60e452ef..0000000000 --- a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddDeviceInfoToUserToken1682371791038 implements MigrationInterface { - name = 'AddDeviceInfoToUserToken1682371791038' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceType" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceOS" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceOS"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceType"`); - } - -} diff --git a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts b/server/src/migrations/1682710252424-DropDeviceInfoTable.ts deleted file mode 100644 index 9a07676351..0000000000 --- a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropDeviceInfoTable1682710252424 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } -} diff --git a/server/src/migrations/1683808254676-AddPartnersTable.ts b/server/src/migrations/1683808254676-AddPartnersTable.ts deleted file mode 100644 index 64afb0b76c..0000000000 --- a/server/src/migrations/1683808254676-AddPartnersTable.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPartnersTable1683808254676 implements MigrationInterface { - name = 'AddPartnersTable1683808254676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners" ("sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_f1cc8f73d16b367f426261a8736" PRIMARY KEY ("sharedById", "sharedWithId"))`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_7e077a8b70b3530138610ff5e04" FOREIGN KEY ("sharedById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3" FOREIGN KEY ("sharedWithId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3"`); - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_7e077a8b70b3530138610ff5e04"`); - await queryRunner.query(`DROP TABLE "partners"`); - } - -} diff --git a/server/src/migrations/1684255168091-AddFacialTables.ts b/server/src/migrations/1684255168091-AddFacialTables.ts deleted file mode 100644 index 1f2426bb0c..0000000000 --- a/server/src/migrations/1684255168091-AddFacialTables.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFacialTables1684255168091 implements MigrationInterface { - name = 'AddFacialTables1684255168091' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "person" ("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(), "ownerId" uuid NOT NULL, "name" character varying NOT NULL DEFAULT '', "thumbnailPath" character varying NOT NULL DEFAULT '', CONSTRAINT "PK_5fdaf670315c4b7e70cce85daa3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "asset_faces" ("assetId" uuid NOT NULL, "personId" uuid NOT NULL, "embedding" real array, CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId"))`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_5527cc99f530a547093f9e577b6" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c"`); - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_5527cc99f530a547093f9e577b6"`); - await queryRunner.query(`DROP TABLE "asset_faces"`); - await queryRunner.query(`DROP TABLE "person"`); - } - -} diff --git a/server/src/migrations/1684273840676-AddSidecarFile.ts b/server/src/migrations/1684273840676-AddSidecarFile.ts deleted file mode 100644 index 46c4b5d375..0000000000 --- a/server/src/migrations/1684273840676-AddSidecarFile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSidecarFile1684273840676 implements MigrationInterface { - name = 'AddSidecarFile1684273840676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "sidecarPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "sidecarPath"`); - } - -} diff --git a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts b/server/src/migrations/1684328185099-RequireChecksumNotNull.ts deleted file mode 100644 index e691fff2b1..0000000000 --- a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RequireChecksumNotNull1684328185099 implements MigrationInterface { - name = 'removeNotNullFromChecksumIndex1684328185099'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_8d3efe36c0755849395e6ea866"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`, - ); - } -} diff --git a/server/src/migrations/1684410565398-AddStorageLabel.ts b/server/src/migrations/1684410565398-AddStorageLabel.ts deleted file mode 100644 index 6c6ea9702f..0000000000 --- a/server/src/migrations/1684410565398-AddStorageLabel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStorageLabel1684410565398 implements MigrationInterface { - name = 'AddStorageLabel1684410565398' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "storageLabel" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a" UNIQUE ("storageLabel")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "storageLabel"`); - } - -} diff --git a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts b/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts deleted file mode 100644 index 273c6b830f..0000000000 --- a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserTokenAndAPIKeyCascades1684867360825 implements MigrationInterface { - name = 'AddUserTokenAndAPIKeyCascades1684867360825' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts b/server/src/migrations/1685044328272-AddSharedLinkCascade.ts deleted file mode 100644 index 3aefd3989e..0000000000 --- a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkCascade1685044328272 implements MigrationInterface { - name = 'AddSharedLinkCascade1685044328272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts b/server/src/migrations/1685370430343-UserDatesTimestamptz.ts deleted file mode 100644 index 0a70e012d8..0000000000 --- a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UserDatesTimestamptz1685370430343 implements MigrationInterface { - name = 'UserDatesTimestamptz1685370430343' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP`); - } - -} diff --git a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts b/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts deleted file mode 100644 index 9a9b00a366..0000000000 --- a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1685731372040 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1685731372040'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1686584273471-ImportAsset.ts b/server/src/migrations/1686584273471-ImportAsset.ts deleted file mode 100644 index d9f5819a8d..0000000000 --- a/server/src/migrations/1686584273471-ImportAsset.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ImportAsset1686584273471 implements MigrationInterface { - name = 'ImportAsset1686584273471' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`); - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - -} diff --git a/server/src/migrations/1686762895180-AddThumbhashColumn.ts b/server/src/migrations/1686762895180-AddThumbhashColumn.ts deleted file mode 100644 index 4ad73163db..0000000000 --- a/server/src/migrations/1686762895180-AddThumbhashColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbhashColumn1685546571785 implements MigrationInterface { - name = 'AddThumbhashColumn1686762895180'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbhash" bytea NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbhash"`); - } -} diff --git a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts b/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts deleted file mode 100644 index b026685bfb..0000000000 --- a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDetectFaceResultInfo1688241394489 implements MigrationInterface { - name = 'AddDetectFaceResultInfo1688241394489'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageWidth" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageHeight" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX2" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY2" integer NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageHeight"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageWidth"`); - } -} diff --git a/server/src/migrations/1688392120838-AddLibraryTable.ts b/server/src/migrations/1688392120838-AddLibraryTable.ts deleted file mode 100644 index 4d394adaf1..0000000000 --- a/server/src/migrations/1688392120838-AddLibraryTable.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLibraries1688392120838 implements MigrationInterface { - name = 'AddLibraryTable1688392120838'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `CREATE TABLE "libraries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "importPaths" text array NOT NULL, "exclusionPatterns" text array NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "refreshedAt" TIMESTAMP WITH TIME ZONE, "isVisible" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_505fedfcad00a09b3734b4223de" PRIMARY KEY ("id"))`, - ); - await queryRunner.query(`ALTER TABLE "assets" ADD "isOffline" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD "libraryId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" ADD "isExternal" boolean NOT NULL DEFAULT false`); - - await queryRunner.query( - `CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" on "assets" ("ownerId", "libraryId", checksum)`, - ); - await queryRunner.query( - `ALTER TABLE "libraries" ADD CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - - // Create default library for each user and assign all assets to it - const users = await queryRunner.query(`SELECT id FROM "users"`); - const userIds: string[] = users.map((user: any) => user.id); - - for (const userId of userIds) { - await queryRunner.query( - `INSERT INTO "libraries" ("name", "ownerId", "type", "importPaths", "exclusionPatterns") VALUES ('Default Library', '${userId}', 'UPLOAD', '{}', '{}')`, - ); - - await queryRunner.query( - `UPDATE "assets" SET "libraryId" = (SELECT id FROM "libraries" WHERE "ownerId" = '${userId}' LIMIT 1) WHERE "ownerId" = '${userId}'`, - ); - } - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_owner_library_originalpath"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`, - ); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isOffline"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isExternal"`); - await queryRunner.query(`DROP TABLE "libraries"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("ownerId", "checksum")`); - } -} diff --git a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts b/server/src/migrations/1689001889950-DropMimeTypeColumn.ts deleted file mode 100644 index 45559313a1..0000000000 --- a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropMimeTypeColumn1689001889950 implements MigrationInterface { - name = 'DropMimeTypeColumn1689001889950' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "mimeType"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "mimeType" character varying`); - } - -} diff --git a/server/src/migrations/1689281196844-AddHiddenFaces.ts b/server/src/migrations/1689281196844-AddHiddenFaces.ts deleted file mode 100644 index 234b77dd34..0000000000 --- a/server/src/migrations/1689281196844-AddHiddenFaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Infra1689281196844 implements MigrationInterface { - name = 'Infra1689281196844' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isHidden" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isHidden"`); - } - -} diff --git a/server/src/migrations/1690469489288-Panoramas.ts b/server/src/migrations/1690469489288-Panoramas.ts deleted file mode 100644 index ee0934b43a..0000000000 --- a/server/src/migrations/1690469489288-Panoramas.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class Panoramas1690217088596 implements MigrationInterface { - name = 'Panoramas1690217088596'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "projectionType" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "projectionType"`); - } -} diff --git a/server/src/migrations/1691209138541-AddAlbumDescription.ts b/server/src/migrations/1691209138541-AddAlbumDescription.ts deleted file mode 100644 index f4167598af..0000000000 --- a/server/src/migrations/1691209138541-AddAlbumDescription.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumDescription1691209138541 implements MigrationInterface { - name = 'AddAlbumDescription1691209138541'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "description" text NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1691600216749-UserMemoryPreference.ts b/server/src/migrations/1691600216749-UserMemoryPreference.ts deleted file mode 100644 index 7238749080..0000000000 --- a/server/src/migrations/1691600216749-UserMemoryPreference.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMemoryPreference1691600216749 implements MigrationInterface { - name = 'UserMemoryPreference1691600216749'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - } -} diff --git a/server/src/migrations/1692057328660-fixGPSNullIsland.ts b/server/src/migrations/1692057328660-fixGPSNullIsland.ts deleted file mode 100644 index 74dc40a474..0000000000 --- a/server/src/migrations/1692057328660-fixGPSNullIsland.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class FixGPSNullIsland1692057328660 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;`); - } - - public async down(): Promise { - // Setting lat,lon to 0 not necessary - } - -} diff --git a/server/src/migrations/1692112147855-AddPersonBirthDate.ts b/server/src/migrations/1692112147855-AddPersonBirthDate.ts deleted file mode 100644 index db2ba35dad..0000000000 --- a/server/src/migrations/1692112147855-AddPersonBirthDate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonBirthDate1692112147855 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "birthDate" date`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "birthDate"`); - } - -} diff --git a/server/src/migrations/1692804658140-AddAuditTable.ts b/server/src/migrations/1692804658140-AddAuditTable.ts deleted file mode 100644 index d398051a79..0000000000 --- a/server/src/migrations/1692804658140-AddAuditTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAuditTable1692804658140 implements MigrationInterface { - name = 'AddAuditTable1692804658140' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "audit" ("id" SERIAL NOT NULL, "entityType" character varying NOT NULL, "entityId" uuid NOT NULL, "action" character varying NOT NULL, "ownerId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_1d3d120ddaf7bc9b1ed68ed463a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_ownerId_createdAt" ON "audit" ("ownerId", "createdAt") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_ownerId_createdAt"`); - await queryRunner.query(`DROP TABLE "audit"`); - } - -} diff --git a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts b/server/src/migrations/1693236627291-RenameMLEnableFlags.ts deleted file mode 100644 index 096356f6e0..0000000000 --- a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class RenameMLEnableFlags1693236627291 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classificationEnabled' THEN 'ffmpeg.classification.enabled' - WHEN key = 'ffmpeg.clipEnabled' THEN 'ffmpeg.clip.enabled' - WHEN key = 'ffmpeg.facialRecognitionEnabled' THEN 'ffmpeg.facialRecognition.enabled' - ELSE key - END - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classification.enabled' THEN 'ffmpeg.classificationEnabled' - WHEN key = 'ffmpeg.clip.enabled' THEN 'ffmpeg.clipEnabled' - WHEN key = 'ffmpeg.facialRecognition.enabled' THEN 'ffmpeg.facialRecognitionEnabled' - ELSE key - END - `); - } -} diff --git a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts b/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts deleted file mode 100644 index 2e3c914eec..0000000000 --- a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonFaceAssetId1693833336881 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "faceAssetId" uuid`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "faceAssetId"`); - } - -} diff --git a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts b/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts deleted file mode 100644 index 3b213b9f04..0000000000 --- a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetDeletedAtColumn1694204416744 implements MigrationInterface { - name = 'AddAssetDeletedAtColumn1694204416744' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "deletedAt"`); - } - -} diff --git a/server/src/migrations/1694525143117-AddLocalDateTime.ts b/server/src/migrations/1694525143117-AddLocalDateTime.ts deleted file mode 100644 index dd24c07c0b..0000000000 --- a/server/src/migrations/1694525143117-AddLocalDateTime.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocalDateTime1694525143117 implements MigrationInterface { - name = 'AddLocalDateTime1694525143117'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "localDateTime" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "assets" SET "localDateTime" = "fileCreatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "localDateTime"`); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } -} diff --git a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts b/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts deleted file mode 100644 index 64a34c3e88..0000000000 --- a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtToAlbums1694638413248 implements MigrationInterface { - name = 'AddDeletedAtToAlbums1694638413248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1694750975773-AddExifColorSpace.ts b/server/src/migrations/1694750975773-AddExifColorSpace.ts deleted file mode 100644 index d6b3a6b2b0..0000000000 --- a/server/src/migrations/1694750975773-AddExifColorSpace.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifColorSpace1694750975773 implements MigrationInterface { - name = 'AddExifColorSpace1694750975773' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "profileDescription" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "colorspace" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "bitsPerSample" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "bitsPerSample"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "colorspace"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "profileDescription"`); - } - -} diff --git a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts b/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts deleted file mode 100644 index 715b46550a..0000000000 --- a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateOpusCodecToLibopus1694758412194 implements MigrationInterface { - name = 'UpdateOpusCodecToLibopus1694758412194' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"libopus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"opus"' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"opus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"libopus"' - `); - } -} diff --git a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts b/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts deleted file mode 100644 index d5150d3a81..0000000000 --- a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackParentIdToAssets1695354433573 implements MigrationInterface { - name = 'AddStackParentIdToAssets1695354433573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "stackParentId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - -} diff --git a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts b/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts deleted file mode 100644 index 20b179ceb6..0000000000 --- a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1695660378655 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1695660378655'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts b/server/src/migrations/1696888644031-AddOriginalPathIndex.ts deleted file mode 100644 index 78e1c92ecb..0000000000 --- a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalPathIndex1696888644031 implements MigrationInterface { - name = 'AddOriginalPathIndex1696888644031'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - } -} diff --git a/server/src/migrations/1696968880063-AddMoveTable.ts b/server/src/migrations/1696968880063-AddMoveTable.ts deleted file mode 100644 index 7ba140d05b..0000000000 --- a/server/src/migrations/1696968880063-AddMoveTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMoveTable1696968880063 implements MigrationInterface { - name = 'AddMoveTable1696968880063' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" character varying NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL, CONSTRAINT "UQ_newPath" UNIQUE ("newPath"), CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType"), CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "move_history"`); - } - -} diff --git a/server/src/migrations/1697272818851-UnassignFace.ts b/server/src/migrations/1697272818851-UnassignFace.ts deleted file mode 100644 index 49eebf4cc1..0000000000 --- a/server/src/migrations/1697272818851-UnassignFace.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnassignFace1697272818851 implements MigrationInterface { - name = 'UnassignFace1697272818851'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_bf339a24070dac7e71304ec530a"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD COLUMN "id" UUID DEFAULT uuid_generate_v4() NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts b/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts deleted file mode 100644 index b6906e3d05..0000000000 --- a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPasswordToSharedLinks1698290827089 implements MigrationInterface { - name = 'AddPasswordToSharedLinks1698290827089' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "password" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "password"`); - } - -} diff --git a/server/src/migrations/1698693294632-AddActivity.ts b/server/src/migrations/1698693294632-AddActivity.ts deleted file mode 100644 index 5556ef2b20..0000000000 --- a/server/src/migrations/1698693294632-AddActivity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddActivity1698693294632 implements MigrationInterface { - name = 'AddActivity1698693294632' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "activity" ("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(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "assetId" uuid, "comment" text, "isLiked" boolean NOT NULL DEFAULT false, CONSTRAINT "CHK_2ab1e70f113f450eb40c1e3ec8" CHECK (("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)), CONSTRAINT "PK_24625a1d6b1b089c8ae206fe467" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_activity_like" ON "activity" ("assetId", "userId", "albumId") WHERE ("isLiked" = true)`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_8091ea76b12338cb4428d33d782" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_1af8519996fbfb3684b58df280b" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`); - await queryRunner.query(`DROP INDEX "IDX_activity_like"`); - await queryRunner.query(`DROP TABLE "activity"`); - } - -} diff --git a/server/src/migrations/1699268680508-DisableActivity.ts b/server/src/migrations/1699268680508-DisableActivity.ts deleted file mode 100644 index d860244f6d..0000000000 --- a/server/src/migrations/1699268680508-DisableActivity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DisableActivity1699268680508 implements MigrationInterface { - name = 'DisableActivity1699268680508' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "isActivityEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "isActivityEnabled"`); - } - -} diff --git a/server/src/migrations/1699322864544-UserNameConsolidation.ts b/server/src/migrations/1699322864544-UserNameConsolidation.ts deleted file mode 100644 index 431a2a92fc..0000000000 --- a/server/src/migrations/1699322864544-UserNameConsolidation.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsername1699322864544 implements MigrationInterface { - name = 'AddUsername1699322864544' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "name" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "name" = CONCAT(COALESCE("firstName", ''), ' ', COALESCE("lastName", ''))`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "firstName"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "users" ADD "lastName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ADD "firstName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "lastName" = COALESCE("email", '')`); - await queryRunner.query(`UPDATE "users" SET "firstName" = COALESCE("email", '')`); - } - -} diff --git a/server/src/migrations/1699345863886-AddJobStatus.ts b/server/src/migrations/1699345863886-AddJobStatus.ts deleted file mode 100644 index c7df6387c0..0000000000 --- a/server/src/migrations/1699345863886-AddJobStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddJobStatus1699345863886 implements MigrationInterface { - name = 'AddJobStatus1699345863886' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_job_status" ("assetId" uuid NOT NULL, "facesRecognizedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_420bec36fc02813bddf5c8b73d4" PRIMARY KEY ("assetId"))`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4"`); - await queryRunner.query(`DROP TABLE "asset_job_status"`); - } - -} diff --git a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts b/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts deleted file mode 100644 index 59c2f229eb..0000000000 --- a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AdddInTimelineToPartnersTable1699562570201 implements MigrationInterface { - name = 'AdddInTimelineToPartnersTable1699562570201' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" ADD "inTimeline" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "inTimeline"`); - } - -} diff --git a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts b/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts deleted file mode 100644 index fdff7ea062..0000000000 --- a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EditFaceAssetForeignKey1699727044012 implements MigrationInterface { - name = 'EditFaceAssetForeignKey1699727044012' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = asset_faces."id" FROM asset_faces WHERE person."faceAssetId" = asset_faces."assetId" AND person."id" = asset_faces."personId"`) - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "asset_faces"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = assets."id" FROM assets, asset_faces WHERE person."faceAssetId" = asset_faces."id" AND asset_faces."assetId" = assets."id"`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1699889987493-AddAvatarColor.ts b/server/src/migrations/1699889987493-AddAvatarColor.ts deleted file mode 100644 index b075a5d2af..0000000000 --- a/server/src/migrations/1699889987493-AddAvatarColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAvatarColor1699889987493 implements MigrationInterface { - name = 'AddAvatarColor1699889987493' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - } - -} diff --git a/server/src/migrations/1700345818045-SystemMetadata.ts b/server/src/migrations/1700345818045-SystemMetadata.ts deleted file mode 100644 index 0bd9162db7..0000000000 --- a/server/src/migrations/1700345818045-SystemMetadata.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SystemMetadata1700345818045 implements MigrationInterface { - name = 'SystemMetadata1700345818045' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "system_metadata" ("key" character varying NOT NULL, "value" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_fa94f6857470fb5b81ec6084465" PRIMARY KEY ("key"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_metadata"`); - } - -} diff --git a/server/src/migrations/1700362016675-Geodata.ts b/server/src/migrations/1700362016675-Geodata.ts deleted file mode 100644 index fa948e0896..0000000000 --- a/server/src/migrations/1700362016675-Geodata.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Geodata1700362016675 implements MigrationInterface { - name = 'Geodata1700362016675' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS cube`) - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS earthdistance`) - await queryRunner.query(`CREATE TABLE "geodata_admin2" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_admin1" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_places" ("id" integer NOT NULL, "name" character varying(200) NOT NULL, "longitude" double precision NOT NULL, "latitude" double precision NOT NULL, "countryCode" character(2) NOT NULL, "admin1Code" character varying(20), "admin2Code" character varying(80), "admin1Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, "admin2Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED, "modificationDate" date NOT NULL, CONSTRAINT "PK_c29918988912ef4036f3d7fbff4" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`) - await queryRunner.query(`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" USING gist ("earthCoord");`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord"`); - await queryRunner.query(`DROP TABLE "geodata_places"`); - await queryRunner.query(`DROP TABLE "geodata_admin1"`); - await queryRunner.query(`DROP TABLE "geodata_admin2"`); - await queryRunner.query(`DROP EXTENSION cube`); - await queryRunner.query(`DROP EXTENSION earthdistance`); - } - -} diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts deleted file mode 100644 index 4511e1001b..0000000000 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { getCLIPModelInfo } from 'src/utils/misc'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UsePgVectors1700713871511 implements MigrationInterface { - name = 'UsePgVectors1700713871511'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${await getVectorExtension(queryRunner)}`); - const faceDimQuery = await queryRunner.query(` - SELECT CARDINALITY(embedding::real[]) as dimsize - FROM asset_faces - LIMIT 1`); - const faceDimSize = faceDimQuery?.[0]?.['dimsize'] ?? 512; - - const clipModelNameQuery = await queryRunner.query( - `SELECT value FROM system_config WHERE key = 'machineLearning.clip.modelName'`, - ); - const clipModelName: string = clipModelNameQuery?.[0]?.['value'] ?? 'ViT-B-32__openai'; - const clipDimSize = getCLIPModelInfo(clipModelName.replaceAll('"', '')).dimSize; - - await queryRunner.query(` - ALTER TABLE asset_faces - ALTER COLUMN embedding SET NOT NULL, - ALTER COLUMN embedding TYPE vector(${faceDimSize})`); - - await queryRunner.query(` - CREATE TABLE smart_search ( - "assetId" uuid PRIMARY KEY NOT NULL REFERENCES assets(id) ON DELETE CASCADE, - embedding vector(${clipDimSize}) NOT NULL )`); - - await queryRunner.query(` - INSERT INTO smart_search("assetId", embedding) - SELECT si."assetId", si."clipEmbedding" - FROM smart_info si - WHERE "clipEmbedding" IS NOT NULL - AND CARDINALITY("clipEmbedding"::real[]) = ${clipDimSize} - AND array_position(si."clipEmbedding", NULL) IS NULL`); - - await queryRunner.query(`ALTER TABLE smart_info DROP COLUMN IF EXISTS "clipEmbedding"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE asset_faces ALTER COLUMN embedding TYPE real array`); - await queryRunner.query(`ALTER TABLE smart_info ADD COLUMN IF NOT EXISTS "clipEmbedding" TYPE real array`); - await queryRunner.query(` - INSERT INTO smart_info - ("assetId", "clipEmbedding") - SELECT s."assetId", s.embedding - FROM smart_search s - ON CONFLICT (s."assetId") DO UPDATE SET "clipEmbedding" = s.embedding`); - await queryRunner.query(`DROP TABLE IF EXISTS smart_search`); - } -} diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts deleted file mode 100644 index 43809d6364..0000000000 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { - name = 'AddCLIPEmbeddingIndex1700713994428'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS clip_index`); - } -} diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts deleted file mode 100644 index 5ee91afbcc..0000000000 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { - name = 'AddFaceEmbeddingIndex1700714033632'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS face_index`); - } -} diff --git a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts b/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts deleted file mode 100644 index b850d3da09..0000000000 --- a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSmartInfoTagsIndex1700714072055 implements MigrationInterface { - name = 'AddSmartInfoTagsIndex1700714072055'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS si_tags ON smart_info USING GIN (tags);`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS si_tags;`); - } -} diff --git a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts b/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts deleted file mode 100644 index b42291f6fd..0000000000 --- a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTextSearchIndex1700714140297 implements MigrationInterface { - name = 'CreateSmartInfoTextSearchIndex1700714140297'; - - public async up(queryRunner: QueryRunner): Promise { - // https://dba.stackexchange.com/a/164081 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_concat_ws(text, text[]) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE AS - 'SELECT array_to_string($2, $1)'`); - - await queryRunner.query(` - ALTER TABLE smart_info ADD "smartInfoTextSearchableColumn" tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR( - 'english', - f_concat_ws( - ' '::text, - COALESCE(tags, array[]::text[]) || COALESCE(objects, array[]::text[]) - ) - ) - ) - STORED NOT NULL`); - - await queryRunner.query(` - CREATE INDEX smart_info_text_searchable_idx - ON smart_info - USING GIN ("smartInfoTextSearchableColumn")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP FUNCTION IF EXISTS immutable_concat_ws`); - await queryRunner.query(`ALTER TABLE smart_info DROP IF EXISTS "smartInfoTextSearchableColumn"`); - } -} diff --git a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts b/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts deleted file mode 100644 index 38dd915139..0000000000 --- a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFaceIndicies1700752078178 implements MigrationInterface { - name = 'AddAssetFaceIndicies1700752078178' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_bf339a24070dac7e71304ec530" ON "asset_faces" ("personId", "assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`DROP INDEX "IDX_bf339a24070dac7e71304ec530"`); - } - -} diff --git a/server/src/migrations/1701665867595-AddExifCityIndex.ts b/server/src/migrations/1701665867595-AddExifCityIndex.ts deleted file mode 100644 index 0899ea1e6b..0000000000 --- a/server/src/migrations/1701665867595-AddExifCityIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifCityIndex1701665867595 implements MigrationInterface { - name = 'AddExifCityIndex1701665867595' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "exif_city" ON "exif" ("city") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "exif_city"`); - } - -} diff --git a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts b/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts deleted file mode 100644 index c2fc0b222f..0000000000 --- a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddWebSocketAttachmentTable1702084989965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'CREATE TABLE IF NOT EXISTS "socket_io_attachments" (id bigserial UNIQUE, created_at timestamptz DEFAULT NOW(), payload bytea);', - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "socket_io_attachments"`); - } -} diff --git a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts b/server/src/migrations/1702257380990-DropNullIslandLatLong.ts deleted file mode 100644 index 173b2c5950..0000000000 --- a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropNullIslandLatLong1702257380990 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;', - ); - } - - public async down(): Promise { - // There's no way to know which assets used to have 0/0 lat-long if we've - // already run this migration. - } -} diff --git a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts b/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts deleted file mode 100644 index c646287c81..0000000000 --- a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullifyFutureBirthDatesAndAddCheckConstraint1702938928766 implements MigrationInterface { - name = 'NullifyFutureBirthDatesAndAddCheckConstraint1702938928766' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "person" SET "birthDate" = NULL WHERE "birthDate" > CURRENT_DATE;`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45" CHECK ("birthDate" <= CURRENT_DATE)`); - } - - public async down(queryRunner: QueryRunner): Promise { - // The down method cannot revert the nullified dates - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45"`); - } - -} diff --git a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts b/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts deleted file mode 100644 index a55b12fa74..0000000000 --- a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixRemovedAssetsSharedLink1702942303661 implements MigrationInterface { - name = 'FixRemovedAssetsSharedLink1702942303661' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1703035138085-AddAutoStackId.ts b/server/src/migrations/1703035138085-AddAutoStackId.ts deleted file mode 100644 index d8c83ac565..0000000000 --- a/server/src/migrations/1703035138085-AddAutoStackId.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAutoStackId1703035138085 implements MigrationInterface { - name = 'AddAutoStackId1703035138085' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "autoStackId" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_auto_stack_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`); - } - -} diff --git a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts b/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts deleted file mode 100644 index 4ea88db8eb..0000000000 --- a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DefaultStorageTemplateOnForExistingInstallations1703288449127 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`) - if(adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_config (key, value) VALUES ('storageTemplate.enabled', 'true')`) - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_config WHERE key = 'storageTemplate.enabled'`) - } - -} diff --git a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts b/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts deleted file mode 100644 index 5d6477b8fb..0000000000 --- a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddQuotaColumnsToUser1704382918223 implements MigrationInterface { - name = 'AddQuotaColumnsToUser1704382918223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "quotaSizeInBytes" bigint`); - await queryRunner.query(`ALTER TABLE "users" ADD "quotaUsageInBytes" bigint NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaUsageInBytes"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaSizeInBytes"`); - } - -} diff --git a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts b/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts deleted file mode 100644 index cd737b2a62..0000000000 --- a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DefaultOnboardingForExistingInstallations1704571051932 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`); - if (adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_metadata (key, value) VALUES ($1, $2)`, [ - 'admin-onboarding', - String.raw`"{\"isOnboarded\":true}"`, - ]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_metadata WHERE key = 'admin-onboarding'`); - } -} diff --git a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts b/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts deleted file mode 100644 index 7b03ee9afb..0000000000 --- a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class SetAssetFaceNullOnPersonDelete1704943345360 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE CASCADE ON UPDATE CASCADE - `); - } - -} diff --git a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts b/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts deleted file mode 100644 index e76d095c10..0000000000 --- a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMetadataExtractedAt1705094221536 implements MigrationInterface { - name = 'AddMetadataExtractedAt1705094221536'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "metadataExtractedAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(` - UPDATE "asset_job_status" - SET "metadataExtractedAt" = NOW() - FROM "exif" - WHERE "exif"."assetId" = "asset_job_status"."assetId"; -`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "metadataExtractedAt"`); - } -} diff --git a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts b/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts deleted file mode 100644 index c62c01f50c..0000000000 --- a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameIndex1705306747072 implements MigrationInterface { - name = 'AddOriginalFileNameIndex1705306747072'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_4d66e76dada1ca180f67a205dc" ON "assets" ("originalFileName") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_4d66e76dada1ca180f67a205dc"`); - } -} diff --git a/server/src/migrations/1705363967169-CreateAssetStackTable.ts b/server/src/migrations/1705363967169-CreateAssetStackTable.ts deleted file mode 100644 index d1591797ff..0000000000 --- a/server/src/migrations/1705363967169-CreateAssetStackTable.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetStackTable1705197515600 implements MigrationInterface { - name = 'CreateAssetStackTable1705197515600'; - - public async up(queryRunner: QueryRunner): Promise { - // create table - await queryRunner.query( - `CREATE TABLE "asset_stack" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "primaryAssetId" uuid NOT NULL, - CONSTRAINT "REL_91704e101438fd0653f582426d" UNIQUE ("primaryAssetId"), - CONSTRAINT "PK_74a27e7fcbd5852463d0af3034b" PRIMARY KEY ("id"))`, - ); - - // create stacks - await queryRunner.query( - `INSERT INTO "asset_stack" ("primaryAssetId") - SELECT DISTINCT("stackParentId" ) - FROM "assets" - WHERE "stackParentId" IS NOT NULL;`, - ); - - // add "stackId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackId" uuid`); - - // set "stackId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."id" = "asset_stack"."primaryAssetId"`, - ); - - // set "stackId" for children - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."stackParentId" = "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_91704e101438fd0653f582426dc" FOREIGN KEY ("primaryAssetId") REFERENCES "assets"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - // drop "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // add "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackParentId" uuid`); - - // set "stackParentId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackParentId" = "asset_stack"."primaryAssetId" - FROM "asset_stack" - WHERE "assets"."stackId" = "asset_stack"."id" and "assets"."id" != "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_91704e101438fd0653f582426dc"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207"`); - - // drop table - await queryRunner.query(`DROP TABLE "asset_stack"`); - - // drop "stackId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackId"`); - } -} diff --git a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts b/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts deleted file mode 100644 index 11c84cf970..0000000000 --- a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddVectorsToSearchPath1707000751533 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const res = await queryRunner.query(`SELECT current_database() as db`); - const databaseName = res[0]['db']; - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`); - } - - public async down(queryRunner: QueryRunner): Promise { - const databaseName = await queryRunner.query(`SELECT current_database()`); - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public`); - } -} diff --git a/server/src/migrations/1708059341865-GeodataLocationSearch.ts b/server/src/migrations/1708059341865-GeodataLocationSearch.ts deleted file mode 100644 index af2d5dc5f3..0000000000 --- a/server/src/migrations/1708059341865-GeodataLocationSearch.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataLocationSearch1708059341865 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS unaccent`); - - // https://stackoverflow.com/a/11007216 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_unaccent(text) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT - RETURN unaccent('unaccent', $1)`); - - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin1Name" varchar`); - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin2Name" varchar`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key"`); - - await queryRunner.query(`DROP TABLE geodata_admin1 CASCADE`); - await queryRunner.query(`DROP TABLE geodata_admin2 CASCADE`); - - await queryRunner.query(` - ALTER TABLE geodata_places - DROP COLUMN "admin1Key", - DROP COLUMN "admin2Key"`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_name - ON geodata_places - USING gin (f_unaccent(name) gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin1_name - ON geodata_places - USING gin (f_unaccent("admin1Name") gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_name - ON geodata_places - USING gin (f_unaccent("admin2Name") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "geodata_admin1" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - CREATE TABLE "geodata_admin2" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - ALTER TABLE geodata_places - ADD COLUMN "admin1Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, - ADD COLUMN "admin2Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED`); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin1" - SELECT DISTINCT - "admin1Key" AS "key", - "admin1Name" AS "name" - FROM geodata_places - WHERE "admin1Name" IS NOT NULL`, - ); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin2" - SELECT DISTINCT - "admin2Key" AS "key", - "admin2Name" AS "name" - FROM geodata_places - WHERE "admin2Name" IS NOT NULL`, - ); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key";`); - } -} diff --git a/server/src/migrations/1708116312820-GeonamesEnhancement.ts b/server/src/migrations/1708116312820-GeonamesEnhancement.ts deleted file mode 100644 index 0cea9a0411..0000000000 --- a/server/src/migrations/1708116312820-GeonamesEnhancement.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class GeonamesEnhancement1708116312820 implements MigrationInterface { - name = 'GeonamesEnhancement1708116312820' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "alternateNames" varchar`); - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_alternate_names - ON geodata_places - USING gin (f_unaccent("alternateNames") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "alternateNames"`); - } - -} diff --git a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts b/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts deleted file mode 100644 index f7ca40cd46..0000000000 --- a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFileCreatedAtIndex1708227417898 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX idx_asset_file_created_at ON assets ("fileCreatedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_asset_file_created_at`); - } -} diff --git a/server/src/migrations/1708425975121-RemoveExternalPath.ts b/server/src/migrations/1708425975121-RemoveExternalPath.ts deleted file mode 100644 index 6c43e351f9..0000000000 --- a/server/src/migrations/1708425975121-RemoveExternalPath.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveExternalPath1708425975121 implements MigrationInterface { - name = 'RemoveExternalPath1708425975121'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } -} diff --git a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts b/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts deleted file mode 100644 index 8b7ff3a675..0000000000 --- a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts b/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts deleted file mode 100644 index 1d4f13410a..0000000000 --- a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetOriginalPathTrigramIndex1709608140355 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX idx_originalFileName_trigram - ON assets - USING gin (f_unaccent("originalFileName") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "idx_originalFileName_trigram"`); - } -} diff --git a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts b/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts deleted file mode 100644 index d1f73b4e3b..0000000000 --- a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExtensionToOriginalFileName1709763765506 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - WITH extension AS (WITH cte AS (SELECT a.id, STRING_TO_ARRAY(a."originalPath", '.')::TEXT[] AS arr - FROM assets a) - SELECT cte.id, cte.arr[ARRAY_UPPER(cte.arr, 1)] AS "ext" - FROM cte) - UPDATE assets - SET "originalFileName" = assets."originalFileName" || '.' || extension."ext" - FROM extension - WHERE assets.id = extension.id; - `); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts b/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts deleted file mode 100644 index 9689741929..0000000000 --- a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CascadeSharedLinksDelete1709825430031 implements MigrationInterface { - name = 'CascadeSharedLinksDelete1709825430031' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1709870213078-AddUserStatus.ts b/server/src/migrations/1709870213078-AddUserStatus.ts deleted file mode 100644 index 858f51258f..0000000000 --- a/server/src/migrations/1709870213078-AddUserStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserStatus1709870213078 implements MigrationInterface { - name = 'AddUserStatus1709870213078' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "status" character varying NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "status"`); - } - -} diff --git a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts b/server/src/migrations/1710182081326-AscendingOrderAlbum.ts deleted file mode 100644 index b672ff2b20..0000000000 --- a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AscendingOrderAlbum1710182081326 implements MigrationInterface { - name = 'AscendingOrderAlbum1710182081326' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "order" character varying NOT NULL DEFAULT 'desc'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "order"`); - } - -} diff --git a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts b/server/src/migrations/1710293990203-AddAssetRelationIndices.ts deleted file mode 100644 index dd0abf7fd5..0000000000 --- a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetRelationIndices1710293990203 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`DROP INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`DROP INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts b/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts deleted file mode 100644 index ab6f2a4e9f..0000000000 --- a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameWebpJpegPaths1711257900274 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath'); - await queryRunner.renameColumn('assets', 'resizePath', 'previewPath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'image.previewSize' - WHERE key = 'thumbnail.jpegSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.thumbnailSize' - WHERE key = 'thumbnail.webpSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.quality' - WHERE key = 'thumbnail.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.colorspace' - WHERE key = 'thumbnail.colorspace'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath'); - await queryRunner.renameColumn('assets', 'previewPath', 'resizePath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'thumbnail.jpegSize' - WHERE key = 'image.previewSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.webpSize' - WHERE key = 'image.thumbnailSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.quality' - WHERE key = 'image.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.colorspace' - WHERE key = 'image.colorspace'`, - ); - } -} diff --git a/server/src/migrations/1711637874206-AddMemoryTable.ts b/server/src/migrations/1711637874206-AddMemoryTable.ts deleted file mode 100644 index b1c5b437d7..0000000000 --- a/server/src/migrations/1711637874206-AddMemoryTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryTable1711637874206 implements MigrationInterface { - name = 'AddMemoryTable1711637874206' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "memories" ("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, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "data" jsonb NOT NULL, "isSaved" boolean NOT NULL DEFAULT false, "memoryAt" TIMESTAMP WITH TIME ZONE NOT NULL, "seenAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_aaa0692d9496fe827b0568612f8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "memories_assets_assets" ("memoriesId" uuid NOT NULL, "assetsId" uuid NOT NULL, CONSTRAINT "PK_fcaf7112a013d1703c011c6793d" PRIMARY KEY ("memoriesId", "assetsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_984e5c9ab1f04d34538cd32334" ON "memories_assets_assets" ("memoriesId") `); - await queryRunner.query(`CREATE INDEX "IDX_6942ecf52d75d4273de19d2c16" ON "memories_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "memories" ADD CONSTRAINT "FK_575842846f0c28fa5da46c99b19" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e" FOREIGN KEY ("memoriesId") REFERENCES "memories"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`); - await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`); - await queryRunner.query(`DROP INDEX "IDX_6942ecf52d75d4273de19d2c16"`); - await queryRunner.query(`DROP INDEX "IDX_984e5c9ab1f04d34538cd32334"`); - await queryRunner.query(`DROP TABLE "memories_assets_assets"`); - await queryRunner.query(`DROP TABLE "memories"`); - } - -} diff --git a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts b/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts deleted file mode 100644 index d295ec2d7c..0000000000 --- a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetDuplicateColumns1711989989911 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets ADD COLUMN "duplicateId" uuid`); - await queryRunner.query(`ALTER TABLE asset_job_status ADD COLUMN "duplicatesDetectedAt" timestamptz`); - await queryRunner.query(`CREATE INDEX "IDX_assets_duplicateId" ON assets ("duplicateId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets DROP COLUMN "duplicateId"`); - await queryRunner.query(`ALTER TABLE asset_job_status DROP COLUMN "duplicatesDetectedAt"`); - } -} diff --git a/server/src/migrations/1713337511945-AddAlbumUserRole.ts b/server/src/migrations/1713337511945-AddAlbumUserRole.ts deleted file mode 100644 index a8d0d3d685..0000000000 --- a/server/src/migrations/1713337511945-AddAlbumUserRole.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAlbumUserRole1713337511945 implements MigrationInterface { - name = 'AddAlbumUserRole1713337511945' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "role" character varying NOT NULL DEFAULT 'editor'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "role"`); - } - -} diff --git a/server/src/migrations/1713490844785-RenameSessionsTable.ts b/server/src/migrations/1713490844785-RenameSessionsTable.ts deleted file mode 100644 index b1b35e8ae6..0000000000 --- a/server/src/migrations/1713490844785-RenameSessionsTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSessionsTable1713490844785 implements MigrationInterface { - name = 'RenameSessionsTable1713490844785'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" RENAME TO "sessions"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" to "FK_57de40bc620f456c7311aa3a1e6"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_57de40bc620f456c7311aa3a1e6" to "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME TO "user_token"`); - } -} diff --git a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts b/server/src/migrations/1714698592332-RemoveIsReadOnly.ts deleted file mode 100644 index bc56f8dac7..0000000000 --- a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveIsReadOnly1714698592332 implements MigrationInterface { - name = 'RemoveIsReadOnly1714698592332' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - } - -} diff --git a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts b/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts deleted file mode 100644 index f037ba1fb0..0000000000 --- a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MotionAssetExtensionMP41715435221124 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "assets" SET "originalFileName" = regexp_replace("originalFileName", '\\.[a-zA-Z0-9]+$', '.mp4') WHERE "originalPath" LIKE '%.mp4' AND "isVisible" = false`, - ); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts b/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts deleted file mode 100644 index 50e99f0b2a..0000000000 --- a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveTextSearchColumn1715623169039 implements MigrationInterface { - name = 'RemoveTextSearchColumn1715623169039' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - } - -} diff --git a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts b/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts deleted file mode 100644 index c16eec7160..0000000000 --- a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'lodash'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveSystemConfigTable1715787369686 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const overrides = await queryRunner.query('SELECT "key", "value" FROM "system_config"'); - if (overrides.length === 0) { - return; - } - - const config = {}; - for (const { key, value } of overrides) { - _.set(config, key, JSON.parse(value)); - } - - await queryRunner.query(`INSERT INTO "system_metadata" ("key", "value") VALUES ($1, $2)`, [ - 'system-config', - // yup, we're double-stringifying it - JSON.stringify(JSON.stringify(config)), - ]); - - await queryRunner.query(`DROP TABLE "system_config"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // no data restore, you just get the table back - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } -} diff --git a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts b/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts deleted file mode 100644 index 45f5248c1a..0000000000 --- a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveLibraryIsVisible1715798702876 implements MigrationInterface { - name = 'RemoveLibraryIsVisible1715798702876' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "isVisible"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" ADD "isVisible" boolean NOT NULL DEFAULT true`); - } - -} diff --git a/server/src/migrations/1715804005643-RemoveLibraryType.ts b/server/src/migrations/1715804005643-RemoveLibraryType.ts deleted file mode 100644 index cd4dc574f2..0000000000 --- a/server/src/migrations/1715804005643-RemoveLibraryType.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryType1715804005643 implements MigrationInterface { - name = 'RemoveLibraryType1715804005643'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(` - UPDATE "assets" - SET "libraryId" = NULL - FROM "libraries" - WHERE "assets"."libraryId" = "libraries"."id" - AND "libraries"."type" = 'UPLOAD' -`); - await queryRunner.query(`DELETE FROM "libraries" WHERE "type" = 'UPLOAD'`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "type"`); - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_checksum" ON "assets" ("ownerId", "checksum") WHERE "libraryId" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" ON "assets" ("ownerId", "libraryId", "checksum") WHERE "libraryId" IS NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1715890481637-FixJsonB.ts b/server/src/migrations/1715890481637-FixJsonB.ts deleted file mode 100644 index afb39565a3..0000000000 --- a/server/src/migrations/1715890481637-FixJsonB.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixJsonB1715890481637 implements MigrationInterface { - name = 'FixJsonB1715890481637'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" DROP DEFAULT`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [value, key]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" SET DEFAULT '{}'`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [ - JSON.stringify(JSON.stringify(value)), - key, - ]); - } - } -} diff --git a/server/src/migrations/1716312279245-UserMetadata.ts b/server/src/migrations/1716312279245-UserMetadata.ts deleted file mode 100644 index b118a8d42a..0000000000 --- a/server/src/migrations/1716312279245-UserMetadata.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMetadata1716312279245 implements MigrationInterface { - name = 'UserMetadata1716312279245'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "user_metadata" ("userId" uuid NOT NULL, "key" character varying NOT NULL, "value" jsonb NOT NULL, CONSTRAINT "PK_5931462150b3438cbc83277fe5a" PRIMARY KEY ("userId", "key"))`, - ); - const users = await queryRunner.query('SELECT "id", "memoriesEnabled", "avatarColor" FROM "users"'); - for (const { id, memoriesEnabled, avatarColor } of users) { - const preferences: any = {}; - if (!memoriesEnabled) { - preferences.memories = { enabled: false }; - } - - if (avatarColor) { - preferences.avatar = { color: avatarColor }; - } - - if (Object.keys(preferences).length === 0) { - continue; - } - - await queryRunner.query('INSERT INTO "user_metadata" ("userId", "key", "value") VALUES ($1, $2, $3)', [ - id, - 'preferences', - preferences, - ]); - } - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - await queryRunner.query( - `ALTER TABLE "user_metadata" ADD CONSTRAINT "FK_6afb43681a21cf7815932bc38ac" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_metadata" DROP CONSTRAINT "FK_6afb43681a21cf7815932bc38ac"`); - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - const items = await queryRunner.query( - `SELECT "userId" as "id", "value" FROM "user_metadata" WHERE "key"='preferences'`, - ); - for (const { id, value } of items) { - if (!value) { - continue; - } - - if (value.avatar?.color) { - await queryRunner.query(`UPDATE "users" SET "avatarColor" = $1 WHERE "id" = $2`, [value.avatar.color, id]); - } - - if (value.memories?.enabled === false) { - await queryRunner.query(`UPDATE "users" SET "memoriesEnabled" = false WHERE "id" = $1`, [id]); - } - } - await queryRunner.query(`DROP TABLE "user_metadata"`); - } -} diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts deleted file mode 100644 index 2bd1acad34..0000000000 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { DatabaseExtension } from 'src/enum'; -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceSearchRelation1718486162779 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - const hasEmbeddings = async (tableName: string): Promise => { - const columns = await queryRunner.query( - `SELECT column_name as name - FROM information_schema.columns - WHERE table_name = '${tableName}'`, - ); - return columns.some((column: { name: string }) => column.name === 'embedding'); - }; - - const hasAssetEmbeddings = await hasEmbeddings('smart_search'); - if (!hasAssetEmbeddings) { - await queryRunner.query(`TRUNCATE smart_search`); - await queryRunner.query(`ALTER TABLE smart_search ADD COLUMN IF NOT EXISTS embedding vector(512) NOT NULL`); - } - - await queryRunner.query(` - CREATE TABLE face_search ( - "faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE, - embedding vector(512) NOT NULL )`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - - const hasFaceEmbeddings = await hasEmbeddings('asset_faces'); - if (hasFaceEmbeddings) { - await queryRunner.query(` - INSERT INTO face_search("faceId", embedding) - SELECT id, embedding - FROM asset_faces faces`); - } - - await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN IF EXISTS embedding`); - - 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(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(` - UPDATE asset_faces - SET embedding = fs.embedding - FROM face_search fs - WHERE id = fs."faceId"`); - await queryRunner.query(`DROP TABLE face_search`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } -} diff --git a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts b/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts deleted file mode 100644 index 9bf2a2b8d7..0000000000 --- a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixLivePhotoVideoRelation1719359859887 implements MigrationInterface { - name = 'FixLivePhotoVideoRelation1719359859887' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - -} diff --git a/server/src/migrations/1720207981949-AddStackOwner.ts b/server/src/migrations/1720207981949-AddStackOwner.ts deleted file mode 100644 index 61394cc985..0000000000 --- a/server/src/migrations/1720207981949-AddStackOwner.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackOwner1720207981949 implements MigrationInterface { - name = 'AddStackOwner1720207981949' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" ADD "ownerId" uuid`); - await queryRunner.query(` - UPDATE "asset_stack" stack - SET "ownerId" = asset."ownerId" - FROM "assets" asset - WHERE stack."primaryAssetId" = asset."id" - `) - await queryRunner.query('ALTER TABLE "asset_stack" ALTER COLUMN "ownerId" SET NOT NULL') - await queryRunner.query(`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8"`); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP COLUMN "ownerId"`); - } -} diff --git a/server/src/migrations/1720375641148-natural-earth-countries.ts b/server/src/migrations/1720375641148-natural-earth-countries.ts deleted file mode 100644 index 8c58321dca..0000000000 --- a/server/src/migrations/1720375641148-natural-earth-countries.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NaturalEarthCountries1720375641148 implements MigrationInterface { - name = 'NaturalEarthCountries1720375641148' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "naturalearth_countries" ("id" SERIAL NOT NULL, "admin" character varying(50) NOT NULL, "admin_a3" character varying(3) NOT NULL, "type" character varying(50) NOT NULL, "coordinates" polygon NOT NULL, CONSTRAINT "PK_21a6d86d1ab5d841648212e5353" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "naturalearth_countries"`); - } - -} diff --git a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts b/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts deleted file mode 100644 index 7f185077ff..0000000000 --- a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSourceColumnToAssetFace1721249222549 implements MigrationInterface { - name = 'AddSourceColumnToAssetFace1721249222549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "sourceType" sourceType NOT NULL DEFAULT 'machine-learning'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "sourceType"`); - await queryRunner.query(`DROP TYPE sourceType`); - } - -} diff --git a/server/src/migrations/1722753178937-AddExifRating.ts b/server/src/migrations/1722753178937-AddExifRating.ts deleted file mode 100644 index 52e8fb71e8..0000000000 --- a/server/src/migrations/1722753178937-AddExifRating.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddRating1722753178937 implements MigrationInterface { - name = 'AddRating1722753178937' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "rating" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "rating"`); - } - -} diff --git a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts b/server/src/migrations/1723719333525-AddApiKeyPermissions.ts deleted file mode 100644 index d585d98bcb..0000000000 --- a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddApiKeyPermissions1723719333525 implements MigrationInterface { - name = 'AddApiKeyPermissions1723719333525'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" ADD "permissions" character varying array NOT NULL DEFAULT '{all}'`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "permissions" DROP DEFAULT`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "permissions"`); - } -} diff --git a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts deleted file mode 100644 index a71ddfbcf3..0000000000 --- a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbnailJobStatus1724080823160 implements MigrationInterface { - name = 'AddThumbnailJobStatus1724080823160'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "previewAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "thumbnailAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."previewPath" IS NOT NULL`); - await queryRunner.query(`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."thumbnailPath" IS NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "thumbnailAt"`); - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "previewAt"`); - } -} diff --git a/server/src/migrations/1724101822106-AddAssetFilesTable.ts b/server/src/migrations/1724101822106-AddAssetFilesTable.ts deleted file mode 100644 index bb086b084e..0000000000 --- a/server/src/migrations/1724101822106-AddAssetFilesTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFilesTable1724101822106 implements MigrationInterface { - name = 'AddAssetFilesTable1724101822106' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_files" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "assetId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "type" character varying NOT NULL, "path" character varying NOT NULL, CONSTRAINT "UQ_assetId_type" UNIQUE ("assetId", "type"), CONSTRAINT "PK_c41dc3e9ef5e1c57ca5a08a0004" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_assetId" ON "asset_files" ("assetId") `); - await queryRunner.query(`ALTER TABLE "asset_files" ADD CONSTRAINT "FK_e3e103a5f1d8bc8402999286040" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - // preview path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'preview', "previewPath" FROM "assets" WHERE "previewPath" IS NOT NULL AND "previewPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "previewPath"`); - - // thumbnail path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'thumbnail', "thumbnailPath" FROM "assets" WHERE "thumbnailPath" IS NOT NULL AND "thumbnailPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbnailPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // undo preview path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "previewPath" character varying`); - await queryRunner.query(`UPDATE "assets" SET "previewPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'preview'`); - - // undo thumbnail path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbnailPath" character varying DEFAULT ''`); - await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`); - - await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`); - await queryRunner.query(`DROP INDEX "IDX_asset_files_assetId"`); - await queryRunner.query(`DROP TABLE "asset_files"`); - } - -} diff --git a/server/src/migrations/1724790460210-NestedTagTable.ts b/server/src/migrations/1724790460210-NestedTagTable.ts deleted file mode 100644 index d468ff6ba4..0000000000 --- a/server/src/migrations/1724790460210-NestedTagTable.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NestedTagTable1724790460210 implements MigrationInterface { - name = 'NestedTagTable1724790460210' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query('TRUNCATE TABLE "tags" CASCADE'); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_tag_name_userId"`); - await queryRunner.query(`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant"))`); - await queryRunner.query(`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor") `); - await queryRunner.query(`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant") `); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "renameTagId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "type"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "value" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - await queryRunner.query(`ALTER TABLE "tags" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "color" character varying`); - await queryRunner.query(`ALTER TABLE "tags" ADD "parentId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1"`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "parentId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "color"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "createdAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`); - await queryRunner.query(`DROP INDEX "IDX_b1a2a7ed45c29179b5ad51548a"`); - await queryRunner.query(`DROP INDEX "IDX_15fbcbc67663c6bfc07b354c22"`); - await queryRunner.query(`DROP TABLE "tags_closure"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1725023079109-FixTagUniqueness.ts b/server/src/migrations/1725023079109-FixTagUniqueness.ts deleted file mode 100644 index 859712621c..0000000000 --- a/server/src/migrations/1725023079109-FixTagUniqueness.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixTagUniqueness1725023079109 implements MigrationInterface { - name = 'FixTagUniqueness1725023079109' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_79d6f16e52bb2c7130375246793"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - } - -} diff --git a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts deleted file mode 100644 index 8eb47db438..0000000000 --- a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts b/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts deleted file mode 100644 index 98a3fe403a..0000000000 --- a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveThumbailAtForMissingThumbnails1725327902980 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "asset_job_status" j SET "thumbnailAt" = NULL WHERE j."thumbnailAt" IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM asset_files f WHERE j."assetId" = f."assetId" AND f."type" = 'thumbnail' AND f."path" IS NOT NULL )`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts b/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts deleted file mode 100644 index 2dfb5b7978..0000000000 --- a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveHiddenAssetsFromAlbums1725730782681 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "albums_assets_assets" WHERE "assetsId" IN (SELECT "id" FROM "assets" WHERE "isVisible" = false)`, - ); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1726491047923-AddprofileChangedAt.ts b/server/src/migrations/1726491047923-AddprofileChangedAt.ts deleted file mode 100644 index bcf568426a..0000000000 --- a/server/src/migrations/1726491047923-AddprofileChangedAt.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddprofileChangedAt1726491047923 implements MigrationInterface { - name = 'AddprofileChangedAt1726491047923' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "profileChangedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "profileChangedAt"`); - } - -} diff --git a/server/src/migrations/1726593009549-AddAssetStatus.ts b/server/src/migrations/1726593009549-AddAssetStatus.ts deleted file mode 100644 index 5b243b05b5..0000000000 --- a/server/src/migrations/1726593009549-AddAssetStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetStatus1726593009549 implements MigrationInterface { - name = 'AddAssetStatus1726593009549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "assets_status_enum" AS ENUM('active', 'trashed', 'deleted')`); - await queryRunner.query(`ALTER TABLE "assets" ADD "status" "assets_status_enum" NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "status"`); - await queryRunner.query(`DROP TYPE "assets_status_enum"`); - } - -} diff --git a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts b/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts deleted file mode 100644 index e02203997f..0000000000 --- a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class SeparateQualityForThumbnailAndPreview1727471863507 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls( - jsonb_build_object( - 'preview', jsonb_build_object( - 'format', value->'image'->'previewFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'previewSize'), - 'thumbnail', jsonb_build_object( - 'format', value->'image'->'thumbnailFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'thumbnailSize'), - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace' - ))) - where key = 'system-config'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls(jsonb_build_object( - 'previewFormat', value->'image'->'preview'->'format', - 'previewSize', value->'image'->'preview'->'size', - 'thumbnailFormat', value->'image'->'thumbnail'->'format', - 'thumbnailSize', value->'image'->'thumbnail'->'size', - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace', - 'quality', value->'image'->'preview'->'quality' - ))) - where key = 'system-config'`); - } -} diff --git a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts b/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts deleted file mode 100644 index 050e9a93cf..0000000000 --- a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class IsOfflineSetDeletedAt1727781844613 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = now() WHERE "isOffline" = true AND "deletedAt" IS NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = null WHERE "isOffline" = true`, - ); - } -} diff --git a/server/src/migrations/1727797340951-AddVersionHistory.ts b/server/src/migrations/1727797340951-AddVersionHistory.ts deleted file mode 100644 index 7eb731d1a3..0000000000 --- a/server/src/migrations/1727797340951-AddVersionHistory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddVersionHistory1727797340951 implements MigrationInterface { - name = 'AddVersionHistory1727797340951' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "version_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "version" character varying NOT NULL, CONSTRAINT "PK_5db259cbb09ce82c0d13cfd1b23" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "version_history"`); - } - -} diff --git a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts b/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts deleted file mode 100644 index 280b34890d..0000000000 --- a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumAssetCreatedAt1729793521993 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "albums_assets_assets" ADD COLUMN "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "createdAt"`); - } -} diff --git a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts b/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts deleted file mode 100644 index 2c929191dd..0000000000 --- a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveNplFromSystemConfig1730227312171 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = value #- '{ffmpeg,npl}' - where key = 'system-config' and value->'ffmpeg'->'npl' is not null`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1730989238718-DropSmartInfoTable.ts b/server/src/migrations/1730989238718-DropSmartInfoTable.ts deleted file mode 100644 index a4de2652d6..0000000000 --- a/server/src/migrations/1730989238718-DropSmartInfoTable.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropSmartInfoTable1730989238718 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE smart_info`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts b/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts deleted file mode 100644 index 3ebe8108cb..0000000000 --- a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class NaturalEarthCountriesIdentityColumn1732072134943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP DEFAULT`); - await queryRunner.query(`DROP SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id ADD GENERATED ALWAYS AS IDENTITY`); - - // same as ll_to_earth, but with explicit schema to avoid weirdness and allow it to work in expression indices - await queryRunner.query(` - CREATE FUNCTION ll_to_earth_public(latitude double precision, longitude double precision) RETURNS public.earth PARALLEL SAFE IMMUTABLE STRICT LANGUAGE SQL AS $$ - SELECT public.cube(public.cube(public.cube(public.earth()*cos(radians(latitude))*cos(radians(longitude))),public.earth()*cos(radians(latitude))*sin(radians(longitude))),public.earth()*sin(radians(latitude)))::public.earth - $$`); - - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "earthCoord"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP GENERATED`); - await queryRunner.query(`CREATE SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query( - `ALTER TABLE naturalearth_countries ALTER id SET DEFAULT nextval('naturalearth_countries_id_seq'::regclass)`, - ); - await queryRunner.query(`DROP FUNCTION ll_to_earth_public`); - await queryRunner.query( - `ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`, - ); - } -} diff --git a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts b/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts deleted file mode 100644 index 65bb02c8e2..0000000000 --- a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameMachineLearningUrlToUrls1733339482860 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,url}', '{machineLearning,urls}'::text[], jsonb_build_array(value->'machineLearning'->'url')) - WHERE key = 'system-config' AND value->'machineLearning'->'url' IS NOT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,urls}', '{machineLearning,url}'::text[], to_jsonb(value->'machineLearning'->'urls'->>0)) - WHERE key = 'system-config' AND value->'machineLearning'->'urls' IS NOT NULL AND jsonb_array_length(value->'machineLearning'->'urls') >= 1 - `); - } -} diff --git a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts b/server/src/migrations/1734574016301-AddTimeBucketIndices.ts deleted file mode 100644 index 2162a713fc..0000000000 --- a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTimeBucketIndices1734574016301 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX idx_local_date_time_month ON assets ((date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX idx_local_date_time ON assets ((("localDateTime" at time zone 'UTC')::date))`, - ); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_local_date_time_month`); - await queryRunner.query(`DROP INDEX idx_local_date_time`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } -} diff --git a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts b/server/src/migrations/1734879118272-AddIsFavoritePerson.ts deleted file mode 100644 index 6f7640f96f..0000000000 --- a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsFavoritePerson1734879118272 implements MigrationInterface { - name = 'AddIsFavoritePerson1734879118272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isFavorite" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isFavorite"`); - } - -} diff --git a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts b/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts deleted file mode 100644 index 74dde826fb..0000000000 --- a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtTriggers1737672307560 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create function updated_at() - returns trigger as $$ - begin - new."updatedAt" = now(); - return new; - end; - $$ language 'plpgsql'`); - - await queryRunner.query(` - create trigger activity_updated_at - before update on activity - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger albums_updated_at - before update on albums - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger api_keys_updated_at - before update on api_keys - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger asset_files_updated_at - before update on asset_files - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger assets_updated_at - before update on assets - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger libraries_updated_at - before update on libraries - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger memories_updated_at - before update on memories - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger partners_updated_at - before update on partners - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger person_updated_at - before update on person - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger sessions_updated_at - before update on sessions - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger tags_updated_at - before update on tags - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger users_updated_at - before update on users - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger activity_updated_at on activity`); - await queryRunner.query(`drop trigger albums_updated_at on albums`); - await queryRunner.query(`drop trigger api_keys_updated_at on api_keys`); - await queryRunner.query(`drop trigger asset_files_updated_at on asset_files`); - await queryRunner.query(`drop trigger assets_updated_at on assets`); - await queryRunner.query(`drop trigger libraries_updated_at on libraries`); - await queryRunner.query(`drop trigger memories_updated_at on memories`); - await queryRunner.query(`drop trigger partners_updated_at on partners`); - await queryRunner.query(`drop trigger person_updated_at on person`); - await queryRunner.query(`drop trigger sessions_updated_at on sessions`); - await queryRunner.query(`drop trigger tags_updated_at on tags`); - await queryRunner.query(`drop trigger users_updated_at on users`); - await queryRunner.query(`drop function updated_at_trigger`); - } -} diff --git a/server/src/migrations/1737845696644-NullableDates.ts b/server/src/migrations/1737845696644-NullableDates.ts deleted file mode 100644 index 8a08b985c5..0000000000 --- a/server/src/migrations/1737845696644-NullableDates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullableDates1737845696644 implements MigrationInterface { - name = 'NullableDates1737845696644' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" DROP NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" SET NOT NULL`); - } - -} diff --git a/server/src/migrations/1738889177573-AddPersonColor.ts b/server/src/migrations/1738889177573-AddPersonColor.ts deleted file mode 100644 index ebdc86f52d..0000000000 --- a/server/src/migrations/1738889177573-AddPersonColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPersonColor1738889177573 implements MigrationInterface { - name = 'AddPersonColor1738889177573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "color" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "color"`); - } - -} diff --git a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts b/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts deleted file mode 100644 index e6f18e2618..0000000000 --- a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtColumnToAssetFacesTable1739466714036 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - ADD COLUMN "deletedAt" TIMESTAMP WITH TIME ZONE DEFAULT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - DROP COLUMN "deletedAt" - `); - } -} diff --git a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts b/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts deleted file mode 100644 index d53c7c17f6..0000000000 --- a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryShowHideDates1739824470990 implements MigrationInterface { - name = 'AddMemoryShowHideDates1739824470990' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" ADD "showAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "memories" ADD "hideAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "hideAt"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "showAt"`); - } - -} diff --git a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts b/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts deleted file mode 100644 index ef75dd7c0d..0000000000 --- a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSessionSyncCheckpointTable1740001232576 implements MigrationInterface { - name = 'AddSessionSyncCheckpointTable1740001232576' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "session_sync_checkpoints" ("sessionId" uuid NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ack" character varying NOT NULL, CONSTRAINT "PK_b846ab547a702863ef7cd9412fb" PRIMARY KEY ("sessionId", "type"))`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc" FOREIGN KEY ("sessionId") REFERENCES "sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(` - create trigger session_sync_checkpoints_updated_at - before update on session_sync_checkpoints - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger session_sync_checkpoints_updated_at on session_sync_checkpoints`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc"`); - await queryRunner.query(`DROP TABLE "session_sync_checkpoints"`); - } - -} diff --git a/server/src/migrations/1740064899123-AddUsersAuditTable.ts b/server/src/migrations/1740064899123-AddUsersAuditTable.ts deleted file mode 100644 index b8f2ce5e3a..0000000000 --- a/server/src/migrations/1740064899123-AddUsersAuditTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsersAuditTable1740064899123 implements MigrationInterface { - name = 'AddUsersAuditTable1740064899123' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_updated_at_asc_id_asc" ON "users" ("updatedAt" ASC, "id" ASC);`) - await queryRunner.query(`CREATE TABLE "users_audit" ("id" SERIAL NOT NULL, "userId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("deletedAt" ASC, "userId" ASC);`) - await queryRunner.query(`CREATE OR REPLACE FUNCTION users_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO users_audit ("userId") - SELECT "id" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER users_delete_audit`); - await queryRunner.query(`DROP FUNCTION users_delete_audit`); - await queryRunner.query(`DROP TABLE "users_audit"`); - } - -} diff --git a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts b/server/src/migrations/1740586617223-AddUpdateIdColumns.ts deleted file mode 100644 index 02d680ddf6..0000000000 --- a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUpdateIdColumns1740586617223 implements MigrationInterface { - name = 'AddUpdateIdColumns1740586617223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create or replace function immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) - returns uuid - as $$ - select encode( - set_bit( - set_bit( - overlay(uuid_send(gen_random_uuid()) - placing substring(int8send(floor(extract(epoch from p_timestamp) * 1000)::bigint) from 3) - from 1 for 6 - ), - 52, 1 - ), - 53, 1 - ), - 'hex')::uuid; - $$ - language SQL - volatile; - `) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - return new; - END; - $$; - `) - await queryRunner.query(`ALTER TABLE "person" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "asset_files" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "libraries" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "users" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "albums" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "sessions" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "partners" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "memories" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "activity" ADD "updateId" uuid`); - - await queryRunner.query(`UPDATE "person" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "asset_files" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "libraries" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "tags" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "assets" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "users" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "albums" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "sessions" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "session_sync_checkpoints" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "partners" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "memories" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "api_keys" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "activity" SET "updateId" = immich_uuid_v7("updatedAt")`); - - await queryRunner.query(`ALTER TABLE "person" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "asset_files" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "libraries" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "tags" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "partners" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "memories" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "activity" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - - await queryRunner.query(`CREATE INDEX "IDX_person_update_id" ON "person" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_update_id" ON "asset_files" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_libraries_update_id" ON "libraries" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_tags_update_id" ON "tags" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_assets_update_id" ON "assets" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_users_update_id" ON "users" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_albums_update_id" ON "albums" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_sessions_update_id" ON "sessions" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_partners_update_id" ON "partners" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_memories_update_id" ON "memories" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_api_keys_update_id" ON "api_keys" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_activity_update_id" ON "activity" ("updateId")`); - - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - DECLARE - clock_timestamp TIMESTAMP := clock_timestamp(); - BEGIN - new."updatedAt" = clock_timestamp; - new."updateId" = immich_uuid_v7(clock_timestamp); - return new; - END; - $$; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "asset_files" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "updateId"`); - await queryRunner.query(`DROP FUNCTION immich_uuid_v7`); - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - new."updatedAt" = now(); - return new; - END; - $$; - `) - } - -} diff --git a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts b/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts deleted file mode 100644 index 59fc4dbd5b..0000000000 --- a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UsersAuditUuidv7PrimaryKey1740595460866 implements MigrationInterface { - name = 'UsersAuditUuidv7PrimaryKey1740595460866' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at_asc_user_id_asc"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT clock_timestamp()`) - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at" ON "users_audit" ("deletedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT now()`); - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("userId", "deletedAt") `); - } - -} diff --git a/server/src/migrations/1740619600996-AddManualSourceType.ts b/server/src/migrations/1740619600996-AddManualSourceType.ts deleted file mode 100644 index dd53312ad7..0000000000 --- a/server/src/migrations/1740619600996-AddManualSourceType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddManualSourceType1740619600996 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TYPE sourceType ADD VALUE 'manual'`); - } - - public async down(queryRunner: QueryRunner): Promise { - // Prior to this migration, manually tagged pictures had the 'machine-learning' type - await queryRunner.query( - `UPDATE "asset_faces" SET "sourceType" = 'machine-learning' WHERE "sourceType" = 'manual';`, - ); - - // Postgres doesn't allow removing values from enums, we have to recreate the type - await queryRunner.query(`ALTER TYPE sourceType RENAME TO oldSourceType`); - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" DROP DEFAULT;`); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" TYPE sourceType USING "sourceType"::text::sourceType;`, - ); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" SET DEFAULT 'machine-learning'::sourceType;`, - ); - await queryRunner.query(`DROP TYPE oldSourceType;`); - } -} diff --git a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts b/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts deleted file mode 100644 index 5c735a60bb..0000000000 --- a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnsetStackedAssetsFromDuplicateStatus1740654480319 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update assets - set "duplicateId" = null - where "stackId" is not null`); - } - - public async down(): Promise { - // No need to revert this migration - } -} diff --git a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts b/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts deleted file mode 100644 index d9c9dc1949..0000000000 --- a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreatePartnersAuditTable1740739778549 implements MigrationInterface { - name = 'CreatePartnersAuditTable1740739778549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_952b50217ff78198a7e380f0359" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_by_id" ON "partners_audit" ("sharedById") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_with_id" ON "partners_audit" ("sharedWithId") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_deleted_at" ON "partners_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION partners_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO partners_audit ("sharedById", "sharedWithId") - SELECT "sharedById", "sharedWithId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_with_id"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_by_id"`); - await queryRunner.query(`DROP TRIGGER partners_delete_audit`); - await queryRunner.query(`DROP FUNCTION partners_delete_audit`); - await queryRunner.query(`DROP TABLE "partners_audit"`); - } - -} diff --git a/server/src/migrations/1741027685381-ResetMemories.ts b/server/src/migrations/1741027685381-ResetMemories.ts deleted file mode 100644 index 6a80372219..0000000000 --- a/server/src/migrations/1741027685381-ResetMemories.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ResetMemories1741027685381 implements MigrationInterface { - name = 'ResetMemories1741027685381'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "memories"`); - await queryRunner.query(`DELETE FROM "system_metadata" WHERE "key" = 'memories-state'`); - } - - public async down(): Promise { - // nothing to do - } -} diff --git a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts b/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts deleted file mode 100644 index 449272341c..0000000000 --- a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MoveHistoryUuidEntityId1741179334403 implements MigrationInterface { - name = 'MoveHistoryUuidEntityId1741179334403'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE uuid USING "entityId"::uuid;`); - await queryRunner.query(`delete from "move_history" - where - "move_history"."entityId" not in ( - select - "id" - from - "assets" - where - "assets"."id" = "move_history"."entityId" - ) - and "move_history"."pathType" = 'original' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE character varying`); - } -} - diff --git a/server/src/migrations/1741191762113-AssetAuditTable.ts b/server/src/migrations/1741191762113-AssetAuditTable.ts deleted file mode 100644 index c02408c384..0000000000 --- a/server/src/migrations/1741191762113-AssetAuditTable.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetAuditTable1741191762113 implements MigrationInterface { - name = 'AssetAuditTable1741191762113' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "assetId" uuid NOT NULL, "ownerId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_99bd5c015f81a641927a32b4212" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_asset_id" ON "assets_audit" ("assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_owner_id" ON "assets_audit" ("ownerId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_deleted_at" ON "assets_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION assets_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO assets_audit ("assetId", "ownerId") - SELECT "id", "ownerId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER assets_delete_audit`); - await queryRunner.query(`DROP FUNCTION assets_delete_audit`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_owner_id"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_asset_id"`); - await queryRunner.query(`DROP TABLE "assets_audit"`); - } -} diff --git a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts b/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts deleted file mode 100644 index 20215c1b59..0000000000 --- a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixAssetAndUserCascadeConditions1741280328985 implements MigrationInterface { - name = 'FixAssetAndUserCascadeConditions1741280328985'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION partners_delete_audit();`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit();`); - } -} diff --git a/server/src/migrations/1741281344519-AddExifUpdateId.ts b/server/src/migrations/1741281344519-AddExifUpdateId.ts deleted file mode 100644 index eb32836a1d..0000000000 --- a/server/src/migrations/1741281344519-AddExifUpdateId.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifUpdateId1741281344519 implements MigrationInterface { - name = 'AddExifUpdateId1741281344519'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "exif" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp()`, - ); - await queryRunner.query(`ALTER TABLE "exif" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId") `); - await queryRunner.query(` - create trigger asset_exif_updated_at - before update on exif - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_asset_exif_update_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updatedAt"`); - await queryRunner.query(`DROP TRIGGER asset_exif_updated_at on exif`); - } -} diff --git a/server/src/migrations/1743595393000-TableCleanup.ts b/server/src/migrations/1743595393000-TableCleanup.ts deleted file mode 100644 index adf9c65afa..0000000000 --- a/server/src/migrations/1743595393000-TableCleanup.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TableCleanup1743595393000 implements MigrationInterface { - name = 'TableCleanup1743595393000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE IF EXISTS "system_config"`); - await queryRunner.query(`DROP TABLE IF EXISTS "socket_io_attachments"`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1743611339000-GeodataCleanup.ts b/server/src/migrations/1743611339000-GeodataCleanup.ts deleted file mode 100644 index 0e25a1268e..0000000000 --- a/server/src/migrations/1743611339000-GeodataCleanup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataCleanup1743611339000 implements MigrationInterface { - name = 'GeodataCleanup1743611339000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_admin2_alternate_names" RENAME TO "idx_geodata_places_alternate_names"`, - ); - await queryRunner.query(`DROP TABLE IF EXISTS "geodata_places_tmp"`); - await queryRunner.query(`DROP TABLE IF EXISTS "naturalearth_countries_tmp"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_alternate_names" RENAME TO "idx_geodata_places_admin2_alternate_names"`, - ); - } -} diff --git a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts b/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts deleted file mode 100644 index 1ba4df01cd..0000000000 --- a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MakeFileMetadataNonNullable1744662638410 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM assets WHERE "fileCreatedAt" IS NULL OR "fileModifiedAt" IS NULL OR "localDateTime" IS NULL`, - ); - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" SET NOT NULL, - ALTER COLUMN "fileModifiedAt" SET NOT NULL, - ALTER COLUMN "localDateTime" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" DROP NOT NULL, - ALTER COLUMN "fileModifiedAt" DROP NOT NULL, - ALTER COLUMN "localDateTime" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts deleted file mode 100644 index db351d5bab..0000000000 --- a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddForeignKeyIndexes1744900200559 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`); - await queryRunner.query(`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`); - await queryRunner.query(`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`); - await queryRunner.query(`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`); - await queryRunner.query(`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`); - await queryRunner.query(`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`); - await queryRunner.query(`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_66fe3837414c5a9f1c33ca4934";`); - await queryRunner.query(`DROP INDEX "IDX_91704e101438fd0653f582426d";`); - await queryRunner.query(`DROP INDEX "IDX_c05079e542fd74de3b5ecb5c1c";`); - await queryRunner.query(`DROP INDEX "IDX_5527cc99f530a547093f9e577b";`); - await queryRunner.query(`DROP INDEX "IDX_2bbabe31656b6778c6b87b6102";`); - await queryRunner.query(`DROP INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c";`); - await queryRunner.query(`DROP INDEX "IDX_9f9590cc11561f1f48ff034ef9";`); - await queryRunner.query(`DROP INDEX "IDX_2c5ac0d6fb58b238fd2068de67";`); - await queryRunner.query(`DROP INDEX "IDX_16294b83fa8c0149719a1f631e";`); - await queryRunner.query(`DROP INDEX "IDX_9977c3c1de01c3d848039a6b90";`); - await queryRunner.query(`DROP INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20";`); - await queryRunner.query(`DROP INDEX "IDX_b22c53f35ef20c28c21637c85f";`); - await queryRunner.query(`DROP INDEX "IDX_05895aa505a670300d4816debc";`); - await queryRunner.query(`DROP INDEX "IDX_57de40bc620f456c7311aa3a1e";`); - await queryRunner.query(`DROP INDEX "IDX_d8ddd9d687816cc490432b3d4b";`); - await queryRunner.query(`DROP INDEX "IDX_d7e875c6c60e661723dbf372fd";`); - await queryRunner.query(`DROP INDEX "IDX_575842846f0c28fa5da46c99b1";`); - await queryRunner.query(`DROP INDEX "IDX_6c2e267ae764a9413b863a2934";`); - await queryRunner.query(`DROP INDEX "IDX_1af8519996fbfb3684b58df280";`); - await queryRunner.query(`DROP INDEX "IDX_3571467bcbe021f66e2bdce96e";`); - await queryRunner.query(`DROP INDEX "IDX_8091ea76b12338cb4428d33d78";`); - } -} diff --git a/server/src/migrations/1744910873956-AddMissingIndex.ts b/server/src/migrations/1744910873956-AddMissingIndex.ts deleted file mode 100644 index 38dd6f4958..0000000000 --- a/server/src/migrations/1744910873956-AddMissingIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMissingIndex1744910873956 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX IF NOT EXISTS "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord";`); - } -} diff --git a/server/src/queries/access.repository.sql b/server/src/queries/access.repository.sql index 9aecaafb52..e98c5c6d98 100644 --- a/server/src/queries/access.repository.sql +++ b/server/src/queries/access.repository.sql @@ -71,6 +71,11 @@ where and "shared_link"."albumId" in ($2) -- AccessRepository.asset.checkAlbumAccess +with + "target" as ( + select + array[$1]::uuid[] as "ids" + ) select "asset"."id", "asset"."livePhotoVideoId" @@ -82,8 +87,12 @@ from left join "album_user" as "albumUsers" on "albumUsers"."albumsId" = "album"."id" left join "user" on "user"."id" = "albumUsers"."usersId" and "user"."deletedAt" is null + cross join "target" where - array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid[] + ( + "asset"."id" = any (target.ids) + or "asset"."livePhotoVideoId" = any (target.ids) + ) and ( "album"."ownerId" = $2 or "user"."id" = $3 diff --git a/server/src/queries/asset.job.repository.sql b/server/src/queries/asset.job.repository.sql index df8163be3e..471de1ac34 100644 --- a/server/src/queries/asset.job.repository.sql +++ b/server/src/queries/asset.job.repository.sql @@ -43,6 +43,18 @@ where limit $2 +-- AssetJobRepository.getForSidecarCheckJob +select + "id", + "sidecarPath", + "originalPath" +from + "asset" +where + "asset"."id" = $1::uuid +limit + $2 + -- AssetJobRepository.streamForThumbnailJob select "asset"."id", @@ -468,9 +480,8 @@ where "asset"."visibility" != $1 and "asset"."deletedAt" is null and "job_status"."previewAt" is not null - and "job_status"."facesRecognizedAt" is null order by - "asset"."createdAt" desc + "asset"."fileCreatedAt" desc -- AssetJobRepository.streamForMigrationJob select diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 9425bd9a11..1283ff0a66 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -7,6 +7,45 @@ set where "assetId" in ($2) +-- AssetRepository.updateDateTimeOriginal +update "asset_exif" +set + "dateTimeOriginal" = "dateTimeOriginal" + $1::interval, + "timeZone" = $2 +where + "assetId" in ($3) +returning + "assetId", + "dateTimeOriginal", + "timeZone" + +-- AssetRepository.getMetadata +select + "key", + "value", + "updatedAt" +from + "asset_metadata" +where + "assetId" = $1 + +-- AssetRepository.getMetadataByKey +select + "key", + "value", + "updatedAt" +from + "asset_metadata" +where + "assetId" = $1 + and "key" = $2 + +-- AssetRepository.deleteMetadataByKey +delete from "asset_metadata" +where + "assetId" = $1 + and "key" = $2 + -- AssetRepository.getByDayOfYear with "res" as ( @@ -170,25 +209,10 @@ where -- AssetRepository.getFileSamples select - "asset"."id", - "asset"."originalPath", - "asset"."sidecarPath", - "asset"."encodedVideoPath", - ( - select - coalesce(json_agg(agg), '[]') - from - ( - select - "path" - from - "asset_file" - where - "asset"."id" = "asset_file"."assetId" - ) as agg - ) as "files" + "assetId", + "path" from - "asset" + "asset_file" limit 3 @@ -280,7 +304,7 @@ with epoch from ( - asset."localDateTime" - asset."fileCreatedAt" at time zone 'UTC' + asset."localDateTime" AT TIME ZONE 'UTC' - asset."fileCreatedAt" at time zone 'UTC' ) )::real / 3600 as "localOffsetHours", "asset"."ownerId", diff --git a/server/src/queries/duplicate.repository.sql b/server/src/queries/duplicate.repository.sql index 8913007dea..3f718f84c2 100644 --- a/server/src/queries/duplicate.repository.sql +++ b/server/src/queries/duplicate.repository.sql @@ -12,7 +12,7 @@ with ) as "assets" from "asset" - left join lateral ( + inner join lateral ( select "asset".*, "asset_exif" as "exifInfo" diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index ef5363126f..e0aaedfdf3 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -77,6 +77,27 @@ union all limit $15 +-- SearchRepository.searchLargeAssets +select + "asset".*, + to_json("asset_exif") as "exifInfo" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + left join "asset_exif" on "asset"."id" = "asset_exif"."assetId" +where + "asset"."visibility" = $1 + and "asset"."fileCreatedAt" >= $2 + and "asset_exif"."lensModel" = $3 + and "asset"."ownerId" = any ($4::uuid[]) + and "asset"."isFavorite" = $5 + and "asset"."deletedAt" is null + and "asset_exif"."fileSizeInByte" > $6 +order by + "asset_exif"."fileSizeInByte" desc +limit + $7 + -- SearchRepository.searchSmart begin set @@ -102,6 +123,14 @@ offset $8 commit +-- SearchRepository.getEmbedding +select + * +from + "smart_search" +where + "assetId" = $1 + -- SearchRepository.searchFaces begin set diff --git a/server/src/queries/session.repository.sql b/server/src/queries/session.repository.sql index 24ffdcb5e1..34d25cce8a 100644 --- a/server/src/queries/session.repository.sql +++ b/server/src/queries/session.repository.sql @@ -10,10 +10,17 @@ from where "id" = $1 +-- SessionRepository.isPendingSyncReset +select + "isPendingSyncReset" +from + "session" +where + "id" = $1 + -- SessionRepository.getByToken select "session"."id", - "session"."isPendingSyncReset", "session"."updatedAt", "session"."pinExpiresAt", ( diff --git a/server/src/queries/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql index ed3507fa2f..0f46846c14 100644 --- a/server/src/queries/shared.link.repository.sql +++ b/server/src/queries/shared.link.repository.sql @@ -38,7 +38,11 @@ from select "album".*, coalesce( - json_agg("assets") filter ( + json_agg( + "assets" + order by + "assets"."fileCreatedAt" asc + ) filter ( where "assets"."id" is not null ), @@ -188,9 +192,47 @@ from "shared_link" left join "album" on "album"."id" = "shared_link"."albumId" where - "shared_link"."key" = $1 - and "album"."deletedAt" is null + "album"."deletedAt" is null and ( - "shared_link"."type" = $2 + "shared_link"."type" = $1 or "album"."id" is not null ) + and "shared_link"."key" = $2 + +-- SharedLinkRepository.getBySlug +select + "shared_link"."id", + "shared_link"."userId", + "shared_link"."expiresAt", + "shared_link"."showExif", + "shared_link"."allowUpload", + "shared_link"."allowDownload", + "shared_link"."password", + ( + select + to_json(obj) + from + ( + select + "user"."id", + "user"."name", + "user"."email", + "user"."isAdmin", + "user"."quotaUsageInBytes", + "user"."quotaSizeInBytes" + from + "user" + where + "user"."id" = "shared_link"."userId" + ) as obj + ) as "user" +from + "shared_link" + left join "album" on "album"."id" = "shared_link"."albumId" +where + "album"."deletedAt" is null + and ( + "shared_link"."type" = $1 + or "album"."id" is not null + ) + and "shared_link"."slug" = $2 diff --git a/server/src/queries/stack.repository.sql b/server/src/queries/stack.repository.sql index a256cdfc76..94a24f69e4 100644 --- a/server/src/queries/stack.repository.sql +++ b/server/src/queries/stack.repository.sql @@ -143,3 +143,13 @@ from "stack" where "id" = $1::uuid + +-- StackRepository.getForAssetRemoval +select + "stackId" as "id", + "stack"."primaryAssetId" +from + "asset" + left join "stack" on "stack"."id" = "asset"."stackId" +where + "asset"."id" = $1 diff --git a/server/src/queries/sync.checkpoint.repository.sql b/server/src/queries/sync.checkpoint.repository.sql index 018054ff79..e99d90cd54 100644 --- a/server/src/queries/sync.checkpoint.repository.sql +++ b/server/src/queries/sync.checkpoint.repository.sql @@ -13,3 +13,7 @@ where delete from "session_sync_checkpoint" where "sessionId" = $1 + +-- SyncCheckpointRepository.getNow +select + immich_uuid_v7 (now() - interval '1 millisecond') as "nowId" diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 7502b79f57..809b59df10 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -9,7 +9,7 @@ from where "usersId" = $1 and "createId" >= $2 - and "createdAt" < now() - interval '1 millisecond' + and "createId" < $3 order by "createId" asc @@ -18,12 +18,13 @@ select "id", "albumId" from - "album_audit" + "album_audit" as "album_audit" where - "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "album_audit"."id" < $1 + and "album_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "album_audit"."id" asc -- SyncRepository.album.getUpserts select distinct @@ -38,13 +39,14 @@ select distinct "album"."order", "album"."updateId" from - "album" + "album" as "album" left join "album_user" as "album_users" on "album"."id" = "album_users"."albumsId" where - "album"."updatedAt" < now() - interval '1 millisecond' + "album"."updateId" < $1 + and "album"."updateId" > $2 and ( - "album"."ownerId" = $1 - or "album_users"."usersId" = $2 + "album"."ownerId" = $3 + or "album_users"."usersId" = $4 ) order by "album"."updateId" asc @@ -66,19 +68,20 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", - "asset"."updateId" + "asset"."libraryId", + "album_asset"."updateId" from - "asset" - inner join "album_asset" on "album_asset"."assetsId" = "asset"."id" + "album_asset" as "album_asset" + inner join "asset" on "asset"."id" = "album_asset"."assetsId" where - "album_asset"."albumsId" = $1 - and "asset"."updatedAt" < now() - interval '1 millisecond' - and "asset"."updateId" <= $2 - and "asset"."updateId" >= $3 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by - "asset"."updateId" asc + "album_asset"."updateId" asc --- SyncRepository.albumAsset.getUpserts +-- SyncRepository.albumAsset.getUpdates select "asset"."id", "asset"."ownerId", @@ -95,21 +98,58 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" inner join "album_asset" on "album_asset"."assetsId" = "asset"."id" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "asset"."updatedAt" < now() - interval '1 millisecond' + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "album_asset"."updateId" <= $3 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $4 + or "album_user"."usersId" = $5 ) order by "asset"."updateId" asc +-- SyncRepository.albumAsset.getCreates +select + "album_asset"."updateId", + "asset"."id", + "asset"."ownerId", + "asset"."originalFileName", + "asset"."thumbhash", + "asset"."checksum", + "asset"."fileCreatedAt", + "asset"."fileModifiedAt", + "asset"."localDateTime", + "asset"."type", + "asset"."deletedAt", + "asset"."isFavorite", + "asset"."visibility", + "asset"."duration", + "asset"."livePhotoVideoId", + "asset"."stackId", + "asset"."libraryId" +from + "album_asset" as "album_asset" + inner join "asset" on "asset"."id" = "album_asset"."assetsId" + inner join "album" on "album"."id" = "album_asset"."albumsId" + left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" +where + "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 + and ( + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 + ) +order by + "album_asset"."updateId" asc + -- SyncRepository.albumAssetExif.getBackfill select "asset_exif"."assetId", @@ -137,19 +177,19 @@ select "asset_exif"."profileDescription", "asset_exif"."rating", "asset_exif"."fps", - "asset_exif"."updateId" + "album_asset"."updateId" from - "asset_exif" - inner join "album_asset" on "album_asset"."assetsId" = "asset_exif"."assetId" + "album_asset" as "album_asset" + inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" where - "album_asset"."albumsId" = $1 - and "asset_exif"."updatedAt" < now() - interval '1 millisecond' - and "asset_exif"."updateId" <= $2 - and "asset_exif"."updateId" >= $3 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by - "asset_exif"."updateId" asc + "album_asset"."updateId" asc --- SyncRepository.albumAssetExif.getUpserts +-- SyncRepository.albumAssetExif.getUpdates select "asset_exif"."assetId", "asset_exif"."description", @@ -178,33 +218,78 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" inner join "album_asset" on "album_asset"."assetsId" = "asset_exif"."assetId" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "asset_exif"."updatedAt" < now() - interval '1 millisecond' + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "album_asset"."updateId" <= $3 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $4 + or "album_user"."usersId" = $5 ) order by "asset_exif"."updateId" asc +-- SyncRepository.albumAssetExif.getCreates +select + "album_asset"."updateId", + "asset_exif"."assetId", + "asset_exif"."description", + "asset_exif"."exifImageWidth", + "asset_exif"."exifImageHeight", + "asset_exif"."fileSizeInByte", + "asset_exif"."orientation", + "asset_exif"."dateTimeOriginal", + "asset_exif"."modifyDate", + "asset_exif"."timeZone", + "asset_exif"."latitude", + "asset_exif"."longitude", + "asset_exif"."projectionType", + "asset_exif"."city", + "asset_exif"."state", + "asset_exif"."country", + "asset_exif"."make", + "asset_exif"."model", + "asset_exif"."lensModel", + "asset_exif"."fNumber", + "asset_exif"."focalLength", + "asset_exif"."iso", + "asset_exif"."exposureTime", + "asset_exif"."profileDescription", + "asset_exif"."rating", + "asset_exif"."fps" +from + "album_asset" as "album_asset" + inner join "asset_exif" on "asset_exif"."assetId" = "album_asset"."assetsId" + inner join "album" on "album"."id" = "album_asset"."albumsId" + left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" +where + "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 + and ( + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 + ) +order by + "album_asset"."updateId" asc + -- SyncRepository.albumToAsset.getBackfill select - "album_assets"."assetsId" as "assetId", - "album_assets"."albumsId" as "albumId", - "album_assets"."updateId" + "album_asset"."assetsId" as "assetId", + "album_asset"."albumsId" as "albumId", + "album_asset"."updateId" from - "album_asset" as "album_assets" + "album_asset" as "album_asset" where - "album_assets"."albumsId" = $1 - and "album_assets"."updatedAt" < now() - interval '1 millisecond' - and "album_assets"."updateId" <= $2 - and "album_assets"."updateId" >= $3 + "album_asset"."updateId" < $1 + and "album_asset"."updateId" <= $2 + and "album_asset"."updateId" >= $3 + and "album_asset"."albumsId" = $4 order by - "album_assets"."updateId" asc + "album_asset"."updateId" asc -- SyncRepository.albumToAsset.getDeletes select @@ -212,15 +297,17 @@ select "assetId", "albumId" from - "album_asset_audit" + "album_asset_audit" as "album_asset_audit" where - "albumId" in ( + "album_asset_audit"."id" < $1 + and "album_asset_audit"."id" > $2 + and "albumId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $3 union ( select @@ -228,12 +315,11 @@ where from "album_user" where - "album_user"."usersId" = $2 + "album_user"."usersId" = $4 ) ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "album_asset_audit"."id" asc -- SyncRepository.albumToAsset.getUpserts select @@ -241,14 +327,15 @@ select "album_asset"."albumsId" as "albumId", "album_asset"."updateId" from - "album_asset" + "album_asset" as "album_asset" inner join "album" on "album"."id" = "album_asset"."albumsId" left join "album_user" on "album_user"."albumsId" = "album_asset"."albumsId" where - "album_asset"."updatedAt" < now() - interval '1 millisecond' + "album_asset"."updateId" < $1 + and "album_asset"."updateId" > $2 and ( - "album"."ownerId" = $1 - or "album_user"."usersId" = $2 + "album"."ownerId" = $3 + or "album_user"."usersId" = $4 ) order by "album_asset"."updateId" asc @@ -260,14 +347,14 @@ select "album_user"."role", "album_user"."updateId" from - "album_user" + "album_user" as "album_user" where - "albumsId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + "album_user"."updateId" < $1 + and "album_user"."updateId" <= $2 + and "album_user"."updateId" >= $3 + and "albumsId" = $4 order by - "updateId" asc + "album_user"."updateId" asc -- SyncRepository.albumUser.getDeletes select @@ -275,15 +362,17 @@ select "userId", "albumId" from - "album_user_audit" + "album_user_audit" as "album_user_audit" where - "albumId" in ( + "album_user_audit"."id" < $1 + and "album_user_audit"."id" > $2 + and "albumId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $3 union ( select @@ -291,12 +380,11 @@ where from "album_user" where - "album_user"."usersId" = $2 + "album_user"."usersId" = $4 ) ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "album_user_audit"."id" asc -- SyncRepository.albumUser.getUpserts select @@ -305,16 +393,17 @@ select "album_user"."role", "album_user"."updateId" from - "album_user" + "album_user" as "album_user" where - "album_user"."updatedAt" < now() - interval '1 millisecond' + "album_user"."updateId" < $1 + and "album_user"."updateId" > $2 and "album_user"."albumsId" in ( select "id" from "album" where - "ownerId" = $1 + "ownerId" = $3 union ( select @@ -322,7 +411,7 @@ where from "album_user" as "albumUsers" where - "albumUsers"."usersId" = $2 + "albumUsers"."usersId" = $4 ) ) order by @@ -333,12 +422,13 @@ select "id", "assetId" from - "asset_audit" + "asset_audit" as "asset_audit" where - "ownerId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "asset_audit"."id" < $1 + and "asset_audit"."id" > $2 + and "ownerId" = $3 order by - "id" asc + "asset_audit"."id" asc -- SyncRepository.asset.getUpserts select @@ -357,14 +447,16 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.assetExif.getUpserts select @@ -395,30 +487,32 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" where - "assetId" in ( + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "assetId" in ( select "id" from "asset" where - "ownerId" = $1 + "ownerId" = $3 ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "asset_exif"."updateId" asc -- SyncRepository.assetFace.getDeletes select "asset_face_audit"."id", "assetFaceId" from - "asset_face_audit" + "asset_face_audit" as "asset_face_audit" left join "asset" on "asset"."id" = "asset_face_audit"."assetId" where - "asset"."ownerId" = $1 - and "asset_face_audit"."deletedAt" < now() - interval '1 millisecond' + "asset_face_audit"."id" < $1 + and "asset_face_audit"."id" > $2 + and "asset"."ownerId" = $3 order by "asset_face_audit"."id" asc @@ -436,25 +530,83 @@ select "sourceType", "asset_face"."updateId" from - "asset_face" + "asset_face" as "asset_face" left join "asset" on "asset"."id" = "asset_face"."assetId" where - "asset_face"."updatedAt" < now() - interval '1 millisecond' - and "asset"."ownerId" = $1 + "asset_face"."updateId" < $1 + and "asset_face"."updateId" > $2 + and "asset"."ownerId" = $3 order by "asset_face"."updateId" asc +-- SyncRepository.assetMetadata.getDeletes +select + "asset_metadata_audit"."id", + "assetId", + "key" +from + "asset_metadata_audit" as "asset_metadata_audit" + left join "asset" on "asset"."id" = "asset_metadata_audit"."assetId" +where + "asset_metadata_audit"."id" < $1 + and "asset_metadata_audit"."id" > $2 + and "asset"."ownerId" = $3 +order by + "asset_metadata_audit"."id" asc + +-- SyncRepository.assetMetadata.getUpserts +select + "assetId", + "key", + "value", + "asset_metadata"."updateId" +from + "asset_metadata" as "asset_metadata" + inner join "asset" on "asset"."id" = "asset_metadata"."assetId" +where + "asset_metadata"."updateId" < $1 + and "asset_metadata"."updateId" > $2 + and "asset"."ownerId" = $3 +order by + "asset_metadata"."updateId" asc + +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "profileImagePath", + "profileChangedAt", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes" +from + "user" as "user" +where + "user"."updateId" < $1 + and "user"."updateId" > $2 + and "id" = $3 +order by + "user"."updateId" asc + -- SyncRepository.memory.getDeletes select "id", "memoryId" from - "memory_audit" + "memory_audit" as "memory_audit" where - "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "memory_audit"."id" < $1 + and "memory_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "memory_audit"."id" asc -- SyncRepository.memory.getUpserts select @@ -472,12 +624,13 @@ select "hideAt", "updateId" from - "memory" + "memory" as "memory" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + "memory"."updateId" < $1 + and "memory"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "memory"."updateId" asc -- SyncRepository.memoryToAsset.getDeletes select @@ -485,19 +638,20 @@ select "memoryId", "assetId" from - "memory_asset_audit" + "memory_asset_audit" as "memory_asset_audit" where - "memoryId" in ( + "memory_asset_audit"."id" < $1 + and "memory_asset_audit"."id" > $2 + and "memoryId" in ( select "id" from "memory" where - "ownerId" = $1 + "ownerId" = $3 ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "memory_asset_audit"."id" asc -- SyncRepository.memoryToAsset.getUpserts select @@ -505,19 +659,20 @@ select "assetsId" as "assetId", "updateId" from - "memory_asset" + "memory_asset" as "memory_asset" where - "memoriesId" in ( + "memory_asset"."updateId" < $1 + and "memory_asset"."updateId" > $2 + and "memoriesId" in ( select "id" from "memory" where - "ownerId" = $1 + "ownerId" = $3 ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "memory_asset"."updateId" asc -- SyncRepository.partner.getCreatedAfter select @@ -528,7 +683,7 @@ from where "sharedWithId" = $1 and "createId" >= $2 - and "createdAt" < now() - interval '1 millisecond' + and "createId" < $3 order by "partner"."createId" asc @@ -538,15 +693,16 @@ select "sharedById", "sharedWithId" from - "partner_audit" + "partner_audit" as "partner_audit" where - ( - "sharedById" = $1 - or "sharedWithId" = $2 + "partner_audit"."id" < $1 + and "partner_audit"."id" > $2 + and ( + "sharedById" = $3 + or "sharedWithId" = $4 ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "partner_audit"."id" asc -- SyncRepository.partner.getUpserts select @@ -555,15 +711,16 @@ select "inTimeline", "updateId" from - "partner" + "partner" as "partner" where - ( - "sharedById" = $1 - or "sharedWithId" = $2 + "partner"."updateId" < $1 + and "partner"."updateId" > $2 + and ( + "sharedById" = $3 + or "sharedWithId" = $4 ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "partner"."updateId" asc -- SyncRepository.partnerAsset.getBackfill select @@ -582,35 +739,37 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + "asset"."updateId" < $1 + and "asset"."updateId" <= $2 + and "asset"."updateId" >= $3 + and "ownerId" = $4 order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.partnerAsset.getDeletes select "id", "assetId" from - "asset_audit" + "asset_audit" as "asset_audit" where - "ownerId" in ( + "asset_audit"."id" < $1 + and "asset_audit"."id" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "asset_audit"."id" asc -- SyncRepository.partnerAsset.getUpserts select @@ -629,21 +788,23 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from - "asset" + "asset" as "asset" where - "ownerId" in ( + "asset"."updateId" < $1 + and "asset"."updateId" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "asset"."updateId" asc -- SyncRepository.partnerAssetExif.getBackfill select @@ -674,13 +835,13 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" inner join "asset" on "asset"."id" = "asset_exif"."assetId" where - "asset"."ownerId" = $1 - and "asset_exif"."updatedAt" < now() - interval '1 millisecond' + "asset_exif"."updateId" < $1 and "asset_exif"."updateId" <= $2 and "asset_exif"."updateId" >= $3 + and "asset"."ownerId" = $4 order by "asset_exif"."updateId" asc @@ -713,9 +874,11 @@ select "asset_exif"."fps", "asset_exif"."updateId" from - "asset_exif" + "asset_exif" as "asset_exif" where - "assetId" in ( + "asset_exif"."updateId" < $1 + and "asset_exif"."updateId" > $2 + and "assetId" in ( select "id" from @@ -727,31 +890,31 @@ where from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "asset_exif"."updateId" asc -- SyncRepository.partnerStack.getDeletes select "id", "stackId" from - "stack_audit" + "stack_audit" as "stack_audit" where - "userId" in ( + "stack_audit"."id" < $1 + and "stack_audit"."id" > $2 + and "userId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "deletedAt" < now() - interval '1 millisecond' order by - "id" asc + "stack_audit"."id" asc -- SyncRepository.partnerStack.getBackfill select @@ -762,14 +925,14 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' - and "updateId" <= $2 - and "updateId" >= $3 + "stack"."updateId" < $1 + and "stack"."updateId" <= $2 + and "stack"."updateId" >= $3 + and "ownerId" = $4 order by - "updateId" asc + "stack"."updateId" asc -- SyncRepository.partnerStack.getUpserts select @@ -780,33 +943,35 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" in ( + "stack"."updateId" < $1 + and "stack"."updateId" > $2 + and "ownerId" in ( select "sharedById" from "partner" where - "sharedWithId" = $1 + "sharedWithId" = $3 ) - and "updatedAt" < now() - interval '1 millisecond' order by - "updateId" asc + "stack"."updateId" asc --- SyncRepository.people.getDeletes +-- SyncRepository.person.getDeletes select "id", "personId" from - "person_audit" + "person_audit" as "person_audit" where - "ownerId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "person_audit"."id" < $1 + and "person_audit"."id" > $2 + and "ownerId" = $3 order by - "id" asc + "person_audit"."id" asc --- SyncRepository.people.getUpserts +-- SyncRepository.person.getUpserts select "id", "createdAt", @@ -820,24 +985,26 @@ select "updateId", "faceAssetId" from - "person" + "person" as "person" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + "person"."updateId" < $1 + and "person"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "person"."updateId" asc -- SyncRepository.stack.getDeletes select "id", "stackId" from - "stack_audit" + "stack_audit" as "stack_audit" where - "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "stack_audit"."id" < $1 + and "stack_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "stack_audit"."id" asc -- SyncRepository.stack.getUpserts select @@ -848,37 +1015,43 @@ select "stack"."ownerId", "updateId" from - "stack" + "stack" as "stack" where - "ownerId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + "stack"."updateId" < $1 + and "stack"."updateId" > $2 + and "ownerId" = $3 order by - "updateId" asc + "stack"."updateId" asc -- SyncRepository.user.getDeletes select "id", "userId" from - "user_audit" + "user_audit" as "user_audit" where - "deletedAt" < now() - interval '1 millisecond' + "user_audit"."id" < $1 + and "user_audit"."id" > $2 order by - "id" asc + "user_audit"."id" asc -- SyncRepository.user.getUpserts select "id", "name", "email", + "avatarColor", "deletedAt", - "updateId" + "updateId", + "profileImagePath", + "profileChangedAt" from - "user" + "user" as "user" where - "updatedAt" < now() - interval '1 millisecond' + "user"."updateId" < $1 + and "user"."updateId" > $2 order by - "updateId" asc + "user"."updateId" asc -- SyncRepository.userMetadata.getDeletes select @@ -886,12 +1059,13 @@ select "userId", "key" from - "user_metadata_audit" + "user_metadata_audit" as "user_metadata_audit" where - "userId" = $1 - and "deletedAt" < now() - interval '1 millisecond' + "user_metadata_audit"."id" < $1 + and "user_metadata_audit"."id" > $2 + and "userId" = $3 order by - "id" asc + "user_metadata_audit"."id" asc -- SyncRepository.userMetadata.getUpserts select @@ -900,9 +1074,10 @@ select "value", "updateId" from - "user_metadata" + "user_metadata" as "user_metadata" where - "userId" = $1 - and "updatedAt" < now() - interval '1 millisecond' + "user_metadata"."updateId" < $1 + and "user_metadata"."updateId" > $2 + and "userId" = $3 order by - "updateId" asc + "user_metadata"."updateId" asc diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index 6a02654781..c5a4f139a7 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -363,6 +363,14 @@ group by order by "user"."createdAt" asc +-- UserRepository.getCount +select + count(*) as "count" +from + "user" +where + "user"."deletedAt" is null + -- UserRepository.updateUsage update "user" set diff --git a/server/src/queries/view.repository.sql b/server/src/queries/view.repository.sql index 81f5ca20b8..31da10123f 100644 --- a/server/src/queries/view.repository.sql +++ b/server/src/queries/view.repository.sql @@ -12,6 +12,8 @@ where and "fileCreatedAt" is not null and "fileModifiedAt" is not null and "localDateTime" is not null +order by + "directoryPath" asc -- ViewRepository.getAssetsByOriginalPath select diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index 5cceb6dbe0..ca12ff040b 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -136,6 +136,7 @@ class AssetAccess { } return this.db + .with('target', (qb) => qb.selectNoFrom(sql`array[${sql.join([...assetIds])}]::uuid[]`.as('ids'))) .selectFrom('album') .innerJoin('album_asset as albumAssets', 'album.id', 'albumAssets.albumsId') .innerJoin('asset', (join) => @@ -143,11 +144,13 @@ class AssetAccess { ) .leftJoin('album_user as albumUsers', 'albumUsers.albumsId', 'album.id') .leftJoin('user', (join) => join.onRef('user.id', '=', 'albumUsers.usersId').on('user.deletedAt', 'is', null)) + .crossJoin('target') .select(['asset.id', 'asset.livePhotoVideoId']) - .where( - sql`array["asset"."id", "asset"."livePhotoVideoId"]`, - '&&', - sql`array[${sql.join([...assetIds])}]::uuid[] `, + .where((eb) => + eb.or([ + eb('asset.id', '=', sql`any(target.ids)`), + eb('asset.livePhotoVideoId', '=', sql`any(target.ids)`), + ]), ) .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('user.id', '=', userId)])) .where('album.deletedAt', 'is', null) diff --git a/server/src/repositories/album.repository.ts b/server/src/repositories/album.repository.ts index f077c36c41..b023068f16 100644 --- a/server/src/repositories/album.repository.ts +++ b/server/src/repositories/album.repository.ts @@ -321,6 +321,14 @@ export class AlbumRepository { .execute(); } + @Chunked({ chunkSize: 30_000 }) + async addAssetIdsToAlbums(values: { albumsId: string; assetsId: string }[]): Promise { + if (values.length === 0) { + return; + } + await this.db.insertInto('album_asset').values(values).execute(); + } + /** * Makes sure all thumbnails for albums are updated by: * - Removing thumbnails from albums without assets diff --git a/server/src/repositories/asset-job.repository.ts b/server/src/repositories/asset-job.repository.ts index 0500bb867f..ca1291b852 100644 --- a/server/src/repositories/asset-job.repository.ts +++ b/server/src/repositories/asset-job.repository.ts @@ -39,10 +39,8 @@ export class AssetJobRepository { return this.db .selectFrom('asset') .where('asset.id', '=', asUuid(id)) - .select((eb) => [ - 'id', - 'sidecarPath', - 'originalPath', + .select(['id', 'sidecarPath', 'originalPath']) + .select((eb) => jsonArrayFrom( eb .selectFrom('tag') @@ -50,7 +48,17 @@ export class AssetJobRepository { .innerJoin('tag_asset', 'tag.id', 'tag_asset.tagsId') .whereRef('asset.id', '=', 'tag_asset.assetsId'), ).as('tags'), - ]) + ) + .limit(1) + .executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.UUID] }) + getForSidecarCheckJob(id: string) { + return this.db + .selectFrom('asset') + .where('asset.id', '=', asUuid(id)) + .select(['id', 'sidecarPath', 'originalPath']) .limit(1) .executeTakeFirst(); } @@ -334,9 +342,9 @@ export class AssetJobRepository { @GenerateSql({ params: [], stream: true }) streamForDetectFacesJob(force?: boolean) { return this.assetsWithPreviews() - .$if(!force, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) + .$if(force === false, (qb) => qb.where('job_status.facesRecognizedAt', 'is', null)) .select(['asset.id']) - .orderBy('asset.createdAt', 'desc') + .orderBy('asset.fileCreatedAt', 'desc') .stream(); } diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index edbafaa22d..5c3bd8996c 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -1,16 +1,16 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely, NotNull, Selectable, UpdateResult, Updateable, sql } from 'kysely'; -import { jsonArrayFrom } from 'kysely/helpers/postgres'; +import { Insertable, Kysely, NotNull, Selectable, sql, Updateable, UpdateResult } from 'kysely'; import { isEmpty, isUndefined, omitBy } from 'lodash'; import { InjectKysely } from 'nestjs-kysely'; import { Stack } from 'src/database'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; +import { AssetFileType, AssetMetadataKey, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; import { AssetTable } from 'src/schema/tables/asset.table'; +import { AssetMetadataItem } from 'src/types'; import { anyUuid, asUuid, @@ -60,6 +60,7 @@ interface AssetBuilderOptions { status?: AssetStatus; assetType?: AssetType; visibility?: AssetVisibility; + withCoordinates?: boolean; } export interface TimeBucketOptions extends AssetBuilderOptions { @@ -170,6 +171,21 @@ export class AssetRepository { await this.db.updateTable('asset_exif').set(options).where('assetId', 'in', ids).execute(); } + @GenerateSql({ params: [[DummyValue.UUID], DummyValue.NUMBER, DummyValue.STRING] }) + @Chunked() + async updateDateTimeOriginal( + ids: string[], + delta?: number, + timeZone?: string, + ): Promise<{ assetId: string; dateTimeOriginal: Date | null; timeZone: string | null }[]> { + return await this.db + .updateTable('asset_exif') + .set({ dateTimeOriginal: sql`"dateTimeOriginal" + ${(delta ?? 0) + ' minute'}::interval`, timeZone }) + .where('assetId', 'in', ids) + .returning(['assetId', 'dateTimeOriginal', 'timeZone']) + .execute(); + } + async upsertJobStatus(...jobStatus: Insertable[]): Promise { if (jobStatus.length === 0) { return; @@ -196,6 +212,43 @@ export class AssetRepository { .execute(); } + @GenerateSql({ params: [DummyValue.UUID] }) + getMetadata(assetId: string) { + return this.db + .selectFrom('asset_metadata') + .select(['key', 'value', 'updatedAt']) + .where('assetId', '=', assetId) + .execute(); + } + + upsertMetadata(id: string, items: AssetMetadataItem[]) { + return this.db + .insertInto('asset_metadata') + .values(items.map((item) => ({ assetId: id, ...item }))) + .onConflict((oc) => + oc + .columns(['assetId', 'key']) + .doUpdateSet((eb) => ({ key: eb.ref('excluded.key'), value: eb.ref('excluded.value') })), + ) + .returning(['key', 'value', 'updatedAt']) + .execute(); + } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) + getMetadataByKey(assetId: string, key: AssetMetadataKey) { + return this.db + .selectFrom('asset_metadata') + .select(['key', 'value', 'updatedAt']) + .where('assetId', '=', assetId) + .where('key', '=', key) + .executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) + async deleteMetadataByKey(id: string, key: AssetMetadataKey) { + await this.db.deleteFrom('asset_metadata').where('assetId', '=', id).where('key', '=', key).execute(); + } + create(asset: Insertable) { return this.db.insertInto('asset').values(asset).returningAll().executeTakeFirstOrThrow(); } @@ -338,19 +391,7 @@ export class AssetRepository { @GenerateSql() getFileSamples() { - return this.db - .selectFrom('asset') - .select((eb) => [ - 'asset.id', - 'asset.originalPath', - 'asset.sidecarPath', - 'asset.encodedVideoPath', - jsonArrayFrom(eb.selectFrom('asset_file').select('path').whereRef('asset.id', '=', 'asset_file.assetId')).as( - 'files', - ), - ]) - .limit(sql.lit(3)) - .execute(); + return this.db.selectFrom('asset_file').select(['assetId', 'path']).limit(sql.lit(3)).execute(); } @GenerateSql({ params: [DummyValue.UUID] }) @@ -564,7 +605,7 @@ export class AssetRepository { sql`asset.type = 'IMAGE'`.as('isImage'), sql`asset."deletedAt" is not null`.as('isTrashed'), 'asset.livePhotoVideoId', - sql`extract(epoch from (asset."localDateTime" - asset."fileCreatedAt" at time zone 'UTC'))::real / 3600`.as( + sql`extract(epoch from (asset."localDateTime" AT TIME ZONE 'UTC' - asset."fileCreatedAt" at time zone 'UTC'))::real / 3600`.as( 'localOffsetHours', ), 'asset.ownerId', @@ -588,6 +629,7 @@ export class AssetRepository { ) .as('ratio'), ]) + .$if(!!options.withCoordinates, (qb) => qb.select(['asset_exif.latitude', 'asset_exif.longitude'])) .where('asset.deletedAt', options.isTrashed ? 'is not' : 'is', null) .$if(options.visibility == undefined, withDefaultVisibility) .$if(!!options.visibility, (qb) => qb.where('asset.visibility', '=', options.visibility!)) @@ -661,6 +703,12 @@ export class AssetRepository { eb.fn.coalesce(eb.fn('array_agg', ['status']), sql.lit('{}')).as('status'), eb.fn.coalesce(eb.fn('array_agg', ['thumbhash']), sql.lit('{}')).as('thumbhash'), ]) + .$if(!!options.withCoordinates, (qb) => + qb.select((eb) => [ + eb.fn.coalesce(eb.fn('array_agg', ['latitude']), sql.lit('{}')).as('latitude'), + eb.fn.coalesce(eb.fn('array_agg', ['longitude']), sql.lit('{}')).as('longitude'), + ]), + ) .$if(!!options.withStacked, (qb) => qb.select((eb) => eb.fn.coalesce(eb.fn('json_agg', ['stack']), sql.lit('[]')).as('stack')), ), diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index c9e96a1803..d5c279099c 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -97,6 +97,7 @@ export interface EnvData { storage: { ignoreMountCheckErrors: boolean; + mediaLocation?: string; }; workers: ImmichWorker[]; @@ -307,6 +308,7 @@ const getEnv = (): EnvData => { storage: { ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS, + mediaLocation: dto.IMMICH_MEDIA_LOCATION, }, telemetry: { diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 1f83630cfa..e5d88339c8 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, resolve } from 'node:path'; +import { join } from 'node:path'; import semver from 'semver'; import { EXTENSION_NAMES, @@ -23,10 +23,9 @@ import { DB } from 'src/schema'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; -import { DataSource, QueryRunner } from 'typeorm'; export let cachedVectorExtension: VectorExtension | undefined; -export async function getVectorExtension(runner: Kysely | QueryRunner): Promise { +export async function getVectorExtension(runner: Kysely): Promise { if (cachedVectorExtension) { return cachedVectorExtension; } @@ -36,14 +35,8 @@ export async function getVectorExtension(runner: Kysely | QueryRunner): Prom return cachedVectorExtension; } - let availableExtensions: { name: VectorExtension }[]; const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`; - if (runner instanceof Kysely) { - const { rows } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); - availableExtensions = rows; - } else { - availableExtensions = (await runner.query(query)) as { name: VectorExtension }[]; - } + const { rows: availableExtensions } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); const extensionNames = new Set(availableExtensions.map((row) => row.name)); cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext)); if (!cachedVectorExtension) { @@ -364,45 +357,9 @@ export class DatabaseRepository { return count; } - async runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise { - const { database } = this.configRepository.getEnv(); + async runMigrations(): Promise { + this.logger.debug('Running migrations'); - this.logger.log('Running migrations, this may take a while'); - - const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; - 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({ - 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(); - this.logger.debug('Finished running typeorm migrations'); - } - - this.logger.debug('Running kysely migrations'); const migrator = new Migrator({ db: this.db, migrationLockTableName: 'kysely_migrations_lock', @@ -429,14 +386,23 @@ export class DatabaseRepository { } if (error) { - this.logger.error(`Kysely migrations failed: ${error}`); + this.logger.error(`Migrations failed: ${error}`); throw error; } - this.logger.debug('Finished running kysely migrations'); + this.logger.debug('Finished running migrations'); } async migrateFilePaths(sourceFolder: string, targetFolder: string): Promise { + // remove trailing slashes + if (sourceFolder.endsWith('/')) { + sourceFolder = sourceFolder.slice(0, -1); + } + + if (targetFolder.endsWith('/')) { + targetFolder = targetFolder.slice(0, -1); + } + // escaping regex special characters with a backslash const sourceRegex = '^' + sourceFolder.replaceAll(/[-[\]{}()*+?.,\\^$|#\s]/g, String.raw`\$&`); const source = sql.raw(`'${sourceRegex}'`); diff --git a/server/src/repositories/duplicate.repository.ts b/server/src/repositories/duplicate.repository.ts index 140c42a643..95ccbea63d 100644 --- a/server/src/repositories/duplicate.repository.ts +++ b/server/src/repositories/duplicate.repository.ts @@ -34,7 +34,7 @@ export class DuplicateRepository { qb .selectFrom('asset') .$call(withDefaultVisibility) - .leftJoinLateral( + .innerJoinLateral( (qb) => qb .selectFrom('asset_exif') diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index c1b26d5dde..ec4c8a8f52 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -81,7 +81,7 @@ type EventMap = { StackDeleteAll: [{ stackIds: string[]; userId: string }]; // user events - UserSignup: [{ notify: boolean; id: string; tempPassword?: string }]; + UserSignup: [{ notify: boolean; id: string; password?: string }]; // websocket events WebsocketConnect: [{ userId: string }]; diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 1833168f3e..576ee6c810 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -85,8 +85,13 @@ export class LoggingRepository { this.logger = new MyConsoleLogger(cls, { context: LoggingRepository.name, color: !noColor }); } - static create() { - return new LoggingRepository(undefined, undefined); + static create(context?: string) { + const logger = new LoggingRepository(undefined, undefined); + if (context) { + logger.setContext(context); + } + + return logger; } setAppName(name: string): void { @@ -137,6 +142,10 @@ export class LoggingRepository { this.handleMessage(LogLevel.Fatal, message, details); } + deprecate(message: string) { + this.warn(`[Deprecated] ${message}`); + } + private handleFunction(level: LogLevel, message: LogFunction, details: LogDetails[]) { if (this.logger.isLevelEnabled(level)) { this.handleMessage(level, message(), details); diff --git a/server/src/repositories/machine-learning.repository.ts b/server/src/repositories/machine-learning.repository.ts index a52bc58bc3..d148dc782b 100644 --- a/server/src/repositories/machine-learning.repository.ts +++ b/server/src/repositories/machine-learning.repository.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; +import { Duration } from 'luxon'; import { readFile } from 'node:fs/promises'; -import { MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME, MACHINE_LEARNING_PING_TIMEOUT } from 'src/constants'; +import { MachineLearningConfig } from 'src/config'; import { CLIPConfig } from 'src/dtos/model-config.dto'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -57,82 +58,100 @@ export type TextEncodingOptions = ModelOptions & { language?: string }; @Injectable() export class MachineLearningRepository { - // Note that deleted URL's are not removed from this map (ie: they're leaked) - // Cleaning them up is low priority since there should be very few over a - // typical server uptime cycle - private urlAvailability: { - [url: string]: - | { - active: boolean; - lastChecked: number; - } - | undefined; - }; + private healthyMap: Record = {}; + private interval?: ReturnType; + private _config?: MachineLearningConfig; + + private get config(): MachineLearningConfig { + if (!this._config) { + throw new Error('Machine learning repository not been setup'); + } + + return this._config; + } constructor(private logger: LoggingRepository) { this.logger.setContext(MachineLearningRepository.name); - this.urlAvailability = {}; } - private setUrlAvailability(url: string, active: boolean) { - const current = this.urlAvailability[url]; - if (current?.active !== active) { - this.logger.verbose(`Setting ${url} ML server to ${active ? 'active' : 'inactive'}.`); + setup(config: MachineLearningConfig) { + this._config = config; + this.teardown(); + + // delete old servers + for (const url of Object.keys(this.healthyMap)) { + if (!config.urls.includes(url)) { + delete this.healthyMap[url]; + } } - this.urlAvailability[url] = { - active, - lastChecked: Date.now(), - }; + + if (!config.availabilityChecks.enabled) { + return; + } + + this.tick(); + this.interval = setInterval( + () => this.tick(), + Duration.fromObject({ milliseconds: config.availabilityChecks.interval }).as('milliseconds'), + ); } - private async checkAvailability(url: string) { - let active = false; + teardown() { + if (this.interval) { + clearInterval(this.interval); + } + } + + private tick() { + for (const url of this.config.urls) { + void this.check(url); + } + } + + private async check(url: string) { + let healthy = false; try { const response = await fetch(new URL('/ping', url), { - signal: AbortSignal.timeout(MACHINE_LEARNING_PING_TIMEOUT), + signal: AbortSignal.timeout(this.config.availabilityChecks.timeout), }); - active = response.ok; + if (response.ok) { + healthy = true; + } } catch { // nothing to do here } - this.setUrlAvailability(url, active); - return active; + + this.setHealthy(url, healthy); } - private async shouldSkipUrl(url: string) { - const availability = this.urlAvailability[url]; - if (availability === undefined) { - // If this is a new endpoint, then check inline and skip if it fails - if (!(await this.checkAvailability(url))) { - return true; - } - return false; + private setHealthy(url: string, healthy: boolean) { + if (this.healthyMap[url] !== healthy) { + this.logger.log(`Machine learning server became ${healthy ? 'healthy' : 'unhealthy'} (${url}).`); } - if (!availability.active && Date.now() - availability.lastChecked < MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME) { - // If this is an old inactive endpoint that hasn't been checked in a - // while then check but don't wait for the result, just skip it - // This avoids delays on every search whilst allowing higher priority - // ML servers to recover over time. - void this.checkAvailability(url); + + this.healthyMap[url] = healthy; + } + + private isHealthy(url: string) { + if (!this.config.availabilityChecks.enabled) { return true; } - return false; + + return this.healthyMap[url]; } - private async predict(urls: string[], payload: ModelPayload, config: MachineLearningRequest): Promise { + private async predict(payload: ModelPayload, config: MachineLearningRequest): Promise { const formData = await this.getFormData(payload, config); - let urlCounter = 0; - for (const url of urls) { - urlCounter++; - const isLast = urlCounter >= urls.length; - if (!isLast && (await this.shouldSkipUrl(url))) { - continue; - } + for (const url of [ + // try healthy servers first + ...this.config.urls.filter((url) => this.isHealthy(url)), + ...this.config.urls.filter((url) => !this.isHealthy(url)), + ]) { try { const response = await fetch(new URL('/predict', url), { method: 'POST', body: formData }); if (response.ok) { - this.setUrlAvailability(url, true); + this.setHealthy(url, true); return response.json(); } @@ -144,20 +163,21 @@ export class MachineLearningRepository { `Machine learning request to "${url}" failed: ${error instanceof Error ? error.message : error}`, ); } - this.setUrlAvailability(url, false); + + this.setHealthy(url, false); } throw new Error(`Machine learning request '${JSON.stringify(config)}' failed for all URLs`); } - async detectFaces(urls: string[], imagePath: string, { modelName, minScore }: FaceDetectionOptions) { + async detectFaces(imagePath: string, { modelName, minScore }: FaceDetectionOptions) { const request = { [ModelTask.FACIAL_RECOGNITION]: { [ModelType.DETECTION]: { modelName, options: { minScore } }, [ModelType.RECOGNITION]: { modelName }, }, }; - const response = await this.predict(urls, { imagePath }, request); + const response = await this.predict({ imagePath }, request); return { imageHeight: response.imageHeight, imageWidth: response.imageWidth, @@ -165,15 +185,15 @@ export class MachineLearningRepository { }; } - async encodeImage(urls: string[], imagePath: string, { modelName }: CLIPConfig) { + async encodeImage(imagePath: string, { modelName }: CLIPConfig) { const request = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: { modelName } } }; - const response = await this.predict(urls, { imagePath }, request); + const response = await this.predict({ imagePath }, request); return response[ModelTask.SEARCH]; } - async encodeText(urls: string[], text: string, { language, modelName }: TextEncodingOptions) { + async encodeText(text: string, { language, modelName }: TextEncodingOptions) { const request = { [ModelTask.SEARCH]: { [ModelType.TEXTUAL]: { modelName, options: { language } } } }; - const response = await this.predict(urls, { text }, request); + const response = await this.predict({ text }, request); return response[ModelTask.SEARCH]; } @@ -182,7 +202,8 @@ export class MachineLearningRepository { formData.append('entries', JSON.stringify(config)); if ('imagePath' in payload) { - formData.append('image', new Blob([await readFile(payload.imagePath)])); + const fileBuffer = await readFile(payload.imagePath); + formData.append('image', new Blob([new Uint8Array(fileBuffer)])); } else if ('text' in payload) { formData.append('text', payload.text); } else { diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index d1f60791c3..7f6e2a967a 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -5,7 +5,7 @@ import { InjectKysely } from 'nestjs-kysely'; import { createReadStream, existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import readLine from 'node:readline'; -import { citiesFile } from 'src/constants'; +import { citiesFile, reverseGeocodeMaxDistance } from 'src/constants'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetVisibility, SystemMetadataKey } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; @@ -145,7 +145,7 @@ export class MapRepository { .selectFrom('geodata_places') .selectAll() .where( - sql`earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), 25000)`, + sql`earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), ${reverseGeocodeMaxDistance})`, '@>', sql`ll_to_earth_public(latitude, longitude)`, ) @@ -165,8 +165,8 @@ export class MapRepository { return { country, state, city }; } - this.logger.warn( - `Response from database for reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`, + this.logger.log( + `Empty response from database for city reverse geocoding lat: ${point.latitude}, lon: ${point.longitude}. Likely cause: no nearby large populated place (500+ within ${reverseGeocodeMaxDistance / 1000}km). Falling back to country boundaries.`, ); const ne_response = await this.db @@ -177,8 +177,8 @@ export class MapRepository { .executeTakeFirst(); if (!ne_response) { - this.logger.warn( - `Response from database for natural earth reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`, + this.logger.log( + `Empty response from database for natural earth country reverse geocoding lat: ${point.latitude}, lon: ${point.longitude}`, ); return { country: null, state: null, city: null }; diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 6266acf0ed..d98e018efb 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -57,28 +57,28 @@ export class MediaRepository { 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); + this.logger.debug(`Could not extract JpgFromRaw2 buffer from image, trying JPEG from RAW next: ${error}`); } 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); + this.logger.debug(`Could not extract JPEG buffer from image, trying PreviewJXL next: ${error}`); } 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); + this.logger.debug(`Could not extract PreviewJXL buffer from image, trying PreviewImage next: ${error}`); } 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); + this.logger.debug(`Could not extract preview buffer from image: ${error}`); return null; } } @@ -141,6 +141,7 @@ export class MediaRepository { failOn: options.processInvalidImages ? 'none' : 'error', limitInputPixels: false, raw: options.raw, + unlimited: true, }) .pipelineColorspace(options.colorspace === Colorspace.Srgb ? 'srgb' : 'rgb16') .withIccProfile(options.colorspace); @@ -202,6 +203,9 @@ export class MediaRepository { isHDR: stream.color_transfer === 'smpte2084' || stream.color_transfer === 'arib-std-b67', bitrate: this.parseInt(stream.bit_rate), pixelFormat: stream.pix_fmt || 'yuv420p', + colorPrimaries: stream.color_primaries, + colorSpace: stream.color_space, + colorTransfer: stream.color_transfer, })), audioStreams: results.streams .filter((stream) => stream.codec_type === 'audio') diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index bf3a96f21f..e2360156e4 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -103,7 +103,7 @@ export class MetadataRepository { readTags(path: string): Promise { return this.exiftool.read(path).catch((error) => { - this.logger.warn(`Error reading exif data (${path}): ${error}`, error?.stack); + this.logger.warn(`Error reading exif data (${path}): ${error}\n${error?.stack}`); return {}; }) as Promise; } diff --git a/server/src/repositories/oauth.repository.ts b/server/src/repositories/oauth.repository.ts index 9a436e4b9a..58b1144647 100644 --- a/server/src/repositories/oauth.repository.ts +++ b/server/src/repositories/oauth.repository.ts @@ -29,6 +29,7 @@ export class OAuthRepository { ); const client = await this.getClient(config); state ??= randomState(); + let codeVerifier: string | null; if (codeChallenge) { codeVerifier = null; @@ -36,13 +37,20 @@ export class OAuthRepository { codeVerifier = randomPKCECodeVerifier(); codeChallenge = await calculatePKCECodeChallenge(codeVerifier); } - const url = buildAuthorizationUrl(client, { + + const params: Record = { redirect_uri: redirectUrl, scope: config.scope, state, - code_challenge: client.serverMetadata().supportsPKCE() ? codeChallenge : '', - code_challenge_method: client.serverMetadata().supportsPKCE() ? 'S256' : '', - }).toString(); + }; + + if (client.serverMetadata().supportsPKCE()) { + params.code_challenge = codeChallenge; + params.code_challenge_method = 'S256'; + } + + const url = buildAuthorizationUrl(client, params).toString(); + return { url, state, codeVerifier }; } diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index f653bb8179..725304938c 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ExpressionBuilder, Insertable, Kysely, Selectable, sql, Updateable } from 'kysely'; +import { ExpressionBuilder, Insertable, Kysely, NotNull, Selectable, sql, Updateable } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { InjectKysely } from 'nestjs-kysely'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; @@ -68,12 +68,6 @@ const withPerson = (eb: ExpressionBuilder) => { ).as('person'); }; -const withAsset = (eb: ExpressionBuilder) => { - return jsonObjectFrom(eb.selectFrom('asset').selectAll('asset').whereRef('asset.id', '=', 'asset_face.assetId')).as( - 'asset', - ); -}; - const withFaceSearch = (eb: ExpressionBuilder) => { return jsonObjectFrom( eb.selectFrom('face_search').selectAll('face_search').whereRef('face_search.faceId', '=', 'asset_face.id'), @@ -481,7 +475,12 @@ export class PersonRepository { return this.db .selectFrom('asset_face') .selectAll('asset_face') - .select(withAsset) + .select((eb) => + jsonObjectFrom(eb.selectFrom('asset').selectAll('asset').whereRef('asset.id', '=', 'asset_face.assetId')).as( + 'asset', + ), + ) + .$narrowType<{ asset: NotNull }>() .select(withPerson) .where('asset_face.assetId', 'in', assetIds) .where('asset_face.personId', 'in', personIds) diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 61e0cc1e29..88de2fb06f 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -8,7 +8,7 @@ import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; -import { anyUuid, searchAssetBuilder } from 'src/utils/database'; +import { anyUuid, searchAssetBuilder, withExif } from 'src/utils/database'; import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -129,6 +129,8 @@ export type SmartSearchOptions = SearchDateOptions & SearchPeopleOptions & SearchTagOptions; +export type LargeAssetSearchOptions = AssetSearchOptions & { minFileSize?: number }; + export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; numResults: number; @@ -237,6 +239,29 @@ export class SearchRepository { return rows; } + @GenerateSql({ + params: [ + 100, + { + takenAfter: DummyValue.DATE, + lensModel: DummyValue.STRING, + withStacked: true, + isFavorite: true, + userIds: [DummyValue.UUID], + }, + ], + }) + searchLargeAssets(size: number, options: LargeAssetSearchOptions) { + const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection; + return searchAssetBuilder(this.db, options) + .selectAll('asset') + .$call(withExif) + .where('asset_exif.fileSizeInByte', '>', options.minFileSize || 0) + .orderBy('asset_exif.fileSizeInByte', orderDirection) + .limit(size) + .execute(); + } + @GenerateSql({ params: [ { page: 1, size: 200 }, @@ -268,6 +293,13 @@ export class SearchRepository { }); } + @GenerateSql({ + params: [DummyValue.UUID], + }) + async getEmbedding(assetId: string) { + return this.db.selectFrom('smart_search').selectAll().where('assetId', '=', assetId).executeTakeFirst(); + } + @GenerateSql({ params: [ { diff --git a/server/src/repositories/session.repository.ts b/server/src/repositories/session.repository.ts index edf999e265..cdc0ab12db 100644 --- a/server/src/repositories/session.repository.ts +++ b/server/src/repositories/session.repository.ts @@ -37,6 +37,16 @@ export class SessionRepository { .executeTakeFirst(); } + @GenerateSql({ params: [DummyValue.UUID] }) + async isPendingSyncReset(id: string) { + const result = await this.db + .selectFrom('session') + .select(['isPendingSyncReset']) + .where('id', '=', id) + .executeTakeFirst(); + return result?.isPendingSyncReset ?? false; + } + @GenerateSql({ params: [DummyValue.STRING] }) getByToken(token: string) { return this.db diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index d5fb3be47d..cdade25f76 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -86,7 +86,16 @@ export class SharedLinkRepository { (join) => join.onTrue(), ) .select((eb) => - eb.fn.coalesce(eb.fn.jsonAgg('assets').filterWhere('assets.id', 'is not', null), sql`'[]'`).as('assets'), + eb.fn + .coalesce( + eb.fn + .jsonAgg('assets') + .orderBy('assets.fileCreatedAt', 'asc') + .filterWhere('assets.id', 'is not', null), + + sql`'[]'`, + ) + .as('assets'), ) .select((eb) => eb.fn.toJson('owner').as('owner')) .groupBy(['album.id', sql`"owner".*`]) @@ -173,10 +182,18 @@ export class SharedLinkRepository { } @GenerateSql({ params: [DummyValue.BUFFER] }) - async getByKey(key: Buffer) { + getByKey(key: Buffer) { + return this.authBuilder().where('shared_link.key', '=', key).executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.BUFFER] }) + getBySlug(slug: string) { + return this.authBuilder().where('shared_link.slug', '=', slug).executeTakeFirst(); + } + + private authBuilder() { return this.db .selectFrom('shared_link') - .where('shared_link.key', '=', key) .leftJoin('album', 'album.id', 'shared_link.albumId') .where('album.deletedAt', 'is', null) .select((eb) => [ @@ -185,8 +202,7 @@ export class SharedLinkRepository { eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'), ).as('user'), ]) - .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])) - .executeTakeFirst(); + .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])); } async create(entity: Insertable & { assetIds?: string[] }) { diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index fe16c8b5eb..ace9468177 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -152,4 +152,14 @@ export class StackRepository { .where('id', '=', asUuid(id)) .executeTakeFirst(); } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) + getForAssetRemoval(assetId: string) { + return this.db + .selectFrom('asset') + .leftJoin('stack', 'stack.id', 'asset.stackId') + .select(['stackId as id', 'stack.primaryAssetId']) + .where('asset.id', '=', assetId) + .executeTakeFirst(); + } } diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 4d89b02a50..7d6b634845 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -162,6 +162,10 @@ export class StorageRepository { } } + existsSync(filepath: string) { + return existsSync(filepath); + } + async checkDiskUsage(folder: string): Promise { const stats = await fs.statfs(folder); return { diff --git a/server/src/repositories/sync-checkpoint.repository.ts b/server/src/repositories/sync-checkpoint.repository.ts index 65fd018136..9db56e1bfe 100644 --- a/server/src/repositories/sync-checkpoint.repository.ts +++ b/server/src/repositories/sync-checkpoint.repository.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Insertable, Kysely } from 'kysely'; +import { Insertable, Kysely, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { DummyValue, GenerateSql } from 'src/decorators'; import { SyncEntityType } from 'src/enum'; @@ -39,4 +39,13 @@ export class SyncCheckpointRepository { .$if(!!types, (qb) => qb.where('type', 'in', types!)) .execute(); } + + @GenerateSql() + getNow() { + return this.db + .selectNoFrom((eb) => [ + eb.fn('immich_uuid_v7', [sql.raw("now() - interval '1 millisecond'")]).as('nowId'), + ]) + .executeTakeFirstOrThrow(); + } } diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index dba52d25a0..d8be720f45 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -1,37 +1,48 @@ import { Injectable } from '@nestjs/common'; -import { Kysely, SelectQueryBuilder, sql } from 'kysely'; +import { Kysely, sql } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { columns } from 'src/database'; import { DummyValue, GenerateSql } from 'src/decorators'; import { DB } from 'src/schema'; import { SyncAck } from 'src/types'; -type AuditTables = - | 'user_audit' - | 'partner_audit' - | 'asset_audit' - | 'album_audit' - | 'album_user_audit' - | 'album_asset_audit' - | 'memory_audit' - | 'memory_asset_audit' - | 'stack_audit' - | 'person_audit' - | 'user_metadata_audit' - | 'asset_face_audit'; -type UpsertTables = - | 'user' - | 'partner' - | 'asset' - | 'asset_exif' - | 'album' - | 'album_user' - | 'memory' - | 'memory_asset' - | 'stack' - | 'person' - | 'user_metadata' - | 'asset_face'; +export type SyncBackfillOptions = { + nowId: string; + afterUpdateId?: string; + beforeUpdateId: string; +}; + +const dummyBackfillOptions = { + nowId: DummyValue.UUID, + beforeUpdateId: DummyValue.UUID, + afterUpdateId: DummyValue.UUID, +}; + +export type SyncCreatedAfterOptions = { + nowId: string; + userId: string; + afterCreateId?: string; +}; + +const dummyCreateAfterOptions = { + nowId: DummyValue.UUID, + userId: DummyValue.UUID, + afterCreateId: DummyValue.UUID, +}; + +export type SyncQueryOptions = { + nowId: string; + userId: string; + ack?: SyncAck; +}; + +const dummyQueryOptions = { + nowId: DummyValue.UUID, + userId: DummyValue.UUID, + ack: { + updateId: DummyValue.UUID, + }, +}; @Injectable() export class SyncRepository { @@ -43,13 +54,15 @@ export class SyncRepository { asset: AssetSync; assetExif: AssetExifSync; assetFace: AssetFaceSync; + assetMetadata: AssetMetadataSync; + authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; partnerAsset: PartnerAssetsSync; partnerAssetExif: PartnerAssetExifsSync; partnerStack: PartnerStackSync; - people: PersonSync; + person: PersonSync; stack: StackSync; user: UserSync; userMetadata: UserMetadataSync; @@ -63,13 +76,15 @@ export class SyncRepository { this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); this.assetFace = new AssetFaceSync(this.db); + this.assetMetadata = new AssetMetadataSync(this.db); + this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); this.partnerAsset = new PartnerAssetsSync(this.db); this.partnerAssetExif = new PartnerAssetExifsSync(this.db); this.partnerStack = new PartnerStackSync(this.db); - this.people = new PersonSync(this.db); + this.person = new PersonSync(this.db); this.stack = new StackSync(this.db); this.user = new UserSync(this.db); this.userMetadata = new UserMetadataSync(this.db); @@ -79,58 +94,80 @@ export class SyncRepository { class BaseSync { constructor(protected db: Kysely) {} - protected auditTableFilters(ack?: SyncAck) { - return , D>(qb: SelectQueryBuilder) => { - const builder = qb as SelectQueryBuilder; - return builder - .where('deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('id', '>', ack!.updateId)) - .orderBy('id', 'asc') as SelectQueryBuilder; - }; + protected backfillQuery(t: T, { nowId, beforeUpdateId, afterUpdateId }: SyncBackfillOptions) { + const { table, ref } = this.db.dynamic; + const updateIdRef = ref(`${t}.updateId`); + + return this.db + .selectFrom(table(t).as(t)) + .where(updateIdRef, '<', nowId) + .where(updateIdRef, '<=', beforeUpdateId) + .$if(!!afterUpdateId, (qb) => qb.where(updateIdRef, '>=', afterUpdateId!)) + .orderBy(updateIdRef, 'asc'); } - protected upsertTableFilters(ack?: SyncAck) { - return , D>(qb: SelectQueryBuilder) => { - const builder = qb as SelectQueryBuilder; - return builder - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('updateId', '>', ack!.updateId)) - .orderBy('updateId', 'asc') as SelectQueryBuilder; - }; + protected auditQuery(t: T, { nowId, ack }: SyncQueryOptions) { + const { table, ref } = this.db.dynamic; + const idRef = ref(`${t}.id`); + + return this.db + .selectFrom(table(t).as(t)) + .where(idRef, '<', nowId) + .$if(!!ack, (qb) => qb.where(idRef, '>', ack!.updateId)) + .orderBy(idRef, 'asc'); + } + + protected auditCleanup(t: T, days: number) { + const { table, ref } = this.db.dynamic; + + return this.db + .deleteFrom(table(t).as(t)) + .where(ref(`${t}.deletedAt`), '<', sql.raw(`now() - interval '${days} days'`)) + .execute(); + } + + protected upsertQuery(t: T, { nowId, ack }: SyncQueryOptions) { + const { table, ref } = this.db.dynamic; + const updateIdRef = ref(`${t}.updateId`); + + return this.db + .selectFrom(table(t).as(t)) + .where(updateIdRef, '<', nowId) + .$if(!!ack, (qb) => qb.where(updateIdRef, '>', ack!.updateId)) + .orderBy(updateIdRef, 'asc'); } } class AlbumSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - getCreatedAfter(userId: string, afterCreateId?: string) { + @GenerateSql({ params: [dummyCreateAfterOptions] }) + getCreatedAfter({ nowId, userId, afterCreateId }: SyncCreatedAfterOptions) { return this.db .selectFrom('album_user') .select(['albumsId as id', 'createId']) .where('usersId', '=', userId) .$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!)) - .where('createdAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('createId', '<', nowId) .orderBy('createId', 'asc') .execute(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('album_audit', options) .select(['id', 'albumId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('album_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album', options) .distinctOn(['album.id', 'album.updateId']) - .where('album.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('album.updateId', '>', ack!.updateId)) - .orderBy('album.updateId', 'asc') .leftJoin('album_user as album_users', 'album.id', 'album_users.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_users.usersId', '=', userId)])) .select([ @@ -150,31 +187,37 @@ class AlbumSync extends BaseSync { } class AlbumAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('asset') - .innerJoin('album_asset', 'album_asset.assetsId', 'asset.id') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) + .innerJoin('asset', 'asset.id', 'album_asset.assetsId') .select(columns.syncAsset) - .select('asset.updateId') + .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('asset.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset.updateId', '>=', afterUpdateId!)) - .orderBy('asset.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyQueryOptions, { updateId: DummyValue.UUID }], stream: true }) + getUpdates(options: SyncQueryOptions, albumToAssetAck: SyncAck) { + const userId = options.userId; + return this.upsertQuery('asset', options) .innerJoin('album_asset', 'album_asset.assetsId', 'asset.id') .select(columns.syncAsset) .select('asset.updateId') - .where('asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('asset.updateId', '>', ack!.updateId)) - .orderBy('asset.updateId', 'asc') + .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send updates for assets that the client already knows about + .innerJoin('album', 'album.id', 'album_asset.albumsId') + .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') + .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) + .stream(); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getCreates(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) + .select('album_asset.updateId') + .innerJoin('asset', 'asset.id', 'album_asset.assetsId') + .select(columns.syncAsset) .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) @@ -183,31 +226,37 @@ class AlbumAssetSync extends BaseSync { } class AlbumAssetExifSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('asset_exif') - .innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) + .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') .select(columns.syncAssetExif) - .select('asset_exif.updateId') + .select('album_asset.updateId') .where('album_asset.albumsId', '=', albumId) - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('asset_exif.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!)) - .orderBy('asset_exif.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions, { updateId: DummyValue.UUID }], stream: true }) + getUpdates(options: SyncQueryOptions, albumToAssetAck: SyncAck) { + const userId = options.userId; + return this.upsertQuery('asset_exif', options) .innerJoin('album_asset', 'album_asset.assetsId', 'asset_exif.assetId') .select(columns.syncAssetExif) .select('asset_exif.updateId') - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('asset_exif.updateId', '>', ack!.updateId)) - .orderBy('asset_exif.updateId', 'asc') + .where('album_asset.updateId', '<=', albumToAssetAck.updateId) // Ensure we only send exif updates for assets that the client already knows about + .innerJoin('album', 'album.id', 'album_asset.albumsId') + .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') + .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) + .stream(); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getCreates(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) + .select('album_asset.updateId') + .innerJoin('asset_exif', 'asset_exif.assetId', 'album_asset.assetsId') + .select(columns.syncAssetExif) .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) @@ -216,23 +265,18 @@ class AlbumAssetExifSync extends BaseSync { } class AlbumToAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_asset as album_assets') - .select(['album_assets.assetsId as assetId', 'album_assets.albumsId as albumId', 'album_assets.updateId']) - .where('album_assets.albumsId', '=', albumId) - .where('album_assets.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('album_assets.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('album_assets.updateId', '>=', afterUpdateId!)) - .orderBy('album_assets.updateId', 'asc') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_asset', options) + .select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId']) + .where('album_asset.albumsId', '=', albumId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album_asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('album_asset_audit', options) .select(['id', 'assetId', 'albumId']) .where((eb) => eb( @@ -252,18 +296,18 @@ class AlbumToAssetSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album_asset') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('album_asset_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_asset', options) .select(['album_asset.assetsId as assetId', 'album_asset.albumsId as albumId', 'album_asset.updateId']) - .where('album_asset.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('album_asset.updateId', '>', ack!.updateId)) - .orderBy('album_asset.updateId', 'asc') .innerJoin('album', 'album.id', 'album_asset.albumsId') .leftJoin('album_user', 'album_user.albumsId', 'album_asset.albumsId') .where((eb) => eb.or([eb('album.ownerId', '=', userId), eb('album_user.usersId', '=', userId)])) @@ -272,24 +316,19 @@ class AlbumToAssetSync extends BaseSync { } class AlbumUserSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(albumId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('album_user') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, albumId: string) { + return this.backfillQuery('album_user', options) .select(columns.syncAlbumUser) .select('album_user.updateId') .where('albumsId', '=', albumId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album_user_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('album_user_audit', options) .select(['id', 'userId', 'albumId']) .where((eb) => eb( @@ -309,19 +348,19 @@ class AlbumUserSync extends BaseSync { ), ), ) - .$call(this.auditTableFilters(ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('album_user') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('album_user_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('album_user', options) .select(columns.syncAlbumUser) .select('album_user.updateId') - .where('album_user.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('album_user.updateId', '>', ack!.updateId)) - .orderBy('album_user.updateId', 'asc') .where((eb) => eb( 'album_user.albumsId', @@ -345,43 +384,55 @@ class AlbumUserSync extends BaseSync { } class AssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_audit', options) .select(['id', 'assetId']) - .where('ownerId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('asset_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .where('ownerId', '=', options.userId) + .stream(); + } +} + +class AuthUserSync extends BaseSync { + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user', options) + .select(columns.syncUser) + .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) + .where('id', '=', options.userId) .stream(); } } class PersonSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('person_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('person_audit', options) .select(['id', 'personId']) - .where('ownerId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('person') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('person_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('person', options) .select([ 'id', 'createdAt', @@ -395,30 +446,28 @@ class PersonSync extends BaseSync { 'updateId', 'faceAssetId', ]) - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .where('ownerId', '=', options.userId) .stream(); } } class AssetFaceSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_face_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_face_audit', options) .select(['asset_face_audit.id', 'assetFaceId']) - .orderBy('asset_face_audit.id', 'asc') .leftJoin('asset', 'asset.id', 'asset_face_audit.assetId') - .where('asset.ownerId', '=', userId) - .where('asset_face_audit.deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('asset_face_audit.id', '>', ack!.updateId)) + .where('asset.ownerId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_face') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('asset_face_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_face', options) .select([ 'asset_face.id', 'assetId', @@ -432,43 +481,39 @@ class AssetFaceSync extends BaseSync { 'sourceType', 'asset_face.updateId', ]) - .where('asset_face.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .$if(!!ack, (qb) => qb.where('asset_face.updateId', '>', ack!.updateId)) - .orderBy('asset_face.updateId', 'asc') .leftJoin('asset', 'asset.id', 'asset_face.assetId') - .where('asset.ownerId', '=', userId) + .where('asset.ownerId', '=', options.userId) .stream(); } } class AssetExifSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') - .where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(ack)) + .where('assetId', 'in', (eb) => eb.selectFrom('asset').select('id').where('ownerId', '=', options.userId)) .stream(); } } class MemorySync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('memory_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('memory_audit', options) .select(['id', 'memoryId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('memory') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('memory_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('memory', options) .select([ 'id', 'createdAt', @@ -484,130 +529,116 @@ class MemorySync extends BaseSync { 'hideAt', ]) .select('updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .where('ownerId', '=', options.userId) .stream(); } } class MemoryToAssetSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('memory_asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('memory_asset_audit', options) .select(['id', 'memoryId', 'assetId']) - .where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.auditTableFilters(ack)) + .where('memoryId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', options.userId)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('memory_asset') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('memory_asset_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('memory_asset', options) .select(['memoriesId as memoryId', 'assetsId as assetId']) .select('updateId') - .where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', userId)) - .$call(this.upsertTableFilters(ack)) + .where('memoriesId', 'in', (eb) => eb.selectFrom('memory').select('id').where('ownerId', '=', options.userId)) .stream(); } } class PartnerSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) - getCreatedAfter(userId: string, afterCreateId?: string) { + @GenerateSql({ params: [dummyCreateAfterOptions] }) + getCreatedAfter({ nowId, userId, afterCreateId }: SyncCreatedAfterOptions) { return this.db .selectFrom('partner') .select(['sharedById', 'createId']) .where('sharedWithId', '=', userId) .$if(!!afterCreateId, (qb) => qb.where('createId', '>=', afterCreateId!)) - .where('createdAt', '<', sql.raw("now() - interval '1 millisecond'")) + .where('createId', '<', nowId) .orderBy('partner.createId', 'asc') .execute(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('partner_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + const userId = options.userId; + return this.auditQuery('partner_audit', options) .select(['id', 'sharedById', 'sharedWithId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.auditTableFilters(ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('partner') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('partner_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + const userId = options.userId; + return this.upsertQuery('partner', options) .select(['sharedById', 'sharedWithId', 'inTimeline', 'updateId']) .where((eb) => eb.or([eb('sharedById', '=', userId), eb('sharedWithId', '=', userId)])) - .$call(this.upsertTableFilters(ack)) .stream(); } } class PartnerAssetsSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', '=', partnerId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('asset_audit', options) .select(['id', 'assetId']) .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.auditTableFilters(ack)) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset', options) .select(columns.syncAsset) .select('asset.updateId') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.upsertTableFilters(ack)) .stream(); } } class PartnerAssetExifsSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') .innerJoin('asset', 'asset.id', 'asset_exif.assetId') .where('asset.ownerId', '=', partnerId) - .where('asset_exif.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('asset_exif.updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('asset_exif.updateId', '>=', afterUpdateId!)) - .orderBy('asset_exif.updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('asset_exif') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('asset_exif', options) .select(columns.syncAssetExif) .select('asset_exif.updateId') .where('assetId', 'in', (eb) => @@ -615,110 +646,126 @@ class PartnerAssetExifsSync extends BaseSync { .selectFrom('asset') .select('id') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ), ) - .$call(this.upsertTableFilters(ack)) .stream(); } } class StackSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('stack_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('stack_audit', options) .select(['id', 'stackId']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('stack') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('stack_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('stack', options) .select(columns.syncStack) .select('updateId') - .where('ownerId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .where('ownerId', '=', options.userId) .stream(); } } class PartnerStackSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('stack_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('stack_audit', options) .select(['id', 'stackId']) - .where('userId', 'in', (eb) => eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId)) - .$call(this.auditTableFilters(ack)) + .where('userId', 'in', (eb) => + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), + ) .stream(); } - @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true }) - getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) { - return this.db - .selectFrom('stack') + @GenerateSql({ params: [dummyBackfillOptions, DummyValue.UUID], stream: true }) + getBackfill(options: SyncBackfillOptions, partnerId: string) { + return this.backfillQuery('stack', options) .select(columns.syncStack) .select('updateId') .where('ownerId', '=', partnerId) - .where('updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) - .where('updateId', '<=', beforeUpdateId) - .$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!)) - .orderBy('updateId', 'asc') .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('stack') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('stack', options) .select(columns.syncStack) .select('updateId') .where('ownerId', 'in', (eb) => - eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', userId), + eb.selectFrom('partner').select(['sharedById']).where('sharedWithId', '=', options.userId), ) - .$call(this.upsertTableFilters(ack)) .stream(); } } class UserSync extends BaseSync { - @GenerateSql({ params: [], stream: true }) - getDeletes(ack?: SyncAck) { - return this.db.selectFrom('user_audit').select(['id', 'userId']).$call(this.auditTableFilters(ack)).stream(); + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('user_audit', options).select(['id', 'userId']).stream(); } - @GenerateSql({ params: [], stream: true }) - getUpserts(ack?: SyncAck) { - return this.db - .selectFrom('user') - .select(['id', 'name', 'email', 'deletedAt', 'updateId']) - .$call(this.upsertTableFilters(ack)) - .stream(); + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('user_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user', options).select(columns.syncUser).stream(); } } class UserMetadataSync extends BaseSync { - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getDeletes(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('user_metadata_audit') + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getDeletes(options: SyncQueryOptions) { + return this.auditQuery('user_metadata_audit', options) .select(['id', 'userId', 'key']) - .where('userId', '=', userId) - .$call(this.auditTableFilters(ack)) + .where('userId', '=', options.userId) .stream(); } - @GenerateSql({ params: [DummyValue.UUID], stream: true }) - getUpserts(userId: string, ack?: SyncAck) { - return this.db - .selectFrom('user_metadata') + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('user_metadata_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions], stream: true }) + getUpserts(options: SyncQueryOptions) { + return this.upsertQuery('user_metadata', options) .select(['userId', 'key', 'value', 'updateId']) - .where('userId', '=', userId) - .$call(this.upsertTableFilters(ack)) + .where('userId', '=', options.userId) + .stream(); + } +} + +class AssetMetadataSync extends BaseSync { + @GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true }) + getDeletes(options: SyncQueryOptions, userId: string) { + return this.auditQuery('asset_metadata_audit', options) + .select(['asset_metadata_audit.id', 'assetId', 'key']) + .leftJoin('asset', 'asset.id', 'asset_metadata_audit.assetId') + .where('asset.ownerId', '=', userId) + .stream(); + } + + cleanupAuditTable(daysAgo: number) { + return this.auditCleanup('asset_metadata_audit', daysAgo); + } + + @GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true }) + getUpserts(options: SyncQueryOptions, userId: string) { + return this.upsertQuery('asset_metadata', options) + .select(['assetId', 'key', 'value', 'asset_metadata.updateId']) + .innerJoin('asset', 'asset.id', 'asset_metadata.assetId') + .where('asset.ownerId', '=', userId) .stream(); } } diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index 9d5f19b26a..20b41c80f8 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -7,13 +7,10 @@ import { columns } from 'src/database'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetType, AssetVisibility, UserStatus } from 'src/enum'; import { DB } from 'src/schema'; -import { UserMetadataTable } from 'src/schema/tables/user-metadata.table'; import { UserTable } from 'src/schema/tables/user.table'; import { UserMetadata, UserMetadataItem } from 'src/types'; import { asUuid } from 'src/utils/database'; -type Upsert = Insertable; - export interface UserListFilter { id?: string; withDeleted?: boolean; @@ -194,6 +191,10 @@ export class UserRepository { .executeTakeFirstOrThrow(); } + async updateAll(dto: Updateable) { + await this.db.updateTable('user').set(dto).execute(); + } + restore(id: string) { return this.db .updateTable('user') @@ -207,12 +208,12 @@ export class UserRepository { async upsertMetadata(id: string, { key, value }: { key: T; value: UserMetadata[T] }) { await this.db .insertInto('user_metadata') - .values({ userId: id, key, value } as Upsert) + .values({ userId: id, key, value }) .onConflict((oc) => oc.columns(['userId', 'key']).doUpdateSet({ key, value, - } as Upsert), + }), ) .execute(); } @@ -285,6 +286,16 @@ export class UserRepository { .execute(); } + @GenerateSql() + async getCount(): Promise { + const result = await this.db + .selectFrom('user') + .select((eb) => eb.fn.countAll().as('count')) + .where('user.deletedAt', 'is', null) + .executeTakeFirstOrThrow(); + return Number(result.count); + } + @GenerateSql({ params: [DummyValue.UUID, DummyValue.NUMBER] }) async updateUsage(id: string, delta: number): Promise { await this.db diff --git a/server/src/repositories/view-repository.ts b/server/src/repositories/view-repository.ts index 93c1280191..ceab79f6eb 100644 --- a/server/src/repositories/view-repository.ts +++ b/server/src/repositories/view-repository.ts @@ -20,6 +20,7 @@ export class ViewRepository { .where('fileCreatedAt', 'is not', null) .where('fileModifiedAt', 'is not', null) .where('localDateTime', 'is not', null) + .orderBy('directoryPath', 'asc') .execute(); return results.map((row) => row.directoryPath.replaceAll(/\/$/g, '')); diff --git a/server/src/schema/functions.ts b/server/src/schema/functions.ts index 786e7a1ffa..e255742b5d 100644 --- a/server/src/schema/functions.ts +++ b/server/src/schema/functions.ts @@ -230,6 +230,19 @@ export const user_metadata_audit = registerFunction({ END`, }); +export const asset_metadata_audit = registerFunction({ + name: 'asset_metadata_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO asset_metadata_audit ("assetId", "key") + SELECT "assetId", "key" + FROM OLD; + RETURN NULL; + END`, +}); + export const asset_face_audit = registerFunction({ name: 'asset_face_audit', returnType: 'TRIGGER', diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index 8982437b34..c8474cda03 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -5,6 +5,7 @@ import { album_user_delete_audit, asset_delete_audit, asset_face_audit, + asset_metadata_audit, f_concat_ws, f_unaccent, immich_uuid_v7, @@ -32,6 +33,8 @@ import { AssetFaceAuditTable } from 'src/schema/tables/asset-face-audit.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; +import { AssetMetadataAuditTable } from 'src/schema/tables/asset-metadata-audit.table'; +import { AssetMetadataTable } from 'src/schema/tables/asset-metadata.table'; import { AssetTable } from 'src/schema/tables/asset.table'; import { AuditTable } from 'src/schema/tables/audit.table'; import { FaceSearchTable } from 'src/schema/tables/face-search.table'; @@ -81,6 +84,8 @@ export class ImmichDatabase { AssetAuditTable, AssetFaceTable, AssetFaceAuditTable, + AssetMetadataTable, + AssetMetadataAuditTable, AssetJobStatusTable, AssetTable, AssetFileTable, @@ -135,6 +140,7 @@ export class ImmichDatabase { stack_delete_audit, person_delete_audit, user_metadata_audit, + asset_metadata_audit, asset_face_audit, ]; @@ -160,12 +166,14 @@ export interface DB { api_key: ApiKeyTable; asset: AssetTable; + asset_audit: AssetAuditTable; asset_exif: AssetExifTable; asset_face: AssetFaceTable; asset_face_audit: AssetFaceAuditTable; asset_file: AssetFileTable; + asset_metadata: AssetMetadataTable; + asset_metadata_audit: AssetMetadataAuditTable; asset_job_status: AssetJobStatusTable; - asset_audit: AssetAuditTable; audit: AuditTable; diff --git a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts index 776b51d1db..e7a1a06ec9 100644 --- a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts +++ b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts @@ -1,25 +1,10 @@ -import { Kysely, sql } from 'kysely'; +// this file used to try to reset the `vchordrq.prewarm_dim;` parameter +// that ends up being a problem on pg 15 + since the extension is not installed. -export async function up(qb: Kysely): Promise { - type Conf = { db: string; guc: string[] }; - const res = await sql` - select current_database() db, to_json(setconfig) guc - from pg_db_role_setting - where setdatabase = (select oid from pg_database where datname = current_database()) - and setrole = 0;`.execute(qb); - if (res.rows.length === 0) { - return; - } - - const { db, guc } = res.rows[0]; - await sql.raw(`alter database "${db}" reset all;`).execute(qb); - for (const parameter of guc) { - const [key, value] = parameter.split('='); - if (key === 'vchordrq.prewarm_dim') { - continue; - } - await sql.raw(`alter database "${db}" set ${key} to ${value};`).execute(qb); - } +export async function up(): Promise { + // noop } -export async function down(): Promise {} +export async function down(): Promise { + // noop +} diff --git a/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts b/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts index 686791e201..086b7c1273 100644 --- a/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts +++ b/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts @@ -1,8 +1,23 @@ import { Kysely, sql } from 'kysely'; export async function up(db: Kysely): Promise { - await sql`ALTER TABLE "geodata_places" DROP CONSTRAINT IF EXISTS "PK_c29918988912ef4036f3d7fbff4";`.execute(db); - await sql`ALTER TABLE "geodata_places" DROP CONSTRAINT IF EXISTS "geodata_places_pkey"`.execute(db); + await sql` + DO $$ + DECLARE + constraint_name text; + BEGIN + SELECT con.conname + INTO constraint_name + FROM pg_catalog.pg_constraint con + JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + WHERE rel.relname = 'geodata_places' AND con.contype = 'p'; + + IF constraint_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE "geodata_places" DROP CONSTRAINT "' || constraint_name || '"'; + END IF; + END; + $$; + `.execute(db); await sql`ALTER TABLE "geodata_places" ADD CONSTRAINT "geodata_places_pkey" PRIMARY KEY ("id");`.execute(db); } diff --git a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts index 68b0c7931e..839af40cf5 100644 --- a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts +++ b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts @@ -1,8 +1,7 @@ import { Kysely, sql } from 'kysely'; import { LoggingRepository } from 'src/repositories/logging.repository'; -const logger = LoggingRepository.create(); -logger.setContext('Migrations'); +const logger = LoggingRepository.create('Migrations'); export async function up(db: Kysely): Promise { if (process.env.IMMICH_MEDIA_LOCATION) { diff --git a/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts b/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts new file mode 100644 index 0000000000..0293a96607 --- /dev/null +++ b/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts @@ -0,0 +1,22 @@ +import { Kysely, sql } from 'kysely'; + +const items = [ + { oldName: 'album.addAsset', newName: 'albumAsset.create' }, + { oldName: 'album.removeAsset', newName: 'albumAsset.delete' }, + { oldName: 'admin.user.create', newName: 'adminUser.create' }, + { oldName: 'admin.user.read', newName: 'adminUser.read' }, + { oldName: 'admin.user.update', newName: 'adminUser.update' }, + { oldName: 'admin.user.delete', newName: 'adminUser.delete' }, +]; + +export async function up(db: Kysely): Promise { + for (const { oldName, newName } of items) { + await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${oldName}, ${newName})`.execute(db); + } +} + +export async function down(db: Kysely): Promise { + for (const { oldName, newName } of items) { + await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${newName}, ${oldName})`.execute(db); + } +} diff --git a/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts new file mode 100644 index 0000000000..1d77eddd07 --- /dev/null +++ b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts @@ -0,0 +1,11 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" ADD "slug" character varying;`.execute(db); + await sql`ALTER TABLE "shared_link" ADD CONSTRAINT "shared_link_slug_uq" UNIQUE ("slug");`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" DROP CONSTRAINT "shared_link_slug_uq";`.execute(db); + await sql`ALTER TABLE "shared_link" DROP COLUMN "slug";`.execute(db); +} diff --git a/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts new file mode 100644 index 0000000000..4f741f2113 --- /dev/null +++ b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts @@ -0,0 +1,25 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} diff --git a/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts b/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts new file mode 100644 index 0000000000..34bdfbd4ab --- /dev/null +++ b/server/src/schema/migrations/1754389095885-ResetAlbumAssetSync.ts @@ -0,0 +1,7 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint WHERE type IN ('AlbumAssetBackfillV1', 'AlbumAssetExifV1', 'AlbumAssetV1')`.execute(db); +} + +export async function down(): Promise {} diff --git a/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts b/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts new file mode 100644 index 0000000000..ba0bad9d9a --- /dev/null +++ b/server/src/schema/migrations/1756318797207-AssetMetadataTables.ts @@ -0,0 +1,58 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE OR REPLACE FUNCTION asset_metadata_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO asset_metadata_audit ("assetId", "key") + SELECT "assetId", "key" + FROM OLD; + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE TABLE "asset_metadata_audit" ( + "id" uuid NOT NULL DEFAULT immich_uuid_v7(), + "assetId" uuid NOT NULL, + "key" character varying NOT NULL, + "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), + CONSTRAINT "asset_metadata_audit_pkey" PRIMARY KEY ("id") +);`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_assetId_idx" ON "asset_metadata_audit" ("assetId");`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_key_idx" ON "asset_metadata_audit" ("key");`.execute(db); + await sql`CREATE INDEX "asset_metadata_audit_deletedAt_idx" ON "asset_metadata_audit" ("deletedAt");`.execute(db); + await sql`CREATE TABLE "asset_metadata" ( + "assetId" uuid NOT NULL, + "key" character varying NOT NULL, + "value" jsonb NOT NULL, + "updateId" uuid NOT NULL DEFAULT immich_uuid_v7(), + "updatedAt" timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT "asset_metadata_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "asset" ("id") ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT "asset_metadata_pkey" PRIMARY KEY ("assetId", "key") +);`.execute(db); + await sql`CREATE INDEX "asset_metadata_updateId_idx" ON "asset_metadata" ("updateId");`.execute(db); + await sql`CREATE INDEX "asset_metadata_updatedAt_idx" ON "asset_metadata" ("updatedAt");`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_metadata_audit" + AFTER DELETE ON "asset_metadata" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION asset_metadata_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_metadata_updated_at" + BEFORE UPDATE ON "asset_metadata" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('function_asset_metadata_audit', '{"type":"function","name":"asset_metadata_audit","sql":"CREATE OR REPLACE FUNCTION asset_metadata_audit()\\n RETURNS TRIGGER\\n LANGUAGE PLPGSQL\\n AS $$\\n BEGIN\\n INSERT INTO asset_metadata_audit (\\"assetId\\", \\"key\\")\\n SELECT \\"assetId\\", \\"key\\"\\n FROM OLD;\\n RETURN NULL;\\n END\\n $$;"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_metadata_audit', '{"type":"trigger","name":"asset_metadata_audit","sql":"CREATE OR REPLACE TRIGGER \\"asset_metadata_audit\\"\\n AFTER DELETE ON \\"asset_metadata\\"\\n REFERENCING OLD TABLE AS \\"old\\"\\n FOR EACH STATEMENT\\n WHEN (pg_trigger_depth() = 0)\\n EXECUTE FUNCTION asset_metadata_audit();"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_metadata_updated_at', '{"type":"trigger","name":"asset_metadata_updated_at","sql":"CREATE OR REPLACE TRIGGER \\"asset_metadata_updated_at\\"\\n BEFORE UPDATE ON \\"asset_metadata\\"\\n FOR EACH ROW\\n EXECUTE FUNCTION updated_at();"}'::jsonb);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TABLE "asset_metadata_audit";`.execute(db); + await sql`DROP TABLE "asset_metadata";`.execute(db); + await sql`DROP FUNCTION asset_metadata_audit;`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'function_asset_metadata_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_metadata_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_metadata_updated_at';`.execute(db); +} diff --git a/server/src/schema/tables/asset-metadata-audit.table.ts b/server/src/schema/tables/asset-metadata-audit.table.ts new file mode 100644 index 0000000000..3b94ce6d1a --- /dev/null +++ b/server/src/schema/tables/asset-metadata-audit.table.ts @@ -0,0 +1,18 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { AssetMetadataKey } from 'src/enum'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; + +@Table('asset_metadata_audit') +export class AssetMetadataAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: Generated; + + @Column({ type: 'uuid', index: true }) + assetId!: string; + + @Column({ index: true }) + key!: AssetMetadataKey; + + @CreateDateColumn({ default: () => 'clock_timestamp()', index: true }) + deletedAt!: Generated; +} diff --git a/server/src/schema/tables/asset-metadata.table.ts b/server/src/schema/tables/asset-metadata.table.ts new file mode 100644 index 0000000000..486101408d --- /dev/null +++ b/server/src/schema/tables/asset-metadata.table.ts @@ -0,0 +1,46 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; +import { AssetMetadataKey } from 'src/enum'; +import { asset_metadata_audit } from 'src/schema/functions'; +import { AssetTable } from 'src/schema/tables/asset.table'; +import { + AfterDeleteTrigger, + Column, + ForeignKeyColumn, + Generated, + PrimaryColumn, + Table, + Timestamp, + UpdateDateColumn, +} from 'src/sql-tools'; +import { AssetMetadata, AssetMetadataItem } from 'src/types'; + +@UpdatedAtTrigger('asset_metadata_updated_at') +@Table('asset_metadata') +@AfterDeleteTrigger({ + scope: 'statement', + function: asset_metadata_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() = 0', +}) +export class AssetMetadataTable implements AssetMetadataItem { + @ForeignKeyColumn(() => AssetTable, { + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + primary: true, + // [assetId, key] is the PK constraint + index: false, + }) + assetId!: string; + + @PrimaryColumn({ type: 'character varying' }) + key!: T; + + @Column({ type: 'jsonb' }) + value!: AssetMetadata[T]; + + @UpdateIdColumn({ index: true }) + updateId!: Generated; + + @UpdateDateColumn({ index: true }) + updatedAt!: Generated; +} diff --git a/server/src/schema/tables/memory-asset-audit.table.ts b/server/src/schema/tables/memory-asset-audit.table.ts index 77a889b455..218c2f19ff 100644 --- a/server/src/schema/tables/memory-asset-audit.table.ts +++ b/server/src/schema/tables/memory-asset-audit.table.ts @@ -1,11 +1,11 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; import { MemoryTable } from 'src/schema/tables/memory.table'; -import { Column, CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools'; +import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('memory_asset_audit') export class MemoryAssetAuditTable { @PrimaryGeneratedUuidV7Column() - id!: string; + id!: Generated; @ForeignKeyColumn(() => MemoryTable, { type: 'uuid', onDelete: 'CASCADE', onUpdate: 'CASCADE' }) memoryId!: string; @@ -14,5 +14,5 @@ export class MemoryAssetAuditTable { assetId!: string; @CreateDateColumn({ default: () => 'clock_timestamp()', index: true }) - deletedAt!: Date; + deletedAt!: Generated; } diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 40fd2bf6a9..80e2d7cdf4 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -48,4 +48,7 @@ export class SharedLinkTable { @Column({ type: 'character varying', nullable: true }) password!: string | null; + + @Column({ type: 'character varying', nullable: true, unique: true }) + slug!: string | null; } diff --git a/server/src/schema/tables/user-metadata-audit.table.ts b/server/src/schema/tables/user-metadata-audit.table.ts index de7d21c874..63f503ab85 100644 --- a/server/src/schema/tables/user-metadata-audit.table.ts +++ b/server/src/schema/tables/user-metadata-audit.table.ts @@ -1,4 +1,5 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { UserMetadataKey } from 'src/enum'; import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('user_metadata_audit') @@ -10,7 +11,7 @@ export class UserMetadataAuditTable { userId!: string; @Column({ indexName: 'IDX_user_metadata_audit_key' }) - key!: string; + key!: UserMetadataKey; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_user_metadata_audit_deleted_at' }) deletedAt!: Generated; diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 6f07a31dd9..e22d486bba 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -776,6 +776,346 @@ describe(AlbumService.name, () => { }); }); + describe('addAssetsToAlbums', () => { + it('should allow the owner to add assets', async () => { + mocks.access.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123', 'album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-123', assetsId: 'asset-1' }, + { albumsId: 'album-123', assetsId: 'asset-2' }, + { albumsId: 'album-123', assetsId: 'asset-3' }, + { albumsId: 'album-321', assetsId: 'asset-1' }, + { albumsId: 'album-321', assetsId: 'asset-2' }, + { albumsId: 'album-321', assetsId: 'asset-3' }, + ]); + }); + + it('should not set the thumbnail if the album has one already', async () => { + mocks.access.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123', 'album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep({ ...albumStub.empty, albumThumbnailAssetId: 'asset-id' })) + .mockResolvedValueOnce(_.cloneDeep({ ...albumStub.oneAsset, albumThumbnailAssetId: 'asset-id' })); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-id', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-id', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-123', assetsId: 'asset-1' }, + { albumsId: 'album-123', assetsId: 'asset-2' }, + { albumsId: 'album-123', assetsId: 'asset-3' }, + { albumsId: 'album-321', assetsId: 'asset-1' }, + { albumsId: 'album-321', assetsId: 'asset-2' }, + { albumsId: 'album-321', assetsId: 'asset-3' }, + ]); + }); + + it('should allow a shared user to add assets', async () => { + mocks.access.album.checkSharedAlbumAccess.mockResolvedValueOnce(new Set(['album-123', 'album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.user1, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-123', assetsId: 'asset-1' }, + { albumsId: 'album-123', assetsId: 'asset-2' }, + { albumsId: 'album-123', assetsId: 'asset-3' }, + { albumsId: 'album-321', assetsId: 'asset-1' }, + { albumsId: 'album-321', assetsId: 'asset-2' }, + { albumsId: 'album-321', assetsId: 'asset-3' }, + ]); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-123', + recipientId: 'admin_id', + }); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-321', + recipientId: 'admin_id', + }); + }); + + it('should not allow a shared user with viewer access to add assets', async () => { + mocks.access.album.checkSharedAlbumAccess.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithAdmin)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.user2, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + error: BulkIdErrorReason.NO_PERMISSION, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + }); + + it('should not allow a shared link user to add assets to multiple albums', async () => { + mocks.access.album.checkSharedLinkAccess.mockResolvedValueOnce(new Set(['album-123'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.adminSharedLink, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(1); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-123', assetsId: 'asset-1' }, + { albumsId: 'album-123', assetsId: 'asset-2' }, + { albumsId: 'album-123', assetsId: 'asset-3' }, + ]); + expect(mocks.event.emit).toHaveBeenCalledWith('AlbumUpdate', { + id: 'album-123', + recipientId: 'user-id', + }); + expect(mocks.access.album.checkSharedLinkAccess).toHaveBeenCalledWith( + authStub.adminSharedLink.sharedLink?.id, + new Set(['album-123', 'album-321']), + ); + }); + + it('should allow adding assets shared via partner sharing', async () => { + mocks.access.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123', 'album-321'])); + mocks.access.asset.checkPartnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(2); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-123', { + id: 'album-123', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.update).toHaveBeenNthCalledWith(2, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-123', assetsId: 'asset-1' }, + { albumsId: 'album-123', assetsId: 'asset-2' }, + { albumsId: 'album-123', assetsId: 'asset-3' }, + { albumsId: 'album-321', assetsId: 'asset-1' }, + { albumsId: 'album-321', assetsId: 'asset-2' }, + { albumsId: 'album-321', assetsId: 'asset-3' }, + ]); + expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + ); + }); + + it('should skip some duplicate assets', async () => { + mocks.access.album.checkOwnerAccess.mockResolvedValueOnce(new Set(['album-123', 'album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2', 'asset-3'])); + mocks.album.getAssetIds + .mockResolvedValueOnce(new Set(['asset-1', 'asset-2', 'asset-3'])) + .mockResolvedValueOnce(new Set()); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ success: true, error: undefined }); + + expect(mocks.album.update).toHaveBeenCalledTimes(1); + expect(mocks.album.update).toHaveBeenNthCalledWith(1, 'album-321', { + id: 'album-321', + updatedAt: expect.any(Date), + albumThumbnailAssetId: 'asset-1', + }); + expect(mocks.album.addAssetIdsToAlbums).toHaveBeenCalledWith([ + { albumsId: 'album-321', assetsId: 'asset-1' }, + { albumsId: 'album-321', assetsId: 'asset-2' }, + { albumsId: 'album-321', assetsId: 'asset-3' }, + ]); + }); + + it('should skip all duplicate assets', async () => { + mocks.access.album.checkOwnerAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1', 'asset-2'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + mocks.album.getAssetIds.mockResolvedValue(new Set(['asset-1', 'asset-2'])); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2'], + }), + ).resolves.toEqual({ + success: false, + error: BulkIdErrorReason.DUPLICATE, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + }); + + it('should skip assets not shared with user', async () => { + mocks.access.album.checkSharedAlbumAccess + .mockResolvedValueOnce(new Set(['album-123'])) + .mockResolvedValueOnce(new Set(['album-321'])); + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + mocks.album.getAssetIds.mockResolvedValueOnce(new Set()).mockResolvedValueOnce(new Set()); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + error: BulkIdErrorReason.NO_PERMISSION, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + expect(mocks.access.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + false, + ); + expect(mocks.access.asset.checkPartnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2', 'asset-3']), + ); + }); + + it('should not allow unauthorized access to the albums', async () => { + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithUser)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.sharedWithMultiple)); + + await expect( + sut.addAssetsToAlbums(authStub.admin, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + error: BulkIdErrorReason.NO_PERMISSION, + }); + + expect(mocks.album.update).not.toHaveBeenCalled(); + expect(mocks.album.addAssetIds).not.toHaveBeenCalled(); + expect(mocks.access.album.checkOwnerAccess).toHaveBeenCalled(); + expect(mocks.access.album.checkSharedAlbumAccess).toHaveBeenCalled(); + }); + + it('should not allow unauthorized shared link access to the album', async () => { + mocks.album.getById + .mockResolvedValueOnce(_.cloneDeep(albumStub.empty)) + .mockResolvedValueOnce(_.cloneDeep(albumStub.oneAsset)); + + await expect( + sut.addAssetsToAlbums(authStub.adminSharedLink, { + albumIds: ['album-123', 'album-321'], + assetIds: ['asset-1', 'asset-2', 'asset-3'], + }), + ).resolves.toEqual({ + success: false, + error: BulkIdErrorReason.NO_PERMISSION, + }); + + expect(mocks.access.album.checkSharedLinkAccess).toHaveBeenCalled(); + }); + }); + describe('removeAssets', () => { it('should allow the owner to remove assets', async () => { mocks.access.album.checkOwnerAccess.mockResolvedValue(new Set(['album-123'])); diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index a2edbb0384..0a9fe5b2b8 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -3,6 +3,8 @@ import { AddUsersDto, AlbumInfoDto, AlbumResponseDto, + AlbumsAddAssetsDto, + AlbumsAddAssetsResponseDto, AlbumStatisticsResponseDto, CreateAlbumDto, GetAlbumsDto, @@ -13,7 +15,7 @@ import { UpdateAlbumDto, UpdateAlbumUserDto, } from 'src/dtos/album.dto'; -import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; +import { BulkIdErrorReason, BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { Permission } from 'src/enum'; import { AlbumAssetCount, AlbumInfoOptions } from 'src/repositories/album.repository'; @@ -166,7 +168,7 @@ export class AlbumService extends BaseService { async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { const album = await this.findOrFail(id, { withAssets: false }); - await this.requireAccess({ auth, permission: Permission.AlbumAddAsset, ids: [id] }); + await this.requireAccess({ auth, permission: Permission.AlbumAssetCreate, ids: [id] }); const results = await addAssets( auth, @@ -194,8 +196,66 @@ export class AlbumService extends BaseService { return results; } + async addAssetsToAlbums(auth: AuthDto, dto: AlbumsAddAssetsDto): Promise { + const results: AlbumsAddAssetsResponseDto = { + success: false, + error: BulkIdErrorReason.DUPLICATE, + }; + + const allowedAlbumIds = await this.checkAccess({ + auth, + permission: Permission.AlbumAssetCreate, + ids: dto.albumIds, + }); + if (allowedAlbumIds.size === 0) { + results.error = BulkIdErrorReason.NO_PERMISSION; + return results; + } + + const allowedAssetIds = await this.checkAccess({ auth, permission: Permission.AssetShare, ids: dto.assetIds }); + if (allowedAssetIds.size === 0) { + results.error = BulkIdErrorReason.NO_PERMISSION; + return results; + } + + const albumAssetValues: { albumsId: string; assetsId: string }[] = []; + const events: { id: string; recipients: string[] }[] = []; + for (const albumId of allowedAlbumIds) { + const existingAssetIds = await this.albumRepository.getAssetIds(albumId, [...allowedAssetIds]); + const notPresentAssetIds = [...allowedAssetIds].filter((id) => !existingAssetIds.has(id)); + if (notPresentAssetIds.length === 0) { + continue; + } + const album = await this.findOrFail(albumId, { withAssets: false }); + results.error = undefined; + results.success = true; + + for (const assetId of notPresentAssetIds) { + albumAssetValues.push({ albumsId: albumId, assetsId: assetId }); + } + await this.albumRepository.update(albumId, { + id: albumId, + updatedAt: new Date(), + albumThumbnailAssetId: album.albumThumbnailAssetId ?? notPresentAssetIds[0], + }); + const allUsersExceptUs = [...album.albumUsers.map(({ user }) => user.id), album.owner.id].filter( + (userId) => userId !== auth.user.id, + ); + events.push({ id: albumId, recipients: allUsersExceptUs }); + } + + await this.albumRepository.addAssetIdsToAlbums(albumAssetValues); + for (const event of events) { + for (const recipientId of event.recipients) { + await this.eventRepository.emit('AlbumUpdate', { id: event.id, recipientId }); + } + } + + return results; + } + async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { - await this.requireAccess({ auth, permission: Permission.AlbumRemoveAsset, ids: [id] }); + await this.requireAccess({ auth, permission: Permission.AlbumAssetDelete, ids: [id] }); const album = await this.findOrFail(id, { withAssets: false }); const results = await removeAssets( diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index fffe7bb536..8d48b47f1e 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -1,4 +1,4 @@ -import { BadRequestException } from '@nestjs/common'; +import { BadRequestException, ForbiddenException } from '@nestjs/common'; import { Permission } from 'src/enum'; import { ApiKeyService } from 'src/services/api-key.service'; import { factory, newUuid } from 'test/small.factory'; @@ -134,6 +134,41 @@ describe(ApiKeyService.name, () => { }); }); + describe('getMine', () => { + it('should not work with a session token', async () => { + const session = factory.session(); + const auth = factory.auth({ session }); + + mocks.apiKey.getById.mockResolvedValue(void 0); + + await expect(sut.getMine(auth)).rejects.toBeInstanceOf(ForbiddenException); + + expect(mocks.apiKey.getById).not.toHaveBeenCalled(); + }); + + it('should throw an error if the key is not found', async () => { + const apiKey = factory.authApiKey(); + const auth = factory.auth({ apiKey }); + + mocks.apiKey.getById.mockResolvedValue(void 0); + + await expect(sut.getMine(auth)).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id); + }); + + it('should get a key by id', async () => { + const auth = factory.auth(); + const apiKey = factory.apiKey({ userId: auth.user.id }); + + mocks.apiKey.getById.mockResolvedValue(apiKey); + + await sut.getById(auth, apiKey.id); + + expect(mocks.apiKey.getById).toHaveBeenCalledWith(auth.user.id, apiKey.id); + }); + }); + describe('getById', () => { it('should throw an error if the key is not found', async () => { const auth = factory.auth(); diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 82d4eabdfd..96671daab1 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common'; import { ApiKey } from 'src/database'; import { APIKeyCreateDto, APIKeyCreateResponseDto, APIKeyResponseDto, APIKeyUpdateDto } from 'src/dtos/api-key.dto'; import { AuthDto } from 'src/dtos/auth.dto'; @@ -46,6 +46,19 @@ export class ApiKeyService extends BaseService { await this.apiKeyRepository.delete(auth.user.id, id); } + async getMine(auth: AuthDto): Promise { + if (!auth.apiKey) { + throw new ForbiddenException('Not authenticated with an API Key'); + } + + const key = await this.apiKeyRepository.getById(auth.user.id, auth.apiKey.id); + if (!key) { + throw new BadRequestException('API Key not found'); + } + + return this.map(key); + } + async getById(auth: AuthDto, id: string): Promise { const key = await this.apiKeyRepository.getById(auth.user.id, id); if (!key) { diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index 27a776e867..143b470750 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -76,23 +76,36 @@ export class ApiService { let status = 200; let html = index; - const shareMatches = request.url.match(/^\/share\/(.+)$/); - if (shareMatches) { + const defaultDomain = request.host ? `${request.protocol}://${request.host}` : undefined; + + let meta: OpenGraphTags | null = null; + + const shareKey = request.url.match(/^\/share\/(.+)$/); + if (shareKey) { try { - const key = shareMatches[1]; - const auth = await this.authService.validateSharedLink(key); - const meta = await this.sharedLinkService.getMetadataTags( - auth, - request.host ? `${request.protocol}://${request.host}` : undefined, - ); - if (meta) { - html = render(index, meta); - } + const key = shareKey[1]; + const auth = await this.authService.validateSharedLinkKey(key); + meta = await this.sharedLinkService.getMetadataTags(auth, defaultDomain); } catch { status = 404; } } + const shareSlug = request.url.match(/^\/s\/(.+)$/); + if (shareSlug) { + try { + const slug = shareSlug[1]; + const auth = await this.authService.validateSharedLinkSlug(slug); + meta = await this.sharedLinkService.getMetadataTags(auth, defaultDomain); + } catch { + status = 404; + } + } + + if (meta) { + html = render(index, meta); + } + res.status(status).type('text/html').header('Cache-Control', 'no-store').send(html); }; } diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index 0585a159ac..a338c30d78 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -25,11 +25,12 @@ const file1 = Buffer.from('d2947b871a706081be194569951b7db246907957', 'hex'); const uploadFile = { nullAuth: { auth: null, + body: {}, fieldName: UploadFieldName.ASSET_DATA, file: { uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/admin/image.jpeg', + originalPath: '/data/library/admin/image.jpeg', originalName: 'image.jpeg', size: 1000, }, @@ -37,12 +38,13 @@ const uploadFile = { filename: (fieldName: UploadFieldName, filename: string) => { return { auth: authStub.admin, + body: {}, fieldName, file: { uuid: 'random-uuid', mimeType: 'image/jpeg', checksum: Buffer.from('checksum', 'utf8'), - originalPath: `upload/admin/${filename}`, + originalPath: `/data/admin/${filename}`, originalName: filename, size: 1000, }, @@ -294,16 +296,16 @@ describe(AssetMediaService.name, () => { it('should return profile for profile uploads', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.PROFILE_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/profile/admin_id'), + expect.stringContaining('/data/profile/admin_id'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile/admin_id')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile/admin_id')); }); it('should return upload for everything else', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.ASSET_DATA, 'image.jpg'))).toEqual( - expect.stringContaining('upload/upload/admin_id/ra/nd'), + expect.stringContaining('/data/upload/admin_id/ra/nd'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload/admin_id/ra/nd')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload/admin_id/ra/nd')); }); }); @@ -897,7 +899,10 @@ describe(AssetMediaService.name, () => { describe('onUploadError', () => { it('should queue a job to delete the uploaded file', async () => { - const request = { user: authStub.user1 } as AuthRequest; + const request = { + body: {}, + user: authStub.user1, + } as AuthRequest; const file = { fieldname: UploadFieldName.ASSET_DATA, @@ -907,14 +912,14 @@ describe(AssetMediaService.name, () => { size: 1000, uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/upload/user-id/ra/nd/random-uuid.jpg', + originalPath: '/data/upload/user-id/ra/nd/random-uuid.jpg', } as unknown as Express.Multer.File; await sut.onUploadError(request, file); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FileDelete, - data: { files: [expect.stringContaining('upload/upload/user-id/ra/nd/random-uuid.jpg')] }, + data: { files: [expect.stringContaining('/data/upload/user-id/ra/nd/random-uuid.jpg')] }, }); }); }); diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 517a1f665f..0747bd7b7b 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -24,20 +24,14 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { AssetStatus, AssetType, AssetVisibility, CacheControl, JobName, Permission, StorageFolder } from 'src/enum'; import { AuthRequest } from 'src/middleware/auth.guard'; import { BaseService } from 'src/services/base.service'; -import { UploadFile } from 'src/types'; +import { UploadFile, UploadRequest } from 'src/types'; import { requireUploadAccess } from 'src/utils/access'; -import { asRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util'; -import { ASSET_CHECKSUM_CONSTRAINT } from 'src/utils/database'; +import { asUploadRequest, getAssetFiles, onBeforeLink } from 'src/utils/asset.util'; +import { isAssetChecksumConstraint } from 'src/utils/database'; import { getFilenameExtension, getFileNameWithoutExtension, ImmichFileResponse } from 'src/utils/file'; import { mimeTypes } from 'src/utils/mime-types'; import { fromChecksum } from 'src/utils/request'; -interface UploadRequest { - auth: AuthDto | null; - fieldName: UploadFieldName; - file: UploadFile; -} - export interface AssetMediaRedirectResponse { targetSize: AssetMediaSize | 'original'; } @@ -89,15 +83,15 @@ export class AssetMediaService extends BaseService { throw new BadRequestException(`Unsupported file type ${filename}`); } - getUploadFilename({ auth, fieldName, file }: UploadRequest): string { + getUploadFilename({ auth, fieldName, file, body }: UploadRequest): string { requireUploadAccess(auth); - const originalExtension = extname(file.originalName); + const extension = extname(body.filename || file.originalName); const lookup = { - [UploadFieldName.ASSET_DATA]: originalExtension, + [UploadFieldName.ASSET_DATA]: extension, [UploadFieldName.SIDECAR_DATA]: '.xmp', - [UploadFieldName.PROFILE_DATA]: originalExtension, + [UploadFieldName.PROFILE_DATA]: extension, }; return sanitize(`${file.uuid}${lookup[fieldName]}`); @@ -117,8 +111,8 @@ export class AssetMediaService extends BaseService { } async onUploadError(request: AuthRequest, file: Express.Multer.File) { - const uploadFilename = this.getUploadFilename(asRequest(request, file)); - const uploadFolder = this.getUploadFolder(asRequest(request, file)); + const uploadFilename = this.getUploadFilename(asUploadRequest(request, file)); + const uploadFolder = this.getUploadFolder(asUploadRequest(request, file)); const uploadPath = `${uploadFolder}/${uploadFilename}`; await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [uploadPath] } }); @@ -318,7 +312,7 @@ export class AssetMediaService extends BaseService { }); // handle duplicates with a success response - if (error.constraint_name === ASSET_CHECKSUM_CONSTRAINT) { + if (isAssetChecksumConstraint(error)) { const duplicateId = await this.assetRepository.getUploadAssetIdByChecksum(auth.user.id, file.checksum); if (!duplicateId) { this.logger.error(`Error locating duplicate for checksum constraint`); @@ -423,6 +417,10 @@ export class AssetMediaService extends BaseService { sidecarPath: sidecarFile?.originalPath, }); + if (dto.metadata) { + await this.assetRepository.upsertMetadata(asset.id, dto.metadata); + } + if (sidecarFile) { await this.storageRepository.utimes(sidecarFile.originalPath, new Date(), new Date(dto.fileModifiedAt)); } diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index 6461735976..93861149c3 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -420,7 +420,7 @@ describe(AssetService.name, () => { ids: ['asset-1'], latitude: 0, longitude: 0, - visibility: undefined, + visibility: AssetVisibility.Archive, isFavorite: false, duplicateId: undefined, rating: undefined, @@ -468,6 +468,33 @@ describe(AssetService.name, () => { }); expect(mocks.asset.updateAll).toHaveBeenCalled(); }); + + it('should update exif table if dateTimeRelative and timeZone field is provided', async () => { + mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); + const dateTimeRelative = 35; + const timeZone = 'UTC+2'; + mocks.asset.updateDateTimeOriginal.mockResolvedValue([ + { assetId: 'asset-1', dateTimeOriginal: new Date('2020-02-25T04:41:00'), timeZone }, + ]); + await sut.updateAll(authStub.admin, { + ids: ['asset-1'], + dateTimeRelative, + timeZone, + }); + expect(mocks.asset.updateDateTimeOriginal).toHaveBeenCalledWith(['asset-1'], dateTimeRelative, timeZone); + expect(mocks.job.queueAll).toHaveBeenCalledWith([ + { + name: JobName.SidecarWrite, + data: { + id: 'asset-1', + dateTimeOriginal: '2020-02-25T06:41:00.000+02:00', + description: undefined, + latitude: undefined, + longitude: undefined, + }, + }, + ]); + }); }); describe('deleteAll', () => { diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 864a9cc512..6cb0219745 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -9,12 +9,14 @@ import { AssetBulkUpdateDto, AssetJobName, AssetJobsDto, + AssetMetadataResponseDto, + AssetMetadataUpsertDto, AssetStatsDto, UpdateAssetDto, mapStats, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { AssetStatus, AssetVisibility, JobName, JobStatus, Permission, QueueName } from 'src/enum'; +import { AssetMetadataKey, AssetStatus, AssetVisibility, JobName, JobStatus, Permission, QueueName } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { ISidecarWriteJob, JobItem, JobOf } from 'src/types'; import { requireElevatedPermission } from 'src/utils/access'; @@ -93,7 +95,7 @@ export class AssetService extends BaseService { } } - await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating }); + await this.updateExif({ id, description, dateTimeOriginal, latitude, longitude, rating }); const asset = await this.assetRepository.update({ id, ...rest }); @@ -113,33 +115,68 @@ export class AssetService extends BaseService { } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { - const { ids, description, dateTimeOriginal, latitude, longitude, ...options } = dto; + const { + ids, + isFavorite, + visibility, + dateTimeOriginal, + latitude, + longitude, + rating, + description, + duplicateId, + dateTimeRelative, + timeZone, + } = dto; await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids }); - if ( - description !== undefined || - dateTimeOriginal !== undefined || - latitude !== undefined || - longitude !== undefined - ) { - await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude }); + const assetDto = { isFavorite, visibility, duplicateId }; + const exifDto = { latitude, longitude, rating, description, dateTimeOriginal }; + + const isExifChanged = Object.values(exifDto).some((v) => v !== undefined); + if (isExifChanged) { + await this.assetRepository.updateAllExif(ids, exifDto); + } + + const assets = + (dateTimeRelative !== undefined && dateTimeRelative !== 0) || timeZone !== undefined + ? await this.assetRepository.updateDateTimeOriginal(ids, dateTimeRelative, timeZone) + : undefined; + + const dateTimesWithTimezone = assets + ? assets.map((asset) => { + const isoString = asset.dateTimeOriginal?.toISOString(); + let dateTime = isoString ? DateTime.fromISO(isoString) : null; + + if (dateTime && asset.timeZone) { + dateTime = dateTime.setZone(asset.timeZone); + } + + return { + assetId: asset.assetId, + dateTimeOriginal: dateTime?.toISO() ?? null, + }; + }) + : ids.map((id) => ({ assetId: id, dateTimeOriginal })); + + if (dateTimesWithTimezone.length > 0) { await this.jobRepository.queueAll( - ids.map((id) => ({ + dateTimesWithTimezone.map(({ assetId: id, dateTimeOriginal }) => ({ name: JobName.SidecarWrite, - data: { id, description, dateTimeOriginal, latitude, longitude }, + data: { + ...exifDto, + id, + dateTimeOriginal: dateTimeOriginal ?? undefined, + }, })), ); } - if ( - options.visibility !== undefined || - options.isFavorite !== undefined || - options.duplicateId !== undefined || - options.rating !== undefined - ) { - await this.assetRepository.updateAll(ids, options); + const isAssetChanged = Object.values(assetDto).some((v) => v !== undefined); + if (isAssetChanged) { + await this.assetRepository.updateAll(ids, assetDto); - if (options.visibility === AssetVisibility.Locked) { + if (visibility === AssetVisibility.Locked) { await this.albumRepository.removeAssetsFromAll(ids); } } @@ -247,6 +284,31 @@ export class AssetService extends BaseService { }); } + async getMetadata(auth: AuthDto, id: string): Promise { + await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] }); + return this.assetRepository.getMetadata(id); + } + + async upsertMetadata(auth: AuthDto, id: string, dto: AssetMetadataUpsertDto): Promise { + await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] }); + return this.assetRepository.upsertMetadata(id, dto.items); + } + + async getMetadataByKey(auth: AuthDto, id: string, key: AssetMetadataKey): Promise { + await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] }); + + const item = await this.assetRepository.getMetadataByKey(id, key); + if (!item) { + throw new BadRequestException(`Metadata with key "${key}" not found for asset with id "${id}"`); + } + return item; + } + + async deleteMetadataByKey(auth: AuthDto, id: string, key: AssetMetadataKey): Promise { + await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] }); + return this.assetRepository.deleteMetadataByKey(id, key); + } + async run(auth: AuthDto, dto: AssetJobsDto) { await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.assetIds }); @@ -287,7 +349,7 @@ export class AssetService extends BaseService { return asset; } - private async updateMetadata(dto: ISidecarWriteJob) { + private async updateExif(dto: ISidecarWriteJob) { const { id, description, dateTimeOriginal, latitude, longitude, rating } = dto; const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, rating }, _.isUndefined); if (Object.keys(writes).length > 0) { diff --git a/server/src/services/auth-admin.service.ts b/server/src/services/auth-admin.service.ts new file mode 100644 index 0000000000..3648a19957 --- /dev/null +++ b/server/src/services/auth-admin.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { BaseService } from 'src/services/base.service'; + +@Injectable() +export class AuthAdminService extends BaseService { + async unlinkAll(_auth: AuthDto) { + // TODO replace '' with null + await this.userRepository.updateAll({ oauthId: '' }); + } +} diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 129877bbdd..d2b287cd5e 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -241,7 +241,6 @@ describe(AuthService.name, () => { const sessionWithToken = { id: session.id, updatedAt: session.updatedAt, - isPendingSyncReset: false, user: factory.authUser(), pinExpiresAt: null, }; @@ -259,7 +258,6 @@ describe(AuthService.name, () => { session: { id: session.id, hasElevatedPermission: false, - isPendingSyncReset: session.isPendingSyncReset, }, }); }); @@ -322,15 +320,18 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('base64url'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('base64url') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); }); it('should accept a hex key', async () => { @@ -340,15 +341,50 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('hex'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('hex') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); + }); + }); + + describe('validate - shared link slug', () => { + it('should not accept a non-existent slug', async () => { + mocks.sharedLink.getBySlug.mockResolvedValue(void 0); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should accept a valid slug', async () => { + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, slug: 'slug-123', user } as any; + + mocks.sharedLink.getBySlug.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug-123' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getBySlug).toHaveBeenCalledWith('slug-123'); }); }); @@ -371,7 +407,6 @@ describe(AuthService.name, () => { id: session.id, updatedAt: session.updatedAt, user: factory.authUser(), - isPendingSyncReset: false, pinExpiresAt: null, }; @@ -388,7 +423,6 @@ describe(AuthService.name, () => { session: { id: session.id, hasElevatedPermission: false, - isPendingSyncReset: session.isPendingSyncReset, }, }); }); @@ -459,18 +493,48 @@ describe(AuthService.name, () => { mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); - await expect( - sut.authenticate({ - headers: { 'x-api-key': 'auth_token' }, - queryParams: {}, - metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, - }), - ).rejects.toBeInstanceOf(ForbiddenException); + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, + }); + + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: asset.read'); + }); + + it('should default to requiring the all permission when omitted', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.AssetRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' }, + }); + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: all'); + }); + + it('should not require any permission when metadata is set to `false`', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.ActivityRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: false }, + }); + await expect(result).resolves.toEqual({ user: authUser, apiKey: expect.objectContaining(authApiKey) }); }); it('should return an auth dto', async () => { const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); + const authApiKey = factory.authApiKey({ permissions: [Permission.All] }); mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); @@ -775,7 +839,7 @@ describe(AuthService.name, () => { mocks.crypto.randomUUID.mockReturnValue(fileId); mocks.oauth.getProfilePicture.mockResolvedValue({ contentType: 'image/jpeg', - data: new Uint8Array([1, 2, 3, 4, 5]), + data: new Uint8Array([1, 2, 3, 4, 5]).buffer, }); mocks.user.update.mockResolvedValue(user); mocks.session.create.mockResolvedValue(factory.session()); @@ -789,7 +853,7 @@ describe(AuthService.name, () => { ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { - profileImagePath: expect.stringContaining(`upload/profile/${user.id}/${fileId}.jpg`), + profileImagePath: expect.stringContaining(`/data/profile/${user.id}/${fileId}.jpg`), profileChangedAt: expect.any(Date), }); expect(mocks.oauth.getProfilePicture).toHaveBeenCalledWith(pictureUrl); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index a5b0de25cd..535df779cd 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -6,7 +6,7 @@ import { IncomingHttpHeaders } from 'node:http'; 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 { AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { AuthDto, AuthStatusResponseDto, @@ -48,7 +48,8 @@ export type ValidateRequest = { metadata: { sharedLinkRoute: boolean; adminRoute: boolean; - permission?: Permission; + /** `false` explicitly means no permission is required, which otherwise defaults to `all` */ + permission?: Permission | false; uri: string; }; }; @@ -174,7 +175,8 @@ export class AuthService extends BaseService { async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise { const authDto = await this.validate({ headers, queryParams }); - const { adminRoute, sharedLinkRoute, permission, uri } = metadata; + const { adminRoute, sharedLinkRoute, uri } = metadata; + const requestedPermission = metadata.permission ?? Permission.All; if (!authDto.user.isAdmin && adminRoute) { this.logger.warn(`Denied access to admin only route: ${uri}`); @@ -186,8 +188,12 @@ export class AuthService extends BaseService { throw new ForbiddenException('Forbidden'); } - if (authDto.apiKey && permission && !isGranted({ requested: [permission], current: authDto.apiKey.permissions })) { - throw new ForbiddenException(`Missing required permission: ${permission}`); + if ( + authDto.apiKey && + requestedPermission !== false && + !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions }) + ) { + throw new ForbiddenException(`Missing required permission: ${requestedPermission}`); } return authDto; @@ -195,6 +201,7 @@ export class AuthService extends BaseService { private async validate({ headers, queryParams }: Omit): Promise { const shareKey = (headers[ImmichHeader.SharedLinkKey] || queryParams[ImmichQuery.SharedLinkKey]) as string; + const shareSlug = (headers[ImmichHeader.SharedLinkSlug] || queryParams[ImmichQuery.SharedLinkSlug]) as string; const session = (headers[ImmichHeader.UserToken] || headers[ImmichHeader.SessionToken] || queryParams[ImmichQuery.SessionKey] || @@ -203,7 +210,11 @@ export class AuthService extends BaseService { const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string; if (shareKey) { - return this.validateSharedLink(shareKey); + return this.validateSharedLinkKey(shareKey); + } + + if (shareSlug) { + return this.validateSharedLinkSlug(shareSlug); } if (session) { @@ -333,7 +344,7 @@ export class AuthService extends BaseService { await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [oldPath] } }); } } catch (error: Error | any) { - this.logger.warn(`Unable to sync oauth profile picture: ${error}`, error?.stack); + this.logger.warn(`Unable to sync oauth profile picture: ${error}\n${error?.stack}`); } } @@ -402,18 +413,33 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.OAuthCodeVerifier] || null; } - async validateSharedLink(key: string | string[]): Promise { + async validateSharedLinkKey(key: string | string[]): Promise { key = Array.isArray(key) ? key[0] : key; const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); const sharedLink = await this.sharedLinkRepository.getByKey(bytes); - if (sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) { - return { - user: sharedLink.user, - sharedLink, - }; + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share key'); } - throw new UnauthorizedException('Invalid share key'); + + return { user: sharedLink.user, sharedLink }; + } + + async validateSharedLinkSlug(slug: string | string[]): Promise { + slug = Array.isArray(slug) ? slug[0] : slug; + + const sharedLink = await this.sharedLinkRepository.getBySlug(slug); + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share slug'); + } + + return { user: sharedLink.user, sharedLink }; + } + + private isValidSharedLink( + sharedLink?: AuthSharedLink & { user: AuthUser | null }, + ): sharedLink is AuthSharedLink & { user: AuthUser } { + return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date()); } private async validateApiKey(key: string): Promise { @@ -466,7 +492,6 @@ export class AuthService extends BaseService { user: session.user, session: { id: session.id, - isPendingSyncReset: session.isPendingSyncReset, hasElevatedPermission, }, }; diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index e36f699f53..ad60e30425 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -1,3 +1,4 @@ +import { DateTime } from 'luxon'; import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; @@ -90,18 +91,23 @@ describe(BackupService.name, () => { it('should remove failed backup files', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); + //`immich-db-backup-${DateTime.now().toFormat("yyyyLLdd'T'HHmmss")}-v${serverVersion.toString()}-pg${databaseVersion.split(' ')[0]}.sql.gz.tmp`, mocks.storage.readdir.mockResolvedValue([ 'immich-db-backup-123.sql.gz.tmp', - 'immich-db-backup-234.sql.gz', - 'immich-db-backup-345.sql.gz.tmp', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-123.sql.gz.tmp`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-345.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250729T110116-v1.234.5-pg14.5.sql.gz.tmp`, ); }); @@ -118,17 +124,21 @@ describe(BackupService.name, () => { it('should remove old backup files over keepLastAmount and failed backups', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.readdir.mockResolvedValue([ - 'immich-db-backup-1.sql.gz.tmp', - 'immich-db-backup-2.sql.gz', - 'immich-db-backup-3.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + 'immich-db-backup-1753789649000.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1753789649000.sql.gz`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-2.sql.gz`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250727T110116-v1.234.5-pg14.5.sql.gz`, ); }); }); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 9daa2c3aea..3d99b6e522 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -53,11 +53,16 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); - const failedBackups = files.filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz\.tmp$/)); + const failedBackups = files.filter((file) => file.match(/immich-db-backup-.*\.sql\.gz\.tmp$/)); const backups = files - .filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz$/)) + .filter((file) => { + const oldBackupStyle = file.match(/immich-db-backup-\d+\.sql\.gz$/); + //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz + const newBackupStyle = file.match(/immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); + return oldBackupStyle || newBackupStyle; + }) .sort() - .reverse(); + .toReversed(); const toDelete = backups.slice(config.keepLastAmount); toDelete.push(...failedBackups); @@ -113,7 +118,7 @@ export class BackupService extends BaseService { { env: { PATH: process.env.PATH, - PGPASSWORD: isUrlConnection ? undefined : config.password, + PGPASSWORD: isUrlConnection ? new URL(config.url).password : config.password, }, }, ); @@ -127,12 +132,12 @@ export class BackupService extends BaseService { gzip.stdout.pipe(fileStream); pgdump.on('error', (err) => { - this.logger.error('Backup failed with error', err); + this.logger.error(`Backup failed with error: ${err}`); reject(err); }); gzip.on('error', (err) => { - this.logger.error('Gzip failed with error', err); + this.logger.error(`Gzip failed with error: ${err}`); reject(err); }); @@ -170,10 +175,10 @@ export class BackupService extends BaseService { }); await this.storageRepository.rename(backupFilePath, backupFilePath.replace('.tmp', '')); } catch (error) { - this.logger.error('Database Backup Failure', error); + this.logger.error(`Database Backup Failure: ${error}`); await this.storageRepository .unlink(backupFilePath) - .catch((error) => this.logger.error('Failed to delete failed backup file', error)); + .catch((error) => this.logger.error(`Failed to delete failed backup file: ${error}`)); throw error; } diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2f0e272883..6b85c3ec6c 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -215,6 +215,7 @@ export class BaseService { payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', '')); } + this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); return this.userRepository.create(payload); } } diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 674b885dc4..38144e95b4 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -86,12 +86,7 @@ export class CliService extends BaseService { } for (const asset of assets) { - paths.push( - asset.originalPath, - asset.sidecarPath, - asset.encodedVideoPath, - ...asset.files.map((file) => file.path), - ); + paths.push(asset.path); } return paths.filter(Boolean) as string[]; diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index b4022ee864..e30722d3d7 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -261,8 +261,12 @@ describe(DatabaseService.name, () => { await expect(sut.onBootstrap()).rejects.toThrow('Failed to update extension'); - expect(mocks.logger.warn.mock.calls[0][0]).toContain( - `The ${extensionName} extension can be updated to ${updateInRange}.`, + expect(mocks.logger.warn.mock.calls).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + expect.stringContaining(`The ${extensionName} extension can be updated to ${updateInRange}.`), + ]), + ]), ); expect(mocks.logger.fatal).not.toHaveBeenCalled(); expect(mocks.database.updateVectorExtension).toHaveBeenCalledWith(extension, updateInRange); @@ -281,8 +285,10 @@ describe(DatabaseService.name, () => { await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.logger.warn).toHaveBeenCalledTimes(1); - expect(mocks.logger.warn.mock.calls[0][0]).toContain(extensionName); + expect(mocks.logger.warn.mock.calls).toEqual( + expect.arrayContaining([expect.arrayContaining([expect.stringContaining(extensionName)])]), + ); + expect(mocks.database.updateVectorExtension).toHaveBeenCalledWith(extension, updateInRange); expect(mocks.database.runMigrations).toHaveBeenCalledTimes(1); expect(mocks.logger.fatal).not.toHaveBeenCalled(); @@ -415,5 +421,21 @@ describe(DatabaseService.name, () => { expect(mocks.database.dropExtension).not.toHaveBeenCalled(); }); + + it(`should warn if using pgvecto.rs`, async () => { + mocks.database.getExtensionVersions.mockResolvedValue([ + { + name: DatabaseExtension.Vectors, + installedVersion: minVersionInRange, + availableVersion: minVersionInRange, + }, + ]); + mocks.database.getVectorExtension.mockResolvedValue(DatabaseExtension.Vectors); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + + expect(mocks.logger.warn).toHaveBeenCalledTimes(1); + expect(mocks.logger.warn.mock.calls[0][0]).toContain('DEPRECATION WARNING'); + }); }); }); diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index e54be28fc2..2ff0e0ca27 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -22,7 +22,7 @@ const messages = { The ${name} extension version is ${version}, which means it is a nightly release. Please run 'DROP EXTENSION IF EXISTS ${extension}' and switch to a release version. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, outOfRange: ({ name, version, range }: OutOfRangeArgs) => `The ${name} extension version is ${version}, but Immich only supports ${range}. Please change ${name} to a compatible version in the Postgres instance.`, @@ -32,20 +32,20 @@ const messages = { If the Postgres instance already has ${name} installed, Immich may not have the necessary permissions to activate it. In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension} CASCADE' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, updateFailed: ({ name, extension, availableVersion }: UpdateFailedArgs) => `The ${name} extension can be updated to ${availableVersion}. Immich attempted to update the extension, but failed to do so. This may be because Immich does not have the necessary permissions to update the extension. Please run 'ALTER EXTENSION ${extension} UPDATE' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, dropFailed: ({ name, extension }: DropFailedArgs) => `The ${name} extension is no longer needed, but could not be dropped. This may be because Immich does not have the necessary permissions to drop the extension. Please run 'DROP EXTENSION ${extension};' manually as a superuser. - See https://immich.app/docs/guides/database-queries for how to query the database.`, + See https://docs.immich.app/guides/database-queries for how to query the database.`, restartRequired: ({ name, availableVersion }: RestartRequiredArgs) => `The ${name} extension has been updated to ${availableVersion}. Please restart the Postgres instance to complete the update.`, @@ -53,6 +53,9 @@ const messages = { `The database currently has ${name} ${installedVersion} activated, but the Postgres instance only has ${availableVersion} available. This most likely means the extension was downgraded. If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`, + deprecatedExtension: (name: string) => + `DEPRECATION WARNING: The ${name} extension is deprecated and support for it will be removed very soon. + See https://docs.immich.app/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`, }; @Injectable() @@ -71,6 +74,9 @@ export class DatabaseService extends BaseService { await this.databaseRepository.withLock(DatabaseLock.Migrations, async () => { const extension = await this.databaseRepository.getVectorExtension(); const name = EXTENSION_NAMES[extension]; + if (extension === DatabaseExtension.Vectors) { + this.logger.warn(messages.deprecatedExtension(name)); + } const extensionRange = this.databaseRepository.getExtensionVersionRange(extension); const extensionVersions = await this.databaseRepository.getExtensionVersions(VECTOR_EXTENSIONS); diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index 940767ff67..86d0bda7f8 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,11 +1,10 @@ import { BadRequestException } from '@nestjs/common'; -import { APP_MEDIA_LOCATION } from 'src/constants'; +import { Readable } from 'node:stream'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; import { vitest } from 'vitest'; const downloadResponse: DownloadResponseDto = { @@ -49,7 +48,7 @@ describe(DownloadService.name, () => { expect(archiveMock.addFile).toHaveBeenCalledTimes(1); expect(archiveMock.addFile).toHaveBeenNthCalledWith( 1, - expect.stringContaining('upload/library/IMG_123.jpg'), + expect.stringContaining('/data/library/IMG_123.jpg'), 'IMG_123.jpg', ); }); @@ -75,8 +74,8 @@ describe(DownloadService.name, () => { expect(mocks.logger.warn).toHaveBeenCalledTimes(2); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should download an archive', async () => { @@ -98,8 +97,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should handle duplicate file names', async () => { @@ -121,8 +120,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should be deterministic', async () => { @@ -144,8 +143,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should resolve symlinks', async () => { @@ -291,7 +290,7 @@ describe(DownloadService.name, () => { id: 'asset-2', livePhotoVideoId: null, size: 23_456, - originalPath: APP_MEDIA_LOCATION + '/encoded-video/uuid-MP.mp4', + originalPath: '/data/encoded-video/uuid-MP.mp4', }, ]), ); diff --git a/server/src/services/index.ts b/server/src/services/index.ts index 88b68d2c13..cad38ca1f4 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -5,6 +5,7 @@ import { ApiService } from 'src/services/api.service'; import { AssetMediaService } from 'src/services/asset-media.service'; import { AssetService } from 'src/services/asset.service'; import { AuditService } from 'src/services/audit.service'; +import { AuthAdminService } from 'src/services/auth-admin.service'; import { AuthService } from 'src/services/auth.service'; import { BackupService } from 'src/services/backup.service'; import { CliService } from 'src/services/cli.service'; @@ -49,6 +50,7 @@ export const services = [ AssetService, AuditService, AuthService, + AuthAdminService, BackupService, CliService, DatabaseService, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index a57db736af..6b85cdff4d 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -11,7 +11,7 @@ describe(JobService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(JobService, {})); + ({ sut, mocks } = newTestService(JobService)); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); }); @@ -42,6 +42,7 @@ describe(JobService.name, () => { { name: JobName.PersonCleanup }, { name: JobName.MemoryCleanup }, { name: JobName.SessionCleanup }, + { name: JobName.AuditTableCleanup }, { name: JobName.AuditLogCleanup }, { name: JobName.MemoryGenerate }, { name: JobName.UserSyncUsage }, @@ -238,11 +239,11 @@ describe(JobService.name, () => { const tests: Array<{ item: JobItem; jobs: JobName[]; stub?: any }> = [ { - item: { name: JobName.SidecarSync, data: { id: 'asset-1' } }, + item: { name: JobName.SidecarCheck, data: { id: 'asset-1' } }, jobs: [JobName.AssetExtractMetadata], }, { - item: { name: JobName.SidecarDiscovery, data: { id: 'asset-1' } }, + item: { name: JobName.SidecarCheck, data: { id: 'asset-1' } }, jobs: [JobName.AssetExtractMetadata], }, { diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index c67f3af39f..dc48c03bd1 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -281,6 +281,7 @@ export class JobService extends BaseService { { name: JobName.PersonCleanup }, { name: JobName.MemoryCleanup }, { name: JobName.SessionCleanup }, + { name: JobName.AuditTableCleanup }, { name: JobName.AuditLogCleanup }, ); } @@ -309,8 +310,7 @@ export class JobService extends BaseService { */ private async onDone(item: JobItem) { switch (item.name) { - case JobName.SidecarSync: - case JobName.SidecarDiscovery: { + case JobName.SidecarCheck: { await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: item.data }); break; } @@ -382,6 +382,7 @@ export class JobService extends BaseService { visibility: asset.visibility, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, exif: { assetId: exif.assetId, diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 308c80fb37..64f0915698 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION, JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; +import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { LibraryService } from 'src/services/library.service'; @@ -24,7 +24,7 @@ describe(LibraryService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(LibraryService, {})); + ({ sut, mocks } = newTestService(LibraryService)); mocks.database.tryLock.mockResolvedValue(true); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); @@ -527,7 +527,7 @@ describe(LibraryService.name, () => { expect(mocks.job.queueAll).toHaveBeenCalledWith([ { - name: JobName.SidecarDiscovery, + name: JobName.SidecarCheck, data: { id: assetStub.external.id, source: 'upload', @@ -573,7 +573,7 @@ describe(LibraryService.name, () => { expect(mocks.job.queueAll).toHaveBeenCalledWith([ { - name: JobName.SidecarDiscovery, + name: JobName.SidecarCheck, data: { id: assetStub.image.id, source: 'upload', @@ -1171,10 +1171,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(true); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: true, message: undefined, }, @@ -1188,10 +1188,10 @@ describe(LibraryService.name, () => { throw error; }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Path does not exist (ENOENT)', }, @@ -1204,10 +1204,10 @@ describe(LibraryService.name, () => { isDirectory: () => false, } as Stats); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/file'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/file'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/file', + importPath: '/external/user1/file', isValid: false, message: 'Not a directory', }, @@ -1220,10 +1220,10 @@ describe(LibraryService.name, () => { throw new Error('Unknown error'); }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Error: Unknown error', }, @@ -1238,10 +1238,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(false); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Lacking read permission for folder', }, @@ -1264,7 +1264,7 @@ describe(LibraryService.name, () => { }); it('should detect when import path is in immich media folder', async () => { - const importPaths = [APP_MEDIA_LOCATION + '/thumbs', `${process.cwd()}/xyz`, APP_MEDIA_LOCATION + '/library']; + const importPaths = ['/data/thumbs', `${process.cwd()}/xyz`, '/data/library']; const library = factory.library({ importPaths }); mocks.storage.stat.mockResolvedValue({ isDirectory: () => true } as Stats); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 4c96ad0062..5f78fa3629 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -123,6 +123,10 @@ export class LibraryService extends BaseService { { usePolling: false, ignoreInitial: true, + awaitWriteFinish: { + stabilityThreshold: 5000, + pollInterval: 1000, + }, }, { onReady: () => _resolve(), @@ -241,7 +245,7 @@ export class LibraryService extends BaseService { job.paths.map((path) => this.processEntity(path, library.ownerId, job.libraryId) .then((asset) => assetImports.push(asset)) - .catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}`, error)), + .catch((error: any) => this.logger.error(`Error processing ${path} for library ${job.libraryId}: ${error}`)), ), ); @@ -410,7 +414,7 @@ export class LibraryService extends BaseService { // We queue a sidecar discovery which, in turn, queues metadata extraction await this.jobRepository.queueAll( assetIds.map((assetId) => ({ - name: JobName.SidecarDiscovery, + name: JobName.SidecarCheck, data: { id: assetId, source: 'upload' }, })), ); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 0f4ba769c0..ad52b0e8b0 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,6 +1,5 @@ import { OutputInfo } from 'sharp'; import { SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { Exif } from 'src/database'; import { AssetFileType, @@ -43,7 +42,6 @@ describe(MediaService.name, () => { mocks.assetJob.streamForThumbnailJob.mockReturnValue(makeStream([assetStub.image])); mocks.person.getAll.mockReturnValue(makeStream([personStub.newThumbnail])); - mocks.person.getFacesByIds.mockResolvedValue([faceStub.face1]); await sut.handleQueueGenerateThumbnails({ force: true }); @@ -205,19 +203,19 @@ describe(MediaService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.FullSize, oldPath: '/uploads/user-id/fullsize/path.webp', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Preview, oldPath: '/uploads/user-id/thumbs/path.jpg', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-preview.jpeg'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-preview.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Thumbnail, oldPath: '/uploads/user-id/webp/path.ext', - newPath: expect.stringContaining('upload/thumbs/user-id/as/se/asset-id-thumbnail.webp'), + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-thumbnail.webp'), }); expect(mocks.move.create).toHaveBeenCalledTimes(3); }); @@ -369,7 +367,7 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,scale=-2:1440:flags=lanczos+accurate_rnd+full_chroma_int:out_range=pc`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,scale=-2:1440:flags=lanczos+accurate_rnd+full_chroma_int:out_range=pc`, ], twoPass: false, }), @@ -404,7 +402,7 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, ], twoPass: false, }), @@ -441,12 +439,13 @@ describe(MediaService.name, () => { '-frames:v 1', '-update 1', '-v verbose', - String.raw`-vf fps=12:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, + String.raw`-vf fps=12:start_time=0:eof_action=pass:round=down,thumbnail=12,select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20),trim=end_frame=2,reverse,tonemapx=tonemap=hable:desat=0:p=bt709:t=bt709:m=bt709:r=pc:peak=100:format=yuv420p`, ], twoPass: false, }), ); }); + it('should not skip intra frames for MTS file', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStreamMTS); mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); @@ -464,6 +463,25 @@ describe(MediaService.name, () => { ); }); + it('should override reserved color metadata', async () => { + mocks.media.probe.mockResolvedValue(probeStub.videoStreamReserved); + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); + await sut.handleGenerateThumbnails({ id: assetStub.video.id }); + + expect(mocks.media.transcode).toHaveBeenCalledWith( + '/original/path.ext', + expect.any(String), + expect.objectContaining({ + inputOptions: expect.arrayContaining([ + '-bsf:v hevc_metadata=colour_primaries=1:matrix_coefficients=1:transfer_characteristics=1', + ]), + outputOptions: expect.any(Array), + progress: expect.any(Object), + twoPass: false, + }), + ); + }); + it('should use scaling divisible by 2 even when using quick sync', async () => { mocks.media.probe.mockResolvedValue(probeStub.videoStream2160p); mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { accel: TranscodeHardwareAcceleration.Qsv } }); @@ -486,8 +504,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-preview.${format}`; - const thumbnailPath = APP_MEDIA_LOCATION + `/thumbs/user-id/as/se/asset-id-thumbnail.webp`; + const previewPath = `/data/thumbs/user-id/as/se/asset-id-preview.${format}`; + const thumbnailPath = `/data/thumbs/user-id/as/se/asset-id-thumbnail.webp`; await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -531,8 +549,8 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-preview.jpeg`); - const thumbnailPath = expect.stringContaining(`upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); + const previewPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-preview.jpeg`); + const thumbnailPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); @@ -843,6 +861,37 @@ describe(MediaService.name, () => { ); }); + it('should always generate full-size preview from non-web-friendly panoramas', async () => { + mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: false } } }); + mocks.media.extract.mockResolvedValue({ buffer: extractedBuffer, format: RawExtractedFormat.Jpeg }); + mocks.media.getImageDimensions.mockResolvedValue({ width: 3840, height: 2160 }); + + mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.panoramaTif); + + await sut.handleGenerateThumbnails({ id: assetStub.image.id }); + + expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); + expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.panoramaTif.originalPath, { + colorspace: Colorspace.Srgb, + orientation: undefined, + processInvalidImages: false, + size: undefined, + }); + + expect(mocks.media.generateThumbnail).toHaveBeenCalledTimes(3); + expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( + rawBuffer, + { + colorspace: Colorspace.Srgb, + format: ImageFormat.Jpeg, + quality: 80, + processInvalidImages: false, + raw: rawInfo, + }, + expect.any(String), + ); + }); + it('should respect encoding options when generating full-size preview', async () => { mocks.systemMetadata.get.mockResolvedValue({ image: { fullsize: { enabled: true, format: ImageFormat.Webp, quality: 90 } }, @@ -2895,7 +2944,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - APP_MEDIA_LOCATION + '/encoded-video/user-id/as/se/asset-id.mp4', + '/data/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:a copy']), diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index a2c3c5ed42..6caa682f5e 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -271,7 +271,9 @@ export class MediaService extends BaseService { // Handle embedded preview extraction for RAW files const extractEmbedded = image.extractEmbedded && mimeTypes.isRaw(asset.originalFileName); const extracted = extractEmbedded ? await this.extractImage(asset.originalPath, image.preview.size) : null; - const generateFullsize = image.fullsize.enabled && !mimeTypes.isWebSupportedImage(asset.originalPath); + const generateFullsize = + (image.fullsize.enabled || asset.exifInfo.projectionType == 'EQUIRECTANGULAR') && + !mimeTypes.isWebSupportedImage(asset.originalPath); const convertFullsize = generateFullsize && (!extracted || !mimeTypes.isWebSupportedImage(` .${extracted.format}`)); const { info, data, colorspace } = await this.decodeImage( diff --git a/server/src/services/memory.service.ts b/server/src/services/memory.service.ts index 7bf9deab4b..1d39169f3e 100644 --- a/server/src/services/memory.service.ts +++ b/server/src/services/memory.service.ts @@ -40,7 +40,7 @@ export class MemoryService extends BaseService { try { await Promise.all(users.map((owner, i) => this.createOnThisDayMemories(owner.id, usersIds[i], target))); } catch (error) { - this.logger.error(`Failed to create memories for ${target.toISO()}`, error); + this.logger.error(`Failed to create memories for ${target.toISO()}: ${error}`); } // update system metadata even when there is an error to minimize the chance of duplicates await this.systemMetadataRepository.set(SystemMetadataKey.MemoriesState, { diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index cc0956b9a8..0adb390f6a 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1,7 +1,6 @@ import { BinaryField, ExifDateTime } from 'exiftool-vendored'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; -import { constants } from 'node:fs/promises'; import { defaults } from 'src/config'; import { MapAsset } from 'src/dtos/asset-response.dto'; import { AssetType, AssetVisibility, ExifOrientation, ImmichWorker, JobName, JobStatus, SourceType } from 'src/enum'; @@ -15,6 +14,21 @@ import { tagStub } from 'test/fixtures/tag.stub'; import { factory } from 'test/small.factory'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; +const forSidecarJob = ( + asset: { + id?: string; + originalPath?: string; + sidecarPath?: string | null; + } = {}, +) => { + return { + id: factory.uuid(), + originalPath: '/path/to/IMG_123.jpg', + sidecarPath: null, + ...asset, + }; +}; + const makeFaceTags = (face: Partial<{ Name: string }> = {}, orientation?: ImmichTags['Orientation']) => ({ Orientation: orientation, RegionInfo: { @@ -587,7 +601,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -645,7 +659,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -703,7 +717,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: expect.stringContaining('upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -1457,7 +1471,7 @@ describe(MetadataService.name, () => { expect(mocks.job.queueAll).toHaveBeenCalledWith([ { - name: JobName.SidecarSync, + name: JobName.SidecarCheck, data: { id: assetStub.sidecar.id }, }, ]); @@ -1471,133 +1485,65 @@ describe(MetadataService.name, () => { expect(mocks.assetJob.streamForSidecar).toHaveBeenCalledWith(false); expect(mocks.job.queueAll).toHaveBeenCalledWith([ { - name: JobName.SidecarDiscovery, + name: JobName.SidecarCheck, data: { id: assetStub.image.id }, }, ]); }); }); - describe('handleSidecarSync', () => { + describe('handleSidecarCheck', () => { it('should do nothing if asset could not be found', async () => { - mocks.asset.getByIds.mockResolvedValue([]); - await expect(sut.handleSidecarSync({ id: assetStub.image.id })).resolves.toBe(JobStatus.Failed); + mocks.assetJob.getForSidecarCheckJob.mockResolvedValue(void 0); + + await expect(sut.handleSidecarCheck({ id: assetStub.image.id })).resolves.toBeUndefined(); + expect(mocks.asset.update).not.toHaveBeenCalled(); }); - it('should do nothing if asset has no sidecar path', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - await expect(sut.handleSidecarSync({ id: assetStub.image.id })).resolves.toBe(JobStatus.Failed); - expect(mocks.asset.update).not.toHaveBeenCalled(); + it('should detect a new sidecar at .jpg.xmp', async () => { + const asset = forSidecarJob({ originalPath: '/path/to/IMG_123.jpg' }); + + mocks.assetJob.getForSidecarCheckJob.mockResolvedValue(asset); + mocks.storage.checkFileExists.mockResolvedValueOnce(true); + + await expect(sut.handleSidecarCheck({ id: asset.id })).resolves.toBe(JobStatus.Success); + + expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, sidecarPath: `/path/to/IMG_123.jpg.xmp` }); }); - it('should set sidecar path if exists (sidecar named photo.ext.xmp)', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecar]); - mocks.storage.checkFileExists.mockResolvedValue(true); + it('should detect a new sidecar at .xmp', async () => { + const asset = forSidecarJob({ originalPath: '/path/to/IMG_123.jpg' }); - await expect(sut.handleSidecarSync({ id: assetStub.sidecar.id })).resolves.toBe(JobStatus.Success); - expect(mocks.storage.checkFileExists).toHaveBeenCalledWith( - `${assetStub.sidecar.originalPath}.xmp`, - constants.R_OK, - ); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.sidecar.id, - sidecarPath: assetStub.sidecar.sidecarPath, - }); - }); - - it('should set sidecar path if exists (sidecar named photo.xmp)', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecarWithoutExt as any]); + mocks.assetJob.getForSidecarCheckJob.mockResolvedValue(asset); mocks.storage.checkFileExists.mockResolvedValueOnce(false); mocks.storage.checkFileExists.mockResolvedValueOnce(true); - await expect(sut.handleSidecarSync({ id: assetStub.sidecarWithoutExt.id })).resolves.toBe(JobStatus.Success); - expect(mocks.storage.checkFileExists).toHaveBeenNthCalledWith( - 2, - assetStub.sidecarWithoutExt.sidecarPath, - constants.R_OK, - ); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.sidecarWithoutExt.id, - sidecarPath: assetStub.sidecarWithoutExt.sidecarPath, - }); - }); + await expect(sut.handleSidecarCheck({ id: asset.id })).resolves.toBe(JobStatus.Success); - it('should set sidecar path if exists (two sidecars named photo.ext.xmp and photo.xmp, should pick photo.ext.xmp)', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecar]); - mocks.storage.checkFileExists.mockResolvedValueOnce(true); - mocks.storage.checkFileExists.mockResolvedValueOnce(true); - - await expect(sut.handleSidecarSync({ id: assetStub.sidecar.id })).resolves.toBe(JobStatus.Success); - expect(mocks.storage.checkFileExists).toHaveBeenNthCalledWith(1, assetStub.sidecar.sidecarPath, constants.R_OK); - expect(mocks.storage.checkFileExists).toHaveBeenNthCalledWith( - 2, - assetStub.sidecarWithoutExt.sidecarPath, - constants.R_OK, - ); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.sidecar.id, - sidecarPath: assetStub.sidecar.sidecarPath, - }); + expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, sidecarPath: '/path/to/IMG_123.xmp' }); }); it('should unset sidecar path if file does not exist anymore', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecar]); + const asset = forSidecarJob({ originalPath: '/path/to/IMG_123.jpg', sidecarPath: '/path/to/IMG_123.jpg.xmp' }); + mocks.assetJob.getForSidecarCheckJob.mockResolvedValue(asset); mocks.storage.checkFileExists.mockResolvedValue(false); - await expect(sut.handleSidecarSync({ id: assetStub.sidecar.id })).resolves.toBe(JobStatus.Success); - expect(mocks.storage.checkFileExists).toHaveBeenCalledWith( - `${assetStub.sidecar.originalPath}.xmp`, - constants.R_OK, - ); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.sidecar.id, - sidecarPath: null, - }); - }); - }); + await expect(sut.handleSidecarCheck({ id: asset.id })).resolves.toBe(JobStatus.Success); - describe('handleSidecarDiscovery', () => { - it('should skip hidden assets', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.livePhotoMotionAsset as any]); - await sut.handleSidecarDiscovery({ id: assetStub.livePhotoMotionAsset.id }); - expect(mocks.storage.checkFileExists).not.toHaveBeenCalled(); + expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, sidecarPath: null }); }); - it('should skip assets with a sidecar path', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.sidecar]); - await sut.handleSidecarDiscovery({ id: assetStub.sidecar.id }); - expect(mocks.storage.checkFileExists).not.toHaveBeenCalled(); - }); + it('should do nothing if the sidecar file still exists', async () => { + const asset = forSidecarJob({ originalPath: '/path/to/IMG_123.jpg', sidecarPath: '/path/to/IMG_123.jpg' }); + + mocks.assetJob.getForSidecarCheckJob.mockResolvedValue(asset); + mocks.storage.checkFileExists.mockResolvedValueOnce(true); + + await expect(sut.handleSidecarCheck({ id: asset.id })).resolves.toBe(JobStatus.Skipped); - it('should do nothing when a sidecar is not found ', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - mocks.storage.checkFileExists.mockResolvedValue(false); - await sut.handleSidecarDiscovery({ id: assetStub.image.id }); expect(mocks.asset.update).not.toHaveBeenCalled(); }); - - it('should update a image asset when a sidecar is found', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.image]); - mocks.storage.checkFileExists.mockResolvedValue(true); - await sut.handleSidecarDiscovery({ id: assetStub.image.id }); - expect(mocks.storage.checkFileExists).toHaveBeenCalledWith('/original/path.jpg.xmp', constants.R_OK); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.image.id, - sidecarPath: '/original/path.jpg.xmp', - }); - }); - - it('should update a video asset when a sidecar is found', async () => { - mocks.asset.getByIds.mockResolvedValue([assetStub.video]); - mocks.storage.checkFileExists.mockResolvedValue(true); - await sut.handleSidecarDiscovery({ id: assetStub.video.id }); - expect(mocks.storage.checkFileExists).toHaveBeenCalledWith('/original/path.ext.xmp', constants.R_OK); - expect(mocks.asset.update).toHaveBeenCalledWith({ - id: assetStub.image.id, - sidecarPath: '/original/path.ext.xmp', - }); - }); }); describe('handleSidecarWrite', () => { @@ -1714,5 +1660,16 @@ describe(MetadataService.name, () => { expect(result?.tag).toBe('GPSDateTime'); expect(result?.dateTime?.toDate()?.toISOString()).toBe('2023-10-10T10:00:00.000Z'); }); + + it('should prefer CreationDate over CreateDate', () => { + const tags = { + CreationDate: '2025:05:24 18:26:20+02:00', + CreateDate: '2025:08:27 08:45:40', + }; + + const result = firstDateTime(tags); + expect(result?.tag).toBe('CreationDate'); + expect(result?.dateTime?.toDate()?.toISOString()).toBe('2025-05-24T16:26:20.000Z'); + }); }); }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 32a3d98f4e..7d3de76550 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import { Duration } from 'luxon'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; -import path from 'node:path'; +import { join, parse } from 'node:path'; import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { Asset, AssetFace } from 'src/database'; @@ -29,6 +29,7 @@ import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { PersonTable } from 'src/schema/tables/person.table'; import { BaseService } from 'src/services/base.service'; import { JobItem, JobOf } from 'src/types'; +import { isAssetChecksumConstraint } from 'src/utils/database'; import { isFaceImportEnabled } from 'src/utils/misc'; import { upsertTags } from 'src/utils/tag'; @@ -38,9 +39,9 @@ const EXIF_DATE_TAGS: Array = [ 'SubSecCreateDate', 'SubSecMediaCreateDate', 'DateTimeOriginal', + 'CreationDate', 'CreateDate', 'MediaCreateDate', - 'CreationDate', 'DateTimeCreated', 'GPSDateTime', 'DateTimeUTC', @@ -101,7 +102,7 @@ const validateRange = (value: number | undefined, min: number, max: number): Non return null; } - return val; + return Math.round(val); }; const getLensModel = (exifTags: ImmichTags): string | null => { @@ -330,7 +331,7 @@ export class MetadataService extends BaseService { const assets = this.assetJobRepository.streamForSidecar(force); for await (const asset of assets) { - jobs.push({ name: force ? JobName.SidecarSync : JobName.SidecarDiscovery, data: { id: asset.id } }); + jobs.push({ name: JobName.SidecarCheck, data: { id: asset.id } }); if (jobs.length >= JOBS_ASSET_PAGINATION_SIZE) { await queueAll(); } @@ -341,14 +342,37 @@ export class MetadataService extends BaseService { return JobStatus.Success; } - @OnJob({ name: JobName.SidecarSync, queue: QueueName.Sidecar }) - handleSidecarSync({ id }: JobOf): Promise { - return this.processSidecar(id, true); - } + @OnJob({ name: JobName.SidecarCheck, queue: QueueName.Sidecar }) + async handleSidecarCheck({ id }: JobOf): Promise { + const asset = await this.assetJobRepository.getForSidecarCheckJob(id); + if (!asset) { + return; + } - @OnJob({ name: JobName.SidecarDiscovery, queue: QueueName.Sidecar }) - handleSidecarDiscovery({ id }: JobOf): Promise { - return this.processSidecar(id, false); + let sidecarPath = null; + for (const candidate of this.getSidecarCandidates(asset)) { + const exists = await this.storageRepository.checkFileExists(candidate, constants.R_OK); + if (!exists) { + continue; + } + + sidecarPath = candidate; + break; + } + + const isChanged = sidecarPath !== asset.sidecarPath; + + this.logger.debug( + `Sidecar check found old=${asset.sidecarPath}, new=${sidecarPath} will ${isChanged ? 'update' : 'do nothing for'} asset ${asset.id}: ${asset.originalPath}`, + ); + + if (!isChanged) { + return JobStatus.Skipped; + } + + await this.assetRepository.update({ id: asset.id, sidecarPath }); + + return JobStatus.Success; } @OnEvent({ name: 'AssetTag' }) @@ -398,6 +422,25 @@ export class MetadataService extends BaseService { return JobStatus.Success; } + private getSidecarCandidates({ sidecarPath, originalPath }: { sidecarPath: string | null; originalPath: string }) { + const candidates: string[] = []; + + if (sidecarPath) { + candidates.push(sidecarPath); + } + + const assetPath = parse(originalPath); + + candidates.push( + // IMG_123.jpg.xmp + `${originalPath}.xmp`, + // IMG_123.xmp + `${join(assetPath.dir, assetPath.name)}.xmp`, + ); + + return candidates; + } + private getImageDimensions(exifTags: ImmichTags): { width?: number; height?: number } { /* * The "true" values for width and height are a bit hidden, depending on the camera model and file format. @@ -545,47 +588,62 @@ export class MetadataService extends BaseService { }); } const checksum = this.cryptoRepository.hashSha1(video); + const checksumQuery = { ownerId: asset.ownerId, libraryId: asset.libraryId ?? undefined, checksum }; - let motionAsset = await this.assetRepository.getByChecksum({ - ownerId: asset.ownerId, - libraryId: asset.libraryId ?? undefined, - checksum, - }); - if (motionAsset) { + let motionAsset = await this.assetRepository.getByChecksum(checksumQuery); + let isNewMotionAsset = false; + + if (!motionAsset) { + try { + const motionAssetId = this.cryptoRepository.randomUUID(); + motionAsset = await this.assetRepository.create({ + id: motionAssetId, + libraryId: asset.libraryId, + type: AssetType.Video, + fileCreatedAt: dates.dateTimeOriginal, + fileModifiedAt: stats.mtime, + localDateTime: dates.localDateTime, + checksum, + ownerId: asset.ownerId, + originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), + originalFileName: `${parse(asset.originalFileName).name}.mp4`, + visibility: AssetVisibility.Hidden, + deviceAssetId: 'NONE', + deviceId: 'NONE', + }); + + isNewMotionAsset = true; + + if (!asset.isExternal) { + await this.userRepository.updateUsage(asset.ownerId, video.byteLength); + } + } catch (error) { + if (!isAssetChecksumConstraint(error)) { + throw error; + } + + motionAsset = await this.assetRepository.getByChecksum(checksumQuery); + if (!motionAsset) { + this.logger.warn(`Unable to find existing motion video asset for ${asset.id}: ${asset.originalPath}`); + return; + } + } + } + + if (!isNewMotionAsset) { this.logger.debugFn(() => { const base64Checksum = checksum.toString('base64'); return `Motion asset with checksum ${base64Checksum} already exists for asset ${asset.id}: ${asset.originalPath}`; }); + } - // Hide the motion photo video asset if it's not already hidden to prepare for linking - if (motionAsset.visibility === AssetVisibility.Timeline) { - await this.assetRepository.update({ - id: motionAsset.id, - visibility: AssetVisibility.Hidden, - }); - this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); - } - } else { - const motionAssetId = this.cryptoRepository.randomUUID(); - motionAsset = await this.assetRepository.create({ - id: motionAssetId, - libraryId: asset.libraryId, - type: AssetType.Video, - fileCreatedAt: dates.dateTimeOriginal, - fileModifiedAt: stats.mtime, - localDateTime: dates.localDateTime, - checksum, - ownerId: asset.ownerId, - originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), - originalFileName: `${path.parse(asset.originalFileName).name}.mp4`, + // Hide the motion photo video asset if it's not already hidden to prepare for linking + if (motionAsset.visibility === AssetVisibility.Timeline) { + await this.assetRepository.update({ + id: motionAsset.id, visibility: AssetVisibility.Hidden, - deviceAssetId: 'NONE', - deviceId: 'NONE', }); - - if (!asset.isExternal) { - await this.userRepository.updateUsage(asset.ownerId, video.byteLength); - } + this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); } if (asset.livePhotoVideoId !== motionAsset.id) { @@ -777,7 +835,11 @@ export class MetadataService extends BaseService { } } - private getDates(asset: { id: string; originalPath: string }, exifTags: ImmichTags, stats: Stats) { + private getDates( + asset: { id: string; originalPath: string; fileCreatedAt: Date }, + exifTags: ImmichTags, + stats: Stats, + ) { const result = firstDateTime(exifTags); const tag = result?.tag; const dateTime = result?.dateTime; @@ -806,7 +868,12 @@ export class MetadataService extends BaseService { if (!localDateTime || !dateTimeOriginal) { // FileCreateDate is not available on linux, likely because exiftool hasn't integrated the statx syscall yet // birthtime is not available in Docker on macOS, so it appears as 0 - const earliestDate = stats.birthtimeMs ? new Date(Math.min(stats.mtimeMs, stats.birthtimeMs)) : stats.mtime; + const earliestDate = new Date( + Math.min( + asset.fileCreatedAt.getTime(), + stats.birthtimeMs ? Math.min(stats.mtimeMs, stats.birthtimeMs) : stats.mtime.getTime(), + ), + ); this.logger.debug( `No exif date time found, falling back on ${earliestDate.toISOString()}, earliest of file creation and modification for asset ${asset.id}: ${asset.originalPath}`, ); @@ -889,60 +956,4 @@ export class MetadataService extends BaseService { return tags; } - - private async processSidecar(id: string, isSync: boolean): Promise { - const [asset] = await this.assetRepository.getByIds([id]); - - if (!asset) { - return JobStatus.Failed; - } - - if (isSync && !asset.sidecarPath) { - return JobStatus.Failed; - } - - if (!isSync && (asset.visibility === AssetVisibility.Hidden || asset.sidecarPath) && !asset.isExternal) { - return JobStatus.Failed; - } - - // XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp - const assetPath = path.parse(asset.originalPath); - const assetPathWithoutExt = path.join(assetPath.dir, assetPath.name); - const sidecarPathWithoutExt = `${assetPathWithoutExt}.xmp`; - const sidecarPathWithExt = `${asset.originalPath}.xmp`; - - const [sidecarPathWithExtExists, sidecarPathWithoutExtExists] = await Promise.all([ - this.storageRepository.checkFileExists(sidecarPathWithExt, constants.R_OK), - this.storageRepository.checkFileExists(sidecarPathWithoutExt, constants.R_OK), - ]); - - let sidecarPath = null; - if (sidecarPathWithExtExists) { - sidecarPath = sidecarPathWithExt; - } else if (sidecarPathWithoutExtExists) { - sidecarPath = sidecarPathWithoutExt; - } - - if (asset.isExternal) { - if (sidecarPath !== asset.sidecarPath) { - await this.assetRepository.update({ id: asset.id, sidecarPath }); - } - return JobStatus.Success; - } - - if (sidecarPath) { - this.logger.debug(`Detected sidecar at '${sidecarPath}' for asset ${asset.id}: ${asset.originalPath}`); - await this.assetRepository.update({ id: asset.id, sidecarPath }); - return JobStatus.Success; - } - - if (!isSync) { - return JobStatus.Failed; - } - - this.logger.debug(`No sidecar found for asset ${asset.id}: ${asset.originalPath}`); - await this.assetRepository.update({ id: asset.id, sidecarPath: null }); - - return JobStatus.Success; - } } diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index eef1c4f8b2..11c385b1e2 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -147,7 +147,7 @@ describe(NotificationService.name, () => { await sut.onUserSignup({ id: '', notify: true }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.NotifyUserSignup, - data: { id: '', tempPassword: undefined }, + data: { id: '', password: undefined }, }); }); }); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index 1a257309b2..91a043d405 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -191,9 +191,9 @@ export class NotificationService extends BaseService { } @OnEvent({ name: 'UserSignup' }) - async onUserSignup({ notify, id, tempPassword }: ArgOf<'UserSignup'>) { + async onUserSignup({ notify, id, password: password }: ArgOf<'UserSignup'>) { if (notify) { - await this.jobRepository.queue({ name: JobName.NotifyUserSignup, data: { id, tempPassword } }); + await this.jobRepository.queue({ name: JobName.NotifyUserSignup, data: { id, password } }); } } @@ -251,70 +251,8 @@ export class NotificationService extends BaseService { 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 }; - } - @OnJob({ name: JobName.NotifyUserSignup, queue: QueueName.Notification }) - async handleUserSignup({ id, tempPassword }: JobOf) { + async handleUserSignup({ id, password }: JobOf) { const user = await this.userRepository.get(id, { withDeleted: false }); if (!user) { return JobStatus.Skipped; @@ -327,7 +265,7 @@ export class NotificationService extends BaseService { baseUrl: getExternalDomain(server), displayName: user.name, username: user.email, - password: tempPassword, + password, }, customTemplate: templates.email.welcomeTemplate, }); diff --git a/server/src/services/partner.service.spec.ts b/server/src/services/partner.service.spec.ts index c6d5762c2c..db057a453a 100644 --- a/server/src/services/partner.service.spec.ts +++ b/server/src/services/partner.service.spec.ts @@ -53,7 +53,7 @@ describe(PartnerService.name, () => { mocks.partner.get.mockResolvedValue(void 0); mocks.partner.create.mockResolvedValue(partner); - await expect(sut.create(auth, user2.id)).resolves.toBeDefined(); + await expect(sut.create(auth, { sharedWithId: user2.id })).resolves.toBeDefined(); expect(mocks.partner.create).toHaveBeenCalledWith({ sharedById: partner.sharedById, @@ -69,7 +69,7 @@ describe(PartnerService.name, () => { mocks.partner.get.mockResolvedValue(partner); - await expect(sut.create(auth, user2.id)).rejects.toBeInstanceOf(BadRequestException); + await expect(sut.create(auth, { sharedWithId: user2.id })).rejects.toBeInstanceOf(BadRequestException); expect(mocks.partner.create).not.toHaveBeenCalled(); }); diff --git a/server/src/services/partner.service.ts b/server/src/services/partner.service.ts index 755b688397..628efa9d49 100644 --- a/server/src/services/partner.service.ts +++ b/server/src/services/partner.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { Partner } from 'src/database'; import { AuthDto } from 'src/dtos/auth.dto'; -import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto'; +import { PartnerCreateDto, PartnerResponseDto, PartnerSearchDto, PartnerUpdateDto } from 'src/dtos/partner.dto'; import { mapUser } from 'src/dtos/user.dto'; import { Permission } from 'src/enum'; import { PartnerDirection, PartnerIds } from 'src/repositories/partner.repository'; @@ -9,7 +9,7 @@ import { BaseService } from 'src/services/base.service'; @Injectable() export class PartnerService extends BaseService { - async create(auth: AuthDto, sharedWithId: string): Promise { + async create(auth: AuthDto, { sharedWithId }: PartnerCreateDto): Promise { const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId }; const exists = await this.partnerRepository.get(partnerId); if (exists) { @@ -39,7 +39,7 @@ export class PartnerService extends BaseService { .map((partner) => this.mapPartner(partner, direction)); } - async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise { + async update(auth: AuthDto, sharedById: string, dto: PartnerUpdateDto): Promise { await this.requireAccess({ auth, permission: Permission.PartnerUpdate, ids: [sharedById] }); const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id }; diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 13c3128317..41c44ea476 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -729,7 +729,6 @@ describe(PersonService.name, () => { mocks.assetJob.getForDetectFacesJob.mockResolvedValue({ ...assetStub.image, files: [assetStub.image.files[1]] }); await sut.handleDetectFaces({ id: assetStub.image.id }); expect(mocks.machineLearning.detectFaces).toHaveBeenCalledWith( - ['http://immich-machine-learning:3003'], '/uploads/user-id/thumbs/path.jpg', expect.objectContaining({ minScore: 0.7, modelName: 'buffalo_l' }), ); diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index d0c43c3dad..6fa9b3fdd2 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -197,6 +197,10 @@ export class PersonService extends BaseService { throw new BadRequestException('Invalid assetId for feature face'); } + if (face.asset.isOffline) { + throw new BadRequestException('An offline asset cannot be used for feature face'); + } + faceId = face.id; } @@ -312,7 +316,6 @@ export class PersonService extends BaseService { } const { imageHeight, imageWidth, faces } = await this.machineLearningRepository.detectFaces( - machineLearning.urls, previewFile.path, machineLearning.facialRecognition, ); diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index d87ccbde1d..b6e09add19 100644 --- a/server/src/services/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -211,7 +211,6 @@ describe(SearchService.name, () => { await sut.searchSmart(authStub.user1, { query: 'test' }); expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith( - [expect.any(String)], 'test', expect.objectContaining({ modelName: expect.any(String) }), ); @@ -225,7 +224,6 @@ describe(SearchService.name, () => { await sut.searchSmart(authStub.user1, { query: 'test', page: 2, size: 50 }); expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith( - [expect.any(String)], 'test', expect.objectContaining({ modelName: expect.any(String) }), ); @@ -243,7 +241,6 @@ describe(SearchService.name, () => { await sut.searchSmart(authStub.user1, { query: 'test' }); expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith( - [expect.any(String)], 'test', expect.objectContaining({ modelName: 'ViT-B-16-SigLIP__webli' }), ); @@ -253,7 +250,6 @@ describe(SearchService.name, () => { await sut.searchSmart(authStub.user1, { query: 'test', language: 'de' }); expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith( - [expect.any(String)], 'test', expect.objectContaining({ language: 'de' }), ); diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 1c75c4a434..fea1670e27 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -4,6 +4,7 @@ import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/ import { AuthDto } from 'src/dtos/auth.dto'; import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, mapPlaces, MetadataSearchDto, PlacesResponseDto, @@ -17,7 +18,7 @@ import { SmartSearchDto, StatisticsSearchDto, } from 'src/dtos/search.dto'; -import { AssetOrder, AssetVisibility } from 'src/enum'; +import { AssetOrder, AssetVisibility, Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { requireElevatedPermission } from 'src/utils/access'; import { getMyPartnerIds } from 'src/utils/asset.util'; @@ -91,6 +92,16 @@ export class SearchService extends BaseService { return items.map((item) => mapAsset(item, { auth })); } + async searchLargeAssets(auth: AuthDto, dto: LargeAssetSearchDto): Promise { + if (dto.visibility === AssetVisibility.Locked) { + requireElevatedPermission(auth); + } + + const userIds = await this.getUserIdsToSearch(auth); + const items = await this.searchRepository.searchLargeAssets(dto.size || 250, { ...dto, userIds }); + return items.map((item) => mapAsset(item, { auth })); + } + async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise { if (dto.visibility === AssetVisibility.Locked) { requireElevatedPermission(auth); @@ -102,14 +113,27 @@ export class SearchService extends BaseService { } const userIds = this.getUserIdsToSearch(auth); - const key = machineLearning.clip.modelName + dto.query + dto.language; - let embedding = this.embeddingCache.get(key); - if (!embedding) { - embedding = await this.machineLearningRepository.encodeText(machineLearning.urls, dto.query, { - modelName: machineLearning.clip.modelName, - language: dto.language, - }); - this.embeddingCache.set(key, embedding); + let embedding; + if (dto.query) { + const key = machineLearning.clip.modelName + dto.query + dto.language; + embedding = this.embeddingCache.get(key); + if (!embedding) { + embedding = await this.machineLearningRepository.encodeText(dto.query, { + modelName: machineLearning.clip.modelName, + language: dto.language, + }); + this.embeddingCache.set(key, embedding); + } + } else if (dto.queryAssetId) { + await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.queryAssetId] }); + const getEmbeddingResponse = await this.searchRepository.getEmbedding(dto.queryAssetId); + const assetEmbedding = getEmbeddingResponse?.embedding; + if (!assetEmbedding) { + throw new BadRequestException(`Asset ${dto.queryAssetId} has no embedding`); + } + embedding = assetEmbedding; + } else { + throw new BadRequestException('Either `query` or `queryAssetId` must be set'); } const page = dto.page ?? 1; const size = dto.size || 100; diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 06ddd32601..a96a9925db 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -28,7 +28,7 @@ describe(ServerService.name, () => { diskUseRaw: 300, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as KiB', async () => { @@ -44,7 +44,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as MiB', async () => { @@ -60,7 +60,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as GiB', async () => { @@ -80,7 +80,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as TiB', async () => { @@ -100,7 +100,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as PiB', async () => { @@ -120,7 +120,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('upload/library')); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); }); diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 8e09580d55..9483cdddff 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -136,6 +136,7 @@ describe(SharedLinkService.name, () => { allowUpload: true, description: null, expiresAt: null, + slug: null, showExif: true, key: Buffer.from('random-bytes', 'utf8'), }); @@ -163,6 +164,7 @@ describe(SharedLinkService.name, () => { userId: authStub.admin.user.id, albumId: null, allowDownload: true, + slug: null, allowUpload: true, assetIds: [assetStub.image.id], description: null, @@ -199,6 +201,7 @@ describe(SharedLinkService.name, () => { description: null, expiresAt: null, showExif: false, + slug: null, key: Buffer.from('random-bytes', 'utf8'), }); }); @@ -223,6 +226,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ id: sharedLinkStub.valid.id, + slug: null, userId: authStub.user1.user.id, allowDownload: false, }); @@ -277,6 +281,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.update).toHaveBeenCalled(); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, + slug: null, assetIds: ['asset-3'], }); }); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 9f8e238c43..096739d056 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { PostgresError } from 'postgres'; import { SharedLink } from 'src/database'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; @@ -64,36 +65,53 @@ export class SharedLinkService extends BaseService { } } - const sharedLink = await this.sharedLinkRepository.create({ - key: this.cryptoRepository.randomBytes(50), - userId: auth.user.id, - type: dto.type, - albumId: dto.albumId || null, - assetIds: dto.assetIds, - description: dto.description || null, - password: dto.password, - expiresAt: dto.expiresAt || null, - allowUpload: dto.allowUpload ?? true, - allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), - showExif: dto.showMetadata ?? true, - }); + try { + const sharedLink = await this.sharedLinkRepository.create({ + key: this.cryptoRepository.randomBytes(50), + userId: auth.user.id, + type: dto.type, + albumId: dto.albumId || null, + assetIds: dto.assetIds, + description: dto.description || null, + password: dto.password, + expiresAt: dto.expiresAt || null, + allowUpload: dto.allowUpload ?? true, + allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), + showExif: dto.showMetadata ?? true, + slug: dto.slug || null, + }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } + } + + private handleError(error: unknown): never { + if ((error as PostgresError).constraint_name === 'shared_link_slug_uq') { + throw new BadRequestException('Shared link with this slug already exists'); + } + throw error; } async update(auth: AuthDto, id: string, dto: SharedLinkEditDto) { await this.findOrFail(auth.user.id, id); - const sharedLink = await this.sharedLinkRepository.update({ - id, - userId: auth.user.id, - description: dto.description, - password: dto.password, - expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, - allowUpload: dto.allowUpload, - allowDownload: dto.allowDownload, - showExif: dto.showMetadata, - }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + try { + const sharedLink = await this.sharedLinkRepository.update({ + id, + userId: auth.user.id, + description: dto.description, + password: dto.password, + expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, + allowUpload: dto.allowUpload, + allowDownload: dto.allowDownload, + showExif: dto.showMetadata, + slug: dto.slug || null, + }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } } async remove(auth: AuthDto, id: string): Promise { diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index edd9f4663a..b3af5cd15f 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -205,7 +205,6 @@ describe(SmartInfoService.name, () => { expect(await sut.handleEncodeClip({ id: assetStub.image.id })).toEqual(JobStatus.Success); expect(mocks.machineLearning.encodeImage).toHaveBeenCalledWith( - ['http://immich-machine-learning:3003'], '/uploads/user-id/thumbs/path.jpg', expect.objectContaining({ modelName: 'ViT-B-32__openai' }), ); @@ -242,7 +241,6 @@ describe(SmartInfoService.name, () => { expect(mocks.database.wait).toHaveBeenCalledWith(512); expect(mocks.machineLearning.encodeImage).toHaveBeenCalledWith( - ['http://immich-machine-learning:3003'], '/uploads/user-id/thumbs/path.jpg', expect.objectContaining({ modelName: 'ViT-B-32__openai' }), ); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index 3b8e2d1fc3..eff16fea45 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -108,11 +108,7 @@ export class SmartInfoService extends BaseService { return JobStatus.Skipped; } - const embedding = await this.machineLearningRepository.encodeImage( - machineLearning.urls, - asset.files[0].path, - machineLearning.clip, - ); + const embedding = await this.machineLearningRepository.encodeImage(asset.files[0].path, machineLearning.clip); if (this.databaseRepository.isBusy(DatabaseLock.CLIPDimSize)) { this.logger.verbose(`Waiting for CLIP dimension size to be updated`); diff --git a/server/src/services/stack.service.spec.ts b/server/src/services/stack.service.spec.ts index 5c7b505cd9..5517cf17f8 100644 --- a/server/src/services/stack.service.spec.ts +++ b/server/src/services/stack.service.spec.ts @@ -188,4 +188,53 @@ describe(StackService.name, () => { }); }); }); + + describe('removeAsset', () => { + it('should require stack.update permissions', async () => { + await expect(sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: 'asset-id' })).rejects.toBeInstanceOf( + BadRequestException, + ); + + expect(mocks.stack.getForAssetRemoval).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the asset is not in the stack', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: null, primaryAssetId: null }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.imageFrom2015.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the assetId is the primaryAssetId', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it("should update the asset to nullify it's stack-id", async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image1.id }); + + expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.image1.id, stackId: null }); + expect(mocks.event.emit).toHaveBeenCalledWith('StackUpdate', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); }); diff --git a/server/src/services/stack.service.ts b/server/src/services/stack.service.ts index 18600abd12..c84ec70fbf 100644 --- a/server/src/services/stack.service.ts +++ b/server/src/services/stack.service.ts @@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto'; import { Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; +import { UUIDAssetIDParamDto } from 'src/validation'; @Injectable() export class StackService extends BaseService { @@ -58,6 +59,24 @@ export class StackService extends BaseService { await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id }); } + async removeAsset(auth: AuthDto, dto: UUIDAssetIDParamDto): Promise { + const { id: stackId, assetId } = dto; + await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [stackId] }); + + const stack = await this.stackRepository.getForAssetRemoval(assetId); + + if (!stack?.id || stack.id !== stackId) { + throw new BadRequestException('Asset not in stack'); + } + + if (stack.primaryAssetId === assetId) { + throw new BadRequestException("Cannot remove stack's primary asset"); + } + + await this.assetRepository.update({ id: assetId, stackId: null }); + await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id }); + } + private async findOrFail(id: string) { const stack = await this.stackRepository.getById(id); if (!stack) { diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 882ffcd328..d0d7ea3a3c 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,6 +1,5 @@ import { Stats } from 'node:fs'; import { defaults, SystemConfig } from 'src/config'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { AssetPathType, JobStatus } from 'src/enum'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { albumStub } from 'test/fixtures/album.stub'; @@ -111,10 +110,8 @@ describe(StorageTemplateService.name, () => { it('should migrate single moving picture', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newMotionPicturePath = - APP_MEDIA_LOCATION + `/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; - const newStillPicturePath = - APP_MEDIA_LOCATION + `/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; + const newMotionPicturePath = `/data/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; + const newStillPicturePath = `/data/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(stillAsset); mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(motionAsset); @@ -160,7 +157,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -183,7 +180,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -219,7 +216,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, newPath: expect.stringContaining( - `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, ), oldPath: asset.originalPath, pathType: AssetPathType.Original, @@ -243,9 +240,7 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: - APP_MEDIA_LOCATION + - `/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + newPath: `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -255,9 +250,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset(); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === asset.originalPath)); mocks.move.getByEntity.mockResolvedValue({ @@ -296,9 +290,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset({ fileSizeInByte: 5000 }); - const previousFailedNewPath = - APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; - const newPath = APP_MEDIA_LOCATION + `/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; + const newPath = `/data/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -332,8 +325,7 @@ describe(StorageTemplateService.name, () => { it('should fail move if copying and hash of asset and the new file do not match', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newPath = - APP_MEDIA_LOCATION + `/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -375,8 +367,8 @@ describe(StorageTemplateService.name, () => { 'should fail to migrate previously failed move from previous new path when old path no longer exists if $reason validation fails', async ({ failedPathChecksum, failedPathSize }) => { mocks.user.get.mockResolvedValue(userStub.user1); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(previousFailedNewPath === path)); mocks.storage.stat.mockResolvedValue({ size: failedPathSize } as Stats); @@ -423,7 +415,7 @@ describe(StorageTemplateService.name, () => { it('should handle an asset with a duplicate destination', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; const newPath2 = newPath.replace('.jpg', '+1.jpg'); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -448,7 +440,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset already matches the template', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -463,7 +455,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset is probably a duplicate', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -480,7 +472,7 @@ describe(StorageTemplateService.name, () => { it('should move an asset', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); mocks.move.create.mockResolvedValue({ @@ -508,7 +500,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, + newPath: `/data/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, }); await sut.handleMigration(); @@ -516,12 +508,12 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, originalPath: expect.stringContaining( - `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + `/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, ), }); }); @@ -529,7 +521,7 @@ describe(StorageTemplateService.name, () => { it('should copy the file if rename fails due to EXDEV (rename across filesystems)', async () => { const asset = assetStub.storageAsset({ originalPath: '/path/to/original.jpg', fileSizeInByte: 5000 }); const oldPath = asset.originalPath; - const newPath = APP_MEDIA_LOCATION + `/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -577,7 +569,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + newPath: `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`, }); mocks.storage.stat.mockResolvedValue({ size: 100, @@ -588,14 +580,14 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.copyFile).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.stat).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -619,7 +611,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - expect.stringContaining(`upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`), + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -630,7 +622,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin({ storageLabel: 'label-1' }); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -639,16 +631,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), - expect.stringContaining(`upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -656,7 +648,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -665,16 +657,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -682,7 +674,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, originalFileName: 'IMG_7065.JPEG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -691,16 +683,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); @@ -708,7 +700,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', + originalPath: '/data/library/user-id/2022/2022-06-19/IMG_7065.JPG', originalFileName: 'IMG_7065.JPG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -717,16 +709,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), - expect.stringContaining(`upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); }); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 6086d62809..1d38bf7011 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -338,7 +338,7 @@ export class StorageTemplateService extends BaseService { return destination; } catch (error: any) { - this.logger.error(`Unable to get template path for ${filename}`, error); + this.logger.error(`Unable to get template path for ${filename}: ${error}`); return asset.originalPath; } } diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 567b78ac09..3ca9cd7ce2 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -19,6 +19,15 @@ describe(StorageService.name, () => { describe('onBootstrap', () => { it('should enable mount folder checking', async () => { mocks.systemMetadata.get.mockResolvedValue(null); + mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -32,34 +41,34 @@ describe(StorageService.name, () => { upload: true, }, }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/encoded-video')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/profile')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/thumbs')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/upload')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/encoded-video')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/thumbs')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/.immich'), + expect.stringContaining('/data/encoded-video/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/.immich'), + expect.stringContaining('/data/profile/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/.immich'), + expect.stringContaining('/data/thumbs/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/.immich'), + expect.stringContaining('/data/upload/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); @@ -75,6 +84,15 @@ describe(StorageService.name, () => { upload: true, }, }); + mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -89,15 +107,15 @@ describe(StorageService.name, () => { }, }); expect(mocks.storage.mkdirSync).toHaveBeenCalledTimes(2); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/library')); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('upload/backups')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledTimes(2); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/library/.immich'), + expect.stringContaining('/data/library/.immich'), expect.any(Buffer), ); expect(mocks.storage.createFile).toHaveBeenCalledWith( - expect.stringContaining('upload/backups/.immich'), + expect.stringContaining('/data/backups/.immich'), expect.any(Buffer), ); }); @@ -128,6 +146,7 @@ describe(StorageService.name, () => { error.code = 'EEXIST'; mocks.systemMetadata.get.mockResolvedValue({ mountChecks: {} }); mocks.storage.createFile.mockRejectedValue(error); + mocks.asset.getFileSamples.mockResolvedValue([]); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -149,6 +168,7 @@ describe(StorageService.name, () => { storage: { ignoreMountCheckErrors: true }, }), ); + mocks.asset.getFileSamples.mockResolvedValue([]); mocks.storage.overwriteFile.mockRejectedValue( new Error("ENOENT: no such file or directory, open '/app/.immich'"), ); diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 632e0c1385..50dffd5465 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,20 +1,50 @@ import { Injectable } from '@nestjs/common'; import { join } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; -import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum'; +import { + BootstrapEventPriority, + DatabaseLock, + JobName, + JobStatus, + QueueName, + StorageFolder, + SystemMetadataKey, +} from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { JobOf, SystemFlags } from 'src/types'; import { ImmichStartupError } from 'src/utils/misc'; -const docsMessage = `Please see https://immich.app/docs/administration/system-integrity#folder-checks for more information.`; +const docsMessage = `Please see https://docs.immich.app/administration/system-integrity#folder-checks for more information.`; @Injectable() export class StorageService extends BaseService { - @OnEvent({ name: 'AppBootstrap' }) - async onBootstrap() { + private detectMediaLocation(): string { const envData = this.configRepository.getEnv(); + if (envData.storage.mediaLocation) { + return envData.storage.mediaLocation; + } + + const targets: string[] = []; + const candidates = ['/data', '/usr/src/app/upload']; + + for (const candidate of candidates) { + const exists = this.storageRepository.existsSync(candidate); + if (exists) { + targets.push(candidate); + } + } + + if (targets.length === 1) { + return targets[0]; + } + + return '/usr/src/app/upload'; + } + + @OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.StorageService }) + async onBootstrap() { + StorageCore.setMediaLocation(this.detectMediaLocation()); await this.databaseRepository.withLock(DatabaseLock.SystemFileMounts, async () => { const flags = @@ -53,6 +83,7 @@ export class StorageService extends BaseService { this.logger.log('Successfully verified system mount folder checks'); } catch (error) { + const envData = this.configRepository.getEnv(); if (envData.storage.ignoreMountCheckErrors) { this.logger.error(error as Error); this.logger.warn('Ignoring mount folder errors'); @@ -63,12 +94,40 @@ export class StorageService extends BaseService { }); await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { - const current = APP_MEDIA_LOCATION; + const current = StorageCore.getMediaLocation(); + const samples = await this.assetRepository.getFileSamples(); const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); - const previous = savedValue?.location || ''; + if (samples.length > 0) { + const path = samples[0].path; - if (previous !== current) { - this.logger.log(`Media location changed (from=${previous}, to=${current})`); + let previous = savedValue?.location || ''; + + if (!previous && this.configRepository.getEnv().storage.mediaLocation) { + previous = current; + } + + if (!previous) { + previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; + } + + if (previous !== current) { + this.logger.log(`Media location changed (from=${previous}, to=${current})`); + + if (!path.startsWith(previous)) { + throw new Error( + 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', + ); + } + + this.logger.warn( + `Detected a change to media location, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, + ); + await this.databaseRepository.migrateFilePaths(previous, current); + } + } + + // Only set MediaLocation in systemMetadataRepository if needed + if (savedValue?.location !== current) { await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); } }); diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index fb582ab038..f354a71791 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -1,8 +1,9 @@ import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/common'; import { Insertable } from 'kysely'; -import { DateTime } from 'luxon'; +import { DateTime, Duration } from 'luxon'; import { Writable } from 'node:stream'; import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; +import { OnJob } from 'src/decorators'; import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { @@ -15,14 +16,24 @@ import { SyncItem, SyncStreamDto, } from 'src/dtos/sync.dto'; -import { AssetVisibility, DatabaseAction, EntityType, Permission, SyncEntityType, SyncRequestType } from 'src/enum'; +import { + AssetVisibility, + DatabaseAction, + EntityType, + JobName, + Permission, + QueueName, + SyncEntityType, + SyncRequestType, +} from 'src/enum'; +import { SyncQueryOptions } from 'src/repositories/sync.repository'; import { SessionSyncCheckpointTable } from 'src/schema/tables/sync-checkpoint.table'; import { BaseService } from 'src/services/base.service'; import { SyncAck } from 'src/types'; import { getMyPartnerIds } from 'src/utils/asset.util'; import { hexOrBufferToBase64 } from 'src/utils/bytes'; import { setIsEqual } from 'src/utils/set'; -import { fromAck, mapJsonLine, serialize, SerializeOptions, toAck } from 'src/utils/sync'; +import { fromAck, serialize, SerializeOptions, toAck } from 'src/utils/sync'; type CheckpointMap = Partial>; type AssetLike = Omit & { @@ -31,6 +42,8 @@ type AssetLike = Omit & { }; const COMPLETE_ID = 'complete'; +const MAX_DAYS = 30; +const MAX_DURATION = Duration.fromObject({ days: MAX_DAYS }); const mapSyncAssetV1 = ({ checksum, thumbhash, ...data }: AssetLike): SyncAssetV1 => ({ ...data, @@ -54,6 +67,7 @@ const sendEntityBackfillCompleteAck = (response: Writable, ackType: SyncEntityTy const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ + SyncRequestType.AuthUsersV1, SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, @@ -72,6 +86,7 @@ export const SYNC_TYPES_ORDER = [ SyncRequestType.PeopleV1, SyncRequestType.AssetFacesV1, SyncRequestType.UserMetadataV1, + SyncRequestType.AssetMetadataV1, ]; const throwSessionRequired = () => { @@ -98,6 +113,10 @@ export class SyncService extends BaseService { const checkpoints: Record> = {}; for (const ack of dto.acks) { const { type } = fromAck(ack); + if (type === SyncEntityType.SyncResetV1) { + await this.sessionRepository.resetSyncProgress(sessionId); + return; + } // TODO proper ack validation via class validator if (!Object.values(SyncEntityType).includes(type)) { throw new BadRequestException(`Invalid ack type: ${type}`); @@ -127,11 +146,11 @@ export class SyncService extends BaseService { if (dto.reset) { await this.sessionRepository.resetSyncProgress(session.id); - session.isPendingSyncReset = false; } - if (session.isPendingSyncReset) { - response.write(mapJsonLine({ type: SyncEntityType.SyncResetV1, data: {} })); + const isPendingSyncReset = await this.sessionRepository.isPendingSyncReset(session.id); + if (isPendingSyncReset) { + send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} }); response.end(); return; } @@ -139,26 +158,38 @@ export class SyncService extends BaseService { const checkpoints = await this.syncCheckpointRepository.getAll(session.id); const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); + if (this.needsFullSync(checkpointMap)) { + send(response, { type: SyncEntityType.SyncResetV1, ids: ['reset'], data: {} }); + response.end(); + return; + } + + const { nowId } = await this.syncCheckpointRepository.getNow(); + const options: SyncQueryOptions = { nowId, userId: auth.user.id }; + const handlers: Record Promise> = { - [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), - [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), - [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), - [SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(response, checkpointMap, auth), - [SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(response, checkpointMap, auth, session.id), + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(options, response, checkpointMap), + [SyncRequestType.UsersV1]: () => this.syncUsersV1(options, response, checkpointMap), + [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(options, response, checkpointMap), + [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(options, response, checkpointMap), + [SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(options, response, checkpointMap), + [SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AssetMetadataV1]: () => this.syncAssetMetadataV1(options, response, checkpointMap, auth), [SyncRequestType.PartnerAssetExifsV1]: () => - this.syncPartnerAssetExifsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(response, checkpointMap, auth), - [SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.AlbumAssetExifsV1]: () => this.syncAlbumAssetExifsV1(response, checkpointMap, auth, session.id), - [SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth), - [SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth), - [SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth), - [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, session.id), - [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(response, checkpointMap, auth), - [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(response, checkpointMap, auth), - [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(response, checkpointMap, auth), + this.syncPartnerAssetExifsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(options, response, checkpointMap), + [SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(options, response, checkpointMap, session.id), + [SyncRequestType.AlbumAssetExifsV1]: () => + this.syncAlbumAssetExifsV1(options, response, checkpointMap, session.id), + [SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(options, response, checkpointMap), + [SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(options, response, checkpointMap), + [SyncRequestType.StacksV1]: () => this.syncStackV1(options, response, checkpointMap), + [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(options, response, checkpointMap, session.id), + [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(options, response, checkpointMap), + [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(options, response, checkpointMap), + [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(options, response, checkpointMap), }; for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) { @@ -166,66 +197,109 @@ export class SyncService extends BaseService { await handler(); } + send(response, { type: SyncEntityType.SyncCompleteV1, ids: [nowId], data: {} }); + response.end(); } - private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { + @OnJob({ name: JobName.AuditTableCleanup, queue: QueueName.BackgroundTask }) + async onAuditTableCleanup() { + const pruneThreshold = MAX_DAYS + 1; + + await this.syncRepository.album.cleanupAuditTable(pruneThreshold); + await this.syncRepository.albumUser.cleanupAuditTable(pruneThreshold); + await this.syncRepository.albumToAsset.cleanupAuditTable(pruneThreshold); + await this.syncRepository.asset.cleanupAuditTable(pruneThreshold); + await this.syncRepository.assetFace.cleanupAuditTable(pruneThreshold); + await this.syncRepository.assetMetadata.cleanupAuditTable(pruneThreshold); + await this.syncRepository.memory.cleanupAuditTable(pruneThreshold); + await this.syncRepository.memoryToAsset.cleanupAuditTable(pruneThreshold); + await this.syncRepository.partner.cleanupAuditTable(pruneThreshold); + await this.syncRepository.person.cleanupAuditTable(pruneThreshold); + await this.syncRepository.stack.cleanupAuditTable(pruneThreshold); + await this.syncRepository.user.cleanupAuditTable(pruneThreshold); + await this.syncRepository.userMetadata.cleanupAuditTable(pruneThreshold); + } + + private needsFullSync(checkpointMap: CheckpointMap) { + const completeAck = checkpointMap[SyncEntityType.SyncCompleteV1]; + if (!completeAck) { + return false; + } + + const milliseconds = Number.parseInt(completeAck.updateId.replaceAll('-', '').slice(0, 12), 16); + + return DateTime.fromMillis(milliseconds) < DateTime.now().minus(MAX_DURATION); + } + + private async syncAuthUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { + const upsertType = SyncEntityType.AuthUserV1; + const upserts = this.syncRepository.authUser.getUpserts({ ...options, ack: checkpointMap[upsertType] }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); + } + } + + private async syncUsersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; - const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); + const deletes = this.syncRepository.user.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserV1; - const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + const upserts = this.syncRepository.user.getUpserts({ ...options, ack: checkpointMap[upsertType] }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } - private async syncPartnersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncPartnersV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PartnerDeleteV1; - const deletes = this.syncRepository.partner.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partner.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PartnerV1; - const upserts = this.syncRepository.partner.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partner.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetDeleteV1; - const deletes = this.syncRepository.asset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.asset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetV1; - const upserts = this.syncRepository.asset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.asset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } } private async syncPartnerAssetsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const deleteType = SyncEntityType.PartnerAssetDeleteV1; - const deletes = this.syncRepository.partnerAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -238,7 +312,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAsset.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { @@ -258,29 +335,32 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); } } - private async syncAssetExifsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetExifsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const upsertType = SyncEntityType.AssetExifV1; - const upserts = this.syncRepository.assetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetExif.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } private async syncPartnerAssetExifsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const backfillType = SyncEntityType.PartnerAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerAssetExifV1; const upsertCheckpoint = checkpointMap[upsertType]; @@ -294,7 +374,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerAssetExif.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerAssetExif.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [partner.createId, updateId], data }); @@ -310,36 +393,44 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerAssetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerAssetExif.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAlbumsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AlbumDeleteV1; - const deletes = this.syncRepository.album.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.album.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AlbumV1; - const upserts = this.syncRepository.album.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.album.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumUsersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncAlbumUsersV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const deleteType = SyncEntityType.AlbumUserDeleteV1; - const deletes = this.syncRepository.albumUser.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumUser.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumUserBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.AlbumUserV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -352,7 +443,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumUser.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumUser.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -368,20 +462,30 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumUser.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumUser.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAlbumAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncAlbumAssetsV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const backfillType = SyncEntityType.AlbumAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); - const upsertType = SyncEntityType.AlbumAssetV1; - const upsertCheckpoint = checkpointMap[upsertType]; - if (upsertCheckpoint) { - const endId = upsertCheckpoint.updateId; + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); + const updateType = SyncEntityType.AlbumAssetUpdateV1; + const createType = SyncEntityType.AlbumAssetCreateV1; + const updateCheckpoint = checkpointMap[updateType]; + const createCheckpoint = checkpointMap[createType]; + if (createCheckpoint) { + const endId = createCheckpoint.updateId; for (const album of albums) { const createId = album.createId; @@ -390,7 +494,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAsset.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data: mapSyncAssetV1(data) }); @@ -406,25 +513,50 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) }); + if (createCheckpoint) { + const updates = this.syncRepository.albumAsset.getUpdates( + { ...options, ack: updateCheckpoint }, + createCheckpoint, + ); + for await (const { updateId, ...data } of updates) { + send(response, { type: updateType, ids: [updateId], data: mapSyncAssetV1(data) }); + } + } + + const creates = this.syncRepository.albumAsset.getCreates({ ...options, ack: createCheckpoint }); + let first = true; + for await (const { updateId, ...data } of creates) { + if (first) { + send(response, { + type: SyncEntityType.SyncAckV1, + data: {}, + ackType: SyncEntityType.AlbumAssetUpdateV1, + ids: [options.nowId], + }); + first = false; + } + send(response, { type: createType, ids: [updateId], data: mapSyncAssetV1(data) }); } } private async syncAlbumAssetExifsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const backfillType = SyncEntityType.AlbumAssetExifBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); - const upsertType = SyncEntityType.AlbumAssetExifV1; - const upsertCheckpoint = checkpointMap[upsertType]; - if (upsertCheckpoint) { - const endId = upsertCheckpoint.updateId; + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); + const updateType = SyncEntityType.AlbumAssetExifUpdateV1; + const createType = SyncEntityType.AlbumAssetExifCreateV1; + const upsertCheckpoint = checkpointMap[updateType]; + const createCheckpoint = checkpointMap[createType]; + if (createCheckpoint) { + const endId = createCheckpoint.updateId; for (const album of albums) { const createId = album.createId; @@ -433,7 +565,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumAssetExif.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumAssetExif.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -449,27 +584,50 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumAssetExif.getUpserts(auth.user.id, checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + if (createCheckpoint) { + const updates = this.syncRepository.albumAssetExif.getUpdates( + { ...options, ack: upsertCheckpoint }, + createCheckpoint, + ); + for await (const { updateId, ...data } of updates) { + send(response, { type: updateType, ids: [updateId], data }); + } + } + + const creates = this.syncRepository.albumAssetExif.getCreates({ ...options, ack: createCheckpoint }); + let first = true; + for await (const { updateId, ...data } of creates) { + if (first) { + send(response, { + type: SyncEntityType.SyncAckV1, + data: {}, + ackType: SyncEntityType.AlbumAssetExifUpdateV1, + ids: [options.nowId], + }); + first = false; + } + send(response, { type: createType, ids: [updateId], data }); } } private async syncAlbumToAssetsV1( + options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap, - auth: AuthDto, sessionId: string, ) { const deleteType = SyncEntityType.AlbumToAssetDeleteV1; - const deletes = this.syncRepository.albumToAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.albumToAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.AlbumToAssetBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const albums = await this.syncRepository.album.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const albums = await this.syncRepository.album.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.AlbumToAssetV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -482,7 +640,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.albumToAsset.getBackfill(album.id, startId, endId); + const backfill = this.syncRepository.albumToAsset.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + album.id, + ); for await (const { updateId, ...data } of backfill) { send(response, { type: backfillType, ids: [createId, updateId], data }); @@ -498,64 +659,72 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.albumToAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.albumToAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncMemoriesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncMemoriesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryDeleteV1; - const deletes = this.syncRepository.memory.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.memory.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryV1; - const upserts = this.syncRepository.memory.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.memory.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncMemoryAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncMemoryAssetsV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.MemoryToAssetDeleteV1; - const deletes = this.syncRepository.memoryToAsset.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.memoryToAsset.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.MemoryToAssetV1; - const upserts = this.syncRepository.memoryToAsset.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.memoryToAsset.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncStackV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncStackV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.StackDeleteV1; - const deletes = this.syncRepository.stack.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.stack.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.StackV1; - const upserts = this.syncRepository.stack.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.stack.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncPartnerStackV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) { + private async syncPartnerStackV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + sessionId: string, + ) { const deleteType = SyncEntityType.PartnerStackDeleteV1; - const deletes = this.syncRepository.partnerStack.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.partnerStack.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const backfillType = SyncEntityType.PartnerStackBackfillV1; const backfillCheckpoint = checkpointMap[backfillType]; - const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId); + const partners = await this.syncRepository.partner.getCreatedAfter({ + ...options, + afterCreateId: backfillCheckpoint?.updateId, + }); const upsertType = SyncEntityType.PartnerStackV1; const upsertCheckpoint = checkpointMap[upsertType]; if (upsertCheckpoint) { @@ -568,7 +737,10 @@ export class SyncService extends BaseService { } const startId = getStartId(createId, backfillCheckpoint); - const backfill = this.syncRepository.partnerStack.getBackfill(partner.sharedById, startId, endId); + const backfill = this.syncRepository.partnerStack.getBackfill( + { ...options, afterUpdateId: startId, beforeUpdateId: endId }, + partner.sharedById, + ); for await (const { updateId, ...data } of backfill) { send(response, { @@ -588,50 +760,77 @@ export class SyncService extends BaseService { }); } - const upserts = this.syncRepository.partnerStack.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.partnerStack.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncPeopleV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncPeopleV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.PersonDeleteV1; - const deletes = this.syncRepository.people.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.person.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.PersonV1; - const upserts = this.syncRepository.people.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.person.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncAssetFacesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncAssetFacesV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.AssetFaceDeleteV1; - const deletes = this.syncRepository.assetFace.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.assetFace.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.AssetFaceV1; - const upserts = this.syncRepository.assetFace.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.assetFace.getUpserts({ ...options, ack: checkpointMap[upsertType] }); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); } } - private async syncUserMetadataV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + private async syncUserMetadataV1(options: SyncQueryOptions, response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserMetadataDeleteV1; - const deletes = this.syncRepository.userMetadata.getDeletes(auth.user.id, checkpointMap[deleteType]); + const deletes = this.syncRepository.userMetadata.getDeletes({ ...options, ack: checkpointMap[deleteType] }); for await (const { id, ...data } of deletes) { send(response, { type: deleteType, ids: [id], data }); } const upsertType = SyncEntityType.UserMetadataV1; - const upserts = this.syncRepository.userMetadata.getUpserts(auth.user.id, checkpointMap[upsertType]); + const upserts = this.syncRepository.userMetadata.getUpserts({ ...options, ack: checkpointMap[upsertType] }); + + for await (const { updateId, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data }); + } + } + + private async syncAssetMetadataV1( + options: SyncQueryOptions, + response: Writable, + checkpointMap: CheckpointMap, + auth: AuthDto, + ) { + const deleteType = SyncEntityType.AssetMetadataDeleteV1; + const deletes = this.syncRepository.assetMetadata.getDeletes( + { ...options, ack: checkpointMap[deleteType] }, + auth.user.id, + ); + + for await (const { id, ...data } of deletes) { + send(response, { type: deleteType, ids: [id], data }); + } + + const upsertType = SyncEntityType.AssetMetadataV1; + const upserts = this.syncRepository.assetMetadata.getUpserts( + { ...options, ack: checkpointMap[upsertType] }, + auth.user.id, + ); for await (const { updateId, ...data } of upserts) { send(response, { type: upsertType, ids: [updateId], data }); diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 20127bab15..5a9c7f4df3 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -52,7 +52,7 @@ const updatedConfig = Object.freeze({ threads: 0, preset: 'ultrafast', targetAudioCodec: AudioCodec.Aac, - acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.LibOpus, AudioCodec.PcmS16le], + acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.LibOpus], targetResolution: '720', targetVideoCodec: VideoCodec.H264, acceptedVideoCodecs: [VideoCodec.H264], @@ -82,6 +82,11 @@ const updatedConfig = Object.freeze({ machineLearning: { enabled: true, urls: ['http://immich-machine-learning:3003'], + availabilityChecks: { + enabled: true, + interval: 30_000, + timeout: 2000, + }, clip: { enabled: true, modelName: 'ViT-B-32__openai', diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index d046b0317a..ea95b4df24 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -16,6 +16,20 @@ export class SystemConfigService extends BaseService { async onBootstrap() { const config = await this.getConfig({ withCache: false }); await this.eventRepository.emit('ConfigInit', { newConfig: config }); + + if ( + process.env.IMMICH_MACHINE_LEARNING_PING_TIMEOUT || + process.env.IMMICH_MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME + ) { + this.logger.deprecate( + 'IMMICH_MACHINE_LEARNING_PING_TIMEOUT and MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME have been moved to system config(`machineLearning.availabilityChecks`) and will be removed in a future release.', + ); + } + } + + @OnEvent({ name: 'AppShutdown' }) + onShutdown() { + this.machineLearningRepository.teardown(); } async getSystemConfig(): Promise { @@ -28,12 +42,14 @@ export class SystemConfigService extends BaseService { } @OnEvent({ name: 'ConfigInit', priority: -100 }) - onConfigInit({ newConfig: { logging } }: ArgOf<'ConfigInit'>) { + onConfigInit({ newConfig: { logging, machineLearning } }: ArgOf<'ConfigInit'>) { const { logLevel: envLevel } = this.configRepository.getEnv(); const configLevel = logging.enabled ? logging.level : false; const level = envLevel ?? configLevel; this.logger.setLogLevel(level); this.logger.log(`LogLevel=${level} ${envLevel ? '(set via IMMICH_LOG_LEVEL)' : '(set via system config)'}`); + + this.machineLearningRepository.setup(machineLearning); } @OnEvent({ name: 'ConfigUpdate', server: true }) diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index 3ae9d429eb..a57072e496 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -38,7 +38,7 @@ export class UserAdminService extends BaseService { await this.eventRepository.emit('UserSignup', { notify: !!notify, id: user.id, - tempPassword: user.shouldChangePassword ? userDto.password : undefined, + password: userDto.password, }); return mapUserAdmin(user); @@ -102,6 +102,7 @@ export class UserAdminService extends BaseService { const status = force ? UserStatus.Removing : UserStatus.Deleted; const user = await this.userRepository.update(id, { status, deletedAt: new Date() }); + this.telemetryRepository.api.addToGauge(`immich.users.total`, -1); if (force) { await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } }); @@ -114,6 +115,7 @@ export class UserAdminService extends BaseService { await this.findOrFail(id, { withDeleted: true }); await this.albumRepository.restoreAll(id); const user = await this.userRepository.restore(id); + this.telemetryRepository.api.addToGauge('immich.users.total', 1); return mapUserAdmin(user); } diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index b4e616974e..bd896ffc24 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -236,23 +236,23 @@ describe(UserService.name, () => { await sut.handleUserDelete({ id: user.id }); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/library/deleted-user'), + expect.stringContaining('/data/library/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/upload/deleted-user'), + expect.stringContaining('/data/upload/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/profile/deleted-user'), + expect.stringContaining('/data/profile/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/thumbs/deleted-user'), + expect.stringContaining('/data/thumbs/deleted-user'), options, ); expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( - expect.stringContaining('upload/encoded-video/deleted-user'), + expect.stringContaining('/data/encoded-video/deleted-user'), options, ); expect(mocks.album.deleteAll).toHaveBeenCalledWith(user.id); @@ -268,7 +268,7 @@ describe(UserService.name, () => { const options = { force: true, recursive: true }; - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('upload/library/admin'), options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('data/library/admin'), options); }); }); diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index 6849b17ac3..fc71777673 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -3,14 +3,14 @@ import { Updateable } from 'kysely'; import { DateTime } from 'luxon'; import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { OnJob } from 'src/decorators'; +import { OnEvent, OnJob } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto'; import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto'; import { CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto'; -import { CacheControl, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; +import { CacheControl, ImmichWorker, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; import { UserFindOptions } from 'src/repositories/user.repository'; import { UserTable } from 'src/schema/tables/user.table'; import { BaseService } from 'src/services/base.service'; @@ -213,6 +213,12 @@ export class UserService extends BaseService { }; } + @OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.Api] }) + async onBootstrap(): Promise { + const userCount = await this.userRepository.getCount(); + this.telemetryRepository.api.addToGauge('immich.users.total', userCount); + } + @OnJob({ name: JobName.UserSyncUsage, queue: QueueName.BackgroundTask }) async handleUserSyncUsage(): Promise { await this.userRepository.syncUsage(); diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index c4d7e9974d..b817363eac 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -95,7 +95,7 @@ export class VersionService extends BaseService { this.eventRepository.clientBroadcast('on_new_release', asNotification(metadata)); } } catch (error: Error | any) { - this.logger.warn(`Unable to run version check: ${error}`, error?.stack); + this.logger.warn(`Unable to run version check: ${error}\n${error?.stack}`); return JobStatus.Failed; } diff --git a/server/src/sql-tools/comparers/column.comparer.spec.ts b/server/src/sql-tools/comparers/column.comparer.spec.ts index fde237ad7b..0fd4ed74b5 100644 --- a/server/src/sql-tools/comparers/column.comparer.spec.ts +++ b/server/src/sql-tools/comparers/column.comparer.spec.ts @@ -62,6 +62,23 @@ describe('compareColumns', () => { ]); }); + it('should detect a change in default', () => { + const source: DatabaseColumn = { ...testColumn, nullable: true }; + const target: DatabaseColumn = { ...testColumn, nullable: true, default: "''" }; + const reason = `default is different (null vs '')`; + expect(compareColumns.onCompare(source, target)).toEqual([ + { + columnName: 'test', + tableName: 'table1', + type: 'ColumnAlter', + changes: { + default: 'NULL', + }, + reason, + }, + ]); + }); + it('should detect a comment change', () => { const source: DatabaseColumn = { ...testColumn, comment: 'new comment' }; const target: DatabaseColumn = { ...testColumn, comment: 'old comment' }; diff --git a/server/src/sql-tools/comparers/column.comparer.ts b/server/src/sql-tools/comparers/column.comparer.ts index 035fd6fc98..d3033430ef 100644 --- a/server/src/sql-tools/comparers/column.comparer.ts +++ b/server/src/sql-tools/comparers/column.comparer.ts @@ -72,9 +72,9 @@ export const compareColumns = { tableName: source.tableName, columnName: source.name, changes: { - default: String(source.default), + default: String(source.default ?? 'NULL'), }, - reason: `default is different (${source.default} vs ${target.default})`, + reason: `default is different (${source.default ?? 'null'} vs ${target.default})`, }); } diff --git a/server/src/sql-tools/helpers.ts b/server/src/sql-tools/helpers.ts index 8131f0350b..12586f27b2 100644 --- a/server/src/sql-tools/helpers.ts +++ b/server/src/sql-tools/helpers.ts @@ -175,7 +175,7 @@ export const isDefaultEqual = (source: DatabaseColumn, target: DatabaseColumn) = if ( withTypeCast(source.default, getColumnType(source)) === target.default || - source.default === withTypeCast(target.default, getColumnType(target)) + withTypeCast(target.default, getColumnType(target)) === source.default ) { return true; } diff --git a/server/src/sql-tools/transformers/column.transformer.spec.ts b/server/src/sql-tools/transformers/column.transformer.spec.ts index 1e29d4bff6..6828e2a72d 100644 --- a/server/src/sql-tools/transformers/column.transformer.spec.ts +++ b/server/src/sql-tools/transformers/column.transformer.spec.ts @@ -116,6 +116,20 @@ describe(transformColumns.name, () => { }), ).toEqual([`ALTER TABLE "table1" ALTER COLUMN "column1" SET DEFAULT uuid_generate_v4();`]); }); + + it('should update the default value to NULL', () => { + expect( + transformColumns(ctx, { + type: 'ColumnAlter', + tableName: 'table1', + columnName: 'column1', + changes: { + default: 'NULL', + }, + reason: 'unknown', + }), + ).toEqual([`ALTER TABLE "table1" ALTER COLUMN "column1" SET DEFAULT NULL;`]); + }); }); describe('ColumnDrop', () => { diff --git a/server/src/types.ts b/server/src/types.ts index 9cd1aa996b..da3889ef7c 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,6 +1,9 @@ import { SystemConfig } from 'src/config'; import { VECTOR_EXTENSIONS } from 'src/constants'; +import { UploadFieldName } from 'src/dtos/asset-media.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; import { + AssetMetadataKey, AssetOrder, AssetType, DatabaseSslMode, @@ -85,6 +88,9 @@ export interface VideoStreamInfo { isHDR: boolean; bitrate: number; pixelFormat: string; + colorPrimaries?: string; + colorSpace?: string; + colorTransfer?: string; } export interface AudioStreamInfo { @@ -246,7 +252,7 @@ export interface IEmailJob { } export interface INotifySignupJob extends IEntityJob { - tempPassword?: string; + password?: string; } export interface INotifyAlbumInviteJob extends IEntityJob { @@ -272,6 +278,9 @@ export interface QueueStatus { } export type JobItem = + // Audit + | { name: JobName.AuditTableCleanup; data?: IBaseJob } + // Backups | { name: JobName.DatabaseBackup; data?: IBaseJob } @@ -306,8 +315,7 @@ export type JobItem = // Sidecar Scanning | { name: JobName.SidecarQueueAll; data: IBaseJob } - | { name: JobName.SidecarDiscovery; data: IEntityJob } - | { name: JobName.SidecarSync; data: IEntityJob } + | { name: JobName.SidecarCheck; data: IEntityJob } | { name: JobName.SidecarWrite; data: ISidecarWriteJob } // Facial Recognition @@ -394,8 +402,8 @@ export interface VectorUpdateResult { } export interface ImmichFile extends Express.Multer.File { - /** sha1 hash of file */ uuid: string; + /** sha1 hash of file */ checksum: Buffer; } @@ -407,6 +415,16 @@ export interface UploadFile { size: number; } +export type UploadRequest = { + auth: AuthDto | null; + fieldName: UploadFieldName; + file: UploadFile; + body: { + filename?: string; + [key: string]: unknown; + }; +}; + export interface UploadFiles { assetData: ImmichFile[]; sidecarData: ImmichFile[]; @@ -465,11 +483,6 @@ export interface SystemMetadata extends Record = { - key: T; - value: UserMetadata[T]; -}; - export interface UserPreferences { albums: { defaultAssetOrder: AssetOrder; @@ -514,8 +527,22 @@ export interface UserPreferences { }; } +export type UserMetadataItem = { + key: T; + value: UserMetadata[T]; +}; + export interface UserMetadata extends Record> { [UserMetadataKey.Preferences]: DeepPartial; [UserMetadataKey.License]: { licenseKey: string; activationKey: string; activatedAt: string }; [UserMetadataKey.Onboarding]: { isOnboarded: boolean }; } + +export type AssetMetadataItem = { + key: T; + value: AssetMetadata[T]; +}; + +export interface AssetMetadata extends Record> { + [AssetMetadataKey.MobileApp]: { iCloudId: string }; +} diff --git a/server/src/utils/access.ts b/server/src/utils/access.ts index 08ff81e840..8427da6f1b 100644 --- a/server/src/utils/access.ts +++ b/server/src/utils/access.ts @@ -92,7 +92,7 @@ const checkSharedLinkAccess = async ( return sharedLink.allowDownload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set(); } - case Permission.AlbumAddAsset: { + case Permission.AlbumAssetCreate: { return sharedLink.allowUpload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set(); } @@ -163,7 +163,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return setUnion(isOwner, isShared); } - case Permission.AlbumAddAsset: { + case Permission.AlbumAssetCreate: { const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids); const isShared = await access.album.checkSharedAlbumAccess( auth.user.id, @@ -195,7 +195,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return setUnion(isOwner, isShared); } - case Permission.AlbumRemoveAsset: { + case Permission.AlbumAssetDelete: { const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids); const isShared = await access.album.checkSharedAlbumAccess( auth.user.id, diff --git a/server/src/utils/asset.util.ts b/server/src/utils/asset.util.ts index 1b9e12c1cd..629b3bf819 100644 --- a/server/src/utils/asset.util.ts +++ b/server/src/utils/asset.util.ts @@ -10,7 +10,7 @@ import { AccessRepository } from 'src/repositories/access.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; -import { IBulkAsset, ImmichFile, UploadFile } from 'src/types'; +import { IBulkAsset, ImmichFile, UploadFile, UploadRequest } from 'src/types'; import { checkAccess } from 'src/utils/access'; export const getAssetFile = (files: AssetFile[], type: AssetFileType | GeneratedImageType) => { @@ -190,9 +190,10 @@ export function mapToUploadFile(file: ImmichFile): UploadFile { }; } -export const asRequest = (request: AuthRequest, file: Express.Multer.File) => { +export const asUploadRequest = (request: AuthRequest, file: Express.Multer.File): UploadRequest => { return { auth: request.user || null, + body: request.body, fieldName: file.fieldname as UploadFieldName, file: mapToUploadFile(file as ImmichFile), }; diff --git a/server/src/utils/database.ts b/server/src/utils/database.ts index 1ef9b8e926..d9fe6b7897 100644 --- a/server/src/utils/database.ts +++ b/server/src/utils/database.ts @@ -14,7 +14,7 @@ import { 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 postgres, { Notice, PostgresError } from 'postgres'; import { columns, Exif, Person } from 'src/database'; import { AssetFileType, AssetVisibility, DatabaseExtension, DatabaseSslMode } from 'src/enum'; import { AssetSearchBuilderOptions } from 'src/repositories/search.repository'; @@ -153,6 +153,10 @@ export function toJson { + return (error as PostgresError)?.constraint_name === 'UQ_assets_owner_checksum'; +}; + export function withDefaultVisibility(qb: SelectQueryBuilder) { return qb.where('asset.visibility', 'in', [sql.lit(AssetVisibility.Archive), sql.lit(AssetVisibility.Timeline)]); } diff --git a/server/src/utils/file.ts b/server/src/utils/file.ts index 2331a45a62..29c7f6f772 100644 --- a/server/src/utils/file.ts +++ b/server/src/utils/file.ts @@ -73,7 +73,7 @@ export const sendFile = async ( // log non-http errors if (error instanceof HttpException === false) { - logger.error(`Unable to send file: ${error.name}`, error.stack); + logger.error(`Unable to send file: ${error}`, error.stack); } res.header('Cache-Control', 'none'); diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index e43ecba49f..f678a32b0f 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -392,9 +392,30 @@ export class ThumbnailConfig extends BaseConfig { getBaseInputOptions(videoStream: VideoStreamInfo, format?: VideoFormat): string[] { // skip_frame nointra skips all frames for some MPEG-TS files. Look at ffmpeg tickets 7950 and 7895 for more details. - return format?.formatName === 'mpegts' - ? ['-sws_flags accurate_rnd+full_chroma_int'] - : ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int']; + const options = + format?.formatName === 'mpegts' + ? ['-sws_flags accurate_rnd+full_chroma_int'] + : ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int']; + + const metadataOverrides = []; + if (videoStream.colorPrimaries === 'reserved') { + metadataOverrides.push('colour_primaries=1'); + } + + if (videoStream.colorSpace === 'reserved') { + metadataOverrides.push('matrix_coefficients=1'); + } + + if (videoStream.colorTransfer === 'reserved') { + metadataOverrides.push('transfer_characteristics=1'); + } + + if (metadataOverrides.length > 0) { + // workaround for https://fftrac-bg.ffmpeg.org/ticket/11020 + options.push(`-bsf:v ${videoStream.codecName}_metadata=${metadataOverrides.join(':')}`); + } + + return options; } getBaseOutputOptions() { @@ -403,7 +424,7 @@ export class ThumbnailConfig extends BaseConfig { getFilterOptions(videoStream: VideoStreamInfo): string[] { return [ - 'fps=12:eof_action=pass:round=down', + 'fps=12:start_time=0:eof_action=pass:round=down', 'thumbnail=12', String.raw`select=gt(scene\,0.1)-eq(prev_selected_n\,n)+isnan(prev_selected_n)+gt(n\,20)`, 'trim=end_frame=2', diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 3acb72b663..a32632b52d 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -6,7 +6,11 @@ import { SwaggerDocumentOptions, SwaggerModule, } from '@nestjs/swagger'; -import { ReferenceObject, SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; +import { + OperationObject, + ReferenceObject, + SchemaObject, +} from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import _ from 'lodash'; import { writeFileSync } from 'node:fs'; import path from 'node:path'; @@ -15,7 +19,7 @@ import parse from 'picomatch/lib/parse'; import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, serverVersion } from 'src/constants'; import { extraSyncModels } from 'src/dtos/sync.dto'; -import { ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; +import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export class ImmichStartupError extends Error {} @@ -198,7 +202,12 @@ const patchOpenAPI = (document: OpenAPIObject) => { trace: path.trace, }; - for (const operation of Object.values(operations)) { + for (const operation of Object.values(operations) as Array< + OperationObject & { + [ApiCustomExtension.AdminOnly]?: boolean; + [ApiCustomExtension.Permission]?: string; + } + >) { if (!operation) { continue; } @@ -211,12 +220,21 @@ const patchOpenAPI = (document: OpenAPIObject) => { // console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`); } - if (operation.description === '') { - delete operation.description; - } + const adminOnly = operation[ApiCustomExtension.AdminOnly] ?? false; + const permission = operation[ApiCustomExtension.Permission]; + if (permission) { + let description = (operation.description || '').trim(); + if (description && !description.endsWith('.')) { + description += '. '; + } - if (operation.parameters) { - operation.parameters = _.orderBy(operation.parameters, 'name'); + operation.description = + description + + `This endpoint ${adminOnly ? 'is an admin-only route, and ' : ''}requires the \`${permission}\` permission.`; + + if (operation.parameters) { + operation.parameters = _.orderBy(operation.parameters, 'name'); + } } } } diff --git a/server/src/validation.spec.ts b/server/src/validation.spec.ts index 7cd7826223..631ba60a60 100644 --- a/server/src/validation.spec.ts +++ b/server/src/validation.spec.ts @@ -1,7 +1,8 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import { DateTime } from 'luxon'; -import { IsDateStringFormat, MaxDateString } from 'src/validation'; +import { IsDateStringFormat, IsNotSiblingOf, MaxDateString, Optional } from 'src/validation'; +import { describe } from 'vitest'; describe('Validation', () => { describe('MaxDateString', () => { @@ -54,4 +55,38 @@ describe('Validation', () => { await expect(validate(dto)).resolves.toHaveLength(1); }); }); + + describe('IsNotSiblingOf', () => { + class MyDto { + @IsNotSiblingOf(['attribute2']) + @Optional() + attribute1?: string; + + @IsNotSiblingOf(['attribute1', 'attribute3']) + @Optional() + attribute2?: string; + + @IsNotSiblingOf(['attribute2']) + @Optional() + attribute3?: string; + + @Optional() + unrelatedAttribute?: string; + } + + it('passes when only one attribute is present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', unrelatedAttribute: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(0); + }); + + it('fails when colliding attributes are present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute2: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(2); + }); + + it('passes when no colliding attributes are present', async () => { + const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute3: 'value2' }); + await expect(validate(dto)).resolves.toHaveLength(0); + }); + }); }); diff --git a/server/src/validation.ts b/server/src/validation.ts index 049b5432d6..6d4bbfbe36 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -22,11 +22,13 @@ import { Validate, ValidateBy, ValidateIf, + ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, buildMessage, isDateString, + isDefined, } from 'class-validator'; import { CronJob } from 'cron'; import { DateTime } from 'luxon'; @@ -63,6 +65,22 @@ export class FileNotEmptyValidator extends FileValidator { } } +type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; +export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { + const { optional, each, nullable, ...apiPropertyOptions } = { + optional: false, + each: false, + nullable: false, + ...options, + }; + return applyDecorators( + IsUUID('4', { each }), + ApiProperty({ format: 'uuid', ...apiPropertyOptions }), + optional ? Optional({ nullable }) : IsNotEmpty(), + each ? IsArray() : IsString(), + ); +}; + export class UUIDParamDto { @IsNotEmpty() @IsUUID('4') @@ -70,6 +88,14 @@ export class UUIDParamDto { id!: string; } +export class UUIDAssetIDParamDto { + @ValidateUUID() + id!: string; + + @ValidateUUID() + assetId!: string; +} + type PinCodeOptions = { optional?: boolean } & OptionalOptions; export const PinCode = (options?: PinCodeOptions & ApiPropertyOptions) => { const { optional, nullable, emptyToNull, ...apiPropertyOptions } = { @@ -122,6 +148,27 @@ export function Optional({ nullable, emptyToNull, ...validationOptions }: Option return applyDecorators(...decorators); } +export function IsNotSiblingOf(siblings: string[], validationOptions?: ValidationOptions) { + return ValidateBy( + { + name: 'isNotSiblingOf', + constraints: siblings, + validator: { + validate(value: any, args: ValidationArguments) { + if (!isDefined(value)) { + return true; + } + return args.constraints.filter((prop) => isDefined((args.object as any)[prop])).length === 0; + }, + defaultMessage: (args: ValidationArguments) => { + return `${args.property} cannot exist alongside any of the following properties: ${args.constraints.join(', ')}`; + }, + }, + }, + validationOptions, + ); +} + export const ValidateHexColor = () => { const decorators = [ IsHexColor(), @@ -131,22 +178,6 @@ export const ValidateHexColor = () => { return applyDecorators(...decorators); }; -type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; -export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { - const { optional, each, nullable, ...apiPropertyOptions } = { - optional: false, - each: false, - nullable: false, - ...options, - }; - return applyDecorators( - IsUUID('4', { each }), - ApiProperty({ format: 'uuid', ...apiPropertyOptions }), - optional ? Optional({ nullable }) : IsNotEmpty(), - each ? IsArray() : IsString(), - ); -}; - type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' }; export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => { const { optional, nullable, format, ...apiPropertyOptions } = { @@ -180,6 +211,18 @@ export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => { return applyDecorators(...decorators); }; +type StringOptions = { optional?: boolean; nullable?: boolean; trim?: boolean }; +export const ValidateString = (options?: StringOptions & ApiPropertyOptions) => { + const { optional, nullable, trim, ...apiPropertyOptions } = options || {}; + const decorators = [ApiProperty(apiPropertyOptions), IsString(), optional ? Optional({ nullable }) : IsNotEmpty()]; + + if (trim) { + decorators.push(Transform(({ value }: { value: string }) => value?.trim())); + } + + return applyDecorators(...decorators); +}; + type BooleanOptions = { optional?: boolean; nullable?: boolean }; export const ValidateBoolean = (options?: BooleanOptions & ApiPropertyOptions) => { const { optional, nullable, ...apiPropertyOptions } = options || {}; diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 991c5d2c4f..0fd2189268 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -35,7 +35,7 @@ export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif primaryAssetId: assets[0].id, createdAt: new Date('2023-02-23T05:06:29.716Z'), updatedAt: new Date('2023-02-23T05:06:29.716Z'), - updateId: 'uuid-v7', + updateId: expect.any(String), }; }; @@ -65,7 +65,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_123.jpg', + originalPath: '/data/library/IMG_123.jpg', files: [thumbnailFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -101,7 +101,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_456.jpg', + originalPath: '/data/library/IMG_456.jpg', files: [previewFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -462,7 +462,7 @@ export const assetStub = { }), imageFrom2015: Object.freeze({ - id: 'asset-id-1', + id: 'asset-id-2015', status: AssetStatus.Active, deviceAssetId: 'device-asset-id', fileModifiedAt: new Date('2015-02-23T05:06:29.716Z'), @@ -484,6 +484,9 @@ export const assetStub = { duration: null, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.ext', faces: [], @@ -863,4 +866,43 @@ export const assetStub = { stackId: null, visibility: AssetVisibility.Timeline, }), + panoramaTif: Object.freeze({ + id: 'asset-id', + status: AssetStatus.Active, + deviceAssetId: 'device-asset-id', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), + owner: userStub.user1, + ownerId: 'user-id', + deviceId: 'device-id', + originalPath: '/original/path.tif', + checksum: Buffer.from('file hash', 'utf8'), + type: AssetType.Image, + files, + thumbhash: Buffer.from('blablabla', 'base64'), + encodedVideoPath: null, + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), + localDateTime: new Date('2023-02-23T05:06:29.716Z'), + isFavorite: true, + duration: null, + isExternal: false, + livePhotoVideo: null, + livePhotoVideoId: null, + sharedLinks: [], + originalFileName: 'asset-id.tif', + faces: [], + deletedAt: null, + sidecarPath: null, + exifInfo: { + fileSizeInByte: 5000, + projectionType: 'EQUIRECTANGULAR', + } as Exif, + duplicateId: null, + isOffline: false, + updateId: '42', + libraryId: null, + stackId: null, + visibility: AssetVisibility.Timeline, + }), }; diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index efbf21d317..727f5ae7cf 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -261,4 +261,15 @@ export const probeStub = { bitrate: 0, }, }), + videoStreamReserved: Object.freeze({ + ...probeStubDefault, + videoStreams: [ + { + ...probeStubDefaultVideoStream[0], + colorPrimaries: 'reserved', + colorSpace: 'reserved', + colorTransfer: 'reserved', + }, + ], + }), }; diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 47201a5b3b..19a62ad193 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -46,6 +46,7 @@ const assetInfo: ExifResponseDto = { const assetResponse: AssetResponseDto = { id: 'id_1', + createdAt: today, deviceAssetId: 'device_asset_id_1', ownerId: 'user_id_1', deviceId: 'device_id_1', @@ -118,6 +119,7 @@ export const sharedLinkStub = { description: null, assets: [assetStub.image], password: 'password', + slug: null, }), valid: Object.freeze({ id: '123', @@ -135,6 +137,7 @@ export const sharedLinkStub = { password: null, assets: [] as MapAsset[], album: null, + slug: null, }), expired: Object.freeze({ id: '123', @@ -152,6 +155,7 @@ export const sharedLinkStub = { albumId: null, assets: [] as MapAsset[], album: null, + slug: null, }), readonlyNoExif: Object.freeze({ id: '123', @@ -166,6 +170,7 @@ export const sharedLinkStub = { description: null, password: null, assets: [], + slug: null, albumId: 'album-123', album: { id: 'album-123', @@ -266,6 +271,7 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, + slug: null, description: null, password: 'password', assets: [], @@ -288,6 +294,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), expired: Object.freeze({ album: undefined, @@ -303,6 +310,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), readonlyNoMetadata: Object.freeze({ id: '123', @@ -316,6 +324,7 @@ export const sharedLinkResponseStub = { allowUpload: false, allowDownload: false, showMetadata: false, + slug: null, album: { ...albumResponse, startDate: assetResponse.localDateTime, endDate: assetResponse.localDateTime }, assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }], }), diff --git a/server/test/fixtures/tag.stub.ts b/server/test/fixtures/tag.stub.ts index 7a2cacf126..ca66af7b94 100644 --- a/server/test/fixtures/tag.stub.ts +++ b/server/test/fixtures/tag.stub.ts @@ -1,5 +1,6 @@ import { Tag } from 'src/database'; import { TagResponseDto } from 'src/dtos/tag.dto'; +import { newUuidV7 } from 'test/small.factory'; const parent = Object.freeze({ id: 'tag-parent', @@ -37,7 +38,10 @@ const color = { parentId: null, }; -const upsert = { userId: 'tag-user', updateId: 'uuid-v7' }; +const upsert = { + userId: 'tag-user', + updateId: newUuidV7(), +}; export const tagStub = { tag, diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index d6038b6b84..c7356e2f1b 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -5,7 +5,15 @@ import { createHash, randomBytes } from 'node:crypto'; import { Writable } from 'node:stream'; import { AssetFace } from 'src/database'; import { AuthDto, LoginResponseDto } from 'src/dtos/auth.dto'; -import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum'; +import { + AlbumUserRole, + AssetType, + AssetVisibility, + MemoryType, + SourceType, + SyncEntityType, + SyncRequestType, +} from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; @@ -25,11 +33,13 @@ import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { SessionRepository } from 'src/repositories/session.repository'; +import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SyncCheckpointRepository } from 'src/repositories/sync-checkpoint.repository'; import { SyncRepository } from 'src/repositories/sync.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { DB } from 'src/schema'; @@ -45,6 +55,7 @@ import { StackTable } from 'src/schema/tables/stack.table'; import { UserTable } from 'src/schema/tables/user.table'; import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service'; import { SyncService } from 'src/services/sync.service'; +import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { factory, newDate, newEmbedding, newUuid } from 'test/small.factory'; import { automock, wait } from 'test/utils'; import { Mocked } from 'vitest'; @@ -249,13 +260,24 @@ export class SyncTestContext extends MediumTestContext { return stream.getResponse(); } + async assertSyncIsComplete(auth: AuthDto, types: SyncRequestType[]) { + await expect(this.syncStream(auth, types)).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + } + async syncAckAll(auth: AuthDto, response: Array<{ type: string; ack: string }>) { const acks: Record = {}; + const syncAcks: string[] = []; for (const { type, ack } of response) { + if (type === SyncEntityType.SyncAckV1) { + syncAcks.push(ack); + continue; + } acks[type] = ack; } - await this.sut.setAcks(auth, { acks: Object.values(acks) }); + await this.sut.setAcks(auth, { acks: [...Object.values(acks), ...syncAcks] }); } } @@ -273,6 +295,7 @@ const newRealRepository = (key: ClassConstructor, db: Kysely): T => { case PersonRepository: case SearchRepository: case SessionRepository: + case SharedLinkRepository: case StackRepository: case SyncRepository: case SyncCheckpointRepository: @@ -326,6 +349,10 @@ const newMockRepository = (key: ClassConstructor) => { return automock(key); } + case TelemetryRepository: { + return newTelemetryRepositoryMock(); + } + case DatabaseRepository: { return automock(DatabaseRepository, { args: [undefined, { setContext: () => {} }, { getEnv: () => ({ database: { vectorExtension: '' } }) }], @@ -378,7 +405,7 @@ const assetInsert = (asset: Partial> = {}) => { checksum: randomBytes(32), type: AssetType.Image, originalPath: '/path/to/something.jpg', - ownerId: '@immich.cloud', + ownerId: 'not-a-valid-uuid', isFavorite: false, fileCreatedAt: now, fileModifiedAt: now, @@ -468,8 +495,6 @@ const personInsert = (person: Partial> & { ownerId: stri name: person.name || 'Test Name', ownerId: person.ownerId || newUuid(), thumbnailPath: person.thumbnailPath || '/path/to/thumbnail.jpg', - updatedAt: person.updatedAt || newDate(), - updateId: person.updateId || newUuid(), }; return { ...defaults, @@ -507,7 +532,14 @@ const userInsert = (user: Partial> = {}) => { deletedAt: null, isAdmin: false, profileImagePath: '', + profileChangedAt: newDate(), shouldChangePassword: true, + storageLabel: null, + pinCode: null, + oauthId: '', + avatarColor: null, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, }; return { ...defaults, ...user, id }; diff --git a/server/test/medium/specs/services/auth-admin.service.spec.ts b/server/test/medium/specs/services/auth-admin.service.spec.ts new file mode 100644 index 0000000000..fa2a69f665 --- /dev/null +++ b/server/test/medium/specs/services/auth-admin.service.spec.ts @@ -0,0 +1,66 @@ +import { Kysely } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { AuthAdminService } from 'src/services/auth-admin.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(AuthAdminService, { + database: db || defaultDatabase, + real: [UserRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(AuthAdminService.name, () => { + describe('unlinkAll', () => { + it('should reset user.oauthId', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user } = await ctx.newUser({ oauthId: 'test-oauth-id' }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + + it('should reset a deleted user', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user } = await ctx.newUser({ oauthId: 'test-oauth-id', deletedAt: new Date() }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + + it('should reset multiple users', async () => { + const { sut, ctx } = setup(); + const userRepo = ctx.get(UserRepository); + const { user: user1 } = await ctx.newUser({ oauthId: '1' }); + const { user: user2 } = await ctx.newUser({ oauthId: '2', deletedAt: new Date() }); + const auth = factory.auth(); + + await expect(sut.unlinkAll(auth)).resolves.toBeUndefined(); + await expect(userRepo.get(user1.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + await expect(userRepo.get(user2.id, { withDeleted: true })).resolves.toEqual( + expect.objectContaining({ oauthId: '' }), + ); + }); + }); +}); diff --git a/server/test/medium/specs/services/auth.service.spec.ts b/server/test/medium/specs/services/auth.service.spec.ts index 14ea1451f2..60d3210f4c 100644 --- a/server/test/medium/specs/services/auth.service.spec.ts +++ b/server/test/medium/specs/services/auth.service.spec.ts @@ -11,6 +11,7 @@ import { LoggingRepository } from 'src/repositories/logging.repository'; import { SessionRepository } from 'src/repositories/session.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { DB } from 'src/schema'; import { AuthService } from 'src/services/auth.service'; @@ -32,7 +33,7 @@ const setup = (db?: Kysely) => { SystemMetadataRepository, UserRepository, ], - mock: [LoggingRepository, StorageRepository, EventRepository], + mock: [LoggingRepository, StorageRepository, EventRepository, TelemetryRepository], }); }; diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts new file mode 100644 index 0000000000..517e6cc277 --- /dev/null +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -0,0 +1,55 @@ +import { Kysely } from 'kysely'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerRepository } from 'src/repositories/partner.repository'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { SearchRepository } from 'src/repositories/search.repository'; +import { DB } from 'src/schema'; +import { SearchService } from 'src/services/search.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SearchService, { + database: db || defaultDatabase, + real: [AccessRepository, DatabaseRepository, SearchRepository, PartnerRepository, PersonRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SearchService.name, () => { + it('should work', () => { + const { sut } = setup(); + expect(sut).toBeDefined(); + }); + + it('should return assets', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + + const assets = []; + const sizes = [12_334, 599, 123_456]; + + for (let i = 0; i < sizes.length; i++) { + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, fileSizeInByte: sizes[i] }); + assets.push(asset); + } + + const auth = factory.auth({ user: { id: user.id } }); + + await expect(sut.searchLargeAssets(auth, {})).resolves.toEqual([ + expect.objectContaining({ id: assets[2].id }), + expect.objectContaining({ id: assets[0].id }), + expect.objectContaining({ id: assets[1].id }), + ]); + }); +}); diff --git a/server/test/medium/specs/services/shared-link.service.spec.ts b/server/test/medium/specs/services/shared-link.service.spec.ts new file mode 100644 index 0000000000..88e7e86df5 --- /dev/null +++ b/server/test/medium/specs/services/shared-link.service.spec.ts @@ -0,0 +1,65 @@ +import { Kysely } from 'kysely'; +import { randomBytes } from 'node:crypto'; +import { SharedLinkType } from 'src/enum'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; +import { StorageRepository } from 'src/repositories/storage.repository'; +import { DB } from 'src/schema'; +import { SharedLinkService } from 'src/services/shared-link.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SharedLinkService, { + database: db || defaultDatabase, + real: [AccessRepository, DatabaseRepository, SharedLinkRepository], + mock: [LoggingRepository, StorageRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SharedLinkService.name, () => { + describe('get', () => { + it('should return the correct dates on the shared link album', async () => { + const { sut, ctx } = setup(); + + const { user } = await ctx.newUser(); + const auth = factory.auth({ user }); + const { album } = await ctx.newAlbum({ ownerId: user.id }); + + const dates = ['2021-01-01T00:00:00.000Z', '2022-01-01T00:00:00.000Z', '2020-01-01T00:00:00.000Z']; + + for (const date of dates) { + const { asset } = await ctx.newAsset({ fileCreatedAt: date, localDateTime: date, ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, make: 'Canon' }); + await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); + } + + const sharedLinkRepo = ctx.get(SharedLinkRepository); + + const sharedLink = await sharedLinkRepo.create({ + key: randomBytes(16), + id: factory.uuid(), + userId: user.id, + albumId: album.id, + allowUpload: true, + type: SharedLinkType.Album, + }); + + await expect(sut.get(auth, sharedLink.id)).resolves.toMatchObject({ + album: expect.objectContaining({ + startDate: '2020-01-01T00:00:00+00:00', + endDate: '2022-01-01T00:00:00+00:00', + }), + }); + }); + }); +}); diff --git a/server/test/medium/specs/services/storage.service.spec.ts b/server/test/medium/specs/services/storage.service.spec.ts new file mode 100644 index 0000000000..6c3fc487d4 --- /dev/null +++ b/server/test/medium/specs/services/storage.service.spec.ts @@ -0,0 +1,46 @@ +import { Kysely } from 'kysely'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { StorageRepository } from 'src/repositories/storage.repository'; +import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { DB } from 'src/schema'; +import { StorageService } from 'src/services/storage.service'; +import { newMediumService } from 'test/medium.factory'; +import { mockEnvData } from 'test/repositories/config.repository.mock'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(StorageService, { + database: db || defaultDatabase, + real: [AssetRepository, DatabaseRepository, SystemMetadataRepository], + mock: [StorageRepository, ConfigRepository, LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(StorageService.name, () => { + describe('onBoostrap', () => { + it('should work', async () => { + const { sut, ctx } = setup(); + + const configMock = ctx.getMock(ConfigRepository); + configMock.getEnv.mockReturnValue(mockEnvData({})); + + const storageMock = ctx.getMock(StorageRepository); + storageMock.mkdirSync.mockReturnValue(void 0); + storageMock.existsSync.mockReturnValue(true); + storageMock.createFile.mockResolvedValue(void 0); + storageMock.overwriteFile.mockResolvedValue(void 0); + storageMock.readFile.mockResolvedValue(Buffer.from('test content')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + }); + }); +}); diff --git a/server/test/medium/specs/services/sync.service.spec.ts b/server/test/medium/specs/services/sync.service.spec.ts new file mode 100644 index 0000000000..b5443d7e62 --- /dev/null +++ b/server/test/medium/specs/services/sync.service.spec.ts @@ -0,0 +1,226 @@ +import { Kysely } from 'kysely'; +import { DateTime } from 'luxon'; +import { AssetMetadataKey, UserMetadataKey } from 'src/enum'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { SyncRepository } from 'src/repositories/sync.repository'; +import { DB } from 'src/schema'; +import { SyncService } from 'src/services/sync.service'; +import { newMediumService } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; +import { v4 } from 'uuid'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SyncService, { + database: db || defaultDatabase, + real: [DatabaseRepository, SyncRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +const deletedLongAgo = DateTime.now().minus({ days: 35 }).toISO(); + +const assertTableCount = async (db: Kysely, t: T, count: number) => { + const { table } = db.dynamic; + const results = await db.selectFrom(table(t).as(t)).selectAll().execute(); + expect(results).toHaveLength(count); +}; + +describe(SyncService.name, () => { + describe('onAuditTableCleanup', () => { + it('should work', async () => { + const { sut } = setup(); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + }); + + it('should cleanup the album_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'album_audit'; + + await ctx.database + .insertInto(tableName) + .values({ albumId: v4(), userId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the album_asset_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'album_asset_audit'; + const { user } = await ctx.newUser(); + const { album } = await ctx.newAlbum({ ownerId: user.id }); + await ctx.database + .insertInto(tableName) + .values({ albumId: album.id, assetId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the album_user_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'album_user_audit'; + await ctx.database + .insertInto(tableName) + .values({ albumId: v4(), userId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the asset_audit table', async () => { + const { sut, ctx } = setup(); + + await ctx.database + .insertInto('asset_audit') + .values({ assetId: v4(), ownerId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, 'asset_audit', 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, 'asset_audit', 0); + }); + + it('should cleanup the asset_face_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'asset_face_audit'; + await ctx.database + .insertInto(tableName) + .values({ assetFaceId: v4(), assetId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the asset_metadata_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'asset_metadata_audit'; + await ctx.database + .insertInto(tableName) + .values({ assetId: v4(), key: AssetMetadataKey.MobileApp, deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the memory_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'memory_audit'; + await ctx.database + .insertInto(tableName) + .values({ memoryId: v4(), userId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the memory_asset_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'memory_asset_audit'; + const { user } = await ctx.newUser(); + const { memory } = await ctx.newMemory({ ownerId: user.id }); + await ctx.database + .insertInto(tableName) + .values({ memoryId: memory.id, assetId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the partner_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'partner_audit'; + await ctx.database + .insertInto(tableName) + .values({ sharedById: v4(), sharedWithId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the stack_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'stack_audit'; + await ctx.database + .insertInto(tableName) + .values({ stackId: v4(), userId: v4(), deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the user_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'user_audit'; + await ctx.database.insertInto(tableName).values({ userId: v4(), deletedAt: deletedLongAgo }).execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should cleanup the user_metadata_audit table', async () => { + const { sut, ctx } = setup(); + const tableName = 'user_metadata_audit'; + await ctx.database + .insertInto(tableName) + .values({ userId: v4(), key: UserMetadataKey.Onboarding, deletedAt: deletedLongAgo }) + .execute(); + + await assertTableCount(ctx.database, tableName, 1); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + await assertTableCount(ctx.database, tableName, 0); + }); + + it('should skip recent records', async () => { + const { sut, ctx } = setup(); + + const keep = { + id: v4(), + assetId: v4(), + ownerId: v4(), + deletedAt: DateTime.now().minus({ days: 25 }).toISO(), + }; + + const remove = { + id: v4(), + assetId: v4(), + ownerId: v4(), + deletedAt: DateTime.now().minus({ days: 35 }).toISO(), + }; + + await ctx.database.insertInto('asset_audit').values([keep, remove]).execute(); + await assertTableCount(ctx.database, 'asset_audit', 2); + await expect(sut.onAuditTableCleanup()).resolves.toBeUndefined(); + + const after = await ctx.database.selectFrom('asset_audit').select(['id']).execute(); + expect(after).toHaveLength(1); + expect(after[0].id).toBe(keep.id); + }); + }); +}); diff --git a/server/test/medium/specs/services/user.service.spec.ts b/server/test/medium/specs/services/user.service.spec.ts index 7643be292e..0d72d39950 100644 --- a/server/test/medium/specs/services/user.service.spec.ts +++ b/server/test/medium/specs/services/user.service.spec.ts @@ -6,6 +6,7 @@ import { CryptoRepository } from 'src/repositories/crypto.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { DB } from 'src/schema'; import { UserService } from 'src/services/user.service'; @@ -21,7 +22,7 @@ const setup = (db?: Kysely) => { return newMediumService(UserService, { database: db || defaultDatabase, real: [CryptoRepository, ConfigRepository, SystemMetadataRepository, UserRepository], - mock: [LoggingRepository, JobRepository], + mock: [LoggingRepository, JobRepository, TelemetryRepository], }); }; diff --git a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts index 808a4785ce..fd563f4db1 100644 --- a/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset-exif.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; @@ -13,6 +14,18 @@ const setup = async (db?: Kysely) => { return { auth, user, session, ctx }; }; +const updateSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetExifUpdateV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + +const backfillSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetExifBackfillV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + beforeAll(async () => { defaultDatabase = await getKyselyDB(); }); @@ -28,8 +41,8 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: { @@ -59,12 +72,13 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { state: null, timeZone: null, }, - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]); }); it('should sync album asset exif for own user', async () => { @@ -74,8 +88,15 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { const { album } = await ctx.newAlbum({ ownerId: auth.user.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetExifV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.SyncAckV1 }), + expect.objectContaining({ type: SyncEntityType.AlbumAssetExifCreateV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); }); it('should not sync album asset exif for unrelated user', async () => { @@ -90,62 +111,56 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { const { session } = await ctx.newSession({ userId: user3.id }); const authUser3 = factory.auth({ session, user: user3 }); - await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetExifV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]); }); it('should backfill album assets exif when a user shares an album with you', async () => { const { auth, ctx } = await setup(); const { user: user2 } = await ctx.newUser(); - const { asset: asset1Owner } = await ctx.newAsset({ ownerId: auth.user.id }); - await ctx.newExif({ assetId: asset1Owner.id, make: 'asset1Owner' }); - await wait(2); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); const { asset: asset1User2 } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newExif({ assetId: asset1User2.id, make: 'asset1User2' }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset1User2.id }); await wait(2); const { asset: asset2User2 } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newExif({ assetId: asset2User2.id, make: 'asset2User2' }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset2User2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await wait(2); const { asset: asset3User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset3User2.id }); await ctx.newExif({ assetId: asset3User2.id, make: 'asset3User2' }); - const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); - await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); + await wait(2); await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ assetId: asset2User2.id, }), - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); // ack initial album asset exif sync await ctx.syncAckAll(auth, response); // create a second album - const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); - await Promise.all( - [asset1User2.id, asset2User2.id, asset3User2.id, asset1Owner.id].map((assetId) => - ctx.newAlbumAsset({ albumId: album2.id, assetId }), - ), - ); await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); // should backfill the album user const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); expect(newResponse).toEqual([ - { - ack: expect.any(String), - data: expect.objectContaining({ - assetId: asset1Owner.id, - }), - type: SyncEntityType.AlbumAssetExifBackfillV1, - }, { ack: expect.any(String), data: expect.objectContaining({ @@ -160,21 +175,192 @@ describe(SyncRequestType.AlbumAssetExifsV1, () => { }), type: SyncEntityType.AlbumAssetExifBackfillV1, }, - { - ack: expect.stringContaining(SyncEntityType.AlbumAssetExifBackfillV1), - data: {}, - type: SyncEntityType.SyncAckV1, - }, + backfillSyncAck, + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ assetId: asset3User2.id, }), - type: SyncEntityType.AlbumAssetExifV1, + type: SyncEntityType.AlbumAssetExifCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]); + }); + + it('should sync old asset exif when a user adds them to an album they share you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: firstAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'firstAsset' }); + await ctx.newExif({ assetId: firstAsset.id, make: 'firstAsset' }); + const { asset: secondAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'secondAsset' }); + await ctx.newExif({ assetId: secondAsset.id, make: 'secondAsset' }); + const { asset: album1Asset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'album1Asset' }); + await ctx.newExif({ assetId: album1Asset.id, make: 'album1Asset' }); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: firstAsset.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id }); + await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(firstAlbumResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: album1Asset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, firstAlbumResponse); + + await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: firstAsset.id, + }), + type: SyncEntityType.AlbumAssetExifBackfillV1, + }, + backfillSyncAck, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + // ack initial album asset sync + await ctx.syncAckAll(auth, response); + + await ctx.newAlbumAsset({ albumId: album2.id, assetId: secondAsset.id }); + await wait(2); + + // should backfill the new asset even though it's older than the first asset + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(newResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: secondAsset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, newResponse); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetExifsV1]); + }); + + it('should sync asset exif updates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: asset.id, make: 'asset' }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: asset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.upsertExif({ + assetId: asset.id, + city: 'New City', + }); + + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: asset.id, + city: 'New City', + }), + type: SyncEntityType.AlbumAssetExifUpdateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); + + it('should sync delayed asset exif creates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: assetWithExif } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: assetWithExif.id, make: 'assetWithExif' }); + const { asset: assetDelayedExif } = await ctx.newAsset({ ownerId: user2.id }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + const { asset: newerAsset } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newExif({ assetId: newerAsset.id, make: 'newerAsset' }); + await ctx.newAlbumAsset({ albumId: album.id, assetId: assetWithExif.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: assetDelayedExif.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: newerAsset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1]); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: assetWithExif.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: newerAsset.id, + }), + type: SyncEntityType.AlbumAssetExifCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.upsertExif({ + assetId: assetDelayedExif.id, + city: 'Delayed Exif', + }); + + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetExifsV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + assetId: assetDelayedExif.id, + city: 'Delayed Exif', + }), + type: SyncEntityType.AlbumAssetExifUpdateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); }); }); diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 9a42c0f027..4f053937b8 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { AlbumUserRole, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { factory } from 'test/small.factory'; @@ -13,6 +14,18 @@ const setup = async (db?: Kysely) => { return { auth, user, session, ctx }; }; +const updateSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetUpdateV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + +const backfillSyncAck = { + ack: expect.stringContaining(SyncEntityType.AlbumAssetBackfillV1), + data: {}, + type: SyncEntityType.SyncAckV1, +}; + beforeAll(async () => { defaultDatabase = await getKyselyDB(); }); @@ -38,14 +51,15 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: '0:10:00.00000', livePhotoVideoId: null, stackId: null, + libraryId: null, }); const { album } = await ctx.newAlbum({ ownerId: user2.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: { @@ -64,13 +78,15 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: asset.duration, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]); }); it('should sync album asset for own user', async () => { @@ -79,8 +95,15 @@ describe(SyncRequestType.AlbumAssetsV1, () => { const { album } = await ctx.newAlbum({ ownerId: auth.user.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.SyncAckV1 }), + expect.objectContaining({ type: SyncEntityType.AlbumAssetCreateV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); }); it('should not sync album asset for unrelated user', async () => { @@ -94,59 +117,52 @@ describe(SyncRequestType.AlbumAssetsV1, () => { const { session } = await ctx.newSession({ userId: user3.id }); const authUser3 = factory.auth({ session, user: user3 }); - await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]); }); it('should backfill album assets when a user shares an album with you', async () => { const { auth, ctx } = await setup(); const { user: user2 } = await ctx.newUser(); - const { asset: asset1Owner } = await ctx.newAsset({ ownerId: auth.user.id }); - await wait(2); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); const { asset: asset1User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset1User2.id }); await wait(2); const { asset: asset2User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset2User2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await wait(2); const { asset: asset3User2 } = await ctx.newAsset({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: asset3User2.id }); await wait(2); - const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); - await ctx.newAlbumAsset({ albumId: album1.id, assetId: asset2User2.id }); await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ id: asset2User2.id, }), - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); // ack initial album asset sync await ctx.syncAckAll(auth, response); - // create a second album - const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); - await Promise.all( - [asset1User2.id, asset2User2.id, asset3User2.id, asset1Owner.id].map((assetId) => - ctx.newAlbumAsset({ albumId: album2.id, assetId }), - ), - ); await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); // should backfill the album user const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); expect(newResponse).toEqual([ - { - ack: expect.any(String), - data: expect.objectContaining({ - id: asset1Owner.id, - }), - type: SyncEntityType.AlbumAssetBackfillV1, - }, { ack: expect.any(String), data: expect.objectContaining({ @@ -161,21 +177,131 @@ describe(SyncRequestType.AlbumAssetsV1, () => { }), type: SyncEntityType.AlbumAssetBackfillV1, }, - { - ack: expect.stringContaining(SyncEntityType.AlbumAssetBackfillV1), - data: {}, - type: SyncEntityType.SyncAckV1, - }, + backfillSyncAck, + updateSyncAck, { ack: expect.any(String), data: expect.objectContaining({ id: asset3User2.id, }), - type: SyncEntityType.AlbumAssetV1, + type: SyncEntityType.AlbumAssetCreateV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]); + }); + + it('should sync old assets when a user adds them to an album they share you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset: firstAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'firstAsset' }); + const { asset: secondAsset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'secondAsset' }); + const { asset: album1Asset } = await ctx.newAsset({ ownerId: user2.id, originalFileName: 'album1Asset' }); + const { album: album1 } = await ctx.newAlbum({ ownerId: user2.id }); + const { album: album2 } = await ctx.newAlbum({ ownerId: user2.id }); + await ctx.newAlbumAsset({ albumId: album2.id, assetId: firstAsset.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id }); + await ctx.newAlbumUser({ albumId: album1.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const firstAlbumResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(firstAlbumResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: album1Asset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, firstAlbumResponse); + + await ctx.newAlbumUser({ albumId: album2.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: firstAsset.id, + }), + type: SyncEntityType.AlbumAssetBackfillV1, + }, + backfillSyncAck, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + // ack initial album asset sync + await ctx.syncAckAll(auth, response); + + await ctx.newAlbumAsset({ albumId: album2.id, assetId: secondAsset.id }); + await wait(2); + + // should backfill the new asset even though it's older than the first asset + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(newResponse).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: secondAsset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, newResponse); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumAssetsV1]); + }); + + it('should sync asset updates for an album shared with you', async () => { + const { auth, ctx } = await setup(); + const { user: user2 } = await ctx.newUser(); + const { asset } = await ctx.newAsset({ ownerId: user2.id, isFavorite: false }); + const { album } = await ctx.newAlbum({ ownerId: user2.id }); + await wait(2); + await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); + await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(response).toEqual([ + updateSyncAck, + { + ack: expect.any(String), + data: expect.objectContaining({ + id: asset.id, + }), + type: SyncEntityType.AlbumAssetCreateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + // update the asset + const assetRepository = ctx.get(AssetRepository); + await assetRepository.update({ + id: asset.id, + isFavorite: true, + }); + + const updateResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumAssetsV1]); + expect(updateResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: asset.id, + isFavorite: true, + }), + type: SyncEntityType.AlbumAssetUpdateV1, + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); }); }); diff --git a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts index ee529c5001..b6bd9db010 100644 --- a/server/test/medium/specs/sync/sync-album-to-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-to-asset.spec.ts @@ -28,7 +28,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -38,10 +37,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should sync album to asset for owned albums', async () => { @@ -51,7 +51,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -61,10 +60,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should detect and sync the album to asset for shared albums', async () => { @@ -76,7 +76,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -86,10 +85,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should not sync album to asset for an album owned by another user', async () => { @@ -98,7 +98,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { const { asset } = await ctx.newAsset({ ownerId: user2.id }); const { album } = await ctx.newAlbum({ ownerId: user2.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should backfill album to assets when a user shares an album with you', async () => { @@ -114,7 +114,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album1.id, assetId: album1Asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -124,6 +123,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); // ack initial album to asset sync @@ -148,10 +148,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { data: {}, type: SyncEntityType.SyncAckV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should detect and sync a deleted album to asset relation', async () => { @@ -162,7 +163,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -172,6 +172,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -179,7 +180,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await wait(2); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -189,10 +189,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should detect and sync a deleted album to asset relation when an asset is deleted', async () => { @@ -203,7 +204,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -213,6 +213,7 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -220,7 +221,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await wait(2); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -230,10 +230,11 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); it('should not sync a deleted album to asset relation when the album is deleted', async () => { @@ -244,7 +245,6 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -254,11 +254,12 @@ describe(SyncRequestType.AlbumToAssetsV1, () => { }, type: SyncEntityType.AlbumToAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await albumRepo.delete(album.id); await wait(2); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumToAssetsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-album-user.spec.ts b/server/test/medium/specs/sync/sync-album-user.spec.ts index e3d8a21493..d779ffd9f3 100644 --- a/server/test/medium/specs/sync/sync-album-user.spec.ts +++ b/server/test/medium/specs/sync/sync-album-user.spec.ts @@ -34,6 +34,7 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); }); @@ -45,7 +46,6 @@ describe(SyncRequestType.AlbumUsersV1, () => { const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -56,10 +56,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); it('should detect and sync an updated shared user', async () => { @@ -71,11 +72,10 @@ describe(SyncRequestType.AlbumUsersV1, () => { const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); await albumUserRepo.update({ albumsId: album.id, usersId: user1.id }, { role: AlbumUserRole.Viewer }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -86,10 +86,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); it('should detect and sync a deleted shared user', async () => { @@ -100,9 +101,8 @@ describe(SyncRequestType.AlbumUsersV1, () => { const { albumUser } = await ctx.newAlbumUser({ albumId: album.id, userId: user1.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(1); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); await albumUserRepo.delete({ albumsId: album.id, usersId: user1.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); @@ -115,10 +115,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); }); @@ -134,7 +135,6 @@ describe(SyncRequestType.AlbumUsersV1, () => { }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -145,10 +145,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); it('should detect and sync an updated shared user', async () => { @@ -161,10 +162,14 @@ describe(SyncRequestType.AlbumUsersV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(2); + expect(response).toEqual([ + expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }), + expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); await albumUserRepo.update({ albumsId: album.id, usersId: user.id }, { role: AlbumUserRole.Viewer }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); @@ -178,10 +183,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); it('should detect and sync a deleted shared user', async () => { @@ -194,10 +200,14 @@ describe(SyncRequestType.AlbumUsersV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(2); + expect(response).toEqual([ + expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }), + expect.objectContaining({ type: SyncEntityType.AlbumUserV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); await albumUserRepo.delete({ albumsId: album.id, usersId: user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); @@ -210,10 +220,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); it('should backfill album users when a user shares an album with you', async () => { @@ -232,7 +243,6 @@ describe(SyncRequestType.AlbumUsersV1, () => { await ctx.newAlbumUser({ albumId: album1.id, userId: user2.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -243,6 +253,7 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); // ack initial user @@ -285,10 +296,11 @@ describe(SyncRequestType.AlbumUsersV1, () => { }), type: SyncEntityType.AlbumUserV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumUsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumUsersV1]); }); }); }); diff --git a/server/test/medium/specs/sync/sync-album.spec.ts b/server/test/medium/specs/sync/sync-album.spec.ts index 9f44e617e3..591d7e1f3c 100644 --- a/server/test/medium/specs/sync/sync-album.spec.ts +++ b/server/test/medium/specs/sync/sync-album.spec.ts @@ -24,7 +24,6 @@ describe(SyncRequestType.AlbumsV1, () => { const { album } = await ctx.newAlbum({ ownerId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -35,10 +34,11 @@ describe(SyncRequestType.AlbumsV1, () => { }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync a new album', async () => { @@ -46,7 +46,6 @@ describe(SyncRequestType.AlbumsV1, () => { const { album } = await ctx.newAlbum({ ownerId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -55,10 +54,11 @@ describe(SyncRequestType.AlbumsV1, () => { }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync an album delete', async () => { @@ -67,7 +67,6 @@ describe(SyncRequestType.AlbumsV1, () => { const { album } = await ctx.newAlbum({ ownerId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -76,12 +75,12 @@ describe(SyncRequestType.AlbumsV1, () => { }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await albumRepo.delete(album.id); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -90,10 +89,11 @@ describe(SyncRequestType.AlbumsV1, () => { }, type: SyncEntityType.AlbumDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); describe('shared albums', () => { @@ -104,17 +104,17 @@ describe(SyncRequestType.AlbumsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), data: expect.objectContaining({ id: album.id }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync an album share (share before sync)', async () => { @@ -124,17 +124,17 @@ describe(SyncRequestType.AlbumsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), data: expect.objectContaining({ id: album.id }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync an album share (share after sync)', async () => { @@ -150,23 +150,24 @@ describe(SyncRequestType.AlbumsV1, () => { data: expect.objectContaining({ id: userAlbum.id }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newAlbumUser({ userId: auth.user.id, albumId: user2Album.id, role: AlbumUserRole.Editor }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), data: expect.objectContaining({ id: user2Album.id }), type: SyncEntityType.AlbumV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync an album delete`', async () => { @@ -177,24 +178,27 @@ describe(SyncRequestType.AlbumsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); + expect(response).toEqual([ + expect.objectContaining({ type: SyncEntityType.AlbumV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); await albumRepo.delete(album.id); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), data: { albumId: album.id }, type: SyncEntityType.AlbumDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); it('should detect and sync an album unshare as an album delete', async () => { @@ -205,10 +209,13 @@ describe(SyncRequestType.AlbumsV1, () => { await ctx.newAlbumUser({ albumId: album.id, userId: auth.user.id, role: AlbumUserRole.Editor }); const response = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); - expect(response).toHaveLength(1); + expect(response).toEqual([ + expect.objectContaining({ type: SyncEntityType.AlbumV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); await albumUserRepo.delete({ albumsId: album.id, usersId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.AlbumsV1]); @@ -218,10 +225,11 @@ describe(SyncRequestType.AlbumsV1, () => { data: { albumId: album.id }, type: SyncEntityType.AlbumDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.AlbumsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AlbumsV1]); }); }); }); diff --git a/server/test/medium/specs/sync/sync-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-asset-exif.spec.ts index 425ea89054..9aae961b0c 100644 --- a/server/test/medium/specs/sync/sync-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-exif.spec.ts @@ -24,7 +24,6 @@ describe(SyncRequestType.AssetExifsV1, () => { await ctx.newExif({ assetId: asset.id, make: 'Canon' }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -57,10 +56,11 @@ describe(SyncRequestType.AssetExifsV1, () => { }, type: SyncEntityType.AssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetExifsV1]); }); it('should only sync asset exif for own user', async () => { @@ -72,7 +72,10 @@ describe(SyncRequestType.AssetExifsV1, () => { const { session } = await ctx.newSession({ userId: user2.id }); const auth2 = factory.auth({ session, user: user2 }); - await expect(ctx.syncStream(auth2, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth2, [SyncRequestType.AssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetExifV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetExifsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-asset-face.spec.ts b/server/test/medium/specs/sync/sync-asset-face.spec.ts index 68d3007c52..8b4310e600 100644 --- a/server/test/medium/specs/sync/sync-asset-face.spec.ts +++ b/server/test/medium/specs/sync/sync-asset-face.spec.ts @@ -26,7 +26,6 @@ describe(SyncEntityType.AssetFaceV1, () => { const { assetFace } = await ctx.newAssetFace({ assetId: asset.id, personId: person.id }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -44,10 +43,11 @@ describe(SyncEntityType.AssetFaceV1, () => { }), type: 'AssetFaceV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]); }); it('should detect and sync a deleted asset face', async () => { @@ -58,7 +58,6 @@ describe(SyncEntityType.AssetFaceV1, () => { await personRepo.deleteAssetFace(assetFace.id); const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -67,10 +66,11 @@ describe(SyncEntityType.AssetFaceV1, () => { }, type: 'AssetFaceDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]); }); it('should not sync an asset face or asset face delete for an unrelated user', async () => { @@ -82,11 +82,18 @@ describe(SyncEntityType.AssetFaceV1, () => { const { assetFace } = await ctx.newAssetFace({ assetId: asset.id }); const auth2 = factory.auth({ session, user: user2 }); - expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetFaceV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]); await personRepo.deleteAssetFace(assetFace.id); - expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetFaceDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetFacesV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-asset-metadata.spec.ts b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts new file mode 100644 index 0000000000..8ba9630520 --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-metadata.spec.ts @@ -0,0 +1,128 @@ +import { Kysely } from 'kysely'; +import { AssetMetadataKey, SyncEntityType, SyncRequestType } from 'src/enum'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AssetMetadataV1, () => { + it('should detect and sync new asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetMetadataV1]); + }); + + it('should update asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc456' } }]); + + const updatedResponse = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(updatedResponse).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc456' }, + }, + type: 'AssetMetadataV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, updatedResponse); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetMetadataV1]); + }); +}); + +describe(SyncEntityType.AssetMetadataDeleteV1, () => { + it('should delete and sync asset metadata', async () => { + const { auth, user, ctx } = await setup(); + + const assetRepo = ctx.get(AssetRepository); + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await assetRepo.upsertMetadata(asset.id, [{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 'abc123' } }]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + key: AssetMetadataKey.MobileApp, + assetId: asset.id, + value: { iCloudId: 'abc123' }, + }, + type: 'AssetMetadataV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + await assetRepo.deleteMetadataByKey(asset.id, AssetMetadataKey.MobileApp); + + await expect(ctx.syncStream(auth, [SyncRequestType.AssetMetadataV1])).resolves.toEqual([ + { + ack: expect.any(String), + data: { + assetId: asset.id, + key: AssetMetadataKey.MobileApp, + }, + type: 'AssetMetadataDeleteV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 52d6bcb524..066cb2de4d 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -36,10 +36,10 @@ describe(SyncEntityType.AssetV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -59,13 +59,15 @@ describe(SyncEntityType.AssetV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: 'AssetV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); }); it('should detect and sync a deleted asset', async () => { @@ -75,7 +77,6 @@ describe(SyncEntityType.AssetV1, () => { await assetRepo.remove(asset); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -84,10 +85,11 @@ describe(SyncEntityType.AssetV1, () => { }, type: 'AssetDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); }); it('should not sync an asset or asset delete for an unrelated user', async () => { @@ -98,11 +100,17 @@ describe(SyncEntityType.AssetV1, () => { const { asset } = await ctx.newAsset({ ownerId: user2.id }); const auth2 = factory.auth({ session, user: user2 }); - expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); await assetRepo.remove(asset); - expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.AssetsV1])).toHaveLength(0); + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-auth-user.spec.ts b/server/test/medium/specs/sync/sync-auth-user.spec.ts new file mode 100644 index 0000000000..43411129d3 --- /dev/null +++ b/server/test/medium/specs/sync/sync-auth-user.spec.ts @@ -0,0 +1,106 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AuthUserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: user.id, + isAdmin: user.isAdmin, + deletedAt: user.deletedAt, + name: user.name, + avatarColor: user.avatarColor, + email: user.email, + pinCode: user.pinCode, + hasProfileImage: false, + profileChangedAt: (user.profileChangedAt as Date).toISOString(), + oauthId: user.oauthId, + quotaSizeInBytes: user.quotaSizeInBytes, + quotaUsageInBytes: user.quotaUsageInBytes, + storageLabel: user.storageLabel, + }, + type: 'AuthUserV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AuthUsersV1]); + }); + + it('should sync a change and then another change to that same user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const userRepo = ctx.get(UserRepository); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + + await ctx.syncAckAll(auth, response); + + await userRepo.update(user.id, { isAdmin: true }); + + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(newResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: true, + }), + type: 'AuthUserV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); + + it('should only sync the auth user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + await ctx.newUser(); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-complete.spec.ts b/server/test/medium/specs/sync/sync-complete.spec.ts new file mode 100644 index 0000000000..8a94061631 --- /dev/null +++ b/server/test/medium/specs/sync/sync-complete.spec.ts @@ -0,0 +1,60 @@ +import { Kysely } from 'kysely'; +import { DateTime } from 'luxon'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { SyncCheckpointRepository } from 'src/repositories/sync-checkpoint.repository'; +import { DB } from 'src/schema'; +import { toAck } from 'src/utils/sync'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; +import { v7 } from 'uuid'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.SyncCompleteV1, () => { + it('should work', async () => { + const { auth, ctx } = await setup(); + + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); + }); + + it('should detect an old checkpoint and send back a reset', async () => { + const { auth, session, ctx } = await setup(); + const updateId = v7({ msecs: DateTime.now().minus({ days: 60 }).toMillis() }); + + await ctx.get(SyncCheckpointRepository).upsertAll([ + { + type: SyncEntityType.SyncCompleteV1, + sessionId: session.id, + ack: toAck({ type: SyncEntityType.SyncCompleteV1, updateId }), + }, + ]); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }]); + }); + + it('should not send back a reset if the checkpoint is recent', async () => { + const { auth, session, ctx } = await setup(); + const updateId = v7({ msecs: DateTime.now().minus({ days: 7 }).toMillis() }); + + await ctx.get(SyncCheckpointRepository).upsertAll([ + { + type: SyncEntityType.SyncCompleteV1, + sessionId: session.id, + ack: toAck({ type: SyncEntityType.SyncCompleteV1, updateId }), + }, + ]); + + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-memory-asset.spec.ts b/server/test/medium/specs/sync/sync-memory-asset.spec.ts index a3247637d7..f0cae0934e 100644 --- a/server/test/medium/specs/sync/sync-memory-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-memory-asset.spec.ts @@ -25,7 +25,6 @@ describe(SyncEntityType.MemoryToAssetV1, () => { await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id }); const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -35,10 +34,11 @@ describe(SyncEntityType.MemoryToAssetV1, () => { }, type: 'MemoryToAssetV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]); }); it('should detect and sync a deleted memory to asset relation', async () => { @@ -50,7 +50,6 @@ describe(SyncEntityType.MemoryToAssetV1, () => { await memoryRepo.removeAssetIds(memory.id, [asset.id]); const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -60,10 +59,11 @@ describe(SyncEntityType.MemoryToAssetV1, () => { }, type: 'MemoryToAssetDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]); }); it('should not sync a memory to asset relation or delete for an unrelated user', async () => { @@ -74,11 +74,18 @@ describe(SyncEntityType.MemoryToAssetV1, () => { const { memory } = await ctx.newMemory({ ownerId: user2.id }); await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id }); - expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0); - expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1); + expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.MemoryToAssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]); await memoryRepo.removeAssetIds(memory.id, [asset.id]); - expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0); - expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1); + + expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.MemoryToAssetDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoryToAssetsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-memory.spec.ts b/server/test/medium/specs/sync/sync-memory.spec.ts index fa833ad094..1889f39626 100644 --- a/server/test/medium/specs/sync/sync-memory.spec.ts +++ b/server/test/medium/specs/sync/sync-memory.spec.ts @@ -23,7 +23,6 @@ describe(SyncEntityType.MemoryV1, () => { const { memory } = await ctx.newMemory({ ownerId: user1.id }); const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -43,10 +42,11 @@ describe(SyncEntityType.MemoryV1, () => { }, type: 'MemoryV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]); }); it('should detect and sync a deleted memory', async () => { @@ -56,7 +56,6 @@ describe(SyncEntityType.MemoryV1, () => { await memoryRepo.delete(memory.id); const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -65,10 +64,11 @@ describe(SyncEntityType.MemoryV1, () => { }, type: 'MemoryDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]); }); it('should sync a memory and then an update to that same memory', async () => { @@ -77,29 +77,29 @@ describe(SyncEntityType.MemoryV1, () => { const { memory } = await ctx.newMemory({ ownerId: user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), data: expect.objectContaining({ id: memory.id }), type: 'MemoryV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await memoryRepo.update(memory.id, { seenAt: new Date() }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), data: expect.objectContaining({ id: memory.id }), type: 'MemoryV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]); }); it('should not sync a memory or a memory delete for an unrelated user', async () => { @@ -108,8 +108,8 @@ describe(SyncEntityType.MemoryV1, () => { const { user: user2 } = await ctx.newUser(); const { memory } = await ctx.newMemory({ ownerId: user2.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]); await memoryRepo.delete(memory.id); - await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.MemoriesV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts index c33eb59dbb..d44c088f17 100644 --- a/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset-exif.spec.ts @@ -26,7 +26,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { await ctx.newExif({ assetId: asset.id, make: 'Canon' }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -59,10 +58,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { }, type: SyncEntityType.PartnerAssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); it('should not sync partner asset exif for own user', async () => { @@ -72,8 +72,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); await ctx.newExif({ assetId: asset.id, make: 'Canon' }); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetExifV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); it('should not sync partner asset exif for unrelated user', async () => { @@ -86,8 +89,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { const { session } = await ctx.newSession({ userId: user3.id }); const authUser3 = factory.auth({ session, user: user3 }); - await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(authUser3, [SyncRequestType.AssetExifsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetExifV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); it('should backfill partner asset exif when a partner shared their library with you', async () => { @@ -102,7 +108,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual( expect.arrayContaining([ { @@ -112,6 +117,7 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { }), type: SyncEntityType.PartnerAssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]), ); @@ -119,7 +125,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(newResponse).toHaveLength(2); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -133,10 +138,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { data: {}, type: SyncEntityType.SyncAckV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); it('should handle partners with users ids lower than a uuidv7', async () => { @@ -151,7 +157,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -160,15 +165,15 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { }), type: SyncEntityType.PartnerAssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); // This checks that our ack upsert is correct - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(newResponse).toHaveLength(2); expect(newResponse).toEqual([ { ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)), @@ -182,10 +187,11 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { data: {}, type: SyncEntityType.SyncAckV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => { @@ -203,7 +209,6 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -212,13 +217,13 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { }), type: SyncEntityType.PartnerAssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1]); - expect(newResponse).toHaveLength(3); expect(newResponse).toEqual([ { ack: expect.stringMatching(new RegExp(`${SyncEntityType.PartnerAssetExifBackfillV1}\\|.+?\\|.+`)), @@ -239,9 +244,10 @@ describe(SyncRequestType.PartnerAssetExifsV1, () => { }), type: SyncEntityType.PartnerAssetExifV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetExifsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetExifsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 2daa750bf3..c30cfcf6bd 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -40,12 +40,12 @@ describe(SyncRequestType.PartnerAssetsV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -65,13 +65,15 @@ describe(SyncRequestType.PartnerAssetsV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: SyncEntityType.PartnerAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should detect and sync a deleted partner asset', async () => { @@ -84,7 +86,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => { await assetRepo.remove(asset); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -93,10 +94,11 @@ describe(SyncRequestType.PartnerAssetsV1, () => { }, type: SyncEntityType.PartnerAssetDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should not sync a deleted partner asset due to a user delete', async () => { @@ -107,7 +109,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); await ctx.newAsset({ ownerId: user2.id }); await userRepo.delete({ id: user2.id }, true); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should not sync a deleted partner asset due to a partner delete (unshare)', async () => { @@ -117,9 +119,12 @@ describe(SyncRequestType.PartnerAssetsV1, () => { const { user: user2 } = await ctx.newUser(); await ctx.newAsset({ ownerId: user2.id }); const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.PartnerAssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await partnerRepo.remove(partner); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should not sync an asset or asset delete for own user', async () => { @@ -130,13 +135,19 @@ describe(SyncRequestType.PartnerAssetsV1, () => { const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); await assetRepo.remove(asset); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should not sync an asset or asset delete for unrelated user', async () => { @@ -148,13 +159,19 @@ describe(SyncRequestType.PartnerAssetsV1, () => { const { asset } = await ctx.newAsset({ ownerId: user2.id }); const auth2 = factory.auth({ session, user: user2 }); - await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); await assetRepo.remove(asset); - await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth2, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should backfill partner assets when a partner shared their library with you', async () => { @@ -168,7 +185,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -177,13 +193,13 @@ describe(SyncRequestType.PartnerAssetsV1, () => { }), type: SyncEntityType.PartnerAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(newResponse).toHaveLength(2); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -197,10 +213,11 @@ describe(SyncRequestType.PartnerAssetsV1, () => { data: {}, type: SyncEntityType.SyncAckV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); it('should only backfill partner assets created prior to the current partner asset checkpoint', async () => { @@ -216,7 +233,6 @@ describe(SyncRequestType.PartnerAssetsV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -225,12 +241,12 @@ describe(SyncRequestType.PartnerAssetsV1, () => { }), type: SyncEntityType.PartnerAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1]); - expect(newResponse).toHaveLength(3); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -251,9 +267,10 @@ describe(SyncRequestType.PartnerAssetsV1, () => { }), type: SyncEntityType.PartnerAssetV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerAssetsV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerAssetsV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-partner-stack.spec.ts b/server/test/medium/specs/sync/sync-partner-stack.spec.ts index 3a879cb580..e1d8416799 100644 --- a/server/test/medium/specs/sync/sync-partner-stack.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-stack.spec.ts @@ -29,7 +29,6 @@ describe(SyncRequestType.PartnerStacksV1, () => { const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -42,10 +41,11 @@ describe(SyncRequestType.PartnerStacksV1, () => { }, type: SyncEntityType.PartnerStackV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should detect and sync a deleted partner stack', async () => { @@ -58,7 +58,6 @@ describe(SyncRequestType.PartnerStacksV1, () => { await stackRepo.delete(stack.id); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.stringContaining('PartnerStackDeleteV1'), @@ -67,10 +66,11 @@ describe(SyncRequestType.PartnerStacksV1, () => { }, type: SyncEntityType.PartnerStackDeleteV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should not sync a deleted partner stack due to a user delete', async () => { @@ -81,7 +81,7 @@ describe(SyncRequestType.PartnerStacksV1, () => { const { asset } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newStack({ ownerId: user2.id }, [asset.id]); await userRepo.delete({ id: user2.id }, true); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should not sync a deleted partner stack due to a partner delete (unshare)', async () => { @@ -91,9 +91,12 @@ describe(SyncRequestType.PartnerStacksV1, () => { const { asset } = await ctx.newAsset({ ownerId: user2.id }); await ctx.newStack({ ownerId: user2.id }, [asset.id]); const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.PartnerStackV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await partnerRepo.remove(partner); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should not sync a stack or stack delete for own user', async () => { @@ -103,11 +106,17 @@ describe(SyncRequestType.PartnerStacksV1, () => { const { asset } = await ctx.newAsset({ ownerId: user.id }); const { stack } = await ctx.newStack({ ownerId: user.id }, [asset.id]); await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.StackV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); await stackRepo.delete(stack.id); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.StackDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should not sync a stack or stack delete for unrelated user', async () => { @@ -119,13 +128,19 @@ describe(SyncRequestType.PartnerStacksV1, () => { const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]); const auth2 = factory.auth({ session, user: user2 }); - await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.StackV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); await stackRepo.delete(stack.id); - await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0); + await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.StackDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should backfill partner stacks when a partner shared their library with you', async () => { @@ -140,7 +155,6 @@ describe(SyncRequestType.PartnerStacksV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.stringContaining('PartnerStackV1'), @@ -149,12 +163,12 @@ describe(SyncRequestType.PartnerStacksV1, () => { }), type: SyncEntityType.PartnerStackV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newPartner({ sharedById: user3.id, sharedWithId: user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(newResponse).toHaveLength(2); expect(newResponse).toEqual([ { ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1), @@ -168,10 +182,11 @@ describe(SyncRequestType.PartnerStacksV1, () => { data: {}, type: SyncEntityType.SyncAckV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); it('should only backfill partner stacks created prior to the current partner stack checkpoint', async () => { @@ -189,7 +204,6 @@ describe(SyncRequestType.PartnerStacksV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.stringContaining(SyncEntityType.PartnerStackV1), @@ -198,12 +212,12 @@ describe(SyncRequestType.PartnerStacksV1, () => { }), type: SyncEntityType.PartnerStackV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]); - expect(newResponse).toHaveLength(3); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -224,9 +238,10 @@ describe(SyncRequestType.PartnerStacksV1, () => { }), type: SyncEntityType.PartnerStackV1, }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnerStacksV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-partner.spec.ts b/server/test/medium/specs/sync/sync-partner.spec.ts index d20970da8f..19c386070a 100644 --- a/server/test/medium/specs/sync/sync-partner.spec.ts +++ b/server/test/medium/specs/sync/sync-partner.spec.ts @@ -26,7 +26,6 @@ describe(SyncEntityType.PartnerV1, () => { const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -37,10 +36,11 @@ describe(SyncEntityType.PartnerV1, () => { }, type: 'PartnerV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); it('should detect and sync a deleted partner', async () => { @@ -53,22 +53,20 @@ describe(SyncEntityType.PartnerV1, () => { await partnerRepo.remove(partner); const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]); - expect(response).toHaveLength(1); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - sharedById: partner.sharedById, - sharedWithId: partner.sharedWithId, - }, - type: 'PartnerDeleteV1', + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + sharedById: partner.sharedById, + sharedWithId: partner.sharedWithId, }, - ]), - ); + type: 'PartnerDeleteV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); it('should detect and sync a partner share both to and from another user', async () => { @@ -79,32 +77,30 @@ describe(SyncEntityType.PartnerV1, () => { const { partner: partner2 } = await ctx.newPartner({ sharedById: user1.id, sharedWithId: user2.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]); - expect(response).toHaveLength(2); - expect(response).toEqual( - expect.arrayContaining([ - { - ack: expect.any(String), - data: { - inTimeline: partner1.inTimeline, - sharedById: partner1.sharedById, - sharedWithId: partner1.sharedWithId, - }, - type: 'PartnerV1', + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + inTimeline: partner1.inTimeline, + sharedById: partner1.sharedById, + sharedWithId: partner1.sharedWithId, }, - { - ack: expect.any(String), - data: { - inTimeline: partner2.inTimeline, - sharedById: partner2.sharedById, - sharedWithId: partner2.sharedWithId, - }, - type: 'PartnerV1', + type: 'PartnerV1', + }, + { + ack: expect.any(String), + data: { + inTimeline: partner2.inTimeline, + sharedById: partner2.sharedById, + sharedWithId: partner2.sharedWithId, }, - ]), - ); + type: 'PartnerV1', + }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); it('should sync a partner and then an update to that same partner', async () => { @@ -116,7 +112,6 @@ describe(SyncEntityType.PartnerV1, () => { const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user1.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -127,6 +122,7 @@ describe(SyncEntityType.PartnerV1, () => { }, type: 'PartnerV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -137,7 +133,6 @@ describe(SyncEntityType.PartnerV1, () => { ); const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnersV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), @@ -148,10 +143,11 @@ describe(SyncEntityType.PartnerV1, () => { }, type: 'PartnerV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); it('should not sync a partner or partner delete for an unrelated user', async () => { @@ -163,9 +159,9 @@ describe(SyncEntityType.PartnerV1, () => { const { user: user3 } = await ctx.newUser(); const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user3.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); await partnerRepo.remove(partner); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); it('should not sync a partner delete after a user is deleted', async () => { @@ -177,6 +173,6 @@ describe(SyncEntityType.PartnerV1, () => { await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); await userRepo.delete({ id: user2.id }, true); - await expect(ctx.syncStream(auth, [SyncRequestType.PartnersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PartnersV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-person.spec.ts b/server/test/medium/specs/sync/sync-person.spec.ts index fbf401e377..6fdb5a58f2 100644 --- a/server/test/medium/specs/sync/sync-person.spec.ts +++ b/server/test/medium/specs/sync/sync-person.spec.ts @@ -24,7 +24,6 @@ describe(SyncEntityType.PersonV1, () => { const { person } = await ctx.newPerson({ ownerId: auth.user.id }); const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -40,10 +39,11 @@ describe(SyncEntityType.PersonV1, () => { }), type: 'PersonV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PeopleV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]); }); it('should detect and sync a deleted person', async () => { @@ -53,7 +53,6 @@ describe(SyncEntityType.PersonV1, () => { await personRepo.delete([person.id]); const response = await ctx.syncStream(auth, [SyncRequestType.PeopleV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -62,10 +61,11 @@ describe(SyncEntityType.PersonV1, () => { }, type: 'PersonDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.PeopleV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]); }); it('should not sync a person or person delete for an unrelated user', async () => { @@ -76,11 +76,18 @@ describe(SyncEntityType.PersonV1, () => { const { person } = await ctx.newPerson({ ownerId: user2.id }); const auth2 = factory.auth({ session, user: user2 }); - expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.PeopleV1])).toHaveLength(0); + expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.PersonV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]); await personRepo.delete([person.id]); - expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toHaveLength(1); - expect(await ctx.syncStream(auth, [SyncRequestType.PeopleV1])).toHaveLength(0); + + expect(await ctx.syncStream(auth2, [SyncRequestType.PeopleV1])).toEqual([ + expect.objectContaining({ type: SyncEntityType.PersonDeleteV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.PeopleV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-reset.spec.ts b/server/test/medium/specs/sync/sync-reset.spec.ts index 4cfdc8249e..9a4c33c1f2 100644 --- a/server/test/medium/specs/sync/sync-reset.spec.ts +++ b/server/test/medium/specs/sync/sync-reset.spec.ts @@ -1,5 +1,6 @@ import { Kysely } from 'kysely'; import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { SessionRepository } from 'src/repositories/session.repository'; import { DB } from 'src/schema'; import { SyncTestContext } from 'test/medium.factory'; import { getKyselyDB } from 'test/utils'; @@ -20,17 +21,18 @@ describe(SyncEntityType.SyncResetV1, () => { it('should work', async () => { const { auth, ctx } = await setup(); - const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); - expect(response).toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.AssetsV1]); }); it('should detect a pending sync reset', async () => { const { auth, ctx } = await setup(); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); - expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {} }]); + expect(response).toEqual([{ type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }]); }); it('should not send other dtos when a reset is pending', async () => { @@ -38,12 +40,17 @@ describe(SyncEntityType.SyncResetV1, () => { await ctx.newAsset({ ownerId: user.id }); - await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toHaveLength(1); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1])).resolves.toEqual([ - { type: SyncEntityType.SyncResetV1, data: {} }, + { type: SyncEntityType.SyncResetV1, data: {}, ack: 'SyncResetV1|reset' }, ]); }); @@ -52,12 +59,36 @@ describe(SyncEntityType.SyncResetV1, () => { await ctx.newAsset({ ownerId: user.id }); - auth.session!.isPendingSyncReset = true; + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); await expect(ctx.syncStream(auth, [SyncRequestType.AssetsV1], true)).resolves.toEqual([ - expect.objectContaining({ - type: SyncEntityType.AssetV1, - }), + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); + }); + + it('should reset the sync progress', async () => { + const { auth, user, ctx } = await setup(); + + await ctx.newAsset({ ownerId: user.id }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + await ctx.syncAckAll(auth, response); + + await ctx.get(SessionRepository).update(auth.session!.id, { + isPendingSyncReset: true, + }); + + const resetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + + await ctx.syncAckAll(auth, resetResponse); + + const postResetResponse = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); + expect(postResetResponse).toEqual([ + expect.objectContaining({ type: SyncEntityType.AssetV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); }); }); diff --git a/server/test/medium/specs/sync/sync-stack.spec.ts b/server/test/medium/specs/sync/sync-stack.spec.ts index 1696172911..d3304ded28 100644 --- a/server/test/medium/specs/sync/sync-stack.spec.ts +++ b/server/test/medium/specs/sync/sync-stack.spec.ts @@ -25,7 +25,6 @@ describe(SyncEntityType.StackV1, () => { const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]); const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.stringContaining('StackV1'), @@ -38,10 +37,11 @@ describe(SyncEntityType.StackV1, () => { }, type: 'StackV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]); }); it('should detect and sync a deleted stack', async () => { @@ -53,17 +53,17 @@ describe(SyncEntityType.StackV1, () => { await stackRepo.delete(stack.id); const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.stringContaining('StackDeleteV1'), data: { stackId: stack.id }, type: 'StackDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]); }); it('should sync a stack and then an update to that same stack', async () => { @@ -74,22 +74,29 @@ describe(SyncEntityType.StackV1, () => { const { stack } = await ctx.newStack({ ownerId: user.id }, [asset1.id, asset2.id]); const response = await ctx.syncStream(auth, [SyncRequestType.StacksV1]); - expect(response).toHaveLength(1); + expect(response).toEqual([ + expect.objectContaining({ type: SyncEntityType.StackV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); await ctx.syncAckAll(auth, response); await stackRepo.update(stack.id, { primaryAssetId: asset2.id }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.StacksV1]); - expect(newResponse).toHaveLength(1); + expect(newResponse).toEqual([ + expect.objectContaining({ type: SyncEntityType.StackV1 }), + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), + ]); expect(newResponse).toEqual([ { ack: expect.stringContaining('StackV1'), data: expect.objectContaining({ id: stack.id, primaryAssetId: asset2.id }), type: 'StackV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, newResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]); }); it('should not sync a stack or stack delete for an unrelated user', async () => { @@ -100,8 +107,8 @@ describe(SyncEntityType.StackV1, () => { const { asset: asset2 } = await ctx.newAsset({ ownerId: user2.id }); const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset1.id, asset2.id]); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]); await stackRepo.delete(stack.id); - await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.StacksV1]); }); }); diff --git a/server/test/medium/specs/sync/sync-user-metadata.spec.ts b/server/test/medium/specs/sync/sync-user-metadata.spec.ts index 7cd53e76e3..1e75f80194 100644 --- a/server/test/medium/specs/sync/sync-user-metadata.spec.ts +++ b/server/test/medium/specs/sync/sync-user-metadata.spec.ts @@ -25,7 +25,6 @@ describe(SyncEntityType.UserMetadataV1, () => { await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } }); const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -36,10 +35,11 @@ describe(SyncEntityType.UserMetadataV1, () => { }, type: 'UserMetadataV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.UserMetadataV1]); }); it('should update user metadata', async () => { @@ -49,7 +49,6 @@ describe(SyncEntityType.UserMetadataV1, () => { await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } }); const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -60,6 +59,7 @@ describe(SyncEntityType.UserMetadataV1, () => { }, type: 'UserMetadataV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -77,10 +77,11 @@ describe(SyncEntityType.UserMetadataV1, () => { }, type: 'UserMetadataV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, updatedResponse); - await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.UserMetadataV1]); }); }); @@ -92,7 +93,6 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => { await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.Onboarding, value: { isOnboarded: true } }); const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), @@ -103,6 +103,7 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => { }, type: 'UserMetadataV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -118,6 +119,7 @@ describe(SyncEntityType.UserMetadataDeleteV1, () => { }, type: 'UserMetadataDeleteV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); }); }); diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 24137e3aea..7a69e7a411 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -28,64 +28,56 @@ describe(SyncEntityType.UserV1, () => { } const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), data: { deletedAt: user.deletedAt, email: user.email, + hasProfileImage: user.profileImagePath !== '', id: user.id, name: user.name, + avatarColor: user.avatarColor, + profileChangedAt: user.profileChangedAt.toISOString(), }, type: 'UserV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]); }); it('should detect and sync a soft deleted user', async () => { const { auth, ctx } = await setup(await getKyselyDB()); - const deletedAt = new Date().toISOString(); - const { user: deleted } = await ctx.newUser({ deletedAt }); + const { user: deleted } = await ctx.newUser({ deletedAt: new Date().toISOString() }); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); - expect(response).toHaveLength(2); expect(response).toEqual( expect.arrayContaining([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: auth.user.id }), type: 'UserV1', }, { ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, + data: expect.objectContaining({ id: deleted.id }), type: 'UserV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]), ); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]); }); it('should detect and sync a deleted user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user: authUser, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -93,7 +85,6 @@ describe(SyncEntityType.UserV1, () => { await userRepo.delete({ id: user.id }, true); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); - expect(response).toHaveLength(2); expect(response).toEqual([ { ack: expect.any(String), @@ -104,38 +95,29 @@ describe(SyncEntityType.UserV1, () => { }, { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: authUser.id }), type: 'UserV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); - await expect(ctx.syncStream(auth, [SyncRequestType.UsersV1])).resolves.toEqual([]); + await ctx.assertSyncIsComplete(auth, [SyncRequestType.UsersV1]); }); it('should sync a user and then an update to that same user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); - expect(response).toHaveLength(1); expect(response).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: user.id }), type: 'UserV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); await ctx.syncAckAll(auth, response); @@ -143,18 +125,13 @@ describe(SyncEntityType.UserV1, () => { const updated = await userRepo.update(auth.user.id, { name: 'new name' }); const newResponse = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); - expect(newResponse).toHaveLength(1); expect(newResponse).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, + data: expect.objectContaining({ id: user.id, name: updated.name }), type: 'UserV1', }, + expect.objectContaining({ type: SyncEntityType.SyncCompleteV1 }), ]); }); }); diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 6fca29d98e..e735b37564 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -8,6 +8,7 @@ export const newAssetRepositoryMock = (): Mocked Promise.resolve(); }; -export const newStorageRepositoryMock = (reset = true): Mocked> => { - if (reset) { - StorageCore.reset(); - } +export const newStorageRepositoryMock = (): Mocked> => { + StorageCore.reset(); + StorageCore.setMediaLocation('/data'); return { createZipStream: vitest.fn(), @@ -53,6 +52,7 @@ export const newStorageRepositoryMock = (reset = true): Mocked randomUUID() as string; +export const newUuid = () => v4(); export const newUuids = () => Array.from({ length: 100 }) .fill(0) .map(() => newUuid()); export const newDate = () => new Date(); -export const newUuidV7 = () => 'uuid-v7'; +export const newUuidV7 = () => v7(); export const newSha1 = () => Buffer.from('this is a fake hash'); export const newEmbedding = () => { const embedding = Array.from({ length: 512 }) @@ -60,7 +60,6 @@ const authFactory = ({ if (session) { auth.session = { id: session.id, - isPendingSyncReset: false, hasElevatedPermission: false, }; } @@ -224,7 +223,7 @@ const assetFactory = (asset: Partial = {}) => ({ livePhotoVideoId: null, localDateTime: newDate(), originalFileName: 'IMG_123.jpg', - originalPath: `upload/12/34/IMG_123.jpg`, + originalPath: `/data/12/34/IMG_123.jpg`, ownerId: newUuid(), sidecarPath: null, stackId: null, diff --git a/server/test/utils.ts b/server/test/utils.ts index af6f2826f9..c23341d64c 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -1,12 +1,16 @@ -import { CallHandler, Provider, ValidationPipe } from '@nestjs/common'; +import { CallHandler, ExecutionContext, Provider, ValidationPipe } from '@nestjs/common'; import { APP_GUARD, APP_PIPE } from '@nestjs/core'; +import { transformException } from '@nestjs/platform-express/multer/multer/multer.utils'; import { Test } from '@nestjs/testing'; import { ClassConstructor } from 'class-transformer'; +import { NextFunction } from 'express'; import { Kysely } from 'kysely'; +import multer from 'multer'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; -import { Writable } from 'node:stream'; +import { Readable, Writable } from 'node:stream'; import { PNG } from 'pngjs'; import postgres from 'postgres'; +import { UploadFieldName } from 'src/dtos/asset-media.dto'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { AuthGuard } from 'src/middleware/auth.guard'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; @@ -71,7 +75,6 @@ import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { ITelemetryRepositoryMock, newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; -import { Readable } from 'typeorm/platform/PlatformTools'; import { assert, Mock, Mocked, vitest } from 'vitest'; export type ControllerContext = { @@ -83,6 +86,24 @@ export type ControllerContext = { export const controllerSetup = async (controller: ClassConstructor, providers: Provider[]) => { const noopInterceptor = { intercept: (ctx: never, next: CallHandler) => next.handle() }; + const upload = multer({ storage: multer.memoryStorage() }); + const memoryFileInterceptor = { + intercept: async (ctx: ExecutionContext, next: CallHandler) => { + const context = ctx.switchToHttp(); + const handler = upload.fields([ + { name: UploadFieldName.ASSET_DATA, maxCount: 1 }, + { name: UploadFieldName.SIDECAR_DATA, maxCount: 1 }, + ]); + + await new Promise((resolve, reject) => { + const next: NextFunction = (error) => (error ? reject(transformException(error)) : resolve()); + const maybePromise = handler(context.getRequest(), context.getResponse(), next); + Promise.resolve(maybePromise).catch((error) => reject(error)); + }); + + return next.handle(); + }, + }; const moduleRef = await Test.createTestingModule({ controllers: [controller], providers: [ @@ -94,7 +115,7 @@ export const controllerSetup = async (controller: ClassConstructor, pro ], }) .overrideInterceptor(FileUploadInterceptor) - .useValue(noopInterceptor) + .useValue(memoryFileInterceptor) .overrideInterceptor(AssetUploadInterceptor) .useValue(noopInterceptor) .compile(); @@ -447,6 +468,16 @@ export async function* makeStream(items: T[] = []): AsyncIterableIterator } } -export const wait = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); +export const wait = (ms: number): Promise => { + return new Promise((resolve) => { + const target = performance.now() + ms; + const checkDone = () => { + if (performance.now() >= target) { + resolve(); + } else { + setTimeout(checkDone, 1); // Check again after 1ms + } + }; + setTimeout(checkDone, ms); + }); }; diff --git a/web/.nvmrc b/web/.nvmrc index fc37597bcc..442c7587a9 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.17.0 +22.20.0 diff --git a/web/Dockerfile b/web/Dockerfile deleted file mode 100644 index 3c119fdd4d..0000000000 --- a/web/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e - -RUN apk add --no-cache tini bash - -USER node -WORKDIR /usr/src/app - -COPY --chown=node:node ./web/package* ./web/ - -WORKDIR /usr/src/app/web -RUN npm ci - -ENV CHOKIDAR_USEPOLLING=true \ - PATH="${PATH}:/usr/src/app/web/bin" -EXPOSE 24678 -EXPOSE 3000 -ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] diff --git a/web/bin/immich-web b/web/bin/immich-web index d2739cf6c3..23377eddd6 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -1,14 +1,12 @@ #!/usr/bin/env sh -TYPESCRIPT_SDK=/usr/src/app/open-api/typescript-sdk +cd /usr/src/app || exit -npm --prefix "$TYPESCRIPT_SDK" install -npm --prefix "$TYPESCRIPT_SDK" run build - -cd /usr/src/app/web || exit 1 +pnpm --filter @immich/sdk build COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" +UPSTREAM="${UPSTREAM%/}" 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..." @@ -16,7 +14,5 @@ until wget --spider --quiet "${UPSTREAM}/api/server/config" > /dev/null 2>&1; do COUNT=$((COUNT + 1)) sleep 1 done - -echo "Connected to $UPSTREAM" - -npx vite dev --host 0.0.0.0 --port 3000 +echo "Connected to $UPSTREAM, starting Immich Web..." +pnpm --filter immich-web exec vite dev --host 0.0.0.0 --port 3000 diff --git a/web/eslint.config.js b/web/eslint.config.js index 6b7b343ad1..792ff90e0c 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -1,5 +1,6 @@ import js from '@eslint/js'; import tslintPluginCompat from '@koddsson/eslint-plugin-tscompat'; +import prettier from 'eslint-config-prettier'; import eslintPluginCompat from 'eslint-plugin-compat'; import eslintPluginSvelte from 'eslint-plugin-svelte'; import eslintPluginUnicorn from 'eslint-plugin-unicorn'; @@ -17,6 +18,7 @@ export default typescriptEslint.config( ...eslintPluginSvelte.configs.recommended, eslintPluginUnicorn.configs.recommended, js.configs.recommended, + prettier, { plugins: { tscompat: tslintPluginCompat, @@ -109,6 +111,7 @@ export default typescriptEslint.config( ], curly: 2, + 'unicorn/no-array-reverse': 'off', // toReversed() is not supported in Chrome 109 or Safari 15.4 'unicorn/no-useless-undefined': 'off', 'unicorn/prefer-spread': 'off', 'unicorn/no-null': 'off', @@ -118,12 +121,14 @@ export default typescriptEslint.config( 'unicorn/filename-case': 'off', 'unicorn/prefer-top-level-await': 'off', 'unicorn/import-style': 'off', + 'unicorn/no-array-sort': 'off', 'svelte/button-has-type': 'error', '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/require-await': 'error', 'object-shorthand': ['error', 'always'], + 'svelte/no-navigation-without-resolve': 'off', }, }, { diff --git a/web/package-lock.json b/web/package-lock.json deleted file mode 100644 index 2bd5409903..0000000000 --- a/web/package-lock.json +++ /dev/null @@ -1,10886 +0,0 @@ -{ - "name": "immich-web", - "version": "1.135.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "immich-web", - "version": "1.135.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.23.2", - "@mapbox/mapbox-gl-rtl-text": "0.2.3", - "@mdi/js": "^7.4.47", - "@photo-sphere-viewer/core": "^5.11.5", - "@photo-sphere-viewer/equirectangular-video-adapter": "^5.11.5", - "@photo-sphere-viewer/resolution-plugin": "^5.11.5", - "@photo-sphere-viewer/settings-plugin": "^5.11.5", - "@photo-sphere-viewer/video-plugin": "^5.11.5", - "@types/geojson": "^7946.0.16", - "@zoom-image/core": "^0.41.0", - "@zoom-image/svelte": "^0.3.0", - "async-mutex": "^0.5.0", - "dom-to-image": "^2.6.0", - "fabric": "^6.5.4", - "geo-coordinates-parser": "^1.7.4", - "geojson": "^0.5.0", - "handlebars": "^4.7.8", - "happy-dom": "^18.0.1", - "intl-messageformat": "^10.7.11", - "justified-layout": "^4.1.0", - "lodash-es": "^4.17.21", - "luxon": "^3.4.4", - "maplibre-gl": "^5.3.0", - "pmtiles": "^4.3.0", - "qrcode": "^1.5.4", - "socket.io-client": "~4.8.0", - "svelte-gestures": "^5.1.3", - "svelte-i18n": "^4.0.1", - "svelte-maplibre": "^1.0.0", - "svelte-persisted-store": "^0.12.0", - "tabbable": "^6.2.0", - "thumbhash": "^0.1.1" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.18.0", - "@faker-js/faker": "^9.3.0", - "@koddsson/eslint-plugin-tscompat": "^0.2.0", - "@socket.io/component-emitter": "^3.1.0", - "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", - "@tailwindcss/vite": "^4.1.7", - "@testing-library/jest-dom": "^6.4.2", - "@testing-library/svelte": "^5.2.8", - "@testing-library/user-event": "^14.5.2", - "@types/chromecast-caf-sender": "^1.0.11", - "@types/dom-to-image": "^2.6.7", - "@types/justified-layout": "^4.1.4", - "@types/lodash-es": "^4.17.12", - "@types/luxon": "^3.4.2", - "@types/qrcode": "^1.5.5", - "@vitest/coverage-v8": "^3.0.0", - "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", - "eslint-p": "^0.25.0", - "eslint-plugin-compat": "^6.0.2", - "eslint-plugin-svelte": "^3.9.0", - "eslint-plugin-unicorn": "^59.0.0", - "factory.ts": "^1.4.1", - "globals": "^16.0.0", - "happy-dom": "^18.0.1", - "prettier": "^3.4.2", - "prettier-plugin-organize-imports": "^4.0.0", - "prettier-plugin-sort-json": "^4.1.1", - "prettier-plugin-svelte": "^3.3.3", - "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", - "svelte-check": "^4.1.5", - "svelte-eslint-parser": "^1.2.0", - "tailwindcss": "^4.1.7", - "tslib": "^2.6.2", - "typescript": "^5.7.3", - "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", - "vitest": "^3.0.0" - } - }, - "../open-api/typescript-sdk": { - "name": "@immich/sdk", - "version": "1.135.3", - "license": "GNU Affero General Public License version 3", - "dependencies": { - "@oazapfts/runtime": "^1.0.2" - }, - "devDependencies": { - "@types/node": "^22.15.33", - "typescript": "^5.3.3" - } - }, - "node_modules/@adobe/css-tools": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", - "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/runtime": { - "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==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "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/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", - "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.14.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@faker-js/faker": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.9.0.tgz", - "integrity": "sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fakerjs" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0.0", - "npm": ">=9.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", - "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", - "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.2", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", - "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/intl-localematcher": "0.6.1", - "decimal.js": "^10.4.3", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", - "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", - "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "@formatjs/icu-skeleton-parser": "1.8.14", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", - "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", - "license": "MIT", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "tslib": "^2.8.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", - "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "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" - ], - "dev": true, - "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.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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "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" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" - } - }, - "node_modules/@img/sharp-linux-x64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "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" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "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" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "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" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@immich/sdk": { - "resolved": "../open-api/typescript-sdk", - "link": true - }, - "node_modules/@immich/ui": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.3.tgz", - "integrity": "sha512-YbYJSv3HqDu2+6MmiHhLThSessZ6HkoVOWun/ZoGb8mKj5x/ZZ4AyXGPIqbyKTamsjzbcD9FInij70G+m4egkg==", - "license": "GNU Affero General Public License version 3", - "dependencies": { - "@mdi/js": "^7.4.47", - "bits-ui": "^2.0.0", - "tailwind-merge": "^3.0.0", - "tailwind-variants": "^1.0.0" - }, - "peerDependencies": { - "svelte": "^5.0.0" - } - }, - "node_modules/@internationalized/date": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", - "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "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/@isaacs/fs-minipass/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@koddsson/eslint-plugin-tscompat": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@koddsson/eslint-plugin-tscompat/-/eslint-plugin-tscompat-0.2.0.tgz", - "integrity": "sha512-Oqd4kWSX0LiO9wWHjcmDfXZNC7TotFV/tLRhwCFU3XUeb//KYvJ75c9OmeSJ+vBv5lkCeB+xYsqyNrBc5j18XA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@mdn/browser-compat-data": "^6.0.17", - "@typescript-eslint/type-utils": "^8.0.1", - "@typescript-eslint/utils": "^8.0.0", - "browserslist": "^4.23.0" - } - }, - "node_modules/@koddsson/eslint-plugin-tscompat/node_modules/@mdn/browser-compat-data": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-6.0.22.tgz", - "integrity": "sha512-zhgOBTouJOd8IbE5dEEcfzg83l+nxKL/7Ru2HPeCVbog9I0JGHg3QZab9IxZquKFTUsc+c7QqU4EVENeZzZWRg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", - "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/geojson-types": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", - "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==", - "license": "ISC", - "peer": true - }, - "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", - "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@mapbox/mapbox-gl-rtl-text": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-rtl-text/-/mapbox-gl-rtl-text-0.2.3.tgz", - "integrity": "sha512-RaCYfnxULUUUxNwcUimV9C/o2295ktTyLEUzD/+VWkqXqvaVfFcZ5slytGzb2Sd/Jj4MlbxD0DCZbfa6CzcmMw==", - "license": "BSD-2-Clause", - "peerDependencies": { - "mapbox-gl": ">=0.32.1 <2.0.0" - } - }, - "node_modules/@mapbox/mapbox-gl-supported": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz", - "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg==", - "license": "BSD-3-Clause", - "peer": true, - "peerDependencies": { - "mapbox-gl": ">=0.32.1 <2.0.0" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", - "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", - "license": "ISC" - }, - "node_modules/@mapbox/tiny-sdf": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz", - "integrity": "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==", - "license": "BSD-2-Clause", - "peer": true - }, - "node_modules/@mapbox/unitbezier": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", - "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==", - "license": "BSD-2-Clause", - "peer": true - }, - "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" - } - }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.3.0.tgz", - "integrity": "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==", - "license": "ISC", - "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", - "json-stringify-pretty-compact": "^4.0.0", - "minimist": "^1.2.8", - "quickselect": "^3.0.0", - "rw": "^1.3.3", - "tinyqueue": "^3.0.0" - }, - "bin": { - "gl-style-format": "dist/gl-style-format.mjs", - "gl-style-migrate": "dist/gl-style-migrate.mjs", - "gl-style-validate": "dist/gl-style-validate.mjs" - } - }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", - "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", - "license": "BSD-2-Clause" - }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", - "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", - "license": "ISC" - }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/tinyqueue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" - }, - "node_modules/@mdi/js": { - "version": "7.4.47", - "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz", - "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==", - "license": "Apache-2.0" - }, - "node_modules/@mdn/browser-compat-data": { - "version": "5.7.6", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.7.6.tgz", - "integrity": "sha512-7xdrMX0Wk7grrTZQwAoy1GkvPMFoizStUoL+VmtUkAxegbCCec+3FKwOM6yc/uGU5+BEczQHXAlWiqvM8JeENg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/@namnode/store": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@namnode/store/-/store-0.1.0.tgz", - "integrity": "sha512-4NGTldxKcmY0UuZ7OEkvCjs8ZEoeYB6M2UwMu74pdLiFMKxXbj9HdNk1Qn213bxX1O7bY5h+PLh5DZsTURZkYA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/willnguyen1312" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.3.tgz", - "integrity": "sha512-6Fjkx2fTUFSmoy6jJxXn+FrwNlMtTi6M8ErEZkQgn7ULenMpIUx6tdFz3l85NM7Br/Rj4CK8IyUo8Le1eI1Fdg==", - "license": "MIT", - "dependencies": { - "three": "^0.175.0" - } - }, - "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.3.tgz", - "integrity": "sha512-Gl63vt9ztRHrw2lxDF4KTPsndDbIByg+hUsaMtEAuc304wLUKj6GIapAh71eCyDqvPBhLoTZ5+4RX/TPafnCGg==", - "license": "MIT", - "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/video-plugin": "5.13.3" - } - }, - "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.3.tgz", - "integrity": "sha512-spSBm7OXDisnw5Fx4+JPR510nb+nGFZf2aecPRhP23GZyxKPg82i1PYoDJgRCWKTlvz8Yq2eilKdAtncmHWgSA==", - "license": "MIT", - "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/settings-plugin": "5.13.3" - } - }, - "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.3.tgz", - "integrity": "sha512-x/KulP3UxoawDS8MuGrZU3DznrzpaYqQ2BQnxlaOVItewrgCdS8WRG18E7nBo+2lfZGDDwGhRsSp/IRUOwScRg==", - "license": "MIT", - "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" - } - }, - "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.3.tgz", - "integrity": "sha512-npNeknhV3jLJLKbbFzaLRVRROiN2POSdHLnV0R6QNiF7rQURC0Cs7Yuztl2nFJWUBOS3MiO13t5Wyz1wwfKfSQ==", - "license": "MIT", - "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "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", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "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==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "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/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", - "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8.9.0" - } - }, - "node_modules/@sveltejs/adapter-static": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz", - "integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@sveltejs/kit": "^2.0.0" - } - }, - "node_modules/@sveltejs/enhanced-img": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.6.1.tgz", - "integrity": "sha512-8oj/cXc/M1soGQOkkkuEzeaiE/LTa3MJnoRwoRzG7GOPKHOfNRJDzsCcx3s1GqxQlcoHc4BJK3HoU1m0OV9UMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.5", - "sharp": "^0.34.1", - "svelte-parse-markup": "^0.1.5", - "vite-imagetools": "^7.1.0", - "zimmerframe": "^1.1.2" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^5.0.0 || ^6.0.0-next.0", - "svelte": "^5.0.0", - "vite": ">= 5.0.0" - } - }, - "node_modules/@sveltejs/kit": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.4.tgz", - "integrity": "sha512-BXK9hTbP8AeQIfoz6+P3uoyVYStVHc5CIKqoTSF7hXm3Q5P9BwFMdEus4jsQuhaYmXGHzukcGlxe2QrsE8BJfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/cookie": "^0.6.0", - "acorn": "^8.14.1", - "cookie": "^0.6.0", - "devalue": "^5.1.0", - "esm-env": "^1.2.2", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "sade": "^1.8.1", - "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.0.0.tgz", - "integrity": "sha512-mma5GJ23pYiWpTNbN//g9XI3Hfob3aAlXPP42qRtvjgTAU6pfJyLyNPTdLjFuj+jfC9JslP4J3AkeiJNhjtLLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0-next.1", - "debug": "^4.4.1", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.17", - "vitefu": "^1.1.1" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "svelte": "^5.0.0", - "vite": "^6.3.0 || ^7.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.0.tgz", - "integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.4.1" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", - "svelte": "^5.0.0", - "vite": "^6.3.0 || ^7.0.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide/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/@tailwindcss/oxide/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@tailwindcss/oxide/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/@tailwindcss/oxide/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/@tailwindcss/oxide/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/@tailwindcss/oxide/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/@tailwindcss/vite": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", - "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "tailwindcss": "4.1.11" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" - } - }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@testing-library/svelte": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.2.8.tgz", - "integrity": "sha512-ucQOtGsJhtawOEtUmbR4rRh53e6RbM1KUluJIXRmh6D4UzxR847iIqqjRtg9mHNFmGQ8Vkam9yVcR5d1mhIHKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@testing-library/dom": "9.x.x || 10.x.x" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", - "vite": "*", - "vitest": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, - "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", - "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*" - } - }, - "node_modules/@types/chrome": { - "version": "0.0.322", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.322.tgz", - "integrity": "sha512-glbRm82TzLLJfi3ttlnn7HR9KIX5OYeTo9Xug0Hna03JvaqNipZT+P/q/O5kxOvUQqKUqmn8NAOrcRSG6BOQAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/filesystem": "*", - "@types/har-format": "*" - } - }, - "node_modules/@types/chromecast-caf-sender": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/chromecast-caf-sender/-/chromecast-caf-sender-1.0.11.tgz", - "integrity": "sha512-Pv3xvNYtxD/cTM/tKfuZRlLasvpxAm+CFni0GJd6Cp8XgiZS9g9tMZkR1uymsi5fIFv057SZKKAWVFFgy7fJtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chrome": "*" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/dom-to-image": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.7.tgz", - "integrity": "sha512-me5VbCv+fcXozblWwG13krNBvuEOm6kA5xoa4RrjDJCNFOZSWR3/QLtOXimBHk1Fisq69Gx3JtOoXtg1N1tijg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" - }, - "node_modules/@types/filesystem": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", - "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/filewriter": "*" - } - }, - "node_modules/@types/filewriter": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", - "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, - "node_modules/@types/geojson-vt": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", - "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/har-format": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", - "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/justified-layout": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/justified-layout/-/justified-layout-4.1.4.tgz", - "integrity": "sha512-q2ybP0u0NVj87oMnGZOGxY2iUN8ddr48zPOBHBdbOLpsMTA/keGj+93ou+OMCnJk0xewzlNIaVEkxM6VBD3E2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/leaflet": { - "version": "1.9.17", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.17.tgz", - "integrity": "sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/lodash": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", - "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/lodash-es": { - "version": "4.17.12", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", - "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/luxon": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", - "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", - "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", - "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, - "node_modules/@types/node": { - "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": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", - "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", - "license": "MIT" - }, - "node_modules/@types/qrcode": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", - "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", - "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/whatwg-mimetype": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", - "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/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/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@zoom-image/core": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.41.0.tgz", - "integrity": "sha512-LAxGru91286gFmyiQB4RjM277YOWxJX+OZcwtIH/N0dyo73y4NfaAE1eGVdnhjxEYv7yVV3xToMyYnm+uQboTw==", - "license": "MIT", - "dependencies": { - "@namnode/store": "^0.1.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/willnguyen1312" - } - }, - "node_modules/@zoom-image/svelte": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.3.4.tgz", - "integrity": "sha512-8cPkFUjh+t3/eYkoT2krvz8hoFiXoiYZKpcHOnYCHLhEwaHr1yjgXg/ttWehotVH9V3Z51JQgIcGF3uhYWKB/Q==", - "license": "MIT", - "dependencies": { - "@zoom-image/core": "0.41.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/willnguyen1312" - }, - "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC", - "optional": true - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "optional": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "license": "ISC", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/ast-metadata-inferer": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.8.1.tgz", - "integrity": "sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mdn/browser-compat-data": "^5.6.19" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" - } - }, - "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT", - "optional": true - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/bits-ui": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.8.10.tgz", - "integrity": "sha512-MOobkqapDZNrpcNmeL2g664xFmH4tZBOKBTxFmsQYMZQuybSZHQnPXy+AjM5XZEXRmCFx5+XRmo6+fC3vHh1hQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/dom": "^1.7.1", - "esm-env": "^1.1.2", - "runed": "^0.29.1", - "svelte-toolbelt": "^0.9.3", - "tabbable": "^6.2.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/huntabyte" - }, - "peerDependencies": { - "@internationalized/date": "^3.8.1", - "svelte": "^5.33.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/builtin-modules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/cli-color": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", - "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "optional": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC", - "optional": true - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-js-compat": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", - "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/csscolorparser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", - "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", - "license": "MIT", - "peer": true - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "license": "MIT", - "optional": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "license": "MIT", - "optional": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "license": "MIT", - "optional": true - }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "license": "MIT", - "optional": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT", - "optional": true - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "license": "MIT" - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/dom-to-image": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz", - "integrity": "sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==", - "license": "MIT" - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "license": "MIT", - "optional": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", - "license": "ISC", - "peer": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "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/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "optional": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "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" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "optional": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "optional": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-p": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/eslint-p/-/eslint-p-0.25.0.tgz", - "integrity": "sha512-e7oYgXN/tgtoaR3tZ0R2dKyPJtf5J41hYKsgpsBtwpi0t2Cxjf3l8G2QwrXCDwQTFVXW1hmD55hAqQZxiId1XA==", - "dev": true, - "license": "ISC", - "dependencies": { - "eslint": "9.30.1" - }, - "bin": { - "eslint-p": "lib/eslint-p.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-compat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.2.tgz", - "integrity": "sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mdn/browser-compat-data": "^5.5.35", - "ast-metadata-inferer": "^0.8.1", - "browserslist": "^4.24.2", - "caniuse-lite": "^1.0.30001687", - "find-up": "^5.0.0", - "globals": "^15.7.0", - "lodash.memoize": "^4.1.2", - "semver": "^7.6.2" - }, - "engines": { - "node": ">=18.x" - }, - "peerDependencies": { - "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-compat/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-svelte": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.10.1.tgz", - "integrity": "sha512-csCh2x0ge/DugXC7dCANh46Igi7bjMZEy6rHZCdS13AoGVJSu7a90Kru3I8oMYLGEemPRE1hQXadxvRPVMAAXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.6.1", - "@jridgewell/sourcemap-codec": "^1.5.0", - "esutils": "^2.0.3", - "globals": "^16.0.0", - "known-css-properties": "^0.37.0", - "postcss": "^8.4.49", - "postcss-load-config": "^3.1.4", - "postcss-safe-parser": "^7.0.0", - "semver": "^7.6.3", - "svelte-eslint-parser": "^1.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": "^8.57.1 || ^9.0.0", - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", - "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", - "esquery": "^1.6.0", - "find-up-simple": "^1.0.1", - "globals": "^16.0.0", - "indent-string": "^5.0.0", - "is-builtin-module": "^5.0.0", - "jsesc": "^3.1.0", - "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.12.0", - "semver": "^7.7.1", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/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/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint/node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/esm-env": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "license": "MIT" - }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrap": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz", - "integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/fabric": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.0.tgz", - "integrity": "sha512-+yKumsh1MvJ44Um2eOhb4Q6CyZ6e2XKBV3IfQvzuGKhl2UkRFQtIKPUi6f06m3gd0r5zspgMUl5iwxtT1dmFAQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - }, - "optionalDependencies": { - "canvas": "^2.11.2", - "jsdom": "^20.0.1" - } - }, - "node_modules/factory.ts": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/factory.ts/-/factory.ts-1.4.2.tgz", - "integrity": "sha512-8x2hqK1+EGkja4Ah8H3nkP7rDUJsBK1N3iFDqzqsaOV114o2IphSdVkFIw9nDHHr37gFFy2NXeN6n10ieqHzZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "source-map-support": "^0.5.21" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "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/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "optional": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC", - "optional": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "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", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "optional": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/geo-coordinates-parser": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/geo-coordinates-parser/-/geo-coordinates-parser-1.7.4.tgz", - "integrity": "sha512-gVGxBW+s1csexXVMf5bIwz3TH9n4sCEglOOOqmrPk8YazUI5f79jCowKjTw05m/0h1//3+Z2m/nv8IIozgZyUw==", - "license": "MIT" - }, - "node_modules/geojson": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", - "integrity": "sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/geojson-vt": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", - "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", - "license": "ISC", - "peer": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "optional": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-prefix": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", - "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", - "license": "MIT", - "dependencies": { - "ini": "^4.1.3", - "kind-of": "^6.0.3", - "which": "^4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/global-prefix/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==", - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "license": "MIT" - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/grid-index": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", - "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", - "license": "ISC", - "peer": true - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/happy-dom": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.1.tgz", - "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^20.0.0", - "@types/whatwg-mimetype": "^3.0.2", - "whatwg-mimetype": "^3.0.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/happy-dom/node_modules/@types/node": { - "version": "20.19.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.2.tgz", - "integrity": "sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "optional": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC", - "optional": true - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "license": "MIT", - "optional": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/imagetools-core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-7.1.0.tgz", - "integrity": "sha512-8Aa4NecBBGmTkaAUjcuRYgTPKHCsBEWYmCnvKCL6/bxedehtVVFyZPdXe8DD0Nevd6UWBq85ifUaJ8498lgqNQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "optional": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC", - "optional": true - }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.16", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", - "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.4", - "@formatjs/fast-memoize": "2.2.7", - "@formatjs/icu-messageformat-parser": "2.11.2", - "tslib": "^2.8.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-builtin-module": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", - "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "builtin-modules": "^5.0.0" - }, - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "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==", - "license": "MIT", - "optional": true - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "license": "MIT" - }, - "node_modules/is-reference": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", - "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-pretty-compact": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", - "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", - "license": "MIT" - }, - "node_modules/just-compare": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/just-compare/-/just-compare-2.3.0.tgz", - "integrity": "sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg==", - "license": "MIT" - }, - "node_modules/justified-layout": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/justified-layout/-/justified-layout-4.1.0.tgz", - "integrity": "sha512-M5FimNMXgiOYerVRGsXZ2YK9YNCaTtwtYp7Hb2308U1Q9TXXHx5G0p08mcVR5O53qf8bWY4NJcPBxE6zuayXSg==", - "license": "ISC" - }, - "node_modules/kdbush": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", - "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", - "license": "ISC" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/known-css-properties": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", - "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", - "dev": true, - "license": "MIT" - }, - "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/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "optional": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", - "license": "SEE LICENSE IN LICENSE.txt", - "peer": true, - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/geojson-types": "^1.0.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^1.5.0", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^1.1.1", - "@mapbox/unitbezier": "^0.0.0", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.2", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.2.1", - "grid-index": "^1.1.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.1", - "quickselect": "^2.0.0", - "rw": "^1.3.3", - "supercluster": "^7.1.0", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.1" - }, - "engines": { - "node": ">=6.4.0" - } - }, - "node_modules/maplibre-gl": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.6.1.tgz", - "integrity": "sha512-TTSfoTaF7RqKUR9wR5qDxCHH2J1XfZ1E85luiLOx0h8r50T/LnwAwwfV0WVNh9o8dA7rwt57Ucivf1emyeukXg==", - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.6", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^23.3.0", - "@types/geojson": "^7946.0.16", - "@types/geojson-vt": "3.2.5", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/pbf": "^3.0.5", - "@types/supercluster": "^7.1.3", - "earcut": "^3.0.1", - "geojson-vt": "^4.0.2", - "gl-matrix": "^3.4.3", - "global-prefix": "^4.0.0", - "kdbush": "^4.0.2", - "murmurhash-js": "^1.0.0", - "pbf": "^3.3.0", - "potpack": "^2.0.0", - "quickselect": "^3.0.0", - "supercluster": "^8.0.1", - "tinyqueue": "^3.0.0", - "vt-pbf": "^3.1.3" - }, - "engines": { - "node": ">=16.14.0", - "npm": ">=8.1.0" - }, - "funding": { - "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" - } - }, - "node_modules/maplibre-gl/node_modules/@mapbox/tiny-sdf": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", - "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==", - "license": "BSD-2-Clause" - }, - "node_modules/maplibre-gl/node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", - "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", - "license": "BSD-2-Clause" - }, - "node_modules/maplibre-gl/node_modules/earcut": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", - "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==", - "license": "ISC" - }, - "node_modules/maplibre-gl/node_modules/geojson-vt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", - "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", - "license": "ISC" - }, - "node_modules/maplibre-gl/node_modules/potpack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", - "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==", - "license": "ISC" - }, - "node_modules/maplibre-gl/node_modules/quickselect": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", - "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", - "license": "ISC" - }, - "node_modules/maplibre-gl/node_modules/supercluster": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", - "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, - "node_modules/maplibre-gl/node_modules/tinyqueue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/memoizee": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", - "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "es5-ext": "^0.10.64", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "optional": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/murmurhash-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", - "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", - "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "license": "ISC" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "optional": true - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "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==", - "license": "MIT", - "optional": true - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "optional": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", - "license": "BSD-3-Clause", - "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "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/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pmtiles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-4.3.0.tgz", - "integrity": "sha512-wnzQeSiYT/MyO63o7AVxwt7+uKqU0QUy2lHrivM7GvecNy0m1A4voVyGey7bujnEW5Hn+ZzLdvHPoFaqrOzbPA==", - "license": "BSD-3-Clause", - "dependencies": { - "fflate": "^0.8.2" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", - "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", - "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/potpack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", - "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", - "license": "ISC", - "peer": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": ">=2.0", - "typescript": ">=2.9", - "vue-tsc": "^2.1.0" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/prettier-plugin-sort-json": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-sort-json/-/prettier-plugin-sort-json-4.1.1.tgz", - "integrity": "sha512-uJ49wCzwJ/foKKV4tIPxqi4jFFvwUzw4oACMRG2dcmDhBKrxBv0L2wSKkAqHCmxKCvj0xcCZS4jO2kSJO/tRJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "prettier": "^3.0.0" - } - }, - "node_modules/prettier-plugin-svelte": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz", - "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", - "license": "MIT" - }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "license": "MIT", - "optional": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", - "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "license": "MIT", - "optional": true - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", - "license": "ISC", - "peer": true - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/redent/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/redent/node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, - "license": "MIT" - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT", - "optional": true - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", - "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "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", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-visualizer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.3.tgz", - "integrity": "sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "open": "^8.0.0", - "picomatch": "^4.0.2", - "source-map": "^0.7.4", - "yargs": "^17.5.1" - }, - "bin": { - "rollup-plugin-visualizer": "dist/bin/cli.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "rolldown": "1.x || ^1.0.0-beta", - "rollup": "2.x || 3.x || 4.x" - }, - "peerDependenciesMeta": { - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/runed": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/runed/-/runed-0.29.1.tgz", - "integrity": "sha512-RGQEB8ZiWv4OvzBJhbMj2hMgRM8QrEptzTrDr7TDfkHaRePKjiUka4vJ9QHGY+8s87KymNvFoZAxFdQ4jtZNcA==", - "funding": [ - "https://github.com/sponsors/huntabyte", - "https://github.com/sponsors/tglide" - ], - "license": "MIT", - "dependencies": { - "esm-env": "^1.0.0" - }, - "peerDependencies": { - "svelte": "^5.7.0" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "optional": true - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "license": "ISC", - "optional": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sharp": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.7.1" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@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": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC", - "optional": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/sirv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true, - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", - "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/supercluster": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", - "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", - "license": "ISC", - "peer": true, - "dependencies": { - "kdbush": "^3.0.0" - } - }, - "node_modules/supercluster/node_modules/kdbush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", - "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", - "license": "ISC", - "peer": true - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/svelte": { - "version": "5.35.5", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.35.5.tgz", - "integrity": "sha512-KuRvI82rhh0RMz1EKsUJD96gZyHJ+h2+8zrwO8iqE/p/CmcNKvIItDUAeUePhuCDgtegDJmF8IKThbHIfmTgTA==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.5", - "@types/estree": "^1.0.5", - "acorn": "^8.12.1", - "aria-query": "^5.3.1", - "axobject-query": "^4.1.0", - "clsx": "^2.1.1", - "esm-env": "^1.2.1", - "esrap": "^2.1.0", - "is-reference": "^3.0.3", - "locate-character": "^3.0.0", - "magic-string": "^0.30.11", - "zimmerframe": "^1.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/svelte-check": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.2.2.tgz", - "integrity": "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "chokidar": "^4.0.1", - "fdir": "^6.2.0", - "picocolors": "^1.0.0", - "sade": "^1.7.4" - }, - "bin": { - "svelte-check": "bin/svelte-check" - }, - "engines": { - "node": ">= 18.0.0" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": ">=5.0.0" - } - }, - "node_modules/svelte-eslint-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", - "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.0", - "postcss": "^8.4.49", - "postcss-scss": "^4.0.9", - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/svelte-gestures": { - "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": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.1.tgz", - "integrity": "sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==", - "license": "MIT", - "dependencies": { - "cli-color": "^2.0.3", - "deepmerge": "^4.2.2", - "esbuild": "^0.19.2", - "estree-walker": "^2", - "intl-messageformat": "^10.5.3", - "sade": "^1.8.1", - "tiny-glob": "^0.2.9" - }, - "bin": { - "svelte-i18n": "dist/cli.js" - }, - "engines": { - "node": ">= 16" - }, - "peerDependencies": { - "svelte": "^3 || ^4 || ^5" - } - }, - "node_modules/svelte-maplibre": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-1.2.0.tgz", - "integrity": "sha512-JKYzL0glnqCJ7LwkdDAMb3jdZdFl8ZDHEZyc043BV624kG9ZVaXlIPgjb8sNktqx1D0rQBNrYNt5rR4XszNCiQ==", - "license": "MIT", - "dependencies": { - "d3-geo": "^3.1.0", - "dequal": "^2.0.3", - "just-compare": "^2.3.0", - "maplibre-gl": "^4.0.0 || ^5.0.1", - "pmtiles": "^3.0.3" - }, - "peerDependencies": { - "@deck.gl/core": "^9", - "@deck.gl/layers": "^9", - "@deck.gl/mapbox": "^9", - "svelte": "^5.0.0" - }, - "peerDependenciesMeta": { - "@deck.gl/core": { - "optional": true - }, - "@deck.gl/layers": { - "optional": true - }, - "@deck.gl/mapbox": { - "optional": true - } - } - }, - "node_modules/svelte-maplibre/node_modules/pmtiles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.2.1.tgz", - "integrity": "sha512-3R4fBwwoli5mw7a6t1IGwOtfmcSAODq6Okz0zkXhS1zi9sz1ssjjIfslwPvcWw5TNhdjNBUg9fgfPLeqZlH6ng==", - "license": "BSD-3-Clause", - "dependencies": { - "@types/leaflet": "^1.9.8", - "fflate": "^0.8.0" - } - }, - "node_modules/svelte-parse-markup": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/svelte-parse-markup/-/svelte-parse-markup-0.1.5.tgz", - "integrity": "sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://bjornlu.com/sponsor" - }, - "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1" - } - }, - "node_modules/svelte-persisted-store": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/svelte-persisted-store/-/svelte-persisted-store-0.12.0.tgz", - "integrity": "sha512-BdBQr2SGSJ+rDWH8/aEV5GthBJDapVP0GP3fuUCA7TjYG5ctcB+O9Mj9ZC0+Jo1oJMfZUd1y9H68NFRR5MyIJA==", - "license": "MIT", - "engines": { - "node": ">=0.14" - }, - "peerDependencies": { - "svelte": "^3.48.0 || ^4 || ^5" - } - }, - "node_modules/svelte-toolbelt": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.9.3.tgz", - "integrity": "sha512-HCSWxCtVmv+c6g1ACb8LTwHVbDqLKJvHpo6J8TaqwUme2hj9ATJCpjCPNISR1OCq2Q4U1KT41if9ON0isINQZw==", - "funding": [ - "https://github.com/sponsors/huntabyte" - ], - "dependencies": { - "clsx": "^2.1.1", - "runed": "^0.29.0", - "style-to-object": "^1.0.8" - }, - "engines": { - "node": ">=18", - "pnpm": ">=8.7.0" - }, - "peerDependencies": { - "svelte": "^5.30.2" - } - }, - "node_modules/svelte/node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "license": "MIT", - "optional": true - }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "license": "MIT" - }, - "node_modules/tailwind-merge": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", - "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwind-variants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-1.0.0.tgz", - "integrity": "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA==", - "license": "MIT", - "dependencies": { - "tailwind-merge": "3.0.2" - }, - "engines": { - "node": ">=16.x", - "pnpm": ">=7.x" - }, - "peerDependencies": { - "tailwindcss": "*" - } - }, - "node_modules/tailwind-variants/node_modules/tailwind-merge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", - "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", - "optional": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/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/test-exclude/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/test-exclude/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/test-exclude/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/three": { - "version": "0.175.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.175.0.tgz", - "integrity": "sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg==", - "license": "MIT" - }, - "node_modules/thumbhash": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz", - "integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==", - "license": "MIT" - }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "license": "MIT", - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "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.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyqueue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", - "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", - "license": "ISC", - "peer": true - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", - "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "license": "ISC" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typescript-eslint/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/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", - "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-imagetools": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-7.1.0.tgz", - "integrity": "sha512-Mqh1uUY2DEMuBOogFz5Rd7cAs70VP6wsdQh2IShrJ+qGk5f7yQa4pN8w0YMLlGIKYW1JfM8oXrznUwVkhG+qxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.5", - "imagetools-core": "^7.1.0", - "sharp": "^0.34.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" - } - }, - "node_modules/vitefu": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", - "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", - "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "license": "MIT", - "optional": true, - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "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==", - "license": "BSD-2-Clause", - "optional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC" - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "optional": true - }, - "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "license": "MIT", - "optional": true, - "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/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "license": "MIT", - "optional": true - }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zimmerframe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", - "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", - "license": "MIT" - } - } -} diff --git a/web/package.json b/web/package.json index c63c52a916..fae4568097 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.135.3", + "version": "2.0.1", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { @@ -9,17 +9,16 @@ "build:stats": "BUILD_STATS=true vite build", "package": "svelte-kit package", "preview": "vite preview", - "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:svelte": "svelte-check --no-tsconfig --fail-on-warnings", "check:typescript": "tsc --noEmit", "check:watch": "npm run check:svelte -- --watch", "check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript", "check:all": "npm run check:code && npm run test:cov", - "lint": "eslint . --max-warnings 0", - "lint:p": "eslint-p . --max-warnings 0 --concurrency=4", + "lint": "eslint . --max-warnings 0 --concurrency 4", "lint:fix": "npm run lint -- --fix", "format": "prettier --check .", "format:fix": "prettier --write . && npm run format:i18n", - "format:i18n": "npx --yes sort-json ../i18n/*.json", + "format:i18n": "pnpx sort-json ../i18n/*.json", "test": "vitest --run", "test:cov": "vitest --coverage", "test:watch": "vitest dev", @@ -28,7 +27,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.29.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -50,27 +49,27 @@ "justified-layout": "^4.1.0", "lodash-es": "^4.17.21", "luxon": "^3.4.4", - "maplibre-gl": "^5.3.0", + "maplibre-gl": "^5.6.2", "pmtiles": "^4.3.0", "qrcode": "^1.5.4", + "simple-icons": "^15.15.0", "socket.io-client": "~4.8.0", - "svelte-gestures": "^5.1.3", + "svelte-gestures": "^5.2.2", "svelte-i18n": "^4.0.1", - "svelte-maplibre": "^1.0.0", + "svelte-maplibre": "^1.2.0", "svelte-persisted-store": "^0.12.0", "tabbable": "^6.2.0", "thumbhash": "^0.1.1" }, "devDependencies": { - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.18.0", - "@faker-js/faker": "^9.3.0", + "@eslint/js": "^9.36.0", + "@faker-js/faker": "^10.0.0", "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.8.0", + "@sveltejs/kit": "^2.27.1", + "@sveltejs/vite-plugin-svelte": "6.2.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -82,14 +81,12 @@ "@types/luxon": "^3.4.2", "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", - "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", - "eslint-p": "^0.25.0", + "dotenv": "^17.0.0", + "eslint": "^9.36.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-compat": "^6.0.2", - "eslint-plugin-svelte": "^3.9.0", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-svelte": "^3.12.4", + "eslint-plugin-unicorn": "^61.0.2", "factory.ts": "^1.4.1", "globals": "^16.0.0", "happy-dom": "^18.0.1", @@ -98,17 +95,16 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.38.10", "svelte-check": "^4.1.5", - "svelte-eslint-parser": "^1.2.0", + "svelte-eslint-parser": "^1.3.3", "tailwindcss": "^4.1.7", - "tslib": "^2.6.2", - "typescript": "^5.7.3", - "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.45.0", + "vite": "^7.1.2", "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.20.0" } } diff --git a/web/src/app.css b/web/src/app.css index db6c43652b..f66743f736 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -169,3 +169,13 @@ filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.8)); } } + +.maplibregl-popup { + .maplibregl-popup-tip { + @apply border-t-subtle! translate-y-[-1px]; + } + + .maplibregl-popup-content { + @apply bg-subtle rounded-lg; + } +} diff --git a/web/src/app.d.ts b/web/src/app.d.ts index d0d25443c9..e0631e3617 100644 --- a/web/src/app.d.ts +++ b/web/src/app.d.ts @@ -36,7 +36,7 @@ type NestedKeys = K extends keyof T & string : never; declare module 'svelte-i18n' { - import type { InterpolationValues } from '$lib/components/i18n/format-message.svelte'; + import type { InterpolationValues } from '$lib/elements/format-message.svelte'; import type { Readable } from 'svelte/store'; type Translations = NestedKeys; diff --git a/web/src/app.html b/web/src/app.html index ec8c4393ff..776764850f 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -21,11 +21,6 @@ html { height: 100%; width: 100%; - background-color: rgb(255, 255, 255); - } - - html.dark { - background-color: rgb(10, 10, 10); } body, @@ -34,10 +29,6 @@ padding: 0; } - body { - transition: background-color 0.15s ease; - } - @keyframes delayedVisibility { to { visibility: visible; diff --git a/web/src/lib/assets/empty-5.svg b/web/src/lib/assets/empty-5.svg new file mode 100644 index 0000000000..e9e24d0499 --- /dev/null +++ b/web/src/lib/assets/empty-5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/lib/assets/svg-paths.ts b/web/src/lib/assets/svg-paths.ts index ded3db0fc8..cc8d0a1800 100644 --- a/web/src/lib/assets/svg-paths.ts +++ b/web/src/lib/assets/svg-paths.ts @@ -4,7 +4,3 @@ export const sunPath = export const moonViewBox = '0 0 20 20'; export const sunViewBox = '0 0 20 20'; - -export const discordPath = - 'M81.15,0c-1.2376,2.1973-2.3489,4.4704-3.3591,6.794-9.5975-1.4396-19.3718-1.4396-28.9945,0-.985-2.3236-2.1216-4.5967-3.3591-6.794-9.0166,1.5407-17.8059,4.2431-26.1405,8.0568C2.779,32.5304-1.6914,56.3725.5312,79.8863c9.6732,7.1476,20.5083,12.603,32.0505,16.0884,2.6014-3.4854,4.8998-7.1981,6.8698-11.0623-3.738-1.3891-7.3497-3.1318-10.8098-5.1523.9092-.6567,1.7932-1.3386,2.6519-1.9953,20.281,9.547,43.7696,9.547,64.0758,0,.8587.7072,1.7427,1.3891,2.6519,1.9953-3.4601,2.0457-7.0718,3.7632-10.835,5.1776,1.97,3.8642,4.2683,7.5769,6.8698,11.0623,11.5419-3.4854,22.3769-8.9156,32.0509-16.0631,2.626-27.2771-4.496-50.9172-18.817-71.8548C98.9811,4.2684,90.1918,1.5659,81.1752.0505l-.0252-.0505ZM42.2802,65.4144c-6.2383,0-11.4159-5.6575-11.4159-12.6535s4.9755-12.6788,11.3907-12.6788,11.5169,5.708,11.4159,12.6788c-.101,6.9708-5.026,12.6535-11.3907,12.6535ZM84.3576,65.4144c-6.2637,0-11.3907-5.6575-11.3907-12.6535s4.9755-12.6788,11.3907-12.6788,11.4917,5.708,11.3906,12.6788c-.101,6.9708-5.026,12.6535-11.3906,12.6535Z'; -export const discordViewBox = '0 0 126.644 96'; diff --git a/web/src/lib/components/ServerAboutItem.svelte b/web/src/lib/components/ServerAboutItem.svelte new file mode 100644 index 0000000000..9e169a9839 --- /dev/null +++ b/web/src/lib/components/ServerAboutItem.svelte @@ -0,0 +1,24 @@ + + +
+ + + {#if versionHref} + {version} + {:else} + {version} + {/if} + +
diff --git a/web/src/lib/components/admin-page/settings/admin-settings.svelte b/web/src/lib/components/admin-settings/AdminSettings.svelte similarity index 100% rename from web/src/lib/components/admin-page/settings/admin-settings.svelte rename to web/src/lib/components/admin-settings/AdminSettings.svelte diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-settings/AuthSettings.svelte similarity index 83% rename from web/src/lib/components/admin-page/settings/auth/auth-settings.svelte rename to web/src/lib/components/admin-settings/AuthSettings.svelte index a1926b4020..dbc96ac02a 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-settings/AuthSettings.svelte @@ -1,18 +1,24 @@
@@ -56,11 +82,11 @@ subtitle={$t('admin.oauth_settings_description')} >
-

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

+ + +
+ {$t('admin.unlink_all_oauth_accounts_description')} + +
+ + 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 { SettingInputFieldType } from '$lib/constants'; + import FormatMessage from '$lib/elements/FormatMessage.svelte'; import type { SystemConfigDto } from '@immich/sdk'; import { isEqual } from 'lodash-es'; - import { fade } from 'svelte/transition'; - import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings'; - import SettingInputField from '$lib/components/shared-components/settings/setting-input-field.svelte'; - import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; - import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; - import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import { t } from 'svelte-i18n'; - import FormatMessage from '$lib/components/i18n/format-message.svelte'; - import { SettingInputFieldType } from '$lib/constants'; + import { fade } from 'svelte/transition'; + import type { SettingsResetEvent, SettingsSaveEvent } from './admin-settings'; interface Props { savedConfig: SystemConfigDto; diff --git a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte b/web/src/lib/components/admin-settings/FFmpegSettings.svelte similarity index 98% rename from web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte rename to web/src/lib/components/admin-settings/FFmpegSettings.svelte index bb9b41f8de..d176839543 100644 --- a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte +++ b/web/src/lib/components/admin-settings/FFmpegSettings.svelte @@ -1,5 +1,12 @@
-

{$t('date_and_time').toUpperCase()}

+

{$t('date_and_time')}

@@ -27,7 +27,7 @@
-

{$t('year').toUpperCase()}

+

{$t('year')}

    {#each options.yearOptions as yearFormat, index (index)}
  • {'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}
  • @@ -36,7 +36,7 @@
-

{$t('month').toUpperCase()}

+

{$t('month')}

    {#each options.monthOptions as monthFormat, index (index)}
  • {'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}
  • @@ -45,7 +45,7 @@
-

{$t('week').toUpperCase()}

+

{$t('week')}

    {#each options.weekOptions as weekFormat, index (index)}
  • {'{{'}{weekFormat}{'}}'} - {getLuxonExample(weekFormat)}
  • @@ -54,7 +54,7 @@
-

{$t('day').toUpperCase()}

+

{$t('day')}

    {#each options.dayOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -63,7 +63,7 @@
-

{$t('hour').toUpperCase()}

+

{$t('hour')}

    {#each options.hourOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -72,7 +72,7 @@
-

{$t('minute').toUpperCase()}

+

{$t('minute')}

    {#each options.minuteOptions as dayFormat, index (index)}
  • {'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}
  • @@ -81,7 +81,7 @@
-

{$t('second').toUpperCase()}

+

{$t('second')}